mirror of
https://github.com/containous/traefik.git
synced 2025-09-24 21:44:26 +03:00
Compare commits
78 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
a3fd484728 | ||
|
f196de90e1 | ||
|
c2a294c872 | ||
|
8e5d4c6ae9 | ||
|
eb07a5ca1a | ||
|
84e20aa9c3 | ||
|
4e441d09ed | ||
|
8f5dd7bd9d | ||
|
86315e0f18 | ||
|
c20af070e3 | ||
|
8593581cbf | ||
|
8103992977 | ||
|
c5b92b5260 | ||
|
c19cf125e8 | ||
|
435d28c790 | ||
|
8272be0eda | ||
|
d2414feaff | ||
|
1aa450c028 | ||
|
f9ff6049d3 | ||
|
ee8305549a | ||
|
a31b026364 | ||
|
20d496268c | ||
|
5f3c30e37b | ||
|
38ac1e75a2 | ||
|
109a8712cc | ||
|
278e739242 | ||
|
db31a4c961 | ||
|
35ce6baaae | ||
|
95f20fc753 | ||
|
1c0094048b | ||
|
590ddfc990 | ||
|
39d7b77609 | ||
|
74e0abf8bf | ||
|
cc14c165c0 | ||
|
f2ba4353b2 | ||
|
42df9afeaf | ||
|
2df655cefe | ||
|
c120b70483 | ||
|
8eadfbb990 | ||
|
cc80568d9e | ||
|
8ffd1854db | ||
|
6baa110adb | ||
|
5658c8ac06 | ||
|
1c80f12bc2 | ||
|
ef5f1b1508 | ||
|
8c19652361 | ||
|
e5c80637fc | ||
|
f437fb4230 | ||
|
9c50129520 | ||
|
00a5f4c401 | ||
|
a79cdd1dfa | ||
|
2096fd7081 | ||
|
6f18344c56 | ||
|
08fe27ce5f | ||
|
27948493aa | ||
|
edc0a52b5a | ||
|
3d2336bc83 | ||
|
7edb9a2101 | ||
|
934ca5fd22 | ||
|
7b477f762a | ||
|
157cf75e38 | ||
|
ab35b3266a | ||
|
0a6b8780f0 | ||
|
fc563d3f6e | ||
|
306d3f277d | ||
|
6f7649fccc | ||
|
e8ab3af74d | ||
|
518caa79f9 | ||
|
4d6cb6af03 | ||
|
9eb804a689 | ||
|
2bb712135d | ||
|
14e5d4b4b3 | ||
|
e485edbe9f | ||
|
61bb3ab991 | ||
|
a42d396ed2 | ||
|
b00f640d72 | ||
|
ac42dd8f83 | ||
|
4b5968e0cc |
36
.github/workflows/build.yaml
vendored
36
.github/workflows/build.yaml
vendored
@@ -16,36 +16,7 @@ env:
|
||||
jobs:
|
||||
|
||||
build-webui:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: webui/.nvmrc
|
||||
cache: yarn
|
||||
cache-dependency-path: webui/yarn.lock
|
||||
|
||||
- name: Build webui
|
||||
working-directory: ./webui
|
||||
run: |
|
||||
yarn install
|
||||
yarn build
|
||||
|
||||
- name: Package webui
|
||||
run: |
|
||||
tar czvf webui.tar.gz ./webui/static/
|
||||
|
||||
- name: Artifact webui
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: webui.tar.gz
|
||||
path: webui.tar.gz
|
||||
uses: ./.github/workflows/template-webui.yaml
|
||||
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -90,6 +61,7 @@ jobs:
|
||||
ImageOS: ${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.goarm }}
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
check-latest: true
|
||||
|
||||
- name: Artifact webui
|
||||
uses: actions/download-artifact@v4
|
||||
@@ -97,7 +69,9 @@ jobs:
|
||||
name: webui.tar.gz
|
||||
|
||||
- name: Untar webui
|
||||
run: tar xvf webui.tar.gz
|
||||
run: |
|
||||
tar xvf webui.tar.gz
|
||||
rm webui.tar.gz
|
||||
|
||||
- name: Build
|
||||
env:
|
||||
|
32
.github/workflows/experimental.yaml
vendored
32
.github/workflows/experimental.yaml
vendored
@@ -12,36 +12,28 @@ env:
|
||||
|
||||
jobs:
|
||||
|
||||
build-webui:
|
||||
if: github.repository == 'traefik/traefik'
|
||||
uses: ./.github/workflows/template-webui.yaml
|
||||
|
||||
experimental:
|
||||
if: github.repository == 'traefik/traefik'
|
||||
name: Build experimental image on branch
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
|
||||
# https://github.com/marketplace/actions/checkout
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: webui/.nvmrc
|
||||
cache: yarn
|
||||
cache-dependency-path: webui/yarn.lock
|
||||
|
||||
- name: Build webui
|
||||
working-directory: ./webui
|
||||
run: |
|
||||
yarn install
|
||||
yarn build
|
||||
|
||||
- name: Set up Go ${{ env.GO_VERSION }}
|
||||
uses: actions/setup-go@v5
|
||||
env:
|
||||
ImageOS: ${{ matrix.os }}-${{ matrix.arch }}-${{ matrix.goarm }}
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
check-latest: true
|
||||
|
||||
- name: Build
|
||||
run: make generate binary
|
||||
@@ -61,6 +53,16 @@ jobs:
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Artifact webui
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: webui.tar.gz
|
||||
|
||||
- name: Untar webui
|
||||
run: |
|
||||
tar xvf webui.tar.gz
|
||||
rm webui.tar.gz
|
||||
|
||||
- name: Build docker experimental image
|
||||
env:
|
||||
DOCKER_BUILDX_ARGS: "--push"
|
||||
|
138
.github/workflows/release.yaml
vendored
Normal file
138
.github/workflows/release.yaml
vendored
Normal file
@@ -0,0 +1,138 @@
|
||||
name: Release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*.*.*'
|
||||
|
||||
env:
|
||||
GO_VERSION: '1.23'
|
||||
CGO_ENABLED: 0
|
||||
VERSION: ${{ github.ref_name }}
|
||||
TRAEFIKER_EMAIL: "traefiker@traefik.io"
|
||||
CODENAME: mimolette
|
||||
|
||||
jobs:
|
||||
|
||||
build-webui:
|
||||
if: github.ref_type == 'tag' && github.repository == 'traefik/traefik'
|
||||
uses: ./.github/workflows/template-webui.yaml
|
||||
|
||||
build:
|
||||
if: github.ref_type == 'tag' && github.repository == 'traefik/traefik'
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ linux-amd64, linux-386, linux-arm, linux-arm64, linux-ppc64le, linux-s390x, linux-riscv64, darwin, windows-amd64, windows-arm64, windows-386, freebsd, openbsd ]
|
||||
needs:
|
||||
- build-webui
|
||||
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Go ${{ env.GO_VERSION }}
|
||||
uses: actions/setup-go@v5
|
||||
env:
|
||||
# Ensure cache consistency on Linux, see https://github.com/actions/setup-go/pull/383
|
||||
ImageOS: ${{ matrix.os }}
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
check-latest: true
|
||||
|
||||
- name: Artifact webui
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: webui.tar.gz
|
||||
|
||||
- name: Untar webui
|
||||
run: |
|
||||
tar xvf webui.tar.gz
|
||||
rm webui.tar.gz
|
||||
|
||||
- name: Go generate
|
||||
run: go generate
|
||||
|
||||
|
||||
- name: Generate goreleaser file
|
||||
run: |
|
||||
GORELEASER_CONFIG_FILE_PATH=$(go run ./internal/release "${{ matrix.os }}")
|
||||
echo "GORELEASER_CONFIG_FILE_PATH=$GORELEASER_CONFIG_FILE_PATH" >> $GITHUB_ENV
|
||||
|
||||
- name: Build with goreleaser
|
||||
uses: goreleaser/goreleaser-action@v6
|
||||
with:
|
||||
distribution: goreleaser
|
||||
# 'latest', 'nightly', or a semver
|
||||
version: '~> v2'
|
||||
args: release --clean --timeout="90m" --config "${{ env.GORELEASER_CONFIG_FILE_PATH }}"
|
||||
|
||||
- name: Artifact binaries
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ matrix.os }}-binaries
|
||||
path: |
|
||||
dist/**/*_checksums.txt
|
||||
dist/**/*.tar.gz
|
||||
dist/**/*.zip
|
||||
retention-days: 1
|
||||
|
||||
release:
|
||||
if: github.ref_type == 'tag' && github.repository == 'traefik/traefik'
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
needs:
|
||||
- build
|
||||
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Artifact webui
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: webui.tar.gz
|
||||
|
||||
- name: Untar webui
|
||||
run: |
|
||||
tar xvf webui.tar.gz
|
||||
rm webui.tar.gz
|
||||
|
||||
- name: Retrieve the secret and decode it to a file
|
||||
env:
|
||||
TRAEFIKER_RSA: ${{ secrets.TRAEFIKER_RSA }}
|
||||
run: |
|
||||
mkdir -p ~/.ssh
|
||||
echo "${TRAEFIKER_RSA}" | base64 --decode > ~/.ssh/traefiker_rsa
|
||||
|
||||
- name: Download All Artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: dist/
|
||||
pattern: "*-binaries"
|
||||
merge-multiple: true
|
||||
|
||||
- name: Publish Release
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
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 "$(id -u)":"$(id -g)" dist/
|
||||
gh release create ${VERSION} ./dist/**/traefik*.{zip,tar.gz} ./dist/traefik*.{tar.gz,txt} --repo traefik/traefik --title ${VERSION} --notes ${VERSION}
|
||||
|
||||
./script/deploy.sh
|
||||
|
37
.github/workflows/template-webui.yaml
vendored
Normal file
37
.github/workflows/template-webui.yaml
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
name: Build Web UI
|
||||
on:
|
||||
workflow_call: {}
|
||||
jobs:
|
||||
|
||||
build-webui:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: webui/.nvmrc
|
||||
cache: yarn
|
||||
cache-dependency-path: webui/yarn.lock
|
||||
|
||||
- name: Build webui
|
||||
working-directory: ./webui
|
||||
run: |
|
||||
yarn install
|
||||
yarn build
|
||||
|
||||
- name: Package webui
|
||||
run: |
|
||||
tar czvf webui.tar.gz ./webui/static/
|
||||
|
||||
- name: Artifact webui
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: webui.tar.gz
|
||||
path: webui.tar.gz
|
||||
retention-days: 1
|
2
.github/workflows/test-integration.yaml
vendored
2
.github/workflows/test-integration.yaml
vendored
@@ -28,6 +28,7 @@ jobs:
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
check-latest: true
|
||||
|
||||
- name: Avoid generating webui
|
||||
run: touch webui/static/index.html
|
||||
@@ -55,6 +56,7 @@ jobs:
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
check-latest: true
|
||||
|
||||
- name: Avoid generating webui
|
||||
run: touch webui/static/index.html
|
||||
|
1
.github/workflows/test-unit.yaml
vendored
1
.github/workflows/test-unit.yaml
vendored
@@ -27,6 +27,7 @@ jobs:
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
check-latest: true
|
||||
|
||||
- name: Avoid generating webui
|
||||
run: touch webui/static/index.html
|
||||
|
29
.github/workflows/validate.yaml
vendored
29
.github/workflows/validate.yaml
vendored
@@ -7,11 +7,31 @@ on:
|
||||
|
||||
env:
|
||||
GO_VERSION: '1.23'
|
||||
GOLANGCI_LINT_VERSION: v1.60.3
|
||||
GOLANGCI_LINT_VERSION: v1.64.2
|
||||
MISSPELL_VERSION: v0.6.0
|
||||
|
||||
jobs:
|
||||
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Go ${{ env.GO_VERSION }}
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
check-latest: true
|
||||
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@v6
|
||||
with:
|
||||
version: "${{ env.GOLANGCI_LINT_VERSION }}"
|
||||
|
||||
validate:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
@@ -25,9 +45,7 @@ jobs:
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
|
||||
- name: Install golangci-lint ${{ env.GOLANGCI_LINT_VERSION }}
|
||||
run: curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin ${GOLANGCI_LINT_VERSION}
|
||||
check-latest: true
|
||||
|
||||
- name: Install misspell ${{ env.MISSPELL_VERSION }}
|
||||
run: curl -sfL https://raw.githubusercontent.com/golangci/misspell/master/install-misspell.sh | sh -s -- -b $(go env GOPATH)/bin ${MISSPELL_VERSION}
|
||||
@@ -36,7 +54,7 @@ jobs:
|
||||
run: touch webui/static/index.html
|
||||
|
||||
- name: Validate
|
||||
run: make validate
|
||||
run: make validate-files
|
||||
|
||||
validate-generate:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -51,6 +69,7 @@ jobs:
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
check-latest: true
|
||||
|
||||
- name: go generate
|
||||
run: |
|
||||
|
@@ -1,5 +1,6 @@
|
||||
run:
|
||||
timeout: 10m
|
||||
relative-path-mode: cfg
|
||||
|
||||
linters-settings:
|
||||
govet:
|
||||
@@ -141,6 +142,9 @@ linters-settings:
|
||||
- name: unreachable-code
|
||||
- name: redefines-builtin-id
|
||||
gomoddirectives:
|
||||
tool-forbidden: true
|
||||
toolchain-pattern: 'go1\.\d+\.\d+$'
|
||||
go-version-pattern: '^1\.\d+(\.0)?$'
|
||||
replace-allow-list:
|
||||
- github.com/abbot/go-http-auth
|
||||
- github.com/gorilla/mux
|
||||
@@ -159,8 +163,7 @@ linters-settings:
|
||||
linters:
|
||||
enable-all: true
|
||||
disable:
|
||||
- execinquery # deprecated
|
||||
- gomnd # deprecated
|
||||
- tenv # Deprecated
|
||||
- sqlclosecheck # not relevant (SQL)
|
||||
- rowserrcheck # not relevant (SQL)
|
||||
- cyclop # duplicate of gocyclo
|
||||
@@ -197,7 +200,6 @@ linters:
|
||||
- maintidx # kind of duplicate of gocyclo
|
||||
- nonamedreturns # Too strict
|
||||
- gosmopolitan # not relevant
|
||||
- exportloopref # Not relevant since go1.22
|
||||
|
||||
issues:
|
||||
exclude-use-default: false
|
||||
@@ -273,3 +275,14 @@ issues:
|
||||
- path: pkg/provider/acme/local_store.go
|
||||
linters:
|
||||
- musttag
|
||||
- path: pkg/tls/certificate.go
|
||||
text: 'the methods of "Certificates" use pointer receiver and non-pointer receiver.'
|
||||
linters:
|
||||
- recvcheck
|
||||
|
||||
output:
|
||||
show-stats: true
|
||||
sort-results: true
|
||||
sort-order:
|
||||
- linter
|
||||
- file
|
||||
|
@@ -1,12 +1,11 @@
|
||||
project_name: traefik
|
||||
version: 2
|
||||
|
||||
[[if .GOARCH]]
|
||||
dist: "./dist/[[ .GOOS ]]-[[ .GOARCH ]]"
|
||||
[[else]]
|
||||
dist: "./dist/[[ .GOOS ]]"
|
||||
|
||||
[[ if eq .GOOS "linux" ]]
|
||||
before:
|
||||
hooks:
|
||||
- go generate
|
||||
[[ end ]]
|
||||
[[end]]
|
||||
|
||||
builds:
|
||||
- binary: traefik
|
||||
@@ -21,6 +20,9 @@ builds:
|
||||
goos:
|
||||
- "[[ .GOOS ]]"
|
||||
goarch:
|
||||
[[if .GOARCH]]
|
||||
- "[[ .GOARCH ]]"
|
||||
[[else]]
|
||||
- amd64
|
||||
- '386'
|
||||
- arm
|
||||
@@ -28,6 +30,7 @@ builds:
|
||||
- ppc64le
|
||||
- s390x
|
||||
- riscv64
|
||||
[[end]]
|
||||
goarm:
|
||||
- '7'
|
||||
- '6'
|
||||
|
@@ -1,63 +1,13 @@
|
||||
version: v1.0
|
||||
name: Traefik
|
||||
name: Traefik Release - deprecated
|
||||
agent:
|
||||
machine:
|
||||
type: e1-standard-4
|
||||
os_image: ubuntu2004
|
||||
|
||||
fail_fast:
|
||||
stop:
|
||||
when: "branch != 'master'"
|
||||
|
||||
auto_cancel:
|
||||
queued:
|
||||
when: "branch != 'master'"
|
||||
running:
|
||||
when: "branch != 'master'"
|
||||
|
||||
global_job_config:
|
||||
prologue:
|
||||
commands:
|
||||
- curl -sSfL https://raw.githubusercontent.com/ldez/semgo/master/godownloader.sh | sudo sh -s -- -b "/usr/local/bin"
|
||||
- sudo semgo go1.23
|
||||
- export "GOPATH=$(go env GOPATH)"
|
||||
- export "SEMAPHORE_GIT_DIR=${GOPATH}/src/github.com/traefik/${SEMAPHORE_PROJECT_NAME}"
|
||||
- export "PATH=${GOPATH}/bin:${PATH}"
|
||||
- mkdir -vp "${SEMAPHORE_GIT_DIR}" "${GOPATH}/bin"
|
||||
- export GOPROXY=https://proxy.golang.org,direct
|
||||
- curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b "${GOPATH}/bin" v1.60.3
|
||||
- curl -sSfL https://gist.githubusercontent.com/traefiker/6d7ac019c11d011e4f131bb2cca8900e/raw/goreleaser.sh | bash -s -- -b "${GOPATH}/bin"
|
||||
- checkout
|
||||
- cache restore traefik-$(checksum go.sum)
|
||||
|
||||
type: f1-standard-2
|
||||
os_image: ubuntu2204
|
||||
blocks:
|
||||
- name: Release
|
||||
dependencies: []
|
||||
run:
|
||||
when: "tag =~ '.*'"
|
||||
- name: 'Do nothing'
|
||||
task:
|
||||
agent:
|
||||
machine:
|
||||
type: e1-standard-8
|
||||
os_image: ubuntu2004
|
||||
secrets:
|
||||
- name: traefik
|
||||
env_vars:
|
||||
- name: GH_VERSION
|
||||
value: 2.32.1
|
||||
- name: CODENAME
|
||||
value: "mimolette"
|
||||
prologue:
|
||||
commands:
|
||||
- export VERSION=${SEMAPHORE_GIT_TAG_NAME}
|
||||
- curl -sSL -o /tmp/gh_${GH_VERSION}_linux_amd64.tar.gz https://github.com/cli/cli/releases/download/v${GH_VERSION}/gh_${GH_VERSION}_linux_amd64.tar.gz
|
||||
- tar -zxvf /tmp/gh_${GH_VERSION}_linux_amd64.tar.gz -C /tmp
|
||||
- sudo mv /tmp/gh_${GH_VERSION}_linux_amd64/bin/gh /usr/local/bin/gh
|
||||
- sudo rm -rf ~/.phpbrew ~/.kerl ~/.sbt ~/.nvm ~/.npm ~/.kiex /usr/lib/jvm /opt/az /opt/firefox /usr/lib/google-cloud-sdk ~/.rbenv ~/.pip_download_cache # Remove unnecessary data.
|
||||
- sudo service docker stop && sudo umount /var/lib/docker && sudo service docker start # Unmounts the docker disk and the whole system disk is usable.
|
||||
jobs:
|
||||
- name: Release
|
||||
- name: 'Do nothing'
|
||||
commands:
|
||||
- make release-packages
|
||||
- gh release create ${SEMAPHORE_GIT_TAG_NAME} ./dist/**/traefik*.{zip,tar.gz} ./dist/traefik*.{tar.gz,txt} --repo traefik/traefik --title ${SEMAPHORE_GIT_TAG_NAME} --notes ${SEMAPHORE_GIT_TAG_NAME}
|
||||
- ./script/deploy.sh
|
||||
- echo "Do nothing"
|
||||
|
123
CHANGELOG.md
123
CHANGELOG.md
@@ -1,3 +1,126 @@
|
||||
# [v2.11.21](https://github.com/traefik/traefik/tree/v2.11.21) (2025-02-24)
|
||||
[All Commits](https://github.com/traefik/traefik/compare/v2.11.20...v2.11.21)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[acme]** Bump github.com/go-acme/lego/v4 to v4.22.2 ([#11537](https://github.com/traefik/traefik/pull/11537) by [ldez](https://github.com/ldez))
|
||||
- **[cli]** Bump github.com/traefik/paerser to v0.2.2 ([#11530](https://github.com/traefik/traefik/pull/11530) by [kevinpollet](https://github.com/kevinpollet))
|
||||
- **[middleware]** Enable the retry middleware in the proxy ([#11536](https://github.com/traefik/traefik/pull/11536) by [kevinpollet](https://github.com/kevinpollet))
|
||||
- **[middleware]** Retry should send headers on Write ([#11534](https://github.com/traefik/traefik/pull/11534) by [kevinpollet](https://github.com/kevinpollet))
|
||||
|
||||
## [v2.11.20](https://github.com/traefik/traefik/tree/v2.11.20) (2025-01-31)
|
||||
[All Commits](https://github.com/traefik/traefik/compare/v2.11.19...v2.11.20)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[acme]** Graceful shutdown for ACME JSON write operation ([#11497](https://github.com/traefik/traefik/pull/11497) by [juliens](https://github.com/juliens))
|
||||
|
||||
**Documentation:**
|
||||
- Change docker-compose to docker compose ([#11496](https://github.com/traefik/traefik/pull/11496) by [khai-pi](https://github.com/khai-pi))
|
||||
|
||||
## [v2.11.19](https://github.com/traefik/traefik/tree/v2.11.19) (2025-01-29)
|
||||
[All Commits](https://github.com/traefik/traefik/compare/v2.11.18...v2.11.19)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[middleware]** Changing log message when client cert is not available to debug ([#11453](https://github.com/traefik/traefik/pull/11453) by [Nelwhix](https://github.com/Nelwhix))
|
||||
- **[service]** Do not create a logger instance for each proxy ([#11487](https://github.com/traefik/traefik/pull/11487) by [kevinpollet](https://github.com/kevinpollet))
|
||||
- **[webui]** Fix auto refresh not clearing on component unmount ([#11477](https://github.com/traefik/traefik/pull/11477) by [DoubleREW](https://github.com/DoubleREW))
|
||||
|
||||
**Documentation:**
|
||||
- Remove awesome.traefik.io reference in documentation section ([#11435](https://github.com/traefik/traefik/pull/11435) by [kevinpollet](https://github.com/kevinpollet))
|
||||
|
||||
## [v2.11.18](https://github.com/traefik/traefik/tree/v2.11.18) (2025-01-07)
|
||||
[All Commits](https://github.com/traefik/traefik/compare/v2.11.17...v2.11.18)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[websocket,server]** Disable http2 connect setting for websocket by default ([#11412](https://github.com/traefik/traefik/pull/11412) by [rtribotte](https://github.com/rtribotte))
|
||||
|
||||
## [v2.11.17](https://github.com/traefik/traefik/tree/v2.11.17) (2025-01-06)
|
||||
[All Commits](https://github.com/traefik/traefik/compare/v2.11.16...v2.11.17)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[acme]** Update go-acme/lego to v4.21.0 ([#11368](https://github.com/traefik/traefik/pull/11368) by [ldez](https://github.com/ldez))
|
||||
- **[middleware]** Fix typo in basicauth note ([#11397](https://github.com/traefik/traefik/pull/11397) by [tieje](https://github.com/tieje))
|
||||
- **[service]** Configure ErrorLog in httputil.ReverseProxy ([#11344](https://github.com/traefik/traefik/pull/11344) by [peacewalker122](https://github.com/peacewalker122))
|
||||
- Bump golang.org/x/net to v0.33.0 ([#11365](https://github.com/traefik/traefik/pull/11365) by [kevinpollet](https://github.com/kevinpollet))
|
||||
|
||||
**Documentation:**
|
||||
- **[acme]** Fix allowACMEByPass TOML example ([#11370](https://github.com/traefik/traefik/pull/11370) by [hannesbraun](https://github.com/hannesbraun))
|
||||
- **[k8s/crd]** Update copyright for 2025 ([#11383](https://github.com/traefik/traefik/pull/11383) by [kevinpollet](https://github.com/kevinpollet))
|
||||
|
||||
## [v2.11.16](https://github.com/traefik/traefik/tree/v2.11.16) (2024-12-16)
|
||||
[All Commits](https://github.com/traefik/traefik/compare/v2.11.15...v2.11.16)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[server]** Update golang.org/x dependencies ([#11336](https://github.com/traefik/traefik/pull/11336) by [rtribotte](https://github.com/rtribotte))
|
||||
|
||||
## [v2.11.15](https://github.com/traefik/traefik/tree/v2.11.15) (2024-12-06)
|
||||
[All Commits](https://github.com/traefik/traefik/compare/v2.11.14...v2.11.15)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[acme]** Update go-acme/lego to v4.20.4 ([#11295](https://github.com/traefik/traefik/pull/11295) by [ldez](https://github.com/ldez))
|
||||
- **[http3]** Update github.com/quic-go/quic-go to v0.48.2 ([#11320](https://github.com/traefik/traefik/pull/11320) by [kevinpollet](https://github.com/kevinpollet))
|
||||
|
||||
## [v2.11.14](https://github.com/traefik/traefik/tree/v2.11.14) (2024-11-20)
|
||||
[All Commits](https://github.com/traefik/traefik/compare/v2.11.13...v2.11.14)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[acme]** Update go-acme/lego to v4.20.2 ([#11263](https://github.com/traefik/traefik/pull/11263) by [ldez](https://github.com/ldez))
|
||||
- **[logs,server]** Change level of peeking first byte error log to DEBUG ([#11254](https://github.com/traefik/traefik/pull/11254) by [rtribotte](https://github.com/rtribotte))
|
||||
- **[middleware,server]** Drop untrusted X-Forwarded-Prefix header ([#11253](https://github.com/traefik/traefik/pull/11253) by [rtribotte](https://github.com/rtribotte))
|
||||
- **[server]** Apply keepalive config to h2c entrypoints ([#11276](https://github.com/traefik/traefik/pull/11276) by [davefu113](https://github.com/davefu113))
|
||||
- **[service]** Fix internal handlers ServiceBuilder composition ([#11281](https://github.com/traefik/traefik/pull/11281) by [juliens](https://github.com/juliens))
|
||||
|
||||
**Documentation:**
|
||||
- **[accesslogs]** Update access-logs.md, add examples for accesslog.format ([#11275](https://github.com/traefik/traefik/pull/11275) by [bluepuma77](https://github.com/bluepuma77))
|
||||
- Fix the defaultRule CLI examples ([#11282](https://github.com/traefik/traefik/pull/11282) by [kevinpollet](https://github.com/kevinpollet))
|
||||
- Fix spelling, grammar, and rephrase sections for clarity in some documentation pages ([#11280](https://github.com/traefik/traefik/pull/11280) by [AntoineDeveloper](https://github.com/AntoineDeveloper))
|
||||
- Fix absolute link in the migration guide ([#11269](https://github.com/traefik/traefik/pull/11269) by [kevinpollet](https://github.com/kevinpollet))
|
||||
- Add X-Forwarded-Prefix to the migration guide ([#11267](https://github.com/traefik/traefik/pull/11267) by [kevinpollet](https://github.com/kevinpollet))
|
||||
- Fix a small typo in entrypoints documentation ([#11261](https://github.com/traefik/traefik/pull/11261) by [quiode](https://github.com/quiode))
|
||||
- Add a warning about environment variables casing for static configuration ([#11226](https://github.com/traefik/traefik/pull/11226) by [anchal00](https://github.com/anchal00))
|
||||
- Improve documentation on dashboard ([#11220](https://github.com/traefik/traefik/pull/11220) by [mloiseleur](https://github.com/mloiseleur))
|
||||
|
||||
## [v2.11.13](https://github.com/traefik/traefik/tree/v2.11.13) (2024-10-28)
|
||||
[All Commits](https://github.com/traefik/traefik/compare/v2.11.12...v2.11.13)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[middleware,service]** Panic on aborted requests to properly close the connection ([#11129](https://github.com/traefik/traefik/pull/11129) by [tonybart1337](https://github.com/tonybart1337))
|
||||
|
||||
**Documentation:**
|
||||
- Update business callouts ([#11217](https://github.com/traefik/traefik/pull/11217) by [tomatokoolaid](https://github.com/tomatokoolaid))
|
||||
|
||||
## [v2.11.12](https://github.com/traefik/traefik/tree/v2.11.12) (2024-10-09)
|
||||
[All Commits](https://github.com/traefik/traefik/compare/v2.11.11...v2.11.12)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[middleware]** Bump github.com/klauspost/compress to dbd6c381492a ([#11162](https://github.com/traefik/traefik/pull/11162) by [kevinpollet](https://github.com/kevinpollet))
|
||||
- **[webui]** Upgrade to node 22.9 and yarn lock to fix vulnerabilities ([#11173](https://github.com/traefik/traefik/pull/11173) by [kevinpollet](https://github.com/kevinpollet))
|
||||
- **[webui]** Adopt a layout for the large amount of entrypoint port numbers ([#11157](https://github.com/traefik/traefik/pull/11157) by [framebassman](https://github.com/framebassman))
|
||||
|
||||
**Documentation:**
|
||||
- **[accesslogs]** Clarify that only header fields may be redacted in access-logs ([#11139](https://github.com/traefik/traefik/pull/11139) by [mattbnz](https://github.com/mattbnz))
|
||||
- Update business callout ([#11172](https://github.com/traefik/traefik/pull/11172) by [tomatokoolaid](https://github.com/tomatokoolaid))
|
||||
|
||||
## [v2.11.11](https://github.com/traefik/traefik/tree/v2.11.11) (2024-10-02)
|
||||
[All Commits](https://github.com/traefik/traefik/compare/v2.11.10...v2.11.11)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[acme]** Ensure defaultGeneratedCert.main as Subject's CN ([#10581](https://github.com/traefik/traefik/pull/10581) by [Lamatte](https://github.com/Lamatte))
|
||||
- **[middleware,authentication]** Clean connection headers for forward auth request only ([#11095](https://github.com/traefik/traefik/pull/11095) by [rtribotte](https://github.com/rtribotte))
|
||||
- **[middleware]** Bump github.com/klauspost/compress to 8e14b1b5a913 ([#11141](https://github.com/traefik/traefik/pull/11141) by [kevinpollet](https://github.com/kevinpollet))
|
||||
- **[server]** Rework condition to not log on timeout ([#11133](https://github.com/traefik/traefik/pull/11133) by [rtribotte](https://github.com/rtribotte))
|
||||
- **[webui]** Remove unused boot files from webui ([#11109](https://github.com/traefik/traefik/pull/11109) by [michelheusschen](https://github.com/michelheusschen))
|
||||
|
||||
**Documentation:**
|
||||
- **[accesslogs]** Specify default format value for access log ([#11130](https://github.com/traefik/traefik/pull/11130) by [darkweaver87](https://github.com/darkweaver87))
|
||||
- **[api]** Update API documentation to mention pagination ([#11115](https://github.com/traefik/traefik/pull/11115) by [lyrandy](https://github.com/lyrandy))
|
||||
|
||||
## [v2.11.10](https://github.com/traefik/traefik/tree/v2.11.10) (2024-09-19)
|
||||
[All Commits](https://github.com/traefik/traefik/compare/v2.11.9...v2.11.10)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[http3]** Bump github.com/quic-go/quic-go to v0.47.0 ([#11104](https://github.com/traefik/traefik/pull/11104) by [rtribotte](https://github.com/rtribotte))
|
||||
- **[server]** Check if ACME certificate resolver is not nil ([#11103](https://github.com/traefik/traefik/pull/11103) by [kevinpollet](https://github.com/kevinpollet))
|
||||
|
||||
## [v2.11.9](https://github.com/traefik/traefik/tree/v2.11.9) (2024-09-16)
|
||||
[All Commits](https://github.com/traefik/traefik/compare/v2.11.8...v2.11.9)
|
||||
|
||||
|
@@ -1,5 +1,5 @@
|
||||
# syntax=docker/dockerfile:1.2
|
||||
FROM alpine:3.20
|
||||
FROM alpine:3.21
|
||||
|
||||
RUN apk add --no-cache --no-progress ca-certificates tzdata
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2024 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
15
Makefile
15
Makefile
@@ -1,13 +1,10 @@
|
||||
SRCS = $(shell git ls-files '*.go' | grep -v '^vendor/')
|
||||
|
||||
TAG_NAME := $(shell git tag -l --contains HEAD)
|
||||
TAG_NAME := $(shell git describe --abbrev=0 --tags --exact-match)
|
||||
SHA := $(shell git rev-parse HEAD)
|
||||
VERSION_GIT := $(if $(TAG_NAME),$(TAG_NAME),$(SHA))
|
||||
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
|
||||
|
||||
@@ -123,20 +120,16 @@ lint:
|
||||
|
||||
.PHONY: validate-files
|
||||
#? validate-files: Validate code and docs
|
||||
validate-files: lint
|
||||
validate-files:
|
||||
$(foreach exec,$(LINT_EXECUTABLES),\
|
||||
$(if $(shell which $(exec)),,$(error "No $(exec) in PATH")))
|
||||
$(CURDIR)/script/validate-vendor.sh
|
||||
$(CURDIR)/script/validate-misspell.sh
|
||||
$(CURDIR)/script/validate-shell-script.sh
|
||||
|
||||
.PHONY: validate
|
||||
#? validate: Validate code, docs, and vendor
|
||||
validate: lint
|
||||
$(foreach exec,$(EXECUTABLES),\
|
||||
$(if $(shell which $(exec)),,$(error "No $(exec) in PATH")))
|
||||
$(CURDIR)/script/validate-vendor.sh
|
||||
$(CURDIR)/script/validate-misspell.sh
|
||||
$(CURDIR)/script/validate-shell-script.sh
|
||||
validate: lint validate-files
|
||||
|
||||
# Target for building images for multiple architectures.
|
||||
.PHONY: multi-arch-image-%
|
||||
|
@@ -61,7 +61,7 @@ _(But if you'd rather configure some of your routes manually, Traefik supports t
|
||||
- Provides HTTPS to your microservices by leveraging [Let's Encrypt](https://letsencrypt.org) (wildcard certificates support)
|
||||
- Circuit breakers, retry
|
||||
- See the magic through its clean web UI
|
||||
- Websocket, HTTP/2, GRPC ready
|
||||
- WebSocket, HTTP/2, GRPC ready
|
||||
- Provides metrics (Rest, Prometheus, Datadog, Statsd, InfluxDB)
|
||||
- Keeps access logs (JSON, CLF)
|
||||
- Fast
|
||||
@@ -91,8 +91,6 @@ You can access the simple HTML frontend of Traefik.
|
||||
|
||||
You can find the complete documentation of Traefik v2 at [https://doc.traefik.io/traefik/](https://doc.traefik.io/traefik/).
|
||||
|
||||
A collection of contributions around Traefik can be found at [https://awesome.traefik.io](https://awesome.traefik.io).
|
||||
|
||||
## Support
|
||||
|
||||
To get community support, you can:
|
||||
|
@@ -1,7 +1,7 @@
|
||||
# Security Policy
|
||||
|
||||
You can join our security mailing list to be aware of the latest announcements from our security team.
|
||||
You can subscribe sending a mail to security+subscribe@traefik.io or on [the online viewer](https://groups.google.com/a/traefik.io/forum/#!forum/security).
|
||||
You can subscribe by sending an email to security+subscribe@traefik.io or on [the online viewer](https://groups.google.com/a/traefik.io/forum/#!forum/security).
|
||||
|
||||
Reported vulnerabilities can be found on [cve.mitre.org](https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=traefik).
|
||||
|
||||
|
@@ -23,6 +23,7 @@ import (
|
||||
"github.com/traefik/traefik/v2/cmd"
|
||||
"github.com/traefik/traefik/v2/cmd/healthcheck"
|
||||
cmdVersion "github.com/traefik/traefik/v2/cmd/version"
|
||||
_ "github.com/traefik/traefik/v2/init"
|
||||
tcli "github.com/traefik/traefik/v2/pkg/cli"
|
||||
"github.com/traefik/traefik/v2/pkg/collector"
|
||||
"github.com/traefik/traefik/v2/pkg/config/dynamic"
|
||||
@@ -187,7 +188,7 @@ func setupServer(staticConfiguration *static.Configuration) (*server.Server, err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
acmeProviders := initACMEProvider(staticConfiguration, &providerAggregator, tlsManager, httpChallengeProvider, tlsChallengeProvider)
|
||||
acmeProviders := initACMEProvider(staticConfiguration, providerAggregator, tlsManager, httpChallengeProvider, tlsChallengeProvider, routinesPool)
|
||||
|
||||
// Entrypoints
|
||||
|
||||
@@ -365,7 +366,7 @@ func switchRouter(routerFactory *server.RouterFactory, serverEntryPointsTCP serv
|
||||
}
|
||||
|
||||
// initACMEProvider creates an acme provider from the ACME part of globalConfiguration.
|
||||
func initACMEProvider(c *static.Configuration, providerAggregator *aggregator.ProviderAggregator, tlsManager *traefiktls.Manager, httpChallengeProvider, tlsChallengeProvider challenge.Provider) []*acme.Provider {
|
||||
func initACMEProvider(c *static.Configuration, providerAggregator *aggregator.ProviderAggregator, tlsManager *traefiktls.Manager, httpChallengeProvider, tlsChallengeProvider challenge.Provider, routinesPool *safe.Pool) []*acme.Provider {
|
||||
localStores := map[string]*acme.LocalStore{}
|
||||
|
||||
var resolvers []*acme.Provider
|
||||
@@ -375,7 +376,7 @@ func initACMEProvider(c *static.Configuration, providerAggregator *aggregator.Pr
|
||||
}
|
||||
|
||||
if localStores[resolver.ACME.Storage] == nil {
|
||||
localStores[resolver.ACME.Storage] = acme.NewLocalStore(resolver.ACME.Storage)
|
||||
localStores[resolver.ACME.Storage] = acme.NewLocalStore(resolver.ACME.Storage, routinesPool)
|
||||
}
|
||||
|
||||
p := &acme.Provider{
|
||||
|
@@ -1,4 +1,4 @@
|
||||
FROM alpine:3.20
|
||||
FROM alpine:3.21
|
||||
|
||||
RUN apk --no-cache --no-progress add \
|
||||
build-base \
|
||||
@@ -14,7 +14,7 @@ RUN apk --no-cache --no-progress add \
|
||||
ruby-json \
|
||||
zlib-dev
|
||||
|
||||
RUN gem install nokogiri --version 1.15.3 --no-document -- --use-system-libraries
|
||||
RUN gem install nokogiri --version 1.16.8 --no-document -- --use-system-libraries
|
||||
RUN gem install html-proofer --version 5.0.7 --no-document -- --use-system-libraries
|
||||
|
||||
# After Ruby, some NodeJS YAY!
|
||||
|
@@ -92,7 +92,7 @@ For development purposes, you can specify which tests to run by using (only work
|
||||
|
||||
Create `tailscale.secret` file in `integration` directory.
|
||||
|
||||
This file need to contains a [Tailscale auth key](https://tailscale.com/kb/1085/auth-keys)
|
||||
This file needs to contain a [Tailscale auth key](https://tailscale.com/kb/1085/auth-keys)
|
||||
(an ephemeral, but reusable, one is recommended).
|
||||
|
||||
Add this section to your tailscale ACLs to auto-approve the routes for the
|
||||
|
@@ -15,13 +15,13 @@ Let's see how.
|
||||
|
||||
### General
|
||||
|
||||
This [documentation](https://doc.traefik.io/traefik/ "Link to the official Traefik documentation") is built with [MkDocs](https://mkdocs.org/ "Link to website of MkDocs").
|
||||
This [documentation](https://doc.traefik.io/traefik/ "Link to the official Traefik documentation") is built with [MkDocs](https://mkdocs.org/ "Link to the website of MkDocs").
|
||||
|
||||
### Method 1: `Docker` and `make`
|
||||
|
||||
Please make sure you have the following requirements installed:
|
||||
|
||||
- [Docker](https://www.docker.com/ "Link to website of Docker")
|
||||
- [Docker](https://www.docker.com/ "Link to the website of Docker")
|
||||
|
||||
You can build the documentation and test it locally (with live reloading), using the `docs-serve` target:
|
||||
|
||||
@@ -51,7 +51,7 @@ $ make docs-build
|
||||
|
||||
Please make sure you have the following requirements installed:
|
||||
|
||||
- [Python](https://www.python.org/ "Link to website of Python")
|
||||
- [Python](https://www.python.org/ "Link to the website of Python")
|
||||
- [pip](https://pypi.org/project/pip/ "Link to the website of pip on PyPI")
|
||||
|
||||
```bash
|
||||
|
@@ -32,7 +32,7 @@ The contributor should also meet one or several of the following requirements:
|
||||
including those of other maintainers and contributors.
|
||||
|
||||
- The contributor is active on Traefik Community forums
|
||||
or other technical forums/boards such as K8S slack, Reddit, StackOverflow, hacker news.
|
||||
or other technical forums/boards, such as K8S Slack, Reddit, StackOverflow, and Hacker News.
|
||||
|
||||
Any existing active maintainer can create an issue to discuss promoting a contributor to maintainer.
|
||||
Other maintainers can vote on the issue, and if the quorum is reached, the contributor is promoted to maintainer.
|
||||
|
@@ -17,7 +17,7 @@ or the list of [confirmed bugs](https://github.com/traefik/traefik/labels/kind%2
|
||||
|
||||
## How We Prioritize
|
||||
|
||||
We wish we could review every pull request right away, but because it's a time consuming operation, it's not always possible.
|
||||
We wish we could review every pull request right away, but because it's a time-consuming operation, it's not always possible.
|
||||
|
||||
The PRs we are able to handle the fastest are:
|
||||
|
||||
@@ -128,7 +128,7 @@ This label can be used when:
|
||||
Traefik Proxy is made by the community for the community,
|
||||
as such the goal is to engage the community to make Traefik the best reverse proxy available.
|
||||
Part of this goal is maintaining a lean codebase and ensuring code velocity.
|
||||
unfortunately, this means that sometimes we will not be able to merge a pull request.
|
||||
Unfortunately, this means that sometimes we will not be able to merge a pull request.
|
||||
|
||||
Because we respect the work you did, you will always be told why we are closing your pull request.
|
||||
If you do not agree with our decision, do not worry; closed pull requests are effortless to recreate,
|
||||
|
@@ -8,7 +8,7 @@ description: "Security is a key part of Traefik Proxy. Read the technical docume
|
||||
## Security Advisories
|
||||
|
||||
We strongly advise you to join our mailing list to be aware of the latest announcements from our security team.
|
||||
You can subscribe sending a mail to security+subscribe@traefik.io or on [the online viewer](https://groups.google.com/a/traefik.io/forum/#!forum/security).
|
||||
You can subscribe by sending an email to security+subscribe@traefik.io or on [the online viewer](https://groups.google.com/a/traefik.io/forum/#!forum/security).
|
||||
|
||||
## CVE
|
||||
|
||||
|
@@ -6,7 +6,9 @@ Below is a non-exhaustive list of versions and their maintenance status:
|
||||
|
||||
| Version | Release Date | Community Support |
|
||||
|---------|--------------|--------------------|
|
||||
| 3.1 | Jul 15, 2024 | Yes |
|
||||
| 3.3 | Jan 06, 2025 | Yes |
|
||||
| 3.2 | Oct 28, 2024 | Ended Jan 06, 2025 |
|
||||
| 3.1 | Jul 15, 2024 | Ended Oct 28, 2024 |
|
||||
| 3.0 | Apr 29, 2024 | Ended Jul 15, 2024 |
|
||||
| 2.11 | Feb 12, 2024 | Ends Apr 29, 2025 |
|
||||
| 2.10 | Apr 24, 2023 | Ended Feb 12, 2024 |
|
||||
|
@@ -251,3 +251,5 @@ In which case, you should make sure your infrastructure is properly set up for a
|
||||
```shell
|
||||
LEGO_DISABLE_CNAME_SUPPORT=true
|
||||
```
|
||||
|
||||
{!traefik-for-business-applications.md!}
|
||||
|
@@ -38,7 +38,7 @@ services:
|
||||
Start your `reverse-proxy` with the following command:
|
||||
|
||||
```shell
|
||||
docker-compose up -d reverse-proxy
|
||||
docker compose up -d reverse-proxy
|
||||
```
|
||||
|
||||
You can open a browser and go to `http://localhost:8080/api/rawdata` to see Traefik's API rawdata (you'll go back there once you have launched a service in step 2).
|
||||
@@ -68,7 +68,7 @@ The above defines `whoami`: a web service that outputs information about the mac
|
||||
Start the `whoami` service with the following command:
|
||||
|
||||
```shell
|
||||
docker-compose up -d whoami
|
||||
docker compose up -d whoami
|
||||
```
|
||||
|
||||
Go back to your browser (`http://localhost:8080/api/rawdata`) and see that Traefik has automatically detected the new container and updated its own configuration.
|
||||
@@ -92,7 +92,7 @@ IP: 172.27.0.3
|
||||
Run more instances of your `whoami` service with the following command:
|
||||
|
||||
```shell
|
||||
docker-compose up -d --scale whoami=2
|
||||
docker compose up -d --scale whoami=2
|
||||
```
|
||||
|
||||
Go back to your browser (`http://localhost:8080/api/rawdata`) and see that Traefik has automatically detected the new instance of the container.
|
||||
|
@@ -316,17 +316,17 @@ For complete details, refer to your provider's _Additional configuration_ link.
|
||||
|
||||
| Provider Name | Provider Code | Environment Variables | |
|
||||
|------------------------------------------------------------------------|--------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------|
|
||||
| [ACME DNS](https://github.com/joohoi/acme-dns) | `acme-dns` | `ACME_DNS_API_BASE`, `ACME_DNS_STORAGE_PATH` | [Additional configuration](https://go-acme.github.io/lego/dns/acme-dns) |
|
||||
| [ACME DNS](https://github.com/joohoi/acme-dns) | `acme-dns` | `ACME_DNS_API_BASE`, `ACME_DNS_STORAGE_PATH`, `ACME_DNS_STORAGE_BASE_URL` | [Additional configuration](https://go-acme.github.io/lego/dns/acme-dns) |
|
||||
| [Alibaba Cloud](https://www.alibabacloud.com) | `alidns` | `ALICLOUD_ACCESS_KEY`, `ALICLOUD_SECRET_KEY`, `ALICLOUD_REGION_ID` | [Additional configuration](https://go-acme.github.io/lego/dns/alidns) |
|
||||
| [all-inkl](https://all-inkl.com) | `allinkl` | `ALL_INKL_LOGIN`, `ALL_INKL_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/allinkl) |
|
||||
| [ArvanCloud](https://www.arvancloud.ir/en) | `arvancloud` | `ARVANCLOUD_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/arvancloud) |
|
||||
| [Auroradns](https://www.pcextreme.com/dns-health-checks) | `auroradns` | `AURORA_USER_ID`, `AURORA_KEY`, `AURORA_ENDPOINT` | [Additional configuration](https://go-acme.github.io/lego/dns/auroradns) |
|
||||
| [Autodns](https://www.internetx.com/domains/autodns/) | `autodns` | `AUTODNS_API_USER`, `AUTODNS_API_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/autodns) |
|
||||
| [Azure](https://azure.microsoft.com/services/dns/) (DEPRECATED) | `azure` | `AZURE_CLIENT_ID`, `AZURE_CLIENT_SECRET`, `AZURE_SUBSCRIPTION_ID`, `AZURE_TENANT_ID`, `AZURE_RESOURCE_GROUP`, `[AZURE_METADATA_ENDPOINT]` | [Additional configuration](https://go-acme.github.io/lego/dns/azure) |
|
||||
| [Azure](https://azure.microsoft.com/services/dns/) (DEPRECATED) | `azure` | `AZURE_CLIENT_ID`, `AZURE_CLIENT_SECRET`, `AZURE_SUBSCRIPTION_ID`, `AZURE_TENANT_ID`, `AZURE_RESOURCE_GROUP`, `[AZURE_METADATA_ENDPOINT]` | [Additional configuration](https://go-acme.github.io/lego/dns/azure) |
|
||||
| [AzureDNS](https://azure.microsoft.com/services/dns/) | `azuredns` | `AZURE_CLIENT_ID`, `AZURE_CLIENT_SECRET`, `AZURE_TENANT_ID`, `AZURE_SUBSCRIPTION_ID`, `AZURE_RESOURCE_GROUP`, `[AZURE_ENVIRONMENT]`, `[AZURE_PRIVATE_ZONE]`, `[AZURE_ZONE_NAME]` | [Additional configuration](https://go-acme.github.io/lego/dns/azuredns) |
|
||||
| [Bindman](https://github.com/labbsr0x/bindman-dns-webhook) | `bindman` | `BINDMAN_MANAGER_ADDRESS` | [Additional configuration](https://go-acme.github.io/lego/dns/bindman) |
|
||||
| [Blue Cat](https://www.bluecatnetworks.com/) | `bluecat` | `BLUECAT_SERVER_URL`, `BLUECAT_USER_NAME`, `BLUECAT_PASSWORD`, `BLUECAT_CONFIG_NAME`, `BLUECAT_DNS_VIEW` | [Additional configuration](https://go-acme.github.io/lego/dns/bluecat) |
|
||||
| [Brandit](https://www.brandit.com) | `brandit` | `BRANDIT_API_USERNAME`, `BRANDIT_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/brandit) |
|
||||
| [Brandit](https://www.brandit.com) (DEPRECATED) | `brandit` | `BRANDIT_API_USERNAME`, `BRANDIT_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/brandit) |
|
||||
| [Bunny](https://bunny.net) | `bunny` | `BUNNY_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/bunny) |
|
||||
| [Checkdomain](https://www.checkdomain.de/) | `checkdomain` | `CHECKDOMAIN_TOKEN`, | [Additional configuration](https://go-acme.github.io/lego/dns/checkdomain/) |
|
||||
| [Civo](https://www.civo.com/) | `civo` | `CIVO_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/civo) |
|
||||
@@ -334,9 +334,10 @@ For complete details, refer to your provider's _Additional configuration_ link.
|
||||
| [CloudDNS](https://vshosting.eu/) | `clouddns` | `CLOUDDNS_CLIENT_ID`, `CLOUDDNS_EMAIL`, `CLOUDDNS_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/clouddns) |
|
||||
| [Cloudflare](https://www.cloudflare.com) | `cloudflare` | `CF_API_EMAIL`, `CF_API_KEY` [^5] or `CF_DNS_API_TOKEN`, `[CF_ZONE_API_TOKEN]` | [Additional configuration](https://go-acme.github.io/lego/dns/cloudflare) |
|
||||
| [ClouDNS](https://www.cloudns.net/) | `cloudns` | `CLOUDNS_AUTH_ID`, `CLOUDNS_AUTH_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/cloudns) |
|
||||
| [CloudXNS](https://www.cloudxns.net) | `cloudxns` | `CLOUDXNS_API_KEY`, `CLOUDXNS_SECRET_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/cloudxns) |
|
||||
| [CloudXNS](https://www.cloudxns.net) (DEPRECATED) | `cloudxns` | `CLOUDXNS_API_KEY`, `CLOUDXNS_SECRET_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/cloudxns) |
|
||||
| [ConoHa](https://www.conoha.jp) | `conoha` | `CONOHA_TENANT_ID`, `CONOHA_API_USERNAME`, `CONOHA_API_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/conoha) |
|
||||
| [Constellix](https://constellix.com) | `constellix` | `CONSTELLIX_API_KEY`, `CONSTELLIX_SECRET_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/constellix) |
|
||||
| [Core-Networks](https://www.core-networks.de) | `corenetworks` | `CORENETWORKS_LOGIN`, `CORENETWORKS_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/corenetworks) |
|
||||
| [CPanel and WHM](https://cpanel.net/) | `cpanel` | `CPANEL_MODE`, `CPANEL_USERNAME`, `CPANEL_TOKEN`, `CPANEL_BASE_URL` | [Additional configuration](https://go-acme.github.io/lego/dns/cpanel) |
|
||||
| [Derak Cloud](https://derak.cloud/) | `derak` | `DERAK_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/derak) |
|
||||
| [deSEC](https://desec.io) | `desec` | `DESEC_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/desec) |
|
||||
@@ -370,6 +371,7 @@ For complete details, refer to your provider's _Additional configuration_ link.
|
||||
| [hosting.de](https://www.hosting.de) | `hostingde` | `HOSTINGDE_API_KEY`, `HOSTINGDE_ZONE_NAME` | [Additional configuration](https://go-acme.github.io/lego/dns/hostingde) |
|
||||
| [Hosttech](https://www.hosttech.eu) | `hosttech` | `HOSTTECH_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/hosttech) |
|
||||
| [http.net](https://www.http.net/) | `httpnet` | `HTTPNET_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/httpnet) |
|
||||
| [Huawei Cloud](https://huaweicloud.com) | `huaweicloud` | `HUAWEICLOUD_ACCESS_KEY_ID`, `HUAWEICLOUD_SECRET_ACCESS_KEY`, `HUAWEICLOUD_REGION` | [Additional configuration](https://go-acme.github.io/lego/dns/huaweicloud) |
|
||||
| [Hurricane Electric](https://dns.he.net) | `hurricane` | `HURRICANE_TOKENS` [^6] | [Additional configuration](https://go-acme.github.io/lego/dns/hurricane) |
|
||||
| [HyperOne](https://www.hyperone.com) | `hyperone` | `HYPERONE_PASSPORT_LOCATION`, `HYPERONE_LOCATION_ID` | [Additional configuration](https://go-acme.github.io/lego/dns/hyperone) |
|
||||
| [IBM Cloud (SoftLayer)](https://www.ibm.com/cloud/) | `ibmcloud` | `SOFTLAYER_USERNAME`, `SOFTLAYER_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/ibmcloud) |
|
||||
@@ -391,9 +393,11 @@ For complete details, refer to your provider's _Additional configuration_ link.
|
||||
| [Loopia](https://loopia.com/) | `loopia` | `LOOPIA_API_PASSWORD`, `LOOPIA_API_USER` | [Additional configuration](https://go-acme.github.io/lego/dns/loopia) |
|
||||
| [LuaDNS](https://luadns.com) | `luadns` | `LUADNS_API_USERNAME`, `LUADNS_API_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/luadns) |
|
||||
| [Mail-in-a-Box](https://mailinabox.email) | `mailinabox` | `MAILINABOX_EMAIL`, `MAILINABOX_PASSWORD`, `MAILINABOX_BASE_URL` | [Additional configuration](https://go-acme.github.io/lego/dns/mailinabox) |
|
||||
| [ManageEngine CloudDNS](https://clouddns.manageengine.com) | `manageengine` | `MANAGEENGINE_CLIENT_ID`, `MANAGEENGINE_CLIENT_SECRET` | [Additional configuration](https://go-acme.github.io/lego/dns/manageengine) |
|
||||
| [Metaname](https://metaname.net) | `metaname` | `METANAME_ACCOUNT_REFERENCE`, `METANAME_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/metaname) |
|
||||
| [mijn.host](https://mijn.host/) | `mijnhost` | `MIJNHOST_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/mijnhost) |
|
||||
| [Mittwald](https://www.mittwald.de) | `mittwald` | `MITTWALD_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/mittwald) |
|
||||
| [myaddr.{tools,dev,io}](https://myaddr.tools/) | `myaddr` | `MYADDR_PRIVATE_KEYS_MAPPING` | [Additional configuration](https://go-acme.github.io/lego/dns/myaddr) |
|
||||
| [MyDNS.jp](https://www.mydns.jp/) | `mydnsjp` | `MYDNSJP_MASTER_ID`, `MYDNSJP_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/mydnsjp) |
|
||||
| [Mythic Beasts](https://www.mythic-beasts.com) | `mythicbeasts` | `MYTHICBEASTS_USER_NAME`, `MYTHICBEASTS_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/mythicbeasts) |
|
||||
| [name.com](https://www.name.com/) | `namedotcom` | `NAMECOM_USERNAME`, `NAMECOM_API_TOKEN`, `NAMECOM_SERVER` | [Additional configuration](https://go-acme.github.io/lego/dns/namedotcom) |
|
||||
@@ -415,8 +419,10 @@ For complete details, refer to your provider's _Additional configuration_ link.
|
||||
| [Porkbun](https://porkbun.com/) | `porkbun` | `PORKBUN_SECRET_API_KEY`, `PORKBUN_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/porkbun) |
|
||||
| [PowerDNS](https://www.powerdns.com) | `pdns` | `PDNS_API_KEY`, `PDNS_API_URL` | [Additional configuration](https://go-acme.github.io/lego/dns/pdns) |
|
||||
| [Rackspace](https://www.rackspace.com/cloud/dns) | `rackspace` | `RACKSPACE_USER`, `RACKSPACE_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/rackspace) |
|
||||
| [Rainyun/雨云](https://www.rainyun.com) | `rainyun` | `RAINYUN_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/rainyun) |
|
||||
| [RcodeZero](https://www.rcodezero.at) | `rcodezero` | `RCODEZERO_API_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/rcodezero) |
|
||||
| [reg.ru](https://www.reg.ru) | `regru` | `REGRU_USERNAME`, `REGRU_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/regru) |
|
||||
| [Regfish](https://regfish.de) | `regfish` | `regfish` | [Additional configuration](https://go-acme.github.io/lego/dns/regfish) |
|
||||
| [RFC2136](https://tools.ietf.org/html/rfc2136) | `rfc2136` | `RFC2136_TSIG_KEY`, `RFC2136_TSIG_SECRET`, `RFC2136_TSIG_ALGORITHM`, `RFC2136_NAMESERVER` | [Additional configuration](https://go-acme.github.io/lego/dns/rfc2136) |
|
||||
| [RimuHosting](https://rimuhosting.com) | `rimuhosting` | `RIMUHOSTING_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/rimuhosting) |
|
||||
| [Route 53](https://aws.amazon.com/route53/) | `route53` | `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `[AWS_REGION]`, `[AWS_HOSTED_ZONE_ID]` or a configured user/instance IAM profile. | [Additional configuration](https://go-acme.github.io/lego/dns/route53) |
|
||||
@@ -424,12 +430,16 @@ For complete details, refer to your provider's _Additional configuration_ link.
|
||||
| [Scaleway](https://www.scaleway.com) | `scaleway` | `SCW_API_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/scaleway) |
|
||||
| [Selectel v2](https://selectel.ru/en/) | `selectelv2` | `SELECTELV2_ACCOUNT_ID`, `SELECTELV2_PASSWORD`, `SELECTELV2_PROJECT_ID`, `SELECTELV2_USERNAME` | [Additional configuration](https://go-acme.github.io/lego/dns/selectelv2) |
|
||||
| [Selectel](https://selectel.ru/en/) | `selectel` | `SELECTEL_API_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/selectel) |
|
||||
| [SelfHost.(de/eu)](https://www.selfhost.de) | `selfhostde` | `SELFHOSTDE_USERNAME`, `SELFHOSTDE_PASSWORD`, `SELFHOSTDE_RECORDS_MAPPING` | [Additional configuration](https://go-acme.github.io/lego/dns/selfhostde) |
|
||||
| [Servercow](https://servercow.de) | `servercow` | `SERVERCOW_USERNAME`, `SERVERCOW_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/servercow) |
|
||||
| [Shellrent](https://www.shellrent.com) | `shellrent` | `SHELLRENT_USERNAME`, `SHELLRENT_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/shellrent) |
|
||||
| [Simply.com](https://www.simply.com/en/domains/) | `simply` | `SIMPLY_ACCOUNT_NAME`, `SIMPLY_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/simply) |
|
||||
| [Sonic](https://www.sonic.com/) | `sonic` | `SONIC_USER_ID`, `SONIC_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/sonic) |
|
||||
| [Spaceship](https://spaceship.com) | `spaceship` | `SPACESHIP_API_KEY`, `SPACESHIP_API_SECRET` | [Additional configuration](https://go-acme.github.io/lego/dns/spaceship) |
|
||||
| [Stackpath](https://www.stackpath.com/) | `stackpath` | `STACKPATH_CLIENT_ID`, `STACKPATH_CLIENT_SECRET`, `STACKPATH_STACK_ID` | [Additional configuration](https://go-acme.github.io/lego/dns/stackpath) |
|
||||
| [Technitium](https://technitium.com) | `technitium` | `TECHNITIUM_SERVER_BASE_URL`, `TECHNITIUM_API_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/technitium) |
|
||||
| [Tencent Cloud DNS](https://cloud.tencent.com/product/cns) | `tencentcloud` | `TENCENTCLOUD_SECRET_ID`, `TENCENTCLOUD_SECRET_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/tencentcloud) |
|
||||
| [Timeweb Cloud](https://timeweb.cloud) | `timewebcloud` | `TIMEWEBCLOUD_AUTH_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/timewebcloud) |
|
||||
| [TransIP](https://www.transip.nl/) | `transip` | `TRANSIP_ACCOUNT_NAME`, `TRANSIP_PRIVATE_KEY_PATH` | [Additional configuration](https://go-acme.github.io/lego/dns/transip) |
|
||||
| [UKFast SafeDNS](https://docs.ukfast.co.uk/domains/safedns/index.html) | `safedns` | `SAFEDNS_AUTH_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/safedns) |
|
||||
| [Ultradns](https://neustarsecurityservices.com/dns-services) | `ultradns` | `ULTRADNS_USERNAME`, `ULTRADNS_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/ultradns) |
|
||||
@@ -439,11 +449,13 @@ For complete details, refer to your provider's _Additional configuration_ link.
|
||||
| [Versio](https://www.versio.nl/domeinnamen) | `versio` | `VERSIO_USERNAME`, `VERSIO_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/versio) |
|
||||
| [VinylDNS](https://www.vinyldns.io) | `vinyldns` | `VINYLDNS_ACCESS_KEY`, `VINYLDNS_SECRET_KEY`, `VINYLDNS_HOST` | [Additional configuration](https://go-acme.github.io/lego/dns/vinyldns) |
|
||||
| [VK Cloud](https://mcs.mail.ru/) | `vkcloud` | `VK_CLOUD_PASSWORD`, `VK_CLOUD_PROJECT_ID`, `VK_CLOUD_USERNAME` | [Additional configuration](https://go-acme.github.io/lego/dns/vkcloud) |
|
||||
| [Volcano Engine](https://www.volcengine.com) | `volcengine` | `VOLC_ACCESSKEY`, `VOLC_SECRETKEY` | [Additional configuration](https://go-acme.github.io/lego/dns/volcengine) |
|
||||
| [Vscale](https://vscale.io/) | `vscale` | `VSCALE_API_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/vscale) |
|
||||
| [VULTR](https://www.vultr.com) | `vultr` | `VULTR_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/vultr) |
|
||||
| [Webnames](https://www.webnames.ru/) | `webnames` | `WEBNAMES_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/webnames) |
|
||||
| [Websupport](https://websupport.sk) | `websupport` | `WEBSUPPORT_API_KEY`, `WEBSUPPORT_SECRET` | [Additional configuration](https://go-acme.github.io/lego/dns/websupport) |
|
||||
| [WEDOS](https://www.wedos.com) | `wedos` | `WEDOS_USERNAME`, `WEDOS_WAPI_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/wedos) |
|
||||
| [West.cn/西部数码](https://www.west.cn) | `westcn` | `WESTCN_USERNAME`, `WESTCN_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/westcn) |
|
||||
| [Yandex 360](https://360.yandex.ru) | `yandex360` | `YANDEX360_OAUTH_TOKEN`, `YANDEX360_ORG_ID` | [Additional configuration](https://go-acme.github.io/lego/dns/yandex360) |
|
||||
| [Yandex Cloud](https://cloud.yandex.com/en/) | `yandexcloud` | `YANDEX_CLOUD_FOLDER_ID`, `YANDEX_CLOUD_IAM_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/yandexcloud) |
|
||||
| [Yandex](https://yandex.com) | `yandex` | `YANDEX_PDD_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/yandex) |
|
||||
@@ -460,11 +472,6 @@ For complete details, refer to your provider's _Additional configuration_ link.
|
||||
[^5]: The `Global API Key` needs to be used, not the `Origin CA Key`.
|
||||
[^6]: As explained in the [LEGO hurricane configuration](https://go-acme.github.io/lego/dns/hurricane/#credentials), each domain or wildcard (record name) needs a token. So each update of record name must be followed by an update of the `HURRICANE_TOKENS` variable, and a restart of Traefik.
|
||||
|
||||
!!! info "`delayBeforeCheck`"
|
||||
By default, the `provider` verifies the TXT record _before_ letting ACME verify.
|
||||
You can delay this operation by specifying a delay (in seconds) with `delayBeforeCheck` (value must be greater than zero).
|
||||
This option is useful when internal networks block external DNS queries.
|
||||
|
||||
#### `resolvers`
|
||||
|
||||
Use custom DNS servers to resolve the FQDN authority.
|
||||
@@ -494,6 +501,66 @@ certificatesResolvers:
|
||||
--certificatesresolvers.myresolver.acme.dnschallenge.resolvers=1.1.1.1:53,8.8.8.8:53
|
||||
```
|
||||
|
||||
#### `delayBeforeCheck`
|
||||
|
||||
By default, the `provider` verifies the TXT record _before_ letting ACME verify.
|
||||
|
||||
You can delay this operation by specifying a delay (in seconds) with `delayBeforeCheck` (value must be greater than zero).
|
||||
|
||||
This option is useful when internal networks block external DNS queries.
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
certificatesResolvers:
|
||||
myresolver:
|
||||
acme:
|
||||
# ...
|
||||
dnsChallenge:
|
||||
# ...
|
||||
delayBeforeCheck: 2s
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[certificatesResolvers.myresolver.acme]
|
||||
# ...
|
||||
[certificatesResolvers.myresolver.acme.dnsChallenge]
|
||||
# ...
|
||||
delayBeforeCheck = "2s"
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
# ...
|
||||
--certificatesresolvers.myresolver.acme.dnschallenge.delayBeforeCheck=2s
|
||||
```
|
||||
|
||||
#### `disablePropagationCheck`
|
||||
|
||||
**Not recommended**
|
||||
|
||||
Disable the TXT records propagation checks before notifying ACME that the DNS challenge is ready.
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
certificatesResolvers:
|
||||
myresolver:
|
||||
acme:
|
||||
# ...
|
||||
dnsChallenge:
|
||||
# ...
|
||||
disablePropagationCheck: true
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[certificatesResolvers.myresolver.acme]
|
||||
# ...
|
||||
[certificatesResolvers.myresolver.acme.dnsChallenge]
|
||||
# ...
|
||||
disablePropagationCheck = true
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
# ...
|
||||
--certificatesresolvers.myresolver.acme.dnschallenge.disablePropagationCheck=true
|
||||
```
|
||||
|
||||
#### Wildcard Domains
|
||||
|
||||
[ACME V2](https://community.letsencrypt.org/t/acme-v2-and-wildcard-certificate-support-is-live/55579) supports wildcard certificates.
|
||||
|
@@ -1,10 +1,10 @@
|
||||
---
|
||||
|
||||
!!! question "Using Traefik OSS in Production? Consider Adding Advanced Capabilities."
|
||||
!!! question "Using Traefik OSS in Production?"
|
||||
|
||||
Add API Gateway or API Management capabilities seamlessly to your existing Traefik deployments.
|
||||
No rip and replace. No learning curve.
|
||||
If you are using Traefik at work, consider adding enterprise-grade API gateway capabilities or commercial support for Traefik OSS.
|
||||
|
||||
- [Explore our API Gateway](https://traefik.io/traefik-hub-api-gateway/)
|
||||
- [Explore our API Management](https://traefik.io/traefik-hub/)
|
||||
- [Get 24/7/365 Commercial Support for Traefik OSS](https://info.traefik.io/request-commercial-support)
|
||||
- [Watch our API Gateway Demo Video](https://info.traefik.io/watch-traefik-api-gw-demo?cta=doc)
|
||||
- [Request 24/7/365 OSS Support](https://info.traefik.io/request-commercial-support?cta=doc)
|
||||
|
||||
Adding API Gateway capabilities to Traefik OSS is fast and seamless. There's no rip and replace and all configurations remain intact. See it in action via [this short video](https://info.traefik.io/watch-traefik-api-gw-demo?cta=doc).
|
||||
|
@@ -18,11 +18,7 @@ Traefik is natively compliant with every major cluster technology, such as Kuber
|
||||
With Traefik, there is no need to maintain and synchronize a separate configuration file: everything happens automatically, in real time (no restarts, no connection interruptions).
|
||||
With Traefik, you spend time developing and deploying new features to your system, not on configuring and maintaining its working state.
|
||||
|
||||
And if your needs change, you can add API gateway and API management capabilities seamlessly to your existing Traefik deployments. It takes less than a minute, there’s no rip-and-replace, and all your configurations are preserved. See how it works in this video:
|
||||
|
||||
<div style="text-align: center;">
|
||||
<iframe src="https://www.youtube.com/embed/zriUO5YPgFg?modestbranding=1&rel=0&controls=1" width="560" height="315" title="Upgrade Traefik Proxy to API Gateway and API Management in Seconds // Traefik Labs" frameborder="0" allowfullscreen></iframe>
|
||||
</div>
|
||||
And if your needs change, you can add API gateway and API management capabilities seamlessly to your existing Traefik deployments. It takes less than a minute, there’s no rip-and-replace, and all your configurations are preserved. See this in action in [our API gateway demo video](https://info.traefik.io/watch-traefik-api-gw-demo?cta=docs).
|
||||
|
||||
Developing Traefik, our main goal is to make it effortless to use, and we're sure you'll enjoy it.
|
||||
|
||||
@@ -30,6 +26,8 @@ Developing Traefik, our main goal is to make it effortless to use, and we're sur
|
||||
|
||||
!!! info
|
||||
|
||||
Join our user friendly and active [Community Forum](https://community.traefik.io "Link to Traefik Community Forum") to discuss, learn, and connect with the Traefik community.
|
||||
Have a question? Join our [Community Forum](https://community.traefik.io "Link to Traefik Community Forum") to discuss, learn, and connect with the Traefik community.
|
||||
|
||||
Using Traefik OSS in Production? Consider our enterprise-grade [API Gateway](https://traefik.io/traefik-hub-api-gateway/), [API Management](https://traefik.io/traefik-hub/), and [Commercial Support](https://info.traefik.io/request-commercial-support) solutions.
|
||||
Using Traefik OSS in Production? Consider our enterprise-grade [API Gateway](https://info.traefik.io/watch-traefik-api-gw-demo?cta=doc) or our [24/7/365 OSS Support](https://info.traefik.io/request-commercial-support?cta=doc).
|
||||
|
||||
Explore our API Gateway upgrade via [this short demo video](https://info.traefik.io/watch-traefik-api-gw-demo?cta=doc).
|
||||
|
@@ -21,7 +21,7 @@ The BasicAuth middleware grants access to services to authorized users only.
|
||||
# To create user:password pair, it's possible to use this command:
|
||||
# echo $(htpasswd -nB user) | sed -e s/\\$/\\$\\$/g
|
||||
#
|
||||
# Also note that dollar signs should NOT be doubled when they not evaluated (e.g. Ansible docker_container module).
|
||||
# Also note that dollar signs should NOT be doubled when they are not being evaluated (e.g. Ansible docker_container module).
|
||||
labels:
|
||||
- "traefik.http.middlewares.test-auth.basicauth.users=test:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/,test2:$$apr1$$d9hr9HBB$$4HxwgUir3HP4EsggP/QNo0"
|
||||
```
|
||||
@@ -404,3 +404,4 @@ http:
|
||||
[http.middlewares.test-auth.basicAuth]
|
||||
removeHeader = true
|
||||
```
|
||||
{!traefik-for-business-applications.md!}
|
||||
|
@@ -637,3 +637,4 @@ http:
|
||||
[http.middlewares.test-auth.forwardAuth.tls]
|
||||
insecureSkipVerify: true
|
||||
```
|
||||
{!traefik-for-business-applications.md!}
|
||||
|
@@ -99,3 +99,5 @@ The `replacement` option defines how to modify the URL to have the new target UR
|
||||
!!! warning
|
||||
|
||||
Care should be taken when defining replacement expand variables: `$1x` is equivalent to `${1x}`, not `${1}x` (see [Regexp.Expand](https://golang.org/pkg/regexp/#Regexp.Expand)), so use `${1}` syntax.
|
||||
|
||||
{!traefik-for-business-applications.md!}
|
||||
|
@@ -170,3 +170,5 @@ http:
|
||||
prefixes = ["/foobar"]
|
||||
forceSlash = false
|
||||
```
|
||||
|
||||
{!traefik-for-business-applications.md!}
|
||||
|
@@ -637,3 +637,22 @@ Increasing the `readTimeout` value could be the solution notably if you are deal
|
||||
- TCP: `Error while handling TCP connection: readfrom tcp X.X.X.X:X->X.X.X.X:X: read tcp X.X.X.X:X->X.X.X.X:X: i/o timeout`
|
||||
- HTTP: `'499 Client Closed Request' caused by: context canceled`
|
||||
- HTTP: `ReverseProxy read error during body copy: read tcp X.X.X.X:X->X.X.X.X:X: use of closed network connection`
|
||||
|
||||
## v2.11.3
|
||||
|
||||
### Connection headers
|
||||
|
||||
In `v2.11.3`, the handling of the request Connection headers directives has changed to prevent any abuse.
|
||||
Before, Traefik removed any header listed in the Connection header just before forwarding the request to the backends.
|
||||
Now, Traefik removes the headers listed in the Connection header as soon as the request is handled.
|
||||
As a consequence, middlewares do not have access to those Connection headers,
|
||||
and a new option has been introduced to specify which ones could go through the middleware chain before being removed: `<entrypoint>.forwardedHeaders.connection`.
|
||||
|
||||
Please check out the [entrypoint forwarded headers connection option configuration](../routing/entrypoints.md#forwarded-headers) documentation.
|
||||
|
||||
## v2.11.14
|
||||
|
||||
### X-Forwarded-Prefix
|
||||
|
||||
In `v2.11.14`, the `X-Forwarded-Prefix` header is now handled like the other `X-Forwarded-*` headers: Traefik removes it when it's sent from an untrusted source.
|
||||
Please refer to the Forwarded headers [documentation](../routing/entrypoints.md#forwarded-headers) for more details.
|
||||
|
@@ -47,6 +47,8 @@ accessLog:
|
||||
|
||||
### `format`
|
||||
|
||||
_Optional, Default="common"_
|
||||
|
||||
By default, logs are written using the Common Log Format (CLF).
|
||||
To write logs in JSON, use `json` in the `format` option.
|
||||
If the given format is unsupported, the default (CLF) is used instead.
|
||||
@@ -57,6 +59,20 @@ If the given format is unsupported, the default (CLF) is used instead.
|
||||
<remote_IP_address> - <client_user_name_if_available> [<timestamp>] "<request_method> <request_path> <request_protocol>" <HTTP_status> <content-length> "<request_referrer>" "<request_user_agent>" <number_of_requests_received_since_Traefik_started> "<Traefik_router_name>" "<Traefik_server_URL>" <request_duration_in_ms>ms
|
||||
```
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
accessLog:
|
||||
format: "json"
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[accessLog]
|
||||
format = "json"
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--accesslog.format=json
|
||||
```
|
||||
|
||||
### `bufferingSize`
|
||||
|
||||
To write the logs in an asynchronous fashion, specify a `bufferingSize` option.
|
||||
@@ -136,7 +152,8 @@ Each field can be set to:
|
||||
|
||||
- `keep` to keep the value
|
||||
- `drop` to drop the value
|
||||
- `redact` to replace the value with "redacted"
|
||||
|
||||
Header fields may also optionally be set to `redact` to replace the value with "REDACTED".
|
||||
|
||||
The `defaultMode` for `fields.names` is `keep`.
|
||||
|
||||
@@ -265,3 +282,5 @@ services:
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
```
|
||||
|
||||
{!traefik-for-business-applications.md!}
|
||||
|
@@ -89,3 +89,5 @@ This allows the logs to be rotated and processed by an external program, such as
|
||||
|
||||
!!! warning
|
||||
This does not work on Windows due to the lack of USR signals.
|
||||
|
||||
{!traefik-for-business-applications.md!}
|
||||
|
@@ -70,7 +70,7 @@ And then define a routing configuration on Traefik itself with the
|
||||
|
||||
### `insecure`
|
||||
|
||||
Enable the API in `insecure` mode, which means that the API will be available directly on the entryPoint named `traefik`.
|
||||
Enable the API in `insecure` mode, which means that the API will be available directly on the entryPoint named `traefik`, on path `/api`.
|
||||
|
||||
!!! info
|
||||
If the entryPoint named `traefik` is not configured, it will be automatically created on port 8080.
|
||||
@@ -136,6 +136,15 @@ api:
|
||||
|
||||
All the following endpoints must be accessed with a `GET` HTTP request.
|
||||
|
||||
!!! info "Pagination"
|
||||
|
||||
By default, up to 100 results are returned per page, and the next page can be checked using the `X-Next-Page` HTTP Header.
|
||||
To control pagination, use the `page` and `per_page` query parameters.
|
||||
|
||||
```bash
|
||||
curl https://traefik.example.com:8080/api/http/routers?page=2&per_page=20
|
||||
```
|
||||
|
||||
| Path | Description |
|
||||
|--------------------------------|---------------------------------------------------------------------------------------------|
|
||||
| `/api/http/routers` | Lists all the HTTP routers information. |
|
||||
@@ -165,3 +174,5 @@ All the following endpoints must be accessed with a `GET` HTTP request.
|
||||
| `/debug/pprof/profile` | See the [pprof Profile](https://golang.org/pkg/net/http/pprof/#Profile) Go documentation. |
|
||||
| `/debug/pprof/symbol` | See the [pprof Symbol](https://golang.org/pkg/net/http/pprof/#Symbol) Go documentation. |
|
||||
| `/debug/pprof/trace` | See the [pprof Trace](https://golang.org/pkg/net/http/pprof/#Trace) Go documentation. |
|
||||
|
||||
{!traefik-for-business-applications.md!}
|
||||
|
@@ -37,32 +37,15 @@ Start by enabling the dashboard by using the following option from [Traefik's AP
|
||||
on the [static configuration](../getting-started/configuration-overview.md#the-static-configuration):
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
api:
|
||||
# Dashboard
|
||||
#
|
||||
# Optional
|
||||
# Default: true
|
||||
#
|
||||
dashboard: true
|
||||
api: {}
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[api]
|
||||
# Dashboard
|
||||
#
|
||||
# Optional
|
||||
# Default: true
|
||||
#
|
||||
dashboard = true
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
# Dashboard
|
||||
#
|
||||
# Optional
|
||||
# Default: true
|
||||
#
|
||||
--api.dashboard=true
|
||||
--api=true
|
||||
```
|
||||
|
||||
Then define a routing configuration on Traefik itself,
|
||||
@@ -106,27 +89,47 @@ rule = "Host(`traefik.example.com`) && PathPrefix(`/api`, `/dashboard`)"
|
||||
|
||||
## Insecure Mode
|
||||
|
||||
This mode is not recommended because it does not allow the use of security features.
|
||||
When _insecure_ mode is enabled, one can access the dashboard on the `traefik` port (default: `8080`) of the Traefik instance,
|
||||
at the following URL: `http://<Traefik IP>:8080/dashboard/` (trailing slash is mandatory).
|
||||
|
||||
To enable the "insecure mode", use the following options from [Traefik's API](./api.md#insecure):
|
||||
This mode is **not** recommended because it does not allow security features.
|
||||
For example, it is not possible to add an authentication middleware with this mode.
|
||||
|
||||
It should be used for testing purpose **only**.
|
||||
|
||||
To enable the _insecure_ mode, use the following options from [Traefik's API](./api.md#insecure):
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
api:
|
||||
dashboard: true
|
||||
insecure: true
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[api]
|
||||
dashboard = true
|
||||
insecure = true
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--api.dashboard=true --api.insecure=true
|
||||
--api.insecure=true
|
||||
```
|
||||
|
||||
You can now access the dashboard on the port `8080` of the Traefik instance,
|
||||
at the following URL: `http://<Traefik IP>:8080/dashboard/` (trailing slash is mandatory).
|
||||
## Disable The Dashboard
|
||||
|
||||
By default, the dashboard is enabled when the API is enabled.
|
||||
If necessary, the dashboard can be disabled by using the following option.
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
api:
|
||||
dashboard: false
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[api]
|
||||
dashboard = false
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--api.dashboard=false
|
||||
```
|
||||
|
||||
{!traefik-for-business-applications.md!}
|
||||
|
@@ -30,3 +30,5 @@ They need not be compiled, and no complex toolchain is necessary to build them.
|
||||
The experience of implementing a Traefik plugin is comparable to writing a web browser extension.
|
||||
|
||||
To learn more about Traefik plugin creation, please refer to the [developer documentation](https://plugins.traefik.io/create).
|
||||
|
||||
{!traefik-for-business-applications.md!}
|
||||
|
@@ -525,7 +525,7 @@ providers:
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.consulcatalog.defaultRule=Host(`{{ .Name }}.{{ index .Labels \"customLabel\"}}`)
|
||||
--providers.consulcatalog.defaultRule='Host(`{{ .Name }}.{{ index .Labels "customLabel"}}`)'
|
||||
# ...
|
||||
```
|
||||
|
||||
|
@@ -466,7 +466,7 @@ providers:
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.docker.defaultRule=Host(`{{ .Name }}.{{ index .Labels \"customLabel\"}}`)
|
||||
--providers.docker.defaultRule='Host(`{{ .Name }}.{{ index .Labels "customLabel"}}`)'
|
||||
# ...
|
||||
```
|
||||
|
||||
|
@@ -259,7 +259,7 @@ providers:
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.ecs.defaultRule=Host(`{{ .Name }}.{{ index .Labels \"customLabel\"}}`)
|
||||
--providers.ecs.defaultRule='Host(`{{ .Name }}.{{ index .Labels "customLabel"}}`)'
|
||||
# ...
|
||||
```
|
||||
|
||||
|
@@ -138,7 +138,7 @@ providers:
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.marathon.defaultRule=Host(`{{ .Name }}.{{ index .Labels \"customLabel\"}}`)
|
||||
--providers.marathon.defaultRule='Host(`{{ .Name }}.{{ index .Labels "customLabel"}}`)'
|
||||
# ...
|
||||
```
|
||||
|
||||
|
@@ -374,7 +374,7 @@ providers:
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.nomad.defaultRule="Host(`{{ .Name }}.{{ index .Labels \"customLabel\"}}`)"
|
||||
--providers.nomad.defaultRule='Host(`{{ .Name }}.{{ index .Labels "customLabel"}}`)'
|
||||
# ...
|
||||
```
|
||||
|
||||
|
@@ -121,7 +121,7 @@ providers:
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
--providers.rancher.defaultRule=Host(`{{ .Name }}.{{ index .Labels \"customLabel\"}}`)
|
||||
--providers.rancher.defaultRule='Host(`{{ .Name }}.{{ index .Labels "customLabel"}}`)'
|
||||
# ...
|
||||
```
|
||||
|
||||
|
@@ -5,4 +5,23 @@ description: "Reference the environment variables for static configuration in Tr
|
||||
|
||||
# Static Configuration: Environment variables
|
||||
|
||||
!!! warning "Environment Variable Casing"
|
||||
|
||||
Traefik normalizes the environment variable key-value pairs by lowercasing them.
|
||||
This means that when you interpolate a string in an environment variable's name,
|
||||
that string will be treated as lowercase, regardless of its original casing.
|
||||
|
||||
For example, assuming you have set environment variables as follows:
|
||||
|
||||
```bash
|
||||
export TRAEFIK_ENTRYPOINTS_WEB=true
|
||||
export TRAEFIK_ENTRYPOINTS_WEB_ADDRESS=:80
|
||||
|
||||
export TRAEFIK_CERTIFICATESRESOLVERS_myResolver=true
|
||||
export TRAEFIK_CERTIFICATESRESOLVERS_myResolver_ACME_CASERVER=....
|
||||
```
|
||||
|
||||
Although the Entrypoint is named `WEB` and the Certificate Resolver is named `myResolver`,
|
||||
they have to be referenced respectively as `web`, and `myresolver` in the configuration.
|
||||
|
||||
--8<-- "content/reference/static-configuration/env-ref.md"
|
||||
|
@@ -254,8 +254,7 @@ entryPoints:
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[entryPoints.foo]
|
||||
[entryPoints.foo.allowACMEByPass]
|
||||
allowACMEByPass = true
|
||||
allowACMEByPass = true
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
@@ -472,7 +471,7 @@ Setting them has no effect for UDP entryPoints.
|
||||
If zero, no timeout exists.
|
||||
Can be provided in a format supported by [time.ParseDuration](https://golang.org/pkg/time/#ParseDuration) or as raw values (digits).
|
||||
If no units are provided, the value is parsed assuming seconds.
|
||||
We strongly suggest to adapt this value accordingly to the your needs.
|
||||
We strongly suggest adapting this value accordingly to your needs.
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
## Static configuration
|
||||
|
@@ -31,7 +31,7 @@ Our starting point is the docker-compose configuration file, to start the k3s cl
|
||||
You can start it with:
|
||||
|
||||
```bash
|
||||
docker-compose -f k3s.yml up
|
||||
docker compose -f k3s.yml up
|
||||
```
|
||||
|
||||
```yaml
|
||||
|
@@ -46,7 +46,7 @@ For the DNS challenge, you'll need:
|
||||
#- "--certificatesresolvers.myresolver.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory"
|
||||
```
|
||||
|
||||
- Run `docker-compose up -d` within the folder where you created the previous file.
|
||||
- Run `docker compose up -d` within the folder where you created the previous file.
|
||||
- Wait a bit and visit `https://your_own_domain` to confirm everything went fine.
|
||||
|
||||
!!! Note
|
||||
@@ -186,3 +186,5 @@ environment:
|
||||
- "OVH_APPLICATION_SECRET_FILE=/run/secrets/ovh_application_secret"
|
||||
- "OVH_CONSUMER_KEY_FILE=/run/secrets/ovh_consumer_key"
|
||||
```
|
||||
|
||||
{!traefik-for-business-applications.md!}
|
||||
|
@@ -32,7 +32,7 @@ For the HTTP challenge you will need:
|
||||
#- "--certificatesresolvers.myresolver.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory"
|
||||
```
|
||||
|
||||
- Run `docker-compose up -d` within the folder where you created the previous file.
|
||||
- Run `docker compose up -d` within the folder where you created the previous file.
|
||||
- Wait a bit and visit `https://your_own_domain` to confirm everything went fine.
|
||||
|
||||
!!! Note
|
||||
|
@@ -32,7 +32,7 @@ For the TLS challenge you will need:
|
||||
#- "--certificatesresolvers.myresolver.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory"
|
||||
```
|
||||
|
||||
- Run `docker-compose up -d` within the folder where you created the previous file.
|
||||
- Run `docker compose up -d` within the folder where you created the previous file.
|
||||
- Wait a bit and visit `https://your_own_domain` to confirm everything went fine.
|
||||
|
||||
!!! Note
|
||||
@@ -82,3 +82,5 @@ labels:
|
||||
# Uses the Host rule to define which certificate to issue
|
||||
- "traefik.http.routers.whoami.tls.certresolver=myresolver"
|
||||
```
|
||||
|
||||
{!traefik-for-business-applications.md!}
|
||||
|
@@ -46,7 +46,7 @@ Create a `docker-compose.yml` file with the following content:
|
||||
|
||||
Replace `whoami.localhost` by your **own domain** within the `traefik.http.routers.whoami.rule` label of the `whoami` service.
|
||||
|
||||
Now run `docker-compose up -d` within the folder where you created the previous file.
|
||||
Now run `docker compose up -d` within the folder where you created the previous file.
|
||||
This will start Docker Compose in background mode.
|
||||
|
||||
!!! info "This can take a moment"
|
||||
|
@@ -1,4 +1,4 @@
|
||||
FROM alpine:3.20
|
||||
FROM alpine:3.21
|
||||
|
||||
ENV PATH="${PATH}:/venv/bin"
|
||||
|
||||
|
@@ -27,7 +27,7 @@ theme:
|
||||
prev: 'Previous'
|
||||
next: 'Next'
|
||||
|
||||
copyright: 'Traefik Labs • Copyright © 2016-2024'
|
||||
copyright: 'Traefik Labs • Copyright © 2016-2025'
|
||||
|
||||
extra_javascript:
|
||||
- assets/js/hljs/highlight.pack.js # Download from https://highlightjs.org/download/ and enable YAML, TOML and Dockerfile
|
||||
|
208
go.mod
208
go.mod
@@ -15,9 +15,9 @@ require (
|
||||
github.com/docker/docker v27.1.1+incompatible
|
||||
github.com/docker/go-connections v0.5.0
|
||||
github.com/fatih/structs v1.1.0
|
||||
github.com/fsnotify/fsnotify v1.7.0
|
||||
github.com/fsnotify/fsnotify v1.8.0
|
||||
github.com/gambol99/go-marathon v0.0.0-20180614232016-99a156b96fb2 // No tag on the repo.
|
||||
github.com/go-acme/lego/v4 v4.18.0
|
||||
github.com/go-acme/lego/v4 v4.22.2
|
||||
github.com/go-kit/kit v0.13.0
|
||||
github.com/go-kit/log v0.2.1
|
||||
github.com/golang/protobuf v1.5.4
|
||||
@@ -27,19 +27,19 @@ require (
|
||||
github.com/hashicorp/consul/api v1.26.1
|
||||
github.com/hashicorp/go-hclog v1.6.3
|
||||
github.com/hashicorp/go-multierror v1.1.1
|
||||
github.com/hashicorp/go-version v1.6.0
|
||||
github.com/hashicorp/go-version v1.7.0
|
||||
github.com/hashicorp/nomad/api v0.0.0-20231213195942-64e3dca9274b // No tag on the repo.
|
||||
github.com/influxdata/influxdb-client-go/v2 v2.7.0
|
||||
github.com/influxdata/influxdb1-client v0.0.0-20200827194710-b269163b24ab // No tag on the repo.
|
||||
github.com/instana/go-sensor v1.38.3
|
||||
github.com/klauspost/compress v1.17.9
|
||||
github.com/klauspost/compress v1.17.11-0.20241004063537-dbd6c381492a // Required to have the content-type fix: https://github.com/klauspost/compress/pull/1013
|
||||
github.com/kvtools/consul v1.0.2
|
||||
github.com/kvtools/etcdv3 v1.0.2
|
||||
github.com/kvtools/redis v1.1.0
|
||||
github.com/kvtools/valkeyrie v1.0.0
|
||||
github.com/kvtools/zookeeper v1.0.2
|
||||
github.com/mailgun/ttlmap v0.0.0-20170619185759-c1c17f74874f // No tag on the repo.
|
||||
github.com/miekg/dns v1.1.59
|
||||
github.com/miekg/dns v1.1.62
|
||||
github.com/mitchellh/copystructure v1.2.0
|
||||
github.com/mitchellh/hashstructure v1.0.0
|
||||
github.com/mitchellh/mapstructure v1.5.0
|
||||
@@ -51,13 +51,13 @@ require (
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // No tag on the repo.
|
||||
github.com/prometheus/client_golang v1.19.1
|
||||
github.com/prometheus/client_model v0.5.0
|
||||
github.com/quic-go/quic-go v0.45.1
|
||||
github.com/quic-go/quic-go v0.48.2
|
||||
github.com/rancher/go-rancher-metadata v0.0.0-20200311180630-7f4c936a06ac // No tag on the repo.
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/stretchr/testify v1.9.0
|
||||
github.com/stretchr/testify v1.10.0
|
||||
github.com/stvp/go-udp-testing v0.0.0-20191102171040-06b61409b154 // No tag on the repo.
|
||||
github.com/testcontainers/testcontainers-go v0.32.0
|
||||
github.com/traefik/paerser v0.2.1
|
||||
github.com/traefik/paerser v0.2.2
|
||||
github.com/traefik/yaegi v0.16.1
|
||||
github.com/uber/jaeger-client-go v2.30.0+incompatible
|
||||
github.com/uber/jaeger-lib v2.4.1+incompatible
|
||||
@@ -67,13 +67,12 @@ require (
|
||||
github.com/vulcand/predicate v1.2.0
|
||||
go.elastic.co/apm/module/apmot/v2 v2.4.8
|
||||
go.elastic.co/apm/v2 v2.4.8
|
||||
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // No tag on the repo.
|
||||
golang.org/x/mod v0.18.0
|
||||
golang.org/x/net v0.26.0
|
||||
golang.org/x/text v0.17.0
|
||||
golang.org/x/time v0.5.0
|
||||
golang.org/x/tools v0.22.0
|
||||
google.golang.org/grpc v1.63.1
|
||||
golang.org/x/mod v0.22.0
|
||||
golang.org/x/net v0.33.0
|
||||
golang.org/x/text v0.21.0
|
||||
golang.org/x/time v0.8.0
|
||||
golang.org/x/tools v0.28.0
|
||||
google.golang.org/grpc v1.67.1
|
||||
gopkg.in/DataDog/dd-trace-go.v1 v1.56.1
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
k8s.io/api v0.26.3
|
||||
@@ -86,15 +85,17 @@ require (
|
||||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go/compute/metadata v0.3.0 // indirect
|
||||
cloud.google.com/go/auth v0.13.0 // indirect
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.6 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.6.0 // indirect
|
||||
dario.cat/mergo v1.0.0 // indirect
|
||||
github.com/AdamSLevy/jsonrpc2/v14 v14.1.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.12.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.6.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.9.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.16.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.2.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.2.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.3.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resourcegraph/armresourcegraph v0.9.0 // indirect
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
|
||||
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
|
||||
@@ -122,35 +123,34 @@ require (
|
||||
github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87 // indirect
|
||||
github.com/VividCortex/gohistogram v1.0.0 // indirect
|
||||
github.com/akamai/AkamaiOPEN-edgegrid-golang v1.2.2 // indirect
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.62.712 // indirect
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.63.72 // indirect
|
||||
github.com/armon/go-metrics v0.4.1 // indirect
|
||||
github.com/armon/go-radix v1.0.1-0.20221118154546-54df44f2176c // indirect
|
||||
github.com/aws/aws-sdk-go-v2 v1.27.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/config v1.27.18 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.18 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.9 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.9 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.11 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/lightsail v1.38.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/route53 v1.40.10 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.20.11 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.24.5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.28.12 // indirect
|
||||
github.com/aws/smithy-go v1.20.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2 v1.32.7 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/config v1.28.7 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.48 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.22 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.26 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.26 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.7 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/lightsail v1.42.8 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/route53 v1.46.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.24.8 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.7 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.33.3 // indirect
|
||||
github.com/aws/smithy-go v1.22.1 // indirect
|
||||
github.com/benbjohnson/clock v1.3.0 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/civo/civogo v0.3.11 // indirect
|
||||
github.com/cloudflare/cloudflare-go v0.97.0 // indirect
|
||||
github.com/cloudflare/cloudflare-go v0.112.0 // indirect
|
||||
github.com/containerd/containerd v1.7.20 // indirect
|
||||
github.com/containerd/log v0.1.0 // indirect
|
||||
github.com/containerd/platforms v0.2.1 // indirect
|
||||
github.com/coreos/go-semver v0.3.0 // indirect
|
||||
github.com/cpu/goacmedns v0.1.1 // indirect
|
||||
github.com/cpuguy83/dockercfg v0.3.1 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/deepmap/oapi-codegen v1.9.1 // indirect
|
||||
@@ -166,40 +166,42 @@ require (
|
||||
github.com/elastic/go-windows v1.0.0 // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
|
||||
github.com/evanphx/json-patch v4.12.0+incompatible // indirect
|
||||
github.com/exoscale/egoscale v0.102.3 // indirect
|
||||
github.com/exoscale/egoscale/v3 v3.1.7 // indirect
|
||||
github.com/fatih/color v1.16.0 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
|
||||
github.com/ghodss/yaml v1.0.0 // indirect
|
||||
github.com/go-errors/errors v1.0.1 // indirect
|
||||
github.com/go-jose/go-jose/v4 v4.0.2 // indirect
|
||||
github.com/go-jose/go-jose/v4 v4.0.4 // indirect
|
||||
github.com/go-logfmt/logfmt v0.5.1 // indirect
|
||||
github.com/go-logr/logr v1.4.1 // indirect
|
||||
github.com/go-logr/logr v1.4.2 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.19.5 // indirect
|
||||
github.com/go-openapi/jsonreference v0.20.0 // indirect
|
||||
github.com/go-openapi/swag v0.19.14 // indirect
|
||||
github.com/go-resty/resty/v2 v2.11.0 // indirect
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.0.0 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.16.0 // indirect
|
||||
github.com/go-resty/resty/v2 v2.16.2 // indirect
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
|
||||
github.com/go-zookeeper/zk v1.0.3 // indirect
|
||||
github.com/goccy/go-json v0.10.3 // indirect
|
||||
github.com/gofrs/flock v0.12.0 // indirect
|
||||
github.com/gofrs/uuid v4.4.0+incompatible // indirect
|
||||
github.com/goccy/go-json v0.10.4 // indirect
|
||||
github.com/gofrs/flock v0.12.1 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.5.1 // indirect
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/google/gnostic v0.5.7-v3refs // indirect
|
||||
github.com/google/go-cmp v0.6.0 // indirect
|
||||
github.com/google/go-querystring v1.1.0 // indirect
|
||||
github.com/google/gofuzz v1.2.0 // indirect
|
||||
github.com/google/pprof v0.0.0-20240402174815-29b9bb013b0f // indirect
|
||||
github.com/google/s2a-go v0.1.7 // indirect
|
||||
github.com/google/pprof v0.0.0-20240910150728-a0b0bb1d4134 // indirect
|
||||
github.com/google/s2a-go v0.1.8 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.12.3 // indirect
|
||||
github.com/gophercloud/gophercloud v1.12.0 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.14.0 // indirect
|
||||
github.com/gophercloud/gophercloud v1.14.1 // indirect
|
||||
github.com/gophercloud/utils v0.0.0-20231010081019-80377eca5d56 // indirect
|
||||
github.com/gravitational/trace v1.1.16-0.20220114165159-14a9a7dd6aaf // indirect
|
||||
github.com/hashicorp/cronexpr v1.1.2 // indirect
|
||||
@@ -210,8 +212,10 @@ require (
|
||||
github.com/hashicorp/go-rootcerts v1.0.2 // indirect
|
||||
github.com/hashicorp/go-uuid v1.0.3 // indirect
|
||||
github.com/hashicorp/golang-lru v1.0.2 // indirect
|
||||
github.com/hashicorp/hcl v1.0.1-vault-5 // indirect
|
||||
github.com/hashicorp/serf v0.10.1 // indirect
|
||||
github.com/huandu/xstrings v1.5.0 // indirect
|
||||
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.128 // indirect
|
||||
github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df // indirect
|
||||
github.com/imdario/mergo v0.3.16 // indirect
|
||||
github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839 // indirect
|
||||
@@ -226,7 +230,8 @@ require (
|
||||
github.com/kylelemons/godebug v1.1.0 // indirect
|
||||
github.com/labbsr0x/bindman-dns-webhook v1.0.2 // indirect
|
||||
github.com/labbsr0x/goh v1.0.1 // indirect
|
||||
github.com/linode/linodego v1.28.0 // indirect
|
||||
github.com/leodido/go-urn v1.2.4 // indirect
|
||||
github.com/linode/linodego v1.44.0 // indirect
|
||||
github.com/liquidweb/liquidweb-cli v0.6.9 // indirect
|
||||
github.com/liquidweb/liquidweb-go v1.6.4 // indirect
|
||||
github.com/looplab/fsm v0.1.0 // indirect
|
||||
@@ -253,23 +258,26 @@ require (
|
||||
github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04 // indirect
|
||||
github.com/nrdcg/auroradns v1.1.0 // indirect
|
||||
github.com/nrdcg/bunny-go v0.0.0-20240207213615-dde5bf4577a3 // indirect
|
||||
github.com/nrdcg/desec v0.8.0 // indirect
|
||||
github.com/nrdcg/desec v0.10.0 // indirect
|
||||
github.com/nrdcg/dnspod-go v0.4.0 // indirect
|
||||
github.com/nrdcg/freemyip v0.2.0 // indirect
|
||||
github.com/nrdcg/freemyip v0.3.0 // indirect
|
||||
github.com/nrdcg/goacmedns v0.2.0 // indirect
|
||||
github.com/nrdcg/goinwx v0.10.0 // indirect
|
||||
github.com/nrdcg/mailinabox v0.2.0 // indirect
|
||||
github.com/nrdcg/namesilo v0.2.1 // indirect
|
||||
github.com/nrdcg/nodion v0.1.0 // indirect
|
||||
github.com/nrdcg/porkbun v0.3.0 // indirect
|
||||
github.com/nrdcg/porkbun v0.4.0 // indirect
|
||||
github.com/nzdjb/go-metaname v1.0.0 // indirect
|
||||
github.com/onsi/ginkgo v1.16.5 // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.17.1 // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.20.2 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.1.0 // indirect
|
||||
github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492 // indirect
|
||||
github.com/oracle/oci-go-sdk/v65 v65.63.1 // indirect
|
||||
github.com/oracle/oci-go-sdk/v65 v65.81.1 // indirect
|
||||
github.com/outcaste-io/ristretto v0.2.3 // indirect
|
||||
github.com/ovh/go-ovh v1.5.1 // indirect
|
||||
github.com/ovh/go-ovh v1.6.0 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
|
||||
github.com/peterhellberg/link v1.2.0 // indirect
|
||||
github.com/philhofer/fwd v1.1.2 // indirect
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
@@ -277,71 +285,81 @@ require (
|
||||
github.com/pquerna/otp v1.4.0 // indirect
|
||||
github.com/prometheus/common v0.48.0 // indirect
|
||||
github.com/prometheus/procfs v0.15.1 // indirect
|
||||
github.com/quic-go/qpack v0.4.0 // indirect
|
||||
github.com/redis/go-redis/v9 v9.2.1 // indirect
|
||||
github.com/quic-go/qpack v0.5.1 // indirect
|
||||
github.com/redis/go-redis/v9 v9.6.1 // indirect
|
||||
github.com/regfish/regfish-dnsapi-go v0.1.1 // indirect
|
||||
github.com/sacloud/api-client-go v0.2.10 // indirect
|
||||
github.com/sacloud/go-http v0.1.8 // indirect
|
||||
github.com/sacloud/iaas-api-go v1.12.0 // indirect
|
||||
github.com/sacloud/iaas-api-go v1.14.0 // indirect
|
||||
github.com/sacloud/packages-go v0.0.10 // indirect
|
||||
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.27 // indirect
|
||||
github.com/sagikazarmark/locafero v0.4.0 // indirect
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
||||
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.30 // indirect
|
||||
github.com/secure-systems-lab/go-securesystemslib v0.7.0 // indirect
|
||||
github.com/segmentio/fasthash v1.0.3 // indirect
|
||||
github.com/selectel/domains-go v1.1.0 // indirect
|
||||
github.com/selectel/go-selvpcclient/v3 v3.1.1 // indirect
|
||||
github.com/selectel/go-selvpcclient/v3 v3.2.1 // indirect
|
||||
github.com/shirou/gopsutil/v3 v3.23.12 // indirect
|
||||
github.com/shoenig/go-m1cpu v0.1.6 // indirect
|
||||
github.com/shopspring/decimal v1.4.0 // indirect
|
||||
github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9 // indirect
|
||||
github.com/softlayer/softlayer-go v1.1.5 // indirect
|
||||
github.com/softlayer/softlayer-go v1.1.7 // indirect
|
||||
github.com/softlayer/xmlrpc v0.0.0-20200409220501-5f089df7cb7e // indirect
|
||||
github.com/sony/gobreaker v0.5.0 // indirect
|
||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||
github.com/spf13/afero v1.11.0 // indirect
|
||||
github.com/spf13/cast v1.7.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/spf13/viper v1.18.2 // indirect
|
||||
github.com/stretchr/objx v0.5.2 // indirect
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.898 // indirect
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.898 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1065 // indirect
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1065 // indirect
|
||||
github.com/tinylib/msgp v1.1.8 // indirect
|
||||
github.com/tjfoc/gmsm v1.4.1 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.12 // indirect
|
||||
github.com/tklauser/numcpus v0.6.1 // indirect
|
||||
github.com/transip/gotransip/v6 v6.23.0 // indirect
|
||||
github.com/ultradns/ultradns-go-sdk v1.6.1-20231103022937-8589b6a // indirect
|
||||
github.com/transip/gotransip/v6 v6.26.0 // indirect
|
||||
github.com/ultradns/ultradns-go-sdk v1.8.0-20241010134910-243eeec // indirect
|
||||
github.com/vinyldns/go-vinyldns v0.9.16 // indirect
|
||||
github.com/vultr/govultr/v3 v3.9.0 // indirect
|
||||
github.com/yandex-cloud/go-genproto v0.0.0-20240318083951-4fe6125f286e // indirect
|
||||
github.com/yandex-cloud/go-sdk v0.0.0-20240318084659-dfa50323a0b4 // indirect
|
||||
github.com/volcengine/volc-sdk-golang v1.0.189 // indirect
|
||||
github.com/vultr/govultr/v3 v3.9.1 // indirect
|
||||
github.com/yandex-cloud/go-genproto v0.0.0-20241220122821-aeb3b05efd1c // indirect
|
||||
github.com/yandex-cloud/go-sdk v0.0.0-20241220131134-2393e243c134 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.3 // indirect
|
||||
go.elastic.co/apm/module/apmhttp/v2 v2.4.8 // indirect
|
||||
go.elastic.co/fastjson v1.1.0 // indirect
|
||||
go.etcd.io/etcd/api/v3 v3.5.6 // indirect
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.6 // indirect
|
||||
go.etcd.io/etcd/client/v3 v3.5.6 // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect
|
||||
go.opentelemetry.io/otel v1.24.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.24.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.24.0 // indirect
|
||||
go.etcd.io/etcd/api/v3 v3.5.10 // indirect
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.10 // indirect
|
||||
go.etcd.io/etcd/client/v3 v3.5.10 // indirect
|
||||
go.mongodb.org/mongo-driver v1.12.1 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 // indirect
|
||||
go.opentelemetry.io/otel v1.29.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.29.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.29.0 // indirect
|
||||
go.uber.org/atomic v1.11.0 // indirect
|
||||
go.uber.org/goleak v1.3.0 // indirect
|
||||
go.uber.org/mock v0.4.0 // indirect
|
||||
go.uber.org/multierr v1.8.0 // indirect
|
||||
go.uber.org/multierr v1.9.0 // indirect
|
||||
go.uber.org/ratelimit v0.3.0 // indirect
|
||||
go.uber.org/zap v1.21.0 // indirect
|
||||
go4.org/intern v0.0.0-20230525184215-6c62f75575cb // indirect
|
||||
go4.org/unsafe/assume-no-moving-gc v0.0.0-20230525183740-e7c30c78aeb2 // indirect
|
||||
golang.org/x/crypto v0.26.0 // indirect
|
||||
golang.org/x/oauth2 v0.21.0 // indirect
|
||||
golang.org/x/sync v0.8.0 // indirect
|
||||
golang.org/x/sys v0.23.0 // indirect
|
||||
golang.org/x/term v0.23.0 // indirect
|
||||
golang.org/x/crypto v0.31.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20241210194714-1829a127f884 // indirect
|
||||
golang.org/x/oauth2 v0.24.0 // indirect
|
||||
golang.org/x/sync v0.10.0 // indirect
|
||||
golang.org/x/sys v0.28.0 // indirect
|
||||
golang.org/x/term v0.27.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
|
||||
google.golang.org/api v0.172.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240311132316-a219d84964c2 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 // indirect
|
||||
google.golang.org/protobuf v1.33.0 // indirect
|
||||
google.golang.org/api v0.214.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20241021214115-324edc3d5d38 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20241118233622-e639e219e697 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576 // indirect
|
||||
google.golang.org/protobuf v1.35.2 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/ns1/ns1-go.v2 v2.9.1 // indirect
|
||||
gopkg.in/ns1/ns1-go.v2 v2.13.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
howett.net/plist v0.0.0-20181124034731-591f970eefbb // indirect
|
||||
inet.af/netaddr v0.0.0-20230525184311-b8eac61e914a // indirect
|
||||
|
21
init/init.go
Normal file
21
init/init.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package init
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// This makes use of the GODEBUG flag `http2xconnect` to deactivate the connect setting for HTTP2 by default.
|
||||
// This type of upgrade is yet incompatible with `net/http` http1 reverse proxy.
|
||||
// Please see https://github.com/golang/go/issues/71128#issuecomment-2574193636.
|
||||
func init() {
|
||||
goDebug := os.Getenv("GODEBUG")
|
||||
if strings.Contains(goDebug, "http2xconnect") {
|
||||
return
|
||||
}
|
||||
|
||||
if len(goDebug) > 0 {
|
||||
goDebug += ","
|
||||
}
|
||||
os.Setenv("GODEBUG", goDebug+"http2xconnect=0")
|
||||
}
|
@@ -9,24 +9,24 @@
|
||||
# Use certificate in net/internal/testcert.go
|
||||
rootCAs = [ """
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDOTCCAiGgAwIBAgIQSRJrEpBGFc7tNb1fb5pKFzANBgkqhkiG9w0BAQsFADAS
|
||||
MIIDSDCCAjCgAwIBAgIQEP/md970HysdBTpuzDOf0DANBgkqhkiG9w0BAQsFADAS
|
||||
MRAwDgYDVQQKEwdBY21lIENvMCAXDTcwMDEwMTAwMDAwMFoYDzIwODQwMTI5MTYw
|
||||
MDAwWjASMRAwDgYDVQQKEwdBY21lIENvMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
|
||||
MIIBCgKCAQEA6Gba5tHV1dAKouAaXO3/ebDUU4rvwCUg/CNaJ2PT5xLD4N1Vcb8r
|
||||
bFSW2HXKq+MPfVdwIKR/1DczEoAGf/JWQTW7EgzlXrCd3rlajEX2D73faWJekD0U
|
||||
aUgz5vtrTXZ90BQL7WvRICd7FlEZ6FPOcPlumiyNmzUqtwGhO+9ad1W5BqJaRI6P
|
||||
YfouNkwR6Na4TzSj5BrqUfP0FwDizKSJ0XXmh8g8G9mtwxOSN3Ru1QFc61Xyeluk
|
||||
POGKBV/q6RBNklTNe0gI8usUMlYyoC7ytppNMW7X2vodAelSu25jgx2anj9fDVZu
|
||||
h7AXF5+4nJS4AAt0n1lNY7nGSsdZas8PbQIDAQABo4GIMIGFMA4GA1UdDwEB/wQE
|
||||
MIIBCgKCAQEAxcl69ROJdxjN+MJZnbFrYxyQooADCsJ6VDkuMyNQIix/Hk15Nk/u
|
||||
FyBX1Me++aEpGmY3RIY4fUvELqT/srvAHsTXwVVSttMcY8pcAFmXSqo3x4MuUTG/
|
||||
jCX3Vftj0r3EM5M8ImY1rzA/jqTTLJg00rD+DmuDABcqQvoXw/RV8w1yTRi5BPoH
|
||||
DFD/AWTt/YgMvk1l2Yq/xI8VbMUIpjBoGXxWsSevQ5i2s1mk9/yZzu0Ysp1tTlzD
|
||||
qOPa4ysFjBitdXiwfxjxtv5nXqOCP5rheKO0sWLk0fetMp1OV5JSJMAJw6c2ZMkl
|
||||
U2WMqAEpRjdE/vHfIuNg+yGaRRqI07NZRQIDAQABo4GXMIGUMA4GA1UdDwEB/wQE
|
||||
AwICpDATBgNVHSUEDDAKBggrBgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud
|
||||
DgQWBBStsdjh3/JCXXYlQryOrL4Sh7BW5TAuBgNVHREEJzAlggtleGFtcGxlLmNv
|
||||
bYcEfwAAAYcQAAAAAAAAAAAAAAAAAAAAATANBgkqhkiG9w0BAQsFAAOCAQEAxWGI
|
||||
5NhpF3nwwy/4yB4i/CwwSpLrWUa70NyhvprUBC50PxiXav1TeDzwzLx/o5HyNwsv
|
||||
cxv3HdkLW59i/0SlJSrNnWdfZ19oTcS+6PtLoVyISgtyN6DpkKpdG1cOkW3Cy2P2
|
||||
+tK/tKHRP1Y/Ra0RiDpOAmqn0gCOFGz8+lqDIor/T7MTpibL3IxqWfPrvfVRHL3B
|
||||
grw/ZQTTIVjjh4JBSW3WyWgNo/ikC1lrVxzl4iPUGptxT36Cr7Zk2Bsg0XqwbOvK
|
||||
5d+NTDREkSnUbie4GeutujmX3Dsx88UiV6UY/4lHJa6I5leHUNOHahRbpbWeOfs/
|
||||
WkBKOclmOV2xlTVuPw==
|
||||
DgQWBBQR5QIzmacmw78ZI1C4MXw7Q0wJ1jA9BgNVHREENjA0ggtleGFtcGxlLmNv
|
||||
bYINKi5leGFtcGxlLmNvbYcEfwAAAYcQAAAAAAAAAAAAAAAAAAAAATANBgkqhkiG
|
||||
9w0BAQsFAAOCAQEACrRNgiioUDzxQftd0fwOa6iRRcPampZRDtuaF68yNHoNWbOu
|
||||
LUwc05eOWxRq3iABGSk2xg+FXM3DDeW4HhAhCFptq7jbVZ+4Jj6HeJG9mYRatAxR
|
||||
Y/dEpa0D0EHhDxxVg6UzKOXB355n0IetGE/aWvyTV9SiDs6QsaC57Q9qq1/mitx5
|
||||
2GFBoapol9L5FxCc77bztzK8CpLujkBi25Vk6GAFbl27opLfpyxkM+rX/T6MXCPO
|
||||
6/YBacNZ7ff1/57Etg4i5mNA6ubCpuc4Gi9oYqCNNohftr2lkJr7REdDR6OW0lsL
|
||||
rF7r4gUnKeC7mYIH1zypY7laskopiLFAfe96Kg==
|
||||
-----END CERTIFICATE-----
|
||||
"""]
|
||||
|
||||
|
@@ -1,20 +1,20 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDOTCCAiGgAwIBAgIQSRJrEpBGFc7tNb1fb5pKFzANBgkqhkiG9w0BAQsFADAS
|
||||
MIIDSDCCAjCgAwIBAgIQEP/md970HysdBTpuzDOf0DANBgkqhkiG9w0BAQsFADAS
|
||||
MRAwDgYDVQQKEwdBY21lIENvMCAXDTcwMDEwMTAwMDAwMFoYDzIwODQwMTI5MTYw
|
||||
MDAwWjASMRAwDgYDVQQKEwdBY21lIENvMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
|
||||
MIIBCgKCAQEA6Gba5tHV1dAKouAaXO3/ebDUU4rvwCUg/CNaJ2PT5xLD4N1Vcb8r
|
||||
bFSW2HXKq+MPfVdwIKR/1DczEoAGf/JWQTW7EgzlXrCd3rlajEX2D73faWJekD0U
|
||||
aUgz5vtrTXZ90BQL7WvRICd7FlEZ6FPOcPlumiyNmzUqtwGhO+9ad1W5BqJaRI6P
|
||||
YfouNkwR6Na4TzSj5BrqUfP0FwDizKSJ0XXmh8g8G9mtwxOSN3Ru1QFc61Xyeluk
|
||||
POGKBV/q6RBNklTNe0gI8usUMlYyoC7ytppNMW7X2vodAelSu25jgx2anj9fDVZu
|
||||
h7AXF5+4nJS4AAt0n1lNY7nGSsdZas8PbQIDAQABo4GIMIGFMA4GA1UdDwEB/wQE
|
||||
MIIBCgKCAQEAxcl69ROJdxjN+MJZnbFrYxyQooADCsJ6VDkuMyNQIix/Hk15Nk/u
|
||||
FyBX1Me++aEpGmY3RIY4fUvELqT/srvAHsTXwVVSttMcY8pcAFmXSqo3x4MuUTG/
|
||||
jCX3Vftj0r3EM5M8ImY1rzA/jqTTLJg00rD+DmuDABcqQvoXw/RV8w1yTRi5BPoH
|
||||
DFD/AWTt/YgMvk1l2Yq/xI8VbMUIpjBoGXxWsSevQ5i2s1mk9/yZzu0Ysp1tTlzD
|
||||
qOPa4ysFjBitdXiwfxjxtv5nXqOCP5rheKO0sWLk0fetMp1OV5JSJMAJw6c2ZMkl
|
||||
U2WMqAEpRjdE/vHfIuNg+yGaRRqI07NZRQIDAQABo4GXMIGUMA4GA1UdDwEB/wQE
|
||||
AwICpDATBgNVHSUEDDAKBggrBgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud
|
||||
DgQWBBStsdjh3/JCXXYlQryOrL4Sh7BW5TAuBgNVHREEJzAlggtleGFtcGxlLmNv
|
||||
bYcEfwAAAYcQAAAAAAAAAAAAAAAAAAAAATANBgkqhkiG9w0BAQsFAAOCAQEAxWGI
|
||||
5NhpF3nwwy/4yB4i/CwwSpLrWUa70NyhvprUBC50PxiXav1TeDzwzLx/o5HyNwsv
|
||||
cxv3HdkLW59i/0SlJSrNnWdfZ19oTcS+6PtLoVyISgtyN6DpkKpdG1cOkW3Cy2P2
|
||||
+tK/tKHRP1Y/Ra0RiDpOAmqn0gCOFGz8+lqDIor/T7MTpibL3IxqWfPrvfVRHL3B
|
||||
grw/ZQTTIVjjh4JBSW3WyWgNo/ikC1lrVxzl4iPUGptxT36Cr7Zk2Bsg0XqwbOvK
|
||||
5d+NTDREkSnUbie4GeutujmX3Dsx88UiV6UY/4lHJa6I5leHUNOHahRbpbWeOfs/
|
||||
WkBKOclmOV2xlTVuPw==
|
||||
DgQWBBQR5QIzmacmw78ZI1C4MXw7Q0wJ1jA9BgNVHREENjA0ggtleGFtcGxlLmNv
|
||||
bYINKi5leGFtcGxlLmNvbYcEfwAAAYcQAAAAAAAAAAAAAAAAAAAAATANBgkqhkiG
|
||||
9w0BAQsFAAOCAQEACrRNgiioUDzxQftd0fwOa6iRRcPampZRDtuaF68yNHoNWbOu
|
||||
LUwc05eOWxRq3iABGSk2xg+FXM3DDeW4HhAhCFptq7jbVZ+4Jj6HeJG9mYRatAxR
|
||||
Y/dEpa0D0EHhDxxVg6UzKOXB355n0IetGE/aWvyTV9SiDs6QsaC57Q9qq1/mitx5
|
||||
2GFBoapol9L5FxCc77bztzK8CpLujkBi25Vk6GAFbl27opLfpyxkM+rX/T6MXCPO
|
||||
6/YBacNZ7ff1/57Etg4i5mNA6ubCpuc4Gi9oYqCNNohftr2lkJr7REdDR6OW0lsL
|
||||
rF7r4gUnKeC7mYIH1zypY7laskopiLFAfe96Kg==
|
||||
-----END CERTIFICATE-----
|
||||
|
@@ -93,7 +93,7 @@ func getHelloClientGRPC() (helloworld.GreeterClient, func() error, error) {
|
||||
roots := x509.NewCertPool()
|
||||
roots.AppendCertsFromPEM(LocalhostCert)
|
||||
credsClient := credentials.NewClientTLSFromCert(roots, "")
|
||||
conn, err := grpc.Dial("127.0.0.1:4443", grpc.WithTransportCredentials(credsClient))
|
||||
conn, err := grpc.NewClient("127.0.0.1:4443", grpc.WithTransportCredentials(credsClient))
|
||||
if err != nil {
|
||||
return nil, func() error { return nil }, err
|
||||
}
|
||||
@@ -101,7 +101,7 @@ func getHelloClientGRPC() (helloworld.GreeterClient, func() error, error) {
|
||||
}
|
||||
|
||||
func getHelloClientGRPCh2c() (helloworld.GreeterClient, func() error, error) {
|
||||
conn, err := grpc.Dial("127.0.0.1:8081", grpc.WithTransportCredentials(insecure.NewCredentials()))
|
||||
conn, err := grpc.NewClient("127.0.0.1:8081", grpc.WithTransportCredentials(insecure.NewCredentials()))
|
||||
if err != nil {
|
||||
return nil, func() error { return nil }, err
|
||||
}
|
||||
|
@@ -41,7 +41,7 @@ func (s *HTTPSuite) TestSimpleConfiguration() {
|
||||
Services: map[string]*dynamic.Service{
|
||||
"serviceHTTP": {
|
||||
LoadBalancer: &dynamic.ServersLoadBalancer{
|
||||
PassHostHeader: boolRef(true),
|
||||
PassHostHeader: pointer(true),
|
||||
Servers: []dynamic.Server{
|
||||
{
|
||||
URL: "http://bacon:80",
|
||||
@@ -81,6 +81,4 @@ func startTestServerWithResponse(response []byte) (ts *httptest.Server) {
|
||||
return ts
|
||||
}
|
||||
|
||||
func boolRef(b bool) *bool {
|
||||
return &b
|
||||
}
|
||||
func pointer[T any](v T) *T { return &v }
|
||||
|
@@ -16,6 +16,7 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"github.com/traefik/traefik/v2/integration/try"
|
||||
"golang.org/x/net/http2"
|
||||
"golang.org/x/net/websocket"
|
||||
)
|
||||
|
||||
@@ -451,6 +452,44 @@ func (s *WebsocketSuite) TestSSLhttp2() {
|
||||
assert.Equal(s.T(), "OK", string(msg))
|
||||
}
|
||||
|
||||
func (s *WebsocketSuite) TestSettingEnableConnectProtocol() {
|
||||
file := s.adaptFile("fixtures/websocket/config_https.toml", struct {
|
||||
WebsocketServer string
|
||||
}{
|
||||
WebsocketServer: "http://127.0.0.1",
|
||||
})
|
||||
|
||||
s.traefikCmd(withConfigFile(file), "--log.level=DEBUG", "--accesslog")
|
||||
|
||||
// Wait for traefik.
|
||||
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 10*time.Second, try.BodyContains("127.0.0.1"))
|
||||
require.NoError(s.T(), err)
|
||||
|
||||
// Add client self-signed cert.
|
||||
roots := x509.NewCertPool()
|
||||
certContent, err := os.ReadFile("./resources/tls/local.cert")
|
||||
require.NoError(s.T(), err)
|
||||
|
||||
roots.AppendCertsFromPEM(certContent)
|
||||
|
||||
// Open a connection to inspect SettingsFrame.
|
||||
conn, err := tls.Dial("tcp", "127.0.0.1:8000", &tls.Config{
|
||||
RootCAs: roots,
|
||||
NextProtos: []string{"h2"},
|
||||
})
|
||||
require.NoError(s.T(), err)
|
||||
|
||||
framer := http2.NewFramer(nil, conn)
|
||||
frame, err := framer.ReadFrame()
|
||||
require.NoError(s.T(), err)
|
||||
|
||||
fr, ok := frame.(*http2.SettingsFrame)
|
||||
require.True(s.T(), ok)
|
||||
|
||||
_, ok = fr.Value(http2.SettingEnableConnectProtocol)
|
||||
assert.False(s.T(), ok)
|
||||
}
|
||||
|
||||
func (s *WebsocketSuite) TestHeaderAreForwarded() {
|
||||
upgrader := gorillawebsocket.Upgrader{} // use default options
|
||||
|
||||
|
@@ -25,14 +25,22 @@ func main() {
|
||||
ParseFiles("./.goreleaser.yml.tmpl"),
|
||||
)
|
||||
|
||||
outputPath := path.Join(os.TempDir(), fmt.Sprintf(".goreleaser_%s.yml", goos))
|
||||
goarch := ""
|
||||
outputFileName := fmt.Sprintf(".goreleaser_%s.yml", goos)
|
||||
if strings.Contains(goos, "-") {
|
||||
split := strings.Split(goos, "-")
|
||||
goos = split[0]
|
||||
goarch = split[1]
|
||||
}
|
||||
|
||||
outputPath := path.Join(os.TempDir(), outputFileName)
|
||||
|
||||
output, err := os.Create(outputPath)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
err = tmpl.Execute(output, map[string]string{"GOOS": goos})
|
||||
err = tmpl.Execute(output, map[string]string{"GOOS": goos, "GOARCH": goarch})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
@@ -3,7 +3,7 @@ package dashboard
|
||||
import (
|
||||
"io/fs"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/traefik/traefik/v2/webui"
|
||||
@@ -25,7 +25,8 @@ func Append(router *mux.Router, customAssets fs.FS) {
|
||||
router.Methods(http.MethodGet).
|
||||
Path("/").
|
||||
HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
|
||||
http.Redirect(resp, req, safePrefix(req)+"/dashboard/", http.StatusFound)
|
||||
prefix := strings.TrimSuffix(req.Header.Get("X-Forwarded-Prefix"), "/")
|
||||
http.Redirect(resp, req, prefix+"/dashboard/", http.StatusFound)
|
||||
})
|
||||
|
||||
router.Methods(http.MethodGet).
|
||||
@@ -48,21 +49,3 @@ func (g Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Security-Policy", "frame-src 'self' https://traefik.io https://*.traefik.io;")
|
||||
http.FileServerFS(assets).ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
func safePrefix(req *http.Request) string {
|
||||
prefix := req.Header.Get("X-Forwarded-Prefix")
|
||||
if prefix == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
parse, err := url.Parse(prefix)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
if parse.Host != "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
return parse.Path
|
||||
}
|
||||
|
@@ -10,53 +10,8 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Test_safePrefix(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
value string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
desc: "host",
|
||||
value: "https://example.com",
|
||||
expected: "",
|
||||
},
|
||||
{
|
||||
desc: "host with path",
|
||||
value: "https://example.com/foo/bar?test",
|
||||
expected: "",
|
||||
},
|
||||
{
|
||||
desc: "path",
|
||||
value: "/foo/bar",
|
||||
expected: "/foo/bar",
|
||||
},
|
||||
{
|
||||
desc: "path without leading slash",
|
||||
value: "foo/bar",
|
||||
expected: "foo/bar",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, "http://localhost", nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
req.Header.Set("X-Forwarded-Prefix", test.value)
|
||||
|
||||
prefix := safePrefix(req)
|
||||
|
||||
assert.Equal(t, test.expected, prefix)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_ContentSecurityPolicy(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
|
@@ -19,7 +19,7 @@ import (
|
||||
"github.com/traefik/traefik/v2/pkg/config/static"
|
||||
)
|
||||
|
||||
func Bool(v bool) *bool { return &v }
|
||||
func pointer[T any](v T) *T { return &v }
|
||||
|
||||
func TestHandler_HTTP(t *testing.T) {
|
||||
type expected struct {
|
||||
@@ -337,7 +337,7 @@ func TestHandler_HTTP(t *testing.T) {
|
||||
si := &runtime.ServiceInfo{
|
||||
Service: &dynamic.Service{
|
||||
LoadBalancer: &dynamic.ServersLoadBalancer{
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
Servers: []dynamic.Server{
|
||||
{
|
||||
URL: "http://127.0.0.1",
|
||||
@@ -354,7 +354,7 @@ func TestHandler_HTTP(t *testing.T) {
|
||||
si := &runtime.ServiceInfo{
|
||||
Service: &dynamic.Service{
|
||||
LoadBalancer: &dynamic.ServersLoadBalancer{
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
Servers: []dynamic.Server{
|
||||
{
|
||||
URL: "http://127.0.0.2",
|
||||
@@ -423,7 +423,7 @@ func TestHandler_HTTP(t *testing.T) {
|
||||
si := &runtime.ServiceInfo{
|
||||
Service: &dynamic.Service{
|
||||
LoadBalancer: &dynamic.ServersLoadBalancer{
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
Servers: []dynamic.Server{
|
||||
{
|
||||
URL: "http://127.0.0.1",
|
||||
@@ -440,7 +440,7 @@ func TestHandler_HTTP(t *testing.T) {
|
||||
si := &runtime.ServiceInfo{
|
||||
Service: &dynamic.Service{
|
||||
LoadBalancer: &dynamic.ServersLoadBalancer{
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
Servers: []dynamic.Server{
|
||||
{
|
||||
URL: "http://127.0.0.2",
|
||||
@@ -457,7 +457,7 @@ func TestHandler_HTTP(t *testing.T) {
|
||||
si := &runtime.ServiceInfo{
|
||||
Service: &dynamic.Service{
|
||||
LoadBalancer: &dynamic.ServersLoadBalancer{
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
Servers: []dynamic.Server{
|
||||
{
|
||||
URL: "http://127.0.0.3",
|
||||
@@ -487,7 +487,7 @@ func TestHandler_HTTP(t *testing.T) {
|
||||
si := &runtime.ServiceInfo{
|
||||
Service: &dynamic.Service{
|
||||
LoadBalancer: &dynamic.ServersLoadBalancer{
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
Servers: []dynamic.Server{
|
||||
{
|
||||
URL: "http://127.0.0.1",
|
||||
@@ -505,7 +505,7 @@ func TestHandler_HTTP(t *testing.T) {
|
||||
si := &runtime.ServiceInfo{
|
||||
Service: &dynamic.Service{
|
||||
LoadBalancer: &dynamic.ServersLoadBalancer{
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
Servers: []dynamic.Server{
|
||||
{
|
||||
URL: "http://127.0.0.2",
|
||||
@@ -536,7 +536,7 @@ func TestHandler_HTTP(t *testing.T) {
|
||||
si := &runtime.ServiceInfo{
|
||||
Service: &dynamic.Service{
|
||||
LoadBalancer: &dynamic.ServersLoadBalancer{
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
Servers: []dynamic.Server{
|
||||
{
|
||||
URL: "http://127.0.0.1",
|
||||
@@ -554,7 +554,7 @@ func TestHandler_HTTP(t *testing.T) {
|
||||
si := &runtime.ServiceInfo{
|
||||
Service: &dynamic.Service{
|
||||
LoadBalancer: &dynamic.ServersLoadBalancer{
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
Servers: []dynamic.Server{
|
||||
{
|
||||
URL: "http://127.0.0.2",
|
||||
@@ -585,7 +585,7 @@ func TestHandler_HTTP(t *testing.T) {
|
||||
si := &runtime.ServiceInfo{
|
||||
Service: &dynamic.Service{
|
||||
LoadBalancer: &dynamic.ServersLoadBalancer{
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
Servers: []dynamic.Server{
|
||||
{
|
||||
URL: "http://127.0.0.1",
|
||||
@@ -614,7 +614,7 @@ func TestHandler_HTTP(t *testing.T) {
|
||||
si := &runtime.ServiceInfo{
|
||||
Service: &dynamic.Service{
|
||||
LoadBalancer: &dynamic.ServersLoadBalancer{
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
Servers: []dynamic.Server{
|
||||
{
|
||||
URL: "http://127.0.0.1",
|
||||
@@ -643,7 +643,7 @@ func TestHandler_HTTP(t *testing.T) {
|
||||
si := &runtime.ServiceInfo{
|
||||
Service: &dynamic.Service{
|
||||
LoadBalancer: &dynamic.ServersLoadBalancer{
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
Servers: []dynamic.Server{
|
||||
{
|
||||
URL: "http://127.0.0.1",
|
||||
|
@@ -38,7 +38,7 @@ func TestHandler_RawData(t *testing.T) {
|
||||
"foo-service@myprovider": {
|
||||
Service: &dynamic.Service{
|
||||
LoadBalancer: &dynamic.ServersLoadBalancer{
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
Servers: []dynamic.Server{
|
||||
{
|
||||
URL: "http://127.0.0.1",
|
||||
|
@@ -4,7 +4,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2024 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
@@ -13,6 +13,8 @@ import (
|
||||
"github.com/traefik/traefik/v2/pkg/types"
|
||||
)
|
||||
|
||||
func pointer[T any](v T) *T { return &v }
|
||||
|
||||
func TestDecodeConfiguration(t *testing.T) {
|
||||
labels := map[string]string{
|
||||
"traefik.http.middlewares.Middleware0.addprefix.prefix": "foobar",
|
||||
@@ -265,7 +267,7 @@ func TestDecodeConfiguration(t *testing.T) {
|
||||
Port: "42",
|
||||
},
|
||||
},
|
||||
TerminationDelay: func(i int) *int { return &i }(42),
|
||||
TerminationDelay: pointer(42),
|
||||
ProxyProtocol: &dynamic.ProxyProtocol{Version: 42},
|
||||
},
|
||||
},
|
||||
@@ -276,7 +278,7 @@ func TestDecodeConfiguration(t *testing.T) {
|
||||
Port: "42",
|
||||
},
|
||||
},
|
||||
TerminationDelay: func(i int) *int { return &i }(42),
|
||||
TerminationDelay: pointer(42),
|
||||
ProxyProtocol: &dynamic.ProxyProtocol{Version: 2},
|
||||
},
|
||||
},
|
||||
@@ -665,9 +667,9 @@ func TestDecodeConfiguration(t *testing.T) {
|
||||
"name0": "foobar",
|
||||
"name1": "foobar",
|
||||
},
|
||||
FollowRedirects: func(v bool) *bool { return &v }(true),
|
||||
FollowRedirects: pointer(true),
|
||||
},
|
||||
PassHostHeader: func(v bool) *bool { return &v }(true),
|
||||
PassHostHeader: pointer(true),
|
||||
ResponseForwarding: &dynamic.ResponseForwarding{
|
||||
FlushInterval: "foobar",
|
||||
},
|
||||
@@ -693,9 +695,9 @@ func TestDecodeConfiguration(t *testing.T) {
|
||||
"name0": "foobar",
|
||||
"name1": "foobar",
|
||||
},
|
||||
FollowRedirects: func(v bool) *bool { return &v }(true),
|
||||
FollowRedirects: pointer(true),
|
||||
},
|
||||
PassHostHeader: func(v bool) *bool { return &v }(true),
|
||||
PassHostHeader: pointer(true),
|
||||
ResponseForwarding: &dynamic.ResponseForwarding{
|
||||
FlushInterval: "foobar",
|
||||
},
|
||||
@@ -773,7 +775,7 @@ func TestEncodeConfiguration(t *testing.T) {
|
||||
Port: "42",
|
||||
},
|
||||
},
|
||||
TerminationDelay: func(i int) *int { return &i }(42),
|
||||
TerminationDelay: pointer(42),
|
||||
},
|
||||
},
|
||||
"Service1": {
|
||||
@@ -783,7 +785,7 @@ func TestEncodeConfiguration(t *testing.T) {
|
||||
Port: "42",
|
||||
},
|
||||
},
|
||||
TerminationDelay: func(i int) *int { return &i }(42),
|
||||
TerminationDelay: pointer(42),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1170,7 +1172,7 @@ func TestEncodeConfiguration(t *testing.T) {
|
||||
"name1": "foobar",
|
||||
},
|
||||
},
|
||||
PassHostHeader: func(v bool) *bool { return &v }(true),
|
||||
PassHostHeader: pointer(true),
|
||||
ResponseForwarding: &dynamic.ResponseForwarding{
|
||||
FlushInterval: "foobar",
|
||||
},
|
||||
@@ -1197,7 +1199,7 @@ func TestEncodeConfiguration(t *testing.T) {
|
||||
"name1": "foobar",
|
||||
},
|
||||
},
|
||||
PassHostHeader: func(v bool) *bool { return &v }(true),
|
||||
PassHostHeader: pointer(true),
|
||||
ResponseForwarding: &dynamic.ResponseForwarding{
|
||||
FlushInterval: "foobar",
|
||||
},
|
||||
|
@@ -24,14 +24,14 @@ type EntryPoint struct {
|
||||
|
||||
// GetAddress strips any potential protocol part of the address field of the
|
||||
// entry point, in order to return the actual address.
|
||||
func (ep EntryPoint) GetAddress() string {
|
||||
func (ep *EntryPoint) GetAddress() string {
|
||||
splitN := strings.SplitN(ep.Address, "/", 2)
|
||||
return splitN[0]
|
||||
}
|
||||
|
||||
// GetProtocol returns the protocol part of the address field of the entry point.
|
||||
// If none is specified, it defaults to "tcp".
|
||||
func (ep EntryPoint) GetProtocol() (string, error) {
|
||||
func (ep *EntryPoint) GetProtocol() (string, error) {
|
||||
splitN := strings.SplitN(ep.Address, "/", 2)
|
||||
if len(splitN) < 2 {
|
||||
return "tcp", nil
|
||||
|
@@ -278,7 +278,7 @@ func TestNewRequest(t *testing.T) {
|
||||
|
||||
if test.expected.err {
|
||||
require.Error(t, err)
|
||||
assert.Nil(t, nil)
|
||||
assert.Nil(t, req)
|
||||
} else {
|
||||
require.NoError(t, err, "failed to create new backend request")
|
||||
require.NotNil(t, req)
|
||||
|
@@ -12,7 +12,6 @@ import (
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -23,7 +22,6 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
ptypes "github.com/traefik/paerser/types"
|
||||
"github.com/traefik/traefik/v2/pkg/middlewares/capture"
|
||||
"github.com/traefik/traefik/v2/pkg/middlewares/recovery"
|
||||
"github.com/traefik/traefik/v2/pkg/types"
|
||||
)
|
||||
|
||||
@@ -810,16 +808,16 @@ func assertValidLogData(t *testing.T, expected string, logData []byte) {
|
||||
assert.Equal(t, resultExpected[OriginContentSize], result[OriginContentSize], formatErrMessage)
|
||||
assert.Equal(t, resultExpected[RequestRefererHeader], result[RequestRefererHeader], formatErrMessage)
|
||||
assert.Equal(t, resultExpected[RequestUserAgentHeader], result[RequestUserAgentHeader], formatErrMessage)
|
||||
assert.Regexp(t, regexp.MustCompile(`\d*`), result[RequestCount], formatErrMessage)
|
||||
assert.Regexp(t, `\d*`, result[RequestCount], formatErrMessage)
|
||||
assert.Equal(t, resultExpected[RouterName], result[RouterName], formatErrMessage)
|
||||
assert.Equal(t, resultExpected[ServiceURL], result[ServiceURL], formatErrMessage)
|
||||
assert.Regexp(t, regexp.MustCompile(`\d*ms`), result[Duration], formatErrMessage)
|
||||
assert.Regexp(t, `\d*ms`, result[Duration], formatErrMessage)
|
||||
}
|
||||
|
||||
func captureStdout(t *testing.T) (out *os.File, restoreStdout func()) {
|
||||
t.Helper()
|
||||
|
||||
file, err := os.CreateTemp("", "testlogger")
|
||||
file, err := os.CreateTemp(t.TempDir(), "testlogger")
|
||||
require.NoError(t, err, "failed to create temp file")
|
||||
|
||||
original := os.Stdout
|
||||
@@ -948,8 +946,14 @@ func doLoggingWithAbortedStream(t *testing.T, config *types.AccessLog) {
|
||||
req = req.WithContext(reqContext)
|
||||
|
||||
chain := alice.New()
|
||||
|
||||
chain = chain.Append(func(next http.Handler) (http.Handler, error) {
|
||||
return recovery.New(context.Background(), next)
|
||||
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||
defer func() {
|
||||
_ = recover() // ignore the stream backend panic to avoid the test to fail.
|
||||
}()
|
||||
next.ServeHTTP(rw, req)
|
||||
}), nil
|
||||
})
|
||||
chain = chain.Append(capture.Wrap)
|
||||
chain = chain.Append(WrapHandler(logger))
|
||||
|
@@ -13,34 +13,26 @@ const (
|
||||
upgradeHeader = "Upgrade"
|
||||
)
|
||||
|
||||
// Remover removes hop-by-hop headers listed in the "Connection" header.
|
||||
// RemoveConnectionHeaders removes hop-by-hop headers listed in the "Connection" header.
|
||||
// See RFC 7230, section 6.1.
|
||||
func Remover(next http.Handler) http.HandlerFunc {
|
||||
return func(rw http.ResponseWriter, req *http.Request) {
|
||||
var reqUpType string
|
||||
if httpguts.HeaderValuesContainsToken(req.Header[connectionHeader], upgradeHeader) {
|
||||
reqUpType = req.Header.Get(upgradeHeader)
|
||||
}
|
||||
|
||||
removeConnectionHeaders(req.Header)
|
||||
|
||||
if reqUpType != "" {
|
||||
req.Header.Set(connectionHeader, upgradeHeader)
|
||||
req.Header.Set(upgradeHeader, reqUpType)
|
||||
} else {
|
||||
req.Header.Del(connectionHeader)
|
||||
}
|
||||
|
||||
next.ServeHTTP(rw, req)
|
||||
func RemoveConnectionHeaders(req *http.Request) {
|
||||
var reqUpType string
|
||||
if httpguts.HeaderValuesContainsToken(req.Header[connectionHeader], upgradeHeader) {
|
||||
reqUpType = req.Header.Get(upgradeHeader)
|
||||
}
|
||||
}
|
||||
|
||||
func removeConnectionHeaders(h http.Header) {
|
||||
for _, f := range h[connectionHeader] {
|
||||
for _, f := range req.Header[connectionHeader] {
|
||||
for _, sf := range strings.Split(f, ",") {
|
||||
if sf = textproto.TrimString(sf); sf != "" {
|
||||
h.Del(sf)
|
||||
req.Header.Del(sf)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if reqUpType != "" {
|
||||
req.Header.Set(connectionHeader, upgradeHeader)
|
||||
req.Header.Set(upgradeHeader, reqUpType)
|
||||
} else {
|
||||
req.Header.Del(connectionHeader)
|
||||
}
|
||||
}
|
||||
|
@@ -50,19 +50,13 @@ func TestRemover(t *testing.T) {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
next := http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {})
|
||||
|
||||
h := Remover(next)
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "https://localhost", nil)
|
||||
|
||||
for k, v := range test.reqHeaders {
|
||||
req.Header.Set(k, v)
|
||||
}
|
||||
|
||||
rw := httptest.NewRecorder()
|
||||
|
||||
h.ServeHTTP(rw, req)
|
||||
RemoveConnectionHeaders(req)
|
||||
|
||||
assert.Equal(t, test.expected, req.Header)
|
||||
})
|
||||
|
@@ -89,7 +89,7 @@ func NewForward(ctx context.Context, next http.Handler, config dynamic.ForwardAu
|
||||
fa.authResponseHeadersRegex = re
|
||||
}
|
||||
|
||||
return Remover(fa), nil
|
||||
return fa, nil
|
||||
}
|
||||
|
||||
func (fa *forwardAuth) GetTracingInformation() (string, ext.SpanKindEnum) {
|
||||
@@ -195,6 +195,8 @@ func (fa *forwardAuth) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||
|
||||
func writeHeader(req, forwardReq *http.Request, trustForwardHeader bool, allowedHeaders []string) {
|
||||
utils.CopyHeaders(forwardReq.Header, req.Header)
|
||||
|
||||
RemoveConnectionHeaders(forwardReq)
|
||||
utils.RemoveHeaders(forwardReq.Header, hopHeaders...)
|
||||
|
||||
forwardReq.Header = filterForwardRequestHeaders(forwardReq.Header, allowedHeaders)
|
||||
|
@@ -20,6 +20,7 @@ const (
|
||||
xForwardedServer = "X-Forwarded-Server"
|
||||
xForwardedURI = "X-Forwarded-Uri"
|
||||
xForwardedMethod = "X-Forwarded-Method"
|
||||
xForwardedPrefix = "X-Forwarded-Prefix"
|
||||
xForwardedTLSClientCert = "X-Forwarded-Tls-Client-Cert"
|
||||
xForwardedTLSClientCertInfo = "X-Forwarded-Tls-Client-Cert-Info"
|
||||
xRealIP = "X-Real-Ip"
|
||||
@@ -35,6 +36,7 @@ var xHeaders = []string{
|
||||
xForwardedServer,
|
||||
xForwardedURI,
|
||||
xForwardedMethod,
|
||||
xForwardedPrefix,
|
||||
xForwardedTLSClientCert,
|
||||
xForwardedTLSClientCertInfo,
|
||||
xRealIP,
|
||||
|
@@ -48,6 +48,7 @@ func TestServeHTTP(t *testing.T) {
|
||||
xForwardedMethod: {"GET"},
|
||||
xForwardedTLSClientCert: {"Cert"},
|
||||
xForwardedTLSClientCertInfo: {"CertInfo"},
|
||||
xForwardedPrefix: {"/prefix"},
|
||||
},
|
||||
expectedHeaders: map[string]string{
|
||||
xForwardedFor: "10.0.1.0, 10.0.1.12",
|
||||
@@ -55,6 +56,7 @@ func TestServeHTTP(t *testing.T) {
|
||||
xForwardedMethod: "GET",
|
||||
xForwardedTLSClientCert: "Cert",
|
||||
xForwardedTLSClientCertInfo: "CertInfo",
|
||||
xForwardedPrefix: "/prefix",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -68,6 +70,7 @@ func TestServeHTTP(t *testing.T) {
|
||||
xForwardedMethod: {"GET"},
|
||||
xForwardedTLSClientCert: {"Cert"},
|
||||
xForwardedTLSClientCertInfo: {"CertInfo"},
|
||||
xForwardedPrefix: {"/prefix"},
|
||||
},
|
||||
expectedHeaders: map[string]string{
|
||||
xForwardedFor: "",
|
||||
@@ -75,6 +78,7 @@ func TestServeHTTP(t *testing.T) {
|
||||
xForwardedMethod: "",
|
||||
xForwardedTLSClientCert: "",
|
||||
xForwardedTLSClientCertInfo: "",
|
||||
xForwardedPrefix: "",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -88,6 +92,7 @@ func TestServeHTTP(t *testing.T) {
|
||||
xForwardedMethod: {"GET"},
|
||||
xForwardedTLSClientCert: {"Cert"},
|
||||
xForwardedTLSClientCertInfo: {"CertInfo"},
|
||||
xForwardedPrefix: {"/prefix"},
|
||||
},
|
||||
expectedHeaders: map[string]string{
|
||||
xForwardedFor: "10.0.1.0, 10.0.1.12",
|
||||
@@ -95,6 +100,7 @@ func TestServeHTTP(t *testing.T) {
|
||||
xForwardedMethod: "GET",
|
||||
xForwardedTLSClientCert: "Cert",
|
||||
xForwardedTLSClientCertInfo: "CertInfo",
|
||||
xForwardedPrefix: "/prefix",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -108,6 +114,7 @@ func TestServeHTTP(t *testing.T) {
|
||||
xForwardedMethod: {"GET"},
|
||||
xForwardedTLSClientCert: {"Cert"},
|
||||
xForwardedTLSClientCertInfo: {"CertInfo"},
|
||||
xForwardedPrefix: {"/prefix"},
|
||||
},
|
||||
expectedHeaders: map[string]string{
|
||||
xForwardedFor: "",
|
||||
@@ -115,6 +122,7 @@ func TestServeHTTP(t *testing.T) {
|
||||
xForwardedMethod: "",
|
||||
xForwardedTLSClientCert: "",
|
||||
xForwardedTLSClientCertInfo: "",
|
||||
xForwardedPrefix: "",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -128,6 +136,7 @@ func TestServeHTTP(t *testing.T) {
|
||||
xForwardedMethod: {"GET"},
|
||||
xForwardedTLSClientCert: {"Cert"},
|
||||
xForwardedTLSClientCertInfo: {"CertInfo"},
|
||||
xForwardedPrefix: {"/prefix"},
|
||||
},
|
||||
expectedHeaders: map[string]string{
|
||||
xForwardedFor: "10.0.1.0, 10.0.1.12",
|
||||
@@ -135,6 +144,7 @@ func TestServeHTTP(t *testing.T) {
|
||||
xForwardedMethod: "GET",
|
||||
xForwardedTLSClientCert: "Cert",
|
||||
xForwardedTLSClientCertInfo: "CertInfo",
|
||||
xForwardedPrefix: "/prefix",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -148,6 +158,7 @@ func TestServeHTTP(t *testing.T) {
|
||||
xForwardedMethod: {"GET"},
|
||||
xForwardedTLSClientCert: {"Cert"},
|
||||
xForwardedTLSClientCertInfo: {"CertInfo"},
|
||||
xForwardedPrefix: {"/prefix"},
|
||||
},
|
||||
expectedHeaders: map[string]string{
|
||||
xForwardedFor: "",
|
||||
@@ -155,6 +166,7 @@ func TestServeHTTP(t *testing.T) {
|
||||
xForwardedMethod: "",
|
||||
xForwardedTLSClientCert: "",
|
||||
xForwardedTLSClientCertInfo: "",
|
||||
xForwardedPrefix: "",
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -283,6 +295,7 @@ func TestServeHTTP(t *testing.T) {
|
||||
xForwardedPort,
|
||||
xForwardedTLSClientCert,
|
||||
xForwardedTLSClientCertInfo,
|
||||
xForwardedPrefix,
|
||||
xRealIP,
|
||||
},
|
||||
xForwardedProto: {"foo"},
|
||||
@@ -293,6 +306,7 @@ func TestServeHTTP(t *testing.T) {
|
||||
xForwardedPort: {"foo"},
|
||||
xForwardedTLSClientCert: {"foo"},
|
||||
xForwardedTLSClientCertInfo: {"foo"},
|
||||
xForwardedPrefix: {"foo"},
|
||||
xRealIP: {"foo"},
|
||||
},
|
||||
expectedHeaders: map[string]string{
|
||||
@@ -304,6 +318,7 @@ func TestServeHTTP(t *testing.T) {
|
||||
xForwardedPort: "80",
|
||||
xForwardedTLSClientCert: "",
|
||||
xForwardedTLSClientCertInfo: "",
|
||||
xForwardedPrefix: "",
|
||||
xRealIP: "",
|
||||
connection: "",
|
||||
},
|
||||
@@ -321,6 +336,7 @@ func TestServeHTTP(t *testing.T) {
|
||||
xForwardedPort,
|
||||
xForwardedTLSClientCert,
|
||||
xForwardedTLSClientCertInfo,
|
||||
xForwardedPrefix,
|
||||
xRealIP,
|
||||
},
|
||||
xForwardedProto: {"foo"},
|
||||
@@ -331,6 +347,7 @@ func TestServeHTTP(t *testing.T) {
|
||||
xForwardedPort: {"foo"},
|
||||
xForwardedTLSClientCert: {"foo"},
|
||||
xForwardedTLSClientCertInfo: {"foo"},
|
||||
xForwardedPrefix: {"foo"},
|
||||
xRealIP: {"foo"},
|
||||
},
|
||||
expectedHeaders: map[string]string{
|
||||
@@ -342,6 +359,7 @@ func TestServeHTTP(t *testing.T) {
|
||||
xForwardedPort: "foo",
|
||||
xForwardedTLSClientCert: "foo",
|
||||
xForwardedTLSClientCertInfo: "foo",
|
||||
xForwardedPrefix: "foo",
|
||||
xRealIP: "foo",
|
||||
connection: "",
|
||||
},
|
||||
@@ -358,6 +376,7 @@ func TestServeHTTP(t *testing.T) {
|
||||
xForwardedPort,
|
||||
xForwardedTLSClientCert,
|
||||
xForwardedTLSClientCertInfo,
|
||||
xForwardedPrefix,
|
||||
xRealIP,
|
||||
},
|
||||
incomingHeaders: map[string][]string{
|
||||
@@ -370,6 +389,7 @@ func TestServeHTTP(t *testing.T) {
|
||||
xForwardedPort,
|
||||
xForwardedTLSClientCert,
|
||||
xForwardedTLSClientCertInfo,
|
||||
xForwardedPrefix,
|
||||
xRealIP,
|
||||
},
|
||||
xForwardedProto: {"foo"},
|
||||
@@ -380,6 +400,7 @@ func TestServeHTTP(t *testing.T) {
|
||||
xForwardedPort: {"foo"},
|
||||
xForwardedTLSClientCert: {"foo"},
|
||||
xForwardedTLSClientCertInfo: {"foo"},
|
||||
xForwardedPrefix: {"foo"},
|
||||
xRealIP: {"foo"},
|
||||
},
|
||||
expectedHeaders: map[string]string{
|
||||
@@ -391,6 +412,7 @@ func TestServeHTTP(t *testing.T) {
|
||||
xForwardedPort: "80",
|
||||
xForwardedTLSClientCert: "",
|
||||
xForwardedTLSClientCertInfo: "",
|
||||
xForwardedPrefix: "",
|
||||
xRealIP: "",
|
||||
connection: "",
|
||||
},
|
||||
@@ -407,6 +429,7 @@ func TestServeHTTP(t *testing.T) {
|
||||
xForwardedPort,
|
||||
xForwardedTLSClientCert,
|
||||
xForwardedTLSClientCertInfo,
|
||||
xForwardedPrefix,
|
||||
xRealIP,
|
||||
},
|
||||
incomingHeaders: map[string][]string{
|
||||
@@ -419,6 +442,7 @@ func TestServeHTTP(t *testing.T) {
|
||||
xForwardedPort,
|
||||
xForwardedTLSClientCert,
|
||||
xForwardedTLSClientCertInfo,
|
||||
xForwardedPrefix,
|
||||
xRealIP,
|
||||
},
|
||||
xForwardedProto: {"foo"},
|
||||
@@ -429,6 +453,7 @@ func TestServeHTTP(t *testing.T) {
|
||||
xForwardedPort: {"foo"},
|
||||
xForwardedTLSClientCert: {"foo"},
|
||||
xForwardedTLSClientCertInfo: {"foo"},
|
||||
xForwardedPrefix: {"foo"},
|
||||
xRealIP: {"foo"},
|
||||
},
|
||||
expectedHeaders: map[string]string{
|
||||
@@ -440,6 +465,7 @@ func TestServeHTTP(t *testing.T) {
|
||||
xForwardedPort: "foo",
|
||||
xForwardedTLSClientCert: "foo",
|
||||
xForwardedTLSClientCertInfo: "foo",
|
||||
xForwardedPrefix: "foo",
|
||||
xRealIP: "foo",
|
||||
connection: "",
|
||||
},
|
||||
|
@@ -152,7 +152,7 @@ func (p *passTLSClientCert) ServeHTTP(rw http.ResponseWriter, req *http.Request)
|
||||
if req.TLS != nil && len(req.TLS.PeerCertificates) > 0 {
|
||||
req.Header.Set(xForwardedTLSClientCert, getCertificates(ctx, req.TLS.PeerCertificates))
|
||||
} else {
|
||||
logger.Warn("Tried to extract a certificate on a request without mutual TLS")
|
||||
logger.Debug("Tried to extract a certificate on a request without mutual TLS")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,7 +161,7 @@ func (p *passTLSClientCert) ServeHTTP(rw http.ResponseWriter, req *http.Request)
|
||||
headerContent := p.getCertInfo(ctx, req.TLS.PeerCertificates)
|
||||
req.Header.Set(xForwardedTLSClientCertInfo, url.QueryEscape(headerContent))
|
||||
} else {
|
||||
logger.Warn("Tried to extract a certificate on a request without mutual TLS")
|
||||
logger.Debug("Tried to extract a certificate on a request without mutual TLS")
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,7 +1,10 @@
|
||||
package recovery
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"runtime"
|
||||
|
||||
@@ -28,12 +31,16 @@ func New(ctx context.Context, next http.Handler) (http.Handler, error) {
|
||||
}
|
||||
|
||||
func (re *recovery) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||
defer recoverFunc(rw, req)
|
||||
re.next.ServeHTTP(rw, req)
|
||||
recoveryRW := newRecoveryResponseWriter(rw)
|
||||
defer recoverFunc(recoveryRW, req)
|
||||
|
||||
re.next.ServeHTTP(recoveryRW, req)
|
||||
}
|
||||
|
||||
func recoverFunc(rw http.ResponseWriter, r *http.Request) {
|
||||
func recoverFunc(rw recoveryResponseWriter, r *http.Request) {
|
||||
if err := recover(); err != nil {
|
||||
defer rw.finalizeResponse()
|
||||
|
||||
logger := log.FromContext(middlewares.GetLoggerCtx(r.Context(), middlewareName, typeName))
|
||||
if !shouldLogPanic(err) {
|
||||
logger.Debugf("Request has been aborted [%s - %s]: %v", r.RemoteAddr, r.URL, err)
|
||||
@@ -45,8 +52,6 @@ func recoverFunc(rw http.ResponseWriter, r *http.Request) {
|
||||
buf := make([]byte, size)
|
||||
buf = buf[:runtime.Stack(buf, false)]
|
||||
logger.Errorf("Stack: %s", buf)
|
||||
|
||||
http.Error(rw, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,3 +61,81 @@ func shouldLogPanic(panicValue interface{}) bool {
|
||||
//nolint:errorlint // false-positive because panicValue is an interface.
|
||||
return panicValue != nil && panicValue != http.ErrAbortHandler
|
||||
}
|
||||
|
||||
type recoveryResponseWriter interface {
|
||||
http.ResponseWriter
|
||||
|
||||
finalizeResponse()
|
||||
}
|
||||
|
||||
func newRecoveryResponseWriter(rw http.ResponseWriter) recoveryResponseWriter {
|
||||
wrapper := &responseWriterWrapper{rw: rw}
|
||||
if _, ok := rw.(http.CloseNotifier); !ok {
|
||||
return wrapper
|
||||
}
|
||||
|
||||
return &responseWriterWrapperWithCloseNotify{wrapper}
|
||||
}
|
||||
|
||||
type responseWriterWrapper struct {
|
||||
rw http.ResponseWriter
|
||||
headersSent bool
|
||||
}
|
||||
|
||||
func (r *responseWriterWrapper) Header() http.Header {
|
||||
return r.rw.Header()
|
||||
}
|
||||
|
||||
func (r *responseWriterWrapper) Write(bytes []byte) (int, error) {
|
||||
r.headersSent = true
|
||||
return r.rw.Write(bytes)
|
||||
}
|
||||
|
||||
func (r *responseWriterWrapper) WriteHeader(code int) {
|
||||
if r.headersSent {
|
||||
return
|
||||
}
|
||||
|
||||
// Handling informational headers.
|
||||
if code >= 100 && code <= 199 {
|
||||
r.rw.WriteHeader(code)
|
||||
return
|
||||
}
|
||||
|
||||
r.headersSent = true
|
||||
r.rw.WriteHeader(code)
|
||||
}
|
||||
|
||||
func (r *responseWriterWrapper) Flush() {
|
||||
if f, ok := r.rw.(http.Flusher); ok {
|
||||
f.Flush()
|
||||
}
|
||||
}
|
||||
|
||||
func (r *responseWriterWrapper) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
||||
if h, ok := r.rw.(http.Hijacker); ok {
|
||||
return h.Hijack()
|
||||
}
|
||||
|
||||
return nil, nil, fmt.Errorf("not a hijacker: %T", r.rw)
|
||||
}
|
||||
|
||||
func (r *responseWriterWrapper) finalizeResponse() {
|
||||
// If headers have been sent this is not possible to respond with an HTTP error,
|
||||
// and we let the server abort the response silently thanks to the http.ErrAbortHandler sentinel panic value.
|
||||
if r.headersSent {
|
||||
panic(http.ErrAbortHandler)
|
||||
}
|
||||
|
||||
// The response has not yet started to be written,
|
||||
// we can safely return a fresh new error response.
|
||||
http.Error(r.rw, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
type responseWriterWrapperWithCloseNotify struct {
|
||||
*responseWriterWrapper
|
||||
}
|
||||
|
||||
func (r *responseWriterWrapperWithCloseNotify) CloseNotify() <-chan bool {
|
||||
return r.rw.(http.CloseNotifier).CloseNotify()
|
||||
}
|
||||
|
@@ -2,6 +2,8 @@ package recovery
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
@@ -11,17 +13,54 @@ import (
|
||||
)
|
||||
|
||||
func TestRecoverHandler(t *testing.T) {
|
||||
fn := func(w http.ResponseWriter, r *http.Request) {
|
||||
panic("I love panicking!")
|
||||
tests := []struct {
|
||||
desc string
|
||||
panicErr error
|
||||
headersSent bool
|
||||
}{
|
||||
{
|
||||
desc: "headers sent and custom panic error",
|
||||
panicErr: errors.New("foo"),
|
||||
headersSent: true,
|
||||
},
|
||||
{
|
||||
desc: "headers sent and error abort handler",
|
||||
panicErr: http.ErrAbortHandler,
|
||||
headersSent: true,
|
||||
},
|
||||
{
|
||||
desc: "custom panic error",
|
||||
panicErr: errors.New("foo"),
|
||||
},
|
||||
{
|
||||
desc: "error abort handler",
|
||||
panicErr: http.ErrAbortHandler,
|
||||
},
|
||||
}
|
||||
recovery, err := New(context.Background(), http.HandlerFunc(fn))
|
||||
require.NoError(t, err)
|
||||
for _, test := range tests {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
server := httptest.NewServer(recovery)
|
||||
defer server.Close()
|
||||
fn := func(rw http.ResponseWriter, req *http.Request) {
|
||||
if test.headersSent {
|
||||
rw.WriteHeader(http.StatusTeapot)
|
||||
}
|
||||
panic(test.panicErr)
|
||||
}
|
||||
recovery, err := New(context.Background(), http.HandlerFunc(fn))
|
||||
require.NoError(t, err)
|
||||
|
||||
resp, err := http.Get(server.URL)
|
||||
require.NoError(t, err)
|
||||
server := httptest.NewServer(recovery)
|
||||
t.Cleanup(server.Close)
|
||||
|
||||
assert.Equal(t, http.StatusInternalServerError, resp.StatusCode)
|
||||
res, err := http.Get(server.URL)
|
||||
if test.headersSent {
|
||||
require.Nil(t, res)
|
||||
assert.ErrorIs(t, err, io.EOF)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, http.StatusInternalServerError, res.StatusCode)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@@ -37,6 +37,48 @@ type Listener interface {
|
||||
// each of them about a retry attempt.
|
||||
type Listeners []Listener
|
||||
|
||||
// Retried exists to implement the Listener interface. It calls Retried on each of its slice entries.
|
||||
func (l Listeners) Retried(req *http.Request, attempt int) {
|
||||
for _, listener := range l {
|
||||
listener.Retried(req, attempt)
|
||||
}
|
||||
}
|
||||
|
||||
type shouldRetryContextKey struct{}
|
||||
|
||||
// ShouldRetry is a function allowing to enable/disable the retry middleware mechanism.
|
||||
type ShouldRetry func(shouldRetry bool)
|
||||
|
||||
// ContextShouldRetry returns the ShouldRetry function if it has been set by the Retry middleware in the chain.
|
||||
func ContextShouldRetry(ctx context.Context) ShouldRetry {
|
||||
f, _ := ctx.Value(shouldRetryContextKey{}).(ShouldRetry)
|
||||
return f
|
||||
}
|
||||
|
||||
// WrapHandler wraps a given http.Handler to inject the httptrace.ClientTrace in the request context when it is needed
|
||||
// by the retry middleware.
|
||||
func WrapHandler(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||
if shouldRetry := ContextShouldRetry(req.Context()); shouldRetry != nil {
|
||||
shouldRetry(true)
|
||||
|
||||
trace := &httptrace.ClientTrace{
|
||||
WroteHeaders: func() {
|
||||
shouldRetry(false)
|
||||
},
|
||||
WroteRequest: func(httptrace.WroteRequestInfo) {
|
||||
shouldRetry(false)
|
||||
},
|
||||
}
|
||||
newCtx := httptrace.WithClientTrace(req.Context(), trace)
|
||||
next.ServeHTTP(rw, req.WithContext(newCtx))
|
||||
return
|
||||
}
|
||||
|
||||
next.ServeHTTP(rw, req)
|
||||
})
|
||||
}
|
||||
|
||||
// retry is a middleware that retries requests.
|
||||
type retry struct {
|
||||
attempts int
|
||||
@@ -83,19 +125,13 @@ func (r *retry) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||
attempts := 1
|
||||
|
||||
operation := func() error {
|
||||
shouldRetry := attempts < r.attempts
|
||||
retryResponseWriter := newResponseWriter(rw, shouldRetry)
|
||||
remainAttempts := attempts < r.attempts
|
||||
retryResponseWriter := newResponseWriter(rw)
|
||||
|
||||
// Disable retries when the backend already received request data
|
||||
trace := &httptrace.ClientTrace{
|
||||
WroteHeaders: func() {
|
||||
retryResponseWriter.DisableRetries()
|
||||
},
|
||||
WroteRequest: func(httptrace.WroteRequestInfo) {
|
||||
retryResponseWriter.DisableRetries()
|
||||
},
|
||||
var shouldRetry ShouldRetry = func(shouldRetry bool) {
|
||||
retryResponseWriter.SetShouldRetry(remainAttempts && shouldRetry)
|
||||
}
|
||||
newCtx := httptrace.WithClientTrace(req.Context(), trace)
|
||||
newCtx := context.WithValue(req.Context(), shouldRetryContextKey{}, shouldRetry)
|
||||
|
||||
r.next.ServeHTTP(retryResponseWriter, req.Clone(newCtx))
|
||||
|
||||
@@ -142,25 +178,17 @@ func (r *retry) newBackOff() backoff.BackOff {
|
||||
return b
|
||||
}
|
||||
|
||||
// Retried exists to implement the Listener interface. It calls Retried on each of its slice entries.
|
||||
func (l Listeners) Retried(req *http.Request, attempt int) {
|
||||
for _, listener := range l {
|
||||
listener.Retried(req, attempt)
|
||||
}
|
||||
}
|
||||
|
||||
type responseWriter interface {
|
||||
http.ResponseWriter
|
||||
http.Flusher
|
||||
ShouldRetry() bool
|
||||
DisableRetries()
|
||||
SetShouldRetry(shouldRetry bool)
|
||||
}
|
||||
|
||||
func newResponseWriter(rw http.ResponseWriter, shouldRetry bool) responseWriter {
|
||||
func newResponseWriter(rw http.ResponseWriter) responseWriter {
|
||||
responseWriter := &responseWriterWithoutCloseNotify{
|
||||
responseWriter: rw,
|
||||
headers: make(http.Header),
|
||||
shouldRetry: shouldRetry,
|
||||
}
|
||||
if _, ok := rw.(http.CloseNotifier); ok {
|
||||
return &responseWriterWithCloseNotify{
|
||||
@@ -181,8 +209,8 @@ func (r *responseWriterWithoutCloseNotify) ShouldRetry() bool {
|
||||
return r.shouldRetry
|
||||
}
|
||||
|
||||
func (r *responseWriterWithoutCloseNotify) DisableRetries() {
|
||||
r.shouldRetry = false
|
||||
func (r *responseWriterWithoutCloseNotify) SetShouldRetry(shouldRetry bool) {
|
||||
r.shouldRetry = shouldRetry
|
||||
}
|
||||
|
||||
func (r *responseWriterWithoutCloseNotify) Header() http.Header {
|
||||
@@ -193,23 +221,17 @@ func (r *responseWriterWithoutCloseNotify) Header() http.Header {
|
||||
}
|
||||
|
||||
func (r *responseWriterWithoutCloseNotify) Write(buf []byte) (int, error) {
|
||||
if r.ShouldRetry() {
|
||||
if r.shouldRetry {
|
||||
return len(buf), nil
|
||||
}
|
||||
if !r.written {
|
||||
r.WriteHeader(http.StatusOK)
|
||||
}
|
||||
return r.responseWriter.Write(buf)
|
||||
}
|
||||
|
||||
func (r *responseWriterWithoutCloseNotify) WriteHeader(code int) {
|
||||
if r.ShouldRetry() && code == http.StatusServiceUnavailable {
|
||||
// We get a 503 HTTP Status Code when there is no backend server in the pool
|
||||
// to which the request could be sent. Also, note that r.ShouldRetry()
|
||||
// will never return true in case there was a connection established to
|
||||
// the backend server and so we can be sure that the 503 was produced
|
||||
// inside Traefik already and we don't have to retry in this cases.
|
||||
r.DisableRetries()
|
||||
}
|
||||
|
||||
if r.ShouldRetry() || r.written {
|
||||
if r.shouldRetry || r.written {
|
||||
return
|
||||
}
|
||||
|
||||
|
@@ -105,12 +105,21 @@ func TestRetry(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
retryAttempts := 0
|
||||
next := http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||
next := http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||
// This signals that a connection will be established with the backend
|
||||
// to enable the Retry middleware mechanism.
|
||||
shouldRetry := ContextShouldRetry(req.Context())
|
||||
if shouldRetry != nil {
|
||||
shouldRetry(true)
|
||||
}
|
||||
|
||||
retryAttempts++
|
||||
|
||||
if retryAttempts > test.amountFaultyEndpoints {
|
||||
// calls WroteHeaders on httptrace.
|
||||
_ = r.Write(io.Discard)
|
||||
// This signals that request headers have been sent to the backend.
|
||||
if shouldRetry != nil {
|
||||
shouldRetry(false)
|
||||
}
|
||||
|
||||
rw.WriteHeader(http.StatusOK)
|
||||
return
|
||||
@@ -152,27 +161,16 @@ func TestRetryEmptyServerList(t *testing.T) {
|
||||
assert.Equal(t, 0, retryListener.timesCalled)
|
||||
}
|
||||
|
||||
func TestRetryListeners(t *testing.T) {
|
||||
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
||||
retryListeners := Listeners{&countingRetryListener{}, &countingRetryListener{}}
|
||||
|
||||
retryListeners.Retried(req, 1)
|
||||
retryListeners.Retried(req, 1)
|
||||
|
||||
for _, retryListener := range retryListeners {
|
||||
listener := retryListener.(*countingRetryListener)
|
||||
if listener.timesCalled != 2 {
|
||||
t.Errorf("retry listener was called %d time(s), want %d time(s)", listener.timesCalled, 2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMultipleRetriesShouldNotLooseHeaders(t *testing.T) {
|
||||
attempt := 0
|
||||
expectedHeaderName := "X-Foo-Test-2"
|
||||
expectedHeaderValue := "bar"
|
||||
|
||||
next := http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||
shouldRetry := ContextShouldRetry(req.Context())
|
||||
if shouldRetry != nil {
|
||||
shouldRetry(true)
|
||||
}
|
||||
|
||||
headerName := fmt.Sprintf("X-Foo-Test-%d", attempt)
|
||||
rw.Header().Add(headerName, expectedHeaderValue)
|
||||
if attempt < 2 {
|
||||
@@ -181,43 +179,54 @@ func TestMultipleRetriesShouldNotLooseHeaders(t *testing.T) {
|
||||
}
|
||||
|
||||
// Request has been successfully written to backend
|
||||
trace := httptrace.ContextClientTrace(req.Context())
|
||||
trace.WroteHeaders()
|
||||
shouldRetry(false)
|
||||
|
||||
// And we decide to answer to client
|
||||
// And we decide to answer to client.
|
||||
rw.WriteHeader(http.StatusNoContent)
|
||||
})
|
||||
|
||||
retry, err := New(context.Background(), next, dynamic.Retry{Attempts: 3}, &countingRetryListener{}, "traefikTest")
|
||||
require.NoError(t, err)
|
||||
|
||||
responseRecorder := httptest.NewRecorder()
|
||||
retry.ServeHTTP(responseRecorder, testhelpers.MustNewRequest(http.MethodGet, "http://test", http.NoBody))
|
||||
res := httptest.NewRecorder()
|
||||
retry.ServeHTTP(res, testhelpers.MustNewRequest(http.MethodGet, "http://test", http.NoBody))
|
||||
|
||||
headerValue := responseRecorder.Header().Get(expectedHeaderName)
|
||||
|
||||
// Validate if we have the correct header
|
||||
if headerValue != expectedHeaderValue {
|
||||
t.Errorf("Expected to have %s for header %s, got %s", expectedHeaderValue, expectedHeaderName, headerValue)
|
||||
}
|
||||
// The third header attempt is kept.
|
||||
headerValue := res.Header().Get("X-Foo-Test-2")
|
||||
assert.Equal(t, expectedHeaderValue, headerValue)
|
||||
|
||||
// Validate that we don't have headers from previous attempts
|
||||
for i := range attempt {
|
||||
headerName := fmt.Sprintf("X-Foo-Test-%d", i)
|
||||
headerValue = responseRecorder.Header().Get("headerName")
|
||||
headerValue = res.Header().Get(headerName)
|
||||
if headerValue != "" {
|
||||
t.Errorf("Expected no value for header %s, got %s", headerName, headerValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// countingRetryListener is a Listener implementation to count the times the Retried fn is called.
|
||||
type countingRetryListener struct {
|
||||
timesCalled int
|
||||
}
|
||||
func TestRetryShouldNotLooseHeadersOnWrite(t *testing.T) {
|
||||
next := http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||
rw.Header().Add("X-Foo-Test", "bar")
|
||||
|
||||
func (l *countingRetryListener) Retried(req *http.Request, attempt int) {
|
||||
l.timesCalled++
|
||||
// Request has been successfully written to backend.
|
||||
shouldRetry := ContextShouldRetry(req.Context())
|
||||
if shouldRetry != nil {
|
||||
shouldRetry(false)
|
||||
}
|
||||
// And we decide to answer to client without calling WriteHeader.
|
||||
_, err := rw.Write([]byte("bar"))
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
retry, err := New(context.Background(), next, dynamic.Retry{Attempts: 3}, &countingRetryListener{}, "traefikTest")
|
||||
require.NoError(t, err)
|
||||
|
||||
res := httptest.NewRecorder()
|
||||
retry.ServeHTTP(res, testhelpers.MustNewRequest(http.MethodGet, "http://test", http.NoBody))
|
||||
|
||||
headerValue := res.Header().Get("X-Foo-Test")
|
||||
assert.Equal(t, "bar", headerValue)
|
||||
}
|
||||
|
||||
func TestRetryWithFlush(t *testing.T) {
|
||||
@@ -275,12 +284,24 @@ func TestRetryWebsocket(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
retryAttempts := 0
|
||||
next := http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||
next := http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||
// This signals that a connection will be established with the backend
|
||||
// to enable the Retry middleware mechanism.
|
||||
shouldRetry := ContextShouldRetry(req.Context())
|
||||
if shouldRetry != nil {
|
||||
shouldRetry(true)
|
||||
}
|
||||
|
||||
retryAttempts++
|
||||
|
||||
if retryAttempts > test.amountFaultyEndpoints {
|
||||
// This signals that request headers have been sent to the backend.
|
||||
if shouldRetry != nil {
|
||||
shouldRetry(false)
|
||||
}
|
||||
|
||||
upgrader := websocket.Upgrader{}
|
||||
_, err := upgrader.Upgrade(rw, r, nil)
|
||||
_, err := upgrader.Upgrade(rw, req, nil)
|
||||
if err != nil {
|
||||
http.Error(rw, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
@@ -387,3 +408,12 @@ func Test1xxResponses(t *testing.T) {
|
||||
|
||||
assert.Equal(t, 0, retryListener.timesCalled)
|
||||
}
|
||||
|
||||
// countingRetryListener is a Listener implementation to count the times the Retried fn is called.
|
||||
type countingRetryListener struct {
|
||||
timesCalled int
|
||||
}
|
||||
|
||||
func (l *countingRetryListener) Retried(req *http.Request, attempt int) {
|
||||
l.timesCalled++
|
||||
}
|
||||
|
@@ -38,24 +38,24 @@ type MockSpan struct {
|
||||
Tags map[string]interface{}
|
||||
}
|
||||
|
||||
func (n MockSpan) Context() opentracing.SpanContext { return MockSpanContext{} }
|
||||
func (n MockSpan) SetBaggageItem(key, val string) opentracing.Span {
|
||||
return MockSpan{Tags: make(map[string]interface{})}
|
||||
func (n *MockSpan) Context() opentracing.SpanContext { return MockSpanContext{} }
|
||||
func (n *MockSpan) SetBaggageItem(key, val string) opentracing.Span {
|
||||
return &MockSpan{Tags: make(map[string]interface{})}
|
||||
}
|
||||
func (n MockSpan) BaggageItem(key string) string { return "" }
|
||||
func (n MockSpan) SetTag(key string, value interface{}) opentracing.Span {
|
||||
func (n *MockSpan) BaggageItem(key string) string { return "" }
|
||||
func (n *MockSpan) SetTag(key string, value interface{}) opentracing.Span {
|
||||
n.Tags[key] = value
|
||||
return n
|
||||
}
|
||||
func (n MockSpan) LogFields(fields ...log.Field) {}
|
||||
func (n MockSpan) LogKV(keyVals ...interface{}) {}
|
||||
func (n MockSpan) Finish() {}
|
||||
func (n MockSpan) FinishWithOptions(opts opentracing.FinishOptions) {}
|
||||
func (n MockSpan) SetOperationName(operationName string) opentracing.Span { return n }
|
||||
func (n MockSpan) Tracer() opentracing.Tracer { return MockTracer{} }
|
||||
func (n MockSpan) LogEvent(event string) {}
|
||||
func (n MockSpan) LogEventWithPayload(event string, payload interface{}) {}
|
||||
func (n MockSpan) Log(data opentracing.LogData) {}
|
||||
func (n *MockSpan) LogFields(fields ...log.Field) {}
|
||||
func (n *MockSpan) LogKV(keyVals ...interface{}) {}
|
||||
func (n *MockSpan) Finish() {}
|
||||
func (n *MockSpan) FinishWithOptions(opts opentracing.FinishOptions) {}
|
||||
func (n *MockSpan) SetOperationName(operationName string) opentracing.Span { return n }
|
||||
func (n *MockSpan) Tracer() opentracing.Tracer { return MockTracer{} }
|
||||
func (n *MockSpan) LogEvent(event string) {}
|
||||
func (n *MockSpan) LogEventWithPayload(event string, payload interface{}) {}
|
||||
func (n *MockSpan) Log(data opentracing.LogData) {}
|
||||
func (n *MockSpan) Reset() {
|
||||
n.Tags = make(map[string]interface{})
|
||||
}
|
||||
|
@@ -102,7 +102,7 @@ func NewMuxer() (*Muxer, error) {
|
||||
|
||||
// Match returns the handler of the first route matching the connection metadata,
|
||||
// and whether the match is exactly from the rule HostSNI(*).
|
||||
func (m Muxer) Match(meta ConnData) (tcp.Handler, bool) {
|
||||
func (m *Muxer) Match(meta ConnData) (tcp.Handler, bool) {
|
||||
for _, route := range m.routes {
|
||||
if route.matchers.match(meta) {
|
||||
return route.handler, route.catchAll
|
||||
|
@@ -1,6 +1,7 @@
|
||||
package acme
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"os"
|
||||
@@ -22,9 +23,9 @@ type LocalStore struct {
|
||||
}
|
||||
|
||||
// NewLocalStore initializes a new LocalStore with a file name.
|
||||
func NewLocalStore(filename string) *LocalStore {
|
||||
func NewLocalStore(filename string, routinesPool *safe.Pool) *LocalStore {
|
||||
store := &LocalStore{filename: filename, saveDataChan: make(chan map[string]*StoredData)}
|
||||
store.listenSaveAction()
|
||||
store.listenSaveAction(routinesPool)
|
||||
return store
|
||||
}
|
||||
|
||||
@@ -99,18 +100,31 @@ func (s *LocalStore) get(resolverName string) (*StoredData, error) {
|
||||
}
|
||||
|
||||
// listenSaveAction listens to a chan to store ACME data in json format into `LocalStore.filename`.
|
||||
func (s *LocalStore) listenSaveAction() {
|
||||
safe.Go(func() {
|
||||
func (s *LocalStore) listenSaveAction(routinesPool *safe.Pool) {
|
||||
routinesPool.GoCtx(func(ctx context.Context) {
|
||||
logger := log.WithoutContext().WithField(log.ProviderName, "acme")
|
||||
for object := range s.saveDataChan {
|
||||
data, err := json.MarshalIndent(object, "", " ")
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
}
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
|
||||
err = os.WriteFile(s.filename, data, 0o600)
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
case object := <-s.saveDataChan:
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
// Stop handling events because Traefik is shutting down.
|
||||
return
|
||||
default:
|
||||
}
|
||||
|
||||
data, err := json.MarshalIndent(object, "", " ")
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
}
|
||||
|
||||
err = os.WriteFile(s.filename, data, 0o600)
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
@@ -1,6 +1,7 @@
|
||||
package acme
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -9,6 +10,7 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/traefik/traefik/v2/pkg/safe"
|
||||
)
|
||||
|
||||
func TestLocalStore_GetAccount(t *testing.T) {
|
||||
@@ -45,7 +47,7 @@ func TestLocalStore_GetAccount(t *testing.T) {
|
||||
|
||||
for _, test := range testCases {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
s := NewLocalStore(test.filename)
|
||||
s := NewLocalStore(test.filename, safe.NewPool(context.Background()))
|
||||
|
||||
account, err := s.GetAccount("test")
|
||||
require.NoError(t, err)
|
||||
@@ -58,7 +60,7 @@ func TestLocalStore_GetAccount(t *testing.T) {
|
||||
func TestLocalStore_SaveAccount(t *testing.T) {
|
||||
acmeFile := filepath.Join(t.TempDir(), "acme.json")
|
||||
|
||||
s := NewLocalStore(acmeFile)
|
||||
s := NewLocalStore(acmeFile, safe.NewPool(context.Background()))
|
||||
|
||||
email := "some@email.com"
|
||||
|
||||
|
@@ -298,19 +298,9 @@ func (p *Provider) getClient() (*lego.Client, error) {
|
||||
}
|
||||
|
||||
err = client.Challenge.SetDNS01Provider(provider,
|
||||
dns01.CondOption(len(p.DNSChallenge.Resolvers) > 0, dns01.AddRecursiveNameservers(p.DNSChallenge.Resolvers)),
|
||||
dns01.WrapPreCheck(func(domain, fqdn, value string, check dns01.PreCheckFunc) (bool, error) {
|
||||
if p.DNSChallenge.DelayBeforeCheck > 0 {
|
||||
logger.Debugf("Delaying %d rather than validating DNS propagation now.", p.DNSChallenge.DelayBeforeCheck)
|
||||
time.Sleep(time.Duration(p.DNSChallenge.DelayBeforeCheck))
|
||||
}
|
||||
|
||||
if p.DNSChallenge.DisablePropagationCheck {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return check(fqdn, value)
|
||||
}),
|
||||
dns01.CondOption(len(p.DNSChallenge.Resolvers) > 0,
|
||||
dns01.AddRecursiveNameservers(p.DNSChallenge.Resolvers)),
|
||||
dns01.PropagationWait(time.Duration(p.DNSChallenge.DelayBeforeCheck), p.DNSChallenge.DisablePropagationCheck),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -421,8 +411,7 @@ func (p *Provider) watchNewDomains(ctx context.Context) {
|
||||
|
||||
if len(route.TLS.Domains) > 0 {
|
||||
domains := deleteUnnecessaryDomains(ctxRouter, route.TLS.Domains)
|
||||
for i := range len(domains) {
|
||||
domain := domains[i]
|
||||
for _, domain := range domains {
|
||||
safe.Go(func() {
|
||||
dom, cert, err := p.resolveCertificate(ctx, domain, traefiktls.DefaultTLSStoreName)
|
||||
if err != nil {
|
||||
@@ -458,8 +447,7 @@ func (p *Provider) watchNewDomains(ctx context.Context) {
|
||||
|
||||
if len(route.TLS.Domains) > 0 {
|
||||
domains := deleteUnnecessaryDomains(ctxRouter, route.TLS.Domains)
|
||||
for i := range len(domains) {
|
||||
domain := domains[i]
|
||||
for _, domain := range domains {
|
||||
safe.Go(func() {
|
||||
dom, cert, err := p.resolveCertificate(ctx, domain, traefiktls.DefaultTLSStoreName)
|
||||
if err != nil {
|
||||
@@ -552,8 +540,11 @@ func (p *Provider) resolveDefaultCertificate(ctx context.Context, domains []stri
|
||||
|
||||
p.resolvingDomainsMutex.Lock()
|
||||
|
||||
sort.Strings(domains)
|
||||
domainKey := strings.Join(domains, ",")
|
||||
sortedDomains := make([]string, len(domains))
|
||||
copy(sortedDomains, domains)
|
||||
sort.Strings(sortedDomains)
|
||||
|
||||
domainKey := strings.Join(sortedDomains, ",")
|
||||
|
||||
if _, ok := p.resolvingDomains[domainKey]; ok {
|
||||
p.resolvingDomainsMutex.Unlock()
|
||||
@@ -947,12 +938,14 @@ func (p *Provider) certExists(validDomains []string) bool {
|
||||
p.certificatesMu.RLock()
|
||||
defer p.certificatesMu.RUnlock()
|
||||
|
||||
sort.Strings(validDomains)
|
||||
sortedDomains := make([]string, len(validDomains))
|
||||
copy(sortedDomains, validDomains)
|
||||
sort.Strings(sortedDomains)
|
||||
|
||||
for _, cert := range p.certificates {
|
||||
domains := cert.Certificate.Domain.ToStrArray()
|
||||
sort.Strings(domains)
|
||||
if reflect.DeepEqual(domains, validDomains) {
|
||||
if reflect.DeepEqual(domains, sortedDomains) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
@@ -67,8 +67,8 @@ type ProviderAggregator struct {
|
||||
}
|
||||
|
||||
// NewProviderAggregator returns an aggregate of all the providers configured in the static configuration.
|
||||
func NewProviderAggregator(conf static.Providers) ProviderAggregator {
|
||||
p := ProviderAggregator{
|
||||
func NewProviderAggregator(conf static.Providers) *ProviderAggregator {
|
||||
p := &ProviderAggregator{
|
||||
providersThrottleDuration: time.Duration(conf.ProvidersThrottleDuration),
|
||||
}
|
||||
|
||||
@@ -172,12 +172,12 @@ func (p *ProviderAggregator) AddProvider(provider provider.Provider) error {
|
||||
}
|
||||
|
||||
// Init the provider.
|
||||
func (p ProviderAggregator) Init() error {
|
||||
func (p *ProviderAggregator) Init() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Provide calls the provide method of every providers.
|
||||
func (p ProviderAggregator) Provide(configurationChan chan<- dynamic.Message, pool *safe.Pool) error {
|
||||
func (p *ProviderAggregator) Provide(configurationChan chan<- dynamic.Message, pool *safe.Pool) error {
|
||||
if p.fileProvider != nil {
|
||||
p.launchProvider(configurationChan, pool, p.fileProvider)
|
||||
}
|
||||
@@ -197,7 +197,7 @@ func (p ProviderAggregator) Provide(configurationChan chan<- dynamic.Message, po
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p ProviderAggregator) launchProvider(configurationChan chan<- dynamic.Message, pool *safe.Pool, prd provider.Provider) {
|
||||
func (p *ProviderAggregator) launchProvider(configurationChan chan<- dynamic.Message, pool *safe.Pool, prd provider.Provider) {
|
||||
jsonConf, err := redactor.RemoveCredentials(prd)
|
||||
if err != nil {
|
||||
log.WithoutContext().Debugf("Cannot marshal the provider configuration %T: %v", prd, err)
|
||||
|
@@ -3,7 +3,9 @@ package provider
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"maps"
|
||||
"reflect"
|
||||
"slices"
|
||||
"sort"
|
||||
"strings"
|
||||
"text/template"
|
||||
@@ -13,7 +15,6 @@ import (
|
||||
"github.com/traefik/traefik/v2/pkg/config/dynamic"
|
||||
"github.com/traefik/traefik/v2/pkg/log"
|
||||
"github.com/traefik/traefik/v2/pkg/tls"
|
||||
"golang.org/x/exp/maps"
|
||||
)
|
||||
|
||||
// Merge merges multiple configurations.
|
||||
@@ -384,7 +385,7 @@ func BuildTCPRouterConfiguration(ctx context.Context, configuration *dynamic.TCP
|
||||
if len(configuration.Services) > 1 {
|
||||
delete(configuration.Routers, routerName)
|
||||
loggerRouter.
|
||||
Errorf("Router %s cannot be linked automatically with multiple Services: %q", routerName, maps.Keys(configuration.Services))
|
||||
Errorf("Router %s cannot be linked automatically with multiple Services: %q", routerName, slices.Collect(maps.Keys(configuration.Services)))
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -406,7 +407,7 @@ func BuildUDPRouterConfiguration(ctx context.Context, configuration *dynamic.UDP
|
||||
if len(configuration.Services) > 1 {
|
||||
delete(configuration.Routers, routerName)
|
||||
loggerRouter.
|
||||
Errorf("Router %s cannot be linked automatically with multiple Services: %q", routerName, maps.Keys(configuration.Services))
|
||||
Errorf("Router %s cannot be linked automatically with multiple Services: %q", routerName, slices.Collect(maps.Keys(configuration.Services)))
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -453,7 +454,7 @@ func BuildRouterConfiguration(ctx context.Context, configuration *dynamic.HTTPCo
|
||||
if len(configuration.Services) > 1 {
|
||||
delete(configuration.Routers, routerName)
|
||||
loggerRouter.
|
||||
Errorf("Router %s cannot be linked automatically with multiple Services: %q", routerName, maps.Keys(configuration.Services))
|
||||
Errorf("Router %s cannot be linked automatically with multiple Services: %q", routerName, slices.Collect(maps.Keys(configuration.Services)))
|
||||
continue
|
||||
}
|
||||
|
||||
|
@@ -13,8 +13,7 @@ import (
|
||||
"github.com/traefik/traefik/v2/pkg/types"
|
||||
)
|
||||
|
||||
func Int(v int) *int { return &v }
|
||||
func Bool(v bool) *bool { return &v }
|
||||
func pointer[T any](v T) *T { return &v }
|
||||
|
||||
func TestDefaultRule(t *testing.T) {
|
||||
testCases := []struct {
|
||||
@@ -64,7 +63,7 @@ func TestDefaultRule(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -118,7 +117,7 @@ func TestDefaultRule(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -164,7 +163,7 @@ func TestDefaultRule(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -210,7 +209,7 @@ func TestDefaultRule(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -262,7 +261,7 @@ func TestDefaultRule(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -350,7 +349,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -407,7 +406,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "https://127.0.0.1:443",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
ServersTransport: "tls-ns-dc1-dev-Test",
|
||||
},
|
||||
},
|
||||
@@ -497,7 +496,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "https://127.0.0.2:444",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
ServersTransport: "tls-ns-dc1-dev-Test",
|
||||
},
|
||||
},
|
||||
@@ -578,7 +577,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
"Test2": {
|
||||
@@ -588,7 +587,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.2:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -651,7 +650,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.2:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -711,7 +710,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.2:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -774,7 +773,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.2:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -826,7 +825,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -879,7 +878,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -924,7 +923,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -982,7 +981,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1030,7 +1029,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
"Service2": {
|
||||
@@ -1040,7 +1039,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1216,7 +1215,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.2:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1267,7 +1266,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1344,7 +1343,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.2:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1408,7 +1407,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.2:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1488,7 +1487,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.3:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1548,7 +1547,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.2:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1622,7 +1621,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.3:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1688,7 +1687,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.2:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1740,7 +1739,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1793,7 +1792,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "h2c://127.0.0.1:8080",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1840,7 +1839,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
"Service2": {
|
||||
@@ -1850,7 +1849,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:8080",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2074,7 +2073,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2137,7 +2136,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2188,7 +2187,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
Address: "127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
TerminationDelay: Int(100),
|
||||
TerminationDelay: pointer(100),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2241,7 +2240,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
Address: "127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
TerminationDelay: Int(100),
|
||||
TerminationDelay: pointer(100),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2337,7 +2336,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
Address: "127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
TerminationDelay: Int(100),
|
||||
TerminationDelay: pointer(100),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2393,7 +2392,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
Address: "127.0.0.1:8080",
|
||||
},
|
||||
},
|
||||
TerminationDelay: Int(100),
|
||||
TerminationDelay: pointer(100),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2515,7 +2514,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
Address: "127.0.0.2:80",
|
||||
},
|
||||
},
|
||||
TerminationDelay: Int(100),
|
||||
TerminationDelay: pointer(100),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2544,7 +2543,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.2:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2631,7 +2630,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.2:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2668,7 +2667,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
Address: "127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
TerminationDelay: Int(100),
|
||||
TerminationDelay: pointer(100),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2760,7 +2759,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
Address: "127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
TerminationDelay: Int(200),
|
||||
TerminationDelay: pointer(200),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2844,7 +2843,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "https://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
ServersTransport: "tls-ns-dc1-Test",
|
||||
},
|
||||
},
|
||||
@@ -2855,7 +2854,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "https://127.0.0.2:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
ServersTransport: "tls-ns-dc1-Test",
|
||||
},
|
||||
},
|
||||
@@ -2933,7 +2932,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
Servers: []dynamic.TCPServer{
|
||||
{Address: "127.0.0.1:80"},
|
||||
},
|
||||
TerminationDelay: Int(100),
|
||||
TerminationDelay: pointer(100),
|
||||
},
|
||||
},
|
||||
"Test-17573747155436217342": {
|
||||
@@ -2941,7 +2940,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
Servers: []dynamic.TCPServer{
|
||||
{Address: "127.0.0.2:80"},
|
||||
},
|
||||
TerminationDelay: Int(100),
|
||||
TerminationDelay: pointer(100),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -3136,7 +3135,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@@ -307,7 +307,7 @@ func (p *Provider) getIPPort(ctx context.Context, container dockerData, serverPo
|
||||
return ip, port, nil
|
||||
}
|
||||
|
||||
func (p Provider) getIPAddress(ctx context.Context, container dockerData) string {
|
||||
func (p *Provider) getIPAddress(ctx context.Context, container dockerData) string {
|
||||
logger := log.FromContext(ctx)
|
||||
|
||||
netNotFound := false
|
||||
|
@@ -71,7 +71,7 @@ func TestDefaultRule(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -130,7 +130,7 @@ func TestDefaultRule(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -191,7 +191,7 @@ func TestDefaultRule(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -244,7 +244,7 @@ func TestDefaultRule(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -297,7 +297,7 @@ func TestDefaultRule(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -356,7 +356,7 @@ func TestDefaultRule(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -579,7 +579,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -658,7 +658,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
"Test2": {
|
||||
@@ -668,7 +668,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.2:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -747,7 +747,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.2:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -807,7 +807,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -868,7 +868,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -921,7 +921,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -987,7 +987,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1043,7 +1043,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
"Service2": {
|
||||
@@ -1053,7 +1053,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1113,7 +1113,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1353,7 +1353,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.2:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1412,7 +1412,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1508,7 +1508,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.2:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1591,7 +1591,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.2:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1696,7 +1696,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.3:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1773,7 +1773,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.2:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1872,7 +1872,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.3:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1954,7 +1954,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.2:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2026,7 +2026,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
"Test2": {
|
||||
@@ -2036,7 +2036,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.2:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2096,7 +2096,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2157,7 +2157,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "h2c://127.0.0.1:8080",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2212,7 +2212,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
"Service2": {
|
||||
@@ -2222,7 +2222,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:8080",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2418,7 +2418,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
Services: map[string]*dynamic.Service{
|
||||
"Test": {
|
||||
LoadBalancer: &dynamic.ServersLoadBalancer{
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2487,7 +2487,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
Services: map[string]*dynamic.TCPService{
|
||||
"Test": {
|
||||
LoadBalancer: &dynamic.TCPServersLoadBalancer{
|
||||
TerminationDelay: Int(100),
|
||||
TerminationDelay: pointer(100),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2676,7 +2676,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2747,7 +2747,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2806,7 +2806,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
Address: "127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
TerminationDelay: Int(100),
|
||||
TerminationDelay: pointer(100),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2867,7 +2867,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
Address: "127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
TerminationDelay: Int(100),
|
||||
TerminationDelay: pointer(100),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2979,7 +2979,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
Address: "127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
TerminationDelay: Int(100),
|
||||
TerminationDelay: pointer(100),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -3043,7 +3043,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
Address: "127.0.0.1:8080",
|
||||
},
|
||||
},
|
||||
TerminationDelay: Int(100),
|
||||
TerminationDelay: pointer(100),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -3215,7 +3215,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.2:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -3314,7 +3314,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
Address: "127.0.0.1:8080",
|
||||
},
|
||||
},
|
||||
TerminationDelay: Int(200),
|
||||
TerminationDelay: pointer(200),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -3391,7 +3391,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://192.168.0.1:8081",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -3451,7 +3451,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:79",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -3958,6 +3958,4 @@ func TestSwarmGetPort(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func Int(v int) *int { return &v }
|
||||
|
||||
func Bool(v bool) *bool { return &v }
|
||||
func pointer[T any](v T) *T { return &v }
|
||||
|
@@ -12,8 +12,7 @@ import (
|
||||
"github.com/traefik/traefik/v2/pkg/types"
|
||||
)
|
||||
|
||||
func Int(v int) *int { return &v }
|
||||
func Bool(v bool) *bool { return &v }
|
||||
func pointer[T any](v T) *T { return &v }
|
||||
|
||||
func TestDefaultRule(t *testing.T) {
|
||||
testCases := []struct {
|
||||
@@ -66,7 +65,7 @@ func TestDefaultRule(t *testing.T) {
|
||||
URL: "http://10.0.0.1:1337",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -120,7 +119,7 @@ func TestDefaultRule(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -176,7 +175,7 @@ func TestDefaultRule(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -224,7 +223,7 @@ func TestDefaultRule(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -272,7 +271,7 @@ func TestDefaultRule(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -326,7 +325,7 @@ func TestDefaultRule(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -528,7 +527,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -597,7 +596,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
"Test2": {
|
||||
@@ -607,7 +606,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.2:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -676,7 +675,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.2:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -731,7 +730,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -787,7 +786,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -835,7 +834,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -896,7 +895,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -947,7 +946,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
"Service2": {
|
||||
@@ -957,7 +956,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1012,7 +1011,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1217,7 +1216,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.2:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1271,7 +1270,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1357,7 +1356,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.2:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1430,7 +1429,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.2:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1520,7 +1519,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.3:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1587,7 +1586,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.2:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1671,7 +1670,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.3:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1744,7 +1743,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.2:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1806,7 +1805,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
"Test2": {
|
||||
@@ -1816,7 +1815,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.2:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1871,7 +1870,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1927,7 +1926,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "h2c://127.0.0.1:8080",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1983,7 +1982,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "h2c://127.0.0.1:8040",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2047,7 +2046,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:32124",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
"Service2": {
|
||||
@@ -2057,7 +2056,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:32123",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2107,7 +2106,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
"Service2": {
|
||||
@@ -2117,7 +2116,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:8080",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2397,7 +2396,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2463,7 +2462,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2517,7 +2516,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
Address: "127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
TerminationDelay: Int(100),
|
||||
TerminationDelay: pointer(100),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2573,7 +2572,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
Address: "127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
TerminationDelay: Int(100),
|
||||
TerminationDelay: pointer(100),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2675,7 +2674,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
Address: "127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
TerminationDelay: Int(100),
|
||||
TerminationDelay: pointer(100),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2734,7 +2733,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
Address: "127.0.0.1:8080",
|
||||
},
|
||||
},
|
||||
TerminationDelay: Int(100),
|
||||
TerminationDelay: pointer(100),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2891,7 +2890,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.2:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2980,7 +2979,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
Address: "127.0.0.1:8080",
|
||||
},
|
||||
},
|
||||
TerminationDelay: Int(200),
|
||||
TerminationDelay: pointer(200),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -3046,7 +3045,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||
URL: "http://127.0.0.1:80",
|
||||
},
|
||||
},
|
||||
PassHostHeader: Bool(true),
|
||||
PassHostHeader: pointer(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2024 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2024 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2024 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2024 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2024 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2024 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2024 Traefik Labs
|
||||
Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user