diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md deleted file mode 100644 index b4717dc18..000000000 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ /dev/null @@ -1,36 +0,0 @@ - - -### What does this PR do? - - - - -### Motivation - - - - -### More - -- [ ] Added/updated tests -- [ ] Added/updated documentation - -### Additional Notes - - diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml deleted file mode 100644 index a6e2e15e3..000000000 --- a/.github/workflows/build.yaml +++ /dev/null @@ -1,81 +0,0 @@ -name: Build Binaries - -on: - pull_request: - branches: - - '*' - paths-ignore: - - 'docs/**' - - '**.md' - - 'script/gcg/**' - -env: - GO_VERSION: '1.24' - CGO_ENABLED: 0 - -jobs: - - build-webui: - uses: ./.github/workflows/template-webui.yaml - - build: - runs-on: ubuntu-latest - - strategy: - matrix: - os: [ darwin, freebsd, linux, openbsd, windows ] - arch: [ amd64, arm64 ] - include: - - os: freebsd - arch: 386 - - os: linux - arch: 386 - - os: linux - arch: arm - goarm: 6 - - os: linux - arch: arm - goarm: 7 - - os: linux - arch: ppc64le - - os: linux - arch: riscv64 - - os: linux - arch: s390x - - os: openbsd - arch: 386 - - os: windows - arch: 386 - needs: - - build-webui - - steps: - - name: Check out code - uses: actions/checkout@v5 - with: - fetch-depth: 0 - - - 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: 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 - env: - GOOS: ${{ matrix.os }} - GOARCH: ${{ matrix.arch }} - GOARM: ${{ matrix.goarm }} - run: make binary diff --git a/.github/workflows/check_doc.yml b/.github/workflows/check_doc.yml deleted file mode 100644 index 48afa3298..000000000 --- a/.github/workflows/check_doc.yml +++ /dev/null @@ -1,25 +0,0 @@ -name: Check Documentation - -on: - pull_request: - branches: - - '*' - -jobs: - - docs: - name: Check, verify and build documentation - runs-on: ubuntu-latest - - steps: - - name: Check out code - uses: actions/checkout@v5 - with: - fetch-depth: 0 - - - name: Check documentation - run: make docs-pull-images docs - env: - # These variables are not passed to workflows that are triggered by a pull request from a fork. - DOCS_VERIFY_SKIP: ${{ vars.DOCS_VERIFY_SKIP }} - DOCS_LINT_SKIP: ${{ vars.DOCS_LINT_SKIP }} diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml deleted file mode 100644 index 30ab2221b..000000000 --- a/.github/workflows/codeql.yml +++ /dev/null @@ -1,70 +0,0 @@ -name: "CodeQL" - -on: - push: - branches: - - master - - v* - schedule: - - cron: '11 22 * * 1' - -jobs: - analyze: - name: Analyze - runs-on: ubuntu-latest - permissions: - actions: read - contents: read - security-events: write - - strategy: - fail-fast: false - matrix: - language: [ 'javascript', 'go' ] - # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] - # Use only 'java' to analyze code written in Java, Kotlin or both - # Use only 'javascript' to analyze code written in JavaScript, TypeScript or both - # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support - - steps: - - name: Checkout repository - uses: actions/checkout@v5 - - - name: setup go - uses: actions/setup-go@v5 - if: ${{ matrix.language == 'go' }} - with: - go-version-file: 'go.mod' - - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL - uses: github/codeql-action/init@v3 - with: - languages: ${{ matrix.language }} - # If you wish to specify custom queries, you can do so here or in a config file. - # By default, queries listed here will override any specified in a config file. - # Prefix the list here with "+" to use these queries and those in the config file. - - # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs - # queries: security-extended,security-and-quality - - - # Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift). - # If this step fails, then you should remove it and run the build manually (see below) - - name: Autobuild - uses: github/codeql-action/autobuild@v3 - - # â„šī¸ Command-line programs to run using the OS shell. - # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun - - # If the Autobuild fails above, remove it and uncomment the following three lines. - # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. - - # - run: | - # echo "Run, Build Application using script" - # ./location_of_script_within_repo/buildscript.sh - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v3 - with: - category: "/language:${{matrix.language}}" diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yml deleted file mode 100644 index 4daba089b..000000000 --- a/.github/workflows/documentation.yml +++ /dev/null @@ -1,53 +0,0 @@ -name: Build and Publish Documentation - -on: - workflow_dispatch: {} - push: - branches: - - master - - v* - -env: - STRUCTOR_VERSION: v1.13.2 - MIXTUS_VERSION: v0.4.1 - -jobs: - - docs: - name: Doc Process - runs-on: ubuntu-latest - if: github.repository == 'traefik/traefik' - - steps: - - name: Check out code - uses: actions/checkout@v5 - with: - fetch-depth: 0 - - - name: Login to DockerHub - uses: docker/login-action@v3 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - - name: Install Structor ${{ env.STRUCTOR_VERSION }} - run: curl -sSfL https://raw.githubusercontent.com/traefik/structor/master/godownloader.sh | sh -s -- -b $HOME/bin ${STRUCTOR_VERSION} - - - name: Install Seo-doc - run: curl -sSfL https://raw.githubusercontent.com/traefik/seo-doc/master/godownloader.sh | sh -s -- -b "${HOME}/bin" - - - name: Install Mixtus ${{ env.MIXTUS_VERSION }} - run: curl -sSfL https://raw.githubusercontent.com/traefik/mixtus/master/godownloader.sh | sh -s -- -b $HOME/bin ${MIXTUS_VERSION} - - - name: Build documentation - run: | - STRUCTOR_LATEST_TAG=$(curl -s https://api.github.com/repos/traefik/traefik/releases/latest | jq -r '.tag_name') - $HOME/bin/structor -o traefik -r traefik --dockerfile-url="https://raw.githubusercontent.com/traefik/traefik/v1.7/docs.Dockerfile" --menu.js-url="https://raw.githubusercontent.com/traefik/structor/master/traefik-menu.js.gotmpl" --rqts-url="https://raw.githubusercontent.com/traefik/structor/master/requirements-override.txt" --force-edit-url --exp-branch=master --debug - - - name: Apply seo - run: $HOME/bin/seo -path=./site -product=traefik - - - name: Publish documentation - run: $HOME/bin/mixtus --dst-doc-path="./traefik" --dst-owner=traefik --dst-repo-name=doc --git-user-email="30906710+traefiker@users.noreply.github.com" --git-user-name=traefiker --src-doc-path="./site" --src-owner=traefik --src-repo-name=traefik - env: - GITHUB_TOKEN: ${{ secrets.GH_TOKEN_REPO }} diff --git a/.github/workflows/experimental.yaml b/.github/workflows/experimental.yaml deleted file mode 100644 index a56928fff..000000000 --- a/.github/workflows/experimental.yaml +++ /dev/null @@ -1,70 +0,0 @@ -name: Build experimental image on branch - -on: - push: - branches: - - master - - v* - -env: - GO_VERSION: '1.24' - CGO_ENABLED: 0 - -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: - - name: Check out code - uses: actions/checkout@v5 - with: - fetch-depth: 0 - - - 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 - - - name: Branch name - run: echo ${GITHUB_REF##*/} - - - name: Login to Docker Hub - uses: docker/login-action@v3 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 - - - 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" - run: | - make multi-arch-image-experimental-${GITHUB_REF##*/} diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml deleted file mode 100644 index db2c59183..000000000 --- a/.github/workflows/release.yaml +++ /dev/null @@ -1,136 +0,0 @@ -name: Release - -on: - push: - tags: - - 'v*.*.*' - -env: - GO_VERSION: '1.24' - CGO_ENABLED: 0 - VERSION: ${{ github.ref_name }} - TRAEFIKER_EMAIL: "traefiker@traefik.io" - CODENAME: chabichou - -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@v5 - 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@v5 - 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 .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} --latest=true - - ./script/deploy.sh - diff --git a/.github/workflows/template-webui.yaml b/.github/workflows/template-webui.yaml deleted file mode 100644 index b841c90f5..000000000 --- a/.github/workflows/template-webui.yaml +++ /dev/null @@ -1,40 +0,0 @@ -name: Build Web UI -on: - workflow_call: {} -jobs: - - build-webui: - runs-on: ubuntu-latest - - steps: - - name: Check out code - uses: actions/checkout@v5 - with: - fetch-depth: 0 - - - name: Enable corepack - run: corepack enable - - - 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 diff --git a/.github/workflows/test-conformance.yaml b/.github/workflows/test-conformance.yaml deleted file mode 100644 index 6373d8f92..000000000 --- a/.github/workflows/test-conformance.yaml +++ /dev/null @@ -1,36 +0,0 @@ -name: Test K8s Gateway API conformance - -on: - pull_request: - branches: - - '*' - paths: - - '.github/workflows/test-conformance.yaml' - - 'pkg/provider/kubernetes/gateway/**' - - 'integration/fixtures/k8s-conformance/**' - - 'integration/k8s_conformance_test.go' - -env: - GO_VERSION: '1.23' - CGO_ENABLED: 0 - -jobs: - - test-conformance: - 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 }} - - - name: K8s Gateway API conformance test and report - run: | - make test-gateway-api-conformance - git diff --exit-code diff --git a/.github/workflows/test-integration.yaml b/.github/workflows/test-integration.yaml deleted file mode 100644 index 0f7504c06..000000000 --- a/.github/workflows/test-integration.yaml +++ /dev/null @@ -1,99 +0,0 @@ -name: Test Integration - -on: - pull_request: - branches: - - '*' - paths-ignore: - - 'docs/**' - - '**.md' - - 'script/gcg/**' - -env: - GO_VERSION: '1.24' - CGO_ENABLED: 0 - -jobs: - - build: - runs-on: ubuntu-latest - - steps: - - name: Check out code - uses: actions/checkout@v5 - 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: Build binary - run: make binary-linux-amd64 - - - name: Save go cache build - uses: actions/cache/save@v4 - with: - path: | - ~/.cache/go-build - key: ${{ runner.os }}-go-build-cache-${{ env.GO_VERSION }}-${{ hashFiles('**/go.sum') }} - - - name: Artifact traefik binary - uses: actions/upload-artifact@v4 - with: - name: traefik - path: ./dist/linux/amd64/traefik - retention-days: 1 - - test-integration: - runs-on: ubuntu-latest - needs: - - build - strategy: - fail-fast: true - matrix: - parallel: [12] - index: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] - - steps: - - name: Check out code - uses: actions/checkout@v5 - 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: Download traefik binary - uses: actions/download-artifact@v4 - with: - name: traefik - path: ./dist/linux/amd64/ - - - name: Make binary executable - run: chmod +x ./dist/linux/amd64/traefik - - - name: Restore go cache build - uses: actions/cache/restore@v4 - with: - path: | - ~/.cache/go-build - key: ${{ runner.os }}-go-build-cache-${{ env.GO_VERSION }}-${{ hashFiles('**/go.sum') }} - - - name: Generate go test Slice - id: test_split - uses: hashicorp-forge/go-test-split-action@v2.0.0 - with: - packages: ./integration - total: ${{ matrix.parallel }} - index: ${{ matrix.index }} - - - name: Run Integration tests - run: | - TESTS=$(echo "${{ steps.test_split.outputs.run}}" | sed 's/\$/\$\$/g') - TESTFLAGS="-run \"${TESTS}\"" make test-integration diff --git a/.github/workflows/test-unit.yaml b/.github/workflows/test-unit.yaml deleted file mode 100644 index d8d612a51..000000000 --- a/.github/workflows/test-unit.yaml +++ /dev/null @@ -1,88 +0,0 @@ -name: Test Unit - -on: - pull_request: - branches: - - '*' - paths-ignore: - - 'docs/**' - - '**.md' - - 'script/gcg/**' - -env: - GO_VERSION: '1.24' - -jobs: - generate-packages: - name: List Go Packages - runs-on: ubuntu-latest - outputs: - matrix: ${{ steps.set-matrix.outputs.matrix }} - steps: - - name: Check out code - uses: actions/checkout@v5 - 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: Generate matrix - id: set-matrix - run: | - matrix_output=$(go run ./internal/testsci/genmatrix.go) - echo "$matrix_output" - echo "$matrix_output" >> $GITHUB_OUTPUT - - test-unit: - runs-on: ubuntu-latest - needs: generate-packages - strategy: - matrix: - package: ${{ fromJson(needs.generate-packages.outputs.matrix) }} - - steps: - - name: Check out code - uses: actions/checkout@v5 - 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: Tests - run: | - go test -v -parallel 8 ${{ matrix.package.group }} - - test-ui-unit: - runs-on: ubuntu-latest - - steps: - - name: Check out code - uses: actions/checkout@v5 - with: - fetch-depth: 0 - - - name: Enable corepack - run: corepack enable - - - name: Set up Node.js ${{ env.NODE_VERSION }} - uses: actions/setup-node@v4 - with: - node-version-file: webui/.nvmrc - cache: 'yarn' - cache-dependency-path: webui/yarn.lock - - - name: UI unit tests - working-directory: ./webui - env: - VITE_APP_BASE_API_URL: "/api" - run: | - yarn install - yarn test:unit:ci diff --git a/.github/workflows/validate.yaml b/.github/workflows/validate.yaml deleted file mode 100644 index 470816bd5..000000000 --- a/.github/workflows/validate.yaml +++ /dev/null @@ -1,84 +0,0 @@ -name: Validate - -on: - pull_request: - branches: - - '*' - -env: - GO_VERSION: '1.24' - GOLANGCI_LINT_VERSION: v2.0.2 - MISSPELL_VERSION: v0.6.0 - -jobs: - - lint: - runs-on: ubuntu-latest - - steps: - - name: Check out code - uses: actions/checkout@v5 - 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@v7 - with: - version: "${{ env.GOLANGCI_LINT_VERSION }}" - - validate: - runs-on: ubuntu-latest - - steps: - - name: Check out code - uses: actions/checkout@v5 - 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: Install misspell ${{ env.MISSPELL_VERSION }} - run: curl -sfL https://raw.githubusercontent.com/golangci/misspell/HEAD/install-misspell.sh | sh -s -- -b $(go env GOPATH)/bin ${MISSPELL_VERSION} - - - name: Validate - run: make validate-files - - validate-generate: - runs-on: ubuntu-latest - - steps: - - name: Check out code - uses: actions/checkout@v5 - 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: go generate - run: | - make generate - git diff --exit-code - - - name: go mod tidy - run: | - go mod tidy - git diff --exit-code - - - name: make generate-crd - run: | - make generate-crd - git diff --exit-code diff --git a/.golangci.yml b/.golangci.yml index 25de20ba1..29a2f9a1c 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -245,10 +245,6 @@ linters: text: Function 'buildConstructor' has too many statements linters: - funlen - - path: pkg/provider/kubernetes/ingress-nginx/kubernetes.go - text: Function 'loadConfiguration' has too many statements - linters: - - funlen - path: pkg/tracing/haystack/logger.go linters: - goprintffuncname @@ -263,7 +259,7 @@ linters: - path: pkg/provider/kubernetes/(crd|gateway)/client.go linters: - interfacebloat - - path: pkg/observability/metrics/metrics.go + - path: pkg/metrics/metrics.go linters: - interfacebloat - path: integration/healthcheck_test.go @@ -308,6 +304,8 @@ linters: text: 'SA1019: cfg.(SSLRedirect|SSLTemporaryRedirect|SSLHost|SSLForceHost|FeaturePolicy) is deprecated' - path: (.+)\.go$ text: 'SA1019: c.Providers.(ConsulCatalog|Consul|Nomad).Namespace is deprecated' + - path: (.+)\.go$ + text: 'SA1019: dockertypes.ContainerNode is deprecated' - path: pkg/provider/kubernetes/crd/kubernetes.go text: "Function 'loadConfigurationFromCRD' has too many statements" linters: diff --git a/.semaphore/semaphore.yml b/.semaphore/semaphore.yml new file mode 100644 index 000000000..32f22b2b8 --- /dev/null +++ b/.semaphore/semaphore.yml @@ -0,0 +1,13 @@ +version: v1.0 +name: Traefik Release - deprecated +agent: + machine: + type: f1-standard-2 + os_image: ubuntu2204 +blocks: + - name: 'Do nothing' + task: + jobs: + - name: 'Do nothing' + commands: + - echo "Do nothing" diff --git a/CHANGELOG.md b/CHANGELOG.md index bf46d9ae7..4ce5dde11 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,305 +1,3 @@ -## [v3.5.4](https://github.com/traefik/traefik/tree/v3.5.4) (2025-10-28) -[All Commits](https://github.com/traefik/traefik/compare/v3.5.3...v3.5.4) - -**Bug fixes:** -- **[acme]** Bump github.com/go-acme/lego/v4 to v4.27.0 ([#12166](https://github.com/traefik/traefik/pull/12166) by [ldez](https://github.com/ldez)) -- **[acme]** Bump github.com/go-acme/lego/v4 to v4.26.0 ([#12063](https://github.com/traefik/traefik/pull/12063) by [ldez](https://github.com/ldez)) -- **[http3]** Bump github.com/quic-go/quic-go to v0.55.0 ([#12121](https://github.com/traefik/traefik/pull/12121) by [GreyXor](https://github.com/GreyXor)) -- **[kv]** Bump github.com/kvtools/etcdv3 to v1.0.3 ([#12163](https://github.com/traefik/traefik/pull/12163) by [kevinpollet](https://github.com/kevinpollet)) -- **[logs,metrics,tracing,accesslogs,otel]** Fix otel not working without USER ([#12103](https://github.com/traefik/traefik/pull/12103) by [mmatur](https://github.com/mmatur)) -- **[logs,otel]** Enable stdout logging when OTLP is enabled ([#12124](https://github.com/traefik/traefik/pull/12124) by [lbenguigui](https://github.com/lbenguigui)) -- **[metrics,otel]** Rename traefik_tls_certs_not_after_milliseconds to traefik_tls_certs_not_after_seconds ([#12141](https://github.com/traefik/traefik/pull/12141) by [shreealt](https://github.com/shreealt)) -- **[otel]** Update OpenTelemetry to v1.38.0 and semantic conventions to v1.37.0 ([#12099](https://github.com/traefik/traefik/pull/12099) by [rtribotte](https://github.com/rtribotte)) -- **[otel]** Do not fail when pod is not found in K8sAttributesDetector ([#12096](https://github.com/traefik/traefik/pull/12096) by [xe-leon](https://github.com/xe-leon)) -- **[tls]** Make the staple updates continuous ([#12142](https://github.com/traefik/traefik/pull/12142) by [rtribotte](https://github.com/rtribotte)) -- **[webui]** Fix version display regression ([#12111](https://github.com/traefik/traefik/pull/12111) by [mdeliatf](https://github.com/mdeliatf)) - -**Documentation:** -- **[accesslogs]** Fix link for accesslog.fields.names in documentation ([#12094](https://github.com/traefik/traefik/pull/12094) by [rmbruntz](https://github.com/rmbruntz)) -- **[acme]** Fix Hetzner env var name inside documentation ([#12187](https://github.com/traefik/traefik/pull/12187) by [ldez](https://github.com/ldez)) -- **[acme]** Replace internal dead links ([#12152](https://github.com/traefik/traefik/pull/12152) by [rtribotte](https://github.com/rtribotte)) -- **[acme]** Clean and avoid collisions of anchors in option tables ([#12149](https://github.com/traefik/traefik/pull/12149) by [rtribotte](https://github.com/rtribotte)) -- **[docker/swarm]** Fix swarm code example ([#12115](https://github.com/traefik/traefik/pull/12115) by [Dr4K4n](https://github.com/Dr4K4n)) -- **[healthcheck]** Clarify health check requirement for server status metric ([#12201](https://github.com/traefik/traefik/pull/12201) by [asafm](https://github.com/asafm)) -- **[k8s/crd]** Fix typo in ingressroute.md from pirority to priority ([#12112](https://github.com/traefik/traefik/pull/12112) by [miromichalicka](https://github.com/miromichalicka)) -- **[k8s]** Fix incorrect option and lint page ([#12108](https://github.com/traefik/traefik/pull/12108) by [sheddy-traefik](https://github.com/sheddy-traefik)) -- **[k8s]** Add API basePath documentation and fix broken links ([#12177](https://github.com/traefik/traefik/pull/12177) by [mloiseleur](https://github.com/mloiseleur)) -- **[k8s]** Merge TLSOption/TLSStore CRD documentation page ([#12164](https://github.com/traefik/traefik/pull/12164) by [nmengin](https://github.com/nmengin)) -- **[logs]** Clarify log.filePath behavior in documentation ([#12153](https://github.com/traefik/traefik/pull/12153) by [Alanxtl](https://github.com/Alanxtl)) -- **[metrics]** Fix incorrect addInternals configuration option ([#12118](https://github.com/traefik/traefik/pull/12118) by [shreealt](https://github.com/shreealt)) -- **[middleware]** Fix anchors in basic auth configuration options ([#12168](https://github.com/traefik/traefik/pull/12168) by [homersimpsons](https://github.com/homersimpsons)) -- **[middleware]** Fix PreserveRequestMethod casing ([#12122](https://github.com/traefik/traefik/pull/12122) by [LeTamanoir](https://github.com/LeTamanoir)) -- **[middleware]** Add missing reference docs for statusRewrites in errors middleware ([#12198](https://github.com/traefik/traefik/pull/12198) by [sevensolutions](https://github.com/sevensolutions)) -- **[middleware]** Document rejectStatusCode ([#12062](https://github.com/traefik/traefik/pull/12062) by [czocher](https://github.com/czocher)) -- **[otel]** Align documentation on default otlp endpoint ([#12151](https://github.com/traefik/traefik/pull/12151) by [mloiseleur](https://github.com/mloiseleur)) -- **[otel]** Fix metric name to metrics.otlp.grpc.insecure ([#12200](https://github.com/traefik/traefik/pull/12200) by [germainlefebvre4](https://github.com/germainlefebvre4)) -- **[server,k8s/crd,k8s]** Add dedicated pages for routers and fix doc links in CRDs ([#12119](https://github.com/traefik/traefik/pull/12119) by [rtribotte](https://github.com/rtribotte)) -- **[tls]** Fix typo in TLS certificate generation description ([#12185](https://github.com/traefik/traefik/pull/12185) by [iraj-jelo](https://github.com/iraj-jelo)) -- Fix wrong references to router's pages ([#12131](https://github.com/traefik/traefik/pull/12131) by [rtribotte](https://github.com/rtribotte)) -- Fix markdown rendering for table anchors ([#12129](https://github.com/traefik/traefik/pull/12129) by [MaBauMeBad](https://github.com/MaBauMeBad)) -- Fix provider option descriptions ([#12135](https://github.com/traefik/traefik/pull/12135) by [kevinpollet](https://github.com/kevinpollet)) -- Format HTTP headers as code ([#12105](https://github.com/traefik/traefik/pull/12105) by [tyilo](https://github.com/tyilo)) -- Fix heading levels and add links ([#12084](https://github.com/traefik/traefik/pull/12084) by [Granjow](https://github.com/Granjow)) - -**Misc:** -- Merge branch v2.11 into v3.5 ([#12206](https://github.com/traefik/traefik/pull/12206) by [kevinpollet](https://github.com/kevinpollet)) -- Merge branch v2.11 into v3.5 ([#12158](https://github.com/traefik/traefik/pull/12158) by [rtribotte](https://github.com/rtribotte)) - -## [v2.11.30](https://github.com/traefik/traefik/tree/v2.11.30) (2025-10-28) -[All Commits](https://github.com/traefik/traefik/compare/v2.11.29...v2.11.30) - -**Bug fixes:** -- **[http3]** Bump github.com/quic-go/quic-go to v0.55.0 ([#12156](https://github.com/traefik/traefik/pull/12156) by [kevinpollet](https://github.com/kevinpollet)) -- **[kv]** Fix KV key name used to check if connection is alive ([#12162](https://github.com/traefik/traefik/pull/12162) by [kevinpollet](https://github.com/kevinpollet)) -- **[server]** Bump golang.org/x/net to v0.46.0 ([#12143](https://github.com/traefik/traefik/pull/12143) by [kevinpollet](https://github.com/kevinpollet)) -- **[tracing]** Bump gopkg.in/DataDog/dd-trace-go.v1 to v1.74.6 ([#12083](https://github.com/traefik/traefik/pull/12083) by [hannahkm](https://github.com/hannahkm)) - -## [v3.5.3](https://github.com/traefik/traefik/tree/v3.5.3) (2025-09-26) -[All Commits](https://github.com/traefik/traefik/compare/v3.5.2...v3.5.3) - -**Bug fixes:** -- **[k8s/crd]** ServersTransport: set minimum MaxIdleConnsPerHost=-1 ([#12077](https://github.com/traefik/traefik/pull/12077) by [xe-leon](https://github.com/xe-leon)) -- **[plugins]** Refactor plugins system ([#12035](https://github.com/traefik/traefik/pull/12035) by [jspdown](https://github.com/jspdown)) -- **[server]** Use client conn to build the proxy protocol header ([#12069](https://github.com/traefik/traefik/pull/12069) by [rtribotte](https://github.com/rtribotte)) -- **[webui]** Update hub-button-app to use a local script ([#12060](https://github.com/traefik/traefik/pull/12060) by [mdeliatf](https://github.com/mdeliatf)) - -**Documentation:** -- **[acme,middleware]** Fix broken links in documentation ([#12057](https://github.com/traefik/traefik/pull/12057) by [mloiseleur](https://github.com/mloiseleur)) -- **[k8s]** Create Traefik Service CRD sub-resource documentation page ([#12080](https://github.com/traefik/traefik/pull/12080) by [nmengin](https://github.com/nmengin)) -- **[k8s]** Fix conflict in IngressRouteTCP documentation ([#12064](https://github.com/traefik/traefik/pull/12064) by [MatBon01](https://github.com/MatBon01)) -- Fix typo in rules and priority documentation ([#12089](https://github.com/traefik/traefik/pull/12089) by [Darkangeel-hd](https://github.com/Darkangeel-hd)) -- Add govern section ([#12067](https://github.com/traefik/traefik/pull/12067) by [sheddy-traefik](https://github.com/sheddy-traefik)) -- Fix entrypoint config examples ([#12056](https://github.com/traefik/traefik/pull/12056) by [markormesher](https://github.com/markormesher)) -- Reorganize the menu entries ([#12044](https://github.com/traefik/traefik/pull/12044) by [nmengin](https://github.com/nmengin)) -- Add New Secure Section to the Documentation ([#11978](https://github.com/traefik/traefik/pull/11978) by [sheddy-traefik](https://github.com/sheddy-traefik)) - -## [v3.5.2](https://github.com/traefik/traefik/tree/v3.5.2) (2025-09-09) -[All Commits](https://github.com/traefik/traefik/compare/v3.5.1...v3.5.2) - -**Bug fixes:** -- **[middleware,accesslogs]** Add GenericCLF log format for access logs ([#12033](https://github.com/traefik/traefik/pull/12033) by [sdelicata](https://github.com/sdelicata)) -- **[middleware]** Fix customerrors query url replacement ([#11876](https://github.com/traefik/traefik/pull/11876) by [DorianBlues](https://github.com/DorianBlues)) -- **[tls,service]** Send proxy protocol header before TLS handshake ([#11956](https://github.com/traefik/traefik/pull/11956) by [rtribotte](https://github.com/rtribotte)) -- **[webui]** Restore empty webui/static to use traefik as library ([#12025](https://github.com/traefik/traefik/pull/12025) by [youkoulayley](https://github.com/youkoulayley)) - -**Documentation:** -- **[accesslogs]** Fix path for access-logs header config ([#12030](https://github.com/traefik/traefik/pull/12030) by [cgatt](https://github.com/cgatt)) -- **[acme]** Fixes typo for OCSP in CLI example ([#12039](https://github.com/traefik/traefik/pull/12039) by [mloiseleur](https://github.com/mloiseleur)) -- **[docker/swarm]** Fixes typo for Swarm mode in CLI example ([#12038](https://github.com/traefik/traefik/pull/12038) by [BilalBudhani](https://github.com/BilalBudhani)) -- **[kv]** Fix broken links in KV store documentation ([#12040](https://github.com/traefik/traefik/pull/12040) by [sheddy-traefik](https://github.com/sheddy-traefik)) -- **[middleware]** Add redis options to ratelimit middleware & Include distributed rate limit middleware ([#12041](https://github.com/traefik/traefik/pull/12041) by [sheddy-traefik](https://github.com/sheddy-traefik)) -- **[server]** Fix link to HTTP3 section in documentation ([#12028](https://github.com/traefik/traefik/pull/12028) by [vincentbernat](https://github.com/vincentbernat)) -- Fix migration path in documentation ([#12032](https://github.com/traefik/traefik/pull/12032) by [nmengin](https://github.com/nmengin)) - -## [v3.5.1](https://github.com/traefik/traefik/tree/v3.5.1) (2025-08-27) -[All Commits](https://github.com/traefik/traefik/compare/v3.5.0...v3.5.1) - -**Bug fixes:** -- **[accesslogs,otel]** Provide Log Body in OTEL access Log ([#11867](https://github.com/traefik/traefik/pull/11867) by [tomMoulard](https://github.com/tomMoulard)) -- **[acme]** Bump github.com/go-acme/lego/v4 to v4.25.1 ([#11882](https://github.com/traefik/traefik/pull/11882) by [ldez](https://github.com/ldez)) -- **[k8s/gatewayapi]** Make app protocol case insensitive ([#11989](https://github.com/traefik/traefik/pull/11989) by [shreealt](https://github.com/shreealt)) -- **[otel]** Fix misspelling in docs ([#11952](https://github.com/traefik/traefik/pull/11952) by [mmanciop](https://github.com/mmanciop)) -- **[server]** Bump to github.com/pires/go-proxyproto v0.8.1 ([#11991](https://github.com/traefik/traefik/pull/11991) by [rtribotte](https://github.com/rtribotte)) -- **[server]** Silent expected errors on receiving sigterm signal ([#11838](https://github.com/traefik/traefik/pull/11838) by [Kwuray](https://github.com/Kwuray)) -- **[tracing]** Fix capturedRequestHeaders and capturedResponseHeaders headers options not being canonicalized in tracing ([#12005](https://github.com/traefik/traefik/pull/12005) by [mcuelenaere](https://github.com/mcuelenaere)) -- **[tracing]** Follow OTel semantic conventions for root span naming ([#11673](https://github.com/traefik/traefik/pull/11673) by [Alex-Waring](https://github.com/Alex-Waring)) -- **[webui]** Update Traefik Proxy dashboard UI development deps ([#11958](https://github.com/traefik/traefik/pull/11958) by [mdeliatf](https://github.com/mdeliatf)) -- Refactor to use reflect.TypeFor ([#12010](https://github.com/traefik/traefik/pull/12010) by [cuiweixie](https://github.com/cuiweixie)) - -**Documentation:** -- **[docker]** Fix missing middleware application for whoami service in docker guide ([#12012](https://github.com/traefik/traefik/pull/12012) by [Copilot](https://github.com/apps/copilot-swe-agent)) -- **[k8s/gatewayapi]** Fix documentation to match new gateway-api selector syntax ([#12006](https://github.com/traefik/traefik/pull/12006) by [Firespray-31](https://github.com/Firespray-31)) -- **[middleware,hub]** Add Traefik Hub Middlewares To Reference Section ([#11937](https://github.com/traefik/traefik/pull/11937) by [sheddy-traefik](https://github.com/sheddy-traefik)) -- **[plugins]** Add extend documentation ([#11904](https://github.com/traefik/traefik/pull/11904) by [sheddy-traefik](https://github.com/sheddy-traefik)) -- Update Broken Links in the Migration Docs ([#12016](https://github.com/traefik/traefik/pull/12016) by [sheddy-traefik](https://github.com/sheddy-traefik)) -- Fix Documentation menu ([#12013](https://github.com/traefik/traefik/pull/12013) by [nmengin](https://github.com/nmengin)) -- Fix invalid links in documentation ([#11995](https://github.com/traefik/traefik/pull/11995) by [mloiseleur](https://github.com/mloiseleur)) -- Fix typo in index ([#11994](https://github.com/traefik/traefik/pull/11994) by [ignyx](https://github.com/ignyx)) -- Restore missing migration section ([#11973](https://github.com/traefik/traefik/pull/11973) by [rtribotte](https://github.com/rtribotte)) -- Clean Documentation ([#11945](https://github.com/traefik/traefik/pull/11945) by [nmengin](https://github.com/nmengin)) -- Add back the link to Peka's page ([#11942](https://github.com/traefik/traefik/pull/11942) by [kevinpollet](https://github.com/kevinpollet)) - -**Misc:** -- Merge branch v2.11 into v3.5 ([#12019](https://github.com/traefik/traefik/pull/12019) by [rtribotte](https://github.com/rtribotte)) -- Merge branch v2.11 into v3.5 ([#12017](https://github.com/traefik/traefik/pull/12017) by [rtribotte](https://github.com/rtribotte)) -- Merge branch v2.11 into v3.5 ([#11966](https://github.com/traefik/traefik/pull/11966) by [kevinpollet](https://github.com/kevinpollet)) -- Merge branch v3.4 into v3.5 ([#11953](https://github.com/traefik/traefik/pull/11953) by [rtribotte](https://github.com/rtribotte)) - -## [v2.11.29](https://github.com/traefik/traefik/tree/v2.11.29) (2025-08-26) -[All Commits](https://github.com/traefik/traefik/compare/v2.11.28...v2.11.29) - -**Bug fixes:** -- **[acme]** Bump github.com/go-acme/lego/v4 to v4.25.2 ([#11983](https://github.com/traefik/traefik/pull/11983) by [ldez](https://github.com/ldez)) -- **[docker]** Bump github.com/docker/docker to v28.3.3 ([#12007](https://github.com/traefik/traefik/pull/12007) by [kevinpollet](https://github.com/kevinpollet)) - -**Documentation:** -- Fix invalid links in documentation ([#11960](https://github.com/traefik/traefik/pull/11960) by [mloiseleur](https://github.com/mloiseleur)) -- Update releases docs for v3.5 ([#11949](https://github.com/traefik/traefik/pull/11949) by [jnoordsij](https://github.com/jnoordsij)) - -## [v3.5.0](https://github.com/traefik/traefik/tree/v3.5.0) (2025-07-23) -[All Commits](https://github.com/traefik/traefik/compare/v3.5.0-rc1...v3.5.0) - -**Enhancements:** -- **[acme]** OCSP stapling ([#8393](https://github.com/traefik/traefik/pull/8393) by [alekitto](https://github.com/alekitto)) -- **[acme]** Add acme.httpChallenge.delay option ([#11643](https://github.com/traefik/traefik/pull/11643) by [ldez](https://github.com/ldez)) -- **[acme]** Allow configuration of ACME provider http timeout ([#11637](https://github.com/traefik/traefik/pull/11637) by [tkw1536](https://github.com/tkw1536)) -- **[healthcheck]** Add url option to healthcheck command ([#11711](https://github.com/traefik/traefik/pull/11711) by [Nelwhix](https://github.com/Nelwhix)) -- **[healthcheck]** Add unhealthy Interval to the health check configuration ([#10610](https://github.com/traefik/traefik/pull/10610) by [sswastik02](https://github.com/sswastik02)) -- **[k8s/gatewayapi]** Bump sigs.k8s.io/gateway-api to v1.3.0 ([#11719](https://github.com/traefik/traefik/pull/11719) by [rtribotte](https://github.com/rtribotte)) -- **[k8s/ingress]** Make the behavior of prefix matching in Ingress consistent with Kubernetes doc ([#11203](https://github.com/traefik/traefik/pull/11203) by [charlie0129](https://github.com/charlie0129)) -- **[k8s]** NGINX Ingress Provider ([#11844](https://github.com/traefik/traefik/pull/11844) by [rtribotte](https://github.com/rtribotte)) -- **[middleware,authentication]** Handle context canceled in ForwardAuth middleware ([#11817](https://github.com/traefik/traefik/pull/11817) by [bengentree](https://github.com/bengentree)) -- **[plugins]** Ability to enable unsafe in yaegi through plugin manifest ([#11589](https://github.com/traefik/traefik/pull/11589) by [Rydez](https://github.com/Rydez)) -- **[tls]** Introduce X25519MLKEM768 for Post-Quantum-Secure TLS ([#11731](https://github.com/traefik/traefik/pull/11731) by [fzoli](https://github.com/fzoli)) -- **[webui]** Migrate Traefik Proxy dashboard UI to React ([#11674](https://github.com/traefik/traefik/pull/11674) by [gndz07](https://github.com/gndz07)) -- **[webui]** Improve visualization for StatusRewrites option of errors middleware ([#11806](https://github.com/traefik/traefik/pull/11806) by [sevensolutions](https://github.com/sevensolutions)) - -**Bug fixes:** -- **[healthcheck]** Revert 11711 adding url param to healthcheck command ([#11927](https://github.com/traefik/traefik/pull/11927) by [lbenguigui](https://github.com/lbenguigui)) -- **[logs,metrics,tracing,accesslogs,otel]** Add missing resource attributes detectors ([#11874](https://github.com/traefik/traefik/pull/11874) by [rtribotte](https://github.com/rtribotte)) -- **[logs,tracing,k8s,otel]** Add k8s resource attributes automatically ([#11906](https://github.com/traefik/traefik/pull/11906) by [kevinpollet](https://github.com/kevinpollet)) -- **[metrics,otel]** Add resourceAttributes option to OTel metrics ([#11908](https://github.com/traefik/traefik/pull/11908) by [kevinpollet](https://github.com/kevinpollet)) -- **[middleware,tracing]** Introduce trace verbosity config and produce less spans by default ([#11870](https://github.com/traefik/traefik/pull/11870) by [rtribotte](https://github.com/rtribotte)) - -**Documentation:** -- **[docker,ecs,docker/swarm,consulcatalog,nomad]** Add constraints key limitations for label providers ([#11893](https://github.com/traefik/traefik/pull/11893) by [bluepuma77](https://github.com/bluepuma77)) -- **[k8s]** Add extended NGinX annotation support documentation ([#11920](https://github.com/traefik/traefik/pull/11920) by [nmengin](https://github.com/nmengin)) -- Remove dead link to Peka blog ([#11934](https://github.com/traefik/traefik/pull/11934) by [kevinpollet](https://github.com/kevinpollet)) -- Prepare release v3.5.0-rc2 ([#11899](https://github.com/traefik/traefik/pull/11899) by [kevinpollet](https://github.com/kevinpollet)) -- Prepare release v3.5.0-rc1 ([#11865](https://github.com/traefik/traefik/pull/11865) by [rtribotte](https://github.com/rtribotte)) - -**Misc:** -- Merge branch v3.4 into v3.5 ([#11933](https://github.com/traefik/traefik/pull/11933) by [rtribotte](https://github.com/rtribotte)) -- Merge branch v3.4 into v3.5 ([#11898](https://github.com/traefik/traefik/pull/11898) by [kevinpollet](https://github.com/kevinpollet)) -- Merge branch v3.4 into master ([#11863](https://github.com/traefik/traefik/pull/11863) by [rtribotte](https://github.com/rtribotte)) -- Merge branch v3.4 into master ([#11861](https://github.com/traefik/traefik/pull/11861) by [rtribotte](https://github.com/rtribotte)) -- Merge branch v3.4 into master ([#11857](https://github.com/traefik/traefik/pull/11857) by [rtribotte](https://github.com/rtribotte)) -- Merge branch v3.4 into master ([#11855](https://github.com/traefik/traefik/pull/11855) by [rtribotte](https://github.com/rtribotte)) -- Merge branch v3.4 into master ([#11813](https://github.com/traefik/traefik/pull/11813) by [kevinpollet](https://github.com/kevinpollet)) -- Merge branch v3.4 into master ([#11758](https://github.com/traefik/traefik/pull/11758) by [mmatur](https://github.com/mmatur)) -- Merge v3.4 into master ([#11752](https://github.com/traefik/traefik/pull/11752) by [mmatur](https://github.com/mmatur)) -- Merge branch v3.4 into master ([#11708](https://github.com/traefik/traefik/pull/11708) by [kevinpollet](https://github.com/kevinpollet)) - -## [v3.4.5](https://github.com/traefik/traefik/tree/v3.4.5) (2025-07-23) -[All Commits](https://github.com/traefik/traefik/compare/v3.4.4...v3.4.5) - -**Bug fixes:** -- **[http3]** Bump github.com/quic-go/quic-go to v0.54.0 ([#11919](https://github.com/traefik/traefik/pull/11919) by [GreyXor](https://github.com/GreyXor)) - -**Documentation:** -- Fix typo in entrypoints page ([#11914](https://github.com/traefik/traefik/pull/11914) by [adk-swisstopo](https://github.com/adk-swisstopo)) - -**Misc:** -- Merge branch v2.11 into v3.4 ([#11930](https://github.com/traefik/traefik/pull/11930) by [kevinpollet](https://github.com/kevinpollet)) -- Merge branch v2.11 into v3.4 ([#11926](https://github.com/traefik/traefik/pull/11926) by [rtribotte](https://github.com/rtribotte)) - -## [v2.11.28](https://github.com/traefik/traefik/tree/v2.11.28) (2025-07-23) -[All Commits](https://github.com/traefik/traefik/compare/v2.11.27...v2.11.28) - -**Bug fixes:** -- **[logs]** Redact logged install configuration ([#11907](https://github.com/traefik/traefik/pull/11907) by [jspdown](https://github.com/jspdown)) -- **[plugins]** Fix client arbitrary file access during archive extraction zipslip ([#11911](https://github.com/traefik/traefik/pull/11911) by [odaysec](https://github.com/odaysec)) -- **[server]** Disable MPTCP by default ([#11918](https://github.com/traefik/traefik/pull/11918) by [rtribotte](https://github.com/rtribotte)) - -**Documentation:** -- **[k8s/crd,k8s]** Remove all mentions of ordering for TLSOption CurvePreferences field ([#11924](https://github.com/traefik/traefik/pull/11924) by [jnoordsij](https://github.com/jnoordsij)) - -## [v3.5.0-rc2](https://github.com/traefik/traefik/tree/v3.5.0-rc2) (2025-07-11) -[All Commits](https://github.com/traefik/traefik/compare/v3.5.0-rc1...v3.5.0-rc2) - -**Bug fixes:** -- **[logs,metrics,tracing,accesslogs,otel]** Add missing resource attributes detectors ([#11874](https://github.com/traefik/traefik/pull/11874) by [rtribotte](https://github.com/rtribotte)) - -**Misc:** -- Merge branch v3.4 into v3.5 ([#11898](https://github.com/traefik/traefik/pull/11898) by [kevinpollet](https://github.com/kevinpollet)) - -## [v3.4.4](https://github.com/traefik/traefik/tree/v3.4.4) (2025-07-11) -[All Commits](https://github.com/traefik/traefik/compare/v3.4.3...v3.4.4) - -**Bug fixes:** -- **[k8s/gatewayapi]** Respect service.nativelb=false annotation when nativeLBByDefault is enabled ([#11847](https://github.com/traefik/traefik/pull/11847) by [sdelicata](https://github.com/sdelicata)) -- **[service]** Fix concurrent access to balancer status map in WRR and P2C strategies ([#11887](https://github.com/traefik/traefik/pull/11887) by [kevinpollet](https://github.com/kevinpollet)) - -**Documentation:** -- **[docker,k8s]** Add New Expose Guides ([#11760](https://github.com/traefik/traefik/pull/11760) by [sheddy-traefik](https://github.com/sheddy-traefik)) -- **[docker,k8s]** Add New Setup Guides ([#11741](https://github.com/traefik/traefik/pull/11741) by [sheddy-traefik](https://github.com/sheddy-traefik)) -- **[docker/swarm]** Fix label for overriding swarm network on container ([#11881](https://github.com/traefik/traefik/pull/11881) by [kevinpollet](https://github.com/kevinpollet)) -- **[logs,accesslogs]** Update Logs and Accesslogs Reference documentation with OTLP Options ([#11845](https://github.com/traefik/traefik/pull/11845) by [sheddy-traefik](https://github.com/sheddy-traefik)) -- Update what is Traefik page to include full Traefik Platform context ([#11885](https://github.com/traefik/traefik/pull/11885) by [tomatokoolaid](https://github.com/tomatokoolaid)) - -**Misc:** -- Merge branch v2.11 into v3.4 ([#11896](https://github.com/traefik/traefik/pull/11896) by [kevinpollet](https://github.com/kevinpollet)) - -## [v2.11.27](https://github.com/traefik/traefik/tree/v2.11.27) (2025-07-11) -[All Commits](https://github.com/traefik/traefik/compare/v2.11.26...v2.11.27) - -**Bug fixes:** -- Bump github.com/go-viper/mapstructure/v2 to v2.3.0 ([#11880](https://github.com/traefik/traefik/pull/11880) by [kevinpollet](https://github.com/kevinpollet)) - -## [v3.5.0-rc1](https://github.com/traefik/traefik/tree/v3.5.0-rc1) (2025-06-26) -[All Commits](https://github.com/traefik/traefik/compare/v3.4.0-rc1...v3.5.0-rc1) - -**Enhancements:** -- **[acme]** OCSP stapling ([#8393](https://github.com/traefik/traefik/pull/8393) by [alekitto](https://github.com/alekitto)) -- **[acme]** Add acme.httpChallenge.delay option ([#11643](https://github.com/traefik/traefik/pull/11643) by [ldez](https://github.com/ldez)) -- **[acme]** Allow configuration of ACME provider http timeout ([#11637](https://github.com/traefik/traefik/pull/11637) by [tkw1536](https://github.com/tkw1536)) -- **[healthcheck]** Add url option to healthcheck command ([#11711](https://github.com/traefik/traefik/pull/11711) by [Nelwhix](https://github.com/Nelwhix)) -- **[healthcheck]** Add unhealthy Interval to the health check configuration ([#10610](https://github.com/traefik/traefik/pull/10610) by [sswastik02](https://github.com/sswastik02)) -- **[k8s/gatewayapi]** Bump sigs.k8s.io/gateway-api to v1.3.0 ([#11719](https://github.com/traefik/traefik/pull/11719) by [rtribotte](https://github.com/rtribotte)) -- **[k8s/ingress]** Make the behavior of prefix matching in Ingress consistent with Kubernetes doc ([#11203](https://github.com/traefik/traefik/pull/11203) by [charlie0129](https://github.com/charlie0129)) -- **[k8s]** NGINX Ingress Provider ([#11844](https://github.com/traefik/traefik/pull/11844) by [rtribotte](https://github.com/rtribotte)) -- **[middleware,authentication]** Handle context canceled in ForwardAuth middleware ([#11817](https://github.com/traefik/traefik/pull/11817) by [bengentree](https://github.com/bengentree)) -- **[plugins]** Ability to enable unsafe in yaegi through plugin manifest ([#11589](https://github.com/traefik/traefik/pull/11589) by [Rydez](https://github.com/Rydez)) -- **[tls]** Introduce X25519MLKEM768 for Post-Quantum-Secure TLS ([#11731](https://github.com/traefik/traefik/pull/11731) by [fzoli](https://github.com/fzoli)) -- **[webui]** Migrate Traefik Proxy dashboard UI to React ([#11674](https://github.com/traefik/traefik/pull/11674) by [gndz07](https://github.com/gndz07)) -- **[webui]** Improve visualization for StatusRewrites option of errors middleware ([#11806](https://github.com/traefik/traefik/pull/11806) by [sevensolutions](https://github.com/sevensolutions)) - -**Misc:** -- Merge branch v3.4 into master ([#11863](https://github.com/traefik/traefik/pull/11863) by [rtribotte](https://github.com/rtribotte)) -- Merge branch v3.4 into master ([#11861](https://github.com/traefik/traefik/pull/11861) by [rtribotte](https://github.com/rtribotte)) -- Merge branch v3.4 into master ([#11857](https://github.com/traefik/traefik/pull/11857) by [rtribotte](https://github.com/rtribotte)) -- Merge branch v3.4 into master ([#11855](https://github.com/traefik/traefik/pull/11855) by [rtribotte](https://github.com/rtribotte)) -- Merge branch v3.4 into master ([#11813](https://github.com/traefik/traefik/pull/11813) by [kevinpollet](https://github.com/kevinpollet)) -- Merge branch v3.4 into master ([#11758](https://github.com/traefik/traefik/pull/11758) by [mmatur](https://github.com/mmatur)) -- Merge v3.4 into master ([#11752](https://github.com/traefik/traefik/pull/11752) by [mmatur](https://github.com/mmatur)) -- Merge branch v3.4 into master ([#11708](https://github.com/traefik/traefik/pull/11708) by [kevinpollet](https://github.com/kevinpollet)) - -## [v3.4.3](https://github.com/traefik/traefik/tree/v3.4.3) (2025-06-26) -[All Commits](https://github.com/traefik/traefik/compare/v3.4.2...v3.4.3) - -**Bug fixes:** -- **[http3]** Bump quic-go to v.0.49.0 ([#11848](https://github.com/traefik/traefik/pull/11848) by [joshua-siw](https://github.com/joshua-siw)) - -## [v3.4.2](https://github.com/traefik/traefik/tree/v3.4.2) (2025-06-26) -[All Commits](https://github.com/traefik/traefik/compare/v3.4.1...v3.4.2) - -**Documentation:** -- **[acme]** Add a note to certificatesDuration ([#11808](https://github.com/traefik/traefik/pull/11808) by [sMteX](https://github.com/sMteX)) -- **[docker,k8s]** Update Getting started Section with New Docker and Kubernetes Tutorial ([#11714](https://github.com/traefik/traefik/pull/11714) by [sheddy-traefik](https://github.com/sheddy-traefik)) -- **[docker]** Remove obsolete version field in compose files ([#11798](https://github.com/traefik/traefik/pull/11798) by [thomas-mauran](https://github.com/thomas-mauran)) -- **[k8s]** Add a note about Ingress Backend Resource support ([#11785](https://github.com/traefik/traefik/pull/11785) by [edysli](https://github.com/edysli)) -- **[logs,metrics,tracing,accesslogs]** Update the EntryPoints Documentation ([#11856](https://github.com/traefik/traefik/pull/11856) by [sheddy-traefik](https://github.com/sheddy-traefik)) -- **[logs,metrics,tracing,accesslogs]** Add New Observe Guides to the Documentation ([#11828](https://github.com/traefik/traefik/pull/11828) by [sheddy-traefik](https://github.com/sheddy-traefik)) -- **[middleware]** Remove conflicting information from the CircuitBreaker documentation. ([#11835](https://github.com/traefik/traefik/pull/11835) by [adk-swisstopo](https://github.com/adk-swisstopo)) -- **[service]** Clarify mirroring service default percent value ([#11804](https://github.com/traefik/traefik/pull/11804) by [Alexy-vda](https://github.com/Alexy-vda)) -- **[websocket]** Add WebSocket guide ([#11623](https://github.com/traefik/traefik/pull/11623) by [NX211](https://github.com/NX211)) - -**Misc:** -- Merge branch v2.11 into v3.4 ([#11859](https://github.com/traefik/traefik/pull/11859) by [rtribotte](https://github.com/rtribotte)) -- Merge branch v2.11 into v3.4 ([#11831](https://github.com/traefik/traefik/pull/11831) by [rtribotte](https://github.com/rtribotte)) -- Merge branch v2.11 into v3.4 ([#11810](https://github.com/traefik/traefik/pull/11810) by [rtribotte](https://github.com/rtribotte)) - -## [v2.11.26](https://github.com/traefik/traefik/tree/v2.11.26) (2025-06-26) -[All Commits](https://github.com/traefik/traefik/compare/v2.11.25...v2.11.26) - -**Bug fixes:** -- **[middleware]** Do not log redis sentinel username and password ([#11819](https://github.com/traefik/traefik/pull/11819) by [kevinpollet](https://github.com/kevinpollet)) - -**Documentation:** -- **[kv]** Fix KV reference rendering ([#11815](https://github.com/traefik/traefik/pull/11815) by [rtribotte](https://github.com/rtribotte)) -- **[middleware,k8s/crd]** Fix typo in redirect middleware documentation ([#11830](https://github.com/traefik/traefik/pull/11830) by [rtribotte](https://github.com/rtribotte)) -- Update supported versions ([#11811](https://github.com/traefik/traefik/pull/11811) by [jnoordsij](https://github.com/jnoordsij)) - ## [v3.4.1](https://github.com/traefik/traefik/tree/v3.4.1) (2025-05-27) [All Commits](https://github.com/traefik/traefik/compare/v3.4.0...v3.4.1) diff --git a/Dockerfile b/Dockerfile index 10ee5e2b5..710d31a74 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # syntax=docker/dockerfile:1.2 -FROM alpine:3.22 +FROM alpine:3.21 RUN apk add --no-cache --no-progress ca-certificates tzdata diff --git a/Makefile b/Makefile index 470b5c3e8..570a04199 100644 --- a/Makefile +++ b/Makefile @@ -30,7 +30,7 @@ dist: .PHONY: build-webui-image #? build-webui-image: Build WebUI Docker image build-webui-image: - docker build -t traefik-webui -f webui/buildx.Dockerfile webui + docker build -t traefik-webui -f webui/Dockerfile webui .PHONY: clean-webui #? clean-webui: Clean WebUI static generated assets @@ -41,9 +41,8 @@ clean-webui: webui/static/index.html: $(MAKE) build-webui-image - docker run --rm -v "$(PWD)/webui/static":'/src/webui/static' traefik-webui yarn build:prod + docker run --rm -v "$(PWD)/webui/static":'/src/webui/static' traefik-webui npm run build:nc docker run --rm -v "$(PWD)/webui/static":'/src/webui/static' traefik-webui chown -R $(shell id -u):$(shell id -g) ./static - printf 'For more information see `webui/readme.md`' > webui/static/DONT-EDIT-FILES-IN-THIS-DIRECTORY.md .PHONY: generate-webui #? generate-webui: Generate WebUI @@ -96,14 +95,14 @@ test-unit: .PHONY: test-integration #? test-integration: Run the integration tests -test-integration: +test-integration: binary GOOS=$(GOOS) GOARCH=$(GOARCH) go test ./integration -test.timeout=20m -failfast -v $(TESTFLAGS) .PHONY: test-gateway-api-conformance #? test-gateway-api-conformance: Run the conformance tests test-gateway-api-conformance: build-image-dirty # In case of a new Minor/Major version, the k8sConformanceTraefikVersion needs to be updated. - GOOS=$(GOOS) GOARCH=$(GOARCH) go test ./integration -v -test.run K8sConformanceSuite -k8sConformance -k8sConformanceTraefikVersion="v3.5" $(TESTFLAGS) + GOOS=$(GOOS) GOARCH=$(GOARCH) go test ./integration -v -test.run K8sConformanceSuite -k8sConformance -k8sConformanceTraefikVersion="v3.4" $(TESTFLAGS) .PHONY: test-ui-unit #? test-ui-unit: Run the unit tests for the webui @@ -184,6 +183,11 @@ generate-crd: generate-genconf: go run ./cmd/internal/gen/ +.PHONY: release-packages +#? release-packages: Create packages for the release +release-packages: generate-webui + $(CURDIR)/script/release-packages.sh + .PHONY: fmt #? fmt: Format the Code fmt: diff --git a/README.md b/README.md index 472057a3a..aa7336940 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ feel free to check out last 2 commits, they are like 100sloc together

+[![Build Status SemaphoreCI](https://traefik-oss.semaphoreci.com/badges/traefik/branches/master.svg?style=shields)](https://traefik-oss.semaphoreci.com/projects/traefik) [![Docs](https://img.shields.io/badge/docs-current-brightgreen.svg)](https://doc.traefik.io/traefik) [![Go Report Card](https://goreportcard.com/badge/traefik/traefik)](https://goreportcard.com/report/traefik/traefik) [![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/traefik/traefik/blob/master/LICENSE.md) @@ -155,7 +156,7 @@ We use [Semantic Versioning](https://semver.org/). ## Credits -Kudos to [Peka](https://www.instagram.com/pierroks/) for his awesome work on the gopher's logo!. +Kudos to [Peka](http://peka.byethost11.com/photoblog/) for his awesome work on the gopher's logo!. The gopher's logo of Traefik is licensed under the Creative Commons 3.0 Attributions license. diff --git a/cmd/traefik/logger.go b/cmd/traefik/logger.go index e97665756..a8b609e4b 100644 --- a/cmd/traefik/logger.go +++ b/cmd/traefik/logger.go @@ -1,7 +1,6 @@ package main import ( - "context" "errors" "fmt" "io" @@ -14,7 +13,7 @@ import ( "github.com/rs/zerolog/log" "github.com/sirupsen/logrus" "github.com/traefik/traefik/v3/pkg/config/static" - "github.com/traefik/traefik/v3/pkg/observability/logs" + "github.com/traefik/traefik/v3/pkg/logs" "gopkg.in/natefinch/lumberjack.v2" ) @@ -23,7 +22,7 @@ func init() { zerolog.SetGlobalLevel(zerolog.ErrorLevel) } -func setupLogger(ctx context.Context, staticConfiguration *static.Configuration) error { +func setupLogger(staticConfiguration *static.Configuration) error { // Validate that the experimental flag is set up at this point, // rather than validating the static configuration before the setupLogger call. // This ensures that validation messages are not logged using an un-configured logger. @@ -40,16 +39,16 @@ func setupLogger(ctx context.Context, staticConfiguration *static.Configuration) zerolog.SetGlobalLevel(logLevel) // create logger - logger := zerolog.New(w).With().Timestamp() + logCtx := zerolog.New(w).With().Timestamp() if logLevel <= zerolog.DebugLevel { - logger = logger.Caller() + logCtx = logCtx.Caller() } - log.Logger = logger.Logger().Level(logLevel) + log.Logger = logCtx.Logger().Level(logLevel) if staticConfiguration.Log != nil && staticConfiguration.Log.OTLP != nil { var err error - log.Logger, err = logs.SetupOTelLogger(ctx, log.Logger, staticConfiguration.Log.OTLP) + log.Logger, err = logs.SetupOTelLogger(log.Logger, staticConfiguration.Log.OTLP) if err != nil { return fmt.Errorf("setting up OpenTelemetry logger: %w", err) } @@ -68,6 +67,10 @@ func setupLogger(ctx context.Context, staticConfiguration *static.Configuration) } func getLogWriter(staticConfiguration *static.Configuration) io.Writer { + if staticConfiguration.Log != nil && staticConfiguration.Log.OTLP != nil { + return io.Discard + } + var w io.Writer = os.Stdout if staticConfiguration.Log != nil && len(staticConfiguration.Log.FilePath) > 0 { _, _ = os.OpenFile(staticConfiguration.Log.FilePath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0o666) diff --git a/cmd/traefik/plugins.go b/cmd/traefik/plugins.go index 2c19cb365..2c3d5dd25 100644 --- a/cmd/traefik/plugins.go +++ b/cmd/traefik/plugins.go @@ -2,62 +2,43 @@ package main import ( "fmt" - "net/http" - "path/filepath" - "time" - "github.com/hashicorp/go-retryablehttp" - "github.com/rs/zerolog/log" "github.com/traefik/traefik/v3/pkg/config/static" - "github.com/traefik/traefik/v3/pkg/observability/logs" "github.com/traefik/traefik/v3/pkg/plugins" ) const outputDir = "./plugins-storage/" func createPluginBuilder(staticConfiguration *static.Configuration) (*plugins.Builder, error) { - manager, plgs, localPlgs, err := initPlugins(staticConfiguration) + client, plgs, localPlgs, err := initPlugins(staticConfiguration) if err != nil { return nil, err } - return plugins.NewBuilder(manager, plgs, localPlgs) + return plugins.NewBuilder(client, plgs, localPlgs) } -func initPlugins(staticCfg *static.Configuration) (*plugins.Manager, map[string]plugins.Descriptor, map[string]plugins.LocalDescriptor, error) { +func initPlugins(staticCfg *static.Configuration) (*plugins.Client, map[string]plugins.Descriptor, map[string]plugins.LocalDescriptor, error) { err := checkUniquePluginNames(staticCfg.Experimental) if err != nil { return nil, nil, nil, err } - var manager *plugins.Manager + var client *plugins.Client plgs := map[string]plugins.Descriptor{} if hasPlugins(staticCfg) { - httpClient := retryablehttp.NewClient() - httpClient.Logger = logs.NewRetryableHTTPLogger(log.Logger) - httpClient.HTTPClient = &http.Client{Timeout: 10 * time.Second} - httpClient.RetryMax = 3 - - // Create separate downloader for HTTP operations - archivesPath := filepath.Join(outputDir, "archives") - downloader, err := plugins.NewRegistryDownloader(plugins.RegistryDownloaderOptions{ - HTTPClient: httpClient.HTTPClient, - ArchivesPath: archivesPath, - }) - if err != nil { - return nil, nil, nil, fmt.Errorf("unable to create plugin downloader: %w", err) - } - - opts := plugins.ManagerOptions{ + opts := plugins.ClientOptions{ Output: outputDir, } - manager, err = plugins.NewManager(downloader, opts) + + var err error + client, err = plugins.NewClient(opts) if err != nil { - return nil, nil, nil, fmt.Errorf("unable to create plugins manager: %w", err) + return nil, nil, nil, fmt.Errorf("unable to create plugins client: %w", err) } - err = plugins.SetupRemotePlugins(manager, staticCfg.Experimental.Plugins) + err = plugins.SetupRemotePlugins(client, staticCfg.Experimental.Plugins) if err != nil { return nil, nil, nil, fmt.Errorf("unable to set up plugins environment: %w", err) } @@ -76,7 +57,7 @@ func initPlugins(staticCfg *static.Configuration) (*plugins.Manager, map[string] localPlgs = staticCfg.Experimental.LocalPlugins } - return manager, plgs, localPlgs, nil + return client, plgs, localPlgs, nil } func checkUniquePluginNames(e *static.Experimental) error { diff --git a/cmd/traefik/traefik.go b/cmd/traefik/traefik.go index 3f35b280f..413907f17 100644 --- a/cmd/traefik/traefik.go +++ b/cmd/traefik/traefik.go @@ -3,6 +3,7 @@ package main import ( "context" "crypto/x509" + "encoding/json" "fmt" "io" stdlog "log" @@ -30,24 +31,23 @@ import ( "github.com/traefik/traefik/v3/pkg/config/dynamic" "github.com/traefik/traefik/v3/pkg/config/runtime" "github.com/traefik/traefik/v3/pkg/config/static" + "github.com/traefik/traefik/v3/pkg/logs" + "github.com/traefik/traefik/v3/pkg/metrics" "github.com/traefik/traefik/v3/pkg/middlewares/accesslog" - "github.com/traefik/traefik/v3/pkg/observability/logs" - "github.com/traefik/traefik/v3/pkg/observability/metrics" - "github.com/traefik/traefik/v3/pkg/observability/tracing" - otypes "github.com/traefik/traefik/v3/pkg/observability/types" "github.com/traefik/traefik/v3/pkg/provider/acme" "github.com/traefik/traefik/v3/pkg/provider/aggregator" "github.com/traefik/traefik/v3/pkg/provider/tailscale" "github.com/traefik/traefik/v3/pkg/provider/traefik" "github.com/traefik/traefik/v3/pkg/proxy" "github.com/traefik/traefik/v3/pkg/proxy/httputil" - "github.com/traefik/traefik/v3/pkg/redactor" "github.com/traefik/traefik/v3/pkg/safe" "github.com/traefik/traefik/v3/pkg/server" "github.com/traefik/traefik/v3/pkg/server/middleware" "github.com/traefik/traefik/v3/pkg/server/service" "github.com/traefik/traefik/v3/pkg/tcp" traefiktls "github.com/traefik/traefik/v3/pkg/tls" + "github.com/traefik/traefik/v3/pkg/tracing" + "github.com/traefik/traefik/v3/pkg/types" "github.com/traefik/traefik/v3/pkg/version" "github.com/traefik/traefik/v3/pkg/updater" ) @@ -91,10 +91,7 @@ Complete documentation is available at https://traefik.io`, } func runCmd(staticConfiguration *static.Configuration) error { - ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM) - defer cancel() - - if err := setupLogger(ctx, staticConfiguration); err != nil { + if err := setupLogger(staticConfiguration); err != nil { return fmt.Errorf("setting up logger: %w", err) } @@ -108,11 +105,12 @@ func runCmd(staticConfiguration *static.Configuration) error { log.Info().Str("version", version.Version). Msgf("Traefik version %s built on %s", version.Version, version.BuildDate) - redactedStaticConfiguration, err := redactor.RemoveCredentials(staticConfiguration) + jsonConf, err := json.Marshal(staticConfiguration) if err != nil { - log.Error().Err(err).Msg("Could not redact static configuration") + log.Error().Err(err).Msg("Could not marshal static configuration") + log.Debug().Interface("staticConfiguration", staticConfiguration).Msg("Static configuration loaded [struct]") } else { - log.Debug().RawJSON("staticConfiguration", []byte(redactedStaticConfiguration)).Msg("Static configuration loaded [json]") + log.Debug().RawJSON("staticConfiguration", jsonConf).Msg("Static configuration loaded [json]") } if staticConfiguration.Global.CheckNewVersion { @@ -126,6 +124,8 @@ func runCmd(staticConfiguration *static.Configuration) error { return err } + ctx, _ := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM) + if staticConfiguration.Ping != nil { staticConfiguration.Ping.WithContext(ctx) } @@ -183,9 +183,7 @@ func setupServer(staticConfiguration *static.Configuration) (*server.Server, err // ACME - tlsManager := traefiktls.NewManager(staticConfiguration.OCSP) - routinesPool.GoCtx(tlsManager.Run) - + tlsManager := traefiktls.NewManager() httpChallengeProvider := acme.NewChallengeHTTP() tlsChallengeProvider := acme.NewChallengeTLSALPN() @@ -213,8 +211,8 @@ func setupServer(staticConfiguration *static.Configuration) (*server.Server, err } } metricsRegistry := metrics.NewMultiRegistry(metricRegistries) - accessLog := setupAccessLog(ctx, staticConfiguration.AccessLog) - tracer, tracerCloser := setupTracing(ctx, staticConfiguration.Tracing) + accessLog := setupAccessLog(staticConfiguration.AccessLog) + tracer, tracerCloser := setupTracing(staticConfiguration.Tracing) observabilityMgr := middleware.NewObservabilityMgr(*staticConfiguration, metricsRegistry, semConvMetricRegistry, accessLog, tracer, tracerCloser) // Entrypoints @@ -511,7 +509,7 @@ func initTailscaleProviders(cfg *static.Configuration, providerAggregator *aggre return providers } -func registerMetricClients(metricsConfig *otypes.Metrics) []metrics.Registry { +func registerMetricClients(metricsConfig *types.Metrics) []metrics.Registry { if metricsConfig == nil { return nil } @@ -592,12 +590,12 @@ func appendCertMetric(gauge gokitmetrics.Gauge, certificate *x509.Certificate) { gauge.With(labels...).Set(notAfter) } -func setupAccessLog(ctx context.Context, conf *otypes.AccessLog) *accesslog.Handler { +func setupAccessLog(conf *types.AccessLog) *accesslog.Handler { if conf == nil { return nil } - accessLoggerMiddleware, err := accesslog.NewHandler(ctx, conf) + accessLoggerMiddleware, err := accesslog.NewHandler(conf) if err != nil { log.Warn().Err(err).Msg("Unable to create access logger") return nil @@ -606,12 +604,12 @@ func setupAccessLog(ctx context.Context, conf *otypes.AccessLog) *accesslog.Hand return accessLoggerMiddleware } -func setupTracing(ctx context.Context, conf *static.Tracing) (*tracing.Tracer, io.Closer) { +func setupTracing(conf *static.Tracing) (*tracing.Tracer, io.Closer) { if conf == nil { return nil, nil } - tracer, closer, err := tracing.NewTracing(ctx, conf) + tracer, closer, err := tracing.NewTracing(conf) if err != nil { log.Warn().Err(err).Msg("Unable to create tracer") return nil, nil diff --git a/docs/check.Dockerfile b/docs/check.Dockerfile index 2a03cf8af..41a389f69 100644 --- a/docs/check.Dockerfile +++ b/docs/check.Dockerfile @@ -1,4 +1,4 @@ -FROM alpine:3.22 +FROM alpine:3.21 RUN apk --no-cache --no-progress add \ build-base \ @@ -9,7 +9,9 @@ RUN apk --no-cache --no-progress add \ ruby \ ruby-bigdecimal \ ruby-dev \ + ruby-etc \ ruby-ffi \ + ruby-json \ zlib-dev RUN gem install nokogiri --version 1.18.6 --no-document -- --use-system-libraries diff --git a/docs/content/assets/css/menu-icons.css b/docs/content/assets/css/menu-icons.css deleted file mode 100644 index eccee6b82..000000000 --- a/docs/content/assets/css/menu-icons.css +++ /dev/null @@ -1,58 +0,0 @@ -/* Traefik Hub Menu icon base styles */ -.menu-icon { - height: 18px; - width: 18px; - vertical-align: middle; - margin-left: 6px; - transition: all 0.2s ease; - filter: drop-shadow(0 1px 1px rgba(0,0,0,0.1)); - display: inline; - white-space: nowrap; -} - -/* Ensure parent container keeps items inline */ -.nav-link-with-icon { - white-space: nowrap !important; - display: inline-flex !important; - align-items: center !important; -} - -/* Hover effects */ -.menu-icon:hover { - transform: scale(1.05); - opacity: 0.8; -} - -/* Tablet responsive */ -@media (max-width: 1024px) { - .menu-icon { - height: 14px; - width: 14px; - margin-left: 4px; - } -} - -/* Mobile responsive */ -@media (max-width: 768px) { - .menu-icon { - height: 12px; - width: 12px; - margin-left: 3px; - vertical-align: middle; - } - - /* Keep mobile navigation items inline */ - .nav-link-with-icon { - display: inline-flex !important; - align-items: center !important; - width: auto !important; - } -} - -/* High DPI displays */ -@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) { - .menu-icon { - image-rendering: -webkit-optimize-contrast; - image-rendering: crisp-edges; - } -} diff --git a/docs/content/assets/img/getting-started/docker-router.png b/docs/content/assets/img/getting-started/docker-router.png deleted file mode 100644 index 3ff33493a..000000000 Binary files a/docs/content/assets/img/getting-started/docker-router.png and /dev/null differ diff --git a/docs/content/assets/img/getting-started/kubernetes-gateway.png b/docs/content/assets/img/getting-started/kubernetes-gateway.png deleted file mode 100644 index 5d59d2c69..000000000 Binary files a/docs/content/assets/img/getting-started/kubernetes-gateway.png and /dev/null differ diff --git a/docs/content/assets/img/getting-started/providers.png b/docs/content/assets/img/getting-started/providers.png deleted file mode 100644 index c1933b656..000000000 Binary files a/docs/content/assets/img/getting-started/providers.png and /dev/null differ diff --git a/docs/content/assets/img/getting-started/traefik-dashboard-docker.png b/docs/content/assets/img/getting-started/traefik-dashboard-docker.png deleted file mode 100644 index 82cbb53ad..000000000 Binary files a/docs/content/assets/img/getting-started/traefik-dashboard-docker.png and /dev/null differ diff --git a/docs/content/assets/img/getting-started/traefik-dashboard.png b/docs/content/assets/img/getting-started/traefik-dashboard.png deleted file mode 100644 index cccd73ef5..000000000 Binary files a/docs/content/assets/img/getting-started/traefik-dashboard.png and /dev/null differ diff --git a/docs/content/assets/img/getting-started/whoami-localhost.png b/docs/content/assets/img/getting-started/whoami-localhost.png deleted file mode 100644 index 79f059107..000000000 Binary files a/docs/content/assets/img/getting-started/whoami-localhost.png and /dev/null differ diff --git a/docs/content/assets/img/middleware/addprefix.png b/docs/content/assets/img/middleware/addprefix.png new file mode 100644 index 000000000..5899a8bfa Binary files /dev/null and b/docs/content/assets/img/middleware/addprefix.png differ diff --git a/docs/content/assets/img/middleware/authforward.png b/docs/content/assets/img/middleware/authforward.png new file mode 100644 index 000000000..149a915c3 Binary files /dev/null and b/docs/content/assets/img/middleware/authforward.png differ diff --git a/docs/content/assets/img/middleware/basicauth.png b/docs/content/assets/img/middleware/basicauth.png new file mode 100644 index 000000000..fd2851f5a Binary files /dev/null and b/docs/content/assets/img/middleware/basicauth.png differ diff --git a/docs/content/assets/img/middleware/buffering.png b/docs/content/assets/img/middleware/buffering.png new file mode 100644 index 000000000..29c2f6722 Binary files /dev/null and b/docs/content/assets/img/middleware/buffering.png differ diff --git a/docs/content/assets/img/middleware/chain.png b/docs/content/assets/img/middleware/chain.png new file mode 100644 index 000000000..e0331ff11 Binary files /dev/null and b/docs/content/assets/img/middleware/chain.png differ diff --git a/docs/content/assets/img/middleware/circuitbreaker.png b/docs/content/assets/img/middleware/circuitbreaker.png new file mode 100644 index 000000000..a18536054 Binary files /dev/null and b/docs/content/assets/img/middleware/circuitbreaker.png differ diff --git a/docs/content/assets/img/middleware/compress.png b/docs/content/assets/img/middleware/compress.png new file mode 100644 index 000000000..696b9617e Binary files /dev/null and b/docs/content/assets/img/middleware/compress.png differ diff --git a/docs/content/assets/img/middleware/digestauth.png b/docs/content/assets/img/middleware/digestauth.png new file mode 100644 index 000000000..8f2cab4f3 Binary files /dev/null and b/docs/content/assets/img/middleware/digestauth.png differ diff --git a/docs/content/assets/img/middleware/errorpages.png b/docs/content/assets/img/middleware/errorpages.png new file mode 100644 index 000000000..5462e2472 Binary files /dev/null and b/docs/content/assets/img/middleware/errorpages.png differ diff --git a/docs/content/assets/img/middleware/headers.png b/docs/content/assets/img/middleware/headers.png new file mode 100644 index 000000000..3f63fe7ff Binary files /dev/null and b/docs/content/assets/img/middleware/headers.png differ diff --git a/docs/content/assets/img/middleware/inflightreq.png b/docs/content/assets/img/middleware/inflightreq.png new file mode 100644 index 000000000..b8b715f20 Binary files /dev/null and b/docs/content/assets/img/middleware/inflightreq.png differ diff --git a/docs/content/assets/img/middleware/ipwhitelist.png b/docs/content/assets/img/middleware/ipwhitelist.png new file mode 100644 index 000000000..8c6b0c97a Binary files /dev/null and b/docs/content/assets/img/middleware/ipwhitelist.png differ diff --git a/docs/content/assets/img/middleware/overview.png b/docs/content/assets/img/middleware/overview.png new file mode 100644 index 000000000..d99487dc3 Binary files /dev/null and b/docs/content/assets/img/middleware/overview.png differ diff --git a/docs/content/assets/img/secure/oidc-auth-flow.png b/docs/content/assets/img/secure/oidc-auth-flow.png deleted file mode 100644 index c41c32558..000000000 Binary files a/docs/content/assets/img/secure/oidc-auth-flow.png and /dev/null differ diff --git a/docs/content/assets/img/setup/route-in-dashboard.png b/docs/content/assets/img/setup/route-in-dashboard.png deleted file mode 100644 index 99c8eaf4a..000000000 Binary files a/docs/content/assets/img/setup/route-in-dashboard.png and /dev/null differ diff --git a/docs/content/assets/img/setup/traefik-dashboard-docker.png b/docs/content/assets/img/setup/traefik-dashboard-docker.png deleted file mode 100644 index 862ba4994..000000000 Binary files a/docs/content/assets/img/setup/traefik-dashboard-docker.png and /dev/null differ diff --git a/docs/content/assets/img/setup/traefik-dashboard-swarm.png b/docs/content/assets/img/setup/traefik-dashboard-swarm.png deleted file mode 100644 index f91e60782..000000000 Binary files a/docs/content/assets/img/setup/traefik-dashboard-swarm.png and /dev/null differ diff --git a/docs/content/assets/img/setup/traefik-dashboard.png b/docs/content/assets/img/setup/traefik-dashboard.png deleted file mode 100644 index 5d1738f45..000000000 Binary files a/docs/content/assets/img/setup/traefik-dashboard.png and /dev/null differ diff --git a/docs/content/assets/img/setup/whoami-json-dump.png b/docs/content/assets/img/setup/whoami-json-dump.png deleted file mode 100644 index 7fd1526f1..000000000 Binary files a/docs/content/assets/img/setup/whoami-json-dump.png and /dev/null differ diff --git a/docs/content/assets/img/webui-dashboard.png b/docs/content/assets/img/webui-dashboard.png index 084d287eb..80a5178b0 100644 Binary files a/docs/content/assets/img/webui-dashboard.png and b/docs/content/assets/img/webui-dashboard.png differ diff --git a/docs/content/contributing/documentation.md b/docs/content/contributing/documentation.md index ba0c22f8e..a57e4c695 100644 --- a/docs/content/contributing/documentation.md +++ b/docs/content/contributing/documentation.md @@ -15,7 +15,7 @@ Let's see how. ### General -This [documentation](../index.md "Link to the official Traefik documentation") is built with [MkDocs](https://mkdocs.org/ "Link to the website of MkDocs"). +This [documentation](../../ "Link to the official Traefik documentation") is built with [MkDocs](https://mkdocs.org/ "Link to the website of MkDocs"). ### Method 1: `Docker` and `make` diff --git a/docs/content/deprecation/releases.md b/docs/content/deprecation/releases.md index c3ec3336a..1a5fc0b03 100644 --- a/docs/content/deprecation/releases.md +++ b/docs/content/deprecation/releases.md @@ -6,13 +6,11 @@ Below is a non-exhaustive list of versions and their maintenance status: | Version | Release Date | Active Support | Security Support | |---------|--------------|--------------------|-------------------| -| 3.5 | Jul 23, 2025 | Yes | Yes | -| 3.4 | May 05, 2025 | Ended Jul 23, 2025 | No | -| 3.3 | Jan 06, 2025 | Ended May 05, 2025 | No | +| 3.3 | Jan 06, 2025 | Yes | Yes | | 3.2 | Oct 28, 2024 | Ended Jan 06, 2025 | No | | 3.1 | Jul 15, 2024 | Ended Oct 28, 2024 | No | | 3.0 | Apr 29, 2024 | Ended Jul 15, 2024 | No | -| 2.11 | Feb 12, 2024 | Ended Apr 29, 2025 | Ends Feb 01, 2026 | +| 2.11 | Feb 12, 2024 | Ends Apr 29, 2025 | Ends Feb 01, 2026 | | 2.10 | Apr 24, 2023 | Ended Feb 12, 2024 | No | | 2.9 | Oct 03, 2022 | Ended Apr 24, 2023 | No | | 2.8 | Jun 29, 2022 | Ended Oct 03, 2022 | No | @@ -34,7 +32,7 @@ Below is a non-exhaustive list of versions and their maintenance status: This page is maintained and updated periodically to reflect our roadmap and any decisions affecting the end of support for Traefik Proxy. -Please refer to our migration guides for specific instructions on upgrading between versions, an example is the [v2 to v3 migration guide](../migrate/v2-to-v3.md). +Please refer to our migration guides for specific instructions on upgrading between versions, an example is the [v2 to v3 migration guide](../migration/v2-to-v3.md). !!! important "All target dates for end of support or feature removal announcements may be subject to change." diff --git a/docs/content/expose/docker.md b/docs/content/expose/docker.md deleted file mode 100644 index 0e6de8cc6..000000000 --- a/docs/content/expose/docker.md +++ /dev/null @@ -1,465 +0,0 @@ -# Exposing Services with Traefik on Docker - -This guide will help you expose your services securely through Traefik Proxy using Docker. We'll cover routing HTTP and HTTPS traffic, implementing TLS, adding middlewares, Let's Encrypt integration, and sticky sessions. - -## Prerequisites - -- Docker and Docker Compose installed -- Basic understanding of Docker concepts -- Traefik deployed using the Traefik Docker Setup guide - -## Expose Your First HTTP Service - -Let's expose a simple HTTP service using the [whoami](https://hub.docker.com/r/traefik/whoami) application. This will demonstrate basic routing to a backend service. - -First, create a `docker-compose.yml` file: - -```yaml -services: - traefik: - image: "traefik:v3.4" - container_name: "traefik" - restart: unless-stopped - security_opt: - - no-new-privileges:true - networks: - - proxy - command: - - "--providers.docker=true" - - "--providers.docker.exposedbydefault=false" - - "--providers.docker.network=proxy" - - "--entryPoints.web.address=:80" - ports: - - "80:80" - - "8080:8080" - volumes: - - "/var/run/docker.sock:/var/run/docker.sock:ro" - - whoami: - image: "traefik/whoami" - restart: unless-stopped - networks: - - proxy - labels: - - "traefik.enable=true" - - "traefik.http.routers.whoami.rule=Host(`whoami.docker.localhost`)" - - "traefik.http.routers.whoami.entrypoints=web" - -networks: - proxy: - name: proxy -``` - -Save this as `docker-compose.yml` and start the services: - -```bash -docker compose up -d -``` - -### Verify Your Service - -Your service is now available at http://whoami.docker.localhost/. Test that it works: - -```bash -curl -H "Host: whoami.docker.localhost" http://localhost/ -``` - -You should see output similar to: - -```bash -Hostname: whoami -IP: 127.0.0.1 -IP: ::1 -IP: 172.18.0.3 -IP: fe80::215:5dff:fe00:c9e -RemoteAddr: 172.18.0.2:55108 -GET / HTTP/1.1 -Host: whoami.docker.localhost -User-Agent: curl/7.68.0 -Accept: */* -Accept-Encoding: gzip -X-Forwarded-For: 172.18.0.1 -X-Forwarded-Host: whoami.docker.localhost -X-Forwarded-Port: 80 -X-Forwarded-Proto: http -X-Forwarded-Server: 5789f594e7d5 -X-Real-Ip: 172.18.0.1 -``` - -This confirms that Traefik is successfully routing requests to your whoami application. - -## Add Routing Rules - -Now we'll enhance our routing by directing traffic to different services based on [URL paths](../reference/routing-configuration/http/routing/rules-and-priority.md#path-pathprefix-and-pathregexp). This is useful for API versioning, frontend/backend separation, or organizing microservices. - -Update your `docker-compose.yml` to add another service: - -```yaml -# ... - -# New service - whoami-api: - image: "traefik/whoami" - networks: - - proxy - container_name: "whoami-api" - environment: - - WHOAMI_NAME=API Service - labels: - - "traefik.enable=true" - # Path-based routing - - "traefik.http.routers.whoami-api.rule=Host(`whoami.docker.localhost`) && PathPrefix(`/api`)" - - "traefik.http.routers.whoami-api.entrypoints=web" -``` - -Apply the changes: - -```bash -docker compose up -d -``` - -### Test the Path-Based Routing - -Verify that different paths route to different services: - -```bash -# Root path should go to the main whoami service -curl -H "Host: whoami.docker.localhost" http://localhost/ - -# /api path should go to the whoami-api service -curl -H "Host: whoami.docker.localhost" http://localhost/api -``` - -For the `/api` requests, you should see the response showing "API Service" in the environment variables section, confirming that your path-based routing is working correctly. - -## Enable TLS - -Let's secure our service with HTTPS by adding TLS. We'll start with a self-signed certificate for local development. - -### Create a Self-Signed Certificate - -Generate a self-signed certificate: - -```bash -mkdir -p certs -openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ - -keyout certs/local.key -out certs/local.crt \ - -subj "/CN=*.docker.localhost" -``` - -Create a directory for dynamic configuration and add a TLS configuration file: - -```bash -mkdir -p dynamic -cat > dynamic/tls.yml << EOF -tls: - certificates: - - certFile: /certs/local.crt - keyFile: /certs/local.key -EOF -``` - -Update your `docker-compose.yml` file with the following changes: - -```yaml -services: - traefik: - image: "traefik:v3.4" - container_name: "traefik" - restart: unless-stopped - security_opt: - - no-new-privileges:true - networks: - - proxy - command: - - "--api.insecure=false" - - "--api.dashboard=true" - - "--providers.docker=true" - - "--providers.docker.exposedbydefault=false" - - "--providers.docker.network=proxy" - - "--providers.file.directory=/etc/traefik/dynamic" - - "--entryPoints.web.address=:80" - - "--entryPoints.websecure.address=:443" - - "--entryPoints.websecure.http.tls=true" - ports: - - "80:80" - - "443:443" - - "8080:8080" - volumes: - - "/var/run/docker.sock:/var/run/docker.sock:ro" - # Add the following volumes - - "./certs:/certs:ro" - - "./dynamic:/etc/traefik/dynamic:ro" - labels: - - "traefik.enable=true" - - "traefik.http.routers.dashboard.rule=Host(`dashboard.docker.localhost`)" - - "traefik.http.routers.dashboard.entrypoints=websecure" - - "traefik.http.routers.dashboard.service=api@internal" - # Add the following label - - "traefik.http.routers.dashboard.tls=true" - - whoami: - image: "traefik/whoami" - restart: unless-stopped - networks: - - proxy - labels: - - "traefik.enable=true" - - "traefik.http.routers.whoami.rule=Host(`whoami.docker.localhost`)" - - "traefik.http.routers.whoami.entrypoints=websecure" - # Add the following label - - "traefik.http.routers.whoami.tls=true" - - whoami-api: - image: "traefik/whoami" - container_name: "whoami-api" - restart: unless-stopped - networks: - - proxy - environment: - - WHOAMI_NAME=API Service - labels: - - "traefik.enable=true" - - "traefik.http.routers.whoami-api.rule=Host(`whoami.docker.localhost`) && PathPrefix(`/api`)" - - "traefik.http.routers.whoami-api.entrypoints=websecure" - # Add the following label - - "traefik.http.routers.whoami-api.tls=true" - -networks: - proxy: - name: proxy -``` - -Apply the changes: - -```bash -docker compose up -d -``` - -Your browser can access https://whoami.docker.localhost/ for the service. You'll need to accept the security warning for the self-signed certificate. - -## Add Middlewares - -Middlewares allow you to modify requests or responses as they pass through Traefik. Let's add two useful middlewares: [Headers](../reference/routing-configuration/http/middlewares/headers.md) for security and [IP allowlisting](../reference/routing-configuration/http/middlewares/ipallowlist.md) for access control. - -Add the following labels to your whoami service in `docker-compose.yml`: - -```yaml -labels: - - # Secure Headers Middleware - - "traefik.http.middlewares.secure-headers.headers.frameDeny=true" - - "traefik.http.middlewares.secure-headers.headers.sslRedirect=true" - - "traefik.http.middlewares.secure-headers.headers.browserXssFilter=true" - - "traefik.http.middlewares.secure-headers.headers.contentTypeNosniff=true" - - "traefik.http.middlewares.secure-headers.headers.stsIncludeSubdomains=true" - - "traefik.http.middlewares.secure-headers.headers.stsPreload=true" - - "traefik.http.middlewares.secure-headers.headers.stsSeconds=31536000" - - # IP Allowlist Middleware - - "traefik.http.middlewares.ip-allowlist.ipallowlist.sourceRange=127.0.0.1/32,192.168.0.0/16,10.0.0.0/8" - - # Apply middlewares to whoami router - - "traefik.http.routers.whoami.middlewares=secure-headers,ip-allowlist" -``` - -Add the same middleware to your whoami-api service: - -```yaml -labels: - - "traefik.http.routers.whoami-api.middlewares=secure-headers,ip-allowlist" -``` - -Apply the changes: - -```bash -docker compose up -d -``` - -### Test the Middlewares - -Now let's verify that our middlewares are working correctly: - -Test the Secure Headers middleware: - -```bash -curl -k -I -H "Host: whoami.docker.localhost" https://localhost/ -``` - -In the response headers, you should see security headers set by the middleware: - -- `X-Frame-Options: DENY` -- `X-Content-Type-Options: nosniff` -- `X-XSS-Protection: 1; mode=block` -- `Strict-Transport-Security` with the appropriate settings - -Test the IP Allowlist middleware: - -If your request comes from an IP that's in the allow list (e.g., 127.0.0.1), it should succeed: - -```bash -curl -k -I -H "Host: whoami.docker.localhost" https://localhost/ -``` - -If you try to access from an IP not in the allow list, the request will be rejected with a `403` Forbidden response. To simulate this in a local environment, you can modify the middleware configuration temporarily to exclude your IP address, then test again. - -## Generate Certificates with Let's Encrypt - -Let's Encrypt provides free, automated TLS certificates. Let's configure Traefik to automatically obtain and renew certificates for our services. - -Instead of using self-signed certificates, update your existing `docker-compose.yml` file with the following changes: - -Add the Let's Encrypt certificate resolver to the Traefik service command section: - -```yaml -command: - - "--api.insecure=false" - - "--api.dashboard=true" - - "--providers.docker=true" - - "--providers.docker.exposedbydefault=false" - - "--providers.docker.network=proxy" - - "--entryPoints.web.address=:80" - - "--entryPoints.websecure.address=:443" - - "--entryPoints.websecure.http.tls=true" - - "--entryPoints.web.http.redirections.entryPoint.to=websecure" - - "--entryPoints.web.http.redirections.entryPoint.scheme=https" - # Let's Encrypt configuration - - "--certificatesresolvers.le.acme.email=your-email@example.com" # replace with your actual email - - "--certificatesresolvers.le.acme.storage=/letsencrypt/acme.json" - - "--certificatesresolvers.le.acme.httpchallenge.entrypoint=web" -``` - -Add a volume for Let's Encrypt certificates: - -```yaml -volumes: - # ...Existing volumes... - - "./letsencrypt:/letsencrypt" -``` - -Update your service labels to use the certificate resolver: - -```yaml -labels: - - "traefik.http.routers.whoami.tls.certresolver=le" -``` - -Do the same for any other services you want to secure: - -```yaml -labels: - - "traefik.http.routers.whoami-api.tls.certresolver=le" -``` - -Create a directory for storing Let's Encrypt certificates: - -```bash -mkdir -p letsencrypt -``` - -Apply the changes: - -```bash -docker compose up -d -``` - -!!! important "Public DNS Required" - Let's Encrypt may require a publicly accessible domain to validate domain ownership. For testing with local domains like `whoami.docker.localhost`, the certificate will remain self-signed. In production, replace it with a real domain that has a publicly accessible DNS record pointing to your Traefik instance. - -Once the certificate is issued, you can verify it: - -```bash -# Verify the certificate chain -curl -v https://whoami.docker.localhost/ 2>&1 | grep -i "server certificate" -``` - -You should see that your certificate is issued by Let's Encrypt. - -## Configure Sticky Sessions - -Sticky sessions ensure that a user's requests always go to the same backend server, which is essential for applications that maintain session state. Let's implement sticky sessions for our whoami service. - -### First, Add Sticky Session Labels - -Add the following labels to your whoami service in the `docker-compose.yml` file: - -```yaml -labels: - - "traefik.http.services.whoami.loadbalancer.sticky.cookie=true" - - "traefik.http.services.whoami.loadbalancer.sticky.cookie.name=sticky_cookie" - - "traefik.http.services.whoami.loadbalancer.sticky.cookie.secure=true" - - "traefik.http.services.whoami.loadbalancer.sticky.cookie.httpOnly=true" -``` - -Apply the changes: - -```bash -docker compose up -d -``` - -### Then, Scale Up the Service - -To demonstrate sticky sessions with Docker, use Docker Compose's scale feature: - -```bash -docker compose up -d --scale whoami=3 -``` - -This creates multiple instances of the whoami service. - -!!! important "Scaling After Configuration Changes" - If you run `docker compose up -d` after scaling, it will reset the number of whoami instances back to 1. Always scale after applying configuration changes and starting the services. - -### Test Sticky Sessions - -You can test the sticky sessions by making multiple requests and observing that they all go to the same backend container: - -```bash -# First request - save cookies to a file -curl -k -c cookies.txt -H "Host: whoami.docker.localhost" https://localhost/ - -# Subsequent requests - use the cookies -curl -k -b cookies.txt -H "Host: whoami.docker.localhost" https://localhost/ -curl -k -b cookies.txt -H "Host: whoami.docker.localhost" https://localhost/ -``` - -Pay attention to the `Hostname` field in each response - it should remain the same across all requests when using the cookie file, confirming that sticky sessions are working. - -For comparison, try making requests without the cookie: - -```bash -# Requests without cookies should be load-balanced across different containers -curl -k -H "Host: whoami.docker.localhost" https://localhost/ -curl -k -H "Host: whoami.docker.localhost" https://localhost/ -``` - -You should see different `Hostname` values in these responses, as each request is load-balanced to a different container. - -!!! important "Browser Testing" - When testing in browsers, you need to use the same browser session to maintain the cookie. The cookie is set with `httpOnly` and `secure` flags for security, so it will only be sent over HTTPS connections and won't be accessible via JavaScript. - -For more advanced configuration options, see the [reference documentation](../reference/routing-configuration/http/load-balancing/service.md). - -## Conclusion - -In this guide, you've learned how to: - -- Expose HTTP services through Traefik in Docker -- Set up path-based routing to direct traffic to different backend services -- Secure your services with TLS using self-signed certificates -- Add security with middlewares like secure headers and IP allow listing -- Automate certificate management with Let's Encrypt -- Implement sticky sessions for stateful applications - -These fundamental capabilities provide a solid foundation for exposing any application through Traefik Proxy in Docker. Each of these can be further customized to meet your specific requirements. - -### Next Steps - -Now that you understand the basics of exposing services with Traefik Proxy, you might want to explore: - -- [Advanced routing options](../reference/routing-configuration/http/routing/rules-and-priority.md) like query parameter matching, header-based routing, and more -- [Additional middlewares](../reference/routing-configuration/http/middlewares/overview.md) for authentication, rate limiting, and request modifications -- [Observability features](../reference/install-configuration/observability/metrics.md) for monitoring and debugging your Traefik deployment -- [TCP services](../reference/routing-configuration/tcp/service.md) for exposing TCP services -- [UDP services](../reference/routing-configuration/udp/service.md) for exposing UDP services -- [Docker provider documentation](../reference/install-configuration/providers/docker.md) for more details about the Docker integration diff --git a/docs/content/expose/kubernetes.md b/docs/content/expose/kubernetes.md deleted file mode 100644 index 7abac99e6..000000000 --- a/docs/content/expose/kubernetes.md +++ /dev/null @@ -1,1014 +0,0 @@ -# Exposing Services with Traefik on Kubernetes - -This guide will help you expose your services securely through Traefik Proxy on Kubernetes. We'll cover routing HTTP and HTTPS traffic, implementing TLS, adding security middleware, and configuring sticky sessions. For routing, this guide gives you two options: - -- [Gateway API](../reference/routing-configuration/kubernetes/gateway-api.md) -- [IngressRoute](../reference/routing-configuration/kubernetes/crd/http/ingressroute.md) - -Feel free to choose the one that fits your needs best. - -## Prerequisites - -- A Kubernetes cluster with Traefik Proxy installed -- `kubectl` configured to interact with your cluster -- Traefik deployed using the Traefik Kubernetes Setup guide - -## Expose Your First HTTP Service - -Let's expose a simple HTTP service using the [whoami](https://github.com/traefik/whoami) application. This will demonstrate basic routing to a backend service. - -First, create the deployment and service: - -```yaml -apiVersion: apps/v1 -kind: Deployment -metadata: - name: whoami - namespace: default -spec: - replicas: 2 - selector: - matchLabels: - app: whoami - template: - metadata: - labels: - app: whoami - spec: - containers: - - name: whoami - image: traefik/whoami - ports: - - containerPort: 80 ---- -apiVersion: v1 -kind: Service -metadata: - name: whoami - namespace: default -spec: - selector: - app: whoami - ports: - - port: 80 -``` - -Save this as `whoami.yaml` and apply it: - -```bash -kubectl apply -f whoami.yaml -``` - -Now, let's create routes using either Gateway API or IngressRoute. - -### Using Gateway API - -```yaml -apiVersion: gateway.networking.k8s.io/v1 -kind: HTTPRoute -metadata: - name: whoami - namespace: default -spec: - parentRefs: - - name: traefik-gateway # This Gateway is automatically created by Traefik - hostnames: - - "whoami.docker.localhost" - rules: - - matches: - - path: - type: PathPrefix - value: / - backendRefs: - - name: whoami - port: 80 -``` - -Save this as `whoami-route.yaml` and apply it: - -```bash -kubectl apply -f whoami-route.yaml -``` - -### Using IngressRoute - -```yaml -apiVersion: traefik.io/v1alpha1 -kind: IngressRoute -metadata: - name: whoami - namespace: default -spec: - entryPoints: - - web - routes: - - match: Host(`whoami.docker.localhost`) - kind: Rule - services: - - name: whoami - port: 80 -``` - -Save this as `whoami-ingressroute.yaml` and apply it: - -```bash -kubectl apply -f whoami-ingressroute.yaml -``` - -### Verify Your Service - -Your service is now available at http://whoami.docker.localhost/. Test that it works: - -```bash -curl -H "Host: whoami.docker.localhost" http://localhost/ -``` - -!!! info - Make sure to remove the `ports.web.redirections` block from the `values.yaml` file if you followed the Kubernetes Setup Guide to install Traefik otherwise you will be redirected to the HTTPS entrypoint: - - ```yaml - redirections: - entryPoint: - to: websecure - ``` - -You should see output similar to: - -```bash -Hostname: whoami-6d5d964cb-8pv4k -IP: 127.0.0.1 -IP: ::1 -IP: 10.42.0.18 -IP: fe80::d4c0:3bff:fe20:b0a3 -RemoteAddr: 10.42.0.17:39872 -GET / HTTP/1.1 -Host: whoami.docker.localhost -User-Agent: curl/7.68.0 -Accept: */* -Accept-Encoding: gzip -X-Forwarded-For: 10.42.0.1 -X-Forwarded-Host: whoami.docker.localhost -X-Forwarded-Port: 80 -X-Forwarded-Proto: http -X-Forwarded-Server: traefik-76cbd5b89c-rx5xn -X-Real-Ip: 10.42.0.1 -``` - -This confirms that Traefik is successfully routing requests to your whoami application. - -## Add Routing Rules - -Now we'll enhance our routing by directing traffic to different services based on URL paths. This is useful for API versioning, frontend/backend separation, or organizing microservices. - -First, deploy a second service to represent an API: - -```yaml -apiVersion: apps/v1 -kind: Deployment -metadata: - name: whoami-api - namespace: default -spec: - replicas: 1 - selector: - matchLabels: - app: whoami-api - template: - metadata: - labels: - app: whoami-api - spec: - containers: - - name: whoami - image: traefik/whoami - env: - - name: WHOAMI_NAME - value: "API Service" - ports: - - containerPort: 80 ---- -apiVersion: v1 -kind: Service -metadata: - name: whoami-api - namespace: default -spec: - selector: - app: whoami-api - ports: - - port: 80 -``` - -Save this as `whoami-api.yaml` and apply it: - -```bash -kubectl apply -f whoami-api.yaml -``` - -Now set up path-based routing: - -### Gateway API with Path Rules - -Update your existing `HTTPRoute` to include path-based routing: - -```yaml -apiVersion: gateway.networking.k8s.io/v1 -kind: HTTPRoute -metadata: - name: whoami - namespace: default -spec: - parentRefs: - - name: traefik-gateway - hostnames: - - "whoami.docker.localhost" - rules: - - matches: - - path: - type: PathPrefix - value: /api - backendRefs: - - name: whoami-api - port: 80 - - matches: - - path: - type: PathPrefix - value: / - backendRefs: - - name: whoami - port: 80 -``` - -Update the file `whoami-route.yaml` and apply it: - -```bash -kubectl apply -f whoami-route.yaml -``` - -### IngressRoute with Path Rules - -Update your existing IngressRoute to include path-based routing: - -```yaml -apiVersion: traefik.io/v1alpha1 -kind: IngressRoute -metadata: - name: whoami - namespace: default -spec: - entryPoints: - - web - routes: - - match: Host(`whoami.docker.localhost`) && Path(`/api`) - kind: Rule - services: - - name: whoami-api - port: 80 - - match: Host(`whoami.docker.localhost`) - kind: Rule - services: - - name: whoami - port: 80 -``` - -Save this as `whoami-ingressroute.yaml` and apply it: - -```bash -kubectl apply -f whoami-ingressroute.yaml -``` - -### Test the Path-Based Routing - -Verify that different paths route to different services: - -```bash -# Root path should go to the main whoami service -curl -H "Host: whoami.docker.localhost" http://localhost/ - -# /api path should go to the whoami-api service -curl -H "Host: whoami.docker.localhost" http://localhost/api -``` - -For the `/api` requests, you should see the response showing "API Service" in the environment variables section, confirming that your path-based routing is working correctly: - -```bash -{"hostname":"whoami-api-67d97b4868-dvvll","ip":["127.0.0.1","::1","10.42.0.9","fe80::10aa:37ff:fe74:31f2"],"headers":{"Accept":["*/*"],"Accept-Encoding":["gzip"],"User-Agent":["curl/8.7.1"],"X-Forwarded-For":["10.42.0.1"],"X-Forwarded-Host":["whoami.docker.localhost"],"X-Forwarded-Port":["80"],"X-Forwarded-Proto":["http"],"X-Forwarded-Server":["traefik-669c479df8-vkj22"],"X-Real-Ip":["10.42.0.1"]},"url":"/api","host":"whoami.docker.localhost","method":"GET","name":"API Service","remoteAddr":"10.42.0.13:36592"} -``` - -## Enable TLS - -Let's secure our service with HTTPS by adding TLS. We'll start with a self-signed certificate for local development. - -### Create a Self-Signed Certificate - -Generate a self-signed certificate: - -```bash -openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ - -keyout tls.key -out tls.crt \ - -subj "/CN=whoami.docker.localhost" -``` - -Create a TLS secret in Kubernetes: - -```bash -kubectl create secret tls whoami-tls --cert=tls.crt --key=tls.key -``` - -!!! important "Prerequisite for Gateway API with TLS" - Before using the Gateway API with TLS, you must define the `websecure` listener in your Traefik installation. This is typically done in your Helm values. - - Example configuration in `values.yaml`: - ```yaml - gateway: - listeners: - web: - port: 80 - protocol: HTTP - namespacePolicy: - from: All - websecure: - port: 443 - protocol: HTTPS - namespacePolicy: - from: All - mode: Terminate - certificateRefs: - - kind: Secret - name: local-selfsigned-tls - group: "" - ``` - - See the Traefik Kubernetes Setup Guide for complete installation details. - -### Gateway API with TLS - -Update your existing `HTTPRoute` to use the secured gateway listener: - -```yaml -apiVersion: gateway.networking.k8s.io/v1 -kind: HTTPRoute -metadata: - name: whoami - namespace: default -spec: - parentRefs: - - name: traefik-gateway - sectionName: websecure # The HTTPS listener - hostnames: - - "whoami.docker.localhost" - rules: - - matches: - - path: - type: PathPrefix - value: /api - backendRefs: - - name: whoami-api - port: 80 - - matches: - - path: - type: PathPrefix - value: / - backendRefs: - - name: whoami - port: 80 -``` - -Update the file `whoami-route.yaml` and apply it: - -```bash -kubectl apply -f whoami-route.yaml -``` - -### IngressRoute with TLS - -Update your existing IngressRoute to use TLS: - -```yaml -apiVersion: traefik.io/v1alpha1 -kind: IngressRoute -metadata: - name: whoami - namespace: default -spec: - entryPoints: - - websecure # Changed from 'web' to 'websecure' - routes: - - match: Host(`whoami.docker.localhost`) && Path(`/api`) - kind: Rule - services: - - name: whoami-api - port: 80 - - match: Host(`whoami.docker.localhost`) - kind: Rule - services: - - name: whoami - port: 80 - tls: - secretName: whoami-tls # Added TLS configuration -``` - -Update the file `whoami-ingressroute.yaml` and apply it: - -```bash -kubectl apply -f whoami-ingressroute.yaml -``` - -### Verify HTTPS Access - -Now you can access your service securely. Since we're using a self-signed certificate, you'll need to skip certificate verification: - -```bash -curl -k -H "Host: whoami.docker.localhost" https://localhost/ -``` - -Your browser can also access https://whoami.docker.localhost/ (you'll need to accept the security warning for the self-signed certificate). - -## Add Middlewares - -Middlewares allow you to modify requests or responses as they pass through Traefik. Let's add two useful middlewares: [Headers](../reference/routing-configuration/http/middlewares/headers.md) for security and [IP allowlisting](../reference/routing-configuration/http/middlewares/ipallowlist.md) for access control. - -### Create Middlewares - -```yaml -apiVersion: traefik.io/v1alpha1 -kind: Middleware -metadata: - name: secure-headers - namespace: default -spec: - headers: - frameDeny: true - sslRedirect: true - browserXssFilter: true - contentTypeNosniff: true - stsIncludeSubdomains: true - stsPreload: true - stsSeconds: 31536000 ---- -apiVersion: traefik.io/v1alpha1 -kind: Middleware -metadata: - name: ip-allowlist - namespace: default -spec: - ipAllowList: - sourceRange: - - 127.0.0.1/32 - - 10.0.0.0/8 # Typical cluster network range - - 192.168.0.0/16 # Common local network range -``` - -Save this as `middlewares.yaml` and apply it: - -```bash -kubectl apply -f middlewares.yaml -``` - -### Apply Middlewares with Gateway API - -In Gateway API, you can apply middlewares using the `ExtensionRef` filter type. This is the preferred and standard way to use Traefik middlewares with Gateway API, as it integrates directly with the HTTPRoute specification. - -First, make sure you have the same middlewares defined: - -```yaml -apiVersion: traefik.io/v1alpha1 -kind: Middleware -metadata: - name: secure-headers - namespace: default -spec: - headers: - frameDeny: true - sslRedirect: true - browserXssFilter: true - contentTypeNosniff: true - stsIncludeSubdomains: true - stsPreload: true - stsSeconds: 31536000 ---- -apiVersion: traefik.io/v1alpha1 -kind: Middleware -metadata: - name: ip-allowlist - namespace: default -spec: - ipAllowList: - sourceRange: - - 127.0.0.1/32 - - 10.0.0.0/8 # Typical cluster network range - - 192.168.0.0/16 # Common local network range -``` - -Now, update your `HTTPRoute` to reference these middlewares using the `ExtensionRef` filter: - -```yaml -apiVersion: gateway.networking.k8s.io/v1 -kind: HTTPRoute -metadata: - name: whoami - namespace: default -spec: - parentRefs: - - name: traefik-gateway - sectionName: websecure - hostnames: - - "whoami.docker.localhost" - rules: - - matches: - - path: - type: PathPrefix - value: /api - filters: - - type: ExtensionRef - extensionRef: # Headers Middleware Definition - group: traefik.io - kind: Middleware - name: secure-headers - - type: ExtensionRef - extensionRef: # IP AllowList Middleware Definition - group: traefik.io - kind: Middleware - name: ip-allowlist - backendRefs: - - name: whoami-api - port: 80 - - matches: - - path: - type: PathPrefix - value: / - filters: - - type: ExtensionRef - extensionRef: # Headers Middleware Definition - group: traefik.io - kind: Middleware - name: secure-headers - - type: ExtensionRef - extensionRef: # IP AllowList Middleware Definition - group: traefik.io - kind: Middleware - name: ip-allowlist - backendRefs: - - name: whoami - port: 80 -``` - -Update the file `whoami-route.yaml` and apply it: - -```bash -kubectl apply -f whoami-route.yaml -``` - -This approach uses the Gateway API's native filter mechanism rather than annotations. The `ExtensionRef` filter type allows you to reference Traefik middlewares directly within the HTTPRoute specification, which is more consistent with the Gateway API design principles. - -### Apply Middlewares with IngressRoute - -Update your existing IngressRoute to include middlewares: - -```yaml -apiVersion: traefik.io/v1alpha1 -kind: IngressRoute -metadata: - name: whoami - namespace: default -spec: - entryPoints: - - websecure - routes: - - match: Host(`whoami.docker.localhost`) && Path(`/api`) - kind: Rule - middlewares: # Middleware Definition - - name: secure-headers - - name: ip-allowlist - services: - - name: whoami-api - port: 80 - - match: Host(`whoami.docker.localhost`) - kind: Rule - middlewares: # Middleware Definition - - name: secure-headers - - name: ip-allowlist - services: - - name: whoami - port: 80 - tls: - certResolver: le -``` - -Update the file `whoami-ingressroute.yaml` and apply it: - -```bash -kubectl apply -f whoami-ingressroute.yaml -``` - -### Verify Middleware Effects - -Check that the security headers are being applied: - -```bash -curl -k -I -H "Host: whoami.docker.localhost" https://localhost/ -``` - -You should see security headers in the response, such as: - -```bash -HTTP/2 200 -x-content-type-options: nosniff -x-frame-options: DENY -x-xss-protection: 1; mode=block -strict-transport-security: max-age=31536000; includeSubDomains; preload -content-type: text/plain; charset=utf-8 -content-length: 403 -``` - -To test the IP allowlist, you can modify the `sourceRange` in the middleware to exclude your IP and verify that access is blocked. - -## Generate Certificates with Let's Encrypt - -!!! info - Traefik's built-in Let's Encrypt integration works with IngressRoute but does not automatically issue certificates for Gateway API listeners. For Gateway API, you should use cert-manager or another certificate controller. - -### Using IngressRoute with Let's Encrypt - -Configure a certificate resolver in your Traefik values.yaml: - -```yaml -additionalArguments: - - "--certificatesresolvers.le.acme.email=your-email@example.com" #replace with your email - - "--certificatesresolvers.le.acme.storage=/data/acme.json" - - "--certificatesresolvers.le.acme.httpchallenge.entrypoint=web" -``` - -!!! important "Public DNS Required" - Let's Encrypt may require a publicly accessible domain to validate domain ownership. For testing with local domains like `whoami.docker.localhost`, the certificate will remain self-signed. In production, replace it with a real domain that has a publicly accessible DNS record pointing to your Traefik instance. - -Update your Traefik installation with this configuration: - -```bash -helm upgrade traefik traefik/traefik -n traefik --reuse-values -f values.yaml -``` - -Update your IngressRoute with the Let's Encrypt certificate: - -```yaml -apiVersion: traefik.io/v1alpha1 -kind: IngressRoute -metadata: - name: whoami - namespace: default -spec: - entryPoints: - - websecure - routes: - - match: Host(`whoami.docker.localhost`) && Path(`/api`) - kind: Rule - middlewares: - - name: secure-headers - - name: ip-allowlist - services: - - name: whoami-api - port: 80 - - match: Host(`whoami.docker.localhost`) - kind: Rule - middlewares: - - name: secure-headers - - name: ip-allowlist - services: - - name: whoami - port: 80 - tls: - certResolver: le -``` - -Apply it: - -```bash -kubectl apply -f whoami-ingressroute.yaml -``` - -### Using Gateway API with cert-manager - -For Gateway API, install cert-manager: - -```bash -kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.10.0/cert-manager.yaml -``` - -Create an Issuer & Certificate: - -```yaml -apiVersion: cert-manager.io/v1 -kind: Issuer -metadata: - name: letsencrypt -spec: - acme: - email: your-email@example.com # replace with your email - server: https://acme-v02-staging.api.letsencrypt.org/directory # Replace with the production server in production - privateKeySecretRef: - name: letsencrypt-account-key - solvers: - - http01: - gatewayHTTPRoute: - parentRefs: - - name: traefik - namespace: default - kind: Gateway ---- -apiVersion: cert-manager.io/v1 -kind: Certificate -metadata: - name: whoami - namespace: default -spec: - secretName: whoami-tls-le # Name of secret where the generated certificate will be stored. - dnsNames: - - "whoami.docker.localhost" # Replace a real domain - issuerRef: - name: letsencrypt - kind: Issuer -``` - -!!! important "Public DNS Required" - Let's Encrypt requires a publicly accessible domain to verify ownership. When using a local domain like `whoami.docker.localhost`, cert-manager will attempt the challenge but it will fail, and the certificate will remain self-signed. For production use, replace the domain with one that has a public DNS record pointing to your cluster's ingress point. - -Save the YAML file and apply: - -```bash -kubectl apply -f letsencrypt-issuer-andwhoami-certificate.yaml -``` - -Now, update your Gateway to use the generated certificate: - -```yaml -apiVersion: gateway.networking.k8s.io/v1 -kind: Gateway -metadata: - name: traefik-gateway - namespace: default -spec: - gatewayClassName: traefik - listeners: - - name: web - port: 80 - protocol: HTTP - allowedRoutes: - namespaces: - from: All - - name: websecure - port: 443 - protocol: HTTPS - allowedRoutes: - namespaces: - from: All - tls: - certificateRefs: - - name: whoami-tls-le # References the secret created by cert-manager -``` - -Apply the updated Gateway: - -```bash -kubectl apply -f gateway.yaml -``` - -Your existing `HTTPRoute` will now use this certificate when connecting to the secured gateway listener. - -### Verify the Let's Encrypt Certificate - -Once the certificate is issued, you can verify it: - -```bash -# Check certificate status -kubectl get certificate -n default - -# Verify the certificate chain -curl -v https://whoami.docker.localhost/ 2>&1 | grep -i "server certificate" -``` - -You should see that your certificate is issued by Let's Encrypt. - -## Configure Sticky Sessions - -Sticky sessions ensure that a user's requests always go to the same backend server, which is essential for applications that maintain session state. Let's implement sticky sessions for our whoami service. - -### First, Scale Up the Deployment - -To demonstrate sticky sessions, first scale up the deployment to 3 replicas: - -```bash -kubectl scale deployment whoami --replicas=3 -``` - -### Using Gateway API with TraefikService - -First, create the `TraefikService` for sticky sessions: - -```yaml -apiVersion: traefik.io/v1alpha1 -kind: TraefikService -metadata: - name: whoami-sticky - namespace: default -spec: - weighted: - services: - - name: whoami - port: 80 - weight: 1 - sticky: - cookie: - name: sticky_cookie - secure: true - httpOnly: true -``` - -Save this as `whoami-sticky-service.yaml` and apply it: - -```bash -kubectl apply -f whoami-sticky-service.yaml -``` - -Now update your `HTTPRoute` with an annotation referencing the `TraefikService`: - -```yaml -apiVersion: gateway.networking.k8s.io/v1 -kind: HTTPRoute -metadata: - name: whoami - namespace: default -spec: - parentRefs: - - name: traefik-gateway - sectionName: websecure - hostnames: - - "whoami.docker.localhost" - rules: - - matches: - - path: - type: PathPrefix - value: /api - filters: - - type: ExtensionRef - extensionRef: # Headers Middleware Definition - group: traefik.io - kind: Middleware - name: secure-headers - - type: ExtensionRef - extensionRef: # IP AllowList Middleware Definition - group: traefik.io - kind: Middleware - name: ip-allowlist - backendRefs: - - name: whoami-api - port: 80 - - matches: - - path: - type: PathPrefix - value: / - backendRefs: - - group: traefik.io # <── tell Gateway this is a TraefikService - kind: TraefikService - name: whoami-sticky - filters: - - type: ExtensionRef - extensionRef: # Headers Middleware Definition - group: traefik.io - kind: Middleware - name: secure-headers - - type: ExtensionRef - extensionRef: # IP AllowList Middleware Definition - group: traefik.io - kind: Middleware - name: ip-allowlist - backendRefs: - - name: whoami - port: 80 -``` - -Update the file `whoami-route.yaml` and apply it: - -```bash -kubectl apply -f whoami-route.yaml -``` - -### Using IngressRoute with TraefikService - -First, create the `TraefikService` for sticky sessions: - -```yaml -apiVersion: traefik.io/v1alpha1 -kind: TraefikService -metadata: - name: whoami-sticky - namespace: default -spec: - weighted: - services: - - name: whoami - port: 80 - sticky: - cookie: - name: sticky_cookie - secure: true - httpOnly: true -``` - -Save this as `whoami-sticky-service.yaml` and apply it: - -```bash -kubectl apply -f whoami-sticky-service.yaml -``` - -Now update your IngressRoute to use this `TraefikService`: - -```yaml -apiVersion: traefik.io/v1alpha1 -kind: IngressRoute -metadata: - name: whoami - namespace: default -spec: - entryPoints: - - websecure - routes: - - match: Host(`whoami.docker.localhost`) && Path(`/api`) - kind: Rule - middlewares: # Middleware Definition - - name: secure-headers - - name: ip-allowlist - services: - - name: whoami-api - port: 80 - - match: Host(`whoami.docker.localhost`) - kind: Rule - middlewares: # Middleware Definition - - name: secure-headers - - name: ip-allowlist - services: - - name: whoami-sticky # Changed from whoami to whoami-sticky - kind: TraefikService # Added kind: TraefikService - tls: - certResolver: le -``` - -Update the file `whoami-ingressroute.yaml` and apply it: - -```bash -kubectl apply -f whoami-ingressroute.yaml -``` - -### Test Sticky Sessions - -You can test the sticky sessions by making multiple requests and observing that they all go to the same backend pod: - -```bash -# First request - save cookies to a file -curl -k -c cookies.txt -H "Host: whoami.docker.localhost" https://localhost/ - -# Subsequent requests - use the cookies -curl -k -b cookies.txt -H "Host: whoami.docker.localhost" https://localhost/ -curl -k -b cookies.txt -H "Host: whoami.docker.localhost" https://localhost/ -``` - -Pay attention to the `Hostname` field in each response - it should remain the same across all requests when using the cookie file, confirming that sticky sessions are working. - -For comparison, try making requests without the cookie: - -```bash -# Requests without cookies should be load-balanced across different pods -curl -k -H "Host: whoami.docker.localhost" https://localhost/ -curl -k -H "Host: whoami.docker.localhost" https://localhost/ -``` - -You should see different `Hostname` values in these responses, as each request is load-balanced to a different pod. - -!!! important "Browser Testing" - When testing in browsers, you need to use the same browser session to maintain the cookie. The cookie is set with `httpOnly` and `secure` flags for security, so it will only be sent over HTTPS connections and won't be accessible via JavaScript. - -For more advanced configuration options, see the [reference documentation](../reference/routing-configuration/http/load-balancing/service.md). - -## Conclusion - -In this guide, you've learned how to: - -- Expose HTTP services through Traefik in Kubernetes using both Gateway API and IngressRoute -- Set up path-based routing to direct traffic to different backend services -- Secure your services with TLS using self-signed certificates -- Add security with middlewares like secure headers and IP allow listing -- Automate certificate management with Let's Encrypt -- Implement sticky sessions for stateful applications - -These fundamental capabilities provide a solid foundation for exposing any application through Traefik Proxy in Kubernetes. Each of these can be further customized to meet your specific requirements. - -### Next Steps - -Now that you understand the basics of exposing services with Traefik Proxy, you might want to explore: - -- [Advanced routing options](../reference/routing-configuration/http/routing/rules-and-priority.md) like query parameter matching, header-based routing, and more -- [Additional middlewares](../reference/routing-configuration/http/middlewares/overview.md) for authentication, rate limiting, and request modifications -- [Observability features](../reference/install-configuration/observability/metrics.md) for monitoring and debugging your Traefik deployment -- [TCP services](../reference/routing-configuration/tcp/service.md) for exposing TCP services -- [UDP services](../reference/routing-configuration/udp/service.md) for exposing UDP services -- [Kubernetes Provider documentation](../reference/install-configuration/providers/kubernetes/kubernetes-crd.md) for more details about the Kubernetes integration. -- [Gateway API provider documentation](../reference/install-configuration/providers/kubernetes/kubernetes-gateway.md) for more details about the Gateway API integration. diff --git a/docs/content/expose/overview.md b/docs/content/expose/overview.md deleted file mode 100644 index 641203a65..000000000 --- a/docs/content/expose/overview.md +++ /dev/null @@ -1,22 +0,0 @@ -# Exposing Services with Traefik Proxy - -This section guides you through exposing services securely with Traefik Proxy. You'll learn how to route HTTP and HTTPS traffic to your services, add security features, and implement advanced load balancing. - -## What You'll Accomplish - -Following these guides, you'll learn how to: - -- Route HTTP traffic to your services with [Gateway API](../reference/routing-configuration/kubernetes/gateway-api.md) and [IngressRoute](../reference/routing-configuration/kubernetes/crd/http/ingressroute.md) -- Configure routing rules to direct requests -- Enable HTTPS with TLS -- Add security middlewares -- Generate certificates automatically with Let's Encrypt -- Implement sticky sessions for session persistence - -## Platform-Specific Guides - -For detailed steps tailored to your environment, follow the guide for your platform: - -- [Kubernetes](./kubernetes.md) -- [Docker](./docker.md) -- [Docker Swarm](./swarm.md) diff --git a/docs/content/expose/swarm.md b/docs/content/expose/swarm.md deleted file mode 100644 index 67a01663a..000000000 --- a/docs/content/expose/swarm.md +++ /dev/null @@ -1,401 +0,0 @@ -# Exposing Services with Traefik on Docker Swarm - -This guide will help you expose your services securely through Traefik Proxy using Docker Swarm. We'll cover routing HTTP and HTTPS traffic, implementing TLS, adding middlewares, Let's Encrypt integration, and sticky sessions. - -## Prerequisites - -- Docker Swarm cluster initialized -- Basic understanding of Docker Swarm concepts -- Traefik deployed using the Traefik Docker Swarm Setup guide - -## Expose Your First HTTP Service - -Let's expose a simple HTTP service using the [whoami](https://hub.docker.com/r/traefik/whoami) application. This will demonstrate basic routing to a backend service. - -First, update your existing `docker-compose.yml` file if you haven't already: - -```yaml -services: - whoami: - image: traefik/whoami - networks: - - traefik_proxy - deploy: - replicas: 3 - labels: - - "traefik.enable=true" - - "traefik.http.routers.whoami.rule=Host(`whoami.swarm.localhost`)" - - "traefik.http.routers.whoami.entrypoints=web,websecure" -``` - -Save this as `docker-compose.yml` and deploy the stack: - -```bash -docker stack deploy -c docker-compose.yml traefik -``` - -### Verify Your Service - -Your service is now available at http://whoami.swarm.localhost/. Test that it works: - -```bash -curl -H "Host: whoami.swarm.localhost" http://localhost/ -``` - -You should see output similar to: - -```bash -Hostname: whoami.1.7c8f7tr56q3p949rscxrkp80e -IP: 127.0.0.1 -IP: ::1 -IP: 10.0.1.8 -IP: fe80::215:5dff:fe00:c9e -RemoteAddr: 10.0.1.2:45098 -GET / HTTP/1.1 -Host: whoami.swarm.localhost -User-Agent: curl/7.68.0 -Accept: */* -Accept-Encoding: gzip -X-Forwarded-For: 10.0.1.1 -X-Forwarded-Host: whoami.swarm.localhost -X-Forwarded-Port: 80 -X-Forwarded-Proto: http -X-Forwarded-Server: 5789f594e7d5 -X-Real-Ip: 10.0.1.1 -``` - -This confirms that Traefik is successfully routing requests to your whoami application. - -## Add Routing Rules - -Now we'll enhance our routing by directing traffic to different services based on [URL paths](../reference/routing-configuration/http/routing/rules-and-priority.md#path-pathprefix-and-pathregexp). This is useful for API versioning, frontend/backend separation, or organizing microservices. - -Update your `docker-compose.yml` to add another service: - -```yaml -# ... - -# New service - whoami-api: - image: traefik/whoami - networks: - - traefik_proxy - environment: - - WHOAMI_NAME=API Service - deploy: - replicas: 2 - labels: - - "traefik.enable=true" - # Path-based routing - - "traefik.http.routers.whoami-api.rule=Host(`whoami.swarm.localhost`) && PathPrefix(`/api`)" - - "traefik.http.routers.whoami-api.entrypoints=web,websecure" - - "traefik.http.routers.whoami-api.service=whoami-api-svc" - - "traefik.http.services.whoami-api-svc.loadbalancer.server.port=80" - -# ... -``` - -Apply the changes: - -```bash -docker stack deploy -c docker-compose.yml traefik -``` - -### Test the Path-Based Routing - -Verify that different paths route to different services: - -```bash -# Root path should go to the main whoami service -curl -H "Host: whoami.swarm.localhost" http://localhost/ - -# /api path should go to the whoami-api service -curl -H "Host: whoami.swarm.localhost" http://localhost/api -``` - -For the `/api` requests, you should see the response showing "API Service" in the environment variables section, confirming that your path-based routing is working correctly. - -## Enable TLS - -Let's secure our service with HTTPS by adding TLS. We'll start with a self-signed certificate for local development. - -### Create a Self-Signed Certificate - -Generate a self-signed certificate and dynamic config file to tell Traefik where the cert lives: - -```bash -mkdir -p certs - -# key + cert (valid for one year) -openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ - -keyout certs/local.key -out certs/local.crt \ - -subj "/CN=*.swarm.localhost" - -# dynamic config that tells Traefik where the cert lives -cat > certs/tls.yml <<'EOF' -tls: - certificates: - - certFile: /certificates/local.crt - keyFile: /certificates/local.key -EOF -``` - -Create a Docker config for the certificate files: - -```bash -docker config create swarm-cert.crt certs/local.crt -docker config create swarm-cert.key certs/local.key -docker config create swarm-tls.yml certs/tls.yml -``` - -Update your `docker-compose.yml` file with the following changes: - -```yaml -# Add to the Traefik command section: -command: - # ... existing commands ... - - "--entryPoints.websecure.address=:443" - - "--entryPoints.websecure.http.tls=true" - - "--providers.file.directory=/etc/traefik/dynamic" -``` - -```yaml -# Add to the root of your docker-compose.yml file: -configs: - swarm-cert.crt: - file: ./certs/local.crt - swarm-cert.key: - file: ./certs/local.key - swarm-tls.yml: - file: ./certs/tls.yml -``` - -Deploy the stack: - -```bash -docker stack deploy -c docker-compose.yml traefik -``` - -Your browser can access https://whoami.swarm.localhost/ for the service. You'll need to accept the security warning for the self-signed certificate. - -## Add Middlewares - -Middlewares allow you to modify requests or responses as they pass through Traefik. Let's add two useful middlewares: [Headers](../reference/routing-configuration/http/middlewares/headers.md) for security and [IP allowlisting](../reference/routing-configuration/http/middlewares/ipallowlist.md) for access control. - -Add the following labels to your whoami service deployment section in `docker-compose.yml`: - -```yaml -deploy: - # ... existing configuration ... - labels: - # ... existing labels ... - - # Secure Headers Middleware - - "traefik.http.middlewares.secure-headers.headers.frameDeny=true" - - "traefik.http.middlewares.secure-headers.headers.sslRedirect=true" - - "traefik.http.middlewares.secure-headers.headers.browserXssFilter=true" - - "traefik.http.middlewares.secure-headers.headers.contentTypeNosniff=true" - - "traefik.http.middlewares.secure-headers.headers.stsIncludeSubdomains=true" - - "traefik.http.middlewares.secure-headers.headers.stsPreload=true" - - "traefik.http.middlewares.secure-headers.headers.stsSeconds=31536000" - - # IP Allowlist Middleware - - "traefik.http.middlewares.ip-allowlist.ipallowlist.sourceRange=127.0.0.1/32,192.168.0.0/16,10.0.0.0/8" - - # Apply the middlewares - - "traefik.http.routers.whoami.middlewares=secure-headers,ip-allowlist" -``` - -Add the same middleware to your whoami-api service: - -```yaml -deploy: - # ... existing configuration ... - labels: - # ... existing labels ... - - "traefik.http.routers.whoami-api.middlewares=secure-headers,ip-allowlist" -``` - -Apply the changes: - -```bash -docker stack deploy -c docker-compose.yml traefik -``` - -### Test the Middlewares - -Now let's verify that our middlewares are working correctly: - -Test the Secure Headers middleware: - -```bash -curl -k -I -H "Host: whoami.swarm.localhost" https://localhost/ -``` - -In the response headers, you should see security headers set by the middleware: - -- `X-Frame-Options: DENY` -- `X-Content-Type-Options: nosniff` -- `X-XSS-Protection: 1; mode=block` -- `Strict-Transport-Security` with the appropriate settings - -Test the IP Allowlist middleware: - -If your request comes from an IP that's in the allow list (e.g., 127.0.0.1), it should succeed: - -```bash -curl -k -I -H "Host: whoami.swarm.localhost" https://localhost/ -``` - -If you try to access from an IP not in the allow list, the request will be rejected with a `403` Forbidden response. To simulate this in a local environment, you can modify the middleware configuration temporarily to exclude your IP address, then test again. - -## Generate Certificates with Let's Encrypt - -Let's Encrypt provides free, automated TLS certificates. Let's configure Traefik to automatically obtain and renew certificates for our services. - -Instead of using self-signed certificates, update your existing `docker-compose.yml` file with the following changes: - -Add the Let's Encrypt certificate resolver to the Traefik service command section: - -```yaml -command: - # ... existing commands ... - # Let's Encrypt configuration - - "--certificatesresolvers.le.acme.email=your-email@example.com" # replace with your actual email - - "--certificatesresolvers.le.acme.storage=/letsencrypt/acme.json" - - "--certificatesresolvers.le.acme.httpchallenge.entrypoint=web" -``` - -Add a volume for Let's Encrypt certificates: - -```yaml -volumes: - # ...Existing volumes... - - letsencrypt:/letsencrypt -``` - -Update your service labels to use the certificate resolver: - -```yaml -labels: - # ... existing labels ... - - "traefik.http.routers.whoami.tls.certresolver=le" -``` - -Do the same for any other services you want to secure: - -```yaml -labels: - # ... existing labels ... - - "traefik.http.routers.whoami-api.tls.certresolver=le" -``` - -Create a named volume for storing Let's Encrypt certificates by adding to the volumes section: - -```yaml -volumes: - # ... existing volumes ... - letsencrypt: - driver: local -``` - -Apply the changes: - -```bash -docker stack deploy -c docker-compose.yml traefik -``` - -!!! important "Public DNS Required" - Let's Encrypt may require a publicly accessible domain to validate domain ownership. For testing with local domains like `whoami.swarm.localhost`, the certificate will remain self-signed. In production, replace it with a real domain that has a publicly accessible DNS record pointing to your Traefik instance. - -Once the certificate is issued, you can verify it: - -```bash -# Verify the certificate chain -curl -v https://whoami.swarm.localhost/ 2>&1 | grep -i "server certificate" -``` - -You should see that your certificate is issued by Let's Encrypt. - -## Configure Sticky Sessions - -Sticky sessions ensure that a user's requests always go to the same backend server, which is essential for applications that maintain session state. Let's implement sticky sessions for our whoami service. - -Docker Swarm already has multiple replicas running; we'll now add sticky session configuration. Update your whoami service in the `docker-compose.yml` file: - -### Add Sticky Session Configuration - -Add the following labels to your whoami service in the `docker-compose.yml` file: - -```yaml -deploy: - # ... existing configuration ... - labels: - # ... existing labels ... - - # Sticky Sessions Configuration - - "traefik.http.services.whoami.loadbalancer.sticky.cookie=true" - - "traefik.http.services.whoami.loadbalancer.sticky.cookie.name=sticky_cookie" - - "traefik.http.services.whoami.loadbalancer.sticky.cookie.secure=true" - - "traefik.http.services.whoami.loadbalancer.sticky.cookie.httpOnly=true" -``` - -Apply the changes: - -```bash -docker stack deploy -c docker-compose.yml traefik -``` - -### Test Sticky Sessions - -You can test the sticky sessions by making multiple requests and observing that they all go to the same backend container: - -```bash -# First request - save cookies to a file -curl -k -c cookies.txt -H "Host: whoami.swarm.localhost" https://localhost/ - -# Subsequent requests - use the cookies -curl -k -b cookies.txt -H "Host: whoami.swarm.localhost" https://localhost/ -curl -k -b cookies.txt -H "Host: whoami.swarm.localhost" https://localhost/ -``` - -Pay attention to the `Hostname` field in each response - it should remain the same across all requests when using the cookie file, confirming that sticky sessions are working. - -For comparison, try making requests without the cookie: - -```bash -# Requests without cookies should be load-balanced across different containers -curl -k -H "Host: whoami.swarm.localhost" https://localhost/ -curl -k -H "Host: whoami.swarm.localhost" https://localhost/ -``` - -You should see different `Hostname` values in these responses, as each request is load-balanced to a different container. - -!!! important "Browser Testing" - When testing in browsers, you need to use the same browser session to maintain the cookie. The cookie is set with `httpOnly` and `secure` flags for security, so it will only be sent over HTTPS connections and won't be accessible via JavaScript. - -For more advanced configuration options, see the [reference documentation](../reference/routing-configuration/http/load-balancing/service.md). - -## Conclusion - -In this guide, you've learned how to: - -- Expose HTTP services through Traefik in Docker Swarm -- Set up path-based routing to direct traffic to different backend services -- Secure your services with TLS using self-signed certificates -- Add security with middlewares like secure headers and IP allow listing -- Automate certificate management with Let's Encrypt -- Implement sticky sessions for stateful applications - -These fundamental capabilities provide a solid foundation for exposing any application through Traefik Proxy in Docker Swarm. Each of these can be further customized to meet your specific requirements. - -### Next Steps - -Now that you understand the basics of exposing services with Traefik Proxy, you might want to explore: - -- [Advanced routing options](../reference/routing-configuration/http/routing/rules-and-priority.md) like query parameter matching, header-based routing, and more -- [Additional middlewares](../reference/routing-configuration/http/middlewares/overview.md) for authentication, rate limiting, and request modifications -- [Observability features](../reference/install-configuration/observability/metrics.md) for monitoring and debugging your Traefik deployment -- [TCP services](../reference/routing-configuration/tcp/service.md) for exposing TCP services -- [UDP services](../reference/routing-configuration/udp/service.md) for exposing UDP services -- [Docker provider documentation](../reference/install-configuration/providers/docker.md) for more details about the Docker integration diff --git a/docs/content/extend/extend-traefik.md b/docs/content/extend/extend-traefik.md deleted file mode 100644 index af0aa24f6..000000000 --- a/docs/content/extend/extend-traefik.md +++ /dev/null @@ -1,56 +0,0 @@ ---- -title: Extend Traefik -description: Extend Traefik with custom plugins using Yaegi and WebAssembly. ---- - -# Extend Traefik - -Plugins are a powerful feature for extending Traefik with custom features and behaviors. The [Plugin Catalog](https://plugins.traefik.io/) is a software-as-a-service (SaaS) platform that provides an exhaustive list of the existing plugins. - -??? note "Plugin Catalog Access" - You can reach the [Plugin Catalog](https://plugins.traefik.io/) from the Traefik Dashboard using the `Plugins` menu entry. - -## Add a new plugin to a Traefik instance - -To add a new plugin to a Traefik instance, you must change that instance's install (static) configuration. Each plugin's **Install** section provides an install (static) configuration example. Many plugins have their own section in the Traefik routing (dynamic) configuration. - -!!! danger "Experimental Features" - Plugins can change the behavior of Traefik in unforeseen ways. Exercise caution when adding new plugins to production Traefik instances. - -To learn more about how to add a new plugin to a Traefik instance, please refer to the [developer documentation](https://plugins.traefik.io/install). - -## Plugin Systems - -Traefik supports two different plugin systems, each designed for different use cases and developer preferences. - -### Yaegi Plugin System - -Traefik [Yaegi](https://github.com/traefik/yaegi) plugins are developed using the Go language. It is essentially a Go package. Unlike pre-compiled plugins, Yaegi plugins are executed on the fly by Yaegi, a Go interpreter embedded in Traefik. - -This approach eliminates the need for compilation and a complex toolchain, making plugin development as straightforward as creating web browser extensions. Yaegi plugins support both middleware and provider functionality. - -#### Key characteristics - -- Written in Go language -- No compilation required -- Executed by embedded interpreter -- Supports full Go feature set -- Hot-reloadable during development - -### WebAssembly (WASM) Plugin System - -Traefik WASM plugins can be developed using any language that compiles to WebAssembly (WASM). This method is based on [http-wasm](https://http-wasm.io/). - -WASM plugins compile to portable binary modules that execute with near-native performance while maintaining security isolation. - -#### Key characteristics - -- Multi-language support (Go, Rust, C++, etc.) -- Compiled to WebAssembly binary -- Near-native performance -- Strong security isolation -- Currently supports middleware only - -## Build Your Own Plugins - -Traefik users can create their own plugins and share them with the community using the [Plugin Catalog](https://plugins.traefik.io/). To learn more about Traefik plugin creation, please refer to the [developer documentation](https://plugins.traefik.io/create). diff --git a/docs/content/getting-started/configuration-overview.md b/docs/content/getting-started/configuration-overview.md index 728004225..e3d74abd6 100644 --- a/docs/content/getting-started/configuration-overview.md +++ b/docs/content/getting-started/configuration-overview.md @@ -12,10 +12,10 @@ How the Magic Happens Configuration in Traefik can refer to two different things: -- The fully dynamic routing configuration (referred to as the _routing configuration_, formerly known as the _dynamic configuration_) -- The startup configuration (referred to as the _install configuration_, formerly known as the _static configuration_) +- The fully dynamic routing configuration (referred to as the _dynamic configuration_) +- The startup configuration (referred to as the _static configuration_) -Elements in the install configuration_ set up connections to [providers](../providers/overview.md) and define the [entrypoints](../routing/entrypoints.md) Traefik will listen to (these elements don't change often). +Elements in the _static configuration_ set up connections to [providers](../providers/overview.md) and define the [entrypoints](../routing/entrypoints.md) Traefik will listen to (these elements don't change often). The _dynamic configuration_ contains everything that defines how the requests are handled by your system. This configuration can change and is seamlessly hot-reloaded, without any request interruption or connection loss. @@ -32,9 +32,9 @@ Since this configuration is specific to your infrastructure choices, we invite y !!! info "" - In the [Quick Start example](../getting-started/quick-start.md), the routing configuration comes from docker in the form of labels attached to your containers. + In the [Quick Start example](../getting-started/quick-start.md), the dynamic configuration comes from docker in the form of labels attached to your containers. -!!! info "HTTPS Certificates also belong to the routing configuration." +!!! info "HTTPS Certificates also belong to the dynamic configuration." You can add / update / remove them without restarting your Traefik instance. @@ -79,14 +79,14 @@ traefik --help # or docker run traefik[:version] --help -# ex: docker run traefik:v3.5 --help +# ex: docker run traefik:v3.4 --help ``` -Check the [CLI reference](../reference/install-configuration/configuration-options.md "Link to CLI reference overview") for an overview about all available arguments. +Check the [CLI reference](../reference/static-configuration/cli.md "Link to CLI reference overview") for an overview about all available arguments. ### Environment Variables -All available environment variables can be found in the [static configuration environment overview](../reference/install-configuration/configuration-options.md). +All available environment variables can be found in the [static configuration environment overview](../reference/static-configuration/env.md). ## Available Configuration Options diff --git a/docs/content/getting-started/docker.md b/docs/content/getting-started/docker.md deleted file mode 100644 index 6cd151b32..000000000 --- a/docs/content/getting-started/docker.md +++ /dev/null @@ -1,162 +0,0 @@ ---- -title: "Docker and Traefik Quick Start" -description: "Deploy Traefik in Docker and expose your first service" ---- - -# Getting Started with Docker and Traefik - -Docker is a first-class citizen in Traefik, offering native support for Docker containers and services. -Whether you're using Docker Compose or running containers directly, Traefik provides a seamless experience for managing your Docker traffic. - -This guide shows you how to: - -- Install Traefik using Docker -- Expose the Traefik dashboard -- Deploy a sample application -- Configure basic routing - -## Prerequisites - -- Docker -- Docker Compose (optional) - -## Install Traefik - -### Using Docker Compose - -Create a Docker Compose file. -This configuration: - -- Exposes ports 80 and 8080. -- Enables the Docker provider -- Configures the dashboard with basic settings. Port 8080 serves the dashboard because we enabled `--api.insecure=true` (development use only) -- Mounts the Docker socket for container discovery - -```yaml -# docker-compose.yml -services: - traefik: - image: traefik:v3.5 - command: - - "--api.insecure=true" - - "--providers.docker=true" - - "--entrypoints.web.address=:80" - ports: - - "80:80" - - "8080:8080" - volumes: - - /var/run/docker.sock:/var/run/docker.sock -``` - -Start Traefik: - -```bash -docker-compose up -d -``` - -### Using Docker CLI - -Alternatively, you can run Traefik directly with Docker. -This command: - -- Exposes ports 80 and 8080 for web traffic and dashboard access -- Mounts the configuration file and Docker socket -- Uses the same configuration as the Docker Compose example - -Create a configuration file: - -```yaml -# traefik.yml -api: - insecure: true -entryPoints: - web: - address: ":80" -providers: - docker: {} -``` - -Start Traefik: - -```bash -docker run -d \ - -p 80:80 \ - -p 8080:8080 \ - -v $PWD/traefik.yml:/etc/traefik/traefik.yml \ - -v /var/run/docker.sock:/var/run/docker.sock \ - traefik:v3.5 -``` - -## Expose the Dashboard - -Because we explicitly enabled insecure mode, the [dashboard](../reference/install-configuration/api-dashboard.md) is reachable on port 8080 without authentication. -**Do not enable this flag in production**. - -You can access the dashboard at: - -[http://localhost:8080/dashboard/](http://localhost:8080/dashboard/) - -![Traefik Dashboard Screenshot](../assets/img/getting-started/traefik-dashboard.png) - -## Deploy a Sample Application - -Create a whoami service: - -```yaml -# whoami.yml -services: - whoami: - image: traefik/whoami - labels: - - "traefik.http.routers.whoami.rule=Host(`whoami.localhost`)" -``` - -Apply the configuration: - -```bash -docker-compose -f whoami.yml up -d -``` - -## Test Your Setup - -You can use the following curl command to verify that the application is correctly exposed: - -```bash -curl http://whoami.localhost - -Hostname: 068c0a29a8b7 -IP: 127.0.0.1 -IP: ::1 -IP: 192.168.147.3 -RemoteAddr: 192.168.147.2:56006 -GET / HTTP/1.1 -Host: whoami.localhost -User-Agent: curl/8.7.1 -Accept: */* -Accept-Encoding: gzip -X-Forwarded-For: 192.168.147.1 -X-Forwarded-Host: whoami.localhost -X-Forwarded-Port: 80 -X-Forwarded-Proto: http -X-Forwarded-Server: 9232cdd4fd6c -X-Real-Ip: 192.168.147.1 -``` - -You can also open [http://whoami.localhost](http://whoami.localhost) in a browser to test the application: - -![whoami application Screenshot](../assets/img/getting-started/whoami-localhost.png) - -If you navigate to the **HTTP Routers** section of the Traefik dashboard, you can see that the `whoami.localhost` route is managed by the Traefik Docker provider: - -![Traefik Dashboard HTTP Routers Section Screenshot](../assets/img/getting-started/docker-router.png) - -That's it! You've successfully deployed Traefik and configured routing in Docker. - -## Next Steps - -- [Configure TLS](../reference/routing-configuration/http/tls/overview.md) -- [Set up Middlewares](../reference/routing-configuration/http/middlewares/overview.md) -- [Enable Metrics](../reference/install-configuration/observability/metrics.md) -- [Learn more about Docker provider](../reference/install-configuration/providers/docker.md) - -{!traefik-for-business-applications.md!} diff --git a/docs/content/getting-started/faq.md b/docs/content/getting-started/faq.md index a64e04186..66f2d4d04 100644 --- a/docs/content/getting-started/faq.md +++ b/docs/content/getting-started/faq.md @@ -12,10 +12,10 @@ and while the documentation often demonstrates configuration options through fil the core feature of Traefik is its dynamic configurability, directly reacting to changes from providers over time. -Notably, a part of the configuration is [static](./configuration-overview.md#the-static-configuration), +Notably, a part of the configuration is [static](../configuration-overview/#the-static-configuration), and can be provided by a file on startup, whereas various providers, such as the file provider, -contribute dynamically all along the traefik instance lifetime to its [dynamic configuration](./configuration-overview.md#the-dynamic-configuration) changes. +contribute dynamically all along the traefik instance lifetime to its [dynamic configuration](../configuration-overview/#the-dynamic-configuration) changes. In addition, the configuration englobes concepts such as the EntryPoint which can be seen as a listener on the Transport Layer (TCP), as apposed to the Router which is more about the Presentation (TLS) and Application layers (HTTP). @@ -147,13 +147,13 @@ for example, by using the `touch` command on the configuration file. By default, the following headers are automatically added when proxying requests: -| Property | HTTP Header | -|---------------------------|--------------------------------| -| Client's IP | `X-Forwarded-For`, `X-Real-Ip` | -| Host | `X-Forwarded-Host` | -| Port | `X-Forwarded-Port` | -| Protocol | `X-Forwarded-Proto` | -| Proxy Server's Hostname | `X-Forwarded-Server` | +| Property | HTTP Header | +|---------------------------|----------------------------| +| Client's IP | X-Forwarded-For, X-Real-Ip | +| Host | X-Forwarded-Host | +| Port | X-Forwarded-Port | +| Protocol | X-Forwarded-Proto | +| Proxy Server's Hostname | X-Forwarded-Server | For more details, please check out the [forwarded header](../routing/entrypoints.md#forwarded-headers) documentation. diff --git a/docs/content/getting-started/index.md b/docs/content/getting-started/index.md deleted file mode 100644 index e9c71dc90..000000000 --- a/docs/content/getting-started/index.md +++ /dev/null @@ -1,25 +0,0 @@ ---- -title: "Getting Started with Traefik" -description: "Quick start guides for deploying Traefik in Kubernetes and Docker environments" ---- - -# Getting Started with Traefik - -Traefik can be deployed in various environments. Choose your preferred deployment method: - -- [Kubernetes Quick Start](./kubernetes.md) - Deploy Traefik using Helm -- [Docker Quick Start](./docker.md) - Deploy Traefik using Docker - -Each guide will help you: - -- Install Traefik -- Expose the dashboard -- Deploy a sample application -- Configure basic routing - -## Before You Begin - -Make sure you have the necessary prerequisites for your chosen environment: - -- **Kubernetes**: A running Kubernetes cluster, Helm 3, and kubectl -- **Docker**: Docker and optionally Docker Compose diff --git a/docs/content/getting-started/install-traefik.md b/docs/content/getting-started/install-traefik.md index 920db9530..51d9ca12f 100644 --- a/docs/content/getting-started/install-traefik.md +++ b/docs/content/getting-started/install-traefik.md @@ -16,12 +16,12 @@ You can install Traefik with the following flavors: Choose one of the [official Docker images](https://hub.docker.com/_/traefik) and run it with one sample configuration file: -* [YAML](https://raw.githubusercontent.com/traefik/traefik/v3.5/traefik.sample.yml) -* [TOML](https://raw.githubusercontent.com/traefik/traefik/v3.5/traefik.sample.toml) +* [YAML](https://raw.githubusercontent.com/traefik/traefik/v3.4/traefik.sample.yml) +* [TOML](https://raw.githubusercontent.com/traefik/traefik/v3.4/traefik.sample.toml) ```shell docker run -d -p 8080:8080 -p 80:80 \ - -v $PWD/traefik.yml:/etc/traefik/traefik.yml traefik:v3.5 + -v $PWD/traefik.yml:/etc/traefik/traefik.yml traefik:v3.4 ``` For more details, go to the [Docker provider documentation](../providers/docker.md) @@ -29,7 +29,7 @@ For more details, go to the [Docker provider documentation](../providers/docker. !!! tip * Prefer a fixed version than the latest that could be an unexpected version. - ex: `traefik:v3.5` + ex: `traefik:v3.4` * Docker images are based from the [Alpine Linux Official image](https://hub.docker.com/_/alpine). * Any orchestrator using docker images can fetch the official Traefik docker image. diff --git a/docs/content/getting-started/kubernetes.md b/docs/content/getting-started/kubernetes.md deleted file mode 100644 index 1c85e6d12..000000000 --- a/docs/content/getting-started/kubernetes.md +++ /dev/null @@ -1,334 +0,0 @@ ---- -title: "Kubernetes and Traefik Quick Start" -description: "Deploy Traefik in Kubernetes using Helm and expose your first service" -slug: quick-start-with-kubernetes ---- - -# Getting Started with Kubernetes and Traefik - -Kubernetes is a first-class citizen in Traefik, offering native support for Kubernetes resources and the latest Kubernetes standards. -Whether you're using Traefik's [IngressRoute CRD](../reference/routing-configuration/kubernetes/crd/http/ingressroute.md), [Ingress](../reference/routing-configuration/kubernetes/ingress.md) or the [Kubernetes Gateway API](../reference/routing-configuration/kubernetes/gateway-api.md), -Traefik provides a seamless experience for managing your Kubernetes traffic. - -This guide shows you how to: - -- Create a Kubernetes cluster using k3d -- Install Traefik using Helm -- Expose the Traefik dashboard -- Deploy a sample application -- Configure basic routing with IngressRoute and Gateway API - -## Prerequisites - -- Kubernetes -- Helm 3 -- kubectl -- k3d (for local cluster creation) - -## Create a Kubernetes Cluster - -### Using k3d - -Create a cluster with the following command. This command: - -- Creates a k3d cluster named "traefik" -- Maps ports 80, 443, and 8000 to the loadbalancer for accessing services -- Disables the built-in Traefik ingress controller to avoid conflicts - -```bash -k3d cluster create traefik \ - --port 80:80@loadbalancer \ - --port 443:443@loadbalancer \ - --port 8000:8000@loadbalancer \ - --k3s-arg "--disable=traefik@server:0" -``` - -Configure kubectl: - -```bash -kubectl cluster-info --context k3d-traefik -``` - -## Install Traefik - -### Using Helm Values File - -Add the Traefik Helm repository: - -```bash -helm repo add traefik https://traefik.github.io/charts -helm repo update -``` - -Create a values file. This configuration: - -- Maps ports 80 and 443 to the web and websecure [entrypoints](../reference/install-configuration/entrypoints.md) -- Enables the [dashboard](../reference/install-configuration/api-dashboard.md) with a specific hostname rule -- Enables the [Kubernetes Gateway API provider](../reference/routing-configuration/kubernetes/gateway-api.md) -- Allows the Gateway to expose [HTTPRoutes](https://gateway-api.sigs.k8s.io/api-types/httproute/) from all namespaces - -```yaml -# values.yaml -ingressRoute: - dashboard: - enabled: true - matchRule: Host(`dashboard.localhost`) - entryPoints: - - web -providers: - kubernetesGateway: - enabled: true -gateway: - listeners: - web: - namespacePolicy: - from: All -``` - -!!! info - The [KubernetesCRD](../reference/install-configuration/providers/kubernetes/kubernetes-crd.md) provider is enabled by default when using the Helm chart so we don't need to set it in the values file. - -Install Traefik: - -```bash -helm install traefik traefik/traefik -f values.yaml --wait -``` - -### Using Helm CLI Arguments - -Alternatively, you can install Traefik using CLI arguments. This command: - -- Maps ports `30000` and `30001` to the web and websecure entrypoints -- Enables the dashboard with a specific hostname rule -- Enables the [Kubernetes Gateway API provider](../reference/routing-configuration/kubernetes/gateway-api.md) -- Allows the Gateway to expose HTTPRoutes from all namespaces - -```bash -helm install traefik traefik/traefik --wait \ - --set ingressRoute.dashboard.enabled=true \ - --set ingressRoute.dashboard.matchRule='Host(`dashboard.localhost`)' \ - --set ingressRoute.dashboard.entryPoints={web} \ - --set providers.kubernetesGateway.enabled=true \ - --set gateway.listeners.web.namespacePolicy.from=All -``` - -!!! info - The [KubernetesCRD](../reference/install-configuration/providers/kubernetes/kubernetes-crd.md) provider is enabled by default when using the Helm chart so we don't need to set it in the CLI arguments. - -When Traefik is installed with the Gateway API provider enabled, it automatically creates a default GatewayClass named **traefik**: - -```bash -kubectl describe GatewayClass traefik -``` - -## Expose the Dashboard - -The dashboard is exposed with an [IngressRoute](../reference/routing-configuration/kubernetes/crd/http/ingressroute.md) provided by the Chart, as we defined in the helm values during installation. - -Access it at: - -[http://dashboard.localhost/dashboard/](http://dashboard.localhost/dashboard/) - -![Traefik Dashboard Screenshot](../assets/img/getting-started/traefik-dashboard.png) - -## Deploy a Sample Application - -Create a deployment: - -```yaml -# whoami.yaml -apiVersion: apps/v1 -kind: Deployment -metadata: - name: whoami -spec: - replicas: 2 - selector: - matchLabels: - app: whoami - template: - metadata: - labels: - app: whoami - spec: - containers: - - name: whoami - image: traefik/whoami - ports: - - containerPort: 80 -``` - -Create a service: - -```yaml -# whoami-service.yaml -apiVersion: v1 -kind: Service -metadata: - name: whoami -spec: - ports: - - port: 80 - selector: - app: whoami -``` - -Apply the manifests: - -```bash -kubectl apply -f whoami.yaml -kubectl apply -f whoami-service.yaml -``` - -## Exposing the Application Using an IngressRoute (CRD) - -Create an IngressRoute: - -```yaml -# whoami-ingressroute.yaml -apiVersion: traefik.io/v1alpha1 -kind: IngressRoute -metadata: - name: whoami -spec: - entryPoints: - - web - routes: - - match: Host(`whoami.localhost`) - kind: Rule - services: - - name: whoami - port: 80 -``` - -Apply the manifest: - -```bash -kubectl apply -f whoami-ingressroute.yaml -``` - -### Test Your Setup - -You can use the following curl command to verify that the application is correctly exposed: - -```bash -curl http://whoami.localhost - -Hostname: whoami-76c9859cfc-6v8hh -IP: 127.0.0.1 -IP: ::1 -IP: 10.42.0.11 -IP: fe80::20ad:eeff:fe44:a63 -RemoteAddr: 10.42.0.9:38280 -GET / HTTP/1.1 -Host: whoami.localhost -User-Agent: curl/8.7.1 -Accept: */* -Accept-Encoding: gzip -X-Forwarded-For: 127.0.0.1 -X-Forwarded-Host: whoami.localhost -X-Forwarded-Port: 80 -X-Forwarded-Proto: http -X-Forwarded-Server: traefik-598946cd7-zds59 -X-Real-Ip: 127.0.0.1 -``` - -You can also visit [http://whoami.localhost](http://whoami.localhost) in a browser to verify that the application is exposed correctly: - -![whoami application Screenshot](../assets/img/getting-started/whoami-localhost.png) - -## Exposing the Application Using the Gateway API - -Traefik supports the Kubernetes Gateway API specification, which provides a more standardized way to configure ingress in Kubernetes. -When we installed Traefik earlier, we enabled the Gateway API provider. -You can verify this in the providers section of the Traefik dashboard. - -![Providers Section Screenshot](../assets/img/getting-started/providers.png) - -To use the Gateway API: - -Install the Gateway API CRDs in your cluster: - -```bash -kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.2.1/standard-install.yaml -``` - -Create an HTTPRoute. This configuration: - -- Creates an HTTPRoute named "whoami" -- Attaches it to the default Gateway that Traefik created during installation -- Configures routing for the hostname "whoami-gatewayapi.localhost" -- Routes all traffic to the whoami service on port 80 - -```yaml -# httproute.yaml -apiVersion: gateway.networking.k8s.io/v1 -kind: HTTPRoute -metadata: - name: whoami -spec: - parentRefs: - - name: traefik-gateway - hostnames: - - "whoami-gatewayapi.localhost" - rules: - - matches: - - path: - type: PathPrefix - value: / - backendRefs: - - name: whoami - port: 80 -``` - -Apply the manifest: - -```bash -kubectl apply -f httproute.yaml -``` - -### Test Your Setup - -You can use the following curl command to verify that the application is correctly exposed: - -```bash -curl http://whoami-gatewayapi.localhost - -Hostname: whoami-76c9859cfc-6v8hh -IP: 127.0.0.1 -IP: ::1 -IP: 10.42.0.11 -IP: fe80::20ad:eeff:fe44:a63 -RemoteAddr: 10.42.0.9:38280 -GET / HTTP/1.1 -Host: whoami.localhost -User-Agent: curl/8.7.1 -Accept: */* -Accept-Encoding: gzip -X-Forwarded-For: 127.0.0.1 -X-Forwarded-Host: whoami.localhost -X-Forwarded-Port: 80 -X-Forwarded-Proto: http -X-Forwarded-Server: traefik-598946cd7-zds59 -X-Real-Ip: 127.0.0.1 -``` - -You can now visit [http://whoami.localhost](http://whoami.localhost) in your browser to verify that the application is exposed correctly: - -![whoami application Screenshot](../assets/img/getting-started/whoami-localhost.png) - -If you navigate to the **HTTP Routes** section of the traefik dashboard, you can see that the `whoami.localhost` route is managed by the Traefik Kubernetes Gateway API provider: - -![Traefik Dashboard HTTP Routes Section Screenshot](../assets/img/getting-started/kubernetes-gateway.png) - -That's it! You've successfully deployed Traefik and configured routing in a Kubernetes cluster. - -## Next Steps - -- [Configure TLS](../reference/routing-configuration/http/tls/overview.md) -- [Set up Middlewares](../reference/routing-configuration/http/middlewares/overview.md) -- [Enable Metrics](../reference/install-configuration/observability/metrics.md) -- [Learn more about Kubernetes CRD provider](../reference/install-configuration/providers/kubernetes/kubernetes-crd.md) -- [Learn more about Kubernetes Gateway API provider](../reference/install-configuration/providers/kubernetes/kubernetes-gateway.md) - -{!traefik-for-business-applications.md!} diff --git a/docs/content/getting-started/quick-start-with-kubernetes.md b/docs/content/getting-started/quick-start-with-kubernetes.md index 7e0b6acca..a595fde55 100644 --- a/docs/content/getting-started/quick-start-with-kubernetes.md +++ b/docs/content/getting-started/quick-start-with-kubernetes.md @@ -3,4 +3,342 @@ title: "Traefik Getting Started With Kubernetes" description: "Get started with Traefik Proxy and Kubernetes." --- ---8<-- "content/getting-started/kubernetes.md" +# Quick Start + +A Use Case of Traefik Proxy and Kubernetes +{: .subtitle } + +This guide is an introduction to using Traefik Proxy in a Kubernetes environment. +The objective is to learn how to run an application behind a Traefik reverse proxy in Kubernetes. +It presents and explains the basic blocks required to start with Traefik such as Ingress Controller, Ingresses, Deployments, static, and dynamic configuration. + +## Permissions and Accesses + +Traefik uses the Kubernetes API to discover running services. + +To use the Kubernetes API, Traefik needs some permissions. +This [permission mechanism](https://kubernetes.io/docs/reference/access-authn-authz/rbac/) is based on roles defined by the cluster administrator. +The role is then bound to an account used by an application, in this case, Traefik Proxy. + +The first step is to create the role. +The [`ClusterRole`](https://kubernetes.io/docs/reference/kubernetes-api/authorization-resources/cluster-role-v1/#ClusterRole) resource enumerates the resources and actions available for the role. +In a file called `00-role.yml`, put the following `ClusterRole`: + +```yaml tab="00-role.yml" +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: traefik-role + +rules: + - apiGroups: + - "" + resources: + - services + - secrets + - nodes + verbs: + - get + - list + - watch + - apiGroups: + - discovery.k8s.io + resources: + - endpointslices + verbs: + - list + - watch + - apiGroups: + - extensions + - networking.k8s.io + resources: + - ingresses + - ingressclasses + verbs: + - get + - list + - watch + - apiGroups: + - extensions + - networking.k8s.io + resources: + - ingresses/status + verbs: + - update + - apiGroups: + - traefik.io + resources: + - middlewares + - middlewaretcps + - ingressroutes + - traefikservices + - ingressroutetcps + - ingressrouteudps + - tlsoptions + - tlsstores + - serverstransports + - serverstransporttcps + verbs: + - get + - list + - watch +``` + +!!! info "You can find the reference for this file [there](../../reference/dynamic-configuration/kubernetes-crd/#rbac)." + +The next step is to create a dedicated service account for Traefik. +In a file called `00-account.yml`, put the following [`ServiceAccount`](https://kubernetes.io/docs/reference/kubernetes-api/authentication-resources/service-account-v1/#ServiceAccount) resource: + +```yaml tab="00-account.yml" +apiVersion: v1 +kind: ServiceAccount +metadata: + name: traefik-account +``` + +And then, bind the role on the account to apply the permissions and rules on the latter. In a file called `01-role-binding.yml`, put the +following [`ClusterRoleBinding`](https://kubernetes.io/docs/reference/kubernetes-api/authorization-resources/cluster-role-binding-v1/#ClusterRoleBinding) resource: + +```yaml tab="01-role-binding.yml" +kind: ClusterRoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: traefik-role-binding + +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: traefik-role +subjects: + - kind: ServiceAccount + name: traefik-account + namespace: default # This tutorial uses the "default" K8s namespace. +``` + +!!! info "`roleRef` is the Kubernetes reference to the role created in `00-role.yml`." + +!!! info "`subjects` is the list of accounts reference." + + In this guide, it only contains the account created in `00-account.yml` + +## Deployment and Exposition + +!!! info "This section can be managed with the help of the [Traefik Helm chart](../install-traefik/#use-the-helm-chart)." + +The [ingress controller](https://traefik.io/glossary/kubernetes-ingress-and-ingress-controller-101/#what-is-a-kubernetes-ingress-controller) +is a software that runs in the same way as any other application on a cluster. +To start Traefik on the Kubernetes cluster, +a [`Deployment`](https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/deployment-v1/) resource must exist to describe how to configure +and scale containers horizontally to support larger workloads. + +Start by creating a file called `02-traefik.yml` and paste the following `Deployment` resource: + +```yaml tab="02-traefik.yml" +kind: Deployment +apiVersion: apps/v1 +metadata: + name: traefik-deployment + labels: + app: traefik + +spec: + replicas: 1 + selector: + matchLabels: + app: traefik + template: + metadata: + labels: + app: traefik + spec: + serviceAccountName: traefik-account + containers: + - name: traefik + image: traefik:v3.4 + args: + - --api.insecure + - --providers.kubernetesingress + ports: + - name: web + containerPort: 80 + - name: dashboard + containerPort: 8080 +``` + +The deployment contains an important attribute for customizing Traefik: `args`. +These arguments are the static configuration for Traefik. +From here, it is possible to enable the dashboard, +configure entry points, +select dynamic configuration providers, +and [more](../reference/static-configuration/cli.md). + +In this deployment, +the static configuration enables the Traefik dashboard, +and uses Kubernetes native Ingress resources as router definitions to route incoming requests. + +!!! info "When there is no entry point in the static configuration" + + Traefik creates a default one called `web` using the port `80` routing HTTP requests. + +!!! info "When enabling the [`api.insecure`](../../operations/api/#insecure) mode, Traefik exposes the dashboard on the port `8080`." + +A deployment manages scaling and then can create lots of containers, called [Pods](https://kubernetes.io/docs/concepts/workloads/pods/). +Each Pod is configured following the `spec` field in the deployment. +Given that, a Deployment can run multiple Traefik Proxy Pods, +a piece is required to forward the traffic to any of the instance: +namely a [`Service`](https://kubernetes.io/docs/reference/kubernetes-api/service-resources/service-v1/#Service). +Create a file called `02-traefik-services.yml` and insert the two `Service` resources: + +```yaml tab="02-traefik-services.yml" +apiVersion: v1 +kind: Service +metadata: + name: traefik-dashboard-service + +spec: + type: LoadBalancer + ports: + - port: 8080 + targetPort: dashboard + selector: + app: traefik +--- +apiVersion: v1 +kind: Service +metadata: + name: traefik-web-service + +spec: + type: LoadBalancer + ports: + - targetPort: web + port: 80 + selector: + app: traefik +``` + +!!! warning "It is possible to expose a service in different ways." + + Depending on your working environment and use case, the `spec.type` might change. + It is strongly recommended to understand the available [service types](https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types) before proceeding to the next step. + +It is now time to apply those files on your cluster to start Traefik. + +```shell +kubectl apply -f 00-role.yml \ + -f 00-account.yml \ + -f 01-role-binding.yml \ + -f 02-traefik.yml \ + -f 02-traefik-services.yml +``` + +## Proxying applications + +The only part still missing is the business application behind the reverse proxy. +For this guide, we use the example application [traefik/whoami](https://github.com/traefik/whoami), +but the principles are applicable to any other application. + +The `whoami` application is an HTTP server running on port 80 which answers host-related information to the incoming requests. +As usual, start by creating a file called `03-whoami.yml` and paste the following `Deployment` resource: + +```yaml tab="03-whoami.yml" +kind: Deployment +apiVersion: apps/v1 +metadata: + name: whoami + labels: + app: whoami + +spec: + replicas: 1 + selector: + matchLabels: + app: whoami + template: + metadata: + labels: + app: whoami + spec: + containers: + - name: whoami + image: traefik/whoami + ports: + - name: web + containerPort: 80 +``` + +And continue by creating the following `Service` resource in a file called `03-whoami-services.yml`: + +```yaml tab="03-whoami-services.yml" +apiVersion: v1 +kind: Service +metadata: + name: whoami + +spec: + ports: + - name: web + port: 80 + targetPort: web + + selector: + app: whoami +``` + +Thanks to the Kubernetes API, +Traefik is notified when an Ingress resource is created, updated, or deleted. +This makes the process dynamic. +The ingresses are, in a way, the [dynamic configuration](../../providers/kubernetes-ingress/) for Traefik. + +!!! tip + + Find more information on [ingress controller](https://kubernetes.io/docs/concepts/services-networking/ingress-controllers/), + and [Ingress](https://kubernetes.io/docs/concepts/services-networking/ingress/) in the official Kubernetes documentation. + +Create a file called `04-whoami-ingress.yml` and insert the `Ingress` resource: + +```yaml tab="04-whoami-ingress.yml" +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: whoami-ingress +spec: + rules: + - http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: whoami + port: + name: web +``` + +This `Ingress` configures Traefik to redirect any incoming requests starting with `/` to the `whoami:80` service. + +At this point, all the configurations are ready. +It is time to apply those new files: + +```shell +kubectl apply -f 03-whoami.yml \ + -f 03-whoami-services.yml \ + -f 04-whoami-ingress.yml +``` + +Now you should be able to access the `whoami` application and the Traefik dashboard. +Load the dashboard on a web browser: [`http://localhost:8080`](http://localhost:8080). + +And now access the `whoami` application: + +```shell +curl -v http://localhost/ +``` + +!!! question "Going further" + + - [Filter the ingresses](../providers/kubernetes-ingress.md#ingressclass) to use with [IngressClass](https://kubernetes.io/docs/concepts/services-networking/ingress/#ingress-class) + - Use [IngressRoute CRD](../providers/kubernetes-crd.md) + - Protect [ingresses with TLS](../routing/providers/kubernetes-ingress.md#enabling-tls-via-annotations) + +{!traefik-for-business-applications.md!} diff --git a/docs/content/getting-started/quick-start.md b/docs/content/getting-started/quick-start.md index 0c1e2312e..a06bba0b1 100644 --- a/docs/content/getting-started/quick-start.md +++ b/docs/content/getting-started/quick-start.md @@ -3,4 +3,122 @@ title: "Traefik Getting Started Quickly" description: "Get started with Traefik Proxy and Docker." --- ---8<-- "content/getting-started/docker.md" +# Quick Start + +A Use Case Using Docker +{: .subtitle } + +![quickstart-diagram](../assets/img/quickstart-diagram.png) + +## Launch Traefik With the Docker Provider + +Create a `docker-compose.yml` file where you will define a `reverse-proxy` service that uses the official Traefik image: + +```yaml +version: '3' + +services: + reverse-proxy: + # The official v3 Traefik docker image + image: traefik:v3.4 + # Enables the web UI and tells Traefik to listen to docker + command: --api.insecure=true --providers.docker + ports: + # The HTTP port + - "80:80" + # The Web UI (enabled by --api.insecure=true) + - "8080:8080" + volumes: + # So that Traefik can listen to the Docker events + - /var/run/docker.sock:/var/run/docker.sock +``` + +**That's it. Now you can launch Traefik!** + +Start your `reverse-proxy` with the following command: + +```shell +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). + +## Traefik Detects New Services and Creates the Route for You + +Now that you have a Traefik instance up and running, you will deploy new services. + +Edit your `docker-compose.yml` file and add the following at the end of your file. + +```yaml +version: '3' + +services: + + ... + + whoami: + # A container that exposes an API to show its IP address + image: traefik/whoami + labels: + - "traefik.http.routers.whoami.rule=Host(`whoami.docker.localhost`)" +``` + +The above defines `whoami`: a web service that outputs information about the machine it is deployed on (its IP address, host, and others). + +Start the `whoami` service with the following command: + +```shell +docker compose up -d whoami +``` + +Browse `http://localhost:8080/api/rawdata` and see that Traefik has automatically detected the new container and updated its own configuration. + +When Traefik detects new services, it creates the corresponding routes, so you can call them ... _let's see!_ (Here, you're using curl) + +```shell +curl -H Host:whoami.docker.localhost http://127.0.0.1 +``` + +_Shows the following output:_ + +```yaml +Hostname: a656c8ddca6c +IP: 172.27.0.3 +#... +``` + +## More Instances? Traefik Load Balances Them + +Run more instances of your `whoami` service with the following command: + +```shell +docker compose up -d --scale whoami=2 +``` + +Browse to `http://localhost:8080/api/rawdata` and see that Traefik has automatically detected the new instance of the container. + +Finally, see that Traefik load-balances between the two instances of your service by running the following command twice: + +```shell +curl -H Host:whoami.docker.localhost http://127.0.0.1 +``` + +The output will show alternatively one of the following: + +```yaml +Hostname: a656c8ddca6c +IP: 172.27.0.3 +#... +``` + +```yaml +Hostname: s458f154e1f1 +IP: 172.27.0.4 +# ... +``` + +!!! question "Where to Go Next?" + + Now that you have a basic understanding of how Traefik can automatically create the routes to your services and load balance them, it is time to dive into [the user guides](../../user-guides/docker-compose/basic-example/ "Link to the user guides") and [the documentation](/ "Link to the docs landing page") and let Traefik work for you! + +{!traefik-for-business-applications.md!} diff --git a/docs/content/govern/index.md b/docs/content/govern/index.md deleted file mode 100644 index 9a574ba90..000000000 --- a/docs/content/govern/index.md +++ /dev/null @@ -1,59 +0,0 @@ ---- -title: "Govern Your APIs with Traefik Hub" -description: "Learn how Traefik Hub provides comprehensive API Gateway and API Management capabilities to govern, secure, and manage your APIs at scale." ---- - -# Govern Your APIs with Traefik Hub - -Traefik Hub transforms your API infrastructure by providing enterprise-grade API Gateway and comprehensive API Management capabilities. Built on the foundation of Traefik Proxy, Hub enables organizations to govern, secure, and manage APIs at scale across cloud-native environments. - -## API Gateway and API Management Capabilities - -Traefik Hub offers two complementary approaches to API governance: - -- [Traefik Hub API Gateway](https://traefik.io/solutions/api-gateway/): Enterprise-grade security, distributed features, and advanced access control for cloud-native API gateway scenarios. It includes AI Gateway capabilities for modern machine learning workloads. - -- [Traefik Hub API Management](https://traefik.io/solutions/api-management/): Comprehensive API lifecycle management, developer portals, and organizational features for teams managing multiple APIs across environments. - -## API Management Features - -Traefik Hub API Management provides a complete suite of features designed to streamline API governance and accelerate developer productivity: - -### Comprehensive API Lifecycle Management - -- **Centralized API Catalog**: Comprehensive API inventory providing real-time visibility and control -- **Advanced Versioning**: Utilize the most flexible API versioning and Kubernetes-native labels to logically organize APIs, track their evolution over time, and avoid breaking changes for client applications -- **Quality Control**: Perform error checks and change impact analysis with Traefik Hub's static analyzer to ensure compliance and prevent misconfiguration by catching errors early -- **Resource Management**: Centralize rate limits, quotas, and policy enforcement across groups of APIs through Plans & Bundles, ensuring consistent and efficient resource management -- **Subscription Management**: Create managed and self-serve subscriptions for enhanced ease-of-use, security, and auditability -- **Standards Compliance**: Import, validate, and manage APIs using industry-standard OpenAPI specifications - -### Developer Experience & Self-Service - -- **Customizable Portals**: Easily create one or more dedicated API Portals for developers to discover, understand, and interact with your APIs -- **Interactive API Testing**: Allow developers to test APIs directly within the portal for immediate feedback -- **Branded Experience**: Customize the API portal to house API documentation, clear integration instructions, and personalized content to foster adoption and streamline development -- **Credential Management**: Generate API access credentials, such as API keys, directly from the portal for simplified access and secure integration -- **Auto-generated Documentation**: Automatically generated, interactive API documentation from OpenAPI specifications in the portal - -### Enterprise-Grade Security - -- **Access Control**: Implement fine-grained permissions through detailed API policies and JWT inspection to secure access to API resources -- **Identity Integration**: Leverage existing identity and access management (IAM) solutions, such as Keycloak, Okta, Auth0, etc. through industry-standard authentication protocols, including native OIDC support -- **Data Protection**: Advanced TLS management, supporting Let's Encrypt (ACME) and SPIFFE for robust data protection -- **Threat Prevention**: Native Web Application Firewall (WAF) powered by OWASP Coraza to defend against known vulnerabilities - -### Observability - -- **Open Standards Monitoring**: Gain deep insights into API health and performance with OpenTelemetry integration to provide metrics and tracing capabilities without vendor lock-in -- **Third-party Integrations**: Turnkey integration with Treblle directly on the Traefik Hub Dashboard for real-time, out-of-the-box observability without maintaining a complex monitoring infrastructure -- **Pre-built Grafana Dashboards**: Ready-to-use monitoring dashboards with key API metrics and performance indicators - -## Getting Started with API Governance - -Transform your API infrastructure with Traefik Hub's governance capabilities: - -1. **Start with API Gateway**: Implement enterprise security, authentication, distributed features, and AI Gateway capabilities -2. **Scale with API Management**: Add comprehensive lifecycle management, developer portals, and governance features - -Ready to govern your APIs at scale? Explore [Traefik Hub API Management](https://traefik.io/solutions/api-management/) and discover how it can transform your API strategy. diff --git a/docs/content/https/acme.md b/docs/content/https/acme.md index 63680ea68..ce613f964 100644 --- a/docs/content/https/acme.md +++ b/docs/content/https/acme.md @@ -250,34 +250,6 @@ when using the `HTTP-01` challenge, `certificatesresolvers.myresolver.acme.httpc !!! info "" Redirection is fully compatible with the `HTTP-01` challenge. -#### `Delay` - -The delay between the creation of the challenge and the validation. -A value lower than or equal to zero means no delay. - -```yaml tab="File (YAML)" -certificatesResolvers: - myresolver: - acme: - # ... - httpChallenge: - # ... - delay: 12 -``` - -```toml tab="File (TOML)" -[certificatesResolvers.myresolver.acme] - # ... - [certificatesResolvers.myresolver.acme.httpChallenge] - # ... - delay = 12 -``` - -```bash tab="CLI" -# ... ---certificatesresolvers.myresolver.acme.httpchallenge.delay=12 -``` - ### `dnsChallenge` Use the `DNS-01` challenge to generate and renew ACME certificates by provisioning a DNS record. @@ -314,7 +286,7 @@ Use the `DNS-01` challenge to generate and renew ACME certificates by provisioni !!! warning "`CNAME` support" `CNAME` are supported (and sometimes even [encouraged](https://letsencrypt.org/2019/10/09/onboarding-your-customers-with-lets-encrypt-and-acme.html#the-advantages-of-a-cname)), - but there are a few cases where they can be [problematic](../getting-started/faq.md#why-does-lets-encrypt-wildcard-certificate-renewalgeneration-with-dns-challenge-fail). + but there are a few cases where they can be [problematic](../../getting-started/faq/#why-does-lets-encrypt-wildcard-certificate-renewalgeneration-with-dns-challenge-fail). If needed, `CNAME` support can be disabled with the following environment variable: @@ -352,16 +324,13 @@ For complete details, refer to your provider's _Additional configuration_ link. | [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) | | [Axelname](https://axelname.ru) | `axelname` | `AXELNAME_NICKNAME`, `AXELNAME_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/axelname) | -| [Azion](https://www.azion.com/en/products/edge-dns/) | `azion` | `AZION_PERSONAL_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/azion) | -| [Azure](https://azure.microsoft.com/services/dns/) (DEPRECATED) | `azure` | DEPRECATED use `azuredns` instead. | [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) | | [Baidu Cloud](https://cloud.baidu.com) | `baiducloud` | `BAIDUCLOUD_ACCESS_KEY_ID`, `BAIDUCLOUD_SECRET_ACCESS_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/baiducloud) | -| [Beget](https://beget.com/) | `beget` | `BEGET_USERNAME`, `BEGET_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/beget) | -| [Binary Lane](https://www.binarylane.com.au/) | `binarylane` | `BINARYLANE_API_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/binarylane) | | [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) | | [BookMyName](https://www.bookmyname.com) | `bookmyname` | `BOOKMYNAME_USERNAME`, `BOOKMYNAME_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/bookmyname) | -| [Brandit](https://www.brandit.com) (DEPRECATED) | `brandit` | DEPRECATED | [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) | @@ -369,8 +338,7 @@ 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) (DEPRECATED) | `cloudxns` | DEPRECATED | [Additional configuration](https://go-acme.github.io/lego/dns/cloudxns) | -| [ConoHa v3](https://www.conoha.jp/) | `conohav3` | `CONOHAV3_TENANT_ID`, `CONOHAV3_API_USER_ID`, `CONOHAV3_API_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/conohav3) | +| [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) | @@ -382,13 +350,12 @@ For complete details, refer to your provider's _Additional configuration_ link. | [DNS Made Easy](https://dnsmadeeasy.com) | `dnsmadeeasy` | `DNSMADEEASY_API_KEY`, `DNSMADEEASY_API_SECRET`, `DNSMADEEASY_SANDBOX` | [Additional configuration](https://go-acme.github.io/lego/dns/dnsmadeeasy) | | [dnsHome.de](https://www.dnshome.de) | `dnsHomede` | `DNSHOMEDE_CREDENTIALS` | [Additional configuration](https://go-acme.github.io/lego/dns/dnshomede) | | [DNSimple](https://dnsimple.com) | `dnsimple` | `DNSIMPLE_OAUTH_TOKEN`, `DNSIMPLE_BASE_URL` | [Additional configuration](https://go-acme.github.io/lego/dns/dnsimple) | -| [DNSPod](https://www.dnspod.com/) (DEPRECATED) | `dnspod` | DEPRECATED use `tencentcloud` instead. | [Additional configuration](https://go-acme.github.io/lego/dns/dnspod) | +| [DNSPod](https://www.dnspod.com/) | `dnspod` | `DNSPOD_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/dnspod) | | [Domain Offensive (do.de)](https://www.do.de/) | `dode` | `DODE_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/dode) | | [Domeneshop](https://domene.shop) | `domeneshop` | `DOMENESHOP_API_TOKEN`, `DOMENESHOP_API_SECRET` | [Additional configuration](https://go-acme.github.io/lego/dns/domeneshop) | | [DreamHost](https://www.dreamhost.com/) | `dreamhost` | `DREAMHOST_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/dreamhost) | | [Duck DNS](https://www.duckdns.org/) | `duckdns` | `DUCKDNS_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/duckdns) | | [Dyn](https://dyn.com) | `dyn` | `DYN_CUSTOMER_NAME`, `DYN_USER_NAME`, `DYN_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/dyn) | -| [DynDnsFree.de](https://www.dyndnsfree.de) | `dyndnsfree` | `DYNDNSFREE_USERNAME`, `DYNDNSFREE_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/dyndnsfree) | | [Dynu](https://www.dynu.com) | `dynu` | `DYNU_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/dynu) | | [EasyDNS](https://easydns.com/) | `easydns` | `EASYDNS_TOKEN`, `EASYDNS_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/easydns) | | [EdgeDNS](https://www.akamai.com/) | `edgedns` | `AKAMAI_CLIENT_TOKEN`, `AKAMAI_CLIENT_SECRET`, `AKAMAI_ACCESS_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/edgedns) | @@ -404,10 +371,9 @@ For complete details, refer to your provider's _Additional configuration_ link. | [Glesys](https://glesys.com/) | `glesys` | `GLESYS_API_USER`, `GLESYS_API_KEY`, `GLESYS_DOMAIN` | [Additional configuration](https://go-acme.github.io/lego/dns/glesys) | | [GoDaddy](https://www.godaddy.com) | `godaddy` | `GODADDY_API_KEY`, `GODADDY_API_SECRET` | [Additional configuration](https://go-acme.github.io/lego/dns/godaddy) | | [Google Cloud DNS](https://cloud.google.com/dns/docs/) | `gcloud` | `GCE_PROJECT`, Application Default Credentials [^2] [^3], [`GCE_SERVICE_ACCOUNT_FILE`] | [Additional configuration](https://go-acme.github.io/lego/dns/gcloud) | -| [Google Domains](https://domains.google) (DEPRECATED) | `googledomains` | DEPRECATED | [Additional configuration](https://go-acme.github.io/lego/dns/googledomains) | -| [Hetzner](https://hetzner.com) | `hetzner` | `HETZNER_API_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/hetzner) | +| [Google Domains](https://domains.google) | `googledomains` | `GOOGLE_DOMAINS_ACCESS_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/googledomains) | +| [Hetzner](https://hetzner.com) | `hetzner` | `HETZNER_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/hetzner) | | [hosting.de](https://www.hosting.de) | `hostingde` | `HOSTINGDE_API_KEY`, `HOSTINGDE_ZONE_NAME` | [Additional configuration](https://go-acme.github.io/lego/dns/hostingde) | -| [Hostinger](https://www.hostinger.com/) | `hostinger` | `HOSTINGER_API_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/hostinger) | | [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) | @@ -424,7 +390,6 @@ For complete details, refer to your provider's _Additional configuration_ link. | [IPv64](https://ipv64.net) | `ipv64` | `IPV64_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/ipv64) | | [iwantmyname](https://iwantmyname.com) | `iwantmyname` | `IWANTMYNAME_USERNAME` , `IWANTMYNAME_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/iwantmyname) | | [Joker.com](https://joker.com) | `joker` | `JOKER_API_MODE` with `JOKER_API_KEY` or `JOKER_USERNAME`, `JOKER_PASSWORD` | [Additional configuration](https://go-acme.github.io/lego/dns/joker) | -| [KeyHelp](https://www.keyweb.de/en/keyhelp/keyhelp/) | `keyhelp` | `KEYHELP_API_KEY`, `KEYHELP_BASE_URL` | [Additional configuration](https://go-acme.github.io/lego/dns/keyhelp) | | [Liara](https://liara.ir) | `liara` | `LIARA_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/liara) | | [Lightsail](https://aws.amazon.com/lightsail/) | `lightsail` | `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `DNS_ZONE` | [Additional configuration](https://go-acme.github.io/lego/dns/lightsail) | | [Lima-City](https://www.lima-city.de) | `limacity` | `LIMACITY_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/limacity) | @@ -452,7 +417,6 @@ For complete details, refer to your provider's _Additional configuration_ link. | [Njalla](https://njal.la) | `njalla` | `NJALLA_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/njalla) | | [Nodion](https://www.nodion.com) | `nodion` | `NODION_API_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/nodion) | | [NS1](https://ns1.com/) | `ns1` | `NS1_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/ns1) | -| [Octenium](https://octenium.com/) | `octenium` | `OCTENIUM_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/octenium) | | [Open Telekom Cloud](https://cloud.telekom.de) | `otc` | `OTC_DOMAIN_NAME`, `OTC_USER_NAME`, `OTC_PASSWORD`, `OTC_PROJECT_NAME`, `OTC_IDENTITY_ENDPOINT` | [Additional configuration](https://go-acme.github.io/lego/dns/otc) | | [Openstack Designate](https://docs.openstack.org/designate) | `designate` | `OS_AUTH_URL`, `OS_USERNAME`, `OS_PASSWORD`, `OS_TENANT_NAME`, `OS_REGION_NAME` | [Additional configuration](https://go-acme.github.io/lego/dns/designate) | | [Oracle Cloud](https://cloud.oracle.com/home) | `oraclecloud` | `OCI_COMPARTMENT_OCID`, `OCI_PRIVKEY_FILE`, `OCI_PRIVKEY_PASS`, `OCI_PUBKEY_FINGERPRINT`, `OCI_REGION`, `OCI_TENANCY_OCID`, `OCI_USER_OCID` | [Additional configuration](https://go-acme.github.io/lego/dns/oraclecloud) | @@ -468,7 +432,6 @@ For complete details, refer to your provider's _Additional configuration_ link. | [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) | -| [RU Center](https://nic.ru/) | `nicru` | `NICRU_USER`, `NICRU_PASSWORD`, `NICRU_SERVICE_ID`, `NICRU_SECRET`, `NICRU_SERVICE_NAME` | [Additional configuration](https://go-acme.github.io/lego/dns/nicru) | | [Sakura Cloud](https://cloud.sakura.ad.jp/) | `sakuracloud` | `SAKURACLOUD_ACCESS_TOKEN`, `SAKURACLOUD_ACCESS_TOKEN_SECRET` | [Additional configuration](https://go-acme.github.io/lego/dns/sakuracloud) | | [Scaleway](https://www.scaleway.com) | `scaleway` | `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) | @@ -482,7 +445,6 @@ For complete details, refer to your provider's _Additional configuration_ link. | [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) | -| [Tencent EdgeOne](https://edgeone.ai/) | `edgeone` | `EDGEONE_SECRET_ID`, `EDGEONE_SECRET_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/edgeone) | | [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) | @@ -504,7 +466,6 @@ For complete details, refer to your provider's _Additional configuration_ link. | [Yandex Cloud](https://cloud.yandex.com/en/) | `yandexcloud` | `YANDEX_CLOUD_FOLDER_ID`, `YANDEX_CLOUD_IAM_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/yandexcloud) | | [Yandex](https://yandex.com) | `yandex` | `YANDEX_PDD_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/yandex) | | [Zone.ee](https://www.zone.ee) | `zoneee` | `ZONEEE_API_USER`, `ZONEEE_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/zoneee) | -| [ZoneEdit](https://www.zoneedit.com) | `zoneedit` | `ZONEEDIT_USER`, `ZONEEDIT_AUTH_TOKEN` | [Additional configuration](https://go-acme.github.io/lego/dns/zoneedit) | | [Zonomi](https://zonomi.com) | `zonomi` | `ZONOMI_API_KEY` | [Additional configuration](https://go-acme.github.io/lego/dns/zonomi) | | External Program | `exec` | `EXEC_PATH` | [Additional configuration](https://go-acme.github.io/lego/dns/exec) | | HTTP request | `httpreq` | `HTTPREQ_ENDPOINT`, `HTTPREQ_MODE`, `HTTPREQ_USERNAME`, `HTTPREQ_PASSWORD` [^1] | [Additional configuration](https://go-acme.github.io/lego/dns/httpreq) | @@ -806,8 +767,6 @@ docker run -v "/my/host/acme:/etc/traefik/acme" traefik _Optional, Default=2160_ -`certificatesDuration` specifies the duration (in hours) of the certificates issued by the CA server. It is used to determine when to renew the certificate, but it **doesn't** define the duration of the certificates, that is up to the CA server. - `certificatesDuration` is used to calculate two durations: - `Renew Period`: the period before the end of the certificate duration, during which the certificate should be renewed. @@ -848,71 +807,6 @@ certificatesResolvers: # ... ``` -### `clientTimeout` - -_Optional, Default=2m_ - -`clientTimeout` is the total timeout for a complete HTTP transaction (including TCP connection, sending request and receiving response) with the ACME server. -It defaults to 2 minutes. - -!!! warning "This timeout encompasses the entire request-response cycle, including the response headers timeout. It must be at least `clientResponseHeaderTimeout`, otherwise the certificate resolver will fail to start." - -```yaml tab="File (YAML)" -certificatesResolvers: - myresolver: - acme: - # ... - clientTimeout: 1m - # ... -``` - -```toml tab="File (TOML)" -[certificatesResolvers.myresolver.acme] - # ... - clientTimeout=1m - # ... -``` - -```bash tab="CLI" -# ... ---certificatesresolvers.myresolver.acme.clientTimeout=1m -# ... -``` - -!!! warning - This should not be confused with any timeouts used for validating challenges. - -### `clientResponseHeaderTimeout` - -_Optional, Default=30s_ - -`clientResponseHeaderTimeout` defines how long the HTTP client waits for response headers when communicating with the `caServer`. -It defaults to 30 seconds. - -!!! warning "It must be lower than `clientTimeout`, otherwise the certificate resolver will fail to start." - -```yaml tab="File (YAML)" -certificatesResolvers: - myresolver: - acme: - # ... - clientResponseHeaderTimeout: 1m - # ... -``` - -```toml tab="File (TOML)" -[certificatesResolvers.myresolver.acme] - # ... - clientResponseHeaderTimeout=1m - # ... -``` - -```bash tab="CLI" -# ... ---certificatesresolvers.myresolver.acme.clientResponseHeaderTimeout=1m -# ... -``` - ### `preferredChain` _Optional, Default=""_ diff --git a/docs/content/https/ocsp.md b/docs/content/https/ocsp.md deleted file mode 100644 index a960d88d0..000000000 --- a/docs/content/https/ocsp.md +++ /dev/null @@ -1,71 +0,0 @@ ---- -title: "Traefik OCSP Documentation" -description: "Learn how to configure Traefik to use OCSP. Read the technical documentation." ---- - -# OCSP - -Check certificate status and perform OCSP stapling. -{: .subtitle } - -## Overview - -### OCSP Stapling - -When OCSP is enabled, Traefik checks the status of every certificate in the store that provides an OCSP responder URL, -including the default certificate, and staples the OCSP response to the TLS handshake. -The OCSP check is performed when the certificate is loaded, -and once every hour until it is successful at the halfway point before the update date. - -### Caching - -Traefik caches the OCSP response as long as the associated certificate is provided by the configuration. -When a certificate is no longer provided, -the OCSP response has a 24 hour TTL waiting to be provided again or eventually removed. -The OCSP response is cached in memory and is not persisted between Traefik restarts. - -## Configuration - -### General - -Enabling OCSP is part of the [static configuration](../getting-started/configuration-overview.md#the-static-configuration). -It can be defined by using a file (YAML or TOML) or CLI arguments: - -```yaml tab="File (YAML)" -## Static configuration -ocsp: {} -``` - -```toml tab="File (TOML)" -## Static configuration -[ocsp] -``` - -```bash tab="CLI" -## Static configuration ---ocsp=true -``` - -### Responder Overrides - -The `responderOverrides` option defines the OCSP responder URLs to use instead of the one provided by the certificate. -This is useful when you want to use a different OCSP responder. - -```yaml tab="File (YAML)" -## Static configuration -ocsp: - responderOverrides: - foo: bar -``` - -```toml tab="File (TOML)" -## Static configuration -[ocsp] - [ocsp.responderOverrides] - foo = "bar" -``` - -```bash tab="CLI" -## Static configuration --ocsp.responderoverrides.foo=bar -``` diff --git a/docs/content/https/ref-acme.toml b/docs/content/https/ref-acme.toml index a93f4775c..e5db57a53 100644 --- a/docs/content/https/ref-acme.toml +++ b/docs/content/https/ref-acme.toml @@ -30,20 +30,6 @@ # # certificatesDuration=2160 - # Timeout for a complete HTTP transaction with the ACME server. - # - # Optional - # Default: 2m - # - # clientTimeout="2m" - - # Timeout for receiving the response headers when communicating with the ACME server. - # - # Optional - # Default: 30s - # - # clientResponseHeaderTimeout="30s" - # Preferred chain to use. # # If the CA offers multiple certificate chains, prefer the chain with an issuer matching this Subject Common Name. diff --git a/docs/content/https/ref-acme.txt b/docs/content/https/ref-acme.txt index 10aa3b5d6..d817a4dbe 100644 --- a/docs/content/https/ref-acme.txt +++ b/docs/content/https/ref-acme.txt @@ -29,20 +29,6 @@ # --certificatesresolvers.myresolver.acme.certificatesDuration=2160 -# Timeout for a complete HTTP transaction with the ACME server. -# -# Optional -# Default: 2m -# ---certificatesresolvers.myresolver.acme.clientTimeout=2m - -# Timeout for receiving the response headers when communicating with the ACME server. -# -# Optional -# Default: 30s -# ---certificatesresolvers.myresolver.acme.clientResponseHeaderTimeout=30s - # Preferred chain to use. # # If the CA offers multiple certificate chains, prefer the chain with an issuer matching this Subject Common Name. diff --git a/docs/content/https/ref-acme.yaml b/docs/content/https/ref-acme.yaml index 65cd2462b..044c7ff9b 100644 --- a/docs/content/https/ref-acme.yaml +++ b/docs/content/https/ref-acme.yaml @@ -32,20 +32,6 @@ certificatesResolvers: # # certificatesDuration: 2160 - # Timeout for a complete HTTP transaction with the ACME server. - # - # Optional - # Default: 2m - # - # clientTimeout: "2m" - - # Timeout for receiving the response headers when communicating with the ACME server. - # - # Optional - # Default: 30s - # - # clientResponseHeaderTimeout: "30s" - # Preferred chain to use. # # If the CA offers multiple certificate chains, prefer the chain with an issuer matching this Subject Common Name. diff --git a/docs/content/https/tls.md b/docs/content/https/tls.md index 6b48e8e46..7dc9bc975 100644 --- a/docs/content/https/tls.md +++ b/docs/content/https/tls.md @@ -234,7 +234,7 @@ The TLS options allow one to configure some parameters of the TLS connection. !!! important "TLSOption in Kubernetes" - When using the [TLSOption resource](../routing/providers/kubernetes-crd.md#kind-tlsoption) in Kubernetes, one might setup a default set of options that, + When using the [TLSOption resource](../../routing/providers/kubernetes-crd#kind-tlsoption) in Kubernetes, one might setup a default set of options that, if not explicitly overwritten, should apply to all ingresses. To achieve that, you'll have to create a TLSOption resource with the name `default`. There may exist only one TLSOption with the name `default` (across all namespaces) - otherwise they will be dropped. @@ -384,11 +384,11 @@ spec: ### Curve Preferences -This option allows to set the enabled elliptic curves for key exchange. +This option allows to set the preferred elliptic curves in a specific order. The names of the curves defined by [`crypto`](https://godoc.org/crypto/tls#CurveID) (e.g. `CurveP521`) and the [RFC defined names](https://tools.ietf.org/html/rfc8446#section-4.2.7) (e. g. `secp521r1`) can be used. -See [CurvePreferences](https://godoc.org/crypto/tls#Config.CurvePreferences) and [CurveID](https://godoc.org/crypto/tls#CurveID) for more information. +See [CurveID](https://godoc.org/crypto/tls#CurveID) for more information. ```yaml tab="File (YAML)" # Dynamic configuration @@ -503,7 +503,7 @@ Traefik supports mutual authentication, through the `clientAuth` section. For authentication policies that require verification of the client certificate, the certificate authority for the certificates should be set in `clientAuth.caFiles`. -In Kubernetes environment, CA certificate can be set in `clientAuth.secretNames`. See [TLSOption resource](../routing/providers/kubernetes-crd.md#kind-tlsoption) for more details. +In Kubernetes environment, CA certificate can be set in `clientAuth.secretNames`. See [TLSOption resource](../../routing/providers/kubernetes-crd#kind-tlsoption) for more details. The `clientAuth.clientAuthType` option governs the behaviour as follows: diff --git a/docs/content/index.md b/docs/content/index.md index de8516356..bc5dd9fb7 100644 --- a/docs/content/index.md +++ b/docs/content/index.md @@ -7,15 +7,18 @@ description: "Traefik Proxy, an open-source Edge Router, auto-discovers configur ![Architecture](assets/img/traefik-architecture.png) -Traefik is an [open-source](https://github.com/traefik/traefik) Application Proxy and the core of the Traefik Hub Runtime Platform. +Traefik is an [open-source](https://github.com/traefik/traefik) *Application Proxy* that makes publishing your services a fun and easy experience. +It receives requests on behalf of your system, identifies which components are responsible for handling them, and routes them securely. -If you start with Traefik for service discovery and routing, you can seamlessly add [API management](https://traefik.io/solutions/api-management/), [API gateway](https://traefik.io/solutions/api-gateway/), [AI gateway](https://traefik.io/solutions/ai-gateway/), and [API mocking](https://traefik.io/solutions/api-mocking/) capabilities as needed. +What sets Traefik apart, besides its many features, is that it automatically discovers the right configuration for your services. +The magic happens when Traefik inspects your infrastructure, where it finds relevant information and discovers which service serves which request. -With 3.3 billion downloads and over 55k stars on GitHub, Traefik is used globally across hybrid cloud, multi-cloud, on prem, and bare metal environments running Kubernetes, Docker Swarm, AWS, [the list goes on](https://doc.traefik.io/traefik/reference/install-configuration/providers/overview/). +Traefik is natively compliant with every major cluster technology, such as Kubernetes, Docker Swarm, AWS, and [the list goes on](./reference/install-configuration/providers/overview.md); and can handle many at the same time. (It even works for legacy software running on bare metal.) + +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. -Here’s how it works—Traefik receives requests on behalf of your system, identifies which components are responsible for handling them, and routes them securely. It automatically discovers the right configuration for your services by inspecting your infrastructure to identify relevant information and which service serves which request. - -Because everything happens automatically, in real time (no restarts, no connection interruptions), you can focus on developing and deploying new features to your system, instead of 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 this in action in [our API gateway demo video](https://info.traefik.io/watch-traefik-api-gw-demo?cta=docs). !!! quote "From the Traefik Maintainer Team" When developing Traefik, our main goal is to make it easy to use, and we're sure you'll enjoy it. @@ -33,7 +36,7 @@ Traefik supports different needs depending on your background. We keep three use Traefik’s main concepts help you understand how requests flow to your services: - [Entrypoints](./reference/install-configuration/entrypoints.md) are the network entry points into Traefik. They define the port that will receive the packets and whether to listen for TCP or UDP. -- [Routers](./reference/routing-configuration/http/routing/rules-and-priority.md) are in charge of connecting incoming requests to the services that can handle them. In the process, routers may use pieces of [middleware](./reference/routing-configuration/http/middlewares/overview.md) to update the request or act before forwarding the request to the service. +- [Routers](./reference/routing-configuration/http/router/rules-and-priority.md) are in charge of connecting incoming requests to the services that can handle them. In the process, routers may use pieces of [middleware](./reference/routing-configuration/http/middlewares/overview.md) to update the request or act before forwarding the request to the service. - [Services](./reference/routing-configuration/http/load-balancing/service.md) are responsible for configuring how to reach the actual services that will eventually handle the incoming requests. - [Providers](./reference/install-configuration/providers/overview.md) are infrastructure components, whether orchestrators, container engines, cloud providers, or key-value stores. The idea is that Traefik queries the provider APIs in order to find relevant information about routing, and when Traefik detects a change, it dynamically updates the routes. @@ -50,6 +53,6 @@ Use the sidebar to navigate to the section that is most appropriate for your nee 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 upgrading to our API gateway ([watch demo video](https://info.traefik.io/watch-traefik-api-gw-demo)) for better security, control, and 24/7 support. + 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). - Just need support? Explore our [24/7/365 support for Traefik OSS](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). diff --git a/docs/content/middlewares/http/addprefix.md b/docs/content/middlewares/http/addprefix.md index 668855d47..011a51f2e 100644 --- a/docs/content/middlewares/http/addprefix.md +++ b/docs/content/middlewares/http/addprefix.md @@ -8,6 +8,8 @@ description: "Learn how to implement the HTTP AddPrefix middleware in Traefik Pr Prefixing the Path {: .subtitle } +![AddPrefix](../../assets/img/middleware/addprefix.png) + The AddPrefix middleware updates the path of a request before forwarding it. ## Configuration Examples diff --git a/docs/content/middlewares/http/basicauth.md b/docs/content/middlewares/http/basicauth.md index 75d3a9a0c..5faaa1e3e 100644 --- a/docs/content/middlewares/http/basicauth.md +++ b/docs/content/middlewares/http/basicauth.md @@ -8,6 +8,8 @@ description: "The HTTP basic authentication (BasicAuth) middleware in Traefik Pr Adding Basic Authentication {: .subtitle } +![BasicAuth](../../assets/img/middleware/basicauth.png) + The BasicAuth middleware grants access to services to authorized users only. ## Configuration Examples diff --git a/docs/content/middlewares/http/buffering.md b/docs/content/middlewares/http/buffering.md index 4c861a8fe..1c1259419 100644 --- a/docs/content/middlewares/http/buffering.md +++ b/docs/content/middlewares/http/buffering.md @@ -8,6 +8,8 @@ description: "The HTTP buffering middleware in Traefik Proxy limits the size of How to Read the Request before Forwarding It {: .subtitle } +![Buffering](../../assets/img/middleware/buffering.png) + The Buffering middleware limits the size of requests that can be forwarded to services. With Buffering, Traefik reads the entire request into memory (possibly buffering large requests into disk), and rejects requests that are over a specified size limit. diff --git a/docs/content/middlewares/http/chain.md b/docs/content/middlewares/http/chain.md index 511409085..caf1a84c9 100644 --- a/docs/content/middlewares/http/chain.md +++ b/docs/content/middlewares/http/chain.md @@ -8,6 +8,8 @@ description: "The HTTP chain middleware lets you define reusable combinations of When One Isn't Enough {: .subtitle } +![Chain](../../assets/img/middleware/chain.png) + The Chain middleware enables you to define reusable combinations of other pieces of middleware. It makes reusing the same groups easier. diff --git a/docs/content/middlewares/http/circuitbreaker.md b/docs/content/middlewares/http/circuitbreaker.md index 035de259e..c1ead36b6 100644 --- a/docs/content/middlewares/http/circuitbreaker.md +++ b/docs/content/middlewares/http/circuitbreaker.md @@ -8,6 +8,8 @@ description: "The HTTP circuit breaker in Traefik Proxy prevents stacking reques Don't Waste Time Calling Unhealthy Services {: .subtitle } +![CircuitBreaker](../../assets/img/middleware/circuitbreaker.png) + The circuit breaker protects your system from stacking requests to unhealthy services, resulting in cascading failures. When your system is healthy, the circuit is closed (normal operations). @@ -158,8 +160,8 @@ Here is the list of supported operators: ### Fallback mechanism -By default the fallback mechanism returns a `HTTP 503 Service Unavailable` to the client instead of calling the target service. -The response code can be configured. +The fallback mechanism returns a `HTTP 503 Service Unavailable` to the client instead of calling the target service. +This behavior cannot be configured. ### `CheckPeriod` diff --git a/docs/content/middlewares/http/compress.md b/docs/content/middlewares/http/compress.md index abb76c914..58d06de96 100644 --- a/docs/content/middlewares/http/compress.md +++ b/docs/content/middlewares/http/compress.md @@ -8,6 +8,8 @@ description: "Traefik Proxy's HTTP middleware lets you compress responses before Compress Allows Compressing Responses before Sending them to the Client {: .subtitle } +![Compress](../../assets/img/middleware/compress.png) + The Compress middleware supports Gzip, Brotli and Zstandard compression. The activation of compression, and the compression method choice rely (among other things) on the request's `Accept-Encoding` header. diff --git a/docs/content/middlewares/http/digestauth.md b/docs/content/middlewares/http/digestauth.md index cdb0128d4..a5e2f3d06 100644 --- a/docs/content/middlewares/http/digestauth.md +++ b/docs/content/middlewares/http/digestauth.md @@ -8,6 +8,8 @@ description: "Traefik Proxy's HTTP DigestAuth middleware restricts access to you Adding Digest Authentication {: .subtitle } +![BasicAuth](../../assets/img/middleware/digestauth.png) + The DigestAuth middleware grants access to services to authorized users only. ## Configuration Examples diff --git a/docs/content/middlewares/http/errorpages.md b/docs/content/middlewares/http/errorpages.md index d435dbcb5..1a4f11c41 100644 --- a/docs/content/middlewares/http/errorpages.md +++ b/docs/content/middlewares/http/errorpages.md @@ -8,6 +8,8 @@ description: "In Traefik Proxy, the Errors middleware returns custom pages accor It Has Never Been Easier to Say That Something Went Wrong {: .subtitle } +![Errors](../../assets/img/middleware/errorpages.png) + The Errors middleware returns a custom page in lieu of the default, according to configured ranges of HTTP Status codes. !!! important diff --git a/docs/content/middlewares/http/forwardauth.md b/docs/content/middlewares/http/forwardauth.md index 5f60cd138..ecf61ab51 100644 --- a/docs/content/middlewares/http/forwardauth.md +++ b/docs/content/middlewares/http/forwardauth.md @@ -8,6 +8,8 @@ description: "In Traefik Proxy, the HTTP ForwardAuth middleware delegates authen Using an External Service to Forward Authentication {: .subtitle } +![AuthForward](../../assets/img/middleware/authforward.png) + The ForwardAuth middleware delegates authentication to an external service. If the service answers with a 2XX code, access is granted, and the original request is performed. Otherwise, the response from the authentication server is returned. @@ -58,11 +60,11 @@ The following request properties are provided to the forward-auth target endpoin | Property | Forward-Request Header | |-------------------|------------------------| -| HTTP Method | `X-Forwarded-Method` | -| Protocol | `X-Forwarded-Proto` | -| Host | `X-Forwarded-Host` | -| Request URI | `X-Forwarded-Uri` | -| Source IP-Address | `X-Forwarded-For` | +| HTTP Method | X-Forwarded-Method | +| Protocol | X-Forwarded-Proto | +| Host | X-Forwarded-Host | +| Request URI | X-Forwarded-Uri | +| Source IP-Address | X-Forwarded-For | ## Configuration Options diff --git a/docs/content/middlewares/http/headers.md b/docs/content/middlewares/http/headers.md index 419642391..afeb6891f 100644 --- a/docs/content/middlewares/http/headers.md +++ b/docs/content/middlewares/http/headers.md @@ -8,6 +8,8 @@ description: "In Traefik Proxy, the HTTP headers middleware manages the headers Managing Request/Response headers {: .subtitle } +![Headers](../../assets/img/middleware/headers.png) + The Headers middleware manages the headers of requests and responses. A set of forwarded headers are automatically added by default. See the [FAQ](../../getting-started/faq.md#what-are-the-forwarded-headers-when-proxying-http-requests) for more information. diff --git a/docs/content/middlewares/http/inflightreq.md b/docs/content/middlewares/http/inflightreq.md index bdde9b699..48200fd90 100644 --- a/docs/content/middlewares/http/inflightreq.md +++ b/docs/content/middlewares/http/inflightreq.md @@ -8,6 +8,8 @@ description: "Traefik Proxy's HTTP middleware lets you limit the number of simul Limiting the Number of Simultaneous In-Flight Requests {: .subtitle } +![InFlightReq](../../assets/img/middleware/inflightreq.png) + To proactively prevent services from being overwhelmed with high load, the number of allowed simultaneous in-flight requests can be limited. ## Configuration Examples @@ -113,7 +115,7 @@ The `depth` option tells Traefik to use the `X-Forwarded-For` header and select If `ipStrategy.ipv6Subnet` is provided and the selected IP is IPv6, the IP is transformed into the first IP of the subnet it belongs to. See [ipStrategy.ipv6Subnet](#ipstrategyipv6subnet) for more details. -!!! example "Example of Depth & `X-Forwarded-For`" +!!! example "Example of Depth & X-Forwarded-For" If `depth` is set to 2, and the request `X-Forwarded-For` header is `"10.0.0.1,11.0.0.1,12.0.0.1,13.0.0.1"` then the "real" client IP is `"10.0.0.1"` (at depth 4) but the IP used as the criterion is `"12.0.0.1"` (`depth=2`). @@ -167,7 +169,7 @@ http: !!! important "If `depth` is specified, `excludedIPs` is ignored." -!!! example "Example of ExcludedIPs & `X-Forwarded-For`" +!!! example "Example of ExcludedIPs & X-Forwarded-For" | `X-Forwarded-For` | `excludedIPs` | clientIP | |-----------------------------------------|-----------------------|--------------| diff --git a/docs/content/middlewares/http/ipallowlist.md b/docs/content/middlewares/http/ipallowlist.md index 60565fd90..4f9331268 100644 --- a/docs/content/middlewares/http/ipallowlist.md +++ b/docs/content/middlewares/http/ipallowlist.md @@ -78,7 +78,7 @@ The `depth` option tells Traefik to use the `X-Forwarded-For` header and take th If `ipStrategy.ipv6Subnet` is provided and the selected IP is IPv6, the IP is transformed into the first IP of the subnet it belongs to. See [ipStrategy.ipv6Subnet](#ipstrategyipv6subnet) for more details. -!!! example "Examples of Depth & `X-Forwarded-For`" +!!! example "Examples of Depth & X-Forwarded-For" If `depth` is set to 2, and the request `X-Forwarded-For` header is `"10.0.0.1,11.0.0.1,12.0.0.1,13.0.0.1"` then the "real" client IP is `"10.0.0.1"` (at depth 4) but the IP used is `"12.0.0.1"` (`depth=2`). @@ -144,7 +144,7 @@ http: !!! important "If `depth` is specified, `excludedIPs` is ignored." -!!! example "Example of ExcludedIPs & `X-Forwarded-For`" +!!! example "Example of ExcludedIPs & X-Forwarded-For" | `X-Forwarded-For` | `excludedIPs` | clientIP | |-----------------------------------------|-----------------------|--------------| @@ -264,45 +264,3 @@ http: [http.middlewares.test-ipallowlist.ipallowlist.sourceCriterion.ipStrategy] ipv6Subnet = 64 ``` - -### `rejectStatusCode` - -The `rejectStatusCode` option sets HTTP status code for refused requests. If not set, the default is 403 (Forbidden). - -```yaml tab="Docker & Swarm" -# Reject requests with a 404 rather than a 403 -labels: - - "traefik.http.middlewares.test-ipallowlist.ipallowlist.rejectstatuscode=404" -``` - -```yaml tab="Kubernetes" -# Reject requests with a 404 rather than a 403 -apiVersion: traefik.io/v1alpha1 -kind: Middleware -metadata: - name: test-ipallowlist -spec: - ipAllowList: - rejectStatusCode: 404 -``` - -```yaml tab="Consul Catalog" -# Reject requests with a 404 rather than a 403 -- "traefik.http.middlewares.test-ipallowlist.ipallowlist.rejectstatuscode=404" -``` - -```yaml tab="File (YAML)" -# Reject requests with a 404 rather than a 403 -http: - middlewares: - test-ipallowlist: - ipAllowList: - rejectStatusCode: 404 -``` - -```toml tab="File (TOML)" -# Reject requests with a 404 rather than a 403 -[http.middlewares] - [http.middlewares.test-ipallowlist.ipAllowList] - rejectStatusCode = 404 -``` diff --git a/docs/content/middlewares/http/ipwhitelist.md b/docs/content/middlewares/http/ipwhitelist.md index 894dfc316..b6aa60136 100644 --- a/docs/content/middlewares/http/ipwhitelist.md +++ b/docs/content/middlewares/http/ipwhitelist.md @@ -8,6 +8,8 @@ description: "Learn how to use IPWhiteList in HTTP middleware for limiting clien Limiting Clients to Specific IPs {: .subtitle } +![IPWhiteList](../../assets/img/middleware/ipwhitelist.png) + IPWhiteList limits allowed requests based on the client IP. !!! warning @@ -82,7 +84,7 @@ The `depth` option tells Traefik to use the `X-Forwarded-For` header and take th If `ipStrategy.ipv6Subnet` is provided and the selected IP is IPv6, the IP is transformed into the first IP of the subnet it belongs to. See [ipStrategy.ipv6Subnet](#ipstrategyipv6subnet) for more details. -!!! example "Examples of Depth & `X-Forwarded-For`" +!!! example "Examples of Depth & X-Forwarded-For" If `depth` is set to 2, and the request `X-Forwarded-For` header is `"10.0.0.1,11.0.0.1,12.0.0.1,13.0.0.1"` then the "real" client IP is `"10.0.0.1"` (at depth 4) but the IP used for the whitelisting is `"12.0.0.1"` (`depth=2`). @@ -148,7 +150,7 @@ http: !!! important "If `depth` is specified, `excludedIPs` is ignored." -!!! example "Example of ExcludedIPs & `X-Forwarded-For`" +!!! example "Example of ExcludedIPs & X-Forwarded-For" | `X-Forwarded-For` | `excludedIPs` | clientIP | |-----------------------------------------|-----------------------|--------------| diff --git a/docs/content/middlewares/http/overview.md b/docs/content/middlewares/http/overview.md index cb40c56b4..b6a1eb1b8 100644 --- a/docs/content/middlewares/http/overview.md +++ b/docs/content/middlewares/http/overview.md @@ -8,6 +8,8 @@ description: "Read the official Traefik Proxy documentation for an overview of t Controlling connections {: .subtitle } +![Overview](../../assets/img/middleware/overview.png) + ## Configuration Example ```yaml tab="Docker & Swarm" diff --git a/docs/content/middlewares/http/ratelimit.md b/docs/content/middlewares/http/ratelimit.md index 12b4644c3..8759e9255 100644 --- a/docs/content/middlewares/http/ratelimit.md +++ b/docs/content/middlewares/http/ratelimit.md @@ -225,7 +225,7 @@ The `depth` option tells Traefik to use the `X-Forwarded-For` header and select If `ipStrategy.ipv6Subnet` is provided and the selected IP is IPv6, the IP is transformed into the first IP of the subnet it belongs to. See [ipStrategy.ipv6Subnet](#ipstrategyipv6subnet) for more details. -!!! example "Example of Depth & `X-Forwarded-For`" +!!! example "Example of Depth & X-Forwarded-For" If `depth` is set to 2, and the request `X-Forwarded-For` header is `"10.0.0.1,11.0.0.1,12.0.0.1,13.0.0.1"` then the "real" client IP is `"10.0.0.1"` (at depth 4) but the IP used as the criterion is `"12.0.0.1"` (`depth=2`). @@ -288,7 +288,7 @@ http: !!! example "Each IP as a distinct source" - | `X-Forwarded-For` | excludedIPs | clientIP | + | X-Forwarded-For | excludedIPs | clientIP | |--------------------------------|-----------------------|--------------| | `"10.0.0.1,11.0.0.1,12.0.0.1"` | `"11.0.0.1,12.0.0.1"` | `"10.0.0.1"` | | `"10.0.0.2,11.0.0.1,12.0.0.1"` | `"11.0.0.1,12.0.0.1"` | `"10.0.0.2"` | @@ -298,7 +298,7 @@ http: !!! example "Group IPs together as same source" - | `X-Forwarded-For` | excludedIPs | clientIP | + | X-Forwarded-For | excludedIPs | clientIP | |--------------------------------|--------------|--------------| | `"10.0.0.1,11.0.0.1,12.0.0.1"` | `"12.0.0.1"` | `"11.0.0.1"` | | `"10.0.0.2,11.0.0.1,12.0.0.1"` | `"12.0.0.1"` | `"11.0.0.1"` | @@ -310,7 +310,7 @@ and the first IP that is _not_ in the pool (if any) is returned. !!! example "Matching for clientIP" - | `X-Forwarded-For` | excludedIPs | clientIP | + | X-Forwarded-For | excludedIPs | clientIP | |--------------------------------|-----------------------|--------------| | `"10.0.0.1,11.0.0.1,13.0.0.1"` | `"11.0.0.1"` | `"13.0.0.1"` | | `"10.0.0.1,11.0.0.1,13.0.0.1"` | `"15.0.0.1,16.0.0.1"` | `"13.0.0.1"` | diff --git a/docs/content/middlewares/http/redirectscheme.md b/docs/content/middlewares/http/redirectscheme.md index 15534a420..793d28b17 100644 --- a/docs/content/middlewares/http/redirectscheme.md +++ b/docs/content/middlewares/http/redirectscheme.md @@ -19,7 +19,7 @@ The RedirectScheme middleware redirects the request if the request scheme is dif When there is at least one other reverse-proxy between the client and Traefik, the other reverse-proxy (i.e. the last hop) needs to be a [trusted](../../routing/entrypoints.md#forwarded-headers) one. - Otherwise, Traefik would clean up the `X-Forwarded` headers coming from this last hop, + Otherwise, Traefik would clean up the X-Forwarded headers coming from this last hop, and as the RedirectScheme middleware relies on them to determine the scheme used, it would not function as intended. diff --git a/docs/content/middlewares/overview.md b/docs/content/middlewares/overview.md index 92be4ecda..a6d7f46be 100644 --- a/docs/content/middlewares/overview.md +++ b/docs/content/middlewares/overview.md @@ -8,6 +8,8 @@ description: "There are several available middleware in Traefik Proxy used to mo Tweaking the Request {: .subtitle } +![Overview](../assets/img/middleware/overview.png) + Attached to the routers, pieces of middleware are a means of tweaking the requests before they are sent to your [service](../routing/services/index.md) (or before the answer from the services are sent to the clients). There are several available middleware in Traefik, some can modify the request, the headers, some are in charge of redirections, some add authentication, and so on. diff --git a/docs/content/middlewares/tcp/overview.md b/docs/content/middlewares/tcp/overview.md index 415a1716c..288f8d827 100644 --- a/docs/content/middlewares/tcp/overview.md +++ b/docs/content/middlewares/tcp/overview.md @@ -8,6 +8,8 @@ description: "Read the official Traefik Proxy documentation for an overview of t Controlling connections {: .subtitle } +![Overview](../../assets/img/middleware/overview.png) + ## Configuration Example ```yaml tab="Docker & Swarm" diff --git a/docs/content/migrate/v1-to-v2.md b/docs/content/migrate/v1-to-v2.md deleted file mode 100644 index 130d730e7..000000000 --- a/docs/content/migrate/v1-to-v2.md +++ /dev/null @@ -1,24 +0,0 @@ ---- -title: "Traefik V2 Migration Documentation" -description: "Migrate from Traefik Proxy v1 to v2 and update all the necessary configurations to take advantage of all the improvements. Read the technical documentation." ---- - -# Migration Guide: From v1 to v2 - -How to Migrate from Traefik v1 to Traefik v2. -{: .subtitle } - -The version 2 of Traefik introduced a number of breaking changes, -which require one to update their configuration when they migrate from v1 to v2. - -For more information about the changes in Traefik v2, please refer to the [v2 documentation](https://doc.traefik.io/traefik/v2.11/migration/v1-to-v2/). - -!!! info "Migration Helper" - - We created a tool to help during the migration: [traefik-migration-tool](https://github.com/traefik/traefik-migration-tool) - - This tool allows to: - - - convert `Ingress` to Traefik `IngressRoute` resources. - - convert `acme.json` file from v1 to v2 format. - - migrate the static configuration contained in the file `traefik.toml` to a Traefik v2 file. diff --git a/docs/content/migrate/v2-to-v3.md b/docs/content/migrate/v2-to-v3.md deleted file mode 100644 index 175f36921..000000000 --- a/docs/content/migrate/v2-to-v3.md +++ /dev/null @@ -1,161 +0,0 @@ ---- -title: "Traefik V3 Migration Documentation" -description: "Migrate from Traefik Proxy v2 to v3 and update all the necessary configurations to take advantage of all the improvements. Read the technical documentation." ---- - -# Migration Guide: From v2 to v3 - -How to Migrate from Traefik v2 to Traefik v3. -{: .subtitle } - -!!! success "Streamlined Migration Process" - Traefik v3 introduces minimal breaking changes and maintains backward compatibility with v2 syntax in dynamic configuration, offering a gradual migration path. - -With Traefik v3, we are introducing a streamlined transition process from v2. Minimal breaking changes have been made to specific options in the [static configuration](./v2-to-v3-details.md#install-configuration-changes "Link to install configuration changes"), and we are ensuring backward compatibility with v2 syntax in the [dynamic configuration](./v2-to-v3-details.md#routing-configuration-changes "Link to routing configuration changes"). This will offer a gradual path for adopting the v3 syntax, allowing users to progressively migrate their Kubernetes ingress resources, Docker labels, etc., to the new format. - -## Migration Overview - -The migration process consists of three progressive steps designed to minimize risk and ensure a smooth transition: - -!!! abstract "Migration Steps" - **Step 1:** [Prepare configurations and test v3](#step-1-prepare-configurations-and-test-v3) - **Step 2:** [Migrate production instances to Traefik v3](#step-2-migrate-production-instances-to-traefik-v3) - **Step 3:** [Progressively migrate dynamic configuration](#step-3-progressively-migrate-dynamic-configuration) - ---- - -## Step 1: Prepare Configurations and Test v3 - -!!! info "Preparation Phase" - This step focuses on updating static configurations and enabling backward compatibility for a safe testing environment. - -### Configuration Updates - -**Review and Update Static Configuration** - -Check the changes in [static configurations](./v2-to-v3-details.md#install-configuration-changes "Link to install configuration changes") and [operations](./v2-to-v3-details.md#operations-changes "Link to operations changes") brought by Traefik v3. Modify your configurations accordingly. - -**Enable v2 Compatibility Mode** - -Add the following configuration to maintain v2 syntax compatibility: - -```yaml -# static configuration -core: - defaultRuleSyntax: v2 -``` - -!!! note "Backward Compatibility" - This snippet in the static configuration makes the [v2 format](../migrate/v2-to-v3-details.md#configure-the-default-syntax-in-static-configuration "Link to configure default syntax in static config") the default rule matchers syntax. - -### Testing Phase - -**Start Your Test Environment** - -1. Start Traefik v3 with the updated configuration -2. Monitor the startup logs for any errors -3. Test routing to your applications - -**Validation Checklist** - -- ✅ Traefik starts without error logs -- ✅ All routes are functioning correctly -- ✅ Applications are accessible through Traefik - -!!! success "Ready for Next Step" - If you don't get any error logs while testing, you are good to go! Otherwise, follow the remaining migration options highlighted in the logs. - -Once your Traefik test instances are starting and routing to your applications, proceed to the next step. - ---- - -## Step 2: Migrate Production Instances to Traefik v3 - -!!! warning "Production Migration" - This is the critical step where you migrate your production environment. Proper monitoring and rollback preparation are essential. - -### Migration Strategy - -**Progressive Deployment** - -We strongly advise you to follow a progressive migration strategy ([Kubernetes rolling update mechanism](https://kubernetes.io/docs/tutorials/kubernetes-basics/update/update-intro/ "Link to the Kubernetes rolling update documentation"), for example) to migrate your production instances to v3. - -**Required Preparations** - -!!! danger "Critical Requirements" - - ✅ **Real-time monitoring solution** for ingress traffic ([monitoring guide](https://traefik.io/blog/capture-traefik-metrics-for-apps-on-kubernetes-with-prometheus/ "Link to the blog on capturing Traefik metrics with Prometheus")) - - ✅ **Rollback plan** ready for immediate execution - - ✅ **Team availability** during migration window - -### Migration Execution - -**During Migration:** - -1. **Monitor continuously:** Watch ingress traffic for any errors or anomalies -2. **Be prepared to rollback:** Have your rollback procedure ready to execute immediately -3. **Use debug logs:** Leverage debug and access logs to understand any issues that arise - -**Validation Steps:** - -- Monitor response times and error rates -- Verify all critical application paths are working -- Check that SSL/TLS termination is functioning correctly -- Validate middleware behavior - -!!! success "Migration Complete" - Once every Traefik instance is updated, you will be on Traefik v3! - ---- - -## Step 3: Progressively Migrate Dynamic Configuration - -!!! info "Optional Immediate Step" - This step can be done later in the process, as Traefik v3 is compatible with the v2 format for [dynamic configuration](./v2-to-v3-details.md#routing-configuration-changes "Link to routing configuration changes"). Enable Traefik logs to get some help if any deprecated option is in use. - -### Migration Process - -**Review Dynamic Configuration Changes** - -Check the changes in [dynamic configuration](./v2-to-v3-details.md#routing-configuration-changes "Link to routing configuration changes") to understand what updates are needed. - -**Progressive Router Migration** - -1. **Select a router** to migrate first (start with non-critical services) -2. **[Switch to v3 syntax](./v2-to-v3-details.md#configure-the-syntax-per-router "Link to configuring the syntax per router")** for that specific router -3. **Test thoroughly** to ensure ingress traffic is not impacted -4. **Deploy and validate** the updated resource -5. **Remove the old v2 resource** once validation is complete -6. **Repeat** for each remaining router - -### Migration Best Practices - -!!! tip "Migration Strategy" - - Start with development or staging environments - - Migrate one service at a time - - Test each migration thoroughly before proceeding - - Keep detailed logs of what was changed - -### Final Configuration Cleanup - -Once all Ingress resources are migrated to v3 syntax, remove the compatibility configuration: - -```yaml -# Remove this from static configuration -core: - defaultRuleSyntax: v2 # ← Delete this entire section -``` - -!!! success "🎉 Migration Complete!" - You are now fully migrated to Traefik v3 and can take advantage of all the new features and improvements! - -### Post-Migration Verification - -**Final Checklist:** - -- ✅ All routers use v3 syntax -- ✅ v2 compatibility mode disabled -- ✅ No deprecated warnings in logs -- ✅ All applications functioning correctly -- ✅ Performance metrics stable - -{!traefik-for-business-applications.md!} diff --git a/docs/content/migrate/v2.md b/docs/content/migrate/v2.md deleted file mode 100644 index 4816bff90..000000000 --- a/docs/content/migrate/v2.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -title: "Traefik V2 Migration Documentation" -description: "Learn the steps needed to migrate to new Traefik Proxy v2 versions, i.e. v2.0 to v2.1 or v2.1 to v2.2. Read the technical documentation." ---- - -# Migration: Steps needed between the v2 versions - -The multiple minor versions of Traefik v2 introduced a number of changes, -which may require one to update their configuration when they migrate. - -For more information about the changes between Traefik v2 minor versions, please refer to the [v2 documentation](https://doc.traefik.io/traefik/v2.11/migration/v2/). diff --git a/docs/content/migrate/v3.md b/docs/content/migrate/v3.md deleted file mode 100644 index 657dac8d6..000000000 --- a/docs/content/migrate/v3.md +++ /dev/null @@ -1,498 +0,0 @@ ---- -title: "Traefik Migration Documentation" -description: "Learn the steps needed to migrate to new Traefik Proxy v3 versions. Read the technical documentation." ---- - -# Migration: Steps needed between the versions - -This guide provides detailed migration steps for upgrading between different Traefik v3 versions. Each section covers breaking changes, deprecations, and configuration updates required for a smooth transition. - ---- - -## v3.0 to v3.1 - -### Kubernetes Provider RBACs - -Starting with v3.1, Traefik's Kubernetes Providers use the [EndpointSlices API](https://kubernetes.io/docs/concepts/services-networking/endpoint-slices/) (requires Kubernetes >=v1.21) for service endpoint discovery. This change also introduces NodePort load-balancing capabilities. - -The following RBAC updates are required for all Kubernetes providers: - -- Remove endpoints permissions and add endpointslices: - -```yaml -# Remove this section from your RBAC -# - apiGroups: [""] -# resources: ["endpoints"] -# verbs: ["get", "list", "watch"] - -# Add this section instead -- apiGroups: - - discovery.k8s.io - resources: - - endpointslices - verbs: - - list - - watch -``` - -- Add nodes permissions for NodePort support: - -```yaml -- apiGroups: - - "" - resources: - - nodes - verbs: - - get - - list - - watch -``` - -!!! note "Affected Providers" - These changes apply to: - - - [KubernetesIngress](../routing/providers/kubernetes-ingress.md#configuration-example) provider - - [KubernetesCRD](../reference/dynamic-configuration/kubernetes-crd.md#rbac) provider - - [KubernetesGateway](../reference/dynamic-configuration/kubernetes-gateway-rbac.yml) provider - -#### Gateway API: KubernetesGateway Provider - -The KubernetesGateway Provider is no longer experimental in v3.1 and can be enabled without the `experimental.kubernetesgateway` option. - -**Deprecated Configuration:** - -??? example "Experimental kubernetesgateway option (deprecated)" - - ```yaml tab="File (YAML)" - experimental: - kubernetesgateway: true - ``` - - ```toml tab="File (TOML)" - [experimental] - kubernetesgateway=true - ``` - - ```bash tab="CLI" - --experimental.kubernetesgateway=true - ``` - -**Migration Steps:** - -1. Remove the `kubernetesgateway` option from the experimental section -2. Configure the provider using the [KubernetesGateway Provider documentation](../providers/kubernetes-gateway.md) - ---- - -## v3.1.0 to v3.1.1 - -### IngressClass Lookup - -The `disableIngressClassLookup` option has been deprecated and will be removed in the next major version. - -**Migration Required:** - -- **Old:** `disableIngressClassLookup` -- **New:** `disableClusterScopeResources` - -The new option provides broader control over cluster scope resources discovery, including both IngressClass and Nodes resources. - ---- - -## v3.1 to v3.2 - -### Kubernetes CRD Provider - -New optional fields have been added to several CRDs. These updates are backward compatible and only add new functionality. - -**Apply the latest CRDs:** - -```shell -kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.3/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml -``` - -**Updated Resources:** - -- [TraefikService](../routing/services/index.md#mirroring-service) ([PR #11032](https://github.com/traefik/traefik/pull/11032)) -- [RateLimit](../middlewares/http/ratelimit.md) & [InFlightReq](../middlewares/http/inflightreq.md) middlewares ([PR #9747](https://github.com/traefik/traefik/pull/9747)) -- [Compress](../middlewares/http/compress.md) middleware ([PR #10943](https://github.com/traefik/traefik/pull/10943)) - -### Kubernetes Gateway Provider Standard Channel - -Starting with v3.2, the Kubernetes Gateway Provider now supports [GRPCRoute](https://gateway-api.sigs.k8s.io/api-types/grpcroute/) resources. - -Therefore, in the corresponding RBACs (see [KubernetesGateway](../reference/dynamic-configuration/kubernetes-gateway-rbac.yml) provider RBACs), -the `grcroutes` and `grpcroutes/status` rights have to be added. - -**Required RBAC Updates:** - -```yaml -... -- apiGroups: - - gateway.networking.k8s.io - resources: - - grpcroutes - verbs: - - get - - list - - watch -- apiGroups: - - gateway.networking.k8s.io - resources: - - grpcroutes/status - verbs: - - update -... -``` - -### Kubernetes Gateway Provider Experimental Channel - -Due to breaking changes in Kubernetes Gateway [v1.2.0-rc1](https://github.com/kubernetes-sigs/gateway-api/releases/tag/v1.2.0-rc1), Traefik v3.3 only supports Kubernetes Gateway v1.2.x when experimental features are enabled. - -**New Feature: BackendTLSPolicy Support** - -The provider now supports [BackendTLSPolicy](https://gateway-api.sigs.k8s.io/api-types/backendtlspolicy/) resources. - -Therefore, in the corresponding RBACs (see [KubernetesGateway](../reference/dynamic-configuration/kubernetes-gateway-rbac.yml) provider RBACs), -the `backendtlspolicies` and `backendtlspolicies/status` rights have to be added. - -**Required RBAC Updates:** - -```yaml - ... - - apiGroups: - - "" - resources: - - configmaps - verbs: - - get - - list - - watch - - apiGroups: - - gateway.networking.k8s.io - resources: - - backendtlspolicies - verbs: - - get - - list - - watch - - apiGroups: - - gateway.networking.k8s.io - resources: - - backendtlspolicies/status - verbs: - - update - ... -``` - ---- - -## v3.2.1 - -### `X-Forwarded-Prefix` Header Changes - -In v3.2.1, the `X-Forwarded-Prefix` header is now handled like other `X-Forwarded-*` headers - Traefik removes it when sent from untrusted sources. - -This change improves security by preventing header spoofing from untrusted clients. Refer to the [Forwarded headers documentation](../routing/entrypoints.md#forwarded-headers) for configuration details. - ---- - -## v3.2.2 - -### Swarm Provider Label Updates - -In v3.2.2, Swarm-specific labels have been deprecated and will be removed in a future version. - -**Migration Required:** - -| Deprecated Label | New Label | -|------------------|-----------| -| `traefik.docker.network` | `traefik.swarm.network` | -| `traefik.docker.lbswarm` | `traefik.swarm.lbswarm` | - ---- - -## v3.2 to v3.3 - -### ACME DNS Certificate Resolver - -In v3.3, DNS challenge configuration options have been reorganized for better clarity. - -**Migration Required:** - -| Deprecated Option | New Option | -|-------------------|------------| -| `acme.dnsChallenge.delaybeforecheck` | `acme.dnsChallenge.propagation.delayBeforeChecks` | -| `acme.dnsChallenge.disablepropagationcheck` | `acme.dnsChallenge.propagation.disableChecks` | - -### Tracing Global Attributes - -In v3.3, the tracing configuration has been clarified to better reflect its purpose. - -**Migration Required:** - -- **Old:** `tracing.globalAttributes` -- **New:** `tracing.resourceAttributes` - -The old option name was misleading as it specifically adds resource attributes for the collector, not global span attributes. - ---- - -## v3.3.4 - -### OpenTelemetry Request Duration Metric - -In v3.3.4, the OpenTelemetry Request Duration metric unit has been standardized to match other providers and naming conventions. - -**Change Details:** - -- **Metric:** `traefik_(entrypoint|router|service)_request_duration_seconds` -- **Old Unit:** Milliseconds -- **New Unit:** Seconds - -This change ensures consistency across all metrics providers and follows standard naming conventions. - ---- - -## v3.3.5 - -### Compress Middleware Default Encodings - -In v3.3.5, the default compression algorithms have been reordered to favor gzip compression. - -**New Default:** `gzip, br, zstd` - -This change affects requests that either: - -- Don't specify preferred algorithms in the `Accept-Encoding` header -- Have no order preference in their `Accept-Encoding` header - -The reordering helps ensure better compatibility with older clients that may not support newer compression algorithms. - ---- - -## v3.3.6 - -### Request Path Sanitization - -Starting with v3.3.6, incoming request paths are now automatically cleaned before processing for security and consistency. - -**What's Changed:** - -The following path segments are now interpreted and collapsed: - -- `/../` (parent directory references) -- `/./` (current directory references) -- Duplicate slash segments (`//`) - -**Disabling Sanitization:** - -```yaml -# EntryPoint HTTP configuration -entryPoints: - web: - address: ":80" - http: - sanitizePath: false # Not recommended -``` - -!!! danger "Security Warning" - Setting `sanitizePath: false` is not safe. This option should only be used with legacy clients that don't properly URL-encode data. Always ensure requests are properly URL-encoded instead of disabling this security feature. - -**Example Risk:** -Base64 data containing "/" characters can lead to unsafe routing when path sanitization is disabled and the data isn't URL-encoded. - ---- - -## v3.3 to v3.4 - -### Kubernetes CRD Provider - -#### Load-Balancing Strategy Updates - -Starting with v3.4, HTTP service definitions now support additional load-balancing strategies for better traffic distribution. - -**Apply Updated CRDs:** - -```shell -kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.4/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml -``` - -**New Strategy Values:** - -- `wrr` (Weighted Round Robin) -- `p2c` (Power of Two Choices) - -!!! warning "Deprecation" - The `RoundRobin` strategy is deprecated but still supported (equivalent to `wrr`). It will be removed in the next major release. - -Refer to the [HTTP Services Load Balancing documentation](../routing/services/index.md#load-balancing-strategy) for detailed information. - -#### ServersTransport CA Certificate Configuration - -A new `rootCAs` option has been added to the `ServersTransport` and `ServersTransportTCP` CRDs. It supports both ConfigMaps and Secrets for CA certificates and replaces the `rootCAsSecrets` option. - -**Apply Updates:** - -```shell -# Update CRDs -kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.4/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml - -# Update RBACs -kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.4/docs/content/reference/dynamic-configuration/kubernetes-crd-rbac.yml -``` - -**New Configuration Format:** - -```yaml ---- -apiVersion: traefik.io/v1alpha1 -kind: ServersTransport -metadata: - name: foo - namespace: bar -spec: - rootCAs: - - configMap: ca-config-map - - secret: ca-secret - ---- -apiVersion: traefik.io/v1alpha1 -kind: ServersTransportTCP -metadata: - name: foo - namespace: bar -spec: - rootCAs: - - configMap: ca-config-map - - secret: ca-secret -``` - -!!! warning "Deprecation" - The `rootCAsSecrets` option (Secrets only) is still supported but deprecated. It will be removed in the next major release. - -### Rule Syntax Configuration - -In v3.4, rule syntax configuration options will be removed in the next major version. - -**Deprecated Options:** - -- `core.defaultRuleSyntax` (static configuration) -- `ruleSyntax` (router option) - -These options were transitional helpers for migrating from v2 to v3 syntax. Please ensure all router rules use v3 syntax before the next major release. - ---- - -## v3.4.1 - -### Request Path Normalization - -Starting with v3.4.1, request paths are now normalized according to RFC 3986 standards for better consistency and security. - -**Normalization Process:** - -1. **Unreserved Character Decoding:** Characters like `%2E` (.) are decoded to their literal form -2. **Case Normalization:** Percent-encoded characters are uppercased (`%2e` becomes `%2E`) - -This follows [RFC 3986 percent-encoding normalization](https://datatracker.ietf.org/doc/html/rfc3986#section-6.2.2.2) and [case normalization](https://datatracker.ietf.org/doc/html/rfc3986#section-6.2.2.1) standards. - -**Processing Order:** - -1. Path normalization (cannot be disabled) -2. Path sanitization (if enabled) - -### Reserved Character Handling in Routing - -Starting with v3.4.1, reserved characters (per [RFC 3986](https://datatracker.ietf.org/doc/html/rfc3986#section-2.2)) remain encoded during router rule matching to prevent routing ambiguity. - -**Why This Matters:** -Reserved characters change the meaning of request paths when decoded. Keeping them encoded during routing prevents security vulnerabilities and ensures predictable routing behavior. - -### Request Path Matching Examples - -The following table illustrates how path matching behavior has changed: - -| Request Path | Router Rule | Traefik v3.4.0 | Traefik v3.4.1 | Explanation | -|-------------------|------------------------------|----------------|----------------|-------------------------------------------------------| -| `/foo%2Fbar` | ```PathPrefix(`/foo/bar`)``` | Match | No match | `%2F` (/) stays encoded, preventing false matches | -| `/foo/../bar` | ```PathPrefix(`/foo`)``` | No match | No match | Path traversal is sanitized away | -| `/foo/../bar` | ```PathPrefix(`/bar`)``` | Match | Match | Resolves to `/bar` after sanitization | -| `/foo/%2E%2E/bar` | ```PathPrefix(`/foo`)``` | Match | No match | Encoded dots normalized then sanitized | -| `/foo/%2E%2E/bar` | ```PathPrefix(`/bar`)``` | No match | Match | Resolves to `/bar` after normalization + sanitization | - -## v3.4.5 - -### MultiPath TCP - -Since `v3.4.5`, the MultiPath TCP support introduced with `v3.4.2` has been removed. -It appears that enabling MPTCP on some platforms can cause Traefik to stop with the following error logs message: - -- `set tcp X.X.X.X:X->X.X.X.X:X: setsockopt: operation not supported` - -However, it can be re-enabled by setting the `multipathtcp` variable in the GODEBUG environment variable, see the related [go documentation](https://go.dev/doc/godebug#go-124). - -## v3.5.0 - -### Observability - -#### TraceVerbosity on Routers and Entrypoints - -Starting with `v3.5.0`, a new `traceVerbosity` option is available for both entrypoints and routers. -This option allows you to control the level of detail for tracing spans. -Routers can override the value inherited from their entrypoint. - -**Impact:** - -- If you rely on tracing, review your configuration to explicitly set the desired verbosity level. -- Existing configurations will default to `minimal` unless overridden, which will result in fewer spans being generated than before. - -Possible values are: - -- `minimal`: produces a single server span and one client span for each request processed by a router. -- `detailed`: enables the creation of additional spans for each middleware executed for each request processed by a router. - -See the updated documentation for [entrypoints](../reference/install-configuration/entrypoints.md) and [dynamic routers](../reference/routing-configuration/http/routing/observability.md#traceverbosity). - -#### K8s Resource Attributes - -Since `v3.5.0`, the semconv attributes `k8s.pod.name` and `k8s.pod.uid` are injected automatically in OTel resource attributes when OTel tracing/logs/metrics are enabled. - -For that purpose, the following right has to be added to the Traefik Kubernetes RBACs: - -```yaml - ... - - apiGroups: - - "" - resources: - - pods - verbs: - - get - ... -``` - ---- - -## v3.5.2 - -### Deprecation of ProxyProtocol option - -Starting with `v3.5.2`, the `proxyProtocol` option for TCP LoadBalancer is deprecated. -This option can now be configured at the `TCPServersTransport` level, please check out the [documentation](../reference/routing-configuration/tcp/serverstransport.md) for more details. - -#### Kubernetes CRD Provider - -To use the new `proxyprotocol` option in the Kubernetes CRD provider, you need to update your CRDs. - -**Apply Updated CRDs:** - -```shell -kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.5/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml -``` - -## v3.5.4 - -### Certificate Metric Renamed with OpenTelemetry - -Starting with `v3.5.4`, and when using OpenTelemetry, the `traefik_tls_certs_not_after_milliseconds` metric is renamed to `traefik_tls_certs_not_after_seconds`. -This change aligns the metric name with its real unit precision, which is in seconds. diff --git a/docs/content/migration/v1-to-v2.md b/docs/content/migration/v1-to-v2.md new file mode 100644 index 000000000..47d89b250 --- /dev/null +++ b/docs/content/migration/v1-to-v2.md @@ -0,0 +1,1153 @@ +--- +title: "Traefik V2 Migration Documentation" +description: "Migrate from Traefik Proxy v1 to v2 and update all the necessary configurations to take advantage of all the improvements. Read the technical documentation." +--- + +# Migration Guide: From v1 to v2 + +How to Migrate from Traefik v1 to Traefik v2. +{: .subtitle } + +The version 2 of Traefik introduces a number of breaking changes, +which require one to update their configuration when they migrate from v1 to v2. +The goal of this page is to recapitulate all of these changes, and in particular to give examples, +feature by feature, of how the configuration looked like in v1, and how it now looks like in v2. + +!!! info "Migration Helper" + + We created a tool to help during the migration: [traefik-migration-tool](https://github.com/traefik/traefik-migration-tool) + + This tool allows to: + + - convert `Ingress` to Traefik `IngressRoute` resources. + - convert `acme.json` file from v1 to v2 format. + - migrate the static configuration contained in the file `traefik.toml` to a Traefik v2 file. + +## Frontends and Backends Are Dead, Long Live Routers, Middlewares, and Services + +During the transition from v1 to v2, a number of internal pieces and components of Traefik were rewritten and reorganized. +As such, the combination of core notions such as frontends and backends has been replaced with the combination of [routers](../routing/routers/index.md), [services](../routing/services/index.md), and [middlewares](../middlewares/overview.md). + +Typically, a router replaces a frontend, and a service assumes the role of a backend, with each router referring to a service. +However, even though a backend was in charge of applying any desired modification on the fly to the incoming request, +the router defers that responsibility to another component. +Instead, a dedicated middleware is now defined for each kind of such modification. +Then any router can refer to an instance of the wanted middleware. + +!!! example "One frontend with basic auth and one backend, become one router, one service, and one basic auth middleware." + + !!! info "v1" + + ```yaml tab="Docker & Swarm" + labels: + - "traefik.frontend.rule=Host:test.localhost;PathPrefix:/test" + - "traefik.frontend.auth.basic.users=test:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/,test2:$$apr1$$d9hr9HBB$$4HxwgUir3HP4EsggP/QNo0" + ``` + + ```yaml tab="Ingress" + apiVersion: networking.k8s.io/v1beta1 + kind: Ingress + metadata: + name: traefik + namespace: kube-system + annotations: + kubernetes.io/ingress.class: traefik + traefik.ingress.kubernetes.io/rule-type: PathPrefix + spec: + rules: + - host: test.localhost + http: + paths: + - path: /test + backend: + serviceName: server0 + servicePort: 80 + - path: /test + backend: + serviceName: server1 + servicePort: 80 + ``` + + ```toml tab="File (TOML)" + [frontends] + [frontends.frontend1] + entryPoints = ["http"] + backend = "backend1" + + [frontends.frontend1.routes] + [frontends.frontend1.routes.route0] + rule = "Host:test.localhost" + [frontends.frontend1.routes.route0] + rule = "PathPrefix:/test" + + [frontends.frontend1.auth] + [frontends.frontend1.auth.basic] + users = [ + "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", + "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", + ] + + [backends] + [backends.backend1] + [backends.backend1.servers.server0] + url = "http://10.10.10.1:80" + [backends.backend1.servers.server1] + url = "http://10.10.10.2:80" + + [backends.backend1.loadBalancer] + method = "wrr" + ``` + + !!! info "v2" + + ```yaml tab="Docker & Swarm" + labels: + - "traefik.http.routers.router0.rule=Host(`test.localhost`) && PathPrefix(`/test`)" + - "traefik.http.routers.router0.middlewares=auth" + - "traefik.http.middlewares.auth.basicauth.users=test:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/,test2:$$apr1$$d9hr9HBB$$4HxwgUir3HP4EsggP/QNo0" + ``` + + ```yaml tab="IngressRoute" + # The definitions below require the definitions for the Middleware and IngressRoute kinds. + # https://doc.traefik.io/traefik/reference/dynamic-configuration/kubernetes-crd/#definitions + apiVersion: traefik.io/v1alpha1 + kind: Middleware + metadata: + name: basicauth + namespace: foo + + spec: + basicAuth: + users: + - test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/ + - test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0 + + --- + apiVersion: traefik.io/v1alpha1 + kind: IngressRoute + metadata: + name: ingressroutebar + + spec: + entryPoints: + - http + routes: + - match: Host(`test.localhost`) && PathPrefix(`/test`) + kind: Rule + services: + - name: server0 + port: 80 + - name: server1 + port: 80 + middlewares: + - name: basicauth + namespace: foo + ``` + + ```yaml tab="File (YAML)" + http: + routers: + router0: + rule: "Host(`test.localhost`) && PathPrefix(`/test`)" + service: my-service + middlewares: + - auth + + services: + my-service: + loadBalancer: + servers: + - url: http://10.10.10.1:80 + - url: http://10.10.10.2:80 + + middlewares: + auth: + basicAuth: + users: + - "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/" + - "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0" + ``` + + ```toml tab="File (TOML)" + [http.routers] + [http.routers.router0] + rule = "Host(`test.localhost`) && PathPrefix(`/test`)" + middlewares = ["auth"] + service = "my-service" + + [http.services] + [[http.services.my-service.loadBalancer.servers]] + url = "http://10.10.10.1:80" + [[http.services.my-service.loadBalancer.servers]] + url = "http://10.10.10.2:80" + + [http.middlewares] + [http.middlewares.auth.basicAuth] + users = [ + "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", + "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0", + ] + ``` + +## TLS Configuration is Now Dynamic, per Router. + +TLS parameters used to be specified in the static configuration, as an entryPoint field. +With Traefik v2, a new dynamic TLS section at the root contains all the desired TLS configurations. +Then, a [router's TLS field](../routing/routers/index.md#tls) can refer to one of the [TLS configurations](../https/tls.md) defined at the root, hence defining the [TLS configuration](../https/tls.md) for that router. + +!!! example "TLS on websecure entryPoint becomes TLS option on Router-1" + + !!! info "v1" + + ```toml tab="File (TOML)" + # static configuration + [entryPoints] + [entryPoints.websecure] + address = ":443" + + [entryPoints.websecure.tls] + minVersion = "VersionTLS12" + cipherSuites = [ + "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", + "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", + "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", + "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", + "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", + "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", + ] + [[entryPoints.websecure.tls.certificates]] + certFile = "path/to/my.cert" + keyFile = "path/to/my.key" + ``` + + ```bash tab="CLI" + --entryPoints='Name:websecure Address::443 TLS:path/to/my.cert,path/to/my.key TLS.MinVersion:VersionTLS12 TLS.CipherSuites:TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256' + ``` + + !!! info "v2" + + ```yaml tab="File (YAML)" + http: + routers: + Router-1: + rule: "Host(`example.com`)" + service: service-id + # will terminate the TLS request + tls: + options: myTLSOptions + + tls: + certificates: + - certFile: /path/to/domain.cert + keyFile: /path/to/domain.key + options: + myTLSOptions: + minVersion: VersionTLS12 + cipherSuites: + - TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 + - TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 + - TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 + - TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 + - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 + ``` + + ```toml tab="File (TOML)" + # dynamic configuration + [http.routers] + [http.routers.Router-1] + rule = "Host(`example.com`)" + service = "service-id" + # will terminate the TLS request + [http.routers.Router-1.tls] + options = "myTLSOptions" + + [[tls.certificates]] + certFile = "/path/to/domain.cert" + keyFile = "/path/to/domain.key" + + [tls.options] + [tls.options.myTLSOptions] + minVersion = "VersionTLS12" + cipherSuites = [ + "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", + "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", + "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", + "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", + "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", + "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", + ] + ``` + + ```yaml tab="IngressRoute" + # The definitions below require the definitions for the TLSOption and IngressRoute kinds. + # https://doc.traefik.io/traefik/reference/dynamic-configuration/kubernetes-crd/#definitions + apiVersion: traefik.io/v1alpha1 + kind: TLSOption + metadata: + name: mytlsoption + namespace: default + + spec: + minVersion: VersionTLS12 + cipherSuites: + - TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 + - TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 + - TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 + - TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 + - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 + + --- + apiVersion: traefik.io/v1alpha1 + kind: IngressRoute + metadata: + name: ingressroutebar + + spec: + entryPoints: + - web + routes: + - match: Host(`example.com`) + kind: Rule + services: + - name: whoami + port: 80 + tls: + options: + name: mytlsoption + namespace: default + ``` + + ```yaml tab="Docker & Swarm" + labels: + # myTLSOptions must be defined by another provider, in this instance in the File Provider. + # see the cross provider section + - "traefik.http.routers.router0.tls.options=myTLSOptions@file" + ``` + +## HTTP to HTTPS Redirection is Now Configured on Routers + +Previously on Traefik v1, the redirection was applied on an entry point or on a frontend. +With Traefik v2 it is applied on an entry point or a [Router](../routing/routers/index.md). + +To apply a redirection: + +- on an entry point, the [HTTP redirection](../routing/entrypoints.md#redirection) has to be configured. +- on a router, one of the redirect middlewares, [RedirectRegex](../middlewares/http/redirectregex.md) or [RedirectScheme](../middlewares/http/redirectscheme.md), has to be configured and added to the router middlewares list. + +!!! example "Global HTTP to HTTPS redirection" + + !!! info "v1" + + ```toml tab="File (TOML)" + # static configuration + defaultEntryPoints = ["web", "websecure"] + + [entryPoints] + [entryPoints.web] + address = ":80" + [entryPoints.web.redirect] + entryPoint = "websecure" + + [entryPoints.websecure] + address = ":443" + [entryPoints.websecure.tls] + ``` + + ```bash tab="CLI" + --entryPoints=Name:web Address::80 Redirect.EntryPoint:websecure + --entryPoints='Name:websecure Address::443 TLS' + ``` + + !!! info "v2" + + ```yaml tab="File (YAML)" + # traefik.yml + ## static configuration + + entryPoints: + web: + address: ":80" + http: + redirections: + entrypoint: + to: websecure + scheme: https + + websecure: + address: ":443" + ``` + + ```toml tab="File (TOML)" + # traefik.toml + ## static configuration + + [entryPoints.web] + address = ":80" + [entryPoints.web.http.redirections.entryPoint] + to = "websecure" + scheme = "https" + + [entryPoints.websecure] + address = ":443" + ``` + + ```bash tab="CLI" + ## static configuration + + --entryPoints.web.address=:80 + --entryPoints.web.http.redirections.entrypoint.to=websecure + --entryPoints.web.http.redirections.entrypoint.scheme=https + --entryPoints.websecure.address=:443 + --providers.docker=true + ``` + +!!! example "HTTP to HTTPS redirection per domain" + + !!! info "v1" + + ```toml tab="File (TOML)" + [entryPoints] + [entryPoints.web] + address = ":80" + + [entryPoints.websecure] + address = ":443" + [entryPoints.websecure.tls] + + [file] + + [frontends] + [frontends.frontend1] + entryPoints = ["web", "websecure"] + [frontends.frontend1.routes] + [frontends.frontend1.routes.route0] + rule = "Host:example.net" + [frontends.frontend1.redirect] + entryPoint = "websecure" + ``` + + !!! info "v2" + + ```yaml tab="Docker & Swarm" + labels: + traefik.http.routers.app.rule: Host(`example.net`) + traefik.http.routers.app.entrypoints: web + traefik.http.routers.app.middlewares: https_redirect + + traefik.http.routers.appsecured.rule: Host(`example.net`) + traefik.http.routers.appsecured.entrypoints: websecure + traefik.http.routers.appsecured.tls: true + + traefik.http.middlewares.https_redirect.redirectscheme.scheme: https + traefik.http.middlewares.https_redirect.redirectscheme.permanent: true + ``` + + ```yaml tab="IngressRoute" + apiVersion: traefik.io/v1alpha1 + kind: IngressRoute + metadata: + name: http-redirect-ingressroute + + spec: + entryPoints: + - web + routes: + - match: Host(`example.net`) + kind: Rule + services: + - name: whoami + port: 80 + middlewares: + - name: https-redirect + + --- + apiVersion: traefik.io/v1alpha1 + kind: IngressRoute + metadata: + name: https-ingressroute + + spec: + entryPoints: + - websecure + routes: + - match: Host(`foo`) + kind: Rule + services: + - name: whoami + port: 80 + tls: {} + + --- + apiVersion: traefik.io/v1alpha1 + kind: Middleware + metadata: + name: https-redirect + spec: + redirectScheme: + scheme: https + permanent: true + ``` + + ```yaml tab="File (YAML)" + ## dynamic configuration + # dynamic-conf.yml + + http: + routers: + router0: + rule: "Host(`example.net`)" + entryPoints: + - web + middlewares: + - https_redirect + service: my-service + + router1: + rule: "Host(`example.net`)" + entryPoints: + - websecure + service: my-service + tls: {} + + middlewares: + https-redirect: + redirectScheme: + scheme: https + permanent: true + ``` + + ```toml tab="File (TOML)" + ## dynamic configuration + # dynamic-conf.toml + + [http.routers] + [http.routers.router0] + rule = "Host(`example.net`)" + service = "my-service" + entrypoints = ["web"] + middlewares = ["https_redirect"] + + [http.routers.router1] + rule = "Host(`example.net`)" + service = "my-service" + entrypoints = ["websecure"] + [http.routers.router1.tls] + + [http.middlewares] + [http.middlewares.https_redirect.redirectScheme] + scheme = "https" + permanent = true + ``` + +## Strip and Rewrite Path Prefixes + +With the new core notions of v2 (introduced earlier in the section +["Frontends and Backends Are Dead, Long Live Routers, Middlewares, and Services"](#frontends-and-backends-are-dead-long-live-routers-middlewares-and-services)), +transforming the URL path prefix of incoming requests is configured with [middlewares](../middlewares/overview.md), +after the routing step with [router rule `PathPrefix`](../routing/routers/index.md#rule). + +Use Case: Incoming requests to `http://example.org/admin` are forwarded to the webapplication "admin", +with the path `/admin` stripped, e.g. to `http://:/`. In this case, you must: + +- First, configure a router named `admin` with a rule matching at least the path prefix with the `PathPrefix` keyword, +- Then, define a middleware of type [`stripprefix`](../middlewares/http/stripprefix.md), which removes the prefix `/admin`, associated to the router `admin`. + +!!! example "Strip Path Prefix When Forwarding to Backend" + + !!! info "v1" + + ```yaml tab="Docker & Swarm" + labels: + - "traefik.frontend.rule=Host:example.org;PathPrefixStrip:/admin" + ``` + + ```yaml tab="Ingress" + apiVersion: networking.k8s.io/v1beta1 + kind: Ingress + metadata: + name: traefik + annotations: + kubernetes.io/ingress.class: traefik + traefik.ingress.kubernetes.io/rule-type: PathPrefixStrip + spec: + rules: + - host: example.org + http: + paths: + - path: /admin + backend: + serviceName: admin-svc + servicePort: admin + ``` + + ```toml tab="File (TOML)" + [frontends.admin] + [frontends.admin.routes.admin_1] + rule = "Host:example.org;PathPrefixStrip:/admin" + ``` + + !!! info "v2" + + ```yaml tab="Docker & Swarm" + labels: + - "traefik.http.routers.admin.rule=Host(`example.org`) && PathPrefix(`/admin`)" + - "traefik.http.routers.admin.middlewares=admin-stripprefix" + - "traefik.http.middlewares.admin-stripprefix.stripprefix.prefixes=/admin" + ``` + + ```yaml tab="IngressRoute" + --- + apiVersion: traefik.io/v1alpha1 + kind: IngressRoute + metadata: + name: http-redirect-ingressroute + namespace: admin-web + spec: + entryPoints: + - web + routes: + - match: Host(`example.org`) && PathPrefix(`/admin`) + kind: Rule + services: + - name: admin-svc + port: admin + middlewares: + - name: admin-stripprefix + --- + apiVersion: traefik.io/v1alpha1 + kind: Middleware + metadata: + name: admin-stripprefix + spec: + stripPrefix: + prefixes: + - /admin + ``` + + ```yaml tab="File (YAML)" + ## Dynamic Configuration + # dynamic-conf.yml + + # As YAML Configuration File + http: + routers: + admin: + service: admin-svc + middlewares: + - "admin-stripprefix" + rule: "Host(`example.org`) && PathPrefix(`/admin`)" + + middlewares: + admin-stripprefix: + stripPrefix: + prefixes: + - "/admin" + + # ... + ``` + + ```toml tab="File (TOML)" + ## Dynamic configuration + # dynamic-conf.toml + + [http.routers.router1] + rule = "Host(`example.org`) && PathPrefix(`/admin`)" + service = "admin-svc" + entrypoints = ["web"] + middlewares = ["admin-stripprefix"] + + [http.middlewares] + [http.middlewares.admin-stripprefix.stripPrefix] + prefixes = ["/admin"] + + # ... + ``` + +??? question "What About Other Path Transformations?" + + Instead of removing the path prefix with the [`stripprefix` middleware](../../middlewares/http/stripprefix/), you can also: + + - Add a path prefix with the [`addprefix` middleware](../../middlewares/http/addprefix/) + - Replace the complete path of the request with the [`replacepath` middleware](../../middlewares/http/replacepath/) + - ReplaceRewrite path using Regexp with the [`replacepathregex` middleware](../../middlewares/http/replacepathregex/) + - And a lot more on the [`HTTP middlewares` page](../../middlewares/http/overview/) + +## ACME (LetsEncrypt) + +[ACME](../https/acme.md) is now a certificate resolver (under a certificatesResolvers section) but remains in the static configuration. + +!!! example "ACME from provider to a specific Certificate Resolver" + + !!! info "v1" + + ```toml tab="File (TOML)" + # static configuration + defaultEntryPoints = ["websecure","web"] + + [entryPoints.web] + address = ":80" + [entryPoints.web.redirect] + entryPoint = "webs" + [entryPoints.websecure] + address = ":443" + [entryPoints.websecure.tls] + + [acme] + email = "your-email-here@example.com" + storage = "acme.json" + entryPoint = "websecure" + onHostRule = true + [acme.tlsChallenge] + ``` + + ```bash tab="CLI" + --defaultentrypoints=websecure,web + --entryPoints=Name:web Address::80 Redirect.EntryPoint:websecure + --entryPoints=Name:websecure Address::443 TLS + --acme.email=your-email-here@example.com + --acme.storage=acme.json + --acme.entryPoint=websecure + --acme.onHostRule=true + --acme.tlschallenge=true + ``` + + !!! info "v2" + + ```yaml tab="File (YAML)" + entryPoints: + web: + address: ":80" + + websecure: + address: ":443" + http: + tls: + certResolver: myresolver + + certificatesResolvers: + myresolver: + acme: + email: your-email@example.com + storage: acme.json + tlsChallenge: {} + ``` + + ```toml tab="File (TOML)" + # static configuration + [entryPoints] + [entryPoints.web] + address = ":80" + + [entryPoints.websecure] + address = ":443" + [entryPoints.websecure.http.tls] + certResolver = "myresolver" + + [certificatesResolvers.myresolver.acme] + email = "your-email@example.com" + storage = "acme.json" + [certificatesResolvers.myresolver.acme.tlsChallenge] + ``` + + ```bash tab="CLI" + --entryPoints.web.address=:80 + --entryPoints.websecure.address=:443 + --certificatesresolvers.myresolver.acme.email=your-email@example.com + --certificatesresolvers.myresolver.acme.storage=acme.json + --certificatesresolvers.myresolver.acme.tlschallenge=true + ``` + +## Traefik Logs + +In the v2, all the [log configuration](../observability/logs.md) remains in the static part but are unified under a `log` section. +There is no more log configuration at the root level. + +!!! example "Simple log configuration" + + !!! info "v1" + + ```toml tab="File (TOML)" + # static configuration + logLevel = "DEBUG" + + [traefikLog] + filePath = "/path/to/traefik.log" + format = "json" + ``` + + ```bash tab="CLI" + --logLevel=DEBUG + --traefikLog.filePath=/path/to/traefik.log + --traefikLog.format=json + ``` + + !!! info "v2" + + ```yaml tab="File (YAML)" + # static configuration + log: + level: DEBUG + filePath: /path/to/log-file.log + format: json + ``` + + ```toml tab="File (TOML)" + # static configuration + [log] + level = "DEBUG" + filePath = "/path/to/log-file.log" + format = "json" + ``` + + ```bash tab="CLI" + --log.level=DEBUG + --log.filePath=/path/to/traefik.log + --log.format=json + ``` + +## Access Logs + +Access Logs are configured in the same way as before. + +But all request headers are now filtered out by default in Traefik v2. +So during migration, you might want to consider enabling some needed fields (see [access log configuration](../observability/access-logs.md)). + +## Tracing + +Traefik v2 retains OpenTracing support. The `backend` root option from the v1 is gone, you just have to set your [tracing configuration](../observability/tracing/overview.md). + +!!! example "Simple Jaeger tracing configuration" + + !!! info "v1" + + ```toml tab="File (TOML)" + # static configuration + [tracing] + backend = "jaeger" + servicename = "tracing" + [tracing.jaeger] + samplingParam = 1.0 + samplingServerURL = "http://12.0.0.1:5778/sampling" + samplingType = "const" + localAgentHostPort = "12.0.0.1:6831" + ``` + + ```bash tab="CLI" + --tracing.backend=jaeger + --tracing.servicename=tracing + --tracing.jaeger.localagenthostport=12.0.0.1:6831 + --tracing.jaeger.samplingparam=1.0 + --tracing.jaeger.samplingserverurl=http://12.0.0.1:5778/sampling + --tracing.jaeger.samplingtype=const + ``` + + !!! info "v2" + + ```yaml tab="File (YAML)" + # static configuration + tracing: + servicename: tracing + jaeger: + samplingParam: 1 + samplingServerURL: 'http://12.0.0.1:5778/sampling' + samplingType: const + localAgentHostPort: '12.0.0.1:6831' + ``` + + ```toml tab="File (TOML)" + # static configuration + [tracing] + servicename = "tracing" + [tracing.jaeger] + samplingParam = 1.0 + samplingServerURL = "http://12.0.0.1:5778/sampling" + samplingType = "const" + localAgentHostPort = "12.0.0.1:6831" + ``` + + ```bash tab="CLI" + --tracing.servicename=tracing + --tracing.jaeger.localagenthostport=12.0.0.1:6831 + --tracing.jaeger.samplingparam=1.0 + --tracing.jaeger.samplingserverurl=http://12.0.0.1:5778/sampling + --tracing.jaeger.samplingtype=const + ``` + +## Metrics + +The v2 retains metrics tools and allows metrics to be configured for the entrypoints and/or services. +For a basic configuration, the [metrics configuration](../observability/metrics/overview.md) remains the same. + +!!! example "Simple Prometheus metrics configuration" + + !!! info "v1" + + ```toml tab="File (TOML)" + # static configuration + [metrics.prometheus] + buckets = [0.1,0.3,1.2,5.0] + entryPoint = "traefik" + ``` + + ```bash tab="CLI" + --metrics.prometheus.buckets=[0.1,0.3,1.2,5.0] + --metrics.prometheus.entrypoint=traefik + ``` + + !!! info "v2" + + ```yaml tab="File (YAML)" + # static configuration + metrics: + prometheus: + buckets: + - 0.1 + - 0.3 + - 1.2 + - 5 + entryPoint: metrics + ``` + + ```toml tab="File (TOML)" + # static configuration + [metrics.prometheus] + buckets = [0.1,0.3,1.2,5.0] + entryPoint = "metrics" + ``` + + ```bash tab="CLI" + --metrics.prometheus.buckets=[0.1,0.3,1.2,5.0] + --metrics.prometheus.entrypoint=metrics + ``` + +## No More Root Level Key/Values + +To avoid any source of confusion, there are no more configuration at the root level. +Each root item has been moved to a related section or removed. + +!!! example "From root to dedicated section" + + !!! info "v1" + + ```toml tab="File (TOML)" + # static configuration + checkNewVersion = false + sendAnonymousUsage = true + logLevel = "DEBUG" + insecureSkipVerify = true + rootCAs = [ "/mycert.cert" ] + maxIdleConnsPerHost = 200 + providersThrottleDuration = "2s" + AllowMinWeightZero = true + debug = true + defaultEntryPoints = ["web", "websecure"] + keepTrailingSlash = false + ``` + + ```bash tab="CLI" + --checknewversion=false + --sendanonymoususage=true + --loglevel=DEBUG + --insecureskipverify=true + --rootcas=/mycert.cert + --maxidleconnsperhost=200 + --providersthrottleduration=2s + --allowminweightzero=true + --debug=true + --defaultentrypoints=web,websecure + --keeptrailingslash=true + ``` + + !!! info "v2" + + ```yaml tab="File (YAML)" + # static configuration + global: + checkNewVersion: true + sendAnonymousUsage: true + + log: + level: DEBUG + + serversTransport: + insecureSkipVerify: true + rootCAs: + - /mycert.cert + maxIdleConnsPerHost: 42 + + providers: + providersThrottleDuration: 42 + ``` + + ```toml tab="File (TOML)" + # static configuration + [global] + checkNewVersion = true + sendAnonymousUsage = true + + [log] + level = "DEBUG" + + [serversTransport] + insecureSkipVerify = true + rootCAs = [ "/mycert.cert" ] + maxIdleConnsPerHost = 42 + + [providers] + providersThrottleDuration = 42 + ``` + + ```bash tab="CLI" + --global.checknewversion=true + --global.sendanonymoususage=true + --log.level=DEBUG + --serverstransport.insecureskipverify=true + --serverstransport.rootcas=/mycert.cert + --serverstransport.maxidleconnsperhost=42 + --providers.providersthrottleduration=42 + ``` + +## Dashboard + +You need to activate the API to access the [dashboard](../operations/dashboard.md). + +To activate the dashboard, you can either: + +- use the [secure mode](../operations/dashboard.md#secure-mode) with the `api@internal` service like in the following examples +- or use the [insecure mode](../operations/api.md#insecure) + +!!! example "Activate and access the dashboard" + + !!! info "v1" + + ```toml tab="File (TOML)" + ## static configuration + # traefik.toml + + [entryPoints.websecure] + address = ":443" + [entryPoints.websecure.tls] + [entryPoints.websecure.auth] + [entryPoints.websecure.auth.basic] + users = [ + "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/" + ] + + [api] + entryPoint = "websecure" + ``` + + ```bash tab="CLI" + --entryPoints='Name:websecure Address::443 TLS Auth.Basic.Users:test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/' + --api + ``` + + !!! info "v2" + + ```yaml tab="Docker & Swarm" + # dynamic configuration + labels: + - "traefik.http.routers.api.rule=Host(`traefik.docker.localhost`)" + - "traefik.http.routers.api.entrypoints=websecure" + - "traefik.http.routers.api.service=api@internal" + - "traefik.http.routers.api.middlewares=myAuth" + - "traefik.http.routers.api.tls" + - "traefik.http.middlewares.myAuth.basicauth.users=test:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/" + ``` + + ```yaml tab="File (YAML)" + ## static configuration + # traefik.yml + + entryPoints: + websecure: + address: ':443' + + api: {} + + providers: + file: + directory: /path/to/dynamic/config + + ##---------------------## + + ## dynamic configuration + # /path/to/dynamic/config/dynamic-conf.yml + + http: + routers: + api: + rule: Host(`traefik.docker.localhost`) + entryPoints: + - websecure + service: api@internal + middlewares: + - myAuth + tls: {} + + middlewares: + myAuth: + basicAuth: + users: + - 'test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/' + ``` + + ```toml tab="File (TOML)" + ## static configuration + # traefik.toml + + [entryPoints.websecure] + address = ":443" + + [api] + + [providers.file] + directory = "/path/to/dynamic/config" + + ##---------------------## + + ## dynamic configuration + # /path/to/dynamic/config/dynamic-conf.toml + + [http.routers.api] + rule = "Host(`traefik.docker.localhost`)" + entrypoints = ["websecure"] + service = "api@internal" + middlewares = ["myAuth"] + [http.routers.api.tls] + + [http.middlewares.myAuth.basicAuth] + users = [ + "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/" + ] + ``` + +## Providers + +Supported [providers](../providers/overview.md), for now: + +- [ ] Azure Service Fabric +- [x] Consul +- [x] Consul Catalog +- [x] Docker +- [ ] DynamoDB +- [ ] ECS +- [x] Etcd +- [ ] Eureka +- [x] File +- [x] Kubernetes Ingress +- [x] Kubernetes IngressRoute +- [x] Marathon +- [ ] Mesos +- [x] Rancher +- [x] Redis +- [x] Rest +- [x] Zookeeper + +## Some Tips You Should Know + +- Different sources of static configuration (file, CLI flags, ...) cannot be [mixed](../getting-started/configuration-overview.md#the-static-configuration). +- Now, configuration elements can be referenced between different providers by using the provider namespace notation: `@`. + For instance, a router named `myrouter` in a File Provider can refer to a service named `myservice` defined in Docker Provider with the following notation: `myservice@docker`. +- Middlewares are applied in the same order as their declaration in router. +- If you have any questions feel free to join our [community forum](https://community.traefik.io). diff --git a/docs/content/migrate/v2-to-v3-details.md b/docs/content/migration/v2-to-v3-details.md similarity index 99% rename from docs/content/migrate/v2-to-v3-details.md rename to docs/content/migration/v2-to-v3-details.md index 257ee0f5f..abd8ae587 100644 --- a/docs/content/migrate/v2-to-v3-details.md +++ b/docs/content/migration/v2-to-v3-details.md @@ -5,7 +5,7 @@ description: "Configuration changes and their details to successfully migrate fr # Configuration Details for Migrating from Traefik v2 to v3 -## Install Configuration Changes +## Static Configuration Changes ### SwarmMode @@ -135,7 +135,7 @@ It is now unsupported and would prevent Traefik to start. ##### Remediation The `http3` option should be removed from the static configuration experimental section. -To configure `http3`, please checkout the [entrypoint configuration documentation](../reference/install-configuration/entrypoints.md#http3). +To configure `http3`, please checkout the [entrypoint configuration documentation](../routing/entrypoints.md#http3_1). ### Consul provider @@ -619,7 +619,7 @@ Please take a look at the observability documentation for more information: In v3, the `ServiceURL` field is not an object anymore but a string representation. An update may be required if you index access logs. -## Routing Configuration Changes +## Dynamic Configuration Changes ### Router Rule Matchers @@ -730,7 +730,7 @@ In v3, we renamed the `IPWhiteList` middleware to `IPAllowList` without changing ### TCP LoadBalancer `terminationDelay` option -The TCP LoadBalancer `terminationDelay` option has been deprecated. +The TCP LoadBalancer `terminationDelay` option has been removed. This option can now be configured directly on the `TCPServersTransport` level, please take a look at this [documentation](../routing/services/index.md#terminationdelay) ### Kubernetes CRDs API Group `traefik.containo.us` diff --git a/docs/content/migration/v2-to-v3.md b/docs/content/migration/v2-to-v3.md new file mode 100644 index 000000000..2b88ce294 --- /dev/null +++ b/docs/content/migration/v2-to-v3.md @@ -0,0 +1,77 @@ +--- +title: "Traefik V3 Migration Documentation" +description: "Migrate from Traefik Proxy v2 to v3 and update all the necessary configurations to take advantage of all the improvements. Read the technical documentation." +--- + +# Migration Guide: From v2 to v3 + +How to Migrate from Traefik v2 to Traefik v3. +{: .subtitle } + +With Traefik v3, we are introducing a streamlined transition process from v2. Minimal breaking changes have been made to specific options in the [static configuration](./v2-to-v3-details.md#static-configuration-changes "Link to static configuration changes"), and we are ensuring backward compatibility with v2 syntax in the [dynamic configuration](./v2-to-v3-details.md#dynamic-configuration-changes "Link to dynamic configuration changes"). This will offer a gradual path for adopting the v3 syntax, allowing users to progressively migrate their Kubernetes ingress resources, Docker labels, etc., to the new format. + +Here are the steps to progressively migrate from Traefik v2 to v3: + +1. [Prepare configurations and test v3](#step-1-prepare-configurations-and-test-v3) +1. [Migrate production instances to Traefik v3](#step-2-migrate-production-instances-to-traefik-v3) +1. [Progressively migrate dynamic configuration](#step-3-progressively-migrate-dynamic-configuration) + +## Step 1: Prepare Configurations and Test v3 + +Check the changes in [static configurations](./v2-to-v3-details.md#static-configuration-changes "Link to static configuration changes") and [operations](./v2-to-v3-details.md#operations-changes "Link to operations changes") brought by Traefik v3. +Modify your configurations accordingly. + +Then, add the following snippet to the static configuration: + +```yaml +# static configuration +core: + defaultRuleSyntax: v2 +``` + +This snippet in the static configuration makes the [v2 format](../migration/v2-to-v3-details.md#configure-the-default-syntax-in-static-configuration "Link to configure default syntax in static config") the default rule matchers syntax. + +Start Traefik v3 with this new configuration to test it. + +If you don’t get any error logs while testing, you are good to go! +Otherwise, follow the remaining migration options highlighted in the logs. + +Once your Traefik test instances are starting and routing to your applications, proceed to the next step. + +## Step 2: Migrate Production Instances to Traefik v3 + +We strongly advise you to follow a progressive migration strategy ([Kubernetes rolling update mechanism](https://kubernetes.io/docs/tutorials/kubernetes-basics/update/update-intro/ "Link to the Kubernetes rolling update documentation"), for example) to migrate your production instances to v3. + +!!! Warning + Ensure you have a [real-time monitoring solution](https://traefik.io/blog/capture-traefik-metrics-for-apps-on-kubernetes-with-prometheus/ "Link to the blog on capturing Traefik metrics with Prometheus") for your ingress traffic to detect issues instantly. + +During the progressive migration, monitor your ingress traffic for any errors. Be prepared to rollback to a working state in case of any issues. + +If you encounter any issues, leverage debug and access logs provided by Traefik to understand what went wrong and how to fix it. + +Once every Traefik instance is updated, you will be on Traefik v3! + +## Step 3: Progressively Migrate Dynamic Configuration + +!!! info + This step can be done later in the process, as Traefik v3 is compatible with the v2 format for [dynamic configuration](./v2-to-v3-details.md#dynamic-configuration-changes "Link to dynamic configuration changes"). + Enable Traefik logs to get some help if any deprecated option is in use. + +Check the changes in [dynamic configuration](./v2-to-v3-details.md#dynamic-configuration-changes "Link to dynamic configuration changes"). + +Then, progressively [switch each router to the v3 syntax](./v2-to-v3-details.md#configure-the-syntax-per-router "Link to configuring the syntax per router"). + +Test and update each Ingress resource and ensure that ingress traffic is not impacted. + +Once a v3 Ingress resource migration is validated, deploy the resource and delete the v2 Ingress resource. +Repeat it until all Ingress resources are migrated. + +Now, remove the following snippet added to the static configuration in Step 1: + +```yaml +# static configuration +core: + defaultRuleSyntax: v2 +``` + +You are now fully migrated to Traefik v3 🎉 diff --git a/docs/content/migration/v2.md b/docs/content/migration/v2.md new file mode 100644 index 000000000..d0a879781 --- /dev/null +++ b/docs/content/migration/v2.md @@ -0,0 +1,708 @@ +--- +title: "Traefik Migration Documentation" +description: "Learn the steps needed to migrate to new Traefik Proxy v2 versions, i.e. v2.0 to v2.1 or v2.1 to v2.2. Read the technical documentation." +--- + +# Migration: Steps needed between the versions + +## v2.0 to v2.1 + +### Kubernetes CRD + +In v2.1, a new Kubernetes CRD called `TraefikService` was added. +While updating an installation to v2.1, +one should apply that CRD, and update the existing `ClusterRole` definition to allow Traefik to use that CRD. + +To add that CRD and enhance the permissions, the following definitions need to be applied to the cluster. + +```yaml tab="TraefikService" +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: traefikservices.traefik.containo.us + +spec: + group: traefik.containo.us + version: v1alpha1 + names: + kind: TraefikService + plural: traefikservices + singular: traefikservice + scope: Namespaced +``` + +```yaml tab="ClusterRole" +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1beta1 +metadata: + name: traefik-ingress-controller + +rules: + - apiGroups: + - "" + resources: + - services + - endpoints + - secrets + verbs: + - get + - list + - watch + - apiGroups: + - extensions + - networking.k8s.io + resources: + - ingresses + verbs: + - get + - list + - watch + - apiGroups: + - extensions + - networking.k8s.io + resources: + - ingresses/status + verbs: + - update + - apiGroups: + - traefik.io + - traefik.containo.us + resources: + - middlewares + - middlewaretcps + - ingressroutes + - traefikservices + - ingressroutetcps + - ingressrouteudps + - tlsoptions + - tlsstores + - serverstransports + - serverstransporttcps + verbs: + - get + - list + - watch +``` + +After having both resources applied, Traefik will work properly. + +## v2.1 to v2.2 + +### Headers middleware: accessControlAllowOrigin + +`accessControlAllowOrigin` is deprecated. +This field will be removed in future 2.x releases. +Please configure your allowed origins in `accessControlAllowOriginList` instead. + +### Kubernetes CRD + +In v2.2, new Kubernetes CRDs called `TLSStore` and `IngressRouteUDP` were added. +While updating an installation to v2.2, +one should apply that CRDs, and update the existing `ClusterRole` definition to allow Traefik to use that CRDs. + +To add that CRDs and enhance the permissions, the following definitions need to be applied to the cluster. + +```yaml tab="TLSStore" +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: tlsstores.traefik.containo.us + +spec: + group: traefik.containo.us + version: v1alpha1 + names: + kind: TLSStore + plural: tlsstores + singular: tlsstore + scope: Namespaced + +``` + +```yaml tab="IngressRouteUDP" +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: ingressrouteudps.traefik.containo.us + +spec: + group: traefik.containo.us + version: v1alpha1 + names: + kind: IngressRouteUDP + plural: ingressrouteudps + singular: ingressrouteudp + scope: Namespaced + +``` + +```yaml tab="ClusterRole" +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1beta1 +metadata: + name: traefik-ingress-controller + +rules: + - apiGroups: + - "" + resources: + - services + - endpoints + - secrets + verbs: + - get + - list + - watch + - apiGroups: + - extensions + - networking.k8s.io + resources: + - ingresses + verbs: + - get + - list + - watch + - apiGroups: + - extensions + - networking.k8s.io + resources: + - ingresses/status + verbs: + - update + - apiGroups: + - traefik.io + - traefik.containo.us + resources: + - middlewares + - middlewaretcps + - ingressroutes + - traefikservices + - ingressroutetcps + - ingressrouteudps + - tlsoptions + - tlsstores + - serverstransports + - serverstransporttcps + verbs: + - get + - list + - watch +``` + +After having both resources applied, Traefik will work properly. + +### Kubernetes Ingress + +To enable HTTPS, it is not sufficient anymore to only rely on a TLS section in the Ingress. + +#### Expose an Ingress on 80 and 443 + +Define the default TLS configuration on the HTTPS entry point. + +```yaml tab="Ingress" +kind: Ingress +apiVersion: networking.k8s.io/v1beta1 +metadata: + name: example + +spec: + tls: + - secretName: my-tls-secret + + rules: + - host: example.com + http: + paths: + - path: "/foo" + backend: + serviceName: example-com + servicePort: 80 +``` + +Entry points definition and enable Ingress provider: + +```yaml tab="File (YAML)" +# Static configuration + +entryPoints: + web: + address: :80 + websecure: + address: :443 + http: + tls: {} + +providers: + kubernetesIngress: {} +``` + +```toml tab="File (TOML)" +# Static configuration + +[entryPoints.web] + address = ":80" + +[entryPoints.websecure] + address = ":443" + [entryPoints.websecure.http] + [entryPoints.websecure.http.tls] + +[providers.kubernetesIngress] +``` + +```bash tab="CLI" +# Static configuration + +--entryPoints.web.address=:80 +--entryPoints.websecure.address=:443 +--entryPoints.websecure.http.tls=true +--providers.kubernetesIngress=true +``` + +#### Use TLS only on one Ingress + +Define the TLS restriction with annotations. + +```yaml tab="Ingress" +kind: Ingress +apiVersion: networking.k8s.io/v1beta1 +metadata: + name: example-tls + annotations: + traefik.ingress.kubernetes.io/router.entrypoints: websecure + traefik.ingress.kubernetes.io/router.tls: "true" + +spec: + tls: + - secretName: my-tls-secret + + rules: + - host: example.com + http: + paths: + - path: "" + backend: + serviceName: example-com + servicePort: 80 +``` + +Entry points definition and enable Ingress provider: + +```yaml tab="File (YAML)" +# Static configuration + +entryPoints: + web: + address: :80 + websecure: + address: :443 + +providers: + kubernetesIngress: {} +``` + +```toml tab="File (TOML)" +# Static configuration + +[entryPoints.web] + address = ":80" + +[entryPoints.websecure] + address = ":443" + +[providers.kubernetesIngress] +``` + +```bash tab="CLI" +# Static configuration + +--entryPoints.web.address=:80 +--entryPoints.websecure.address=:443 +--providers.kubernetesIngress=true +``` + +## v2.2.2 to v2.2.5 + +### InsecureSNI removal + +In `v2.2.2` we introduced a new flag (`insecureSNI`) which was available as a global option to disable domain fronting. +Since `v2.2.5` this global option has been removed, and you should not use it anymore. + +### HostSNI rule matcher removal + +In `v2.2.2` we introduced a new rule matcher (`HostSNI`) for HTTP routers which was allowing to match the Server Name Indication at the router level. +Since `v2.2.5` this rule has been removed for HTTP routers, and you should not use it anymore. + +## v2.2 to v2.3 + +### X.509 CommonName Deprecation + +The deprecated, legacy behavior of treating the CommonName field on X.509 certificates as a host name when no Subject Alternative Names are present, is now disabled by default. + +It means that if one is using https with your backend servers, and a certificate with only a CommonName, +Traefik will not try to match the server name indication with the CommonName anymore. + +It can be temporarily re-enabled by adding the value `x509ignoreCN=0` to the `GODEBUG` environment variable. + +More information: https://golang.org/doc/go1.15#commonname + +### File Provider + +The file parser has been changed, since v2.3 the unknown options/fields in a dynamic configuration file are treated as errors. + +### IngressClass + +In `v2.3`, the support of `IngressClass`, which is available since Kubernetes version `1.18`, has been introduced. +In order to be able to use this new resource the [Kubernetes RBAC](../reference/dynamic-configuration/kubernetes-crd.md#rbac) must be updated. + +## v2.3 to v2.4 + +### ServersTransport + +In `v2.4.0`, the support of `ServersTransport` has been introduced. +It is therefore necessary to update [RBAC](../reference/dynamic-configuration/kubernetes-crd.md#rbac) and [CRD](../reference/dynamic-configuration/kubernetes-crd.md) definitions. + +## v2.4.7 to v2.4.8 + +### Non-ASCII Domain Names + +In `v2.4.8`, we introduced a new check on domain names used in HTTP router rule `Host` and `HostRegexp` expressions, +and in TCP router rule `HostSNI` expression. +This check ensures that provided domain names don't contain non-ASCII characters. +If not, an error is raised, and the associated router will be shown as invalid in the dashboard. + +This new behavior is intended to show what was failing silently previously and to help troubleshooting configuration issues. +It doesn't change the support for non-ASCII domain names in routers rules, which is not part of the Traefik feature set so far. + +In order to use non-ASCII domain names in a router's rule, one should use the Punycode form of the domain name. +For more information, please read the [HTTP routers rule](../routing/routers/index.md#rule) part or [TCP router rules](../routing/routers/index.md#rule_1) part of the documentation. + +## v2.4.8 to v2.4.9 + +### Tracing Span + +In `v2.4.9`, we changed span error to log only server errors (>= 500). + +## v2.4.9 to v2.4.10 + +### K8S CrossNamespace + +In `v2.4.10`, the default value for `allowCrossNamespace` has been changed to `false`. + +### K8S ExternalName Service + +In `v2.4.10`, by default, it is no longer authorized to reference Kubernetes ExternalName services. +To allow it, the `allowExternalNameServices` option should be set to `true`. + +## v2.4 to v2.5 + +### Kubernetes CRD + +In `v2.5`, the [Traefik CRDs](../reference/dynamic-configuration/kubernetes-crd.md#definitions) have been updated to support the new API version `apiextensions.k8s.io/v1`. +As required by `apiextensions.k8s.io/v1`, we have included the OpenAPI validation schema. + +After deploying the new [Traefik CRDs](../reference/dynamic-configuration/kubernetes-crd.md#definitions), the resources will be validated only on creation or update. + +Please note that the unknown fields will not be pruned when migrating from `apiextensions.k8s.io/v1beta1` to `apiextensions.k8s.io/v1` CRDs. +For more details check out the official [documentation](https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/#specifying-a-structural-schema). + +### Kubernetes Ingress + +Traefik v2.5 moves forward for the Ingress provider to support Kubernetes v1.22. + +Traefik now supports only v1.14+ Kubernetes clusters, which means the support of `extensions/v1beta1` API Version ingresses has been dropped. + +The `extensions/v1beta1` API Version should now be replaced either by `networking.k8s.io/v1beta1` or by `networking.k8s.io/v1` (as of Kubernetes v1.19+). + +The support of the `networking.k8s.io/v1beta1` API Version will stop in Kubernetes v1.22. + +### Headers middleware: ssl redirect options + +`sslRedirect`, `sslTemporaryRedirect`, `sslHost` and `sslForceHost` are deprecated in Traefik v2.5. + +For simple HTTP to HTTPS redirection, you may use [EntryPoints redirections](../routing/entrypoints.md#redirection). + +For more advanced use cases, you can use either the [RedirectScheme middleware](../middlewares/http/redirectscheme.md) or the [RedirectRegex middleware](../middlewares/http/redirectregex.md). + +### Headers middleware: accessControlAllowOrigin + +`accessControlAllowOrigin` is no longer supported in Traefik v2.5. + +### X.509 CommonName Deprecation Bis + +Following up on the deprecation started [previously](#x509-commonname-deprecation), +as the `x509ignoreCN=0` value for the `GODEBUG` is [deprecated in Go 1.17](https://tip.golang.org/doc/go1.17#crypto/x509), +the legacy behavior related to the CommonName field cannot be enabled at all anymore. + +## v2.5.3 to v2.5.4 + +### Errors middleware + +In `v2.5.4`, when the errors service is configured with the [`PassHostHeader`](../routing/services/index.md#pass-host-header) option to `true` (default), +the forwarded Host header value is now set to the client request Host value and not `0.0.0.0`. +Check out the [Errors middleware](../middlewares/http/errorpages.md#service) documentation for more details. + +## v2.5 to v2.6 + +### HTTP/3 + +Traefik v2.6 introduces the `AdvertisedPort` option, +which allows advertising, in the `Alt-Svc` header, a UDP port different from the one on which Traefik is actually listening (the EntryPoint's port). +By doing so, it introduces a new configuration structure `http3`, which replaces the `enableHTTP3` option (which therefore doesn't exist anymore). +To enable HTTP/3 on an EntryPoint, please check out the [HTTP/3 configuration](../routing/entrypoints.md#http3) documentation. + +### Kubernetes Gateway API Provider + +In `v2.6`, the [Kubernetes Gateway API provider](../providers/kubernetes-gateway.md) now only supports the version [v1alpha2](https://gateway-api.sigs.k8s.io/v1alpha2/guides/) of the specification and +[route namespaces](https://gateway-api.sigs.k8s.io/v1alpha2/references/spec/#gateway.networking.k8s.io/v1beta1.RouteNamespaces) selectors, which requires Traefik to fetch and watch the cluster namespaces. +Therefore, the RBAC and CRD definitions must be updated. + +## v2.6.0 to v2.6.1 + +### Metrics + +In `v2.6.1`, the metrics system does not support any more custom HTTP method verbs to prevent potential metrics cardinality overhead. +In consequence, for metrics having the method label, +if the HTTP method verb of a request is not one defined in the set of common methods for [`HTTP/1.1`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods) +or the [`PRI`](https://datatracker.ietf.org/doc/html/rfc7540#section-11.6) verb (for `HTTP/2`), +the value for the method label becomes `EXTENSION_METHOD`, instead of the request's one. + +### Tracing + +In `v2.6.1`, the Datadog tags added to a span changed from `service.name` to `traefik.service.name` and from `router.name` to `traefik.router.name`. + +## v2.8 + +### TLS client authentication + +In `v2.8`, the `caOptional` option is deprecated as TLS client authentication is a server side option. +This option available in the ForwardAuth middleware, as well as in the HTTP, Consul, Etcd, Redis, ZooKeeper, Marathon, Consul Catalog, and Docker providers has no effect and must not be used anymore. + +### Consul Enterprise Namespaces + +In `v2.8`, the `namespace` option of Consul and Consul Catalog providers is deprecated, please use the `namespaces` options instead. + +### Traefik Pilot + +In `v2.8`, the `pilot.token` and `pilot.dashboard` options are deprecated. +Please check our Blog for migration instructions later this year. + +## v2.8.2 + +Since `v2.5.0`, the `PreferServerCipherSuites` is [deprecated and ignored](https://tip.golang.org/doc/go1.17#crypto/tls) by Go, +in `v2.8.2` the `preferServerCipherSuites` option is also deprecated and ignored in Traefik. + +In `v2.8.2`, Traefik now reject certificates signed with the SHA-1 hash function. ([details](https://tip.golang.org/doc/go1.18#sha1)) + +## v2.9 + +### Traefik Pilot + +In `v2.9`, Traefik Pilot support has been removed. + +## v2.10 + +### Nomad Namespace + +In `v2.10`, the `namespace` option of the Nomad provider is deprecated, please use the `namespaces` options instead. + +### Kubernetes CRDs + +In `v2.10`, the Kubernetes CRDs API Group `traefik.containo.us` is deprecated, and its support will end starting with Traefik v3. Please use the API Group `traefik.io` instead. + +As the Kubernetes CRD provider still works with both API Versions (`traefik.io/v1alpha1` and `traefik.containo.us/v1alpha1`), +it means that for the same kind, namespace and name, the provider will only keep the `traefik.io/v1alpha1` resource. + +In addition, the Kubernetes CRDs API Version `traefik.containo.us/v1alpha1` will not be supported in Traefik v3 itself. + +Please note that it is a requirement to update the CRDs and the RBAC in the cluster before upgrading Traefik. +To do so, please apply the required [CRDs](https://raw.githubusercontent.com/traefik/traefik/v2.10/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml) and [RBAC](https://raw.githubusercontent.com/traefik/traefik/v2.10/docs/content/reference/dynamic-configuration/kubernetes-crd-rbac.yml) manifests for v2.10: + +```bash +kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v2.10/docs/content/reference/dynamic-configuration/kubernetes-crd-rbac.yml +kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v2.10/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml +``` + +### Traefik Hub + +In `v2.10`, Traefik Hub configuration has been removed because Traefik Hub v2 doesn't require this configuration. + +## v2.11 + +### IPWhiteList (HTTP) + +In `v2.11`, the `IPWhiteList` middleware is deprecated, please use the [IPAllowList](../middlewares/http/ipallowlist.md) middleware instead. + +### IPWhiteList (TCP) + +In `v2.11`, the `IPWhiteList` middleware is deprecated, please use the [IPAllowList](../middlewares/tcp/ipallowlist.md) middleware instead. + +### TLS CipherSuites + +> By default, cipher suites without ECDHE support are no longer offered by either clients or servers during pre-TLS 1.3 handshakes. +> This change can be reverted with the `tlsrsakex=1 GODEBUG` setting. +> (https://go.dev/doc/go1.22#crypto/tls) + +The _RSA key exchange_ cipher suites are way less secure than the modern ECDHE cipher suites and exposes to potential vulnerabilities like [the Marvin Attack](https://people.redhat.com/~hkario/marvin). +Decision has been made to support ECDHE cipher suites only by default. + +The following ciphers have been removed from the default list: + +- `TLS_RSA_WITH_AES_128_CBC_SHA` +- `TLS_RSA_WITH_AES_256_CBC_SHA` +- `TLS_RSA_WITH_AES_128_GCM_SHA256` +- `TLS_RSA_WITH_AES_256_GCM_SHA384` + +To enable these ciphers, please set the option `CipherSuites` in your [TLS configuration](../https/tls.md#cipher-suites) or set the environment variable `GODEBUG=tlsrsakex=1`. + +### Minimum TLS Version + +> By default, the minimum version offered by `crypto/tls` servers is now TLS 1.2 if not specified with config.MinimumVersion, +> matching the behavior of crypto/tls clients. +> This change can be reverted with the `tls10server=1 GODEBUG` setting. +> (https://go.dev/doc/go1.22#crypto/tls) + +To enable TLS 1.0, please set the option `MinVersion` to `VersionTLS10` in your [TLS configuration](../https/tls.md#cipher-suites) or set the environment variable `GODEBUG=tls10server=1`. + +## v2.11.1 + +### Maximum Router Priority Value + +Before v2.11.1, the maximum user-defined router priority value is: + +- `MaxInt32` for 32-bit platforms, +- `MaxInt64` for 64-bit platforms. + +Please check out the [go documentation](https://pkg.go.dev/math#pkg-constants) for more information. + +In v2.11.1, Traefik reserves a range of priorities for its internal routers and now, +the maximum user-defined router priority value is: + +- `(MaxInt32 - 1000)` for 32-bit platforms, +- `(MaxInt64 - 1000)` for 64-bit platforms. + +### EntryPoint.Transport.RespondingTimeouts. + +Starting with `v2.11.1` the following timeout options are deprecated: + +- `.transport.respondingTimeouts.readTimeout` +- `.transport.respondingTimeouts.writeTimeout` +- `.transport.respondingTimeouts.idleTimeout` + +They have been replaced by: + +- `.transport.respondingTimeouts.http.readTimeout` +- `.transport.respondingTimeouts.http.writeTimeout` +- `.transport.respondingTimeouts.http.idleTimeout` + +### EntryPoint.Transport.RespondingTimeouts.TCP.LingeringTimeout + +Starting with `v2.11.1` a new `lingeringTimeout` entryPoints option has been introduced, with a default value of 2s. + +The lingering timeout defines the maximum duration between each TCP read operation on the connection. +As a layer 4 timeout, it applies during HTTP handling but respects the configured HTTP server `readTimeout`. + +This change avoids Traefik instances with the default configuration hanging while waiting for bytes to be read on the connection. + +We suggest to adapt this value accordingly to your situation. +The new default value is purposely narrowed and can close the connection too early. + +Increasing the `lingeringTimeout` value could be the solution notably if you are dealing with the following errors: + +- 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.2 + +### LingeringTimeout + +Starting with `v2.11.2` the `.transport.respondingTimeouts.tcp.lingeringTimeout` introduced in `v2.11.1` has been removed. + +### RespondingTimeouts.TCP and RespondingTimeouts.HTTP + +Starting with `v2.11.2` the `respondingTimeouts.tcp` and `respondingTimeouts.http` sections introduced in `v2.11.1` have been removed. +To configure the responding timeouts, please use the [`respondingTimeouts`](../routing/entrypoints.md#respondingtimeouts) section. + +### EntryPoint.Transport.RespondingTimeouts.ReadTimeout + +Starting with `v2.11.2` the entryPoints [`readTimeout`](../routing/entrypoints.md#respondingtimeouts) option default value changed to 60 seconds. + +For HTTP, this option defines the maximum duration for reading the entire request, including the body. +For TCP, this option defines the maximum duration for the first bytes to be read on the connection. + +The default value was previously set to zero, which means no timeout. + +This change has been done to avoid Traefik instances with the default configuration to be hanging forever while waiting for bytes to be read on the connection. + +Increasing the `readTimeout` value could be the solution notably if you are dealing with the following errors: + +- 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: `.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. + +## v2.11.24 + +### Request Path Sanitization + +Since `v2.11.24`, the incoming request path is now cleaned before being used to match the router rules and sent to the backends. +Any `/../`, `/./` or duplicate slash segments in the request path is interpreted and/or collapsed. + +If you want to disable this behavior, you can set the [`sanitizePath` option](../routing/entrypoints.md#sanitizepath) to `false` in the entryPoint HTTP configuration. +This can be useful when dealing with legacy clients that are not url-encoding data in the request path. +For example, as base64 uses the “/” character internally, +if it's not url encoded, +it can lead to unsafe routing when the `sanitizePath` option is set to `false`. + +!!! warning "Security" + + Setting the `sanitizePath` option to `false` is not safe. + Ensure every request is properly url encoded instead. + +## v2.11.25 + +### Request Path Normalization + +Since `v2.11.25`, the request path is now normalized by decoding unreserved characters in the request path, +and also uppercasing the percent-encoded characters. +This follows [RFC 3986 percent-encoding normalization](https://datatracker.ietf.org/doc/html/rfc3986#section-6.2.2.2), +and [RFC 3986 case normalization](https://datatracker.ietf.org/doc/html/rfc3986#section-6.2.2.1). + +The normalization happens before the request path is sanitized, +and cannot be disabled. +This notably helps with encoded dots characters (which are unreserved characters) to be sanitized properly. + +### Routing Path + +Since `v2.11.25`, the reserved characters [(as per RFC 3986)](https://datatracker.ietf.org/doc/html/rfc3986#section-2.2) are kept encoded in the request path when matching the router rules. +Those characters, when decoded, change the meaning of the request path for routing purposes, +and Traefik now keeps them encoded to avoid any ambiguity. + +### Request Path Matching Examples + +| Request Path | Router Rule | Traefik v2.11.24 | Traefik v2.11.25 | +|-------------------|------------------------|------------------|------------------| +| `/foo%2Fbar` | PathPrefix(`/foo/bar`) | Match | No match | +| `/foo/../bar` | PathPrefix(`/foo`) | No match | No match | +| `/foo/../bar` | PathPrefix(`/bar`) | Match | Match | +| `/foo/%2E%2E/bar` | PathPrefix(`/foo`) | Match | No match | +| `/foo/%2E%2E/bar` | PathPrefix(`/bar`) | No match | Match | diff --git a/docs/content/migration/v3.md b/docs/content/migration/v3.md new file mode 100644 index 000000000..6581b5f39 --- /dev/null +++ b/docs/content/migration/v3.md @@ -0,0 +1,321 @@ +--- +title: "Traefik Migration Documentation" +description: "Learn the steps needed to migrate to new Traefik Proxy v3 versions. Read the technical documentation." +--- + +# Migration: Steps needed between the versions + +## v3.0 to v3.1 + +### Kubernetes Provider RBACs + +Starting with v3.1, the Kubernetes Providers now use the [EndpointSlices API](https://kubernetes.io/docs/concepts/services-networking/endpoint-slices/) (Kubernetes >=v1.21) to discover service endpoint addresses. +It also brings NodePort load-balancing which requires Nodes resources lookup. + +Therefore, in the corresponding RBACs (see [KubernetesIngress](../routing/providers/kubernetes-ingress.md#configuration-example), [KubernetesCRD](../reference/dynamic-configuration/kubernetes-crd.md#rbac), and [KubernetesGateway](../reference/dynamic-configuration/kubernetes-gateway-rbac.yml) provider RBACs): + +- the `endpoints` right has to be removed and the following `endpointslices` right has to be added: + +```yaml + ... + - apiGroups: + - discovery.k8s.io + resources: + - endpointslices + verbs: + - list + - watch + ... +``` + +- the `nodes` right has to be added: + +```yaml + ... + - apiGroups: + - "" + resources: + - nodes + verbs: + - get + - list + - watch + ... +``` + +#### Gateway API: KubernetesGateway Provider + +In v3.1, the KubernetesGateway Provider is no longer an experimental feature. +It can be enabled without the associated `experimental.kubernetesgateway` option, which is now deprecated. + +??? example "An example of the experimental `kubernetesgateway` option" + + ```yaml tab="File (YAML)" + experimental: + kubernetesgateway: true + ``` + + ```toml tab="File (TOML)" + [experimental] + kubernetesgateway=true + ``` + + ```bash tab="CLI" + --experimental.kubernetesgateway=true + ``` + +##### Remediation + +The `kubernetesgateway` option should be removed from the experimental section of the static configuration. +To configure `kubernetesgateway`, please check out the [KubernetesGateway Provider documentation](../providers/kubernetes-gateway.md). + +## v3.1.0 to v3.1.1 + +### IngressClass Lookup + +The Kubernetes Ingress provider option `disableIngressClassLookup` has been deprecated in v3.1.1, and will be removed in the next major version. +Please use the `disableClusterScopeResources` option instead to avoid cluster scope resources discovery (IngressClass, Nodes). + +## v3.1 to v3.2 + +### Kubernetes CRD Provider + +Starting with v3.2, the CRDs has been updated on [TraefikService](../../routing/services#mirroring-service) (PR [#11032](https://github.com/traefik/traefik/pull/11032)), on [RateLimit](../../middlewares/http/ratelimit) & [InFlightReq](../../middlewares/http/inflightreq) middlewares (PR [#9747](https://github.com/traefik/traefik/pull/9747)) and on [Compress](../../middlewares/http/compress) middleware (PR [#10943](https://github.com/traefik/traefik/pull/10943)). + +This update adds only new optional fields. +CRDs can be updated with this command: + +```shell +kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.3/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml +``` + +### Kubernetes Gateway Provider Standard Channel + +Starting with v3.2, the Kubernetes Gateway Provider now supports [GRPCRoute](https://gateway-api.sigs.k8s.io/api-types/grpcroute/). + +Therefore, in the corresponding RBACs (see [KubernetesGateway](../reference/dynamic-configuration/kubernetes-gateway-rbac.yml) provider RBACs), +the `grcroutes` and `grpcroutes/status` rights have to be added. + +```yaml + ... + - apiGroups: + - gateway.networking.k8s.io + resources: + - grpcroutes + verbs: + - get + - list + - watch + - apiGroups: + - gateway.networking.k8s.io + resources: + - grpcroutes/status + verbs: + - update + ... +``` + +### Kubernetes Gateway Provider Experimental Channel + +!!! warning "Breaking changes" + + Because of a breaking change introduced in Kubernetes Gateway [v1.2.0-rc1](https://github.com/kubernetes-sigs/gateway-api/releases/tag/v1.2.0-rc1), + Traefik v3.3 only supports Kubernetes Gateway v1.2.x when experimental channel features are enabled. + +Starting with v3.2, the Kubernetes Gateway Provider now supports [BackendTLSPolicy](https://gateway-api.sigs.k8s.io/api-types/backendtlspolicy/). + +Therefore, in the corresponding RBACs (see [KubernetesGateway](../reference/dynamic-configuration/kubernetes-gateway-rbac.yml) provider RBACs), +the `backendtlspolicies` and `backendtlspolicies/status` rights have to be added. + +```yaml + ... + - apiGroups: + - "" + resources: + - configmaps + verbs: + - get + - list + - watch + - apiGroups: + - gateway.networking.k8s.io + resources: + - backendtlspolicies + verbs: + - get + - list + - watch + - apiGroups: + - gateway.networking.k8s.io + resources: + - backendtlspolicies/status + verbs: + - update + ... +``` + +## v3.2.1 + +### X-Forwarded-Prefix + +In `v3.2.1`, 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. + +## v3.2.2 + +### Swarm Provider + +In `v3.2.2`, the `traefik.docker.network` and `traefik.docker.lbswarm` labels have been deprecated, +please use the `traefik.swarm.network` and `traefik.swarm.lbswarm` labels instead. + +## v3.2 to v3.3 + +### ACME DNS Certificate Resolver + +In `v3.3`, the `acme.dnsChallenge.delaybeforecheck` and `acme.dnsChallenge.disablepropagationcheck` options of the ACME certificate resolver are deprecated, +please use respectively `acme.dnsChallenge.propagation.delayBeforeChecks` and `acme.dnsChallenge.propagation.disableChecks` options instead. + +### Tracing Global Attributes + +In `v3.3`, the `tracing.globalAttributes` option has been deprecated, please use the `tracing.resourceAttributes` option instead. +The `tracing.globalAttributes` option is misleading as its name does not reflect the operation of adding resource attributes to be sent to the collector, +and will be removed in the next major version. + +## v3.3.4 + +### OpenTelemetry Request Duration metric + +In `v3.3.4`, the OpenTelemetry Request Duration metric (named `traefik_(entrypoint|router|service)_request_duration_seconds`) unit has been changed from milliseconds to seconds. +To be consistent with the naming and other metrics providers, the metric now reports the duration in seconds. + +## v3.3.5 + +### Compress Middleware + +In `v3.3.5`, the compress middleware `encodings` option default value is now `gzip, br, zstd`. +This change helps the algorithm selection to favor the `gzip` algorithm over the other algorithms. + +It impacts requests that do not specify their preferred algorithm, +or has no order preference, in the `Accept-Encoding` header. + +## v3.3.6 + +### Request Path Sanitization + +Since `v3.3.6`, the incoming request path is now cleaned before being used to match the router rules and sent to the backends. +Any `/../`, `/./` or duplicate slash segments in the request path is interpreted and/or collapsed. + +If you want to disable this behavior, you can set the [`sanitizePath` option](../reference/install-configuration/entrypoints.md#sanitizepath) to `false` in the entryPoint HTTP configuration. +This can be useful when dealing with legacy clients that are not url-encoding data in the request path. +For example, as base64 uses the “/” character internally, +if it's not url encoded, +it can lead to unsafe routing when the `sanitizePath` option is set to `false`. + +!!! warning "Security" + + Setting the `sanitizePath` option to `false` is not safe. + Ensure every request is properly url encoded instead. + +## v3.3 to v3.4 + +### Kubernetes CRD Provider + +#### Load-Balancing + +In `v3.4`, the HTTP service definition has been updated. +The strategy field now supports two new values: `wrr` and `p2c` (please refer to the [HTTP Services Load Balancing documentation](../../routing/services/#load-balancing-strategy) for more details). + +CRDs can be updated with this command: + +```shell +kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.4/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml +``` + +Please note that the `RoundRobin` strategy value is now deprecated, but still supported and equivalent to `wrr`, and will be removed in the next major release. + +#### ServersTransport CA Certificate + +In `v3.4`, a new `rootCAs` option has been added to the `ServersTransport` and `ServersTransportTCP` CRDs. +It allows the configuration of CA certificates from both `ConfigMaps` and `Secrets`, +and replaces the `rootCAsSecrets` option, as shown below: + +CRDs can be updated with this command: + +```shell +kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.4/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml +``` + +RBACs need to be updated with this command: + +```shell +kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.4/docs/content/reference/dynamic-configuration/kubernetes-crd-rbac.yml +``` + +```yaml +--- +apiVersion: traefik.io/v1alpha1 +kind: ServersTransport +metadata: + name: foo + namespace: bar +spec: + rootCAs: + - configMap: ca-config-map + - secret: ca-secret + +--- +apiVersion: traefik.io/v1alpha1 +kind: ServersTransportTCP +metadata: + name: foo + namespace: bar +spec: + rootCAs: + - configMap: ca-config-map + - secret: ca-secret +``` + +The `rootCAsSecrets` option, which allows only `Secrets` references, +is still supported, but is now deprecated, +and will be removed in the next major release. + +### Rule Syntax + +In `v3.4.0`, the `core.defaultRuleSyntax` static configuration option and the `ruleSyntax` router option have been deprecated, +and will be removed in the next major version. + +This `core.defaultRuleSyntax` option was used to switch between the v2 and v3 syntax for the router's rules, +and to help with the migration from v2 to v3. + +The `ruleSyntax` router's option was used to override the default rule syntax for a specific router. + +In preparation for the next major release, please remove any use of these two options and use the v3 syntax for writing the router's rules. + +## v3.4.1 + +### Request Path Normalization + +Since `v3.4.1`, the request path is now normalized by decoding unreserved characters in the request path, +and also uppercasing the percent-encoded characters. +This follows [RFC 3986 percent-encoding normalization](https://datatracker.ietf.org/doc/html/rfc3986#section-6.2.2.2), +and [RFC 3986 case normalization](https://datatracker.ietf.org/doc/html/rfc3986#section-6.2.2.1). + +The normalization happens before the request path is sanitized, +and cannot be disabled. +This notably helps with encoded dots characters (which are unreserved characters) to be sanitized properly. + +### Routing Path + +Since `v3.4.1`, the reserved characters [(as per RFC 3986)](https://datatracker.ietf.org/doc/html/rfc3986#section-2.2) are kept encoded in the request path when matching the router rules. +Those characters, when decoded, change the meaning of the request path for routing purposes, +and Traefik now keeps them encoded to avoid any ambiguity. + +### Request Path Matching Examples + +| Request Path | Router Rule | Traefik v3.4.0 | Traefik v3.4.1 | +|-------------------|------------------------|----------------|----------------| +| `/foo%2Fbar` | PathPrefix(`/foo/bar`) | Match | No match | +| `/foo/../bar` | PathPrefix(`/foo`) | No match | No match | +| `/foo/../bar` | PathPrefix(`/bar`) | Match | Match | +| `/foo/%2E%2E/bar` | PathPrefix(`/foo`) | Match | No match | +| `/foo/%2E%2E/bar` | PathPrefix(`/bar`) | No match | Match | diff --git a/docs/content/observability/access-logs.md b/docs/content/observability/access-logs.md index 3091df45e..4a74b814b 100644 --- a/docs/content/observability/access-logs.md +++ b/docs/content/observability/access-logs.md @@ -69,43 +69,27 @@ accessLog: _Optional, Default="common"_ -By default, logs are written using the Traefik Common Log Format (CLF). -The available log formats are: +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. -- `common` - Traefik's extended CLF format (default) -- `genericCLF` - Generic CLF format compatible with standard log analyzers -- `json` - JSON format for structured logging +!!! info "Common Log Format" -If the given format is unsupported, the default (`common`) is used instead. - -!!! info "Traefik Common Log Format vs Generic CLF" - - **Traefik Common Log Format (`common`):** ```html - [] " " "" "" "" "" ms ``` - - **Generic CLF Format (`genericCLF`):** - ```html - - [] " " "" "" - ``` - - The `genericCLF` format omits Traefik-specific fields (request count, router name, service URL, and duration) for better compatibility with standard CLF parsers. ```yaml tab="File (YAML)" -# JSON format accessLog: format: "json" ``` ```toml tab="File (TOML)" -# JSON format [accessLog] format = "json" ``` ```bash tab="CLI" -# JSON format --accesslog.format=json ``` @@ -304,9 +288,11 @@ It is possible to configure the Traefik to timestamp in a specific timezone by e Example utilizing Docker Compose: ```yaml +version: "3.7" + services: traefik: - image: traefik:v3.5 + image: traefik:v3.4 environment: - TZ=US/Alaska command: @@ -356,54 +342,6 @@ accesslog: The OpenTelemetry Logger exporter will export access logs to the collector using HTTPS by default to https://localhost:4318/v1/logs, see the [gRPC Section](#grpc-configuration) to use gRPC. -### `serviceName` - -_Optional, Default="traefik"_ - -Defines the service name resource attribute. - -```yaml tab="File (YAML)" -accesslog: - otlp: - serviceName: name -``` - -```toml tab="File (TOML)" -[accesslog] - [accesslog.otlp] - serviceName = "name" -``` - -```bash tab="CLI" ---accesslog.otlp.serviceName=name -``` - -### `resourceAttributes` - -_Optional, Default=empty_ - -Defines additional resource attributes to be sent to the collector. - -```yaml tab="File (YAML)" -accesslog: - otlp: - resourceAttributes: - attr1: foo - attr2: bar -``` - -```toml tab="File (TOML)" -[accesslog] - [accesslog.otlp.resourceAttributes] - attr1 = "foo" - attr2 = "bar" -``` - -```bash tab="CLI" ---accesslog.otlp.resourceAttributes.attr1=foo ---accesslog.otlp.resourceAttributes.attr2=bar -``` - ### HTTP configuration _Optional_ diff --git a/docs/content/observability/logs.md b/docs/content/observability/logs.md index 8f82e9936..51be940b8 100644 --- a/docs/content/observability/logs.md +++ b/docs/content/observability/logs.md @@ -20,7 +20,6 @@ Traefik logs concern everything that happens to Traefik itself (startup, configu By default, the logs are written to the standard output. You can configure a file path instead using the `filePath` option. -When `filePath` is specified, Traefik will write logs only to that file (not to standard output). ```yaml tab="File (YAML)" # Writing Logs to a File @@ -220,54 +219,6 @@ log: The OpenTelemetry Logger exporter will export logs to the collector using HTTPS by default to https://localhost:4318/v1/logs, see the [gRPC Section](#grpc-configuration) to use gRPC. -### `serviceName` - -_Optional, Default="traefik"_ - -Defines the service name resource attribute. - -```yaml tab="File (YAML)" -log: - otlp: - serviceName: name -``` - -```toml tab="File (TOML)" -[log] - [log.otlp] - serviceName = "name" -``` - -```bash tab="CLI" ---log.otlp.serviceName=name -``` - -### `resourceAttributes` - -_Optional, Default=empty_ - -Defines additional resource attributes to be sent to the collector. - -```yaml tab="File (YAML)" -log: - otlp: - resourceAttributes: - attr1: foo - attr2: bar -``` - -```toml tab="File (TOML)" -[log] - [log.otlp.resourceAttributes] - attr1 = "foo" - attr2 = "bar" -``` - -```bash tab="CLI" ---log.otlp.resourceAttributes.attr1=foo ---log.otlp.resourceAttributes.attr2=bar -``` - ### HTTP configuration _Optional_ diff --git a/docs/content/observability/metrics/opentelemetry.md b/docs/content/observability/metrics/opentelemetry.md index 1967b1800..0bcb96587 100644 --- a/docs/content/observability/metrics/opentelemetry.md +++ b/docs/content/observability/metrics/opentelemetry.md @@ -143,7 +143,7 @@ metrics: _Optional, Default="traefik"_ -Defines the service name resource attribute. +OTEL service name to use. ```yaml tab="File (YAML)" metrics: @@ -160,31 +160,6 @@ metrics: ```bash tab="CLI" --metrics.otlp.serviceName=name ``` -#### `resourceAttributes` - -_Optional, Default=empty_ - -Defines additional resource attributes to be sent to the collector. - -```yaml tab="File (YAML)" -metrics: - otlp: - resourceAttributes: - attr1: foo - attr2: bar -``` - -```toml tab="File (TOML)" -[metrics] - [metrics.otlp.resourceAttributes] - attr1 = "foo" - attr2 = "bar" -``` - -```bash tab="CLI" ---metrics.otlp.resourceAttributes.attr1=foo ---metrics.otlp.resourceAttributes.attr2=bar -``` ### HTTP configuration diff --git a/docs/content/observability/tracing/opentelemetry.md b/docs/content/observability/tracing/opentelemetry.md index 23c7166ae..921ce8399 100644 --- a/docs/content/observability/tracing/opentelemetry.md +++ b/docs/content/observability/tracing/opentelemetry.md @@ -5,7 +5,7 @@ description: "Traefik supports several tracing backends, including OpenTelemetry # OpenTelemetry -Traefik Proxy follows [official OpenTelemetry semantic conventions v1.37.0](https://github.com/open-telemetry/semantic-conventions/blob/v1.37.0/docs/http/http-spans.md). +Traefik Proxy follows [official OpenTelemetry semantic conventions v1.26.0](https://github.com/open-telemetry/semantic-conventions/blob/v1.26.0/docs/http/http-spans.md). To enable the OpenTelemetry tracer: diff --git a/docs/content/observe/logs-and-access-logs.md b/docs/content/observe/logs-and-access-logs.md deleted file mode 100644 index 51feeb557..000000000 --- a/docs/content/observe/logs-and-access-logs.md +++ /dev/null @@ -1,180 +0,0 @@ ---- -title: "Logs and Access Logs" -description: "Logs and Access Logs in Traefik Proxy provide real-time insight into the health of your system. They enable swift error detection and intervention through alerts. By centralizing logs, you can streamline the debugging process during incident resolution." ---- - -## Logs - -Logs concern everything that happens to Traefik itself (startup, configuration, events, shutdown, and so on). - -### Configuration Example - -To enable and configure logs in Traefik Proxy, you can use the static configuration file or Helm values if you are using the [Helm chart](https://github.com/traefik/traefik-helm-chart). - -```yaml tab="Structured (YAML)" -log: - filePath: "/path/to/log-file.log" - format: json - level: INFO -``` - -```toml tab="Structured (TOML)" -[log] - filePath = "/path/to/log-file.log" - format = "json" - level = "INFO" -``` - -```yaml tab="Helm Chart Values" -logs: - general: - filePath: "/path/to/log-file.log" - format: json - level: INFO -``` - -## Access Logs - -Access logs concern everything that happens to the requests handled by Traefik. - -### Configuration Example - -To enable and configure access logs in Traefik Proxy, you can use the static configuration file or Helm values if you are using the [Helm chart](https://github.com/traefik/traefik-helm-chart). - -The following example enables access logs in JSON format, filters them to only include specific status codes, and customizes the fields that are kept or dropped. - -```yaml tab="Structured (YAML)" -accessLog: - format: json - filters: - statusCodes: - - "200" - - "400-404" - - "500-503" - fields: - names: - ClientUsername: drop - headers: - defaultMode: keep - names: - User-Agent: redact - Content-Type: keep -``` - -```toml tab="Structured (TOML)" -[accessLog] - format = "json" - [accessLog.filters] - statusCodes = ["200", "400-404", "500-503"] - [accessLog.fields] - [accessLog.fields.names] - ClientUsername = "drop" - [accessLog.fields.headers] - defaultMode = "keep" - [accessLog.fields.headers.names] - "User-Agent" = "redact" - "Content-Type" = "keep" -``` - -```yaml tab="Helm Chart Values" -# values.yaml -logs: - access: - enabled: true - format: json - filters: - statusCodes: - - "200" - - "400-404" - - "500-503" - fields: - names: - ClientUsername: drop - headers: - defaultMode: keep - names: - User-Agent: redact - Content-Type: keep -``` - -## Per-Router Access Logs - -You can enable or disable access logs for a specific router. This is useful for turning off logging for noisy routes while keeping it on globally. - -Here's an example of disabling access logs on a specific router: - -```yaml tab="Structured (YAML)" -http: - routers: - my-router: - rule: "Host(`example.com`)" - service: my-service - observability: - accessLogs: false -``` - -```toml tab="Structured (TOML)" -[http.routers.my-router.observability] - accessLogs = false -``` - -```yaml tab="Kubernetes" -# ingressroute.yaml -apiVersion: traefik.io/v1alpha1 -kind: IngressRoute -metadata: - name: my-router -spec: - routes: - - kind: Rule - match: Host(`example.com`) - services: - - name: my-service - port: 80 - observability: - accessLogs: false -``` - -```bash tab="Labels" -labels: - - "traefik.http.routers.my-router.observability.accesslogs=false" -``` - -```json tab="Tags" -{ - // ... - "Tags": [ - "traefik.http.routers.my-router.observability.accesslogs=false" - ] -} -``` - -When the `observability` options are not defined on a router, it inherits the behavior from the [entrypoint's observability configuration](./overview.md), or the global one. - -## Log Formats - -Traefik Proxy supports the following log formats: - -- `common` - Traefik's extended CLF format (default) -- `genericCLF` - Generic CLF format compatible with standard log analyzers -- `json` - JSON format for structured logging - -## Access Log Filters - -You can configure Traefik Proxy to only record access logs for requests that match certain criteria. This is useful for reducing the volume of logs and focusing on specific events. - -The available filters are: - -- **Status Codes:** Keep logs only for requests with specific HTTP status codes or ranges (e.g., `200`, `400-404`). -- **Retry Attempts:** Keep logs only when a request retry has occurred. -- **Minimum Duration:** Keep logs only for requests that take longer than a specified duration. - -## Log Fields Customization - -When using the `json` format, you can customize which fields are included in your access logs. - -- **Request Fields:** You can choose to `keep`, `drop`, or `redact` any of the standard request fields. A complete list of available fields like `ClientHost`, `RequestMethod`, and `Duration` can be found in the [reference documentation](../reference/install-configuration/observability/logs-and-accesslogs.md#json-format-fields). -- **Request Headers:** You can also specify which request headers should be included in the logs, and whether their values should be `kept`, `dropped`, or `redacted`. - -!!! info - For detailed configuration options, refer to the [reference documentation](../reference/install-configuration/observability/logs-and-accesslogs.md). diff --git a/docs/content/observe/metrics.md b/docs/content/observe/metrics.md deleted file mode 100644 index d78a502c9..000000000 --- a/docs/content/observe/metrics.md +++ /dev/null @@ -1,104 +0,0 @@ ---- -title: "Metrics" -description: "Metrics in Traefik Proxy offer a comprehensive view of your infrastructure's health. They allow you to monitor critical indicators like incoming traffic volume. Metrics graphs and visualizations are helpful during incident triage in understanding the causes and implementing proactive measures." ---- - -# Metrics - -Metrics in Traefik Proxy offer a comprehensive view of your infrastructure's health. They allow you to monitor critical indicators like incoming traffic volume. Metrics graphs and visualizations are helpful during incident triage in understanding the causes and implementing proactive measures. - -## Available Metrics Providers - -Traefik Proxy supports the following metrics providers: - -- OpenTelemetry -- Prometheus -- Datadog -- InfluxDB 2.X -- StatsD - -## Configuration - -To enable metrics in Traefik Proxy, you need to configure the metrics provider in your static configuration file or helm values if you are using the [Helm chart](https://github.com/traefik/traefik-helm-chart). The following example shows how to configure the OpenTelemetry provider to send metrics to a collector. - -```yaml tab="Structured (YAML)" -metrics: - otlp: - http: - endpoint: http://myotlpcollector:4318/v1/metrics -``` - -```toml tab="Structured (TOML)" -[metrics.otlp.http] - endpoint = "http://myotlpcollector:4318/v1/metrics" -``` - -```yaml tab="Helm Chart Values" -# values.yaml -metrics: - # Disable Prometheus (enabled by default) - prometheus: null - # Enable providing OTel metrics - otlp: - enabled: true - http: - enabled: true - endpoint: http://myotlpcollector:4318/v1/metrics -``` - -## Per-Router Metrics - -You can enable or disable metrics collection for a specific router. This can be useful for excluding certain routes from your metrics data. - -Here's an example of disabling metrics on a specific router: - -```yaml tab="Structured (YAML)" -http: - routers: - my-router: - rule: "Host(`example.com`)" - service: my-service - observability: - metrics: false -``` - -```toml tab="Structured (TOML)" -[http.routers.my-router.observability] - metrics = false -``` - -```yaml tab="Kubernetes" -# ingressroute.yaml -apiVersion: traefik.io/v1alpha1 -kind: IngressRoute -metadata: - name: my-router -spec: - routes: - - kind: Rule - match: Host(`example.com`) - services: - - name: my-service - port: 80 - observability: - metrics: false -``` - -```bash tab="Labels" -labels: - - "traefik.http.routers.my-router.observability.metrics=false" -``` - -```json tab="Tags" -{ - // ... - "Tags": [ - "traefik.http.routers.my-router.observability.metrics=false" - ] -} -``` - -When the `observability` options are not defined on a router, it inherits the behavior from the [entrypoint's observability configuration](./overview.md), or the global one. - -!!! info - For detailed configuration options, refer to the [reference documentation](../reference/install-configuration/observability/metrics.md). diff --git a/docs/content/observe/overview.md b/docs/content/observe/overview.md deleted file mode 100644 index e5e822221..000000000 --- a/docs/content/observe/overview.md +++ /dev/null @@ -1,80 +0,0 @@ ---- -title: "Observability Overview" -description: "Traefik Proxy provides comprehensive monitoring and observability capabilities to maintain reliability and efficiency." ---- - -# Observability Overview - -Traefik Proxy provides comprehensive monitoring and observability capabilities to maintain reliability and efficiency: - -- [Logs and Access Logs](./logs-and-access-logs.md) provide real-time insight into the health of your system. They enable swift error detection and intervention through alerts. By centralizing logs, you can streamline the debugging process during incident resolution. - -- [Metrics](./metrics.md) offer a comprehensive view of your infrastructure's health. They allow you to monitor critical indicators like incoming traffic volume. Metrics graphs and visualizations are helpful during incident triage in understanding the causes and implementing proactive measures. - -- [Tracing](./tracing.md) enables tracking the flow of operations within your system. Using traces and spans, you can identify performance bottlenecks and pinpoint applications causing slowdowns to optimize response times effectively. - -## Configuration Example - -You can enable access logs, metrics, and tracing globally: - -```yaml tab="Structured (YAML)" -accessLog: {} - -metrics: - otlp: {} - -tracing: {} -``` - -```toml tab="Structured (TOML)" -[accessLog] - -[metrics.otlp] - -[tracing.otlp] -``` - -```yaml tab="Helm Chart Values" -# values.yaml -accessLog: - enabled: true - -metrics: - otlp: - enabled: true - -tracing: - otlp: - enabled: true -``` - -You can disable access logs, metrics, and tracing for a specific [entrypoint](../reference/install-configuration/entrypoints.md): - -```yaml tab="Structured (YAML)" -entryPoints: - EntryPoint0: - address: ':8000/udp' - observability: - accessLogs: false - tracing: false - metrics: false -``` - -```toml tab="Structured (TOML)" -[entryPoints.EntryPoint0.observability] - accessLogs = false - tracing = false - metrics = false -``` - -```yaml tab="Helm Chart Values" -additionalArguments: - - "--entrypoints.entrypoint0.observability.accesslogs=false" - - "--entrypoints.entrypoint0.observability.tracing=false" - - "--entrypoints.entrypoint0.observability.metrics=false" -``` - -!!! note - A router with its own observability configuration will override the global default. - -{!traefik-for-business-applications.md!} diff --git a/docs/content/observe/tracing.md b/docs/content/observe/tracing.md deleted file mode 100644 index cd3e80739..000000000 --- a/docs/content/observe/tracing.md +++ /dev/null @@ -1,93 +0,0 @@ ---- -title: "Tracing" -description: "Tracing in Traefik Proxy allows you to track the flow of operations within your system. Using traces and spans, you can identify performance bottlenecks and pinpoint applications causing slowdowns to optimize response times effectively." ---- - -# Tracing - -Tracing in Traefik Proxy allows you to track the flow of operations within your system. Using traces and spans, you can identify performance bottlenecks and pinpoint applications causing slowdowns to optimize response times effectively. - -Traefik Proxy uses [OpenTelemetry](https://opentelemetry.io/) to export traces. OpenTelemetry is an open-source observability framework. You can send traces to an OpenTelemetry collector, which can then export them to a variety of backends like Jaeger, Zipkin, or Datadog. - -## Configuration - -To enable tracing in Traefik Proxy, you need to configure it in your static configuration file or Helm values if you are using the [Helm chart](https://github.com/traefik/traefik-helm-chart). The following example shows how to configure the OpenTelemetry provider to send traces to a collector via HTTP. - -```yaml tab="Structured (YAML)" -tracing: - otlp: - http: - endpoint: http://myotlpcollector:4318/v1/traces -``` - -```toml tab="Structured (TOML)" -[tracing.otlp.http] - endpoint = "http://myotlpcollector:4318/v1/traces" -``` - -```yaml tab="Helm Chart Values" -# values.yaml -tracing: - otlp: - enabled: true - http: - enabled: true - endpoint: http://myotlpcollector:4318/v1/traces -``` - -!!! info - For detailed configuration options, refer to the [tracing reference documentation](../reference/install-configuration/observability/tracing.md). - -## Per-Router Tracing - -You can enable or disable tracing for a specific router. This is useful for turning off tracing for specific routes while keeping it on globally. - -Here's an example of disabling tracing on a specific router: - -```yaml tab="Structured (YAML)" -http: - routers: - my-router: - rule: "Host(`example.com`)" - service: my-service - observability: - tracing: false -``` - -```toml tab="Structured (TOML)" -[http.routers.my-router.observability] - tracing = false -``` - -```yaml tab="Kubernetes" -# ingressoute.yaml -apiVersion: traefik.io/v1alpha1 -kind: IngressRoute -metadata: - name: my-router -spec: - routes: - - kind: Rule - match: Host(`example.com`) - services: - - name: my-service - port: 80 - observability: - tracing: false -``` - -```yaml tab="Labels" -labels: - - "traefik.http.routers.my-router.observability.tracing=false" -``` - -```json tab="Tags" -{ - // ... - "Tags": [ - "traefik.http.routers.my-router.observability.tracing=false" - ] -} -``` - -When the `observability` options are not defined on a router, it inherits the behavior from the [entrypoint's observability configuration](./overview.md), or the global one. diff --git a/docs/content/operations/api.md b/docs/content/operations/api.md index 8e867bdad..2099c2060 100644 --- a/docs/content/operations/api.md +++ b/docs/content/operations/api.md @@ -46,7 +46,7 @@ And then define a routing configuration on Traefik itself with the --8<-- "content/operations/include-api-examples.md" -??? warning "The router's [rule](../routing/routers/index.md#rule) must catch requests for the URI path `/api`" +??? warning "The router's [rule](../../routing/routers#rule) must catch requests for the URI path `/api`" Using an "Host" rule is recommended, by catching all the incoming traffic on this host domain to the API. However, you can also use "path prefix" rule or any combination or rules. @@ -109,7 +109,7 @@ api: --api.dashboard=true ``` -!!! warning "With Dashboard enabled, the router [rule](../routing/routers/index.md#rule) must catch requests for both `/api` and `/dashboard`" +!!! warning "With Dashboard enabled, the router [rule](../../routing/routers#rule) must catch requests for both `/api` and `/dashboard`" Please check the [Dashboard documentation](./dashboard.md#dashboard-router-rule) to learn more about this and to get examples. ### `debug` diff --git a/docs/content/operations/cli.md b/docs/content/operations/cli.md index 1f20a8f10..a7c9a7fbf 100644 --- a/docs/content/operations/cli.md +++ b/docs/content/operations/cli.md @@ -31,7 +31,7 @@ traefik [--flag=flag_argument] [-f [flag_argument]] traefik [--flag[=true|false| ]] [-f [true|false| ]] ``` -All flags are documented in the [(static configuration) CLI reference](../reference/install-configuration/configuration-options.md). +All flags are documented in the [(static configuration) CLI reference](../reference/static-configuration/cli.md). !!! info "Flags are case-insensitive." diff --git a/docs/content/operations/dashboard.md b/docs/content/operations/dashboard.md index c3776a369..b7df89025 100644 --- a/docs/content/operations/dashboard.md +++ b/docs/content/operations/dashboard.md @@ -11,7 +11,7 @@ See What's Going On The dashboard is the central place that shows you the current active routes handled by Traefik.
- Dashboard - Providers + Dashboard - Providers
The dashboard in action
diff --git a/docs/content/providers/consul-catalog.md b/docs/content/providers/consul-catalog.md index 34b81f0ed..5b3b97e65 100644 --- a/docs/content/providers/consul-catalog.md +++ b/docs/content/providers/consul-catalog.md @@ -477,7 +477,7 @@ _Optional, Default=true_ Expose Consul Catalog services by default in Traefik. If set to `false`, services that don't have a `traefik.enable=true` tag will be ignored from the resulting routing configuration. -For additional information, refer to [Restrict the Scope of Service Discovery](./overview.md#exposedbydefault-and-traefikenable). +For additional information, refer to [Restrict the Scope of Service Discovery](./overview.md#restrict-the-scope-of-service-discovery). ```yaml tab="File (YAML)" providers: @@ -672,7 +672,7 @@ providers: # ... ``` -For additional information, refer to [Restrict the Scope of Service Discovery](./overview.md#exposedbydefault-and-traefikenable). +For additional information, refer to [Restrict the Scope of Service Discovery](./overview.md#restrict-the-scope-of-service-discovery). ### `namespaces` diff --git a/docs/content/providers/docker.md b/docs/content/providers/docker.md index 75840998d..e164ae349 100644 --- a/docs/content/providers/docker.md +++ b/docs/content/providers/docker.md @@ -40,6 +40,7 @@ This provider works with [Docker (standalone) Engine](https://docs.docker.com/en Attaching labels to containers (in your docker compose file) ```yaml + version: "3" services: my-container: # ... @@ -161,9 +162,11 @@ See the [Docker API Access](#docker-api-access) section for more information. The docker-compose file shares the docker sock with the Traefik container ```yaml + version: '3' + services: traefik: - image: traefik:v3.5 # The official v3 Traefik docker image + image: traefik:v3.4 # The official v3 Traefik docker image ports: - "80:80" volumes: @@ -380,7 +383,7 @@ _Optional, Default=true_ Expose containers by default through Traefik. If set to `false`, containers that do not have a `traefik.enable=true` label are ignored from the resulting routing configuration. -For additional information, refer to [Restrict the Scope of Service Discovery](./overview.md#exposedbydefault-and-traefikenable). +For additional information, refer to [Restrict the Scope of Service Discovery](./overview.md#restrict-the-scope-of-service-discovery). ```yaml tab="File (YAML)" providers: @@ -554,7 +557,7 @@ as well as the usual boolean logic, as shown in examples below. constraints = "LabelRegex(`a.label.name`, `a.+`)" ``` -For additional information, refer to [Restrict the Scope of Service Discovery](./overview.md#exposedbydefault-and-traefikenable). +For additional information, refer to [Restrict the Scope of Service Discovery](./overview.md#restrict-the-scope-of-service-discovery). ```yaml tab="File (YAML)" providers: diff --git a/docs/content/providers/ecs.md b/docs/content/providers/ecs.md index 420e8c695..2cf8cf1e9 100644 --- a/docs/content/providers/ecs.md +++ b/docs/content/providers/ecs.md @@ -214,7 +214,7 @@ as well as the usual boolean logic, as shown in examples below. constraints = "LabelRegex(`a.label.name`, `a.+`)" ``` -For additional information, refer to [Restrict the Scope of Service Discovery](./overview.md#exposedbydefault-and-traefikenable). +For additional information, refer to [Restrict the Scope of Service Discovery](./overview.md#restrict-the-scope-of-service-discovery). ```yaml tab="File (YAML)" providers: diff --git a/docs/content/providers/file.md b/docs/content/providers/file.md index c1f328e32..2ac4ee1e5 100644 --- a/docs/content/providers/file.md +++ b/docs/content/providers/file.md @@ -103,7 +103,7 @@ It supports providing configuration through a [single configuration file](#filen ## Provider Configuration -For an overview of all the options that can be set with the file provider, see the [routing configuration](../reference/routing-configuration/other-providers/file.md) and [install configuration](../reference/install-configuration/configuration-options.md) references. +For an overview of all the options that can be set with the file provider, see the [dynamic configuration](../reference/dynamic-configuration/file.md) and [static configuration](../reference/static-configuration/overview.md) references. !!! warning "Limitations" diff --git a/docs/content/providers/kubernetes-crd.md b/docs/content/providers/kubernetes-crd.md index f00b5c68e..6979fd263 100644 --- a/docs/content/providers/kubernetes-crd.md +++ b/docs/content/providers/kubernetes-crd.md @@ -31,10 +31,10 @@ the Traefik engineering team developed a [Custom Resource Definition](https://ku ```bash # Install Traefik Resource Definitions: - kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.5/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml + kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.4/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml # Install RBAC for Traefik: - kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.5/docs/content/reference/dynamic-configuration/kubernetes-crd-rbac.yml + kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.4/docs/content/reference/dynamic-configuration/kubernetes-crd-rbac.yml ``` ## Resource Configuration diff --git a/docs/content/providers/kubernetes-gateway.md b/docs/content/providers/kubernetes-gateway.md index 22158dfb0..650d4fcfd 100644 --- a/docs/content/providers/kubernetes-gateway.md +++ b/docs/content/providers/kubernetes-gateway.md @@ -8,11 +8,11 @@ description: "Learn how to use the Kubernetes Gateway API as a provider for conf The Kubernetes Gateway provider is a Traefik implementation of the [Gateway API](https://gateway-api.sigs.k8s.io/) specification from the Kubernetes Special Interest Groups (SIGs). -This provider supports Standard version [v1.3.0](https://github.com/kubernetes-sigs/gateway-api/releases/tag/v1.3.0) of the Gateway API specification. +This provider supports Standard version [v1.2.1](https://github.com/kubernetes-sigs/gateway-api/releases/tag/v1.2.1) of the Gateway API specification. It fully supports all HTTP core and some extended features, as well as the `TCPRoute` and `TLSRoute` resources from the [Experimental channel](https://gateway-api.sigs.k8s.io/concepts/versioning/?h=#release-channels). -For more details, check out the conformance [report](https://github.com/kubernetes-sigs/gateway-api/tree/main/conformance/reports/v1.3.0/traefik-traefik). +For more details, check out the conformance [report](https://github.com/kubernetes-sigs/gateway-api/tree/main/conformance/reports/v1.2.1/traefik-traefik). ## Requirements @@ -27,14 +27,14 @@ For more details, check out the conformance [report](https://github.com/kubernet ```bash # Install Gateway API CRDs from the Standard channel. - kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.3.0/standard-install.yaml + kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.2.1/standard-install.yaml ``` 2. Install the additional Traefik RBAC required for Gateway API. ```bash # Install Traefik RBACs. - kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.5/docs/content/reference/dynamic-configuration/kubernetes-gateway-rbac.yml + kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.4/docs/content/reference/dynamic-configuration/kubernetes-gateway-rbac.yml ``` 3. Deploy Traefik and enable the `kubernetesGateway` provider in the static configuration as detailed below: @@ -275,7 +275,7 @@ providers: ```bash # Install Gateway API CRDs from the Experimental channel. - kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.3.0/experimental-install.yaml + kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.2.1/experimental-install.yaml ``` ### `labelselector` diff --git a/docs/content/providers/kubernetes-ingress.md b/docs/content/providers/kubernetes-ingress.md index ce9276480..08c77b888 100644 --- a/docs/content/providers/kubernetes-ingress.md +++ b/docs/content/providers/kubernetes-ingress.md @@ -529,32 +529,9 @@ providers: --providers.kubernetesingress.nativeLBByDefault=true ``` -### `strictPrefixMatching` - -_Optional, Default: false_ - -Make prefix matching strictly comply with the Kubernetes Ingress specification (path-element-wise matching instead of character-by-character string matching). For example, a PathPrefix of `/foo` will match `/foo`, `/foo/`, and `/foo/bar` but not `/foobar`. - -```yaml tab="File (YAML)" -providers: - kubernetesIngress: - strictPrefixMatching: true - # ... -``` - -```toml tab="File (TOML)" -[providers.kubernetesIngress] - strictPrefixMatching = true - # ... -``` - -```bash tab="CLI" ---providers.kubernetesingress.strictPrefixMatching=true -``` - ### Further To learn more about the various aspects of the Ingress specification that Traefik supports, -many examples of Ingresses definitions are located in the test [examples](https://github.com/traefik/traefik/tree/v3.5/pkg/provider/kubernetes/ingress/fixtures) of the Traefik repository. +many examples of Ingresses definitions are located in the test [examples](https://github.com/traefik/traefik/tree/v3.4/pkg/provider/kubernetes/ingress/fixtures) of the Traefik repository. {!traefik-for-business-applications.md!} diff --git a/docs/content/providers/nomad.md b/docs/content/providers/nomad.md index ef28d8c98..ed55aa799 100644 --- a/docs/content/providers/nomad.md +++ b/docs/content/providers/nomad.md @@ -384,7 +384,7 @@ _Optional, Default=true_ Expose Nomad services by default in Traefik. If set to `false`, services that do not have a `traefik.enable=true` tag will be ignored from the resulting routing configuration. -For additional information, refer to [Restrict the Scope of Service Discovery](./overview.md#exposedbydefault-and-traefikenable). +For additional information, refer to [Restrict the Scope of Service Discovery](./overview.md#restrict-the-scope-of-service-discovery). ```yaml tab="File (YAML)" providers: @@ -504,7 +504,7 @@ providers: # ... ``` -For additional information, refer to [Restrict the Scope of Service Discovery](./overview.md#exposedbydefault-and-traefikenable). +For additional information, refer to [Restrict the Scope of Service Discovery](./overview.md#restrict-the-scope-of-service-discovery). ### `namespaces` diff --git a/docs/content/providers/swarm.md b/docs/content/providers/swarm.md index e82e1d518..c5c158ba8 100644 --- a/docs/content/providers/swarm.md +++ b/docs/content/providers/swarm.md @@ -53,6 +53,7 @@ This provider works with [Docker Swarm Mode](https://docs.docker.com/engine/swar then that service is automatically assigned to the router. ```yaml + version: "3" services: my-container: deploy: @@ -175,6 +176,8 @@ docker service create \ ``` ```yml tab="With Docker Compose" +version: '3' + services: traefik: # ... @@ -205,6 +208,8 @@ See the [Docker Swarm API Access](#docker-api-access) section for more informati The docker-compose file shares the docker sock with the Traefik container ```yaml + version: '3' + services: traefik: image: traefik:v3.4 # The official v3 Traefik docker image @@ -424,7 +429,7 @@ _Optional, Default=true_ Expose containers by default through Traefik. If set to `false`, containers that do not have a `traefik.enable=true` label are ignored from the resulting routing configuration. -For additional information, refer to [Restrict the Scope of Service Discovery](./overview.md#exposedbydefault-and-traefikenable). +For additional information, refer to [Restrict the Scope of Service Discovery](./overview.md#restrict-the-scope-of-service-discovery). ```yaml tab="File (YAML)" providers: @@ -450,7 +455,10 @@ _Optional, Default=""_ Defines a default docker network to use for connections to all containers. -This option can be overridden on a per-container basis with the `traefik.swarm.network` [routing label](../routing/providers/swarm.md#traefikswarmnetwork). +This option can be overridden on a per-container basis with the `traefik.docker.network` [routing label](../routing/providers/swarm.md#traefikdockernetwork). + +!!! warning + The Docker Swarm provider still uses the same per-container mechanism as the Docker provider, so therefore the label still uses the `docker` keyword intentionally. ```yaml tab="File (YAML)" providers: @@ -621,7 +629,7 @@ as well as the usual boolean logic, as shown in examples below. constraints = "LabelRegex(`a.label.name`, `a.+`)" ``` -For additional information, refer to [Restrict the Scope of Service Discovery](./overview.md#exposedbydefault-and-traefikenable). +For additional information, refer to [Restrict the Scope of Service Discovery](./overview.md#restrict-the-scope-of-service-discovery). ```yaml tab="File (YAML)" providers: diff --git a/docs/content/reference/dynamic-configuration/consul-catalog.md b/docs/content/reference/dynamic-configuration/consul-catalog.md new file mode 100644 index 000000000..2669b8153 --- /dev/null +++ b/docs/content/reference/dynamic-configuration/consul-catalog.md @@ -0,0 +1,16 @@ +--- +title: "Traefik Consul Configuration Documentation" +description: "View the reference for performing dynamic configurations with Traefik Proxy and Consul Catalog. Read the technical documentation." +--- + +# Consul Catalog Configuration Reference + +Dynamic configuration with Consul Catalog +{: .subtitle } + +The labels are case-insensitive. + +```yaml +--8<-- "content/reference/dynamic-configuration/consul-catalog.yml" +--8<-- "content/reference/dynamic-configuration/docker-labels.yml" +``` diff --git a/docs/content/reference/dynamic-configuration/consul-catalog.yml b/docs/content/reference/dynamic-configuration/consul-catalog.yml new file mode 100644 index 000000000..e125eba19 --- /dev/null +++ b/docs/content/reference/dynamic-configuration/consul-catalog.yml @@ -0,0 +1,2 @@ +- "traefik.enable=true" +- "traefik.consulcatalog.connect=true" diff --git a/docs/content/reference/dynamic-configuration/docker-labels.yml b/docs/content/reference/dynamic-configuration/docker-labels.yml index 3399c2762..d6534790b 100644 --- a/docs/content/reference/dynamic-configuration/docker-labels.yml +++ b/docs/content/reference/dynamic-configuration/docker-labels.yml @@ -169,7 +169,6 @@ - "traefik.http.routers.router0.middlewares=foobar, foobar" - "traefik.http.routers.router0.observability.accesslogs=true" - "traefik.http.routers.router0.observability.metrics=true" -- "traefik.http.routers.router0.observability.traceverbosity=foobar" - "traefik.http.routers.router0.observability.tracing=true" - "traefik.http.routers.router0.priority=42" - "traefik.http.routers.router0.rule=foobar" @@ -186,7 +185,6 @@ - "traefik.http.routers.router1.middlewares=foobar, foobar" - "traefik.http.routers.router1.observability.accesslogs=true" - "traefik.http.routers.router1.observability.metrics=true" -- "traefik.http.routers.router1.observability.traceverbosity=foobar" - "traefik.http.routers.router1.observability.tracing=true" - "traefik.http.routers.router1.priority=42" - "traefik.http.routers.router1.rule=foobar" @@ -211,7 +209,6 @@ - "traefik.http.services.service02.loadbalancer.healthcheck.scheme=foobar" - "traefik.http.services.service02.loadbalancer.healthcheck.status=42" - "traefik.http.services.service02.loadbalancer.healthcheck.timeout=42s" -- "traefik.http.services.service02.loadbalancer.healthcheck.unhealthyinterval=42s" - "traefik.http.services.service02.loadbalancer.passhostheader=true" - "traefik.http.services.service02.loadbalancer.responseforwarding.flushinterval=42s" - "traefik.http.services.service02.loadbalancer.serverstransport=foobar" diff --git a/docs/content/reference/dynamic-configuration/docker.md b/docs/content/reference/dynamic-configuration/docker.md new file mode 100644 index 000000000..3b9163a53 --- /dev/null +++ b/docs/content/reference/dynamic-configuration/docker.md @@ -0,0 +1,17 @@ +--- +title: "Traefik Docker Configuration Documentation" +description: "Reference dynamic configuration with Docker labels in Traefik Proxy. Read the technical documentation." +--- + +# Docker Configuration Reference + +Dynamic configuration with Docker Labels +{: .subtitle } + +The labels are case-insensitive. + +```yaml +labels: + --8<-- "content/reference/dynamic-configuration/docker.yml" + --8<-- "content/reference/dynamic-configuration/docker-labels.yml" +``` diff --git a/docs/content/reference/dynamic-configuration/docker.yml b/docs/content/reference/dynamic-configuration/docker.yml new file mode 100644 index 000000000..097de499d --- /dev/null +++ b/docs/content/reference/dynamic-configuration/docker.yml @@ -0,0 +1,2 @@ +- "traefik.enable=true" +- "traefik.docker.network=foobar" diff --git a/docs/content/reference/dynamic-configuration/ecs.md b/docs/content/reference/dynamic-configuration/ecs.md new file mode 100644 index 000000000..cf721befd --- /dev/null +++ b/docs/content/reference/dynamic-configuration/ecs.md @@ -0,0 +1,16 @@ +--- +title: "Traefik AWS ECS Configuration Documentation" +description: "Learn how to do dynamic configuration in Traefik Proxy with AWS ECS. Read the technical documentation." +--- + +# ECS Configuration Reference + +Dynamic configuration with ECS provider +{: .subtitle } + +The labels are case-insensitive. + +```yaml +--8<-- "content/reference/dynamic-configuration/ecs.yml" +--8<-- "content/reference/dynamic-configuration/docker-labels.yml" +``` diff --git a/docs/content/reference/dynamic-configuration/ecs.yml b/docs/content/reference/dynamic-configuration/ecs.yml new file mode 100644 index 000000000..23efc00c6 --- /dev/null +++ b/docs/content/reference/dynamic-configuration/ecs.yml @@ -0,0 +1 @@ +- "traefik.enable=true" diff --git a/docs/content/reference/dynamic-configuration/file.md b/docs/content/reference/dynamic-configuration/file.md new file mode 100644 index 000000000..eac9dc9a1 --- /dev/null +++ b/docs/content/reference/dynamic-configuration/file.md @@ -0,0 +1,17 @@ +--- +title: "Traefik File Dynamic Configuration" +description: "This guide will provide you with the YAML and TOML files for dynamic configuration in Traefik Proxy. Read the technical documentation." +--- + +# File Configuration Reference + +Dynamic configuration with files +{: .subtitle } + +```yml tab="YAML" +--8<-- "content/reference/dynamic-configuration/file.yaml" +``` + +```toml tab="TOML" +--8<-- "content/reference/dynamic-configuration/file.toml" +``` diff --git a/docs/content/reference/dynamic-configuration/file.toml b/docs/content/reference/dynamic-configuration/file.toml index 985f96e21..9915ff223 100644 --- a/docs/content/reference/dynamic-configuration/file.toml +++ b/docs/content/reference/dynamic-configuration/file.toml @@ -22,9 +22,8 @@ sans = ["foobar", "foobar"] [http.routers.Router0.observability] accessLogs = true - metrics = true tracing = true - traceVerbosity = "foobar" + metrics = true [http.routers.Router1] entryPoints = ["foobar", "foobar"] middlewares = ["foobar", "foobar"] @@ -45,9 +44,8 @@ sans = ["foobar", "foobar"] [http.routers.Router1.observability] accessLogs = true - metrics = true tracing = true - traceVerbosity = "foobar" + metrics = true [http.services] [http.services.Service01] [http.services.Service01.failover] @@ -86,7 +84,6 @@ status = 42 port = 42 interval = "42s" - unhealthyInterval = "42s" timeout = "42s" hostname = "foobar" followRedirects = true @@ -454,16 +451,16 @@ [tcp.services.TCPService01.loadBalancer] serversTransport = "foobar" terminationDelay = 42 - - [[tcp.services.TCPService01.loadBalancer.servers]] - address = "foobar" - tls = true - - [[tcp.services.TCPService01.loadBalancer.servers]] - address = "foobar" - tls = true [tcp.services.TCPService01.loadBalancer.proxyProtocol] version = 42 + + [[tcp.services.TCPService01.loadBalancer.servers]] + address = "foobar" + tls = true + + [[tcp.services.TCPService01.loadBalancer.servers]] + address = "foobar" + tls = true [tcp.services.TCPService02] [tcp.services.TCPService02.weighted] @@ -489,8 +486,6 @@ dialKeepAlive = "42s" dialTimeout = "42s" terminationDelay = "42s" - [tcp.serversTransports.TCPServersTransport0.proxyProtocol] - version = 42 [tcp.serversTransports.TCPServersTransport0.tls] serverName = "foobar" insecureSkipVerify = true @@ -511,8 +506,6 @@ dialKeepAlive = "42s" dialTimeout = "42s" terminationDelay = "42s" - [tcp.serversTransports.TCPServersTransport1.proxyProtocol] - version = 42 [tcp.serversTransports.TCPServersTransport1.tls] serverName = "foobar" insecureSkipVerify = true diff --git a/docs/content/reference/dynamic-configuration/file.yaml b/docs/content/reference/dynamic-configuration/file.yaml index 29822fddb..30f0afa59 100644 --- a/docs/content/reference/dynamic-configuration/file.yaml +++ b/docs/content/reference/dynamic-configuration/file.yaml @@ -27,9 +27,8 @@ http: - foobar observability: accessLogs: true - metrics: true tracing: true - traceVerbosity: foobar + metrics: true Router1: entryPoints: - foobar @@ -55,9 +54,8 @@ http: - foobar observability: accessLogs: true - metrics: true tracing: true - traceVerbosity: foobar + metrics: true services: Service01: failover: @@ -91,7 +89,6 @@ http: status: 42 port: 42 interval: 42s - unhealthyInterval: 42s timeout: 42s hostname: foobar followRedirects: true @@ -518,14 +515,14 @@ tcp: services: TCPService01: loadBalancer: + proxyProtocol: + version: 42 servers: - address: foobar tls: true - address: foobar tls: true serversTransport: foobar - proxyProtocol: - version: 42 terminationDelay: 42 TCPService02: weighted: @@ -552,8 +549,6 @@ tcp: TCPServersTransport0: dialKeepAlive: 42s dialTimeout: 42s - proxyProtocol: - version: 42 terminationDelay: 42s tls: serverName: foobar @@ -575,8 +570,6 @@ tcp: TCPServersTransport1: dialKeepAlive: 42s dialTimeout: 42s - proxyProtocol: - version: 42 terminationDelay: 42s tls: serverName: foobar diff --git a/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml b/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml index ea2618637..4a471058f 100644 --- a/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml +++ b/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml @@ -43,7 +43,7 @@ spec: description: |- EntryPoints defines the list of entry point names to bind to. Entry points have to be configured in the static configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/install-configuration/entrypoints/ + More info: https://doc.traefik.io/traefik/v3.4/routing/entrypoints/ Default: all. items: type: string @@ -64,12 +64,12 @@ spec: match: description: |- Match defines the router's rule. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/routing/rules-and-priority/ + More info: https://doc.traefik.io/traefik/v3.4/routing/routers/#rule type: string middlewares: description: |- Middlewares defines the list of references to Middleware resources. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/kubernetes/crd/http/middleware/ + More info: https://doc.traefik.io/traefik/v3.4/routing/providers/kubernetes-crd/#kind-middleware items: description: MiddlewareRef is a reference to a Middleware resource. @@ -89,30 +89,19 @@ spec: observability: description: |- Observability defines the observability configuration for a router. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/routing/observability/ + More info: https://doc.traefik.io/traefik/v3.4/routing/routers/#observability properties: accessLogs: - description: AccessLogs enables access logs for this router. type: boolean metrics: - description: Metrics enables metrics for this router. type: boolean - traceVerbosity: - default: minimal - description: TraceVerbosity defines the verbosity level - of the tracing for this router. - enum: - - minimal - - detailed - type: string tracing: - description: Tracing enables tracing for this router. type: boolean type: object priority: description: |- Priority defines the router's priority. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/routing/rules-and-priority/#priority + More info: https://doc.traefik.io/traefik/v3.4/routing/routers/#priority maximum: 9223372036854775000 type: integer services: @@ -147,7 +136,7 @@ spec: - type: integer - type: string description: |- - Interval defines the frequency of the health check calls for healthy targets. + Interval defines the frequency of the health check calls. Default: 30s x-kubernetes-int-or-string: true method: @@ -183,15 +172,6 @@ spec: Timeout defines the maximum duration Traefik will wait for a health check request before considering the server unhealthy. Default: 5s x-kubernetes-int-or-string: true - unhealthyInterval: - anyOf: - - type: integer - - type: string - description: |- - UnhealthyInterval defines the frequency of the health check calls for unhealthy targets. - When UnhealthyInterval is not defined, it defaults to the Interval value. - Default: 30s - x-kubernetes-int-or-string: true type: object kind: description: Kind defines the kind of the Service. @@ -263,7 +243,7 @@ spec: sticky: description: |- Sticky defines the sticky sessions configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/load-balancing/service/#sticky-sessions + More info: https://doc.traefik.io/traefik/v3.4/routing/services/#sticky-sessions properties: cookie: description: Cookie defines the sticky cookie configuration. @@ -332,7 +312,7 @@ spec: syntax: description: |- Syntax defines the router's rule syntax. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/routing/rules-and-priority/#rulesyntax + More info: https://doc.traefik.io/traefik/v3.4/routing/routers/#rulesyntax Deprecated: Please do not use this field and rewrite the router rules to use the v3 syntax. type: string required: @@ -342,18 +322,18 @@ spec: tls: description: |- TLS defines the TLS configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/routing/router/#tls + More info: https://doc.traefik.io/traefik/v3.4/routing/routers/#tls properties: certResolver: description: |- CertResolver defines the name of the certificate resolver to use. Cert resolvers have to be configured in the static configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/install-configuration/tls/certificate-resolvers/acme/ + More info: https://doc.traefik.io/traefik/v3.4/https/acme/#certificate-resolvers type: string domains: description: |- Domains defines the list of domains that will be used to issue certificates. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/tls/tls-certificates/#domains + More info: https://doc.traefik.io/traefik/v3.4/routing/routers/#domains items: description: Domain holds a domain name with SANs. properties: @@ -372,17 +352,17 @@ spec: description: |- Options defines the reference to a TLSOption, that specifies the parameters of the TLS connection. If not defined, the `default` TLSOption is used. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/tls/tls-options/ + More info: https://doc.traefik.io/traefik/v3.4/https/tls/#tls-options properties: name: description: |- Name defines the name of the referenced TLSOption. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/kubernetes/crd/http/tlsoption/ + More info: https://doc.traefik.io/traefik/v3.4/routing/providers/kubernetes-crd/#kind-tlsoption type: string namespace: description: |- Namespace defines the namespace of the referenced TLSOption. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/kubernetes/crd/http/tlsoption/ + More info: https://doc.traefik.io/traefik/v3.4/routing/providers/kubernetes-crd/#kind-tlsoption type: string required: - name @@ -399,12 +379,12 @@ spec: name: description: |- Name defines the name of the referenced TLSStore. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/kubernetes/crd/http/tlsstore/ + More info: https://doc.traefik.io/traefik/v3.4/routing/providers/kubernetes-crd/#kind-tlsstore type: string namespace: description: |- Namespace defines the namespace of the referenced TLSStore. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/kubernetes/crd/http/tlsstore/ + More info: https://doc.traefik.io/traefik/v3.4/routing/providers/kubernetes-crd/#kind-tlsstore type: string required: - name @@ -464,7 +444,7 @@ spec: description: |- EntryPoints defines the list of entry point names to bind to. Entry points have to be configured in the static configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/install-configuration/entrypoints/ + More info: https://doc.traefik.io/traefik/v3.4/routing/entrypoints/ Default: all. items: type: string @@ -477,7 +457,7 @@ spec: match: description: |- Match defines the router's rule. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/routing/rules-and-priority/ + More info: https://doc.traefik.io/traefik/v3.4/routing/routers/#rule_1 type: string middlewares: description: Middlewares defines the list of references to MiddlewareTCP @@ -501,7 +481,7 @@ spec: priority: description: |- Priority defines the router's priority. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/routing/rules-and-priority/#priority + More info: https://doc.traefik.io/traefik/v3.4/routing/routers/#priority_1 maximum: 9223372036854775000 type: integer services: @@ -543,8 +523,7 @@ spec: proxyProtocol: description: |- ProxyProtocol defines the PROXY protocol configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/service/#proxy-protocol - Deprecated: ProxyProtocol will not be supported in future APIVersions, please use ServersTransport to configure ProxyProtocol instead. + More info: https://doc.traefik.io/traefik/v3.4/routing/services/#proxy-protocol properties: version: description: Version defines the PROXY Protocol version @@ -585,7 +564,7 @@ spec: syntax: description: |- Syntax defines the router's rule syntax. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/routing/rules-and-priority/#rulesyntax + More info: https://doc.traefik.io/traefik/v3.4/routing/routers/#rulesyntax_1 Deprecated: Please do not use this field and rewrite the router rules to use the v3 syntax. enum: - v3 @@ -598,18 +577,18 @@ spec: tls: description: |- TLS defines the TLS configuration on a layer 4 / TCP Route. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/routing/router/#tls + More info: https://doc.traefik.io/traefik/v3.4/routing/routers/#tls_1 properties: certResolver: description: |- CertResolver defines the name of the certificate resolver to use. Cert resolvers have to be configured in the static configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/install-configuration/tls/certificate-resolvers/acme/ + More info: https://doc.traefik.io/traefik/v3.4/https/acme/#certificate-resolvers type: string domains: description: |- Domains defines the list of domains that will be used to issue certificates. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/tls/#domains + More info: https://doc.traefik.io/traefik/v3.4/routing/routers/#domains items: description: Domain holds a domain name with SANs. properties: @@ -628,7 +607,7 @@ spec: description: |- Options defines the reference to a TLSOption, that specifies the parameters of the TLS connection. If not defined, the `default` TLSOption is used. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/tls/#tls-options + More info: https://doc.traefik.io/traefik/v3.4/https/tls/#tls-options properties: name: description: Name defines the name of the referenced Traefik @@ -720,7 +699,7 @@ spec: description: |- EntryPoints defines the list of entry point names to bind to. Entry points have to be configured in the static configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/install-configuration/entrypoints/ + More info: https://doc.traefik.io/traefik/v3.4/routing/entrypoints/ Default: all. items: type: string @@ -808,7 +787,7 @@ spec: openAPIV3Schema: description: |- Middleware is the CRD implementation of a Traefik Middleware. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/overview/ + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/overview/ properties: apiVersion: description: |- @@ -834,7 +813,7 @@ spec: description: |- AddPrefix holds the add prefix middleware configuration. This middleware updates the path of a request before forwarding it. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/addprefix/ + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/addprefix/ properties: prefix: description: |- @@ -849,12 +828,12 @@ spec: description: |- BasicAuth holds the basic auth middleware configuration. This middleware restricts access to your services to known users. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/basicauth/ + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/basicauth/ properties: headerField: description: |- HeaderField defines a header field to store the authenticated user. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/basicauth/#headerfield + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/basicauth/#headerfield type: string realm: description: |- @@ -875,7 +854,7 @@ spec: description: |- Buffering holds the buffering middleware configuration. This middleware retries or limits the size of requests that can be forwarded to backends. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/buffering/#maxrequestbodybytes + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/buffering/#maxrequestbodybytes properties: maxRequestBodyBytes: description: |- @@ -907,14 +886,14 @@ spec: description: |- RetryExpression defines the retry conditions. It is a logical combination of functions with operators AND (&&) and OR (||). - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/buffering/#retryexpression + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/buffering/#retryexpression type: string type: object chain: description: |- Chain holds the configuration of the chain middleware. This middleware enables to define reusable combinations of other pieces of middleware. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/chain/ + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/chain/ properties: middlewares: description: Middlewares is the list of MiddlewareRef which composes @@ -977,7 +956,7 @@ spec: description: |- Compress holds the compress middleware configuration. This middleware compresses responses before sending them to the client, using gzip, brotli, or zstd compression. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/compress/ + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/compress/ properties: defaultEncoding: description: DefaultEncoding specifies the default encoding if @@ -1027,12 +1006,12 @@ spec: description: |- DigestAuth holds the digest auth middleware configuration. This middleware restricts access to your services to known users. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/digestauth/ + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/digestauth/ properties: headerField: description: |- HeaderField defines a header field to store the authenticated user. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/digestauth/#headerfield + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/basicauth/#headerfield type: string realm: description: |- @@ -1052,7 +1031,7 @@ spec: description: |- ErrorPage holds the custom error middleware configuration. This middleware returns a custom page in lieu of the default, according to configured ranges of HTTP Status codes. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/errorpages/ + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/errorpages/ properties: query: description: |- @@ -1064,7 +1043,7 @@ spec: service: description: |- Service defines the reference to a Kubernetes Service that will serve the error page. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/errorpages/#service + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/errorpages/#service properties: healthCheck: description: Healthcheck defines health checks for ExternalName @@ -1090,7 +1069,7 @@ spec: - type: integer - type: string description: |- - Interval defines the frequency of the health check calls for healthy targets. + Interval defines the frequency of the health check calls. Default: 30s x-kubernetes-int-or-string: true method: @@ -1126,15 +1105,6 @@ spec: Timeout defines the maximum duration Traefik will wait for a health check request before considering the server unhealthy. Default: 5s x-kubernetes-int-or-string: true - unhealthyInterval: - anyOf: - - type: integer - - type: string - description: |- - UnhealthyInterval defines the frequency of the health check calls for unhealthy targets. - When UnhealthyInterval is not defined, it defaults to the Interval value. - Default: 30s - x-kubernetes-int-or-string: true type: object kind: description: Kind defines the kind of the Service. @@ -1206,7 +1176,7 @@ spec: sticky: description: |- Sticky defines the sticky sessions configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/load-balancing/service/#sticky-sessions + More info: https://doc.traefik.io/traefik/v3.4/routing/services/#sticky-sessions properties: cookie: description: Cookie defines the sticky cookie configuration. @@ -1293,7 +1263,7 @@ spec: description: |- ForwardAuth holds the forward auth middleware configuration. This middleware delegates the request authentication to a Service. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/forwardauth/ + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/forwardauth/ properties: addAuthCookiesToResponse: description: AddAuthCookiesToResponse defines the list of cookies @@ -1321,7 +1291,7 @@ spec: authResponseHeadersRegex: description: |- AuthResponseHeadersRegex defines the regex to match headers to copy from the authentication server response and set on forwarded request, after stripping all headers that match the regex. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/forwardauth/#authresponseheadersregex + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/forwardauth/#authresponseheadersregex type: string forwardBody: description: ForwardBody defines whether to send the request body @@ -1330,7 +1300,7 @@ spec: headerField: description: |- HeaderField defines a header field to store the authenticated user. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/forwardauth/#headerfield + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/forwardauth/#headerfield type: string maxBodySize: description: MaxBodySize defines the maximum body size in bytes @@ -1392,7 +1362,7 @@ spec: description: |- Headers holds the headers middleware configuration. This middleware manages the requests and responses headers. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/headers/#customrequestheaders + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/headers/#customrequestheaders properties: accessControlAllowCredentials: description: AccessControlAllowCredentials defines whether the @@ -1564,7 +1534,7 @@ spec: description: |- InFlightReq holds the in-flight request middleware configuration. This middleware limits the number of requests being processed and served concurrently. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/inflightreq/ + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/inflightreq/ properties: amount: description: |- @@ -1578,12 +1548,12 @@ spec: SourceCriterion defines what criterion is used to group requests as originating from a common source. If several strategies are defined at the same time, an error will be raised. If none are set, the default is to use the requestHost. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/inflightreq/#sourcecriterion + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/inflightreq/#sourcecriterion properties: ipStrategy: description: |- IPStrategy holds the IP strategy configuration used by Traefik to determine the client IP. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/ipallowlist/#ipstrategy + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/ipallowlist/#ipstrategy properties: depth: description: Depth tells Traefik to use the X-Forwarded-For @@ -1619,12 +1589,12 @@ spec: description: |- IPAllowList holds the IP allowlist middleware configuration. This middleware limits allowed requests based on the client IP. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/ipallowlist/ + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/ipallowlist/ properties: ipStrategy: description: |- IPStrategy holds the IP strategy configuration used by Traefik to determine the client IP. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/ipallowlist/#ipstrategy + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/ipallowlist/#ipstrategy properties: depth: description: Depth tells Traefik to use the X-Forwarded-For @@ -1662,7 +1632,7 @@ spec: ipStrategy: description: |- IPStrategy holds the IP strategy configuration used by Traefik to determine the client IP. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/ipallowlist/#ipstrategy + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/ipallowlist/#ipstrategy properties: depth: description: Depth tells Traefik to use the X-Forwarded-For @@ -1693,7 +1663,7 @@ spec: description: |- PassTLSClientCert holds the pass TLS client cert middleware configuration. This middleware adds the selected data from the passed client TLS certificate to a header. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/passtlsclientcert/ + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/passtlsclientcert/ properties: info: description: Info selects the specific client certificate details @@ -1796,13 +1766,13 @@ spec: x-kubernetes-preserve-unknown-fields: true description: |- Plugin defines the middleware plugin configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/overview/#community-middlewares + More info: https://doc.traefik.io/traefik/plugins/ type: object rateLimit: description: |- RateLimit holds the rate limit configuration. This middleware ensures that services will receive a fair amount of requests, and allows one to define what fair is. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/ratelimit/ + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/ratelimit/ properties: average: description: |- @@ -1921,7 +1891,7 @@ spec: ipStrategy: description: |- IPStrategy holds the IP strategy configuration used by Traefik to determine the client IP. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/ipallowlist/#ipstrategy + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/ipallowlist/#ipstrategy properties: depth: description: Depth tells Traefik to use the X-Forwarded-For @@ -1957,11 +1927,11 @@ spec: description: |- RedirectRegex holds the redirect regex middleware configuration. This middleware redirects a request using regex matching and replacement. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/redirectregex/#regex + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/redirectregex/#regex properties: permanent: description: Permanent defines whether the redirection is permanent - (308). + (301). type: boolean regex: description: Regex defines the regex used to match and capture @@ -1976,11 +1946,11 @@ spec: description: |- RedirectScheme holds the redirect scheme middleware configuration. This middleware redirects requests from a scheme/port to another. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/redirectscheme/ + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/redirectscheme/ properties: permanent: description: Permanent defines whether the redirection is permanent - (308). + (301). type: boolean port: description: Port defines the port of the new URL. @@ -1993,7 +1963,7 @@ spec: description: |- ReplacePath holds the replace path middleware configuration. This middleware replaces the path of the request URL and store the original path in an X-Replaced-Path header. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/replacepath/ + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/replacepath/ properties: path: description: Path defines the path to use as replacement in the @@ -2004,7 +1974,7 @@ spec: description: |- ReplacePathRegex holds the replace path regex middleware configuration. This middleware replaces the path of a URL using regex matching and replacement. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/replacepathregex/ + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/replacepathregex/ properties: regex: description: Regex defines the regular expression used to match @@ -2020,7 +1990,7 @@ spec: Retry holds the retry middleware configuration. This middleware reissues requests a given number of times to a backend server if that server does not reply. As soon as the server answers, the middleware stops retrying, regardless of the response status. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/retry/ + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/retry/ properties: attempts: description: Attempts defines how many times the request should @@ -2044,7 +2014,7 @@ spec: description: |- StripPrefix holds the strip prefix middleware configuration. This middleware removes the specified prefixes from the URL path. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/stripprefix/ + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/stripprefix/ properties: forceSlash: description: |- @@ -2063,7 +2033,7 @@ spec: description: |- StripPrefixRegex holds the strip prefix regex middleware configuration. This middleware removes the matching prefixes from the URL path. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/stripprefixregex/ + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/stripprefixregex/ properties: regex: description: Regex defines the regular expression to match the @@ -2100,7 +2070,7 @@ spec: openAPIV3Schema: description: |- MiddlewareTCP is the CRD implementation of a Traefik TCP middleware. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/middlewares/overview/ + More info: https://doc.traefik.io/traefik/v3.4/middlewares/overview/ properties: apiVersion: description: |- @@ -2137,7 +2107,7 @@ spec: description: |- IPAllowList defines the IPAllowList middleware configuration. This middleware accepts/refuses connections based on the client IP. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/middlewares/ipallowlist/ + More info: https://doc.traefik.io/traefik/v3.4/middlewares/tcp/ipallowlist/ properties: sourceRange: description: SourceRange defines the allowed IPs (or ranges of @@ -2151,7 +2121,7 @@ spec: IPWhiteList defines the IPWhiteList middleware configuration. This middleware accepts/refuses connections based on the client IP. Deprecated: please use IPAllowList instead. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/middlewares/ipwhitelist/ + More info: https://doc.traefik.io/traefik/v3.4/middlewares/tcp/ipwhitelist/ properties: sourceRange: description: SourceRange defines the allowed IPs (or ranges of @@ -2190,7 +2160,7 @@ spec: ServersTransport is the CRD implementation of a ServersTransport. If no serversTransport is specified, the default@internal will be used. The default@internal serversTransport is created from the static configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/load-balancing/serverstransport/ + More info: https://doc.traefik.io/traefik/v3.4/routing/services/#serverstransport_1 properties: apiVersion: description: |- @@ -2276,7 +2246,7 @@ spec: maxIdleConnsPerHost: description: MaxIdleConnsPerHost controls the maximum idle (keep-alive) to keep per-host. - minimum: -1 + minimum: 0 type: integer peerCertURI: description: PeerCertURI defines the peer cert URI used to match against @@ -2359,7 +2329,7 @@ spec: ServersTransportTCP is the CRD implementation of a TCPServersTransport. If no tcpServersTransport is specified, a default one named default@internal will be used. The default@internal tcpServersTransport can be configured in the static configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/serverstransport/ + More info: https://doc.traefik.io/traefik/v3.4/routing/services/#serverstransport_3 properties: apiVersion: description: |- @@ -2401,15 +2371,6 @@ spec: to a backend server can be established. pattern: ^([0-9]+(ns|us|Âĩs|ms|s|m|h)?)+$ x-kubernetes-int-or-string: true - proxyProtocol: - description: ProxyProtocol holds the PROXY Protocol configuration. - properties: - version: - description: Version defines the PROXY Protocol version to use. - maximum: 2 - minimum: 1 - type: integer - type: object terminationDelay: anyOf: - type: integer @@ -2513,7 +2474,7 @@ spec: openAPIV3Schema: description: |- TLSOption is the CRD implementation of a Traefik TLS Option, allowing to configure some parameters of the TLS connection. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/tls/tls-certificates/#certificates-stores#tls-options + More info: https://doc.traefik.io/traefik/v3.4/https/tls/#tls-options properties: apiVersion: description: |- @@ -2538,14 +2499,14 @@ spec: alpnProtocols: description: |- ALPNProtocols defines the list of supported application level protocols for the TLS handshake, in order of preference. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/tls/tls-certificates/#certificates-stores#alpn-protocols + More info: https://doc.traefik.io/traefik/v3.4/https/tls/#alpn-protocols items: type: string type: array cipherSuites: description: |- CipherSuites defines the list of supported cipher suites for TLS versions up to TLS 1.2. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/tls/tls-certificates/#certificates-stores#cipher-suites + More info: https://doc.traefik.io/traefik/v3.4/https/tls/#cipher-suites items: type: string type: array @@ -2572,8 +2533,8 @@ spec: type: object curvePreferences: description: |- - CurvePreferences defines the preferred elliptic curves. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/tls/tls-certificates/#certificates-stores#curve-preferences + CurvePreferences defines the preferred elliptic curves in a specific order. + More info: https://doc.traefik.io/traefik/v3.4/https/tls/#curve-preferences items: type: string type: array @@ -2633,7 +2594,7 @@ spec: TLSStore is the CRD implementation of a Traefik TLS Store. For the time being, only the TLSStore named default is supported. This means that you cannot have two stores that are named default in different Kubernetes namespaces. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/tls/tls-certificates/#certificates-stores#certificates-stores + More info: https://doc.traefik.io/traefik/v3.4/https/tls/#certificates-stores properties: apiVersion: description: |- @@ -2731,7 +2692,7 @@ spec: TraefikService object allows to: - Apply weight to Services on load-balancing - Mirror traffic on services - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/kubernetes/crd/http/traefikservice/ + More info: https://doc.traefik.io/traefik/v3.4/routing/providers/kubernetes-crd/#kind-traefikservice properties: apiVersion: description: |- @@ -2780,7 +2741,7 @@ spec: - type: integer - type: string description: |- - Interval defines the frequency of the health check calls for healthy targets. + Interval defines the frequency of the health check calls. Default: 30s x-kubernetes-int-or-string: true method: @@ -2816,15 +2777,6 @@ spec: Timeout defines the maximum duration Traefik will wait for a health check request before considering the server unhealthy. Default: 5s x-kubernetes-int-or-string: true - unhealthyInterval: - anyOf: - - type: integer - - type: string - description: |- - UnhealthyInterval defines the frequency of the health check calls for unhealthy targets. - When UnhealthyInterval is not defined, it defaults to the Interval value. - Default: 30s - x-kubernetes-int-or-string: true type: object kind: description: Kind defines the kind of the Service. @@ -2874,7 +2826,7 @@ spec: - type: integer - type: string description: |- - Interval defines the frequency of the health check calls for healthy targets. + Interval defines the frequency of the health check calls. Default: 30s x-kubernetes-int-or-string: true method: @@ -2910,15 +2862,6 @@ spec: Timeout defines the maximum duration Traefik will wait for a health check request before considering the server unhealthy. Default: 5s x-kubernetes-int-or-string: true - unhealthyInterval: - anyOf: - - type: integer - - type: string - description: |- - UnhealthyInterval defines the frequency of the health check calls for unhealthy targets. - When UnhealthyInterval is not defined, it defaults to the Interval value. - Default: 30s - x-kubernetes-int-or-string: true type: object kind: description: Kind defines the kind of the Service. @@ -2995,7 +2938,7 @@ spec: sticky: description: |- Sticky defines the sticky sessions configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/load-balancing/service/#sticky-sessions + More info: https://doc.traefik.io/traefik/v3.4/routing/services/#sticky-sessions properties: cookie: description: Cookie defines the sticky cookie configuration. @@ -3123,7 +3066,7 @@ spec: sticky: description: |- Sticky defines the sticky sessions configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/load-balancing/service/#sticky-sessions + More info: https://doc.traefik.io/traefik/v3.4/routing/services/#sticky-sessions properties: cookie: description: Cookie defines the sticky cookie configuration. @@ -3220,7 +3163,7 @@ spec: - type: integer - type: string description: |- - Interval defines the frequency of the health check calls for healthy targets. + Interval defines the frequency of the health check calls. Default: 30s x-kubernetes-int-or-string: true method: @@ -3256,15 +3199,6 @@ spec: Timeout defines the maximum duration Traefik will wait for a health check request before considering the server unhealthy. Default: 5s x-kubernetes-int-or-string: true - unhealthyInterval: - anyOf: - - type: integer - - type: string - description: |- - UnhealthyInterval defines the frequency of the health check calls for unhealthy targets. - When UnhealthyInterval is not defined, it defaults to the Interval value. - Default: 30s - x-kubernetes-int-or-string: true type: object kind: description: Kind defines the kind of the Service. @@ -3336,7 +3270,7 @@ spec: sticky: description: |- Sticky defines the sticky sessions configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/load-balancing/service/#sticky-sessions + More info: https://doc.traefik.io/traefik/v3.4/routing/services/#sticky-sessions properties: cookie: description: Cookie defines the sticky cookie configuration. @@ -3404,7 +3338,7 @@ spec: sticky: description: |- Sticky defines whether sticky sessions are enabled. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/kubernetes/crd/http/traefikservice/#stickiness-and-load-balancing + More info: https://doc.traefik.io/traefik/v3.4/routing/providers/kubernetes-crd/#stickiness-and-load-balancing properties: cookie: description: Cookie defines the sticky cookie configuration. diff --git a/docs/content/reference/dynamic-configuration/kubernetes-crd-rbac.yml b/docs/content/reference/dynamic-configuration/kubernetes-crd-rbac.yml index eb3f708bf..87a438386 100644 --- a/docs/content/reference/dynamic-configuration/kubernetes-crd-rbac.yml +++ b/docs/content/reference/dynamic-configuration/kubernetes-crd-rbac.yml @@ -15,14 +15,6 @@ rules: - get - list - watch - # The pods right is needed to inject k8s.pod.uid and k8s.pod.name OTel attributes. - # When OTel tracing/logs/metrics are not enabled, this rule is not needed. - - apiGroups: - - "" - resources: - - pods - verbs: - - get - apiGroups: - discovery.k8s.io resources: diff --git a/docs/content/reference/dynamic-configuration/kubernetes-gateway-rbac.yml b/docs/content/reference/dynamic-configuration/kubernetes-gateway-rbac.yml index c03dc0147..31b0837c8 100644 --- a/docs/content/reference/dynamic-configuration/kubernetes-gateway-rbac.yml +++ b/docs/content/reference/dynamic-configuration/kubernetes-gateway-rbac.yml @@ -11,14 +11,6 @@ rules: verbs: - list - watch - # The pods get right is needed to inject k8s.pod.uid and k8s.pod.name in OTel attributes. - # When OTel tracing/logs/metrics are not enabled, this rule is not needed. - - apiGroups: - - "" - resources: - - pods - verbs: - - get - apiGroups: - "" resources: diff --git a/docs/content/reference/dynamic-configuration/kubernetes-gateway-traefik-lb-svc.yml b/docs/content/reference/dynamic-configuration/kubernetes-gateway-traefik-lb-svc.yml index 2d09a2a16..36c2ce27b 100644 --- a/docs/content/reference/dynamic-configuration/kubernetes-gateway-traefik-lb-svc.yml +++ b/docs/content/reference/dynamic-configuration/kubernetes-gateway-traefik-lb-svc.yml @@ -25,7 +25,7 @@ spec: serviceAccountName: traefik-controller containers: - name: traefik - image: traefik:v3.5 + image: traefik:v3.4 args: - --entryPoints.web.address=:80 - --entryPoints.websecure.address=:443 diff --git a/docs/content/reference/dynamic-configuration/kv-ref.md b/docs/content/reference/dynamic-configuration/kv-ref.md index d73df2bfa..65f3b8760 100644 --- a/docs/content/reference/dynamic-configuration/kv-ref.md +++ b/docs/content/reference/dynamic-configuration/kv-ref.md @@ -2,467 +2,459 @@ CODE GENERATED AUTOMATICALLY THIS FILE MUST NOT BE EDITED BY HAND --> - -| Key (Path) | Value | -|------------|-------| -| `traefik/http/middlewares/Middleware01/addPrefix/prefix` | `foobar` | -| `traefik/http/middlewares/Middleware02/basicAuth/headerField` | `foobar` | -| `traefik/http/middlewares/Middleware02/basicAuth/realm` | `foobar` | -| `traefik/http/middlewares/Middleware02/basicAuth/removeHeader` | `true` | -| `traefik/http/middlewares/Middleware02/basicAuth/users/0` | `foobar` | -| `traefik/http/middlewares/Middleware02/basicAuth/users/1` | `foobar` | -| `traefik/http/middlewares/Middleware02/basicAuth/usersFile` | `foobar` | -| `traefik/http/middlewares/Middleware03/buffering/maxRequestBodyBytes` | `42` | -| `traefik/http/middlewares/Middleware03/buffering/maxResponseBodyBytes` | `42` | -| `traefik/http/middlewares/Middleware03/buffering/memRequestBodyBytes` | `42` | -| `traefik/http/middlewares/Middleware03/buffering/memResponseBodyBytes` | `42` | -| `traefik/http/middlewares/Middleware03/buffering/retryExpression` | `foobar` | -| `traefik/http/middlewares/Middleware04/chain/middlewares/0` | `foobar` | -| `traefik/http/middlewares/Middleware04/chain/middlewares/1` | `foobar` | -| `traefik/http/middlewares/Middleware05/circuitBreaker/checkPeriod` | `42s` | -| `traefik/http/middlewares/Middleware05/circuitBreaker/expression` | `foobar` | -| `traefik/http/middlewares/Middleware05/circuitBreaker/fallbackDuration` | `42s` | -| `traefik/http/middlewares/Middleware05/circuitBreaker/recoveryDuration` | `42s` | -| `traefik/http/middlewares/Middleware05/circuitBreaker/responseCode` | `42` | -| `traefik/http/middlewares/Middleware06/compress/defaultEncoding` | `foobar` | -| `traefik/http/middlewares/Middleware06/compress/encodings/0` | `foobar` | -| `traefik/http/middlewares/Middleware06/compress/encodings/1` | `foobar` | -| `traefik/http/middlewares/Middleware06/compress/excludedContentTypes/0` | `foobar` | -| `traefik/http/middlewares/Middleware06/compress/excludedContentTypes/1` | `foobar` | -| `traefik/http/middlewares/Middleware06/compress/includedContentTypes/0` | `foobar` | -| `traefik/http/middlewares/Middleware06/compress/includedContentTypes/1` | `foobar` | -| `traefik/http/middlewares/Middleware06/compress/minResponseBodyBytes` | `42` | -| `traefik/http/middlewares/Middleware07/contentType/autoDetect` | `true` | -| `traefik/http/middlewares/Middleware08/digestAuth/headerField` | `foobar` | -| `traefik/http/middlewares/Middleware08/digestAuth/realm` | `foobar` | -| `traefik/http/middlewares/Middleware08/digestAuth/removeHeader` | `true` | -| `traefik/http/middlewares/Middleware08/digestAuth/users/0` | `foobar` | -| `traefik/http/middlewares/Middleware08/digestAuth/users/1` | `foobar` | -| `traefik/http/middlewares/Middleware08/digestAuth/usersFile` | `foobar` | -| `traefik/http/middlewares/Middleware09/errors/query` | `foobar` | -| `traefik/http/middlewares/Middleware09/errors/service` | `foobar` | -| `traefik/http/middlewares/Middleware09/errors/status/0` | `foobar` | -| `traefik/http/middlewares/Middleware09/errors/status/1` | `foobar` | -| `traefik/http/middlewares/Middleware09/errors/statusRewrites/name0` | `42` | -| `traefik/http/middlewares/Middleware09/errors/statusRewrites/name1` | `42` | -| `traefik/http/middlewares/Middleware10/forwardAuth/addAuthCookiesToResponse/0` | `foobar` | -| `traefik/http/middlewares/Middleware10/forwardAuth/addAuthCookiesToResponse/1` | `foobar` | -| `traefik/http/middlewares/Middleware10/forwardAuth/address` | `foobar` | -| `traefik/http/middlewares/Middleware10/forwardAuth/authRequestHeaders/0` | `foobar` | -| `traefik/http/middlewares/Middleware10/forwardAuth/authRequestHeaders/1` | `foobar` | -| `traefik/http/middlewares/Middleware10/forwardAuth/authResponseHeaders/0` | `foobar` | -| `traefik/http/middlewares/Middleware10/forwardAuth/authResponseHeaders/1` | `foobar` | -| `traefik/http/middlewares/Middleware10/forwardAuth/authResponseHeadersRegex` | `foobar` | -| `traefik/http/middlewares/Middleware10/forwardAuth/forwardBody` | `true` | -| `traefik/http/middlewares/Middleware10/forwardAuth/headerField` | `foobar` | -| `traefik/http/middlewares/Middleware10/forwardAuth/maxBodySize` | `42` | -| `traefik/http/middlewares/Middleware10/forwardAuth/preserveLocationHeader` | `true` | -| `traefik/http/middlewares/Middleware10/forwardAuth/preserveRequestMethod` | `true` | -| `traefik/http/middlewares/Middleware10/forwardAuth/tls/ca` | `foobar` | -| `traefik/http/middlewares/Middleware10/forwardAuth/tls/caOptional` | `true` | -| `traefik/http/middlewares/Middleware10/forwardAuth/tls/cert` | `foobar` | -| `traefik/http/middlewares/Middleware10/forwardAuth/tls/insecureSkipVerify` | `true` | -| `traefik/http/middlewares/Middleware10/forwardAuth/tls/key` | `foobar` | -| `traefik/http/middlewares/Middleware10/forwardAuth/trustForwardHeader` | `true` | -| `traefik/http/middlewares/Middleware11/grpcWeb/allowOrigins/0` | `foobar` | -| `traefik/http/middlewares/Middleware11/grpcWeb/allowOrigins/1` | `foobar` | -| `traefik/http/middlewares/Middleware12/headers/accessControlAllowCredentials` | `true` | -| `traefik/http/middlewares/Middleware12/headers/accessControlAllowHeaders/0` | `foobar` | -| `traefik/http/middlewares/Middleware12/headers/accessControlAllowHeaders/1` | `foobar` | -| `traefik/http/middlewares/Middleware12/headers/accessControlAllowMethods/0` | `foobar` | -| `traefik/http/middlewares/Middleware12/headers/accessControlAllowMethods/1` | `foobar` | -| `traefik/http/middlewares/Middleware12/headers/accessControlAllowOriginList/0` | `foobar` | -| `traefik/http/middlewares/Middleware12/headers/accessControlAllowOriginList/1` | `foobar` | -| `traefik/http/middlewares/Middleware12/headers/accessControlAllowOriginListRegex/0` | `foobar` | -| `traefik/http/middlewares/Middleware12/headers/accessControlAllowOriginListRegex/1` | `foobar` | -| `traefik/http/middlewares/Middleware12/headers/accessControlExposeHeaders/0` | `foobar` | -| `traefik/http/middlewares/Middleware12/headers/accessControlExposeHeaders/1` | `foobar` | -| `traefik/http/middlewares/Middleware12/headers/accessControlMaxAge` | `42` | -| `traefik/http/middlewares/Middleware12/headers/addVaryHeader` | `true` | -| `traefik/http/middlewares/Middleware12/headers/allowedHosts/0` | `foobar` | -| `traefik/http/middlewares/Middleware12/headers/allowedHosts/1` | `foobar` | -| `traefik/http/middlewares/Middleware12/headers/browserXssFilter` | `true` | -| `traefik/http/middlewares/Middleware12/headers/contentSecurityPolicy` | `foobar` | -| `traefik/http/middlewares/Middleware12/headers/contentSecurityPolicyReportOnly` | `foobar` | -| `traefik/http/middlewares/Middleware12/headers/contentTypeNosniff` | `true` | -| `traefik/http/middlewares/Middleware12/headers/customBrowserXSSValue` | `foobar` | -| `traefik/http/middlewares/Middleware12/headers/customFrameOptionsValue` | `foobar` | -| `traefik/http/middlewares/Middleware12/headers/customRequestHeaders/name0` | `foobar` | -| `traefik/http/middlewares/Middleware12/headers/customRequestHeaders/name1` | `foobar` | -| `traefik/http/middlewares/Middleware12/headers/customResponseHeaders/name0` | `foobar` | -| `traefik/http/middlewares/Middleware12/headers/customResponseHeaders/name1` | `foobar` | -| `traefik/http/middlewares/Middleware12/headers/featurePolicy` | `foobar` | -| `traefik/http/middlewares/Middleware12/headers/forceSTSHeader` | `true` | -| `traefik/http/middlewares/Middleware12/headers/frameDeny` | `true` | -| `traefik/http/middlewares/Middleware12/headers/hostsProxyHeaders/0` | `foobar` | -| `traefik/http/middlewares/Middleware12/headers/hostsProxyHeaders/1` | `foobar` | -| `traefik/http/middlewares/Middleware12/headers/isDevelopment` | `true` | -| `traefik/http/middlewares/Middleware12/headers/permissionsPolicy` | `foobar` | -| `traefik/http/middlewares/Middleware12/headers/publicKey` | `foobar` | -| `traefik/http/middlewares/Middleware12/headers/referrerPolicy` | `foobar` | -| `traefik/http/middlewares/Middleware12/headers/sslForceHost` | `true` | -| `traefik/http/middlewares/Middleware12/headers/sslHost` | `foobar` | -| `traefik/http/middlewares/Middleware12/headers/sslProxyHeaders/name0` | `foobar` | -| `traefik/http/middlewares/Middleware12/headers/sslProxyHeaders/name1` | `foobar` | -| `traefik/http/middlewares/Middleware12/headers/sslRedirect` | `true` | -| `traefik/http/middlewares/Middleware12/headers/sslTemporaryRedirect` | `true` | -| `traefik/http/middlewares/Middleware12/headers/stsIncludeSubdomains` | `true` | -| `traefik/http/middlewares/Middleware12/headers/stsPreload` | `true` | -| `traefik/http/middlewares/Middleware12/headers/stsSeconds` | `42` | -| `traefik/http/middlewares/Middleware13/ipAllowList/ipStrategy/depth` | `42` | -| `traefik/http/middlewares/Middleware13/ipAllowList/ipStrategy/excludedIPs/0` | `foobar` | -| `traefik/http/middlewares/Middleware13/ipAllowList/ipStrategy/excludedIPs/1` | `foobar` | -| `traefik/http/middlewares/Middleware13/ipAllowList/ipStrategy/ipv6Subnet` | `42` | -| `traefik/http/middlewares/Middleware13/ipAllowList/rejectStatusCode` | `42` | -| `traefik/http/middlewares/Middleware13/ipAllowList/sourceRange/0` | `foobar` | -| `traefik/http/middlewares/Middleware13/ipAllowList/sourceRange/1` | `foobar` | -| `traefik/http/middlewares/Middleware14/ipWhiteList/ipStrategy/depth` | `42` | -| `traefik/http/middlewares/Middleware14/ipWhiteList/ipStrategy/excludedIPs/0` | `foobar` | -| `traefik/http/middlewares/Middleware14/ipWhiteList/ipStrategy/excludedIPs/1` | `foobar` | -| `traefik/http/middlewares/Middleware14/ipWhiteList/ipStrategy/ipv6Subnet` | `42` | -| `traefik/http/middlewares/Middleware14/ipWhiteList/sourceRange/0` | `foobar` | -| `traefik/http/middlewares/Middleware14/ipWhiteList/sourceRange/1` | `foobar` | -| `traefik/http/middlewares/Middleware15/inFlightReq/amount` | `42` | -| `traefik/http/middlewares/Middleware15/inFlightReq/sourceCriterion/ipStrategy/depth` | `42` | -| `traefik/http/middlewares/Middleware15/inFlightReq/sourceCriterion/ipStrategy/excludedIPs/0` | `foobar` | -| `traefik/http/middlewares/Middleware15/inFlightReq/sourceCriterion/ipStrategy/excludedIPs/1` | `foobar` | -| `traefik/http/middlewares/Middleware15/inFlightReq/sourceCriterion/ipStrategy/ipv6Subnet` | `42` | -| `traefik/http/middlewares/Middleware15/inFlightReq/sourceCriterion/requestHeaderName` | `foobar` | -| `traefik/http/middlewares/Middleware15/inFlightReq/sourceCriterion/requestHost` | `true` | -| `traefik/http/middlewares/Middleware16/passTLSClientCert/info/issuer/commonName` | `true` | -| `traefik/http/middlewares/Middleware16/passTLSClientCert/info/issuer/country` | `true` | -| `traefik/http/middlewares/Middleware16/passTLSClientCert/info/issuer/domainComponent` | `true` | -| `traefik/http/middlewares/Middleware16/passTLSClientCert/info/issuer/locality` | `true` | -| `traefik/http/middlewares/Middleware16/passTLSClientCert/info/issuer/organization` | `true` | -| `traefik/http/middlewares/Middleware16/passTLSClientCert/info/issuer/province` | `true` | -| `traefik/http/middlewares/Middleware16/passTLSClientCert/info/issuer/serialNumber` | `true` | -| `traefik/http/middlewares/Middleware16/passTLSClientCert/info/notAfter` | `true` | -| `traefik/http/middlewares/Middleware16/passTLSClientCert/info/notBefore` | `true` | -| `traefik/http/middlewares/Middleware16/passTLSClientCert/info/sans` | `true` | -| `traefik/http/middlewares/Middleware16/passTLSClientCert/info/serialNumber` | `true` | -| `traefik/http/middlewares/Middleware16/passTLSClientCert/info/subject/commonName` | `true` | -| `traefik/http/middlewares/Middleware16/passTLSClientCert/info/subject/country` | `true` | -| `traefik/http/middlewares/Middleware16/passTLSClientCert/info/subject/domainComponent` | `true` | -| `traefik/http/middlewares/Middleware16/passTLSClientCert/info/subject/locality` | `true` | -| `traefik/http/middlewares/Middleware16/passTLSClientCert/info/subject/organization` | `true` | -| `traefik/http/middlewares/Middleware16/passTLSClientCert/info/subject/organizationalUnit` | `true` | -| `traefik/http/middlewares/Middleware16/passTLSClientCert/info/subject/province` | `true` | -| `traefik/http/middlewares/Middleware16/passTLSClientCert/info/subject/serialNumber` | `true` | -| `traefik/http/middlewares/Middleware16/passTLSClientCert/pem` | `true` | -| `traefik/http/middlewares/Middleware17/plugin/PluginConf0/name0` | `foobar` | -| `traefik/http/middlewares/Middleware17/plugin/PluginConf0/name1` | `foobar` | -| `traefik/http/middlewares/Middleware17/plugin/PluginConf1/name0` | `foobar` | -| `traefik/http/middlewares/Middleware17/plugin/PluginConf1/name1` | `foobar` | -| `traefik/http/middlewares/Middleware18/rateLimit/average` | `42` | -| `traefik/http/middlewares/Middleware18/rateLimit/burst` | `42` | -| `traefik/http/middlewares/Middleware18/rateLimit/period` | `42s` | -| `traefik/http/middlewares/Middleware18/rateLimit/redis/db` | `42` | -| `traefik/http/middlewares/Middleware18/rateLimit/redis/dialTimeout` | `42s` | -| `traefik/http/middlewares/Middleware18/rateLimit/redis/endpoints/0` | `foobar` | -| `traefik/http/middlewares/Middleware18/rateLimit/redis/endpoints/1` | `foobar` | -| `traefik/http/middlewares/Middleware18/rateLimit/redis/maxActiveConns` | `42` | -| `traefik/http/middlewares/Middleware18/rateLimit/redis/minIdleConns` | `42` | -| `traefik/http/middlewares/Middleware18/rateLimit/redis/password` | `foobar` | -| `traefik/http/middlewares/Middleware18/rateLimit/redis/poolSize` | `42` | -| `traefik/http/middlewares/Middleware18/rateLimit/redis/readTimeout` | `42s` | -| `traefik/http/middlewares/Middleware18/rateLimit/redis/tls/ca` | `foobar` | -| `traefik/http/middlewares/Middleware18/rateLimit/redis/tls/cert` | `foobar` | -| `traefik/http/middlewares/Middleware18/rateLimit/redis/tls/insecureSkipVerify` | `true` | -| `traefik/http/middlewares/Middleware18/rateLimit/redis/tls/key` | `foobar` | -| `traefik/http/middlewares/Middleware18/rateLimit/redis/username` | `foobar` | -| `traefik/http/middlewares/Middleware18/rateLimit/redis/writeTimeout` | `42s` | -| `traefik/http/middlewares/Middleware18/rateLimit/sourceCriterion/ipStrategy/depth` | `42` | -| `traefik/http/middlewares/Middleware18/rateLimit/sourceCriterion/ipStrategy/excludedIPs/0` | `foobar` | -| `traefik/http/middlewares/Middleware18/rateLimit/sourceCriterion/ipStrategy/excludedIPs/1` | `foobar` | -| `traefik/http/middlewares/Middleware18/rateLimit/sourceCriterion/ipStrategy/ipv6Subnet` | `42` | -| `traefik/http/middlewares/Middleware18/rateLimit/sourceCriterion/requestHeaderName` | `foobar` | -| `traefik/http/middlewares/Middleware18/rateLimit/sourceCriterion/requestHost` | `true` | -| `traefik/http/middlewares/Middleware19/redirectRegex/permanent` | `true` | -| `traefik/http/middlewares/Middleware19/redirectRegex/regex` | `foobar` | -| `traefik/http/middlewares/Middleware19/redirectRegex/replacement` | `foobar` | -| `traefik/http/middlewares/Middleware20/redirectScheme/permanent` | `true` | -| `traefik/http/middlewares/Middleware20/redirectScheme/port` | `foobar` | -| `traefik/http/middlewares/Middleware20/redirectScheme/scheme` | `foobar` | -| `traefik/http/middlewares/Middleware21/replacePath/path` | `foobar` | -| `traefik/http/middlewares/Middleware22/replacePathRegex/regex` | `foobar` | -| `traefik/http/middlewares/Middleware22/replacePathRegex/replacement` | `foobar` | -| `traefik/http/middlewares/Middleware23/retry/attempts` | `42` | -| `traefik/http/middlewares/Middleware23/retry/initialInterval` | `42s` | -| `traefik/http/middlewares/Middleware24/stripPrefix/forceSlash` | `true` | -| `traefik/http/middlewares/Middleware24/stripPrefix/prefixes/0` | `foobar` | -| `traefik/http/middlewares/Middleware24/stripPrefix/prefixes/1` | `foobar` | -| `traefik/http/middlewares/Middleware25/stripPrefixRegex/regex/0` | `foobar` | -| `traefik/http/middlewares/Middleware25/stripPrefixRegex/regex/1` | `foobar` | -| `traefik/http/routers/Router0/entryPoints/0` | `foobar` | -| `traefik/http/routers/Router0/entryPoints/1` | `foobar` | -| `traefik/http/routers/Router0/middlewares/0` | `foobar` | -| `traefik/http/routers/Router0/middlewares/1` | `foobar` | -| `traefik/http/routers/Router0/observability/accessLogs` | `true` | -| `traefik/http/routers/Router0/observability/metrics` | `true` | -| `traefik/http/routers/Router0/observability/traceVerbosity` | `foobar` | -| `traefik/http/routers/Router0/observability/tracing` | `true` | -| `traefik/http/routers/Router0/priority` | `42` | -| `traefik/http/routers/Router0/rule` | `foobar` | -| `traefik/http/routers/Router0/ruleSyntax` | `foobar` | -| `traefik/http/routers/Router0/service` | `foobar` | -| `traefik/http/routers/Router0/tls/certResolver` | `foobar` | -| `traefik/http/routers/Router0/tls/domains/0/main` | `foobar` | -| `traefik/http/routers/Router0/tls/domains/0/sans/0` | `foobar` | -| `traefik/http/routers/Router0/tls/domains/0/sans/1` | `foobar` | -| `traefik/http/routers/Router0/tls/domains/1/main` | `foobar` | -| `traefik/http/routers/Router0/tls/domains/1/sans/0` | `foobar` | -| `traefik/http/routers/Router0/tls/domains/1/sans/1` | `foobar` | -| `traefik/http/routers/Router0/tls/options` | `foobar` | -| `traefik/http/routers/Router1/entryPoints/0` | `foobar` | -| `traefik/http/routers/Router1/entryPoints/1` | `foobar` | -| `traefik/http/routers/Router1/middlewares/0` | `foobar` | -| `traefik/http/routers/Router1/middlewares/1` | `foobar` | -| `traefik/http/routers/Router1/observability/accessLogs` | `true` | -| `traefik/http/routers/Router1/observability/metrics` | `true` | -| `traefik/http/routers/Router1/observability/traceVerbosity` | `foobar` | -| `traefik/http/routers/Router1/observability/tracing` | `true` | -| `traefik/http/routers/Router1/priority` | `42` | -| `traefik/http/routers/Router1/rule` | `foobar` | -| `traefik/http/routers/Router1/ruleSyntax` | `foobar` | -| `traefik/http/routers/Router1/service` | `foobar` | -| `traefik/http/routers/Router1/tls/certResolver` | `foobar` | -| `traefik/http/routers/Router1/tls/domains/0/main` | `foobar` | -| `traefik/http/routers/Router1/tls/domains/0/sans/0` | `foobar` | -| `traefik/http/routers/Router1/tls/domains/0/sans/1` | `foobar` | -| `traefik/http/routers/Router1/tls/domains/1/main` | `foobar` | -| `traefik/http/routers/Router1/tls/domains/1/sans/0` | `foobar` | -| `traefik/http/routers/Router1/tls/domains/1/sans/1` | `foobar` | -| `traefik/http/routers/Router1/tls/options` | `foobar` | -| `traefik/http/serversTransports/ServersTransport0/certificates/0/certFile` | `foobar` | -| `traefik/http/serversTransports/ServersTransport0/certificates/0/keyFile` | `foobar` | -| `traefik/http/serversTransports/ServersTransport0/certificates/1/certFile` | `foobar` | -| `traefik/http/serversTransports/ServersTransport0/certificates/1/keyFile` | `foobar` | -| `traefik/http/serversTransports/ServersTransport0/disableHTTP2` | `true` | -| `traefik/http/serversTransports/ServersTransport0/forwardingTimeouts/dialTimeout` | `42s` | -| `traefik/http/serversTransports/ServersTransport0/forwardingTimeouts/idleConnTimeout` | `42s` | -| `traefik/http/serversTransports/ServersTransport0/forwardingTimeouts/pingTimeout` | `42s` | -| `traefik/http/serversTransports/ServersTransport0/forwardingTimeouts/readIdleTimeout` | `42s` | -| `traefik/http/serversTransports/ServersTransport0/forwardingTimeouts/responseHeaderTimeout` | `42s` | -| `traefik/http/serversTransports/ServersTransport0/insecureSkipVerify` | `true` | -| `traefik/http/serversTransports/ServersTransport0/maxIdleConnsPerHost` | `42` | -| `traefik/http/serversTransports/ServersTransport0/peerCertURI` | `foobar` | -| `traefik/http/serversTransports/ServersTransport0/rootCAs/0` | `foobar` | -| `traefik/http/serversTransports/ServersTransport0/rootCAs/1` | `foobar` | -| `traefik/http/serversTransports/ServersTransport0/serverName` | `foobar` | -| `traefik/http/serversTransports/ServersTransport0/spiffe/ids/0` | `foobar` | -| `traefik/http/serversTransports/ServersTransport0/spiffe/ids/1` | `foobar` | -| `traefik/http/serversTransports/ServersTransport0/spiffe/trustDomain` | `foobar` | -| `traefik/http/serversTransports/ServersTransport1/certificates/0/certFile` | `foobar` | -| `traefik/http/serversTransports/ServersTransport1/certificates/0/keyFile` | `foobar` | -| `traefik/http/serversTransports/ServersTransport1/certificates/1/certFile` | `foobar` | -| `traefik/http/serversTransports/ServersTransport1/certificates/1/keyFile` | `foobar` | -| `traefik/http/serversTransports/ServersTransport1/disableHTTP2` | `true` | -| `traefik/http/serversTransports/ServersTransport1/forwardingTimeouts/dialTimeout` | `42s` | -| `traefik/http/serversTransports/ServersTransport1/forwardingTimeouts/idleConnTimeout` | `42s` | -| `traefik/http/serversTransports/ServersTransport1/forwardingTimeouts/pingTimeout` | `42s` | -| `traefik/http/serversTransports/ServersTransport1/forwardingTimeouts/readIdleTimeout` | `42s` | -| `traefik/http/serversTransports/ServersTransport1/forwardingTimeouts/responseHeaderTimeout` | `42s` | -| `traefik/http/serversTransports/ServersTransport1/insecureSkipVerify` | `true` | -| `traefik/http/serversTransports/ServersTransport1/maxIdleConnsPerHost` | `42` | -| `traefik/http/serversTransports/ServersTransport1/peerCertURI` | `foobar` | -| `traefik/http/serversTransports/ServersTransport1/rootCAs/0` | `foobar` | -| `traefik/http/serversTransports/ServersTransport1/rootCAs/1` | `foobar` | -| `traefik/http/serversTransports/ServersTransport1/serverName` | `foobar` | -| `traefik/http/serversTransports/ServersTransport1/spiffe/ids/0` | `foobar` | -| `traefik/http/serversTransports/ServersTransport1/spiffe/ids/1` | `foobar` | -| `traefik/http/serversTransports/ServersTransport1/spiffe/trustDomain` | `foobar` | -| `traefik/http/services/Service01/failover/fallback` | `foobar` | -| `traefik/http/services/Service01/failover/healthCheck` | `` | -| `traefik/http/services/Service01/failover/service` | `foobar` | -| `traefik/http/services/Service02/loadBalancer/healthCheck/followRedirects` | `true` | -| `traefik/http/services/Service02/loadBalancer/healthCheck/headers/name0` | `foobar` | -| `traefik/http/services/Service02/loadBalancer/healthCheck/headers/name1` | `foobar` | -| `traefik/http/services/Service02/loadBalancer/healthCheck/hostname` | `foobar` | -| `traefik/http/services/Service02/loadBalancer/healthCheck/interval` | `42s` | -| `traefik/http/services/Service02/loadBalancer/healthCheck/method` | `foobar` | -| `traefik/http/services/Service02/loadBalancer/healthCheck/mode` | `foobar` | -| `traefik/http/services/Service02/loadBalancer/healthCheck/path` | `foobar` | -| `traefik/http/services/Service02/loadBalancer/healthCheck/port` | `42` | -| `traefik/http/services/Service02/loadBalancer/healthCheck/scheme` | `foobar` | -| `traefik/http/services/Service02/loadBalancer/healthCheck/status` | `42` | -| `traefik/http/services/Service02/loadBalancer/healthCheck/timeout` | `42s` | -| `traefik/http/services/Service02/loadBalancer/healthCheck/unhealthyInterval` | `42s` | -| `traefik/http/services/Service02/loadBalancer/passHostHeader` | `true` | -| `traefik/http/services/Service02/loadBalancer/responseForwarding/flushInterval` | `42s` | -| `traefik/http/services/Service02/loadBalancer/servers/0/preservePath` | `true` | -| `traefik/http/services/Service02/loadBalancer/servers/0/url` | `foobar` | -| `traefik/http/services/Service02/loadBalancer/servers/0/weight` | `42` | -| `traefik/http/services/Service02/loadBalancer/servers/1/preservePath` | `true` | -| `traefik/http/services/Service02/loadBalancer/servers/1/url` | `foobar` | -| `traefik/http/services/Service02/loadBalancer/servers/1/weight` | `42` | -| `traefik/http/services/Service02/loadBalancer/serversTransport` | `foobar` | -| `traefik/http/services/Service02/loadBalancer/sticky/cookie/domain` | `foobar` | -| `traefik/http/services/Service02/loadBalancer/sticky/cookie/httpOnly` | `true` | -| `traefik/http/services/Service02/loadBalancer/sticky/cookie/maxAge` | `42` | -| `traefik/http/services/Service02/loadBalancer/sticky/cookie/name` | `foobar` | -| `traefik/http/services/Service02/loadBalancer/sticky/cookie/path` | `foobar` | -| `traefik/http/services/Service02/loadBalancer/sticky/cookie/sameSite` | `foobar` | -| `traefik/http/services/Service02/loadBalancer/sticky/cookie/secure` | `true` | -| `traefik/http/services/Service02/loadBalancer/strategy` | `foobar` | -| `traefik/http/services/Service03/mirroring/healthCheck` | `` | -| `traefik/http/services/Service03/mirroring/maxBodySize` | `42` | -| `traefik/http/services/Service03/mirroring/mirrorBody` | `true` | -| `traefik/http/services/Service03/mirroring/mirrors/0/name` | `foobar` | -| `traefik/http/services/Service03/mirroring/mirrors/0/percent` | `42` | -| `traefik/http/services/Service03/mirroring/mirrors/1/name` | `foobar` | -| `traefik/http/services/Service03/mirroring/mirrors/1/percent` | `42` | -| `traefik/http/services/Service03/mirroring/service` | `foobar` | -| `traefik/http/services/Service04/weighted/healthCheck` | `` | -| `traefik/http/services/Service04/weighted/services/0/name` | `foobar` | -| `traefik/http/services/Service04/weighted/services/0/weight` | `42` | -| `traefik/http/services/Service04/weighted/services/1/name` | `foobar` | -| `traefik/http/services/Service04/weighted/services/1/weight` | `42` | -| `traefik/http/services/Service04/weighted/sticky/cookie/domain` | `foobar` | -| `traefik/http/services/Service04/weighted/sticky/cookie/httpOnly` | `true` | -| `traefik/http/services/Service04/weighted/sticky/cookie/maxAge` | `42` | -| `traefik/http/services/Service04/weighted/sticky/cookie/name` | `foobar` | -| `traefik/http/services/Service04/weighted/sticky/cookie/path` | `foobar` | -| `traefik/http/services/Service04/weighted/sticky/cookie/sameSite` | `foobar` | -| `traefik/http/services/Service04/weighted/sticky/cookie/secure` | `true` | -| `traefik/tcp/middlewares/TCPMiddleware01/ipAllowList/sourceRange/0` | `foobar` | -| `traefik/tcp/middlewares/TCPMiddleware01/ipAllowList/sourceRange/1` | `foobar` | -| `traefik/tcp/middlewares/TCPMiddleware02/ipWhiteList/sourceRange/0` | `foobar` | -| `traefik/tcp/middlewares/TCPMiddleware02/ipWhiteList/sourceRange/1` | `foobar` | -| `traefik/tcp/middlewares/TCPMiddleware03/inFlightConn/amount` | `42` | -| `traefik/tcp/routers/TCPRouter0/entryPoints/0` | `foobar` | -| `traefik/tcp/routers/TCPRouter0/entryPoints/1` | `foobar` | -| `traefik/tcp/routers/TCPRouter0/middlewares/0` | `foobar` | -| `traefik/tcp/routers/TCPRouter0/middlewares/1` | `foobar` | -| `traefik/tcp/routers/TCPRouter0/priority` | `42` | -| `traefik/tcp/routers/TCPRouter0/rule` | `foobar` | -| `traefik/tcp/routers/TCPRouter0/ruleSyntax` | `foobar` | -| `traefik/tcp/routers/TCPRouter0/service` | `foobar` | -| `traefik/tcp/routers/TCPRouter0/tls/certResolver` | `foobar` | -| `traefik/tcp/routers/TCPRouter0/tls/domains/0/main` | `foobar` | -| `traefik/tcp/routers/TCPRouter0/tls/domains/0/sans/0` | `foobar` | -| `traefik/tcp/routers/TCPRouter0/tls/domains/0/sans/1` | `foobar` | -| `traefik/tcp/routers/TCPRouter0/tls/domains/1/main` | `foobar` | -| `traefik/tcp/routers/TCPRouter0/tls/domains/1/sans/0` | `foobar` | -| `traefik/tcp/routers/TCPRouter0/tls/domains/1/sans/1` | `foobar` | -| `traefik/tcp/routers/TCPRouter0/tls/options` | `foobar` | -| `traefik/tcp/routers/TCPRouter0/tls/passthrough` | `true` | -| `traefik/tcp/routers/TCPRouter1/entryPoints/0` | `foobar` | -| `traefik/tcp/routers/TCPRouter1/entryPoints/1` | `foobar` | -| `traefik/tcp/routers/TCPRouter1/middlewares/0` | `foobar` | -| `traefik/tcp/routers/TCPRouter1/middlewares/1` | `foobar` | -| `traefik/tcp/routers/TCPRouter1/priority` | `42` | -| `traefik/tcp/routers/TCPRouter1/rule` | `foobar` | -| `traefik/tcp/routers/TCPRouter1/ruleSyntax` | `foobar` | -| `traefik/tcp/routers/TCPRouter1/service` | `foobar` | -| `traefik/tcp/routers/TCPRouter1/tls/certResolver` | `foobar` | -| `traefik/tcp/routers/TCPRouter1/tls/domains/0/main` | `foobar` | -| `traefik/tcp/routers/TCPRouter1/tls/domains/0/sans/0` | `foobar` | -| `traefik/tcp/routers/TCPRouter1/tls/domains/0/sans/1` | `foobar` | -| `traefik/tcp/routers/TCPRouter1/tls/domains/1/main` | `foobar` | -| `traefik/tcp/routers/TCPRouter1/tls/domains/1/sans/0` | `foobar` | -| `traefik/tcp/routers/TCPRouter1/tls/domains/1/sans/1` | `foobar` | -| `traefik/tcp/routers/TCPRouter1/tls/options` | `foobar` | -| `traefik/tcp/routers/TCPRouter1/tls/passthrough` | `true` | -| `traefik/tcp/serversTransports/TCPServersTransport0/dialKeepAlive` | `42s` | -| `traefik/tcp/serversTransports/TCPServersTransport0/dialTimeout` | `42s` | -| `traefik/tcp/serversTransports/TCPServersTransport0/proxyProtocol/version` | `42` | -| `traefik/tcp/serversTransports/TCPServersTransport0/terminationDelay` | `42s` | -| `traefik/tcp/serversTransports/TCPServersTransport0/tls/certificates/0/certFile` | `foobar` | -| `traefik/tcp/serversTransports/TCPServersTransport0/tls/certificates/0/keyFile` | `foobar` | -| `traefik/tcp/serversTransports/TCPServersTransport0/tls/certificates/1/certFile` | `foobar` | -| `traefik/tcp/serversTransports/TCPServersTransport0/tls/certificates/1/keyFile` | `foobar` | -| `traefik/tcp/serversTransports/TCPServersTransport0/tls/insecureSkipVerify` | `true` | -| `traefik/tcp/serversTransports/TCPServersTransport0/tls/peerCertURI` | `foobar` | -| `traefik/tcp/serversTransports/TCPServersTransport0/tls/rootCAs/0` | `foobar` | -| `traefik/tcp/serversTransports/TCPServersTransport0/tls/rootCAs/1` | `foobar` | -| `traefik/tcp/serversTransports/TCPServersTransport0/tls/serverName` | `foobar` | -| `traefik/tcp/serversTransports/TCPServersTransport0/tls/spiffe/ids/0` | `foobar` | -| `traefik/tcp/serversTransports/TCPServersTransport0/tls/spiffe/ids/1` | `foobar` | -| `traefik/tcp/serversTransports/TCPServersTransport0/tls/spiffe/trustDomain` | `foobar` | -| `traefik/tcp/serversTransports/TCPServersTransport1/dialKeepAlive` | `42s` | -| `traefik/tcp/serversTransports/TCPServersTransport1/dialTimeout` | `42s` | -| `traefik/tcp/serversTransports/TCPServersTransport1/proxyProtocol/version` | `42` | -| `traefik/tcp/serversTransports/TCPServersTransport1/terminationDelay` | `42s` | -| `traefik/tcp/serversTransports/TCPServersTransport1/tls/certificates/0/certFile` | `foobar` | -| `traefik/tcp/serversTransports/TCPServersTransport1/tls/certificates/0/keyFile` | `foobar` | -| `traefik/tcp/serversTransports/TCPServersTransport1/tls/certificates/1/certFile` | `foobar` | -| `traefik/tcp/serversTransports/TCPServersTransport1/tls/certificates/1/keyFile` | `foobar` | -| `traefik/tcp/serversTransports/TCPServersTransport1/tls/insecureSkipVerify` | `true` | -| `traefik/tcp/serversTransports/TCPServersTransport1/tls/peerCertURI` | `foobar` | -| `traefik/tcp/serversTransports/TCPServersTransport1/tls/rootCAs/0` | `foobar` | -| `traefik/tcp/serversTransports/TCPServersTransport1/tls/rootCAs/1` | `foobar` | -| `traefik/tcp/serversTransports/TCPServersTransport1/tls/serverName` | `foobar` | -| `traefik/tcp/serversTransports/TCPServersTransport1/tls/spiffe/ids/0` | `foobar` | -| `traefik/tcp/serversTransports/TCPServersTransport1/tls/spiffe/ids/1` | `foobar` | -| `traefik/tcp/serversTransports/TCPServersTransport1/tls/spiffe/trustDomain` | `foobar` | -| `traefik/tcp/services/TCPService01/loadBalancer/proxyProtocol/version` | `42` | -| `traefik/tcp/services/TCPService01/loadBalancer/servers/0/address` | `foobar` | -| `traefik/tcp/services/TCPService01/loadBalancer/servers/0/tls` | `true` | -| `traefik/tcp/services/TCPService01/loadBalancer/servers/1/address` | `foobar` | -| `traefik/tcp/services/TCPService01/loadBalancer/servers/1/tls` | `true` | -| `traefik/tcp/services/TCPService01/loadBalancer/serversTransport` | `foobar` | -| `traefik/tcp/services/TCPService01/loadBalancer/terminationDelay` | `42` | -| `traefik/tcp/services/TCPService02/weighted/services/0/name` | `foobar` | -| `traefik/tcp/services/TCPService02/weighted/services/0/weight` | `42` | -| `traefik/tcp/services/TCPService02/weighted/services/1/name` | `foobar` | -| `traefik/tcp/services/TCPService02/weighted/services/1/weight` | `42` | -| `traefik/tls/certificates/0/certFile` | `foobar` | -| `traefik/tls/certificates/0/keyFile` | `foobar` | -| `traefik/tls/certificates/0/stores/0` | `foobar` | -| `traefik/tls/certificates/0/stores/1` | `foobar` | -| `traefik/tls/certificates/1/certFile` | `foobar` | -| `traefik/tls/certificates/1/keyFile` | `foobar` | -| `traefik/tls/certificates/1/stores/0` | `foobar` | -| `traefik/tls/certificates/1/stores/1` | `foobar` | -| `traefik/tls/options/Options0/alpnProtocols/0` | `foobar` | -| `traefik/tls/options/Options0/alpnProtocols/1` | `foobar` | -| `traefik/tls/options/Options0/cipherSuites/0` | `foobar` | -| `traefik/tls/options/Options0/cipherSuites/1` | `foobar` | -| `traefik/tls/options/Options0/clientAuth/caFiles/0` | `foobar` | -| `traefik/tls/options/Options0/clientAuth/caFiles/1` | `foobar` | -| `traefik/tls/options/Options0/clientAuth/clientAuthType` | `foobar` | -| `traefik/tls/options/Options0/curvePreferences/0` | `foobar` | -| `traefik/tls/options/Options0/curvePreferences/1` | `foobar` | -| `traefik/tls/options/Options0/disableSessionTickets` | `true` | -| `traefik/tls/options/Options0/maxVersion` | `foobar` | -| `traefik/tls/options/Options0/minVersion` | `foobar` | -| `traefik/tls/options/Options0/preferServerCipherSuites` | `true` | -| `traefik/tls/options/Options0/sniStrict` | `true` | -| `traefik/tls/options/Options1/alpnProtocols/0` | `foobar` | -| `traefik/tls/options/Options1/alpnProtocols/1` | `foobar` | -| `traefik/tls/options/Options1/cipherSuites/0` | `foobar` | -| `traefik/tls/options/Options1/cipherSuites/1` | `foobar` | -| `traefik/tls/options/Options1/clientAuth/caFiles/0` | `foobar` | -| `traefik/tls/options/Options1/clientAuth/caFiles/1` | `foobar` | -| `traefik/tls/options/Options1/clientAuth/clientAuthType` | `foobar` | -| `traefik/tls/options/Options1/curvePreferences/0` | `foobar` | -| `traefik/tls/options/Options1/curvePreferences/1` | `foobar` | -| `traefik/tls/options/Options1/disableSessionTickets` | `true` | -| `traefik/tls/options/Options1/maxVersion` | `foobar` | -| `traefik/tls/options/Options1/minVersion` | `foobar` | -| `traefik/tls/options/Options1/preferServerCipherSuites` | `true` | -| `traefik/tls/options/Options1/sniStrict` | `true` | -| `traefik/tls/stores/Store0/defaultCertificate/certFile` | `foobar` | -| `traefik/tls/stores/Store0/defaultCertificate/keyFile` | `foobar` | -| `traefik/tls/stores/Store0/defaultGeneratedCert/domain/main` | `foobar` | -| `traefik/tls/stores/Store0/defaultGeneratedCert/domain/sans/0` | `foobar` | -| `traefik/tls/stores/Store0/defaultGeneratedCert/domain/sans/1` | `foobar` | -| `traefik/tls/stores/Store0/defaultGeneratedCert/resolver` | `foobar` | -| `traefik/tls/stores/Store1/defaultCertificate/certFile` | `foobar` | -| `traefik/tls/stores/Store1/defaultCertificate/keyFile` | `foobar` | -| `traefik/tls/stores/Store1/defaultGeneratedCert/domain/main` | `foobar` | -| `traefik/tls/stores/Store1/defaultGeneratedCert/domain/sans/0` | `foobar` | -| `traefik/tls/stores/Store1/defaultGeneratedCert/domain/sans/1` | `foobar` | -| `traefik/tls/stores/Store1/defaultGeneratedCert/resolver` | `foobar` | -| `traefik/udp/routers/UDPRouter0/entryPoints/0` | `foobar` | -| `traefik/udp/routers/UDPRouter0/entryPoints/1` | `foobar` | -| `traefik/udp/routers/UDPRouter0/service` | `foobar` | -| `traefik/udp/routers/UDPRouter1/entryPoints/0` | `foobar` | -| `traefik/udp/routers/UDPRouter1/entryPoints/1` | `foobar` | -| `traefik/udp/routers/UDPRouter1/service` | `foobar` | -| `traefik/udp/services/UDPService01/loadBalancer/servers/0/address` | `foobar` | -| `traefik/udp/services/UDPService01/loadBalancer/servers/1/address` | `foobar` | -| `traefik/udp/services/UDPService02/weighted/services/0/name` | `foobar` | -| `traefik/udp/services/UDPService02/weighted/services/0/weight` | `42` | -| `traefik/udp/services/UDPService02/weighted/services/1/name` | `foobar` | -| `traefik/udp/services/UDPService02/weighted/services/1/weight` | `42` | +| `traefik/http/middlewares/Middleware01/addPrefix/prefix` | `foobar` | +| `traefik/http/middlewares/Middleware02/basicAuth/headerField` | `foobar` | +| `traefik/http/middlewares/Middleware02/basicAuth/realm` | `foobar` | +| `traefik/http/middlewares/Middleware02/basicAuth/removeHeader` | `true` | +| `traefik/http/middlewares/Middleware02/basicAuth/users/0` | `foobar` | +| `traefik/http/middlewares/Middleware02/basicAuth/users/1` | `foobar` | +| `traefik/http/middlewares/Middleware02/basicAuth/usersFile` | `foobar` | +| `traefik/http/middlewares/Middleware03/buffering/maxRequestBodyBytes` | `42` | +| `traefik/http/middlewares/Middleware03/buffering/maxResponseBodyBytes` | `42` | +| `traefik/http/middlewares/Middleware03/buffering/memRequestBodyBytes` | `42` | +| `traefik/http/middlewares/Middleware03/buffering/memResponseBodyBytes` | `42` | +| `traefik/http/middlewares/Middleware03/buffering/retryExpression` | `foobar` | +| `traefik/http/middlewares/Middleware04/chain/middlewares/0` | `foobar` | +| `traefik/http/middlewares/Middleware04/chain/middlewares/1` | `foobar` | +| `traefik/http/middlewares/Middleware05/circuitBreaker/checkPeriod` | `42s` | +| `traefik/http/middlewares/Middleware05/circuitBreaker/expression` | `foobar` | +| `traefik/http/middlewares/Middleware05/circuitBreaker/fallbackDuration` | `42s` | +| `traefik/http/middlewares/Middleware05/circuitBreaker/recoveryDuration` | `42s` | +| `traefik/http/middlewares/Middleware05/circuitBreaker/responseCode` | `42` | +| `traefik/http/middlewares/Middleware06/compress/defaultEncoding` | `foobar` | +| `traefik/http/middlewares/Middleware06/compress/encodings/0` | `foobar` | +| `traefik/http/middlewares/Middleware06/compress/encodings/1` | `foobar` | +| `traefik/http/middlewares/Middleware06/compress/excludedContentTypes/0` | `foobar` | +| `traefik/http/middlewares/Middleware06/compress/excludedContentTypes/1` | `foobar` | +| `traefik/http/middlewares/Middleware06/compress/includedContentTypes/0` | `foobar` | +| `traefik/http/middlewares/Middleware06/compress/includedContentTypes/1` | `foobar` | +| `traefik/http/middlewares/Middleware06/compress/minResponseBodyBytes` | `42` | +| `traefik/http/middlewares/Middleware07/contentType/autoDetect` | `true` | +| `traefik/http/middlewares/Middleware08/digestAuth/headerField` | `foobar` | +| `traefik/http/middlewares/Middleware08/digestAuth/realm` | `foobar` | +| `traefik/http/middlewares/Middleware08/digestAuth/removeHeader` | `true` | +| `traefik/http/middlewares/Middleware08/digestAuth/users/0` | `foobar` | +| `traefik/http/middlewares/Middleware08/digestAuth/users/1` | `foobar` | +| `traefik/http/middlewares/Middleware08/digestAuth/usersFile` | `foobar` | +| `traefik/http/middlewares/Middleware09/errors/query` | `foobar` | +| `traefik/http/middlewares/Middleware09/errors/service` | `foobar` | +| `traefik/http/middlewares/Middleware09/errors/status/0` | `foobar` | +| `traefik/http/middlewares/Middleware09/errors/status/1` | `foobar` | +| `traefik/http/middlewares/Middleware09/errors/statusRewrites/name0` | `42` | +| `traefik/http/middlewares/Middleware09/errors/statusRewrites/name1` | `42` | +| `traefik/http/middlewares/Middleware10/forwardAuth/addAuthCookiesToResponse/0` | `foobar` | +| `traefik/http/middlewares/Middleware10/forwardAuth/addAuthCookiesToResponse/1` | `foobar` | +| `traefik/http/middlewares/Middleware10/forwardAuth/address` | `foobar` | +| `traefik/http/middlewares/Middleware10/forwardAuth/authRequestHeaders/0` | `foobar` | +| `traefik/http/middlewares/Middleware10/forwardAuth/authRequestHeaders/1` | `foobar` | +| `traefik/http/middlewares/Middleware10/forwardAuth/authResponseHeaders/0` | `foobar` | +| `traefik/http/middlewares/Middleware10/forwardAuth/authResponseHeaders/1` | `foobar` | +| `traefik/http/middlewares/Middleware10/forwardAuth/authResponseHeadersRegex` | `foobar` | +| `traefik/http/middlewares/Middleware10/forwardAuth/forwardBody` | `true` | +| `traefik/http/middlewares/Middleware10/forwardAuth/headerField` | `foobar` | +| `traefik/http/middlewares/Middleware10/forwardAuth/maxBodySize` | `42` | +| `traefik/http/middlewares/Middleware10/forwardAuth/preserveLocationHeader` | `true` | +| `traefik/http/middlewares/Middleware10/forwardAuth/preserveRequestMethod` | `true` | +| `traefik/http/middlewares/Middleware10/forwardAuth/tls/ca` | `foobar` | +| `traefik/http/middlewares/Middleware10/forwardAuth/tls/caOptional` | `true` | +| `traefik/http/middlewares/Middleware10/forwardAuth/tls/cert` | `foobar` | +| `traefik/http/middlewares/Middleware10/forwardAuth/tls/insecureSkipVerify` | `true` | +| `traefik/http/middlewares/Middleware10/forwardAuth/tls/key` | `foobar` | +| `traefik/http/middlewares/Middleware10/forwardAuth/trustForwardHeader` | `true` | +| `traefik/http/middlewares/Middleware11/grpcWeb/allowOrigins/0` | `foobar` | +| `traefik/http/middlewares/Middleware11/grpcWeb/allowOrigins/1` | `foobar` | +| `traefik/http/middlewares/Middleware12/headers/accessControlAllowCredentials` | `true` | +| `traefik/http/middlewares/Middleware12/headers/accessControlAllowHeaders/0` | `foobar` | +| `traefik/http/middlewares/Middleware12/headers/accessControlAllowHeaders/1` | `foobar` | +| `traefik/http/middlewares/Middleware12/headers/accessControlAllowMethods/0` | `foobar` | +| `traefik/http/middlewares/Middleware12/headers/accessControlAllowMethods/1` | `foobar` | +| `traefik/http/middlewares/Middleware12/headers/accessControlAllowOriginList/0` | `foobar` | +| `traefik/http/middlewares/Middleware12/headers/accessControlAllowOriginList/1` | `foobar` | +| `traefik/http/middlewares/Middleware12/headers/accessControlAllowOriginListRegex/0` | `foobar` | +| `traefik/http/middlewares/Middleware12/headers/accessControlAllowOriginListRegex/1` | `foobar` | +| `traefik/http/middlewares/Middleware12/headers/accessControlExposeHeaders/0` | `foobar` | +| `traefik/http/middlewares/Middleware12/headers/accessControlExposeHeaders/1` | `foobar` | +| `traefik/http/middlewares/Middleware12/headers/accessControlMaxAge` | `42` | +| `traefik/http/middlewares/Middleware12/headers/addVaryHeader` | `true` | +| `traefik/http/middlewares/Middleware12/headers/allowedHosts/0` | `foobar` | +| `traefik/http/middlewares/Middleware12/headers/allowedHosts/1` | `foobar` | +| `traefik/http/middlewares/Middleware12/headers/browserXssFilter` | `true` | +| `traefik/http/middlewares/Middleware12/headers/contentSecurityPolicy` | `foobar` | +| `traefik/http/middlewares/Middleware12/headers/contentSecurityPolicyReportOnly` | `foobar` | +| `traefik/http/middlewares/Middleware12/headers/contentTypeNosniff` | `true` | +| `traefik/http/middlewares/Middleware12/headers/customBrowserXSSValue` | `foobar` | +| `traefik/http/middlewares/Middleware12/headers/customFrameOptionsValue` | `foobar` | +| `traefik/http/middlewares/Middleware12/headers/customRequestHeaders/name0` | `foobar` | +| `traefik/http/middlewares/Middleware12/headers/customRequestHeaders/name1` | `foobar` | +| `traefik/http/middlewares/Middleware12/headers/customResponseHeaders/name0` | `foobar` | +| `traefik/http/middlewares/Middleware12/headers/customResponseHeaders/name1` | `foobar` | +| `traefik/http/middlewares/Middleware12/headers/featurePolicy` | `foobar` | +| `traefik/http/middlewares/Middleware12/headers/forceSTSHeader` | `true` | +| `traefik/http/middlewares/Middleware12/headers/frameDeny` | `true` | +| `traefik/http/middlewares/Middleware12/headers/hostsProxyHeaders/0` | `foobar` | +| `traefik/http/middlewares/Middleware12/headers/hostsProxyHeaders/1` | `foobar` | +| `traefik/http/middlewares/Middleware12/headers/isDevelopment` | `true` | +| `traefik/http/middlewares/Middleware12/headers/permissionsPolicy` | `foobar` | +| `traefik/http/middlewares/Middleware12/headers/publicKey` | `foobar` | +| `traefik/http/middlewares/Middleware12/headers/referrerPolicy` | `foobar` | +| `traefik/http/middlewares/Middleware12/headers/sslForceHost` | `true` | +| `traefik/http/middlewares/Middleware12/headers/sslHost` | `foobar` | +| `traefik/http/middlewares/Middleware12/headers/sslProxyHeaders/name0` | `foobar` | +| `traefik/http/middlewares/Middleware12/headers/sslProxyHeaders/name1` | `foobar` | +| `traefik/http/middlewares/Middleware12/headers/sslRedirect` | `true` | +| `traefik/http/middlewares/Middleware12/headers/sslTemporaryRedirect` | `true` | +| `traefik/http/middlewares/Middleware12/headers/stsIncludeSubdomains` | `true` | +| `traefik/http/middlewares/Middleware12/headers/stsPreload` | `true` | +| `traefik/http/middlewares/Middleware12/headers/stsSeconds` | `42` | +| `traefik/http/middlewares/Middleware13/ipAllowList/ipStrategy/depth` | `42` | +| `traefik/http/middlewares/Middleware13/ipAllowList/ipStrategy/excludedIPs/0` | `foobar` | +| `traefik/http/middlewares/Middleware13/ipAllowList/ipStrategy/excludedIPs/1` | `foobar` | +| `traefik/http/middlewares/Middleware13/ipAllowList/ipStrategy/ipv6Subnet` | `42` | +| `traefik/http/middlewares/Middleware13/ipAllowList/rejectStatusCode` | `42` | +| `traefik/http/middlewares/Middleware13/ipAllowList/sourceRange/0` | `foobar` | +| `traefik/http/middlewares/Middleware13/ipAllowList/sourceRange/1` | `foobar` | +| `traefik/http/middlewares/Middleware14/ipWhiteList/ipStrategy/depth` | `42` | +| `traefik/http/middlewares/Middleware14/ipWhiteList/ipStrategy/excludedIPs/0` | `foobar` | +| `traefik/http/middlewares/Middleware14/ipWhiteList/ipStrategy/excludedIPs/1` | `foobar` | +| `traefik/http/middlewares/Middleware14/ipWhiteList/ipStrategy/ipv6Subnet` | `42` | +| `traefik/http/middlewares/Middleware14/ipWhiteList/sourceRange/0` | `foobar` | +| `traefik/http/middlewares/Middleware14/ipWhiteList/sourceRange/1` | `foobar` | +| `traefik/http/middlewares/Middleware15/inFlightReq/amount` | `42` | +| `traefik/http/middlewares/Middleware15/inFlightReq/sourceCriterion/ipStrategy/depth` | `42` | +| `traefik/http/middlewares/Middleware15/inFlightReq/sourceCriterion/ipStrategy/excludedIPs/0` | `foobar` | +| `traefik/http/middlewares/Middleware15/inFlightReq/sourceCriterion/ipStrategy/excludedIPs/1` | `foobar` | +| `traefik/http/middlewares/Middleware15/inFlightReq/sourceCriterion/ipStrategy/ipv6Subnet` | `42` | +| `traefik/http/middlewares/Middleware15/inFlightReq/sourceCriterion/requestHeaderName` | `foobar` | +| `traefik/http/middlewares/Middleware15/inFlightReq/sourceCriterion/requestHost` | `true` | +| `traefik/http/middlewares/Middleware16/passTLSClientCert/info/issuer/commonName` | `true` | +| `traefik/http/middlewares/Middleware16/passTLSClientCert/info/issuer/country` | `true` | +| `traefik/http/middlewares/Middleware16/passTLSClientCert/info/issuer/domainComponent` | `true` | +| `traefik/http/middlewares/Middleware16/passTLSClientCert/info/issuer/locality` | `true` | +| `traefik/http/middlewares/Middleware16/passTLSClientCert/info/issuer/organization` | `true` | +| `traefik/http/middlewares/Middleware16/passTLSClientCert/info/issuer/province` | `true` | +| `traefik/http/middlewares/Middleware16/passTLSClientCert/info/issuer/serialNumber` | `true` | +| `traefik/http/middlewares/Middleware16/passTLSClientCert/info/notAfter` | `true` | +| `traefik/http/middlewares/Middleware16/passTLSClientCert/info/notBefore` | `true` | +| `traefik/http/middlewares/Middleware16/passTLSClientCert/info/sans` | `true` | +| `traefik/http/middlewares/Middleware16/passTLSClientCert/info/serialNumber` | `true` | +| `traefik/http/middlewares/Middleware16/passTLSClientCert/info/subject/commonName` | `true` | +| `traefik/http/middlewares/Middleware16/passTLSClientCert/info/subject/country` | `true` | +| `traefik/http/middlewares/Middleware16/passTLSClientCert/info/subject/domainComponent` | `true` | +| `traefik/http/middlewares/Middleware16/passTLSClientCert/info/subject/locality` | `true` | +| `traefik/http/middlewares/Middleware16/passTLSClientCert/info/subject/organization` | `true` | +| `traefik/http/middlewares/Middleware16/passTLSClientCert/info/subject/organizationalUnit` | `true` | +| `traefik/http/middlewares/Middleware16/passTLSClientCert/info/subject/province` | `true` | +| `traefik/http/middlewares/Middleware16/passTLSClientCert/info/subject/serialNumber` | `true` | +| `traefik/http/middlewares/Middleware16/passTLSClientCert/pem` | `true` | +| `traefik/http/middlewares/Middleware17/plugin/PluginConf0/name0` | `foobar` | +| `traefik/http/middlewares/Middleware17/plugin/PluginConf0/name1` | `foobar` | +| `traefik/http/middlewares/Middleware17/plugin/PluginConf1/name0` | `foobar` | +| `traefik/http/middlewares/Middleware17/plugin/PluginConf1/name1` | `foobar` | +| `traefik/http/middlewares/Middleware18/rateLimit/average` | `42` | +| `traefik/http/middlewares/Middleware18/rateLimit/burst` | `42` | +| `traefik/http/middlewares/Middleware18/rateLimit/period` | `42s` | +| `traefik/http/middlewares/Middleware18/rateLimit/redis/db` | `42` | +| `traefik/http/middlewares/Middleware18/rateLimit/redis/dialTimeout` | `42s` | +| `traefik/http/middlewares/Middleware18/rateLimit/redis/endpoints/0` | `foobar` | +| `traefik/http/middlewares/Middleware18/rateLimit/redis/endpoints/1` | `foobar` | +| `traefik/http/middlewares/Middleware18/rateLimit/redis/maxActiveConns` | `42` | +| `traefik/http/middlewares/Middleware18/rateLimit/redis/minIdleConns` | `42` | +| `traefik/http/middlewares/Middleware18/rateLimit/redis/password` | `foobar` | +| `traefik/http/middlewares/Middleware18/rateLimit/redis/poolSize` | `42` | +| `traefik/http/middlewares/Middleware18/rateLimit/redis/readTimeout` | `42s` | +| `traefik/http/middlewares/Middleware18/rateLimit/redis/tls/ca` | `foobar` | +| `traefik/http/middlewares/Middleware18/rateLimit/redis/tls/cert` | `foobar` | +| `traefik/http/middlewares/Middleware18/rateLimit/redis/tls/insecureSkipVerify` | `true` | +| `traefik/http/middlewares/Middleware18/rateLimit/redis/tls/key` | `foobar` | +| `traefik/http/middlewares/Middleware18/rateLimit/redis/username` | `foobar` | +| `traefik/http/middlewares/Middleware18/rateLimit/redis/writeTimeout` | `42s` | +| `traefik/http/middlewares/Middleware18/rateLimit/sourceCriterion/ipStrategy/depth` | `42` | +| `traefik/http/middlewares/Middleware18/rateLimit/sourceCriterion/ipStrategy/excludedIPs/0` | `foobar` | +| `traefik/http/middlewares/Middleware18/rateLimit/sourceCriterion/ipStrategy/excludedIPs/1` | `foobar` | +| `traefik/http/middlewares/Middleware18/rateLimit/sourceCriterion/ipStrategy/ipv6Subnet` | `42` | +| `traefik/http/middlewares/Middleware18/rateLimit/sourceCriterion/requestHeaderName` | `foobar` | +| `traefik/http/middlewares/Middleware18/rateLimit/sourceCriterion/requestHost` | `true` | +| `traefik/http/middlewares/Middleware19/redirectRegex/permanent` | `true` | +| `traefik/http/middlewares/Middleware19/redirectRegex/regex` | `foobar` | +| `traefik/http/middlewares/Middleware19/redirectRegex/replacement` | `foobar` | +| `traefik/http/middlewares/Middleware20/redirectScheme/permanent` | `true` | +| `traefik/http/middlewares/Middleware20/redirectScheme/port` | `foobar` | +| `traefik/http/middlewares/Middleware20/redirectScheme/scheme` | `foobar` | +| `traefik/http/middlewares/Middleware21/replacePath/path` | `foobar` | +| `traefik/http/middlewares/Middleware22/replacePathRegex/regex` | `foobar` | +| `traefik/http/middlewares/Middleware22/replacePathRegex/replacement` | `foobar` | +| `traefik/http/middlewares/Middleware23/retry/attempts` | `42` | +| `traefik/http/middlewares/Middleware23/retry/initialInterval` | `42s` | +| `traefik/http/middlewares/Middleware24/stripPrefix/forceSlash` | `true` | +| `traefik/http/middlewares/Middleware24/stripPrefix/prefixes/0` | `foobar` | +| `traefik/http/middlewares/Middleware24/stripPrefix/prefixes/1` | `foobar` | +| `traefik/http/middlewares/Middleware25/stripPrefixRegex/regex/0` | `foobar` | +| `traefik/http/middlewares/Middleware25/stripPrefixRegex/regex/1` | `foobar` | +| `traefik/http/routers/Router0/entryPoints/0` | `foobar` | +| `traefik/http/routers/Router0/entryPoints/1` | `foobar` | +| `traefik/http/routers/Router0/middlewares/0` | `foobar` | +| `traefik/http/routers/Router0/middlewares/1` | `foobar` | +| `traefik/http/routers/Router0/observability/accessLogs` | `true` | +| `traefik/http/routers/Router0/observability/metrics` | `true` | +| `traefik/http/routers/Router0/observability/tracing` | `true` | +| `traefik/http/routers/Router0/priority` | `42` | +| `traefik/http/routers/Router0/rule` | `foobar` | +| `traefik/http/routers/Router0/ruleSyntax` | `foobar` | +| `traefik/http/routers/Router0/service` | `foobar` | +| `traefik/http/routers/Router0/tls/certResolver` | `foobar` | +| `traefik/http/routers/Router0/tls/domains/0/main` | `foobar` | +| `traefik/http/routers/Router0/tls/domains/0/sans/0` | `foobar` | +| `traefik/http/routers/Router0/tls/domains/0/sans/1` | `foobar` | +| `traefik/http/routers/Router0/tls/domains/1/main` | `foobar` | +| `traefik/http/routers/Router0/tls/domains/1/sans/0` | `foobar` | +| `traefik/http/routers/Router0/tls/domains/1/sans/1` | `foobar` | +| `traefik/http/routers/Router0/tls/options` | `foobar` | +| `traefik/http/routers/Router1/entryPoints/0` | `foobar` | +| `traefik/http/routers/Router1/entryPoints/1` | `foobar` | +| `traefik/http/routers/Router1/middlewares/0` | `foobar` | +| `traefik/http/routers/Router1/middlewares/1` | `foobar` | +| `traefik/http/routers/Router1/observability/accessLogs` | `true` | +| `traefik/http/routers/Router1/observability/metrics` | `true` | +| `traefik/http/routers/Router1/observability/tracing` | `true` | +| `traefik/http/routers/Router1/priority` | `42` | +| `traefik/http/routers/Router1/rule` | `foobar` | +| `traefik/http/routers/Router1/ruleSyntax` | `foobar` | +| `traefik/http/routers/Router1/service` | `foobar` | +| `traefik/http/routers/Router1/tls/certResolver` | `foobar` | +| `traefik/http/routers/Router1/tls/domains/0/main` | `foobar` | +| `traefik/http/routers/Router1/tls/domains/0/sans/0` | `foobar` | +| `traefik/http/routers/Router1/tls/domains/0/sans/1` | `foobar` | +| `traefik/http/routers/Router1/tls/domains/1/main` | `foobar` | +| `traefik/http/routers/Router1/tls/domains/1/sans/0` | `foobar` | +| `traefik/http/routers/Router1/tls/domains/1/sans/1` | `foobar` | +| `traefik/http/routers/Router1/tls/options` | `foobar` | +| `traefik/http/serversTransports/ServersTransport0/certificates/0/certFile` | `foobar` | +| `traefik/http/serversTransports/ServersTransport0/certificates/0/keyFile` | `foobar` | +| `traefik/http/serversTransports/ServersTransport0/certificates/1/certFile` | `foobar` | +| `traefik/http/serversTransports/ServersTransport0/certificates/1/keyFile` | `foobar` | +| `traefik/http/serversTransports/ServersTransport0/disableHTTP2` | `true` | +| `traefik/http/serversTransports/ServersTransport0/forwardingTimeouts/dialTimeout` | `42s` | +| `traefik/http/serversTransports/ServersTransport0/forwardingTimeouts/idleConnTimeout` | `42s` | +| `traefik/http/serversTransports/ServersTransport0/forwardingTimeouts/pingTimeout` | `42s` | +| `traefik/http/serversTransports/ServersTransport0/forwardingTimeouts/readIdleTimeout` | `42s` | +| `traefik/http/serversTransports/ServersTransport0/forwardingTimeouts/responseHeaderTimeout` | `42s` | +| `traefik/http/serversTransports/ServersTransport0/insecureSkipVerify` | `true` | +| `traefik/http/serversTransports/ServersTransport0/maxIdleConnsPerHost` | `42` | +| `traefik/http/serversTransports/ServersTransport0/peerCertURI` | `foobar` | +| `traefik/http/serversTransports/ServersTransport0/rootCAs/0` | `foobar` | +| `traefik/http/serversTransports/ServersTransport0/rootCAs/1` | `foobar` | +| `traefik/http/serversTransports/ServersTransport0/serverName` | `foobar` | +| `traefik/http/serversTransports/ServersTransport0/spiffe/ids/0` | `foobar` | +| `traefik/http/serversTransports/ServersTransport0/spiffe/ids/1` | `foobar` | +| `traefik/http/serversTransports/ServersTransport0/spiffe/trustDomain` | `foobar` | +| `traefik/http/serversTransports/ServersTransport1/certificates/0/certFile` | `foobar` | +| `traefik/http/serversTransports/ServersTransport1/certificates/0/keyFile` | `foobar` | +| `traefik/http/serversTransports/ServersTransport1/certificates/1/certFile` | `foobar` | +| `traefik/http/serversTransports/ServersTransport1/certificates/1/keyFile` | `foobar` | +| `traefik/http/serversTransports/ServersTransport1/disableHTTP2` | `true` | +| `traefik/http/serversTransports/ServersTransport1/forwardingTimeouts/dialTimeout` | `42s` | +| `traefik/http/serversTransports/ServersTransport1/forwardingTimeouts/idleConnTimeout` | `42s` | +| `traefik/http/serversTransports/ServersTransport1/forwardingTimeouts/pingTimeout` | `42s` | +| `traefik/http/serversTransports/ServersTransport1/forwardingTimeouts/readIdleTimeout` | `42s` | +| `traefik/http/serversTransports/ServersTransport1/forwardingTimeouts/responseHeaderTimeout` | `42s` | +| `traefik/http/serversTransports/ServersTransport1/insecureSkipVerify` | `true` | +| `traefik/http/serversTransports/ServersTransport1/maxIdleConnsPerHost` | `42` | +| `traefik/http/serversTransports/ServersTransport1/peerCertURI` | `foobar` | +| `traefik/http/serversTransports/ServersTransport1/rootCAs/0` | `foobar` | +| `traefik/http/serversTransports/ServersTransport1/rootCAs/1` | `foobar` | +| `traefik/http/serversTransports/ServersTransport1/serverName` | `foobar` | +| `traefik/http/serversTransports/ServersTransport1/spiffe/ids/0` | `foobar` | +| `traefik/http/serversTransports/ServersTransport1/spiffe/ids/1` | `foobar` | +| `traefik/http/serversTransports/ServersTransport1/spiffe/trustDomain` | `foobar` | +| `traefik/http/services/Service01/failover/fallback` | `foobar` | +| `traefik/http/services/Service01/failover/healthCheck` | `` | +| `traefik/http/services/Service01/failover/service` | `foobar` | +| `traefik/http/services/Service02/loadBalancer/healthCheck/followRedirects` | `true` | +| `traefik/http/services/Service02/loadBalancer/healthCheck/headers/name0` | `foobar` | +| `traefik/http/services/Service02/loadBalancer/healthCheck/headers/name1` | `foobar` | +| `traefik/http/services/Service02/loadBalancer/healthCheck/hostname` | `foobar` | +| `traefik/http/services/Service02/loadBalancer/healthCheck/interval` | `42s` | +| `traefik/http/services/Service02/loadBalancer/healthCheck/method` | `foobar` | +| `traefik/http/services/Service02/loadBalancer/healthCheck/mode` | `foobar` | +| `traefik/http/services/Service02/loadBalancer/healthCheck/path` | `foobar` | +| `traefik/http/services/Service02/loadBalancer/healthCheck/port` | `42` | +| `traefik/http/services/Service02/loadBalancer/healthCheck/scheme` | `foobar` | +| `traefik/http/services/Service02/loadBalancer/healthCheck/status` | `42` | +| `traefik/http/services/Service02/loadBalancer/healthCheck/timeout` | `42s` | +| `traefik/http/services/Service02/loadBalancer/passHostHeader` | `true` | +| `traefik/http/services/Service02/loadBalancer/responseForwarding/flushInterval` | `42s` | +| `traefik/http/services/Service02/loadBalancer/servers/0/preservePath` | `true` | +| `traefik/http/services/Service02/loadBalancer/servers/0/url` | `foobar` | +| `traefik/http/services/Service02/loadBalancer/servers/0/weight` | `42` | +| `traefik/http/services/Service02/loadBalancer/servers/1/preservePath` | `true` | +| `traefik/http/services/Service02/loadBalancer/servers/1/url` | `foobar` | +| `traefik/http/services/Service02/loadBalancer/servers/1/weight` | `42` | +| `traefik/http/services/Service02/loadBalancer/serversTransport` | `foobar` | +| `traefik/http/services/Service02/loadBalancer/sticky/cookie/domain` | `foobar` | +| `traefik/http/services/Service02/loadBalancer/sticky/cookie/httpOnly` | `true` | +| `traefik/http/services/Service02/loadBalancer/sticky/cookie/maxAge` | `42` | +| `traefik/http/services/Service02/loadBalancer/sticky/cookie/name` | `foobar` | +| `traefik/http/services/Service02/loadBalancer/sticky/cookie/path` | `foobar` | +| `traefik/http/services/Service02/loadBalancer/sticky/cookie/sameSite` | `foobar` | +| `traefik/http/services/Service02/loadBalancer/sticky/cookie/secure` | `true` | +| `traefik/http/services/Service02/loadBalancer/strategy` | `foobar` | +| `traefik/http/services/Service03/mirroring/healthCheck` | `` | +| `traefik/http/services/Service03/mirroring/maxBodySize` | `42` | +| `traefik/http/services/Service03/mirroring/mirrorBody` | `true` | +| `traefik/http/services/Service03/mirroring/mirrors/0/name` | `foobar` | +| `traefik/http/services/Service03/mirroring/mirrors/0/percent` | `42` | +| `traefik/http/services/Service03/mirroring/mirrors/1/name` | `foobar` | +| `traefik/http/services/Service03/mirroring/mirrors/1/percent` | `42` | +| `traefik/http/services/Service03/mirroring/service` | `foobar` | +| `traefik/http/services/Service04/weighted/healthCheck` | `` | +| `traefik/http/services/Service04/weighted/services/0/name` | `foobar` | +| `traefik/http/services/Service04/weighted/services/0/weight` | `42` | +| `traefik/http/services/Service04/weighted/services/1/name` | `foobar` | +| `traefik/http/services/Service04/weighted/services/1/weight` | `42` | +| `traefik/http/services/Service04/weighted/sticky/cookie/domain` | `foobar` | +| `traefik/http/services/Service04/weighted/sticky/cookie/httpOnly` | `true` | +| `traefik/http/services/Service04/weighted/sticky/cookie/maxAge` | `42` | +| `traefik/http/services/Service04/weighted/sticky/cookie/name` | `foobar` | +| `traefik/http/services/Service04/weighted/sticky/cookie/path` | `foobar` | +| `traefik/http/services/Service04/weighted/sticky/cookie/sameSite` | `foobar` | +| `traefik/http/services/Service04/weighted/sticky/cookie/secure` | `true` | +| `traefik/tcp/middlewares/TCPMiddleware01/ipAllowList/sourceRange/0` | `foobar` | +| `traefik/tcp/middlewares/TCPMiddleware01/ipAllowList/sourceRange/1` | `foobar` | +| `traefik/tcp/middlewares/TCPMiddleware02/ipWhiteList/sourceRange/0` | `foobar` | +| `traefik/tcp/middlewares/TCPMiddleware02/ipWhiteList/sourceRange/1` | `foobar` | +| `traefik/tcp/middlewares/TCPMiddleware03/inFlightConn/amount` | `42` | +| `traefik/tcp/routers/TCPRouter0/entryPoints/0` | `foobar` | +| `traefik/tcp/routers/TCPRouter0/entryPoints/1` | `foobar` | +| `traefik/tcp/routers/TCPRouter0/middlewares/0` | `foobar` | +| `traefik/tcp/routers/TCPRouter0/middlewares/1` | `foobar` | +| `traefik/tcp/routers/TCPRouter0/priority` | `42` | +| `traefik/tcp/routers/TCPRouter0/rule` | `foobar` | +| `traefik/tcp/routers/TCPRouter0/ruleSyntax` | `foobar` | +| `traefik/tcp/routers/TCPRouter0/service` | `foobar` | +| `traefik/tcp/routers/TCPRouter0/tls/certResolver` | `foobar` | +| `traefik/tcp/routers/TCPRouter0/tls/domains/0/main` | `foobar` | +| `traefik/tcp/routers/TCPRouter0/tls/domains/0/sans/0` | `foobar` | +| `traefik/tcp/routers/TCPRouter0/tls/domains/0/sans/1` | `foobar` | +| `traefik/tcp/routers/TCPRouter0/tls/domains/1/main` | `foobar` | +| `traefik/tcp/routers/TCPRouter0/tls/domains/1/sans/0` | `foobar` | +| `traefik/tcp/routers/TCPRouter0/tls/domains/1/sans/1` | `foobar` | +| `traefik/tcp/routers/TCPRouter0/tls/options` | `foobar` | +| `traefik/tcp/routers/TCPRouter0/tls/passthrough` | `true` | +| `traefik/tcp/routers/TCPRouter1/entryPoints/0` | `foobar` | +| `traefik/tcp/routers/TCPRouter1/entryPoints/1` | `foobar` | +| `traefik/tcp/routers/TCPRouter1/middlewares/0` | `foobar` | +| `traefik/tcp/routers/TCPRouter1/middlewares/1` | `foobar` | +| `traefik/tcp/routers/TCPRouter1/priority` | `42` | +| `traefik/tcp/routers/TCPRouter1/rule` | `foobar` | +| `traefik/tcp/routers/TCPRouter1/ruleSyntax` | `foobar` | +| `traefik/tcp/routers/TCPRouter1/service` | `foobar` | +| `traefik/tcp/routers/TCPRouter1/tls/certResolver` | `foobar` | +| `traefik/tcp/routers/TCPRouter1/tls/domains/0/main` | `foobar` | +| `traefik/tcp/routers/TCPRouter1/tls/domains/0/sans/0` | `foobar` | +| `traefik/tcp/routers/TCPRouter1/tls/domains/0/sans/1` | `foobar` | +| `traefik/tcp/routers/TCPRouter1/tls/domains/1/main` | `foobar` | +| `traefik/tcp/routers/TCPRouter1/tls/domains/1/sans/0` | `foobar` | +| `traefik/tcp/routers/TCPRouter1/tls/domains/1/sans/1` | `foobar` | +| `traefik/tcp/routers/TCPRouter1/tls/options` | `foobar` | +| `traefik/tcp/routers/TCPRouter1/tls/passthrough` | `true` | +| `traefik/tcp/serversTransports/TCPServersTransport0/dialKeepAlive` | `42s` | +| `traefik/tcp/serversTransports/TCPServersTransport0/dialTimeout` | `42s` | +| `traefik/tcp/serversTransports/TCPServersTransport0/terminationDelay` | `42s` | +| `traefik/tcp/serversTransports/TCPServersTransport0/tls/certificates/0/certFile` | `foobar` | +| `traefik/tcp/serversTransports/TCPServersTransport0/tls/certificates/0/keyFile` | `foobar` | +| `traefik/tcp/serversTransports/TCPServersTransport0/tls/certificates/1/certFile` | `foobar` | +| `traefik/tcp/serversTransports/TCPServersTransport0/tls/certificates/1/keyFile` | `foobar` | +| `traefik/tcp/serversTransports/TCPServersTransport0/tls/insecureSkipVerify` | `true` | +| `traefik/tcp/serversTransports/TCPServersTransport0/tls/peerCertURI` | `foobar` | +| `traefik/tcp/serversTransports/TCPServersTransport0/tls/rootCAs/0` | `foobar` | +| `traefik/tcp/serversTransports/TCPServersTransport0/tls/rootCAs/1` | `foobar` | +| `traefik/tcp/serversTransports/TCPServersTransport0/tls/serverName` | `foobar` | +| `traefik/tcp/serversTransports/TCPServersTransport0/tls/spiffe/ids/0` | `foobar` | +| `traefik/tcp/serversTransports/TCPServersTransport0/tls/spiffe/ids/1` | `foobar` | +| `traefik/tcp/serversTransports/TCPServersTransport0/tls/spiffe/trustDomain` | `foobar` | +| `traefik/tcp/serversTransports/TCPServersTransport1/dialKeepAlive` | `42s` | +| `traefik/tcp/serversTransports/TCPServersTransport1/dialTimeout` | `42s` | +| `traefik/tcp/serversTransports/TCPServersTransport1/terminationDelay` | `42s` | +| `traefik/tcp/serversTransports/TCPServersTransport1/tls/certificates/0/certFile` | `foobar` | +| `traefik/tcp/serversTransports/TCPServersTransport1/tls/certificates/0/keyFile` | `foobar` | +| `traefik/tcp/serversTransports/TCPServersTransport1/tls/certificates/1/certFile` | `foobar` | +| `traefik/tcp/serversTransports/TCPServersTransport1/tls/certificates/1/keyFile` | `foobar` | +| `traefik/tcp/serversTransports/TCPServersTransport1/tls/insecureSkipVerify` | `true` | +| `traefik/tcp/serversTransports/TCPServersTransport1/tls/peerCertURI` | `foobar` | +| `traefik/tcp/serversTransports/TCPServersTransport1/tls/rootCAs/0` | `foobar` | +| `traefik/tcp/serversTransports/TCPServersTransport1/tls/rootCAs/1` | `foobar` | +| `traefik/tcp/serversTransports/TCPServersTransport1/tls/serverName` | `foobar` | +| `traefik/tcp/serversTransports/TCPServersTransport1/tls/spiffe/ids/0` | `foobar` | +| `traefik/tcp/serversTransports/TCPServersTransport1/tls/spiffe/ids/1` | `foobar` | +| `traefik/tcp/serversTransports/TCPServersTransport1/tls/spiffe/trustDomain` | `foobar` | +| `traefik/tcp/services/TCPService01/loadBalancer/proxyProtocol/version` | `42` | +| `traefik/tcp/services/TCPService01/loadBalancer/servers/0/address` | `foobar` | +| `traefik/tcp/services/TCPService01/loadBalancer/servers/0/tls` | `true` | +| `traefik/tcp/services/TCPService01/loadBalancer/servers/1/address` | `foobar` | +| `traefik/tcp/services/TCPService01/loadBalancer/servers/1/tls` | `true` | +| `traefik/tcp/services/TCPService01/loadBalancer/serversTransport` | `foobar` | +| `traefik/tcp/services/TCPService01/loadBalancer/terminationDelay` | `42` | +| `traefik/tcp/services/TCPService02/weighted/services/0/name` | `foobar` | +| `traefik/tcp/services/TCPService02/weighted/services/0/weight` | `42` | +| `traefik/tcp/services/TCPService02/weighted/services/1/name` | `foobar` | +| `traefik/tcp/services/TCPService02/weighted/services/1/weight` | `42` | +| `traefik/tls/certificates/0/certFile` | `foobar` | +| `traefik/tls/certificates/0/keyFile` | `foobar` | +| `traefik/tls/certificates/0/stores/0` | `foobar` | +| `traefik/tls/certificates/0/stores/1` | `foobar` | +| `traefik/tls/certificates/1/certFile` | `foobar` | +| `traefik/tls/certificates/1/keyFile` | `foobar` | +| `traefik/tls/certificates/1/stores/0` | `foobar` | +| `traefik/tls/certificates/1/stores/1` | `foobar` | +| `traefik/tls/options/Options0/alpnProtocols/0` | `foobar` | +| `traefik/tls/options/Options0/alpnProtocols/1` | `foobar` | +| `traefik/tls/options/Options0/cipherSuites/0` | `foobar` | +| `traefik/tls/options/Options0/cipherSuites/1` | `foobar` | +| `traefik/tls/options/Options0/clientAuth/caFiles/0` | `foobar` | +| `traefik/tls/options/Options0/clientAuth/caFiles/1` | `foobar` | +| `traefik/tls/options/Options0/clientAuth/clientAuthType` | `foobar` | +| `traefik/tls/options/Options0/curvePreferences/0` | `foobar` | +| `traefik/tls/options/Options0/curvePreferences/1` | `foobar` | +| `traefik/tls/options/Options0/disableSessionTickets` | `true` | +| `traefik/tls/options/Options0/maxVersion` | `foobar` | +| `traefik/tls/options/Options0/minVersion` | `foobar` | +| `traefik/tls/options/Options0/preferServerCipherSuites` | `true` | +| `traefik/tls/options/Options0/sniStrict` | `true` | +| `traefik/tls/options/Options1/alpnProtocols/0` | `foobar` | +| `traefik/tls/options/Options1/alpnProtocols/1` | `foobar` | +| `traefik/tls/options/Options1/cipherSuites/0` | `foobar` | +| `traefik/tls/options/Options1/cipherSuites/1` | `foobar` | +| `traefik/tls/options/Options1/clientAuth/caFiles/0` | `foobar` | +| `traefik/tls/options/Options1/clientAuth/caFiles/1` | `foobar` | +| `traefik/tls/options/Options1/clientAuth/clientAuthType` | `foobar` | +| `traefik/tls/options/Options1/curvePreferences/0` | `foobar` | +| `traefik/tls/options/Options1/curvePreferences/1` | `foobar` | +| `traefik/tls/options/Options1/disableSessionTickets` | `true` | +| `traefik/tls/options/Options1/maxVersion` | `foobar` | +| `traefik/tls/options/Options1/minVersion` | `foobar` | +| `traefik/tls/options/Options1/preferServerCipherSuites` | `true` | +| `traefik/tls/options/Options1/sniStrict` | `true` | +| `traefik/tls/stores/Store0/defaultCertificate/certFile` | `foobar` | +| `traefik/tls/stores/Store0/defaultCertificate/keyFile` | `foobar` | +| `traefik/tls/stores/Store0/defaultGeneratedCert/domain/main` | `foobar` | +| `traefik/tls/stores/Store0/defaultGeneratedCert/domain/sans/0` | `foobar` | +| `traefik/tls/stores/Store0/defaultGeneratedCert/domain/sans/1` | `foobar` | +| `traefik/tls/stores/Store0/defaultGeneratedCert/resolver` | `foobar` | +| `traefik/tls/stores/Store1/defaultCertificate/certFile` | `foobar` | +| `traefik/tls/stores/Store1/defaultCertificate/keyFile` | `foobar` | +| `traefik/tls/stores/Store1/defaultGeneratedCert/domain/main` | `foobar` | +| `traefik/tls/stores/Store1/defaultGeneratedCert/domain/sans/0` | `foobar` | +| `traefik/tls/stores/Store1/defaultGeneratedCert/domain/sans/1` | `foobar` | +| `traefik/tls/stores/Store1/defaultGeneratedCert/resolver` | `foobar` | +| `traefik/udp/routers/UDPRouter0/entryPoints/0` | `foobar` | +| `traefik/udp/routers/UDPRouter0/entryPoints/1` | `foobar` | +| `traefik/udp/routers/UDPRouter0/service` | `foobar` | +| `traefik/udp/routers/UDPRouter1/entryPoints/0` | `foobar` | +| `traefik/udp/routers/UDPRouter1/entryPoints/1` | `foobar` | +| `traefik/udp/routers/UDPRouter1/service` | `foobar` | +| `traefik/udp/services/UDPService01/loadBalancer/servers/0/address` | `foobar` | +| `traefik/udp/services/UDPService01/loadBalancer/servers/1/address` | `foobar` | +| `traefik/udp/services/UDPService02/weighted/services/0/name` | `foobar` | +| `traefik/udp/services/UDPService02/weighted/services/0/weight` | `42` | +| `traefik/udp/services/UDPService02/weighted/services/1/name` | `foobar` | +| `traefik/udp/services/UDPService02/weighted/services/1/weight` | `42` | diff --git a/docs/content/reference/dynamic-configuration/kv.md b/docs/content/reference/dynamic-configuration/kv.md new file mode 100644 index 000000000..8db8415e5 --- /dev/null +++ b/docs/content/reference/dynamic-configuration/kv.md @@ -0,0 +1,13 @@ +--- +title: "Traefik Dynamic Configuration with KV stores" +description: "Read the technical documentation to learn the Traefik Dynamic Configuration with KV stores." +--- + +# KV Configuration Reference + +Dynamic configuration with KV stores. +{: .subtitle } + +| Key (Path) | Value | +|----------------------------------------------------------------------------------------------|-------------| +--8<-- "content/reference/dynamic-configuration/kv-ref.md" diff --git a/docs/content/reference/dynamic-configuration/nomad.md b/docs/content/reference/dynamic-configuration/nomad.md new file mode 100644 index 000000000..680e621b4 --- /dev/null +++ b/docs/content/reference/dynamic-configuration/nomad.md @@ -0,0 +1,16 @@ +--- +title: "Traefik Nomad Service Discovery Configuration Documentation" +description: "View the reference for performing dynamic configurations with Traefik Proxy and Nomad Service Discovery. Read the technical documentation." +--- + +# Nomad Service Discovery Configuration Reference + +Dynamic configuration with Nomad Service Discovery +{: .subtitle } + +The labels are case-insensitive. + +```yaml +--8<-- "content/reference/dynamic-configuration/nomad.yml" +--8<-- "content/reference/dynamic-configuration/docker-labels.yml" +``` diff --git a/docs/content/reference/dynamic-configuration/nomad.yml b/docs/content/reference/dynamic-configuration/nomad.yml new file mode 100644 index 000000000..23efc00c6 --- /dev/null +++ b/docs/content/reference/dynamic-configuration/nomad.yml @@ -0,0 +1 @@ +- "traefik.enable=true" diff --git a/docs/content/reference/dynamic-configuration/rancher.md b/docs/content/reference/dynamic-configuration/rancher.md new file mode 100644 index 000000000..e69de29bb diff --git a/docs/content/reference/dynamic-configuration/swarm.md b/docs/content/reference/dynamic-configuration/swarm.md new file mode 100644 index 000000000..67fec341c --- /dev/null +++ b/docs/content/reference/dynamic-configuration/swarm.md @@ -0,0 +1,17 @@ +--- +title: "Traefik Docker Swarm Configuration Documentation" +description: "Reference dynamic configuration with Docker Swarm labels in Traefik Proxy. Read the technical documentation." +--- + +# Docker Swarm Configuration Reference + +Dynamic configuration with Docker Labels +{: .subtitle } + +The labels are case-insensitive. + +```yaml +labels: + --8<-- "content/reference/dynamic-configuration/swarm.yml" + --8<-- "content/reference/dynamic-configuration/docker-labels.yml" +``` diff --git a/docs/content/reference/dynamic-configuration/swarm.yml b/docs/content/reference/dynamic-configuration/swarm.yml new file mode 100644 index 000000000..1b40b4483 --- /dev/null +++ b/docs/content/reference/dynamic-configuration/swarm.yml @@ -0,0 +1,3 @@ +- "traefik.enable=true" +- "traefik.swarm.network=foobar" +- "traefik.swarm.lbswarm=true" diff --git a/docs/content/reference/dynamic-configuration/traefik.containo.us_tlsoptions.yaml b/docs/content/reference/dynamic-configuration/traefik.containo.us_tlsoptions.yaml deleted file mode 100644 index 6c7fdc914..000000000 --- a/docs/content/reference/dynamic-configuration/traefik.containo.us_tlsoptions.yaml +++ /dev/null @@ -1,114 +0,0 @@ ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.16.1 - name: tlsoptions.traefik.containo.us -spec: - group: traefik.containo.us - names: - kind: TLSOption - listKind: TLSOptionList - plural: tlsoptions - singular: tlsoption - scope: Namespaced - versions: - - name: v1alpha1 - schema: - openAPIV3Schema: - description: |- - TLSOption is the CRD implementation of a Traefik TLS Option, allowing to configure some parameters of the TLS connection. - More info: https://doc.traefik.io/traefik/v2.11/https/tls/#tls-options - properties: - apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources - type: string - kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds - type: string - metadata: - type: object - spec: - description: TLSOptionSpec defines the desired state of a TLSOption. - properties: - alpnProtocols: - description: |- - ALPNProtocols defines the list of supported application level protocols for the TLS handshake, in order of preference. - More info: https://doc.traefik.io/traefik/v2.11/https/tls/#alpn-protocols - items: - type: string - type: array - cipherSuites: - description: |- - CipherSuites defines the list of supported cipher suites for TLS versions up to TLS 1.2. - More info: https://doc.traefik.io/traefik/v2.11/https/tls/#cipher-suites - items: - type: string - type: array - clientAuth: - description: ClientAuth defines the server's policy for TLS Client - Authentication. - properties: - clientAuthType: - description: ClientAuthType defines the client authentication - type to apply. - enum: - - NoClientCert - - RequestClientCert - - RequireAnyClientCert - - VerifyClientCertIfGiven - - RequireAndVerifyClientCert - type: string - secretNames: - description: SecretNames defines the names of the referenced Kubernetes - Secret storing certificate details. - items: - type: string - type: array - type: object - curvePreferences: - description: |- - CurvePreferences defines the preferred elliptic curves. - More info: https://doc.traefik.io/traefik/v2.11/https/tls/#curve-preferences - items: - type: string - type: array - maxVersion: - description: |- - MaxVersion defines the maximum TLS version that Traefik will accept. - Possible values: VersionTLS10, VersionTLS11, VersionTLS12, VersionTLS13. - Default: None. - type: string - minVersion: - description: |- - MinVersion defines the minimum TLS version that Traefik will accept. - Possible values: VersionTLS10, VersionTLS11, VersionTLS12, VersionTLS13. - Default: VersionTLS10. - type: string - preferServerCipherSuites: - description: |- - PreferServerCipherSuites defines whether the server chooses a cipher suite among his own instead of among the client's. - It is enabled automatically when minVersion or maxVersion is set. - Deprecated: https://github.com/golang/go/issues/45430 - type: boolean - sniStrict: - description: SniStrict defines whether Traefik allows connections - from clients connections that do not specify a server_name extension. - type: boolean - type: object - required: - - metadata - - spec - type: object - served: true - storage: true diff --git a/docs/content/reference/dynamic-configuration/traefik.io_ingressroutes.yaml b/docs/content/reference/dynamic-configuration/traefik.io_ingressroutes.yaml index 7c40866ea..160703eb8 100644 --- a/docs/content/reference/dynamic-configuration/traefik.io_ingressroutes.yaml +++ b/docs/content/reference/dynamic-configuration/traefik.io_ingressroutes.yaml @@ -43,7 +43,7 @@ spec: description: |- EntryPoints defines the list of entry point names to bind to. Entry points have to be configured in the static configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/install-configuration/entrypoints/ + More info: https://doc.traefik.io/traefik/v3.4/routing/entrypoints/ Default: all. items: type: string @@ -64,12 +64,12 @@ spec: match: description: |- Match defines the router's rule. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/routing/rules-and-priority/ + More info: https://doc.traefik.io/traefik/v3.4/routing/routers/#rule type: string middlewares: description: |- Middlewares defines the list of references to Middleware resources. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/kubernetes/crd/http/middleware/ + More info: https://doc.traefik.io/traefik/v3.4/routing/providers/kubernetes-crd/#kind-middleware items: description: MiddlewareRef is a reference to a Middleware resource. @@ -89,30 +89,19 @@ spec: observability: description: |- Observability defines the observability configuration for a router. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/routing/observability/ + More info: https://doc.traefik.io/traefik/v3.4/routing/routers/#observability properties: accessLogs: - description: AccessLogs enables access logs for this router. type: boolean metrics: - description: Metrics enables metrics for this router. type: boolean - traceVerbosity: - default: minimal - description: TraceVerbosity defines the verbosity level - of the tracing for this router. - enum: - - minimal - - detailed - type: string tracing: - description: Tracing enables tracing for this router. type: boolean type: object priority: description: |- Priority defines the router's priority. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/routing/rules-and-priority/#priority + More info: https://doc.traefik.io/traefik/v3.4/routing/routers/#priority maximum: 9223372036854775000 type: integer services: @@ -147,7 +136,7 @@ spec: - type: integer - type: string description: |- - Interval defines the frequency of the health check calls for healthy targets. + Interval defines the frequency of the health check calls. Default: 30s x-kubernetes-int-or-string: true method: @@ -183,15 +172,6 @@ spec: Timeout defines the maximum duration Traefik will wait for a health check request before considering the server unhealthy. Default: 5s x-kubernetes-int-or-string: true - unhealthyInterval: - anyOf: - - type: integer - - type: string - description: |- - UnhealthyInterval defines the frequency of the health check calls for unhealthy targets. - When UnhealthyInterval is not defined, it defaults to the Interval value. - Default: 30s - x-kubernetes-int-or-string: true type: object kind: description: Kind defines the kind of the Service. @@ -263,7 +243,7 @@ spec: sticky: description: |- Sticky defines the sticky sessions configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/load-balancing/service/#sticky-sessions + More info: https://doc.traefik.io/traefik/v3.4/routing/services/#sticky-sessions properties: cookie: description: Cookie defines the sticky cookie configuration. @@ -332,7 +312,7 @@ spec: syntax: description: |- Syntax defines the router's rule syntax. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/routing/rules-and-priority/#rulesyntax + More info: https://doc.traefik.io/traefik/v3.4/routing/routers/#rulesyntax Deprecated: Please do not use this field and rewrite the router rules to use the v3 syntax. type: string required: @@ -342,18 +322,18 @@ spec: tls: description: |- TLS defines the TLS configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/routing/router/#tls + More info: https://doc.traefik.io/traefik/v3.4/routing/routers/#tls properties: certResolver: description: |- CertResolver defines the name of the certificate resolver to use. Cert resolvers have to be configured in the static configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/install-configuration/tls/certificate-resolvers/acme/ + More info: https://doc.traefik.io/traefik/v3.4/https/acme/#certificate-resolvers type: string domains: description: |- Domains defines the list of domains that will be used to issue certificates. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/tls/tls-certificates/#domains + More info: https://doc.traefik.io/traefik/v3.4/routing/routers/#domains items: description: Domain holds a domain name with SANs. properties: @@ -372,17 +352,17 @@ spec: description: |- Options defines the reference to a TLSOption, that specifies the parameters of the TLS connection. If not defined, the `default` TLSOption is used. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/tls/tls-options/ + More info: https://doc.traefik.io/traefik/v3.4/https/tls/#tls-options properties: name: description: |- Name defines the name of the referenced TLSOption. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/kubernetes/crd/http/tlsoption/ + More info: https://doc.traefik.io/traefik/v3.4/routing/providers/kubernetes-crd/#kind-tlsoption type: string namespace: description: |- Namespace defines the namespace of the referenced TLSOption. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/kubernetes/crd/http/tlsoption/ + More info: https://doc.traefik.io/traefik/v3.4/routing/providers/kubernetes-crd/#kind-tlsoption type: string required: - name @@ -399,12 +379,12 @@ spec: name: description: |- Name defines the name of the referenced TLSStore. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/kubernetes/crd/http/tlsstore/ + More info: https://doc.traefik.io/traefik/v3.4/routing/providers/kubernetes-crd/#kind-tlsstore type: string namespace: description: |- Namespace defines the namespace of the referenced TLSStore. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/kubernetes/crd/http/tlsstore/ + More info: https://doc.traefik.io/traefik/v3.4/routing/providers/kubernetes-crd/#kind-tlsstore type: string required: - name diff --git a/docs/content/reference/dynamic-configuration/traefik.io_ingressroutetcps.yaml b/docs/content/reference/dynamic-configuration/traefik.io_ingressroutetcps.yaml index 37a02f2fb..a42968a0c 100644 --- a/docs/content/reference/dynamic-configuration/traefik.io_ingressroutetcps.yaml +++ b/docs/content/reference/dynamic-configuration/traefik.io_ingressroutetcps.yaml @@ -43,7 +43,7 @@ spec: description: |- EntryPoints defines the list of entry point names to bind to. Entry points have to be configured in the static configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/install-configuration/entrypoints/ + More info: https://doc.traefik.io/traefik/v3.4/routing/entrypoints/ Default: all. items: type: string @@ -56,7 +56,7 @@ spec: match: description: |- Match defines the router's rule. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/routing/rules-and-priority/ + More info: https://doc.traefik.io/traefik/v3.4/routing/routers/#rule_1 type: string middlewares: description: Middlewares defines the list of references to MiddlewareTCP @@ -80,7 +80,7 @@ spec: priority: description: |- Priority defines the router's priority. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/routing/rules-and-priority/#priority + More info: https://doc.traefik.io/traefik/v3.4/routing/routers/#priority_1 maximum: 9223372036854775000 type: integer services: @@ -122,8 +122,7 @@ spec: proxyProtocol: description: |- ProxyProtocol defines the PROXY protocol configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/service/#proxy-protocol - Deprecated: ProxyProtocol will not be supported in future APIVersions, please use ServersTransport to configure ProxyProtocol instead. + More info: https://doc.traefik.io/traefik/v3.4/routing/services/#proxy-protocol properties: version: description: Version defines the PROXY Protocol version @@ -164,7 +163,7 @@ spec: syntax: description: |- Syntax defines the router's rule syntax. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/routing/rules-and-priority/#rulesyntax + More info: https://doc.traefik.io/traefik/v3.4/routing/routers/#rulesyntax_1 Deprecated: Please do not use this field and rewrite the router rules to use the v3 syntax. enum: - v3 @@ -177,18 +176,18 @@ spec: tls: description: |- TLS defines the TLS configuration on a layer 4 / TCP Route. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/routing/router/#tls + More info: https://doc.traefik.io/traefik/v3.4/routing/routers/#tls_1 properties: certResolver: description: |- CertResolver defines the name of the certificate resolver to use. Cert resolvers have to be configured in the static configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/install-configuration/tls/certificate-resolvers/acme/ + More info: https://doc.traefik.io/traefik/v3.4/https/acme/#certificate-resolvers type: string domains: description: |- Domains defines the list of domains that will be used to issue certificates. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/tls/#domains + More info: https://doc.traefik.io/traefik/v3.4/routing/routers/#domains items: description: Domain holds a domain name with SANs. properties: @@ -207,7 +206,7 @@ spec: description: |- Options defines the reference to a TLSOption, that specifies the parameters of the TLS connection. If not defined, the `default` TLSOption is used. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/tls/#tls-options + More info: https://doc.traefik.io/traefik/v3.4/https/tls/#tls-options properties: name: description: Name defines the name of the referenced Traefik diff --git a/docs/content/reference/dynamic-configuration/traefik.io_ingressrouteudps.yaml b/docs/content/reference/dynamic-configuration/traefik.io_ingressrouteudps.yaml index dc08ce018..d23d7e851 100644 --- a/docs/content/reference/dynamic-configuration/traefik.io_ingressrouteudps.yaml +++ b/docs/content/reference/dynamic-configuration/traefik.io_ingressrouteudps.yaml @@ -43,7 +43,7 @@ spec: description: |- EntryPoints defines the list of entry point names to bind to. Entry points have to be configured in the static configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/install-configuration/entrypoints/ + More info: https://doc.traefik.io/traefik/v3.4/routing/entrypoints/ Default: all. items: type: string diff --git a/docs/content/reference/dynamic-configuration/traefik.io_middlewares.yaml b/docs/content/reference/dynamic-configuration/traefik.io_middlewares.yaml index 5321da0b0..f45b0b68f 100644 --- a/docs/content/reference/dynamic-configuration/traefik.io_middlewares.yaml +++ b/docs/content/reference/dynamic-configuration/traefik.io_middlewares.yaml @@ -19,7 +19,7 @@ spec: openAPIV3Schema: description: |- Middleware is the CRD implementation of a Traefik Middleware. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/overview/ + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/overview/ properties: apiVersion: description: |- @@ -45,7 +45,7 @@ spec: description: |- AddPrefix holds the add prefix middleware configuration. This middleware updates the path of a request before forwarding it. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/addprefix/ + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/addprefix/ properties: prefix: description: |- @@ -60,12 +60,12 @@ spec: description: |- BasicAuth holds the basic auth middleware configuration. This middleware restricts access to your services to known users. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/basicauth/ + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/basicauth/ properties: headerField: description: |- HeaderField defines a header field to store the authenticated user. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/basicauth/#headerfield + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/basicauth/#headerfield type: string realm: description: |- @@ -86,7 +86,7 @@ spec: description: |- Buffering holds the buffering middleware configuration. This middleware retries or limits the size of requests that can be forwarded to backends. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/buffering/#maxrequestbodybytes + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/buffering/#maxrequestbodybytes properties: maxRequestBodyBytes: description: |- @@ -118,14 +118,14 @@ spec: description: |- RetryExpression defines the retry conditions. It is a logical combination of functions with operators AND (&&) and OR (||). - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/buffering/#retryexpression + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/buffering/#retryexpression type: string type: object chain: description: |- Chain holds the configuration of the chain middleware. This middleware enables to define reusable combinations of other pieces of middleware. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/chain/ + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/chain/ properties: middlewares: description: Middlewares is the list of MiddlewareRef which composes @@ -188,7 +188,7 @@ spec: description: |- Compress holds the compress middleware configuration. This middleware compresses responses before sending them to the client, using gzip, brotli, or zstd compression. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/compress/ + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/compress/ properties: defaultEncoding: description: DefaultEncoding specifies the default encoding if @@ -238,12 +238,12 @@ spec: description: |- DigestAuth holds the digest auth middleware configuration. This middleware restricts access to your services to known users. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/digestauth/ + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/digestauth/ properties: headerField: description: |- HeaderField defines a header field to store the authenticated user. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/digestauth/#headerfield + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/basicauth/#headerfield type: string realm: description: |- @@ -263,7 +263,7 @@ spec: description: |- ErrorPage holds the custom error middleware configuration. This middleware returns a custom page in lieu of the default, according to configured ranges of HTTP Status codes. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/errorpages/ + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/errorpages/ properties: query: description: |- @@ -275,7 +275,7 @@ spec: service: description: |- Service defines the reference to a Kubernetes Service that will serve the error page. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/errorpages/#service + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/errorpages/#service properties: healthCheck: description: Healthcheck defines health checks for ExternalName @@ -301,7 +301,7 @@ spec: - type: integer - type: string description: |- - Interval defines the frequency of the health check calls for healthy targets. + Interval defines the frequency of the health check calls. Default: 30s x-kubernetes-int-or-string: true method: @@ -337,15 +337,6 @@ spec: Timeout defines the maximum duration Traefik will wait for a health check request before considering the server unhealthy. Default: 5s x-kubernetes-int-or-string: true - unhealthyInterval: - anyOf: - - type: integer - - type: string - description: |- - UnhealthyInterval defines the frequency of the health check calls for unhealthy targets. - When UnhealthyInterval is not defined, it defaults to the Interval value. - Default: 30s - x-kubernetes-int-or-string: true type: object kind: description: Kind defines the kind of the Service. @@ -417,7 +408,7 @@ spec: sticky: description: |- Sticky defines the sticky sessions configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/load-balancing/service/#sticky-sessions + More info: https://doc.traefik.io/traefik/v3.4/routing/services/#sticky-sessions properties: cookie: description: Cookie defines the sticky cookie configuration. @@ -504,7 +495,7 @@ spec: description: |- ForwardAuth holds the forward auth middleware configuration. This middleware delegates the request authentication to a Service. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/forwardauth/ + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/forwardauth/ properties: addAuthCookiesToResponse: description: AddAuthCookiesToResponse defines the list of cookies @@ -532,7 +523,7 @@ spec: authResponseHeadersRegex: description: |- AuthResponseHeadersRegex defines the regex to match headers to copy from the authentication server response and set on forwarded request, after stripping all headers that match the regex. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/forwardauth/#authresponseheadersregex + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/forwardauth/#authresponseheadersregex type: string forwardBody: description: ForwardBody defines whether to send the request body @@ -541,7 +532,7 @@ spec: headerField: description: |- HeaderField defines a header field to store the authenticated user. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/forwardauth/#headerfield + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/forwardauth/#headerfield type: string maxBodySize: description: MaxBodySize defines the maximum body size in bytes @@ -603,7 +594,7 @@ spec: description: |- Headers holds the headers middleware configuration. This middleware manages the requests and responses headers. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/headers/#customrequestheaders + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/headers/#customrequestheaders properties: accessControlAllowCredentials: description: AccessControlAllowCredentials defines whether the @@ -775,7 +766,7 @@ spec: description: |- InFlightReq holds the in-flight request middleware configuration. This middleware limits the number of requests being processed and served concurrently. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/inflightreq/ + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/inflightreq/ properties: amount: description: |- @@ -789,12 +780,12 @@ spec: SourceCriterion defines what criterion is used to group requests as originating from a common source. If several strategies are defined at the same time, an error will be raised. If none are set, the default is to use the requestHost. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/inflightreq/#sourcecriterion + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/inflightreq/#sourcecriterion properties: ipStrategy: description: |- IPStrategy holds the IP strategy configuration used by Traefik to determine the client IP. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/ipallowlist/#ipstrategy + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/ipallowlist/#ipstrategy properties: depth: description: Depth tells Traefik to use the X-Forwarded-For @@ -830,12 +821,12 @@ spec: description: |- IPAllowList holds the IP allowlist middleware configuration. This middleware limits allowed requests based on the client IP. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/ipallowlist/ + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/ipallowlist/ properties: ipStrategy: description: |- IPStrategy holds the IP strategy configuration used by Traefik to determine the client IP. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/ipallowlist/#ipstrategy + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/ipallowlist/#ipstrategy properties: depth: description: Depth tells Traefik to use the X-Forwarded-For @@ -873,7 +864,7 @@ spec: ipStrategy: description: |- IPStrategy holds the IP strategy configuration used by Traefik to determine the client IP. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/ipallowlist/#ipstrategy + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/ipallowlist/#ipstrategy properties: depth: description: Depth tells Traefik to use the X-Forwarded-For @@ -904,7 +895,7 @@ spec: description: |- PassTLSClientCert holds the pass TLS client cert middleware configuration. This middleware adds the selected data from the passed client TLS certificate to a header. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/passtlsclientcert/ + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/passtlsclientcert/ properties: info: description: Info selects the specific client certificate details @@ -1007,13 +998,13 @@ spec: x-kubernetes-preserve-unknown-fields: true description: |- Plugin defines the middleware plugin configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/overview/#community-middlewares + More info: https://doc.traefik.io/traefik/plugins/ type: object rateLimit: description: |- RateLimit holds the rate limit configuration. This middleware ensures that services will receive a fair amount of requests, and allows one to define what fair is. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/ratelimit/ + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/ratelimit/ properties: average: description: |- @@ -1132,7 +1123,7 @@ spec: ipStrategy: description: |- IPStrategy holds the IP strategy configuration used by Traefik to determine the client IP. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/ipallowlist/#ipstrategy + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/ipallowlist/#ipstrategy properties: depth: description: Depth tells Traefik to use the X-Forwarded-For @@ -1168,11 +1159,11 @@ spec: description: |- RedirectRegex holds the redirect regex middleware configuration. This middleware redirects a request using regex matching and replacement. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/redirectregex/#regex + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/redirectregex/#regex properties: permanent: description: Permanent defines whether the redirection is permanent - (308). + (301). type: boolean regex: description: Regex defines the regex used to match and capture @@ -1187,11 +1178,11 @@ spec: description: |- RedirectScheme holds the redirect scheme middleware configuration. This middleware redirects requests from a scheme/port to another. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/redirectscheme/ + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/redirectscheme/ properties: permanent: description: Permanent defines whether the redirection is permanent - (308). + (301). type: boolean port: description: Port defines the port of the new URL. @@ -1204,7 +1195,7 @@ spec: description: |- ReplacePath holds the replace path middleware configuration. This middleware replaces the path of the request URL and store the original path in an X-Replaced-Path header. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/replacepath/ + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/replacepath/ properties: path: description: Path defines the path to use as replacement in the @@ -1215,7 +1206,7 @@ spec: description: |- ReplacePathRegex holds the replace path regex middleware configuration. This middleware replaces the path of a URL using regex matching and replacement. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/replacepathregex/ + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/replacepathregex/ properties: regex: description: Regex defines the regular expression used to match @@ -1231,7 +1222,7 @@ spec: Retry holds the retry middleware configuration. This middleware reissues requests a given number of times to a backend server if that server does not reply. As soon as the server answers, the middleware stops retrying, regardless of the response status. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/retry/ + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/retry/ properties: attempts: description: Attempts defines how many times the request should @@ -1255,7 +1246,7 @@ spec: description: |- StripPrefix holds the strip prefix middleware configuration. This middleware removes the specified prefixes from the URL path. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/stripprefix/ + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/stripprefix/ properties: forceSlash: description: |- @@ -1274,7 +1265,7 @@ spec: description: |- StripPrefixRegex holds the strip prefix regex middleware configuration. This middleware removes the matching prefixes from the URL path. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/stripprefixregex/ + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/stripprefixregex/ properties: regex: description: Regex defines the regular expression to match the diff --git a/docs/content/reference/dynamic-configuration/traefik.io_middlewaretcps.yaml b/docs/content/reference/dynamic-configuration/traefik.io_middlewaretcps.yaml index 5f7604923..26d9ba184 100644 --- a/docs/content/reference/dynamic-configuration/traefik.io_middlewaretcps.yaml +++ b/docs/content/reference/dynamic-configuration/traefik.io_middlewaretcps.yaml @@ -19,7 +19,7 @@ spec: openAPIV3Schema: description: |- MiddlewareTCP is the CRD implementation of a Traefik TCP middleware. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/middlewares/overview/ + More info: https://doc.traefik.io/traefik/v3.4/middlewares/overview/ properties: apiVersion: description: |- @@ -56,7 +56,7 @@ spec: description: |- IPAllowList defines the IPAllowList middleware configuration. This middleware accepts/refuses connections based on the client IP. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/middlewares/ipallowlist/ + More info: https://doc.traefik.io/traefik/v3.4/middlewares/tcp/ipallowlist/ properties: sourceRange: description: SourceRange defines the allowed IPs (or ranges of @@ -70,7 +70,7 @@ spec: IPWhiteList defines the IPWhiteList middleware configuration. This middleware accepts/refuses connections based on the client IP. Deprecated: please use IPAllowList instead. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/middlewares/ipwhitelist/ + More info: https://doc.traefik.io/traefik/v3.4/middlewares/tcp/ipwhitelist/ properties: sourceRange: description: SourceRange defines the allowed IPs (or ranges of diff --git a/docs/content/reference/dynamic-configuration/traefik.io_serverstransports.yaml b/docs/content/reference/dynamic-configuration/traefik.io_serverstransports.yaml index 0828291ee..99d820da2 100644 --- a/docs/content/reference/dynamic-configuration/traefik.io_serverstransports.yaml +++ b/docs/content/reference/dynamic-configuration/traefik.io_serverstransports.yaml @@ -21,7 +21,7 @@ spec: ServersTransport is the CRD implementation of a ServersTransport. If no serversTransport is specified, the default@internal will be used. The default@internal serversTransport is created from the static configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/load-balancing/serverstransport/ + More info: https://doc.traefik.io/traefik/v3.4/routing/services/#serverstransport_1 properties: apiVersion: description: |- @@ -107,7 +107,7 @@ spec: maxIdleConnsPerHost: description: MaxIdleConnsPerHost controls the maximum idle (keep-alive) to keep per-host. - minimum: -1 + minimum: 0 type: integer peerCertURI: description: PeerCertURI defines the peer cert URI used to match against diff --git a/docs/content/reference/dynamic-configuration/traefik.io_serverstransporttcps.yaml b/docs/content/reference/dynamic-configuration/traefik.io_serverstransporttcps.yaml index f8be2b1d9..35f5dab93 100644 --- a/docs/content/reference/dynamic-configuration/traefik.io_serverstransporttcps.yaml +++ b/docs/content/reference/dynamic-configuration/traefik.io_serverstransporttcps.yaml @@ -21,7 +21,7 @@ spec: ServersTransportTCP is the CRD implementation of a TCPServersTransport. If no tcpServersTransport is specified, a default one named default@internal will be used. The default@internal tcpServersTransport can be configured in the static configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/serverstransport/ + More info: https://doc.traefik.io/traefik/v3.4/routing/services/#serverstransport_3 properties: apiVersion: description: |- @@ -63,15 +63,6 @@ spec: to a backend server can be established. pattern: ^([0-9]+(ns|us|Âĩs|ms|s|m|h)?)+$ x-kubernetes-int-or-string: true - proxyProtocol: - description: ProxyProtocol holds the PROXY Protocol configuration. - properties: - version: - description: Version defines the PROXY Protocol version to use. - maximum: 2 - minimum: 1 - type: integer - type: object terminationDelay: anyOf: - type: integer diff --git a/docs/content/reference/dynamic-configuration/traefik.io_tlsoptions.yaml b/docs/content/reference/dynamic-configuration/traefik.io_tlsoptions.yaml index c32974fae..758a0ab96 100644 --- a/docs/content/reference/dynamic-configuration/traefik.io_tlsoptions.yaml +++ b/docs/content/reference/dynamic-configuration/traefik.io_tlsoptions.yaml @@ -19,7 +19,7 @@ spec: openAPIV3Schema: description: |- TLSOption is the CRD implementation of a Traefik TLS Option, allowing to configure some parameters of the TLS connection. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/tls/tls-certificates/#certificates-stores#tls-options + More info: https://doc.traefik.io/traefik/v3.4/https/tls/#tls-options properties: apiVersion: description: |- @@ -44,14 +44,14 @@ spec: alpnProtocols: description: |- ALPNProtocols defines the list of supported application level protocols for the TLS handshake, in order of preference. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/tls/tls-certificates/#certificates-stores#alpn-protocols + More info: https://doc.traefik.io/traefik/v3.4/https/tls/#alpn-protocols items: type: string type: array cipherSuites: description: |- CipherSuites defines the list of supported cipher suites for TLS versions up to TLS 1.2. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/tls/tls-certificates/#certificates-stores#cipher-suites + More info: https://doc.traefik.io/traefik/v3.4/https/tls/#cipher-suites items: type: string type: array @@ -78,8 +78,8 @@ spec: type: object curvePreferences: description: |- - CurvePreferences defines the preferred elliptic curves. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/tls/tls-certificates/#certificates-stores#curve-preferences + CurvePreferences defines the preferred elliptic curves in a specific order. + More info: https://doc.traefik.io/traefik/v3.4/https/tls/#curve-preferences items: type: string type: array diff --git a/docs/content/reference/dynamic-configuration/traefik.io_tlsstores.yaml b/docs/content/reference/dynamic-configuration/traefik.io_tlsstores.yaml index 779c93908..bdf4a93d6 100644 --- a/docs/content/reference/dynamic-configuration/traefik.io_tlsstores.yaml +++ b/docs/content/reference/dynamic-configuration/traefik.io_tlsstores.yaml @@ -21,7 +21,7 @@ spec: TLSStore is the CRD implementation of a Traefik TLS Store. For the time being, only the TLSStore named default is supported. This means that you cannot have two stores that are named default in different Kubernetes namespaces. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/tls/tls-certificates/#certificates-stores#certificates-stores + More info: https://doc.traefik.io/traefik/v3.4/https/tls/#certificates-stores properties: apiVersion: description: |- diff --git a/docs/content/reference/dynamic-configuration/traefik.io_traefikservices.yaml b/docs/content/reference/dynamic-configuration/traefik.io_traefikservices.yaml index 77e97156b..6715233b3 100644 --- a/docs/content/reference/dynamic-configuration/traefik.io_traefikservices.yaml +++ b/docs/content/reference/dynamic-configuration/traefik.io_traefikservices.yaml @@ -22,7 +22,7 @@ spec: TraefikService object allows to: - Apply weight to Services on load-balancing - Mirror traffic on services - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/kubernetes/crd/http/traefikservice/ + More info: https://doc.traefik.io/traefik/v3.4/routing/providers/kubernetes-crd/#kind-traefikservice properties: apiVersion: description: |- @@ -71,7 +71,7 @@ spec: - type: integer - type: string description: |- - Interval defines the frequency of the health check calls for healthy targets. + Interval defines the frequency of the health check calls. Default: 30s x-kubernetes-int-or-string: true method: @@ -107,15 +107,6 @@ spec: Timeout defines the maximum duration Traefik will wait for a health check request before considering the server unhealthy. Default: 5s x-kubernetes-int-or-string: true - unhealthyInterval: - anyOf: - - type: integer - - type: string - description: |- - UnhealthyInterval defines the frequency of the health check calls for unhealthy targets. - When UnhealthyInterval is not defined, it defaults to the Interval value. - Default: 30s - x-kubernetes-int-or-string: true type: object kind: description: Kind defines the kind of the Service. @@ -165,7 +156,7 @@ spec: - type: integer - type: string description: |- - Interval defines the frequency of the health check calls for healthy targets. + Interval defines the frequency of the health check calls. Default: 30s x-kubernetes-int-or-string: true method: @@ -201,15 +192,6 @@ spec: Timeout defines the maximum duration Traefik will wait for a health check request before considering the server unhealthy. Default: 5s x-kubernetes-int-or-string: true - unhealthyInterval: - anyOf: - - type: integer - - type: string - description: |- - UnhealthyInterval defines the frequency of the health check calls for unhealthy targets. - When UnhealthyInterval is not defined, it defaults to the Interval value. - Default: 30s - x-kubernetes-int-or-string: true type: object kind: description: Kind defines the kind of the Service. @@ -286,7 +268,7 @@ spec: sticky: description: |- Sticky defines the sticky sessions configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/load-balancing/service/#sticky-sessions + More info: https://doc.traefik.io/traefik/v3.4/routing/services/#sticky-sessions properties: cookie: description: Cookie defines the sticky cookie configuration. @@ -414,7 +396,7 @@ spec: sticky: description: |- Sticky defines the sticky sessions configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/load-balancing/service/#sticky-sessions + More info: https://doc.traefik.io/traefik/v3.4/routing/services/#sticky-sessions properties: cookie: description: Cookie defines the sticky cookie configuration. @@ -511,7 +493,7 @@ spec: - type: integer - type: string description: |- - Interval defines the frequency of the health check calls for healthy targets. + Interval defines the frequency of the health check calls. Default: 30s x-kubernetes-int-or-string: true method: @@ -547,15 +529,6 @@ spec: Timeout defines the maximum duration Traefik will wait for a health check request before considering the server unhealthy. Default: 5s x-kubernetes-int-or-string: true - unhealthyInterval: - anyOf: - - type: integer - - type: string - description: |- - UnhealthyInterval defines the frequency of the health check calls for unhealthy targets. - When UnhealthyInterval is not defined, it defaults to the Interval value. - Default: 30s - x-kubernetes-int-or-string: true type: object kind: description: Kind defines the kind of the Service. @@ -627,7 +600,7 @@ spec: sticky: description: |- Sticky defines the sticky sessions configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/load-balancing/service/#sticky-sessions + More info: https://doc.traefik.io/traefik/v3.4/routing/services/#sticky-sessions properties: cookie: description: Cookie defines the sticky cookie configuration. @@ -695,7 +668,7 @@ spec: sticky: description: |- Sticky defines whether sticky sessions are enabled. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/kubernetes/crd/http/traefikservice/#stickiness-and-load-balancing + More info: https://doc.traefik.io/traefik/v3.4/routing/providers/kubernetes-crd/#stickiness-and-load-balancing properties: cookie: description: Cookie defines the sticky cookie configuration. diff --git a/docs/content/reference/install-configuration/api-dashboard.md b/docs/content/reference/install-configuration/api-dashboard.md index 9f03454ca..ed485d499 100644 --- a/docs/content/reference/install-configuration/api-dashboard.md +++ b/docs/content/reference/install-configuration/api-dashboard.md @@ -155,12 +155,11 @@ enabing the dashboard [here](https://github.com/traefik/traefik-helm-chart/blob/ | Field | Description | Default | Required | |:-----------|:---------------------------------|:--------|:---------| -| `api` | Enable api/dashboard. When set to `true`, its sub option `api.dashboard` is also set to true.| false | No | -| api.basepath | Defines the base path where the API and Dashboard will be exposed. | / | No | -| `api.dashboard` | Enable dashboard. | false | No | -| `api.debug` | Enable additional endpoints for debugging and profiling. | false | No | -| `api.disabledashboardad` | Disable the advertisement from the dashboard. | false | No | -| `api.insecure` | Enable the API and the dashboard on the entryPoint named traefik.| false | No | +| `api` | Enable api/dashboard. When set to `true`, its sub option `api.dashboard` is also set to true.| false | No | +| `api.dashboard` | Enable dashboard. | false | No | +| `api.debug` | Enable additional endpoints for debugging and profiling. | false | No | +| `api.disabledashboardad` | Disable the advertisement from the dashboard. | false | No | +| `api.insecure` | Enable the API and the dashboard on the entryPoint named traefik.| false | No | ## Endpoints @@ -168,42 +167,37 @@ All the following endpoints must be accessed with a `GET` HTTP request. | Path | Description | |--------------------------------|---------------------------------------------------------------------------------------------| -| `/api/http/routers` | Lists all the HTTP routers information. | -| `/api/http/routers/{name}` | Returns the information of the HTTP router specified by `name`. | -| `/api/http/services` | Lists all the HTTP services information. | -| `/api/http/services/{name}` | Returns the information of the HTTP service specified by `name`. | -| `/api/http/middlewares` | Lists all the HTTP middlewares information. | -| `/api/http/middlewares/{name}` | Returns the information of the HTTP middleware specified by `name`. | -| `/api/tcp/routers` | Lists all the TCP routers information. | -| `/api/tcp/routers/{name}` | Returns the information of the TCP router specified by `name`. | -| `/api/tcp/services` | Lists all the TCP services information. | -| `/api/tcp/services/{name}` | Returns the information of the TCP service specified by `name`. | -| `/api/tcp/middlewares` | Lists all the TCP middlewares information. | -| `/api/tcp/middlewares/{name}` | Returns the information of the TCP middleware specified by `name`. | -| `/api/udp/routers` | Lists all the UDP routers information. | -| `/api/udp/routers/{name}` | Returns the information of the UDP router specified by `name`. | -| `/api/udp/services` | Lists all the UDP services information. | -| `/api/udp/services/{name}` | Returns the information of the UDP service specified by `name`. | -| `/api/entrypoints` | Lists all the entry points information. | -| `/api/entrypoints/{name}` | Returns the information of the entry point specified by `name`. | -| `/api/overview` | Returns statistic information about HTTP, TCP and about enabled features and providers. | -| `/api/rawdata` | Returns information about dynamic configurations, errors, status and dependency relations. | -| `/api/version` | Returns information about Traefik version. | -| `/debug/vars` | See the [expvar](https://golang.org/pkg/expvar/) Go documentation. | -| `/debug/pprof/` | See the [pprof Index](https://golang.org/pkg/net/http/pprof/#Index) Go documentation. | -| `/debug/pprof/cmdline` | See the [pprof Cmdline](https://golang.org/pkg/net/http/pprof/#Cmdline) Go documentation. | -| `/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. | - - -!!! note "Base Path Configuration" - - By default, Traefik exposes its API and Dashboard under the `/` base path. It's possible to configure it with `api.basepath`. When configured, all endpoints (api, dashboard, debug) are using it. +| `/api/http/routers` | Lists all the HTTP routers information. | +| `/api/http/routers/{name}` | Returns the information of the HTTP router specified by `name`. | +| `/api/http/services` | Lists all the HTTP services information. | +| `/api/http/services/{name}` | Returns the information of the HTTP service specified by `name`. | +| `/api/http/middlewares` | Lists all the HTTP middlewares information. | +| `/api/http/middlewares/{name}` | Returns the information of the HTTP middleware specified by `name`. | +| `/api/tcp/routers` | Lists all the TCP routers information. | +| `/api/tcp/routers/{name}` | Returns the information of the TCP router specified by `name`. | +| `/api/tcp/services` | Lists all the TCP services information. | +| `/api/tcp/services/{name}` | Returns the information of the TCP service specified by `name`. | +| `/api/tcp/middlewares` | Lists all the TCP middlewares information. | +| `/api/tcp/middlewares/{name}` | Returns the information of the TCP middleware specified by `name`. | +| `/api/udp/routers` | Lists all the UDP routers information. | +| `/api/udp/routers/{name}` | Returns the information of the UDP router specified by `name`. | +| `/api/udp/services` | Lists all the UDP services information. | +| `/api/udp/services/{name}` | Returns the information of the UDP service specified by `name`. | +| `/api/entrypoints` | Lists all the entry points information. | +| `/api/entrypoints/{name}` | Returns the information of the entry point specified by `name`. | +| `/api/overview` | Returns statistic information about HTTP, TCP and about enabled features and providers. | +| `/api/rawdata` | Returns information about dynamic configurations, errors, status and dependency relations. | +| `/api/version` | Returns information about Traefik version. | +| `/debug/vars` | See the [expvar](https://golang.org/pkg/expvar/) Go documentation. | +| `/debug/pprof/` | See the [pprof Index](https://golang.org/pkg/net/http/pprof/#Index) Go documentation. | +| `/debug/pprof/cmdline` | See the [pprof Cmdline](https://golang.org/pkg/net/http/pprof/#Cmdline) Go documentation. | +| `/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. | ## Dashboard -The dashboard is available at the same location as the API, but by default on the path `/dashboard/`. +The dashboard is available at the same location as the [API](../../operations/api.md), but by default on the path `/dashboard/`. !!! note diff --git a/docs/content/reference/install-configuration/boot-environment.md b/docs/content/reference/install-configuration/boot-environment.md index ca4f01c59..223977752 100644 --- a/docs/content/reference/install-configuration/boot-environment.md +++ b/docs/content/reference/install-configuration/boot-environment.md @@ -7,14 +7,14 @@ description: "Read the official Traefik documentation to get started with config Traefik Proxy’s configuration is divided into two main categories: -- **Install Configuration**: (formerly known as the static configuration) Defines parameters that require Traefik to restart when changed. This includes entry points, providers, API/dashboard settings, and logging levels. -- **Routing Configuration**: (formerly known as the dynamic configuration) Involves elements that can be updated without restarting Traefik, such as routers, services, and middlewares. +- **Static Configuration**: Defines parameters that require Traefik to restart when changed. This includes entry points, providers, API/dashboard settings, and logging levels. +- **Dynamic Configuration**: Involves elements that can be updated without restarting Traefik, such as routers, services, and middlewares. -This section focuses on setting up the install configuration, which is essential for Traefik’s initial boot. +This section focuses on setting up the static configuration, which is essential for Traefik’s initial boot. ## Configuration Methods -Traefik offers multiple methods to define install configuration. +Traefik offers multiple methods to define static configuration. !!! warning "Note" It’s crucial to choose one method and stick to it, as mixing different configuration options is not supported and can lead to unexpected behavior. @@ -28,7 +28,7 @@ Here are the methods available for configuring the Traefik proxy: ## File -You can define the install configuration in a file using formats like YAML or TOML. +You can define the static configuration in a file using formats like YAML or TOML. ### Configuration Example @@ -69,7 +69,7 @@ log: ### Configuration File -At startup, Traefik searches for install configuration in a file named `traefik.yml` (or `traefik.yaml` or `traefik.toml`) in the following directories: +At startup, Traefik searches for static configuration in a file named `traefik.yml` (or `traefik.yaml` or `traefik.toml`) in the following directories: - `/etc/traefik/` - `$XDG_CONFIG_HOME/` @@ -84,7 +84,7 @@ traefik --configFile=foo/bar/myconfigfile.yml ## CLI -Using the CLI, you can pass install configuration directly as command-line arguments when starting Traefik. +Using the CLI, you can pass static configuration directly as command-line arguments when starting Traefik. ### Configuration Example @@ -99,7 +99,7 @@ traefik \ ## Environment Variables -You can also set the install configuration using environment variables. Each option corresponds to an environment variable prefixed with `TRAEFIK_`. +You can also set the static configuration using environment variables. Each option corresponds to an environment variable prefixed with `TRAEFIK_`. ### Configuration Example @@ -109,7 +109,7 @@ TRAEFIK_ENTRYPOINTS_WEB_ADDRESS=":80" TRAEFIK_ENTRYPOINTS_WEBSECURE_ADDRESS=":44 ## Helm -When deploying Traefik Proxy using Helm in a Kubernetes cluster, the install configuration is defined in a `values.yaml` file. +When deploying Traefik Proxy using Helm in a Kubernetes cluster, the static configuration is defined in a `values.yaml` file. You can find the official Traefik Helm chart on [GitHub](https://github.com/traefik/traefik-helm-chart/blob/master/traefik/VALUES.md) diff --git a/docs/content/reference/install-configuration/configuration-options.md b/docs/content/reference/install-configuration/configuration-options.md deleted file mode 100644 index 596c69283..000000000 --- a/docs/content/reference/install-configuration/configuration-options.md +++ /dev/null @@ -1,491 +0,0 @@ - -# Install Configuration Options -## Configuration Options - -| Field | Description | Default | -|:-------|:------------|:-------| -| accesslog | Access log settings. | false | -| accesslog.addinternals | Enables access log for internal services (ping, dashboard, etc...). | false | -| accesslog.bufferingsize | Number of access log lines to process in a buffered way. | 0 | -| accesslog.fields.defaultmode | Default mode for fields: keep | drop | keep | -| accesslog.fields.headers.defaultmode | Default mode for fields: keep | drop | redact | drop | -| accesslog.fields.headers.names._name_ | Override mode for headers | | -| accesslog.fields.names._name_ | Override mode for fields | | -| accesslog.filepath | Access log file path. Stdout is used when omitted or empty. | | -| accesslog.filters.minduration | Keep access logs when request took longer than the specified duration. | 0 | -| accesslog.filters.retryattempts | Keep access logs when at least one retry happened. | false | -| accesslog.filters.statuscodes | Keep access logs with status codes in the specified range. | | -| accesslog.format | Access log format: json, common, or genericCLF | common | -| accesslog.otlp | Settings for OpenTelemetry. | false | -| accesslog.otlp.grpc | gRPC configuration for the OpenTelemetry collector. | false | -| accesslog.otlp.grpc.endpoint | Sets the gRPC endpoint (host:port) of the collector. | localhost:4317 | -| accesslog.otlp.grpc.headers._name_ | Headers sent with payload. | | -| accesslog.otlp.grpc.insecure | Disables client transport security for the exporter. | false | -| accesslog.otlp.grpc.tls.ca | TLS CA | | -| accesslog.otlp.grpc.tls.cert | TLS cert | | -| accesslog.otlp.grpc.tls.insecureskipverify | TLS insecure skip verify | false | -| accesslog.otlp.grpc.tls.key | TLS key | | -| accesslog.otlp.http | HTTP configuration for the OpenTelemetry collector. | false | -| accesslog.otlp.http.endpoint | Sets the HTTP endpoint (scheme://host:port/path) of the collector. | https://localhost:4318 | -| accesslog.otlp.http.headers._name_ | Headers sent with payload. | | -| accesslog.otlp.http.tls.ca | TLS CA | | -| accesslog.otlp.http.tls.cert | TLS cert | | -| accesslog.otlp.http.tls.insecureskipverify | TLS insecure skip verify | false | -| accesslog.otlp.http.tls.key | TLS key | | -| accesslog.otlp.resourceattributes._name_ | Defines additional resource attributes (key:value). | | -| accesslog.otlp.servicename | Defines the service name resource attribute. | traefik | -| api | Enable api/dashboard. | false | -| api.basepath | Defines the base path where the API and Dashboard will be exposed. | / | -| api.dashboard | Activate dashboard. | true | -| api.debug | Enable additional endpoints for debugging and profiling. | false | -| api.disabledashboardad | Disable ad in the dashboard. | false | -| api.insecure | Activate API directly on the entryPoint named traefik. | false | -| certificatesresolvers._name_ | Certificates resolvers configuration. | false | -| certificatesresolvers._name_.acme.cacertificates | Specify the paths to PEM encoded CA Certificates that can be used to authenticate an ACME server with an HTTPS certificate not issued by a CA in the system-wide trusted root list. | | -| certificatesresolvers._name_.acme.caserver | CA server to use. | https://acme-v02.api.letsencrypt.org/directory | -| certificatesresolvers._name_.acme.caservername | Specify the CA server name that can be used to authenticate an ACME server with an HTTPS certificate not issued by a CA in the system-wide trusted root list. | | -| certificatesresolvers._name_.acme.casystemcertpool | Define if the certificates pool must use a copy of the system cert pool. | false | -| certificatesresolvers._name_.acme.certificatesduration | Certificates' duration in hours. | 2160 | -| certificatesresolvers._name_.acme.clientresponseheadertimeout | Timeout for receiving the response headers when communicating with the ACME server. | 30 | -| certificatesresolvers._name_.acme.clienttimeout | Timeout for a complete HTTP transaction with the ACME server. | 120 | -| certificatesresolvers._name_.acme.dnschallenge | Activate DNS-01 Challenge. | false | -| certificatesresolvers._name_.acme.dnschallenge.delaybeforecheck | (Deprecated) Assume DNS propagates after a delay in seconds rather than finding and querying nameservers. | 0 | -| certificatesresolvers._name_.acme.dnschallenge.disablepropagationcheck | (Deprecated) Disable the DNS propagation checks before notifying ACME that the DNS challenge is ready. [not recommended] | false | -| certificatesresolvers._name_.acme.dnschallenge.propagation | DNS propagation checks configuration | false | -| certificatesresolvers._name_.acme.dnschallenge.propagation.delaybeforechecks | Defines the delay before checking the challenge TXT record propagation. | 0 | -| certificatesresolvers._name_.acme.dnschallenge.propagation.disableanschecks | Disables the challenge TXT record propagation checks against authoritative nameservers. | false | -| certificatesresolvers._name_.acme.dnschallenge.propagation.disablechecks | Disables the challenge TXT record propagation checks (not recommended). | false | -| certificatesresolvers._name_.acme.dnschallenge.propagation.requireallrns | Requires the challenge TXT record to be propagated to all recursive nameservers. | false | -| certificatesresolvers._name_.acme.dnschallenge.provider | Use a DNS-01 based challenge provider rather than HTTPS. | | -| certificatesresolvers._name_.acme.dnschallenge.resolvers | Use following DNS servers to resolve the FQDN authority. | | -| certificatesresolvers._name_.acme.eab.hmacencoded | Base64 encoded HMAC key from External CA. | | -| certificatesresolvers._name_.acme.eab.kid | Key identifier from External CA. | | -| certificatesresolvers._name_.acme.email | Email address used for registration. | | -| certificatesresolvers._name_.acme.emailaddresses | CSR email addresses to use. | | -| certificatesresolvers._name_.acme.httpchallenge | Activate HTTP-01 Challenge. | false | -| certificatesresolvers._name_.acme.httpchallenge.delay | Delay between the creation of the challenge and the validation. | 0 | -| certificatesresolvers._name_.acme.httpchallenge.entrypoint | HTTP challenge EntryPoint | | -| certificatesresolvers._name_.acme.keytype | KeyType used for generating certificate private key. Allow value 'EC256', 'EC384', 'RSA2048', 'RSA4096', 'RSA8192'. | RSA4096 | -| certificatesresolvers._name_.acme.preferredchain | Preferred chain to use. | | -| certificatesresolvers._name_.acme.profile | Certificate profile to use. | | -| certificatesresolvers._name_.acme.storage | Storage to use. | acme.json | -| certificatesresolvers._name_.acme.tlschallenge | Activate TLS-ALPN-01 Challenge. | true | -| certificatesresolvers._name_.tailscale | Enables Tailscale certificate resolution. | true | -| core.defaultrulesyntax | Defines the rule parser default syntax (v2 or v3) | v3 | -| entrypoints._name_ | Entry points definition. | false | -| entrypoints._name_.address | Entry point address. | | -| entrypoints._name_.allowacmebypass | Enables handling of ACME TLS and HTTP challenges with custom routers. | false | -| entrypoints._name_.asdefault | Adds this EntryPoint to the list of default EntryPoints to be used on routers that don't have any Entrypoint defined. | false | -| entrypoints._name_.forwardedheaders.connection | List of Connection headers that are allowed to pass through the middleware chain before being removed. | | -| entrypoints._name_.forwardedheaders.insecure | Trust all forwarded headers. | false | -| entrypoints._name_.forwardedheaders.trustedips | Trust only forwarded headers from selected IPs. | | -| entrypoints._name_.http | HTTP configuration. | | -| entrypoints._name_.http.encodequerysemicolons | Defines whether request query semicolons should be URLEncoded. | false | -| entrypoints._name_.http.maxheaderbytes | Maximum size of request headers in bytes. | 1048576 | -| entrypoints._name_.http.middlewares | Default middlewares for the routers linked to the entry point. | | -| entrypoints._name_.http.redirections.entrypoint.permanent | Applies a permanent redirection. | true | -| entrypoints._name_.http.redirections.entrypoint.priority | Priority of the generated router. | 9223372036854775806 | -| entrypoints._name_.http.redirections.entrypoint.scheme | Scheme used for the redirection. | https | -| entrypoints._name_.http.redirections.entrypoint.to | Targeted entry point of the redirection. | | -| entrypoints._name_.http.sanitizepath | Defines whether to enable request path sanitization (removal of /./, /../ and multiple slash sequences). | true | -| entrypoints._name_.http.tls | Default TLS configuration for the routers linked to the entry point. | false | -| entrypoints._name_.http.tls.certresolver | Default certificate resolver for the routers linked to the entry point. | | -| entrypoints._name_.http.tls.domains | Default TLS domains for the routers linked to the entry point. | | -| entrypoints._name_.http.tls.domains[0].main | Default subject name. | | -| entrypoints._name_.http.tls.domains[0].sans | Subject alternative names. | | -| entrypoints._name_.http.tls.options | Default TLS options for the routers linked to the entry point. | | -| entrypoints._name_.http2.maxconcurrentstreams | Specifies the number of concurrent streams per connection that each client is allowed to initiate. | 250 | -| entrypoints._name_.http3 | HTTP/3 configuration. | false | -| entrypoints._name_.http3.advertisedport | UDP port to advertise, on which HTTP/3 is available. | 0 | -| entrypoints._name_.observability.accesslogs | Enables access-logs for this entryPoint. | true | -| entrypoints._name_.observability.metrics | Enables metrics for this entryPoint. | true | -| entrypoints._name_.observability.traceverbosity | Defines the tracing verbosity level for this entryPoint. | minimal | -| entrypoints._name_.observability.tracing | Enables tracing for this entryPoint. | true | -| entrypoints._name_.proxyprotocol | Proxy-Protocol configuration. | false | -| entrypoints._name_.proxyprotocol.insecure | Trust all. | false | -| entrypoints._name_.proxyprotocol.trustedips | Trust only selected IPs. | | -| entrypoints._name_.reuseport | Enables EntryPoints from the same or different processes listening on the same TCP/UDP port. | false | -| entrypoints._name_.transport.keepalivemaxrequests | Maximum number of requests before closing a keep-alive connection. | 0 | -| entrypoints._name_.transport.keepalivemaxtime | Maximum duration before closing a keep-alive connection. | 0 | -| entrypoints._name_.transport.lifecycle.gracetimeout | Duration to give active requests a chance to finish before Traefik stops. | 10 | -| entrypoints._name_.transport.lifecycle.requestacceptgracetimeout | Duration to keep accepting requests before Traefik initiates the graceful shutdown procedure. | 0 | -| entrypoints._name_.transport.respondingtimeouts.idletimeout | IdleTimeout is the maximum amount duration an idle (keep-alive) connection will remain idle before closing itself. If zero, no timeout is set. | 180 | -| entrypoints._name_.transport.respondingtimeouts.readtimeout | ReadTimeout is the maximum duration for reading the entire request, including the body. If zero, no timeout is set. | 60 | -| entrypoints._name_.transport.respondingtimeouts.writetimeout | WriteTimeout is the maximum duration before timing out writes of the response. If zero, no timeout is set. | 0 | -| entrypoints._name_.udp.timeout | Timeout defines how long to wait on an idle session before releasing the related resources. | 3 | -| experimental.abortonpluginfailure | Defines whether all plugins must be loaded successfully for Traefik to start. | false | -| experimental.fastproxy | Enables the FastProxy implementation. | false | -| experimental.fastproxy.debug | Enable debug mode for the FastProxy implementation. | false | -| experimental.kubernetesgateway | (Deprecated) Allow the Kubernetes gateway api provider usage. | false | -| experimental.kubernetesingressnginx | Allow the Kubernetes Ingress NGINX provider usage. | false | -| experimental.localplugins._name_ | Local plugins configuration. | false | -| experimental.localplugins._name_.modulename | Plugin's module name. | | -| experimental.localplugins._name_.settings | Plugin's settings (works only for wasm plugins). | | -| experimental.localplugins._name_.settings.envs | Environment variables to forward to the wasm guest. | | -| experimental.localplugins._name_.settings.mounts | Directory to mount to the wasm guest. | | -| experimental.localplugins._name_.settings.useunsafe | Allow the plugin to use unsafe package. | false | -| experimental.otlplogs | Enables the OpenTelemetry logs integration. | false | -| experimental.plugins._name_.hash | plugin's hash to validate' | | -| experimental.plugins._name_.modulename | plugin's module name. | | -| experimental.plugins._name_.settings | Plugin's settings (works only for wasm plugins). | | -| experimental.plugins._name_.settings.envs | Environment variables to forward to the wasm guest. | | -| experimental.plugins._name_.settings.mounts | Directory to mount to the wasm guest. | | -| experimental.plugins._name_.settings.useunsafe | Allow the plugin to use unsafe package. | false | -| experimental.plugins._name_.version | plugin's version. | | -| global.checknewversion | Periodically check if a new version has been released. | true | -| global.sendanonymoususage | Periodically send anonymous usage statistics. If the option is not specified, it will be disabled by default. | false | -| global.updatercallbacks | Callback urls for updater script (example: https://localhost:8080/callback) | | -| hostresolver | Enable CNAME Flattening. | false | -| hostresolver.cnameflattening | A flag to enable/disable CNAME flattening | false | -| hostresolver.resolvconfig | resolv.conf used for DNS resolving | /etc/resolv.conf | -| hostresolver.resolvdepth | The maximal depth of DNS recursive resolving | 5 | -| log | Traefik log settings. | false | -| log.compress | Determines if the rotated log files should be compressed using gzip. | false | -| log.filepath | Traefik log file path. Stdout is used when omitted or empty. | | -| log.format | Traefik log format: json | common | common | -| log.level | Log level set to traefik logs. | ERROR | -| log.maxage | Maximum number of days to retain old log files based on the timestamp encoded in their filename. | 0 | -| log.maxbackups | Maximum number of old log files to retain. | 0 | -| log.maxsize | Maximum size in megabytes of the log file before it gets rotated. | 0 | -| log.nocolor | When using the 'common' format, disables the colorized output. | false | -| log.otlp | Settings for OpenTelemetry. | false | -| log.otlp.grpc | gRPC configuration for the OpenTelemetry collector. | false | -| log.otlp.grpc.endpoint | Sets the gRPC endpoint (host:port) of the collector. | localhost:4317 | -| log.otlp.grpc.headers._name_ | Headers sent with payload. | | -| log.otlp.grpc.insecure | Disables client transport security for the exporter. | false | -| log.otlp.grpc.tls.ca | TLS CA | | -| log.otlp.grpc.tls.cert | TLS cert | | -| log.otlp.grpc.tls.insecureskipverify | TLS insecure skip verify | false | -| log.otlp.grpc.tls.key | TLS key | | -| log.otlp.http | HTTP configuration for the OpenTelemetry collector. | false | -| log.otlp.http.endpoint | Sets the HTTP endpoint (scheme://host:port/path) of the collector. | https://localhost:4318 | -| log.otlp.http.headers._name_ | Headers sent with payload. | | -| log.otlp.http.tls.ca | TLS CA | | -| log.otlp.http.tls.cert | TLS cert | | -| log.otlp.http.tls.insecureskipverify | TLS insecure skip verify | false | -| log.otlp.http.tls.key | TLS key | | -| log.otlp.resourceattributes._name_ | Defines additional resource attributes (key:value). | | -| log.otlp.servicename | Defines the service name resource attribute. | traefik | -| metrics.addinternals | Enables metrics for internal services (ping, dashboard, etc...). | false | -| metrics.datadog | Datadog metrics exporter type. | false | -| metrics.datadog.addentrypointslabels | Enable metrics on entry points. | true | -| metrics.datadog.address | Datadog's address. | localhost:8125 | -| metrics.datadog.addrouterslabels | Enable metrics on routers. | false | -| metrics.datadog.addserviceslabels | Enable metrics on services. | true | -| metrics.datadog.prefix | Prefix to use for metrics collection. | traefik | -| metrics.datadog.pushinterval | Datadog push interval. | 10 | -| metrics.influxdb2 | InfluxDB v2 metrics exporter type. | false | -| metrics.influxdb2.addentrypointslabels | Enable metrics on entry points. | true | -| metrics.influxdb2.additionallabels._name_ | Additional labels (influxdb tags) on all metrics | | -| metrics.influxdb2.address | InfluxDB v2 address. | http://localhost:8086 | -| metrics.influxdb2.addrouterslabels | Enable metrics on routers. | false | -| metrics.influxdb2.addserviceslabels | Enable metrics on services. | true | -| metrics.influxdb2.bucket | InfluxDB v2 bucket ID. | | -| metrics.influxdb2.org | InfluxDB v2 org ID. | | -| metrics.influxdb2.pushinterval | InfluxDB v2 push interval. | 10 | -| metrics.influxdb2.token | InfluxDB v2 access token. | | -| metrics.otlp | OpenTelemetry metrics exporter type. | false | -| metrics.otlp.addentrypointslabels | Enable metrics on entry points. | true | -| metrics.otlp.addrouterslabels | Enable metrics on routers. | false | -| metrics.otlp.addserviceslabels | Enable metrics on services. | true | -| metrics.otlp.explicitboundaries | Boundaries for latency metrics. | 0.005000, 0.010000, 0.025000, 0.050000, 0.075000, 0.100000, 0.250000, 0.500000, 0.750000, 1.000000, 2.500000, 5.000000, 7.500000, 10.000000 | -| metrics.otlp.grpc | gRPC configuration for the OpenTelemetry collector. | false | -| metrics.otlp.grpc.endpoint | Sets the gRPC endpoint (host:port) of the collector. | localhost:4317 | -| metrics.otlp.grpc.headers._name_ | Headers sent with payload. | | -| metrics.otlp.grpc.insecure | Disables client transport security for the exporter. | false | -| metrics.otlp.grpc.tls.ca | TLS CA | | -| metrics.otlp.grpc.tls.cert | TLS cert | | -| metrics.otlp.grpc.tls.insecureskipverify | TLS insecure skip verify | false | -| metrics.otlp.grpc.tls.key | TLS key | | -| metrics.otlp.http | HTTP configuration for the OpenTelemetry collector. | false | -| metrics.otlp.http.endpoint | Sets the HTTP endpoint (scheme://host:port/path) of the collector. | https://localhost:4318 | -| metrics.otlp.http.headers._name_ | Headers sent with payload. | | -| metrics.otlp.http.tls.ca | TLS CA | | -| metrics.otlp.http.tls.cert | TLS cert | | -| metrics.otlp.http.tls.insecureskipverify | TLS insecure skip verify | false | -| metrics.otlp.http.tls.key | TLS key | | -| metrics.otlp.pushinterval | Period between calls to collect a checkpoint. | 10 | -| metrics.otlp.resourceattributes._name_ | Defines additional resource attributes (key:value). | | -| metrics.otlp.servicename | Defines the service name resource attribute. | traefik | -| metrics.prometheus | Prometheus metrics exporter type. | false | -| metrics.prometheus.addentrypointslabels | Enable metrics on entry points. | true | -| metrics.prometheus.addrouterslabels | Enable metrics on routers. | false | -| metrics.prometheus.addserviceslabels | Enable metrics on services. | true | -| metrics.prometheus.buckets | Buckets for latency metrics. | 0.100000, 0.300000, 1.200000, 5.000000 | -| metrics.prometheus.entrypoint | EntryPoint | traefik | -| metrics.prometheus.headerlabels._name_ | Defines the extra labels for the requests_total metrics, and for each of them, the request header containing the value for this label. | | -| metrics.prometheus.manualrouting | Manual routing | false | -| metrics.statsd | StatsD metrics exporter type. | false | -| metrics.statsd.addentrypointslabels | Enable metrics on entry points. | true | -| metrics.statsd.address | StatsD address. | localhost:8125 | -| metrics.statsd.addrouterslabels | Enable metrics on routers. | false | -| metrics.statsd.addserviceslabels | Enable metrics on services. | true | -| metrics.statsd.prefix | Prefix to use for metrics collection. | traefik | -| metrics.statsd.pushinterval | StatsD push interval. | 10 | -| ocsp | OCSP configuration. | false | -| ocsp.responderoverrides._name_ | Defines a map of OCSP responders to replace for querying OCSP servers. | | -| ping | Enable ping. | false | -| ping.entrypoint | EntryPoint | traefik | -| ping.manualrouting | Manual routing | false | -| ping.terminatingstatuscode | Terminating status code | 503 | -| providers.consul | Enables Consul provider. | false | -| providers.consul.endpoints | KV store endpoints. | 127.0.0.1:8500 | -| providers.consul.namespaces | Sets the namespaces used to discover the configuration (Consul Enterprise only). | | -| providers.consul.rootkey | Root key used for KV store. | traefik | -| providers.consul.tls.ca | TLS CA | | -| providers.consul.tls.cert | TLS cert | | -| providers.consul.tls.insecureskipverify | TLS insecure skip verify | false | -| providers.consul.tls.key | TLS key | | -| providers.consul.token | Per-request ACL token. | | -| providers.consulcatalog | Enables Consul Catalog provider. | false | -| providers.consulcatalog.cache | Use local agent caching for catalog reads. | false | -| providers.consulcatalog.connectaware | Enable Consul Connect support. | false | -| providers.consulcatalog.connectbydefault | Consider every service as Connect capable by default. | false | -| providers.consulcatalog.constraints | Constraints is an expression that Traefik matches against the container's labels to determine whether to create any route for that container. | | -| providers.consulcatalog.defaultrule | Default rule. | Host(`{{ normalize .Name }}`) | -| providers.consulcatalog.endpoint.address | The address of the Consul server | | -| providers.consulcatalog.endpoint.datacenter | Data center to use. If not provided, the default agent data center is used | | -| providers.consulcatalog.endpoint.endpointwaittime | WaitTime limits how long a Watch will block. If not provided, the agent default values will be used | 0 | -| providers.consulcatalog.endpoint.httpauth.password | Basic Auth password | | -| providers.consulcatalog.endpoint.httpauth.username | Basic Auth username | | -| providers.consulcatalog.endpoint.scheme | The URI scheme for the Consul server | | -| providers.consulcatalog.endpoint.tls.ca | TLS CA | | -| providers.consulcatalog.endpoint.tls.cert | TLS cert | | -| providers.consulcatalog.endpoint.tls.insecureskipverify | TLS insecure skip verify | false | -| providers.consulcatalog.endpoint.tls.key | TLS key | | -| providers.consulcatalog.endpoint.token | Token is used to provide a per-request ACL token which overrides the agent's default token | | -| providers.consulcatalog.exposedbydefault | Expose containers by default. | true | -| providers.consulcatalog.namespaces | Sets the namespaces used to discover services (Consul Enterprise only). | | -| providers.consulcatalog.prefix | Prefix for consul service tags. | traefik | -| providers.consulcatalog.refreshinterval | Interval for check Consul API. | 15 | -| providers.consulcatalog.requireconsistent | Forces the read to be fully consistent. | false | -| providers.consulcatalog.servicename | Name of the Traefik service in Consul Catalog (needs to be registered via the orchestrator or manually). | traefik | -| providers.consulcatalog.stale | Use stale consistency for catalog reads. | false | -| providers.consulcatalog.strictchecks | A list of service health statuses to allow taking traffic. | passing, warning | -| providers.consulcatalog.watch | Watch Consul API events. | false | -| providers.docker | Enables Docker provider. | false | -| providers.docker.allowemptyservices | Disregards the Docker containers health checks with respect to the creation or removal of the corresponding services. | false | -| providers.docker.constraints | Constraints is an expression that Traefik matches against the container's labels to determine whether to create any route for that container. | | -| providers.docker.defaultrule | Default rule. | Host(`{{ normalize .Name }}`) | -| providers.docker.endpoint | Docker server endpoint. Can be a TCP or a Unix socket endpoint. | unix:///var/run/docker.sock | -| providers.docker.exposedbydefault | Expose containers by default. | true | -| providers.docker.httpclienttimeout | Client timeout for HTTP connections. | 0 | -| providers.docker.labelmap | Label shorthands. | | -| providers.docker.labelmap[0].from | Shorthand label. | | -| providers.docker.labelmap[0].to | Full label with templates. | | -| providers.docker.labelmap[0].value | Optional override; used instead of user input if set. | | -| providers.docker.network | Default Docker network used. | | -| providers.docker.password | Password for Basic HTTP authentication. | | -| providers.docker.tls.ca | TLS CA | | -| providers.docker.tls.cert | TLS cert | | -| providers.docker.tls.insecureskipverify | TLS insecure skip verify | false | -| providers.docker.tls.key | TLS key | | -| providers.docker.usebindportip | Use the ip address from the bound port, rather than from the inner network. | false | -| providers.docker.username | Username for Basic HTTP authentication. | | -| providers.docker.watch | Watch Docker events. | true | -| providers.ecs | Enables AWS ECS provider. | false | -| providers.ecs.accesskeyid | AWS credentials access key ID to use for making requests. | | -| providers.ecs.autodiscoverclusters | Auto discover cluster. | false | -| providers.ecs.clusters | ECS Cluster names. | default | -| providers.ecs.constraints | Constraints is an expression that Traefik matches against the container's labels to determine whether to create any route for that container. | | -| providers.ecs.defaultrule | Default rule. | Host(`{{ normalize .Name }}`) | -| providers.ecs.ecsanywhere | Enable ECS Anywhere support. | false | -| providers.ecs.exposedbydefault | Expose services by default. | true | -| providers.ecs.healthytasksonly | Determines whether to discover only healthy tasks. | false | -| providers.ecs.refreshseconds | Polling interval (in seconds). | 15 | -| providers.ecs.region | AWS region to use for requests. | | -| providers.ecs.secretaccesskey | AWS credentials access key to use for making requests. | | -| providers.etcd | Enables Etcd provider. | false | -| providers.etcd.endpoints | KV store endpoints. | 127.0.0.1:2379 | -| providers.etcd.password | Password for authentication. | | -| providers.etcd.rootkey | Root key used for KV store. | traefik | -| providers.etcd.tls.ca | TLS CA | | -| providers.etcd.tls.cert | TLS cert | | -| providers.etcd.tls.insecureskipverify | TLS insecure skip verify | false | -| providers.etcd.tls.key | TLS key | | -| providers.etcd.username | Username for authentication. | | -| providers.file.debugloggeneratedtemplate | Enable debug logging of generated configuration template. | false | -| providers.file.directory | Load dynamic configuration from one or more .yml or .toml files in a directory. | | -| providers.file.filename | Load dynamic configuration from a file. | | -| providers.file.watch | Watch provider. | true | -| providers.http | Enables HTTP provider. | false | -| providers.http.endpoint | Load configuration from this endpoint. | | -| providers.http.headers._name_ | Define custom headers to be sent to the endpoint. | | -| providers.http.pollinterval | Polling interval for endpoint. | 5 | -| providers.http.polltimeout | Polling timeout for endpoint. | 5 | -| providers.http.tls.ca | TLS CA | | -| providers.http.tls.cert | TLS cert | | -| providers.http.tls.insecureskipverify | TLS insecure skip verify | false | -| providers.http.tls.key | TLS key | | -| providers.kubernetescrd | Enables Kubernetes CRD provider. | false | -| providers.kubernetescrd.allowcrossnamespace | Allow cross namespace resource reference. | false | -| providers.kubernetescrd.allowemptyservices | Allow the creation of services without endpoints. | false | -| providers.kubernetescrd.allowexternalnameservices | Allow ExternalName services. | false | -| providers.kubernetescrd.certauthfilepath | Kubernetes certificate authority file path (not needed for in-cluster client). | | -| providers.kubernetescrd.disableclusterscoperesources | Disables the lookup of cluster scope resources (incompatible with IngressClasses and NodePortLB enabled services). | false | -| providers.kubernetescrd.endpoint | Kubernetes server endpoint (required for external cluster client). | | -| providers.kubernetescrd.ingressclass | Value of kubernetes.io/ingress.class annotation to watch for. | | -| providers.kubernetescrd.labelselector | Kubernetes label selector to use. | | -| providers.kubernetescrd.namespaces | Kubernetes namespaces. | | -| providers.kubernetescrd.nativelbbydefault | Defines whether to use Native Kubernetes load-balancing mode by default. | false | -| providers.kubernetescrd.throttleduration | Ingress refresh throttle duration | 0 | -| providers.kubernetescrd.token | Kubernetes bearer token (not needed for in-cluster client). It accepts either a token value or a file path to the token. | | -| providers.kubernetesgateway | Enables Kubernetes Gateway API provider. | false | -| providers.kubernetesgateway.certauthfilepath | Kubernetes certificate authority file path (not needed for in-cluster client). | | -| providers.kubernetesgateway.endpoint | Kubernetes server endpoint (required for external cluster client). | | -| providers.kubernetesgateway.experimentalchannel | Toggles Experimental Channel resources support (TCPRoute, TLSRoute...). | false | -| providers.kubernetesgateway.labelselector | Kubernetes label selector to select specific GatewayClasses. | | -| providers.kubernetesgateway.namespaces | Kubernetes namespaces. | | -| providers.kubernetesgateway.nativelbbydefault | Defines whether to use Native Kubernetes load-balancing by default. | false | -| providers.kubernetesgateway.statusaddress.hostname | Hostname used for Kubernetes Gateway status address. | | -| providers.kubernetesgateway.statusaddress.ip | IP used to set Kubernetes Gateway status address. | | -| providers.kubernetesgateway.statusaddress.service | Published Kubernetes Service to copy status addresses from. | | -| providers.kubernetesgateway.statusaddress.service.name | Name of the Kubernetes service. | | -| providers.kubernetesgateway.statusaddress.service.namespace | Namespace of the Kubernetes service. | | -| providers.kubernetesgateway.throttleduration | Kubernetes refresh throttle duration | 0 | -| providers.kubernetesgateway.token | Kubernetes bearer token (not needed for in-cluster client). It accepts either a token value or a file path to the token. | | -| providers.kubernetesingress | Enables Kubernetes Ingress provider. | false | -| providers.kubernetesingress.allowemptyservices | Allow creation of services without endpoints. | false | -| providers.kubernetesingress.allowexternalnameservices | Allow ExternalName services. | false | -| providers.kubernetesingress.certauthfilepath | Kubernetes certificate authority file path (not needed for in-cluster client). | | -| providers.kubernetesingress.disableclusterscoperesources | Disables the lookup of cluster scope resources (incompatible with IngressClasses and NodePortLB enabled services). | false | -| providers.kubernetesingress.disableingressclasslookup | Disables the lookup of IngressClasses (Deprecated, please use DisableClusterScopeResources). | false | -| providers.kubernetesingress.endpoint | Kubernetes server endpoint (required for external cluster client). | | -| providers.kubernetesingress.ingressclass | Value of kubernetes.io/ingress.class annotation or IngressClass name to watch for. | | -| providers.kubernetesingress.ingressendpoint.hostname | Hostname used for Kubernetes Ingress endpoints. | | -| providers.kubernetesingress.ingressendpoint.ip | IP used for Kubernetes Ingress endpoints. | | -| providers.kubernetesingress.ingressendpoint.publishedservice | Published Kubernetes Service to copy status from. | | -| providers.kubernetesingress.labelselector | Kubernetes Ingress label selector to use. | | -| providers.kubernetesingress.namespaces | Kubernetes namespaces. | | -| providers.kubernetesingress.nativelbbydefault | Defines whether to use Native Kubernetes load-balancing mode by default. | false | -| providers.kubernetesingress.strictprefixmatching | Make prefix matching strictly comply with the Kubernetes Ingress specification (path-element-wise matching instead of character-by-character string matching). | false | -| providers.kubernetesingress.throttleduration | Ingress refresh throttle duration | 0 | -| providers.kubernetesingress.token | Kubernetes bearer token (not needed for in-cluster client). It accepts either a token value or a file path to the token. | | -| providers.kubernetesingressnginx | Enables Kubernetes Ingress NGINX provider. | false | -| providers.kubernetesingressnginx.certauthfilepath | Kubernetes certificate authority file path (not needed for in-cluster client). | | -| providers.kubernetesingressnginx.controllerclass | Ingress Class Controller value this controller satisfies. | k8s.io/ingress-nginx | -| providers.kubernetesingressnginx.defaultbackendservice | Service used to serve HTTP requests not matching any known server name (catch-all). Takes the form 'namespace/name'. | | -| providers.kubernetesingressnginx.disablesvcexternalname | Disable support for Services of type ExternalName. | false | -| providers.kubernetesingressnginx.endpoint | Kubernetes server endpoint (required for external cluster client). | | -| providers.kubernetesingressnginx.ingressclass | Name of the ingress class this controller satisfies. | nginx | -| providers.kubernetesingressnginx.ingressclassbyname | Define if Ingress Controller should watch for Ingress Class by Name together with Controller Class. | false | -| providers.kubernetesingressnginx.publishservice | Service fronting the Ingress controller. Takes the form 'namespace/name'. | | -| providers.kubernetesingressnginx.publishstatusaddress | Customized address (or addresses, separated by comma) to set as the load-balancer status of Ingress objects this controller satisfies. | | -| providers.kubernetesingressnginx.throttleduration | Ingress refresh throttle duration. | 0 | -| providers.kubernetesingressnginx.token | Kubernetes bearer token (not needed for in-cluster client). It accepts either a token value or a file path to the token. | | -| providers.kubernetesingressnginx.watchingresswithoutclass | Define if Ingress Controller should also watch for Ingresses without an IngressClass or the annotation specified. | false | -| providers.kubernetesingressnginx.watchnamespace | Namespace the controller watches for updates to Kubernetes objects. All namespaces are watched if this parameter is left empty. | | -| providers.kubernetesingressnginx.watchnamespaceselector | Selector selects namespaces the controller watches for updates to Kubernetes objects. | | -| providers.nomad | Enables Nomad provider. | false | -| providers.nomad.allowemptyservices | Allow the creation of services without endpoints. | false | -| providers.nomad.constraints | Constraints is an expression that Traefik matches against the Nomad service's tags to determine whether to create route(s) for that service. | | -| providers.nomad.defaultrule | Default rule. | Host(`{{ normalize .Name }}`) | -| providers.nomad.endpoint.address | The address of the Nomad server, including scheme and port. | http://127.0.0.1:4646 | -| providers.nomad.endpoint.endpointwaittime | WaitTime limits how long a Watch will block. If not provided, the agent default values will be used | 0 | -| providers.nomad.endpoint.region | Nomad region to use. If not provided, the local agent region is used. | | -| providers.nomad.endpoint.tls.ca | TLS CA | | -| providers.nomad.endpoint.tls.cert | TLS cert | | -| providers.nomad.endpoint.tls.insecureskipverify | TLS insecure skip verify | false | -| providers.nomad.endpoint.tls.key | TLS key | | -| providers.nomad.endpoint.token | Token is used to provide a per-request ACL token. | | -| providers.nomad.exposedbydefault | Expose Nomad services by default. | true | -| providers.nomad.namespaces | Sets the Nomad namespaces used to discover services. | | -| providers.nomad.prefix | Prefix for nomad service tags. | traefik | -| providers.nomad.refreshinterval | Interval for polling Nomad API. | 15 | -| providers.nomad.stale | Use stale consistency for catalog reads. | false | -| providers.nomad.throttleduration | Watch throttle duration. | 0 | -| providers.nomad.watch | Watch Nomad Service events. | false | -| providers.plugin._name_ | Plugins configuration. | | -| providers.providersthrottleduration | Backends throttle duration: minimum duration between 2 events from providers before applying a new configuration. It avoids unnecessary reloads if multiples events are sent in a short amount of time. | 2 | -| providers.redis | Enables Redis provider. | false | -| providers.redis.db | Database to be selected after connecting to the server. | 0 | -| providers.redis.endpoints | KV store endpoints. | 127.0.0.1:6379 | -| providers.redis.password | Password for authentication. | | -| providers.redis.rootkey | Root key used for KV store. | traefik | -| providers.redis.sentinel.latencystrategy | Defines whether to route commands to the closest master or replica nodes (mutually exclusive with RandomStrategy and ReplicaStrategy). | false | -| providers.redis.sentinel.mastername | Name of the master. | | -| providers.redis.sentinel.password | Password for Sentinel authentication. | | -| providers.redis.sentinel.randomstrategy | Defines whether to route commands randomly to master or replica nodes (mutually exclusive with LatencyStrategy and ReplicaStrategy). | false | -| providers.redis.sentinel.replicastrategy | Defines whether to route all commands to replica nodes (mutually exclusive with LatencyStrategy and RandomStrategy). | false | -| providers.redis.sentinel.usedisconnectedreplicas | Use replicas disconnected with master when cannot get connected replicas. | false | -| providers.redis.sentinel.username | Username for Sentinel authentication. | | -| providers.redis.tls.ca | TLS CA | | -| providers.redis.tls.cert | TLS cert | | -| providers.redis.tls.insecureskipverify | TLS insecure skip verify | false | -| providers.redis.tls.key | TLS key | | -| providers.redis.username | Username for authentication. | | -| providers.rest | Enables Rest provider. | false | -| providers.rest.insecure | Activate REST Provider directly on the entryPoint named traefik. | false | -| providers.swarm | Enables Docker Swarm provider. | false | -| providers.swarm.allowemptyservices | Disregards the Docker containers health checks with respect to the creation or removal of the corresponding services. | false | -| providers.swarm.constraints | Constraints is an expression that Traefik matches against the container's labels to determine whether to create any route for that container. | | -| providers.swarm.defaultrule | Default rule. | Host(`{{ normalize .Name }}`) | -| providers.swarm.endpoint | Docker server endpoint. Can be a TCP or a Unix socket endpoint. | unix:///var/run/docker.sock | -| providers.swarm.exposedbydefault | Expose containers by default. | true | -| providers.swarm.httpclienttimeout | Client timeout for HTTP connections. | 0 | -| providers.swarm.labelmap | Label shorthands. | | -| providers.swarm.labelmap[0].from | Shorthand label. | | -| providers.swarm.labelmap[0].to | Full label with templates. | | -| providers.swarm.labelmap[0].value | Optional override; used instead of user input if set. | | -| providers.swarm.network | Default Docker network used. | | -| providers.swarm.password | Password for Basic HTTP authentication. | | -| providers.swarm.refreshseconds | Polling interval for swarm mode. | 15 | -| providers.swarm.tls.ca | TLS CA | | -| providers.swarm.tls.cert | TLS cert | | -| providers.swarm.tls.insecureskipverify | TLS insecure skip verify | false | -| providers.swarm.tls.key | TLS key | | -| providers.swarm.usebindportip | Use the ip address from the bound port, rather than from the inner network. | false | -| providers.swarm.username | Username for Basic HTTP authentication. | | -| providers.swarm.watch | Watch Docker events. | true | -| providers.zookeeper | Enables ZooKeeper provider. | false | -| providers.zookeeper.endpoints | KV store endpoints. | 127.0.0.1:2181 | -| providers.zookeeper.password | Password for authentication. | | -| providers.zookeeper.rootkey | Root key used for KV store. | traefik | -| providers.zookeeper.username | Username for authentication. | | -| serverstransport.forwardingtimeouts.dialtimeout | The amount of time to wait until a connection to a backend server can be established. If zero, no timeout exists. | 30 | -| serverstransport.forwardingtimeouts.idleconntimeout | The maximum period for which an idle HTTP keep-alive connection will remain open before closing itself | 90 | -| serverstransport.forwardingtimeouts.responseheadertimeout | The amount of time to wait for a server's response headers after fully writing the request (including its body, if any). If zero, no timeout exists. | 0 | -| serverstransport.insecureskipverify | Disable SSL certificate verification. | false | -| serverstransport.maxidleconnsperhost | If non-zero, controls the maximum idle (keep-alive) to keep per-host. If zero, DefaultMaxIdleConnsPerHost is used. If negative, disables connection reuse. | 200 | -| serverstransport.rootcas | Add cert file for self-signed certificate. | | -| serverstransport.spiffe | Defines the SPIFFE configuration. | false | -| serverstransport.spiffe.ids | Defines the allowed SPIFFE IDs (takes precedence over the SPIFFE TrustDomain). | | -| serverstransport.spiffe.trustdomain | Defines the allowed SPIFFE trust domain. | | -| spiffe.workloadapiaddr | Defines the workload API address. | | -| tcpserverstransport.dialkeepalive | Defines the interval between keep-alive probes for an active network connection. If zero, keep-alive probes are sent with a default value (currently 15 seconds), if supported by the protocol and operating system. Network protocols or operating systems that do not support keep-alives ignore this field. If negative, keep-alive probes are disabled | 15 | -| tcpserverstransport.dialtimeout | Defines the amount of time to wait until a connection to a backend server can be established. If zero, no timeout exists. | 30 | -| tcpserverstransport.terminationdelay | Defines the delay to wait before fully terminating the connection, after one connected peer has closed its writing capability. | 0 | -| tcpserverstransport.tls | Defines the TLS configuration. | false | -| tcpserverstransport.tls.insecureskipverify | Disables SSL certificate verification. | false | -| tcpserverstransport.tls.rootcas | Defines a list of CA secret used to validate self-signed certificate | | -| tcpserverstransport.tls.spiffe | Defines the SPIFFE TLS configuration. | false | -| tcpserverstransport.tls.spiffe.ids | Defines the allowed SPIFFE IDs (takes precedence over the SPIFFE TrustDomain). | | -| tcpserverstransport.tls.spiffe.trustdomain | Defines the allowed SPIFFE trust domain. | | -| tracing | Tracing configuration. | false | -| tracing.addinternals | Enables tracing for internal services (ping, dashboard, etc...). | false | -| tracing.capturedrequestheaders | Request headers to add as attributes for server and client spans. | | -| tracing.capturedresponseheaders | Response headers to add as attributes for server and client spans. | | -| tracing.globalattributes._name_ | (Deprecated) Defines additional resource attributes (key:value). | | -| tracing.otlp | Settings for OpenTelemetry. | false | -| tracing.otlp.grpc | gRPC configuration for the OpenTelemetry collector. | false | -| tracing.otlp.grpc.endpoint | Sets the gRPC endpoint (host:port) of the collector. | localhost:4317 | -| tracing.otlp.grpc.headers._name_ | Headers sent with payload. | | -| tracing.otlp.grpc.insecure | Disables client transport security for the exporter. | false | -| tracing.otlp.grpc.tls.ca | TLS CA | | -| tracing.otlp.grpc.tls.cert | TLS cert | | -| tracing.otlp.grpc.tls.insecureskipverify | TLS insecure skip verify | false | -| tracing.otlp.grpc.tls.key | TLS key | | -| tracing.otlp.http | HTTP configuration for the OpenTelemetry collector. | false | -| tracing.otlp.http.endpoint | Sets the HTTP endpoint (scheme://host:port/path) of the collector. | https://localhost:4318 | -| tracing.otlp.http.headers._name_ | Headers sent with payload. | | -| tracing.otlp.http.tls.ca | TLS CA | | -| tracing.otlp.http.tls.cert | TLS cert | | -| tracing.otlp.http.tls.insecureskipverify | TLS insecure skip verify | false | -| tracing.otlp.http.tls.key | TLS key | | -| tracing.resourceattributes._name_ | Defines additional resource attributes (key:value). | | -| tracing.safequeryparams | Query params to not redact. | | -| tracing.samplerate | Sets the rate between 0.0 and 1.0 of requests to trace. | 1.000000 | -| tracing.servicename | Defines the service name resource attribute. | traefik | diff --git a/docs/content/reference/install-configuration/entrypoints.md b/docs/content/reference/install-configuration/entrypoints.md index 9a41e3bc3..5b313f58b 100644 --- a/docs/content/reference/install-configuration/entrypoints.md +++ b/docs/content/reference/install-configuration/entrypoints.md @@ -18,38 +18,11 @@ entryPoints: to: websecure scheme: https permanent: true - observability: - accessLogs: false - metrics: false - tracing: false websecure: address: :443 - http: - tls: {} - middlewares: - - auth@kubernetescrd - - strip@kubernetescrd -``` - -```toml tab="File (TOML)" -[entryPoints] - [entryPoints.web] - address = ":80" - [entryPoints.web.http] - [entryPoints.web.http.redirections] - entryPoint = "websecure" - scheme = "https" - permanent = true - [entryPoints.web.observability] - accessLogs = false - metrics = false - tracing = false - - [entryPoints.websecure] - address = ":443" - [entryPoints.websecure.tls] - [entryPoints.websecure.middlewares] + tls: {} + middlewares: - auth@kubernetescrd - strip@kubernetescrd ``` @@ -70,9 +43,6 @@ additionalArguments: - --entryPoints.web.http.redirections.to=websecure - --entryPoints.web.http.redirections.scheme=https - --entryPoints.web.http.redirections.permanent=true - - --entryPoints.web.observability.accessLogs=false - - --entryPoints.web.observability.metrics=false - - --entryPoints.web.observability.tracing=false ``` !!! tip @@ -84,40 +54,39 @@ additionalArguments: ## Configuration Options -| Field | Description | Default | Required | -|:----------------------------------------------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:------------------------|:---------| -| `address` | Define the port, and optionally the hostname, on which to listen for incoming connections and packets.
It also defines the protocol to use (TCP or UDP).
If no protocol is specified, the default is TCP. The format is:`[host]:port[/tcp\|/udp] | - | Yes | -| `asDefault` | Mark the `entryPoint` to be in the list of default `entryPoints`.
`entryPoints`in this list are used (by default) on HTTP and TCP routers that do not define their own `entryPoints` option.
More information [here](#asdefault). | false | No | -| `forwardedHeaders.trustedIPs` | Set the IPs or CIDR from where Traefik trusts the forwarded headers information (`X-Forwarded-*`). | - | No | -| `forwardedHeaders.insecure` | Set the insecure mode to always trust the forwarded headers information (`X-Forwarded-*`).
We recommend to use this option only for tests purposes, not in production. | false | No | -| `http.redirections.`
`entryPoint.to`
| The target element to enable (permanent) redirecting of all incoming requests on an entry point to another one.
The target element can be an entry point name (ex: `websecure`), or a port (`:443`). | - | Yes | -| `http.redirections.`
`entryPoint.scheme`
| The target scheme to use for (permanent) redirection of all incoming requests. | https | No | -| `http.redirections.`
`entryPoint.permanent`
| Enable permanent redirecting of all incoming requests on an entry point to another one changing the scheme.
The target element, it can be an entry point name (ex: `websecure`), or a port (`:443`). | false | No | -| `http.redirections.`
`entryPoint.priority`
| Default priority applied to the routers attached to the `entryPoint`. | MaxInt32-1 (2147483646) | No | -| `http.encodeQuerySemicolons` | Enable query semicolons encoding.
Use this option to avoid non-encoded semicolons to be interpreted as query parameter separators by Traefik.
When using this option, the non-encoded semicolons characters in query will be transmitted encoded to the backend.
More information [here](#encodequerysemicolons). | false | No | -| `http.sanitizePath` | Defines whether to enable the request path sanitization.
More information [here](#sanitizepath). | false | No | -| `http.middlewares` | Set the list of middlewares that are prepended by default to the list of middlewares of each router associated to the named entry point.
More information [here](#httpmiddlewares). | - | No | -| `http.tls` | Enable TLS on every router attached to the `entryPoint`.
If no certificate are set, a default self-signed certificate is generated by Traefik.
We recommend to not use self signed certificates in production. | - | No | -| `http.tls.options` | Apply TLS options on every router attached to the `entryPoint`.
The TLS options can be overidden per router.
More information in the [dedicated section](../../routing/providers/kubernetes-crd.md#kind-tlsoption). | - | No | -| `http.tls.certResolver` | Apply a certificate resolver on every router attached to the `entryPoint`.
The TLS options can be overidden per router.
More information in the [dedicated section](../install-configuration/tls/certificate-resolvers/overview.md). | - | No | -| `http2.maxConcurrentStreams` | Set the number of concurrent streams per connection that each client is allowed to initiate.
The value must be greater than zero. | 250 | No | -| `http3` | Enable HTTP/3 protocol on the `entryPoint`.
HTTP/3 requires a TCP `entryPoint`. as HTTP/3 always starts as a TCP connection that then gets upgraded to UDP. In most scenarios, this `entryPoint` is the same as the one used for TLS traffic.
More information [here](#http3). | - | No | -| `http3.advertisedPort` | Set the UDP port to advertise as the HTTP/3 authority.
It defaults to the entryPoint's address port.
It can be used to override the authority in the `alt-svc` header, for example if the public facing port is different from where Traefik is listening. | - | No | -| `observability.accessLogs` | Defines whether a router attached to this EntryPoint produces access-logs by default. Nonetheless, a router defining its own observability configuration will opt-out from this default. | true | No | -| `observability.metrics` | Defines whether a router attached to this EntryPoint produces metrics by default. Nonetheless, a router defining its own observability configuration will opt-out from this default. | true | No | -| `observability.tracing` | Defines whether a router attached to this EntryPoint produces traces by default. Nonetheless, a router defining its own observability configuration will opt-out from this default. | true | No | -| `observability.traceVerbosity` | Defines the tracing verbosity level for routers attached to this EntryPoint. Possible values: `minimal` (default), `detailed`. Routers can override this value in their own observability configuration.
More information [here](#traceverbosity). | minimal | No | -| `proxyProtocol.trustedIPs` | Enable PROXY protocol with Trusted IPs.
Traefik supports [PROXY protocol](https://www.haproxy.org/download/2.0/doc/proxy-protocol.txt) version 1 and 2.
If PROXY protocol header parsing is enabled for the entry point, this entry point can accept connections with or without PROXY protocol headers.
If the PROXY protocol header is passed, then the version is determined automatically.
More information [here](#proxyprotocol-and-load-balancers). | - | No | -| `proxyProtocol.insecure` | Enable PROXY protocol trusting every incoming connection.
Every remote client address will be replaced (`trustedIPs`) won't have any effect).
Traefik supports [PROXY protocol](https://www.haproxy.org/download/2.0/doc/proxy-protocol.txt) version 1 and 2.
If PROXY protocol header parsing is enabled for the entry point, this entry point can accept connections with or without PROXY protocol headers.
If the PROXY protocol header is passed, then the version is determined automatically.
We recommend to use this option only for tests purposes, not in production.
More information [here](#proxyprotocol-and-load-balancers). | - | No | -| `reusePort` | Enable `entryPoints` from the same or different processes listening on the same TCP/UDP port by utilizing the `SO_REUSEPORT` socket option.
It also allows the kernel to act like a load balancer to distribute incoming connections between entry points.
More information [here](#reuseport). | false | No | -| `transport.`
`respondingTimeouts.`
`readTimeout`
| Set the timeouts for incoming requests to the Traefik instance. This is the maximum duration for reading the entire request, including the body. 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. | 60s (seconds) | No | -| `transport.`
`respondingTimeouts.`
`writeTimeout`
| Maximum duration before timing out writes of the response.
It covers the time from the end of the request header read to the end of the response write.
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. | 0s (seconds) | No | -| `transport.`
`respondingTimeouts.`
`idleTimeout`
| Maximum duration an idle (keep-alive) connection will remain idle before closing itself.
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 | 180s (seconds) | No | -| `transport.`
`lifeCycle.`
`graceTimeOut`
| Set the duration to give active requests a chance to finish before Traefik stops.
Can be provided in a format supported by [time.ParseDuration](https://golang.org/pkg/time/#ParseDuration) or as raw values (digits).
If no units are provided, the value is parsed assuming seconds
In this time frame no new requests are accepted. | 10s (seconds) | No | -| `transport.`
`lifeCycle.`
`requestAcceptGraceTimeout`
| Set the duration to keep accepting requests prior to initiating the graceful termination period (as defined by the `transportlifeCycle.graceTimeOut` option).
This option is meant to give downstream load-balancers sufficient time to take Traefik out of rotation.
Can be provided in a format supported by [time.ParseDuration](https://golang.org/pkg/time/#ParseDuration) or as raw values (digits).
If no units are provided, the value is parsed assuming seconds | 0s (seconds) | No | -| `transport.`
`keepAliveMaxRequests`
| Set the maximum number of requests Traefik can handle before sending a `Connection: Close` header to the client (for HTTP2, Traefik sends a GOAWAY).
Zero means no limit. | 0 | No | -| `transport.`
`keepAliveMaxTime`
| Set the maximum duration Traefik can handle requests before sending a `Connection: Close` header to the client (for HTTP2, Traefik sends a GOAWAY). Zero means no limit. | 0s (seconds) | No | -| `udp.timeout` | Define how long to wait on an idle session before releasing the related resources.
The Timeout value must be greater than zero. | 3s (seconds) | No | +| Field | Description | Default | Required | +|:----------------------------------------------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:--------|:---------| +| `address` | Define the port, and optionally the hostname, on which to listen for incoming connections and packets.
It also defines the protocol to use (TCP or UDP).
If no protocol is specified, the default is TCP. The format is:`[host]:port[/tcp\|/udp]`. | - | Yes | +| `accessLogs` | Defines whether a router attached to this EntryPoint produces access-logs by default. Nonetheless, a router defining its own observability configuration will opt-out from this default. | true | No | +| `asDefault` | Mark the `entryPoint` to be in the list of default `entryPoints`.
`entryPoints`in this list are used (by default) on HTTP and TCP routers that do not define their own `entryPoints` option.
More information [here](#asdefault). | false | No | +| `forwardedHeaders.trustedIPs` | Set the IPs or CIDR from where Traefik trusts the forwarded headers information (`X-Forwarded-*`). | - | No | +| `forwardedHeaders.insecure` | Set the insecure mode to always trust the forwarded headers information (`X-Forwarded-*`).
We recommend to use this option only for tests purposes, not in production. | false | No | +| `http.redirections.`
`entryPoint.to` | The target element to enable (permanent) redirecting of all incoming requests on an entry point to another one.
The target element can be an entry point name (ex: `websecure`), or a port (`:443`). | - | Yes | +| `http.redirections.`
`entryPoint.scheme` | The target scheme to use for (permanent) redirection of all incoming requests. | https | No | +| `http.redirections.`
`entryPoint.permanent` | Enable permanent redirecting of all incoming requests on an entry point to another one changing the scheme.
The target element, it can be an entry point name (ex: `websecure`), or a port (`:443`). | false | No | +| `http.redirections.`
`entryPoint.priority` | Default priority applied to the routers attached to the `entryPoint`. | MaxInt32-1 (2147483646) | No | +| `http.encodeQuerySemicolons` | Enable query semicolons encoding.
Use this option to avoid non-encoded semicolons to be interpreted as query parameter separators by Traefik.
When using this option, the non-encoded semicolons characters in query will be transmitted encoded to the backend.
More information [here](#encodequerysemicolons). | false | No | +| `http.sanitizePath` | Defines whether to enable the request path sanitization.
More information [here](#sanitizepath). | false | No | +| `http.middlewares` | Set the list of middlewares that are prepended by default to the list of middlewares of each router associated to the named entry point.
More information [here](#httpmiddlewares). | - | No | +| `http.tls` | Enable TLS on every router attached to the `entryPoint`.
If no certificate are set, a default self-signed certificate is generates by Traefik.
We recommend to not use self signed certificates in production. | - | No | +| `http.tls.options` | Apply TLS options on every router attached to the `entryPoint`.
The TLS options can be overidden per router.
More information in the [dedicated section](../../routing/providers/kubernetes-crd.md#kind-tlsoption). | - | No | +| `http.tls.certResolver` | Apply a certificate resolver on every router attached to the `entryPoint`.
The TLS options can be overidden per router.
More information in the [dedicated section](../install-configuration/tls/certificate-resolvers/overview.md). | - | No | +| `http2.maxConcurrentStreams` | Set the number of concurrent streams per connection that each client is allowed to initiate.
The value must be greater than zero. | 250 | No | +| `http3` | Enable HTTP/3 protocol on the `entryPoint`.
HTTP/3 requires a TCP `entryPoint`. as HTTP/3 always starts as a TCP connection that then gets upgraded to UDP. In most scenarios, this `entryPoint` is the same as the one used for TLS traffic.
More information [here](#http3. | - | No | +| `http3.advertisedPort` | Set the UDP port to advertise as the HTTP/3 authority.
It defaults to the entryPoint's address port.
It can be used to override the authority in the `alt-svc` header, for example if the public facing port is different from where Traefik is listening. | - | No | +| `metrics` | Defines whether a router attached to this EntryPoint produces metrics by default. Nonetheless, a router defining its own observability configuration will opt-out from this default. | true | No | +| `proxyProtocol.trustedIPs` | Enable PROXY protocol with Trusted IPs.
Traefik supports [PROXY protocol](https://www.haproxy.org/download/2.0/doc/proxy-protocol.txt) version 1 and 2.
If PROXY protocol header parsing is enabled for the entry point, this entry point can accept connections with or without PROXY protocol headers.
If the PROXY protocol header is passed, then the version is determined automatically.
More information [here](#proxyprotocol-and-load-balancers). | - | No | +| `proxyProtocol.insecure` | Enable PROXY protocol trusting every incoming connection.
Every remote client address will be replaced (`trustedIPs`) won't have any effect).
Traefik supports [PROXY protocol](https://www.haproxy.org/download/2.0/doc/proxy-protocol.txt) version 1 and 2.
If PROXY protocol header parsing is enabled for the entry point, this entry point can accept connections with or without PROXY protocol headers.
If the PROXY protocol header is passed, then the version is determined automatically.
We recommend to use this option only for tests purposes, not in production.
More information [here](#proxyprotocol-and-load-balancers). | - | No | +| `reusePort` | Enable `entryPoints` from the same or different processes listening on the same TCP/UDP port by utilizing the `SO_REUSEPORT` socket option.
It also allows the kernel to act like a load balancer to distribute incoming connections between entry points..
More information [here](#reuseport). | false | No | +| `tracing` | Defines whether a router attached to this EntryPoint produces traces by default. Nonetheless, a router defining its own observability configuration will opt-out from this default. | true | No | +| `transport.`
`respondingTimeouts.`
`readTimeout` | Set the timeouts for incoming requests to the Traefik instance. This is the maximum duration for reading the entire request, including the body. 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. | 60s (seconds) | No | +| `transport.`
`respondingTimeouts.`
`writeTimeout` | Maximum duration before timing out writes of the response.
It covers the time from the end of the request header read to the end of the response write.
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. | 0s (seconds) | No | +| `transport.`
`respondingTimeouts.`
`idleTimeout` | Maximum duration an idle (keep-alive) connection will remain idle before closing itself.
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 | 180s (seconds) | No | +| `transport.`
`lifeCycle.`
`graceTimeOut` | Set the duration to give active requests a chance to finish before Traefik stops.
Can be provided in a format supported by [time.ParseDuration](https://golang.org/pkg/time/#ParseDuration) or as raw values (digits).
If no units are provided, the value is parsed assuming seconds
In this time frame no new requests are accepted. | 10s (seconds) | No | +| `transport.`
`lifeCycle.`
`requestAcceptGraceTimeout` | Set the duration to keep accepting requests prior to initiating the graceful termination period (as defined by the `transportlifeCycle.graceTimeOut` option).
This option is meant to give downstream load-balancers sufficient time to take Traefik out of rotation.
Can be provided in a format supported by [time.ParseDuration](https://golang.org/pkg/time/#ParseDuration) or as raw values (digits).
If no units are provided, the value is parsed assuming seconds | 0s (seconds) | No | +| `transport.`
`keepAliveMaxRequests` | Set the maximum number of requests Traefik can handle before sending a `Connection: Close` header to the client (for HTTP2, Traefik sends a GOAWAY).
Zero means no limit. | 0 | No | +| `transport.`
`keepAliveMaxTime` | Set the maximum duration Traefik can handle requests before sending a `Connection: Close` header to the client (for HTTP2, Traefik sends a GOAWAY). Zero means no limit. | 0s (seconds) | No | +| `udp.timeout` | Define how long to wait on an idle session before releasing the related resources.
The Timeout value must be greater than zero. | 3s (seconds)| No | ### asDefault @@ -152,20 +121,18 @@ are applied after the ones declared on the Entrypoint) entryPoints: web: address: :80 - http: - middlewares: - - auth@kubernetescrd - - strip@file + middlewares: + - auth@kubernetescrd + - strip@file ``` ```yaml tab="Helm Chart Values" ports: web: port: :80 - http: - middlewares: - - auth@kubernetescrd - - strip@file + middlewares: + - auth@kubernetescrd + - strip@file ``` ### encodeQuerySemicolons @@ -174,10 +141,10 @@ Behavior examples: | EncodeQuerySemicolons | Request Query | Resulting Request Query | |-----------------------|---------------------|-------------------------| -| false | foo=bar;baz=bar | foo=bar&baz=bar | -| true | foo=bar;baz=bar | foo=bar%3Bbaz=bar | -| false | foo=bar&baz=bar;foo | foo=bar&baz=bar&foo | -| true | foo=bar&baz=bar;foo | foo=bar&baz=bar%3Bfoo | +| false | foo=bar;baz=bar | foo=bar&baz=bar | +| true | foo=bar;baz=bar | foo=bar%3Bbaz=bar | +| false | foo=bar&baz=bar;foo | foo=bar&baz=bar&foo | +| true | foo=bar&baz=bar;foo | foo=bar&baz=bar%3Bfoo | ### SanitizePath @@ -197,14 +164,14 @@ it can lead to unsafe routing when the `sanitizePath` option is set to `false`. | SanitizePath | Request Path | Resulting Request Path | |--------------|-----------------|------------------------| -| false | /./foo/bar | /./foo/bar | -| true | /./foo/bar | /foo/bar | -| false | /foo/../bar | /foo/../bar | -| true | /foo/../bar | /bar | -| false | /foo/bar// | /foo/bar// | -| true | /foo/bar// | /foo/bar/ | -| false | /./foo/../bar// | /./foo/../bar// | -| true | /./foo/../bar// | /bar/ | +| false | /./foo/bar | /./foo/bar | +| true | /./foo/bar | /foo/bar | +| false | /foo/../bar | /foo/../bar | +| true | /foo/../bar | /bar | +| false | /foo/bar// | /foo/bar// | +| true | /foo/bar// | /foo/bar/ | +| false | /./foo/../bar// | /./foo/../bar// | +| true | /./foo/../bar// | /bar/ | ### HTTP3 @@ -217,7 +184,7 @@ only routers with TLS enabled will be usable with HTTP/3. ### ProxyProtocol and Load-Balancers -The replacement of the remote client address will occur only for IP addresses listed in `trustedIPs`. This is where you specify your load balancer IPs or CIDR ranges. +The replacement of the remote client address will occur only for IP addresses listed in `trustedIPs`. This is where yoÃĨu specify your load balancer IPs or CIDR ranges. When queuing Traefik behind another load-balancer, make sure to configure PROXY protocol on both sides. @@ -275,13 +242,3 @@ Use the `reusePort` option with the other option `transport.lifeCycle.gracetimeo to do canary deployments against Traefik itself. Like upgrading Traefik version or reloading the static configuration without any service downtime. - -#### Trace Verbosity - -`observability.traceVerbosity` defines the tracing verbosity level for routers attached to this EntryPoint. -Routers can override this value in their own observability configuration. - -Possible values are: - -- `minimal`: produces a single server span and one client span for each request processed by a router. -- `detailed`: enables the creation of additional spans for each middleware executed for each request processed by a router. diff --git a/docs/content/reference/install-configuration/observability/healthcheck.md b/docs/content/reference/install-configuration/observability/healthcheck.md index d207095c1..742480286 100644 --- a/docs/content/reference/install-configuration/observability/healthcheck.md +++ b/docs/content/reference/install-configuration/observability/healthcheck.md @@ -36,7 +36,7 @@ whose default value is `traefik` (port `8080`). | Path | Method | Description | |---------|---------------|-----------------------------------------------------------------------------------------------------| -| `/ping` | `GET`, `HEAD` | An endpoint to check for Traefik process liveness. Return a code `200` with the content: `OK` | +| `/ping` | `GET`, `HEAD` | An endpoint to check for Traefik process liveness. Return a code `200` with the content: `OK` | ### Configuration Example @@ -58,9 +58,9 @@ ping: {} | Field | Description | Default | Required | |:------|:----------------------------------------------------------|:---------------------|:---------| -| `ping.entryPoint` | Enables `/ping` on a dedicated EntryPoint. | traefik | No | -| `ping.manualRouting` | Disables the default internal router in order to allow one to create a custom router for the `ping@internal` service when set to `true`. | false | No | -| `ping.terminatingStatusCode` | Defines the status code for the ping handler during a graceful shut down. See more information [here](#terminatingstatuscode) | 503 | No | +| `ping.entryPoint` | Enables `/ping` on a dedicated EntryPoint. | traefik | No | +| `ping.manualRouting` | Disables the default internal router in order to allow one to create a custom router for the `ping@internal` service when set to `true`. | false | No | +| `ping.terminatingStatusCode` | Defines the status code for the ping handler during a graceful shut down. See more information [here](#terminatingstatuscode) | 503 | No | #### `terminatingStatusCode` diff --git a/docs/content/reference/install-configuration/observability/healthcheck/cli.md b/docs/content/reference/install-configuration/observability/healthcheck/cli.md deleted file mode 100644 index a68f80d83..000000000 --- a/docs/content/reference/install-configuration/observability/healthcheck/cli.md +++ /dev/null @@ -1,28 +0,0 @@ ---- -title: "Traefik Health Check CLI Command Documentation" -description: "In Traefik Proxy, the healthcheck CLI command lets you check the health of your Traefik instances. Read the technical documentation for configuration examples and options." ---- - -# Healthcheck Command - -Checking the Health of your Traefik Instances. -{: .subtitle } - -## Usage - -The healthcheck command allows you to make a request to the `/ping` endpoint (defined in the install (static) configuration) to check the health of Traefik. Its exit status is `0` if Traefik is healthy and `1` otherwise. - -This can be used with [HEALTHCHECK](https://docs.docker.com/engine/reference/builder/#healthcheck) instruction or any other health check orchestration mechanism. - -```sh -traefik healthcheck [command] [flags] [arguments] -``` - -Example: - -```sh -$ traefik healthcheck -OK: http://:8082/ping -``` - -The command uses the [ping](./ping.md) endpoint that is defined in the Traefik install (static) configuration. diff --git a/docs/content/reference/install-configuration/observability/healthcheck/ping.md b/docs/content/reference/install-configuration/observability/healthcheck/ping.md deleted file mode 100644 index 7b8372129..000000000 --- a/docs/content/reference/install-configuration/observability/healthcheck/ping.md +++ /dev/null @@ -1,66 +0,0 @@ ---- -title: "Traefik Ping Option Documentation" -description: "In Traefik Proxy, the option Ping lets you check the health of your Traefik instances. Read the technical documentation for configuration examples and options." ---- - -# Ping - -Checking the Health of your Traefik Instances -{: .subtitle } - -The `ping` options allows you to enable the ping endpoint to check Traefik liveness. - -The ping endpoint is reachable using the path `/ping` and the methods `GET`and `HEAD`. - -If the Traefik instance is alive, it returns the `200` HTTP code with the content: `OK`. - -## Configuration Example - -To enable the API handler: - -```yaml tab="File (YAML)" -ping: {} -``` - -```toml tab="File (TOML)" -[ping] -``` - -```bash tab="CLI" ---ping=true -``` - -## Configuration Options - -The `ping` option is defined in the install (static) configuration. -You can define it using the same [configuration methods](../../boot-environment.md#configuration-methods) as Traefik. - -| Field | Description | Default | Required | -|:------|:----------------------------------------------------------|:---------------------|:---------| -| `ping.entryPoint` | Enables `/ping` on a dedicated EntryPoint. | traefik | No | -| `ping.manualRouting` | Disables the default internal router in order to allow one to create a custom router for the `ping@internal` service when set to `true`. | false | No | -| `ping.terminatingStatusCode` | Defines the status code for the ping handler during a graceful shut down. See more information [here](#terminatingstatuscode) | 503 | No | - -### `terminatingStatusCode` - -During the period in which Traefik is gracefully shutting down, the ping handler -returns a `503` status code by default. -If Traefik is behind, for example a load-balancer -doing health checks (such as the Kubernetes LivenessProbe), another code might -be expected as the signal for graceful termination. -In that case, the terminatingStatusCode can be used to set the code returned by the ping -handler during termination. - -```yaml tab="File (YAML)" -ping: - terminatingStatusCode: 204 -``` - -```toml tab="File (TOML)" -[ping] - terminatingStatusCode = 204 -``` - -```bash tab="CLI" ---ping.terminatingStatusCode=204 -``` diff --git a/docs/content/reference/install-configuration/observability/logs-and-accesslogs.md b/docs/content/reference/install-configuration/observability/logs-and-accesslogs.md index 7fab52d14..d13226ad4 100644 --- a/docs/content/reference/install-configuration/observability/logs-and-accesslogs.md +++ b/docs/content/reference/install-configuration/observability/logs-and-accesslogs.md @@ -35,112 +35,17 @@ The section below describe how to configure Traefik logs using the static config | Field | Description | Default | Required | |:-----------|:----------------------------|:--------|:---------| -| `log.filePath` | By default, the logs are written to the standard output.
You can configure a file path instead using the `filePath` option. When `filePath` is specified, Traefik will write logs only to that file (not to standard output).| - | No | -| `log.format` | Log format (`common`or `json`).
The fields displayed with the format `common` cannot be customized. | "common" | No | -| `log.level` | Log level (`TRACE`, `DEBUG`, `INFO`, `WARN`, `ERROR`, `FATAL`, and `PANIC`)| ERROR | No | -| `log.noColor` | When using the format `common`, disables the colorized output. | false | No | -| `log.maxSize` | Maximum size in megabytes of the log file before it gets rotated. | 100MB | No | -| `log.maxAge` | Maximum number of days to retain old log files based on the timestamp encoded in their filename.
A day is defined as 24 hours and may not exactly correspond to calendar days due to daylight savings, leap seconds, etc.
By default files are not removed based on their age. | 0 | No | -| `log.maxBackups` | Maximum number of old log files to retain.
The default is to retain all old log files. | 0 | No | -| `log.compress` | Compress log files in gzip after rotation. | false | No | - -### OpenTelemetry - -Traefik supports OpenTelemetry for logging. To enable OpenTelemetry, you need to set the following in the static configuration: - -```yaml tab="File (YAML)" -experimental: - otlpLogs: true -``` - -```toml tab="File (TOML)" -[experimental] - otlpLogs = true -``` - -```sh tab="CLI" ---experimental.otlpLogs=true -``` - -!!! warning - This is an experimental feature. - -!!! note "Stdio logs remain available" - When OTLP logging is enabled, standard output (stdio) logs are still available and will continue to be written alongside OTLP exports. - -#### Configuration Example - -```yaml tab="File (YAML)" -experimental: - otlpLogs: true - -log: - otlp: - http: - endpoint: https://collector:4318/v1/logs - headers: - Authorization: Bearer auth_asKXRhIMplM7El1JENjrotGouS1LYRdL -``` - -```toml tab="File (TOML)" -[experimental] - otlpLogs = true - -[log.otlp] - http.endpoint = "https://collector:4318/v1/logs" - http.headers.Authorization = "Bearer auth_asKXRhIMplM7El1JENjrotGouS1LYRdL" -``` - -```sh tab="CLI" ---experimental.otlpLogs=true ---log.otlp.http.endpoint=https://collector:4318/v1/logs ---log.otlp.http.headers.Authorization=Bearer auth_asKXRhIMplM7El1JENjrotGouS1LYRdL -``` - -#### Configuration Options - -| Field | Description | Default | Required | -|:---------------------------------------|:---------------------------------------------------------------------------------------------------------------------------------------|:---------------------------------|:---------| -| `log.otlp.serviceName` | Service name used in selected backend. | "traefik" | No | -| `log.otlp.resourceAttributes` | Defines additional resource attributes to be sent to the collector. See [resourceAttributes](#resourceattributes) for details. | [] | No | -| `log.otlp.http` | This instructs the exporter to send logs to the OpenTelemetry Collector using HTTP. | | No | -| `log.otlp.http.endpoint` | The endpoint of the OpenTelemetry Collector. (format=`://:`) | `https://localhost:4318/v1/logs` | No | -| `log.otlp.http.headers` | Additional headers sent with logs by the exporter to the OpenTelemetry Collector. | [ ] | No | -| `log.otlp.http.tls` | Defines the Client TLS configuration used by the exporter to send logs to the OpenTelemetry Collector. | | No | -| `log.otlp.http.tls.ca` | The path to the certificate authority used for the secure connection to the OpenTelemetry Collector, it defaults to the system bundle. | | No | -| `log.otlp.http.tls.cert` | The path to the certificate to use for the OpenTelemetry Collector. | | No | -| `log.otlp.http.tls.key` | The path to the key to use for the OpenTelemetry Collector. | | No | -| `log.otlp.http.tls.insecureSkipVerify` | Instructs the OpenTelemetry Collector to accept any certificate presented by the server regardless of the hostname in the certificate. | false | No | -| `log.otlp.grpc` | This instructs the exporter to send logs to the OpenTelemetry Collector using gRPC. | | No | -| `log.otlp.grpc.endpoint` | The endpoint of the OpenTelemetry Collector. (format=`:`) | `localhost:4317` | No | -| `log.otlp.grpc.headers` | Additional headers sent with logs by the exporter to the OpenTelemetry Collector. | [ ] | No | -| `log.otlp.grpc.insecure` | Instructs the exporter to send logs to the OpenTelemetry Collector using an insecure protocol. | false | No | -| `log.otlp.grpc.tls` | Defines the Client TLS configuration used by the exporter to send logs to the OpenTelemetry Collector. | | No | -| `log.otlp.grpc.tls.ca` | The path to the certificate authority used for the secure connection to the OpenTelemetry Collector, it defaults to the system bundle. | | No | -| `log.otlp.grpc.tls.cert` | The path to the certificate to use for the OpenTelemetry Collector. | | No | -| `log.otlp.grpc.tls.key` | The path to the key to use for the OpenTelemetry Collector. | | No | -| `log.otlp.grpc.tls.insecureSkipVerify` | Instructs the OpenTelemetry Collector to accept any certificate presented by the server regardless of the hostname in the certificate. | false | No | - -#### resourceAttributes - -The `resourceAttributes` option allows setting the resource attributes sent along the traces. -Traefik also supports the `OTEL_RESOURCE_ATTRIBUTES` env variable to set up the resource attributes. - -!!! info "Kubernetes Resource Attributes Detection" - - Additionally, Traefik automatically discovers the following [Kubernetes resource attributes](https://opentelemetry.io/docs/specs/semconv/non-normative/k8s-attributes/) when running in a Kubernetes cluster: - - - `k8s.namespace.name` - - `k8s.pod.uid` - - `k8s.pod.name` - - Note that this automatic detection can fail, like if the Traefik pod is running in host network mode. - In this case, you should provide the attributes with the option or the env variable. +| `log.filePath` | By default, the logs are written to the standard output.
You can configure a file path instead using the `filePath` option.| - | No | +| `log.format` | Log format (`common`or `json`).
The fields displayed with the format `common` cannot be customized. | "common" | No | +| `log.level` | Log level (`TRACE`, `DEBUG`, `INFO`, `WARN`, `ERROR`, `FATAL`, and `PANIC`)| ERROR | No | +| `log.noColor` | When using the format `common`, disables the colorized output. | false | No | +| `log.maxSize` | Maximum size in megabytes of the log file before it gets rotated. | 100MB | No | +| `log.maxAge` | Maximum number of days to retain old log files based on the timestamp encoded in their filename.
A day is defined as 24 hours and may not exactly correspond to calendar days due to daylight savings, leap seconds, etc.
By default files are not removed based on their age. | 0 | No | +| `log.maxBackups` | Maximum number of old log files to retain.
The default is to retain all old log files. | 0 | No | +| `log.compress` | Compress log files in gzip after rotation. | false | No | ## AccessLogs -Access logs concern everything that happens to the requests handled by Traefik. - ### Configuration Example ```yaml tab="File (YAML)" @@ -212,112 +117,21 @@ The section below describes how to configure Traefik access logs using the stati | Field | Description | Default | Required | |:-----------|:--------------------------|:--------|:---------| -| `accesslog.filePath` | By default, the access logs are written to the standard output.
You can configure a file path instead using the `filePath` option.| | No | -| `accesslog.format` | By default, logs are written using the Traefik Common Log Format (CLF).
Available formats: [`common`](#traefik-clf-format-fields) (Traefik extended CLF), [`genericCLF`](#generic-clf-format-fields) (standard CLF compatible with analyzers), or [`json`](#json-format-fields).
If the given format is unsupported, the default (`common`) is used instead. | "common" | No | -| `accesslog.bufferingSize` | To write the logs in an asynchronous fashion, specify a `bufferingSize` option.
This option represents the number of log lines Traefik will keep in memory before writing them to the selected output.
In some cases, this option can greatly help performances.| 0 | No | -| `accesslog.addInternals` | Enables access logs for internal resources (e.g.: `ping@internal`). | false | No | -| `accesslog.filters.statusCodes` | Limit the access logs to requests with a status codes in the specified range. | [ ] | No | -| `accesslog.filters.retryAttempts` | Keep the access logs when at least one retry has happened. | false | No | -| `accesslog.filters.minDuration` | Keep access logs when requests take longer than the specified duration (provided in seconds or as a valid duration format, see [time.ParseDuration](https://golang.org/pkg/time/#ParseDuration)). | 0 | No | -| `accesslog.fields.defaultMode` | Mode to apply by default to the access logs fields (`keep`, `redact` or `drop`). | keep | No | -| `accesslog.fields.names` | Set the fields list to display in the access logs (format `name:mode`).
Available fields list [here](#json-format-fields). | [ ] | No | -| `accesslog.fields.headers.defaultMode` | Mode to apply by default to the access logs headers (`keep`, `redact` or `drop`). | drop | No | -| `accesslog.fields.headers.names` | Set the headers list to display in the access logs (format `name:mode`). | [ ] | No | +| `accesslog.filePath` | By default, the access logs are written to the standard output.
You can configure a file path instead using the `filePath` option.| | No | +| `accesslog.format` | 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.
More information about CLF fields [here](#clf-format-fields). | "common" | No | +| `accesslog.bufferingSize` | To write the logs in an asynchronous fashion, specify a `bufferingSize` option.
This option represents the number of log lines Traefik will keep in memory before writing them to the selected output.
In some cases, this option can greatly help performances.| 0 | No | +| `accesslog.addInternals` | Enables access logs for internal resources (e.g.: `ping@internal`). | false | No | +| `accesslog.filters.statusCodes` | Limit the access logs to requests with a status codes in the specified range. | false | No | +| `accesslog.filters.retryAttempts` | Keep the access logs when at least one retry has happened. | false | No | +| `accesslog.filters.minDuration` | Keep access logs when requests take longer than the specified duration (provided in seconds or as a valid duration format, see [time.ParseDuration](https://golang.org/pkg/time/#ParseDuration)). | 0 | No | +| `accesslog.fields.defaultMode` | Mode to apply by default to the access logs fields (`keep`, `redact` or `drop`). | keep | No | +| `accesslog.fields.names` | Set the fields list to display in the access logs (format `name:mode`).
Available fields list [here](#available-fields). | - | No | +| `accesslog.headers.defaultMode` | Mode to apply by default to the access logs headers (`keep`, `redact` or `drop`). | drop | No | +| `accesslog.headers.names` | Set the headers list to display in the access logs (format `name:mode`). | - | No | -### OpenTelemetry +#### CLF format fields -Traefik supports OpenTelemetry for access logs. To enable OpenTelemetry, you need to set the following in the static configuration: - -```yaml tab="File (YAML)" -experimental: - otlpLogs: true -``` - -```toml tab="File (TOML)" -[experimental] - otlpLogs = true -``` - -```sh tab="CLI" ---experimental.otlpLogs=true -``` - -!!! warning - This is an experimental feature. - -#### Configuration Example - -```yaml tab="File (YAML)" -experimental: - otlpLogs: true - -accesslog: - otlp: - http: - endpoint: https://collector:4318/v1/logs - headers: - Authorization: Bearer auth_asKXRhIMplM7El1JENjrotGouS1LYRdL -``` - -```toml tab="File (TOML)" -[experimental] - otlpLogs = true - -[accesslog.otlp] - http.endpoint = "https://collector:4318/v1/logs" - http.headers.Authorization = "Bearer auth_asKXRhIMplM7El1JENjrotGouS1LYRdL" -``` - -```yaml tab="CLI" ---experimental.otlpLogs=true ---accesslog.otlp.http.endpoint=https://collector:4318/v1/logs ---accesslog.otlp.http.headers.Authorization=Bearer auth_asKXRhIMplM7El1JENjrotGouS1LYRdL -``` - -#### Configuration Options - -| Field | Description | Default | Required | -|:---------------------------------------------|:---------------------------------------------------------------------------------------------------------------------------------------|:---------------------------------|:---------| -| `accesslog.otlp.serviceName` | Defines the service name resource attribute. | "traefik" | No | -| `accesslog.otlp.resourceAttributes` | Defines additional resource attributes to be sent to the collector. See [resourceAttributes](#resourceattributes_1) for details. | [] | No | -| `accesslog.otlp.http` | This instructs the exporter to send access logs to the OpenTelemetry Collector using HTTP. | | No | -| `accesslog.otlp.http.endpoint` | The endpoint of the OpenTelemetry Collector. (format=`://:`) | `https://localhost:4318/v1/logs` | No | -| `accesslog.otlp.http.headers` | Additional headers sent with access logs by the exporter to the OpenTelemetry Collector. | [ ] | No | -| `accesslog.otlp.http.tls` | Defines the Client TLS configuration used by the exporter to send access logs to the OpenTelemetry Collector. | | No | -| `accesslog.otlp.http.tls.ca` | The path to the certificate authority used for the secure connection to the OpenTelemetry Collector, it defaults to the system bundle. | | No | -| `accesslog.otlp.http.tls.cert` | The path to the certificate to use for the OpenTelemetry Collector. | | No | -| `accesslog.otlp.http.tls.key` | The path to the key to use for the OpenTelemetry Collector. | | No | -| `accesslog.otlp.http.tls.insecureSkipVerify` | Instructs the OpenTelemetry Collector to accept any certificate presented by the server regardless of the hostname in the certificate. | false | No | -| `accesslog.otlp.grpc` | This instructs the exporter to send access logs to the OpenTelemetry Collector using gRPC. | | No | -| `accesslog.otlp.grpc.endpoint` | The endpoint of the OpenTelemetry Collector. (format=`:`) | `localhost:4317` | No | -| `accesslog.otlp.grpc.headers` | Additional headers sent with access logs by the exporter to the OpenTelemetry Collector. | [ ] | No | -| `accesslog.otlp.grpc.insecure` | Instructs the exporter to send access logs to the OpenTelemetry Collector using an insecure protocol. | false | No | -| `accesslog.otlp.grpc.tls` | Defines the Client TLS configuration used by the exporter to send access logs to the OpenTelemetry Collector. | | No | -| `accesslog.otlp.grpc.tls.ca` | The path to the certificate authority used for the secure connection to the OpenTelemetry Collector, it defaults to the system bundle. | | No | -| `accesslog.otlp.grpc.tls.cert` | The path to the certificate to use for the OpenTelemetry Collector. | | No | -| `accesslog.otlp.grpc.tls.key` | The path to the key to use for the OpenTelemetry Collector. | | No | -| `accesslog.otlp.grpc.tls.insecureSkipVerify` | Instructs the OpenTelemetry Collector to accept any certificate presented by the server regardless of the hostname in the certificate. | false | No | - -#### resourceAttributes - -The `resourceAttributes` option allows setting the resource attributes sent along the traces. -Traefik also supports the `OTEL_RESOURCE_ATTRIBUTES` env variable to set up the resource attributes. - -!!! info "Kubernetes Resource Attributes Detection" - - Additionally, Traefik automatically discovers the following [Kubernetes resource attributes](https://opentelemetry.io/docs/specs/semconv/non-normative/k8s-attributes/) when running in a Kubernetes cluster: - - - `k8s.namespace.name` - - `k8s.pod.uid` - - `k8s.pod.name` - - Note that this automatic detection can fail, like if the Traefik pod is running in host network mode. - In this case, you should provide the attributes with the option or the env variable. - -### Traefik CLF format fields - -It's the default format provided by Traefik. -Below the fields displayed with the Traefik CLF format: +Below the fields displayed with the CLF format: ```html - [] @@ -326,56 +140,46 @@ Below the fields displayed with the Traefik CLF format: "" "" ms ``` -### Generic CLF format fields - -Below the fields displayed with the generic CLF format: - -```html - - [] -" " -"" "" -``` - -### JSON format fields +#### Available Fields | Field | Description | |-------------------------|------------------| -| `StartUTC` | The time at which request processing started. | -| `StartLocal` | The local time at which request processing started. | -| `Duration` | The total time taken (in nanoseconds) by processing the response, including the origin server's time but not the log writing time. | -| `RouterName` | The name of the Traefik router. | -| `ServiceName` | The name of the Traefik backend. | -| `ServiceURL` | The URL of the Traefik backend. | -| `ServiceAddr` | The IP:port of the Traefik backend (extracted from `ServiceURL`). | -| `ClientAddr` | The remote address in its original form (usually IP:port). | -| `ClientHost` | The remote IP address from which the client request was received. | -| `ClientPort` | The remote TCP port from which the client request was received. | -| `ClientUsername` | The username provided in the URL, if present. | -| `RequestAddr` | The HTTP Host header (usually IP:port). This is treated as not a header by the Go API. | -| `RequestHost` | The HTTP Host server name (not including port). | -| `RequestPort` | The TCP port from the HTTP Host. | -| `RequestMethod` | The HTTP method. | -| `RequestPath` | The HTTP request URI, not including the scheme, host or port. | -| `RequestProtocol` | The version of HTTP requested. | -| `RequestScheme` | The HTTP scheme requested `http` or `https`. | -| `RequestLine` | The `RequestMethod`, + `RequestPath` and `RequestProtocol`. | -| `RequestContentSize` | The number of bytes in the request entity (a.k.a. body) sent by the client. | -| `OriginDuration` | The time taken (in nanoseconds) by the origin server ('upstream') to return its response. | -| `OriginContentSize` | The content length specified by the origin server, or 0 if unspecified. | -| `OriginStatus` | The HTTP status code returned by the origin server. If the request was handled by this Traefik instance (e.g. with a redirect), then this value will be absent (0). | -| `OriginStatusLine` | `OriginStatus` + Status code explanation | -| `DownstreamStatus` | The HTTP status code returned to the client. | -| `DownstreamStatusLine` | The `DownstreamStatus` and status code explanation. | -| `DownstreamContentSize` | The number of bytes in the response entity returned to the client. This is in addition to the "Content-Length" header, which may be present in the origin response. | -| `RequestCount` | The number of requests received since the Traefik instance started. | -| `GzipRatio` | The response body compression ratio achieved. | -| `Overhead` | The processing time overhead (in nanoseconds) caused by Traefik. | -| `RetryAttempts` | The amount of attempts the request was retried. | -| `TLSVersion` | The TLS version used by the connection (e.g. `1.2`) (if connection is TLS). | -| `TLSCipher` | The TLS cipher used by the connection (e.g. `TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA`) (if connection is TLS). | -| `TLSClientSubject` | The string representation of the TLS client certificate's Subject (e.g. `CN=username,O=organization`). | +| `StartUTC` | The time at which request processing started. | +| `StartLocal` | The local time at which request processing started. | +| `Duration` | The total time taken (in nanoseconds) by processing the response, including the origin server's time but not the log writing time. | +| `RouterName` | The name of the Traefik router. | +| `ServiceName` | The name of the Traefik backend. | +| `ServiceURL` | The URL of the Traefik backend. | +| `ServiceAddr` | The IP:port of the Traefik backend (extracted from `ServiceURL`). | +| `ClientAddr` | The remote address in its original form (usually IP:port). | +| `ClientHost` | The remote IP address from which the client request was received. | +| `ClientPort` | The remote TCP port from which the client request was received. | +| `ClientUsername` | The username provided in the URL, if present. | +| `RequestAddr` | The HTTP Host header (usually IP:port). This is treated as not a header by the Go API. | +| `RequestHost` | The HTTP Host server name (not including port). | +| `RequestPort` | The TCP port from the HTTP Host. | +| `RequestMethod` | The HTTP method. | +| `RequestPath` | The HTTP request URI, not including the scheme, host or port. | +| `RequestProtocol` | The version of HTTP requested. | +| `RequestScheme` | The HTTP scheme requested `http` or `https`. | +| `RequestLine` | The `RequestMethod`, + `RequestPath` and `RequestProtocol`. | +| `RequestContentSize` | The number of bytes in the request entity (a.k.a. body) sent by the client. | +| `OriginDuration` | The time taken (in nanoseconds) by the origin server ('upstream') to return its response. | +| `OriginContentSize` | The content length specified by the origin server, or 0 if unspecified. | +| `OriginStatus` | The HTTP status code returned by the origin server. If the request was handled by this Traefik instance (e.g. with a redirect), then this value will be absent (0). | +| `OriginStatusLine` | `OriginStatus` + Status code explanation | +| `DownstreamStatus` | The HTTP status code returned to the client. | +| `DownstreamStatusLine` | The `DownstreamStatus` and status code explanation. | +| `DownstreamContentSize` | The number of bytes in the response entity returned to the client. This is in addition to the "Content-Length" header, which may be present in the origin response. | +| `RequestCount` | The number of requests received since the Traefik instance started. | +| `GzipRatio` | The response body compression ratio achieved. | +| `Overhead` | The processing time overhead (in nanoseconds) caused by Traefik. | +| `RetryAttempts` | The amount of attempts the request was retried. | +| `TLSVersion` | The TLS version used by the connection (e.g. `1.2`) (if connection is TLS). | +| `TLSCipher` | The TLS cipher used by the connection (e.g. `TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA`) (if connection is TLS). | +| `TLSClientSubject` | The string representation of the TLS client certificate's Subject (e.g. `CN=username,O=organization`). | -### Log Rotation +#### Log Rotation Traefik close and reopen its log files, assuming they're configured, on receipt of a USR1 signal. This allows the logs to be rotated and processed by an external program, such as `logrotate`. @@ -383,7 +187,7 @@ 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. -### Time Zones +#### Time Zones Traefik will timestamp each log line in UTC time by default. @@ -395,9 +199,11 @@ It is possible to configure the Traefik to timestamp in a specific timezone by e Example utilizing Docker Compose: ```yaml +version: "3.7" + services: traefik: - image: traefik:v3.5 + image: traefik:v3.4 environment: - TZ=US/Alaska command: diff --git a/docs/content/reference/install-configuration/observability/metrics.md b/docs/content/reference/install-configuration/observability/metrics.md index 979fb230d..c2c6f4bd0 100644 --- a/docs/content/reference/install-configuration/observability/metrics.md +++ b/docs/content/reference/install-configuration/observability/metrics.md @@ -60,47 +60,29 @@ metrics: ### Configuration Options -| Field | Description | Default | Required | -|:-------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------|:---------------------------------------------------|:---------| -| `metrics.addInternals` | Enables metrics for internal resources (e.g.: `ping@internal`). | false | No | -| `metrics.otlp.serviceName` | Defines the service name resource attribute. | "traefik" | No | -| `metrics.otlp.resourceAttributes` | Defines additional resource attributes to be sent to the collector. See [resourceAttributes](#resourceattributes) for details. | [] | No | -| `metrics.otlp.addEntryPointsLabels` | Enable metrics on entry points. | true | No | -| `metrics.otlp.addRoutersLabels` | Enable metrics on routers. | false | No | -| `metrics.otlp.addServicesLabels` | Enable metrics on services. | true | No | -| `metrics.otlp.explicitBoundaries` | Explicit boundaries for Histogram data points. | ".005, .01, .025, .05, .1, .25, .5, 1, 2.5, 5, 10" | No | -| `metrics.otlp.pushInterval` | Interval at which metrics are sent to the OpenTelemetry Collector. | 10s | No | -| `metrics.otlp.http` | This instructs the exporter to send the metrics to the OpenTelemetry Collector using HTTP.
Setting the sub-options with their default values. | null/false | No | -| `metrics.otlp.http.endpoint` | URL of the OpenTelemetry Collector to send metrics to.
Format="`://:`" | "https://localhost:4318/v1/metrics" | Yes | -| `metrics.otlp.http.headers` | Additional headers sent with metrics by the exporter to the OpenTelemetry Collector. | - | No | -| `metrics.otlp.http.tls.ca` | Path to the certificate authority used for the secure connection to the OpenTelemetry Collector,
it defaults to the system bundle. | "" | No | -| `metrics.otlp.http.tls.cert` | Path to the public certificate used for the secure connection to the OpenTelemetry Collector.
When using this option, setting the `key` option is required. | "" | No | -| `metrics.otlp.http.tls.key` | This instructs the exporter to send the metrics to the OpenTelemetry Collector using HTTP.
Setting the sub-options with their default values. | null/false | No | -| `metrics.otlp.http.tls.insecureskipverify` | Allow the TLS connection to the OpenTelemetry Collector accepts any certificate presented by the server regardless of the hostnames it covers. | false | Yes | -| `metrics.otlp.grpc` | This instructs the exporter to send metrics to the OpenTelemetry Collector using gRPC. | null/false | No | -| `metrics.otlp.grpc.endpoint` | Address of the OpenTelemetry Collector to send metrics to.
Format="`:`" | "localhost:4317" | Yes | -| `metrics.otlp.grpc.headers` | Additional headers sent with metrics by the exporter to the OpenTelemetry Collector. | - | No | -| `metrics.otlp.grpc.insecure` | Allows exporter to send metrics to the OpenTelemetry Collector without using a secured protocol. | false | Yes | -| `metrics.otlp.grpc.tls.ca` | Path to the certificate authority used for the secure connection to the OpenTelemetry Collector,
it defaults to the system bundle. | - | No | -| `metrics.otlp.grpc.tls.cert` | Path to the public certificate used for the secure connection to the OpenTelemetry Collector.
When using this option, setting the `key` option is required. | - | No | -| `metrics.otlp.grpc.tls.key` | This instructs the exporter to send the metrics to the OpenTelemetry Collector using HTTP.
Setting the sub-options with their default values. | null/false | No | -| `metrics.otlp.grpc.tls.insecureskipverify` | Allow the TLS connection to the OpenTelemetry Collector accepts any certificate presented by the server regardless of the hostnames it covers. | false | Yes | - -### resourceAttributes - -The `resourceAttributes` option allows setting the resource attributes sent along the traces. -Traefik also supports the `OTEL_RESOURCE_ATTRIBUTES` env variable to set up the resource attributes. - -!!! info "Kubernetes Resource Attributes Detection" - - Additionally, Traefik automatically discovers the following [Kubernetes resource attributes](https://opentelemetry.io/docs/specs/semconv/non-normative/k8s-attributes/) when running in a Kubernetes cluster: - - - `k8s.namespace.name` - - `k8s.pod.uid` - - `k8s.pod.name` - - Note that this automatic detection can fail, like if the Traefik pod is running in host network mode. - In this case, you should provide the attributes with the option or the env variable. +| Field | Description | Default | Required | +|:-----------|---------------|:--------|:---------| +| `metrics.addInternals` | Enables metrics for internal resources (e.g.: `ping@internal`). | false | No | +| `metrics.otlp.addEntryPointsLabels` | Enable metrics on entry points. | true | No | +| `metrics.otlp.addRoutersLabels` | Enable metrics on routers. | false | No | +| `metrics.otlp.addServicesLabels` | Enable metrics on services.| true | No | +| `metrics.otlp.explicitBoundaries` | Explicit boundaries for Histogram data points. | ".005, .01, .025, .05, .1, .25, .5, 1, 2.5, 5, 10" | No | +| `metrics.otlp.pushInterval` | Interval at which metrics are sent to the OpenTelemetry Collector. | 10s | No | +| `metrics.otlp.http` | This instructs the exporter to send the metrics to the OpenTelemetry Collector using HTTP.
Setting the sub-options with their default values. | null/false | No | +| `metrics.otlp.http.endpoint` | URL of the OpenTelemetry Collector to send metrics to.
Format="`://:`" | "http://localhost:4318/v1/metrics" | Yes | +| `metrics.otlp.http.headers` | Additional headers sent with metrics by the exporter to the OpenTelemetry Collector. | - | No | +| `metrics.otlp.http.tls.ca` | Path to the certificate authority used for the secure connection to the OpenTelemetry Collector,
it defaults to the system bundle. | "" | No | +| `metrics.otlp.http.tls.cert` | Path to the public certificate used for the secure connection to the OpenTelemetry Collector.
When using this option, setting the `key` option is required. | "" | No | +| `metrics.otlp.http.tls.key` | This instructs the exporter to send the metrics to the OpenTelemetry Collector using HTTP.
Setting the sub-options with their default values. | null/false | No | +| `metrics.otlp.http.tls.insecureskipverify` | Allow the TLS connection to the OpenTelemetry Collector accepts any certificate presented by the server regardless of the hostnames it covers. | false | Yes | +| `metrics.otlp.grpc` | This instructs the exporter to send metrics to the OpenTelemetry Collector using gRPC. | null/false | No | +| `metrics.otlp.grpc.endpoint` | Address of the OpenTelemetry Collector to send metrics to.
Format="`:`" | "localhost:4317" | Yes | +| `metrics.otlp.grpc.headers` | Additional headers sent with metrics by the exporter to the OpenTelemetry Collector. | - | No | +| `metrics.otlp.http.grpc.insecure` |Allows exporter to send metrics to the OpenTelemetry Collector without using a secured protocol. | false | Yes | +| `metrics.otlp.grpc.tls.ca` | Path to the certificate authority used for the secure connection to the OpenTelemetry Collector,
it defaults to the system bundle. | - | No | +| `metrics.otlp.grpc.tls.cert` | Path to the public certificate used for the secure connection to the OpenTelemetry Collector.
When using this option, setting the `key` option is required. | - | No | +| `metrics.otlp.grpc.tls.key` | This instructs the exporter to send the metrics to the OpenTelemetry Collector using HTTP.
Setting the sub-options with their default values. | null/false | No | +| `metrics.otlp.grpc.tls.insecureskipverify` | Allow the TLS connection to the OpenTelemetry Collector accepts any certificate presented by the server regardless of the hostnames it covers. | false | Yes | ## Vendors @@ -128,13 +110,13 @@ metrics: | Field | Description | Default | Required | |:------|:-------------------------------|:---------------------|:---------| -| `metrics.addInternals` | Enables metrics for internal resources (e.g.: `ping@internal`). | false | No | -| `datadog.address` | Defines the address for the exporter to send metrics to datadog-agent. More information [here](#address)| `127.0.0.1:8125` | Yes | -| `datadog.addEntryPointsLabels` | Enable metrics on entry points. | true | No | -| `datadog.addRoutersLabels` | Enable metrics on routers. | false | No | -| `datadog.addServicesLabels` | Enable metrics on services. | true | No | -| `datadog.pushInterval` | Defines the interval used by the exporter to push metrics to datadog-agent. | 10s | No | -| `datadog.prefix` | Defines the prefix to use for metrics collection. | "traefik" | No | +| `metrics.addInternals` | Enables metrics for internal resources (e.g.: `ping@internal`). | false | No | +| `datadog.address` | Defines the address for the exporter to send metrics to datadog-agent. More information [here](#address)| `127.0.0.1:8125` | Yes | +| `datadog.addEntryPointsLabels` | Enable metrics on entry points. | true | No | +| `datadog.addRoutersLabels` | Enable metrics on routers. | false | No | +| `datadog.addServicesLabels` | Enable metrics on services. | true | No | +| `datadog.pushInterval` | Defines the interval used by the exporter to push metrics to datadog-agent. | 10s | No | +| `datadog.prefix` | Defines the prefix to use for metrics collection. | "traefik" | No | ##### `address` @@ -186,16 +168,16 @@ metrics: | Field | Description | Default | Required | |:-----------|-------------------------|:--------|:---------| -| `metrics.addInternal` | Enables metrics for internal resources (e.g.: `ping@internal`). | false | No | -| `metrics.influxDB2.addEntryPointsLabels` | Enable metrics on entry points. | true | No | -| `metrics.influxDB2.addRoutersLabels` | Enable metrics on routers. | false | No | -| `metrics.influxDB2.addServicesLabels` | Enable metrics on services.| true | No | -| `metrics.influxDB2.additionalLabels` | Additional labels (InfluxDB tags) on all metrics. | - | No | -| `metrics.influxDB2.pushInterval` | The interval used by the exporter to push metrics to InfluxDB server. | 10s | No | -| `metrics.influxDB2.address` | Address of the InfluxDB v2 instance. | "http://localhost:8086" | Yes | -| `metrics.influxDB2.token` | Token with which to connect to InfluxDB v2. | - | Yes | -| `metrics.influxDB2.org` | Organisation where metrics will be stored. | - | Yes | -| `metrics.influxDB2.bucket` | Bucket where metrics will be stored. | - | Yes | +| `metrics.addInternal` | Enables metrics for internal resources (e.g.: `ping@internal`). | false | No | +| `metrics.influxDB2.addEntryPointsLabels` | Enable metrics on entry points. | true | No | +| `metrics.influxDB2.addRoutersLabels` | Enable metrics on routers. | false | No | +| `metrics.influxDB2.addServicesLabels` | Enable metrics on services.| true | No | +| `metrics.influxDB2.additionalLabels` | Additional labels (InfluxDB tags) on all metrics. | - | No | +| `metrics.influxDB2.pushInterval` | The interval used by the exporter to push metrics to InfluxDB server. | 10s | No | +| `metrics.influxDB2.address` | Address of the InfluxDB v2 instance. | "http://localhost:8086" | Yes | +| `metrics.influxDB2.token` | Token with which to connect to InfluxDB v2. | - | Yes | +| `metrics.influxDB2.org` | Organisation where metrics will be stored. | - | Yes | +| `metrics.influxDB2.bucket` | Bucket where metrics will be stored. | - | Yes | ### Prometheus @@ -231,14 +213,14 @@ metrics: | Field | Description | Default | Required | |:-----------|---------------------|:--------|:---------| -| `metrics.addInternals` | Enables metrics for internal resources (e.g.: `ping@internals`). | false | No | -| `metrics.prometheus.addEntryPointsLabels` | Enable metrics on entry points. | true | No | -| `metrics.prometheus.addRoutersLabels` | Enable metrics on routers. | false | No | -| `metrics.prometheus.addServicesLabels` | Enable metrics on services.| true | No | -| `metrics.prometheus.buckets` | Buckets for latency metrics. |"0.100000, 0.300000, 1.200000, 5.000000" | No | -| `metrics.prometheus.manualRouting` | Set to _true_, it disables the default internal router in order to allow creating a custom router for the `prometheus@internal` service. | false | No | -| `metrics.prometheus.entryPoint` | Traefik Entrypoint name used to expose metrics. | "traefik" | No | -| `metrics.prometheus.headerLabels` | Defines extra labels extracted from request headers for the `requests_total` metrics.
More information [here](#headerlabels). | | Yes | +| `metrics.prometheus.addInternals` | Enables metrics for internal resources (e.g.: `ping@internals`). | false | No | +| `metrics.prometheus.addEntryPointsLabels` | Enable metrics on entry points. | true | No | +| `metrics.prometheus.addRoutersLabels` | Enable metrics on routers. | false | No | +| `metrics.prometheus.addServicesLabels` | Enable metrics on services.| true | No | +| `metrics.prometheus.buckets` | Buckets for latency metrics. |"0.100000, 0.300000, 1.200000, 5.000000" | No | +| `metrics.prometheus.manualRouting` | Set to _true_, it disables the default internal router in order to allow creating a custom router for the `prometheus@internal` service. | false | No | +| `metrics.prometheus.entryPoint` | Traefik Entrypoint name used to expose metrics. | "traefik" | No | +| `metrics.prometheus.headerLabels` | Defines extra labels extracted from request headers for the `requests_total` metrics.
More information [here](#headerlabels). | | Yes | ##### headerLabels @@ -304,13 +286,13 @@ metrics: | Field | Description | Default | Required | |:-----------|:-------------------------|:--------|:---------| -| `metrics.addInternals` | Enables metrics for internal resources (e.g.: `ping@internals`). | false | No | -| `metrics.statsD.addEntryPointsLabels` | Enable metrics on entry points. | true | No | -| `metrics.statsD.addRoutersLabels` | Enable metrics on routers. | false | No | -| `metrics.statsD.addServicesLabels` | Enable metrics on services.| true | No | -| `metrics.statsD.pushInterval` | The interval used by the exporter to push metrics to DataDog server. | 10s | No | -| `metrics.statsD.address` | Address instructs exporter to send metrics to statsd at this address. | "127.0.0.1:8125" | Yes | -| `metrics.statsD.prefix` | The prefix to use for metrics collection. | "traefik" | No | +| `metrics.addInternals` | Enables metrics for internal resources (e.g.: `ping@internals`). | false | No | +| `metrics.statsD.addEntryPointsLabels` | Enable metrics on entry points. | true | No | +| `metrics.statsD.addRoutersLabels` | Enable metrics on routers. | false | No | +| `metrics.statsD.addServicesLabels` | Enable metrics on services.| true | No | +| `metrics.statsD.pushInterval` | The interval used by the exporter to push metrics to DataDog server. | 10s | No | +| `metrics.statsD.address` | Address instructs exporter to send metrics to statsd at this address. | "127.0.0.1:8125" | Yes | +| `metrics.statsD.prefix` | The prefix to use for metrics collection. | "traefik" | No | ## Metrics Provided @@ -319,42 +301,42 @@ metrics: === "OpenTelemetry" | Metric | Type | [Labels](#labels) | Description | |----------------------------|-------|--------------------------|--------------------------------------------------------------------| - | `traefik_config_reloads_total` | Count | | The total count of configuration reloads. | - | `traefik_config_last_reload_success` | Gauge | | The timestamp of the last configuration reload success. | - | `traefik_open_connections` | Gauge | `entrypoint`, `protocol` | The current count of open connections, by entrypoint and protocol. | - | `traefik_tls_certs_not_after` | Gauge | | The expiration date of certificates. | + | `traefik_config_reloads_total` | Count | | The total count of configuration reloads. | + | `traefik_config_last_reload_success` | Gauge | | The timestamp of the last configuration reload success. | + | `traefik_open_connections` | Gauge | `entrypoint`, `protocol` | The current count of open connections, by entrypoint and protocol. | + | `traefik_tls_certs_not_after` | Gauge | | The expiration date of certificates. | === "Prometheus" | Metric | Type | [Labels](#labels) | Description | |----------------------------|-------|--------------------------|--------------------------------------------------------------------| - | `traefik_config_reloads_total` | Count | | The total count of configuration reloads. | - | `traefik_config_last_reload_success` | Gauge | | The timestamp of the last configuration reload success. | - | `traefik_open_connections` | Gauge | `entrypoint`, `protocol` | The current count of open connections, by entrypoint and protocol. | - | `traefik_tls_certs_not_after` | Gauge | | The expiration date of certificates. | + | `traefik_config_reloads_total` | Count | | The total count of configuration reloads. | + | `traefik_config_last_reload_success` | Gauge | | The timestamp of the last configuration reload success. | + | `traefik_open_connections` | Gauge | `entrypoint`, `protocol` | The current count of open connections, by entrypoint and protocol. | + | `traefik_tls_certs_not_after` | Gauge | | The expiration date of certificates. | === "Datadog" | Metric | Type | [Labels](#labels) | Description | |----------------------------|-------|--------------------------|--------------------------------------------------------------------| - | `config.reload.total` | Count | | The total count of configuration reloads. | - | `config.reload.lastSuccessTimestamp` | Gauge | | The timestamp of the last configuration reload success. | - | `open.connections` | Gauge | `entrypoint`, `protocol` | The current count of open connections, by entrypoint and protocol. | - | `tls.certs.notAfterTimestamp` | Gauge | | The expiration date of certificates. | + | `config.reload.total` | Count | | The total count of configuration reloads. | + | `config.reload.lastSuccessTimestamp` | Gauge | | The timestamp of the last configuration reload success. | + | `open.connections` | Gauge | `entrypoint`, `protocol` | The current count of open connections, by entrypoint and protocol. | + | `tls.certs.notAfterTimestamp` | Gauge | | The expiration date of certificates. | === "InfluxDB2" | Metric | Type | [Labels](#labels) | Description | |----------------------------|-------|--------------------------|--------------------------------------------------------------------| - | `traefik.config.reload.total` | Count | | The total count of configuration reloads. | - | `traefik.config.reload.lastSuccessTimestamp` | Gauge | | The timestamp of the last configuration reload success. | - | `traefik.open.connections` | Gauge | `entrypoint`, `protocol` | The current count of open connections, by entrypoint and protocol. | - | `traefik.tls.certs.notAfterTimestamp` | Gauge | | The expiration date of certificates. | + | `traefik.config.reload.total` | Count | | The total count of configuration reloads. | + | `traefik.config.reload.lastSuccessTimestamp` | Gauge | | The timestamp of the last configuration reload success. | + | `traefik.open.connections` | Gauge | `entrypoint`, `protocol` | The current count of open connections, by entrypoint and protocol. | + | `traefik.tls.certs.notAfterTimestamp` | Gauge | | The expiration date of certificates. | === "StatsD" | Metric | Type | [Labels](#labels) | Description | |----------------------------|-------|--------------------------|--------------------------------------------------------------------| - | `{prefix}.config.reload.total` | Count | | The total count of configuration reloads. | - | `{prefix}.config.reload.lastSuccessTimestamp` | Gauge | | The timestamp of the last configuration reload success. | - | `{prefix}.open.connections` | Gauge | `entrypoint`, `protocol` | The current count of open connections, by entrypoint and protocol. | - | `{prefix}.tls.certs.notAfterTimestamp` | Gauge | | The expiration date of certificates. | + | `{prefix}.config.reload.total` | Count | | The total count of configuration reloads. | + | `{prefix}.config.reload.lastSuccessTimestamp` | Gauge | | The timestamp of the last configuration reload success. | + | `{prefix}.open.connections` | Gauge | `entrypoint`, `protocol` | The current count of open connections, by entrypoint and protocol. | + | `{prefix}.tls.certs.notAfterTimestamp` | Gauge | | The expiration date of certificates. | !!! note "\{prefix\} Default Value" By default, \{prefix\} value is `traefik`. @@ -365,8 +347,8 @@ Here is a comprehensive list of labels that are provided by the global metrics: | Label | Description | example | |--------------|----------------------------------------|----------------------| -| `entrypoint` | Entrypoint that handled the connection | "example_entrypoint" | -| `protocol` | Connection protocol | "TCP" | +| `entrypoint` | Entrypoint that handled the connection | "example_entrypoint" | +| `protocol` | Connection protocol | "TCP" | ### OpenTelemetry Semantic Conventions @@ -376,7 +358,7 @@ Traefik Proxy follows [official OpenTelemetry semantic conventions v1.23.1](http | Metric | Type | [Labels](#labels) | Description | |----------|-----------|-------------------------|------------------| -| `http.server.request.duration` | Histogram | `error.type`, `http.request.method`, `http.response.status_code`, `network.protocol.name`, `server.address`, `server.port`, `url.scheme` | Duration of HTTP server requests | +| `http.server.request.duration` | Histogram | `error.type`, `http.request.method`, `http.response.status_code`, `network.protocol.name`, `server.address`, `server.port`, `url.scheme` | Duration of HTTP server requests | ##### Labels @@ -384,35 +366,35 @@ Here is a comprehensive list of labels that are provided by the metrics: | Label | Description | example | |-----------------------------|--------|---------------| -| `error.type` | Describes a class of error the operation ended with | "500" | -| `http.request.method` | HTTP request method | "GET" | -| `http.response.status_code` | HTTP response status code | "200" | -| `network.protocol.name` | OSI application layer or non-OSI equivalent | "http/1.1" | -| `network.protocol.version` | Version of the protocol specified in `network.protocol.name` | "1.1" | -| `server.address` | Name of the local HTTP server that received the request | "example.com" | -| `server.port` | Port of the local HTTP server that received the request | "80" | -| `url.scheme` | The URI scheme component identifying the used protocol | "http" | +| `error.type` | Describes a class of error the operation ended with | "500" | +| `http.request.method` | HTTP request method | "GET" | +| `http.response.status_code` | HTTP response status code | "200" | +| `network.protocol.name` | OSI application layer or non-OSI equivalent | "http/1.1" | +| `network.protocol.version` | Version of the protocol specified in `network.protocol.name` | "1.1" | +| `server.address` | Name of the local HTTP server that received the request | "example.com" | +| `server.port` | Port of the local HTTP server that received the request | "80" | +| `url.scheme` | The URI scheme component identifying the used protocol | "http" | #### HTTP Client | Metric | Type | [Labels](#labels) | Description | |-------------------------------|-----------|-----------------|--------| -| `http.client.request.duration` | Histogram | `error.type`, `http.request.method`, `http.response.status_code`, `network.protocol.name`, `server.address`, `server.port`, `url.scheme` | Duration of HTTP client requests | +| `http.client.request.duration` | Histogram | `error.type`, `http.request.method`, `http.response.status_code`, `network.protocol.name`, `server.address`, `server.port`, `url.scheme` | Duration of HTTP client requests | ##### Labels Here is a comprehensive list of labels that are provided by the metrics: -| Label | Description | example | -|----------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------|---------------| -| `error.type` | Describes a class of error the operation ended with | "500" | -| `http.request.method` | HTTP request method | "GET" | -| `http.response.status_code` | HTTP response status code | "200" | -| `network.protocol.name` | OSI application layer or non-OSI equivalent | "http/1.1" | -| `network.protocol.version` | Version of the protocol specified in `network.protocol.name` | "1.1" | -| `server.address` | Name of the local HTTP server that received the request | "example.com" | -| `server.port` | Port of the local HTTP server that received the request | "80" | -| `url.scheme` | The URI scheme component identifying the used protocol | "http" | +| Label | Description | example | +|------ -----|------------|---------------| +| `error.type` | Describes a class of error the operation ended with | "500" | +| `http.request.method` | HTTP request method | "GET" | +| `http.response.status_code` | HTTP response status code | "200" | +| `network.protocol.name` | OSI application layer or non-OSI equivalent | "http/1.1" | +| `network.protocol.version` | Version of the protocol specified in `network.protocol.name` | "1.1" | +| `server.address` | Name of the local HTTP server that received the request | "example.com" | +| `server.port` | Port of the local HTTP server that received the request | "80" | +| `url.scheme` | The URI scheme component identifying the used protocol | "http" | ### HTTP Metrics @@ -424,51 +406,51 @@ On top of the official OpenTelemetry semantic conventions, Traefik provides its | Metric | Type | [Labels](#labels) | Description | |-----------------------|-----------|--------------------|--------------------------| - | `traefik_entrypoint_requests_total` | Count | `code`, `method`, `protocol`, `entrypoint` | The total count of HTTP requests received by an entrypoint. | - | `traefik_entrypoint_requests_tls_total` | Count | `tls_version`, `tls_cipher`, `entrypoint` | The total count of HTTPS requests received by an entrypoint. | - | `traefik_entrypoint_request_duration_seconds` | Histogram | `code`, `method`, `protocol`, `entrypoint` | Request processing duration histogram on an entrypoint. | - | `traefik_entrypoint_requests_bytes_total` | Count | `code`, `method`, `protocol`, `entrypoint` | The total size of HTTP requests in bytes handled by an entrypoint. | - | `traefik_entrypoint_responses_bytes_total` | Count | `code`, `method`, `protocol`, `entrypoint` | The total size of HTTP responses in bytes handled by an entrypoint. | + | `traefik_entrypoint_requests_total` | Count | `code`, `method`, `protocol`, `entrypoint` | The total count of HTTP requests received by an entrypoint. | + | `traefik_entrypoint_requests_tls_total` | Count | `tls_version`, `tls_cipher`, `entrypoint` | The total count of HTTPS requests received by an entrypoint. | + | `traefik_entrypoint_request_duration_seconds` | Histogram | `code`, `method`, `protocol`, `entrypoint` | Request processing duration histogram on an entrypoint. | + | `traefik_entrypoint_requests_bytes_total` | Count | `code`, `method`, `protocol`, `entrypoint` | The total size of HTTP requests in bytes handled by an entrypoint. | + | `traefik_entrypoint_responses_bytes_total` | Count | `code`, `method`, `protocol`, `entrypoint` | The total size of HTTP responses in bytes handled by an entrypoint. | === "Prometheus" | Metric | Type | [Labels](#labels) | Description | |-----------------------|-----------|------------------------|-------------------------| - | `traefik_entrypoint_requests_total` | Count | `code`, `method`, `protocol`, `entrypoint` | The total count of HTTP requests received by an entrypoint. | - | `traefik_entrypoint_requests_tls_total` | Count | `tls_version`, `tls_cipher`, `entrypoint` | The total count of HTTPS requests received by an entrypoint. | - | `traefik_entrypoint_request_duration_seconds` | Histogram | `code`, `method`, `protocol`, `entrypoint` | Request processing duration histogram on an entrypoint. | - | `traefik_entrypoint_requests_bytes_total` | Count | `code`, `method`, `protocol`, `entrypoint` | The total size of HTTP requests in bytes handled by an entrypoint. | - | `traefik_entrypoint_responses_bytes_total` | Count | `code`, `method`, `protocol`, `entrypoint` | The total size of HTTP responses in bytes handled by an entrypoint. | + | `traefik_entrypoint_requests_total` | Count | `code`, `method`, `protocol`, `entrypoint` | The total count of HTTP requests received by an entrypoint. | + | `traefik_entrypoint_requests_tls_total` | Count | `tls_version`, `tls_cipher`, `entrypoint` | The total count of HTTPS requests received by an entrypoint. | + | `traefik_entrypoint_request_duration_seconds` | Histogram | `code`, `method`, `protocol`, `entrypoint` | Request processing duration histogram on an entrypoint. | + | `traefik_entrypoint_requests_bytes_total` | Count | `code`, `method`, `protocol`, `entrypoint` | The total size of HTTP requests in bytes handled by an entrypoint. | + | `traefik_entrypoint_responses_bytes_total` | Count | `code`, `method`, `protocol`, `entrypoint` | The total size of HTTP responses in bytes handled by an entrypoint. | === "Datadog" | Metric | Type | [Labels](#labels) | Description | |-----------------------|-----------|------------------|---------------------------| - | `entrypoint.requests.total` | Count | `code`, `method`, `protocol`, `entrypoint` | The total count of HTTP requests received by an entrypoint. | - | `entrypoint.requests.tls.total` | Count | `tls_version`, `tls_cipher`, `entrypoint` | The total count of HTTPS requests received by an entrypoint. | - | `entrypoint.request.duration.seconds` | Histogram | `code`, `method`, `protocol`, `entrypoint` | Request processing duration histogram on an entrypoint. | - | `entrypoint.requests.bytes.total` | Count | `code`, `method`, `protocol`, `entrypoint` | The total size of HTTP requests in bytes handled by an entrypoint. | - | `entrypoint.responses.bytes.total` | Count | `code`, `method`, `protocol`, `entrypoint` | The total size of HTTP responses in bytes handled by an entrypoint. | + | `entrypoint.requests.total` | Count | `code`, `method`, `protocol`, `entrypoint` | The total count of HTTP requests received by an entrypoint. | + | `entrypoint.requests.tls.total` | Count | `tls_version`, `tls_cipher`, `entrypoint` | The total count of HTTPS requests received by an entrypoint. | + | `entrypoint.request.duration.seconds` | Histogram | `code`, `method`, `protocol`, `entrypoint` | Request processing duration histogram on an entrypoint. | + | `entrypoint.requests.bytes.total` | Count | `code`, `method`, `protocol`, `entrypoint` | The total size of HTTP requests in bytes handled by an entrypoint. | + | `entrypoint.responses.bytes.total` | Count | `code`, `method`, `protocol`, `entrypoint` | The total size of HTTP responses in bytes handled by an entrypoint. | === "InfluxDB2" | Metric | Type | [Labels](#labels) | Description | |------------|-----------|-------------------|-----------------| - | `traefik.entrypoint.requests.total` | Count | `code`, `method`, `protocol`, `entrypoint` | The total count of HTTP requests received by an entrypoint. | - | `traefik.entrypoint.requests.tls.total` | Count | `tls_version`, `tls_cipher`, `entrypoint` | The total count of HTTPS requests received by an entrypoint. | - | `traefik.entrypoint.request.duration.seconds` | Histogram | `code`, `method`, `protocol`, `entrypoint` | Request processing duration histogram on an entrypoint. | - | `traefik.entrypoint.requests.bytes.total` | Count | `code`, `method`, `protocol`, `entrypoint` | The total size of HTTP requests in bytes handled by an entrypoint. | - | `traefik.entrypoint.responses.bytes.total` | Count | `code`, `method`, `protocol`, `entrypoint` | The total size of HTTP responses in bytes handled by an entrypoint. | + | `traefik.entrypoint.requests.total` | Count | `code`, `method`, `protocol`, `entrypoint` | The total count of HTTP requests received by an entrypoint. | + | `traefik.entrypoint.requests.tls.total` | Count | `tls_version`, `tls_cipher`, `entrypoint` | The total count of HTTPS requests received by an entrypoint. | + | `traefik.entrypoint.request.duration.seconds` | Histogram | `code`, `method`, `protocol`, `entrypoint` | Request processing duration histogram on an entrypoint. | + | `traefik.entrypoint.requests.bytes.total` | Count | `code`, `method`, `protocol`, `entrypoint` | The total size of HTTP requests in bytes handled by an entrypoint. | + | `traefik.entrypoint.responses.bytes.total` | Count | `code`, `method`, `protocol`, `entrypoint` | The total size of HTTP responses in bytes handled by an entrypoint. | === "StatsD" | Metric | Type | [Labels](#labels) | Description | |----------------------------|-------|--------------------------|--------------------------------------------------------------------| - | `{prefix}.entrypoint.requests.total` | Count | `code`, `method`, `protocol`, `entrypoint` | The total count of HTTP requests received by an entrypoint. | - | `{prefix}.entrypoint.requests.tls.total` | Count | `tls_version`, `tls_cipher`, `entrypoint` | The total count of HTTPS requests received by an entrypoint. | - | `{prefix}.entrypoint.request.duration.seconds` | Histogram | `code`, `method`, `protocol`, `entrypoint` | Request processing duration histogram on an entrypoint. | - | `{prefix}.entrypoint.requests.bytes.total` | Count | `code`, `method`, `protocol`, `entrypoint` | The total size of HTTP requests in bytes handled by an entrypoint. | - | `{prefix}.entrypoint.responses.bytes.total` | Count | `code`, `method`, `protocol`, `entrypoint` | The total size of HTTP responses in bytes handled by an entrypoint. | + | `{prefix}.entrypoint.requests.total` | Count | `code`, `method`, `protocol`, `entrypoint` | The total count of HTTP requests received by an entrypoint. | + | `{prefix}.entrypoint.requests.tls.total` | Count | `tls_version`, `tls_cipher`, `entrypoint` | The total count of HTTPS requests received by an entrypoint. | + | `{prefix}.entrypoint.request.duration.seconds` | Histogram | `code`, `method`, `protocol`, `entrypoint` | Request processing duration histogram on an entrypoint. | + | `{prefix}.entrypoint.requests.bytes.total` | Count | `code`, `method`, `protocol`, `entrypoint` | The total size of HTTP requests in bytes handled by an entrypoint. | + | `{prefix}.entrypoint.responses.bytes.total` | Count | `code`, `method`, `protocol`, `entrypoint` | The total size of HTTP responses in bytes handled by an entrypoint. | !!! note "\{prefix\} Default Value" By default, \{prefix\} value is `traefik`. @@ -479,51 +461,51 @@ On top of the official OpenTelemetry semantic conventions, Traefik provides its | Metric | Type | [Labels](#labels) | Description | |-----------------------|-----------|----------------------|--------------------------------| - | `traefik_router_requests_total` | Count | `code`, `method`, `protocol`, `router`, `service` | The total count of HTTP requests handled by a router. | - | `traefik_router_requests_tls_total` | Count | `tls_version`, `tls_cipher`, `router`, `service` | The total count of HTTPS requests handled by a router. | - | `traefik_router_request_duration_seconds` | Histogram | `code`, `method`, `protocol`, `router`, `service` | Request processing duration histogram on a router. | - | `traefik_router_requests_bytes_total` | Count | `code`, `method`, `protocol`, `router`, `service` | The total size of HTTP requests in bytes handled by a router. | - | `traefik_router_responses_bytes_total` | Count | `code`, `method`, `protocol`, `router`, `service` | The total size of HTTP responses in bytes handled by a router. | + | `traefik_router_requests_total` | Count | `code`, `method`, `protocol`, `router`, `service` | The total count of HTTP requests handled by a router. | + | `traefik_router_requests_tls_total` | Count | `tls_version`, `tls_cipher`, `router`, `service` | The total count of HTTPS requests handled by a router. | + | `traefik_router_request_duration_seconds` | Histogram | `code`, `method`, `protocol`, `router`, `service` | Request processing duration histogram on a router. | + | `traefik_router_requests_bytes_total` | Count | `code`, `method`, `protocol`, `router`, `service` | The total size of HTTP requests in bytes handled by a router. | + | `traefik_router_responses_bytes_total` | Count | `code`, `method`, `protocol`, `router`, `service` | The total size of HTTP responses in bytes handled by a router. | === "Prometheus" | Metric | Type | [Labels](#labels) | Description | |-----------------------|-----------|---------------------------------------------------|----------------------------------------------------------------| - | `traefik_router_requests_total` | Count | `code`, `method`, `protocol`, `router`, `service` | The total count of HTTP requests handled by a router. | - | `traefik_router_requests_tls_total` | Count | `tls_version`, `tls_cipher`, `router`, `service` | The total count of HTTPS requests handled by a router. | - | `traefik_router_request_duration_seconds` | Histogram | `code`, `method`, `protocol`, `router`, `service` | Request processing duration histogram on a router. | - | `traefik_router_requests_bytes_total` | Count | `code`, `method`, `protocol`, `router`, `service` | The total size of HTTP requests in bytes handled by a router. | - | `traefik_router_responses_bytes_total` | Count | `code`, `method`, `protocol`, `router`, `service` | The total size of HTTP responses in bytes handled by a router. | + | `traefik_router_requests_total` | Count | `code`, `method`, `protocol`, `router`, `service` | The total count of HTTP requests handled by a router. | + | `traefik_router_requests_tls_total` | Count | `tls_version`, `tls_cipher`, `router`, `service` | The total count of HTTPS requests handled by a router. | + | `traefik_router_request_duration_seconds` | Histogram | `code`, `method`, `protocol`, `router`, `service` | Request processing duration histogram on a router. | + | `traefik_router_requests_bytes_total` | Count | `code`, `method`, `protocol`, `router`, `service` | The total size of HTTP requests in bytes handled by a router. | + | `traefik_router_responses_bytes_total` | Count | `code`, `method`, `protocol`, `router`, `service` | The total size of HTTP responses in bytes handled by a router. | === "Datadog" | Metric | Type | [Labels](#labels) | Description | |-------------|-----------|---------------|---------------------| - | `router.requests.total` | Count | `code`, `method`, `protocol`, `router`, `service` | The total count of HTTP requests handled by a router. | - | `router.requests.tls.total` | Count | `tls_version`, `tls_cipher`, `router`, `service` | The total count of HTTPS requests handled by a router. | - | `router.request.duration.seconds` | Histogram | `code`, `method`, `protocol`, `router`, `service` | Request processing duration histogram on a router. | - | `router.requests.bytes.total` | Count | `code`, `method`, `protocol`, `router`, `service` | The total size of HTTP requests in bytes handled by a router. | - | `router.responses.bytes.total` | Count | `code`, `method`, `protocol`, `router`, `service` | The total size of HTTP responses in bytes handled by a router. | + | `router.requests.total` | Count | `code`, `method`, `protocol`, `router`, `service` | The total count of HTTP requests handled by a router. | + | `router.requests.tls.total` | Count | `tls_version`, `tls_cipher`, `router`, `service` | The total count of HTTPS requests handled by a router. | + | `router.request.duration.seconds` | Histogram | `code`, `method`, `protocol`, `router`, `service` | Request processing duration histogram on a router. | + | `router.requests.bytes.total` | Count | `code`, `method`, `protocol`, `router`, `service` | The total size of HTTP requests in bytes handled by a router. | + | `router.responses.bytes.total` | Count | `code`, `method`, `protocol`, `router`, `service` | The total size of HTTP responses in bytes handled by a router. | === "InfluxDB2" | Metric | Type | [Labels](#labels) | Description | |-----------------------|-----------|---------------------------------------------------|----------------------------------------------------------------| - | `traefik.router.requests.total` | Count | `code`, `method`, `protocol`, `router`, `service` | The total count of HTTP requests handled by a router. | - | `traefik.router.requests.tls.total` | Count | `tls_version`, `tls_cipher`, `router`, `service` | The total count of HTTPS requests handled by a router. | - | `traefik.router.request.duration.seconds` | Histogram | `code`, `method`, `protocol`, `router`, `service` | Request processing duration histogram on a router. | - | `traefik.router.requests.bytes.total` | Count | `code`, `method`, `protocol`, `router`, `service` | The total size of HTTP requests in bytes handled by a router. | - | `traefik.router.responses.bytes.total` | Count | `code`, `method`, `protocol`, `router`, `service` | The total size of HTTP responses in bytes handled by a router. | + | `traefik.router.requests.total` | Count | `code`, `method`, `protocol`, `router`, `service` | The total count of HTTP requests handled by a router. | + | `traefik.router.requests.tls.total` | Count | `tls_version`, `tls_cipher`, `router`, `service` | The total count of HTTPS requests handled by a router. | + | `traefik.router.request.duration.seconds` | Histogram | `code`, `method`, `protocol`, `router`, `service` | Request processing duration histogram on a router. | + | `traefik.router.requests.bytes.total` | Count | `code`, `method`, `protocol`, `router`, `service` | The total size of HTTP requests in bytes handled by a router. | + | `traefik.router.responses.bytes.total` | Count | `code`, `method`, `protocol`, `router`, `service` | The total size of HTTP responses in bytes handled by a router. | === "StatsD" | Metric | Type | [Labels](#labels) | Description | |-----------------------|-----------|---------------|-------------| - | `{prefix}.router.requests.total` | Count | `code`, `method`, `protocol`, `router`, `service` | The total count of HTTP requests handled by a router. | - | `{prefix}.router.requests.tls.total` | Count | `tls_version`, `tls_cipher`, `router`, `service` | The total count of HTTPS requests handled by a router. | - | `{prefix}.router.request.duration.seconds` | Histogram | `code`, `method`, `protocol`, `router`, `service` | Request processing duration histogram on a router. | - | `{prefix}.router.requests.bytes.total` | Count | `code`, `method`, `protocol`, `router`, `service` | The total size of HTTP requests in bytes handled by a router. | - | `{prefix}.router.responses.bytes.total` | Count | `code`, `method`, `protocol`, `router`, `service` | The total size of HTTP responses in bytes handled by a router. | + | `{prefix}.router.requests.total` | Count | `code`, `method`, `protocol`, `router`, `service` | The total count of HTTP requests handled by a router. | + | `{prefix}.router.requests.tls.total` | Count | `tls_version`, `tls_cipher`, `router`, `service` | The total count of HTTPS requests handled by a router. | + | `{prefix}.router.request.duration.seconds` | Histogram | `code`, `method`, `protocol`, `router`, `service` | Request processing duration histogram on a router. | + | `{prefix}.router.requests.bytes.total` | Count | `code`, `method`, `protocol`, `router`, `service` | The total size of HTTP requests in bytes handled by a router. | + | `{prefix}.router.responses.bytes.total` | Count | `code`, `method`, `protocol`, `router`, `service` | The total size of HTTP responses in bytes handled by a router. | !!! note "\{prefix\} Default Value" By default, \{prefix\} value is `traefik`. @@ -534,61 +516,61 @@ On top of the official OpenTelemetry semantic conventions, Traefik provides its | Metric | Type | Labels | Description | |-----------------------|-----------|------------|------------| - | `traefik_service_requests_total` | Count | `code`, `method`, `protocol`, `service` | The total count of HTTP requests processed on a service. | - | `traefik_service_requests_tls_total` | Count | `tls_version`, `tls_cipher`, `service` | The total count of HTTPS requests processed on a service. | - | `traefik_service_request_duration_seconds` | Histogram | `code`, `method`, `protocol`, `service` | Request processing duration histogram on a service. | - | `traefik_service_retries_total` | Count | `service` | The count of requests retries on a service. | - | `traefik_service_server_up` | Gauge | `service`, `url` | Current service's server status, 0 for a down or 1 for up. Only for services configured with healthcheck. | - | `traefik_service_requests_bytes_total` | Count | `code`, `method`, `protocol`, `service` | The total size of requests in bytes received by a service. | - | `traefik_service_responses_bytes_total` | Count | `code`, `method`, `protocol`, `service` | The total size of responses in bytes returned by a service. | + | `traefik_service_requests_total` | Count | `code`, `method`, `protocol`, `service` | The total count of HTTP requests processed on a service. | + | `traefik_service_requests_tls_total` | Count | `tls_version`, `tls_cipher`, `service` | The total count of HTTPS requests processed on a service. | + | `traefik_service_request_duration_seconds` | Histogram | `code`, `method`, `protocol`, `service` | Request processing duration histogram on a service. | + | `traefik_service_retries_total` | Count | `service` | The count of requests retries on a service. | + | `traefik_service_server_up` | Gauge | `service`, `url` | Current service's server status, 0 for a down or 1 for up. | + | `traefik_service_requests_bytes_total` | Count | `code`, `method`, `protocol`, `service` | The total size of requests in bytes received by a service. | + | `traefik_service_responses_bytes_total` | Count | `code`, `method`, `protocol`, `service` | The total size of responses in bytes returned by a service. | === "Prometheus" | Metric | Type | Labels | Description | |-----------------------|-----------|-------|------------| - | `traefik_service_requests_total` | Count | `code`, `method`, `protocol`, `service` | The total count of HTTP requests processed on a service. | - | `traefik_service_requests_tls_total` | Count | `tls_version`, `tls_cipher`, `service` | The total count of HTTPS requests processed on a service. | - | `traefik_service_request_duration_seconds` | Histogram | `code`, `method`, `protocol`, `service` | Request processing duration histogram on a service. | - | `traefik_service_retries_total` | Count | `service` | The count of requests retries on a service. | - | `traefik_service_server_up` | Gauge | `service`, `url` | Current service's server status, 0 for a down or 1 for up. Only for services configured with healthcheck. | - | `traefik_service_requests_bytes_total` | Count | `code`, `method`, `protocol`, `service` | The total size of requests in bytes received by a service. | - | `traefik_service_responses_bytes_total` | Count | `code`, `method`, `protocol`, `service` | The total size of responses in bytes returned by a service. | + | `traefik_service_requests_total` | Count | `code`, `method`, `protocol`, `service` | The total count of HTTP requests processed on a service. | + | `traefik_service_requests_tls_total` | Count | `tls_version`, `tls_cipher`, `service` | The total count of HTTPS requests processed on a service. | + | `traefik_service_request_duration_seconds` | Histogram | `code`, `method`, `protocol`, `service` | Request processing duration histogram on a service. | + | `traefik_service_retries_total` | Count | `service` | The count of requests retries on a service. | + | `traefik_service_server_up` | Gauge | `service`, `url` | Current service's server status, 0 for a down or 1 for up. | + | `traefik_service_requests_bytes_total` | Count | `code`, `method`, `protocol`, `service` | The total size of requests in bytes received by a service. | + | `traefik_service_responses_bytes_total` | Count | `code`, `method`, `protocol`, `service` | The total size of responses in bytes returned by a service. | === "Datadog" | Metric | Type | Labels | Description | |-----------------------|-----------|--------|------------------| - | `service.requests.total` | Count | `code`, `method`, `protocol`, `service` | The total count of HTTP requests processed on a service. | - | `router.service.tls.total` | Count | `tls_version`, `tls_cipher`, `service` | The total count of HTTPS requests processed on a service. | - | `service.request.duration.seconds` | Histogram | `code`, `method`, `protocol`, `service` | Request processing duration histogram on a service. | - | `service.retries.total` | Count | `service` | The count of requests retries on a service. | - | `service.server.up` | Gauge | `service`, `url` | Current service's server status, 0 for a down or 1 for up. Only for services configured with healthcheck. | - | `service.requests.bytes.total` | Count | `code`, `method`, `protocol`, `service` | The total size of requests in bytes received by a service. | - | `service.responses.bytes.total` | Count | `code`, `method`, `protocol`, `service` | The total size of responses in bytes returned by a service. | + | `service.requests.total` | Count | `code`, `method`, `protocol`, `service` | The total count of HTTP requests processed on a service. | + | `router.service.tls.total` | Count | `tls_version`, `tls_cipher`, `service` | The total count of HTTPS requests processed on a service. | + | `service.request.duration.seconds` | Histogram | `code`, `method`, `protocol`, `service` | Request processing duration histogram on a service. | + | `service.retries.total` | Count | `service` | The count of requests retries on a service. | + | `service.server.up` | Gauge | `service`, `url` | Current service's server status, 0 for a down or 1 for up. | + | `service.requests.bytes.total` | Count | `code`, `method`, `protocol`, `service` | The total size of requests in bytes received by a service. | + | `service.responses.bytes.total` | Count | `code`, `method`, `protocol`, `service` | The total size of responses in bytes returned by a service. | === "InfluxDB2" | Metric | Type | Labels | Description | |-----------------------|-----------|-----------------------------------------|-------------------------------------------------------------| - | `traefik.service.requests.total` | Count | `code`, `method`, `protocol`, `service` | The total count of HTTP requests processed on a service. | - | `traefik.service.requests.tls.total` | Count | `tls_version`, `tls_cipher`, `service` | The total count of HTTPS requests processed on a service. | - | `traefik.service.request.duration.seconds` | Histogram | `code`, `method`, `protocol`, `service` | Request processing duration histogram on a service. | - | `traefik.service.retries.total` | Count | `service` | The count of requests retries on a service. | - | `traefik.service.server.up` | Gauge | `service`, `url` | Current service's server status, 0 for a down or 1 for up. Only for services configured with healthcheck. | - | `traefik.service.requests.bytes.total` | Count | `code`, `method`, `protocol`, `service` | The total size of requests in bytes received by a service. | - | `traefik.service.responses.bytes.total` | Count | `code`, `method`, `protocol`, `service` | The total size of responses in bytes returned by a service. | + | `traefik.service.requests.total` | Count | `code`, `method`, `protocol`, `service` | The total count of HTTP requests processed on a service. | + | `traefik.service.requests.tls.total` | Count | `tls_version`, `tls_cipher`, `service` | The total count of HTTPS requests processed on a service. | + | `traefik.service.request.duration.seconds` | Histogram | `code`, `method`, `protocol`, `service` | Request processing duration histogram on a service. | + | `traefik.service.retries.total` | Count | `service` | The count of requests retries on a service. | + | `traefik.service.server.up` | Gauge | `service`, `url` | Current service's server status, 0 for a down or 1 for up. | + | `traefik.service.requests.bytes.total` | Count | `code`, `method`, `protocol`, `service` | The total size of requests in bytes received by a service. | + | `traefik.service.responses.bytes.total` | Count | `code`, `method`, `protocol`, `service` | The total size of responses in bytes returned by a service. | === "StatsD" | Metric | Type | Labels | Description | |-----------------------|-----------|-----|---------| - | `{prefix}.service.requests.total` | Count | `code`, `method`, `protocol`, `service` | The total count of HTTP requests processed on a service. | - | `{prefix}.service.requests.tls.total` | Count | `tls_version`, `tls_cipher`, `service` | The total count of HTTPS requests processed on a service. | - | `{prefix}.service.request.duration.seconds` | Histogram | `code`, `method`, `protocol`, `service` | Request processing duration histogram on a service. | - | `{prefix}.service.retries.total` | Count | `service` | The count of requests retries on a service. | - | `{prefix}.service.server.up` | Gauge | `service`, `url` | Current service's server status, 0 for a down or 1 for up. Only for services configured with healthcheck. | - | `{prefix}.service.requests.bytes.total` | Count | `code`, `method`, `protocol`, `service` | The total size of requests in bytes received by a service. | - | `{prefix}.service.responses.bytes.total` | Count | `code`, `method`, `protocol`, `service` | The total size of responses in bytes returned by a service. | + | `{prefix}.service.requests.total` | Count | `code`, `method`, `protocol`, `service` | The total count of HTTP requests processed on a service. | + | `{prefix}.service.requests.tls.total` | Count | `tls_version`, `tls_cipher`, `service` | The total count of HTTPS requests processed on a service. | + | `{prefix}.service.request.duration.seconds` | Histogram | `code`, `method`, `protocol`, `service` | Request processing duration histogram on a service. | + | `{prefix}.service.retries.total` | Count | `service` | The count of requests retries on a service. | + | `{prefix}.service.server.up` | Gauge | `service`, `url` | Current service's server status, 0 for a down or 1 for up. | + | `{prefix}.service.requests.bytes.total` | Count | `code`, `method`, `protocol`, `service` | The total size of requests in bytes received by a service. | + | `{prefix}.service.responses.bytes.total` | Count | `code`, `method`, `protocol`, `service` | The total size of responses in bytes returned by a service. | !!! note "\{prefix\} Default Value" By default, \{prefix\} value is `traefik`. @@ -599,18 +581,18 @@ Here is a comprehensive list of labels that are provided by the metrics: | Label | Description | example | |---------------|-------------------|----------------------------| -| `cn` | Certificate Common Name | "example.com" | -| `code` | Request code | "200" | -| `entrypoint` | Entrypoint that handled the request | "example_entrypoint" | -| `method` | Request Method | "GET" | -| `protocol` | Request protocol | "http" | -| `router` | Router that handled the request | "example_router" | -| `sans` | Certificate Subject Alternative NameS | "example.com" | -| `serial` | Certificate Serial Number | "123..." | -| `service` | Service that handled the request | "example_service@provider" | -| `tls_cipher` | TLS cipher used for the request | "TLS_FALLBACK_SCSV" | -| `tls_version` | TLS version used for the request | "1.0" | -| `url` | Service server url | "http://example.com" | +| `cn` | Certificate Common Name | "example.com" | +| `code` | Request code | "200" | +| `entrypoint` | Entrypoint that handled the request | "example_entrypoint" | +| `method` | Request Method | "GET" | +| `protocol` | Request protocol | "http" | +| `router` | Router that handled the request | "example_router" | +| `sans` | Certificate Subject Alternative NameS | "example.com" | +| `serial` | Certificate Serial Number | "123..." | +| `service` | Service that handled the request | "example_service@provider" | +| `tls_cipher` | TLS cipher used for the request | "TLS_FALLBACK_SCSV" | +| `tls_version` | TLS version used for the request | "1.0" | +| `url` | Service server url | "http://example.com" | !!! info "`method` label value" diff --git a/docs/content/reference/install-configuration/observability/tracing.md b/docs/content/reference/install-configuration/observability/tracing.md index c3748e5f1..f7b4987c1 100644 --- a/docs/content/reference/install-configuration/observability/tracing.md +++ b/docs/content/reference/install-configuration/observability/tracing.md @@ -36,43 +36,27 @@ tracing: {} ## Configuration Options -| Field | Description | Default | Required | -|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:------------------------------------|:---------| -| `tracing.addInternals` | Enables tracing for internal resources (e.g.: `ping@internal`). | false | No | -| `tracing.serviceName` | Defines the service name resource attribute. | "traefik" | No | -| `tracing.resourceAttributes` | Defines additional resource attributes to be sent to the collector. See [resourceAttributes](#resourceattributes) for details. | [] | No | -| `tracing.sampleRate` | The proportion of requests to trace, specified between 0.0 and 1.0. | 1.0 | No | -| `tracing.capturedRequestHeaders` | Defines the list of request headers to add as attributes.
It applies to client and server kind spans. | [] | No | -| `tracing.capturedResponseHeaders` | Defines the list of response headers to add as attributes.
It applies to client and server kind spans. | [] | False | -| `tracing.safeQueryParams` | By default, all query parameters are redacted.
Defines the list of query parameters to not redact. | [] | No | -| `tracing.otlp.http` | This instructs the exporter to send the tracing to the OpenTelemetry Collector using HTTP.
Setting the sub-options with their default values. | null/false | No | -| `tracing.otlp.http.endpoint` | URL of the OpenTelemetry Collector to send tracing to.
Format="`://:`" | "https://localhost:4318/v1/tracing" | Yes | -| `tracing.otlp.http.headers` | Additional headers sent with tracing by the exporter to the OpenTelemetry Collector. | | No | -| `tracing.otlp.http.tls.ca` | Path to the certificate authority used for the secure connection to the OpenTelemetry Collector, it defaults to the system bundle. | "" | No | -| `tracing.otlp.http.tls.cert` | Path to the public certificate used for the secure connection to the OpenTelemetry Collector. When using this option, setting the `key` option is required. | "" | No | -| `tracing.otlp.http.tls.key` | This instructs the exporter to send the tracing to the OpenTelemetry Collector using HTTP.
Setting the sub-options with their default values. | ""null/false "" | No | -| `tracing.otlp.http.tls.insecureskipverify` | If `insecureSkipVerify` is `true`, the TLS connection to the OpenTelemetry Collector accepts any certificate presented by the server regardless of the hostnames it covers. | false | Yes | -| `tracing.otlp.grpc` | This instructs the exporter to send tracing to the OpenTelemetry Collector using gRPC. | false | No | -| `tracing.otlp.grpc.endpoint` | Address of the OpenTelemetry Collector to send tracing to.
Format="`:`" | "localhost:4317" | Yes | -| `tracing.otlp.grpc.headers` | Additional headers sent with tracing by the exporter to the OpenTelemetry Collector. | [] | No | -| `tracing.otlp.grpc.insecure` | Allows exporter to send tracing to the OpenTelemetry Collector without using a secured protocol. | false | Yes | -| `tracing.otlp.grpc.tls.ca` | Path to the certificate authority used for the secure connection to the OpenTelemetry Collector, it defaults to the system bundle. | "" | No | -| `tracing.otlp.grpc.tls.cert` | Path to the public certificate used for the secure connection to the OpenTelemetry Collector. When using this option, setting the `key` option is required. | "" | No | -| `tracing.otlp.grpc.tls.key` | This instructs the exporter to send the tracing to the OpenTelemetry Collector using HTTP.
Setting the sub-options with their default values. | ""null/false "" | No | -| `tracing.otlp.grpc.tls.insecureskipverify` | If `insecureSkipVerify` is `true`, the TLS connection to the OpenTelemetry Collector accepts any certificate presented by the server regardless of the hostnames it covers. | false | Yes | - -## resourceAttributes - -The `resourceAttributes` option allows setting the resource attributes sent along the traces. -Traefik also supports the `OTEL_RESOURCE_ATTRIBUTES` env variable to set up the resource attributes. - -!!! info "Kubernetes Resource Attributes Detection" - - Additionally, Traefik automatically discovers the following [Kubernetes resource attributes](https://opentelemetry.io/docs/specs/semconv/non-normative/k8s-attributes/) when running in a Kubernetes cluster: - - - `k8s.namespace.name` - - `k8s.pod.uid` - - `k8s.pod.name` - - Note that this automatic detection can fail, like if the Traefik pod is running in host network mode. - In this case, you should provide the attributes with the option or the env variable. +| Field | Description | Default | Required | +|:-------------------------------------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-----------------------------------|:---------| +| `tracing.addInternals` | Enables tracing for internal resources (e.g.: `ping@internal`). | false | No | +| `tracing.serviceName` | Service name used in selected backend. | "traefik" | No | +| `tracing.sampleRate` | The proportion of requests to trace, specified between 0.0 and 1.0. | 1.0 | No | +| `tracing.resourceAttributes` | Defines additional resource attributes to be sent to the collector. | [] | No | +| `tracing.capturedRequestHeaders` | Defines the list of request headers to add as attributes.
It applies to client and server kind spans.| [] | No | +| `tracing.capturedResponseHeaders` | Defines the list of response headers to add as attributes.
It applies to client and server kind spans.| [] |False | +| `tracing.safeQueryParams` | By default, all query parameters are redacted.
Defines the list of query parameters to not redact. | [] | No | +| `tracing.otlp.http` | This instructs the exporter to send the tracing to the OpenTelemetry Collector using HTTP.
Setting the sub-options with their default values. | null/false | No | +| `tracing.otlp.http.endpoint` | URL of the OpenTelemetry Collector to send tracing to.
Format="`://:`" | "http://localhost:4318/v1/tracing" | Yes | +| `tracing.otlp.http.headers` | Additional headers sent with tracing by the exporter to the OpenTelemetry Collector. | | No | +| `tracing.otlp.http.tls.ca` | Path to the certificate authority used for the secure connection to the OpenTelemetry Collector, it defaults to the system bundle. | "" | No | +| `tracing.otlp.http.tls.cert` | Path to the public certificate used for the secure connection to the OpenTelemetry Collector. When using this option, setting the `key` option is required. | "" | No | +| `tracing.otlp.http.tls.key` | This instructs the exporter to send the tracing to the OpenTelemetry Collector using HTTP.
Setting the sub-options with their default values. | ""null/false "" | No | +| `tracing.otlp.http.tls.insecureskipverify` |If `insecureSkipVerify` is `true`, the TLS connection to the OpenTelemetry Collector accepts any certificate presented by the server regardless of the hostnames it covers. | false | Yes | +| `tracing.otlp.grpc` | This instructs the exporter to send tracing to the OpenTelemetry Collector using gRPC. | false | No | +| `tracing.otlp.grpc.endpoint` | Address of the OpenTelemetry Collector to send tracing to.
Format="`:`" | "localhost:4317" | Yes | +| `tracing.otlp.grpc.headers` | Additional headers sent with tracing by the exporter to the OpenTelemetry Collector. | [] | No | +| `tracing.otlp.grpc.insecure` |Allows exporter to send tracing to the OpenTelemetry Collector without using a secured protocol. | false | Yes | +| `tracing.otlp.grpc.tls.ca` | Path to the certificate authority used for the secure connection to the OpenTelemetry Collector, it defaults to the system bundle. | "" | No | +| `tracing.otlp.grpc.tls.cert` | Path to the public certificate used for the secure connection to the OpenTelemetry Collector. When using this option, setting the `key` option is required. | "" | No | +| `tracing.otlp.grpc.tls.key` | This instructs the exporter to send the tracing to the OpenTelemetry Collector using HTTP.
Setting the sub-options with their default values. | ""null/false "" | No | +| `tracing.otlp.grpc.tls.insecureskipverify` |If `insecureSkipVerify` is `true`, the TLS connection to the OpenTelemetry Collector accepts any certificate presented by the server regardless of the hostnames it covers. | false | Yes | diff --git a/docs/content/reference/install-configuration/providers/docker.md b/docs/content/reference/install-configuration/providers/docker.md index eff2a9297..74e250ae0 100644 --- a/docs/content/reference/install-configuration/providers/docker.md +++ b/docs/content/reference/install-configuration/providers/docker.md @@ -29,6 +29,7 @@ providers: Attach labels to containers (in your Docker compose file) ```yaml +version: "3" services: my-container: # ... @@ -40,22 +41,22 @@ services: | Field | Description | Default | Required | |:------|:----------------------------------------------------------|:---------------------|:---------| -| `providers.providersThrottleDuration` | Minimum amount of time to wait for, after a configuration reload, before taking into account any new configuration refresh event.
If multiple events occur within this time, only the most recent one is taken into account, and all others are discarded.
**This option cannot be set per provider, but the throttling algorithm applies to each of them independently.** | 2s | No | -| `providers.docker.endpoint` | Specifies the Docker API endpoint. See [here](#endpoint) for more information| "unix:///var/run/docker.sock" | Yes | -| `providers.docker.username` | Defines the username for Basic HTTP authentication. This should be used when the Docker daemon socket is exposed through an HTTP proxy that requires Basic HTTP authentication.| "" | No | -| `providers.docker.password` | Defines the password for Basic HTTP authentication. This should be used when the Docker daemon socket is exposed through an HTTP proxy that requires Basic HTTP authentication.| "" | No | -| `providers.docker.useBindPortIP` | Instructs Traefik to use the IP/Port attached to the container's binding instead of its inner network IP/Port. See [here](#usebindportip) for more information | false | No | -| `providers.docker.exposedByDefault` | Expose containers by default through Traefik. See [here](./overview.md#exposedbydefault-and-traefikenable) for additional information | true | No | -| `providers.docker.network` | Defines a default docker network to use for connections to all containers. This option can be overridden on a per-container basis with the `traefik.docker.network` label.| "" | No | -| `providers.docker.defaultRule` | Defines what routing rule to apply to a container if no rule is defined by a label. See [here](#defaultrule) for more information. | ```"Host(`{{ normalize .Name }}`)"``` | No | -| `providers.docker.httpClientTimeout` | Defines the client timeout (in seconds) for HTTP connections. If its value is 0, no timeout is set. | 0 | No | -| `providers.docker.watch` | Instructs Traefik to watch Docker events or not. | True | No | -| `providers.docker.constraints` | Defines an expression that Traefik matches against the container labels to determine whether to create any route for that container. See [here](#constraints) for more information. | "" | No | -| `providers.docker.allowEmptyServices` | Instructs the provider to create any [servers load balancer](../../../routing/services/index.md#servers-load-balancer) defined for Docker containers regardless of the [healthiness](https://docs.docker.com/engine/reference/builder/#healthcheck) of the corresponding containers. | false | No | -| `providers.docker.tls.ca` | Defines the path to the certificate authority used for the secure connection to Docker, it defaults to the system bundle. | "" | No | -| `providers.docker.tls.cert` | Defines the path to the public certificate used for the secure connection to Docker. When using this option, setting the `key` option is required. | "" | Yes | -| `providers.docker.tls.key` | Defines the path to the private key used for the secure connection to Docker. When using this option, setting the `cert` option is required. | "" | Yes | -| `providers.docker.tls.insecureSkipVerify` | Instructs the provider to accept any certificate presented by the Docker server when establishing a TLS connection, regardless of the hostnames the certificate covers. | false | No | +| `providers.providersThrottleDuration` | Minimum amount of time to wait for, after a configuration reload, before taking into account any new configuration refresh event.
If multiple events occur within this time, only the most recent one is taken into account, and all others are discarded.
**This option cannot be set per provider, but the throttling algorithm applies to each of them independently.** | 2s | No | +| `providers.docker.endpoint` | Specifies the Docker API endpoint. See [here](#endpoint) for more information| "unix:///var/run/docker.sock" | Yes | +| `providers.docker.username` | Defines the username for Basic HTTP authentication. This should be used when the Docker daemon socket is exposed through an HTTP proxy that requires Basic HTTP authentication.| "" | No | +| `providers.docker.password` | Defines the password for Basic HTTP authentication. This should be used when the Docker daemon socket is exposed through an HTTP proxy that requires Basic HTTP authentication.| "" | No | +| `providers.docker.useBindPortIP` | Instructs Traefik to use the IP/Port attached to the container's binding instead of its inner network IP/Port. See [here](#usebindportip) for more information | false | No | +| `providers.docker.exposedByDefault` | Expose containers by default through Traefik. See [here](./overview.md#restrict-the-scope-of-service-discovery) for additional information | true | No | +| `providers.docker.network` | Defines a default docker network to use for connections to all containers. This option can be overridden on a per-container basis with the `traefik.docker.network` label.| "" | No | +| `providers.docker.defaultRule` | Defines what routing rule to apply to a container if no rule is defined by a label. See [here](#defaultrule) for more information. | ```"Host(`{{ normalize .Name }}`)"``` | No | +| `providers.docker.httpClientTimeout` | Defines the client timeout (in seconds) for HTTP connections. If its value is 0, no timeout is set. | 0 | No | +| `providers.docker.watch` | Instructs Traefik to watch Docker events or not. | True | No | +| `providers.docker.constraints` | Defines an expression that Traefik matches against the container labels to determine whether to create any route for that container. See [here](#constraints) for more information. | "" | No | +| `providers.docker.allowEmptyServices` | Instructs the provider to create any [servers load balancer](../../../routing/services/index.md#servers-load-balancer) defined for Docker containers regardless of the [healthiness](https://docs.docker.com/engine/reference/builder/#healthcheck) of the corresponding containers. | false | No | +| `providers.docker.tls.ca` | Defines the path to the certificate authority used for the secure connection to Docker, it defaults to the system bundle. | "" | No | +| `providers.docker.tls.cert` | Defines the path to the public certificate used for the secure connection to Docker. When using this option, setting the `key` option is required. | "" | Yes | +| `providers.docker.tls.key` | Defines the path to the private key used for the secure connection to Docker. When using this option, setting the `cert` option is required. | "" | Yes | +| `providers.docker.tls.insecureSkipVerify` | Instructs the provider to accept any certificate presented by the Docker server when establishing a TLS connection, regardless of the hostnames the certificate covers. | false | No | ### `endpoint` @@ -66,6 +67,8 @@ See the [Docker API Access](#docker-api-access) section for more information. The docker-compose file shares the docker sock with the Traefik container ```yaml + version: '3' + services: traefik: image: traefik:v3.1 # The official v3 Traefik docker image @@ -192,13 +195,13 @@ but still uses the `traefik.http.services..loadbalancer.server.port` that | port label | Container's binding | Routes to | |--------------------|----------------------------------------------------|----------------| - | - | - | IntIP:IntPort | - | - | ExtPort:IntPort | IntIP:IntPort | - | - | ExtIp:ExtPort:IntPort | ExtIp:ExtPort | - | LblPort | - | IntIp:LblPort | - | LblPort | ExtIp:ExtPort:LblPort | ExtIp:ExtPort | - | LblPort | ExtIp:ExtPort:OtherPort | IntIp:LblPort | - | LblPort | ExtIp1:ExtPort1:IntPort1 & ExtIp2:LblPort:IntPort2 | ExtIp2:LblPort | + | - | - | IntIP:IntPort | + | - | ExtPort:IntPort | IntIP:IntPort | + | - | ExtIp:ExtPort:IntPort | ExtIp:ExtPort | + | LblPort | - | IntIp:LblPort | + | LblPort | ExtIp:ExtPort:LblPort | ExtIp:ExtPort | + | LblPort | ExtIp:ExtPort:OtherPort | IntIp:LblPort | + | LblPort | ExtIp1:ExtPort1:IntPort1 & ExtIp2:LblPort:IntPort2 | ExtIp2:LblPort | !!! info "" In the above table: @@ -270,10 +273,6 @@ created. If the expression is empty, all detected containers are included. The expression syntax is based on the `Label("key", "value")`, and `LabelRegex("key", "value")` functions, as well as the usual boolean logic, as shown in examples below. -!!! tip "Constraints key limitations" - - Note that `traefik.*` is a reserved label namespace for configuration and can not be used as a key for custom constraints. - ??? example "Constraints Expression Examples" ```toml @@ -306,7 +305,7 @@ as well as the usual boolean logic, as shown in examples below. constraints = "LabelRegex(`a.label.name`, `a.+`)" ``` -For additional information, refer to [Restrict the Scope of Service Discovery](./overview.md#exposedbydefault-and-traefikenable). +For additional information, refer to [Restrict the Scope of Service Discovery](./overview.md#restrict-the-scope-of-service-discovery). ```yaml tab="File (YAML)" providers: diff --git a/docs/content/reference/install-configuration/providers/hashicorp/consul-catalog.md b/docs/content/reference/install-configuration/providers/hashicorp/consul-catalog.md index e69717389..975c0685d 100644 --- a/docs/content/reference/install-configuration/providers/hashicorp/consul-catalog.md +++ b/docs/content/reference/install-configuration/providers/hashicorp/consul-catalog.md @@ -32,34 +32,34 @@ Attaching tags to services: | Field | Description | Default | Required | |:------|:----------------------------------------------------------|:---------------------|:---------| -| `providers.providersThrottleDuration` | Minimum amount of time to wait for, after a configuration reload, before taking into account any new configuration refresh event.
If multiple events occur within this time, only the most recent one is taken into account, and all others are discarded.
**This option cannot be set per provider, but the throttling algorithm applies to each of them independently.** | 2s | No | -| `providers.consulCatalog.refreshInterval` | Defines the polling interval.| 15s | No | -| `providers.consulCatalog.prefix` | Defines the prefix for Consul Catalog tags defining Traefik labels.| traefik | yes | -| `providers.consulCatalog.requireConsistent` | Forces the read to be fully consistent. See [here](#requireconsistent) for more information.| false | yes | -| `providers.consulCatalog.exposedByDefault` | Expose Consul Catalog services by default in Traefik. If set to `false`, services that do not have a `traefik.enable=true` tag will be ignored from the resulting routing configuration. See [here](../overview.md#exposedbydefault-and-traefikenable). | true | no | -| `providers.consulCatalog.defaultRule` | The Default Host rule for all services. See [here](#defaultrule) for more information. | ```"Host(`{{ normalize .Name }}`)"``` | No | -| `providers.consulCatalog.connectAware` | Enable Consul Connect support. If set to `true`, Traefik will be enabled to communicate with Connect services. | false | No | -| `providers.consulCatalog.connectByDefault` | Consider every service as Connect capable by default. If set to true, Traefik will consider every Consul Catalog service to be Connect capable by default. The option can be overridden on an instance basis with the traefik.consulcatalog.connect tag. | false | No | -| `providers.consulCatalog.serviceName` | Defines the name of the Traefik service in Consul Catalog. | "traefik" | No | -| `providers.consulCatalog.constraints` | Defines an expression that Traefik matches against the container labels to determine whether to create any route for that container. See [here](#constraints) for more information. | "" | No | -| `providers.consulCatalog.namespaces` | Defines the namespaces to query. See [here](#namespaces) for more information. | "" | no | -| `providers.consulCatalog.stale` | Instruct Traefik to use stale consistency for catalog reads. | false | no | -| `providers.consulCatalog.cache` | Instruct Traefik to use local agent caching for catalog reads. | false | no | -| `providers.consulCatalog.endpoint` | Defines the Consul server endpoint. | - | yes | -| `providers.consulCatalog.endpoint.address` | Defines the address of the Consul server. | 127.0.0.1:8500 | no | -| `providers.consulCatalog.endpoint.scheme` | Defines the URI scheme for the Consul server. | "" | no | -| `providers.consulCatalog.endpoint.datacenter` | Defines the datacenter to use. If not provided in Traefik, Consul uses the default agent datacenter. | "" | no | -| `providers.consulCatalog.endpoint.token` | Defines a per-request ACL token which overwrites the agent's default token. | "" | no | -| `providers.consulCatalog.endpoint.endpointWaitTime` | Defines a duration for which a `watch` can block. If not provided, the agent default values will be used. | "" | no | -| `providers.consulCatalog.endpoint.httpAuth` | Defines authentication settings for the HTTP client using HTTP Basic Authentication. | N/A | no | -| `providers.consulCatalog.endpoint.httpAuth.username` | Defines the username to use for HTTP Basic Authentication. | "" | no | -| `providers.consulCatalog.endpoint.httpAuth.password` | Defines the password to use for HTTP Basic Authentication. | "" | no | -| `providers.consulCatalog.strictChecks` | Define which [Consul Service health checks](https://developer.hashicorp.com/consul/docs/services/usage/checks#define-initial-health-check-status) are allowed to take on traffic. | "passing,warning" | no | -| `providers.consulCatalog.tls.ca` | Defines the path to the certificate authority used for the secure connection to Consul Calatog, it defaults to the system bundle. | "" | No | -| `providers.consulCatalog.tls.cert` | Defines the path to the public certificate used for the secure connection to Consul Calatog. When using this option, setting the `key` option is required. | "" | Yes | -| `providers.consulCatalog.tls.key` | Defines the path to the private key used for the secure connection to Consul Catalog. When using this option, setting the `cert` option is required. | "" | Yes | -| `providers.consulCatalog.tls.insecureSkipVerify` | Instructs the provider to accept any certificate presented by Consul Catalog when establishing a TLS connection, regardless of the hostnames the certificate covers. | false | No | -| `providers.consulCatalog.watch` | When set to `true`, watches for Consul changes ([Consul watches checks](https://www.consul.io/docs/dynamic-app-config/watches#checks)). | false | No | +| `providers.providersThrottleDuration` | Minimum amount of time to wait for, after a configuration reload, before taking into account any new configuration refresh event.
If multiple events occur within this time, only the most recent one is taken into account, and all others are discarded.
**This option cannot be set per provider, but the throttling algorithm applies to each of them independently.** | 2s | No | +| `providers.consulCatalog.refreshInterval` | Defines the polling interval.| 15s | No | +| `providers.consulCatalog.prefix` | Defines the prefix for Consul Catalog tags defining Traefik labels.| traefik | yes | +| `providers.consulCatalog.requireConsistent` | Forces the read to be fully consistent. See [here](#requireconsistent) for more information.| false | yes | +| `providers.consulCatalog.exposedByDefault` | Expose Consul Catalog services by default in Traefik. If set to `false`, services that do not have a `traefik.enable=true` tag will be ignored from the resulting routing configuration. See [here](../overview.md#restrict-the-scope-of-service-discovery). | true | no | +| `providers.consulCatalog.defaultRule` | The Default Host rule for all services. See [here](#defaultrule) for more information. | ```"Host(`{{ normalize .Name }}`)"``` | No | +| `providers.consulCatalog.connectAware` | Enable Consul Connect support. If set to `true`, Traefik will be enabled to communicate with Connect services. | false | No | +| `providers.consulCatalog.connectByDefault` | Consider every service as Connect capable by default. If set to true, Traefik will consider every Consul Catalog service to be Connect capable by default. The option can be overridden on an instance basis with the traefik.consulcatalog.connect tag. | false | No | +| `providers.consulCatalog.serviceName` | Defines the name of the Traefik service in Consul Catalog. | "traefik" | No | +| `providers.consulCatalog.constraints` | Defines an expression that Traefik matches against the container labels to determine whether to create any route for that container. See [here](#constraints) for more information. | "" | No | +| `providers.consulCatalog.namespaces` | Defines the namespaces to query. See [here](#namespaces) for more information. | "" | no | +| `providers.consulCatalog.stale` | Instruct Traefik to use stale consistency for catalog reads. | false | no | +| `providers.consulCatalog.cache` | Instruct Traefik to use local agent caching for catalog reads. | false | no | +| `providers.consulCatalog.endpoint` | Defines the Consul server endpoint. | - | yes | +| `providers.consulCatalog.endpoint.address` | Defines the address of the Consul server. | 127.0.0.1:8500 | no | +| `providers.consulCatalog.endpoint.scheme` | Defines the URI scheme for the Consul server. | "" | no | +| `providers.consulCatalog.endpoint.datacenter` | Defines the datacenter to use. If not provided in Traefik, Consul uses the default agent datacenter. | "" | no | +| `providers.consulCatalog.endpoint.token` | Defines a per-request ACL token which overwrites the agent's default token. | "" | no | +| `providers.consulCatalog.endpoint.endpointWaitTime` | Defines a duration for which a `watch` can block. If not provided, the agent default values will be used. | "" | no | +| `providers.consulCatalog.endpoint.httpAuth` | Defines authentication settings for the HTTP client using HTTP Basic Authentication. | N/A | no | +| `providers.consulCatalog.endpoint.httpAuth.username` | Defines the username to use for HTTP Basic Authentication. | "" | no | +| `providers.consulCatalog.endpoint.httpAuth.password` | Defines the password to use for HTTP Basic Authentication. | "" | no | +| `providers.consulCatalog.strictChecks` | Define which [Consul Service health checks](https://developer.hashicorp.com/consul/docs/services/usage/checks#define-initial-health-check-status) are allowed to take on traffic. | "passing,warning" | no | +| `providers.consulCatalog.tls.ca` | Defines the path to the certificate authority used for the secure connection to Consul Calatog, it defaults to the system bundle. | "" | No | +| `providers.consulCatalog.tls.cert` | Defines the path to the public certificate used for the secure connection to Consul Calatog. When using this option, setting the `key` option is required. | "" | Yes | +| `providers.consulCatalog.tls.key` | Defines the path to the private key used for the secure connection to Consul Catalog. When using this option, setting the `cert` option is required. | "" | Yes | +| `providers.consulCatalog.tls.insecureSkipVerify` | Instructs the provider to accept any certificate presented by Consul Catalog when establishing a TLS connection, regardless of the hostnames the certificate covers. | false | No | +| `providers.consulCatalog.watch` | When set to `true`, watches for Consul changes ([Consul watches checks](https://www.consul.io/docs/dynamic-app-config/watches#checks)). | false | No | ### `requireConsistent` @@ -112,10 +112,6 @@ created. If the expression is empty, all detected services are included. The expression syntax is based on the ```Tag(`tag`)```, and ```TagRegex(`tag`)``` functions, as well as the usual boolean logic, as shown in examples below. -!!! tip "Constraints key limitations" - - Note that `traefik.*` is a reserved label namespace for configuration and can not be used as a key for custom constraints. - ??? example "Constraints Expression Examples" ```toml @@ -166,7 +162,7 @@ providers: # ... ``` -For additional information, refer to [Restrict the Scope of Service Discovery](../overview.md#exposedbydefault-and-traefikenable). +For additional information, refer to [Restrict the Scope of Service Discovery](../overview.md#restrict-the-scope-of-service-discovery). ### `namespaces` diff --git a/docs/content/reference/install-configuration/providers/hashicorp/consul.md b/docs/content/reference/install-configuration/providers/hashicorp/consul.md index 1f72646ce..01c29796f 100644 --- a/docs/content/reference/install-configuration/providers/hashicorp/consul.md +++ b/docs/content/reference/install-configuration/providers/hashicorp/consul.md @@ -26,18 +26,18 @@ providers: | Field | Description | Default | Required | |:------|:----------------------------------------------------------|:---------------------|:---------| -| `providers.providersThrottleDuration` | Minimum amount of time to wait for, after a configuration reload, before taking into account any new configuration refresh event.
If multiple events occur within this time, only the most recent one is taken into account, and all others are discarded.
**This option cannot be set per provider, but the throttling algorithm applies to each of them independently.** | 2s | No | -| `providers.consul.endpoints` | Defines the endpoint to access Consul. | "127.0.0.1:8500" | yes | -| `providers.consul.rootKey` | Defines the root key of the configuration. | "traefik" | yes | -| `providers.consul.namespaces` | Defines the namespaces to query. See [here](#namespaces) for more information | "" | no | -| `providers.consul.username` | Defines a username to connect to Consul with. | "" | no | -| `providers.consul.password` | Defines a password with which to connect to Consul. | "" | no | -| `providers.consul.token` | Defines a token with which to connect to Consul. | "" | no | -| `providers.consul.tls` | Defines the TLS configuration used for the secure connection to Consul | - | No | -| `providers.consul.tls.ca` | Defines the path to the certificate authority used for the secure connection to Consul, it defaults to the system bundle. | - | Yes | -| `providers.consul.tls.cert` | Defines the path to the public certificate used for the secure connection to Consul. When using this option, setting the `key` option is required. | - | Yes | -| `providers.consul.tls.key` | Defines the path to the private key used for the secure connection to Consul. When using this option, setting the `cert` option is required. | - | Yes | -| `providers.consul.tls.insecureSkipVerify` | Instructs the provider to accept any certificate presented by Consul when establishing a TLS connection, regardless of the hostnames the certificate covers. | false | No | +| `providers.providersThrottleDuration` | Minimum amount of time to wait for, after a configuration reload, before taking into account any new configuration refresh event.
If multiple events occur within this time, only the most recent one is taken into account, and all others are discarded.
**This option cannot be set per provider, but the throttling algorithm applies to each of them independently.** | 2s | No | +| `providers.consul.endpoints` | Defines the endpoint to access Consul. | "127.0.0.1:8500" | yes | +| `providers.consul.rootKey` | Defines the root key of the configuration. | "traefik" | yes | +| `providers.consul.namespaces` | Defines the namespaces to query. See [here](#namespaces) for more information | "" | no | +| `providers.consul.username` | Defines a username to connect to Consul with. | "" | no | +| `providers.consul.password` | Defines a password with which to connect to Consul. | "" | no | +| `providers.consul.token` | Defines a token with which to connect to Consul. | "" | no | +| `providers.consul.tls` | Defines the TLS configuration used for the secure connection to Consul | - | No | +| `providers.consul.tls.ca` | Defines the path to the certificate authority used for the secure connection to Consul, it defaults to the system bundle. | - | Yes | +| `providers.consul.tls.cert` | Defines the path to the public certificate used for the secure connection to Consul. When using this option, setting the `key` option is required. | - | Yes | +| `providers.consul.tls.key` | Defines the path to the private key used for the secure connection to Consul. When using this option, setting the `cert` option is required. | - | Yes | +| `providers.consul.tls.insecureSkipVerify` | Instructs the provider to accept any certificate presented by Consul when establishing a TLS connection, regardless of the hostnames the certificate covers. | false | No | ### `namespaces` diff --git a/docs/content/reference/install-configuration/providers/hashicorp/nomad.md b/docs/content/reference/install-configuration/providers/hashicorp/nomad.md index 29ddc764e..19608cd94 100644 --- a/docs/content/reference/install-configuration/providers/hashicorp/nomad.md +++ b/docs/content/reference/install-configuration/providers/hashicorp/nomad.md @@ -39,25 +39,25 @@ service { | Field | Description | Default | Required | |:------|:----------------------------------------------------------|:---------------------|:---------| -| `providers.providersThrottleDuration` | Minimum amount of time to wait for, after a configuration reload, before taking into account any new configuration refresh event.
If multiple events occur within this time, only the most recent one is taken into account, and all others are discarded.
**This option cannot be set per provider, but the throttling algorithm applies to each of them independently.** | 2s | No | -| `providers.nomad.namespaces` | Defines the namespaces in which the nomad services will be discovered.| "" | No | -| `providers.nomad.refreshInterval` | Defines the polling interval. This option is ignored when the `watch` option is enabled | 15s | No | -| `providers.nomad.watch` | Enables the watch mode to refresh the configuration on a per-event basis. | false | No | -| `providers.nomad.throttleDuration` | Defines how often the provider is allowed to handle service events from Nomad. This option is only compatible when the `watch` option is enabled | 0s | No | -| `providers.nomad.defaultRule` | The Default Host rule for all services. See [here](#defaultrule) for more information | ```"Host(`{{ normalize .Name }}`)"``` | No | -| `providers.nomad.constraints` | Defines an expression that Traefik matches against the container labels to determine whether to create any route for that container. See [here](#constraints) for more information. | "" | No | -| `providers.nomad.exposedByDefault` | Expose Nomad services by default in Traefik. If set to `false`, services that do not have a `traefik.enable=true` tag will be ignored from the resulting routing configuration. See [here](../overview.md#exposedbydefault-and-traefikenable) for additional information | true | No | -| `providers.nomad.allowEmptyServices` | Instructs the provider to create any [servers load balancer](../../../../routing/services/index.md#servers-load-balancer) defined for Docker containers regardless of the [healthiness](https://docs.docker.com/engine/reference/builder/#healthcheck) of the corresponding containers. | false | No | -| `providers.nomad.prefix` | Defines the prefix for Nomad service tags defining Traefik labels. | `traefik` | yes | -| `providers.nomad.stale` | Instructs Traefik to use stale consistency for Nomad service API reads. See [here](#stale) for more information | false | No | -| `providers.nomad.endpoint.address` | Defines the Address of the Nomad server. | `http://127.0.0.1:4646` | No | -| `providers.nomad.endpoint.token` | Defines a per-request ACL token if Nomad ACLs are enabled. See [here](#token) for more information | "" | No | -| `providers.nomad.endpoint.endpointWaitTime` | Defines a duration for which a `watch` can block. If not provided, the agent default values will be used. | "" | No | -| `providers.nomad.endpoint.tls` | Defines the TLS configuration used for the secure connection to the Nomad APi. | - | No | -| `providers.nomad.endpoint.tls.ca` | Defines the path to the certificate authority used for the secure connection to the Nomad API, it defaults to the system bundle. | "" | No | -| `providers.nomad.endpoint.tls.cert` | Defines the path to the public certificate used for the secure connection to the Nomad API. When using this option, setting the `key` option is required. | '" | Yes | -| `providers.nomad.endpoint.tls.key` | Defines the path to the private key used for the secure connection to the Nomad API. When using this option, setting the `cert` option is required. | "" | Yes | -| `providers.nomad.endpoint.tls.insecureSkipVerify` | Instructs the provider to accept any certificate presented by Nomad when establishing a TLS connection, regardless of the hostnames the certificate covers. | false | No | +| `providers.providersThrottleDuration` | Minimum amount of time to wait for, after a configuration reload, before taking into account any new configuration refresh event.
If multiple events occur within this time, only the most recent one is taken into account, and all others are discarded.
**This option cannot be set per provider, but the throttling algorithm applies to each of them independently.** | 2s | No | +| `providers.nomad.namespaces` | Defines the namespaces in which the nomad services will be discovered.| "" | No | +| `providers.nomad.refreshInterval` | Defines the polling interval. This option is ignored when the `watch` option is enabled | 15s | No | +| `providers.nomad.watch` | Enables the watch mode to refresh the configuration on a per-event basis. | false | No | +| `providers.nomad.throttleDuration` | Defines how often the provider is allowed to handle service events from Nomad. This option is only compatible when the `watch` option is enabled | 0s | No | +| `providers.nomad.defaultRule` | The Default Host rule for all services. See [here](#defaultrule) for more information | ```"Host(`{{ normalize .Name }}`)"``` | No | +| `providers.nomad.constraints` | Defines an expression that Traefik matches against the container labels to determine whether to create any route for that container. See [here](#constraints) for more information. | "" | No | +| `providers.nomad.exposedByDefault` | Expose Nomad services by default in Traefik. If set to `false`, services that do not have a `traefik.enable=true` tag will be ignored from the resulting routing configuration. See [here](../overview.md#restrict-the-scope-of-service-discovery) for additional information | true | No | +| `providers.nomad.allowEmptyServices` | Instructs the provider to create any [servers load balancer](../../../../routing/services/index.md#servers-load-balancer) defined for Docker containers regardless of the [healthiness](https://docs.docker.com/engine/reference/builder/#healthcheck) of the corresponding containers. | false | No | +| `providers.nomad.prefix` | Defines the prefix for Nomad service tags defining Traefik labels. | `traefik` | yes | +| `providers.nomad.stale` | Instructs Traefik to use stale consistency for Nomad service API reads. See [here](#stale) for more information | false | No | +| `providers.nomad.endpoint.address` | Defines the Address of the Nomad server. | `http://127.0.0.1:4646` | No | +| `providers.nomad.endpoint.token` | Defines a per-request ACL token if Nomad ACLs are enabled. See [here](#token) for more information | "" | No | +| `providers.nomad.endpoint.endpointWaitTime` | Defines a duration for which a `watch` can block. If not provided, the agent default values will be used. | "" | No | +| `providers.nomad.endpoint.tls` | Defines the TLS configuration used for the secure connection to the Nomad APi. | - | No | +| `providers.nomad.endpoint.tls.ca` | Defines the path to the certificate authority used for the secure connection to the Nomad API, it defaults to the system bundle. | "" | No | +| `providers.nomad.endpoint.tls.cert` | Defines the path to the public certificate used for the secure connection to the Nomad API. When using this option, setting the `key` option is required. | '" | Yes | +| `providers.nomad.endpoint.tls.key` | Defines the path to the private key used for the secure connection to the Nomad API. When using this option, setting the `cert` option is required. | "" | Yes | +| `providers.nomad.endpoint.tls.insecureSkipVerify` | Instructs the provider to accept any certificate presented by Nomad when establishing a TLS connection, regardless of the hostnames the certificate covers. | false | No | ### `namespaces` @@ -191,10 +191,6 @@ created. If the expression is empty, all detected services are included. The expression syntax is based on the ```Tag(`tag`)```, and ```TagRegex(`tag`)``` functions, as well as the usual boolean logic, as shown in examples below. -!!! tip "Constraints key limitations" - - Note that `traefik.*` is a reserved label namespace for configuration and can not be used as a key for custom constraints. - ??? example "Constraints Expression Examples" ```toml @@ -245,7 +241,7 @@ providers: # ... ``` -For additional information, refer to [Restrict the Scope of Service Discovery](../overview.md#exposedbydefault-and-traefikenable). +For additional information, refer to [Restrict the Scope of Service Discovery](../overview.md#restrict-the-scope-of-service-discovery). ## Routing Configuration diff --git a/docs/content/reference/install-configuration/providers/kubernetes/kubernetes-crd.md b/docs/content/reference/install-configuration/providers/kubernetes/kubernetes-crd.md index 50ea7ff2d..6e787d206 100644 --- a/docs/content/reference/install-configuration/providers/kubernetes/kubernetes-crd.md +++ b/docs/content/reference/install-configuration/providers/kubernetes/kubernetes-crd.md @@ -20,10 +20,10 @@ When you install Traefik without using the Helm Chart, or when you are upgrading ```bash # Install Traefik Resource Definitions: -kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.5/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml +kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.4/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml # Install RBAC for Traefik: -kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.5/docs/content/reference/dynamic-configuration/kubernetes-crd-rbac.yml +kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.4/docs/content/reference/dynamic-configuration/kubernetes-crd-rbac.yml ``` ## Configuration Example @@ -54,19 +54,19 @@ providers: | Field | Description | Default | Required | |:------|:----------------------------------------------------------|:--------|:---------| -| `providers.providersThrottleDuration` | Minimum amount of time to wait for, after a configuration reload, before taking into account any new configuration refresh event.
If multiple events occur within this time, only the most recent one is taken into account, and all others are discarded.
**This option cannot be set per provider, but the throttling algorithm applies to each of them independently.** | 2s | No | -| `providers.kubernetesCRD.endpoint` | Server endpoint URL.
More information [here](#endpoint). | "" | No | -| `providers.kubernetesCRD.token` | Bearer token used for the Kubernetes client configuration. | "" | No | -| `providers.kubernetesCRD.certAuthFilePath` | Path to the certificate authority file.
Used for the Kubernetes client configuration. | "" | No | -| `providers.kubernetesCRD.namespaces` | Array of namespaces to watch.
If left empty, watch all namespaces. | [] | No | -| `providers.kubernetesCRD.labelselector` | Allow filtering on specific resource objects only using label selectors.
Only to Traefik [Custom Resources](#list-of-resources) (they all must match the filter).
No effect on Kubernetes `Secrets`, `EndpointSlices` and `Services`.
See [label-selectors](https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors) for details. | "" | No | -| `providers.kubernetesCRD.ingressClass` | Value of `kubernetes.io/ingress.class` annotation that identifies resource objects to be processed.
If empty, resources missing the annotation, having an empty value, or the value `traefik` are processed. | "" | No | -| `providers.kubernetesCRD.throttleDuration` | Minimum amount of time to wait between two Kubernetes events before producing a new configuration.
This prevents a Kubernetes cluster that updates many times per second from continuously changing your Traefik configuration.
If empty, every event is caught. | 0s | No | -| `providers.kubernetesCRD.allowEmptyServices` | Allows creating a route to reach a service that has no endpoint available.
It allows Traefik to handle the requests and responses targeting this service (applying middleware or observability operations) before returning a `503` HTTP Status. | false | No | -| `providers.kubernetesCRD.allowCrossNamespace` | Allows the `IngressRoutes` to reference resources in namespaces other than theirs. | false | No | -| `providers.kubernetesCRD.allowExternalNameServices` | Allows the `IngressRoutes` to reference ExternalName services. | false | No | -| `providers.kubernetesCRD.nativeLBByDefault` | Allow using the Kubernetes Service load balancing between the pods instead of the one provided by Traefik for every `IngressRoute` by default.
It can br overridden in the [`ServerTransport`](../../../../routing/services/index.md#serverstransport). | false | No | -| `providers.kubernetesCRD.disableClusterScopeResources` | Prevent from discovering cluster scope resources (`IngressClass` and `Nodes`).
By doing so, it alleviates the requirement of giving Traefik the rights to look up for cluster resources.
Furthermore, Traefik will not handle IngressRoutes with IngressClass references, therefore such Ingresses will be ignored (please note that annotations are not affected by this option).
This will also prevent from using the `NodePortLB` options on services. | false | No | +| `providers.providersThrottleDuration` | Minimum amount of time to wait for, after a configuration reload, before taking into account any new configuration refresh event.
If multiple events occur within this time, only the most recent one is taken into account, and all others are discarded.
**This option cannot be set per provider, but the throttling algorithm applies to each of them independently.** | 2s | No | +| `providers.kubernetesCRD.endpoint` | Server endpoint URL.
More information [here](#endpoint). | "" | No | +| `providers.kubernetesCRD.token` | Bearer token used for the Kubernetes client configuration. | "" | No | +| `providers.kubernetesCRD.certAuthFilePath` | Path to the certificate authority file.
Used for the Kubernetes client configuration. | "" | No | +| `providers.kubernetesCRD.namespaces` | Array of namespaces to watch.
If left empty, watch all namespaces. | [] | No | +| `providers.kubernetesCRD.labelselector` | Allow filtering on specific resource objects only using label selectors.
Only to Traefik [Custom Resources](#list-of-resources) (they all must match the filter).
No effect on Kubernetes `Secrets`, `EndpointSlices` and `Services`.
See [label-selectors](https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors) for details. | "" | No | +| `providers.kubernetesCRD.ingressClass` | Value of `kubernetes.io/ingress.class` annotation that identifies resource objects to be processed.
If empty, resources missing the annotation, having an empty value, or the value `traefik` are processed. | "" | No | +| `providers.kubernetesCRD.throttleDuration` | Minimum amount of time to wait between two Kubernetes events before producing a new configuration.
This prevents a Kubernetes cluster that updates many times per second from continuously changing your Traefik configuration.
If empty, every event is caught. | 0s | No | +| `providers.kubernetesCRD.allowEmptyServices` | Allows creating a route to reach a service that has no endpoint available.
It allows Traefik to handle the requests and responses targeting this service (applying middleware or observability operations) before returning a `503` HTTP Status. | false | No | +| `providers.kubernetesCRD.allowCrossNamespace` | Allows the `IngressRoutes` to reference resources in namespaces other than theirs. | false | No | +| `providers.kubernetesCRD.allowExternalNameServices` | Allows the `IngressRoutes` to reference ExternalName services. | false | No | +| `providers.kubernetesCRD.nativeLBByDefault` | Allow using the Kubernetes Service load balancing between the pods instead of the one provided by Traefik for every `IngressRoute` by default.
It can br overridden in the [`ServerTransport`](../../../../routing/services/index.md#serverstransport). | false | No | +| `providers.kubernetesCRD.disableClusterScopeResources` | Prevent from discovering cluster scope resources (`IngressClass` and `Nodes`).
By doing so, it alleviates the requirement of giving Traefik the rights to look up for cluster resources.
Furthermore, Traefik will not handle IngressRoutes with IngressClass references, therefore such Ingresses will be ignored (please note that annotations are not affected by this option).
This will also prevent from using the `NodePortLB` options on services. | false | No | ### endpoint @@ -108,18 +108,18 @@ See the dedicated section in [routing](../../../../routing/providers/kubernetes- -| Resource | Purpose | -|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------| -| [IngressRoute](../../../routing-configuration/kubernetes/crd/http/ingressroute.md) | HTTP Routing | -| [Middleware](../../../routing-configuration/kubernetes/crd/http/middleware.md) | Tweaks the HTTP requests before they are sent to your service | -| [TraefikService](../../../routing-configuration/kubernetes/crd/http/traefikservice.md) | Abstraction for HTTP loadbalancing/mirroring | -| [TLSOptions](../../../routing-configuration/kubernetes/crd/tls/tlsoption.md) | Allows configuring some parameters of the TLS connection | -| [TLSStores](../../../routing-configuration/kubernetes/crd/tls/tlsstore.md) | Allows configuring the default TLS store | -| [ServersTransport](../../../routing-configuration/kubernetes/crd/http/serverstransport.md) | Allows configuring the transport between Traefik and the backends | -| [IngressRouteTCP](../../../routing-configuration/kubernetes/crd/tcp/ingressroutetcp.md) | TCP Routing | -| [MiddlewareTCP](../../../routing-configuration/kubernetes/crd/tcp/middlewaretcp.md) | Tweaks the TCP requests before they are sent to your service | -| [ServersTransportTCP](../../../routing-configuration/kubernetes/crd/tcp/serverstransporttcp.md) | Allows configuring the transport between Traefik and the backends | -| [IngressRouteUDP](../../../routing-configuration/kubernetes/crd/udp/ingressrouteudp.md) | UDP Routing | +| Resource | Purpose | +|--------------------------------------------------|--------------------------------------------------------------------| +| [IngressRoute](../../../../routing/providers/kubernetes-crd.md#kind-ingressroute) | HTTP Routing | +| [Middleware](../../../../middlewares/http/overview.md) | Tweaks the HTTP requests before they are sent to your service | +| [TraefikService](../../../../routing/providers/kubernetes-crd.md#kind-traefikservice) | Abstraction for HTTP loadbalancing/mirroring | +| [TLSOptions](../../../../routing/providers/kubernetes-crd.md#kind-tlsoption) | Allows configuring some parameters of the TLS connection | +| [TLSStores](../../../../routing/providers/kubernetes-crd.md#kind-tlsstore) | Allows configuring the default TLS store | +| [ServersTransport](../../../../routing/providers/kubernetes-crd.md#kind-serverstransport) | Allows configuring the transport between Traefik and the backends | +| [IngressRouteTCP](../../../../routing/providers/kubernetes-crd.md#kind-ingressroutetcp) | TCP Routing | +| [MiddlewareTCP](../../../../routing/providers/kubernetes-crd.md#kind-middlewaretcp) | Tweaks the TCP requests before they are sent to your service | +| [ServersTransportTCP](../../../../routing/providers/kubernetes-crd.md#kind-serverstransporttc) | Allows configuring the transport between Traefik and the backends | +| [IngressRouteUDP](../../../../routing/providers/kubernetes-crd.md#kind-ingressrouteudp) | UDP Routing | ## Particularities diff --git a/docs/content/reference/install-configuration/providers/kubernetes/kubernetes-gateway.md b/docs/content/reference/install-configuration/providers/kubernetes/kubernetes-gateway.md index bdbf04428..98b3f2add 100644 --- a/docs/content/reference/install-configuration/providers/kubernetes/kubernetes-gateway.md +++ b/docs/content/reference/install-configuration/providers/kubernetes/kubernetes-gateway.md @@ -8,11 +8,11 @@ description: "Learn how to use the Kubernetes Gateway API as a provider for conf The Kubernetes Gateway provider is a Traefik implementation of the [Gateway API](https://gateway-api.sigs.k8s.io/) specification from the Kubernetes Special Interest Groups (SIGs). -This provider supports Standard version [v1.3.0](https://github.com/kubernetes-sigs/gateway-api/releases/tag/v1.3.0) of the Gateway API specification. +This provider supports Standard version [v1.2.1](https://github.com/kubernetes-sigs/gateway-api/releases/tag/v1.2.1) of the Gateway API specification. It fully supports all HTTP core and some extended features, as well as the `TCPRoute` and `TLSRoute` resources from the [Experimental channel](https://gateway-api.sigs.k8s.io/concepts/versioning/?h=#release-channels). -For more details, check out the conformance [report](https://github.com/kubernetes-sigs/gateway-api/tree/main/conformance/reports/v1.3.0/traefik-traefik). +For more details, check out the conformance [report](https://github.com/kubernetes-sigs/gateway-api/tree/main/conformance/reports/v1.2.1/traefik-traefik). !!! info "Using The Helm Chart" @@ -27,14 +27,14 @@ For more details, check out the conformance [report](https://github.com/kubernet ```bash # Install Gateway API CRDs from the Standard channel. - kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.3.0/standard-install.yaml + kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.2.1/standard-install.yaml ``` 2. Install/update the Traefik [RBAC](../../../dynamic-configuration/kubernetes-gateway-rbac.yml). ```bash # Install Traefik RBACs. - kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.5/docs/content/reference/dynamic-configuration/kubernetes-gateway-rbac.yml + kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.4/docs/content/reference/dynamic-configuration/kubernetes-gateway-rbac.yml ``` ## Configuration Example @@ -69,19 +69,19 @@ providers: | Field | Description | Default | Required | |:----------------------------------------------------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:--------|:---------| -| `providers.providersThrottleDuration` | Minimum amount of time to wait for, after a configuration reload, before taking into account any new configuration refresh event.
If multiple events occur within this time, only the most recent one is taken into account, and all others are discarded.
**This option cannot be set per provider, but the throttling algorithm applies to each of them independently.** | 2s | No | -| `providers.kubernetesGateway.endpoint` | Server endpoint URL.
More information [here](#endpoint). | "" | No | -| `providers.kubernetesGateway.experimentalChannel` | Toggles support for the Experimental Channel resources ([Gateway API release channels documentation](https://gateway-api.sigs.k8s.io/concepts/versioning/#release-channels)).
(ex: `TCPRoute` and `TLSRoute`) | false | No | -| `providers.kubernetesGateway.token` | Bearer token used for the Kubernetes client configuration. | "" | No | -| `providers.kubernetesGateway.certAuthFilePath` | Path to the certificate authority file.
Used for the Kubernetes client configuration. | "" | No | -| `providers.kubernetesGateway.namespaces` | Array of namespaces to watch.
If left empty, watch all namespaces. | [] | No | -| `providers.kubernetesGateway.labelselector` | Allow filtering on specific resource objects only using label selectors.
Only to Traefik [Custom Resources](./kubernetes-crd.md#list-of-resources) (they all must match the filter).
No effect on Kubernetes `Secrets`, `EndpointSlices` and `Services`.
See [label-selectors](https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors) for details. | "" | No | -| `providers.kubernetesGateway.throttleDuration` | Minimum amount of time to wait between two Kubernetes events before producing a new configuration.
This prevents a Kubernetes cluster that updates many times per second from continuously changing your Traefik configuration.
If empty, every event is caught. | 0s | No | -| `providers.kubernetesGateway.nativeLBByDefault` | Defines whether to use Native Kubernetes load-balancing mode by default. For more information, please check out the `traefik.io/service.nativelb` service annotation documentation. | false | No | -| `providers.kubernetesGateway.`
`statusAddress.hostname`
| Hostname copied to the Gateway `status.addresses`. | "" | No | -| `providers.kubernetesGateway.`
`statusAddress.ip`
| IP address copied to the Gateway `status.addresses`, and currently only supports one IP value (IPv4 or IPv6). | "" | No | -| `providers.kubernetesGateway.`
`statusAddress.service.namespace`
| The namespace of the Kubernetes service to copy status addresses from.
When using third parties tools like External-DNS, this option can be used to copy the service `loadbalancer.status` (containing the service's endpoints IPs) to the Gateway `status.addresses`. | "" | No | -| `providers.kubernetesGateway.`
`statusAddress.service.name`
| The name of the Kubernetes service to copy status addresses from.
When using third parties tools like External-DNS, this option can be used to copy the service `loadbalancer.status` (containing the service's endpoints IPs) to the Gateway `status.addresses`. | "" | No | +| `providers.providersThrottleDuration` | Minimum amount of time to wait for, after a configuration reload, before taking into account any new configuration refresh event.
If multiple events occur within this time, only the most recent one is taken into account, and all others are discarded.
**This option cannot be set per provider, but the throttling algorithm applies to each of them independently.** | 2s | No | +| `providers.kubernetesGateway.endpoint` | Server endpoint URL.
More information [here](#endpoint). | "" | No | +| `providers.kubernetesGateway.experimentalChannel` | Toggles support for the Experimental Channel resources ([Gateway API release channels documentation](https://gateway-api.sigs.k8s.io/concepts/versioning/#release-channels)).
(ex: `TCPRoute` and `TLSRoute`) | false | No | +| `providers.kubernetesGateway.token` | Bearer token used for the Kubernetes client configuration. | "" | No | +| `providers.kubernetesGateway.certAuthFilePath` | Path to the certificate authority file.
Used for the Kubernetes client configuration. | "" | No | +| `providers.kubernetesGateway.namespaces` | Array of namespaces to watch.
If left empty, watch all namespaces. | [] | No | +| `providers.kubernetesGateway.labelselector` | Allow filtering on specific resource objects only using label selectors.
Only to Traefik [Custom Resources](./kubernetes-crd.md#list-of-resources) (they all must match the filter).
No effect on Kubernetes `Secrets`, `EndpointSlices` and `Services`.
See [label-selectors](https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors) for details. | "" | No | +| `providers.kubernetesGateway.throttleDuration` | Minimum amount of time to wait between two Kubernetes events before producing a new configuration.
This prevents a Kubernetes cluster that updates many times per second from continuously changing your Traefik configuration.
If empty, every event is caught. | 0s | No | +| `providers.kubernetesGateway.nativeLBByDefault` | Defines whether to use Native Kubernetes load-balancing mode by default. For more information, please check out the `traefik.io/service.nativelb` service annotation documentation. | false | No | +| `providers.kubernetesGateway.`
`statusAddress.hostname` | Hostname copied to the Gateway `status.addresses`. | "" | No | +| `providers.kubernetesGateway.`
`statusAddress.ip` | IP address copied to the Gateway `status.addresses`, and currently only supports one IP value (IPv4 or IPv6). | "" | No | +| `providers.kubernetesGateway.`
`statusAddress.service.namespace` | The namespace of the Kubernetes service to copy status addresses from.
When using third parties tools like External-DNS, this option can be used to copy the service `loadbalancer.status` (containing the service's endpoints IPs) to the Gateway `status.addresses`. | "" | No | +| `providers.kubernetesGateway.`
`statusAddress.service.name` | The name of the Kubernetes service to copy status addresses from.
When using third parties tools like External-DNS, this option can be used to copy the service `loadbalancer.status` (containing the service's endpoints IPs) to the Gateway `status.addresses`. | "" | No | diff --git a/docs/content/reference/install-configuration/providers/kubernetes/kubernetes-ingress-nginx.md b/docs/content/reference/install-configuration/providers/kubernetes/kubernetes-ingress-nginx.md deleted file mode 100644 index 2d9e93c1e..000000000 --- a/docs/content/reference/install-configuration/providers/kubernetes/kubernetes-ingress-nginx.md +++ /dev/null @@ -1,112 +0,0 @@ ---- -title: "Traefik Kubernetes Ingress NGINX Documentation" -description: "Understand the requirements, routing configuration, and how to set up the Kubernetes Ingress NGINX provider. Read the technical documentation." ---- - -# Traefik & Ingresses with NGINX Annotations - -The experimental Traefik Kubernetes Ingress NGINX provider is a Kubernetes Ingress controller; i.e, -it manages access to cluster services by supporting the [Ingress](https://kubernetes.io/docs/concepts/services-networking/ingress/) specification. -It also supports some of the [ingress-nginx](https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/annotations/) annotations on ingresses to customize their behavior. - -!!! warning "Ingress Discovery" - - The Kubernetes Ingress NGINX provider is discovering by default all Ingresses in the cluster, - which may lead to duplicated routers if you are also using the Kubernetes Ingress provider. - We recommend to use IngressClass for the Ingresses you want to be handled by this provider, - or to use the `watchNamespace` or `watchNamespaceSelector` options to limit the discovery of Ingresses to a specific namespace or set of namespaces. - -## Configuration Example - -As this provider is an experimental feature, it needs to be enabled in the experimental and in the provider sections of the configuration. -You can enable the Kubernetes Ingress NGINX provider as detailed below: - -```yaml tab="File (YAML)" -experimental: - kubernetesIngressNGINX: true - -providers: - kubernetesIngressNGINX: {} -``` - -```toml tab="File (TOML)" -[experimental.kubernetesIngressNGINX] - -[providers.kubernetesIngressNGINX] -``` - -```bash tab="CLI" ---experimental.kubernetesingressnginx=true ---providers.kubernetesingressnginx=true -``` - -The provider then watches for incoming ingresses events, such as the example below, -and derives the corresponding dynamic configuration from it, -which in turn creates the resulting routers, services, handlers, etc. - -## Configuration Options - - -| Field | Description | Default | Required | -|:------------------------------------------------------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:--------|:---------| -| `providers.providersThrottleDuration` | Minimum amount of time to wait for, after a configuration reload, before taking into account any new configuration refresh event.
If multiple events occur within this time, only the most recent one is taken into account, and all others are discarded.
**This option cannot be set per provider, but the throttling algorithm applies to each of them independently.** | 2s | No | -| `providers.kubernetesIngressNGINX.endpoint` | Server endpoint URL.
More information [here](#endpoint). | "" | No | -| `providers.kubernetesIngressNGINX.token` | Bearer token used for the Kubernetes client configuration. | "" | No | -| `providers.kubernetesIngressNGINX.certAuthFilePath` | Path to the certificate authority file.
Used for the Kubernetes client configuration. | "" | No | -| `providers.kubernetesIngressNGINX.throttleDuration` | Minimum amount of time to wait between two Kubernetes events before producing a new configuration.
This prevents a Kubernetes cluster that updates many times per second from continuously changing your Traefik configuration.
If empty, every event is caught. | 0s | No | -| `providers.kubernetesIngressNGINX.watchNamespace` | Namespace the controller watches for updates to Kubernetes objects. All namespaces are watched if this parameter is left empty. | "" | No | -| `providers.kubernetesIngressNGINX.watchNamespaceSelector` | Selector selects namespaces the controller watches for updates to Kubernetes objects. | "" | No | -| `providers.kubernetesIngressNGINX.ingressClass` | Name of the ingress class this controller satisfies. | "" | No | -| `providers.kubernetesIngressNGINX.controllerClass` | Ingress Class Controller value this controller satisfies. | "" | No | -| `providers.kubernetesIngressNGINX.watchIngressWithoutClass` | Define if Ingress Controller should also watch for Ingresses without an IngressClass or the annotation specified. | false | No | -| `providers.kubernetesIngressNGINX.ingressClassByName` | Define if Ingress Controller should watch for Ingress Class by Name together with Controller Class. | false | No | -| `providers.kubernetesIngressNGINX.publishService` | Service fronting the Ingress controller. Takes the form namespace/name. | "" | No | -| `providers.kubernetesIngressNGINX.publishStatusAddress` | Customized address (or addresses, separated by comma) to set as the load-balancer status of Ingress objects this controller satisfies. | "" | No | -| `providers.kubernetesIngressNGINX.defaultBackendService` | Service used to serve HTTP requests not matching any known server name (catch-all). Takes the form 'namespace/name'. | "" | No | -| `providers.kubernetesIngressNGINX.disableSvcExternalName` | Disable support for Services of type ExternalName. | false | No | - - - -### `endpoint` - -The Kubernetes server endpoint URL. - -When deployed into Kubernetes, Traefik reads the environment variables `KUBERNETES_SERVICE_HOST` -and `KUBERNETES_SERVICE_PORT` or `KUBECONFIG` to construct the endpoint. - -The access token is looked up in `/var/run/secrets/kubernetes.io/serviceaccount/token` -and the SSL CA certificate in `/var/run/secrets/kubernetes.io/serviceaccount/ca.crt`. -Both are mounted automatically when deployed inside Kubernetes. - -The endpoint may be specified to override the environment variable values inside -a cluster. - -When the environment variables are not found, Traefik tries to connect to the -Kubernetes API server with an external-cluster client. - -In this case, the endpoint is required. -Specifically, it may be set to the URL used by `kubectl proxy` to connect to a Kubernetes -cluster using the granted authentication and authorization of the associated kubeconfig. - -```yaml tab="File (YAML)" -providers: - kubernetesIngressNGINX: - endpoint: "http://localhost:8080" - # ... -``` - -```toml tab="File (TOML)" -[providers.kubernetesIngressNGINX] - endpoint = "http://localhost:8080" - # ... -``` - -```bash tab="CLI" ---providers.kubernetesingressnginx.endpoint=http://localhost:8080 -``` - -## Routing Configuration - -See the dedicated section in [routing](../../../routing-configuration/kubernetes/ingress-nginx.md). - -{!traefik-for-business-applications.md!} diff --git a/docs/content/reference/install-configuration/providers/kubernetes/kubernetes-ingress.md b/docs/content/reference/install-configuration/providers/kubernetes/kubernetes-ingress.md index 5ac53b269..adf102e64 100644 --- a/docs/content/reference/install-configuration/providers/kubernetes/kubernetes-ingress.md +++ b/docs/content/reference/install-configuration/providers/kubernetes/kubernetes-ingress.md @@ -3,16 +3,11 @@ title: "Traefik Kubernetes Ingress Documentation" description: "Understand the requirements, routing configuration, and how to set up Traefik Proxy as your Kubernetes Ingress Controller. Read the technical documentation." --- -# Traefik & Kubernetes +# Traefik & Kubernetes The Traefik Kubernetes Ingress provider is a Kubernetes Ingress controller; i.e, it manages access to cluster services by supporting the [Ingress](https://kubernetes.io/docs/concepts/services-networking/ingress/) specification. -??? warning "Ingress Backend Resource not supported" - - Referencing backend service endpoints using [`spec.rules.http.paths.backend.resource`](https://kubernetes.io/docs/reference/kubernetes-api/service-resources/ingress-v1/#IngressBackend) is not supported. - Use `spec.rules.http.paths.backend.service` instead. - ## Configuration Example You can enable the `kubernetesIngress` provider as detailed below: @@ -42,29 +37,27 @@ and derives the corresponding dynamic configuration from it, which in turn creates the resulting routers, services, handlers, etc. ## Configuration Options - -| Field | Description | Default | Required | -| :------------------------------------------------------------------ | :------------- | :------ | :------- | -| `providers.providersThrottleDuration` | Minimum amount of time to wait for, after a configuration reload, before taking into account any new configuration refresh event.
If multiple events occur within this time, only the most recent one is taken into account, and all others are discarded.
**This option cannot be set per provider, but the throttling algorithm applies to each of them independently.** | 2s | No | -| `providers.kubernetesIngress.endpoint` | Server endpoint URL.
More information [here](#endpoint). | "" | No | -| `providers.kubernetesIngress.token` | Bearer token used for the Kubernetes client configuration. | "" | No | -| `providers.kubernetesIngress.certAuthFilePath` | Path to the certificate authority file.
Used for the Kubernetes client configuration. | "" | No | -| `providers.kubernetesIngress.namespaces` | Array of namespaces to watch.
If left empty, watch all namespaces. | | No | -| `providers.kubernetesIngress.labelselector` | Allow filtering on Ingress objects using label selectors.
No effect on Kubernetes `Secrets`, `EndpointSlices` and `Services`.
See [label-selectors](https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors) for details. | "" | No | -| `providers.kubernetesIngress.ingressClass` | The `IngressClass` resource name or the `kubernetes.io/ingress.class` annotation value that identifies resource objects to be processed.
If empty, resources missing the annotation, having an empty value, or the value `traefik` are processed. | "" | No | -| `providers.kubernetesIngress.disableIngressClassLookup` | Prevent to discover IngressClasses in the cluster.
It alleviates the requirement of giving Traefik the rights to look IngressClasses up.
Ignore Ingresses with IngressClass.
Annotations are not affected by this option. | false | No | -| `providers.kubernetesIngress.`
`ingressEndpoint.hostname`
| Hostname used for Kubernetes Ingress endpoints. | "" | No | -| `providers.kubernetesIngress.`
`ingressEndpoint.ip`
| This IP will get copied to the Ingress `status.loadbalancer.ip`, and currently only supports one IP value (IPv4 or IPv6). | "" | No | -| `providers.kubernetesIngress.`
`ingressEndpoint.publishedService`
| The Kubernetes service to copy status from.
More information [here](#ingressendpointpublishedservice). | "" | No | -| `providers.kubernetesIngress.throttleDuration` | Minimum amount of time to wait between two Kubernetes events before producing a new configuration.
This prevents a Kubernetes cluster that updates many times per second from continuously changing your Traefik configuration.
If empty, every event is caught. | 0s | No | -| `providers.kubernetesIngress.allowEmptyServices` | Allows creating a route to reach a service that has no endpoint available.
It allows Traefik to handle the requests and responses targeting this service (applying middleware or observability operations) before returning a `503` HTTP Status. | false | No | -| `providers.kubernetesIngress.allowCrossNamespace` | Allows the `Ingress` to reference resources in namespaces other than theirs. | false | No | -| `providers.kubernetesIngress.allowExternalNameServices` | Allows the `Ingress` to reference ExternalName services. | false | No | -| `providers.kubernetesIngress.nativeLBByDefault` | Allow using the Kubernetes Service load balancing between the pods instead of the one provided by Traefik for every `Ingress` by default.
It can br overridden in the [`ServerTransport`](../../../../routing/services/index.md#serverstransport). | false | No | -| `providers.kubernetesIngress.disableClusterScopeResources` | Prevent from discovering cluster scope resources (`IngressClass` and `Nodes`).
By doing so, it alleviates the requirement of giving Traefik the rights to look up for cluster resources.
Furthermore, Traefik will not handle Ingresses with IngressClass references, therefore such Ingresses will be ignored (please note that annotations are not affected by this option).
This will also prevent from using the `NodePortLB` options on services. | false | No | -| `providers.kubernetesIngress.strictPrefixMatching` | Make prefix matching strictly comply with the Kubernetes Ingress specification (path-element-wise matching instead of character-by-character string matching). For example, a PathPrefix of `/foo` will match `/foo`, `/foo/`, and `/foo/bar` but not `/foobar`. | false | No | +| Field | Description | Default | Required | +|:-----------------------------------------------------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:--------|:---------| +| `providers.providersThrottleDuration` | Minimum amount of time to wait for, after a configuration reload, before taking into account any new configuration refresh event.
If multiple events occur within this time, only the most recent one is taken into account, and all others are discarded.
**This option cannot be set per provider, but the throttling algorithm applies to each of them independently.** | 2s | No | +| `providers.kubernetesIngress.endpoint` | Server endpoint URL.
More information [here](#endpoint). | "" | No | +| `providers.kubernetesIngress.token` | Bearer token used for the Kubernetes client configuration. | "" | No | +| `providers.kubernetesIngress.certAuthFilePath` | Path to the certificate authority file.
Used for the Kubernetes client configuration. | "" | No | +| `providers.kubernetesCRD.namespaces` | Array of namespaces to watch.
If left empty, watch all namespaces. | | No | +| `providers.kubernetesIngress.labelselector` | Allow filtering on Ingress objects using label selectors.
No effect on Kubernetes `Secrets`, `EndpointSlices` and `Services`.
See [label-selectors](https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors) for details. | "" | No | +| `providers.kubernetesIngress.ingressClass` | The `IngressClass` resource name or the `kubernetes.io/ingress.class` annotation value that identifies resource objects to be processed.
If empty, resources missing the annotation, having an empty value, or the value `traefik` are processed. | "" | No | +| `providers.kubernetesIngress.disableIngressClassLookup` | Prevent to discover IngressClasses in the cluster.
It alleviates the requirement of giving Traefik the rights to look IngressClasses up.
Ignore Ingresses with IngressClass.
Annotations are not affected by this option. | false | No | +| `providers.kubernetesIngress.`
`ingressEndpoint.hostname` | Hostname used for Kubernetes Ingress endpoints. | "" | No | +| `providers.kubernetesIngress.`
`ingressEndpoint.ip` | This IP will get copied to the Ingress `status.loadbalancer.ip`, and currently only supports one IP value (IPv4 or IPv6). | "" | No | +| `providers.kubernetesIngress.`
`ingressEndpoint.publishedService` | The Kubernetes service to copy status from.
More information [here](#ingressendpointpublishedservice). | "" | No | +| `providers.kubernetesIngress.throttleDuration` | Minimum amount of time to wait between two Kubernetes events before producing a new configuration.
This prevents a Kubernetes cluster that updates many times per second from continuously changing your Traefik configuration.
If empty, every event is caught. | 0s | No | +| `providers.kubernetesIngress.allowEmptyServices` | Allows creating a route to reach a service that has no endpoint available.
It allows Traefik to handle the requests and responses targeting this service (applying middleware or observability operations) before returning a `503` HTTP Status. | false | No | +| `providers.kubernetesIngress.allowCrossNamespace` | Allows the `Ingress` to reference resources in namespaces other than theirs. | false | No | +| `providers.kubernetesIngress.allowExternalNameServices` | Allows the `Ingress` to reference ExternalName services. | false | No | +| `providers.kubernetesIngress.nativeLBByDefault` | Allow using the Kubernetes Service load balancing between the pods instead of the one provided by Traefik for every `Ingress` by default.
It can br overridden in the [`ServerTransport`](../../../../routing/services/index.md#serverstransport). | false | No | +| `providers.kubernetesIngress.disableClusterScopeResources` | Prevent from discovering cluster scope resources (`IngressClass` and `Nodes`).
By doing so, it alleviates the requirement of giving Traefik the rights to look up for cluster resources.
Furthermore, Traefik will not handle Ingresses with IngressClass references, therefore such Ingresses will be ignored (please note that annotations are not affected by this option).
This will also prevent from using the `NodePortLB` options on services. | false | No | @@ -82,7 +75,7 @@ Both are mounted automatically when deployed inside Kubernetes. The endpoint may be specified to override the environment variable values inside a cluster. -When the environment variables are not found, Traefik tries to connect to the +When the environment variables are not found, Traefik tries to connect to the Kubernetes API server with an external-cluster client. In this case, the endpoint is required. @@ -106,7 +99,7 @@ providers: --providers.kubernetesingress.endpoint=http://localhost:8080 ``` -### `ingressEndpoint.publishedService` +### `ingressEndpoint.publishedService` Format: `namespace/servicename`. @@ -137,16 +130,17 @@ providers: --providers.kubernetesingress.ingressendpoint.publishedservice=namespace/foo-service ``` + ## Routing Configuration See the dedicated section in [routing](../../../../routing/providers/kubernetes-ingress.md). ## Further -To learn more about the various aspects of the Ingress specification that +To learn more about the various aspects of the Ingress specification that Traefik supports, -many examples of Ingresses definitions are located in the test -[examples](https://github.com/traefik/traefik/tree/v3.1/pkg/provider/kubernetes/ingress/fixtures) +many examples of Ingresses definitions are located in the test +[examples](https://github.com/traefik/traefik/tree/v3.1/pkg/provider/kubernetes/ingress/fixtures) of the Traefik repository. {!traefik-for-business-applications.md!} diff --git a/docs/content/reference/install-configuration/providers/kv/etcd.md b/docs/content/reference/install-configuration/providers/kv/etcd.md index 9cc848604..b5e80e6b6 100644 --- a/docs/content/reference/install-configuration/providers/kv/etcd.md +++ b/docs/content/reference/install-configuration/providers/kv/etcd.md @@ -26,16 +26,16 @@ providers: | Field | Description | Default | Required | |:------|:----------------------------------------------------------|:---------------------|:---------| -| `providers.providersThrottleDuration` | Minimum amount of time to wait for, after a configuration reload, before taking into account any new configuration refresh event.
If multiple events occur within this time, only the most recent one is taken into account, and all others are discarded.
**This option cannot be set per provider, but the throttling algorithm applies to each of them independently.** | 2s | No | -| `providers.etcd.endpoints` | Defines the endpoint to access etcd. | "127.0.0.1:2379" | Yes | -| `providers.etcd.rootKey` | Defines the root key for the configuration. | "traefik" | Yes | -| `providers.etcd.username` | Defines a username with which to connect to etcd. | "" | No | -| `providers.etcd.password` | Defines a password for connecting to etcd. | "" | No | -| `providers.etcd.tls` | Defines the TLS configuration used for the secure connection to etcd. | - | No | -| `providers.etcd.tls.ca` | Defines the path to the certificate authority used for the secure connection to etcd, it defaults to the system bundle. | "" | No | -| `providers.etcd.tls.cert` | Defines the path to the public certificate used for the secure connection to etcd. When using this option, setting the `key` option is required. | "" | Yes | -| `providers.etcd.tls.key` | Defines the path to the private key used for the secure connection to etcd. When using this option, setting the `cert` option is required. | "" | Yes | -| `providers.etcd.tls.insecureSkipVerify` | Instructs the provider to accept any certificate presented by etcd when establishing a TLS connection, regardless of the hostnames the certificate covers. | false | No | +| `providers.providersThrottleDuration` | Minimum amount of time to wait for, after a configuration reload, before taking into account any new configuration refresh event.
If multiple events occur within this time, only the most recent one is taken into account, and all others are discarded.
**This option cannot be set per provider, but the throttling algorithm applies to each of them independently.** | 2s | No | +| `providers.etcd.endpoints` | Defines the endpoint to access etcd. | "127.0.0.1:2379" | Yes | +| `providers.etcd.rootKey` | Defines the root key for the configuration. | "traefik" | Yes | +| `providers.etcd.username` | Defines a username with which to connect to etcd. | "" | No | +| `providers.etcd.password` | Defines a password for connecting to etcd. | "" | No | +| `providers.etcd.tls` | Defines the TLS configuration used for the secure connection to etcd. | - | No | +| `providers.etcd.tls.ca` | Defines the path to the certificate authority used for the secure connection to etcd, it defaults to the system bundle. | "" | No | +| `providers.etcd.tls.cert` | Defines the path to the public certificate used for the secure connection to etcd. When using this option, setting the `key` option is required. | "" | Yes | +| `providers.etcd.tls.key` | Defines the path to the private key used for the secure connection to etcd. When using this option, setting the `cert` option is required. | "" | Yes | +| `providers.etcd.tls.insecureSkipVerify` | Instructs the provider to accept any certificate presented by etcd when establishing a TLS connection, regardless of the hostnames the certificate covers. | false | No | ## Routing Configuration diff --git a/docs/content/reference/install-configuration/providers/kv/redis.md b/docs/content/reference/install-configuration/providers/kv/redis.md index ae5f25b18..40c91bfbc 100644 --- a/docs/content/reference/install-configuration/providers/kv/redis.md +++ b/docs/content/reference/install-configuration/providers/kv/redis.md @@ -26,25 +26,25 @@ providers: | Field | Description | Default | Required | |:------|:----------------------------------------------------------|:---------------------|:---------| -| `providers.providersThrottleDuration` | Minimum amount of time to wait for, after a configuration reload, before taking into account any new configuration refresh event.
If multiple events occur within this time, only the most recent one is taken into account, and all others are discarded.
**This option cannot be set per provider, but the throttling algorithm applies to each of them independently.** | 2s | No | -| `providers.redis.endpoints` | Defines the endpoint to access Redis. | "127.0.0.1:6379" | Yes | -| `providers.redis.rootKey` | Defines the root key for the configuration. | "traefik" | Yes | -| `providers.redis.username` | Defines a username for connecting to Redis. | "" | No | -| `providers.redis.password` | Defines a password for connecting to Redis. | "" | No | -| `providers.redis.db` | Defines the database to be selected after connecting to the Redis. | 0 | No | -| `providers.redis.tls` | Defines the TLS configuration used for the secure connection to Redis. | - | No | -| `providers.redis.tls.ca` | Defines the path to the certificate authority used for the secure connection to Redis, it defaults to the system bundle. | "" | No | -| `providers.redis.tls.cert` | Defines the path to the public certificate used for the secure connection to Redis. When using this option, setting the `key` option is required. | "" | Yes | -| `providers.redis.tls.key` | Defines the path to the private key used for the secure connection to Redis. When using this option, setting the `cert` option is required. | "" | Yes | -| `providers.redis.tls.insecureSkipVerify` | Instructs the provider to accept any certificate presented by Redis when establishing a TLS connection, regardless of the hostnames the certificate covers. | false | No | -| `providers.redis.sentinel` | Defines the Sentinel configuration used to interact with Redis Sentinel. | - | No | -| `providers.redis.sentinel.masterName` | Defines the name of the Sentinel master. | "" | Yes | -| `providers.redis.sentinel.username` | Defines the username for Sentinel authentication. | "" | No | -| `providers.redis.sentinel.password` | Defines the password for Sentinel authentication. | "" | No | -| `providers.redis.sentinel.latencyStrategy` | Defines whether to route commands to the closest master or replica nodes (mutually exclusive with RandomStrategy and ReplicaStrategy). | false | No | -| `providers.redis.sentinel.randomStrategy` | Defines whether to route commands randomly to master or replica nodes (mutually exclusive with LatencyStrategy and ReplicaStrategy). | false | No | -| `providers.redis.sentinel.replicaStrategy` | Defines whether to route commands randomly to master or replica nodes (mutually exclusive with LatencyStrategy and ReplicaStrategy). | false | No | -| `providers.redis.sentinel.useDisconnectedReplicas` | Defines whether to use replicas disconnected with master when cannot get connected replicas. | false | false | +| `providers.providersThrottleDuration` | Minimum amount of time to wait for, after a configuration reload, before taking into account any new configuration refresh event.
If multiple events occur within this time, only the most recent one is taken into account, and all others are discarded.
**This option cannot be set per provider, but the throttling algorithm applies to each of them independently.** | 2s | No | +| `providers.redis.endpoints` | Defines the endpoint to access Redis. | "127.0.0.1:6379" | Yes | +| `providers.redis.rootKey` | Defines the root key for the configuration. | "traefik" | Yes | +| `providers.redis.username` | Defines a username for connecting to Redis. | "" | No | +| `providers.redis.password` | Defines a password for connecting to Redis. | "" | No | +| `providers.redis.db` | Defines the database to be selected after connecting to the Redis. | 0 | No | +| `providers.redis.tls` | Defines the TLS configuration used for the secure connection to Redis. | - | No | +| `providers.redis.tls.ca` | Defines the path to the certificate authority used for the secure connection to Redis, it defaults to the system bundle. | "" | No | +| `providers.redis.tls.cert` | Defines the path to the public certificate used for the secure connection to Redis. When using this option, setting the `key` option is required. | "" | Yes | +| `providers.redis.tls.key` | Defines the path to the private key used for the secure connection to Redis. When using this option, setting the `cert` option is required. | "" | Yes | +| `providers.redis.tls.insecureSkipVerify` | Instructs the provider to accept any certificate presented by Redis when establishing a TLS connection, regardless of the hostnames the certificate covers. | false | No | +| `providers.redis.sentinel` | Defines the Sentinel configuration used to interact with Redis Sentinel. | - | No | +| `providers.redis.sentinel.masterName` | Defines the name of the Sentinel master. | "" | Yes | +| `providers.redis.sentinel.username` | Defines the username for Sentinel authentication. | "" | No | +| `providers.redis.sentinel.password` | Defines the password for Sentinel authentication. | "" | No | +| `providers.redis.sentinel.latencyStrategy` | Defines whether to route commands to the closest master or replica nodes (mutually exclusive with RandomStrategy and ReplicaStrategy). | false | No | +| `providers.redis.sentinel.randomStrategy` | Defines whether to route commands randomly to master or replica nodes (mutually exclusive with LatencyStrategy and ReplicaStrategy). | false | No | +| `providers.redis.sentinel.replicaStrategy` | Defines whether to route commands randomly to master or replica nodes (mutually exclusive with LatencyStrategy and ReplicaStrategy). | false | No | +| `providers.redis.sentinel.useDisconnectedReplicas` | Defines whether to use replicas disconnected with master when cannot get connected replicas. | false | false | ## Routing Configuration diff --git a/docs/content/reference/install-configuration/providers/kv/zk.md b/docs/content/reference/install-configuration/providers/kv/zk.md index b77e5a23d..38042a1a9 100644 --- a/docs/content/reference/install-configuration/providers/kv/zk.md +++ b/docs/content/reference/install-configuration/providers/kv/zk.md @@ -26,16 +26,16 @@ providers: | Field | Description | Default | Required | |:------|:----------------------------------------------------------|:---------------------|:---------| -| `providers.providersThrottleDuration` | Minimum amount of time to wait for, after a configuration reload, before taking into account any new configuration refresh event.
If multiple events occur within this time, only the most recent one is taken into account, and all others are discarded.
**This option cannot be set per provider, but the throttling algorithm applies to each of them independently.** | 2s | No | -| `providers.zooKeeper.endpoints` | Defines the endpoint to access ZooKeeper. | "127.0.0.1:2181" | Yes | -| `providers.zooKeeper.rootKey` | Defines the root key for the configuration. | "traefik" | Yes | -| `providers.zooKeeper.username` | Defines a username with which to connect to zooKeeper. | "" | No | -| `providers.zooKeeper.password` | Defines a password for connecting to zooKeeper. | "" | No | -| `providers.zooKeeper.tls` | Defines the TLS configuration used for the secure connection to zooKeeper. | - | No | -| `providers.zooKeeper.tls.ca` | Defines the path to the certificate authority used for the secure connection to zooKeeper, it defaults to the system bundle. | "" | No | -| `providers.zooKeeper.tls.cert` | Defines the path to the public certificate used for the secure connection to zooKeeper. When using this option, setting the `key` option is required. | "" | Yes | -| `providers.zooKeeper.tls.key` | Defines the path to the private key used for the secure connection to zooKeeper. When using this option, setting the `cert` option is required. | "" | Yes | -| `providers.zooKeeper.tls.insecureSkipVerify` | Instructs the provider to accept any certificate presented by etcd when establishing a TLS connection, regardless of the hostnames the certificate covers. | false | No | +| `providers.providersThrottleDuration` | Minimum amount of time to wait for, after a configuration reload, before taking into account any new configuration refresh event.
If multiple events occur within this time, only the most recent one is taken into account, and all others are discarded.
**This option cannot be set per provider, but the throttling algorithm applies to each of them independently.** | 2s | No | +| `providers.zooKeeper.endpoints` | Defines the endpoint to access ZooKeeper. | "127.0.0.1:2181" | Yes | +| `providers.zooKeeper.rootKey` | Defines the root key for the configuration. | "traefik" | Yes | +| `providers.zooKeeper.username` | Defines a username with which to connect to zooKeeper. | "" | No | +| `providers.zooKeeper.password` | Defines a password for connecting to zooKeeper. | "" | No | +| `providers.zooKeeper.tls` | Defines the TLS configuration used for the secure connection to zooKeeper. | - | No | +| `providers.zooKeeper.tls.ca` | Defines the path to the certificate authority used for the secure connection to zooKeeper, it defaults to the system bundle. | "" | No | +| `providers.zooKeeper.tls.cert` | Defines the path to the public certificate used for the secure connection to zooKeeper. When using this option, setting the `key` option is required. | "" | Yes | +| `providers.zooKeeper.tls.key` | Defines the path to the private key used for the secure connection to zooKeeper. When using this option, setting the `cert` option is required. | "" | Yes | +| `providers.zooKeeper.tls.insecureSkipVerify` | Instructs the provider to accept any certificate presented by etcd when establishing a TLS connection, regardless of the hostnames the certificate covers. | false | No | ## Routing Configuration diff --git a/docs/content/reference/install-configuration/providers/others/ecs.md b/docs/content/reference/install-configuration/providers/others/ecs.md index 2cca65977..f6aeb2731 100644 --- a/docs/content/reference/install-configuration/providers/others/ecs.md +++ b/docs/content/reference/install-configuration/providers/others/ecs.md @@ -26,18 +26,18 @@ providers: | Field | Description | Default | Required | |:------|:----------------------------------------------------------|:---------------------|:---------| -| `providers.providersThrottleDuration` | Minimum amount of time to wait for, after a configuration reload, before taking into account any new configuration refresh event.
If multiple events occur within this time, only the most recent one is taken into account, and all others are discarded.
**This option cannot be set per provider, but the throttling algorithm applies to each of them independently.** | 2s | No | -| `providers.ecs.autoDiscoverClusters` | Search for services in cluster list. If set to `true` service discovery is enabled for all clusters. | false | No | -| `providers.ecs.ecsAnywhere` | Enable ECS Anywhere support. | false | No | -| `providers.ecs.clusters` | Search for services in cluster list. This option is ignored if `autoDiscoverClusters` is set to `true`. | `["default"]` | No | -| `providers.ecs.exposedByDefault` | Expose ECS services by default in Traefik. | true | No | -| `providers.ecs.constraints` | Defines an expression that Traefik matches against the container labels to determine whether to create any route for that container. See [here](#constraints) for more information. | true | No | -| `providers.ecs.healthyTasksOnly` | Defines whether Traefik discovers only healthy tasks (`HEALTHY` healthStatus). | false | No | -| `providers.ecs.defaultRule` | The Default Host rule for all services. See [here](#defaultrule) for more information. | ```"Host(`{{ normalize .Name }}`)"``` | No | -| `providers.ecs.refreshSeconds` | Defines the polling interval (in seconds). | 15 | No | -| `providers.ecs.region` | Defines the region of the ECS instance. See [here](#credentials) for more information. | "" | No | -| `providers.ecs.accessKeyID` | Defines the Access Key ID for the ECS instance. See [here](#credentials) for more information. | "" | No | -| `providers.ecs.secretAccessKey` | Defines the Secret Access Key for the ECS instance. See [here](#credentials) for more information. | "" | No | +| `providers.providersThrottleDuration` | Minimum amount of time to wait for, after a configuration reload, before taking into account any new configuration refresh event.
If multiple events occur within this time, only the most recent one is taken into account, and all others are discarded.
**This option cannot be set per provider, but the throttling algorithm applies to each of them independently.** | 2s | No | +| `providers.ecs.autoDiscoverClusters` | Search for services in cluster list. If set to `true` service discovery is enabled for all clusters. | false | No | +| `providers.ecs.ecsAnywhere` | Enable ECS Anywhere support. | false | No | +| `providers.ecs.clusters` | Search for services in cluster list. This option is ignored if `autoDiscoverClusters` is set to `true`. | `["default"]` | No | +| `providers.ecs.exposedByDefault` | Expose ECS services by default in Traefik. | true | No | +| `providers.ecs.constraints` | Defines an expression that Traefik matches against the container labels to determine whether to create any route for that container. See [here](#constraints) for more information. | true | No | +| `providers.ecs.healthyTasksOnly` | Defines whether Traefik discovers only healthy tasks (`HEALTHY` healthStatus). | false | No | +| `providers.ecs.defaultRule` | The Default Host rule for all services. See [here](#defaultrule) for more information. | ```"Host(`{{ normalize .Name }}`)"``` | No | +| `providers.ecs.refreshSeconds` | Defines the polling interval (in seconds). | 15 | No | +| `providers.ecs.region` | Defines the region of the ECS instance. See [here](#credentials) for more information. | "" | No | +| `providers.ecs.accessKeyID` | Defines the Access Key ID for the ECS instance. See [here](#credentials) for more information. | "" | No | +| `providers.ecs.secretAccessKey` | Defines the Secret Access Key for the ECS instance. See [here](#credentials) for more information. | "" | No | ### `constraints` @@ -49,10 +49,6 @@ If the expression is empty, all detected containers are included. The expression syntax is based on the `Label("key", "value")`, and `LabelRegex("key", "value")` functions, as well as the usual boolean logic, as shown in examples below. -!!! tip "Constraints key limitations" - - Note that `traefik.*` is a reserved label namespace for configuration and can not be used as a key for custom constraints. - ??? example "Constraints Expression Examples" ```toml @@ -103,7 +99,7 @@ providers: # ... ``` -For additional information, refer to [Restrict the Scope of Service Discovery](../overview.md#exposedbydefault-and-traefikenable). +For additional information, refer to [Restrict the Scope of Service Discovery](../overview.md#restrict-the-scope-of-service-discovery). ### `defaultRule` diff --git a/docs/content/reference/install-configuration/providers/others/file.md b/docs/content/reference/install-configuration/providers/others/file.md index 737c7a65c..6d4fad6d2 100644 --- a/docs/content/reference/install-configuration/providers/others/file.md +++ b/docs/content/reference/install-configuration/providers/others/file.md @@ -100,10 +100,10 @@ http: | Field | Description | Default | Required | |:------|:----------------------------------------------------------|:---------------------|:---------| -| `providers.providersThrottleDuration` | Minimum amount of time to wait for, after a configuration reload, before taking into account any new configuration refresh event.
If multiple events occur within this time, only the most recent one is taken into account, and all others are discarded.
**This option cannot be set per provider, but the throttling algorithm applies to each of them independently.** | 2s | No | -| `providers.file.filename` | Defines the path to the configuration file. | "" | Yes | -| `providers.file.directory` | Defines the path to the directory that contains the configuration files. The `filename` and `directory` options are mutually exclusive. It is recommended to use `directory`. | "" | Yes | -| `providers.file.watch` | Set the `watch` option to `true` to allow Traefik to automatically watch for file changes. It works with both the `filename` and the `directory` options. | true | No | +| `providers.providersThrottleDuration` | Minimum amount of time to wait for, after a configuration reload, before taking into account any new configuration refresh event.
If multiple events occur within this time, only the most recent one is taken into account, and all others are discarded.
**This option cannot be set per provider, but the throttling algorithm applies to each of them independently.** | 2s | No | +| `providers.file.filename` | Defines the path to the configuration file. | "" | Yes | +| `providers.file.directory` | Defines the path to the directory that contains the configuration files. The `filename` and `directory` options are mutually exclusive. It is recommended to use `directory`. | "" | Yes | +| `providers.file.watch` | Set the `watch` option to `true` to allow Traefik to automatically watch for file changes. It works with both the `filename` and the `directory` options. | true | No | !!! warning "Limitations" diff --git a/docs/content/reference/install-configuration/providers/others/http.md b/docs/content/reference/install-configuration/providers/others/http.md index 9e67df2e3..4ac29b6fe 100644 --- a/docs/content/reference/install-configuration/providers/others/http.md +++ b/docs/content/reference/install-configuration/providers/others/http.md @@ -30,15 +30,15 @@ providers: | Field | Description | Default | Required | |:------|:----------------------------------------------------------|:---------------------|:---------| -| `providers.providersThrottleDuration` | Minimum amount of time to wait for, after a configuration reload, before taking into account any new configuration refresh event.
If multiple events occur within this time, only the most recent one is taken into account, and all others are discarded.
**This option cannot be set per provider, but the throttling algorithm applies to each of them independently.** | 2s | No | -| `providers.http.endpoint` | Defines the HTTP(S) endpoint to poll. | "" | Yes | -| `providers.http.pollInterval` | Defines the polling interval. | 5s | No | -| `providers.http.pollTimeout` | Defines the polling timeout when connecting to the endpoint. | 5s | No | -| `providers.http.headers` | Defines custom headers to be sent to the endpoint. | "" | No | -| `providers.http.tls.ca` | Defines the path to the certificate authority used for the secure connection to the endpoint, it defaults to the system bundle. | "" | No | -| `providers.http.tls.cert` | Defines the path to the public certificate used for the secure connection to the endpoint. When using this option, setting the `key` option is required. | "" | Yes | -| `providers.http.tls.key` | Defines the path to the private key used for the secure connection to the endpoint. When using this option, setting the `cert` option is required. | "" | Yes | -| `providers.http.tls.insecureSkipVerify` | Instructs the provider to accept any certificate presented by endpoint when establishing a TLS connection, regardless of the hostnames the certificate covers. | false | No | +| `providers.providersThrottleDuration` | Minimum amount of time to wait for, after a configuration reload, before taking into account any new configuration refresh event.
If multiple events occur within this time, only the most recent one is taken into account, and all others are discarded.
**This option cannot be set per provider, but the throttling algorithm applies to each of them independently.** | 2s | No | +| `providers.http.endpoint` | Defines the HTTP(S) endpoint to poll. | "" | Yes | +| `providers.http.pollInterval` | Defines the polling interval. | 5s | No | +| `providers.http.pollTimeout` | Defines the polling timeout when connecting to the endpoint. | 5s | No | +| `providers.http.headers` | Defines custom headers to be sent to the endpoint. | "" | No | +| `providers.http.tls.ca` | Defines the path to the certificate authority used for the secure connection to the endpoint, it defaults to the system bundle. | "" | No | +| `providers.http.tls.cert` | Defines the path to the public certificate used for the secure connection to the endpoint. When using this option, setting the `key` option is required. | "" | Yes | +| `providers.http.tls.key` | Defines the path to the private key used for the secure connection to the endpoint. When using this option, setting the `cert` option is required. | "" | Yes | +| `providers.http.tls.insecureSkipVerify` | Instructs the provider to accept any certificate presented by endpoint when establishing a TLS connection, regardless of the hostnames the certificate covers. | false | No | ### headers diff --git a/docs/content/reference/install-configuration/providers/overview.md b/docs/content/reference/install-configuration/providers/overview.md index 5253f57a0..3edc93471 100644 --- a/docs/content/reference/install-configuration/providers/overview.md +++ b/docs/content/reference/install-configuration/providers/overview.md @@ -51,20 +51,20 @@ Below is the list of the currently supported providers in Traefik. | Provider | Type | Configuration Type | Provider Name | |--------------------------------------------------------------|--------------|----------------------|---------------------| -| [Docker](./docker.md) | Orchestrator | Label | `docker` | -| [Docker Swarm](./swarm.md) | Orchestrator | Label | `swarm` | -| [Kubernetes IngressRoute](./kubernetes/kubernetes-crd.md) | Orchestrator | Custom Resource | `kubernetescrd` | -| [Kubernetes Ingress](./kubernetes/kubernetes-ingress.md) | Orchestrator | Ingress | `kubernetes` | -| [Kubernetes Gateway API](./kubernetes/kubernetes-gateway.md) | Orchestrator | Gateway API Resource | `kubernetesgateway` | -| [Consul Catalog](./hashicorp/consul-catalog.md) | Orchestrator | Label | `consulcatalog` | -| [Nomad](./hashicorp/nomad.md) | Orchestrator | Label | `nomad` | -| [ECS](./others/ecs.md) | Orchestrator | Label | `ecs` | -| [File](./others/file.md) | Manual | YAML/TOML format | `file` | -| [Consul](./hashicorp/consul.md) | KV | KV | `consul` | -| [Etcd](./kv/etcd.md) | KV | KV | `etcd` | -| [ZooKeeper](./kv/zk.md) | KV | KV | `zookeeper` | -| [Redis](./kv/redis.md) | KV | KV | `redis` | -| [HTTP](./others/http.md) | Manual | JSON/YAML format | `http` | +| [Docker](./docker.md) | Orchestrator | Label | `docker` | +| [Docker Swarm](./swarm.md) | Orchestrator | Label | `swarm` | +| [Kubernetes IngressRoute](./kubernetes/kubernetes-crd.md) | Orchestrator | Custom Resource | `kubernetescrd` | +| [Kubernetes Ingress](./kubernetes/kubernetes-ingress.md) | Orchestrator | Ingress | `kubernetes` | +| [Kubernetes Gateway API](./kubernetes/kubernetes-gateway.md) | Orchestrator | Gateway API Resource | `kubernetesgateway` | +| [Consul Catalog](./hashicorp/consul-catalog.md) | Orchestrator | Label | `consulcatalog` | +| [Nomad](./hashicorp/nomad.md) | Orchestrator | Label | `nomad` | +| [ECS](./others/ecs.md) | Orchestrator | Label | `ecs` | +| [File](./others/file.md) | Manual | YAML/TOML format | `file` | +| [Consul](./hashicorp/consul.md) | KV | KV | `consul` | +| [Etcd](./kv/etcd.md) | KV | KV | `etcd` | +| [ZooKeeper](./kv/zk.md) | KV | KV | `zookeeper` | +| [Redis](./kv/redis.md) | KV | KV | `redis` | +| [HTTP](./others/http.md) | Manual | JSON/YAML format | `http` | !!! info "More Providers" diff --git a/docs/content/reference/install-configuration/providers/swarm.md b/docs/content/reference/install-configuration/providers/swarm.md index 8f876dfd2..becfdd577 100644 --- a/docs/content/reference/install-configuration/providers/swarm.md +++ b/docs/content/reference/install-configuration/providers/swarm.md @@ -33,6 +33,7 @@ When there is only one service, and the router does not specify a service, then that service is automatically assigned to the router. ```yaml tab="Labels" +version: "3" services: my-container: deploy: @@ -43,25 +44,25 @@ services: ## Configuration Options -| Field | Description | Default | Required | -|:-----------------------------------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:--------------------------------------|:---------| -| `providers.providersThrottleDuration` | Minimum amount of time to wait for, after a configuration reload, before taking into account any new configuration refresh event.
If multiple events occur within this time, only the most recent one is taken into account, and all others are discarded.
**This option cannot be set per provider, but the throttling algorithm applies to each of them independently.** | 2s | No | -| `providers.swarm.endpoint` | Specifies the Docker API endpoint. See [here](#endpoint) for more information | `unix:///var/run/docker.sock` | Yes | -| `providers.swarm.username` | Defines the username for Basic HTTP authentication. This should be used when the Docker daemon socket is exposed through an HTTP proxy that requires Basic HTTP authentication. | "" | No | -| `providers.swarm.password` | Defines the password for Basic HTTP authentication. This should be used when the Docker daemon socket is exposed through an HTTP proxy that requires Basic HTTP authentication. | "" | No | -| `providers.swarm.useBindPortIP` | Instructs Traefik to use the IP/Port attached to the container's binding instead of its inner network IP/Port. See [here](#usebindportip) for more information | false | No | -| `providers.swarm.exposedByDefault` | Expose containers by default through Traefik. See [here](./overview.md#exposedbydefault-and-traefikenable) for additional information | true | No | -| `providers.swarm.network` | Defines a default docker network to use for connections to all containers. This option can be overridden on a per-container basis with the `traefik.swarm.network` label. | "" | No | -| `providers.swarm.defaultRule` | Defines what routing rule to apply to a container if no rule is defined by a label. See [here](#defaultrule) for more information | ```"Host(`{{ normalize .Name }}`)"``` | No | -| `providers.swarm.refreshSeconds` | Defines the polling interval for Swarm Mode. | "15s" | No | -| `providers.swarm.httpClientTimeout` | Defines the client timeout (in seconds) for HTTP connections. If its value is 0, no timeout is set. | 0 | No | -| `providers.swarm.watch` | Instructs Traefik to watch Docker events or not. | True | No | -| `providers.swarm.constraints` | Defines an expression that Traefik matches against the container labels to determine whether to create any route for that container. See [here](#constraints) for more information. | "" | No | -| `providers.swarm.allowEmptyServices` | Instructs the provider to create any [servers load balancer](../../../routing/services/index.md#servers-load-balancer) defined for Docker containers regardless of the [healthiness](https://docs.docker.com/engine/reference/builder/#healthcheck) of the corresponding containers. | false | No | -| `providers.swarm.tls.ca` | Defines the path to the certificate authority used for the secure connection to Docker, it defaults to the system bundle. | "" | No | -| `providers.swarm.tls.cert` | Defines the path to the public certificate used for the secure connection to Docker. When using this option, setting the `key` option is required. | "" | Yes | -| `providers.swarm.tls.key` | Defines the path to the private key used for the secure connection to Docker. When using this option, setting the `cert` option is required. | "" | Yes | -| `providers.swarm.tls.insecureSkipVerify` | Instructs the provider to accept any certificate presented by the Docker server when establishing a TLS connection, regardless of the hostnames the certificate covers. | false | No | +| Field | Description | Default | Required | +|:------|:----------------------------------------------------------|:---------------------|:---------| +| `providers.providersThrottleDuration` | Minimum amount of time to wait for, after a configuration reload, before taking into account any new configuration refresh event.
If multiple events occur within this time, only the most recent one is taken into account, and all others are discarded.
**This option cannot be set per provider, but the throttling algorithm applies to each of them independently.** | 2s | No | +| `providers.swarm.endpoint` | Specifies the Docker API endpoint. See [here](#endpoint) for more information| `unix:///var/run/docker.sock` | Yes | +| `providers.swarm.username` | Defines the username for Basic HTTP authentication. This should be used when the Docker daemon socket is exposed through an HTTP proxy that requires Basic HTTP authentication.| "" | No | +| `providers.swarm.password` | Defines the password for Basic HTTP authentication. This should be used when the Docker daemon socket is exposed through an HTTP proxy that requires Basic HTTP authentication.| "" | No | +| `providers.swarm.useBindPortIP` | Instructs Traefik to use the IP/Port attached to the container's binding instead of its inner network IP/Port. See [here](#usebindportip) for more information | false | No | +| `providers.swarm.exposedByDefault` | Expose containers by default through Traefik. See [here](./overview.md#restrict-the-scope-of-service-discovery) for additional information | true | No | +| `providers.swarm.network` | Defines a default docker network to use for connections to all containers. This option can be overridden on a per-container basis with the `traefik.docker.network` label.| "" | No | +| `providers.swarm.defaultRule` | Defines what routing rule to apply to a container if no rule is defined by a label. See [here](#defaultrule) for more information | ```"Host(`{{ normalize .Name }}`)"``` | No | +| `providers.swarm.refreshSeconds` | Defines the polling interval for Swarm Mode. | "15s" | No | +| `providers.swarm.httpClientTimeout` | Defines the client timeout (in seconds) for HTTP connections. If its value is 0, no timeout is set. | 0 | No | +| `providers.swarm.watch` | Instructs Traefik to watch Docker events or not. | True | No | +| `providers.swarm.constraints` | Defines an expression that Traefik matches against the container labels to determine whether to create any route for that container. See [here](#constraints) for more information. | "" | No | +| `providers.swarm.allowEmptyServices` | Instructs the provider to create any [servers load balancer](../../../routing/services/index.md#servers-load-balancer) defined for Docker containers regardless of the [healthiness](https://docs.docker.com/engine/reference/builder/#healthcheck) of the corresponding containers. | false | No | +| `providers.swarm.tls.ca` | Defines the path to the certificate authority used for the secure connection to Docker, it defaults to the system bundle. | "" | No | +| `providers.swarm.tls.cert` | Defines the path to the public certificate used for the secure connection to Docker. When using this option, setting the `key` option is required. | "" | Yes | +| `providers.swarm.tls.key` | Defines the path to the private key used for the secure connection to Docker. When using this option, setting the `cert` option is required. | "" | Yes | +| `providers.swarm.tls.insecureSkipVerify` | Instructs the provider to accept any certificate presented by the Docker server when establishing a TLS connection, regardless of the hostnames the certificate covers. | false | No | ### `endpoint` @@ -72,6 +73,8 @@ See the [Docker Swarm API Access](#docker-api-access) section for more informati The docker-compose file shares the docker sock with the Traefik container ```yaml + version: '3' + services: traefik: image: traefik:v3.1 # The official v3 Traefik docker image @@ -97,7 +100,7 @@ See the [Docker Swarm API Access](#docker-api-access) section for more informati ``` ```bash tab="CLI" - --providers.swarm.endpoint=unix:///var/run/docker.sock + --providers.docker.endpoint=unix:///var/run/docker.sock # ... ``` @@ -198,13 +201,13 @@ but still uses the `traefik.http.services..loadbalancer.server.port` that | port label | Container's binding | Routes to | |--------------------|----------------------------------------------------|----------------| - | - | - | IntIP:IntPort | - | - | ExtPort:IntPort | IntIP:IntPort | - | - | ExtIp:ExtPort:IntPort | ExtIp:ExtPort | - | LblPort | - | IntIp:LblPort | - | LblPort | ExtIp:ExtPort:LblPort | ExtIp:ExtPort | - | LblPort | ExtIp:ExtPort:OtherPort | IntIp:LblPort | - | LblPort | ExtIp1:ExtPort1:IntPort1 & ExtIp2:LblPort:IntPort2 | ExtIp2:LblPort | + | - | - | IntIP:IntPort | + | - | ExtPort:IntPort | IntIP:IntPort | + | - | ExtIp:ExtPort:IntPort | ExtIp:ExtPort | + | LblPort | - | IntIp:LblPort | + | LblPort | ExtIp:ExtPort:LblPort | ExtIp:ExtPort | + | LblPort | ExtIp:ExtPort:OtherPort | IntIp:LblPort | + | LblPort | ExtIp1:ExtPort1:IntPort1 & ExtIp2:LblPort:IntPort2 | ExtIp2:LblPort | !!! info "" In the above table: @@ -276,10 +279,6 @@ created. If the expression is empty, all detected containers are included. The expression syntax is based on the `Label("key", "value")`, and `LabelRegex("key", "value")` functions, as well as the usual boolean logic, as shown in examples below. -!!! tip "Constraints key limitations" - - Note that `traefik.*` is a reserved label namespace for configuration and can not be used as a key for custom constraints. - ??? example "Constraints Expression Examples" ```toml @@ -312,7 +311,7 @@ as well as the usual boolean logic, as shown in examples below. constraints = "LabelRegex(`a.label.name`, `a.+`)" ``` -For additional information, refer to [Restrict the Scope of Service Discovery](./overview.md#exposedbydefault-and-traefikenable). +For additional information, refer to [Restrict the Scope of Service Discovery](./overview.md#restrict-the-scope-of-service-discovery). ```yaml tab="File (YAML)" providers: @@ -406,6 +405,8 @@ docker service create \ ``` ```yml tab="With Docker Compose" +version: '3' + services: traefik: # ... diff --git a/docs/content/reference/install-configuration/tls/certificate-resolvers/acme.md b/docs/content/reference/install-configuration/tls/certificate-resolvers/acme.md index f923e6c72..1b05bde5a 100644 --- a/docs/content/reference/install-configuration/tls/certificate-resolvers/acme.md +++ b/docs/content/reference/install-configuration/tls/certificate-resolvers/acme.md @@ -73,30 +73,27 @@ certificatesResolvers: ACME certificate resolvers have the following configuration options: -| Field | Description | Default | Required | -|:--------------------------------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-----------------------------------------------|:---------| -| `acme.email` | Email address used for registration. | "" | Yes | -| `acme.caServer` | CA server to use. | https://acme-v02.api.letsencrypt.org/directory | No | -| `acme.preferredChain` | Preferred chain to use. If the CA offers multiple certificate chains, prefer the chain with an issuer matching this Subject Common Name. If no match, the default offered chain will be used. | "" | No | -| `acme.keyType` | KeyType to use. | "RSA4096" | No | -| `acme.eab` | Enable external account binding. | | No | -| `acme.eab.kid` | Key identifier from External CA. | "" | No | -| `acme.eab.hmacEncoded` | HMAC key from External CA, should be in Base64 URL Encoding without padding format. | "" | No | -| `acme.certificatesDuration` | The certificates' duration in hours, exclusively used to determine renewal dates. | 2160 | No | -| `acme.clientTimeout` | Timeout for HTTP Client used to communicate with the ACME server. | 2m | No | -| `acme.clientResponseHeaderTimeout` | Timeout for response headers for HTTP Client used to communicate with the ACME server. | 30s | No | -| `acme.dnsChallenge` | Enable DNS-01 challenge. More information [here](#dnschallenge). | - | No | -| `acme.dnsChallenge.provider` | DNS provider to use. | "" | No | -| `acme.dnsChallenge.resolvers` | DNS servers to resolve the FQDN authority. | [] | No | -| `acme.dnsChallenge.propagation.delayBeforeChecks` | By default, the provider will verify the TXT DNS challenge record before letting ACME verify. If `delayBeforeCheck` is greater than zero, this check is delayed for the configured duration in seconds. This is Useful if internal networks block external DNS queries. | 0s | No | -| `acme.dnsChallenge.propagation.disableChecks` | Disables the challenge TXT record propagation checks, before notifying ACME that the DNS challenge is ready. Please note that disabling checks can prevent the challenge from succeeding. | false | No | -| `acme.dnsChallenge.propagation.requireAllRNS` | Enables the challenge TXT record to be propagated to all recursive nameservers. If you have disabled authoritative nameservers checks (with `propagation.disableANSChecks`), it is recommended to check all recursive nameservers instead. | false | No | -| `acme.dnsChallenge.propagation.disableANSChecks` | Disables the challenge TXT record propagation checks against authoritative nameservers. This option will skip the propagation check against the nameservers of the authority (SOA). It should be used only if the nameservers of the authority are not reachable. | false | No | -| `acme.httpChallenge` | Enable HTTP-01 challenge. More information [here](#httpchallenge). | | No | -| `acme.httpChallenge.entryPoint` | EntryPoint to use for the HTTP-01 challenges. Must be reachable by Let's Encrypt through port 80 | "" | Yes | -| `acme.httpChallenge.delay` | The delay between the creation of the challenge and the validation. A value lower than or equal to zero means no delay. | 0 | No | -| `acme.tlsChallenge` | Enable TLS-ALPN-01 challenge. Traefik must be reachable by Let's Encrypt through port 443. More information [here](#tlschallenge). | - | No | -| `acme.storage` | File path used for certificates storage. | "acme.json" | Yes | +| Field | Description | Default | Required | +|:------------------|:--------------------|:-----------------------------------------------|:---------| +| `acme.email` | Email address used for registration. | "" | Yes | +| `acme.caServer` | CA server to use. | https://acme-v02.api.letsencrypt.org/directory | No | +| `acme.preferredChain` | Preferred chain to use. If the CA offers multiple certificate chains, prefer the chain with an issuer matching this Subject Common Name. If no match, the default offered chain will be used. | "" | No | +| `acme.keyType` | KeyType to use. | "RSA4096" | No | +| `acme.eab` | Enable external account binding.| | No | +| `acme.eab.kid` | Key identifier from External CA. | "" | No | +| `acme.eab.hmacEncoded` | HMAC key from External CA, should be in Base64 URL Encoding without padding format. | "" | No | +| `acme.certificatesDuration` | The certificates' duration in hours, exclusively used to determine renewal dates. | 2160 | No | +| `acme.dnsChallenge` | Enable DNS-01 challenge. More information [here](#dnschallenge). | - | No | +| `acme.dnsChallenge.provider` | DNS provider to use. | "" | No | +| `acme.dnsChallenge.resolvers` | DNS servers to resolve the FQDN authority. | [] | No | +| `acme.dnsChallenge.propagation.delayBeforeChecks` | By default, the provider will verify the TXT DNS challenge record before letting ACME verify. If `delayBeforeCheck` is greater than zero, this check is delayed for the configured duration in seconds. This is Useful if internal networks block external DNS queries. | 0s | No | +| `acme.dnsChallenge.propagation.disableChecks` | Disables the challenge TXT record propagation checks, before notifying ACME that the DNS challenge is ready. Please note that disabling checks can prevent the challenge from succeeding. | false | No | +| `acme.dnsChallenge.propagation.requireAllRNS` | Enables the challenge TXT record to be propagated to all recursive nameservers. If you have disabled authoritative nameservers checks (with `propagation.disableANSChecks`), it is recommended to check all recursive nameservers instead. | false | No | +| `acme.dnsChallenge.propagation.disableANSChecks` | Disables the challenge TXT record propagation checks against authoritative nameservers. This option will skip the propagation check against the nameservers of the authority (SOA). It should be used only if the nameservers of the authority are not reachable. | false | No | +| `acme.httpChallenge` | Enable HTTP-01 challenge. More information [here](#httpchallenge). | | No | +| `acme.httpChallenge.entryPoint` | EntryPoint to use for the HTTP-01 challenges. Must be reachable by Let's Encrypt through port 80 | "" | Yes | +| `acme.tlsChallenge` | Enable TLS-ALPN-01 challenge. Traefik must be reachable by Let's Encrypt through port 443. More information [here](#tlschallenge). | - | No | +| `acme.storage` | File path used for certificates storage. | "acme.json" | Yes | ## Automatic Certificate Renewal diff --git a/docs/content/reference/install-configuration/tls/ocsp.md b/docs/content/reference/install-configuration/tls/ocsp.md deleted file mode 100644 index 577f3749c..000000000 --- a/docs/content/reference/install-configuration/tls/ocsp.md +++ /dev/null @@ -1,71 +0,0 @@ ---- -title: "Traefik OCSP Documentation" -description: "Learn how to configure Traefik to use OCSP. Read the technical documentation." ---- - -# OCSP - -Check certificate status and perform OCSP stapling. -{: .subtitle } - -## Overview - -### OCSP Stapling - -When OCSP is enabled, Traefik checks the status of every certificate in the store that provides an OCSP responder URL, -including the default certificate, and staples the OCSP response to the TLS handshake. -The OCSP check is performed when the certificate is loaded, -and once every hour until it is successful at the halfway point before the update date. - -### Caching - -Traefik caches the OCSP response as long as the associated certificate is provided by the configuration. -When a certificate is no longer provided, -the OCSP response has a 24 hour TTL waiting to be provided again or eventually removed. -The OCSP response is cached in memory and is not persisted between Traefik restarts. - -## Configuration - -### General - -Enabling OCSP is part of the [install configuration](../boot-environment.md). -It can be defined by using a file (YAML or TOML) or CLI arguments: - -```yaml tab="File (YAML)" -## Static configuration -ocsp: {} -``` - -```toml tab="File (TOML)" -## Static configuration -[ocsp] -``` - -```bash tab="CLI" -## Static configuration ---ocsp=true -``` - -### Responder Overrides - -The `responderOverrides` option defines the OCSP responder URLs to use instead of the one provided by the certificate. -This is useful when you want to use a different OCSP responder. - -```yaml tab="File (YAML)" -## Static configuration -ocsp: - responderOverrides: - foo: bar -``` - -```toml tab="File (TOML)" -## Static configuration -[ocsp] - [ocsp.responderOverrides] - foo = "bar" -``` - -```bash tab="CLI" -## Static configuration ---ocsp.responderoverrides.foo=bar -``` diff --git a/docs/content/reference/install-configuration/tls/spiffe.md b/docs/content/reference/install-configuration/tls/spiffe.md index ca4c10ddc..d7067c8f0 100644 --- a/docs/content/reference/install-configuration/tls/spiffe.md +++ b/docs/content/reference/install-configuration/tls/spiffe.md @@ -44,7 +44,7 @@ spiffe: ## ServersTransport Enabling SPIFFE does not imply that backend connections are going to use it automatically. -Each [ServersTransport](../../routing-configuration/http/load-balancing/serverstransport.md) or [TCPServersTransport](../../routing-configuration/tcp/serverstransport.md), that is meant to be secured with SPIFFE, must explicitly enable it (see [SPIFFE with ServersTransport](../../routing-configuration/http/load-balancing/serverstransport.md#opt-spiffe) or [SPIFFE with TCPServersTransport](../../routing-configuration/tcp/serverstransport.md#opt-serverstransport-spiffe)). +Each [ServersTransport](../../../routing/services/index.md#serverstransport_1) or [TCPServersTransport](../../../routing/services/index.md#serverstransport_2), that is meant to be secured with SPIFFE, must explicitly enable it (see [SPIFFE with ServersTransport](../../../routing/services/index.md#spiffe) or [SPIFFE with TCPServersTransport](../../../routing/services/index.md#spiffe_1)). ### Configuration Example diff --git a/docs/content/reference/routing-configuration/dynamic-configuration-methods.md b/docs/content/reference/routing-configuration/dynamic-configuration-methods.md index 20b30222e..cb158d0b2 100644 --- a/docs/content/reference/routing-configuration/dynamic-configuration-methods.md +++ b/docs/content/reference/routing-configuration/dynamic-configuration-methods.md @@ -72,6 +72,8 @@ When using Docker or Amazon ECS, you can define routing configuration using cont When deploying a Docker container, you can specify labels to define routing rules and services: ```yaml + version: '3' + services: my-service: image: my-image diff --git a/docs/content/reference/routing-configuration/http/load-balancing/serverstransport.md b/docs/content/reference/routing-configuration/http/load-balancing/serverstransport.md index 5774e60df..89d7b31c4 100644 --- a/docs/content/reference/routing-configuration/http/load-balancing/serverstransport.md +++ b/docs/content/reference/routing-configuration/http/load-balancing/serverstransport.md @@ -94,20 +94,19 @@ labels: ## Configuration Options -| Field | Description | Default | Required | -|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------|:--------|:---------| -| `serverName` | Configures the server name that will be used as the SNI. | "" | No | -| `certificates` | Defines the list of certificates (as file paths, or data bytes) that will be set as client certificates for mTLS. | [] | No | -| `insecureSkipVerify` | Controls whether the server's certificate chain and host name is verified. | false | No | -| `rootcas` | Set of root certificate authorities to use when verifying server certificates. (for mTLS connections). | [] | No | -| `maxIdleConnsPerHost` | Maximum idle (keep-alive) connections to keep per-host. | 200 | No | -| `disableHTTP2` | Disables HTTP/2 for connections with servers. | false | No | -| `peerCertURI` | Defines the URI used to match against SAN URIs during the server's certificate verification. | "" | No | -| `forwardingTimeouts.dialTimeout` | Amount of time to wait until a connection to a server can be established.
0 = no timeout | 30s | No | -| `forwardingTimeouts.responseHeaderTimeout` | Amount of time to wait for a server's response headers after fully writing the request (including its body, if any).
0 = no timeout | 0s | No | -| `forwardingTimeouts.idleConnTimeout` | Maximum amount of time an idle (keep-alive) connection will remain idle before closing itself.
0 = no timeout | 90s | No | -| `forwardingTimeouts.readIdleTimeout` | Defines the timeout after which a health check using ping frame will be carried out if no frame is received on the HTTP/2 connection. | 0s | No | -| `forwardingTimeouts.pingTimeout` | Defines the timeout after which the HTTP/2 connection will be closed if a response to ping is not received. | 15s | No | -| `spiffe` | Defines the SPIFFE configuration. An empty `spiffe` section enables SPIFFE (that allows any SPIFFE ID). | | No | -| `spiffe.ids` | Defines the allowed SPIFFE IDs.
This takes precedence over the SPIFFE TrustDomain. | [] | No | -| `spiffe.trustDomain` | Defines the SPIFFE trust domain. | "" | No | +| Field | Description | Default | Required | +|:------|:----------------------------------------------------------|:---------------------|:---------| +| `serverName` | Configures the server name that will be used as the SNI. | "" | No | +| `certificates` | Defines the list of certificates (as file paths, or data bytes) that will be set as client certificates for mTLS. | [] | No | +| `insecureSkipVerify` | Controls whether the server's certificate chain and host name is verified. | false | No | +| `rootcas` | Set of root certificate authorities to use when verifying server certificates. (for mTLS connections). | [] | No | +| `maxIdleConnsPerHost` | Maximum idle (keep-alive) connections to keep per-host. | 200 | No | +| `disableHTTP2` | Disables HTTP/2 for connections with servers. | false | No | +| `peerCertURI` | Defines the URI used to match against SAN URIs during the server's certificate verification. | "" | No | +| `forwardingTimeouts.dialTimeout` | Amount of time to wait until a connection to a server can be established.
0 = no timeout | 30s | No | +| `forwardingTimeouts.responseHeaderTimeout` | Amount of time to wait for a server's response headers after fully writing the request (including its body, if any).
0 = no timeout | 0s | No | +| `forwardingTimeouts.idleConnTimeout` | Maximum amount of time an idle (keep-alive) connection will remain idle before closing itself.
0 = no timeout | 90s | No | +| `forwardingTimeouts.readIdleTimeout` | Defines the timeout after which a health check using ping frame will be carried out if no frame is received on the HTTP/2 connection. | 0s | No | +| `forwardingTimeouts.pingTimeout` | Defines the timeout after which the HTTP/2 connection will be closed if a response to ping is not received. | 15s | No | +| `spiffe.ids` | Defines the allowed SPIFFE IDs.
This takes precedence over the SPIFFE TrustDomain. | [] | No | +| `spiffe.trustDomain` | Defines the SPIFFE trust domain. | "" | No | diff --git a/docs/content/reference/routing-configuration/http/load-balancing/service.md b/docs/content/reference/routing-configuration/http/load-balancing/service.md index 38f26d077..eaa665417 100644 --- a/docs/content/reference/routing-configuration/http/load-balancing/service.md +++ b/docs/content/reference/routing-configuration/http/load-balancing/service.md @@ -1,10 +1,7 @@ --- title: "Traefik HTTP Services Documentation" description: "A service is in charge of connecting incoming requests to the Servers that can handle them. Read the technical documentation." ---- - -Traefik services define how to distribute incoming traffic across your backend servers. -Each service implements one of the load balancing strategies detailed on this page to ensure optimal traffic distribution and high availability. +--- ## Service Load Balancer @@ -12,7 +9,7 @@ The load balancers are able to load balance the requests between multiple instan Each service has a load-balancer, even if there is only one server to forward traffic to. -### Configuration Example +## Configuration Example ```yaml tab="Structured (YAML)" http: @@ -73,6 +70,7 @@ labels: ```json tab="Tags" { + // ... "Tags": [ "traefik.http.services.my-service.loadBalancer.servers[0].url=http://private-ip-server-1/", "traefik.http.services.my-service.loadBalancer.servers[0].weight=2", @@ -90,15 +88,15 @@ labels: ### Configuration Options -| Field | Description | Required | -|------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------| -| `servers` | Represents individual backend instances for your service | Yes | -| `sticky` | Defines a `Set-Cookie` header is set on the initial response to let the client know which server handles the first response. | No | -| `healthcheck` | Configures health check to remove unhealthy servers from the load balancing rotation. | No | -| `passHostHeader` | Allows forwarding of the client Host header to server. By default, `passHostHeader` is true. | No | -| `serversTransport` | Allows to reference an [HTTP ServersTransport](./serverstransport.md) configuration for the communication between Traefik and your servers. If no `serversTransport` is specified, the `default@internal` will be used. | No | -| `responseForwarding` | Configures how Traefik forwards the response from the backend server to the client. | No | -| `responseForwarding.FlushInterval` | Specifies the interval in between flushes to the client while copying the response body. It is a duration in milliseconds, defaulting to 100ms. A negative value means to flush immediately after each write to the client. The `FlushInterval` is ignored when ReverseProxy recognizes a response as a streaming response; for such responses, writes are flushed to the client immediately. | No | +| Field | Description | Required | +|----------|------------------------------------------|----------| +|`servers`| Represents individual backend instances for your service | Yes | +|`sticky`| Defines a `Set-Cookie` header is set on the initial response to let the client know which server handles the first response. | No | +|`healthcheck`| Configures health check to remove unhealthy servers from the load balancing rotation. | No | +|`passHostHeader`| Allows forwarding of the client Host header to server. By default, `passHostHeader` is true. | No | +|`serversTransport`| Allows to reference an [HTTP ServersTransport](./serverstransport.md) configuration for the communication between Traefik and your servers. If no `serversTransport` is specified, the `default@internal` will be used. | No | +| `responseForwarding` | Configures how Traefik forwards the response from the backend server to the client.| No | +| `responseForwarding.FlushInterval` | Specifies the interval in between flushes to the client while copying the response body. It is a duration in milliseconds, defaulting to 100ms. A negative value means to flush immediately after each write to the client. The `FlushInterval` is ignored when ReverseProxy recognizes a response as a streaming response; for such responses, writes are flushed to the client immediately. | No | #### Servers @@ -106,11 +104,11 @@ Servers represent individual backend instances for your service. The [service lo ##### Configuration Options -| Field | Description | Required | -|----------------|----------------------------------------------------|----------------------------------------------------------------------------------| -| `url` | Points to a specific instance. | Yes for File provider, No for [Docker provider](../../other-providers/docker.md) | -| `weight` | Allows for weighted load balancing on the servers. | No | -| `preservePath` | Allows to preserve the URL path. | No | +| Field | Description | Required | +|----------|------------------------------------------|----------| +|`url`| Points to a specific instance. | Yes for File provider, No for [Docker provider](../../other-providers/docker.md) | +|`weight`| Allows for weighted load balancing on the servers. | No | +|`preservePath`| Allows to preserve the URL path. | No | #### Health Check @@ -120,179 +118,20 @@ To propagate status changes (e.g. all servers of this service are down) upwards, Below are the available options for the health check mechanism: -| Field | Description | Default | Required | -|---------------------|-------------------------------------------------------------------------------------------------------------------------------|---------|----------| -| `path` | Defines the server URL path for the health check endpoint. | "" | Yes | -| `scheme` | Replaces the server URL scheme for the health check endpoint. | | No | -| `mode` | If defined to `grpc`, will use the gRPC health check protocol to probe the server. | http | No | -| `hostname` | Defines the value of hostname in the Host header of the health check request. | "" | No | -| `port` | Replaces the server URL port for the health check endpoint. | | No | -| `interval` | Defines the frequency of the health check calls for healthy targets. | 30s | No | -| `unhealthyInterval` | Defines the frequency of the health check calls for unhealthy targets. When not defined, it defaults to the `interval` value. | 30s | No | -| `timeout` | Defines the maximum duration Traefik will wait for a health check request before considering the server unhealthy. | 5s | No | -| `headers` | Defines custom headers to be sent to the health check endpoint. | | No | -| `followRedirects` | Defines whether redirects should be followed during the health check calls. | true | No | -| `hostname` | Defines the value of hostname in the Host header of the health check request. | "" | No | -| `method` | Defines the HTTP method that will be used while connecting to the endpoint. | GET | No | -| `status` | Defines the expected HTTP status code of the response to the health check request. | | No | - -#### Sticky sessions - -When sticky sessions are enabled, a `Set-Cookie` header is set on the initial response to let the client know which server handles the first response. -On subsequent requests, to keep the session alive with the same server, the client should send the cookie with the value set. - -##### Stickiness on multiple levels - - When chaining or mixing load-balancers (e.g. a load-balancer of servers is one of the "children" of a load-balancer of services), for stickiness to work all the way, the option needs to be specified at all required levels. Which means the client needs to send a cookie with as many key/value pairs as there are sticky levels. - -##### Stickiness & Unhealthy Servers - - If the server specified in the cookie becomes unhealthy, the request will be forwarded to a new server (and the cookie will keep track of the new server). - -##### Cookie Name - - The default cookie name is an abbreviation of a sha1 (ex: `_1d52e`). - -##### MaxAge - - By default, the affinity cookie will never expire as the `MaxAge` option is set to zero. - - This option indicates the number of seconds until the cookie expires. - When set to a negative number, the cookie expires immediately. - -##### Secure & HTTPOnly & SameSite flags - - By default, the affinity cookie is created without those flags. - One however can change that through configuration. - - `SameSite` can be `none`, `lax`, `strict` or empty. - -##### Domain - - The Domain attribute of a cookie specifies the domain for which the cookie is valid. - - By setting the Domain attribute, the cookie can be shared across subdomains (for example, a cookie set for example.com would be accessible to www.example.com, api.example.com, etc.). This is particularly useful in cases where sticky sessions span multiple subdomains, ensuring that the session is maintained even when the client interacts with different parts of the infrastructure. - -??? example "Adding Stickiness -- Using the [File Provider](../../../install-configuration/providers/others/file.md)" - - ```yaml tab="YAML" - ## Dynamic configuration - http: - services: - my-service: - loadBalancer: - sticky: - cookie: {} - ``` - - ```toml tab="TOML" - ## Dynamic configuration - [http.services] - [http.services.my-service] - [http.services.my-service.loadBalancer.sticky.cookie] - ``` - -??? example "Adding Stickiness with custom Options -- Using the [File Provider](../../../install-configuration/providers/others/file.md)" - - ```yaml tab="YAML" - ## Dynamic configuration - http: - services: - my-service: - loadBalancer: - sticky: - cookie: - name: my_sticky_cookie_name - secure: true - domain: mysite.site - httpOnly: true - ``` - - ```toml tab="TOML" - ## Dynamic configuration - [http.services] - [http.services.my-service] - [http.services.my-service.loadBalancer.sticky.cookie] - name = "my_sticky_cookie_name" - secure = true - httpOnly = true - domain = "mysite.site" - sameSite = "none" - ``` - -??? example "Setting Stickiness on all the required levels -- Using the [File Provider](../../../install-configuration/providers/others/file.md)" - - ```yaml tab="YAML" - ## Dynamic configuration - http: - services: - wrr1: - weighted: - sticky: - cookie: - name: lvl1 - services: - - name: whoami1 - weight: 1 - - name: whoami2 - weight: 1 - - whoami1: - loadBalancer: - sticky: - cookie: - name: lvl2 - servers: - - url: http://127.0.0.1:8081 - - url: http://127.0.0.1:8082 - - whoami2: - loadBalancer: - sticky: - cookie: - name: lvl2 - servers: - - url: http://127.0.0.1:8083 - - url: http://127.0.0.1:8084 - ``` - - ```toml tab="TOML" - ## Dynamic configuration - [http.services] - [http.services.wrr1] - [http.services.wrr1.weighted.sticky.cookie] - name = "lvl1" - [[http.services.wrr1.weighted.services]] - name = "whoami1" - weight = 1 - [[http.services.wrr1.weighted.services]] - name = "whoami2" - weight = 1 - - [http.services.whoami1] - [http.services.whoami1.loadBalancer] - [http.services.whoami1.loadBalancer.sticky.cookie] - name = "lvl2" - [[http.services.whoami1.loadBalancer.servers]] - url = "http://127.0.0.1:8081" - [[http.services.whoami1.loadBalancer.servers]] - url = "http://127.0.0.1:8082" - - [http.services.whoami2] - [http.services.whoami2.loadBalancer] - [http.services.whoami2.loadBalancer.sticky.cookie] - name = "lvl2" - [[http.services.whoami2.loadBalancer.servers]] - url = "http://127.0.0.1:8083" - [[http.services.whoami2.loadBalancer.servers]] - url = "http://127.0.0.1:8084" - ``` - - To keep a session open with the same server, the client would then need to specify the two levels within the cookie for each request, e.g. with curl: - - ``` - curl -b "lvl1=whoami1; lvl2=http://127.0.0.1:8081" http://localhost:8000 - ``` +| Field | Description | Default | Required | +|----------|------------------------------------------|----------|--------| +|`path`| Defines the server URL path for the health check endpoint. | "" | Yes | +|`scheme`| Replaces the server URL scheme for the health check endpoint. | | No | +|`mode`| If defined to `grpc`, will use the gRPC health check protocol to probe the server. | http | No | +|`hostname`| Defines the value of hostname in the Host header of the health check request. | "" | No | +|`port`| Replaces the server URL port for the health check endpoint. | | No | +|`interval`| Defines the frequency of the health check calls. | 30s | No | +|`timeout`| Defines the maximum duration Traefik will wait for a health check request before considering the server unhealthy. | 5s | No | +|`headers`| Defines custom headers to be sent to the health check endpoint. | | No | +|`followRedirects`| Defines whether redirects should be followed during the health check calls. | true | No | +|`hostname`| Defines the value of hostname in the Host header of the health check request. | "" | No | +|`method`| Defines the HTTP method that will be used while connecting to the endpoint. | GET | No | +|`status`| Defines the expected HTTP status code of the response to the health check request. | | No | ## Weighted Round Robin (WRR) @@ -302,7 +141,7 @@ This strategy is only available to load balance between services and not between !!! info "Supported Providers" - This strategy can be defined currently with the [File](../../../install-configuration/providers/others/file.md) or [IngressRoute](../../../install-configuration/providers/kubernetes/kubernetes-crd.md) providers. To load balance between servers based on weights, the Load Balancer service should be used instead. + This strategy can be defined currently with the [File](../../../install-configuration/providers/others/file.md) or [IngressRoute](../../../install-configuration/providers/kubernetes/kubernetes-ingress.md) providers. To load balance between servers based on weights, the Load Balancer service should be used instead. ```yaml tab="Structured (YAML)" ## Dynamic configuration @@ -421,49 +260,14 @@ http: [[http.services.appv2.loadBalancer.servers]] url = "http://private-ip-server-2/" ``` -## P2C - -Power of two choices algorithm is a load balancing strategy that selects two servers at random and chooses the one with the least number of active requests. - -??? example "P2C Load Balancing -- Using the [File Provider](../../../install-configuration/providers/others/file.md)" - - ```yaml tab="YAML" - ## Dynamic configuration - http: - services: - my-service: - loadBalancer: - strategy: "p2c" - servers: - - url: "http://private-ip-server-1/" - - url: "http://private-ip-server-2/" - - url: "http://private-ip-server-3/" - ``` - - ```toml tab="TOML" - ## Dynamic configuration - [http.services] - [http.services.my-service.loadBalancer] - strategy = "p2c" - [[http.services.my-service.loadBalancer.servers]] - url = "http://private-ip-server-1/" - [[http.services.my-service.loadBalancer.servers]] - url = "http://private-ip-server-2/" - [[http.services.my-service.loadBalancer.servers]] - url = "http://private-ip-server-3/" - ``` ## Mirroring The mirroring is able to mirror requests sent to a service to other services. Please note that by default the whole request is buffered in memory while it is being mirrored. See the `maxBodySize` option in the example below for how to modify this behaviour. You can also omit the request body by setting the `mirrorBody` option to false. -!!! warning "Default behavior of `percent`" - - When configuring a `mirror` service, if the `percent` field is not set, it defaults to `0`, meaning **no traffic will be sent to the mirror**. - !!! info "Supported Providers" - This strategy can be defined currently with the [File](../../../install-configuration/providers/others/file.md) or [IngressRoute](../../../install-configuration/providers/kubernetes/kubernetes-crd.md) providers. + This strategy can be defined currently with the [File](../../../install-configuration/providers/others/file.md) or [IngressRoute](../../../install-configuration/providers/kubernetes/kubernetes-ingress.md) providers. ```yaml tab="Structured (YAML)" ## Dynamic configuration @@ -481,8 +285,6 @@ http: maxBodySize: 1024 mirrors: - name: appv2 - # Percent defines the percentage of requests that should be mirrored. - # Default value is 0, which means no traffic will be sent to the mirror. percent: 10 appv1: diff --git a/docs/content/reference/routing-configuration/http/middlewares/addprefix.md b/docs/content/reference/routing-configuration/http/middlewares/addprefix.md index 0bb182d8b..84c34e8ca 100644 --- a/docs/content/reference/routing-configuration/http/middlewares/addprefix.md +++ b/docs/content/reference/routing-configuration/http/middlewares/addprefix.md @@ -3,6 +3,8 @@ title: "Traefik AddPrefix Documentation" description: "Learn how to implement the HTTP AddPrefix middleware in Traefik Proxy to updates request paths before being forwarded. Read the technical documentation." --- +![AddPrefix](../../../../assets/img/middleware/addprefix.png) + The `addPrefix` middleware updates the path of a request before forwarding it. ## Configuration Examples @@ -54,4 +56,4 @@ spec: | Field | Description | Default | Required | |:-----------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:--------|:---------| -| `prefix` | String to add **before** the current path in the requested URL. It should include a leading slash (`/`). | "" | Yes | +| `prefix` | String to add **before** the current path in the requested URL. It should include a leading slash (`/`). | "" | Yes | diff --git a/docs/content/reference/routing-configuration/http/middlewares/apikey.md b/docs/content/reference/routing-configuration/http/middlewares/apikey.md deleted file mode 100644 index 454c141a1..000000000 --- a/docs/content/reference/routing-configuration/http/middlewares/apikey.md +++ /dev/null @@ -1,56 +0,0 @@ ---- -title: 'API Key Authentication' -description: 'Traefik Hub API Gateway - The API Key authentication middleware allows you to secure an API by requiring a secret key, base64 encoded or not, to be given, via an HTTP header, a cookie or a query parameter.' ---- - -!!! info "Traefik Hub Feature" - This middleware is available exclusively in [Traefik Hub](https://traefik.io/traefik-hub/). Learn more about [Traefik Hub's advanced features](https://doc.traefik.io/traefik-hub/api-gateway/intro). - -The API Key authentication middleware allows you to secure an API by requiring a secret key, base64 encoded or not, to be given, via an HTTP header, a cookie or a query parameter. - ---- - -## Configuration Example - -```yaml tab="Middleware API Key" -apiVersion: traefik.io/v1alpha1 -kind: Middleware -metadata: - name: test-apikey - namespace: apps -spec: - plugin: - apiKey: - keySource: - headerAuthScheme: Bearer - header: Authorization - secretNonBase64Encoded: true - secretValues: - - "urn:k8s:secret:apikey:secret" - - "urn:k8s:secret:apikey:othersecret" -``` - -```yaml tab="Values Secret" -apiVersion: v1 -kind: Secret -type: Opaque -metadata: - name: apikey - namespace: whoami -stringData: - secret: $2y$05$D4SPFxzfWKcx1OXfVhRbvOTH/QB0Lm6AXTk8.NOmU4rPLX2t6UUuW # htpasswd -nbB "" foo | cut -c 2- - othersecret: $2y$05$HbLL.g5dUqJippH0RuAGL.RaM9wNS2cT7hp6.vbv5okdCmVBSDzzK # htpasswd -nbB "" bar | cut -c 2- -``` - -## Configuration Options - -| Field | Description | Default | Required | -|:-----------------------------|:------------------------------------------------|:--------|:---------| -| `keySource.header` | Defines the header name containing the secret sent by the client.
Either `keySource.header` or `keySource.query` or `keySource.cookie` must be set. | "" | No | -| `keySource.headerAuthScheme` | Defines the scheme when using `Authorization` as header name.
Check out the `Authorization` header [documentation](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Authorization#syntax). | "" | No | -| `keySource.query` | Defines the query parameter name containing the secret sent by the client.
Either `keySource.header` or `keySource.query` or `keySource.cookie` must be set. | "" | No | -| `keySource.cookie` | Defines the cookie name containing the secret sent by the client.
Either `keySource.header` or `keySource.query` or `keySource.cookie` must be set. | "" | No | -| `secretNonBase64Encoded` | Defines whether the secret sent by the client is base64 encoded. | false | No | -| `secretValues` | Contain the hash of the API keys.
Supported hashing algorithms are Bcrypt, SHA1 and MD5.
The hash should be generated using `htpasswd`.
Can reference a Kubernetes Secret using the URN format: `urn:k8s:secret:[name]:[valueKey]` | [] | Yes | - -{!traefik-for-business-applications.md!} diff --git a/docs/content/reference/routing-configuration/http/middlewares/basicauth.md b/docs/content/reference/routing-configuration/http/middlewares/basicauth.md index be372250b..29587f6a3 100644 --- a/docs/content/reference/routing-configuration/http/middlewares/basicauth.md +++ b/docs/content/reference/routing-configuration/http/middlewares/basicauth.md @@ -3,6 +3,8 @@ title: "Traefik BasicAuth Documentation" description: "The HTTP basic authentication (BasicAuth) middleware in Traefik Proxy restricts access to your Services to known users. Read the technical documentation." --- +![BasicAuth](../../../../assets/img/middleware/basicauth.png) + The `basicAuth` middleware grants access to services to authorized users only. ## Configuration Examples @@ -64,11 +66,11 @@ spec: | Field | Description | Default | Required | |:-----------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:--------|:---------| -| `users` | Array of authorized users. Each user must be declared using the `name:hashed-password` format. (More information [here](#users-usersfile))| "" | No | -| `usersFile` | Path to an external file that contains the authorized users for the middleware.
The file content is a list of `name:hashed-password`. (More information [here](#users-usersfile)) | "" | No | -| `realm` | Allow customizing the realm for the authentication.| "traefik" | No | -| `headerField` | Allow defining a header field to store the authenticated user.| "" | No | -| `removeHeader` | Allow removing the authorization header before forwarding the request to your service. | false | No | +| `users` | Array of authorized users. Each user must be declared using the `name:hashed-password` format. (More information [here](#users))| "" | No | +| `usersFile` | Path to an external file that contains the authorized users for the middleware.
The file content is a list of `name:hashed-password`. (More information [here](#usersfile)) | "" | No | +| `realm` | Allow customizing the realm for the authentication.| "traefik" | No | +| `headerField` | Allow defining a header field to store the authenticated user.| "" | No | +| `removeHeader` | Allow removing the authorization header before forwarding the request to your service. | false | No | ### Passwords format diff --git a/docs/content/reference/routing-configuration/http/middlewares/buffering.md b/docs/content/reference/routing-configuration/http/middlewares/buffering.md index af334a221..4fe818ded 100644 --- a/docs/content/reference/routing-configuration/http/middlewares/buffering.md +++ b/docs/content/reference/routing-configuration/http/middlewares/buffering.md @@ -3,6 +3,8 @@ title: "Traefik Buffering Documentation" description: "The HTTP buffering middleware in Traefik Proxy limits the size of requests that can be forwarded to Services. Read the technical documentation." --- +![Buffering](../../../../assets/img/middleware/buffering.png) + The `buffering` middleware limits the size of requests that can be forwarded to services. With buffering, Traefik reads the entire request into memory (possibly buffering large requests into disk), and rejects requests that are over a specified size limit. @@ -58,11 +60,11 @@ spec: | Field | Description | Default | Required | |:------|:------------|:--------|:---------| -| `maxRequestBodyBytes` | Maximum allowed body size for the request (in bytes).
If the request exceeds the allowed size, it is not forwarded to the Service, and the client gets a `413` (Request Entity Too Large) response. | 0 | No | -| `memRequestBodyBytes` | Threshold (in bytes) from which the request will be buffered on disk instead of in memory with the `memRequestBodyBytes` option.| 1048576 | No | -| `maxResponseBodyBytes` | Maximum allowed response size from the Service (in bytes).
If the response exceeds the allowed size, it is not forwarded to the client. The client gets a `500` (Internal Server Error) response instead. | 0 | No | -| `memResponseBodyBytes` | Threshold (in bytes) from which the response will be buffered on disk instead of in memory with the `memResponseBodyBytes` option.| 1048576 | No | -| `retryExpression` | Replay the request using `retryExpression`.
More information [here](#retryexpression). | "" | No | +| `maxRequestBodyBytes` | Maximum allowed body size for the request (in bytes).
If the request exceeds the allowed size, it is not forwarded to the Service, and the client gets a `413` (Request Entity Too Large) response. | 0 | No | +| `memRequestBodyBytes` | Threshold (in bytes) from which the request will be buffered on disk instead of in memory with the `memRequestBodyBytes` option.| 1048576 | No | +| `maxResponseBodyBytes` | Maximum allowed response size from the Service (in bytes).
If the response exceeds the allowed size, it is not forwarded to the client. The client gets a `500` (Internal Server Error) response instead. | 0 | No | +| `memResponseBodyBytes` | Threshold (in bytes) from which the response will be buffered on disk instead of in memory with the `memResponseBodyBytes` option.| 1048576 | No | +| `retryExpression` | Replay the request using `retryExpression`.
More information [here](#retryexpression). | "" | No | ### retryExpression diff --git a/docs/content/reference/routing-configuration/http/middlewares/chain.md b/docs/content/reference/routing-configuration/http/middlewares/chain.md index 2cb81665d..3eb1e6ad4 100644 --- a/docs/content/reference/routing-configuration/http/middlewares/chain.md +++ b/docs/content/reference/routing-configuration/http/middlewares/chain.md @@ -168,4 +168,4 @@ spec: | Field | Description | Default | Required | |:------|:------------|:--------|:---------| -| `middlewares` | List of middlewares to chain.
The middlewares have to be in the same namespace as the `chain` middleware. | [] | Yes | +| `middlewares` | List of middlewares to chain.
The middlewares have to be in the same namespace as the `chain` middleware. | [] | Yes | diff --git a/docs/content/reference/routing-configuration/http/middlewares/circuitbreaker.md b/docs/content/reference/routing-configuration/http/middlewares/circuitbreaker.md index 999041adc..dfd2be843 100644 --- a/docs/content/reference/routing-configuration/http/middlewares/circuitbreaker.md +++ b/docs/content/reference/routing-configuration/http/middlewares/circuitbreaker.md @@ -3,6 +3,8 @@ title: "Traefik CircuitBreaker Documentation" description: "The HTTP circuit breaker in Traefik Proxy prevents stacking requests to unhealthy Services, resulting in cascading failures. Read the technical documentation." --- +![Circuit Breaker](../../../../assets/img/middleware/circuitbreaker.png) + The HTTP circuit breaker prevents stacking requests to unhealthy Services, resulting in cascading failures. When your system is healthy, the circuit is closed (normal operations). @@ -65,11 +67,11 @@ spec: | Field | Description | Default | Required | |:------|:------------|:--------|:---------| -| `expression` | Condition to open the circuit breaker and applies the fallback mechanism instead of calling your services.
More information [here](#expression) | 100ms | No | -| `checkPeriod` | The interval between successive checks of the circuit breaker condition (when in standby state). | 100ms | No | -| `fallbackDuration` | The duration for which the circuit breaker will wait before trying to recover (from a tripped state). | 10s | No | -| `recoveryDuration` | The duration for which the circuit breaker will try to recover (as soon as it is in recovering state). | 10s | No | -| `responseCode` | The status code that the circuit breaker will return while it is in the open state. | 503 | No | +| `expression` | Condition to open the circuit breaker and applies the fallback mechanism instead of calling your services.
More information [here](#expression) | 100ms | No | +| `checkPeriod` | The interval between successive checks of the circuit breaker condition (when in standby state). | 100ms | No | +| `fallbackDuration` | The duration for which the circuit breaker will wait before trying to recover (from a tripped state). | 10s | No | +| `recoveryDuration` | The duration for which the circuit breaker will try to recover (as soon as it is in recovering state). | 10s | No | +| `responseCode` | The status code that the circuit breaker will return while it is in the open state. | 503 | No | ### expression @@ -77,9 +79,9 @@ The `expression` option can check three different metrics: | Metrics | Description | Example | |:------|:------------|:--------| -| `NetworkErrorRatio` | The network error ratio to open the circuit breaker. | `NetworkErrorRatio() > 0.30` opens the circuit breaker at a 30% ratio of network errors | -| `ResponseCodeRatio` | The status code ratio to open the circuit breaker.
More information [below](#responsecoderatio) | `ResponseCodeRatio(500, 600, 0, 600) > 0.25` opens the circuit breaker if 25% of the requests returned a 5XX status (amongst the request that returned a status code from 0 to 5XX) | -| `LatencyAtQuantileMS` | The latency at a quantile in milliseconds to open the circuit breaker when a given proportion of your requests become too slow.
Only floating point number (with the trailing .0) for the quantile value. | `LatencyAtQuantileMS(50.0) > 100` opens the circuit breaker when the median latency (quantile 50) reaches 100ms. | +| `NetworkErrorRatio` | The network error ratio to open the circuit breaker. | `NetworkErrorRatio() > 0.30` opens the circuit breaker at a 30% ratio of network errors | +| `ResponseCodeRatio` | The status code ratio to open the circuit breaker.
More information [below](#responsecoderatio) | `ResponseCodeRatio(500, 600, 0, 600) > 0.25` opens the circuit breaker if 25% of the requests returned a 5XX status (amongst the request that returned a status code from 0 to 5XX) | +| `LatencyAtQuantileMS` | The latency at a quantile in milliseconds to open the circuit breaker when a given proportion of your requests become too slow.
Only floating point number (with the trailing .0) for the quantile value. | `LatencyAtQuantileMS(50.0) > 100` opens the circuit breaker when the median latency (quantile 50) reaches 100ms. | #### ResponseCodeRatio @@ -111,8 +113,8 @@ Here is the list of supported operators: ### Fallback mechanism -By default the fallback mechanism returns a `HTTP 503 Service Unavailable` to the client instead of calling the target service. -The response code can be configured. +The fallback mechanism returns a `HTTP 503 Service Unavailable` to the client instead of calling the target service. +This behavior cannot be configured. ## State diff --git a/docs/content/reference/routing-configuration/http/middlewares/compress.md b/docs/content/reference/routing-configuration/http/middlewares/compress.md index f67c62c9a..e1b8ad8d4 100644 --- a/docs/content/reference/routing-configuration/http/middlewares/compress.md +++ b/docs/content/reference/routing-configuration/http/middlewares/compress.md @@ -51,11 +51,11 @@ spec: | Field | Description | Default | Required | |:-----------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:--------|:---------| -| `excludedContentTypes` | List of content types to compare the `Content-Type` header of the incoming requests and responses before compressing.
The responses with content types defined in `excludedContentTypes` are not compressed.
Content types are compared in a case-insensitive, whitespace-ignored manner.
**The `excludedContentTypes` and `includedContentTypes` options are mutually exclusive.** | "" | No | -| `defaultEncoding` | specifies the default encoding if the `Accept-Encoding` header is not in the request or contains a wildcard (`*`). | "" | No | -| `encodings` | Specifies the list of supported compression encodings. At least one encoding value must be specified, and valid entries are `zstd` (Zstandard), `br` (Brotli), and `gzip` (Gzip). The order of the list also sets the priority, the top entry has the highest priority. | zstd, br, gzip | No | -| `includedContentTypes` | List of content types to compare the `Content-Type` header of the responses before compressing.
The responses with content types defined in `includedContentTypes` are compressed.
Content types are compared in a case-insensitive, whitespace-ignored manner.
**The `excludedContentTypes` and `includedContentTypes` options are mutually exclusive.** | "" | No | -| `minResponseBodyBytes` | `Minimum amount of bytes a response body must have to be compressed.
Responses smaller than the specified values will **not** be compressed. | 1024 | No | +|`excludedContentTypes` | List of content types to compare the `Content-Type` header of the incoming requests and responses before compressing.
The responses with content types defined in `excludedContentTypes` are not compressed.
Content types are compared in a case-insensitive, whitespace-ignored manner.
**The `excludedContentTypes` and `includedContentTypes` options are mutually exclusive.** | "" | No | +|`defaultEncoding` | specifies the default encoding if the `Accept-Encoding` header is not in the request or contains a wildcard (`*`). | "" | No | +|`encodings` | Specifies the list of supported compression encodings. At least one encoding value must be specified, and valid entries are `zstd` (Zstandard), `br` (Brotli), and `gzip` (Gzip). The order of the list also sets the priority, the top entry has the highest priority. | zstd, br, gzip | No | +| `includedContentTypes` | List of content types to compare the `Content-Type` header of the responses before compressing.
The responses with content types defined in `includedContentTypes` are compressed.
Content types are compared in a case-insensitive, whitespace-ignored manner.
**The `excludedContentTypes` and `includedContentTypes` options are mutually exclusive.** | "" | No | +| `minResponseBodyBytes` | `Minimum amount of bytes a response body must have to be compressed.
Responses smaller than the specified values will **not** be compressed. | 1024 | No | ## Compression activation diff --git a/docs/content/reference/routing-configuration/http/middlewares/digestauth.md b/docs/content/reference/routing-configuration/http/middlewares/digestauth.md index 974ef48ce..22dd5b4bf 100644 --- a/docs/content/reference/routing-configuration/http/middlewares/digestauth.md +++ b/docs/content/reference/routing-configuration/http/middlewares/digestauth.md @@ -3,6 +3,8 @@ title: "Traefik DigestAuth Documentation" description: "Traefik Proxy's HTTP DigestAuth middleware restricts access to your services to known users. Read the technical documentation." --- +![DigestAuth](../../../../assets/img/middleware/digestauth.png) + The `DigestAuth` middleware grants access to services to authorized users only. ## Configuration Examples @@ -59,11 +61,11 @@ spec: | Field | Description | Default | Required | |:-----------|:---------------------------------------------------------------------------------|:--------|:---------| -| `users` | Array of authorized users. Each user must be declared using the `name:realm:encoded-password` format.
The option `users` supports Kubernetes secrets.
(More information [here](#users--usersfile))| [] | No | -| `usersFile` | Path to an external file that contains the authorized users for the middleware.
The file content is a list of `name:realm:encoded-password`. (More information [here](#users--usersfile)) | "" | No | -| `realm` | Allow customizing the realm for the authentication.| "traefik" | No | -| `headerField` | Allow defining a header field to store the authenticated user.| "" | No | -| `removeHeader` | Allow removing the authorization header before forwarding the request to your service. | false | No | +| `users` | Array of authorized users. Each user must be declared using the `name:realm:encoded-password` format.
The option `users` supports Kubernetes secrets.
(More information [here](#users--usersfile))| [] | No | +| `usersFile` | Path to an external file that contains the authorized users for the middleware.
The file content is a list of `name:realm:encoded-password`. (More information [here](#users--usersfile)) | "" | No | +| `realm` | Allow customizing the realm for the authentication.| "traefik" | No | +| `headerField` | Allow defining a header field to store the authenticated user.| "" | No | +| `removeHeader` | Allow removing the authorization header before forwarding the request to your service. | false | No | ### Passwords format diff --git a/docs/content/reference/routing-configuration/http/middlewares/distributed-ratelimit.md b/docs/content/reference/routing-configuration/http/middlewares/distributed-ratelimit.md deleted file mode 100644 index a5f7eb94e..000000000 --- a/docs/content/reference/routing-configuration/http/middlewares/distributed-ratelimit.md +++ /dev/null @@ -1,181 +0,0 @@ ---- -title: "Distributed RateLimit" -description: "Traefik Hub API Gateway - The Distributed RateLimit middleware ensures Services receive fair amounts of requests throughout your cluster and not only on an individual proxy." ---- - -!!! info "Traefik Hub Feature" - This middleware is available exclusively in [Traefik Hub](https://traefik.io/traefik-hub/). Learn more about [Traefik Hub's advanced features](https://doc.traefik.io/traefik-hub/api-gateway/intro). - -The Distributed RateLimit middleware ensures that requests are limited over time throughout your cluster and not only on an individual proxy. - -It is based on a [token bucket](https://en.wikipedia.org/wiki/Token_bucket) implementation. - ---- - -## Configuration Example - -Below is an advanced configuration that enables the Distributed RateLimit middleware with Redis backend for cluster-wide rate limiting. - -```yaml tab="Middleware Distributed Rate Limit" -# Here, a limit of 100 requests per second is allowed. -# In addition, a burst of 200 requests is allowed. -apiVersion: traefik.io/v1alpha1 -kind: Middleware -metadata: - name: test-distributedratelimit - namespace: traefik -spec: - plugin: - distributedRateLimit: - burst: 200 - denyOnError: false - limit: 100 - period: 1s - responseHeaders: true - sourceCriterion: - ipStrategy: - excludedIPs: - - 172.20.176.201 - store: - redis: - endpoints: - - my-release-redis-master.default.svc.cluster.local:6379 - # Use the field password of the Secret redis in the same namespace - password: urn:k8s:secret:redis:password - timeout: 500ms -``` - -```yaml tab="Kubernetes Secret" -apiVersion: v1 -kind: Secret -metadata: - name: redis - namespace: traefik -stringData: - password: mysecret12345678 -``` - -## Rate and Burst - -The rate is defined by dividing `limit` by `period`. -For a rate below 1 req/s, define a `period` larger than a second - -The middleware is based on a [token bucket](https://en.wikipedia.org/wiki/Token_bucket) implementation. -In this analogy, the `limit` and `period` parameters define the **rate** at which the bucket refills, and the `burst` is the size (volume) of the bucket. - -```yaml -apiVersion: traefik.io/v1alpha1 -kind: Middleware -metadata: - name: test-ratelimit -spec: - plugin: - distributedRateLimit: - burst: 100 - period: 1m - limit: 6 -``` - -In the example above, the middleware allows up to 100 connections in parallel (`burst`). -Each connection consume a token, once the 100 tokens are consumed, the other ones are blocked until at least one token is available in the bucket. - -When the bucket is not full, on token is generated every 10 seconds (6 every 1 minutes (`period` / `limit`)). - -## Configuration Options - -| Field | Description | Default | Required | -|:-----------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:--------|:---------| -| `limit` | Number of requests used to define the rate using the `period`.
0 means **no rate limiting**.
More information [here](#rate-and-burst).| 0 | No | -| `period` | Period of time used to define the rate.
More information [here](#rate-and-burst).| 1s | No | -| `burst` | Maximum number of requests allowed to go through at the very same moment.
More information [here](#rate-and-burst). | 1 | No | -| `denyOnError` | Forces to return a 429 error if the number of remaining requests accepted cannot be get.
Set to `false`, this option allows the request to reach the backend. | true | No | -| `responseHeaders` | Injects the following rate limiting headers in the response:
- `X-Rate-Limit-Remaining`
- `X-Rate-Limit-Limit`
- `X-Rate-Limit-Period`
- `X-Rate-Limit-Reset`
The added headers indicate how many tokens are left in the bucket (in the token bucket analogy) after the reservation for the request was made. | false | No | -| `store.redis.endpoints` | Endpoints of the Redis instances to connect to (example: `redis.traefik-hub.svc.cluster.local:6379`) | "" | Yes | -| `store.redis.username` | The username Traefik Hub will use to connect to Redis | "" | No | -| `store.redis.password` | The password Traefik Hub will use to connect to Redis | "" | No | -| `store.redis.database` | The database Traefik Hub will use to sore information (default: `0`) | "" | No | -| `store.redis.cluster` | Enable Redis Cluster | "" | No | -| `store.redis.tls.caBundle` | Custom CA bundle | "" | No | -| `store.redis.tls.cert` | TLS certificate | "" | No | -| `store.redis.tls.key` | TLS key | "" | No | -| `store.redis.tls.insecureSkipVerify` | Allow skipping the TLS verification | "" | No | -| `store.redis.sentinel.masterSet` | Name of the set of main nodes to use for main selection. Required when using Sentinel. | "" | No | -| `store.redis.sentinel.username` | Username to use for sentinel authentication (can be different from `username`) | "" | No | -| `store.redis.sentinel.password` | Password to use for sentinel authentication (can be different from `password`) | "" | No | -| `sourceCriterion.requestHost` | Whether to consider the request host as the source.
More information about `sourceCriterion`[here](#sourcecriterion). | false | No | -| `sourceCriterion.requestHeaderName` | Name of the header used to group incoming requests.
More information about `sourceCriterion`[here](#sourcecriterion). | "" | No | -| `sourceCriterion.ipStrategy.depth` | Depth position of the IP to select in the `X-Forwarded-For` header (starting from the right).
0 means no depth.
If greater than the total number of IPs in `X-Forwarded-For`, then the client IP is empty
If higher than 0, the `excludedIPs` options is not evaluated.
More information about [`sourceCriterion`](#sourcecriterion), [`ipStrategy`](#ipstrategy), and [`depth`](#sourcecriterionipstrategydepth) below. | 0 | No | -| `sourceCriterion.ipStrategy.excludedIPs` | Allows Traefik to scan the `X-Forwarded-For` header and select the first IP not in the list.
If `depth` is specified, `excludedIPs` is ignored.
More information about [`sourceCriterion`](#sourcecriterion), [`ipStrategy`](#ipstrategy), and [`excludedIPs`](#sourcecriterionipstrategyexcludedips) below. | | No | - -### sourceCriterion - -The `sourceCriterion` option defines what criterion is used to group requests as originating from a common source. -If several strategies are defined at the same time, an error will be raised. -If none are set, the default is to use the request's remote address field (as an `ipStrategy`). - -### ipStrategy - -The `ipStrategy` option defines two parameters that configures how Traefik determines the client IP: `depth`, and `excludedIPs`. - -As a middleware, rate-limiting happens before the actual proxying to the backend takes place. -In addition, the previous network hop only gets appended to `X-Forwarded-For` during the last stages of proxying, that is after it has already passed through rate-limiting. -Therefore, during rate-limiting, as the previous network hop is not yet present in `X-Forwarded-For`, it cannot be found and/or relied upon. - -### sourceCriterion.ipStrategy.depth - -If `depth` is set to 2, and the request `X-Forwarded-For` header is `"10.0.0.1,11.0.0.1,12.0.0.1,13.0.0.1"` then the "real" client IP is `"10.0.0.1"` (at depth 4) but the IP used as the criterion is `"12.0.0.1"` (`depth=2`). - -| `X-Forwarded-For` | `depth` | clientIP | -|-----------------------------------------|---------|--------------| -| `"10.0.0.1,11.0.0.1,12.0.0.1,13.0.0.1"` | `1` | `"13.0.0.1"` | -| `"10.0.0.1,11.0.0.1,12.0.0.1,13.0.0.1"` | `3` | `"11.0.0.1"` | -| `"10.0.0.1,11.0.0.1,12.0.0.1,13.0.0.1"` | `5` | `""` | - -### sourceCriterion.ipStrategy.excludedIPs - -Contrary to what the name might suggest, this option is *not* about excluding an IP from the rate limiter, and therefore cannot be used to deactivate rate limiting for some IPs. - -`excludedIPs` is meant to address two classes of somewhat distinct use-cases: - -1. Distinguish IPs which are behind the same (set of) reverse-proxies so that each of them contributes, independently to the others, to its own rate-limit "bucket" (cf the [token bucket](https://en.wikipedia.org/wiki/Token_bucket)). -In this case, `excludedIPs` should be set to match the list of `X-Forwarded-For IPs` that are to be excluded, in order to find the actual clientIP. - -Example to use each IP as a distinct source: - -| `X-Forwarded-For` | excludedIPs | clientIP | -|--------------------------------|-----------------------|--------------| -| `"10.0.0.1,11.0.0.1,12.0.0.1"` | `"11.0.0.1,12.0.0.1"` | `"10.0.0.1"` | -| `"10.0.0.2,11.0.0.1,12.0.0.1"` | `"11.0.0.1,12.0.0.1"` | `"10.0.0.2"` | - -2. Group together a set of IPs (also behind a common set of reverse-proxies) so that they are considered the same source, and all contribute to the same rate-limit bucket. - -Example to group IPs together as same source: - -| `X-Forwarded-For` | excludedIPs | clientIP | -|--------------------------------|--------------|--------------| -| `"10.0.0.1,11.0.0.1,12.0.0.1"` | `"12.0.0.1"` | `"11.0.0.1"` | -| `"10.0.0.2,11.0.0.1,12.0.0.1"` | `"12.0.0.1"` | `"11.0.0.1"` | -| `"10.0.0.3,11.0.0.1,12.0.0.1"` | `"12.0.0.1"` | `"11.0.0.1"` | - -### store - -A Distributed Rate Limit middleware uses a persistent KV storage to store data. - -Refer to the [redis options](#configuration-options) to configure the Redis connection. - -Connection parameters to your [Redis](https://redis.io/ "Link to website of Redis") server are attached to your Middleware deployment. - -The following Redis modes are supported: - -- Single instance mode -- [Redis Cluster](https://redis.io/docs/management/scaling "Link to official Redis documentation about Redis Cluster mode") -- [Redis Sentinel](https://redis.io/docs/management/sentinel "Link to official Redis documentation about Redis Sentinel mode") - -For more information about Redis, we recommend the [official Redis documentation](https://redis.io/docs/ "Link to official Redis documentation"). - -!!! info - - If you use Redis in single instance mode or Redis Sentinel, you can configure the `database` field. - This value won't be taken into account if you use Redis Cluster (only database `0` is available). - - In this case, a warning is displayed, and the value is ignored. diff --git a/docs/content/reference/routing-configuration/http/middlewares/errorpages.md b/docs/content/reference/routing-configuration/http/middlewares/errorpages.md index 2855a5cbb..456badfbd 100644 --- a/docs/content/reference/routing-configuration/http/middlewares/errorpages.md +++ b/docs/content/reference/routing-configuration/http/middlewares/errorpages.md @@ -3,6 +3,8 @@ title: "Traefik Errors Documentation" description: "In Traefik Proxy, the Errors middleware returns custom pages according to configured ranges of HTTP Status codes. Read the technical documentation." --- +![Errors](../../../../assets/img/middleware/errorpages.png) + The `errors` middleware returns a custom page in lieu of the default, according to configured ranges of HTTP Status codes. ## Configuration Examples @@ -18,9 +20,6 @@ http: - "501" - "503" - "505-599" - statusRewrites: - "418": "404" - "502-504": "500" service: error-handler-service query: "/{status}.html" @@ -36,10 +35,6 @@ http: service = "error-handler-service" query = "/{status}.html" - [http.middlewares.test-errors.errors.statusRewrites] - "418" = "404" - "502-504" = "500" - [http.services] # ... definition of the error-handler-service ``` @@ -48,8 +43,6 @@ http: # Dynamic Custom Error Page for 5XX Status Code labels: - "traefik.http.middlewares.test-errors.errors.status=500,501,503,505-599" - - "traefik.http.middlewares.test-errors.errors.statusRewrites.418=404" - - "traefik.http.middlewares.test-errors.errors.statusRewrites.502-504=500" - "traefik.http.middlewares.test-errors.errors.service=error-handler-service" - "traefik.http.middlewares.test-errors.errors.query=/{status}.html" ``` @@ -60,8 +53,6 @@ labels: // ... "Tags": [ "traefik.http.middlewares.test-errors.errors.status=500,501,503,505-599", - "traefik.http.middlewares.test-errors.errors.statusRewrites.418=404", - "traefik.http.middlewares.test-errors.errors.statusRewrites.502-504=500", "traefik.http.middlewares.test-errors.errors.service=error-handler-service", "traefik.http.middlewares.test-errors.errors.query=/{status}.html" ] @@ -82,9 +73,6 @@ spec: - "501" - "503" - "505-599" - statusRewrites: - "418": "404" - "502-504": "500" query: /{status}.html service: name: error-handler-service @@ -95,10 +83,9 @@ spec: | Field | Description | Default | Required | |:-----------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:--------|:---------| -| `status` | Defines which status or range of statuses should result in an error page.
The status code ranges are inclusive (`505-599` will trigger with every code between `505` and `599`, `505` and `599` included).
You can define either a status code as a number (`500`), as multiple comma-separated numbers (`500,502`), as ranges by separating two codes with a dash (`505-599`), or a combination of the two (`404,418,505-599`). | [] | No | -| `statusRewrites` | An optional mapping of status codes to be rewritten. More information [here](#statusrewrites). | [] | No | -| `service` | The service that will serve the new requested error page.
More information [here](#service-and-hostheader). | "" | No | -| `query` | The URL for the error page (hosted by `service`).
More information [here](#query) | "" | No | +| `status` | Defines which status or range of statuses should result in an error page.
The status code ranges are inclusive (`505-599` will trigger with every code between `505` and `599`, `505` and `599` included).
You can define either a status code as a number (`500`), as multiple comma-separated numbers (`500,502`), as ranges by separating two codes with a dash (`505-599`), or a combination of the two (`404,418,505-599`). | [] | No | +| `service` | The service that will serve the new requested error page.
More information [here](#service-and-hostheader). | "" | No | +| `query` | The URL for the error page (hosted by `service`).
More information [here](#query) | "" | No | ### service and HostHeader @@ -109,15 +96,6 @@ the [`passHostHeader`](../../../../routing/services/index.md#pass-host-header) o !!!info "Kubernetes" When specifying a service in Kubernetes (e.g., in an IngressRoute), you need to reference the `name`, `namespace`, and `port` of your Kubernetes Service resource. For example, `my-service.my-namespace@kubernetescrd` (or `my-service.my-namespace@kubernetescrd:80`) ensures that requests go to the correct service and port. -### statusRewrites - -`statusRewrites` is an optional mapping of status codes to be rewritten. - -For example, if a service returns a 418, you might want to rewrite it to a 404. -You can map individual status codes or even ranges to a different status code. - -The syntax for ranges follows the same rules as the `status` option. - ### query There are multiple variables that can be placed in the `query` option to insert values in the URL. @@ -126,6 +104,5 @@ The table below lists all the available variables and their associated values. | Variable | Value | |------------|------------------------------------------------------------------| -| `{status}` | The response status code. | -| `{originalStatus}` | The original response status code, if it has been modified by the `statusRewrites` option. | -| `{url}` | The [escaped](https://pkg.go.dev/net/url#QueryEscape) request URL.| +| `{status}` | The response status code. | +| `{url}` | The [escaped](https://pkg.go.dev/net/url#QueryEscape) request URL.| diff --git a/docs/content/reference/routing-configuration/http/middlewares/forwardauth.md b/docs/content/reference/routing-configuration/http/middlewares/forwardauth.md index 5df864c26..62a61bc7e 100644 --- a/docs/content/reference/routing-configuration/http/middlewares/forwardauth.md +++ b/docs/content/reference/routing-configuration/http/middlewares/forwardauth.md @@ -3,6 +3,8 @@ title: "Traefik ForwardAuth Documentation" description: "In Traefik Proxy, the HTTP ForwardAuth middleware delegates authentication to an external Service. Read the technical documentation." --- +![AuthForward](../../../../assets/img/middleware/authforward.png) + The `forwardAuth` middleware delegates authentication to an external service. If the service answers with a 2XX code, access is granted, and the original request is performed. Otherwise, the response from the authentication server is returned. @@ -55,23 +57,23 @@ spec: | Field | Description | Default | Required | |:-----------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:--------|:---------| -| `address` | Authentication server address. | "" | Yes | -| `trustForwardHeader` | Trust all `X-Forwarded-*` headers. | false | No | -| `authResponseHeaders` | List of headers to copy from the authentication server response and set on forwarded request, replacing any existing conflicting headers. | [] | No | -| `authResponseHeadersRegex` | Regex to match by the headers to copy from the authentication server response and set on forwarded request, after stripping all headers that match the regex.
More information [here](#authresponseheadersregex). | "" | No | -| `authRequestHeaders` | List of the headers to copy from the request to the authentication server.
It allows filtering headers that should not be passed to the authentication server.
If not set or empty, then all request headers are passed. | [] | No | -| `addAuthCookiesToResponse` | List of cookies to copy from the authentication server to the response, replacing any existing conflicting cookie from the forwarded response.
Please note that all backend cookies matching the configured list will not be added to the response. | [] | No | -| `forwardBody` | Sets the `forwardBody` option to `true` to send the Body. As body is read inside Traefik before forwarding, this breaks streaming. | false | No | -| `maxBodySize` | Set the `maxBodySize` to limit the body size in bytes. If body is bigger than this, it returns a 401 (unauthorized). | -1 | No | -| `headerField` | Defines a header field to store the authenticated user. | "" | No | -| `preserveLocationHeader` | Defines whether to forward the Location header to the client as is or prefix it with the domain name of the authentication server. | false | No | -| `preserveRequestMethod` | Defines whether to preserve the original request method while forwarding the request to the authentication server. | false | No | -| `tls.ca` | Sets the path to the certificate authority used for the secured connection to the authentication server, it defaults to the system bundle. | "" | No | -| `tls.cert` | Sets the path to the public certificate used for the secure connection to the authentication server. When using this option, setting the key option is required. | "" | No | -| `tls.key` | Sets the path to the private key used for the secure connection to the authentication server. When using this option, setting the `cert` option is required. | "" | No | -| `tls.caSecret` | Defines the secret that contains the certificate authority used for the secured connection to the authentication server, it defaults to the system bundle. **This option is only available for the Kubernetes CRD**. | | No | -| `tls.certSecret` | Defines the secret that contains both the private and public certificates used for the secure connection to the authentication server. **This option is only available for the Kubernetes CRD**. | | No | -| `tls.insecureSkipVerify` | During TLS connections, if this option is set to `true`, the authentication server will accept any certificate presented by the server regardless of the host names it covers. | false | No | +| `address` | Authentication server address. | "" | Yes | +| `trustForwardHeader` | Trust all `X-Forwarded-*` headers. | false | No | +| `authResponseHeaders` | List of headers to copy from the authentication server response and set on forwarded request, replacing any existing conflicting headers. | [] | No | +| `authResponseHeadersRegex` | Regex to match by the headers to copy from the authentication server response and set on forwarded request, after stripping all headers that match the regex.
More information [here](#authresponseheadersregex). | "" | No | +| `authRequestHeaders` | List of the headers to copy from the request to the authentication server.
It allows filtering headers that should not be passed to the authentication server.
If not set or empty, then all request headers are passed. | [] | No | +| `addAuthCookiesToResponse` | List of cookies to copy from the authentication server to the response, replacing any existing conflicting cookie from the forwarded response.
Please note that all backend cookies matching the configured list will not be added to the response. | [] | No | +| `forwardBody` | Sets the `forwardBody` option to `true` to send the Body. As body is read inside Traefik before forwarding, this breaks streaming. | false | No | +| `maxBodySize` | Set the `maxBodySize` to limit the body size in bytes. If body is bigger than this, it returns a 401 (unauthorized). | -1 | No | +| `headerField` | Defines a header field to store the authenticated user. | "" | No | +| `preserveLocationHeader` | Defines whether to forward the Location header to the client as is or prefix it with the domain name of the authentication server. | false | No | +| `PreserveRequestMethod` | Defines whether to preserve the original request method while forwarding the request to the authentication server. | false | No | +| `tls.ca` | Sets the path to the certificate authority used for the secured connection to the authentication server, it defaults to the system bundle. | "" | No | +| `tls.cert` | Sets the path to the public certificate used for the secure connection to the authentication server. When using this option, setting the key option is required. | "" | No | +| `tls.key` | Sets the path to the private key used for the secure connection to the authentication server. When using this option, setting the `cert` option is required. | "" | No | +| `tls.caSecret` | Defines the secret that contains the certificate authority used for the secured connection to the authentication server, it defaults to the system bundle. **This option is only available for the Kubernetes CRD**. | | No | +| `tls.certSecret` | Defines the secret that contains both the private and public certificates used for the secure connection to the authentication server. **This option is only available for the Kubernetes CRD**. | | No | +| `tls.insecureSkipVerify` | During TLS connections, if this option is set to `true`, the authentication server will accept any certificate presented by the server regardless of the host names it covers. | false | No | ### authResponseHeadersRegex @@ -87,10 +89,10 @@ The following request properties are provided to the forward-auth target endpoin | Property | Forward-Request Header | |-------------------|------------------------| -| HTTP Method | `X-Forwarded-Method` | -| Protocol | `X-Forwarded-Proto` | -| Host | `X-Forwarded-Host` | -| Request URI | `X-Forwarded-Uri` | -| Source IP-Address | `X-Forwarded-For` | +| HTTP Method | X-Forwarded-Method | +| Protocol | X-Forwarded-Proto | +| Host | X-Forwarded-Host | +| Request URI | X-Forwarded-Uri | +| Source IP-Address | X-Forwarded-For | {!traefik-for-business-applications.md!} diff --git a/docs/content/reference/routing-configuration/http/middlewares/grpcweb.md b/docs/content/reference/routing-configuration/http/middlewares/grpcweb.md index d478c0b60..782266fdd 100644 --- a/docs/content/reference/routing-configuration/http/middlewares/grpcweb.md +++ b/docs/content/reference/routing-configuration/http/middlewares/grpcweb.md @@ -56,7 +56,7 @@ spec: | Field | Description | Default | Required | |:-----------------------------|:------------------------------------------|:--------|:---------| -| `allowOrigins` | List of allowed origins.
A wildcard origin `*` can also be configured to match all requests.
More information [here](#alloworigins). | [] | No | +| `allowOrigins` | List of allowed origins.
A wildcard origin `*` can also be configured to match all requests.
More information [here](#alloworigins). | [] | No | ### allowOrigins diff --git a/docs/content/reference/routing-configuration/http/middlewares/headers.md b/docs/content/reference/routing-configuration/http/middlewares/headers.md index 5d468363e..2d419fc9f 100644 --- a/docs/content/reference/routing-configuration/http/middlewares/headers.md +++ b/docs/content/reference/routing-configuration/http/middlewares/headers.md @@ -3,17 +3,19 @@ title: "Traefik Headers Documentation" description: "In Traefik Proxy, the HTTP headers middleware manages the headers of requests and responses. Read the technical documentation." --- +![Headers](../../../../assets/img/middleware/headers.png) + The Headers middleware manages the headers of requests and responses. By default, the following headers are automatically added when proxying requests: | Property | HTTP Header | |---------------------------|----------------------------| -| Client's IP | `X-Forwarded-For`, `X-Real-Ip` | -| Host | `X-Forwarded-Host` | -| Port | `X-Forwarded-Port` | -| Protocol | `X-Forwarded-Proto` | -| Proxy Server's Hostname | `X-Forwarded-Server` | +| Client's IP | X-Forwarded-For, X-Real-Ip | +| Host | X-Forwarded-Host | +| Port | X-Forwarded-Port | +| Protocol | X-Forwarded-Proto | +| Proxy Server's Hostname | X-Forwarded-Server | ## Configuration Examples @@ -266,34 +268,34 @@ spec: | Field | Description | Default | Required | | ----------------------------- | ------------------------------------------------- | --------- | -------- | -| `customRequestHeaders` | Lists the header names and values for requests. | [] | No | -| `customResponseHeaders` | Lists the header names and values for responses. | [] | No | -| `accessControlAllowCredentials` | Indicates if the request can include user credentials.| false | No | -| `accessControlAllowHeaders` | Specifies allowed request header names. | [] | No | -| `accessControlAllowMethods` | Specifies allowed request methods. | [] | No | -| `accessControlAllowOriginList` | Specifies allowed origins. More information [here](#accesscontrolalloworiginlist) | [] | No | -| `accessControlAllowOriginListRegex` | Allows origins matching regex. More information [here](#accesscontrolalloworiginlistregex) | [] | No | -| `accessControlExposeHeaders` | Specifies which headers are safe to expose to the API of a CORS API specification. | [] | No | -| `accessControlMaxAge` | Time (in seconds) to cache preflight requests. | 0 | No | -| `addVaryHeader` | Used in conjunction with `accessControlAllowOriginList` to determine whether the `Vary` header should be added or modified to demonstrate that server responses can differ based on the value of the origin header. | false | No | -| `allowedHosts` | Lists allowed domain names. | [] | No | -| `hostsProxyHeaders` | Specifies header keys for proxied hostname. | [] | No | -| `sslProxyHeaders` | Defines a set of header keys with associated values that would indicate a valid HTTPS request. It can be useful when using other proxies (example: `"X-Forwarded-Proto": "https"`). | {} | No | -| `stsSeconds` | Max age for `Strict-Transport-Security` header. | 0 | No | -| `stsIncludeSubdomains` | If set to `true`, the `includeSubDomains` directive is appended to the `Strict-Transport-Security` header. | false | No | -| `stsPreload` | Adds preload flag to STS header. | false | No | -| `forceSTSHeader` | Adds STS header for HTTP connections. | false | No | -| `frameDeny` | Set `frameDeny` to `true` to add the `X-Frame-Options` header with the value of `DENY`. | false | No | -| `customFrameOptionsValue` | allows the `X-Frame-Options` header value to be set with a custom value. This overrides the `FrameDeny` option. | "" | No | -| `contentTypeNosniff` | Set `contentTypeNosniff` to true to add the `X-Content-Type-Options` header with the value `nosniff`. | false | No | -| `browserXssFilter` | Set `browserXssFilter` to true to add the `X-XSS-Protection` header with the value `1; mode=block`. | false | No | -| `customBrowserXSSValue` | allows the `X-XSS-Protection` header value to be set with a custom value. This overrides the `BrowserXssFilter` option. | false | No | -| `contentSecurityPolicy` | allows the `Content-Security-Policy` header value to be set with a custom value. | false | No | -| `contentSecurityPolicyReportOnly` | allows the `Content-Security-Policy-Report-Only` header value to be set with a custom value. | "" | No | -| `publicKey` | Implements HPKP for certificate pinning. | "" | No | -| `referrerPolicy` | Controls forwarding of `Referer` header. | "" | No | -| `permissionsPolicy` | allows sites to control browser features. | "" | No | -| `isDevelopment` | Set `true` when developing to mitigate the unwanted effects of the `AllowedHosts`, SSL, and STS options. Usually testing takes place using HTTP, not HTTPS, and on `localhost`, not your production domain. | false | No | +| `customRequestHeaders` | Lists the header names and values for requests. | [] | No | +| `customResponseHeaders` | Lists the header names and values for responses. | [] | No | +| `accessControlAllowCredentials` | Indicates if the request can include user credentials.| false | No | +| `accessControlAllowHeaders` | Specifies allowed request header names. | [] | No | +| `accessControlAllowMethods` | Specifies allowed request methods. | [] | No | +| `accessControlAllowOriginList` | Specifies allowed origins. More information [here](#accesscontrolalloworiginlist) | [] | No | +| `accessControlAllowOriginListRegex` | Allows origins matching regex. More information [here](#accesscontrolalloworiginlistregex) | [] | No | +| `accessControlExposeHeaders` | Specifies which headers are safe to expose to the API of a CORS API specification. | [] | No | +| `accessControlMaxAge` | Time (in seconds) to cache preflight requests. | 0 | No | +| `addVaryHeader` | Used in conjunction with `accessControlAllowOriginList` to determine whether the `Vary` header should be added or modified to demonstrate that server responses can differ based on the value of the origin header. | false | No | +| `allowedHosts` | Lists allowed domain names. | [] | No | +| `hostsProxyHeaders` | Specifies header keys for proxied hostname. | [] | No | +| `sslProxyHeaders` | Defines a set of header keys with associated values that would indicate a valid HTTPS request. It can be useful when using other proxies (example: `"X-Forwarded-Proto": "https"`). | {} | No | +| `stsSeconds` | Max age for `Strict-Transport-Security` header. | 0 | No | +| `stsIncludeSubdomains` | If set to `true`, the `includeSubDomains` directive is appended to the `Strict-Transport-Security` header. | false | No | +| `stsPreload` | Adds preload flag to STS header. | false | No | +| `forceSTSHeader` | Adds STS header for HTTP connections. | false | No | +| `frameDeny` | Set `frameDeny` to `true` to add the `X-Frame-Options` header with the value of `DENY`. | false | No | +| `customFrameOptionsValue` | allows the `X-Frame-Options` header value to be set with a custom value. This overrides the `FrameDeny` option. | "" | No | +| `contentTypeNosniff` | Set `contentTypeNosniff` to true to add the `X-Content-Type-Options` header with the value `nosniff`. | false | No | +| `browserXssFilter` | Set `browserXssFilter` to true to add the `X-XSS-Protection` header with the value `1; mode=block`. | false | No | +| `customBrowserXSSValue` | allows the `X-XSS-Protection` header value to be set with a custom value. This overrides the `BrowserXssFilter` option. | false | No | +| `contentSecurityPolicy` | allows the `Content-Security-Policy` header value to be set with a custom value. | false | No | +| `contentSecurityPolicyReportOnly` | allows the `Content-Security-Policy-Report-Only` header value to be set with a custom value. | "" | No | +| `publicKey` | Implements HPKP for certificate pinning. | "" | No | +| `referrerPolicy` | Controls forwarding of `Referer` header. | "" | No | +| `permissionsPolicy` | allows sites to control browser features. | "" | No | +| `isDevelopment` | Set `true` when developing to mitigate the unwanted effects of the `AllowedHosts`, SSL, and STS options. Usually testing takes place using HTTP, not HTTPS, and on `localhost`, not your production domain. | false | No | ### `accessControlAllowOriginList` diff --git a/docs/content/reference/routing-configuration/http/middlewares/hmac.md b/docs/content/reference/routing-configuration/http/middlewares/hmac.md deleted file mode 100644 index 2eabab306..000000000 --- a/docs/content/reference/routing-configuration/http/middlewares/hmac.md +++ /dev/null @@ -1,207 +0,0 @@ ---- -title: "HMAC" -description: "Traefik Hub API Gateway - The HMAC Middleware allows you secure your APIs using the HMAC mechanism." ---- - -!!! info "Traefik Hub Feature" - This middleware is available exclusively in [Traefik Hub](https://traefik.io/traefik-hub/). Learn more about [Traefik Hub's advanced features](https://doc.traefik.io/traefik-hub/api-gateway/intro). - -This middleware validates a digital signature computed using the content of an HTTP request and a shared secret that is -sent to the proxy using the `Authorization` or `Proxy-Authorization` header. - -It ensures: - -- **The identity of the sender (Authentication)**: If the signature is validated by the proxy, it means that the sender -actually owns the shared secret. As a consequence, the sender's identity is considered to be proven. -- **The integrity of the request**: As the signature is based on a subset of the HTTP request, it means that if the -signature is validated by the proxy, the request used to generate the signature has not been modified between the sender -and the proxy. This middleware also allows validating the content integrity using the -[Digest header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Digest). - -This middleware is based on the [HTTP Signature Draft](https://datatracker.ietf.org/doc/html/draft-cavage-http-signatures-12). - ---- - -## Configuration Example - -Below is an advanced configuration that enables the HMAC middleware, sets one secret, ensures that the digest sum of the -request body is validated and ensures that the given headers must be included in the computation of the signature of the -request. - -```yaml tab="Middleware HMAC" -apiVersion: traefik.io/v1alpha1 -kind: Middleware -metadata: - name: hmac-auth -spec: - plugin: - hmac: - keys: - - id: secret-key - key: secret - validateDigest: true - enforcedHeaders: - - (request-target) - - (created) - - (expires) - - host - -``` - -## Configuration Options - -| Field | Description | Default | Required | -|:------------------|:---------------------------------------------|:--------|:---------| -| `keys` | A static set of secret keys to be used by HMAC middleware. | | Yes | -| `validateDigest` | Determines whether the middleware should validate the digest sum of the request body. | true | No | -| `enforcedHeaders` | A set of headers that must be included in the computation of the signature of the request. | | No | - -## Authentication Mechanism - -The sender and proxy share a common secret identified by a `keyId`. How the sender gets the secret and keeps it safe is out of scope of this documentation. - -### Crafting the Authorization Header - -To authenticate a request, the sender must provide an `Authorization` or `ProxyAuthorization` header fulfilling the HMAC authentication scheme. - -This header carries a set of parameters: - -```bash -Authorization: Hmac keyId="secret-id-1",algorithm="hmac-sha256",headers="(request-target) (created) (expires) host x-example",signature="c29tZXNpZ25hdHVyZQ==",created="1584453022",expires="1584453032" -``` - -| Parameter | Description | Example | -|-------------|--------------------------------|------------------------------------| -| `keyId` | Identifier of the key being used by the sender to build the signature | `keyId="secret-key-1"` | -| `algorithm` | Algorithm used to generate the signature.
Supported values are `hmac-sha1`, `hmac-sha256`, `hmac-sha384` and `hmac-sha512`. | `algorithm="hmac-sha512"` | -| `headers` | List of headers to use in order to build the signature string.
Each item **must** be lowercase. | `headers="host content-type"` | -| `signature` | Digital Signature of the request. See [computing the signature](#computing-the-signature). | `signature="c29tZXNpZ25hdHVyZQ=="` | -| `created` | Unix timestamp of the signature creation. | `created="1574453022"` | -| `expires` | Unix timestamp of the signature expiration. | `expires="1574453022"` | - -!!! danger "Time sensitivity" - If the `created` timestamp is in the future or the `expires` timestamp is in the past, the middleware will refuse the request. - This behaviour makes using this middleware sensitive to clock skew between the client and the server. - - Make sure that your client and your server have their clocks synchronized. - -### Computing the Signature - -The signature is the base64-encoded value of the result of an HMAC signature algorithm computed with a `signature string` and the sender's `secret key`. - -For example: - -```bash -signature=base64(HMAC(signatureString, secret)) -``` - -### Crafting the Signature String - -A signed HTTP request needs to be tolerant of some trivial alterations during transmission as it goes through gateways, proxies and other entities. -As a consequence, signing the whole request is not an option as a single header modification could result in a not valid signature. - -To avoid this problem, this middleware builds the `signature string` from a subset of header values defined by the sender with the `headers` parameter of the authorization header. - -To build the signature string, the client **must** take the values of each header specified by the `headers` parameter **in the order they appear**, then apply the following logic to each of them: - -1. If the header is a special header, then evaluate its value according to [the special headers values section](#special-header-values) -2. If the header is not a special header, then append the lowercase header name followed with an ASCII colon `:`, an ASCII space \` \` and the header value. -If the header has multiple values then append those values separated by an ASCII comma `,` and an ASCII space \` \` -3. If value is not the last value then append an ASCII newline `\n`. The signature string MUST NOT include a trailing ASCII newline - -!!! warning - All headers values are trimmed from their spaces." - -#### Special Header Values - -By design, all the information of an HTTP request is not available through headers. However, it makes sense to secure the request using them. - -To allow this, the `headers` parameter accepts special header names that can be used. - -| Value | Description | Signature String Example | -| --------------------- | ------------------------------------------------------------- |------------------------- | -| `(request-target)` | Obtained by concatenating the lowercase `:method`, an ASCII space, and the `:path` pseudo-headers ([as specified in HTTP/2](https://tools.ietf.org/html/rfc7540#section-8.1.2.3)). | `(request-target): get /api/V1/resource?query=foo` | -| `(created)` | Value of the authorization header `created` parameter. | `(created): 1584453022` | -| `(expires)` | Value of the authorization header `expires` parameter. | `(expires): 1584453082` | - -Their evaluated value is obtained by appending the special header name with an ASCII colon `:` an ASCII space \` \` then the designated value. - -```bash tab="Example" -(created): 1929494939 -(request-target): get /foo/bar -``` - -#### Signature String Example - -Here is an example with the authorization header parameters set: - -- `headers="(request-target) (created) (expires) host x-example x-emptyheader cache-control"` -- `created="1584466921"` -- `expires="1584466931"` - -```bash tab="Request" -GET /foo HTTP/1.1 -Host: example.org -X-Example: Example header - with some whitespace. -X-EmptyHeader: -X-NotIncluded: always -Cache-Control: max-age=60 -Cache-Control: must-revalidate -``` - -```bash tab="Expected Signature String" -(request-target): get /foo -(created): 1584466921 -(expires): 1584466931 -host: example.org -x-example: Example header with some whitespace. -x-emptyheader: -cache-control: max-age=60, must-revalidate -``` - -#### Enforced Headers - -It is possible to configure the middleware to enforce a minimum set of headers to create the signature string. -This means that any request that does not have the enforced headers in its signature is systematically refused. - -This option also configures the headers list returned when [initiating the authentication](#initiating-the-authentication). - -It defaults to `(request-target) (created) (expires)`. - -!!! danger "Always enforce (created) and (expires)" - The `created` and `expires` header parameters protect against replay attacks. - To make sure that their value is not modified during transport, it is **highly recommended** to always include those parameters values in the signature using the `(created)` and `(expired)` special headers value. - - To do so, it is recommended to **always** configure the middleware to enforce `(created)` and `(expires)`. - -### Initiating the Authentication - -The authentication can be initiated by the proxy. A `401 Unauthorized` response is returned with a `WWW-Authenticate` header indicating to use the `Hmac` authentication scheme. - -```bash -WWW-Authenticate: Hmac headers="(request-target) (created) (expires) host x-example" -``` - -This header indicates that the sender needs to provide an Authorization header that fulfills the `Hmac` authentication schemes. -It also indicates a list of headers that have to be included in the signature using the `headers` parameter. - -!!! note "Enforced headers" - The list of headers carried in the `WWW-Authenticate` header is the list of [enforced headers](#enforced-headers) indicated in the middleware configuration. - -## Validating Request Body Integrity - -It is possible to make sure that the body of the incoming request has not been altered during transmission by including the [digest header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Digest) in the signature string. - -This middleware, by default, validates the digest sum of the body, if there is one. - -Only SHA-256 and SHA-512 checksums are supported for checksum computation. - -!!! warning "Potential CPU and memory usage" - - Validating the digest makes the middleware read the request body and computes a checksum for it. - As a consequence it can cause high memory and CPU usage on proxies. - - To disable this feature and only perform authentication, set the `validateDigest` option to `false` in the middleware configuration. - -{!traefik-for-business-applications.md!} diff --git a/docs/content/reference/routing-configuration/http/middlewares/inflightreq.md b/docs/content/reference/routing-configuration/http/middlewares/inflightreq.md index 32d157162..e8fc6a001 100644 --- a/docs/content/reference/routing-configuration/http/middlewares/inflightreq.md +++ b/docs/content/reference/routing-configuration/http/middlewares/inflightreq.md @@ -54,12 +54,12 @@ spec: | Field | Description | Default | Required | |:-----------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:--------|:---------| -| `amount` | The `amount` option defines the maximum amount of allowed simultaneous in-flight request.
The middleware responds with `HTTP 429 Too Many Requests` if there are already `amount` requests in progress (based on the same `sourceCriterion` strategy). | 0 | No | -| `sourceCriterion.requestHost` | Whether to consider the request host as the source.
More information about `sourceCriterion`[here](#sourcecriterion). | false | No | -| `sourceCriterion.requestHeaderName` | Name of the header used to group incoming requests.
More information about `sourceCriterion`[here](#sourcecriterion). | "" | No | -| `sourceCriterion.ipStrategy.depth` | Depth position of the IP to select in the `X-Forwarded-For` header (starting from the right).
0 means no depth.
If greater than the total number of IPs in `X-Forwarded-For`, then the client IP is empty
If higher than 0, the `excludedIPs` options is not evaluated.
More information about [`sourceCriterion`](#sourcecriterion), [`ipStrategy](#ipstrategy), and [`depth`](#example-of-depth--x-forwarded-for) below. | 0 | No | -| `sourceCriterion.ipStrategy.excludedIPs` | Allows Traefik to scan the `X-Forwarded-For` header and select the first IP not in the list.
If `depth` is specified, `excludedIPs` is ignored.
More information about [`sourceCriterion`](#sourcecriterion), [`ipStrategy](#ipstrategy), and [`excludedIPs`](#example-of-excludedips--x-forwarded-for) below. | | No | -| `sourceCriterion.ipStrategy.ipv6Subnet` | If `ipv6Subnet` is provided and the selected IP is IPv6, the IP is transformed into the first IP of the subnet it belongs to.
More information about [`sourceCriterion`](#sourcecriterion), [`ipStrategy.ipv6Subnet`](#ipstrategyipv6subnet), and [`excludedIPs`](#example-of-excludedips--x-forwarded-for) below. | | No | +| `amount` | The `amount` option defines the maximum amount of allowed simultaneous in-flight request.
The middleware responds with `HTTP 429 Too Many Requests` if there are already `amount` requests in progress (based on the same `sourceCriterion` strategy). | 0 | No | +| `sourceCriterion.requestHost` | Whether to consider the request host as the source.
More information about `sourceCriterion`[here](#sourcecriterion). | false | No | +| `sourceCriterion.requestHeaderName` | Name of the header used to group incoming requests.
More information about `sourceCriterion`[here](#sourcecriterion). | "" | No | +| `sourceCriterion.ipStrategy.depth` | Depth position of the IP to select in the `X-Forwarded-For` header (starting from the right).
0 means no depth.
If greater than the total number of IPs in `X-Forwarded-For`, then the client IP is empty
If higher than 0, the `excludedIPs` options is not evaluated.
More information about [`sourceCriterion`](#sourcecriterion), [`ipStrategy](#ipstrategy), and [`depth`](#example-of-depth--x-forwarded-for) below. | 0 | No | +| `sourceCriterion.ipStrategy.excludedIPs` | Allows Traefik to scan the `X-Forwarded-For` header and select the first IP not in the list.
If `depth` is specified, `excludedIPs` is ignored.
More information about [`sourceCriterion`](#sourcecriterion), [`ipStrategy](#ipstrategy), and [`excludedIPs`](#example-of-excludedips--x-forwarded-for) below. | | No | +| `sourceCriterion.ipStrategy.ipv6Subnet` | If `ipv6Subnet` is provided and the selected IP is IPv6, the IP is transformed into the first IP of the subnet it belongs to.
More information about [`sourceCriterion`](#sourcecriterion), [`ipStrategy.ipv6Subnet`](#ipstrategyipv6subnet), and [`excludedIPs`](#example-of-excludedips--x-forwarded-for) below. | | No | ### sourceCriterion @@ -90,26 +90,26 @@ If `ipv6Subnet` is provided, the IP is transformed in the following way. | IP | ipv6Subnet | clientIP | |---------------------------|--------------|-----------------------| -| `"::abcd:1111:2222:3333"` | `64` | `"::0:0:0:0"` | -| `"::abcd:1111:2222:3333"` | `80` | `"::abcd:0:0:0:0"` | -| `"::abcd:1111:2222:3333"` | `96` | `"::abcd:1111:0:0:0"` | +| `"::abcd:1111:2222:3333"` | `64` | `"::0:0:0:0"` | +| `"::abcd:1111:2222:3333"` | `80` | `"::abcd:0:0:0:0"` | +| `"::abcd:1111:2222:3333"` | `96` | `"::abcd:1111:0:0:0"` | -### Example of Depth & `X-Forwarded-For` +### Example of Depth & X-Forwarded-For If `depth` is set to 2, and the request `X-Forwarded-For` header is `"10.0.0.1,11.0.0.1,12.0.0.1,13.0.0.1"` then the "real" client IP is `"10.0.0.1"` (at depth 4) but the IP used as the criterion is `"12.0.0.1"` (`depth=2`). -| `X-Forwarded-For` | depth | clientIP | -|-----------------------------------------|-------|--------------| -| `"10.0.0.1,11.0.0.1,12.0.0.1,13.0.0.1"` | `1` | `"13.0.0.1"` | -| `"10.0.0.1,11.0.0.1,12.0.0.1,13.0.0.1"` | `3` | `"11.0.0.1"` | -| `"10.0.0.1,11.0.0.1,12.0.0.1,13.0.0.1"` | `5` | `""` | +| X-Forwarded-For | depth | clientIP | +|-----------------------------------------|---------|--------------| +| `"10.0.0.1,11.0.0.1,12.0.0.1,13.0.0.1"` | `1` | `"13.0.0.1"` | +| `"10.0.0.1,11.0.0.1,12.0.0.1,13.0.0.1"` | `3` | `"11.0.0.1"` | +| `"10.0.0.1,11.0.0.1,12.0.0.1,13.0.0.1"` | `5` | `""` | ### Example of ExcludedIPs & X-Forwarded-For -| `X-Forwarded-For` | excludedIPs | clientIP | +| X-Forwarded-For | excludedIPs | clientIP | |-----------------------------------------|-----------------------|--------------| -| `"10.0.0.1,11.0.0.1,12.0.0.1,13.0.0.1"` | `"12.0.0.1,13.0.0.1"` | `"11.0.0.1"` | -| `"10.0.0.1,11.0.0.1,12.0.0.1,13.0.0.1"` | `"15.0.0.1,13.0.0.1"` | `"12.0.0.1"` | -| `"10.0.0.1,11.0.0.1,12.0.0.1,13.0.0.1"` | `"10.0.0.1,13.0.0.1"` | `"12.0.0.1"` | -| `"10.0.0.1,11.0.0.1,12.0.0.1,13.0.0.1"` | `"15.0.0.1,16.0.0.1"` | `"13.0.0.1"` | -| `"10.0.0.1,11.0.0.1"` | `"10.0.0.1,11.0.0.1"` | `""` | +| `"10.0.0.1,11.0.0.1,12.0.0.1,13.0.0.1"` | `"12.0.0.1,13.0.0.1"` | `"11.0.0.1"` | +| `"10.0.0.1,11.0.0.1,12.0.0.1,13.0.0.1"` | `"15.0.0.1,13.0.0.1"` | `"12.0.0.1"` | +| `"10.0.0.1,11.0.0.1,12.0.0.1,13.0.0.1"` | `"10.0.0.1,13.0.0.1"` | `"12.0.0.1"` | +| `"10.0.0.1,11.0.0.1,12.0.0.1,13.0.0.1"` | `"15.0.0.1,16.0.0.1"` | `"13.0.0.1"` | +| `"10.0.0.1,11.0.0.1"` | `"10.0.0.1,11.0.0.1"` | `""` | diff --git a/docs/content/reference/routing-configuration/http/middlewares/ipallowlist.md b/docs/content/reference/routing-configuration/http/middlewares/ipallowlist.md index a370f85c8..8703b2a3a 100644 --- a/docs/content/reference/routing-configuration/http/middlewares/ipallowlist.md +++ b/docs/content/reference/routing-configuration/http/middlewares/ipallowlist.md @@ -56,10 +56,10 @@ spec: | Field | Description | Default | Required | |:-----------|:------------------------------|:--------|:---------| -| `sourceRange` | List of allowed IPs (or ranges of allowed IPs by using CIDR notation). | | Yes | -| `ipStrategy.depth` | Depth position of the IP to select in the `X-Forwarded-For` header (starting from the right).
0 means no depth.
If greater than the total number of IPs in `X-Forwarded-For`, then the client IP is empty
If higher than 0, the `excludedIPs` options is not evaluated.
More information about [`ipStrategy](#ipstrategy), and [`depth`](#example-of-depth--x-forwarded-for) below. | 0 | No | -| `ipStrategy.excludedIPs` | Allows Traefik to scan the `X-Forwarded-For` header and select the first IP not in the list.
If `depth` is specified, `excludedIPs` is ignored.
More information about [`ipStrategy](#ipstrategy), and [`excludedIPs`](#example-of-excludedips--x-forwarded-for) below. | | No | -| `ipStrategy.ipv6Subnet` | If `ipv6Subnet` is provided and the selected IP is IPv6, the IP is transformed into the first IP of the subnet it belongs to.
More information about [`ipStrategy.ipv6Subnet`](#ipstrategyipv6subnet), and [`excludedIPs`](#example-of-excludedips--x-forwarded-for) below. | | No | +| `sourceRange` | List of allowed IPs (or ranges of allowed IPs by using CIDR notation). | | Yes | +| `ipStrategy.depth` | Depth position of the IP to select in the `X-Forwarded-For` header (starting from the right).
0 means no depth.
If greater than the total number of IPs in `X-Forwarded-For`, then the client IP is empty
If higher than 0, the `excludedIPs` options is not evaluated.
More information about [`ipStrategy](#ipstrategy), and [`depth`](#example-of-depth--x-forwarded-for) below. | 0 | No | +| `ipStrategy.excludedIPs` | Allows Traefik to scan the `X-Forwarded-For` header and select the first IP not in the list.
If `depth` is specified, `excludedIPs` is ignored.
More information about [`ipStrategy](#ipstrategy), and [`excludedIPs`](#example-of-excludedips--x-forwarded-for) below. | | No | +| `ipStrategy.ipv6Subnet` | If `ipv6Subnet` is provided and the selected IP is IPv6, the IP is transformed into the first IP of the subnet it belongs to.
More information about [`ipStrategy.ipv6Subnet`](#ipstrategyipv6subnet), and [`excludedIPs`](#example-of-excludedips--x-forwarded-for) below. | | No | ### ipStrategy @@ -95,26 +95,26 @@ If `ipv6Subnet` is provided, the IP is transformed in the following way. | IP | ipv6Subnet | clientIP | |---------------------------|--------------|-----------------------| -| `"::abcd:1111:2222:3333"` | `64` | `"::0:0:0:0"` | -| `"::abcd:1111:2222:3333"` | `80` | `"::abcd:0:0:0:0"` | -| `"::abcd:1111:2222:3333"` | `96` | `"::abcd:1111:0:0:0"` | +| `"::abcd:1111:2222:3333"` | `64` | `"::0:0:0:0"` | +| `"::abcd:1111:2222:3333"` | `80` | `"::abcd:0:0:0:0"` | +| `"::abcd:1111:2222:3333"` | `96` | `"::abcd:1111:0:0:0"` | -### Example of Depth & `X-Forwarded-For` +### Example of Depth & X-Forwarded-For If `depth` is set to 2, and the request `X-Forwarded-For` header is `"10.0.0.1,11.0.0.1,12.0.0.1,13.0.0.1"` then the "real" client IP is `"10.0.0.1"` (at depth 4) but the IP used as the criterion is `"12.0.0.1"` (`depth=2`). -| `X-Forwarded-For` | depth | clientIP | +| X-Forwarded-For | depth | clientIP | |-----------------------------------------|---------|--------------| -| `"10.0.0.1,11.0.0.1,12.0.0.1,13.0.0.1"` | `1` | `"13.0.0.1"` | -| `"10.0.0.1,11.0.0.1,12.0.0.1,13.0.0.1"` | `3` | `"11.0.0.1"` | -| `"10.0.0.1,11.0.0.1,12.0.0.1,13.0.0.1"` | `5` | `""` | +| `"10.0.0.1,11.0.0.1,12.0.0.1,13.0.0.1"` | `1` | `"13.0.0.1"` | +| `"10.0.0.1,11.0.0.1,12.0.0.1,13.0.0.1"` | `3` | `"11.0.0.1"` | +| `"10.0.0.1,11.0.0.1,12.0.0.1,13.0.0.1"` | `5` | `""` | -### Example of ExcludedIPs & `X-Forwarded-For` +### Example of ExcludedIPs & X-Forwarded-For -| `X-Forwarded-For` | excludedIPs | clientIP | +| X-Forwarded-For | excludedIPs | clientIP | |-----------------------------------------|-----------------------|--------------| -| `"10.0.0.1,11.0.0.1,12.0.0.1,13.0.0.1"` | `"12.0.0.1,13.0.0.1"` | `"11.0.0.1"` | -| `"10.0.0.1,11.0.0.1,12.0.0.1,13.0.0.1"` | `"15.0.0.1,13.0.0.1"` | `"12.0.0.1"` | -| `"10.0.0.1,11.0.0.1,12.0.0.1,13.0.0.1"` | `"10.0.0.1,13.0.0.1"` | `"12.0.0.1"` | -| `"10.0.0.1,11.0.0.1,12.0.0.1,13.0.0.1"` | `"15.0.0.1,16.0.0.1"` | `"13.0.0.1"` | -| `"10.0.0.1,11.0.0.1"` | `"10.0.0.1,11.0.0.1"` | `""` | +| `"10.0.0.1,11.0.0.1,12.0.0.1,13.0.0.1"` | `"12.0.0.1,13.0.0.1"` | `"11.0.0.1"` | +| `"10.0.0.1,11.0.0.1,12.0.0.1,13.0.0.1"` | `"15.0.0.1,13.0.0.1"` | `"12.0.0.1"` | +| `"10.0.0.1,11.0.0.1,12.0.0.1,13.0.0.1"` | `"10.0.0.1,13.0.0.1"` | `"12.0.0.1"` | +| `"10.0.0.1,11.0.0.1,12.0.0.1,13.0.0.1"` | `"15.0.0.1,16.0.0.1"` | `"13.0.0.1"` | +| `"10.0.0.1,11.0.0.1"` | `"10.0.0.1,11.0.0.1"` | `""` | diff --git a/docs/content/reference/routing-configuration/http/middlewares/jwt.md b/docs/content/reference/routing-configuration/http/middlewares/jwt.md deleted file mode 100644 index 2aebcdf86..000000000 --- a/docs/content/reference/routing-configuration/http/middlewares/jwt.md +++ /dev/null @@ -1,233 +0,0 @@ ---- -title: 'JWT Authentication' -description: 'Traefik Hub API Gateway - The JWT Authentication middleware verifies that a valid JWT token is provided in the Authorization header.' ---- - -!!! info "Traefik Hub Feature" - This middleware is available exclusively in [Traefik Hub](https://traefik.io/traefik-hub/). Learn more about [Traefik Hub's advanced features](https://doc.traefik.io/traefik-hub/api-gateway/intro). - -The JWT middleware verifies that a valid JWT token is provided in the `Authorization` header (`Authorization: Bearer `). -If the token can't be passed as an `Authorization` header, it can be given as form data or as a query parameter. -See the `tokenKey` option for more information. - -With no specific configuration, a JWT middleware only validates the signature of a JWT and checks the `nbf`, `exp` and `iat` standard claims (if they are present). -Custom claim validation can be configured with [Custom Claims Validation](#claims). - ---- - -## Configuration Example - -```yaml tab="Middleware JWT" -apiVersion: traefik.io/v1alpha1 -kind: Middleware -metadata: - name: test-jwt -spec: - plugin: - jwt: - signingSecret: my-secret - forwardHeaders: - Group: grp - Expires-At: exp - claims: Equals(`grp`, `admin`) -``` - -## Configuration Options - -| Field | Description | Default | Required | -|:----------------|:------------------------------------------------|:--------|:---------| -| `signingSecret` | Defines the secret used for signing the JWT certificates.
It is then used by the middleware to verify incoming requests.
At least one of `signingSecret`, `publicKey`, `jwksFile` or `jwksUrl` options must be set. (More information [here](#signingsecret)) | "" | No | -| `signingSecretBase64Encoded` | Defines whether the `signingSecret` is base64-encoded.
If set to `true`, the `signingSecret` is base64-decoded before being used. | false | No | -| `publicKey` | Defines the public key used to verify secret signature in incoming requests.
In that case, users should sign their token using a private key corresponding to the configured public key.
At least one of `signingSecret`, `publicKey`, `jwksFile` or `jwksUrl` options must be set. | "" | No | -| `jwksFile` | Defines a set of [JWK](https://tools.ietf.org/html/rfc7517) to be used to verify the signature of JWTs.
The option can either be a path to a file mounted on the API Gateway or directly the content of a JWK set file.
At least one of `signingSecret`, `publicKey`, `jwksFile` or `jwksUrl` options must be set. (More information [here](#jwksfile)) | "" | No | -| `jwksUrl` | Defines the URL of the host serving a [JWK](https://tools.ietf.org/html/rfc7517) set.
The keys are cached if the HTTP Cache Control allows for caching.
At least one of `signingSecret`, `publicKey`, `jwksFile` or `jwksUrl` options must be set.
(More information [here](#jwksurl)) | "" | No | -| `forwardAuthorization` | Defines whether the authorization header will be forwarded or stripped from a request after it has been approved by the middleware. | false | No | -| `tokenKey` | Defines the name of the query and form data parameter used for passing the JWT, for applications that can't pass it in the `Authorization` header.
The middleware always looks in the `Authorization` header first, even with this option enabled.
This option should only be enabled if the JWT cannot be passed as an Authorization header, as it is not recommended by the [RFC](https://www.rfc-editor.org/rfc/rfc6750#section-2). | "" | No | -| `claims` | Defines the claims to validate in order to authorize the request.
The `claims` option can only be used with JWT-formatted token. (More information [here](#claims)) | "" | No | -| `usernameClaim` | Defines the claim that will be evaluated to populate the `clientusername` in the access logs.
The `usernameClaim` option can only be used with JWT-formatted token.| "" | No | -| `forwardHeaders` | Defines the HTTP headers to add to requests and populates them with values extracted from the access token claims returned by the authorization server.
Claims to be forwarded that are not found in the JWT result in empty headers.
The `forwardHeaders` option can only be used with JWT-formatted token. | [] | No | -| `clientConfig.tls.ca` | PEM-encoded certificate bundle or a URN referencing a secret containing the certificate bundle used to establish a TLS connection with the authorization server (More information [here](#clientconfig)) | "" | No | -| `clientConfig.tls.cert` | PEM-encoded certificate or a URN referencing a secret containing the certificate used to establish a TLS connection with the Vault server (More information [here](#clientconfig)) | "" | No | -| `clientConfig.tls.key` | PEM-encoded key or a URN referencing a secret containing the key used to establish a TLS connection with the Vault server. (More information [here](#clientconfig)) | "" | No | -| `clientConfig.tls.insecureSkipVerify` | Disables TLS certificate verification when communicating with the authorization server.
Useful for testing purposes but strongly discouraged for production. (More information [here](#clientconfig)) | "" | No | -| `clientConfig.timeoutSeconds` | Defines the time before giving up requests to the authorization server. | 5 | No | -| `clientConfig.maxRetries` | Defines the number of retries for requests to authorization server that fail. | 3 | No | - -### claims - -#### Syntax - -The following functions are supported in `claims`: - -| Function | Description | Example | -|-------------------|--------------------|-----------------| -| Equals | Validates the equality of the value in `key` with `value`. | Equals(\`grp\`, \`admin\`) | -| Prefix | Validates the value in `key` has the prefix of `value`. | Prefix(\`referrer\`, \`http://example.com\`) | -| Contains (string) | Validates the value in `key` contains `value`. | Contains(\`referrer\`, \`/foo/\`) | -| Contains (array) | Validates the `key` array contains the `value`. | Contains(\`areas\`, \`home\`) | -| SplitContains | Validates the value in `key` contains the `value` once split by the separator. | SplitContains(\`scope\`, \` \`, \`writer\`) | -| OneOf | Validates the `key` array contains one of the `values`. | OneOf(\`areas\`, \`office\`, \`lab\`) | - -All functions can be joined by boolean operands. The supported operands are: - -| Operand | Description | Example | -|---------|--------------------|-----------------| -| && | Compares two functions and returns true only if both evaluate to true. | Equals(\`grp\`, \`admin\`) && Equals(\`active\`, \`true\`) | -| \|\| | Compares two functions and returns true if either evaluate to true. | Equals(\`grp\`, \`admin\`) \|\| Equals(\`active\`, \`true\`) | -| ! | Returns false if the function is true, otherwise returns true. | !Equals(\`grp\`, \`testers\`) | - -All examples will return true for the following data structure: - -```json tab="JSON" -{ - "active": true, - "grp": "admin", - "scope": "reader writer deploy", - "referrer": "http://example.com/foo/bar", - "areas": [ - "office", - "home" - ] -} -``` - -#### Nested Claims - -Nested claims are supported by using a `.` between keys. For example: - -```bash tab="Key" -user.name -``` - -```json tab="Claims" -{ - "active": true, - "grp": "admin", - "scope": "reader writer deploy", - "referrer": "http://example.com/foo/bar", - "areas": [ - "office", - "home" - ], - "user" { - "name": "John Snow", - "status": "undead" - } -} -``` - -```bash tab="Result" -John Snow -``` - -!!! note "Handling keys that contain a '.'" - -If the `key` contains a dot, the dot can be escaped using `\.` - -!!! note "Handling a key that contains a '\'" - -If the `key` contains a `\`, it needs to be doubled `\\`. - -### clientConfig - -Defines the configuration used to connect the API Gateway to a Third Party Software such as an Identity Provider. - -#### `clientConfig.tls` - -##### Storing secret values in Kubernetes secrets - -When configuring the `tls.ca`, `tls.cert`, `tls.key`, it is possible to reference Kubernetes secrets defined in the same namespace as the Middleware. -The reference to a Kubernetes secret takes the form of a URN: - -```text -urn:k8s:secret:[name]:[valueKey] -``` - -```yaml tab="Middleware JWT" -apiVersion: traefik.io/v1alpha1 -kind: Middleware -metadata: - name: test-jwt -spec: - plugin: - jwt: - clientConfig: - tls: - ca: "urn:k8s:secret:tls:ca" - cert: "urn:k8s:secret:tls:cert" - key: "urn:k8s:secret:tls:key" - insecureSkipVerify: true -``` - -```yaml tab="Kubernetes TLS Secret" -apiVersion: v1 -kind: Secret -metadata: - name: tls -stringData: - ca: |- - -----BEGIN CERTIFICATE----- - MIIB9TCCAWACAQAwgbgxGTAXBgNVBAoMEFF1b1ZhZGlzIExpbWl0ZWQxHDAaBgNV - BAsME0RvY3VtZW50IERlcGFydG1lbnQxOTA3BgNVBAMMMFdoeSBhcmUgeW91IGRl - Y29kaW5nIG1lPyAgVGhpcyBpcyBvbmx5IGEgdGVzdCEhITERMA8GA1UEBwwISGFt - aWx0b24xETAPBgNVBAgMCFBlbWJyb2tlMQswCQYDVQQGEwJCTTEPMA0GCSqGSIb3 - DQEJARYAMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCJ9WRanG/fUvcfKiGl - EL4aRLjGt537mZ28UU9/3eiJeJznNSOuNLnF+hmabAu7H0LT4K7EdqfF+XUZW/2j - RKRYcvOUDGF9A7OjW7UfKk1In3+6QDCi7X34RE161jqoaJjrm/T18TOKcgkkhRzE - apQnIDm0Ea/HVzX/PiSOGuertwIDAQABMAsGCSqGSIb3DQEBBQOBgQBzMJdAV4QP - Awel8LzGx5uMOshezF/KfP67wJ93UW+N7zXY6AwPgoLj4Kjw+WtU684JL8Dtr9FX - ozakE+8p06BpxegR4BR3FMHf6p+0jQxUEAkAyb/mVgm66TyghDGC6/YkiKoZptXQ - 98TwDIK/39WEB/V607As+KoYazQG8drorw== - -----END CERTIFICATE----- - cert: |- - -----BEGIN CERTIFICATE----- - MIIB9TCCAWACAQAwgbgxGTAXBgNVBAoMEFF1b1ZhZGlzIExpbWl0ZWQxHDAaBgNV - BAsME0RvY3VtZW50IERlcGFydG1lbnQxOTA3BgNVBAMMMFdoeSBhcmUgeW91IGRl - Y29kaW5nIG1lPyAgVGhpcyBpcyBvbmx5IGEgdGVzdCEhITERMA8GA1UEBwwISGFt - aWx0b24xETAPBgNVBAgMCFBlbWJyb2tlMQswCQYDVQQGEwJCTTEPMA0GCSqGSIb3 - DQEJARYAMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCJ9WRanG/fUvcfKiGl - EL4aRLjGt537mZ28UU9/3eiJeJznNSOuNLnF+hmabAu7H0LT4K7EdqfF+XUZW/2j - RKRYcvOUDGF9A7OjW7UfKk1In3+6QDCi7X34RE161jqoaJjrm/T18TOKcgkkhRzE - apQnIDm0Ea/HVzX/PiSOGuertwIDAQABMAsGCSqGSIb3DQEBBQOBgQBzMJdAV4QP - Awel8LzGx5uMOshezF/KfP67wJ93UW+N7zXY6AwPgoLj4Kjw+WtU684JL8Dtr9FX - ozakE+8p06BpxegR4BR3FMHf6p+0jQxUEAkAyb/mVgm66TyghDGC6/YkiKoZptXQ - 98TwDIK/39WEB/V607As+KoYazQG8drorw== - -----END CERTIFICATE----- - key: |- - -----BEGIN EC PRIVATE KEY----- - MHcCAQEEIC8CsJ/B115S+JtR1/l3ZQwKA3XdXt9zLqusF1VXc/KloAoGCCqGSM49 - AwEHoUQDQgAEpwUmRIZHFt8CdDHYm1ikScCScd2q6QVYXxJu+G3fQZ78ScGtN7fu - KXMnQqVjXVRAr8qUY8yipVKuMCepnPXScQ== - -----END EC PRIVATE KEY----- -``` - -### jwksFile - -#### JWT Header Key ID - -If the JWT header contains a `kid` header, the middleware expects to find a JWK. -If a JWK cannot be found, it returns a `401 Unauthorized` error. - -### jwksUrl - -#### JWT Header Key ID - -If the JWT header contains a `kid` header, the middleware expects to find a JWK. -If a JWK cannot be found, it returns a `401 Unauthorized` error. - -#### JWT Issuer Claim - -If `jwksUrl` is set to a path and the `iss` property is missing in the JWT it's trying to verify, the middleware returns a `401 Unauthorized` error. - -### signingSecret - -#### Storing secret values in Kubernetes secrets - -When configuring the `signingSecret`, it is possible to reference a Kubernetes secret defined in the same namespace as the Middleware. -The reference to a Kubernetes secret takes the form of a URN: - -```text -urn:k8s:secret:[name]:[valueKey] -``` - -{!traefik-for-business-applications.md!} diff --git a/docs/content/reference/routing-configuration/http/middlewares/ldap.md b/docs/content/reference/routing-configuration/http/middlewares/ldap.md deleted file mode 100644 index 9a0efc6f2..000000000 --- a/docs/content/reference/routing-configuration/http/middlewares/ldap.md +++ /dev/null @@ -1,105 +0,0 @@ ---- -title: 'LDAP Authentication' -description: 'Traefik Hub API Gateway - The LDAP Authentication middleware secures your applications by delegating the authentication to an external LDAP server.' ---- - -!!! info "Traefik Hub Feature" - This middleware is available exclusively in [Traefik Hub](https://traefik.io/traefik-hub/). Learn more about [Traefik Hub's advanced features](https://doc.traefik.io/traefik-hub/api-gateway/intro). - -The LDAP Authentication middleware secures your applications by delegating the authentication to an external LDAP server. - -The LDAP middleware will look for user credentials in the `Authorization` header of each request. Credentials must be encoded with the following format: `base64(username:password)`. - ---- - -## Configuration Examples - -```yaml tab="Basic usage" -apiVersion: traefik.io/v1alpha1 -kind: Middleware -metadata: - name: test-ldap-auth - namespace: apps -spec: - plugin: - ldap: - url: ldap://ldap.example.org:636 - baseDN: dc=example,dc=org -``` - -```yaml tab="Basic usage with bind need" -apiVersion: traefik.io/v1alpha1 -kind: Middleware -metadata: - name: test-ldap-auth - namespace: apps -spec: - plugin: - ldap: - url: ldap://ldap.example.org:636 - baseDN: dc=example,dc=org - bindDN: cn=binding_user,dc=example,dc=org - bindPassword: "urn:k8s:secret:my-secret:bindpassword" -``` - -```yaml tab="Enabling search, bind & WWW-Authenticate header" -apiVersion: traefik.io/v1alpha1 -kind: Middleware -metadata: - name: test-ldap-auth - namespace: apps -spec: - plugin: - ldap: - url: ldap://ldap.example.org:636 - baseDN: dc=example,dc=org - searchFilter: (&(objectClass=inetOrgPerson)(gidNumber=500)(uid=%s)) - forwardUsername: true - forwardUsernameHeader: Custom-Username-Header-Name - wwwAuthenticateHeader: true - wwwAuthenticateHeaderRealm: traefikee -``` - -## Configuration Options - -| Field | Description | Default | Required | -|:------|:------------|:--------|:---------| -| `url` | LDAP server URL. Either the `ldaps` or `ldap` protocol and end with a port (ex: `ldaps://ldap.example.org:636`). | "" | Yes | -| `startTLS` | Enable [`StartTLS`](https://tools.ietf.org/html/rfc4511#section-4.14) request when initializing the connection with the LDAP server. | false | No | -| `certificateAuthority` | PEM-encoded certificate to use to establish a connection with the LDAP server if the connection uses TLS but that the certificate was signed by a custom Certificate Authority. | "" | No | -| `insecureSkipVerify` | Allow proceeding and operating even for server TLS connections otherwise considered insecure. | false | No | -| `bindDN` | Domain name to bind to in order to authenticate to the LDAP server when running on search mode.
Leaving this empty with search mode means binds are anonymous, which is rarely expected behavior.
Not used when running in [bind mode](#bind-mode-vs-search-mode). | "" | No | -| `bindPassword` | Password for the `bindDN` used in search mode to authenticate with the LDAP server. More information [here](#bindpassword) | "" | No | -| `connPool` | Pool of connections to the LDAP server (to minimize the impact on the performance). | None | No | -| `connPool.size` | Number of connections managed by the pool can be customized with the `size` property. | 10 | No | -| `connPool.burst` | Ephemeral connections that are opened when the pool is already full. Once the number of connection exceeds `size` + `burst`, a `Too Many Connections` error is returned. | 5 | No | -| `connPool.ttl` | Pooled connections are still meant to be short-lived, so they are closed after roughly one minute by default. This behavior can be modified with the `ttl` property. | 60s | No | -| `baseDN` | Base domain name that should be used for bind and search queries. | "" | Yes | -| `attribute` | The attribute used to bind a user. Bind queries use this pattern: `=,`, where the username is extracted from the request header. | cn | Yes | -| `forwardUsername` | Forward the username in a specific header, defined using the `forwardUsernameHeader` option. | "" | No | -| `forwardUsernameHeader` | Name of the header to put the username in when forwarding it. This is not used if the `forwardUsername` option is set to `false`. | Username | Yes | -| `forwardAuthorization` | Enable to forward the authorization header from the request after it has been approved by the middleware. | false | Yes | -| `searchFilter` | If not empty, the middleware will run in [search mode](#bind-mode-vs-search-mode), filtering search results with the given query.
Filter queries can use the `%s` placeholder that is replaced by the username provided in the `Authorization` header of the request (for example: `(&(objectClass=inetOrgPerson)(gidNumber=500)(uid=%s))`). | "" | No | -| `wwwAuthenticateHeader` | Allow setting a `WWW-Authenticate` header in the `401 Unauthorized` response. See [the WWW-Authenticate header documentation](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/WWW-Authenticate) for more information.
The `realm` directive of the `WWW-Authenticate` header can be customized with the `wwwAuthenticateHeaderRealm` option. | false | No | -| `wwwAuthenticateHeaderRealm` | Realm name to set in the `WWW-Authenticate` header. This option is ineffective unless the `wwwAuthenticateHeader` option is set to `true`. | "" | No | - -### bindPassword - -When setting the `bindPassword`, you can reference a Kubernetes secret from the same namespace as the Middleware using the following URN format: - -```text -urn:k8s:secret:[secretName]:[key] -``` - -## Bind Mode vs Search Mode - -If no filter is specified in its configuration, the middleware runs in the default bind mode, -meaning it tries to make a bind request to the LDAP server with the credentials provided in the request headers. -If the bind succeeds, the middleware forwards the request, otherwise it returns a `401 Unauthorized` status code. - -If a filter query is specified in the middleware configuration, and the Authentication Source referenced has a `bindDN` -and a `bindPassword`, then the middleware runs in search mode. In this mode, a search query with the given filter is -issued to the LDAP server before trying to bind. If result of this search returns only 1 record, -it tries to issue a bind request with this record, otherwise it aborts a `401 Unauthorized` status code. - -{!traefik-for-business-applications.md!} diff --git a/docs/content/reference/routing-configuration/http/middlewares/oauth2-client-credentials.md b/docs/content/reference/routing-configuration/http/middlewares/oauth2-client-credentials.md deleted file mode 100644 index 80eecea88..000000000 --- a/docs/content/reference/routing-configuration/http/middlewares/oauth2-client-credentials.md +++ /dev/null @@ -1,255 +0,0 @@ ---- -title: 'OAuth 2.0 Client Credentials Authentication' -description: 'Traefik Hub API Gateway - The OAuth 2.0 Client Credentials Authentication middleware secures your applications using the client credentials flow' ---- - -!!! info "Traefik Hub Feature" - This middleware is available exclusively in [Traefik Hub](https://traefik.io/traefik-hub/). Learn more about [Traefik Hub's advanced features](https://doc.traefik.io/traefik-hub/api-gateway/intro). - -The OAuth 2.0 Client Credentials Authentication middleware allows Traefik Hub to secure routes using the OAuth 2.0 Client Credentials flow as described in the [RFC 6749](https://www.rfc-editor.org/rfc/rfc6749.html#section-4.4). -Access tokens can be cached using an external KV store. - -The OAuth Client Credentials Authentication middleware allows using Redis (or Sentinel) as persistent KV store to authorization access tokens -while they are valid. This reduces latency and the number of calls made to the authorization server. - ---- - -## Configuration Example - -```yaml tab="Middleware OAuth Client Credentials" -apiVersion: traefik.io/v1alpha1 -kind: Middleware -metadata: - name: test-client-creds -spec: - plugin: - oAuthClientCredentials: - url: https://tenant.auth0.com/oauth/token - clientID: urn:k8s:my-secret:my-secret:clientID - clientSecret: urn:k8s:my-secret:my-secret:clientSecret - audience: https://api.example.com - forwardHeaders: - Group: grp - Expires-At: exp - claims: Equals(`grp`, `admin`) -``` - -```yaml tab="Kubernetes Secret" -apiVersion: v1 -kind: Secret -type: Opaque -metadata: - name: my-secret -stringData: - clientID: my-oauth-client-name - clientSecret: mypasswd -``` - -## Configuration Options - -| Field | Description | Default | Required | -|:------|:--------------------------------------------------------------------------------------------|:--------|:---------| -| `audience` | Defines the audience configured in your authorization server.
The audience value is the base address of the resource being accessed, for example: https://api.example.com. | "" | Yes | -| `claims` | Defines the claims to validate in order to authorize the request.
The `claims` option can only be used with JWT-formatted token. (More information [here](#claims)) | "" | No | -| `clientConfig.tls.ca` | PEM-encoded certificate bundle or a URN referencing a secret containing the certificate bundle used to establish a TLS connection with the authorization server (More information [here](#clientconfig)) | "" | No | -| `clientConfig.tls.cert` | PEM-encoded certificate or a URN referencing a secret containing the certificate used to establish a TLS connection with the Vault server (More information [here](#clientconfig)) | "" | No | -| `clientConfig.tls.key` | PEM-encoded key or a URN referencing a secret containing the key used to establish a TLS connection with the Vault server. (More information [here](#clientconfig)) | "" | No | -| `clientConfig.tls.insecureSkipVerify` | Disables TLS certificate verification when communicating with the authorization server.
Useful for testing purposes but strongly discouraged for production. (More information [here](#clientconfig)) | "" | No | -| `clientConfig.timeoutSeconds` | Defines the time before giving up requests to the authorization server. | 5 | No | -| `clientConfig.maxRetries` | Defines the number of retries for requests to authorization server that fail. | 3 | No | -| `clientID` | Defines the unique client identifier for an account on the OpenID Connect provider, must be set when the `clientSecret` option is set.
More information [here](#storing-secret-values-in-kubernetes-secrets). | "" | Yes | -| `clientSecret` | Defines the unique client secret for an account on the OpenID Connect provider, must be set when the `clientID` option is set.
More information [here](#storing-secret-values-in-kubernetes-secrets). | "" | Yes | -| `forwardHeaders` | Defines the HTTP headers to add to requests and populates them with values extracted from the access token claims returned by the authorization server.
Claims to be forwarded that are not found in the JWT result in empty headers.
The `forwardHeaders` option can only be used with JWT-formatted token. | [] | No | -| `store.keyPrefix` | Defines the prefix of the key for the entries that store the sessions. | "" | No | -| `store.redis.endpoints` | Endpoints of the Redis instances to connect to (example: `redis.traefik-hub.svc.cluster.local:6379`) | "" | Yes | -| `store.redis.username` | The username Traefik Hub will use to connect to Redis | "" | No | -| `store.redis.password` | The password Traefik Hub will use to connect to Redis | "" | No | -| `store.redis.database` | The database Traefik Hub will use to sore information (default: `0`) | "" | No | -| `store.redis.cluster` | Enable Redis Cluster | "" | No | -| `store.redis.tls.caBundle` | Custom CA bundle | "" | No | -| `store.redis.tls.cert` | TLS certificate | "" | No | -| `store.redis.tls.key` | TLS | "" | No | -| `store.redis.tls.insecureSkipVerify` | Allow skipping the TLS verification | "" | No | -| `store.redis.sentinel.masterSet` | Name of the set of main nodes to use for main selection. Required when using Sentinel. | "" | No | -| `store.redis.sentinel.username` | Username to use for sentinel authentication (can be different from `username`) | "" | No | -| `store.redis.sentinel.password` | Password to use for sentinel authentication (can be different from `password`) | "" | No | -| `url` | Defines the authorization server URL (for example: `https://tenant.auth0.com/oauth/token`). | "" | Yes | -| `usernameClaim` | Defines the claim that will be evaluated to populate the `clientusername` in the access logs.
The `usernameClaim` option can only be used with JWT-formatted token.| "" | No | - -### Storing secret values in Kubernetes secrets - -It is possible to reference Kubernetes secrets defined in the same namespace as the Middleware. -The reference to a Kubernetes secret takes the form of a URN: - -```text -urn:k8s:secret:[name]:[valueKey] -``` - -### claims - -#### Syntax - -The following functions are supported in `claims`: - -| Function | Description | Example | -|-------------------|--------------------|-----------------| -| Equals | Validates the equality of the value in `key` with `value`. | Equals(\`grp\`, \`admin\`) | -| Prefix | Validates the value in `key` has the prefix of `value`. | Prefix(\`referrer\`, \`http://example.com\`) | -| Contains (string) | Validates the value in `key` contains `value`. | Contains(\`referrer\`, \`/foo/\`) | -| Contains (array) | Validates the `key` array contains the `value`. | Contains(\`areas\`, \`home\`) | -| SplitContains | Validates the value in `key` contains the `value` once split by the separator. | SplitContains(\`scope\`, \` \`, \`writer\`) | -| OneOf | Validates the `key` array contains one of the `values`. | OneOf(\`areas\`, \`office\`, \`lab\`) | - -All functions can be joined by boolean operands. The supported operands are: - -| Operand | Description | Example | -|---------|--------------------|-----------------| -| && | Compares two functions and returns true only if both evaluate to true. | Equals(\`grp\`, \`admin\`) && Equals(\`active\`, \`true\`) | -| \|\| | Compares two functions and returns true if either evaluate to true. | Equals(\`grp\`, \`admin\`) \|\| Equals(\`active\`, \`true\`) | -| ! | Returns false if the function is true, otherwise returns true. | !Equals(\`grp\`, \`testers\`) | - -All examples will return true for the following data structure: - -```json tab="JSON" -{ - "active": true, - "grp": "admin", - "scope": "reader writer deploy", - "referrer": "http://example.com/foo/bar", - "areas": [ - "office", - "home" - ] -} -``` - -#### Nested Claims - -Nested claims are supported by using a `.` between keys. For example: - -```bash tab="Key" -user.name -``` - -```json tab="Claims" -{ - "active": true, - "grp": "admin", - "scope": "reader writer deploy", - "referrer": "http://example.com/foo/bar", - "areas": [ - "office", - "home" - ], - "user" { - "name": "John Snow", - "status": "undead" - } -} -``` - -```bash tab="Result" -John Snow -``` - -!!! note "Handling keys that contain a '.'" - -If the `key` contains a dot, the dot can be escaped using `\.` - -!!! note "Handling a key that contains a '\'" - -If the `key` contains a `\`, it needs to be doubled `\\`. - -### clientConfig - -Defines the configuration used to connect the API Gateway to a Third Party Software such as an Identity Provider. - -#### `clientConfig.tls` - -##### Storing secret values in Kubernetes secrets - -When configuring the `tls.ca`, `tls.cert`, `tls.key`, it is possible to reference Kubernetes secrets defined in the same namespace as the Middleware. -The reference to a Kubernetes secret takes the form of a URN: - -```text -urn:k8s:secret:[name]:[valueKey] -``` - -```yaml tab="Middleware JWT" -apiVersion: traefik.io/v1alpha1 -kind: Middleware -metadata: - name: test-client-creds -spec: - plugin: - oAuthClientCredentials: - clientConfig: - tls: - ca: "urn:k8s:secret:tls:ca" - cert: "urn:k8s:secret:tls:cert" - key: "urn:k8s:secret:tls:key" - insecureSkipVerify: true -``` - -```yaml tab="Kubernetes TLS Secret" -apiVersion: v1 -kind: Secret -metadata: - name: tls -stringData: - ca: |- - -----BEGIN CERTIFICATE----- - MIIB9TCCAWACAQAwgbgxGTAXBgNVBAoMEFF1b1ZhZGlzIExpbWl0ZWQxHDAaBgNV - BAsME0RvY3VtZW50IERlcGFydG1lbnQxOTA3BgNVBAMMMFdoeSBhcmUgeW91IGRl - Y29kaW5nIG1lPyAgVGhpcyBpcyBvbmx5IGEgdGVzdCEhITERMA8GA1UEBwwISGFt - aWx0b24xETAPBgNVBAgMCFBlbWJyb2tlMQswCQYDVQQGEwJCTTEPMA0GCSqGSIb3 - DQEJARYAMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCJ9WRanG/fUvcfKiGl - EL4aRLjGt537mZ28UU9/3eiJeJznNSOuNLnF+hmabAu7H0LT4K7EdqfF+XUZW/2j - RKRYcvOUDGF9A7OjW7UfKk1In3+6QDCi7X34RE161jqoaJjrm/T18TOKcgkkhRzE - apQnIDm0Ea/HVzX/PiSOGuertwIDAQABMAsGCSqGSIb3DQEBBQOBgQBzMJdAV4QP - Awel8LzGx5uMOshezF/KfP67wJ93UW+N7zXY6AwPgoLj4Kjw+WtU684JL8Dtr9FX - ozakE+8p06BpxegR4BR3FMHf6p+0jQxUEAkAyb/mVgm66TyghDGC6/YkiKoZptXQ - 98TwDIK/39WEB/V607As+KoYazQG8drorw== - -----END CERTIFICATE----- - cert: |- - -----BEGIN CERTIFICATE----- - MIIB9TCCAWACAQAwgbgxGTAXBgNVBAoMEFF1b1ZhZGlzIExpbWl0ZWQxHDAaBgNV - BAsME0RvY3VtZW50IERlcGFydG1lbnQxOTA3BgNVBAMMMFdoeSBhcmUgeW91IGRl - Y29kaW5nIG1lPyAgVGhpcyBpcyBvbmx5IGEgdGVzdCEhITERMA8GA1UEBwwISGFt - aWx0b24xETAPBgNVBAgMCFBlbWJyb2tlMQswCQYDVQQGEwJCTTEPMA0GCSqGSIb3 - DQEJARYAMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCJ9WRanG/fUvcfKiGl - EL4aRLjGt537mZ28UU9/3eiJeJznNSOuNLnF+hmabAu7H0LT4K7EdqfF+XUZW/2j - RKRYcvOUDGF9A7OjW7UfKk1In3+6QDCi7X34RE161jqoaJjrm/T18TOKcgkkhRzE - apQnIDm0Ea/HVzX/PiSOGuertwIDAQABMAsGCSqGSIb3DQEBBQOBgQBzMJdAV4QP - Awel8LzGx5uMOshezF/KfP67wJ93UW+N7zXY6AwPgoLj4Kjw+WtU684JL8Dtr9FX - ozakE+8p06BpxegR4BR3FMHf6p+0jQxUEAkAyb/mVgm66TyghDGC6/YkiKoZptXQ - 98TwDIK/39WEB/V607As+KoYazQG8drorw== - -----END CERTIFICATE----- - key: |- - -----BEGIN EC PRIVATE KEY----- - MHcCAQEEIC8CsJ/B115S+JtR1/l3ZQwKA3XdXt9zLqusF1VXc/KloAoGCCqGSM49 - AwEHoUQDQgAEpwUmRIZHFt8CdDHYm1ikScCScd2q6QVYXxJu+G3fQZ78ScGtN7fu - KXMnQqVjXVRAr8qUY8yipVKuMCepnPXScQ== - -----END EC PRIVATE KEY----- -``` - -### store.redis - -Connection parameters to your [Redis](https://redis.io/ "Link to website of Redis") server are attached to your Middleware deployment. - -The following Redis modes are supported: - -- Single instance mode -- [Redis Cluster](https://redis.io/docs/management/scaling "Link to official Redis documentation about Redis Cluster mode") -- [Redis Sentinel](https://redis.io/docs/management/sentinel "Link to official Redis documentation about Redis Sentinel mode") - -!!! info - - If you use Redis in single instance mode or Redis Sentinel, you can configure the `database` field. - This value won't be taken into account if you use Redis Cluster (only database `0` is available). - - In this case, a warning is displayed, and the value is ignored. - -For more information about Redis, we recommend the [official Redis documentation](https://redis.io/docs/ "Link to official Redis documentation"). - -{!traefik-for-business-applications.md!} diff --git a/docs/content/reference/routing-configuration/http/middlewares/oauth2-token-introspection.md b/docs/content/reference/routing-configuration/http/middlewares/oauth2-token-introspection.md deleted file mode 100644 index 8633abcbb..000000000 --- a/docs/content/reference/routing-configuration/http/middlewares/oauth2-token-introspection.md +++ /dev/null @@ -1,209 +0,0 @@ ---- -title: 'OAuth 2.0 Token Introspection Authentication' -description: 'Traefik Hub API Gateway - OAuth 2.0 Token Introspection allows to retrieve metadata about an access token from an OAuth 2.0 server.' ---- - -!!! info "Traefik Hub Feature" - This middleware is available exclusively in [Traefik Hub](https://traefik.io/traefik-hub/). Learn more about [Traefik Hub's advanced features](https://doc.traefik.io/traefik-hub/api-gateway/intro). - -OAuth 2.0 Token Introspection allows Traefik Hub to retrieve metadata about an access token from an OAuth 2.0 server with the Token Introspection extension. - -The metadata can be used to restrict the access to applications. For more information please refer to the [RFC](https://tools.ietf.org/html/rfc7662). - ---- - -## Configuration Example - -```yaml tab="Middleware OAuth Token Introspection" -apiVersion: traefik.io/v1alpha1 -kind: Middleware -metadata: - name: test-oauth-intro -spec: - plugin: - oAuthIntrospection: - tokenSource: - header: Authorization - headerAuthScheme: Bearer - clientConfig: - url: "https://YOUR-KEYCLOAK-ADDRESS/realms/YOUR-REALM/protocol/openid-connect/token/introspect" - headers: - Authorization: Basic ZXhhbXBsZTpleGFtcGxl # echo -n "$CLIENT_ID:$CLIENT_SECRET" | base64 - tokenTypeHint: access_token - forwardHeaders: - Group: grp - Expires-At: exp - claims: Equals(`grp`, `admin`) -``` - -## Configuration Options - -| Field | Description | Default | Required | -|:------|:------------|:--------|:---------| -| `claims` | Defines the claims to validate in order to authorize the request.
The `claims` option can only be used with JWT-formatted token. (More information [here](#claims)) | "" | No | -| `clientConfig.url` | Defines the introspection endpoint URL. It must include the scheme and path. | "" | Yes | -| `clientConfig.headers` | Defines the headers to send in every introspection request. Values can be plain strings or a valid [Go template](https://pkg.go.dev/text/template).
Currently, a variable of type [`Request`](https://pkg.go.dev/net/http#Request) corresponding to the request being introspected is accessible in templates. | "" | No | -| `clientConfig.tokenTypeHint` | Defines the type of token being introspected, sent as a hint to the introspection server.
Please refer to the [official documentation](https://tools.ietf.org/html/rfc7662) for more details. | "" | No | -| `clientConfig.tls.ca` | PEM-encoded certificate bundle or a URN referencing a secret containing the certificate bundle used to establish a TLS connection with the authorization server (More information [here](#clientconfig)) | "" | No | -| `clientConfig.tls.cert` | PEM-encoded certificate or a URN referencing a secret containing the certificate used to establish a TLS connection with the Vault server (More information [here](#clientconfig)) | "" | No | -| `clientConfig.tls.key` | PEM-encoded key or a URN referencing a secret containing the key used to establish a TLS connection with the Vault server. (More information [here](#clientconfig)) | "" | No | -| `clientConfig.tls.insecureSkipVerify` | Disables TLS certificate verification when communicating with the authorization server.
Useful for testing purposes but strongly discouraged for production. (More information [here](#clientconfig)) | "" | No | -| `clientConfig.timeoutSeconds` | Defines the time before giving up requests to the authorization server. | 5 | No | -| `clientConfig.maxRetries` | Defines the number of retries for requests to authorization server that fail. | 3 | No | -| `forwardAuthorization` | Defines whether the authorization header will be forwarded or stripped from a request after it has been approved by the middleware. | false | No | -| `forwardHeaders` | Defines the HTTP headers to add to requests and populates them with values extracted from the access token claims returned by the authorization server.
Claims to be forwarded that are not found in the JWT result in empty headers.
The `forwardHeaders` option can only be used with JWT-formatted token. | [] | No | -| `tokenSource.header` | Defines the header name containing the secret sent by the client.
At least one `tokenSource`option must be set.| "" | No | -| `tokenSource.headerAuthScheme` | Defines the scheme when using `Authorization` as header name.
Check out the `Authorization` header [documentation](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Authorization#syntax).
At least one `tokenSource`option must be set. | "" | No | -| `tokenSource.query` | Defines the query parameter name containing the secret sent by the client.
At least one `tokenSource`option must be set.| "" | No | -| `tokenSource.cookie` | Defines the cookie name containing the secret sent by the client.
At least one `tokenSource`option must be set.| "" | No | -| `usernameClaim` | Defines the claim that will be evaluated to populate the `clientusername` in the access logs.
The `usernameClaim` option can only be used with JWT-formatted token.| "" | No | - -### claims - -#### Syntax - -The following functions are supported in `claims`: - -| Function | Description | Example | -|-------------------|--------------------|-----------------| -| Equals | Validates the equality of the value in `key` with `value`. | Equals(\`grp\`, \`admin\`) | -| Prefix | Validates the value in `key` has the prefix of `value`. | Prefix(\`referrer\`, \`http://example.com\`) | -| Contains (string) | Validates the value in `key` contains `value`. | Contains(\`referrer\`, \`/foo/\`) | -| Contains (array) | Validates the `key` array contains the `value`. | Contains(\`areas\`, \`home\`) | -| SplitContains | Validates the value in `key` contains the `value` once split by the separator. | SplitContains(\`scope\`, \` \`, \`writer\`) | -| OneOf | Validates the `key` array contains one of the `values`. | OneOf(\`areas\`, \`office\`, \`lab\`) | - -All functions can be joined by boolean operands. The supported operands are: - -| Operand | Description | Example | -|---------|--------------------|-----------------| -| && | Compares two functions and returns true only if both evaluate to true. | Equals(\`grp\`, \`admin\`) && Equals(\`active\`, \`true\`) | -| \|\| | Compares two functions and returns true if either evaluate to true. | Equals(\`grp\`, \`admin\`) \|\| Equals(\`active\`, \`true\`) | -| ! | Returns false if the function is true, otherwise returns true. | !Equals(\`grp\`, \`testers\`) | - -All examples will return true for the following data structure: - -```json tab="JSON" -{ - "active": true, - "grp": "admin", - "scope": "reader writer deploy", - "referrer": "http://example.com/foo/bar", - "areas": [ - "office", - "home" - ] -} -``` - -#### Nested Claims - -Nested claims are supported by using a `.` between keys. For example: - -```bash tab="Key" -user.name -``` - -```json tab="Claims" -{ - "active": true, - "grp": "admin", - "scope": "reader writer deploy", - "referrer": "http://example.com/foo/bar", - "areas": [ - "office", - "home" - ], - "user" { - "name": "John Snow", - "status": "undead" - } -} -``` - -```bash tab="Result" -John Snow -``` - -!!! note "Handling keys that contain a '.'" - -If the `key` contains a dot, the dot can be escaped using `\.` - -!!! note "Handling a key that contains a '\'" - -If the `key` contains a `\`, it needs to be doubled `\\`. - -### clientConfig - -Defines the configuration used to connect the API Gateway to a Third Party Software such as an Identity Provider. - -#### `clientConfig.tls` - -##### Storing secret values in Kubernetes secrets - -When configuring the `tls.ca`, `tls.cert`, `tls.key`, it is possible to reference Kubernetes secrets defined in the same namespace as the Middleware. -The reference to a Kubernetes secret takes the form of a URN: - -```text -urn:k8s:secret:[name]:[valueKey] -``` - -```yaml tab="Middleware JWT" -apiVersion: traefik.io/v1alpha1 -kind: Middleware -metadata: - name: test-oauth-intro -spec: - plugin: - oAuthIntrospection: - clientConfig: - tls: - ca: "urn:k8s:secret:tls:ca" - cert: "urn:k8s:secret:tls:cert" - key: "urn:k8s:secret:tls:key" - insecureSkipVerify: true -``` - -```yaml tab="Kubernetes TLS Secret" -apiVersion: v1 -kind: Secret -metadata: - name: tls -stringData: - ca: |- - -----BEGIN CERTIFICATE----- - MIIB9TCCAWACAQAwgbgxGTAXBgNVBAoMEFF1b1ZhZGlzIExpbWl0ZWQxHDAaBgNV - BAsME0RvY3VtZW50IERlcGFydG1lbnQxOTA3BgNVBAMMMFdoeSBhcmUgeW91IGRl - Y29kaW5nIG1lPyAgVGhpcyBpcyBvbmx5IGEgdGVzdCEhITERMA8GA1UEBwwISGFt - aWx0b24xETAPBgNVBAgMCFBlbWJyb2tlMQswCQYDVQQGEwJCTTEPMA0GCSqGSIb3 - DQEJARYAMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCJ9WRanG/fUvcfKiGl - EL4aRLjGt537mZ28UU9/3eiJeJznNSOuNLnF+hmabAu7H0LT4K7EdqfF+XUZW/2j - RKRYcvOUDGF9A7OjW7UfKk1In3+6QDCi7X34RE161jqoaJjrm/T18TOKcgkkhRzE - apQnIDm0Ea/HVzX/PiSOGuertwIDAQABMAsGCSqGSIb3DQEBBQOBgQBzMJdAV4QP - Awel8LzGx5uMOshezF/KfP67wJ93UW+N7zXY6AwPgoLj4Kjw+WtU684JL8Dtr9FX - ozakE+8p06BpxegR4BR3FMHf6p+0jQxUEAkAyb/mVgm66TyghDGC6/YkiKoZptXQ - 98TwDIK/39WEB/V607As+KoYazQG8drorw== - -----END CERTIFICATE----- - cert: |- - -----BEGIN CERTIFICATE----- - MIIB9TCCAWACAQAwgbgxGTAXBgNVBAoMEFF1b1ZhZGlzIExpbWl0ZWQxHDAaBgNV - BAsME0RvY3VtZW50IERlcGFydG1lbnQxOTA3BgNVBAMMMFdoeSBhcmUgeW91IGRl - Y29kaW5nIG1lPyAgVGhpcyBpcyBvbmx5IGEgdGVzdCEhITERMA8GA1UEBwwISGFt - aWx0b24xETAPBgNVBAgMCFBlbWJyb2tlMQswCQYDVQQGEwJCTTEPMA0GCSqGSIb3 - DQEJARYAMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCJ9WRanG/fUvcfKiGl - EL4aRLjGt537mZ28UU9/3eiJeJznNSOuNLnF+hmabAu7H0LT4K7EdqfF+XUZW/2j - RKRYcvOUDGF9A7OjW7UfKk1In3+6QDCi7X34RE161jqoaJjrm/T18TOKcgkkhRzE - apQnIDm0Ea/HVzX/PiSOGuertwIDAQABMAsGCSqGSIb3DQEBBQOBgQBzMJdAV4QP - Awel8LzGx5uMOshezF/KfP67wJ93UW+N7zXY6AwPgoLj4Kjw+WtU684JL8Dtr9FX - ozakE+8p06BpxegR4BR3FMHf6p+0jQxUEAkAyb/mVgm66TyghDGC6/YkiKoZptXQ - 98TwDIK/39WEB/V607As+KoYazQG8drorw== - -----END CERTIFICATE----- - key: |- - -----BEGIN EC PRIVATE KEY----- - MHcCAQEEIC8CsJ/B115S+JtR1/l3ZQwKA3XdXt9zLqusF1VXc/KloAoGCCqGSM49 - AwEHoUQDQgAEpwUmRIZHFt8CdDHYm1ikScCScd2q6QVYXxJu+G3fQZ78ScGtN7fu - KXMnQqVjXVRAr8qUY8yipVKuMCepnPXScQ== - -----END EC PRIVATE KEY----- -``` - -{!traefik-for-business-applications.md!} diff --git a/docs/content/reference/routing-configuration/http/middlewares/oidc.md b/docs/content/reference/routing-configuration/http/middlewares/oidc.md deleted file mode 100644 index 120e79bf9..000000000 --- a/docs/content/reference/routing-configuration/http/middlewares/oidc.md +++ /dev/null @@ -1,430 +0,0 @@ ---- -title: 'OpenID Connect Authentication' -description: 'Traefik Hub API Gateway - The OIDC Authentication middleware secures your applications by delegating the authentication to an external provider.' ---- - -!!! info "Traefik Hub Feature" - This middleware is available exclusively in [Traefik Hub](https://traefik.io/traefik-hub/). Learn more about [Traefik Hub's advanced features](https://doc.traefik.io/traefik-hub/api-gateway/intro). - -The OIDC Authentication middleware secures your applications by delegating the authentication to an external provider - ---- - -## Configuration Example - -```yaml tab="Middleware OIDC" -apiVersion: traefik.io/v1alpha1 -kind: Middleware -metadata: - name: test-oidc - namespace: whoami -spec: - plugin: - oidc: - issuer: "https://tenant.auth0.com/realms/myrealm" - redirectUrl: "/callback" - clientID: "urn:k8s:secret:my-secret:clientId" - clientSecret: "urn:k8s:secret:my-secret:clientSecret" - session: - name: customsessioncookiename - sliding: false - refresh: false - expiry: 10 - sameSite: none - httpOnly: false - secure: true - stateCookie: - name: customstatecookiename - maxAge: 10 - sameSite: none - httpOnly: true - secure: true - forwardHeaders: - Group: grp - Expires-At: exp - claims: Equals(`grp`, `admin`) - csrf: {} -``` - -```yaml tab="Kubernetes Secret" -apiVersion: v1 -kind: Secret -metadata: - name: my-secret -stringData: - clientID: my-oidc-client-name - clientSecret: mysecret -``` - -## Configuration Options - -| Field | Description | Default | Required | -|:------|:------------|:--------|:---------| -| `issuer` | Defines the URL to the OpenID Connect provider (for example, `https://accounts.google.com`).
It should point to the server which provides the OpenID Connect configuration. | "" | Yes | -| `redirectUrl` | Defines the URL used by the OpenID Connect provider to redirect back to the middleware once the authorization is complete. (More information [here](#redirecturl)) | "" | Yes | -| `clientID` | Defines the unique client identifier for an account on the OpenID Connect provider, must be set when the `clientSecret` option is set. (More information [here](#clientid-clientsecret)) | "" | Yes | -| `clientSecret` | Defines the unique client secret for an account on the OpenID Connect provider, must be set when the `clientID` option is set. (More information [here](#clientid-clientsecret)) | "" | Yes | -| `claims` | Defines the claims to validate in order to authorize the request.
The `claims` option can only be used with JWT-formatted token. (More information [here](#claims)) | "" | No | -| `usernameClaim` | Defines the claim that will be evaluated to populate the `clientusername` in the access logs.
The `usernameClaim` option can only be used with JWT-formatted token.| "" | No | -| `forwardHeaders` | Defines the HTTP headers to add to requests and populates them with values extracted from the access token claims returned by the authorization server.
Claims to be forwarded that are not found in the JWT result in empty headers.
The `forwardHeaders` option can only be used with JWT-formatted token. | [] | No | -| `clientConfig.tls.ca` | PEM-encoded certificate bundle or a URN referencing a secret containing the certificate bundle used to establish a TLS connection with the authorization server (More information [here](#clientconfig)) | "" | No | -| `clientConfig.tls.cert` | PEM-encoded certificate or a URN referencing a secret containing the certificate used to establish a TLS connection with the Vault server (More information [here](#clientconfig)) | "" | No | -| `clientConfig.tls.key` | PEM-encoded key or a URN referencing a secret containing the key used to establish a TLS connection with the Vault server. (More information [here](#clientconfig)) | "" | No | -| `clientConfig.tls.insecureSkipVerify` | Disables TLS certificate verification when communicating with the authorization server.
Useful for testing purposes but strongly discouraged for production. (More information [here](#clientconfig)) | "" | No | -| `clientConfig.timeoutSeconds` | Defines the time before giving up requests to the authorization server. | 5 | No | -| `clientConfig.maxRetries` | Defines the number of retries for requests to authorization server that fail. | 3 | No | -| `pkce` | Defines the Proof Key for Code Exchange as described in [RFC 7636](https://datatracker.ietf.org/doc/html/rfc7636). | false | No | -| `discoveryParams` | A map of arbitrary query parameters to be added to the openid-configuration well-known URI during the discovery mechanism. | "" | No | -| `scopes` | The scopes to request. Must include `openid`. | openid | No | -| `authParams` | A map of the arbitrary query parameters to be passed to the Authentication Provider.
When a `prompt` key is set to an empty string in the AuthParams,the prompt parameter is not added to the OAuth2 authorization URL Which means the user won't be prompted for consent.| "" | No | -| `disableLogin` | Disables redirections to the authentication provider
This can be useful for protecting APIs where redirecting to a login page is undesirable. | false | No | -| `loginUrl` | Defines the URL used to start authorization when needed.
All other requests that are not already authorized will return a 401 Unauthorized. When left empty, all requests can start authorization.
It can be a path (`/login` for example), a host and a path (`example.com/login`) or a complete URL (`https://example.com/login`).
Only `http` and `https` schemes are supported.| "" | No | -| `logoutUrl` |Defines the URL on which the session should be deleted in order to log users out.
It can be a path (`/logout` for example), a host and a path (`example.com/logout`) or a complete URL (`https://example.com/logout`).
Only `http` and `https` schemes are supported.| "" | No | -| `postLoginRedirectUrl` |If set and used in conjunction with `loginUrl`, the middleware will redirect to this URL after successful login.
It can be a path (`/after/login` for example), a host and a path (`example.com/after/login`) or a complete URL (`https://example.com/after/login`).
Only `http` and `https` schemes are supported. | "" | No | -| `postLogoutRedirectUrl` | If set and used in conjunction with `logoutUrl`, the middleware will redirect to this URL after logout.
It can be a path (`/after/logout` for example), a host and a path (`example.com/after/logout`) or a complete URL (`https://example.com/after/logout`).
Only `http` and `https` schemes are supported. | "" | No | -| `backchannelLogoutUrl` | Defines the URL called by the OIDC provider when a user logs out (see https://openid.net/specs/openid-connect-rpinitiated-1_0.html#OpenID.BackChannel).
It can be a path (`/backchannel-logout` for example), a host and a path (`example.com/backchannel-logout`) or a complete URL (`https://example.com/backchannel-logout`).
Only `http` and `https` schemes are supported.
This feature is currently in an experimental state and has been tested exclusively with the Keycloak OIDC provider. | "" | No | -| `backchannelLogoutSessionsRequired` | This specifies whether the OIDC provider includes the sid (session ID) Claim in the Logout Token to identify the user session (see https://openid.net/specs/openid-connect-backchannel-1_0.html#BCRegistration).
If omitted, the default value is false.
This feature is currently in an experimental state and has been tested exclusively with the Keycloak OIDC provider. | false | No | -| `stateCookie.name` | Defines the name of the state cookie. |"`MIDDLEWARE_NAME`-state" | No | -| `stateCookie.path` | Defines the URL path that must exist in the requested URL in order to send the Cookie header.
The `%x2F` ('/') character is considered a directory separator, and subdirectories will match as well.
For example, if `stateCookie.path` is set to `/docs`, these paths will match: `/docs`,`/docs/web/`,`/docs/web/http`.| "/" | No | -| `stateCookie.domain` | Defines the hosts that are allowed to receive the cookie.
If specified, then subdomains are always included.
For example, if it is set to `example.com`, then cookies are included on subdomains like `api.example.com`. | "" | No | -| `stateCookie.maxAge` |Defines the number of seconds after which the state cookie should expire.
A zero or negative number will expire the cookie immediately. | 600 | No | -| `stateCookie.sameSite` | Informsbrowsers how they should handle the state cookie on cross-site requests.
Setting it to `lax` or `strict` can provide some protection against cross-site request forgery attacks ([CSRF](https://developer.mozilla.org/en-US/docs/Glossary/CSRF)).
More information [here](#samesite---accepted-values). | lax | No | -| `stateCookie.httpOnly` | Forbids JavaScript from accessing the cookie.
For example, through the `Document.cookie` property, the `XMLHttpRequest` API, or the `Request` API.
This mitigates attacks against cross-site scripting ([XSS](https://developer.mozilla.org/en-US/docs/Glossary/XSS)). | true | No | -| `stateCookie.secure` | Defines whether the state cookie is only sent to the server when a request is made with the `https` scheme. | false | No | -| `session.name` | The name of the session cookie. |"`MIDDLEWARE_NAME`-session"| No | -| `session.path` | Defines the URL path that must exist in the requested URL in order to send the Cookie header.
The `%x2F` ('/'') character is considered a directory separator, and subdirectories will match as well.
For example, if `stateCookie.path` is set to `/docs`, these paths will match: `/docs`,`/docs/web/`,`/docs/web/http`.| "/" | No | -| `session.domain` | Specifies the hosts that are allowed to receive the cookie. If specified, then subdomains are always included. If specified, then subdomains are always included.
For example, if it is set to `example.com`, then cookies are included on subdomains like `api.example.com`.| "" | No | -| `session.expiry` | Number of seconds after which the session should expire. A zero or negative number is **prohibited**. | 86400 (24h) | No | -| `session.sliding` | Forces the middleware to renew the session cookie each time an authenticated request is received. | true | No | -| `session.refresh` | Enables the access token refresh when it expires. | true | No | -| `session.sameSite` | Inform browsers how they should handle the session cookie on cross-site requests.
Setting it to `lax` or `strict` can provide some protection against cross-site request forgery attacks ([CSRF](https://developer.mozilla.org/en-US/docs/Glossary/CSRF)).
More information [here](#samesite---accepted-values). | lax | No | -| `session.httpOnly` | Forbids JavaScript from accessing the cookie.
For example, through the `Document.cookie` property, the `XMLHttpRequest` API, or the `Request` API.
This mitigates attacks against cross-site scripting ([XSS](https://developer.mozilla.org/en-US/docs/Glossary/XSS)). | true | No | -| `session.secure` | Defines whether the session cookie is only sent to the server when a request is made with the `https` scheme. | false | No | -| `session.store.redis.endpoints` | Endpoints of the Redis instances to connect to (example: `redis.traefik-hub.svc.cluster.local:6379`) | "" | Yes | -| `session.store.redis.username` | The username Traefik Hub will use to connect to Redis | "" | No | -| `session.store.redis.password` | The password Traefik Hub will use to connect to Redis | "" | No | -| `session.store.redis.database` | The database Traefik Hub will use to sore information (default: `0`) | "" | No | -| `session.store.redis.cluster` | Enable Redis Cluster | "" | No | -| `session.store.redis.tls.caBundle` | Custom CA bundle | "" | No | -| `session.store.redis.tls.cert` | TLS certificate | "" | No | -| `session.store.redis.tls.key` | TLS key | "" | No | -| `session.store.redis.tls.insecureSkipVerify` | Allow skipping the TLS verification | "" | No | -| `session.store.redis.sentinel.masterSet` | Name of the set of main nodes to use for main selection. Required when using Sentinel. | "" | No | -| `session.store.redis.sentinel.username` | Username to use for sentinel authentication (can be different from `username`) | "" | No | -| `session.store.redis.sentinel.password` | Password to use for sentinel authentication (can be different from `password`) | "" | No | -| `csrf` | When enabled, a CSRF cookie, named `traefikee-csrf-token`, is bound to the OIDC session to protect service from CSRF attacks.
It is based on the [Signed Double Submit Cookie](https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#signed-double-submit-cookie) implementation as defined by the OWASP Foundation.
Moreinformation [here](#csrf). | "" | No | -| `csrf.secure` | Defines whether the CSRF cookie is only sent to the server when a request is made with the `https` scheme. | false | No | -| `csrf.headerName` | Defines the name of the header used to send the CSRF token value received previously in the CSRF cookie. | TraefikHub-Csrf-Token | No | - -### redirectUrl - -#### Add specific rule on the IngressRoute - -The URL informs the OpenID Connect provider how to return to the middleware. -If the router rule is accepting all paths on a domain, no extra work is needed. -If the router rule is specific about the paths allowed, the path set in this option should be included. - -```yaml tab="Defining specific rule for redirectUrl" -apiVersion: traefik.io/v1alpha1 -kind: IngressRoute -metadata: - name: whoami -spec: - entryPoints: - - web - - websecure - routes: - # Rules to match the loginUrl and redirectUrl can be added into - # your current router. - - match: Path(`/myapi`) || Path(`/login`) || Path(`/callback`) - kind: Rule - middlewares: - - name: test-oidc -``` - -This URL will not be passed to the upstream application, but rather handled by the middleware itself. -The chosen URL should therefore not conflict with any URLs needed by the upstream application. - -This URL sometimes needs to be set in the OpenID Connect Provider's configuration as well (like for Google Accounts for example). - -It can be the absolute URL, relative to the protocol (inherits the request protocol), or relative to the domain (inherits the request domain and protocol). -See the following examples. - -#### Inherit the Protocol and Domain from the Request and Uses the Redirecturl’s Path - -| Request URL | RedirectURL| Result | -|:------------|:-----------|:-------| -| `http://expl.co` | `/cback` | `http://expl.co/cback` | - -#### Inherit the Protocol from the Request and Uses the Redirecturl’s Domain and Path - -| Request URL | RedirectURL| Result | -|:------------|:-----------|:-------| -| `https://scur.co` | `expl.co/cback`| `https://expl.co/cback` | - -#### Replace the Request URL with the Redirect URL since It Is an Absolute URL - -| Request URL | RedirectURL| Result | -|:------------|:-----------|:-------| -| `https://scur.co` | `http://expl.co/cback` | `http://expl.co/cback` | - -!!! note "Supported Schemes" - - Only `http` and `https` schemes are supported. - -```yaml tab="Defining the redirectUrl" -apiVersion: traefik.io/v1alpha1 -kind: Middleware -metadata: - name: test-oidc -spec: - plugin: - oidc: - issuer: "https://tenant.auth0.com/realms/myrealm" - redirectUrl: "/callback" - clientID: my-oidc-client-name - clientSecret: mysecret -``` - -### clientID, clientSecret - -#### Storing secret values in Kubernetes secrets - -When configuring the `clientID` and the `clientSecret`, it is possible to reference Kubernetes secrets defined in the same namespace as the Middleware. -The reference to a Kubernetes secret takes the form of a URN: - -```text -urn:k8s:secret:[name]:[valueKey] -``` - -### claims - -#### Syntax - -The following functions are supported in `claims`: - -| Function | Description | Example | -|-------------------|--------------------|-----------------| -| Equals | Validates the equality of the value in `key` with `value`. | Equals(\`grp\`, \`admin\`) | -| Prefix | Validates the value in `key` has the prefix of `value`. | Prefix(\`referrer\`, \`http://example.com\`) | -| Contains (string) | Validates the value in `key` contains `value`. | Contains(\`referrer\`, \`/foo/\`) | -| Contains (array) | Validates the `key` array contains the `value`. | Contains(\`areas\`, \`home\`) | -| SplitContains | Validates the value in `key` contains the `value` once split by the separator. | SplitContains(\`scope\`, \` \`, \`writer\`) | -| OneOf | Validates the `key` array contains one of the `values`. | OneOf(\`areas\`, \`office\`, \`lab\`) | - -All functions can be joined by boolean operands. The supported operands are: - -| Operand | Description | Example | -|---------|--------------------|-----------------| -| && | Compares two functions and returns true only if both evaluate to true. | Equals(\`grp\`, \`admin\`) && Equals(\`active\`, \`true\`) | -| \|\| | Compares two functions and returns true if either evaluate to true. | Equals(\`grp\`, \`admin\`) \|\| Equals(\`active\`, \`true\`) | -| ! | Returns false if the function is true, otherwise returns true. | !Equals(\`grp\`, \`testers\`) | - -All examples will return true for the following data structure: - -```json tab="JSON" -{ - "active": true, - "grp": "admin", - "scope": "reader writer deploy", - "referrer": "http://example.com/foo/bar", - "areas": [ - "office", - "home" - ] -} -``` - -#### Nested Claims - -Nested claims are supported by using a `.` between keys. For example: - -```bash tab="Key" -user.name -``` - -```json tab="Claims" -{ - "active": true, - "grp": "admin", - "scope": "reader writer deploy", - "referrer": "http://example.com/foo/bar", - "areas": [ - "office", - "home" - ], - "user" { - "name": "John Snow", - "status": "undead" - } -} -``` - -```bash tab="Result" -John Snow -``` - -!!! note "Handling keys that contain a '.'" - -If the `key` contains a dot, the dot can be escaped using `\.` - -!!! note "Handling a key that contains a '\'" - -If the `key` contains a `\`, it needs to be doubled `\\`. - -!!! note "Access Token and ID Token claims" - - The first argument of the function, which represents the key to look for in the token claims, can be prefixed to specify which of the two kinds of token is inspected. - Possible prefix values are `id_token.` and `access_token.`. If no prefix is specified, it defaults to the ID token. - - | Example | Description | - | ----------------------------------------- | ------------------------------------------------------------------------------ | - | Equals(\`id_token.grp\`, \`admin\`) | Checks if the value of claim `grp` in the ID token is `admin`. | - | Prefix(\`access_token.referrer\`, \`http://example.com\`) | Checks if the value of claim `referrer` in the access token is prefixed by `http://example.com\`.| - | OneOf(\`areas\`, \`office\`, \`lab\`) | Checks if the value of claim `areas` in the ID token is `office` or `labs`. | - -### clientConfig - -Defines the configuration used to connect the API Gateway to a Third Party Software such as an Identity Provider. - -#### `clientConfig.tls` - -##### Storing secret values in Kubernetes secrets - -When configuring the `tls.ca`, `tls.cert`, `tls.key`, it is possible to reference Kubernetes secrets defined in the same namespace as the Middleware. -The reference to a Kubernetes secret takes the form of a URN: - -```text -urn:k8s:secret:[name]:[valueKey] -``` - -```yaml tab="Middleware JWT" -apiVersion: traefik.io/v1alpha1 -kind: Middleware -metadata: - name: test-oidc -spec: - plugin: - oidc: - clientConfig: - tls: - ca: "urn:k8s:secret:tls:ca" - cert: "urn:k8s:secret:tls:cert" - key: "urn:k8s:secret:tls:key" - insecureSkipVerify: true -``` - -```yaml tab="Kubernetes TLS Secret" -apiVersion: v1 -kind: Secret -metadata: - name: tls -stringData: - ca: |- - -----BEGIN CERTIFICATE----- - MIIB9TCCAWACAQAwgbgxGTAXBgNVBAoMEFF1b1ZhZGlzIExpbWl0ZWQxHDAaBgNV - BAsME0RvY3VtZW50IERlcGFydG1lbnQxOTA3BgNVBAMMMFdoeSBhcmUgeW91IGRl - Y29kaW5nIG1lPyAgVGhpcyBpcyBvbmx5IGEgdGVzdCEhITERMA8GA1UEBwwISGFt - aWx0b24xETAPBgNVBAgMCFBlbWJyb2tlMQswCQYDVQQGEwJCTTEPMA0GCSqGSIb3 - DQEJARYAMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCJ9WRanG/fUvcfKiGl - EL4aRLjGt537mZ28UU9/3eiJeJznNSOuNLnF+hmabAu7H0LT4K7EdqfF+XUZW/2j - RKRYcvOUDGF9A7OjW7UfKk1In3+6QDCi7X34RE161jqoaJjrm/T18TOKcgkkhRzE - apQnIDm0Ea/HVzX/PiSOGuertwIDAQABMAsGCSqGSIb3DQEBBQOBgQBzMJdAV4QP - Awel8LzGx5uMOshezF/KfP67wJ93UW+N7zXY6AwPgoLj4Kjw+WtU684JL8Dtr9FX - ozakE+8p06BpxegR4BR3FMHf6p+0jQxUEAkAyb/mVgm66TyghDGC6/YkiKoZptXQ - 98TwDIK/39WEB/V607As+KoYazQG8drorw== - -----END CERTIFICATE----- - cert: |- - -----BEGIN CERTIFICATE----- - MIIB9TCCAWACAQAwgbgxGTAXBgNVBAoMEFF1b1ZhZGlzIExpbWl0ZWQxHDAaBgNV - BAsME0RvY3VtZW50IERlcGFydG1lbnQxOTA3BgNVBAMMMFdoeSBhcmUgeW91IGRl - Y29kaW5nIG1lPyAgVGhpcyBpcyBvbmx5IGEgdGVzdCEhITERMA8GA1UEBwwISGFt - aWx0b24xETAPBgNVBAgMCFBlbWJyb2tlMQswCQYDVQQGEwJCTTEPMA0GCSqGSIb3 - DQEJARYAMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCJ9WRanG/fUvcfKiGl - EL4aRLjGt537mZ28UU9/3eiJeJznNSOuNLnF+hmabAu7H0LT4K7EdqfF+XUZW/2j - RKRYcvOUDGF9A7OjW7UfKk1In3+6QDCi7X34RE161jqoaJjrm/T18TOKcgkkhRzE - apQnIDm0Ea/HVzX/PiSOGuertwIDAQABMAsGCSqGSIb3DQEBBQOBgQBzMJdAV4QP - Awel8LzGx5uMOshezF/KfP67wJ93UW+N7zXY6AwPgoLj4Kjw+WtU684JL8Dtr9FX - ozakE+8p06BpxegR4BR3FMHf6p+0jQxUEAkAyb/mVgm66TyghDGC6/YkiKoZptXQ - 98TwDIK/39WEB/V607As+KoYazQG8drorw== - -----END CERTIFICATE----- - key: |- - -----BEGIN EC PRIVATE KEY----- - MHcCAQEEIC8CsJ/B115S+JtR1/l3ZQwKA3XdXt9zLqusF1VXc/KloAoGCCqGSM49 - AwEHoUQDQgAEpwUmRIZHFt8CdDHYm1ikScCScd2q6QVYXxJu+G3fQZ78ScGtN7fu - KXMnQqVjXVRAr8qUY8yipVKuMCepnPXScQ== - -----END EC PRIVATE KEY----- -``` - -### sameSite - Accepted values - -- `none`: Thebrowser will send cookies with both cross-site requests and same-site requests. -- `strict`: Thebrowser will only send cookies for same-site requests (requests originating from the site that set the cookie). - If the request originated from a different URL than the URL of the current location, none of the cookies tagged with the `strict` attribute will be included. -- `lax`: Same-site cookies are withheld on cross-site subrequests, such as calls to load images or frames, but will be sent when a user navigates to the URL from an external site; for example, by following a link. - -### session.store - -An OpenID Connect Authentication middleware can use a persistent KV storage to store the `HTTP` sessions data -instead of keeping all the state in cookies. -It avoids cookies growing inconveniently large, which can lead to latency issues. - -Refer to the [redis options](#configuration-options) to configure the Redis connection. - -Connection parameters to your [Redis](https://redis.io/ "Link to website of Redis") server are attached to your Middleware deployment. - -The following Redis modes are supported: - -- Single instance mode -- [Redis Cluster](https://redis.io/docs/management/scaling "Link to official Redis documentation about Redis Cluster mode") -- [Redis Sentinel](https://redis.io/docs/management/sentinel "Link to official Redis documentation about Redis Sentinel mode") - -!!! info - - If you use Redis in single instance mode or Redis Sentinel, you can configure the `database` field. - This value won't be taken into account if you use Redis Cluster (only database `0` is available). - - In this case, a warning is displayed, and the value is ignored. - -For more information about Redis, we recommend the [official Redis documentation](https://redis.io/docs/ "Link to official Redis documentation"). - -```yaml tab="Defining Redis connection" -apiVersion: traefik.io/v1alpha1 -kind: Middleware -metadata: - name: test-oidc -spec: - plugin: - oidc: - issuer: "https://tenant.auth0.com/realms/myrealm" - redirectUrl: "/callback" - clientID: my-oidc-client-name - clientSecret: mysecret - session: - store: - redis: - endpoints: - - redis-master.traefik-hub.svc.cluster.local:6379 - password: "urn:k8s:secret:oidc:redisPass" -``` - -```yaml tab="Creating the Kubernetes secret" -apiVersion: v1 -kind: Secret -metadata: - name: oidc -stringData: - redisPass: mysecret12345678 -``` - -### csrf - -#### CSRF Internal Behavior - -When the OIDC session is expired, the corresponding CSRF cookie is deleted. -This means that a new CSRF token will be generated and sent to the client whenever the session is refreshed or recreated. - -When a request is sent and uses a non-safe method (see [RFC7231#section-4.2.1](https://datatracker.ietf.org/doc/html/rfc7231.html#section-4.2.1)), -the CSRF token value (extracted from the cookie) have to be sent to the server in the header configured with the [headerName option](#configuration-options). - -{!traefik-for-business-applications.md!} diff --git a/docs/content/reference/routing-configuration/http/middlewares/opa.md b/docs/content/reference/routing-configuration/http/middlewares/opa.md deleted file mode 100644 index 4dabfe455..000000000 --- a/docs/content/reference/routing-configuration/http/middlewares/opa.md +++ /dev/null @@ -1,72 +0,0 @@ ---- -title: 'Open Policy Agent' -description: 'Traefik Hub API Gateway - The Open Policy Agent (OPA) middleware that allows you to restrict access to your services.' ---- - -!!! info "Traefik Hub Feature" - This middleware is available exclusively in [Traefik Hub](https://traefik.io/traefik-hub/). Learn more about [Traefik Hub's advanced features](https://doc.traefik.io/traefik-hub/api-gateway/intro). - -Traefik Hub comes with an Open Policy Agent middleware that allows you to restrict access to your services. It also allows you to enrich request headers with data extracted from policies. -The OPA middleware works as an [OPA agent](https://www.openpolicyagent.org/). - -!!! note "OPA Version" - - This middleware uses the [v1.3.0 of the OPA specification](https://www.openpolicyagent.org/docs). - -## Configuration Example - -```yaml tab="Allow requests with specific JWT claim" -apiVersion: traefik.io/v1alpha1 -kind: Middleware -metadata: - name: opa-allow-jwt-claim - namespace: apps -spec: - plugin: - opa: - policy: | - package example.policies - - allow { - [_, encoded] := split(input.headers.Authorization, " ") - [header, payload, signature] = io.jwt.decode(encoded) - payload["email"] == "bibi@example.com" - } - forwardHeaders: - Group: data.package.grp -``` - -```yaml tab="Deny requests with JSON Accept Header" -apiVersion: traefik.io/v1alpha1 -kind: Middleware -metadata: - name: opa-deny-json - namespace: apps -spec: - plugin: - opa: - policy: | - package example.policies - - default allow = false - - json_content { - input.headers["Accept"] == "application/json" - } - - allow { - not json_content - } - allow: data.example.policies.allow -``` - -## Configuration Options - -| Field | Description | Default | Required | -|:---------|-----------------------|:--------|:----------------------------| -| `policy` | Path or the content of a [policy file](https://www.openpolicyagent.org/docs/v0.66.0/kubernetes-primer/#writing-policies). | "" | No (one of `policy` or `bundlePath` must be set) | -| `bundlePath` | The `bundlePath` option should contain the path to an OPA [bundle](https://www.openpolicyagent.org/docs/v0.66.0/management-bundles/). | "" | No (one of `policy` or `bundlePath` must be set) | -| `allow` | The `allow` option sets the expression to evaluate that determines if the request should be authorized. | "" | No (one of `allow` or `forwardHeaders` must be set) | -| `forwardHeaders` | The `forwardHeaders` option sets the HTTP headers to add to requests and populates them with the result of the given expression. | "" | No (one of `allow` or `forwardHeaders` must be set) | - -{!traefik-for-business-applications.md!} diff --git a/docs/content/reference/routing-configuration/http/middlewares/overview.md b/docs/content/reference/routing-configuration/http/middlewares/overview.md index 40ddb6015..07ea72df0 100644 --- a/docs/content/reference/routing-configuration/http/middlewares/overview.md +++ b/docs/content/reference/routing-configuration/http/middlewares/overview.md @@ -20,29 +20,29 @@ Middlewares that use the same protocol can be combined into chains to fit every | Middleware | Purpose | Area | |-------------------------------------------|---------------------------------------------------|-----------------------------| -| [AddPrefix](addprefix.md) | Adds a Path Prefix | Path Modifier | -| [BasicAuth](basicauth.md) | Adds Basic Authentication | Security, Authentication | -| [Buffering](buffering.md) | Buffers the request/response | Request Lifecycle | -| [Chain](chain.md) | Combines multiple pieces of middleware | Misc | -| [CircuitBreaker](circuitbreaker.md) | Prevents calling unhealthy services | Request Lifecycle | -| [Compress](compress.md) | Compresses the response | Content Modifier | -| [ContentType](contenttype.md) | Handles Content-Type auto-detection | Misc | -| [DigestAuth](digestauth.md) | Adds Digest Authentication | Security, Authentication | -| [Errors](errorpages.md) | Defines custom error pages | Request Lifecycle | -| [ForwardAuth](forwardauth.md) | Delegates Authentication | Security, Authentication | -| [GrpcWeb](grpcweb.md) | Converts gRPC Web requests to HTTP/2 gRPC requests. | Request | -| [Headers](headers.md) | Adds / Updates headers | Security | -| [IPAllowList](ipallowlist.md) | Limits the allowed client IPs | Security, Request lifecycle | -| [InFlightReq](inflightreq.md) | Limits the number of simultaneous connections | Security, Request lifecycle | -| [PassTLSClientCert](passtlsclientcert.md) | Adds Client Certificates in a Header | Security | -| [RateLimit](ratelimit.md) | Limits the call frequency | Security, Request lifecycle | -| [RedirectScheme](redirectscheme.md) | Redirects based on scheme | Request lifecycle | -| [RedirectRegex](redirectregex.md) | Redirects based on regex | Request lifecycle | -| [ReplacePath](replacepath.md) | Changes the path of the request | Path Modifier | -| [ReplacePathRegex](replacepathregex.md) | Changes the path of the request | Path Modifier | -| [Retry](retry.md) | Automatically retries in case of error | Request lifecycle | -| [StripPrefix](stripprefix.md) | Changes the path of the request | Path Modifier | -| [StripPrefixRegex](stripprefixregex.md) | Changes the path of the request | Path Modifier | +| [AddPrefix](addprefix.md) | Adds a Path Prefix | Path Modifier | +| [BasicAuth](basicauth.md) | Adds Basic Authentication | Security, Authentication | +| [Buffering](buffering.md) | Buffers the request/response | Request Lifecycle | +| [Chain](chain.md) | Combines multiple pieces of middleware | Misc | +| [CircuitBreaker](circuitbreaker.md) | Prevents calling unhealthy services | Request Lifecycle | +| [Compress](compress.md) | Compresses the response | Content Modifier | +| [ContentType](contenttype.md) | Handles Content-Type auto-detection | Misc | +| [DigestAuth](digestauth.md) | Adds Digest Authentication | Security, Authentication | +| [Errors](errorpages.md) | Defines custom error pages | Request Lifecycle | +| [ForwardAuth](forwardauth.md) | Delegates Authentication | Security, Authentication | +| [GrpcWeb](grpcweb.md) | Converts gRPC Web requests to HTTP/2 gRPC requests. | Request | +| [Headers](headers.md) | Adds / Updates headers | Security | +| [IPAllowList](ipallowlist.md) | Limits the allowed client IPs | Security, Request lifecycle | +| [InFlightReq](inflightreq.md) | Limits the number of simultaneous connections | Security, Request lifecycle | +| [PassTLSClientCert](passtlsclientcert.md) | Adds Client Certificates in a Header | Security | +| [RateLimit](ratelimit.md) | Limits the call frequency | Security, Request lifecycle | +| [RedirectScheme](redirectscheme.md) | Redirects based on scheme | Request lifecycle | +| [RedirectRegex](redirectregex.md) | Redirects based on regex | Request lifecycle | +| [ReplacePath](replacepath.md) | Changes the path of the request | Path Modifier | +| [ReplacePathRegex](replacepathregex.md) | Changes the path of the request | Path Modifier | +| [Retry](retry.md) | Automatically retries in case of error | Request lifecycle | +| [StripPrefix](stripprefix.md) | Changes the path of the request | Path Modifier | +| [StripPrefixRegex](stripprefixregex.md) | Changes the path of the request | Path Modifier | ## Community Middlewares diff --git a/docs/content/reference/routing-configuration/http/middlewares/passtlsclientcert.md b/docs/content/reference/routing-configuration/http/middlewares/passtlsclientcert.md index c56394109..10b40bb01 100644 --- a/docs/content/reference/routing-configuration/http/middlewares/passtlsclientcert.md +++ b/docs/content/reference/routing-configuration/http/middlewares/passtlsclientcert.md @@ -206,28 +206,28 @@ spec: | Field | Description | Default | Required | |:-----------|:------------------------------------------------------------|:--------|:---------| -| `pem` | Fills the `X-Forwarded-Tls-Client-Cert` header with the certificate information.
More information [here](#pem). | false | No | -| `info.serialNumber` | Add the `Serial Number` of the certificate.
More information about `info` [here](#info). | false | No | -| `info.notAfter` | Add the `Not After` information from the `Validity` part.
More information about `info` [here](#info). | false | No | -| `info.notBefore` | Add the `Not Before` information from the `Validity` part.
More information about `info` [here](#info). | false | No | -| `info.sans` | Add the `Subject Alternative Name` information from the `Subject Alternative Name` part.
More information about `info` [here](#info). | false | No | -| `info.subject` | The `info.subject` selects the specific client certificate subject details you want to add to the `X-Forwarded-Tls-Client-Cert-Info` header.
More information about `info` [here](#info). | false | No | -| `info.subject.country` | Add the `country` information into the subject.
The data is taken from the subject part with the `C` key.
More information about `info` [here](#info). | false | No | -| `info.subject.province` | Add the `province` information into the subject.
The data is taken from the subject part with the `ST` key.
More information about `info` [here](#info). | false | No | -| `info.subject.locality` | Add the `locality` information into the subject.
The data is taken from the subject part with the `L` key.
More information about `info` [here](#info). | false | No | -| `info.subject.organization` | Add the `organization` information into the subject.
The data is taken from the subject part with the `O` key.
More information about `info` [here](#info). | false | No | -| `info.subject.organizationalUnit` | Add the `organizationalUnit` information into the subject.
The data is taken from the subject part with the `OU` key.
More information about `info` [here](#info). | false | No | -| `info.subject.commonName` | Add the `commonName` information into the subject.
The data is taken from the subject part with the `CN` key.| false | No | -| `info.subject.serialNumber` | Add the `serialNumber` information into the subject.
The data is taken from the subject part with the `SN` key.| false | No | -| `info.subject.domainComponent` | Add the `domainComponent` information into the subject.
The data is taken from the subject part with the `DC` key.
More information about `info` [here](#info). | false | No | -| `info.issuer` | The `info.issuer` selects the specific client certificate issuer details you want to add to the `X-Forwarded-Tls-Client-Cert-Info` header.
More information about `info` [here](#info). | false | No | -| `info.issuer.country` | Add the `country` information into the issuer.
The data is taken from the issuer part with the `C` key.
More information about `info` [here](#info). | false | No | -| `info.issuer.province` | Add the `province` information into the issuer.
The data is taken from the issuer part with the `ST` key.
More information about `info` [here](#info). | false | No | -| `info.issuer.locality` | Add the `locality` information into the issuer.
The data is taken from the issuer part with the `L` key.
More information about `info` [here](#info). | false | No | -| `info.issuer.organization` | Add the `organization` information into the issuer.
The data is taken from the issuer part with the `O` key.
More information about `info` [here](#info). | false | No | -| `info.issuer.commonName` |Add the `commonName` information into the issuer.
The data is taken from the issuer part with the `CN` key.
More information about `info` [here](#info). | false | No | -| `info.issuer.serialNumber` |Add the `serialNumber` information into the issuer.
The data is taken from the issuer part with the `SN` key.
More information about `info` [here](#info). | false | No | -| `info.issuer.domainComponent` | Add the `domainComponent` information into the issuer.
The data is taken from the issuer part with the `DC` key.
More information about `info` [here](#info). | false | No | +| `pem` | Fills the `X-Forwarded-Tls-Client-Cert` header with the certificate information.
More information [here](#pem). | false | No | +| `info.serialNumber` | Add the `Serial Number` of the certificate.
More information about `info` [here](#info). | false | No | +| `info.notAfter` | Add the `Not After` information from the `Validity` part.
More information about `info` [here](#info). | false | No | +| `info.notBefore` | Add the `Not Before` information from the `Validity` part.
More information about `info` [here](#info). | false | No | +| `info.sans` | Add the `Subject Alternative Name` information from the `Subject Alternative Name` part.
More information about `info` [here](#info). | false | No | +| `info.subject` | The `info.subject` selects the specific client certificate subject details you want to add to the `X-Forwarded-Tls-Client-Cert-Info` header.
More information about `info` [here](#info). | false | No | +| `info.subject.country` | Add the `country` information into the subject.
The data is taken from the subject part with the `C` key.
More information about `info` [here](#info). | false | No | +| `info.subject.province` | Add the `province` information into the subject.
The data is taken from the subject part with the `ST` key.
More information about `info` [here](#info). | false | No | +| `info.subject.locality` | Add the `locality` information into the subject.
The data is taken from the subject part with the `L` key.
More information about `info` [here](#info). | false | No | +| `info.subject.organization` | Add the `organization` information into the subject.
The data is taken from the subject part with the `O` key.
More information about `info` [here](#info). | false | No | +| `info.subject.organizationalUnit` | Add the `organizationalUnit` information into the subject.
The data is taken from the subject part with the `OU` key.
More information about `info` [here](#info). | false | No | +| `info.subject.commonName` | Add the `commonName` information into the subject.
The data is taken from the subject part with the `CN` key.| false | No | +| `info.subject.serialNumber` | Add the `serialNumber` information into the subject.
The data is taken from the subject part with the `SN` key.| false | No | +| `info.subject.domainComponent` | Add the `domainComponent` information into the subject.
The data is taken from the subject part with the `DC` key.
More information about `info` [here](#info). | false | No | +| `info.issuer` | The `info.issuer` selects the specific client certificate issuer details you want to add to the `X-Forwarded-Tls-Client-Cert-Info` header.
More information about `info` [here](#info). | false | No | +| `info.issuer.country` | Add the `country` information into the issuer.
The data is taken from the issuer part with the `C` key.
More information about `info` [here](#info). | false | No | +| `info.issuer.province` | Add the `province` information into the issuer.
The data is taken from the issuer part with the `ST` key.
More information about `info` [here](#info). | false | No | +| `info.issuer.locality` | Add the `locality` information into the issuer.
The data is taken from the issuer part with the `L` key.
More information about `info` [here](#info). | false | No | +| `info.issuer.organization` | Add the `organization` information into the issuer.
The data is taken from the issuer part with the `O` key.
More information about `info` [here](#info). | false | No | +| `info.issuer.commonName` |Add the `commonName` information into the issuer.
The data is taken from the issuer part with the `CN` key.
More information about `info` [here](#info). | false | No | +| `info.issuer.serialNumber` |Add the `serialNumber` information into the issuer.
The data is taken from the issuer part with the `SN` key.
More information about `info` [here](#info). | false | No | +| `info.issuer.domainComponent` | Add the `domainComponent` information into the issuer.
The data is taken from the issuer part with the `DC` key.
More information about `info` [here](#info). | false | No | ### pem diff --git a/docs/content/reference/routing-configuration/http/middlewares/ratelimit.md b/docs/content/reference/routing-configuration/http/middlewares/ratelimit.md index 80d1b4d49..216d3d9f1 100644 --- a/docs/content/reference/routing-configuration/http/middlewares/ratelimit.md +++ b/docs/content/reference/routing-configuration/http/middlewares/ratelimit.md @@ -18,108 +18,38 @@ For a rate below 1 req/s, define a `period` larger than a second ```yaml tab="Structured (YAML)" # Here, an average of 100 requests per second is allowed. # In addition, a burst of 200 requests is allowed. -# Redis distributed rate limiting is configured with all available options. http: middlewares: test-ratelimit: rateLimit: average: 100 - period: 1s burst: 200 - redis: - endpoints: - - "redis-primary.example.com:6379" - - "redis-replica.example.com:6379" - username: "ratelimit-user" - password: "secure-password" - db: 2 - poolSize: 50 - minIdleConns: 10 - maxActiveConns: 200 - readTimeout: 3s - writeTimeout: 3s - dialTimeout: 5s - tls: - ca: "/etc/ssl/redis-ca.crt" - cert: "/etc/ssl/redis-client.crt" - key: "/etc/ssl/redis-client.key" - insecureSkipVerify: false ``` ```toml tab="Structured (TOML)" # Here, an average of 100 requests per second is allowed. # In addition, a burst of 200 requests is allowed. -# Redis distributed rate limiting is configured with all available options. [http.middlewares] [http.middlewares.test-ratelimit.rateLimit] average = 100 - period = "1s" burst = 200 - [http.middlewares.test-ratelimit.rateLimit.redis] - endpoints = ["redis-primary.example.com:6379", "redis-replica.example.com:6379"] - username = "ratelimit-user" - password = "secure-password" - db = 2 - poolSize = 50 - minIdleConns = 10 - maxActiveConns = 200 - readTimeout = "3s" - writeTimeout = "3s" - dialTimeout = "5s" - [http.middlewares.test-ratelimit.rateLimit.redis.tls] - ca = "/etc/ssl/redis-ca.crt" - cert = "/etc/ssl/redis-client.crt" - key = "/etc/ssl/redis-client.key" - insecureSkipVerify = false ``` ```yaml tab="Labels" # Here, an average of 100 requests per second is allowed. # In addition, a burst of 200 requests is allowed. -# Redis distributed rate limiting is configured with all available options. labels: - "traefik.http.middlewares.test-ratelimit.ratelimit.average=100" - - "traefik.http.middlewares.test-ratelimit.ratelimit.period=1s" - "traefik.http.middlewares.test-ratelimit.ratelimit.burst=200" - - "traefik.http.middlewares.test-ratelimit.ratelimit.redis.endpoints=redis-primary.example.com:6379,redis-replica.example.com:6379" - - "traefik.http.middlewares.test-ratelimit.ratelimit.redis.username=ratelimit-user" - - "traefik.http.middlewares.test-ratelimit.ratelimit.redis.password=secure-password" - - "traefik.http.middlewares.test-ratelimit.ratelimit.redis.db=2" - - "traefik.http.middlewares.test-ratelimit.ratelimit.redis.poolSize=50" - - "traefik.http.middlewares.test-ratelimit.ratelimit.redis.minIdleConns=10" - - "traefik.http.middlewares.test-ratelimit.ratelimit.redis.maxActiveConns=200" - - "traefik.http.middlewares.test-ratelimit.ratelimit.redis.readTimeout=3s" - - "traefik.http.middlewares.test-ratelimit.ratelimit.redis.writeTimeout=3s" - - "traefik.http.middlewares.test-ratelimit.ratelimit.redis.dialTimeout=5s" - - "traefik.http.middlewares.test-ratelimit.ratelimit.redis.tls.ca=/etc/ssl/redis-ca.crt" - - "traefik.http.middlewares.test-ratelimit.ratelimit.redis.tls.cert=/etc/ssl/redis-client.crt" - - "traefik.http.middlewares.test-ratelimit.ratelimit.redis.tls.key=/etc/ssl/redis-client.key" - - "traefik.http.middlewares.test-ratelimit.ratelimit.redis.tls.insecureSkipVerify=false" ``` ```json tab="Tags" // Here, an average of 100 requests per second is allowed. // In addition, a burst of 200 requests is allowed. -// Redis distributed rate limiting is configured with all available options. { "Tags": [ "traefik.http.middlewares.test-ratelimit.ratelimit.average=100", - "traefik.http.middlewares.test-ratelimit.ratelimit.period=1s", - "traefik.http.middlewares.test-ratelimit.ratelimit.burst=200", - "traefik.http.middlewares.test-ratelimit.ratelimit.redis.endpoints=redis-primary.example.com:6379,redis-replica.example.com:6379", - "traefik.http.middlewares.test-ratelimit.ratelimit.redis.username=ratelimit-user", - "traefik.http.middlewares.test-ratelimit.ratelimit.redis.password=secure-password", - "traefik.http.middlewares.test-ratelimit.ratelimit.redis.db=2", - "traefik.http.middlewares.test-ratelimit.ratelimit.redis.poolSize=50", - "traefik.http.middlewares.test-ratelimit.ratelimit.redis.minIdleConns=10", - "traefik.http.middlewares.test-ratelimit.ratelimit.redis.maxActiveConns=200", - "traefik.http.middlewares.test-ratelimit.ratelimit.redis.readTimeout=3s", - "traefik.http.middlewares.test-ratelimit.ratelimit.redis.writeTimeout=3s", - "traefik.http.middlewares.test-ratelimit.ratelimit.redis.dialTimeout=5s", - "traefik.http.middlewares.test-ratelimit.ratelimit.redis.tls.ca=/etc/ssl/redis-ca.crt", - "traefik.http.middlewares.test-ratelimit.ratelimit.redis.tls.cert=/etc/ssl/redis-client.crt", - "traefik.http.middlewares.test-ratelimit.ratelimit.redis.tls.key=/etc/ssl/redis-client.key", - "traefik.http.middlewares.test-ratelimit.ratelimit.redis.tls.insecureSkipVerify=false" + "traefik.http.middlewares.test-ratelimit.ratelimit.burst=50" ] } ``` @@ -127,7 +57,6 @@ labels: ```yaml tab="Kubernetes" # Here, an average of 100 requests per second is allowed. # In addition, a burst of 200 requests is allowed. -# Redis distributed rate limiting is configured with all available options. apiVersion: traefik.io/v1alpha1 kind: Middleware metadata: @@ -135,82 +64,21 @@ metadata: spec: rateLimit: average: 100 - period: 1s burst: 200 - redis: - endpoints: - - "redis-primary.example.com:6379" - - "redis-replica.example.com:6379" - secret: redis-credentials - db: 2 - poolSize: 50 - minIdleConns: 10 - maxActiveConns: 200 - readTimeout: 3s - writeTimeout: 3s - dialTimeout: 5s - tls: - caSecret: redis-ca - certSecret: redis-client-cert - insecureSkipVerify: false - ---- -apiVersion: v1 -kind: Secret -metadata: - name: redis-credentials - namespace: default -data: - username: cmF0ZWxpbWl0LXVzZXI= # base64 encoded "ratelimit-user" - password: c2VjdXJlLXBhc3N3b3Jk # base64 encoded "secure-password" - ---- -apiVersion: v1 -kind: Secret -metadata: - name: redis-ca - namespace: default -data: - tls.ca: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0t... - ---- -apiVersion: v1 -kind: Secret -metadata: - name: redis-client-cert - namespace: default -data: - tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0t... - tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0t... ``` ## Configuration Options | Field | Description | Default | Required | |:-----------|:-------------------------------------------------------|:--------|:---------| -| `average` | Number of requests used to define the rate using the `period`.
0 means **no rate limiting**.
More information [here](#rate-and-burst). | 0 | No | -| `period` | Period of time used to define the rate.
More information [here](#rate-and-burst). | 1s | No | -| `burst` | Maximum number of requests allowed to go through at the very same moment.
More information [here](#rate-and-burst).| 1 | No | -| `sourceCriterion.requestHost` | Whether to consider the request host as the source.
More information about `sourceCriterion`[here](#sourcecriterion). | false | No | -| `sourceCriterion.requestHeaderName` | Name of the header used to group incoming requests.
More information about `sourceCriterion`[here](#sourcecriterion). | "" | No | -| `sourceCriterion.ipStrategy.depth` | Depth position of the IP to select in the `X-Forwarded-For` header (starting from the right).
0 means no depth.
If greater than the total number of IPs in `X-Forwarded-For`, then the client IP is empty
If higher than 0, the `excludedIPs` options is not evaluated.
More information about [`sourceCriterion`](#sourcecriterion), [`ipStrategy`](#ipstrategy), and [`depth`](#sourcecriterionipstrategydepth) below. | 0 | No | -| `sourceCriterion.ipStrategy.excludedIPs` | Allows scanning the `X-Forwarded-For` header and select the first IP not in the list.
If `depth` is specified, `excludedIPs` is ignored.
More information about [`sourceCriterion`](#sourcecriterion), [`ipStrategy`](#ipstrategy), and [`excludedIPs`](#sourcecriterionipstrategyexcludedips) below. | | No | -| `sourceCriterion.ipStrategy.ipv6Subnet` | If `ipv6Subnet` is provided and the selected IP is IPv6, the IP is transformed into the first IP of the subnet it belongs to.
More information about [`sourceCriterion`](#sourcecriterion), [`ipStrategy.ipv6Subnet`](#sourcecriterionipstrategyipv6subnet) below. | | No | -| `redis` | The `redis` configuration enables distributed rate limiting by using Redis to store rate limit tokens across multiple Traefik instances. This allows you to enforce consistent rate limits across a cluster of Traefik proxies.
When Redis is not configured, Traefik uses in-memory storage for rate limiting, which works only for the individual Traefik instance.| | No | -| `redis.endpoints` | List of Redis server endpoints for distributed rate limiting. You can specify multiple endpoints for Redis cluster or high availability setups. | "127.0.0.1:6379" | No | -| `redis.username` | Username for Redis authentication. | "" | No | -| `redis.password` | Password for Redis authentication. In Kubernetes, these can be provided via secrets. | "" | No | -| `redis.db` | Redis database number to select. | 0 | No | -| `redis.poolSize` | Defines the base number of socket connections in the pool. If set to 0, it defaults to 10 connections per CPU core as reported by `runtime.GOMAXPROCS`.
If there are not enough connections in the pool, new connections will be allocated beyond `poolSize`, up to `maxActiveConns`. | 0 | No | -| `redis.minIdleConns` | Minimum number of idle connections to maintain in the pool. This is useful when establishing new connections is slow. A value of 0 means idle connections are not automatically closed. | 0 | No | -| `redis.maxActiveConns` | Maximum number of connections the pool can allocate at any given time. A value of 0 means no limit. | 0 | No | -| `redis.readTimeout` | Timeout for socket reads. If reached, commands will fail with a timeout instead of blocking. Zero means no timeout. | 3s | No | -| `redis.writeTimeout` | Timeout for socket writes. If reached, commands will fail with a timeout instead of blocking. Zero means no timeout. | 3s | No | -| `redis.dialTimeout` | Timeout for establishing new connections. Zero means no timeout. | 5s | No | -| `redis.tls.ca` | Path to the certificate authority used for the secure connection to Redis, it defaults to the system bundle. | "" | No | -| `redis.tls.cert` | Path to the public certificate used for the secure connection to Redis. When this option is set, the `key` option is required. | "" | No | -| `redis.tls.key` | Path to the private key used for the secure connection to Redis. When this option is set, the `cert` option is required. | "" | No | -| `redis.tls.insecureSkipVerify` | If `insecureSkipVerify` is `true`, the TLS connection to Redis accepts any certificate presented by the server regardless of the hostnames it covers. | false | No | +| `average` | Number of requests used to define the rate using the `period`.
0 means **no rate limiting**.
More information [here](#rate-and-burst). | 0 | No | +| `period` | Period of time used to define the rate.
More information [here](#rate-and-burst). | 1s | No | +| `burst` | Maximum number of requests allowed to go through at the very same moment.
More information [here](#rate-and-burst).| 1 | No | +| `sourceCriterion.requestHost` | Whether to consider the request host as the source.
More information about `sourceCriterion`[here](#sourcecriterion). | false | No | +| `sourceCriterion.requestHeaderName` | Name of the header used to group incoming requests.
More information about `sourceCriterion`[here](#sourcecriterion). | "" | No | +| `sourceCriterion.ipStrategy.depth` | Depth position of the IP to select in the `X-Forwarded-For` header (starting from the right).
0 means no depth.
If greater than the total number of IPs in `X-Forwarded-For`, then the client IP is empty
If higher than 0, the `excludedIPs` options is not evaluated.
More information about [`sourceCriterion`](#sourcecriterion), [`ipStrategy`](#ipstrategy), and [`depth`](#sourcecriterionipstrategydepth) below. | 0 | No | +| `sourceCriterion.ipStrategy.excludedIPs` | Allows scanning the `X-Forwarded-For` header and select the first IP not in the list.
If `depth` is specified, `excludedIPs` is ignored.
More information about [`sourceCriterion`](#sourcecriterion), [`ipStrategy`](#ipstrategy), and [`excludedIPs`](#sourcecriterionipstrategyexcludedips) below. | | No | +| `sourceCriterion.ipStrategy.ipv6Subnet` | If `ipv6Subnet` is provided and the selected IP is IPv6, the IP is transformed into the first IP of the subnet it belongs to.
More information about [`sourceCriterion`](#sourcecriterion), [`ipStrategy.ipv6Subnet`](#sourcecriterionipstrategyipv6subnet) below. | | No | ### sourceCriterion @@ -241,9 +109,9 @@ If `ipv6Subnet` is provided, the IP is transformed in the following way. | `IP` | `ipv6Subnet` | clientIP | |---------------------------|--------------|-----------------------| -| `"::abcd:1111:2222:3333"` | `64` | `"::0:0:0:0"` | -| `"::abcd:1111:2222:3333"` | `80` | `"::abcd:0:0:0:0"` | -| `"::abcd:1111:2222:3333"` | `96` | `"::abcd:1111:0:0:0"` | +| `"::abcd:1111:2222:3333"` | `64` | `"::0:0:0:0"` | +| `"::abcd:1111:2222:3333"` | `80` | `"::abcd:0:0:0:0"` | +| `"::abcd:1111:2222:3333"` | `96` | `"::abcd:1111:0:0:0"` | ### sourceCriterion.ipStrategy.depth @@ -251,9 +119,9 @@ If `depth` is set to 2, and the request `X-Forwarded-For` header is `"10.0.0.1,1 | `X-Forwarded-For` | `depth` | clientIP | |-----------------------------------------|---------|--------------| -| `"10.0.0.1,11.0.0.1,12.0.0.1,13.0.0.1"` | `1` | `"13.0.0.1"` | -| `"10.0.0.1,11.0.0.1,12.0.0.1,13.0.0.1"` | `3` | `"11.0.0.1"` | -| `"10.0.0.1,11.0.0.1,12.0.0.1,13.0.0.1"` | `5` | `""` | +| `"10.0.0.1,11.0.0.1,12.0.0.1,13.0.0.1"` | `1` | `"13.0.0.1"` | +| `"10.0.0.1,11.0.0.1,12.0.0.1,13.0.0.1"` | `3` | `"11.0.0.1"` | +| `"10.0.0.1,11.0.0.1,12.0.0.1,13.0.0.1"` | `5` | `""` | ### sourceCriterion.ipStrategy.excludedIPs @@ -266,17 +134,17 @@ In this case, `excludedIPs` should be set to match the list of `X-Forwarded-For Example to use each IP as a distinct source: -| `X-Forwarded-For` | excludedIPs | clientIP | +| X-Forwarded-For | excludedIPs | clientIP | |--------------------------------|-----------------------|--------------| -| `"10.0.0.1,11.0.0.1,12.0.0.1"` | `"11.0.0.1,12.0.0.1"` | `"10.0.0.1"` | -| `"10.0.0.2,11.0.0.1,12.0.0.1"` | `"11.0.0.1,12.0.0.1"` | `"10.0.0.2"` | +| `"10.0.0.1,11.0.0.1,12.0.0.1"` | `"11.0.0.1,12.0.0.1"` | `"10.0.0.1"` | +| `"10.0.0.2,11.0.0.1,12.0.0.1"` | `"11.0.0.1,12.0.0.1"` | `"10.0.0.2"` | 2. Group together a set of IPs (also behind a common set of reverse-proxies) so that they are considered the same source, and all contribute to the same rate-limit bucket. Example to group IPs together as same source: -| `X-Forwarded-For` | excludedIPs | clientIP | +| X-Forwarded-For | excludedIPs | clientIP | |--------------------------------|--------------|--------------| -| `"10.0.0.1,11.0.0.1,12.0.0.1"` | `"12.0.0.1"` | `"11.0.0.1"` | -| `"10.0.0.2,11.0.0.1,12.0.0.1"` | `"12.0.0.1"` | `"11.0.0.1"` | -| `"10.0.0.3,11.0.0.1,12.0.0.1"` | `"12.0.0.1"` | `"11.0.0.1"` | +| `"10.0.0.1,11.0.0.1,12.0.0.1"` | `"12.0.0.1"` | `"11.0.0.1"` | +| `"10.0.0.2,11.0.0.1,12.0.0.1"` | `"12.0.0.1"` | `"11.0.0.1"` | +| `"10.0.0.3,11.0.0.1,12.0.0.1"` | `"12.0.0.1"` | `"11.0.0.1"` | diff --git a/docs/content/reference/routing-configuration/http/middlewares/redirectregex.md b/docs/content/reference/routing-configuration/http/middlewares/redirectregex.md index 942f2e833..0685e0153 100644 --- a/docs/content/reference/routing-configuration/http/middlewares/redirectregex.md +++ b/docs/content/reference/routing-configuration/http/middlewares/redirectregex.md @@ -63,9 +63,9 @@ spec: | Field | Description | Default | Required | |:-----------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:--------|:---------| -| `regex` | The `regex` option is the regular expression to match and capture elements from the request URL.| "" | Yes | -| `permanent` | Enable a permanent redirection. | false | No | -| `replacement` | The `replacement` option defines how to modify the URL to have the new target URL..
`$1x` is equivalent to `${1x}`, not `${1}x` (see [Regexp.Expand](https://golang.org/pkg/regexp/#Regexp.Expand)), so use `${1}` syntax. | "" | No | +| `regex` | The `regex` option is the regular expression to match and capture elements from the request URL.| "" | Yes | +| `permanent` | Enable a permanent redirection. | false | No | +| `replacement` | The `replacement` option defines how to modify the URL to have the new target URL..
`$1x` is equivalent to `${1x}`, not `${1}x` (see [Regexp.Expand](https://golang.org/pkg/regexp/#Regexp.Expand)), so use `${1}` syntax. | "" | No | ### `regex` diff --git a/docs/content/reference/routing-configuration/http/middlewares/redirectscheme.md b/docs/content/reference/routing-configuration/http/middlewares/redirectscheme.md index 4589fd3df..73002551e 100644 --- a/docs/content/reference/routing-configuration/http/middlewares/redirectscheme.md +++ b/docs/content/reference/routing-configuration/http/middlewares/redirectscheme.md @@ -10,7 +10,7 @@ The `RedirectScheme` middleware redirects the request if the request scheme is d When there is at least one other reverse-proxy between the client and Traefik, the other reverse-proxy (i.e. the last hop) needs to be a [trusted](../../../install-configuration/entrypoints.md#configuration-options) one. - Otherwise, Traefik would clean up the `X-Forwarded` headers coming from this last hop, + Otherwise, Traefik would clean up the X-Forwarded headers coming from this last hop, and as the RedirectScheme middleware relies on them to determine the scheme used, it would not function as intended. @@ -69,6 +69,6 @@ spec: | Field | Description | Default | Required | |:-----------------------------|----------------------------------------------------------|:--------|:---------| -| `scheme` | Scheme of the new URL. | "" | Yes | -| `permanent` | Enable a permanent redirection. | false | No | -| `port` | Port of the new URL.
Set a string, **not** a numeric value. | "" | No | +| `scheme` | Scheme of the new URL. | "" | Yes | +| `permanent` | Enable a permanent redirection. | false | No | +| `port` | Port of the new URL.
Set a string, **not** a numeric value. | "" | No | diff --git a/docs/content/reference/routing-configuration/http/middlewares/replacepath.md b/docs/content/reference/routing-configuration/http/middlewares/replacepath.md index 55fed2b9a..f26a83daa 100644 --- a/docs/content/reference/routing-configuration/http/middlewares/replacepath.md +++ b/docs/content/reference/routing-configuration/http/middlewares/replacepath.md @@ -57,4 +57,4 @@ spec: | Field | Description | |:------|:------------| -| `path` | The `path` option defines the path to use as replacement in the request URL. | +| `path` | The `path` option defines the path to use as replacement in the request URL. | diff --git a/docs/content/reference/routing-configuration/http/middlewares/replacepathregex.md b/docs/content/reference/routing-configuration/http/middlewares/replacepathregex.md index 11506ae1c..6ed264409 100644 --- a/docs/content/reference/routing-configuration/http/middlewares/replacepathregex.md +++ b/docs/content/reference/routing-configuration/http/middlewares/replacepathregex.md @@ -57,8 +57,8 @@ labels: | Field | Description | Default | Required | |:-----------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:--------|:---------| -| `regex` | Regular expression to match and capture the path from the request URL. | | Yes | -| `replacement` | Replacement path format, which can include captured variables.
`$1x` is equivalent to `${1x}`, not `${1}x` (see [Regexp.Expand](https://golang.org/pkg/regexp/#Regexp.Expand)), so use `${1}` syntax. | | No +| `regex` | Regular expression to match and capture the path from the request URL. | | Yes | +| `replacement` | Replacement path format, which can include captured variables.
`$1x` is equivalent to `${1x}`, not `${1}x` (see [Regexp.Expand](https://golang.org/pkg/regexp/#Regexp.Expand)), so use `${1}` syntax. | | No !!! tip diff --git a/docs/content/reference/routing-configuration/http/middlewares/retry.md b/docs/content/reference/routing-configuration/http/middlewares/retry.md index e17a2965e..4c8135244 100644 --- a/docs/content/reference/routing-configuration/http/middlewares/retry.md +++ b/docs/content/reference/routing-configuration/http/middlewares/retry.md @@ -64,5 +64,5 @@ spec: | Field | Description | Default | Required | |:------|:------------|:--------|:---------| -| `attempts` | number of times the request should be retried. | | Yes | -| `initialInterval` | First wait time in the exponential backoff series.
The maximum interval is calculated as twice the `initialInterval`.
If unspecified, requests will be retried immediately.
Defined in seconds or as a valid duration format, see [time.ParseDuration](https://golang.org/pkg/time/#ParseDuration). | 0 | No | +| `attempts` | number of times the request should be retried. | | Yes | +| `initialInterval` | First wait time in the exponential backoff series.
The maximum interval is calculated as twice the `initialInterval`.
If unspecified, requests will be retried immediately.
Defined in seconds or as a valid duration format, see [time.ParseDuration](https://golang.org/pkg/time/#ParseDuration). | 0 | No | diff --git a/docs/content/reference/routing-configuration/http/middlewares/stripprefix.md b/docs/content/reference/routing-configuration/http/middlewares/stripprefix.md index b3e6ebfd8..f1209b8a2 100644 --- a/docs/content/reference/routing-configuration/http/middlewares/stripprefix.md +++ b/docs/content/reference/routing-configuration/http/middlewares/stripprefix.md @@ -61,6 +61,6 @@ spec: | Field | Description | Default | Required | |:-----------------------------|:--------------------------------------------------------------|:--------|:---------| -| `prefixes` | List of prefixes to strip from the request URL.
If your backend is serving assets (for example, images or JavaScript files), it can use the `X-Forwarded-Prefix` header to construct relative URLs. | [] | No | +| `prefixes` | List of prefixes to strip from the request URL.
If your backend is serving assets (for example, images or JavaScript files), it can use the `X-Forwarded-Prefix` header to construct relative URLs. | [] | No | {!traefik-for-business-applications.md!} diff --git a/docs/content/reference/routing-configuration/http/middlewares/stripprefixregex.md b/docs/content/reference/routing-configuration/http/middlewares/stripprefixregex.md index 01f07b2e1..b9a3fef6d 100644 --- a/docs/content/reference/routing-configuration/http/middlewares/stripprefixregex.md +++ b/docs/content/reference/routing-configuration/http/middlewares/stripprefixregex.md @@ -56,7 +56,7 @@ spec: | Field | Description | Default | Required | |:-----------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:--------|:---------| -| `regex` | List of regular expressions to match the path prefix from the request URL.
For instance, `/products` also matches `/products/shoes` and `/products/shirts`.
More information [here](#regex). | | No | +| `regex` | List of regular expressions to match the path prefix from the request URL.
For instance, `/products` also matches `/products/shoes` and `/products/shirts`.
More information [here](#regex). | | No | ### regex diff --git a/docs/content/reference/routing-configuration/http/middlewares/waf.md b/docs/content/reference/routing-configuration/http/middlewares/waf.md deleted file mode 100644 index e5e864db7..000000000 --- a/docs/content/reference/routing-configuration/http/middlewares/waf.md +++ /dev/null @@ -1,64 +0,0 @@ ---- -title: 'Coraza Web Application Firewall' -description: 'Traefik Hub API Gateway - The HTTP Coraza in Traefik Hub API Gateway provides web application firewall capabilities' ---- - -!!! info "Traefik Hub Feature" - This middleware is available exclusively in [Traefik Hub](https://traefik.io/traefik-hub/). Learn more about [Traefik Hub's advanced features](https://doc.traefik.io/traefik-hub/api-gateway/intro). - -The [Coraza WAF](https://coraza.io/) middleware in Traefik Hub API Gateway provides web application firewall capabilities. - -The native middleware in Hub API Gateway provides at least 23 times more performance compared to the -WASM-based [Coraza plugin](https://plugins.traefik.io/plugins/65f2aea146079255c9ffd1ec/coraza-waf) available with the open-source Traefik Proxy. - -To learn how to write rules, please visit [Coraza documentation](https://coraza.io/docs/tutorials/introduction/ "Link to Coraza introduction tutorial") and -[OWASP CRS documentation](https://coreruleset.org/docs/ "Link to the OWAP CRS project documentation"). - -!!! warning - - Starting with Traefik Hub v3.11.0, Coraza needs to have read/write permissions to `/tmp`. This is related to [this upstream PR](https://github.com/corazawaf/coraza/pull/1030). - ---- - -## Configuration Examples - -```yaml tab="Deny the /admin path" -apiVersion: traefik.io/v1alpha1 -kind: Middleware -metadata: - name: waf -spec: - plugin: - coraza: - directives: - - SecRuleEngine On - - SecRule REQUEST_URI "@streq /admin" "id:101,phase:1,t:lowercase,log,deny" -``` - -```yaml tab="Allow only GET methods" -apiVersion: traefik.io/v1alpha1 -kind: Middleware -metadata: - name: wafcrs - namespace: apps -spec: - plugin: - coraza: - crsEnabled: true - directives: - - SecDefaultAction "phase:1,log,auditlog,deny,status:403" - - SecDefaultAction "phase:2,log,auditlog,deny,status:403" - - SecAction "id:900110, phase:1, pass, t:none, nolog, setvar:tx.inbound_anomaly_score_threshold=5, setvar:tx.outbound_anomaly_score_threshold=4" - - SecAction "id:900200, phase:1, pass, t:none, nolog, setvar:'tx.allowed_methods=GET'" - - Include @owasp_crs/REQUEST-911-METHOD-ENFORCEMENT.conf - - Include @owasp_crs/REQUEST-949-BLOCKING-EVALUATION.conf -``` - -## Configuration Options - -| Field | Description | Default | Required | -|:---------|:-----------------------|:--------|:----------------------------| -| `directives` | List of WAF rules to enforce. | | Yes | -| `crsEnabled` | Enable [CRS rulesets](https://github.com/corazawaf/coraza-coreruleset/tree/main/rules/%40owasp_crs).
Once the ruleset is enabled, it can be used in the middleware. | false | False | - -{!traefik-for-business-applications.md!} diff --git a/docs/content/reference/routing-configuration/http/router/observability.md b/docs/content/reference/routing-configuration/http/router/observability.md new file mode 100644 index 000000000..ecadcaeed --- /dev/null +++ b/docs/content/reference/routing-configuration/http/router/observability.md @@ -0,0 +1,80 @@ +--- +title: "Per-Router Observability" +description: "You can disable access logs, metrics, and tracing for a specific entrypoint attached to a HTTP Router. Read the technical documentation." +--- + +Traefik's observability features include logs, access logs, metrics, and tracing. You can configure these options globally or at more specific levels, such as per router or per entry point. + +By default, the router observability configuration is inherited from the attached EntryPoints and can be configured with the observability [options](../../../install-configuration/entrypoints.md#configuration-options)). +However, a router defining its own observability configuration will opt-out from these defaults. + +!!! info + To enable router-level observability, you must first enable access-logs, tracing, and metrics. + + When metrics layers are not enabled with the `addEntryPointsLabels`, `addRoutersLabels` and/or `addServicesLabels` options, + enabling metrics for a router will not enable them. + +!!! warning "AddInternals option" + + By default, and for any type of signal (access-logs, metrics and tracing), + Traefik disables observability for internal resources. + The observability options described below cannot interfere with the `AddInternals` ones, + and will be ignored. + + For instance, if a router exposes the `api@internal` service and `metrics.AddInternals` is false, + it will never produces metrics, even if the router observability configuration enables metrics. + +## Configuration Example + +```yaml tab="Structured (YAML)" +http: + routers: + my-router: + rule: "Path(`/foo`)" + service: service-foo + observability: + metrics: false + accessLogs: false + tracing: false +``` + +```yaml tab="Structured (TOML)" +[http.routers.my-router] + rule = "Path(`/foo`)" + service = "service-foo" + + [http.routers.my-router.observability] + metrics = false + accessLogs = false + tracing = false +``` + +```yaml tab="Labels" +labels: + - "traefik.http.routers.my-router.rule=Path(`/foo`)" + - "traefik.http.routers.my-router.service=service-foo" + - "traefik.http.routers.my-router.observability.metrics=false" + - "traefik.http.routers.my-router.observability.accessLogs=false" + - "traefik.http.routers.my-router.observability.tracing=false" +``` + +```json tab="Tags" +{ + // ... + "Tags": [ + "traefik.http.routers.my-router.rule=Path(`/foo`)", + "traefik.http.routers.my-router.service=service-foo", + "traefik.http.routers.my-router.observability.metrics=false", + "traefik.http.routers.my-router.observability.accessLogs=false", + "traefik.http.routers.my-router.observability.tracing=false" + ] +} +``` + +## Configuration Options + +| Field | Description | Default | Required | +|:------|:------------|:--------|:---------| +| `accessLogs` | The `accessLogs` option controls whether the router will produce access-logs. | `true` | No | +| `metrics` | The `metrics` option controls whether the router will produce metrics. | `true` | No | +| `tracing` | The `tracing` option controls whether the router will produce traces. | `true` | No | diff --git a/docs/content/reference/routing-configuration/http/router/rules-and-priority.md b/docs/content/reference/routing-configuration/http/router/rules-and-priority.md new file mode 100644 index 000000000..ad095da07 --- /dev/null +++ b/docs/content/reference/routing-configuration/http/router/rules-and-priority.md @@ -0,0 +1,296 @@ +--- +title: "Traefik HTTP Routers Rules & Priority Documentation" +description: "In Traefik Proxy, an HTTP router is in charge of connecting incoming requests to the Services that can handle them. Read the technical documentation." +--- + +An HTTP router is in charge of connecting incoming requests to the services that can handle them. Traefik allows you to define your matching rules and [prioritize](#priority-calculation) the routes. + +## Rules + +Rules are a set of matchers configured with values, that determine if a particular request matches a specific criteria. +If the rule is verified, the router becomes active, calls middlewares, and then forwards the request to the service. + +- The character `@` is not authorized in the router name. +- To set the value of a rule, use [backticks](https://en.wiktionary.org/wiki/backtick) ` or escaped double-quotes ``\"``. +- Single quotes ' are not accepted since the values are [Go's String Literals](https://golang.org/ref/spec#String_literals). +- Regular Expressions: + - Matchers that accept a regexp as their value use a [Go](https://golang.org/pkg/regexp/) flavored syntax. + - The usual `AND` (&&) and `OR` (||) logical operators can be used, with the expected precedence rules, as well as parentheses to express complex rules. + - The `NOT` (!) operator allows you to invert the matcher. + +The table below lists all the available matchers: + +| Matcher | Description | +|-----------------------------------------------------------------|:-------------------------------------------------------------------------------| +| [```Header(`key`, `value`)```](#header-and-headerregexp) | Matches requests containing a header named `key` set to `value`. | +| [```HeaderRegexp(`key`, `regexp`)```](#header-and-headerregexp) | Matches requests containing a header named `key` matching `regexp`. | +| [```Host(`domain`)```](#host-and-hostregexp) | Matches requests host set to `domain`. | +| [```HostRegexp(`regexp`)```](#host-and-hostregexp) | Matches requests host matching `regexp`. | +| [```Method(`method`)```](#method) | Matches requests method set to `method`. | +| [```Path(`path`)```](#path-pathprefix-and-pathregexp) | Matches requests path set to `path`. | +| [```PathPrefix(`prefix`)```](#path-pathprefix-and-pathregexp) | Matches requests path prefix set to `prefix`. | +| [```PathRegexp(`regexp`)```](#path-pathprefix-and-pathregexp) | Matches request path using `regexp`. | +| [```Query(`key`, `value`)```](#query-and-queryregexp) | Matches requests query parameters named `key` set to `value`. | +| [```QueryRegexp(`key`, `regexp`)```](#query-and-queryregexp) | Matches requests query parameters named `key` matching `regexp`. | +| [```ClientIP(`ip`)```](#clientip) | Matches requests client IP using `ip`. It accepts IPv4, IPv6 and CIDR formats. | + +### Header and HeaderRegexp + +The `Header` and `HeaderRegexp` matchers allow matching requests that contain specific header. + +| Behavior | Rule | +|-----------------------------------------------------------------|:------------------------------------------------------------------------| +| Match requests with a `Content-Type` header set to `application/yaml`.| ```Header(`Content-Type`, `application/yaml`)``` | +| Match requests with a `Content-Type` header set to either `application/json` or `application/yaml`. | ```HeaderRegexp(`Content-Type`, `^application/(json\|yaml)$`)``` | +| Match headers [case-insensitively](https://en.wikipedia.org/wiki/Case_sensitivity). | ```HeaderRegexp(`Content-Type`, `(?i)^application/(json\|yaml)$`)``` | + +### Host and HostRegexp + +The `Host` and `HostRegexp` matchers allow matching requests that are targeted to a given host. + +These matchers do not support non-ASCII characters, use punycode encoded values ([rfc 3492](https://tools.ietf.org/html/rfc3492)) to match such domains. + +If no `Host` is set in the request URL (for example, it's an IP address), these matchers will look at the `Host` header. + +These matchers will match the request's host in lowercase. + +| Behavior | Rule | +|-----------------------------------------------------------------|:------------------------------------------------------------------------| +| Match requests with `Host` set to `example.com`. | ```Host(`example.com`)``` | +| Match requests sent to any subdomain of `example.com`. | ```HostRegexp(`^.+\.example\.com$`)``` | +| Match requests with `Host` set to either `example.com` or `example.org`. | ```HostRegexp(`^example\.(com\|org)$`)``` | +| Match `Host` [case-insensitively](https://en.wikipedia.org/wiki/Case_sensitivity). | ```HostRegexp(`(?i)^example\.(com\|org)$`)``` | + +### Method + +The `Method` matchers allows matching requests sent based on their HTTP method (also known as request verb). + +| Behavior | Rule | +|-----------------------------------------------------------------|:------------------------------------------------------------------------| +| Match `OPTIONS` requests. | ```Method(`OPTIONS`)``` | + +### Path, PathPrefix, and PathRegexp + +These matchers allow matching requests based on their URL path. + +For exact matches, use `Path` and its prefixed alternative `PathPrefix`, for regexp matches, use `PathRegexp`. + +Path are always starting with a `/`, except for `PathRegexp`. + +| Behavior | Rule | +|-----------------------------------------------------------------|:------------------------------------------------------------------------| +| Match `/products` but neither `/products/shoes` nor `/products/`. | ```Path(`/products`)``` | +| Match `/products` as well as everything under `/products`, such as `/products/shoes`, `/products/` but also `/products-for-sale`. | ```PathPrefix(`/products`)``` | +| Match both `/products/shoes` and `/products/socks` with and ID like `/products/shoes/31`. | ```PathRegexp(`^/products/(shoes\|socks)/[0-9]+$`)``` | +| Match requests with a path ending in either `.jpeg`, `.jpg` or `.png`. | ```PathRegexp(`\.(jpeg\|jpg\|png)$`)``` | +| Match `/products` as well as everything under `/products`, such as `/products/shoes`, `/products/` but also `/products-for-sale`, [case-insensitively](https://en.wikipedia.org/wiki/Case_sensitivity). | ```HostRegexp(`(?i)^/products`)``` | + +### Query and QueryRegexp + +The `Query` and `QueryRegexp` matchers allow matching requests based on query parameters. + +| Behavior | Rule | +|-----------------------------------------------------------------|:------------------------------------------------------------------------| +| Match requests with a `mobile` query parameter set to `true`, such as in `/search?mobile=true`. | ```Query(`mobile`, `true`)``` | +| Match requests with a query parameter `mobile` that has no value, such as in `/search?mobile`. | ```Query(`mobile`)``` | +| Match requests with a `mobile` query parameter set to either `true` or `yes`. | ```QueryRegexp(`mobile`, `^(true\|yes)$`)``` | +| Match requests with a `mobile` query parameter set to any value (including the empty value). | ```QueryRegexp(`mobile`, `^.*$`)``` | +| Match query parameters [case-insensitively](https://en.wikipedia.org/wiki/Case_sensitivity). | ```QueryRegexp(`mobile`, `(?i)^(true\|yes)$`)``` | + +### ClientIP + +The `ClientIP` matcher allows matching requests sent from the given client IP. + +It only matches the request client IP and does not use the `X-Forwarded-For` header for matching. + +| Behavior | Rule | +|-----------------------------------------------------------------|:------------------------------------------------------------------------| +| Match requests coming from a given IP (IPv4). | ```ClientIP(`10.76.105.11`)``` | +| Match requests coming from a given IP (IPv6). | ```ClientIP(`::1`)``` | +| Match requests coming from a given subnet (IPv4). | ```ClientIP(`192.168.1.0/24`)``` | +| Match requests coming from a given subnet (IPv6). | ```ClientIP(`fe80::/10`)``` | + +### RuleSyntax + +!!! warning + + RuleSyntax option is deprecated and will be removed in the next major version. + Please do not use this field and rewrite the router rules to use the v3 syntax. + +In Traefik v3 a new rule syntax has been introduced ([migration guide](../../../../migration/v3.md)). the `ruleSyntax` option allows to configure the rule syntax to be used for parsing the rule on a per-router basis. This allows to have heterogeneous router configurations and ease migration. + +The default value of the `ruleSyntax` option is inherited from the `defaultRuleSyntax` option in the install configuration (formerly known as static configuration). By default, the `defaultRuleSyntax` static option is v3, meaning that the default rule syntax is also v3 + +#### Configuration Example + +The configuration below uses the [File Provider (Structured)](../../../install-configuration/providers/others/file.md) to configure the `ruleSyntax` to allow `Router-v2` to use v2 syntax, while for `Router-v3` it is configured to use v3 syntax. + +```yaml tab="Structured (YAML)" +## Dynamic configuration +http: + routers: + Router-v3: + rule: HostRegexp(`[a-z]+\\.traefik\\.com`) + ruleSyntax: v3 + Router-v2: + rule: HostRegexp(`{subdomain:[a-z]+}.traefik.com`) + ruleSyntax: v2 +``` + +```toml tab="Structured (TOML)" +## Dynamic configuration +[http.routers] + [http.routers.Router-v3] + rule = "HostRegexp(`[a-z]+\\.traefik\\.com`)" + ruleSyntax = v3 + [http.routers.Router-v2] + rule = "HostRegexp(`{subdomain:[a-z]+}.traefik.com`)" + ruleSyntax = v2 +``` + +```yaml tab="Labels" +labels: + - "traefik.http.routers.Router-v3.rule=HostRegexp(`[a-z]+\\.traefik\\.com`)" + - "traefik.http.routers.Router-v3.ruleSyntax=v3" + - "traefik.http.routers.Router-v2.rule=HostRegexp(`{subdomain:[a-z]+}.traefik.com`)" + - "traefik.http.routers.Router-v2.ruleSyntax=v2" +``` + +```json tab="Tags" +{ + // ... + "Tags": [ + "traefik.http.routers.Router-v3.rule=HostRegexp(`[a-z]+\\.traefik\\.com`)", + "traefik.http.routers.Router-v3.ruleSyntax=v3" + "traefik.http.routers.Router-v2.rule=HostRegexp(`{subdomain:[a-z]+}.traefik.com`)", + "traefik.http.routers.Router-v2.ruleSyntax=v2" + ] +}, +``` + +## Priority Calculation + +??? info "How default priorities are computed" + + ```yaml tab="Structured (YAML)" + http: + routers: + Router-1: + rule: "HostRegexp(`[a-z]+\.traefik\.com`)" + # ... + Router-2: + rule: "Host(`foobar.traefik.com`)" + # ... + ``` + + ```toml tab="Structured (TOML)" + [http.routers] + [http.routers.Router-1] + rule = "HostRegexp(`[a-z]+\\.traefik\\.com`)" + # ... + [http.routers.Router-2] + rule = "Host(`foobar.traefik.com`)" + # ... + ``` + + ```yaml tab="Labels" + labels: + - "traefik.http.routers.Router-1.rule=HostRegexp(`[a-z]+\\.traefik\\.com`)" + - "traefik.http.routers.Router-2.rule=Host(`foobar.traefik.com`)" + ``` + + ```json tab="Tags" + { + // ... + "Tags": [ + "traefik.http.routers.Router-1.rule=HostRegexp(`[a-z]+\\.traefik\\.com`)", + "traefik.http.routers.Router-2.rule=Host(`foobar.traefik.com`)" + ] + } + ``` + + In this case, all requests with host `foobar.traefik.com` will be routed through `Router-1` instead of `Router-2`. + + | Name | Rule | Priority | + |----------|------------------------------------------|----------| + | Router-1 | ```HostRegexp(`[a-z]+\.traefik\.com`)``` | 34 | + | Router-2 | ```Host(`foobar.traefik.com`)``` | 26 | + + The previous table shows that `Router-1` has a higher priority than `Router-2`. + + To solve this issue, the priority must be set. + +To avoid path overlap, routes are sorted, by default, in descending order using rules length. +The priority is directly equal to the length of the rule, and so the longest length has the highest priority. + +A value of `0` for the priority is ignored: `priority: 0` means that the default rules length sorting is used. + +Traefik reserves a range of priorities for its internal routers, the maximum user-defined router priority value is: + +- `(MaxInt32 - 1000)` for 32-bit platforms, +- `(MaxInt64 - 1000)` for 64-bit platforms. + +### Example + +```yaml tab="Structured (YAML)" +## Dynamic configuration +http: + routers: + Router-1: + rule: "HostRegexp(`[a-z]+\\.traefik\\.com`)" + entryPoints: + - "web" + service: service-1 + priority: 1 + Router-2: + rule: "Host(`foobar.traefik.com`)" + entryPoints: + - "web" + priority: 2 + service: service-2 +``` + +```toml tab="Structured (TOML)" +## Dynamic configuration +[http.routers] + [http.routers.Router-1] + rule = "HostRegexp(`[a-z]+\\.traefik\\.com`)" + entryPoints = ["web"] + service = "service-1" + priority = 1 + [http.routers.Router-2] + rule = "Host(`foobar.traefik.com`)" + entryPoints = ["web"] + priority = 2 + service = "service-2" +``` + +```yaml tab="Labels" +labels: + - "traefik.http.routers.Router-1.rule=HostRegexp(`[a-z]+\\.traefik\\.com`)" + - "traefik.http.routers.Router-1.entryPoints=web" + - "traefik.http.routers.Router-1.service=service-1" + - "traefik.http.routers.Router-1.priority=1" + - "traefik.http.routers.Router-2.rule=Host(`foobar.traefik.com`)" + - "traefik.http.routers.Router-2.entryPoints=web" + - "traefik.http.routers.Router-2.service=service-2" + - "traefik.http.routers.Router-2.priority=2" +``` + +```json tab="Tags" + { + // ... + "Tags": [ + "traefik.http.routers.Router-1.rule=HostRegexp(`[a-z]+\\.traefik\\.com`)", + "traefik.http.routers.Router-1.entryPoints=web", + "traefik.http.routers.Router-1.service=service-1", + "traefik.http.routers.Router-1.priority=1" + "traefik.http.routers.Router-2.rule=Host(`foobar.traefik.com`)", + "traefik.http.routers.Router-2.entryPoints=web", + "traefik.http.routers.Router-2.service=service-2", + "traefik.http.routers.Router-2.priority=2" + ] + } +``` + +In the example above, the priority is configured to allow `Router-2` to handle requests with the `foobar.traefik.com` host. diff --git a/docs/content/reference/routing-configuration/http/routing/observability.md b/docs/content/reference/routing-configuration/http/routing/observability.md deleted file mode 100644 index 59110cd30..000000000 --- a/docs/content/reference/routing-configuration/http/routing/observability.md +++ /dev/null @@ -1,97 +0,0 @@ ---- -title: "Per-Router Observability" -description: "You can disable access logs, metrics, and tracing for a specific entrypoint attached to a HTTP Router. Read the technical documentation." ---- - -Traefik's observability features include logs, access logs, metrics, and tracing. You can configure these options globally or at more specific levels, such as per router or per entry point. - -By default, the router observability configuration is inherited from the attached EntryPoints and can be configured with the observability [options](../../../install-configuration/entrypoints.md#configuration-options). -However, a router defining its own observability configuration will opt-out from these defaults. - -!!! info - To enable router-level observability, you must first enable - [access-logs](../../../install-configuration/observability/logs-and-accesslogs.md#accesslogs), - [tracing](../../../install-configuration/observability/tracing.md), - and [metrics](../../../install-configuration/observability/metrics.md). - - When metrics layers are not enabled with the `addEntryPointsLabels`, `addRoutersLabels` and/or `addServicesLabels` options, - enabling metrics for a router will not enable them. - -!!! warning "AddInternals option" - - By default, and for any type of signal (access-logs, metrics and tracing), - Traefik disables observability for internal resources. - The observability options described below cannot interfere with the `AddInternals` ones, - and will be ignored. - - For instance, if a router exposes the `api@internal` service and `metrics.AddInternals` is false, - it will never produces metrics, even if the router observability configuration enables metrics. - -## Configuration Example - -```yaml tab="Structured (YAML)" -http: - routers: - my-router: - rule: "Path(`/foo`)" - service: service-foo - observability: - metrics: false - accessLogs: false - tracing: false - traceVerbosity: detailed -``` - -```yaml tab="Structured (TOML)" -[http.routers.my-router] - rule = "Path(`/foo`)" - service = "service-foo" - - [http.routers.my-router.observability] - metrics = false - accessLogs = false - tracing = false - traceVerbosity = "detailed" -``` - -```yaml tab="Labels" -labels: - - "traefik.http.routers.my-router.rule=Path(`/foo`)" - - "traefik.http.routers.my-router.service=service-foo" - - "traefik.http.routers.my-router.observability.metrics=false" - - "traefik.http.routers.my-router.observability.accessLogs=false" - - "traefik.http.routers.my-router.observability.tracing=false" - - "traefik.http.routers.my-router.observability.traceVerbosity=detailed" -``` - -```json tab="Tags" -{ - // ... - "Tags": [ - "traefik.http.routers.my-router.rule=Path(`/foo`)", - "traefik.http.routers.my-router.service=service-foo", - "traefik.http.routers.my-router.observability.metrics=false", - "traefik.http.routers.my-router.observability.accessLogs=false", - "traefik.http.routers.my-router.observability.tracing=false", - "traefik.http.routers.my-router.observability.traceVerbosity=detailed" - ] -} -``` - -## Configuration Options - -| Field | Description | Default | Required | -|:-----------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:----------|:---------| -| `accessLogs` | The `accessLogs` option controls whether the router will produce access-logs. | `true` | No | -| `metrics` | The `metrics` option controls whether the router will produce metrics. | `true` | No | -| `tracing` | The `tracing` option controls whether the router will produce traces. | `true` | No | -| `traceVerbosity` | The `traceVerbosity` option controls the tracing verbosity level for the router. Possible values: `minimal` (default), `detailed`. If not set, the value is inherited from the entryPoint. | `minimal` | No | - -#### traceVerbosity - -`observability.traceVerbosity` defines the tracing verbosity level for the router. - -Possible values are: - -- `minimal`: produces a single server span and one client span for each request processed by a router. -- `detailed`: enables the creation of additional spans for each middleware executed for each request processed by a router. diff --git a/docs/content/reference/routing-configuration/http/routing/router.md b/docs/content/reference/routing-configuration/http/routing/router.md deleted file mode 100644 index 2e9a7eb56..000000000 --- a/docs/content/reference/routing-configuration/http/routing/router.md +++ /dev/null @@ -1,116 +0,0 @@ ---- -title: "Traefik HTTP Routers Documentation" -description: "HTTP routers are responsible for connecting incoming requests to the services that can handle them. Read the technical documentation." ---- - -## HTTP Router - -An HTTP router is in charge of connecting incoming requests to the services that can handle them. Routers analyze incoming requests based on rules, and when a match is found, forward the request through any configured middlewares to the appropriate service. - -## Configuration Example - -```yaml tab="Structured (YAML)" -http: - routers: - my-router: - entryPoints: - - "web" - - "websecure" - rule: "Host(`example.com`) && Path(`/api`)" - priority: 10 - middlewares: - - "auth" - - "ratelimit" - tls: - certResolver: "letsencrypt" - options: "modern" - domains: - - main: "example.com" - sans: - - "www.example.com" - observability: - metrics: true - accessLogs: true - tracing: true - service: my-service -``` - -```toml tab="Structured (TOML)" -[http.routers] - [http.routers.my-router] - entryPoints = ["web", "websecure"] - rule = "Host(`example.com`) && Path(`/api`)" - priority = 10 - middlewares = ["auth", "ratelimit"] - service = "my-service" - - [http.routers.my-router.tls] - certResolver = "letsencrypt" - options = "modern" - - [[http.routers.my-router.tls.domains]] - main = "example.com" - sans = ["www.example.com"] - - [http.routers.my-router.observability] - metrics = true - accessLogs = true - tracing = true -``` - -```yaml tab="Labels" -labels: - - "traefik.http.routers.my-router.entrypoints=web,websecure" - - "traefik.http.routers.my-router.rule=Host(`example.com`) && Path(`/api`)" - - "traefik.http.routers.my-router.priority=10" - - "traefik.http.routers.my-router.middlewares=auth,ratelimit" - - "traefik.http.routers.my-router.service=my-service" - - "traefik.http.routers.my-router.tls.certresolver=letsencrypt" - - "traefik.http.routers.my-router.tls.options=modern" - - "traefik.http.routers.my-router.tls.domains[0].main=example.com" - - "traefik.http.routers.my-router.tls.domains[0].sans=www.example.com" - - "traefik.http.routers.my-router.observability.metrics=true" - - "traefik.http.routers.my-router.observability.accessLogs=true" - - "traefik.http.routers.my-router.observability.tracing=true" -``` - -```json tab="Tags" -{ - "Tags": [ - "traefik.http.routers.my-router.entrypoints=web,websecure", - "traefik.http.routers.my-router.rule=Host(`example.com`) && Path(`/api`)", - "traefik.http.routers.my-router.priority=10", - "traefik.http.routers.my-router.middlewares=auth,ratelimit", - "traefik.http.routers.my-router.service=my-service", - "traefik.http.routers.my-router.tls.certresolver=letsencrypt", - "traefik.http.routers.my-router.tls.options=modern", - "traefik.http.routers.my-router.tls.domains[0].main=example.com", - "traefik.http.routers.my-router.tls.domains[0].sans=www.example.com", - "traefik.http.routers.my-router.observability.metrics=true", - "traefik.http.routers.my-router.observability.accessLogs=true", - "traefik.http.routers.my-router.observability.tracing=true" - ] -} -``` - -## Configuration Options - -| Field | Description | Default | Required | -|----------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------|----------| -| `entryPoints` | The list of entry points to which the router is attached. If not specified, HTTP routers are attached to all entry points. | All entry points | No | -| `rule` | Rules are a set of matchers configured with values, that determine if a particular request matches specific criteria. If the rule is verified, the router becomes active, calls middlewares, and then forwards the request to the service. See [Rules & Priority](./rules-and-priority.md) for details. | | Yes | -| `priority` | To avoid path overlap, routes are sorted, by default, in descending order using rules length. The priority is directly equal to the length of the rule, and so the longest length has the highest priority. A value of `0` for the priority is ignored. See [Rules & Priority](./rules-and-priority.md) for details. | Rule length | No | -| `middlewares` | The list of middlewares that are applied to the router. Middlewares are applied in the order they are declared. See [Middlewares overview](../middlewares/overview.md) for available middlewares. | | No | -| `tls` | TLS configuration for the router. When specified, the router will only handle HTTPS requests. | | No | -| `tls.certResolver` | The name of the certificate resolver to use for automatic certificate generation. See [Certificate Resolver](../tls/overview.md#certificate-resolver) for details. | | No | -| `tls.options` | The name of the TLS options to use for configuring TLS parameters (cipher suites, min/max TLS version, client authentication, etc.). See [TLS Options](../tls/tls-options.md) for detailed configuration. | `default` | No | -| `tls.domains` | List of domains and Subject Alternative Names (SANs) for explicit certificate domain specification. When using ACME certificate resolvers, domains are automatically extracted from router rules, making this option optional. | | No | -| `observability` | Observability configuration for the router. Allows fine-grained control over access logs, metrics, and tracing per router. See [Observability](./observability.md) for details. | Inherited from entry points | No | -| `service` | The name of the service that will handle the matched requests. Services can be load balancer services, weighted round robin, mirroring, or failover services. See [Service](../load-balancing/service.md) for details. | | Yes | - -## Router Naming - -- The character `@` is not authorized in the router name -- In provider-specific configurations (Docker, Kubernetes), router names are often auto-generated based on service names and rules - -{!traefik-for-business-applications.md!} diff --git a/docs/content/reference/routing-configuration/http/routing/rules-and-priority.md b/docs/content/reference/routing-configuration/http/routing/rules-and-priority.md deleted file mode 100644 index 7e15cb954..000000000 --- a/docs/content/reference/routing-configuration/http/routing/rules-and-priority.md +++ /dev/null @@ -1,296 +0,0 @@ ---- -title: "Traefik HTTP Routers Rules & Priority Documentation" -description: "In Traefik Proxy, an HTTP router is in charge of connecting incoming requests to the Services that can handle them. Read the technical documentation." ---- - -An HTTP router is in charge of connecting incoming requests to the services that can handle them. Traefik allows you to define your matching rules and [prioritize](#priority-calculation) the routes. - -## Rules - -Rules are a set of matchers configured with values, that determine if a particular request matches a specific criteria. -If the rule is verified, the router becomes active, calls middlewares, and then forwards the request to the service. - -- The character `@` is not authorized in the router name. -- To set the value of a rule, use [backticks](https://en.wiktionary.org/wiki/backtick) ` or escaped double-quotes ``\"``. -- Single quotes ' are not accepted since the values are [Go's String Literals](https://golang.org/ref/spec#String_literals). -- Regular Expressions: - - Matchers that accept a regexp as their value use a [Go](https://golang.org/pkg/regexp/) flavored syntax. - - The usual `AND` (&&) and `OR` (||) logical operators can be used, with the expected precedence rules, as well as parentheses to express complex rules. - - The `NOT` (!) operator allows you to invert the matcher. - -The table below lists all the available matchers: - -| Matcher | Description | -|-----------------------------------------------------------------|:-------------------------------------------------------------------------------| -| [```Header(`key`, `value`)```](#header-and-headerregexp) | Matches requests containing a header named `key` set to `value`. | -| [```HeaderRegexp(`key`, `regexp`)```](#header-and-headerregexp) | Matches requests containing a header named `key` matching `regexp`. | -| [```Host(`domain`)```](#host-and-hostregexp) | Matches requests host set to `domain`. | -| [```HostRegexp(`regexp`)```](#host-and-hostregexp) | Matches requests host matching `regexp`. | -| [```Method(`method`)```](#method) | Matches requests method set to `method`. | -| [```Path(`path`)```](#path-pathprefix-and-pathregexp) | Matches requests path set to `path`. | -| [```PathPrefix(`prefix`)```](#path-pathprefix-and-pathregexp) | Matches requests path prefix set to `prefix`. | -| [```PathRegexp(`regexp`)```](#path-pathprefix-and-pathregexp) | Matches request path using `regexp`. | -| [```Query(`key`, `value`)```](#query-and-queryregexp) | Matches requests query parameters named `key` set to `value`. | -| [```QueryRegexp(`key`, `regexp`)```](#query-and-queryregexp) | Matches requests query parameters named `key` matching `regexp`. | -| [```ClientIP(`ip`)```](#clientip) | Matches requests client IP using `ip`. It accepts IPv4, IPv6 and CIDR formats. | - -### Header and HeaderRegexp - -The `Header` and `HeaderRegexp` matchers allow matching requests that contain specific header. - -| Behavior | Rule | -|-----------------------------------------------------------------|:------------------------------------------------------------------------| -| Match requests with a `Content-Type` header set to `application/yaml`. | ```Header(`Content-Type`, `application/yaml`)``` | -| Match requests with a `Content-Type` header set to either `application/json` or `application/yaml`. | ```HeaderRegexp(`Content-Type`, `^application/(json\|yaml)$`)``` | -| Match headers [case-insensitively](https://en.wikipedia.org/wiki/Case_sensitivity). | ```HeaderRegexp(`Content-Type`, `(?i)^application/(json\|yaml)$`)``` | - -### Host and HostRegexp - -The `Host` and `HostRegexp` matchers allow matching requests that are targeted to a given host. - -These matchers do not support non-ASCII characters, use punycode encoded values ([rfc 3492](https://tools.ietf.org/html/rfc3492)) to match such domains. - -If no `Host` is set in the request URL (for example, it's an IP address), these matchers will look at the `Host` header. - -These matchers will match the request's host in lowercase. - -| Behavior | Rule | -|-----------------------------------------------------------------|:------------------------------------------------------------------------| -| Match requests with `Host` set to `example.com`. | ```Host(`example.com`)``` | -| Match requests sent to any subdomain of `example.com`. | ```HostRegexp(`^.+\.example\.com$`)``` | -| Match requests with `Host` set to either `example.com` or `example.org`. | ```HostRegexp(`^example\.(com\|org)$`)``` | -| Match `Host` [case-insensitively](https://en.wikipedia.org/wiki/Case_sensitivity). | ```HostRegexp(`(?i)^example\.(com\|org)$`)``` | - -### Method - -The `Method` matchers allows matching requests sent based on their HTTP method (also known as request verb). - -| Behavior | Rule | -|-----------------------------------------------------------------|:------------------------------------------------------------------------| -| Match `OPTIONS` requests. | ```Method(`OPTIONS`)``` | - -### Path, PathPrefix, and PathRegexp - -These matchers allow matching requests based on their URL path. - -For exact matches, use `Path` and its prefixed alternative `PathPrefix`, for regexp matches, use `PathRegexp`. - -Path are always starting with a `/`, except for `PathRegexp`. - -| Behavior | Rule | -|-----------------------------------------------------------------|:------------------------------------------------------------------------| -| Match `/products` but neither `/products/shoes` nor `/products/`. | ```Path(`/products`)``` | -| Match `/products` as well as everything under `/products`, such as `/products/shoes`, `/products/` but also `/products-for-sale`. | ```PathPrefix(`/products`)``` | -| Match both `/products/shoes` and `/products/socks` with and ID like `/products/shoes/31`. | ```PathRegexp(`^/products/(shoes\|socks)/[0-9]+$`)``` | -| Match requests with a path ending in either `.jpeg`, `.jpg` or `.png`. | ```PathRegexp(`\.(jpeg\|jpg\|png)$`)``` | -| Match `/products` as well as everything under `/products`, such as `/products/shoes`, `/products/` but also `/products-for-sale`, [case-insensitively](https://en.wikipedia.org/wiki/Case_sensitivity). | ```PathRegexp(`(?i)^/products`)``` | - -### Query and QueryRegexp - -The `Query` and `QueryRegexp` matchers allow matching requests based on query parameters. - -| Behavior | Rule | -|-----------------------------------------------------------------|:------------------------------------------------------------------------| -| Match requests with a `mobile` query parameter set to `true`, such as in `/search?mobile=true`. | ```Query(`mobile`, `true`)``` | -| Match requests with a query parameter `mobile` that has no value, such as in `/search?mobile`. | ```Query(`mobile`)``` | -| Match requests with a `mobile` query parameter set to either `true` or `yes`. | ```QueryRegexp(`mobile`, `^(true\|yes)$`)``` | -| Match requests with a `mobile` query parameter set to any value (including the empty value). | ```QueryRegexp(`mobile`, `^.*$`)``` | -| Match query parameters [case-insensitively](https://en.wikipedia.org/wiki/Case_sensitivity). | ```QueryRegexp(`mobile`, `(?i)^(true\|yes)$`)``` | - -### ClientIP - -The `ClientIP` matcher allows matching requests sent from the given client IP. - -It only matches the request client IP and does not use the `X-Forwarded-For` header for matching. - -| Behavior | Rule | -|-----------------------------------------------------------------|:------------------------------------------------------------------------| -| Match requests coming from a given IP (IPv4). | ```ClientIP(`10.76.105.11`)``` | -| Match requests coming from a given IP (IPv6). | ```ClientIP(`::1`)``` | -| Match requests coming from a given subnet (IPv4). | ```ClientIP(`192.168.1.0/24`)``` | -| Match requests coming from a given subnet (IPv6). | ```ClientIP(`fe80::/10`)``` | - -### RuleSyntax - -!!! warning - - RuleSyntax option is deprecated and will be removed in the next major version. - Please do not use this field and rewrite the router rules to use the v3 syntax. - -In Traefik v3 a new rule syntax has been introduced ([migration guide](../../../../migrate/v3.md)). the `ruleSyntax` option allows to configure the rule syntax to be used for parsing the rule on a per-router basis. This allows to have heterogeneous router configurations and ease migration. - -The default value of the `ruleSyntax` option is inherited from the `defaultRuleSyntax` option in the install configuration (formerly known as static configuration). By default, the `defaultRuleSyntax` static option is v3, meaning that the default rule syntax is also v3 - -#### Configuration Example - -The configuration below uses the [File Provider (Structured)](../../../install-configuration/providers/others/file.md) to configure the `ruleSyntax` to allow `Router-v2` to use v2 syntax, while for `Router-v3` it is configured to use v3 syntax. - -```yaml tab="Structured (YAML)" -## Dynamic configuration -http: - routers: - Router-v3: - rule: HostRegexp(`[a-z]+\\.traefik\\.com`) - ruleSyntax: v3 - Router-v2: - rule: HostRegexp(`{subdomain:[a-z]+}.traefik.com`) - ruleSyntax: v2 -``` - -```toml tab="Structured (TOML)" -## Dynamic configuration -[http.routers] - [http.routers.Router-v3] - rule = "HostRegexp(`[a-z]+\\.traefik\\.com`)" - ruleSyntax = v3 - [http.routers.Router-v2] - rule = "HostRegexp(`{subdomain:[a-z]+}.traefik.com`)" - ruleSyntax = v2 -``` - -```yaml tab="Labels" -labels: - - "traefik.http.routers.Router-v3.rule=HostRegexp(`[a-z]+\\.traefik\\.com`)" - - "traefik.http.routers.Router-v3.ruleSyntax=v3" - - "traefik.http.routers.Router-v2.rule=HostRegexp(`{subdomain:[a-z]+}.traefik.com`)" - - "traefik.http.routers.Router-v2.ruleSyntax=v2" -``` - -```json tab="Tags" -{ - // ... - "Tags": [ - "traefik.http.routers.Router-v3.rule=HostRegexp(`[a-z]+\\.traefik\\.com`)", - "traefik.http.routers.Router-v3.ruleSyntax=v3" - "traefik.http.routers.Router-v2.rule=HostRegexp(`{subdomain:[a-z]+}.traefik.com`)", - "traefik.http.routers.Router-v2.ruleSyntax=v2" - ] -}, -``` - -## Priority Calculation - -??? info "How default priorities are computed" - - ```yaml tab="Structured (YAML)" - http: - routers: - Router-1: - rule: "HostRegexp(`[a-z]+\.traefik\.com`)" - # ... - Router-2: - rule: "Host(`foobar.traefik.com`)" - # ... - ``` - - ```toml tab="Structured (TOML)" - [http.routers] - [http.routers.Router-1] - rule = "HostRegexp(`[a-z]+\\.traefik\\.com`)" - # ... - [http.routers.Router-2] - rule = "Host(`foobar.traefik.com`)" - # ... - ``` - - ```yaml tab="Labels" - labels: - - "traefik.http.routers.Router-1.rule=HostRegexp(`[a-z]+\\.traefik\\.com`)" - - "traefik.http.routers.Router-2.rule=Host(`foobar.traefik.com`)" - ``` - - ```json tab="Tags" - { - // ... - "Tags": [ - "traefik.http.routers.Router-1.rule=HostRegexp(`[a-z]+\\.traefik\\.com`)", - "traefik.http.routers.Router-2.rule=Host(`foobar.traefik.com`)" - ] - } - ``` - - In this case, all requests with host `foobar.traefik.com` will be routed through `Router-1` instead of `Router-2`. - - | Name | Rule | Priority | - |----------|------------------------------------------|----------| - | Router-1 | ```HostRegexp(`[a-z]+\.traefik\.com`)``` | 34 | - | Router-2 | ```Host(`foobar.traefik.com`)``` | 26 | - - The previous table shows that `Router-1` has a higher priority than `Router-2`. - - To solve this issue, the priority must be set. - -To avoid path overlap, routes are sorted, by default, in descending order using rules length. -The priority is directly equal to the length of the rule, and so the longest length has the highest priority. - -A value of `0` for the priority is ignored: `priority: 0` means that the default rules length sorting is used. - -Traefik reserves a range of priorities for its internal routers, the maximum user-defined router priority value is: - -- `(MaxInt32 - 1000)` for 32-bit platforms, -- `(MaxInt64 - 1000)` for 64-bit platforms. - -### Example - -```yaml tab="Structured (YAML)" -## Dynamic configuration -http: - routers: - Router-1: - rule: "HostRegexp(`[a-z]+\\.traefik\\.com`)" - entryPoints: - - "web" - service: service-1 - priority: 1 - Router-2: - rule: "Host(`foobar.traefik.com`)" - entryPoints: - - "web" - priority: 2 - service: service-2 -``` - -```toml tab="Structured (TOML)" -## Dynamic configuration -[http.routers] - [http.routers.Router-1] - rule = "HostRegexp(`[a-z]+\\.traefik\\.com`)" - entryPoints = ["web"] - service = "service-1" - priority = 1 - [http.routers.Router-2] - rule = "Host(`foobar.traefik.com`)" - entryPoints = ["web"] - priority = 2 - service = "service-2" -``` - -```yaml tab="Labels" -labels: - - "traefik.http.routers.Router-1.rule=HostRegexp(`[a-z]+\\.traefik\\.com`)" - - "traefik.http.routers.Router-1.entryPoints=web" - - "traefik.http.routers.Router-1.service=service-1" - - "traefik.http.routers.Router-1.priority=1" - - "traefik.http.routers.Router-2.rule=Host(`foobar.traefik.com`)" - - "traefik.http.routers.Router-2.entryPoints=web" - - "traefik.http.routers.Router-2.service=service-2" - - "traefik.http.routers.Router-2.priority=2" -``` - -```json tab="Tags" - { - // ... - "Tags": [ - "traefik.http.routers.Router-1.rule=HostRegexp(`[a-z]+\\.traefik\\.com`)", - "traefik.http.routers.Router-1.entryPoints=web", - "traefik.http.routers.Router-1.service=service-1", - "traefik.http.routers.Router-1.priority=1" - "traefik.http.routers.Router-2.rule=Host(`foobar.traefik.com`)", - "traefik.http.routers.Router-2.entryPoints=web", - "traefik.http.routers.Router-2.service=service-2", - "traefik.http.routers.Router-2.priority=2" - ] - } -``` - -In the example above, the priority is configured to allow `Router-2` to handle requests with the `foobar.traefik.com` host. diff --git a/docs/content/reference/routing-configuration/http/tls/overview.md b/docs/content/reference/routing-configuration/http/tls/overview.md index c1e1a6892..3c9a3b712 100644 --- a/docs/content/reference/routing-configuration/http/tls/overview.md +++ b/docs/content/reference/routing-configuration/http/tls/overview.md @@ -1,103 +1,10 @@ --- -title: "Traefik HTTP TLS Documentation" -description: "Learn how to configure the transport layer security (TLS) connection for HTTP services in Traefik Proxy. Read the technical documentation." +title: "Traefik TLS Documentation" +description: "Learn how to configure the transport layer security (TLS) connection in Traefik Proxy. Read the technical documentation." --- -## General +Traefik's TLS configuration defines how TLS negotiation is handled for incoming connections. -When an HTTP router is configured to handle HTTPS traffic, include a `tls` field in its definition. -This field tells Traefik that the router should process only TLS requests and ignore non-TLS traffic. - -By default, an HTTP router with a TLS field will terminate the TLS connections, -meaning that it will send decrypted data to the services. -The TLS configuration provides several options for fine-tuning the TLS behavior, -including automatic certificate generation, custom TLS options, and explicit domain specification. - -## Configuration Example - -```yaml tab="Structured (YAML)" -http: - routers: - my-https-router: - rule: "Host(`example.com`) && Path(`/api`)" - service: "my-http-service" - tls: - certResolver: "letsencrypt" - options: "modern-tls" - domains: - - main: "example.com" - sans: - - "www.example.com" - - "api.example.com" -``` - -```toml tab="Structured (TOML)" -[http.routers.my-https-router] - rule = "Host(`example.com`) && Path(`/api`)" - service = "my-http-service" - - [http.routers.my-https-router.tls] - certResolver = "letsencrypt" - options = "modern-tls" - - [[http.routers.my-https-router.tls.domains]] - main = "example.com" - sans = ["www.example.com", "api.example.com"] -``` - -```yaml tab="Labels" -labels: - - "traefik.http.routers.my-https-router.rule=Host(`example.com`) && Path(`/api`)" - - "traefik.http.routers.my-https-router.service=my-http-service" - - "traefik.http.routers.my-https-router.tls=true" - - "traefik.http.routers.my-https-router.tls.certresolver=letsencrypt" - - "traefik.http.routers.my-https-router.tls.options=modern-tls" - - "traefik.http.routers.my-https-router.tls.domains[0].main=example.com" - - "traefik.http.routers.my-https-router.tls.domains[0].sans=www.example.com,api.example.com" -``` - -```json tab="Tags" -{ - "Tags": [ - "traefik.http.routers.my-https-router.rule=Host(`example.com`) && Path(`/api`)", - "traefik.http.routers.my-https-router.service=my-http-service", - "traefik.http.routers.my-https-router.tls=true", - "traefik.http.routers.my-https-router.tls.certresolver=letsencrypt", - "traefik.http.routers.my-https-router.tls.options=modern-tls", - "traefik.http.routers.my-https-router.tls.domains[0].main=example.com", - "traefik.http.routers.my-https-router.tls.domains[0].sans=www.example.com,api.example.com" - ] -} -``` - -## Configuration Options - -| Field | Description | Default | Required | -|:-----------------------------------------------------------------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:----------|:---------| -| `options` | The name of the TLS options to use for configuring TLS parameters (cipher suites, min/max TLS version, client authentication, etc.). See [TLS Options](./tls-options.md) for detailed configuration. | `default` | No | -| `certResolver` | The name of the certificate resolver to use for automatic certificate generation via ACME providers (such as Let's Encrypt). See the [Certificate Resolver](./#certificate-resolver) section for more details. | "" | No | -| `domains` | List of domains and Subject Alternative Names (SANs) for explicit certificate domain specification. See the [Custom Domains](./#custom-domains) section for more details. | [] | No | - -## Certificate Resolver - -The `tls.certResolver` option allows you to specify a certificate resolver for automatic certificate generation via ACME providers (such as Let's Encrypt). - -When a certificate resolver is configured for a router, -Traefik will automatically obtain and manage TLS certificates for the domains specified in the router's rule (in the `Host` matcher) or in the `tls.domains` configuration (with `tls.domains` taking precedence). - -!!! important "Prerequisites" - - - Certificate resolvers must be defined in the [static configuration](../../../install-configuration/tls/certificate-resolvers/acme.md) - - The router must have `tls` enabled - - An ACME challenge type must be configured for the certificate resolver - -## Custom Domains - -When using ACME certificate resolvers, domains are automatically extracted from router rules, -but the `tls.domains` option allows you to explicitly specify the domains and Subject Alternative Names (SANs) for which certificates should be generated. - -This provides fine-grained control over certificate generation and takes precedence over domains automatically extracted from router rules. - -Every domain must have A/AAAA records pointing to Traefik. +The next section of this documentation explains how to configure TLS connections through a definition in the dynamic configuration and how to configure TLS options, and certificates stores. {!traefik-for-business-applications.md!} diff --git a/docs/content/reference/routing-configuration/http/tls/tls-options.md b/docs/content/reference/routing-configuration/http/tls/tls-options.md index 939edf91e..992b3497c 100644 --- a/docs/content/reference/routing-configuration/http/tls/tls-options.md +++ b/docs/content/reference/routing-configuration/http/tls/tls-options.md @@ -106,7 +106,7 @@ tls: ### Curve Preferences -This option allows to set the preferred elliptic curves. +This option allows to set the preferred elliptic curves in a specific order. The names of the curves defined by [`crypto`](https://godoc.org/crypto/tls#CurveID) (e.g. `CurveP521`) and the [RFC defined names](https://tools.ietf.org/html/rfc8446#section-4.2.7) (e. g. `secp521r1`) can be used. @@ -188,17 +188,17 @@ Traefik supports mutual authentication, through the `clientAuth` section. For authentication policies that require verification of the client certificate, the certificate authority for the certificates should be set in `clientAuth.caFiles`. -In Kubernetes environment, CA certificate can be set in `clientAuth.secretNames`. See [TLSOption resource](../../kubernetes/crd/tls/tlsoption.md) for more details. +In Kubernetes environment, CA certificate can be set in `clientAuth.secretNames`. See [TLSOption resource](../../kubernetes/crd/http/tlsoption.md) for more details. The `clientAuth.clientAuthType` option governs the behaviour as follows: | Option | Operation | | --------- | ----------- | -| `NoClientCert` | Disregards any client certificate.| -| `RequestClientCert` | Asks for a certificate but proceeds anyway if none is provided. | -| `RequireAnyClientCert` | Requires a certificate but does not verify if it is signed by a CA listed in `clientAuth.caFiles` or in `clientAuth.secretNames`. | -| `VerifyClientCertIfGiven` | If a certificate is provided, verifies if it is signed by a CA listed in `clientAuth.caFiles` or in `clientAuth.secretNames`. Otherwise proceeds without any certificate. | -| `RequireAndVerifyClientCert` | requires a certificate, which must be signed by a CA listed in `clientAuth.caFiles` or in `clientAuth.secretNames`. | +| `NoClientCert` | Disregards any client certificate.| +| `RequestClientCert` | Asks for a certificate but proceeds anyway if none is provided. | +| `RequireAnyClientCert` | Requires a certificate but does not verify if it is signed by a CA listed in `clientAuth.caFiles` or in `clientAuth.secretNames`. | +| `VerifyClientCertIfGiven` | If a certificate is provided, verifies if it is signed by a CA listed in `clientAuth.caFiles` or in `clientAuth.secretNames`. Otherwise proceeds without any certificate. | +| `RequireAndVerifyClientCert` | requires a certificate, which must be signed by a CA listed in `clientAuth.caFiles` or in `clientAuth.secretNames`. | ```yaml tab="Structured (YAML)" # Dynamic configuration diff --git a/docs/content/reference/routing-configuration/kubernetes/crd/http/ingressroute.md b/docs/content/reference/routing-configuration/kubernetes/crd/http/ingressroute.md index 4328ebfd1..28651bb89 100644 --- a/docs/content/reference/routing-configuration/kubernetes/crd/http/ingressroute.md +++ b/docs/content/reference/routing-configuration/kubernetes/crd/http/ingressroute.md @@ -3,7 +3,7 @@ title: "Kubernetes IngressRoute" description: "An IngressRoute is a Traefik CRD is in charge of connecting incoming requests to the Services that can handle them in HTTP." --- -`IngressRoute` is the CRD implementation of a [Traefik HTTP router](../../../http/routing/rules-and-priority.md). +`IngressRoute` is the CRD implementation of a [Traefik HTTP router](../../../http/router/rules-and-priority.md). Before creating `IngressRoute` objects, you need to apply the [Traefik Kubernetes CRDs](https://doc.traefik.io/traefik/reference/dynamic-configuration/kubernetes-crd/#definitions) to your Kubernetes cluster. @@ -36,7 +36,7 @@ spec: accessLogs: true metrics: true tracing: true - # Set a priority + # Set a pirority priority: 10 services: # Target a Kubernetes Support @@ -74,29 +74,164 @@ spec: ## Configuration Options -| Field | Description | Default | Required | -|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:--------|:---------| -| `entryPoints` | List of [entry points](../../../../install-configuration/entrypoints.md) names.
If not specified, HTTP routers will accept requests from all EntryPoints in the list of default EntryPoints. | | No | -| `routes` | List of routes. | | Yes | -| `routes[n].kind` | Kind of router matching, only `Rule` is allowed yet. | "Rule" | No | -| `routes[n].match` | Defines the [rule](../../../http/routing/rules-and-priority.md#rules) corresponding to an underlying router. | | Yes | -| `routes[n].priority` | Defines the [priority](../../../http/routing/rules-and-priority.md#priority-calculation) to disambiguate rules of the same length, for route matching.
If not set, the priority is directly equal to the length of the rule, and so the longest length has the highest priority.
A value of `0` for the priority is ignored, the default rules length sorting is used. | 0 | No | -| `routes[n].middlewares` | List of middlewares to attach to the IngressRoute.
More information [here](#middleware). | "" | No | -| `routes[n].`
`middlewares[m].`
`name`
| Middleware name.
The character `@` is not authorized.
More information [here](#middleware). | | Yes | -| `routes[n].`
`middlewares[m].`
`namespace`
| Middleware namespace.
Can be empty if the middleware belongs to the same namespace as the IngressRoute.
More information [here](#middleware). | | No | -| `routes[n].`
`observability.`
`accesslogs`
| Defines whether the route will produce [access-logs](../../../../install-configuration/observability/logs-and-accesslogs.md). See [here](../../../http/routing/observability.md) for more information. | false | No | -| `routes[n].`
`observability.`
`metrics`
| Defines whether the route will produce [metrics](../../../../install-configuration/observability/metrics.md). See [here](../../../http/routing/observability.md) for more information. | false | No | -| `routes[n].`
`observability.`
`tracing`
| Defines whether the route will produce [traces](../../../../install-configuration/observability/tracing.md). See [here](../../../http/routing/observability.md) for more information. | false | No | -| `tls` | TLS configuration.
Can be an empty value(`{}`):
A self signed is generated in such a case
(or the [default certificate](../tls/tlsstore.md) is used if it is defined.) | | No | -| `routes[n].`
`services`
| List of any combination of [TraefikService](./traefikservice.md) and [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/).
Exhaustive list of option in the [`Service`](./service.md#configuration-options) documentation. | | No | -| `tls.secretName` | [Secret](https://kubernetes.io/docs/concepts/configuration/secret/) name used to store the certificate (in the same namesapce as the `IngressRoute`) | "" | No | -| `tls.`
`options.name`
| Name of the [`TLSOption`](../tls/tlsoption.md) to use.
More information [here](#tls-options). | "" | No | -| `tls.`
`options.namespace`
| Namespace of the [`TLSOption`](../tls/tlsoption.md) to use. | "" | No | -| `tls.certResolver` | Name of the [Certificate Resolver](../../../../install-configuration/tls/certificate-resolvers/overview.md) to use to generate automatic TLS certificates. | "" | No | -| `tls.domains` | List of domains to serve using the certificates generates (one `tls.domain`= one certificate).
More information in the [dedicated section](../../../../install-configuration/tls/certificate-resolvers/acme.md#domain-definition). | | No | -| `tls.`
`domains[n].main`
| Main domain name | "" | Yes | -| `tls.`
`domains[n].sans`
| List of alternative domains (SANs) | | No | +| Field | Description | Default | Required | +|:------|:----------------------------------------------------------|:---------------------|:---------| +| `entryPoints` | List of [entry points](../../../../install-configuration/entrypoints.md) names.
If not specified, HTTP routers will accept requests from all EntryPoints in the list of default EntryPoints. | | No | +| `routes` | List of routes. | | Yes | +| `routes[n].kind` | Kind of router matching, only `Rule` is allowed yet. | "Rule" | No | +| `routes[n].match` | Defines the [rule](../../../http/router/rules-and-priority.md#rules) corresponding to an underlying router. | | Yes | +| `routes[n].priority` | Defines the [priority](../../../http/router/rules-and-priority.md#priority-calculation) to disambiguate rules of the same length, for route matching.
If not set, the priority is directly equal to the length of the rule, and so the longest length has the highest priority.
A value of `0` for the priority is ignored, the default rules length sorting is used. | 0 | No | +| `routes[n].middlewares` | List of middlewares to attach to the IngressRoute.
More information [here](#middleware). | "" | No | +| `routes[n].`
`middlewares[m].`
`name` | Middleware name.
The character `@` is not authorized.
More information [here](#middleware). | | Yes | +| `routes[n].`
`middlewares[m].`
`namespace` | Middleware namespace.
Can be empty if the middleware belongs to the same namespace as the IngressRoute.
More information [here](#middleware). | | No | +| `routes[n].`
`observability.`
`accesslogs` | Defines whether the route will produce [access-logs](../../../../install-configuration/observability/logs-and-accesslogs.md). See [here](../../../http/router/observability.md) for more information. | false | No | +| `routes[n].`
`observability.`
`metrics` | Defines whether the route will produce [metrics](../../../../install-configuration/observability/metrics.md). See [here](../../../http/router/observability.md) for more information. | false | No | +| `routes[n].`
`observability.`
`tracing` | Defines whether the route will produce [traces](../../../../install-configuration/observability/tracing.md). See [here](../../../http/router/observability.md) for more information. | false | No | +| `routes[n].`
`services` | List of any combination of TraefikService and [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/).
More information [here](#externalname-service). | | No | +| `routes[n].`
`services[m].`
`kind` | Kind of the service targeted.
Two values allowed:
- **Service**: Kubernetes Service
**TraefikService**: Traefik Service.
More information [here](#externalname-service). | "Service" | No | +| `routes[n].`
`services[m].`
`name` | Service name.
The character `@` is not authorized.
More information [here](#middleware). | | Yes | +| `routes[n].`
`services[m].`
`namespace` | Service namespace.
Can be empty if the service belongs to the same namespace as the IngressRoute.
More information [here](#externalname-service). | | No | +| `routes[n].`
`services[m].`
`port` | Service port (number or port name).
Evaluated only if the kind is **Service**. | | No | +| `routes[n].`
`services[m].`
`responseForwarding.`
`flushInterval` | Interval, in milliseconds, in between flushes to the client while copying the response body.
A negative value means to flush immediately after each write to the client.
This configuration is ignored when a response is a streaming response; for such responses, writes are flushed to the client immediately.
Evaluated only if the kind is **Service**. | 100ms | No | +| `routes[n].`
`services[m].`
`scheme` | Scheme to use for the request to the upstream Kubernetes Service.
Evaluated only if the kind is **Service**. | "http"
"https" if `port` is 443 or contains the string *https*. | No | +| `routes[n].`
`services[m].`
`serversTransport` | Name of ServersTransport resource to use to configure the transport between Traefik and your servers.
Evaluated only if the kind is **Service**. | "" | No | +| `routes[n].`
`services[m].`
`passHostHeader` | Forward client Host header to server.
Evaluated only if the kind is **Service**. | true | No | +| `routes[n].`
`services[m].`
`healthCheck.scheme` | Server URL scheme for the health check endpoint.
Evaluated only if the kind is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type [ExternalName](#externalname-service). | "" | No | +| `routes[n].`
`services[m].`
`healthCheck.mode` | Health check mode.
If defined to grpc, will use the gRPC health check protocol to probe the server.
Evaluated only if the kind is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type [ExternalName](#externalname-service). | "http" | No | +| `routes[n].`
`services[m].`
`healthCheck.path` | Server URL path for the health check endpoint.
Evaluated only if the kind is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type [ExternalName](#externalname-service). | "" | No | +| `routes[n].`
`services[m].`
`healthCheck.interval` | Frequency of the health check calls.
Evaluated only if the kind is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type [ExternalName](#externalname-service). | "100ms" | No | +| `routes[n].`
`services[m].`
`healthCheck.method` | HTTP method for the health check endpoint.
Evaluated only if the kind is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type [ExternalName](#externalname-service). | "GET" | No | +| `routes[n].`
`services[m].`
`healthCheck.status` | Expected HTTP status code of the response to the health check request.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type ExternalName.
If not set, expect a status between 200 and 399.
Evaluated only if the kind is **Service**. | | No | +| `routes[n].`
`services[m].`
`healthCheck.port` | URL port for the health check endpoint.
Evaluated only if the kind is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type [ExternalName](#externalname-service). | | No | +| `routes[n].`
`services[m].`
`healthCheck.timeout` | Maximum duration to wait before considering the server unhealthy.
Evaluated only if the kind is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type [ExternalName](#externalname-service). | "5s" | No | +| `routes[n].`
`services[m].`
`healthCheck.hostname` | Value in the Host header of the health check request.
Evaluated only if the kind is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type [ExternalName](#externalname-service). | "" | No | +| `routes[n].`
`services[m].`
`healthCheck.`
`followRedirect` | Follow the redirections during the healtchcheck.
Evaluated only if the kind is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type [ExternalName](#externalname-service). | true | No | +| `routes[n].`
`services[m].`
`healthCheck.headers` | Map of header to send to the health check endpoint
Evaluated only if the kind is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type [ExternalName](#externalname-service)). | | No | +| `routes[n].`
`services[m].`
`sticky.`
`cookie.name` | Name of the cookie used for the stickiness.
When sticky sessions are enabled, a `Set-Cookie` header is set on the initial response to let the client know which server handles the first response.
On subsequent requests, to keep the session alive with the same server, the client should send the cookie with the value set.
If the server pecified in the cookie becomes unhealthy, the request will be forwarded to a new server (and the cookie will keep track of the new server).
Evaluated only if the kind is **Service**. | "" | No | +| `routes[n].`
`services[m].`
`sticky.`
`cookie.httpOnly` | Allow the cookie can be accessed by client-side APIs, such as JavaScript.
Evaluated only if the kind is **Service**. | false | No | +| `routes[n].`
`services[m].`
`sticky.`
`cookie.secure` | Allow the cookie can only be transmitted over an encrypted connection (i.e. HTTPS).
Evaluated only if the kind is **Service**. | false | No | +| `routes[n].`
`services[m].`
`sticky.`
`cookie.sameSite` | [SameSite](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite) policy
Allowed values:
-`none`
-`lax`
`strict`
Evaluated only if the kind is **Service**. | "" | No | +| `routes[n].`
`services[m].`
`sticky.`
`cookie.maxAge` | Number of seconds until the cookie expires.
Negative number, the cookie expires immediately.
0, the cookie never expires.
Evaluated only if the kind is **Service**. | 0 | No | +| `routes[n].`
`services[m].`
`strategy` | Load balancing strategy between the servers.
RoundRobin is the only supported value yet.
Evaluated only if the kind is **Service**. | "RoundRobin" | No | +| `routes[n].`
`services[m].`
`weight` | Service weight.
To use only to refer to WRR TraefikService | "" | No | +| `routes[n].`
`services[m].`
`nativeLB` | Allow using the Kubernetes Service load balancing between the pods instead of the one provided by Traefik.
Evaluated only if the kind is **Service**. | false | No | +| `routes[n].`
`services[m].`
`nodePortLB` | Use the nodePort IP address when the service type is NodePort.
It allows services to be reachable when Traefik runs externally from the Kubernetes cluster but within the same network of the nodes.
Evaluated only if the kind is **Service**. | false | No | +| `tls` | TLS configuration.
Can be an empty value(`{}`):
A self signed is generated in such a case
(or the [default certificate](tlsstore.md) is used if it is defined.) | | No | +| `tls.secretName` | [Secret](https://kubernetes.io/docs/concepts/configuration/secret/) name used to store the certificate (in the same namesapce as the `IngressRoute`) | "" | No | +| `tls.`
`options.name` | Name of the [`TLSOption`](tlsoption.md) to use.
More information [here](#tls-options). | "" | No | +| `tls.`
`options.namespace` | Namespace of the [`TLSOption`](tlsoption.md) to use. | "" | No | +| `tls.certResolver` | Name of the [Certificate Resolver](../../../../install-configuration/tls/certificate-resolvers/overview.md) to use to generate automatic TLS certificates. | "" | No | +| `tls.domains` | List of domains to serve using the certificates generates (one `tls.domain`= one certificate).
More information in the [dedicated section](../../../../install-configuration/tls/certificate-resolvers/acme.md#domain-definition). | | No | +| `tls.`
`domains[n].main` | Main domain name | "" | Yes | +| `tls.`
`domains[n].sans` | List of alternative domains (SANs) | | No | +### ExternalName Service + +Traefik backends creation needs a port to be set, however Kubernetes [ExternalName Service](https://kubernetes.io/docs/concepts/services-networking/service/#externalname) could be defined without any port. Accordingly, Traefik supports defining a port in two ways: + +- only on `IngressRoute` service +- on both sides, you'll be warned if the ports don't match, and the `IngressRoute` service port is used + +Thus, in case of two sides port definition, Traefik expects a match between ports. + +=== "Ports defined on Resource" + + ```yaml tab="IngressRoute" + apiVersion: traefik.io/v1alpha1 + kind: IngressRoute + metadata: + name: test.route + namespace: apps + + spec: + entryPoints: + - foo + routes: + - match: Host(`example.net`) + kind: Rule + services: + - name: external-svc + port: 80 + ``` + + ```yaml tab="Service ExternalName" + apiVersion: v1 + kind: Service + metadata: + name: external-svc + namespace: apps + + spec: + externalName: external.domain + type: ExternalName + ``` + +=== "Port defined on the Service" + + ```yaml tab="IngressRoute" + apiVersion: traefik.io/v1alpha1 + kind: IngressRoute + metadata: + name: test.route + namespace: apps + + spec: + entryPoints: + - foo + routes: + - match: Host(`example.net`) + kind: Rule + services: + - name: external-svc + ``` + + ```yaml tab="Service ExternalName" + apiVersion: v1 + kind: Service + metadata: + name: external-svc + namespace: apps + + spec: + externalName: external.domain + type: ExternalName + ports: + - port: 80 + ``` + +=== "Port defined on both sides" + + ```yaml tab="IngressRoute" + apiVersion: traefik.io/v1alpha1 + kind: IngressRoute + metadata: + name: test.route + namespace: apps + + spec: + entryPoints: + - foo + routes: + - match: Host(`example.net`) + kind: Rule + services: + - name: external-svc + port: 80 + ``` + + ```yaml tab="Service ExternalName" + apiVersion: v1 + kind: Service + metadata: + name: external-svc + namespace: apps + + spec: + externalName: external.domain + type: ExternalName + ports: + - port: 80 + ``` ### Middleware @@ -146,11 +281,114 @@ same namespace as the IngressRoute) - `Service` (default value): to reference a [Kubernetes Service](https://kubernetes.io/docs/concepts/services-networking/service/) - `TraefikService`: to reference an object [`TraefikService`](../http/traefikservice.md) +### Port Definition + +Traefik backends creation needs a port to be set, however Kubernetes [ExternalName Service](https://kubernetes.io/docs/concepts/services-networking/service/#externalname) could be defined without any port. Accordingly, Traefik supports defining a port in two ways: + +- only on `IngressRoute` service +- on both sides, you'll be warned if the ports don't match, and the `IngressRoute` service port is used + +Thus, in case of two sides port definition, Traefik expects a match between ports. + +??? example + + ```yaml tab="IngressRoute" + --- + apiVersion: traefik.io/v1alpha1 + kind: IngressRoute + metadata: + name: test.route + namespace: default + + spec: + entryPoints: + - foo + + routes: + - match: Host(`example.net`) + kind: Rule + services: + - name: external-svc + port: 80 + + --- + apiVersion: v1 + kind: Service + metadata: + name: external-svc + namespace: default + spec: + externalName: external.domain + type: ExternalName + ``` + + ```yaml tab="ExternalName Service" + --- + apiVersion: traefik.io/v1alpha1 + kind: IngressRoute + metadata: + name: test.route + namespace: default + + spec: + entryPoints: + - foo + + routes: + - match: Host(`example.net`) + kind: Rule + services: + - name: external-svc + + --- + apiVersion: v1 + kind: Service + metadata: + name: external-svc + namespace: default + spec: + externalName: external.domain + type: ExternalName + ports: + - port: 80 + ``` + + ```yaml tab="Both sides" + --- + apiVersion: traefik.io/v1alpha1 + kind: IngressRoute + metadata: + name: test.route + namespace: default + + spec: + entryPoints: + - foo + + routes: + - match: Host(`example.net`) + kind: Rule + services: + - name: external-svc + port: 80 + + --- + apiVersion: v1 + kind: Service + metadata: + name: external-svc + namespace: default + spec: + externalName: external.domain + type: ExternalName + ports: + - port: 80 + ``` ### TLS Options The `options` field enables fine-grained control of the TLS parameters. -It refers to a [TLSOption](../tls/tlsoption.md) and will be applied only if a `Host` +It refers to a [TLSOption](./tlsoption.md) and will be applied only if a `Host` rule is defined. #### Server Name Association @@ -216,3 +454,108 @@ TLS options references, a conflict occurs, such as in the example below. If that happens, both mappings are discarded, and the host name (`example.net` in the example) for these routers gets associated with the default TLS options instead. + +### Load Balancing + +You can declare and use Kubernetes Service load balancing as detailed below: + +```yaml tab="IngressRoute" +apiVersion: traefik.io/v1alpha1 +kind: IngressRoute +metadata: + name: ingressroutebar + namespace: default + +spec: + entryPoints: + - web + routes: + - match: Host(`example.com`) && PathPrefix(`/foo`) + kind: Rule + services: + - name: svc1 + namespace: default + - name: svc2 + namespace: default +``` + +```yaml tab="K8s Service" +apiVersion: v1 +kind: Service +metadata: + name: svc1 + namespace: default + +spec: + ports: + - name: http + port: 80 + selector: + app: traefiklabs + task: app1 +--- +apiVersion: v1 +kind: Service +metadata: + name: svc2 + namespace: default + +spec: + ports: + - name: http + port: 80 + selector: + app: traefiklabs + task: app2 +``` + +!!! important "Kubernetes Service Native Load-Balancing" + + To avoid creating the server load-balancer with the pod IPs and use Kubernetes Service clusterIP directly, + one should set the service `NativeLB` option to true. + Please note that, by default, Traefik reuses the established connections to the backends for performance purposes. This can prevent the requests load balancing between the replicas from behaving as one would expect when the option is set. + By default, `NativeLB` is false. + + ??? example "Example" + + ```yaml + --- + apiVersion: traefik.io/v1alpha1 + kind: IngressRoute + metadata: + name: test.route + namespace: default + + spec: + entryPoints: + - foo + + routes: + - match: Host(`example.net`) + kind: Rule + services: + - name: svc + port: 80 + # Here, nativeLB instructs to build the server load-balancer with the Kubernetes Service clusterIP only. + nativeLB: true + + --- + apiVersion: v1 + kind: Service + metadata: + name: svc + namespace: default + spec: + type: ClusterIP + ... + ``` + +### Configuring Backend Protocol + +There are 3 ways to configure the backend protocol for communication between Traefik and your pods: + +- Setting the scheme explicitly (http/https/h2c) +- Configuring the name of the kubernetes service port to start with https (https) +- Setting the kubernetes service port to use port 443 (https) + +If you do not configure the above, Traefik will assume an http connection. diff --git a/docs/content/reference/routing-configuration/kubernetes/crd/http/serverstransport.md b/docs/content/reference/routing-configuration/kubernetes/crd/http/serverstransport.md index 63f5c04a2..7c364f685 100644 --- a/docs/content/reference/routing-configuration/kubernetes/crd/http/serverstransport.md +++ b/docs/content/reference/routing-configuration/kubernetes/crd/http/serverstransport.md @@ -55,18 +55,18 @@ spec: | Field | Description | Default | Required | |:------|:----------------------------------------------------------|:---------------------|:---------| -| `serverstransport.`
`serverName`
| Defines the server name that will be used for SNI. | | No | -| `serverstransport.`
`insecureSkipVerify`
| Controls whether the server's certificate chain and host name is verified. | false | No | -| `serverstransport.`
`rootcas`
| Set of root certificate authorities to use when verifying server certificates. (for mTLS connections). | | No | -| `serverstransport.`
`certificatesSecrets`
| Certificates to present to the server for mTLS. | | No | -| `serverstransport.`
`maxIdleConnsPerHost`
| Maximum idle (keep-alive) connections to keep per-host. | 200 | No | -| `serverstransport.`
`disableHTTP2`
| Disables HTTP/2 for connections with servers. | false | No | -| `serverstransport.`
`peerCertURI`
| Defines the URI used to match against SAN URIs during the server's certificate verification. | "" | No | -| `serverstransport.`
`forwardingTimeouts.dialTimeout`
| Amount of time to wait until a connection to a server can be established.
Zero means no timeout. | 30s | No | -| `serverstransport.`
`forwardingTimeouts.responseHeaderTimeout`
| Amount of time to wait for a server's response headers after fully writing the request (including its body, if any).
Zero means no timeout | 0s | No | -| `serverstransport.`
`forwardingTimeouts.idleConnTimeout`
| Maximum amount of time an idle (keep-alive) connection will remain idle before closing itself.
Zero means no timeout. | 90s | No | -| `serverstransport.`
`spiffe.ids`
| Allow SPIFFE IDs.
This takes precedence over the SPIFFE TrustDomain. | | No | -| `serverstransport.`
`spiffe.trustDomain`
| Allow SPIFFE trust domain. | "" | No | +| `serverstransport.`
`serverName` | Defines the server name that will be used for SNI. | | No | +| `serverstransport.`
`insecureSkipVerify` | Controls whether the server's certificate chain and host name is verified. | false | No | +| `serverstransport.`
`rootcas` | Set of root certificate authorities to use when verifying server certificates. (for mTLS connections). | | No | +| `serverstransport.`
`certificatesSecrets` | Certificates to present to the server for mTLS. | | No | +| `serverstransport.`
`maxIdleConnsPerHost` | Maximum idle (keep-alive) connections to keep per-host. | 200 | No | +| `serverstransport.`
`disableHTTP2` | Disables HTTP/2 for connections with servers. | false | No | +| `serverstransport.`
`peerCertURI` | Defines the URI used to match against SAN URIs during the server's certificate verification. | "" | No | +| `serverstransport.`
`forwardingTimeouts.dialTimeout` | Amount of time to wait until a connection to a server can be established.
Zero means no timeout. | 30s | No | +| `serverstransport.`
`forwardingTimeouts.responseHeaderTimeout` | Amount of time to wait for a server's response headers after fully writing the request (including its body, if any).
Zero means no timeout | 0s | No | +| `serverstransport.`
`forwardingTimeouts.idleConnTimeout` | Maximum amount of time an idle (keep-alive) connection will remain idle before closing itself.
Zero means no timeout. | 90s | No | +| `serverstransport.`
`spiffe.ids` | Allow SPIFFE IDs.
This takes precedence over the SPIFFE TrustDomain. | | No | +| `serverstransport.`
`spiffe.trustDomain` | Allow SPIFFE trust domain. | "" | No | !!! note "CA Secret" The CA secret must contain a base64 encoded certificate under either a tls.ca or a ca.crt key. diff --git a/docs/content/reference/routing-configuration/kubernetes/crd/http/service.md b/docs/content/reference/routing-configuration/kubernetes/crd/http/service.md deleted file mode 100644 index b69515b25..000000000 --- a/docs/content/reference/routing-configuration/kubernetes/crd/http/service.md +++ /dev/null @@ -1,430 +0,0 @@ ---- -title: "Kubernetes Service" -description: "A Service is a not Traefik CRD, it allows you to describe the Service option in an IngressRoute or a Traefik Service." ---- - -`Service` is the implementation of a [Traefik HTTP service](../../../http/load-balancing/service.md). - -There is no dedicated CRD, a `Service` is part of: - -- [`IngressRoute`](./ingressroute.md) -- [`TraefikService`](./traefikservice.md) - -Note that, before creating `IngressRoute` or `TraefikService` objects, you need to apply the [Traefik Kubernetes CRDs](https://doc.traefik.io/traefik/reference/dynamic-configuration/kubernetes-crd/#definitions) to your Kubernetes cluster. - -This registers the Traefik-specific resources. - -## Configuration Example - -You can declare a `Service` either as part of an `IngressRoute` or a `TraefikService` as detailed below: - -```yaml tab="IngressRoute" -apiVersion: traefik.io/v1alpha1 -kind: IngressRoute -metadata: - name: test-name - namespace: apps -spec: - entryPoints: - - web - routes: - - kind: Rule - # Rule on the Host - match: Host(`test.example.com`) - services: - # Target a Kubernetes Service - - kind: Service - name: foo - namespace: apps - # Customize the connection between Traefik and the backend - passHostHeader: true - port: 80 - responseForwarding: - flushInterval: 1ms - scheme: https - sticky: - cookie: - httpOnly: true - name: cookie - secure: true - strategy: RoundRobin -``` - -```yaml tab="TraefikService" -apiVersion: traefik.io/v1alpha1 -kind: TraefikService -metadata: - name: wrr1 - namespace: apps - -spec: - weighted: - services: - # Target a Kubernetes Service - - kind: Service - name: foo - namespace: apps - # Customize the connection between Traefik and the backend - passHostHeader: true - port: 80 - responseForwarding: - flushInterval: 1ms - scheme: https - sticky: - cookie: - httpOnly: true - name: cookie - secure: true - strategy: RoundRobin -``` - -## Configuration Options - -| Field | Description | Default | Required | -|:---------------------------------------------------------------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:---------------------------------------------------------------------|:---------| -| `kind` | Kind of the service targeted.
Two values allowed:
- **Service**: Kubernetes Service
**TraefikService**: Traefik Service.
More information [here](#externalname-service). | "Service" | No | -| `name` | Service name.
The character `@` is not authorized.
More information [here](#middleware). | | Yes | -| `namespace` | Service namespace.
Can be empty if the service belongs to the same namespace as the IngressRoute.
More information [here](#externalname-service). | | No | -| `port` | Service port (number or port name).
Evaluated only if the kind is **Service**. | | No | -| `responseForwarding.`
`flushInterval`
| Interval, in milliseconds, in between flushes to the client while copying the response body.
A negative value means to flush immediately after each write to the client.
This configuration is ignored when a response is a streaming response; for such responses, writes are flushed to the client immediately.
Evaluated only if the kind is **Service**. | 100ms | No | -| `scheme` | Scheme to use for the request to the upstream Kubernetes Service.
Evaluated only if the kind is **Service**. | "http"
"https" if `port` is 443 or contains the string *https*. | No | -| `serversTransport` | Name of ServersTransport resource to use to configure the transport between Traefik and your servers.
Evaluated only if the kind is **Service**. | "" | No | -| `passHostHeader` | Forward client Host header to server.
Evaluated only if the kind is **Service**. | true | No | -| `healthCheck.scheme` | Server URL scheme for the health check endpoint.
Evaluated only if the kind is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type [ExternalName](#externalname-service). | "" | No | -| `healthCheck.mode` | Health check mode.
If defined to grpc, will use the gRPC health check protocol to probe the server.
Evaluated only if the kind is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type [ExternalName](#externalname-service). | "http" | No | -| `healthCheck.path` | Server URL path for the health check endpoint.
Evaluated only if the kind is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type [ExternalName](#externalname-service). | "" | No | -| `healthCheck.interval` | Frequency of the health check calls for healthy targets.
Evaluated only if the kind is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type [ExternalName](#externalname-service). | "100ms" | No | -| `healthCheck.unhealthyInterval` | Frequency of the health check calls for unhealthy targets.
When not defined, it defaults to the `interval` value.
Evaluated only if the kind is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type [ExternalName](#externalname-service). | "100ms" | No | -| `healthCheck.method` | HTTP method for the health check endpoint.
Evaluated only if the kind is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type [ExternalName](#externalname-service). | "GET" | No | -| `healthCheck.status` | Expected HTTP status code of the response to the health check request.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type ExternalName.
If not set, expect a status between 200 and 399.
Evaluated only if the kind is **Service**. | | No | -| `healthCheck.port` | URL port for the health check endpoint.
Evaluated only if the kind is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type [ExternalName](#externalname-service). | | No | -| `healthCheck.timeout` | Maximum duration to wait before considering the server unhealthy.
Evaluated only if the kind is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type [ExternalName](#externalname-service). | "5s" | No | -| `healthCheck.hostname` | Value in the Host header of the health check request.
Evaluated only if the kind is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type [ExternalName](#externalname-service). | "" | No | -| `healthCheck.`
`followRedirect`
| Follow the redirections during the healtchcheck.
Evaluated only if the kind is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type [ExternalName](#externalname-service). | true | No | -| `healthCheck.headers` | Map of header to send to the health check endpoint
Evaluated only if the kind is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type [ExternalName](#externalname-service)). | | No | -| `sticky.`
`cookie.name`
| Name of the cookie used for the stickiness.
When sticky sessions are enabled, a `Set-Cookie` header is set on the initial response to let the client know which server handles the first response.
On subsequent requests, to keep the session alive with the same server, the client should send the cookie with the value set.
If the server pecified in the cookie becomes unhealthy, the request will be forwarded to a new server (and the cookie will keep track of the new server).
Evaluated only if the kind is **Service**. | "" | No | -| `sticky.`
`cookie.httpOnly`
| Allow the cookie can be accessed by client-side APIs, such as JavaScript.
Evaluated only if the kind is **Service**. | false | No | -| `sticky.`
`cookie.secure`
| Allow the cookie can only be transmitted over an encrypted connection (i.e. HTTPS).
Evaluated only if the kind is **Service**. | false | No | -| `sticky.`
`cookie.sameSite`
| [SameSite](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite) policy
Allowed values:
-`none`
-`lax`
`strict`
Evaluated only if the kind is **Service**. | "" | No | -| `sticky.`
`cookie.maxAge`
| Number of seconds until the cookie expires.
Negative number, the cookie expires immediately.
0, the cookie never expires.
Evaluated only if the kind is **Service**. | 0 | No | -| `strategy` | Load balancing strategy between the servers.
RoundRobin is the only supported value yet.
Evaluated only if the kind is **Service**. | "RoundRobin" | No | -| `nativeLB` | Allow using the Kubernetes Service load balancing between the pods instead of the one provided by Traefik.
Evaluated only if the kind is **Service**. | false | No | -| `nodePortLB` | Use the nodePort IP address when the service type is NodePort.
It allows services to be reachable when Traefik runs externally from the Kubernetes cluster but within the same network of the nodes.
Evaluated only if the kind is **Service**. | false | No | - - -### ExternalName Service - -Traefik backends creation needs a port to be set, however Kubernetes [ExternalName Service](https://kubernetes.io/docs/concepts/services-networking/service/#externalname) could be defined without any port. Accordingly, Traefik supports defining a port in two ways: - -- only on `IngressRoute` service -- on both sides, you'll be warned if the ports don't match, and the `IngressRoute` service port is used - -Thus, in case of two sides port definition, Traefik expects a match between ports. - -=== "Ports defined on Resource" - - ```yaml tab="IngressRoute" - apiVersion: traefik.io/v1alpha1 - kind: IngressRoute - metadata: - name: test.route - namespace: apps - - spec: - entryPoints: - - foo - routes: - - match: Host(`example.net`) - kind: Rule - services: - - name: external-svc - port: 80 - ``` - - ```yaml tab="Service ExternalName" - apiVersion: v1 - kind: Service - metadata: - name: external-svc - namespace: apps - - spec: - externalName: external.domain - type: ExternalName - ``` - -=== "Port defined on the Service" - - ```yaml tab="IngressRoute" - apiVersion: traefik.io/v1alpha1 - kind: IngressRoute - metadata: - name: test.route - namespace: apps - - spec: - entryPoints: - - foo - routes: - - match: Host(`example.net`) - kind: Rule - services: - - name: external-svc - ``` - - ```yaml tab="Service ExternalName" - apiVersion: v1 - kind: Service - metadata: - name: external-svc - namespace: apps - - spec: - externalName: external.domain - type: ExternalName - ports: - - port: 80 - ``` - -=== "Port defined on both sides" - - ```yaml tab="IngressRoute" - apiVersion: traefik.io/v1alpha1 - kind: IngressRoute - metadata: - name: test.route - namespace: apps - - spec: - entryPoints: - - foo - routes: - - match: Host(`example.net`) - kind: Rule - services: - - name: external-svc - port: 80 - ``` - - ```yaml tab="Service ExternalName" - apiVersion: v1 - kind: Service - metadata: - name: external-svc - namespace: apps - - spec: - externalName: external.domain - type: ExternalName - ports: - - port: 80 - ``` - -### Port Definition - -Traefik backends creation needs a port to be set, however Kubernetes [ExternalName Service](https://kubernetes.io/docs/concepts/services-networking/service/#externalname) could be defined without any port. Accordingly, Traefik supports defining a port in two ways: - -- only on `IngressRoute` service -- on both sides, you'll be warned if the ports don't match, and the `IngressRoute` service port is used - -Thus, in case of two sides port definition, Traefik expects a match between ports. - -??? example - - ```yaml tab="IngressRoute" - --- - apiVersion: traefik.io/v1alpha1 - kind: IngressRoute - metadata: - name: test.route - namespace: default - - spec: - entryPoints: - - foo - - routes: - - match: Host(`example.net`) - kind: Rule - services: - - name: external-svc - port: 80 - - --- - apiVersion: v1 - kind: Service - metadata: - name: external-svc - namespace: default - spec: - externalName: external.domain - type: ExternalName - ``` - - ```yaml tab="ExternalName Service" - --- - apiVersion: traefik.io/v1alpha1 - kind: IngressRoute - metadata: - name: test.route - namespace: default - - spec: - entryPoints: - - foo - - routes: - - match: Host(`example.net`) - kind: Rule - services: - - name: external-svc - - --- - apiVersion: v1 - kind: Service - metadata: - name: external-svc - namespace: default - spec: - externalName: external.domain - type: ExternalName - ports: - - port: 80 - ``` - - ```yaml tab="Both sides" - --- - apiVersion: traefik.io/v1alpha1 - kind: IngressRoute - metadata: - name: test.route - namespace: default - - spec: - entryPoints: - - foo - - routes: - - match: Host(`example.net`) - kind: Rule - services: - - name: external-svc - port: 80 - - --- - apiVersion: v1 - kind: Service - metadata: - name: external-svc - namespace: default - spec: - externalName: external.domain - type: ExternalName - ports: - - port: 80 - ``` - -### Load Balancing - -You can declare and use Kubernetes Service load balancing as detailed below: - -```yaml tab="IngressRoute" -apiVersion: traefik.io/v1alpha1 -kind: IngressRoute -metadata: - name: ingressroutebar - namespace: default - -spec: - entryPoints: - - web - routes: - - match: Host(`example.com`) && PathPrefix(`/foo`) - kind: Rule - services: - - name: svc1 - namespace: default - - name: svc2 - namespace: default -``` - -```yaml tab="K8s Service" -apiVersion: v1 -kind: Service -metadata: - name: svc1 - namespace: default - -spec: - ports: - - name: http - port: 80 - selector: - app: traefiklabs - task: app1 ---- -apiVersion: v1 -kind: Service -metadata: - name: svc2 - namespace: default - -spec: - ports: - - name: http - port: 80 - selector: - app: traefiklabs - task: app2 -``` - -!!! important "Kubernetes Service Native Load-Balancing" - - To avoid creating the server load-balancer with the pod IPs and use Kubernetes Service clusterIP directly, - one should set the service `NativeLB` option to true. - Please note that, by default, Traefik reuses the established connections to the backends for performance purposes. This can prevent the requests load balancing between the replicas from behaving as one would expect when the option is set. - By default, `NativeLB` is false. - - ??? example "Example" - - ```yaml - --- - apiVersion: traefik.io/v1alpha1 - kind: IngressRoute - metadata: - name: test.route - namespace: default - - spec: - entryPoints: - - foo - - routes: - - match: Host(`example.net`) - kind: Rule - services: - - name: svc - port: 80 - # Here, nativeLB instructs to build the server load-balancer with the Kubernetes Service clusterIP only. - nativeLB: true - - --- - apiVersion: v1 - kind: Service - metadata: - name: svc - namespace: default - spec: - type: ClusterIP - ... - ``` - -### Configuring Backend Protocol - -There are 3 ways to configure the backend protocol for communication between Traefik and your pods: - -- Setting the scheme explicitly (http/https/h2c) -- Configuring the name of the kubernetes service port to start with https (https) -- Setting the kubernetes service port to use port 443 (https) - -If you do not configure the above, Traefik will assume an http connection. diff --git a/docs/content/reference/routing-configuration/kubernetes/crd/tls/tlsoption.md b/docs/content/reference/routing-configuration/kubernetes/crd/http/tlsoption.md similarity index 53% rename from docs/content/reference/routing-configuration/kubernetes/crd/tls/tlsoption.md rename to docs/content/reference/routing-configuration/kubernetes/crd/http/tlsoption.md index 8ff6515dc..6887e8516 100644 --- a/docs/content/reference/routing-configuration/kubernetes/crd/tls/tlsoption.md +++ b/docs/content/reference/routing-configuration/kubernetes/crd/http/tlsoption.md @@ -48,19 +48,19 @@ spec: | Field | Description | Default | Required | |:----------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:---------------------------|:---------| -| `minVersion` | Minimum TLS version that is acceptable. | "VersionTLS12" | No | -| `maxVersion` | Maximum TLS version that is acceptable.
We do not recommend setting this option to disable TLS 1.3. | | No | -| `cipherSuites` | List of supported [cipher suites](https://godoc.org/crypto/tls#pkg-constants) for TLS versions up to TLS 1.2.
[Cipher suites defined for TLS 1.2 and below cannot be used in TLS 1.3, and vice versa.](https://tools.ietf.org/html/rfc8446)
With TLS 1.3, [the cipher suites are not configurable](https://golang.org/doc/go1.12#tls_1_3) (all supported cipher suites are safe in this case). | | No | -| `curvePreferences` | List of the elliptic curves references that will be used in an ECDHE handshake.
Use curves names from [`crypto`](https://godoc.org/crypto/tls#CurveID) or the [RFC](https://tools.ietf.org/html/rfc8446#section-4.2.7).
See [CurveID](https://godoc.org/crypto/tls#CurveID) for more information. | | No | -| `clientAuth.secretNames` | Client Authentication (mTLS) option.
List of names of the referenced Kubernetes [Secrets](https://kubernetes.io/docs/concepts/configuration/secret/) (in TLSOption namespace).
The secret must contain a certificate under either a `tls.ca` or a `ca.crt` key. | | No | -| `clientAuth.clientAuthType` | Client Authentication (mTLS) option.
Client authentication type to apply. Available values [here](#client-authentication-mtls). | | No | -| `sniStrict` | Allow rejecting connections from clients connections that do not specify a server_name extension.
The [default certificate](../../../http/tls/tls-certificates.md#default-certificate) is never served is the option is enabled. | false | No | -| `alpnProtocols` | List of supported application level protocols for the TLS handshake, in order of preference.
If the client supports ALPN, the selected protocol will be one from this list, and the connection will fail if there is no mutually supported protocol. | "h2, http/1.1, acme-tls/1" | No | -| `disableSessiontTickets` | Allow disabling the use of session tickets, forcing every client to perform a full TLS handshake instead of resuming sessions. | false | No | +| `minVersion` | Minimum TLS version that is acceptable. | "VersionTLS12" | No | +| `maxVersion` | Maximum TLS version that is acceptable.
We do not recommend setting this option to disable TLS 1.3. | | No | +| `cipherSuites` | List of supported [cipher suites](https://godoc.org/crypto/tls#pkg-constants) for TLS versions up to TLS 1.2.
[Cipher suites defined for TLS 1.2 and below cannot be used in TLS 1.3, and vice versa.](https://tools.ietf.org/html/rfc8446)
With TLS 1.3, [the cipher suites are not configurable](https://golang.org/doc/go1.12#tls_1_3) (all supported cipher suites are safe in this case). | | No | +| `curvePreferences` | List of the elliptic curves references that will be used in an ECDHE handshake, in preference order.
Use curves names from [`crypto`](https://godoc.org/crypto/tls#CurveID) or the [RFC](https://tools.ietf.org/html/rfc8446#section-4.2.7).
See [CurveID](https://godoc.org/crypto/tls#CurveID) for more information. | | No | +| `clientAuth.secretNames` | Client Authentication (mTLS) option.
List of names of the referenced Kubernetes [Secrets](https://kubernetes.io/docs/concepts/configuration/secret/) (in TLSOption namespace).
The secret must contain a certificate under either a `tls.ca` or a `ca.crt` key. | | No | +| `clientAuth.clientAuthType` | Client Authentication (mTLS) option.
Client authentication type to apply. Available values [here](#client-authentication-mtls). | | No | +| `sniStrict` | Allow rejecting connections from clients connections that do not specify a server_name extension.
The [default certificate](../../../http/tls/tls-certificates.md#default-certificate) is never served is the option is enabled. | false | No | +| `alpnProtocols` | List of supported application level protocols for the TLS handshake, in order of preference.
If the client supports ALPN, the selected protocol will be one from this list, and the connection will fail if there is no mutually supported protocol. | "h2, http/1.1, acme-tls/1" | No | +| `disableSessiontTickets` | Allow disabling the use of session tickets, forcing every client to perform a full TLS handshake instead of resuming sessions. | false | No | ### Client Authentication (mTLS) -The `clientAuth.clientAuthType` option governs the behavior as follows: +The `clientAuth.clientAuthType` option governs the behaviour as follows: - `NoClientCert`: disregards any client certificate. - `RequestClientCert`: asks for a certificate but proceeds anyway if none is provided. @@ -78,6 +78,6 @@ The default behavior is summed up in the table below: | Configuration | Behavior | |:--------------------------|:------------------------------------------------------------| -| No `default` TLS Option | Default internal set of TLS Options by default. | -| One `default` TLS Option | Custom TLS Options applied by default. | -| Many `default` TLS Option | Error log + Default internal set of TLS Options by default. | +| No `default` TLS Option | Default internal set of TLS Options by default. | +| One `default` TLS Option | Custom TLS Options applied by default. | +| Many `default` TLS Option | Error log + Default internal set of TLS Options by default. | diff --git a/docs/content/reference/routing-configuration/kubernetes/crd/http/tlsstore.md b/docs/content/reference/routing-configuration/kubernetes/crd/http/tlsstore.md new file mode 100644 index 000000000..cab2b1cea --- /dev/null +++ b/docs/content/reference/routing-configuration/kubernetes/crd/http/tlsstore.md @@ -0,0 +1,39 @@ +--- +title: "TLSStore" +description: "TLS Store in Traefik Proxy" +--- + +In Traefik, certificates are grouped together in certificates stores. + +`TLSStore` is the CRD implementation of a [Traefik TLS Store](../../../http/tls/tls-certificates.md#certificates-stores). + +Before creating `TLSStore` objects, you need to apply the [Traefik Kubernetes CRDs](https://doc.traefik.io/traefik/reference/dynamic-configuration/kubernetes-crd/#definitions) to your Kubernetes cluster. + +!!! Tip "Default TLS Store" + Traefik currently only uses the TLS Store named "default". This default `TLSStore` should be in a namespace discoverable by Traefik. Since it is used by default on `IngressRoute` and `IngressRouteTCP` objects, there never is a need to actually reference it. This means that you cannot have two stores that are named default in different Kubernetes namespaces. As a consequence, with respect to TLS stores, the only change that makes sense (and only if needed) is to configure the default `TLSStore`. + +## Configuration Example + +```yaml tab="TLSStore" +apiVersion: traefik.io/v1alpha1 +kind: TLSStore +metadata: + name: default + +spec: + defaultCertificate: + secretName: supersecret +``` + +## Configuration Options + +| Field | Description | Required | +|:---------------------------------------|:-------------------------|:---------| +| `certificates[n].secretName` | List of Kubernetes [Secrets](https://kubernetes.io/docs/concepts/configuration/secret/), each of them holding a key/certificate pair to add to the store. | No | +| `defaultCertificate.secretName` | Name of the Kubernetes [Secret](https://kubernetes.io/docs/concepts/configuration/secret/) served for connections without a SNI, or without a matching domain. If no default certificate is provided, Traefik will use the generated one. Do not use if the option `defaultGeneratedCert` is set. | No | +| `defaultGeneratedCert.resolver` | Name of the ACME resolver to use to generate the default certificate.
Do not use if the option `defaultCertificate` is set. | No | +| `defaultGeneratedCert.domain.main` | Main domain used to generate the default certificate.
Do not use if the option `defaultCertificate` is set. | No | +| `defaultGeneratedCert.domain.sans` | List of [Subject Alternative Name](https://en.wikipedia.org/wiki/Subject_Alternative_Name) used to generate the default certificate.
Do not use if the option `defaultCertificate` is set. | No | + +!!! note "DefaultCertificate vs DefaultGeneratedCert" + If both `defaultCertificate` and `defaultGeneratedCert` are set, the TLS certificate contained in `defaultCertificate.secretName` is served. The ACME default certificate is not generated. diff --git a/docs/content/reference/routing-configuration/kubernetes/crd/http/traefikservice.md b/docs/content/reference/routing-configuration/kubernetes/crd/http/traefikservice.md index 55bae513e..acda27c48 100644 --- a/docs/content/reference/routing-configuration/kubernetes/crd/http/traefikservice.md +++ b/docs/content/reference/routing-configuration/kubernetes/crd/http/traefikservice.md @@ -148,15 +148,42 @@ data: ### Configuration Options -| Field | Description | Default | Required | -|:---------------------------------------------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:---------------------------------------------------------------------|:---------| -| `services` | List of any combination of TraefikService and [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/).
. Exhaustive list of option in the [`Service`](./service.md#configuration-options) documentation. | | No | -| `services[m].weight` | Service weight. | "" | No | -| `sticky.`
`cookie.name`
| Name of the cookie used for the stickiness at the WRR service level.
When sticky sessions are enabled, a `Set-Cookie` header is set on the initial response to let the client know which server handles the first response.
On subsequent requests, to keep the session alive with the same server, the client should send the cookie with the value set.
If the server pecified in the cookie becomes unhealthy, the request will be forwarded to a new server (and the cookie will keep track of the new server).
More information about WRR stickiness [here](#stickiness-on-multiple-levels) | Abbreviation of a sha1
(ex: `_1d52e`). | No | -| `sticky.`
`cookie.httpOnly`
| Allow the cookie used for the stickiness at the WRR service level to be accessed by client-side APIs, such as JavaScript.
More information about WRR stickiness [here](#stickiness-on-multiple-levels) | false | No | -| `sticky.`
`cookie.secure`
| Allow the cookie used for the stickiness at the WRR service level to be only transmitted over an encrypted connection (i.e. HTTPS).
More information about WRR stickiness [here](#stickiness-on-multiple-levels) | false | No | -| `sticky.`
`cookie.sameSite`
| [SameSite](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite) policy for the cookie used for the stickiness at the WRR service level.
Allowed values:
-`none`
-`lax`
`strict`
More information about WRR stickiness [here](#stickiness-on-multiple-levels) | "" | No | -| `sticky.`
`cookie.maxAge`
| Number of seconds until the cookie used for the stickiness at the WRR service level expires.
Negative number, the cookie expires immediately.
0, the cookie never expires. | 0 | No | +| Field | Description | Default | Required | +|:------|:----------------------------------------------------------|:---------------------|:---------| +| `services` | List of any combination of TraefikService and [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/).
. | | No | +| `services[m].`
`kind` | Kind of the service targeted.
Two values allowed:
- **Service**: Kubernetes Service
- **TraefikService**: Traefik Service. | "" | No | +| `services[m].`
`name` | Service name.
The character `@` is not authorized. | "" | Yes | +| `services[m].`
`namespace` | Service namespace. | "" | No | +| `services[m].`
`port` | Service port (number or port name).
Evaluated only if the kind is **Service**. | "" | No | +| `services[m].`
`responseForwarding.`
`flushInterval` | Interval, in milliseconds, in between flushes to the client while copying the response body.
A negative value means to flush immediately after each write to the client.
This configuration is ignored when a response is a streaming response; for such responses, writes are flushed to the client immediately.
Evaluated only if the kind is **Service**. | 100ms | No | +| `services[m].`
`scheme` | Scheme to use for the request to the upstream Kubernetes Service.
Evaluated only if the kind is **Service**. | "http"
"https" if `port` is 443 or contains the string *https*. | No | +| `services[m].`
`serversTransport` | Name of ServersTransport resource to use to configure the transport between Traefik and your servers.
Evaluated only if the kind is **Service**. | "" | No | +| `services[m].`
`passHostHeader` | Forward client Host header to server.
Evaluated only if the kind is **Service**. | true | No | +| `services[m].`
`healthCheck.scheme` | Server URL scheme for the health check endpoint.
Evaluated only if the kind is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type `ExternalName`. | "" | No | +| `services[m].`
`healthCheck.mode` | Health check mode.
If defined to grpc, will use the gRPC health check protocol to probe the server.
Evaluated only if the kind is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type `ExternalName`. | "http" | No | +| `services[m].`
`healthCheck.path` | Server URL path for the health check endpoint.
Evaluated only if the kind is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type `ExternalName`. | "" | No | +| `services[m].`
`healthCheck.interval` | Frequency of the health check calls.
Evaluated only if the kind is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type [ExternalName]`ExternalName`. | "100ms" | No | +| `services[m].`
`healthCheck.method` | HTTP method for the health check endpoint.
Evaluated only if the kind is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type `ExternalName`. | "GET" | No | +| `services[m].`
`healthCheck.status` | Expected HTTP status code of the response to the health check request.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type ExternalName.
If not set, expect a status between 200 and 399.
Evaluated only if the kind is **Service**. | | No | +| `services[m].`
`healthCheck.port` | URL port for the health check endpoint.
Evaluated only if the kind is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type `ExternalName`. | | No | +| `services[m].`
`healthCheck.timeout` | Maximum duration to wait before considering the server unhealthy.
Evaluated only if the kind is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type `ExternalName`. | "5s" | No | +| `services[m].`
`healthCheck.hostname` | Value in the Host header of the health check request.
Evaluated only if the kind is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type `ExternalName`. | "" | No | +| `services[m].`
`healthCheck.`
`followRedirect` | Follow the redirections during the healtchcheck.
Evaluated only if the kind is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type `ExternalName`. | true | No | +| `services[m].`
`healthCheck.headers` | Map of header to send to the health check endpoint
Evaluated only if the kind is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type `ExternalName`. | | No | +| `services[m].`
`sticky.`
`cookie.name` | Name of the cookie used for the stickiness.
Evaluated only if the kind is **Service**. | Abbreviation of a sha1
(ex: `_1d52e`). | No | +| `services[m].`
`sticky.`
`cookie.httpOnly` | Allow the cookie can be accessed by client-side APIs, such as JavaScript.
Evaluated only if the kind is **Service**. | false | No | +| `services[m].`
`sticky.`
`cookie.secure` | Allow the cookie can only be transmitted over an encrypted connection (i.e. HTTPS).
Evaluated only if the kind is **Service**. | false | No | +| `services[m].`
`sticky.`
`cookie.sameSite` | [SameSite](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite) policy.
Allowed values:
-`none`
-`lax`
`strict`
Evaluated only if the kind is **Service**. | "" | No | +| `services[m].`
`sticky.`
`cookie.maxAge` | Number of seconds until the cookie expires.
Negative number, the cookie expires immediately.
0, the cookie never expires.
Evaluated only if the kind is **Service**. | 0 | No | +| `services[m].`
`strategy` | Load balancing strategy between the servers.
RoundRobin is the only supported value yet.
Evaluated only if the kind is **Service**. | "RoundRobin" | No | +| `services[m].`
`weight` | Service weight.
To use only to refer to WRR TraefikService | "" | No | +| `services[m].`
`nativeLB` | Allow using the Kubernetes Service load balancing between the pods instead of the one provided by Traefik.
Evaluated only if the kind is **Service**. | false | No | +| `services[m].`
`nodePortLB` | Use the nodePort IP address when the service type is NodePort.
It allows services to be reachable when Traefik runs externally from the Kubernetes cluster but within the same network of the nodes.
Evaluated only if the kind is **Service**. | false | No | +| `sticky.`
`cookie.name` | Name of the cookie used for the stickiness at the WRR service level.
When sticky sessions are enabled, a `Set-Cookie` header is set on the initial response to let the client know which server handles the first response.
On subsequent requests, to keep the session alive with the same server, the client should send the cookie with the value set.
If the server pecified in the cookie becomes unhealthy, the request will be forwarded to a new server (and the cookie will keep track of the new server).
More information about WRR stickiness [here](#stickiness-on-multiple-levels) | Abbreviation of a sha1
(ex: `_1d52e`). | No | +| `sticky.`
`cookie.httpOnly` | Allow the cookie used for the stickiness at the WRR service level to be accessed by client-side APIs, such as JavaScript.
More information about WRR stickiness [here](#stickiness-on-multiple-levels) | false | No | +| `sticky.`
`cookie.secure` | Allow the cookie used for the stickiness at the WRR service level to be only transmitted over an encrypted connection (i.e. HTTPS).
More information about WRR stickiness [here](#stickiness-on-multiple-levels) | false | No | +| `sticky.`
`cookie.sameSite` | [SameSite](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite) policy for the cookie used for the stickiness at the WRR service level.
Allowed values:
-`none`
-`lax`
`strict`
More information about WRR stickiness [here](#stickiness-on-multiple-levels) | "" | No | +| `sticky.`
`cookie.maxAge` | Number of seconds until the cookie used for the stickiness at the WRR service level expires.
Negative number, the cookie expires immediately.
0, the cookie never expires. | 0 | No | #### Stickiness on multiple levels @@ -277,8 +304,6 @@ spec: mirroring: name: svc1 # svc1 receives 100% of the traffic port: 80 - mirrorBody: true # Set to false by default - maxBodySize: 1M mirrors: - name: svc2 # svc2 receives a copy of 20% of this traffic port: 80 @@ -341,31 +366,71 @@ spec: ### Configuration Options -#### Main Service Options +!!!note "Main and mirrored services" -The main service properties are set as the option root level. + The main service properties are set as the option root level. -The main service provides the same options as a [`Service`](./service.md). + The mirrored services properties are set in the `mirrors` list. -The exhaustive list of the service options is described in the [`Service`](./service.md#configuration-options) documentation. -The mirror main service dedicated option are described below. - -| Field | Description | Default | Required | -|:--------------------------------------------------------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:---------------------------------------------------------------------|:---------| -| `mirrorBody` | Defines whether the request body should be mirrored. | true | No | -| `maxBodySize` | Maximum size allowed for the body of the request.
If the body is larger, the request is not mirrored.
-1 means unlimited size. | -1 | No | -| `mirrors` | List of mirrored services to target.
It can be any combination of TraefikService and [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/).
Exhaustive list of option in the [`Service`](./service.md#configuration-options) documentation. | | Yes | - -#### Mirrored Services Options - -The mirrored services properties are set in the `mirrors` list. - -A mirrored service provides the same options as a [`Service`](./service.md). - -The exhaustive list of the service options is described in the [`Service`](./service.md#configuration-options) documentation. -The mirrorerd service dedicated option are described below. - - -| Field | Description | Default | Required | -|:--------------------------------------------------------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:---------------------------------------------------------------------|:---------| -| `mirrors[m].percent` | Traffic percentage to route to the service. | 0 | No | +| Field | Description | Default | Required | +|:------|:----------------------------------------------------------|:---------------------|:---------| +| `kind` | Kind of the main service.
Two values allowed:
- **Service**: Kubernetes Service
- **TraefikService**: Traefik Service.
More information [here](#services) | "" | No | +| `name` | Main service name.
The character `@` is not authorized. | "" | Yes | +| `namespace` | Main service namespace.
More information [here](#services). | "" | No | +| `port` | Main service port (number or port name).
Evaluated only if the kind of the main service is **Service**. | "" | No | +| `responseForwarding.`
`flushInterval` | Interval, in milliseconds, in between flushes to the client while copying the response body.
A negative value means to flush immediately after each write to the client.
This configuration is ignored when a response is a streaming response; for such responses, writes are flushed to the client immediately.
Evaluated only if the kind of the main service is **Service**. | 100ms | No | +| `scheme` | Scheme to use for the request to the upstream Kubernetes Service.
Evaluated only if the kind of the main service is **Service**. | "http"
"https" if `port` is 443 or contains the string *https*. | No | +| `serversTransport` | Name of ServersTransport resource to use to configure the transport between Traefik and the main service's servers.
Evaluated only if the kind of the main service is **Service**. | "" | No | +| `passHostHeader` | Forward client Host header to main service's server.
Evaluated only if the kind of the main service is **Service**. | true | No | +| `healthCheck.scheme` | Server URL scheme for the health check endpoint.
Evaluated only if the kind of the main service is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type [ExternalName](#services). | "" | No | +| `healthCheck.mode` | Health check mode.
If defined to grpc, will use the gRPC health check protocol to probe the server.
Evaluated only if the kind of the main service is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type [ExternalName](#services). | "http" | No | +| `healthCheck.path` | Server URL path for the health check endpoint.
Evaluated only if the kind of the main service is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type [ExternalName](#services). | "" | No | +| `healthCheck.interval` | Frequency of the health check calls.
Evaluated only if the kind of the main service is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type [ExternalName](#services). | "100ms" | No | +| `healthCheck.method` | HTTP method for the health check endpoint.
Evaluated only if the kind of the main service is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type [ExternalName](#services). | "GET" | No | +| `healthCheck.status` | Expected HTTP status code of the response to the health check request.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type ExternalName.
If not set, expect a status between 200 and 399.
Evaluated only if the kind of the main service is **Service**. | | No | +| `healthCheck.port` | URL port for the health check endpoint.
Evaluated only if the kind of the main service is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type [ExternalName](#services). | | No | +| `healthCheck.timeout` | Maximum duration to wait before considering the server unhealthy.
Evaluated only if the kind of the main service is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type [ExternalName](#services). | "5s" | No | +| `healthCheck.hostname` | Value in the Host header of the health check request.
Evaluated only if the kind of the main service is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type [ExternalName](#services). | "" | No | +| `healthCheck.`
`followRedirect` | Follow the redirections during the healtchcheck.
Evaluated only if the kind of the main service is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type [ExternalName](#services). | true | No | +| `healthCheck.headers` | Map of header to send to the health check endpoint
Evaluated only if the kind of the main service is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type [ExternalName](#services). | | No | +| `sticky.`
`cookie.name` | Name of the cookie used for the stickiness on the main service.
Evaluated only if the kind of the main service is **Service**. | Abbreviation of a sha1
(ex: `_1d52e`). | No | +| `sticky.`
`cookie.httpOnly` | Allow the cookie can be accessed by client-side APIs, such as JavaScript.
Evaluated only if the kind of the main service is **Service**. | false | No | +| `sticky.`
`cookie.secure` | Allow the cookie can only be transmitted over an encrypted connection (i.e. HTTPS).
Evaluated only if the kind of the main service is **Service**. | false | No | +| `sticky.`
`cookie.sameSite` | [SameSite](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite) policy.
Allowed values:
-`none`
-`lax`
`strict`
Evaluated only if the kind of the main service is **Service**. | "" | No | +| `sticky.`
`cookie.maxAge` | Number of seconds until the cookie expires.
Negative number, the cookie expires immediately.
0, the cookie never expires.
Evaluated only if the kind of the main service is **Service**. | 0 | No | +| `strategy` | Load balancing strategy between the main service's servers.
RoundRobin is the only supported value yet.
Evaluated only if the kind of the main service is **Service**. | "RoundRobin" | No | +| `weight` | Service weight.
To use only to refer to WRR TraefikService | "" | No | +| `nativeLB` | Allow using the Kubernetes Service load balancing between the pods instead of the one provided by Traefik.
Evaluated only if the kind of the main service is **Service**. | false | No | +| `nodePortLB` | Use the nodePort IP address when the service type is NodePort.
It allows services to be reachable when Traefik runs externally from the Kubernetes cluster but within the same network of the nodes.
Evaluated only if the kind of the main service is **Service**. | false | No | +| `maxBodySize` | Maximum size allowed for the body of the request.
If the body is larger, the request is not mirrored.
-1 means unlimited size. | -1 | No | +| `mirrors` | List of mirrored services to target.
It can be any combination of TraefikService and [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/).
More information [here](#services). | | No | +| `mirrors[m].`
`kind` | Kind of the mirrored service targeted.
Two values allowed:
- **Service**: Kubernetes Service
- **TraefikService**: Traefik Service.
More information [here](#services) | "" | No | +| `mirrors[m].`
`name` | Mirrored service name.
The character `@` is not authorized. | "" | Yes | +| `mirrors[m].`
`namespace` | Mirrored service namespace.
More information [here](#services). | "" | No | +| `mirrors[m].`
`port` | Mirrored service port (number or port name).
Evaluated only if the kind of the mirrored service is **Service**. | "" | No | +| `mirrors[m].`
`percent` | Part of the traffic to mirror in percent (from 0 to 100) | 0 | No | +| `mirrors[m].`
`responseForwarding.`
`flushInterval` | Interval, in milliseconds, in between flushes to the client while copying the response body.
A negative value means to flush immediately after each write to the client.
This configuration is ignored when a response is a streaming response; for such responses, writes are flushed to the client immediately.
Evaluated only if the kind of the mirrored service is **Service**. | 100ms | No | +| `mirrors[m].`
`scheme` | Scheme to use for the request to the mirrored service.
Evaluated only if the kind of the mirrored service is **Service**. | "http"
"https" if `port` is 443 or contains the string *https*. | No | +| `mirrors[m].`
`serversTransport` | Name of ServersTransport resource to use to configure the transport between Traefik and the mirrored service servers.
Evaluated only if the kind of the mirrored service is **Service**. | "" | No | +| `mirrors[m].`
`passHostHeader` | Forward client Host header to the mirrored service servers.
Evaluated only if the kind of the mirrored service is **Service**. | true | No | +| `mirrors[m].`
`healthCheck.scheme` | Server URL scheme for the health check endpoint.
Evaluated only if the kind of the mirrored service is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type [ExternalName](#services). | "" | No | +| `mirrors[m].`
`healthCheck.mode` | Health check mode.
If defined to grpc, will use the gRPC health check protocol to probe the server.
Evaluated only if the kind of the mirrored service is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type [ExternalName](#services). | "http" | No | +| `mirrors[m].`
`healthCheck.path` | Server URL path for the health check endpoint.
Evaluated only if the kind of the mirrored service is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type [ExternalName](#services). | "" | No | +| `mirrors[m].`
`healthCheck.interval` | Frequency of the health check calls.
Evaluated only if the kind of the mirrored service is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type [ExternalName](#services). | "100ms" | No | +| `mirrors[m].`
`healthCheck.method` | HTTP method for the health check endpoint.
Evaluated only if the kind of the mirrored service is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type [ExternalName](#services). | "GET" | No | +| `mirrors[m].`
`healthCheck.status` | Expected HTTP status code of the response to the health check request.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type ExternalName.
If not set, expect a status between 200 and 399.
Evaluated only if the kind of the mirrored service is **Service**. | | No | +| `mirrors[m].`
`healthCheck.port` | URL port for the health check endpoint.
Evaluated only if the kind of the mirrored service is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type [ExternalName](#services). | | No | +| `mirrors[m].`
`healthCheck.timeout` | Maximum duration to wait before considering the server unhealthy.
Evaluated only if the kind of the mirrored service is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type [ExternalName](#services). | "5s" | No | +| `mirrors[m].`
`healthCheck.hostname` | Value in the Host header of the health check request.
Evaluated only if the kind of the mirrored service is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type [ExternalName](#services). | "" | No | +| `mirrors[m].`
`healthCheck.`
`followRedirect` | Follow the redirections during the healtchcheck.
Evaluated only if the kind of the mirrored service is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type [ExternalName](#services). | true | No | +| `mirrors[m].`
`healthCheck.headers` | Map of header to send to the health check endpoint
Evaluated only if the kind of the mirrored service is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type [ExternalName](#services). | | No | +| `mirrors[m].`
`sticky.`
`cookie.name` | Name of the cookie used for the stickiness.
When sticky sessions are enabled, a `Set-Cookie` header is set on the initial response to let the client know which server handles the first response.
On subsequent requests, to keep the session alive with the same server, the client should send the cookie with the value set.
If the server pecified in the cookie becomes unhealthy, the request will be forwarded to a new server (and the cookie will keep track of the new server).
Evaluated only if the kind of the mirrored service is **Service**. | "" | No | +| `mirrors[m].`
`sticky.`
`cookie.httpOnly` | Allow the cookie can be accessed by client-side APIs, such as JavaScript.
Evaluated only if the kind of the mirrored service is **Service**. | false | No | +| `mirrors[m].`
`sticky.`
`cookie.secure` | Allow the cookie can only be transmitted over an encrypted connection (i.e. HTTPS).
Evaluated only if the kind of the mirrored service is **Service**. | false | No | +| `mirrors[m].`
`sticky.`
`cookie.sameSite` | [SameSite](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite) policy.
Allowed values:
-`none`
-`lax`
`strict`
Evaluated only if the kind of the mirrored service is **Service**. | "" | No | +| `mirrors[m].`
`sticky.`
`cookie.maxAge` | Number of seconds until the cookie expires.
Negative number, the cookie expires immediately.
0, the cookie never expires.
Evaluated only if the kind of the mirrored service is **Service**. | 0 | No | +| `mirrors[m].`
`strategy` | Load balancing strategy between the servers.
RoundRobin is the only supported value yet.
Evaluated only if the kind of the mirrored service is **Service**. | "RoundRobin" | No | +| `mirrors[m].`
`weight` | Service weight.
To use only to refer to WRR TraefikService | "" | No | +| `mirrors[m].`
`nativeLB` | Allow using the Kubernetes Service load balancing between the pods instead of the one provided by Traefik.
Evaluated only if the kind of the mirrored service is **Service**. | false | No | +| `mirrors[m].`
`nodePortLB` | Use the nodePort IP address when the service type is NodePort.
It allows services to be reachable when Traefik runs externally from the Kubernetes cluster but within the same network of the nodes.
Evaluated only if the kind of the mirrored service is **Service**. | false | No | +| `mirrorBody` | Defines whether the request body should be mirrored. | true | No | diff --git a/docs/content/reference/routing-configuration/kubernetes/crd/tcp/ingressroutetcp.md b/docs/content/reference/routing-configuration/kubernetes/crd/tcp/ingressroutetcp.md index 4ae214dae..44598e18a 100644 --- a/docs/content/reference/routing-configuration/kubernetes/crd/tcp/ingressroutetcp.md +++ b/docs/content/reference/routing-configuration/kubernetes/crd/tcp/ingressroutetcp.md @@ -3,7 +3,7 @@ title: "Kubernetes IngressRouteTCP" description: "An IngressRouteTCP is a Traefik CRD is in charge of connecting incoming TCP connections to the Services that can handle them." --- -`IngressRouteTCP` is the CRD implementation of a [Traefik TCP router](../../../tcp/routing/rules-and-priority.md). +`IngressRouteTCP` is the CRD implementation of a [Traefik TCP router](../../../tcp/router/rules-and-priority.md). Before creating `IngressRouteTCP` objects, you need to apply the [Traefik Kubernetes CRDs](https://doc.traefik.io/traefik/reference/dynamic-configuration/kubernetes-crd/#definitions) to your Kubernetes cluster. @@ -16,7 +16,7 @@ This registers the `IngressRouteTCP` kind and other Traefik-specific resources. You can declare an `IngressRouteTCP` as detailed below: -```yaml tab="IngressRouteTCP" +```yaml tab="IngressRoute" apiVersion: traefik.io/v1alpha1 kind: IngressRouteTCP metadata: @@ -36,9 +36,12 @@ spec: - name: foo port: 8080 weight: 10 + proxyProtocol: + version: 1 serversTransport: transport nativeLB: true nodePortLB: true + tls: false tls: secretName: supersecret @@ -56,33 +59,33 @@ spec: ## Configuration Options -| Field | Description | Default | Required | -|-------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------|-----------------------| -| `entryPoints` | List of entrypoints names. | | No | -| `routes` | List of routes. | | Yes | -| `routes[n].match` | Defines the [rule](../../../tcp/routing/rules-and-priority.md#rules) of the underlying router. | | Yes | -| `routes[n].priority` | Defines the [priority](../../../tcp/routing/rules-and-priority.md#priority-calculation) to disambiguate rules of the same length, for route matching. | | No | -| `routes[n].middlewares[n].name` | Defines the [MiddlewareTCP](./middlewaretcp.md) name. | | Yes | -| `routes[n].middlewares[n].namespace` | Defines the [MiddlewareTCP](./middlewaretcp.md) namespace. | ""| No| -| `routes[n].services` | List of [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) definitions. | | No | -| `routes[n].services[n].name` | Defines the name of a [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/). | | Yes | -| `routes[n].services[n].port` | Defines the port of a [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/). This can be a reference to a named port. | | Yes | -| `routes[n].services[n].weight` | Defines the weight to apply to the server load balancing. | 1 | No | -| `routes[n].services[n].proxyProtocol` | Defines the [PROXY protocol](../../../../install-configuration/entrypoints.md#proxyprotocol-and-load-balancers) configuration. | | No | -| `routes[n].services[n].proxyProtocol.version` | Defines the [PROXY protocol](../../../../install-configuration/entrypoints.md#proxyprotocol-and-load-balancers) version. | | No | -| `routes[n].services[n].serversTransport` | Defines the [ServersTransportTCP](./serverstransporttcp.md).
The `ServersTransport` namespace is assumed to be the [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) namespace. | | No | -| `routes[n].services[n].nativeLB` | Controls, when creating the load-balancer, whether the LB's children are directly the pods IPs or if the only child is the Kubernetes Service clusterIP. See [here](#nativelb) for more information. | false | No | -| `routes[n].services[n].nodePortLB` | Controls, when creating the load-balancer, whether the LB's children are directly the nodes internal IPs using the nodePort when the service type is `NodePort`. It allows services to be reachable when Traefik runs externally from the Kubernetes cluster but within the same network of the nodes. | false | No | -| `tls` | Defines [TLS](../../../../install-configuration/tls/certificate-resolvers/overview.md) certificate configuration. | | No | -| `tls.secretName` | Defines the [secret](https://kubernetes.io/docs/concepts/configuration/secret/) name used to store the certificate (in the `IngressRoute` namespace). | "" | No | -| `tls.options` | Defines the reference to a [TLSOption](../tls/tlsoption.md). | "" | No | -| `tls.options.name` | Defines the [TLSOption](../tls/tlsoption.md) name. | "" | No | -| `tls.options.namespace` | Defines the [TLSOption](../tls/tlsoption.md) namespace. | "" | No | -| `tls.certResolver` | Defines the reference to a [CertResolver](../../../../install-configuration/tls/certificate-resolvers/overview.md). | "" | No | -| `tls.domains` | List of domains. | "" | No | -| `tls.domains[n].main` | Defines the main domain name. | "" | No | -| `tls.domains[n].sans` | List of SANs (alternative domains). | "" | No | -| `tls.passthrough` | If `true`, delegates the TLS termination to the backend. | false | No | +| Field | Description | Default | Required | +|-------------------------------------|-----------------------------|-------------------------------------------|-----------------------| +| `entryPoints` | List of entrypoints names. | | No | +| `routes` | List of routes. | | Yes | +| `routes[n].match` | Defines the [rule](../../../tcp/router/rules-and-priority.md#rules) of the underlying router. | | Yes | +| `routes[n].priority` | Defines the [priority](../../../tcp/router/rules-and-priority.md#priority) to disambiguate rules of the same length, for route matching. | | No | +| `routes[n].middlewares[n].name` | Defines the [MiddlewareTCP](./middlewaretcp.md) name. | | Yes | +| `routes[n].middlewares[n].namespace` | Defines the [MiddlewareTCP](./middlewaretcp.md) namespace. | ""| No| +| `routes[n].services` | List of [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) definitions. | | No | +| `routes[n].services[n].name` | Defines the name of a [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/). | | Yes | +| `routes[n].services[n].port` | Defines the port of a [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/). This can be a reference to a named port.| | Yes | +| `routes[n].services[n].weight` | Defines the weight to apply to the server load balancing. | 1 | No | +| `routes[n].services[n].proxyProtocol` | Defines the [PROXY protocol](../../../../install-configuration/entrypoints.md#proxyprotocol-and-load-balancers) configuration. | | No | +| `routes[n].services[n].proxyProtocol.version` | Defines the [PROXY protocol](../../../../install-configuration/entrypoints.md#proxyprotocol-and-load-balancers) version. | | No | +| `routes[n].services[n].serversTransport` | Defines the [ServersTransportTCP](./serverstransporttcp.md).
The `ServersTransport` namespace is assumed to be the [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) namespace. | | No | +| `routes[n].services[n].nativeLB` | Controls, when creating the load-balancer, whether the LB's children are directly the pods IPs or if the only child is the Kubernetes Service clusterIP. See [here](#nativelb) for more information. | false | No | +| `routes[n].services[n].nodePortLB` | Controls, when creating the load-balancer, whether the LB's children are directly the nodes internal IPs using the nodePort when the service type is `NodePort`. It allows services to be reachable when Traefik runs externally from the Kubernetes cluster but within the same network of the nodes. | false | No | +| `tls` | Defines [TLS](../../../../install-configuration/tls/certificate-resolvers/overview.md) certificate configuration. | | No | +| `tls.secretName` | Defines the [secret](https://kubernetes.io/docs/concepts/configuration/secret/) name used to store the certificate (in the `IngressRoute` namespace). | "" | No | +| `tls.options` | Defines the reference to a [TLSOption](../http/tlsoption.md). | "" | No | +| `tls.options.name` | Defines the [TLSOption](../http/tlsoption.md) name. | "" | No | +| `tls.options.namespace` | Defines the [TLSOption](../http/tlsoption.md) namespace. | "" | No | +| `tls.certResolver` | Defines the reference to a [CertResolver](../../../../install-configuration/tls/certificate-resolvers/overview.md). | "" | No | +| `tls.domains` | List of domains. | "" | No | +| `tls.domains[n].main` | Defines the main domain name. | "" | No | +| `tls.domains[n].sans` | List of SANs (alternative domains). | "" | No | +| `tls.passthrough` | If `true`, delegates the TLS termination to the backend. | false | No | ### ExternalName Service diff --git a/docs/content/reference/routing-configuration/kubernetes/crd/tcp/serverstransporttcp.md b/docs/content/reference/routing-configuration/kubernetes/crd/tcp/serverstransporttcp.md index 456ed9d2b..7e275df61 100644 --- a/docs/content/reference/routing-configuration/kubernetes/crd/tcp/serverstransporttcp.md +++ b/docs/content/reference/routing-configuration/kubernetes/crd/tcp/serverstransporttcp.md @@ -29,9 +29,6 @@ metadata: namespace: default spec: - proxyProtocol: - version: 2 - terminationDelay: 100ms tls: serverName: example.org insecureSkipVerify: true @@ -39,18 +36,16 @@ spec: ## Configuration Options -| Field | Description | Default | Required | -|---------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------|----------| -| `dialTimeout` | The amount of time to wait until a connection to a server can be established. If zero, no timeout exists. | 30s | No | -| `dialKeepAlive` | The interval between keep-alive probes for an active network connection.
If this option is set to zero, keep-alive probes are sent with a default value (currently 15 seconds),
if supported by the protocol and operating system. Network protocols or operating systems that do not support keep-alives ignore this field.
If negative, keep-alive probes are turned off. | 15s | No | -| `proxyProtocol` | Defines the Proxy Protocol configuration. An empty `proxyProtocol` section enables Proxy Protocol version 2. | | No | -| `proxyProtocol.version` | Traefik supports PROXY Protocol version 1 and 2 on TCP Services. | | No | -| `terminationDelay` | Defines the delay to wait before fully terminating the connection, after one connected peer has closed its writing capability. | 100ms | No | -| `tls.serverName` | ServerName used to contact the server. | "" | No | -| `tls.insecureSkipVerify` | Controls whether the server's certificate chain and host name is verified. | false | No | -| `tls.peerCertURI` | Defines the URI used to match against SAN URIs during the server's certificate verification. | "" | No | -| `tls.rootCAsSecrets` | Defines the set of root certificate authorities to use when verifying server certificates.
The CA secret must contain a base64 encoded certificate under either a `tls.ca` or a `ca.crt` key. | "" | No | -| `tls.certificatesSecrets` | Certificates to present to the server for mTLS. | "" | No | -| `spiffe` | Configures [SPIFFE](../../../../install-configuration/tls/spiffe.md) options. | "" | No | -| `spiffe.ids` | Defines the allowed SPIFFE IDs. This takes precedence over the SPIFFE `trustDomain`. | "" | No | -| `spiffe.trustDomain` | Defines the allowed SPIFFE trust domain. | "" | No | +| Field | Description | Default | Required | +|-------------------------------------|-----------------------------|-------------------------------------------|-----------------------| +| `dialTimeout` | The amount of time to wait until a connection to a server can be established. If zero, no timeout exists. | 30s | No | +| `dialKeepAlive` | The interval between keep-alive probes for an active network connection.
If this option is set to zero, keep-alive probes are sent with a default value (currently 15 seconds),
if supported by the protocol and operating system. Network protocols or operating systems that do not support keep-alives ignore this field.
If negative, keep-alive probes are turned off.| 15s | No | +| `terminationDelay` | Defines the delay to wait before fully terminating the connection, after one connected peer has closed its writing capability.| 100ms | No | +| `tls.serverName` | ServerName used to contact the server. | "" | No | +| `tls.insecureSkipVerify` | Controls whether the server's certificate chain and host name is verified. | false | No | +| `tls.peerCertURI` | Defines the URI used to match against SAN URIs during the server's certificate verification. | "" | No | +| `tls.rootCAsSecrets` | Defines the set of root certificate authorities to use when verifying server certificates.
The CA secret must contain a base64 encoded certificate under either a `tls.ca` or a `ca.crt` key.| "" | No | +| `tls.certificatesSecrets` | Certificates to present to the server for mTLS.| "" | No | +| `spiffe` | Configures [SPIFFE](../../../../install-configuration/tls/spiffe.md) options. | "" | No | +| `spiffe.ids` | Defines the allowed SPIFFE IDs. This takes precedence over the SPIFFE `trustDomain`. |""| No | +| `spiffe.trustDomain` | Defines the allowed SPIFFE trust domain. | "" | No | diff --git a/docs/content/reference/routing-configuration/kubernetes/crd/tcp/tlsoption.md b/docs/content/reference/routing-configuration/kubernetes/crd/tcp/tlsoption.md new file mode 100644 index 000000000..4d4db1b01 --- /dev/null +++ b/docs/content/reference/routing-configuration/kubernetes/crd/tcp/tlsoption.md @@ -0,0 +1,6 @@ +--- +title: "TLSOption" +description: "TLS Options in Traefik Proxy" +--- + +--8<-- "content/reference/routing-configuration/kubernetes/crd/http/tlsoption.md" diff --git a/docs/content/reference/routing-configuration/kubernetes/crd/tcp/tlsstore.md b/docs/content/reference/routing-configuration/kubernetes/crd/tcp/tlsstore.md new file mode 100644 index 000000000..27c98164a --- /dev/null +++ b/docs/content/reference/routing-configuration/kubernetes/crd/tcp/tlsstore.md @@ -0,0 +1,6 @@ +--- +title: "TLSStore" +description: "TLS Store in Traefik Proxy" +--- + +--8<-- "content/reference/routing-configuration/kubernetes/crd/http/tlsstore.md" diff --git a/docs/content/reference/routing-configuration/kubernetes/crd/tls/tlsstore.md b/docs/content/reference/routing-configuration/kubernetes/crd/tls/tlsstore.md deleted file mode 100644 index f4c99c805..000000000 --- a/docs/content/reference/routing-configuration/kubernetes/crd/tls/tlsstore.md +++ /dev/null @@ -1,39 +0,0 @@ ---- -title: "TLSStore" -description: "TLS Store in Traefik Proxy" ---- - -In Traefik, certificates are grouped together in certificates stores. - -`TLSStore` is the CRD implementation of a [Traefik TLS Store](../../../http/tls/tls-certificates.md#certificates-stores). - -Before creating `TLSStore` objects, you need to apply the [Traefik Kubernetes CRDs](https://doc.traefik.io/traefik/reference/dynamic-configuration/kubernetes-crd/#definitions) to your Kubernetes cluster. - -!!! Tip "Default TLS Store" - Traefik currently only uses the TLS Store named "default". This default `TLSStore` should be in a namespace discoverable by Traefik. Since it is used by default on `IngressRoute` and `IngressRouteTCP` objects, there never is a need to actually reference it. This means that you cannot have two stores that are named default in different Kubernetes namespaces. As a consequence, with respect to TLS stores, the only change that makes sense (and only if needed) is to configure the default `TLSStore`. - -## Configuration Example - -```yaml tab="TLSStore" -apiVersion: traefik.io/v1alpha1 -kind: TLSStore -metadata: - name: default - -spec: - defaultCertificate: - secretName: supersecret -``` - -## Configuration Options - -| Field | Description | Required | -|:---------------------------------------|:-------------------------|:---------| -| `certificates[n].secretName` | List of Kubernetes [Secrets](https://kubernetes.io/docs/concepts/configuration/secret/), each of them holding a key/certificate pair to add to the store. | No | -| `defaultCertificate.secretName` | Name of the Kubernetes [Secret](https://kubernetes.io/docs/concepts/configuration/secret/) served for connections without a SNI, or without a matching domain. If no default certificate is provided, Traefik will use the generated one. Do not use if the option `defaultGeneratedCert` is set. | No | -| `defaultGeneratedCert.resolver` | Name of the ACME resolver to use to generate the default certificate.
Do not use if the option `defaultCertificate` is set. | No | -| `defaultGeneratedCert.domain.main` | Main domain used to generate the default certificate.
Do not use if the option `defaultCertificate` is set. | No | -| `defaultGeneratedCert.domain.sans` | List of [Subject Alternative Name](https://en.wikipedia.org/wiki/Subject_Alternative_Name) used to generate the default certificate.
Do not use if the option `defaultCertificate` is set. | No | - -!!! note "DefaultCertificate vs DefaultGeneratedCert" - If both `defaultCertificate` and `defaultGeneratedCert` are set, the TLS certificate contained in `defaultCertificate.secretName` is served. The ACME default certificate is not generated. diff --git a/docs/content/reference/routing-configuration/kubernetes/crd/udp/ingressrouteudp.md b/docs/content/reference/routing-configuration/kubernetes/crd/udp/ingressrouteudp.md index eadfd1773..3dfdc1367 100644 --- a/docs/content/reference/routing-configuration/kubernetes/crd/udp/ingressrouteudp.md +++ b/docs/content/reference/routing-configuration/kubernetes/crd/udp/ingressrouteudp.md @@ -3,7 +3,7 @@ title: "IngressRouteUDP" description: "Understand the routing configuration for the Kubernetes IngressRouteUDP & Traefik CRD" --- -`IngressRouteUDP` is the CRD implementation of a [Traefik UDP router](../../../udp/routing/rules-priority.md). +`IngressRouteUDP` is the CRD implementation of a [Traefik UDP router](../../../udp/router/rules-priority.md). Before creating `IngressRouteUDP` objects, you need to apply the [Traefik Kubernetes CRDs](https://doc.traefik.io/traefik/reference/dynamic-configuration/kubernetes-crd/#definitions) to your Kubernetes cluster. @@ -32,14 +32,14 @@ spec: | Field | Description | Default | Required | |------------------------------------|-----------------------------|-------------------------------------------|-----------------------| -| `entryPoints` | List of entrypoints names. | | No | -| ` routes ` | List of routes. | | Yes | -| `routes[n].services` | List of [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) definitions. See [here](#externalname-service) for `ExternalName Service` setup. | | No | -| `services[n].name` | Defines the name of a [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/). | | Yes | -| `routes[n].services[n].port` | Defines the port of a [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/). This can be a reference to a named port.| | Yes | -| `routes[n].services[n].weight` | Defines the weight to apply to the server load balancing. | 1 | No | -| `routes[n].services[n].nativeLB` | Controls, when creating the load-balancer, whether the LB's children are directly the pods IPs or if the only child is the Kubernetes Service clusterIP. | false | No | -| `routes[n].services[n].nodePortLB` | Controls, when creating the load-balancer, whether the LB's children are directly the nodes internal IPs using the nodePort when the service type is NodePort. It allows services to be reachable when Traefik runs externally from the Kubernetes cluster but within the same network of the nodes. See [here](#nativelb) for more information. | false | No | +| `entryPoints` | List of entrypoints names. | | No | +| ` routes ` | List of routes. | | Yes | +| `routes[n].services` | List of [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) definitions. See [here](#externalname-service) for `ExternalName Service` setup. | | No | +| `services[n].name` | Defines the name of a [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/). | | Yes | +| `routes[n].services[n].port` | Defines the port of a [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/). This can be a reference to a named port.| | Yes | +| `routes[n].services[n].weight` | Defines the weight to apply to the server load balancing. | 1 | No | +| `routes[n].services[n].nativeLB` | Controls, when creating the load-balancer, whether the LB's children are directly the pods IPs or if the only child is the Kubernetes Service clusterIP. | false | No | +| `routes[n].services[n].nodePortLB` | Controls, when creating the load-balancer, whether the LB's children are directly the nodes internal IPs using the nodePort when the service type is NodePort. It allows services to be reachable when Traefik runs externally from the Kubernetes cluster but within the same network of the nodes. See [here](#nativelb) for more information. | false | No | ### ExternalName Service diff --git a/docs/content/reference/routing-configuration/kubernetes/gateway-api.md b/docs/content/reference/routing-configuration/kubernetes/gateway-api.md index a25e147c4..19128303c 100644 --- a/docs/content/reference/routing-configuration/kubernetes/gateway-api.md +++ b/docs/content/reference/routing-configuration/kubernetes/gateway-api.md @@ -748,6 +748,7 @@ By default, NativeLB is `false`. Note that it is possible to override the default value by using the option [`nativeLBByDefault`](../../install-configuration/providers/kubernetes/kubernetes-gateway.md) at the provider level. ```yaml +--- apiVersion: v1 kind: Service metadata: @@ -756,10 +757,7 @@ metadata: annotations: traefik.io/service.nativelb: "true" spec: - ports: - - name: web - port: 80 - +[...] ``` {!traefik-for-business-applications.md!} diff --git a/docs/content/reference/routing-configuration/kubernetes/ingress-nginx.md b/docs/content/reference/routing-configuration/kubernetes/ingress-nginx.md deleted file mode 100644 index af98d1b2b..000000000 --- a/docs/content/reference/routing-configuration/kubernetes/ingress-nginx.md +++ /dev/null @@ -1,402 +0,0 @@ ---- -title: "Traefik Kubernetes Ingress NGINX Routing Configuration" -description: "Understand the routing configuration for the Kubernetes Ingress NGINX Controller and Traefik Proxy. Read the technical documentation." ---- - -# Traefik & Ingresses with NGINX Annotations - -The experimental Kubernetes Controller for Ingresses with NGINX annotations. -{: .subtitle } - -!!! warning "Ingress Discovery" - - The Kubernetes Ingress NGINX provider is discovering by default all Ingresses in the cluster, - which may lead to duplicated routers if you are also using the Kubernetes Ingress provider. - We recommend to use IngressClass for the Ingresses you want to be handled by this provider, - or to use the `watchNamespace` or `watchNamespaceSelector` options to limit the discovery of Ingresses to a specific namespace or set of namespaces. - -## Routing Configuration - -The Kubernetes Ingress NGINX provider watches for incoming ingresses events, such as the example below, -and derives the corresponding dynamic configuration from it, -which in turn will create the resulting routers, services, handlers, etc. - -## Configuration Example - -??? example "Configuring Kubernetes Ingress NGINX Controller" - - ```yaml tab="RBAC" - --- - apiVersion: rbac.authorization.k8s.io/v1 - kind: ClusterRole - metadata: - name: traefik-ingress-controller - rules: - - apiGroups: - - "" - resources: - - namespaces - verbs: - - get - - apiGroups: - - "" - resources: - - configmaps - - pods - - secrets - - endpoints - verbs: - - get - - list - - watch - - apiGroups: - - "" - resources: - - services - verbs: - - get - - list - - watch - - apiGroups: - - networking.k8s.io - resources: - - ingresses - verbs: - - get - - list - - watch - - apiGroups: - - networking.k8s.io - resources: - - ingresses/status - verbs: - - update - - apiGroups: - - networking.k8s.io - resources: - - ingressclasses - verbs: - - get - - list - - watch - - apiGroups: - - "" - resources: - - events - verbs: - - create - - patch - - apiGroups: - - discovery.k8s.io - resources: - - endpointslices - verbs: - - list - - watch - - get - - --- - apiVersion: rbac.authorization.k8s.io/v1 - kind: ClusterRoleBinding - metadata: - name: traefik-ingress-controller - roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: traefik-ingress-controller - subjects: - - kind: ServiceAccount - name: traefik-ingress-controller - namespace: default - ``` - - ```yaml tab="Traefik" - --- - apiVersion: v1 - kind: ServiceAccount - metadata: - name: traefik-ingress-controller - - --- - apiVersion: apps/v1 - kind: Deployment - metadata: - name: traefik - labels: - app: traefik - - spec: - replicas: 1 - selector: - matchLabels: - app: traefik - template: - metadata: - labels: - app: traefik - spec: - serviceAccountName: traefik-ingress-controller - containers: - - name: traefik - image: traefik:v3.5 - args: - - --entryPoints.web.address=:80 - - --providers.kubernetesingressnginx - ports: - - name: web - containerPort: 80 - - --- - apiVersion: v1 - kind: Service - metadata: - name: traefik - spec: - type: LoadBalancer - selector: - app: traefik - ports: - - name: web - port: 80 - targetPort: 80 - ``` - - ```yaml tab="Whoami" - --- - apiVersion: apps/v1 - kind: Deployment - metadata: - name: whoami - labels: - app: whoami - - spec: - replicas: 2 - selector: - matchLabels: - app: whoami - template: - metadata: - labels: - app: whoami - spec: - containers: - - name: whoami - image: traefik/whoami - ports: - - containerPort: 80 - - --- - apiVersion: v1 - kind: Service - metadata: - name: whoami - - spec: - selector: - app: whoami - ports: - - name: http - port: 80 - ``` - - ```yaml tab="Ingress" - --- - apiVersion: networking.k8s.io/v1 - kind: IngressClass - metadata: - name: nginx - spec: - controller: k8s.io/ingress-nginx - - --- - apiVersion: networking.k8s.io/v1 - kind: Ingress - metadata: - name: myingress - - spec: - ingressClassName: nginx - rules: - - host: whoami.localhost - http: - paths: - - path: /bar - pathType: Exact - backend: - service: - name: whoami - port: - number: 80 - - path: /foo - pathType: Exact - backend: - service: - name: whoami - port: - number: 80 - ``` - -## Annotations Support - -This section lists all known NGINX Ingress annotations, split between those currently implemented (with limitations if any) and those not implemented. -Limitations or behavioral differences are indicated where relevant. - -!!! warning "Global configuration" - - Traefik does not expose all global configuration options to control default behaviors for ingresses. - - Some behaviors that are globally configurable in NGINX (such as default SSL redirect, rate limiting, or affinity) are currently not supported and cannot be overridden per-ingress as in NGINX. - -### Caveats and Key Behavioral Differences - -- **Authentication**: Forward auth behaves differently and session caching is not supported. NGINX supports sub-request based auth, while Traefik forwards the original request. -- **Session Affinity**: Only persistent mode is supported. -- **Leader Election**: Not supported; no cluster mode with leader election. -- **Default Backend**: Only `defaultBackend` in Ingress spec is supported; the annotation is ignored. -- **Load Balancing**: Only round_robin is supported; EWMA and IP hash are not supported. -- **CORS**: NGINX responds with all configured headers unconditionally; Traefik handles headers differently between pre-flight and regular requests. -- **TLS/Backend Protocols**: AUTO_HTTP, FCGI and some TLS options are not supported in Traefik. -- **Path Handling**: Traefik preserves trailing slashes by default; NGINX removes them unless configured otherwise. - -### Supported NGINX Annotations - -| Annotation | Limitations / Notes | -|-------------------------------------------------------|--------------------------------------------------------------------------------------------| -| `nginx.ingress.kubernetes.io/affinity` | | -| `nginx.ingress.kubernetes.io/affinity-mode` | Only persistent mode supported; balanced/canary not supported. | -| `nginx.ingress.kubernetes.io/auth-type` | | -| `nginx.ingress.kubernetes.io/auth-secret` | | -| `nginx.ingress.kubernetes.io/auth-secret-type` | | -| `nginx.ingress.kubernetes.io/auth-realm` | | -| `nginx.ingress.kubernetes.io/auth-url` | Only URL and response headers copy supported. Forward auth behaves differently than NGINX. | -| `nginx.ingress.kubernetes.io/auth-method` | | -| `nginx.ingress.kubernetes.io/auth-response-headers` | | -| `nginx.ingress.kubernetes.io/ssl-redirect` | Cannot opt-out per route if enabled globally. | -| `nginx.ingress.kubernetes.io/force-ssl-redirect` | Cannot opt-out per route if enabled globally. | -| `nginx.ingress.kubernetes.io/ssl-passthrough` | Some differences in SNI/default backend handling. | -| `nginx.ingress.kubernetes.io/use-regex` | | -| `nginx.ingress.kubernetes.io/session-cookie-name` | | -| `nginx.ingress.kubernetes.io/session-cookie-path` | | -| `nginx.ingress.kubernetes.io/session-cookie-domain` | | -| `nginx.ingress.kubernetes.io/session-cookie-samesite` | | -| `nginx.ingress.kubernetes.io/load-balance` | Only round_robin supported; ewma and IP hash not supported. | -| `nginx.ingress.kubernetes.io/backend-protocol` | FCGI and AUTO_HTTP not supported. | -| `nginx.ingress.kubernetes.io/enable-cors` | Partial support. | -| `nginx.ingress.kubernetes.io/cors-allow-credentials` | | -| `nginx.ingress.kubernetes.io/cors-allow-headers` | | -| `nginx.ingress.kubernetes.io/cors-allow-methods` | | -| `nginx.ingress.kubernetes.io/cors-allow-origin` | | -| `nginx.ingress.kubernetes.io/cors-max-age` | | -| `nginx.ingress.kubernetes.io/proxy-ssl-server-name` | | -| `nginx.ingress.kubernetes.io/proxy-ssl-name` | | -| `nginx.ingress.kubernetes.io/proxy-ssl-verify` | | -| `nginx.ingress.kubernetes.io/proxy-ssl-secret` | | -| `nginx.ingress.kubernetes.io/service-upstream` | | - -### Unsupported NGINX Annotations - -!!! question "Want to Add Support for More Annotations?" - - You can help extend support in two ways: - - - [**Open a PR**](../../../contributing/submitting-pull-requests.md) with the new annotation support. - - **Reach out** to the [Traefik Labs support team](https://info.traefik.io/request-commercial-support?cta=doc). - - All contributions and suggestions are welcome — let's build this together! - - -| Annotation | Notes | -|-----------------------------------------------------------------------------|------------------------------------------------------| -| `nginx.ingress.kubernetes.io/app-root` | Not supported yet. | -| `nginx.ingress.kubernetes.io/affinity-canary-behavior` | Not supported yet. | -| `nginx.ingress.kubernetes.io/auth-tls-secret` | Not supported yet. | -| `nginx.ingress.kubernetes.io/auth-tls-verify-depth` | Not supported yet. | -| `nginx.ingress.kubernetes.io/auth-tls-verify-client` | Not supported yet. | -| `nginx.ingress.kubernetes.io/auth-tls-error-page` | Not supported yet. | -| `nginx.ingress.kubernetes.io/auth-tls-pass-certificate-to-upstream` | Not supported yet. | -| `nginx.ingress.kubernetes.io/auth-tls-match-cn` | Not supported yet. | -| `nginx.ingress.kubernetes.io/auth-cache-key` | Not supported yet. | -| `nginx.ingress.kubernetes.io/auth-cache-duration` | Not supported yet. | -| `nginx.ingress.kubernetes.io/auth-keepalive` | Not supported yet. | -| `nginx.ingress.kubernetes.io/auth-keepalive-share-vars` | Not supported yet. | -| `nginx.ingress.kubernetes.io/auth-keepalive-requests` | Not supported yet. | -| `nginx.ingress.kubernetes.io/auth-keepalive-timeout` | Not supported yet. | -| `nginx.ingress.kubernetes.io/auth-proxy-set-headers` | Not supported yet. | -| `nginx.ingress.kubernetes.io/auth-snippet` | Not supported yet. | -| `nginx.ingress.kubernetes.io/enable-global-auth` | Not supported yet. | -| `nginx.ingress.kubernetes.io/canary` | Not supported yet. | -| `nginx.ingress.kubernetes.io/canary-by-header` | Not supported yet. | -| `nginx.ingress.kubernetes.io/canary-by-header-value` | Not supported yet. | -| `nginx.ingress.kubernetes.io/canary-by-header-pattern` | Not supported yet. | -| `nginx.ingress.kubernetes.io/canary-by-cookie` | Not supported yet. | -| `nginx.ingress.kubernetes.io/canary-weight` | Not supported yet. | -| `nginx.ingress.kubernetes.io/canary-weight-total` | Not supported yet. | -| `nginx.ingress.kubernetes.io/client-body-buffer-size` | Not supported yet. | -| `nginx.ingress.kubernetes.io/configuration-snippet` | Not supported yet. | -| `nginx.ingress.kubernetes.io/custom-http-errors` | Not supported yet. | -| `nginx.ingress.kubernetes.io/disable-proxy-intercept-errors` | Not supported yet. | -| `nginx.ingress.kubernetes.io/default-backend` | Not supported yet; use `defaultBackend` in Ingress spec. | -| `nginx.ingress.kubernetes.io/limit-rate-after` | Not supported yet. | -| `nginx.ingress.kubernetes.io/limit-rate` | Not supported yet. | -| `nginx.ingress.kubernetes.io/limit-whitelist` | Not supported yet. | -| `nginx.ingress.kubernetes.io/limit-rps` | Not supported yet. | -| `nginx.ingress.kubernetes.io/limit-rpm` | Not supported yet. | -| `nginx.ingress.kubernetes.io/limit-burst-multiplier` | Not supported yet. | -| `nginx.ingress.kubernetes.io/limit-connections` | Not supported yet. | -| `nginx.ingress.kubernetes.io/global-rate-limit` | Not supported yet. | -| `nginx.ingress.kubernetes.io/global-rate-limit-window` | Not supported yet. | -| `nginx.ingress.kubernetes.io/global-rate-limit-key` | Not supported yet. | -| `nginx.ingress.kubernetes.io/global-rate-limit-ignored-cidrs` | Not supported yet. | -| `nginx.ingress.kubernetes.io/permanent-redirect` | Not supported yet. | -| `nginx.ingress.kubernetes.io/permanent-redirect-code` | Not supported yet. | -| `nginx.ingress.kubernetes.io/temporal-redirect` | Not supported yet. | -| `nginx.ingress.kubernetes.io/preserve-trailing-slash` | Not supported yet; Traefik preserves by default. | -| `nginx.ingress.kubernetes.io/proxy-cookie-domain` | Not supported yet. | -| `nginx.ingress.kubernetes.io/proxy-cookie-path` | Not supported yet. | -| `nginx.ingress.kubernetes.io/proxy-connect-timeout` | Not supported yet. | -| `nginx.ingress.kubernetes.io/proxy-send-timeout` | Not supported yet. | -| `nginx.ingress.kubernetes.io/proxy-read-timeout` | Not supported yet. | -| `nginx.ingress.kubernetes.io/proxy-next-upstream` | Not supported yet. | -| `nginx.ingress.kubernetes.io/proxy-next-upstream-timeout` | Not supported yet. | -| `nginx.ingress.kubernetes.io/proxy-next-upstream-tries` | Not supported yet. | -| `nginx.ingress.kubernetes.io/proxy-request-buffering` | Not supported yet. | -| `nginx.ingress.kubernetes.io/proxy-redirect-from` | Not supported yet. | -| `nginx.ingress.kubernetes.io/proxy-redirect-to` | Not supported yet. | -| `nginx.ingress.kubernetes.io/proxy-http-version` | Not supported yet. | -| `nginx.ingress.kubernetes.io/proxy-ssl-ciphers` | Not supported yet. | -| `nginx.ingress.kubernetes.io/proxy-ssl-verify-depth` | Not supported yet. | -| `nginx.ingress.kubernetes.io/proxy-ssl-protocols` | Not supported yet. | -| `nginx.ingress.kubernetes.io/enable-rewrite-log` | Not supported yet. | -| `nginx.ingress.kubernetes.io/rewrite-target` | Not supported yet. | -| `nginx.ingress.kubernetes.io/satisfy` | Not supported yet. | -| `nginx.ingress.kubernetes.io/server-alias` | Not supported yet. | -| `nginx.ingress.kubernetes.io/server-snippet` | Not supported yet. | -| `nginx.ingress.kubernetes.io/session-cookie-conditional-samesite-none` | Not supported yet. | -| `nginx.ingress.kubernetes.io/session-cookie-expires` | Not supported yet. | -| `nginx.ingress.kubernetes.io/session-cookie-change-on-failure` | Not supported yet. | -| `nginx.ingress.kubernetes.io/ssl-ciphers` | Not supported yet. | -| `nginx.ingress.kubernetes.io/ssl-prefer-server-ciphers` | Not supported yet. | -| `nginx.ingress.kubernetes.io/connection-proxy-header` | Not supported yet. | -| `nginx.ingress.kubernetes.io/enable-access-log` | Not supported yet. | -| `nginx.ingress.kubernetes.io/enable-opentracing` | Not supported yet. | -| `nginx.ingress.kubernetes.io/opentracing-trust-incoming-span` | Not supported yet. | -| `nginx.ingress.kubernetes.io/enable-opentelemetry` | Not supported yet. | -| `nginx.ingress.kubernetes.io/opentelemetry-trust-incoming-span` | Not supported yet. | -| `nginx.ingress.kubernetes.io/enable-modsecurity` | Not supported yet. | -| `nginx.ingress.kubernetes.io/enable-owasp-core-rules` | Not supported yet. | -| `nginx.ingress.kubernetes.io/modsecurity-transaction-id` | Not supported yet. | -| `nginx.ingress.kubernetes.io/modsecurity-snippet` | Not supported yet. | -| `nginx.ingress.kubernetes.io/mirror-request-body` | Not supported yet. | -| `nginx.ingress.kubernetes.io/mirror-target` | Not supported yet. | -| `nginx.ingress.kubernetes.io/mirror-host` | Not supported yet. | -| `nginx.ingress.kubernetes.io/x-forwarded-prefix` | Not supported yet. | -| `nginx.ingress.kubernetes.io/upstream-hash-by` | Not supported yet. | -| `nginx.ingress.kubernetes.io/upstream-vhost` | Not supported yet. | -| `nginx.ingress.kubernetes.io/denylist-source-range` | Not supported yet. | -| `nginx.ingress.kubernetes.io/whitelist-source-range` | Not supported yet. | -| `nginx.ingress.kubernetes.io/proxy-buffering` | Not supported yet. | -| `nginx.ingress.kubernetes.io/proxy-buffers-number` | Not supported yet. | -| `nginx.ingress.kubernetes.io/proxy-buffer-size` | Not supported yet. | -| `nginx.ingress.kubernetes.io/proxy-max-temp-file-size` | Not supported yet. | -| `nginx.ingress.kubernetes.io/stream-snippet` | Not supported yet. | diff --git a/docs/content/reference/routing-configuration/kubernetes/ingress.md b/docs/content/reference/routing-configuration/kubernetes/ingress.md index a9b0aab71..f0dba8651 100644 --- a/docs/content/reference/routing-configuration/kubernetes/ingress.md +++ b/docs/content/reference/routing-configuration/kubernetes/ingress.md @@ -69,7 +69,7 @@ spec: ??? info "`traefik.ingress.kubernetes.io/router.priority`" - See [priority](../http/routing/rules-and-priority.md#priority-calculation) for more information. + See [priority](../http/router/rules-and-priority.md#priority-calculation) for more information. ```yaml traefik.ingress.kubernetes.io/router.priority: "42" @@ -82,7 +82,7 @@ spec: RuleSyntax option is deprecated and will be removed in the next major version. Please do not use this field and rewrite the router rules to use the v3 syntax. - See [rule syntax](../http/routing/rules-and-priority.md#rulesyntax) for more information. + See [rule syntax](../http/router/rules-and-priority.md#rulesyntax) for more information. ```yaml traefik.ingress.kubernetes.io/router.rulesyntax: "v2" @@ -133,7 +133,7 @@ spec: ??? info "`traefik.ingress.kubernetes.io/router.tls.options`" - See [options](../kubernetes/crd/tls/tlsoption.md) for more information. + See [options](../kubernetes/crd/http/tlsoption.md) for more information. ```yaml traefik.ingress.kubernetes.io/router.tls.options: foobar@file @@ -141,7 +141,7 @@ spec: ??? info "`traefik.ingress.kubernetes.io/router.observability.accesslogs`" - See [here](../http/routing/observability.md) for more information. + See [here](../http/router/observability.md) for more information. ```yaml traefik.ingress.kubernetes.io/router.observability.accesslogs: true @@ -149,7 +149,7 @@ spec: ??? info "`traefik.ingress.kubernetes.io/router.observability.metrics`" - See [here](../http/routing/observability.md) for more information. + See [here](../http/router/observability.md) for more information. ```yaml traefik.ingress.kubernetes.io/router.observability.metrics: true @@ -157,7 +157,7 @@ spec: ??? info "`traefik.ingress.kubernetes.io/router.observability.tracing`" - See [here](../http/routing/observability.md) for more information. + See [here](../http/router/observability.md) for more information. ```yaml traefik.ingress.kubernetes.io/router.observability.tracing: true @@ -402,7 +402,7 @@ This way, any Ingress attached to this Entrypoint will have TLS termination by d serviceAccountName: traefik-ingress-controller containers: - name: traefik - image: traefik:v3.5 + image: traefik:v3.4 args: - --entryPoints.websecure.address=:443 - --entryPoints.websecure.http.tls @@ -589,7 +589,7 @@ and will connect via TLS automatically. Please note that by enabling TLS communication between traefik and your pods, you will have to have trusted certificates that have the proper trust chain and IP subject name. If this is not an option, you may need to skip TLS certificate verification. - See the [`insecureSkipVerify` TLSOption](../kubernetes/crd/tls/tlsoption.md) setting for more details. + See the [`insecureSkipVerify` TLSOption](../kubernetes/crd/http/tlsoption.md) setting for more details. ## Global Default Backend Ingresses diff --git a/docs/content/reference/routing-configuration/other-providers/consul-catalog.md b/docs/content/reference/routing-configuration/other-providers/consul-catalog.md index 7958a4c01..534891cf3 100644 --- a/docs/content/reference/routing-configuration/other-providers/consul-catalog.md +++ b/docs/content/reference/routing-configuration/other-providers/consul-catalog.md @@ -25,7 +25,7 @@ With Consul Catalog, Traefik can leverage tags attached to a service to generate ### General -Traefik creates, for each consul Catalog service, a corresponding [service](../http/load-balancing/service.md) and [router](../http/routing/rules-and-priority.md). +Traefik creates, for each consul Catalog service, a corresponding [service](../http/load-balancing/service.md) and [router](../http/router/rules-and-priority.md). The Service automatically gets a server per instance in this consul Catalog service, and the router gets a default rule attached to it, based on the service name. @@ -37,7 +37,7 @@ For example, to change the rule, you could add the tag ```traefik.http.routers.m ??? info "`traefik.http.routers..rule`" - See [rule](../http/routing/rules-and-priority.md) for more information. + See [rule](../http/router/rules-and-priority.md) for more information. ```yaml traefik.http.routers.myrouter.rule=Host(`example.com`) @@ -50,7 +50,7 @@ For example, to change the rule, you could add the tag ```traefik.http.routers.m RuleSyntax option is deprecated and will be removed in the next major version. Please do not use this field and rewrite the router rules to use the v3 syntax. - See [ruleSyntax](../http/routing/rules-and-priority.md#rulesyntax) for more information. + See [ruleSyntax](../http/router/rules-and-priority.md#rulesyntax) for more information. ```yaml traefik.http.routers.myrouter.ruleSyntax=v3 @@ -58,7 +58,7 @@ For example, to change the rule, you could add the tag ```traefik.http.routers.m ??? info "`traefik.http.routers..priority`" - See [priority](../http/routing/rules-and-priority.md#priority-calculation) for more information. + See [priority](../http/router/rules-and-priority.md#priority-calculation) for more information. ```yaml - "traefik.tcp.routers.mytcprouter.priority=42" @@ -222,14 +222,6 @@ you'd add the tag `traefik.http.services.{name-of-your-choice}.loadbalancer.pass traefik.http.services.myservice.loadbalancer.healthcheck.interval=10 ``` -??? info "`traefik.http.services..loadbalancer.healthcheck.unhealthyinterval`" - - See [health check](../http/load-balancing/service.md#health-check) for more information. - - ```yaml - traefik.http.services.myservice.loadbalancer.healthcheck.unhealthyinterval=10 - ``` - ??? info "`traefik.http.services..loadbalancer.healthcheck.path`" See [health check](../http/load-balancing/service.md#health-check) for more information. @@ -385,7 +377,7 @@ You can declare TCP Routers, Middlewares and/or Services using tags. ??? info "`traefik.tcp.routers..rule`" - See [rule](../tcp/routing/rules-and-priority.md#rules) for more information. + See [rule](../tcp/router/rules-and-priority.md#rules) for more information. ```yaml traefik.tcp.routers.mytcprouter.rule=HostSNI(`example.com`) @@ -405,7 +397,7 @@ You can declare TCP Routers, Middlewares and/or Services using tags. ``` ??? info "`traefik.tcp.routers..priority`" - See [priority](../tcp/routing/rules-and-priority.md#priority-calculation) for more information. + See [priority](../tcp/router/rules-and-priority.md#priority) for more information. ```yaml - "traefik.tcp.routers.mytcprouter.priority=42" ``` @@ -460,7 +452,7 @@ You can declare TCP Routers, Middlewares and/or Services using tags. ??? info "`traefik.tcp.routers..tls.passthrough`" - See [Passthrough](../tcp/tls.md#opt-passthrough) for more information. + See [Passthrough](../tcp/tls.md#passthrough) for more information. ```yaml traefik.tcp.routers.mytcprouter.tls.passthrough=true @@ -485,6 +477,14 @@ You can declare TCP Routers, Middlewares and/or Services using tags. traefik.tcp.services.mytcpservice.loadbalancer.server.tls=true ``` +??? info "`traefik.tcp.services..loadbalancer.proxyprotocol.version`" + + See [PROXY protocol](../tcp/service.md#proxy-protocol) for more information. + + ```yaml + traefik.tcp.services.mytcpservice.loadbalancer.proxyprotocol.version=1 + ``` + ??? info "`traefik.tcp.services..loadbalancer.serverstransport`" Allows to reference a ServersTransport resource that is defined either with the File provider or the Kubernetes CRD one. diff --git a/docs/content/reference/routing-configuration/other-providers/docker.md b/docs/content/reference/routing-configuration/other-providers/docker.md index a6773b190..e17c9fc91 100644 --- a/docs/content/reference/routing-configuration/other-providers/docker.md +++ b/docs/content/reference/routing-configuration/other-providers/docker.md @@ -35,6 +35,7 @@ With Docker, Traefik can leverage labels attached to a container to generate rou Attaching labels to containers (in your docker compose file) ```yaml + version: "3" services: my-container: # ... @@ -47,6 +48,7 @@ With Docker, Traefik can leverage labels attached to a container to generate rou Forward requests for `http://example.com` to `http://:12345`: ```yaml + version: "3" services: my-container: # ... @@ -69,6 +71,7 @@ With Docker, Traefik can leverage labels attached to a container to generate rou In this example, requests are forwarded for `http://example-a.com` to `http://:8000` in addition to `http://example-b.com` forwarding to `http://:9000`: ```yaml + version: "3" services: my-container: # ... @@ -93,7 +96,7 @@ With Docker, Traefik can leverage labels attached to a container to generate rou ### General -Traefik creates, for each container, a corresponding [service](../http/load-balancing/service.md) and [router](../http/routing/rules-and-priority.md). +Traefik creates, for each container, a corresponding [service](../http/load-balancing/service.md) and [router](../http/router/rules-and-priority.md). The Service automatically gets a server per instance of the container, and the router automatically gets a rule defined by `defaultRule` (if no rule for it was defined in labels). @@ -147,7 +150,7 @@ For example, to change the rule, you could add the label ```traefik.http.routers ??? info "`traefik.http.routers..rule`" - See [rule](../http/routing/rules-and-priority.md) for more information. + See [rule](../http/router/rules-and-priority.md) for more information. ```yaml "traefik.http.routers.myrouter.rule=Host(`example.com`)" @@ -160,7 +163,7 @@ For example, to change the rule, you could add the label ```traefik.http.routers RuleSyntax option is deprecated and will be removed in the next major version. Please do not use this field and rewrite the router rules to use the v3 syntax. - See [ruleSyntax](../http/routing/rules-and-priority.md#rulesyntax) for more information. + See [ruleSyntax](../http/router/rules-and-priority.md#rulesyntax) for more information. ```yaml traefik.http.routers.myrouter.ruleSyntax=v3 @@ -252,7 +255,7 @@ For example, to change the rule, you could add the label ```traefik.http.routers ??? info "`traefik.http.routers..priority`" - See [priority](../http/routing/rules-and-priority.md#priority-calculation) for more information. + See [priority](../http/router/rules-and-priority.md#priority-calculation) for more information. ```yaml "traefik.http.routers.myrouter.priority=42" @@ -324,14 +327,6 @@ you'd add the label `traefik.http.services..loadbalancer.pa "traefik.http.services.myservice.loadbalancer.healthcheck.interval=10s" ``` -??? info "`traefik.http.services..loadbalancer.healthcheck.unhealthyinterval`" - - See [health check](../http/load-balancing/service.md#health-check) for more information. - - ```yaml - "traefik.http.services.myservice.loadbalancer.healthcheck.unhealthyinterval=10s" - ``` - ??? info "`traefik.http.services..loadbalancer.healthcheck.path`" See [health check](../http/load-balancing/service.md#health-check) for more information. @@ -498,7 +493,7 @@ You can declare TCP Routers and/or Services using labels. ??? info "`traefik.tcp.routers..rule`" - See [rule](../tcp/routing/rules-and-priority.md#rules) for more information. + See [rule](../tcp/router/rules-and-priority.md#rules) for more information. ```yaml "traefik.tcp.routers.mytcprouter.rule=HostSNI(`example.com`)" @@ -565,7 +560,7 @@ You can declare TCP Routers and/or Services using labels. ??? info "`traefik.tcp.routers..tls.passthrough`" - See [TLS](../tcp/tls.md#opt-passthrough) for more information. + See [TLS](../tcp/tls.md#passthrough) for more information. ```yaml "traefik.tcp.routers.mytcprouter.tls.passthrough=true" @@ -573,7 +568,7 @@ You can declare TCP Routers and/or Services using labels. ??? info "`traefik.tcp.routers..priority`" - See [priority](../tcp/routing/rules-and-priority.md) for more information. + See [priority](../tcp/router/rules-and-priority.md) for more information. ```yaml "traefik.tcp.routers.mytcprouter.priority=42" @@ -597,6 +592,14 @@ You can declare TCP Routers and/or Services using labels. "traefik.tcp.services.mytcpservice.loadbalancer.server.tls=true" ``` +??? info "`traefik.tcp.services..loadbalancer.proxyprotocol.version`" + + See [PROXY protocol](../tcp/service.md#proxy-protocol) for more information. + + ```yaml + "traefik.tcp.services.mytcpservice.loadbalancer.proxyprotocol.version=1" + ``` + ??? info "`traefik.tcp.services..loadbalancer.serverstransport`" Allows to reference a ServersTransport resource that is defined either with the File provider or the Kubernetes CRD one. diff --git a/docs/content/reference/routing-configuration/other-providers/ecs.md b/docs/content/reference/routing-configuration/other-providers/ecs.md index 03b4f4d45..b17d9330a 100644 --- a/docs/content/reference/routing-configuration/other-providers/ecs.md +++ b/docs/content/reference/routing-configuration/other-providers/ecs.md @@ -25,7 +25,7 @@ With ECS, Traefik can leverage labels attached to a container to generate routin ### General -Traefik creates, for each elastic service, a corresponding [service](../http/load-balancing/service.md) and [router](../http/routing/rules-and-priority.md). +Traefik creates, for each elastic service, a corresponding [service](../http/load-balancing/service.md) and [router](../http/router/rules-and-priority.md). The Service automatically gets a server per elastic container, and the router gets a default rule attached to it, based on the service name. @@ -39,7 +39,7 @@ For example, to change the rule, you could add the label ```traefik.http.routers ??? info "`traefik.http.routers..rule`" - See [rule](../http/routing/rules-and-priority.md#rules) for more information. + See [rule](../http/router/rules-and-priority.md#rules) for more information. ```yaml traefik.http.routers.myrouter.rule=Host(`example.com`) @@ -52,7 +52,7 @@ For example, to change the rule, you could add the label ```traefik.http.routers RuleSyntax option is deprecated and will be removed in the next major version. Please do not use this field and rewrite the router rules to use the v3 syntax. - See [ruleSyntax](../http/routing/rules-and-priority.md#rulesyntax) for more information. + See [ruleSyntax](../http/router/rules-and-priority.md#rulesyntax) for more information. ```yaml traefik.http.routers.myrouter.ruleSyntax=v3 @@ -146,7 +146,7 @@ For example, to change the rule, you could add the label ```traefik.http.routers ??? info "`traefik.http.routers..priority`" - See [priority](../http/routing/rules-and-priority.md#priority-calculation) for more information. + See [priority](../http/router/rules-and-priority.md#priority-calculation) for more information. ```yaml traefik.http.routers.myrouter.priority=42 @@ -218,14 +218,6 @@ you'd add the label `traefik.http.services.{name-of-your-choice}.loadbalancer.pa traefik.http.services.myservice.loadbalancer.healthcheck.interval=10 ``` -??? info "`traefik.http.services..loadbalancer.healthcheck.unhealthyinterval`" - - See [health check](../http/load-balancing/service.md#health-check) for more information. - - ```yaml - traefik.http.services.myservice.loadbalancer.healthcheck.unhealthyinterval=10 - ``` - ??? info "`traefik.http.services..loadbalancer.healthcheck.path`" See [health check](../http/load-balancing/service.md#health-check) for more information. @@ -454,7 +446,7 @@ You can declare TCP Routers and/or Services using labels. ??? info "`traefik.tcp.routers..tls.passthrough`" - See [Passthrough](../tcp/tls.md#opt-passthrough) for more information. + See [Passthrough](../tcp/tls.md#passthrough) for more information. ```yaml traefik.tcp.routers.mytcprouter.tls.passthrough=true @@ -462,7 +454,7 @@ You can declare TCP Routers and/or Services using labels. ??? info "`traefik.tcp.routers..priority`" - See [priority](../tcp/routing/rules-and-priority.md#priority-calculation) for more information. + See [priority](../tcp/router/rules-and-priority.md#priority) for more information. ```yaml traefik.tcp.routers.mytcprouter.priority=42 @@ -494,6 +486,14 @@ You can declare TCP Routers and/or Services using labels. traefik.http.services.myservice.loadbalancer.server.weight=42 ``` +??? info "`traefik.tcp.services..loadbalancer.proxyprotocol.version`" + + See [PROXY protocol](../tcp/service.md#proxy-protocol) for more information. + + ```yaml + traefik.tcp.services.mytcpservice.loadbalancer.proxyprotocol.version=1 + ``` + ??? info "`traefik.tcp.services..loadbalancer.serverstransport`" Allows to reference a ServersTransport resource that is defined either with the File provider or the Kubernetes CRD one. diff --git a/docs/content/reference/routing-configuration/other-providers/file.md b/docs/content/reference/routing-configuration/other-providers/file.md deleted file mode 100644 index 58dd5b2fc..000000000 --- a/docs/content/reference/routing-configuration/other-providers/file.md +++ /dev/null @@ -1,120 +0,0 @@ ---- -title: "Traefik File Dynamic Configuration" -description: "This guide will provide you with the YAML and TOML files for dynamic configuration in Traefik Proxy. Read the technical documentation." ---- - - -# Traefik and Configuration Files - -!!! warning "Work In Progress" - - This page is still work in progress to provide a better documention of the routing options. - - It has been created to provide a centralized page with all the option in YAML and TOML format. - -## Configuration Options - -```yml tab="YAML" ---8<-- "content/reference/routing-configuration/other-providers/file.yaml" -``` - -```toml tab="TOML" ---8<-- "content/reference/routing-configuration/other-providers/file.toml" -``` - -## Go Templating - -!!! warning - - Go Templating only works with dedicated dynamic configuration files. - Templating does not work in the Traefik main static configuration file. - -Traefik supports using Go templating to automatically generate repetitive sections of configuration files. -These sections must be a valid [Go template](https://pkg.go.dev/text/template/), and can use -[sprig template functions](https://masterminds.github.io/sprig/). - -To illustrate, it is possible to easily define multiple routers, services, and TLS certificates as described in the following examples: - -??? example "Configuring Using Templating" - - ```yaml tab="YAML" - http: - routers: - {{range $i, $e := until 100 }} - router{{ $e }}-{{ env "MY_ENV_VAR" }}: - # ... - {{end}} - - services: - {{range $i, $e := until 100 }} - application{{ $e }}: - # ... - {{end}} - - tcp: - routers: - {{range $i, $e := until 100 }} - router{{ $e }}: - # ... - {{end}} - - services: - {{range $i, $e := until 100 }} - service{{ $e }}: - # ... - {{end}} - - tls: - certificates: - {{ range $i, $e := until 10 }} - - certFile: "/etc/traefik/cert-{{ $e }}.pem" - keyFile: "/etc/traefik/cert-{{ $e }}.key" - store: - - "my-store-foo-{{ $e }}" - - "my-store-bar-{{ $e }}" - {{end}} - ``` - - ```toml tab="TOML" - # template-rules.toml - [http] - - [http.routers] - {{ range $i, $e := until 100 }} - [http.routers.router{{ $e }}-{{ env "MY_ENV_VAR" }}] - # ... - {{ end }} - - [http.services] - {{ range $i, $e := until 100 }} - [http.services.service{{ $e }}] - # ... - {{ end }} - - [tcp] - - [tcp.routers] - {{ range $i, $e := until 100 }} - [tcp.routers.router{{ $e }}] - # ... - {{ end }} - - [tcp.services] - {{ range $i, $e := until 100 }} - [http.services.service{{ $e }}] - # ... - {{ end }} - - {{ range $i, $e := until 10 }} - [[tls.certificates]] - certFile = "/etc/traefik/cert-{{ $e }}.pem" - keyFile = "/etc/traefik/cert-{{ $e }}.key" - stores = ["my-store-foo-{{ $e }}", "my-store-bar-{{ $e }}"] - {{ end }} - - [tls.config] - {{ range $i, $e := until 10 }} - [tls.config.TLS{{ $e }}] - # ... - {{ end }} - ``` diff --git a/docs/content/reference/routing-configuration/other-providers/file.toml b/docs/content/reference/routing-configuration/other-providers/file.toml deleted file mode 100644 index 985f96e21..000000000 --- a/docs/content/reference/routing-configuration/other-providers/file.toml +++ /dev/null @@ -1,615 +0,0 @@ -## CODE GENERATED AUTOMATICALLY -## THIS FILE MUST NOT BE EDITED BY HAND -[http] - [http.routers] - [http.routers.Router0] - entryPoints = ["foobar", "foobar"] - middlewares = ["foobar", "foobar"] - service = "foobar" - rule = "foobar" - ruleSyntax = "foobar" - priority = 42 - [http.routers.Router0.tls] - options = "foobar" - certResolver = "foobar" - - [[http.routers.Router0.tls.domains]] - main = "foobar" - sans = ["foobar", "foobar"] - - [[http.routers.Router0.tls.domains]] - main = "foobar" - sans = ["foobar", "foobar"] - [http.routers.Router0.observability] - accessLogs = true - metrics = true - tracing = true - traceVerbosity = "foobar" - [http.routers.Router1] - entryPoints = ["foobar", "foobar"] - middlewares = ["foobar", "foobar"] - service = "foobar" - rule = "foobar" - ruleSyntax = "foobar" - priority = 42 - [http.routers.Router1.tls] - options = "foobar" - certResolver = "foobar" - - [[http.routers.Router1.tls.domains]] - main = "foobar" - sans = ["foobar", "foobar"] - - [[http.routers.Router1.tls.domains]] - main = "foobar" - sans = ["foobar", "foobar"] - [http.routers.Router1.observability] - accessLogs = true - metrics = true - tracing = true - traceVerbosity = "foobar" - [http.services] - [http.services.Service01] - [http.services.Service01.failover] - service = "foobar" - fallback = "foobar" - [http.services.Service01.failover.healthCheck] - [http.services.Service02] - [http.services.Service02.loadBalancer] - strategy = "foobar" - passHostHeader = true - serversTransport = "foobar" - [http.services.Service02.loadBalancer.sticky] - [http.services.Service02.loadBalancer.sticky.cookie] - name = "foobar" - secure = true - httpOnly = true - sameSite = "foobar" - maxAge = 42 - path = "foobar" - domain = "foobar" - - [[http.services.Service02.loadBalancer.servers]] - url = "foobar" - weight = 42 - preservePath = true - - [[http.services.Service02.loadBalancer.servers]] - url = "foobar" - weight = 42 - preservePath = true - [http.services.Service02.loadBalancer.healthCheck] - scheme = "foobar" - mode = "foobar" - path = "foobar" - method = "foobar" - status = 42 - port = 42 - interval = "42s" - unhealthyInterval = "42s" - timeout = "42s" - hostname = "foobar" - followRedirects = true - [http.services.Service02.loadBalancer.healthCheck.headers] - name0 = "foobar" - name1 = "foobar" - [http.services.Service02.loadBalancer.responseForwarding] - flushInterval = "42s" - [http.services.Service03] - [http.services.Service03.mirroring] - service = "foobar" - mirrorBody = true - maxBodySize = 42 - - [[http.services.Service03.mirroring.mirrors]] - name = "foobar" - percent = 42 - - [[http.services.Service03.mirroring.mirrors]] - name = "foobar" - percent = 42 - [http.services.Service03.mirroring.healthCheck] - [http.services.Service04] - [http.services.Service04.weighted] - - [[http.services.Service04.weighted.services]] - name = "foobar" - weight = 42 - - [[http.services.Service04.weighted.services]] - name = "foobar" - weight = 42 - [http.services.Service04.weighted.sticky] - [http.services.Service04.weighted.sticky.cookie] - name = "foobar" - secure = true - httpOnly = true - sameSite = "foobar" - maxAge = 42 - path = "foobar" - domain = "foobar" - [http.services.Service04.weighted.healthCheck] - [http.middlewares] - [http.middlewares.Middleware01] - [http.middlewares.Middleware01.addPrefix] - prefix = "foobar" - [http.middlewares.Middleware02] - [http.middlewares.Middleware02.basicAuth] - users = ["foobar", "foobar"] - usersFile = "foobar" - realm = "foobar" - removeHeader = true - headerField = "foobar" - [http.middlewares.Middleware03] - [http.middlewares.Middleware03.buffering] - maxRequestBodyBytes = 42 - memRequestBodyBytes = 42 - maxResponseBodyBytes = 42 - memResponseBodyBytes = 42 - retryExpression = "foobar" - [http.middlewares.Middleware04] - [http.middlewares.Middleware04.chain] - middlewares = ["foobar", "foobar"] - [http.middlewares.Middleware05] - [http.middlewares.Middleware05.circuitBreaker] - expression = "foobar" - checkPeriod = "42s" - fallbackDuration = "42s" - recoveryDuration = "42s" - responseCode = 42 - [http.middlewares.Middleware06] - [http.middlewares.Middleware06.compress] - excludedContentTypes = ["foobar", "foobar"] - includedContentTypes = ["foobar", "foobar"] - minResponseBodyBytes = 42 - encodings = ["foobar", "foobar"] - defaultEncoding = "foobar" - [http.middlewares.Middleware07] - [http.middlewares.Middleware07.contentType] - autoDetect = true - [http.middlewares.Middleware08] - [http.middlewares.Middleware08.digestAuth] - users = ["foobar", "foobar"] - usersFile = "foobar" - removeHeader = true - realm = "foobar" - headerField = "foobar" - [http.middlewares.Middleware09] - [http.middlewares.Middleware09.errors] - status = ["foobar", "foobar"] - service = "foobar" - query = "foobar" - [http.middlewares.Middleware09.errors.statusRewrites] - name0 = 42 - name1 = 42 - [http.middlewares.Middleware10] - [http.middlewares.Middleware10.forwardAuth] - address = "foobar" - trustForwardHeader = true - authResponseHeaders = ["foobar", "foobar"] - authResponseHeadersRegex = "foobar" - authRequestHeaders = ["foobar", "foobar"] - addAuthCookiesToResponse = ["foobar", "foobar"] - headerField = "foobar" - forwardBody = true - maxBodySize = 42 - preserveLocationHeader = true - preserveRequestMethod = true - [http.middlewares.Middleware10.forwardAuth.tls] - ca = "foobar" - cert = "foobar" - key = "foobar" - insecureSkipVerify = true - caOptional = true - [http.middlewares.Middleware11] - [http.middlewares.Middleware11.grpcWeb] - allowOrigins = ["foobar", "foobar"] - [http.middlewares.Middleware12] - [http.middlewares.Middleware12.headers] - accessControlAllowCredentials = true - accessControlAllowHeaders = ["foobar", "foobar"] - accessControlAllowMethods = ["foobar", "foobar"] - accessControlAllowOriginList = ["foobar", "foobar"] - accessControlAllowOriginListRegex = ["foobar", "foobar"] - accessControlExposeHeaders = ["foobar", "foobar"] - accessControlMaxAge = 42 - addVaryHeader = true - allowedHosts = ["foobar", "foobar"] - hostsProxyHeaders = ["foobar", "foobar"] - stsSeconds = 42 - stsIncludeSubdomains = true - stsPreload = true - forceSTSHeader = true - frameDeny = true - customFrameOptionsValue = "foobar" - contentTypeNosniff = true - browserXssFilter = true - customBrowserXSSValue = "foobar" - contentSecurityPolicy = "foobar" - contentSecurityPolicyReportOnly = "foobar" - publicKey = "foobar" - referrerPolicy = "foobar" - permissionsPolicy = "foobar" - isDevelopment = true - featurePolicy = "foobar" - sslRedirect = true - sslTemporaryRedirect = true - sslHost = "foobar" - sslForceHost = true - [http.middlewares.Middleware12.headers.customRequestHeaders] - name0 = "foobar" - name1 = "foobar" - [http.middlewares.Middleware12.headers.customResponseHeaders] - name0 = "foobar" - name1 = "foobar" - [http.middlewares.Middleware12.headers.sslProxyHeaders] - name0 = "foobar" - name1 = "foobar" - [http.middlewares.Middleware13] - [http.middlewares.Middleware13.ipAllowList] - sourceRange = ["foobar", "foobar"] - rejectStatusCode = 42 - [http.middlewares.Middleware13.ipAllowList.ipStrategy] - depth = 42 - excludedIPs = ["foobar", "foobar"] - ipv6Subnet = 42 - [http.middlewares.Middleware14] - [http.middlewares.Middleware14.ipWhiteList] - sourceRange = ["foobar", "foobar"] - [http.middlewares.Middleware14.ipWhiteList.ipStrategy] - depth = 42 - excludedIPs = ["foobar", "foobar"] - ipv6Subnet = 42 - [http.middlewares.Middleware15] - [http.middlewares.Middleware15.inFlightReq] - amount = 42 - [http.middlewares.Middleware15.inFlightReq.sourceCriterion] - requestHeaderName = "foobar" - requestHost = true - [http.middlewares.Middleware15.inFlightReq.sourceCriterion.ipStrategy] - depth = 42 - excludedIPs = ["foobar", "foobar"] - ipv6Subnet = 42 - [http.middlewares.Middleware16] - [http.middlewares.Middleware16.passTLSClientCert] - pem = true - [http.middlewares.Middleware16.passTLSClientCert.info] - notAfter = true - notBefore = true - sans = true - serialNumber = true - [http.middlewares.Middleware16.passTLSClientCert.info.subject] - country = true - province = true - locality = true - organization = true - organizationalUnit = true - commonName = true - serialNumber = true - domainComponent = true - [http.middlewares.Middleware16.passTLSClientCert.info.issuer] - country = true - province = true - locality = true - organization = true - commonName = true - serialNumber = true - domainComponent = true - [http.middlewares.Middleware17] - [http.middlewares.Middleware17.plugin] - [http.middlewares.Middleware17.plugin.PluginConf0] - name0 = "foobar" - name1 = "foobar" - [http.middlewares.Middleware17.plugin.PluginConf1] - name0 = "foobar" - name1 = "foobar" - [http.middlewares.Middleware18] - [http.middlewares.Middleware18.rateLimit] - average = 42 - period = "42s" - burst = 42 - [http.middlewares.Middleware18.rateLimit.sourceCriterion] - requestHeaderName = "foobar" - requestHost = true - [http.middlewares.Middleware18.rateLimit.sourceCriterion.ipStrategy] - depth = 42 - excludedIPs = ["foobar", "foobar"] - ipv6Subnet = 42 - [http.middlewares.Middleware18.rateLimit.redis] - endpoints = ["foobar", "foobar"] - username = "foobar" - password = "foobar" - db = 42 - poolSize = 42 - minIdleConns = 42 - maxActiveConns = 42 - readTimeout = "42s" - writeTimeout = "42s" - dialTimeout = "42s" - [http.middlewares.Middleware18.rateLimit.redis.tls] - ca = "foobar" - cert = "foobar" - key = "foobar" - insecureSkipVerify = true - [http.middlewares.Middleware19] - [http.middlewares.Middleware19.redirectRegex] - regex = "foobar" - replacement = "foobar" - permanent = true - [http.middlewares.Middleware20] - [http.middlewares.Middleware20.redirectScheme] - scheme = "foobar" - port = "foobar" - permanent = true - [http.middlewares.Middleware21] - [http.middlewares.Middleware21.replacePath] - path = "foobar" - [http.middlewares.Middleware22] - [http.middlewares.Middleware22.replacePathRegex] - regex = "foobar" - replacement = "foobar" - [http.middlewares.Middleware23] - [http.middlewares.Middleware23.retry] - attempts = 42 - initialInterval = "42s" - [http.middlewares.Middleware24] - [http.middlewares.Middleware24.stripPrefix] - prefixes = ["foobar", "foobar"] - forceSlash = true - [http.middlewares.Middleware25] - [http.middlewares.Middleware25.stripPrefixRegex] - regex = ["foobar", "foobar"] - [http.serversTransports] - [http.serversTransports.ServersTransport0] - serverName = "foobar" - insecureSkipVerify = true - rootCAs = ["foobar", "foobar"] - maxIdleConnsPerHost = 42 - disableHTTP2 = true - peerCertURI = "foobar" - - [[http.serversTransports.ServersTransport0.certificates]] - certFile = "foobar" - keyFile = "foobar" - - [[http.serversTransports.ServersTransport0.certificates]] - certFile = "foobar" - keyFile = "foobar" - [http.serversTransports.ServersTransport0.forwardingTimeouts] - dialTimeout = "42s" - responseHeaderTimeout = "42s" - idleConnTimeout = "42s" - readIdleTimeout = "42s" - pingTimeout = "42s" - [http.serversTransports.ServersTransport0.spiffe] - ids = ["foobar", "foobar"] - trustDomain = "foobar" - [http.serversTransports.ServersTransport1] - serverName = "foobar" - insecureSkipVerify = true - rootCAs = ["foobar", "foobar"] - maxIdleConnsPerHost = 42 - disableHTTP2 = true - peerCertURI = "foobar" - - [[http.serversTransports.ServersTransport1.certificates]] - certFile = "foobar" - keyFile = "foobar" - - [[http.serversTransports.ServersTransport1.certificates]] - certFile = "foobar" - keyFile = "foobar" - [http.serversTransports.ServersTransport1.forwardingTimeouts] - dialTimeout = "42s" - responseHeaderTimeout = "42s" - idleConnTimeout = "42s" - readIdleTimeout = "42s" - pingTimeout = "42s" - [http.serversTransports.ServersTransport1.spiffe] - ids = ["foobar", "foobar"] - trustDomain = "foobar" - -[tcp] - [tcp.routers] - [tcp.routers.TCPRouter0] - entryPoints = ["foobar", "foobar"] - middlewares = ["foobar", "foobar"] - service = "foobar" - rule = "foobar" - ruleSyntax = "foobar" - priority = 42 - [tcp.routers.TCPRouter0.tls] - passthrough = true - options = "foobar" - certResolver = "foobar" - - [[tcp.routers.TCPRouter0.tls.domains]] - main = "foobar" - sans = ["foobar", "foobar"] - - [[tcp.routers.TCPRouter0.tls.domains]] - main = "foobar" - sans = ["foobar", "foobar"] - [tcp.routers.TCPRouter1] - entryPoints = ["foobar", "foobar"] - middlewares = ["foobar", "foobar"] - service = "foobar" - rule = "foobar" - ruleSyntax = "foobar" - priority = 42 - [tcp.routers.TCPRouter1.tls] - passthrough = true - options = "foobar" - certResolver = "foobar" - - [[tcp.routers.TCPRouter1.tls.domains]] - main = "foobar" - sans = ["foobar", "foobar"] - - [[tcp.routers.TCPRouter1.tls.domains]] - main = "foobar" - sans = ["foobar", "foobar"] - [tcp.services] - [tcp.services.TCPService01] - [tcp.services.TCPService01.loadBalancer] - serversTransport = "foobar" - terminationDelay = 42 - - [[tcp.services.TCPService01.loadBalancer.servers]] - address = "foobar" - tls = true - - [[tcp.services.TCPService01.loadBalancer.servers]] - address = "foobar" - tls = true - [tcp.services.TCPService01.loadBalancer.proxyProtocol] - version = 42 - [tcp.services.TCPService02] - [tcp.services.TCPService02.weighted] - - [[tcp.services.TCPService02.weighted.services]] - name = "foobar" - weight = 42 - - [[tcp.services.TCPService02.weighted.services]] - name = "foobar" - weight = 42 - [tcp.middlewares] - [tcp.middlewares.TCPMiddleware01] - [tcp.middlewares.TCPMiddleware01.ipAllowList] - sourceRange = ["foobar", "foobar"] - [tcp.middlewares.TCPMiddleware02] - [tcp.middlewares.TCPMiddleware02.ipWhiteList] - sourceRange = ["foobar", "foobar"] - [tcp.middlewares.TCPMiddleware03] - [tcp.middlewares.TCPMiddleware03.inFlightConn] - amount = 42 - [tcp.serversTransports] - [tcp.serversTransports.TCPServersTransport0] - dialKeepAlive = "42s" - dialTimeout = "42s" - terminationDelay = "42s" - [tcp.serversTransports.TCPServersTransport0.proxyProtocol] - version = 42 - [tcp.serversTransports.TCPServersTransport0.tls] - serverName = "foobar" - insecureSkipVerify = true - rootCAs = ["foobar", "foobar"] - peerCertURI = "foobar" - - [[tcp.serversTransports.TCPServersTransport0.tls.certificates]] - certFile = "foobar" - keyFile = "foobar" - - [[tcp.serversTransports.TCPServersTransport0.tls.certificates]] - certFile = "foobar" - keyFile = "foobar" - [tcp.serversTransports.TCPServersTransport0.tls.spiffe] - ids = ["foobar", "foobar"] - trustDomain = "foobar" - [tcp.serversTransports.TCPServersTransport1] - dialKeepAlive = "42s" - dialTimeout = "42s" - terminationDelay = "42s" - [tcp.serversTransports.TCPServersTransport1.proxyProtocol] - version = 42 - [tcp.serversTransports.TCPServersTransport1.tls] - serverName = "foobar" - insecureSkipVerify = true - rootCAs = ["foobar", "foobar"] - peerCertURI = "foobar" - - [[tcp.serversTransports.TCPServersTransport1.tls.certificates]] - certFile = "foobar" - keyFile = "foobar" - - [[tcp.serversTransports.TCPServersTransport1.tls.certificates]] - certFile = "foobar" - keyFile = "foobar" - [tcp.serversTransports.TCPServersTransport1.tls.spiffe] - ids = ["foobar", "foobar"] - trustDomain = "foobar" - -[udp] - [udp.routers] - [udp.routers.UDPRouter0] - entryPoints = ["foobar", "foobar"] - service = "foobar" - [udp.routers.UDPRouter1] - entryPoints = ["foobar", "foobar"] - service = "foobar" - [udp.services] - [udp.services.UDPService01] - [udp.services.UDPService01.loadBalancer] - - [[udp.services.UDPService01.loadBalancer.servers]] - address = "foobar" - - [[udp.services.UDPService01.loadBalancer.servers]] - address = "foobar" - [udp.services.UDPService02] - [udp.services.UDPService02.weighted] - - [[udp.services.UDPService02.weighted.services]] - name = "foobar" - weight = 42 - - [[udp.services.UDPService02.weighted.services]] - name = "foobar" - weight = 42 - -[tls] - - [[tls.certificates]] - certFile = "foobar" - keyFile = "foobar" - stores = ["foobar", "foobar"] - - [[tls.certificates]] - certFile = "foobar" - keyFile = "foobar" - stores = ["foobar", "foobar"] - [tls.options] - [tls.options.Options0] - minVersion = "foobar" - maxVersion = "foobar" - cipherSuites = ["foobar", "foobar"] - curvePreferences = ["foobar", "foobar"] - sniStrict = true - alpnProtocols = ["foobar", "foobar"] - disableSessionTickets = true - preferServerCipherSuites = true - [tls.options.Options0.clientAuth] - caFiles = ["foobar", "foobar"] - clientAuthType = "foobar" - [tls.options.Options1] - minVersion = "foobar" - maxVersion = "foobar" - cipherSuites = ["foobar", "foobar"] - curvePreferences = ["foobar", "foobar"] - sniStrict = true - alpnProtocols = ["foobar", "foobar"] - disableSessionTickets = true - preferServerCipherSuites = true - [tls.options.Options1.clientAuth] - caFiles = ["foobar", "foobar"] - clientAuthType = "foobar" - [tls.stores] - [tls.stores.Store0] - [tls.stores.Store0.defaultCertificate] - certFile = "foobar" - keyFile = "foobar" - [tls.stores.Store0.defaultGeneratedCert] - resolver = "foobar" - [tls.stores.Store0.defaultGeneratedCert.domain] - main = "foobar" - sans = ["foobar", "foobar"] - [tls.stores.Store1] - [tls.stores.Store1.defaultCertificate] - certFile = "foobar" - keyFile = "foobar" - [tls.stores.Store1.defaultGeneratedCert] - resolver = "foobar" - [tls.stores.Store1.defaultGeneratedCert.domain] - main = "foobar" - sans = ["foobar", "foobar"] diff --git a/docs/content/reference/routing-configuration/other-providers/file.yaml b/docs/content/reference/routing-configuration/other-providers/file.yaml deleted file mode 100644 index 29822fddb..000000000 --- a/docs/content/reference/routing-configuration/other-providers/file.yaml +++ /dev/null @@ -1,698 +0,0 @@ -## CODE GENERATED AUTOMATICALLY -## THIS FILE MUST NOT BE EDITED BY HAND -http: - routers: - Router0: - entryPoints: - - foobar - - foobar - middlewares: - - foobar - - foobar - service: foobar - rule: foobar - ruleSyntax: foobar - priority: 42 - tls: - options: foobar - certResolver: foobar - domains: - - main: foobar - sans: - - foobar - - foobar - - main: foobar - sans: - - foobar - - foobar - observability: - accessLogs: true - metrics: true - tracing: true - traceVerbosity: foobar - Router1: - entryPoints: - - foobar - - foobar - middlewares: - - foobar - - foobar - service: foobar - rule: foobar - ruleSyntax: foobar - priority: 42 - tls: - options: foobar - certResolver: foobar - domains: - - main: foobar - sans: - - foobar - - foobar - - main: foobar - sans: - - foobar - - foobar - observability: - accessLogs: true - metrics: true - tracing: true - traceVerbosity: foobar - services: - Service01: - failover: - service: foobar - fallback: foobar - healthCheck: {} - Service02: - loadBalancer: - sticky: - cookie: - name: foobar - secure: true - httpOnly: true - sameSite: foobar - maxAge: 42 - path: foobar - domain: foobar - servers: - - url: foobar - weight: 42 - preservePath: true - - url: foobar - weight: 42 - preservePath: true - strategy: foobar - healthCheck: - scheme: foobar - mode: foobar - path: foobar - method: foobar - status: 42 - port: 42 - interval: 42s - unhealthyInterval: 42s - timeout: 42s - hostname: foobar - followRedirects: true - headers: - name0: foobar - name1: foobar - passHostHeader: true - responseForwarding: - flushInterval: 42s - serversTransport: foobar - Service03: - mirroring: - service: foobar - mirrorBody: true - maxBodySize: 42 - mirrors: - - name: foobar - percent: 42 - - name: foobar - percent: 42 - healthCheck: {} - Service04: - weighted: - services: - - name: foobar - weight: 42 - - name: foobar - weight: 42 - sticky: - cookie: - name: foobar - secure: true - httpOnly: true - sameSite: foobar - maxAge: 42 - path: foobar - domain: foobar - healthCheck: {} - middlewares: - Middleware01: - addPrefix: - prefix: foobar - Middleware02: - basicAuth: - users: - - foobar - - foobar - usersFile: foobar - realm: foobar - removeHeader: true - headerField: foobar - Middleware03: - buffering: - maxRequestBodyBytes: 42 - memRequestBodyBytes: 42 - maxResponseBodyBytes: 42 - memResponseBodyBytes: 42 - retryExpression: foobar - Middleware04: - chain: - middlewares: - - foobar - - foobar - Middleware05: - circuitBreaker: - expression: foobar - checkPeriod: 42s - fallbackDuration: 42s - recoveryDuration: 42s - responseCode: 42 - Middleware06: - compress: - excludedContentTypes: - - foobar - - foobar - includedContentTypes: - - foobar - - foobar - minResponseBodyBytes: 42 - encodings: - - foobar - - foobar - defaultEncoding: foobar - Middleware07: - contentType: - autoDetect: true - Middleware08: - digestAuth: - users: - - foobar - - foobar - usersFile: foobar - removeHeader: true - realm: foobar - headerField: foobar - Middleware09: - errors: - status: - - foobar - - foobar - statusRewrites: - name0: 42 - name1: 42 - service: foobar - query: foobar - Middleware10: - forwardAuth: - address: foobar - tls: - ca: foobar - cert: foobar - key: foobar - insecureSkipVerify: true - caOptional: true - trustForwardHeader: true - authResponseHeaders: - - foobar - - foobar - authResponseHeadersRegex: foobar - authRequestHeaders: - - foobar - - foobar - addAuthCookiesToResponse: - - foobar - - foobar - headerField: foobar - forwardBody: true - maxBodySize: 42 - preserveLocationHeader: true - preserveRequestMethod: true - Middleware11: - grpcWeb: - allowOrigins: - - foobar - - foobar - Middleware12: - headers: - customRequestHeaders: - name0: foobar - name1: foobar - customResponseHeaders: - name0: foobar - name1: foobar - accessControlAllowCredentials: true - accessControlAllowHeaders: - - foobar - - foobar - accessControlAllowMethods: - - foobar - - foobar - accessControlAllowOriginList: - - foobar - - foobar - accessControlAllowOriginListRegex: - - foobar - - foobar - accessControlExposeHeaders: - - foobar - - foobar - accessControlMaxAge: 42 - addVaryHeader: true - allowedHosts: - - foobar - - foobar - hostsProxyHeaders: - - foobar - - foobar - sslProxyHeaders: - name0: foobar - name1: foobar - stsSeconds: 42 - stsIncludeSubdomains: true - stsPreload: true - forceSTSHeader: true - frameDeny: true - customFrameOptionsValue: foobar - contentTypeNosniff: true - browserXssFilter: true - customBrowserXSSValue: foobar - contentSecurityPolicy: foobar - contentSecurityPolicyReportOnly: foobar - publicKey: foobar - referrerPolicy: foobar - permissionsPolicy: foobar - isDevelopment: true - featurePolicy: foobar - sslRedirect: true - sslTemporaryRedirect: true - sslHost: foobar - sslForceHost: true - Middleware13: - ipAllowList: - sourceRange: - - foobar - - foobar - ipStrategy: - depth: 42 - excludedIPs: - - foobar - - foobar - ipv6Subnet: 42 - rejectStatusCode: 42 - Middleware14: - ipWhiteList: - sourceRange: - - foobar - - foobar - ipStrategy: - depth: 42 - excludedIPs: - - foobar - - foobar - ipv6Subnet: 42 - Middleware15: - inFlightReq: - amount: 42 - sourceCriterion: - ipStrategy: - depth: 42 - excludedIPs: - - foobar - - foobar - ipv6Subnet: 42 - requestHeaderName: foobar - requestHost: true - Middleware16: - passTLSClientCert: - pem: true - info: - notAfter: true - notBefore: true - sans: true - serialNumber: true - subject: - country: true - province: true - locality: true - organization: true - organizationalUnit: true - commonName: true - serialNumber: true - domainComponent: true - issuer: - country: true - province: true - locality: true - organization: true - commonName: true - serialNumber: true - domainComponent: true - Middleware17: - plugin: - PluginConf0: - name0: foobar - name1: foobar - PluginConf1: - name0: foobar - name1: foobar - Middleware18: - rateLimit: - average: 42 - period: 42s - burst: 42 - sourceCriterion: - ipStrategy: - depth: 42 - excludedIPs: - - foobar - - foobar - ipv6Subnet: 42 - requestHeaderName: foobar - requestHost: true - redis: - endpoints: - - foobar - - foobar - tls: - ca: foobar - cert: foobar - key: foobar - insecureSkipVerify: true - username: foobar - password: foobar - db: 42 - poolSize: 42 - minIdleConns: 42 - maxActiveConns: 42 - readTimeout: 42s - writeTimeout: 42s - dialTimeout: 42s - Middleware19: - redirectRegex: - regex: foobar - replacement: foobar - permanent: true - Middleware20: - redirectScheme: - scheme: foobar - port: foobar - permanent: true - Middleware21: - replacePath: - path: foobar - Middleware22: - replacePathRegex: - regex: foobar - replacement: foobar - Middleware23: - retry: - attempts: 42 - initialInterval: 42s - Middleware24: - stripPrefix: - prefixes: - - foobar - - foobar - forceSlash: true - Middleware25: - stripPrefixRegex: - regex: - - foobar - - foobar - serversTransports: - ServersTransport0: - serverName: foobar - insecureSkipVerify: true - rootCAs: - - foobar - - foobar - certificates: - - certFile: foobar - keyFile: foobar - - certFile: foobar - keyFile: foobar - maxIdleConnsPerHost: 42 - forwardingTimeouts: - dialTimeout: 42s - responseHeaderTimeout: 42s - idleConnTimeout: 42s - readIdleTimeout: 42s - pingTimeout: 42s - disableHTTP2: true - peerCertURI: foobar - spiffe: - ids: - - foobar - - foobar - trustDomain: foobar - ServersTransport1: - serverName: foobar - insecureSkipVerify: true - rootCAs: - - foobar - - foobar - certificates: - - certFile: foobar - keyFile: foobar - - certFile: foobar - keyFile: foobar - maxIdleConnsPerHost: 42 - forwardingTimeouts: - dialTimeout: 42s - responseHeaderTimeout: 42s - idleConnTimeout: 42s - readIdleTimeout: 42s - pingTimeout: 42s - disableHTTP2: true - peerCertURI: foobar - spiffe: - ids: - - foobar - - foobar - trustDomain: foobar -tcp: - routers: - TCPRouter0: - entryPoints: - - foobar - - foobar - middlewares: - - foobar - - foobar - service: foobar - rule: foobar - ruleSyntax: foobar - priority: 42 - tls: - passthrough: true - options: foobar - certResolver: foobar - domains: - - main: foobar - sans: - - foobar - - foobar - - main: foobar - sans: - - foobar - - foobar - TCPRouter1: - entryPoints: - - foobar - - foobar - middlewares: - - foobar - - foobar - service: foobar - rule: foobar - ruleSyntax: foobar - priority: 42 - tls: - passthrough: true - options: foobar - certResolver: foobar - domains: - - main: foobar - sans: - - foobar - - foobar - - main: foobar - sans: - - foobar - - foobar - services: - TCPService01: - loadBalancer: - servers: - - address: foobar - tls: true - - address: foobar - tls: true - serversTransport: foobar - proxyProtocol: - version: 42 - terminationDelay: 42 - TCPService02: - weighted: - services: - - name: foobar - weight: 42 - - name: foobar - weight: 42 - middlewares: - TCPMiddleware01: - ipAllowList: - sourceRange: - - foobar - - foobar - TCPMiddleware02: - ipWhiteList: - sourceRange: - - foobar - - foobar - TCPMiddleware03: - inFlightConn: - amount: 42 - serversTransports: - TCPServersTransport0: - dialKeepAlive: 42s - dialTimeout: 42s - proxyProtocol: - version: 42 - terminationDelay: 42s - tls: - serverName: foobar - insecureSkipVerify: true - rootCAs: - - foobar - - foobar - certificates: - - certFile: foobar - keyFile: foobar - - certFile: foobar - keyFile: foobar - peerCertURI: foobar - spiffe: - ids: - - foobar - - foobar - trustDomain: foobar - TCPServersTransport1: - dialKeepAlive: 42s - dialTimeout: 42s - proxyProtocol: - version: 42 - terminationDelay: 42s - tls: - serverName: foobar - insecureSkipVerify: true - rootCAs: - - foobar - - foobar - certificates: - - certFile: foobar - keyFile: foobar - - certFile: foobar - keyFile: foobar - peerCertURI: foobar - spiffe: - ids: - - foobar - - foobar - trustDomain: foobar -udp: - routers: - UDPRouter0: - entryPoints: - - foobar - - foobar - service: foobar - UDPRouter1: - entryPoints: - - foobar - - foobar - service: foobar - services: - UDPService01: - loadBalancer: - servers: - - address: foobar - - address: foobar - UDPService02: - weighted: - services: - - name: foobar - weight: 42 - - name: foobar - weight: 42 -tls: - certificates: - - certFile: foobar - keyFile: foobar - stores: - - foobar - - foobar - - certFile: foobar - keyFile: foobar - stores: - - foobar - - foobar - options: - Options0: - minVersion: foobar - maxVersion: foobar - cipherSuites: - - foobar - - foobar - curvePreferences: - - foobar - - foobar - clientAuth: - caFiles: - - foobar - - foobar - clientAuthType: foobar - sniStrict: true - alpnProtocols: - - foobar - - foobar - disableSessionTickets: true - preferServerCipherSuites: true - Options1: - minVersion: foobar - maxVersion: foobar - cipherSuites: - - foobar - - foobar - curvePreferences: - - foobar - - foobar - clientAuth: - caFiles: - - foobar - - foobar - clientAuthType: foobar - sniStrict: true - alpnProtocols: - - foobar - - foobar - disableSessionTickets: true - preferServerCipherSuites: true - stores: - Store0: - defaultCertificate: - certFile: foobar - keyFile: foobar - defaultGeneratedCert: - resolver: foobar - domain: - main: foobar - sans: - - foobar - - foobar - Store1: - defaultCertificate: - certFile: foobar - keyFile: foobar - defaultGeneratedCert: - resolver: foobar - domain: - main: foobar - sans: - - foobar - - foobar diff --git a/docs/content/reference/routing-configuration/other-providers/kv.md b/docs/content/reference/routing-configuration/other-providers/kv.md index 8f11d50ba..963d83cb0 100644 --- a/docs/content/reference/routing-configuration/other-providers/kv.md +++ b/docs/content/reference/routing-configuration/other-providers/kv.md @@ -5,87 +5,387 @@ description: "Read the technical documentation to learn the Traefik Routing Conf # Traefik & KV Stores -## Configuration Options +## Routing Configuration !!! info "Keys" Keys are case-insensitive. -### HTTP - -#### Routers +### Routers !!! warning "The character `@` is not authorized in the router name ``." -| Key (Path) | Description | Value | -|--------------------------------------|--------------------------------------|----------------------------| -| `traefik/http/routers//rule` | See [rule](../http/routing/rules-and-priority.md#rules) for more information. | ```Host(`example.com`)``` | -| `traefik/http/routers//ruleSyntax` | See [rule](../http/routing/rules-and-priority.md#rulesyntax) for more information.
RuleSyntax option is deprecated and will be removed in the next major version.
Please do not use this field and rewrite the router rules to use the v3 syntax. | `v3` | -| `traefik/http/routers//entrypoints/0` | See [entry points](../../install-configuration/entrypoints.md) for more information. | `web` | -| `traefik/http/routers//entrypoints/1` | See [entry points](../../install-configuration/entrypoints.md) for more information. | `websecure` | -| `traefik/http/routers//middlewares/0` | See [middlewares overview](../http/middlewares/overview.md) for more information. | `auth` | -| `traefik/http/routers//middlewares/1` | | `prefix` | -| `traefik/http/routers//service` | See [service](../http/load-balancing/service.md) for more information. | `myservice` | -| `traefik/http/routers//tls` | See [tls](../http/tls/overview.md) for more information. | `true` | -| `traefik/http/routers//tls/certresolver` | See [certResolver](../../install-configuration/tls/certificate-resolvers/overview.md) for more information. | `myresolver` | -| `traefik/http/routers//tls/domains/0/main` | See [domains](../../install-configuration/tls/certificate-resolvers/acme.md#domain-definition) for more information. | `example.org` | -| `traefik/http/routers//tls/domains/0/sans/0` | See [domains](../../install-configuration/tls/certificate-resolvers/acme.md#domain-definition) for more information. | `test.example.org` | -| `traefik/http/routers//tls/domains/0/sans/1` | See [domains](../../install-configuration/tls/certificate-resolvers/acme.md#domain-definition) for more information. | `dev.example.org` | -| `traefik/http/routers//tls/options` | See [TLS Options](../http/tls/tls-options.md) for more information. | `foobar` | -| `traefik/http/routers//observability/accesslogs` | The accessLogs option controls whether the router will produce access-logs. | `true` | -| `traefik/http/routers//observability/metrics` | The metrics option controls whether the router will produce metrics. | `true` | -| `traefik/http/routers//observability/tracing` | The tracing option controls whether the router will produce traces. | `true` | -| `traefik/http/routers//priority` | See [priority](../http/routing/rules-and-priority.md#priority-calculation) for more information. | `42` | +??? info "`traefik/http/routers//rule`" -#### Services + See [rule](../http/router/rules-and-priority.md#rules) for more information. + + | Key (Path) | Value | + |--------------------------------------|----------------------------| + | `traefik/http/routers/myrouter/rule` | ```Host(`example.com`)``` | + +??? info "`traefik/http/routers//ruleSyntax`" + + !!! warning + + RuleSyntax option is deprecated and will be removed in the next major version. + Please do not use this field and rewrite the router rules to use the v3 syntax. + + See [rule](../http/router/rules-and-priority.md#rulesyntax) for more information. + + | Key (Path) | Value | + |--------------------------------------|----------------------------| + | `traefik/http/routers/myrouter/ruleSyntax` | `v3` | + +??? info "`traefik/http/routers//entrypoints`" + + See [entry points](../../install-configuration/entrypoints.md) for more information. + + | Key (Path) | Value | + |-----------------------------------------------|-------------| + | `traefik/http/routers/myrouter/entrypoints/0` | `web` | + | `traefik/http/routers/myrouter/entrypoints/1` | `websecure` | + +??? info "`traefik/http/routers//middlewares`" + + See [middlewares overview](../http/middlewares/overview.md) for more information. + + | Key (Path) | Value | + |-----------------------------------------------|-------------| + | `traefik/http/routers/myrouter/middlewares/0` | `auth` | + | `traefik/http/routers/myrouter/middlewares/1` | `prefix` | + | `traefik/http/routers/myrouter/middlewares/2` | `cb` | + +??? info "`traefik/http/routers//service`" + + See [service](../http/load-balancing/service.md) for more information. + + | Key (Path) | Value | + |-----------------------------------------|-------------| + | `traefik/http/routers/myrouter/service` | `myservice` | + +??? info "`traefik/http/routers//tls`" + + See [tls](../http/tls/overview.md) for more information. + + | Key (Path) | Value | + |-------------------------------------|--------| + | `traefik/http/routers/myrouter/tls` | `true` | + +??? info "`traefik/http/routers//tls/certresolver`" + + See [certResolver](../../install-configuration/tls/certificate-resolvers/overview.md) for more information. + + | Key (Path) | Value | + |--------------------------------------------------|--------------| + | `traefik/http/routers/myrouter/tls/certresolver` | `myresolver` | + +??? info "`traefik/http/routers//tls/domains//main`" + + See [domains](../../install-configuration/tls/certificate-resolvers/acme.md#domain-definition) for more information. + + | Key (Path) | Value | + |----------------------------------------------------|---------------| + | `traefik/http/routers/myrouter/tls/domains/0/main` | `example.org` | + +??? info "`traefik/http/routers//tls/domains//sans/`" + + See [domains](../../install-configuration/tls/certificate-resolvers/acme.md#domain-definition) for more information. + + | Key (Path) | Value | + |------------------------------------------------------|--------------------| + | `traefik/http/routers/myrouter/tls/domains/0/sans/0` | `test.example.org` | + | `traefik/http/routers/myrouter/tls/domains/0/sans/1` | `dev.example.org` | + +??? info "`traefik/http/routers//tls/options`" + + See [TLS](../http/tls/overview.md) for more information. + + | Key (Path) | Value | + |---------------------------------------------|----------| + | `traefik/http/routers/myrouter/tls/options` | `foobar` | + +??? info "`traefik/http/routers//observability/accesslogs`" + + The accessLogs option controls whether the router will produce access-logs. + + | Key (Path) | Value | + |----------------------------------------------------------|--------| + | `traefik/http/routers/myrouter/observability/accesslogs` | `true` | + +??? info "`traefik/http/routers//observability/metrics`" + + The metrics option controls whether the router will produce metrics. + + | Key (Path) | Value | + |-------------------------------------------------------|--------| + | `traefik/http/routers/myrouter/observability/metrics` | `true` | + +??? info "`traefik/http/routers//observability/tracing`" + + The tracing option controls whether the router will produce traces. + + | Key (Path) | Value | + |-------------------------------------------------------|--------| + | `traefik/http/routers/myrouter/observability/tracing` | `true` | + +??? info "`traefik/http/routers//priority`" + + See [domains](../../install-configuration/tls/certificate-resolvers/acme.md#domain-definition) for more information. + + | Key (Path) | Value | + |------------------------------------------|-------| + | `traefik/http/routers/myrouter/priority` | `42` | + +### Services !!! warning "The character `@` is not authorized in the service name ``." -| Key (Path) | Description | Value | -|-----------------------------------------------------------------|-----------------------------------------------------------------|-----------------------------------------| -| `traefik/http/services/myservice/loadbalancer/servers/0/url` | See [servers](../http/load-balancing/service.md#servers) for more information. | `http://:/` | -| `traefik/http/services/myservice/loadbalancer/servers/0/preservePath` | See [servers](../http/load-balancing/service.md#servers) for more information. | `true` | -| `traefik/http/services/myservice/loadbalancer/servers/0/weight` | See [servers](../http/load-balancing/service.md#servers) for more information. | `1` | -| `traefik/http/services/myservice/loadbalancer/serverstransport` | Allows to reference a ServersTransport resource that is defined either with the File provider or the Kubernetes CRD one.
See [serverstransport](../http/load-balancing/serverstransport.md) for more information. | `foobar@file` | -| `traefik/http/services/myservice/loadbalancer/passhostheader` | See [Service](../http/load-balancing/service.md) for more information. | `true` | -| `traefik/http/services/myservice/loadbalancer/healthcheck/headers/X-Foo` | See [health check](../http/load-balancing/service.md#health-check) for more information. | `foobar` | -| `traefik/http/services/myservice/loadbalancer/healthcheck/hostname` | See [health check](../http/load-balancing/service.md#health-check) for more information. | `example.org` | -| `traefik/http/services/myservice/loadbalancer/healthcheck/interval` | See [health check](../http/load-balancing/service.md#health-check) for more information. | `10` | -| `traefik/http/services/myservice/loadbalancer/healthcheck/path` | See [health check](../http/load-balancing/service.md#health-check) for more information. | `/foo` | -| `traefik/http/services/myservice/loadbalancer/healthcheck/method` | See [health check](../http/load-balancing/service.md#health-check) for more information. | `foobar` | -| `traefik/http/services/myservice/loadbalancer/healthcheck/status` | See [health check](../http/load-balancing/service.md#health-check) for more information. | `42` | -| `traefik/http/services/myservice/loadbalancer/healthcheck/port` | See [health check](../http/load-balancing/service.md#health-check) for more information. | `42` | -| `traefik/http/services/myservice/loadbalancer/healthcheck/scheme` | See [health check](../http/load-balancing/service.md#health-check) for more information. | `http` | -| `traefik/http/services/myservice/loadbalancer/healthcheck/timeout` | See [health check](../http/load-balancing/service.md#health-check) for more information. | `10` | -| `traefik/http/services/myservice/loadbalancer/sticky` | See [Service](../http/load-balancing/service.md#sticky-sessions) for more information. | `true` | -| `traefik/http/services/myservice/loadbalancer/sticky/cookie/httponly` | See [Service](../http/load-balancing/service.md#sticky-sessions) for more information. | `true` | -| `traefik/http/services/myservice/loadbalancer/sticky/cookie/name` | See [Service](../http/load-balancing/service.md#sticky-sessions) for more information. | `foobar` | -| `traefik/http/services/myservice/loadbalancer/sticky/cookie/path` | See [Service](../http/load-balancing/service.md#sticky-sessions) for more information. | `/foobar` | -| `traefik/http/services/myservice/loadbalancer/sticky/cookie/secure` | See [Service](../http/load-balancing/service.md#sticky-sessions) for more information. | `true` | -| `traefik/http/services/myservice/loadbalancer/sticky/cookie/samesite` | See [Service](../http/load-balancing/service.md#sticky-sessions) for more information. | `none` | -| `traefik/http/services/myservice/loadbalancer/sticky/cookie/maxage` | See [Service](../http/load-balancing/service.md#sticky-sessions) for more information. | `42` | -| `traefik/http/services/myservice/loadbalancer/responseforwarding/flushinterval` | See [Service](../http/load-balancing/service.md) for more information. | `10` | -| `traefik/http/services//mirroring/service` | See [Service](../http/load-balancing/service.md#mirroring) for more information. | `foobar` | -| `traefik/http/services//mirroring/mirrors//name` | See [Service](../http/load-balancing/service.md#mirroring) for more information. | `foobar` | -| `traefik/http/services//mirroring/mirrors//percent` | See [Service](../http/load-balancing/service.md#mirroring)for more information. | `42` | -| `traefik/http/services//weighted/services//name` | See [Service](../http/load-balancing/service.md#weighted-round-robin-wrr) for more information. | `foobar` | -| `traefik/http/services//weighted/services//weight` | See [Service](../http/load-balancing/service.md#weighted-round-robin-wrr) for more information. | `42` | -| `traefik/http/services//weighted/sticky/cookie/name` | See [Service](../http/load-balancing/service.md#weighted-round-robin-wrr) for more information. | `foobar` | -| `traefik/http/services//weighted/sticky/cookie/secure` | See [Service](../http/load-balancing/service.md#weighted-round-robin-wrr) for more information. | `true` | -| `traefik/http/services//weighted/sticky/cookie/samesite` | See [Service](../http/load-balancing/service.md#weighted-round-robin-wrr) for more information. | `none` | -| `traefik/http/services//weighted/sticky/cookie/httpOnly` | See [Service](../http/load-balancing/service.md#weighted-round-robin-wrr) for more information. | `true` | -| `traefik/http/services//weighted/sticky/cookie/maxage` | See [Service](../http/load-balancing/service.md#weighted-round-robin-wrr) for more information. | `42` | -| `traefik/http/services//failover/fallback` | See [Failover](../http/load-balancing/service.md#failover) for more information. | `backup` | -| `traefik/http/services//failover/healthcheck` | See [Failover](../http/load-balancing/service.md#failover) for more information. | `{}` | -| `traefik/http/services//failover/service` | See [Failover](../http/load-balancing/service.md#failover) for more information. | `main` | +??? info "`traefik/http/services//loadbalancer/servers//url`" -#### Middleware + See [servers](../http/load-balancing/service.md#servers) for more information. -##### Configuration Options + | Key (Path) | Value | + |-----------------------------------------------------------------|-----------------------------------------| + | `traefik/http/services/myservice/loadbalancer/servers/0/url` | `http://:/` | -| Key (Path) | Description | Value | -|-----------------------------------------------------------------|-----------------------------------------------------------------|-----------------------------------------| -| `traefik/http/middlewares/mymiddleware/middleware_type/middleware_option` | With `middleware_type` the type of middleware (ex: `forwardAuth`, `headers`, etc)
and `middleware_option` the middleware option to set (ex for the middleware `addPrefix`: `prefix`).
More information about available middlewares in the dedicated [middlewares section](../http/middlewares/overview.md). | `foobar` | +??? info "`traefik/http/services//loadbalancer/servers//preservePath`" + + See [servers](../http/load-balancing/service.md#servers) for more information. + + | Key (Path) | Value | + |-----------------------------------------------------------------|-----------------------------------------| + | `traefik/http/services/myservice/loadbalancer/servers/0/preservePath` | `true` | + +??? info "`traefik/http/services//loadbalancer/servers//weight`" + + See [servers](../http/load-balancing/service.md#servers) for more information. + + | Key (Path) | Value | + |-----------------------------------------------------------------|-----------------------------------------| + | `traefik/http/services/myservice/loadbalancer/servers/0/weight` | `1` | + +??? info "`traefik/http/services//loadbalancer/serverstransport`" + + Allows to reference a ServersTransport resource that is defined either with the File provider or the Kubernetes CRD one. + See [serverstransport](../http/load-balancing/serverstransport.md) for more information. + + | Key (Path) | Value | + |-----------------------------------------------------------------|---------------| + | `traefik/http/services/myservice/loadbalancer/serverstransport` | `foobar@file` | + +??? info "`traefik/http/services//loadbalancer/passhostheader`" + + | Key (Path) | Value | + |-----------------------------------------------------------------|--------| + | `traefik/http/services/myservice/loadbalancer/passhostheader` | `true` | + +??? info "`traefik/http/services//loadbalancer/healthcheck/headers/`" + + See [health check](../http/load-balancing/service.md#health-check) for more information. + + | Key (Path) | Value | + |--------------------------------------------------------------------------|----------| + | `traefik/http/services/myservice/loadbalancer/healthcheck/headers/X-Foo` | `foobar` | + +??? info "`traefik/http/services//loadbalancer/healthcheck/hostname`" + + See [health check](../http/load-balancing/service.md#health-check) for more information. + + | Key (Path) | Value | + |---------------------------------------------------------------------|---------------| + | `traefik/http/services/myservice/loadbalancer/healthcheck/hostname` | `example.org` | + +??? info "`traefik/http/services//loadbalancer/healthcheck/interval`" + + See [health check](../http/load-balancing/service.md#health-check) for more information. + + | Key (Path) | Value | + |---------------------------------------------------------------------|-------| + | `traefik/http/services/myservice/loadbalancer/healthcheck/interval` | `10` | + +??? info "`traefik/http/services//loadbalancer/healthcheck/path`" + + See [health check](../http/load-balancing/service.md#health-check) for more information. + + | Key (Path) | Value | + |-----------------------------------------------------------------|--------| + | `traefik/http/services/myservice/loadbalancer/healthcheck/path` | `/foo` | + +??? info "`traefik/http/services//loadbalancer/healthcheck/method`" + + See [health check](../http/load-balancing/service.md#health-check) for more information. + + | Key (Path) | Value | + |-------------------------------------------------------------------|----------| + | `traefik/http/services/myservice/loadbalancer/healthcheck/method` | `foobar` | + +??? info "`traefik/http/services//loadbalancer/healthcheck/status`" + + See [health check](../http/load-balancing/service.md#health-check) for more information. + + | Key (Path) | Value | + |-------------------------------------------------------------------|-------| + | `traefik/http/services/myservice/loadbalancer/healthcheck/status` | `42` | + +??? info "`traefik/http/services//loadbalancer/healthcheck/port`" + + See [health check](../http/load-balancing/service.md#health-check) for more information. + + | Key (Path) | Value | + |-----------------------------------------------------------------|-------| + | `traefik/http/services/myservice/loadbalancer/healthcheck/port` | `42` | + +??? info "`traefik/http/services//loadbalancer/healthcheck/scheme`" + + See [health check](../http/load-balancing/service.md#health-check) for more information. + + | Key (Path) | Value | + |-------------------------------------------------------------------|--------| + | `traefik/http/services/myservice/loadbalancer/healthcheck/scheme` | `http` | + +??? info "`traefik/http/services//loadbalancer/healthcheck/timeout`" + + See [health check](../http/load-balancing/service.md#health-check) for more information. + + | Key (Path) | Value | + |--------------------------------------------------------------------|-------| + | `traefik/http/services/myservice/loadbalancer/healthcheck/timeout` | `10` | + +??? info "`traefik/http/services//loadbalancer/sticky`" + + | Key (Path) | Value | + |-------------------------------------------------------|--------| + | `traefik/http/services/myservice/loadbalancer/sticky` | `true` | + +??? info "`traefik/http/services//loadbalancer/sticky/cookie/httponly`" + + | Key (Path) | Value | + |-----------------------------------------------------------------------|--------| + | `traefik/http/services/myservice/loadbalancer/sticky/cookie/httponly` | `true` | + +??? info "`traefik/http/services//loadbalancer/sticky/cookie/name`" + + | Key (Path) | Value | + |-------------------------------------------------------------------|----------| + | `traefik/http/services/myservice/loadbalancer/sticky/cookie/name` | `foobar` | + +??? info "`traefik/http/services//loadbalancer/sticky/cookie/path`" + + | Key (Path) | Value | + |-------------------------------------------------------------------|-----------| + | `traefik/http/services/myservice/loadbalancer/sticky/cookie/path` | `/foobar` | + +??? info "`traefik/http/services//loadbalancer/sticky/cookie/secure`" + + | Key (Path) | Value | + |---------------------------------------------------------------------|--------| + | `traefik/http/services/myservice/loadbalancer/sticky/cookie/secure` | `true` | + +??? info "`traefik/http/services//loadbalancer/sticky/cookie/samesite`" + + | Key (Path) | Value | + |-----------------------------------------------------------------------|--------| + | `traefik/http/services/myservice/loadbalancer/sticky/cookie/samesite` | `none` | + +??? info "`traefik/http/services//loadbalancer/sticky/cookie/maxage`" + + | Key (Path) | Value | + |---------------------------------------------------------------------|-------| + | `traefik/http/services/myservice/loadbalancer/sticky/cookie/maxage` | `42` | + +??? info "`traefik/http/services//loadbalancer/responseforwarding/flushinterval`" + + | Key (Path) | Value | + |---------------------------------------------------------------------------------|-------| + | `traefik/http/services/myservice/loadbalancer/responseforwarding/flushinterval` | `10` | + +??? info "`traefik/http/services//mirroring/service`" + + | Key (Path) | Value | + |----------------------------------------------------------|----------| + | `traefik/http/services//mirroring/service` | `foobar` | + +??? info "`traefik/http/services//mirroring/mirrors//name`" + + | Key (Path) | Value | + |-------------------------------------------------------------------|----------| + | `traefik/http/services//mirroring/mirrors//name` | `foobar` | + +??? info "`traefik/http/services//mirroring/mirrors//percent`" + + | Key (Path) | Value | + |----------------------------------------------------------------------|-------| + | `traefik/http/services//mirroring/mirrors//percent` | `42` | + +??? info "`traefik/http/services//weighted/services//name`" + + | Key (Path) | Value | + |-------------------------------------------------------------------|----------| + | `traefik/http/services//weighted/services//name` | `foobar` | + +??? info "`traefik/http/services//weighted/services//weight`" + + | Key (Path) | Value | + |---------------------------------------------------------------------|-------| + | `traefik/http/services//weighted/services//weight` | `42` | + +??? info "`traefik/http/services//weighted/sticky/cookie/name`" + + | Key (Path) | Value | + |--------------------------------------------------------------------|----------| + | `traefik/http/services//weighted/sticky/cookie/name` | `foobar` | + +??? info "`traefik/http/services//weighted/sticky/cookie/secure`" + + | Key (Path) | Value | + |----------------------------------------------------------------------|--------| + | `traefik/http/services//weighted/sticky/cookie/secure` | `true` | + +??? info "`traefik/http/services//weighted/sticky/cookie/samesite`" + + | Key (Path) | Value | + |------------------------------------------------------------------------|--------| + | `traefik/http/services//weighted/sticky/cookie/samesite` | `none` | + +??? info "`traefik/http/services//weighted/sticky/cookie/httpOnly`" + + | Key (Path) | Value | + |------------------------------------------------------------------------|--------| + | `traefik/http/services//weighted/sticky/cookie/httpOnly` | `true` | + +??? info "`traefik/http/services//weighted/sticky/cookie/maxage`" + + | Key (Path) | Value | + |----------------------------------------------------------------------|-------| + | `traefik/http/services//weighted/sticky/cookie/maxage` | `42` | + +??? info "`traefik/http/services//failover/fallback`" + + See [Failover](../http/load-balancing/service.md#failover) for more information + + | Key (Path) | Value | + |----------------------------------------------------------------------|-------| + | `traefik/http/services//failover/fallback` | `backup` | + +??? info "`traefik/http/services//failover/healthcheck`" + + See [Failover](../http/load-balancing/service.md#failover) for more information + + | Key (Path) | Value | + |----------------------------------------------------------------------|-------| + | `traefik/http/services//failover/healthcheck` | `{}` | + +??? info "`traefik/http/services//failover/service`" + + See [Failover](../http/load-balancing/service.md#failover) for more information + + | Key (Path) | Value | + |----------------------------------------------------------------------|-------| + | `traefik/http/services//failover/service` | `main` | + +### Middleware + +More information about available middlewares in the dedicated [middlewares section](../http/middlewares/overview.md). !!! warning "The character `@` is not authorized in the middleware name." @@ -93,68 +393,142 @@ description: "Read the technical documentation to learn the Traefik Routing Conf If you declare multiple middleware with the same name but with different parameters, the middleware fails to be declared. -##### Configuration Example - -```bash -# Declaring a middleware -traefik/http/middlewares/myAddPrefix/addPrefix/prefix=/foobar -# Referencing a middleware -traefik/http/routers//middlewares/0=myAddPrefix -``` - -#### ServerTransport - -##### Configuration Options - -| Key (Path) | Description | Value | -|-----------------------------------------------------------------|-----------------------------------------------------------------|-----------------------------------------| -| `traefik/http/serversTransports//st_option` | With `st_option` the ServerTransport option to set (ex `maxIdleConnsPerHost`).
More information about available options in the dedicated [ServerTransport section](../http/load-balancing/serverstransport.md). | ServerTransport Options | - -##### Configuration Example - -```bash -# Declaring a ServerTransport -traefik/http/serversTransports/myServerTransport/maxIdleConnsPerHost=-1 -traefik/http/serversTransports/myServerTransport/certificates/0/certFile=mypath/cert.pem -traefik/http/serversTransports/myServerTransport/certificates/0/keyFile=mypath/key.pem -# Referencing a middleware -traefik/http/services/myService/serversTransports/0=myServerTransport -``` - ### TCP You can declare TCP Routers and/or Services using KV. -#### Routers +#### TCP Routers -| Key (Path) | Description | Value | -|-------------------------------------------------|-------------------------------------------------|-------| -| `traefik/tcp/routers/mytcprouter/entrypoints/0` | See [entry points](../../install-configuration/entrypoints.md) for more information. | `ep1` | -| `traefik/tcp/routers/mytcprouter/entrypoints/1` | See [entry points](../../install-configuration/entrypoints.md) for more information. | `ep2` | -| `traefik/tcp/routers/my-router/rule` | See [entry points](../../install-configuration/entrypoints.md) for more information. | ```HostSNI(`example.com`)``` | -| `traefik/tcp/routers/mytcprouter/service` | See [service](../tcp/service.md) for more information. | `myservice` | -| `traefik/tcp/routers/mytcprouter/tls` | See [TLS](../tcp/tls.md) for more information. | `true` | -| `traefik/tcp/routers/mytcprouter/tls/certresolver` | See [certResolver](../tcp/tls.md#configuration-options) for more information. | `myresolver` | -| `traefik/tcp/routers/mytcprouter/tls/domains/0/main` | See [TLS](../tcp/tls.md) for more information. | `example.org` | -| `traefik/tcp/routers/mytcprouter/tls/domains/0/sans/0` | See [TLS](../tcp/tls.md) for more information. | `test.example.org` | -| `traefik/tcp/routers/mytcprouter/tls/domains/0/sans/1` | See [TLS](../tcp/tls.md) for more information. | `dev.example.org` | -| `traefik/tcp/routers/mytcprouter/tls/options` | See [TLS](../tcp/tls.md) for more information. | `foobar` | -| `traefik/tcp/routers/mytcprouter/tls/passthrough` | See [TLS](../tcp/tls.md) for more information. | `true` | -| `traefik/tcp/routers/mytcprouter/priority` | See [priority](../tcp/routing/rules-and-priority.md#priority-calculation) for more information. | `42` | +??? info "`traefik/tcp/routers//entrypoints`" -#### Services + See [entry points](../../install-configuration/entrypoints.md) for more information. -| Key (Path) | Description | Value | -|--------------------------------------------------------------------|--------------------------------------------------------------------|------------------| -| `traefik/tcp/services/mytcpservice/loadbalancer/servers/0/address` | See [servers](../tcp/service.md#servers-load-balancer) for more information. | `xx.xx.xx.xx:xx` | -| `traefik/tcp/services/mytcpservice/loadbalancer/servers/0/tls` | See [servers](../tcp/service.md#servers-load-balancer) for more information. | `true` | -| `traefik/tcp/services/myservice/loadbalancer/serverstransport` | Allows to reference a ServersTransport resource that is defined either with the File provider or the Kubernetes CRD one.
See [serverstransport](../tcp/serverstransport.md) for more information. | `foobar@file` | -| `traefik/tcp/services//weighted/services/0/name` | See [Service](../tcp/service.md#weighted-round-robin) for more information. | `foobar` | -| `traefik/tcp/services//weighted/services/0/weight` | See [Service](../tcp/service.md#weighted-round-robin) for more information. | `42` | + | Key (Path) | Value | + |-------------------------------------------------|-------| + | `traefik/tcp/routers/mytcprouter/entrypoints/0` | `ep1` | + | `traefik/tcp/routers/mytcprouter/entrypoints/1` | `ep2` | + +??? info "`traefik/tcp/routers//rule`" -#### Middleware + See [entry points](../../install-configuration/entrypoints.md) for more information. -##### Configuration Options + | Key (Path) | Value | + |--------------------------------------|------------------------------| + | `traefik/tcp/routers/my-router/rule` | ```HostSNI(`example.com`)``` | + +??? info "`traefik/tcp/routers//service`" + + See [service](../tcp/service.md) for more information. + + | Key (Path) | Value | + |-------------------------------------------|-------------| + | `traefik/tcp/routers/mytcprouter/service` | `myservice` | + +??? info "`traefik/tcp/routers//tls`" + + See [TLS](../tcp/tls.md) for more information. + + | Key (Path) | Value | + |---------------------------------------|--------| + | `traefik/tcp/routers/mytcprouter/tls` | `true` | + +??? info "`traefik/tcp/routers//tls/certresolver`" + + See [certResolver](../tcp/tls.md#configuration-options) for more information. + + | Key (Path) | Value | + |----------------------------------------------------|--------------| + | `traefik/tcp/routers/mytcprouter/tls/certresolver` | `myresolver` | + +??? info "`traefik/tcp/routers//tls/domains//main`" + + See [TLS](../tcp/tls.md) for more information. + + | Key (Path) | Value | + |------------------------------------------------------|---------------| + | `traefik/tcp/routers/mytcprouter/tls/domains/0/main` | `example.org` | + +??? info "`traefik/tcp/routers//tls/domains//sans`" + + See [TLS](../tcp/tls.md) for more information. + + | Key (Path) | Value | + |--------------------------------------------------------|--------------------| + | `traefik/tcp/routers/mytcprouter/tls/domains/0/sans/0` | `test.example.org` | + | `traefik/tcp/routers/mytcprouter/tls/domains/0/sans/1` | `dev.example.org` | + +??? info "`traefik/tcp/routers//tls/options`" + + See [TLS](../tcp/tls.md) for more information. + + | Key (Path) | Value | + |-----------------------------------------------|----------| + | `traefik/tcp/routers/mytcprouter/tls/options` | `foobar` | + +??? info "`traefik/tcp/routers//tls/passthrough`" + + See [TLS](../tcp/tls.md) for more information. + + | Key (Path) | Value | + |---------------------------------------------------|--------| + | `traefik/tcp/routers/mytcprouter/tls/passthrough` | `true` | + +??? info "`traefik/tcp/routers//priority`" + + See [priority](../tcp/router/rules-and-priority.md#priority) for more information. + + | Key (Path) | Value | + |------------------------------------------|-------| + | `traefik/tcp/routers/mytcprouter/priority` | `42` | + +#### TCP Services + +??? info "`traefik/tcp/services//loadbalancer/servers//address`" + + See [servers](../tcp/service.md#servers-load-balancer) for more information. + + | Key (Path) | Value | + |--------------------------------------------------------------------|------------------| + | `traefik/tcp/services/mytcpservice/loadbalancer/servers/0/address` | `xx.xx.xx.xx:xx` | + +??? info "`traefik/tcp/services//loadbalancer/servers//tls`" + + See [servers](../tcp/service.md#servers-load-balancer) for more information. + + | Key (Path) | Value | + |--------------------------------------------------------------------|------------------| + | `traefik/tcp/services/mytcpservice/loadbalancer/servers/0/tls` | `true` | + +??? info "`traefik/tcp/services//loadbalancer/proxyprotocol/version`" + + See [PROXY protocol](../tcp/service.md#proxy-protocol) for more information. + + | Key (Path) | Value | + |------------------------------------------------------------------------|-------| + | `traefik/tcp/services/mytcpservice/loadbalancer/proxyprotocol/version` | `1` | + +??? info "`traefik/tcp/services//loadbalancer/serverstransport`" + + Allows to reference a ServersTransport resource that is defined either with the File provider or the Kubernetes CRD one. + See [serverstransport](../tcp/serverstransport.md) for more information. + + | Key (Path) | Value | + |-----------------------------------------------------------------|---------------| + | `traefik/tcp/services/myservice/loadbalancer/serverstransport` | `foobar@file` | + +??? info "`traefik/tcp/services//weighted/services//name`" + + | Key (Path) | Value | + |---------------------------------------------------------------------|----------| + | `traefik/tcp/services//weighted/services/0/name` | `foobar` | + +??? info "`traefik/tcp/services//weighted/services//weight`" + + | Key (Path) | Value | + |------------------------------------------------------------------|-------| + | `traefik/tcp/services//weighted/services/0/weight` | `42` | + +#### TCP Middleware You can declare pieces of middleware using tags starting with `traefik/tcp/middlewares/{name-of-your-choice}.`, followed by the middleware type/options. @@ -162,83 +536,80 @@ For example, to declare a middleware [`InFlightConn`](../tcp/middlewares/infligh More information about available middlewares in the dedicated [middlewares section](../tcp/middlewares/overview.md). -| Key (Path) | Description | Value | -|-----------------------------------------------------------------|-----------------------------------------------------------------|-----------------------------------------| -| `traefik/tcp/middlewares/mymiddleware/middleware_type/middleware_option` | With `middleware_type` the type of middleware (ex: `inflightconn`)
and `middleware_option` the middleware option to set (ex for the middleware `inflightconn`: `amount`).
More information about available middlewares in the dedicated [middlewares section](../tcp/middlewares/overview.md). | `foobar` | +??? example "Declaring and Referencing a Middleware" + + ```bash + # ... + # Declaring a middleware + traefik/tcp/middlewares/test-inflightconn/amount=10 + # Referencing a middleware + traefik/tcp/routers.my-service/middlewares=test-inflightconn + ``` !!! warning "Conflicts in Declaration" If you declare multiple middleware with the same name but with different parameters, the middleware fails to be declared. -##### Configuration Example - -```bash -# Declaring a middleware -traefik/tcp/middlewares/test-inflightconn/amount=10 -# Referencing a middleware -traefik/tcp/routers//middlewares/0=test-inflightconn -``` - -#### ServerTransport - -##### Configuration Options - -| Key (Path) | Description | Value | -|-----------------------------------------------------------------|-----------------------------------------------------------------|-----------------------------------------| -| `traefik/tcp/serversTransports//st_option` | With `st_option` the ServerTransport option to set (ex `maxIdleConnsPerHost`).
More information about available options in the dedicated [ServerTransport section](../tcp/serverstransport.md). | ServerTransport Options | - -##### Configuration Example - -```bash -# Declaring a ServerTransport -traefik/tcp/serversTransports/myServerTransport/maxIdleConnsPerHost=-1 -# Referencing a middleware -traefik/tcp/services/myService/serversTransports/0=myServerTransport -``` - ### UDP You can declare UDP Routers and/or Services using KV. -#### Routers +#### UDP Routers -| Key (Path) | Description | Value | -|------------------------------------------------------------------|------------------------------------------------------------------|-------| -| `traefik/udp/routers/myudprouter/entrypoints/0` | See [UDP Router](../udp/routing/rules-priority.md#entrypoints) for more information. | `foobar` | -| `traefik/udp/routers/myudprouter/service` | See [UDP Router](../udp/routing/rules-priority.md#configuration-example) for more information. | `foobar` | +??? info "`traefik/udp/routers//entrypoints/`" -#### Services + | Key (Path) | Value | + |------------------------------------------------------------------|-------| + | `traefik/udp/routers/myudprouter/entrypoints/0` | `foobar` | -| Key (Path) | Description | Value | -|------------------------------------------------------------------|------------------------------------------------------------------|-------| -| `traefik/udp/services/loadBalancer/servers//address` | See [UDP Service](../udp/service.md) for more information. | `foobar` | -| `traefik/udp/services/weighted/services/0/name` | See [UDP Service](../udp/service.md) for more information. | `foobar` | -| `traefik/udp/services/weighted/servers/0/weight` |See [UDP Service](../udp/service.md) for more information. | `42` | +??? info "`traefik/udp/routers//service`" + + | Key (Path) | Value | + |------------------------------------------------------------------|-------| + | `traefik/udp/routers/myudprouter/service` | `foobar` | + +#### UDP Services + +??? info "`traefik/udp/services/loadBalancer/servers//address`" + + | Key (Path) | Value | + |------------------------------------------------------------------|-------| + | `traefik/udp/services/loadBalancer/servers//address` | `foobar` | + +??? info "`traefik/udp/services/weighted/services//name`" + + | Key (Path) | Value | + |------------------------------------------------------------------|-------| + | `traefik/udp/services/weighted/services/0/name` | `foobar` | + +??? info "`traefik/udp/services/weighted/services//name`" + + | Key (Path) | Value | + |------------------------------------------------------------------|-------| + | `traefik/udp/services/weighted/servers/0/weight` | `42` | ## TLS ### TLS Options -With the KV provider, you configure some parameters of the TLS connection using the `tls/options` key. +With the KV provider, you configure some parameters of the TLS connection using the `tls/options` key. For example, you can define a basic setup like this: -For example, you can define a basic setup like this: +| Key (Path) | Value | +|------------------------------------------------------|----------| +| `traefik/tls/options/Options0/alpnProtocols/0` | `foobar` | +| `traefik/tls/options/Options0/cipherSuites/0` | `foobar` | +| `traefik/tls/options/Options0/clientAuth/caFiles/0` | `foobar` | +| `traefik/tls/options/Options0/disableSessiontickets` | `true` | -| Key (Path) | Description | Value | -|------------------------------------------------------|------------------------------------------------------|----------| -| `traefik/tls/options/Options0/alpnProtocols/0` | See [TLS Options](../http/tls/tls-options.md) for more information. | `foobar` | -| `traefik/tls/options/Options0/cipherSuites/0` | See [TLS Options](../http/tls/tls-options.md) for more information. | `foobar` | -| `traefik/tls/options/Options0/clientAuth/caFiles/0` | See [TLS Options](../http/tls/tls-options.md) for more information. | `foobar` | -| `traefik/tls/options/Options0/disableSessiontickets` | See [TLS Options](../http/tls/tls-options.md) for more information. | `true` | +For more information on the available TLS options that can be configured, please refer to the [TLS Options](../http/tls/tls-options.md) page. ### TLS Default Generated Certificates -You can configure Traefik to use an ACME provider (like Let's Encrypt) to generate the default certificate. +You can configure Traefik to use an ACME provider (like Let's Encrypt) to generate the default certificate. The configuration to resolve the default certificate should be defined in a TLS store: -The configuration to resolve the default certificate should be defined in a TLS store. - -| Key (Path) | Description | Value | -|----------------------------------------------------------------|----------------------------------------------------------------|----------| -| `traefik/tls/stores/Store0/defaultGeneratedCert/domain/main` | See [TLS](../http/tls/tls-certificates.md#certificates-stores) for more information. | `foobar` | -| `traefik/tls/stores/Store0/defaultGeneratedCert/domain/sans/0` | See [TLS](../http/tls/tls-certificates.md#certificates-stores) for more information| `foobar` | -| `traefik/tls/stores/Store0/defaultGeneratedCert/domain/sans/1` | See [TLS](../http/tls/tls-certificates.md#certificates-stores) for more information| `foobar` | -| `traefik/tls/stores/Store0/defaultGeneratedCert/resolver` | See [TLS](../http/tls/tls-certificates.md#certificates-stores) for more information| `foobar` | +| Key (Path) | Value | +|----------------------------------------------------------------|----------| +| `traefik/tls/stores/Store0/defaultGeneratedCert/domain/main` | `foobar` | +| `traefik/tls/stores/Store0/defaultGeneratedCert/domain/sans/0` | `foobar` | +| `traefik/tls/stores/Store0/defaultGeneratedCert/domain/sans/1` | `foobar` | +| `traefik/tls/stores/Store0/defaultGeneratedCert/resolver` | `foobar` | diff --git a/docs/content/reference/routing-configuration/other-providers/nomad.md b/docs/content/reference/routing-configuration/other-providers/nomad.md index 18c962ed7..b8cc400c9 100644 --- a/docs/content/reference/routing-configuration/other-providers/nomad.md +++ b/docs/content/reference/routing-configuration/other-providers/nomad.md @@ -25,7 +25,7 @@ With Nomad, Traefik can leverage tags attached to a service to generate routing ### General -Traefik creates, for each Nomad service, a corresponding Traefik [service](../http/load-balancing/service.md) and [router](../http/routing/rules-and-priority.md). +Traefik creates, for each Nomad service, a corresponding Traefik [service](../http/load-balancing/service.md) and [router](../http/router/rules-and-priority.md). The Traefik service automatically gets a server per instance in this Nomad service, and the router gets a default rule attached to it, based on the Nomad service name. @@ -37,7 +37,7 @@ For example, to change the rule, you could add the tag ```traefik.http.routers.m ??? info "`traefik.http.routers..rule`" - See [rule](../http/routing/rules-and-priority.md) for more information. + See [rule](../http/router/rules-and-priority.md) for more information. ```yaml traefik.http.routers.myrouter.rule=Host(`example.com`) @@ -50,7 +50,7 @@ For example, to change the rule, you could add the tag ```traefik.http.routers.m RuleSyntax option is deprecated and will be removed in the next major version. Please do not use this field and rewrite the router rules to use the v3 syntax. - See [ruleSyntax](../http/routing/rules-and-priority.md#rulesyntax) for more information. + See [ruleSyntax](../http/router/rules-and-priority.md#rulesyntax) for more information. ```yaml traefik.http.routers.myrouter.ruleSyntax=v3 @@ -120,7 +120,7 @@ For example, to change the rule, you could add the tag ```traefik.http.routers.m ??? info "`traefik.http.routers..priority`" - See [priority](../http/routing/rules-and-priority.md#priority-calculation) for more information. + See [priority](../http/router/rules-and-priority.md#priority-calculation) for more information. ```yaml traefik.http.routers.myrouter.priority=42 @@ -222,14 +222,6 @@ you'd add the tag `traefik.http.services.{name-of-your-choice}.loadbalancer.pass traefik.http.services.myservice.loadbalancer.healthcheck.interval=10 ``` -??? info "`traefik.http.services..loadbalancer.healthcheck.unhealthyinterval`" - - See [health check](../http/load-balancing/service.md#health-check) for more information. - - ```yaml - traefik.http.services.myservice.loadbalancer.healthcheck.unhealthyinterval=10 - ``` - ??? info "`traefik.http.services..loadbalancer.healthcheck.path`" See [health check](../http/load-balancing/service.md#health-check) for more information. @@ -377,7 +369,7 @@ You can declare TCP Routers and/or Services using tags. ??? info "`traefik.tcp.routers..rule`" - See [rule](../tcp/routing/rules-and-priority.md#rules) for more information. + See [rule](../tcp/router/rules-and-priority.md#rules) for more information. ```yaml traefik.tcp.routers.mytcprouter.rule=HostSNI(`example.com`) @@ -398,7 +390,7 @@ You can declare TCP Routers and/or Services using tags. ??? info "`traefik.tcp.routers..priority`" - See [priority](../tcp/routing/rules-and-priority.md#priority-calculation) for more information. + See [priority](../tcp/router/rules-and-priority.md#priority) for more information. ```yaml traefik.tcp.routers.myrouter.priority=42 @@ -454,7 +446,7 @@ You can declare TCP Routers and/or Services using tags. ??? info "`traefik.tcp.routers..tls.passthrough`" - See [Passthrough](../tcp/tls.md#opt-passthrough) for more information. + See [Passthrough](../tcp/tls.md#passthrough) for more information. ```yaml traefik.tcp.routers.mytcprouter.tls.passthrough=true @@ -478,6 +470,14 @@ You can declare TCP Routers and/or Services using tags. traefik.tcp.services.mytcpservice.loadbalancer.server.tls=true ``` +??? info "`traefik.tcp.services..loadbalancer.proxyprotocol.version`" + + See [PROXY protocol](../tcp/service.md#proxy-protocol) for more information. + + ```yaml + traefik.tcp.services.mytcpservice.loadbalancer.proxyprotocol.version=1 + ``` + ??? info "`traefik.tcp.services..loadbalancer.serverstransport`" Allows to reference a ServersTransport resource that is defined either with the File provider or the Kubernetes CRD one. diff --git a/docs/content/reference/routing-configuration/other-providers/swarm.md b/docs/content/reference/routing-configuration/other-providers/swarm.md index 427484d8d..51b2371c0 100644 --- a/docs/content/reference/routing-configuration/other-providers/swarm.md +++ b/docs/content/reference/routing-configuration/other-providers/swarm.md @@ -48,6 +48,7 @@ With Docker Swarm, Traefik can leverage labels attached to a service to generate then that service is automatically assigned to the router. ```yaml + version: "3" services: my-container: deploy: @@ -66,6 +67,7 @@ With Docker Swarm, Traefik can leverage labels attached to a service to generate Forward requests for `http://example.com` to `http://:12345`: ```yaml + version: "3" services: my-container: # ... @@ -91,6 +93,7 @@ With Docker Swarm, Traefik can leverage labels attached to a service to generate In this example, requests are forwarded for `http://example-a.com` to `http://:8000` in addition to `http://example-b.com` forwarding to `http://:9000`: ```yaml + version: "3" services: my-container: # ... @@ -116,7 +119,7 @@ With Docker Swarm, Traefik can leverage labels attached to a service to generate ### General -Traefik creates, for each container, a corresponding [service](../http/load-balancing/service.md) and [router](../http/routing/rules-and-priority.md). +Traefik creates, for each container, a corresponding [service](../http/load-balancing/service.md) and [router](../http/router/rules-and-priority.md). The Service automatically gets a server per instance of the container, and the router automatically gets a rule defined by `defaultRule` (if no rule for it was defined in labels). @@ -158,7 +161,7 @@ For example, to change the rule, you could add the label ```traefik.http.routers ??? info "`traefik.http.routers..rule`" - See [rule](../http/routing/rules-and-priority.md) for more information. + See [rule](../http/router/rules-and-priority.md) for more information. ```yaml - "traefik.http.routers.myrouter.rule=Host(`example.com`)" @@ -171,7 +174,7 @@ For example, to change the rule, you could add the label ```traefik.http.routers RuleSyntax option is deprecated and will be removed in the next major version. Please do not use this field and rewrite the router rules to use the v3 syntax. - See [ruleSyntax](../http/routing/rules-and-priority.md#rulesyntax) for more information. + See [ruleSyntax](../http/router/rules-and-priority.md#rulesyntax) for more information. ```yaml traefik.http.routers.myrouter.ruleSyntax=v3 @@ -265,7 +268,7 @@ For example, to change the rule, you could add the label ```traefik.http.routers ??? info "`traefik.http.routers..priority`" - See [priority](../http/routing/rules-and-priority.md#priority-calculation) for more information. + See [priority](../http/router/rules-and-priority.md#priority-calculation) for more information. ```yaml - "traefik.http.routers.myrouter.priority=42" @@ -348,14 +351,6 @@ you'd add the label `traefik.http.services..loadbalancer.pa - "traefik.http.services.myservice.loadbalancer.healthcheck.interval=10s" ``` -??? info "`traefik.http.services..loadbalancer.healthcheck.unhealthyinterval`" - - See [health check](../http/load-balancing/service.md#health-check) for more information. - - ```yaml - - "traefik.http.services.myservice.loadbalancer.healthcheck.unhealthyinterval=10s" - ``` - ??? info "`traefik.http.services..loadbalancer.healthcheck.path`" See [health check](../http/load-balancing/service.md#health-check) for more information. @@ -520,7 +515,7 @@ You can declare TCP Routers and/or Services using labels. ??? info "`traefik.tcp.routers..rule`" - See [rule](../tcp/routing/rules-and-priority.md#rules) for more information. + See [rule](../tcp/router/rules-and-priority.md#rules) for more information. ```yaml - "traefik.tcp.routers.mytcprouter.rule=HostSNI(`example.com`)" @@ -589,7 +584,7 @@ You can declare TCP Routers and/or Services using labels. ??? info "`traefik.tcp.routers..tls.passthrough`" - See [Passthrough](../tcp/tls.md#opt-passthrough) for more information. + See [Passthrough](../tcp/tls.md#passthrough) for more information. ```yaml - "traefik.tcp.routers.mytcprouter.tls.passthrough=true" @@ -597,7 +592,7 @@ You can declare TCP Routers and/or Services using labels. ??? info "`traefik.tcp.routers..priority`" - See [priority](../tcp/routing/rules-and-priority.md) for more information. + See [priority](../tcp/router/rules-and-priority.md) for more information. ```yaml - "traefik.tcp.routers.myrouter.priority=42" @@ -621,6 +616,14 @@ You can declare TCP Routers and/or Services using labels. - "traefik.tcp.services.mytcpservice.loadbalancer.server.tls=true" ``` +??? info "`traefik.tcp.services..loadbalancer.proxyprotocol.version`" + + See [PROXY protocol](../tcp/service.md#proxy-protocol) for more information. + + ```yaml + - "traefik.tcp.services.mytcpservice.loadbalancer.proxyprotocol.version=1" + ``` + ??? info "`traefik.tcp.services..loadbalancer.serverstransport`" Allows to reference a ServersTransport resource that is defined either with the File provider or the Kubernetes CRD one. @@ -730,7 +733,7 @@ otherwise it will randomly pick one (depending on how docker is returning them). #### `traefik.swarm.lbswarm` ```yaml -- "traefik.swarm.lbswarm=true" +- "traefik.docker.lbswarm=true" ``` Enables Swarm's inbuilt load balancer (only relevant in Swarm Mode). diff --git a/docs/content/reference/routing-configuration/tcp/middlewares/inflightconn.md b/docs/content/reference/routing-configuration/tcp/middlewares/inflightconn.md index 17cd4cb75..3c12074d8 100644 --- a/docs/content/reference/routing-configuration/tcp/middlewares/inflightconn.md +++ b/docs/content/reference/routing-configuration/tcp/middlewares/inflightconn.md @@ -52,4 +52,4 @@ spec: | Field | Description | Default | Required | |:------|:------------|------------------|-------| -| `amount` | The `amount` option defines the maximum amount of allowed simultaneous connections.
The middleware closes the connection if there are already `amount` connections opened. | "" | Yes | +| `amount` | The `amount` option defines the maximum amount of allowed simultaneous connections.
The middleware closes the connection if there are already `amount` connections opened. | "" | Yes | diff --git a/docs/content/reference/routing-configuration/tcp/middlewares/ipallowlist.md b/docs/content/reference/routing-configuration/tcp/middlewares/ipallowlist.md index 6f8916fa4..fc9e3ace4 100644 --- a/docs/content/reference/routing-configuration/tcp/middlewares/ipallowlist.md +++ b/docs/content/reference/routing-configuration/tcp/middlewares/ipallowlist.md @@ -57,4 +57,4 @@ spec: | Field | Description | Default | Required | |:------|:------------|------------------|-------| -| `sourceRange` | The `sourceRange` option sets the allowed IPs (or ranges of allowed IPs by using CIDR notation).| | Yes | +| `sourceRange` | The `sourceRange` option sets the allowed IPs (or ranges of allowed IPs by using CIDR notation).| | Yes | diff --git a/docs/content/reference/routing-configuration/tcp/middlewares/overview.md b/docs/content/reference/routing-configuration/tcp/middlewares/overview.md index 8cc6269fd..6c1066b0d 100644 --- a/docs/content/reference/routing-configuration/tcp/middlewares/overview.md +++ b/docs/content/reference/routing-configuration/tcp/middlewares/overview.md @@ -108,5 +108,5 @@ spec: | Middleware | Purpose | Area | |-------------------------------------------|---------------------------------------------------|-----------------------------| -| [InFlightConn](inflightconn.md) | Limits the number of simultaneous connections. | Security, Request lifecycle | -| [IPAllowList](ipallowlist.md) | Limit the allowed client IPs. | Security, Request lifecycle | +| [InFlightConn](inflightconn.md) | Limits the number of simultaneous connections. | Security, Request lifecycle | +| [IPAllowList](ipallowlist.md) | Limit the allowed client IPs. | Security, Request lifecycle | diff --git a/docs/content/reference/routing-configuration/tcp/routing/rules-and-priority.md b/docs/content/reference/routing-configuration/tcp/router/rules-and-priority.md similarity index 85% rename from docs/content/reference/routing-configuration/tcp/routing/rules-and-priority.md rename to docs/content/reference/routing-configuration/tcp/router/rules-and-priority.md index 359a17846..f5a259775 100644 --- a/docs/content/reference/routing-configuration/tcp/routing/rules-and-priority.md +++ b/docs/content/reference/routing-configuration/tcp/router/rules-and-priority.md @@ -18,10 +18,10 @@ The table below lists all the available matchers: | Rule | Description | |-------------------------------------------------------------|:-------------------------------------------------------------------------------------------------| -| [```HostSNI(`domain`)```](#hostsni-and-hostsniregexp) | Checks if the connection's Server Name Indication is equal to `domain`.
More information [here](#hostsni-and-hostsniregexp). | -| [```HostSNIRegexp(`regexp`)```](#hostsni-and-hostsniregexp) | Checks if the connection's Server Name Indication matches `regexp`.
Use a [Go](https://golang.org/pkg/regexp/) flavored syntax.
More information [here](#hostsni-and-hostsniregexp). | -| [```ClientIP(`ip`)```](#clientip) | Checks if the connection's client IP correspond to `ip`. It accepts IPv4, IPv6 and CIDR formats.
More information [here](#clientip). | -| [```ALPN(`protocol`)```](#alpn) | Checks if the connection's ALPN protocol equals `protocol`.
More information [here](#alpn). | +| [```HostSNI(`domain`)```](#hostsni-and-hostsniregexp) | Checks if the connection's Server Name Indication is equal to `domain`.
More information [here](#hostsni-and-hostsniregexp). | +| [```HostSNIRegexp(`regexp`)```](#hostsni-and-hostsniregexp) | Checks if the connection's Server Name Indication matches `regexp`.
Use a [Go](https://golang.org/pkg/regexp/) flavored syntax.
More information [here](#hostsni-and-hostsniregexp). | +| [```ClientIP(`ip`)```](#clientip) | Checks if the connection's client IP correspond to `ip`. It accepts IPv4, IPv6 and CIDR formats.
More information [here](#clientip). | +| [```ALPN(`protocol`)```](#alpn) | Checks if the connection's ALPN protocol equals `protocol`.
More information [here](#alpn). | !!! tip "Backticks or Quotes?" diff --git a/docs/content/reference/routing-configuration/tcp/routing/router.md b/docs/content/reference/routing-configuration/tcp/routing/router.md deleted file mode 100644 index 4761ebf5c..000000000 --- a/docs/content/reference/routing-configuration/tcp/routing/router.md +++ /dev/null @@ -1,104 +0,0 @@ ---- -title: "Traefik TCP Routers Documentation" -description: "TCP routers are responsible for connecting incoming TCP connections to the services that can handle them. Read the technical documentation." ---- - -## TCP Router - -A TCP router is in charge of connecting incoming TCP connections to the services that can handle them. TCP routers analyze incoming connections based on rules, and when a match is found, forward the connection through any configured middlewares to the appropriate service. - -!!! note "TCP vs HTTP Routing" - If both HTTP routers and TCP routers listen to the same EntryPoint, the TCP routers will apply before the HTTP routers. If no matching route is found for the TCP routers, then the HTTP routers will take over. - -## Configuration Example - -```yaml tab="Structured (YAML)" -tcp: - routers: - my-tcp-router: - entryPoints: - - "tcp-ep" - - "websecure" - rule: "HostSNI(`example.com`)" - priority: 10 - middlewares: - - "tcp-ipallowlist" - tls: - passthrough: false - certResolver: "letsencrypt" - options: "modern-tls" - domains: - - main: "example.com" - sans: - - "www.example.com" - service: my-tcp-service -``` - -```toml tab="Structured (TOML)" -[tcp.routers] - [tcp.routers.my-tcp-router] - entryPoints = ["tcp-ep", "websecure"] - rule = "HostSNI(`example.com`)" - priority = 10 - middlewares = ["tcp-ipallowlist"] - service = "my-tcp-service" - - [tcp.routers.my-tcp-router.tls] - passthrough = false - certResolver = "letsencrypt" - options = "modern-tls" - - [[tcp.routers.my-tcp-router.tls.domains]] - main = "example.com" - sans = ["www.example.com"] -``` - -```yaml tab="Labels" -labels: - - "traefik.tcp.routers.my-tcp-router.entrypoints=tcp-ep,websecure" - - "traefik.tcp.routers.my-tcp-router.rule=HostSNI(`example.com`)" - - "traefik.tcp.routers.my-tcp-router.priority=10" - - "traefik.tcp.routers.my-tcp-router.middlewares=tcp-ipallowlist" - - "traefik.tcp.routers.my-tcp-router.tls.certresolver=letsencrypt" - - "traefik.tcp.routers.my-tcp-router.tls.passthrough=false" - - "traefik.tcp.routers.my-tcp-router.tls.options=modern-tls" - - "traefik.tcp.routers.my-tcp-router.tls.domains[0].main=example.com" - - "traefik.tcp.routers.my-tcp-router.tls.domains[0].sans=www.example.com" - - "traefik.tcp.routers.my-tcp-router.service=my-tcp-service" -``` - -```json tab="Tags" -{ - "Tags": [ - "traefik.tcp.routers.my-tcp-router.entrypoints=tcp-ep,websecure", - "traefik.tcp.routers.my-tcp-router.rule=HostSNI(`example.com`)", - "traefik.tcp.routers.my-tcp-router.priority=10", - "traefik.tcp.routers.my-tcp-router.middlewares=tcp-ipallowlist", - "traefik.tcp.routers.my-tcp-router.tls.certresolver=letsencrypt", - "traefik.tcp.routers.my-tcp-router.tls.passthrough=false", - "traefik.tcp.routers.my-tcp-router.tls.options=modern-tls", - "traefik.tcp.routers.my-tcp-router.tls.domains[0].main=example.com", - "traefik.tcp.routers.my-tcp-router.tls.domains[0].sans=www.example.com", - "traefik.tcp.routers.my-tcp-router.service=my-tcp-service" - ] -} -``` - -## Configuration Options - -| Field | Description | Default | Required | -|--------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------|----------| -| `entryPoints` | The list of entry points to which the router is attached. If not specified, TCP routers are attached to all TCP entry points. | All TCP entry points | No | -| `rule` | Rules are a set of matchers configured with values, that determine if a particular connection matches specific criteria. If the rule is verified, the router becomes active, calls middlewares, and then forwards the connection to the service. See [Rules & Priority](./rules-and-priority.md) for details. | | Yes | -| `priority` | To avoid rule overlap, routes are sorted, by default, in descending order using rules length. The priority is directly equal to the length of the rule, and so the longest length has the highest priority. A value of `0` for the priority is ignored. See [Rules & Priority](./rules-and-priority.md) for details. | Rule length | No | -| `middlewares` | The list of middlewares that are applied to the router. Middlewares are applied in the order they are declared. See [TCP Middlewares overview](../middlewares/overview.md) for available TCP middlewares. | | No | -| `tls` | TLS configuration for the router. When specified, the router will only handle TLS connections. See [TLS configuration](../tls.md) for detailed TLS options. | | No | -| `service` | The name of the service that will handle the matched connections. Services can be load balancer services or weighted round robin services. See [TCP Service](../service.md) for details. | | Yes | - -## Router Naming - -- The character `@` is not authorized in the router name -- Router names should be descriptive and follow your naming conventions -- In provider-specific configurations (Docker, Kubernetes), router names are often auto-generated based on service names and rules - -{!traefik-for-business-applications.md!} diff --git a/docs/content/reference/routing-configuration/tcp/serverstransport.md b/docs/content/reference/routing-configuration/tcp/serverstransport.md index b3a865f94..9677be35b 100644 --- a/docs/content/reference/routing-configuration/tcp/serverstransport.md +++ b/docs/content/reference/routing-configuration/tcp/serverstransport.md @@ -84,22 +84,19 @@ labels: ## Configuration Options -| Field | Description | Default | Required | -|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:--------|:---------| -| `serverstransport.`
`dialTimeout`
| Defines the timeout when dialing the backend TCP service. If zero, no timeout exists. | 30s | No | -| `serverstransport.`
`dialKeepAlive`
| Defines the interval between keep-alive probes for an active network connection. | 15s | No | -| `serverstransport.`
`terminationDelay`
| Sets the time limit for the proxy to fully terminate connections on both sides after initiating the termination sequence, with a negative value indicating no deadline. More Information [here](#terminationdelay) | 100ms | No | -| `serverstransport.`
`proxyProtocol`
| Defines the Proxy Protocol configuration. An empty `proxyProtocol` section enables Proxy Protocol version 2. | | No | -| `serverstransport.`
`proxyProtocol.version`
| Traefik supports PROXY Protocol version 1 and 2 on TCP Services. More Information [here](#proxyprotocolversion) | 2 | No | -| `serverstransport.`
`tls`
| Defines the TLS configuration. An empty `tls` section enables TLS. | | No | -| `serverstransport.`
`tls`
`.serverName`
| Configures the server name that will be used for SNI. | | No | -| `serverstransport.`
`tls`
`.certificates`
| Defines the list of certificates (as file paths, or data bytes) that will be set as client certificates for mTLS. | | No | -| `serverstransport.`
`tls`
`.insecureSkipVerify`
| Controls whether the server's certificate chain and host name is verified. | false | No | -| `serverstransport.`
`tls`
`.rootcas`
| Defines the root certificate authorities to use when verifying server certificates. (for mTLS connections). | | No | -| `serverstransport.`
`tls.`
`peerCertURI`
| Defines the URI used to match against SAN URIs during the server's certificate verification. | false | No | -| `serverstransport.`
`spiffe`
| Defines the SPIFFE configuration. An empty `spiffe` section enables SPIFFE (that allows any SPIFFE ID). | | No | -| `serverstransport.`
`spiffe`
`.ids`
| Allow SPIFFE IDs.
This takes precedence over the SPIFFE TrustDomain. | | No | -| `serverstransport.`
`spiffe`
`.trustDomain`
| Allow SPIFFE trust domain. | "" | No | +| Field | Description | Default | Required | +|:------|:----------------------------------------------------------|:---------------------|:---------| +| `serverstransport.`
`dialTimeout` | Defines the timeout when dialing the backend TCP service. If zero, no timeout exists. | 30s | No | +| `serverstransport.`
`dialKeepAlive` | Defines the interval between keep-alive probes for an active network connection. | 15s | No | +| `serverstransport.`
`terminationDelay` | Sets the time limit for the proxy to fully terminate connections on both sides after initiating the termination sequence, with a negative value indicating no deadline. More Information [here](#terminationdelay) | 100ms | No | +| `serverstransport.`
`tls` | Defines the TLS configuration. An empty `tls` section enables TLS. | | No | +| `serverstransport.`
`tls`
`.serverName` | Configures the server name that will be used for SNI. | | No | +| `serverstransport.`
`tls`
`.certificates` | Defines the list of certificates (as file paths, or data bytes) that will be set as client certificates for mTLS. | | No | +| `serverstransport.`
`tls`
`.insecureSkipVerify` | Controls whether the server's certificate chain and host name is verified. | false | No | +| `serverstransport.`
`tls`
`.rootcas` | Defines the root certificate authorities to use when verifying server certificates. (for mTLS connections). | | No | +| `serverstransport.`
`tls.`
`peerCertURI` | Defines the URI used to match against SAN URIs during the server's certificate verification. | false | No | +| `serverstransport.`
`spiffe`
`.ids` | Allow SPIFFE IDs.
This takes precedence over the SPIFFE TrustDomain. | | No | +| `serverstransport.`
`spiffe`
`.trustDomain` | Allow SPIFFE trust domain. | "" | No | !!! note "SPIFFE" @@ -117,9 +114,3 @@ To that end, as soon as the proxy enters this termination sequence, it sets a de The termination delay controls that deadline. A negative value means an infinite deadline (i.e. the connection is never fully terminated by the proxy itself). - -### `proxyProtocol.version` - -Traefik supports [PROXY Protocol](https://www.haproxy.org/download/2.0/doc/proxy-protocol.txt) version 1 and 2 on TCP Services. -It can be configured by setting `proxyProtocol.version` on the serversTransport. -The option specifies the version of the protocol to be used. Either 1 or 2. diff --git a/docs/content/reference/routing-configuration/tcp/service.md b/docs/content/reference/routing-configuration/tcp/service.md index d85a75102..e89174008 100644 --- a/docs/content/reference/routing-configuration/tcp/service.md +++ b/docs/content/reference/routing-configuration/tcp/service.md @@ -38,10 +38,16 @@ tcp: | Field | Description | Default | |----------|------------------------------------------|--------- | -| `servers` | Servers declare a single instance of your program. | "" | -| `servers.address` | The address option (IP:Port) point to a specific instance. | "" | -| `servers.tls` | The `tls` option determines whether to use TLS when dialing with the backend. | false | -| `servers.serversTransport` | `serversTransport` allows to reference a TCP [ServersTransport](./serverstransport.md configuration for the communication between Traefik and your servers. If no serversTransport is specified, the default@internal will be used. | "" | +| `servers` | Servers declare a single instance of your program. | "" | +| `servers.address` | The address option (IP:Port) point to a specific instance. | "" | +| `servers.tls` | The `tls` option determines whether to use TLS when dialing with the backend. | false | +| `servers.serversTransport` | `serversTransport` allows to reference a TCP [ServersTransport](./serverstransport.md configuration for the communication between Traefik and your servers. If no serversTransport is specified, the default@internal will be used. | "" | +| `servers.proxyProtocol.version` | Traefik supports PROXY Protocol version 1 and 2 on TCP Services. More Information [here](#serversproxyprotocolversion) | 2 | + +### servers.proxyProtocol.version + +Traefik supports [PROXY Protocol](https://www.haproxy.org/download/2.0/doc/proxy-protocol.txt) version 1 and 2 on TCP Services. It can be enabled by setting `proxyProtocol` on the load balancer. +The option specifies the version of the protocol to be used. Either 1 or 2. ## Weighted Round Robin @@ -95,4 +101,4 @@ tcp: [[tcp.services.appv2.loadBalancer.servers]] address = "private-ip-server-2:8080/" ``` - + \ No newline at end of file diff --git a/docs/content/reference/routing-configuration/tcp/tls.md b/docs/content/reference/routing-configuration/tcp/tls.md index ccaf54dfb..5c040b837 100644 --- a/docs/content/reference/routing-configuration/tcp/tls.md +++ b/docs/content/reference/routing-configuration/tcp/tls.md @@ -5,7 +5,7 @@ description: "Learn how to configure the transport layer security (TLS) connecti ## General -When a TCP router is configured to handle TLS traffic, include a `tls` field in its definition. This field tells Traefik that the router should process only TLS connections and ignore non-TLS traffic. +When a router is configured to handle HTTPS traffic, include a `tls` field in its definition. This field tells Traefik that the router should process only TLS requests and ignore non-TLS traffic. By default, a router with a TLS field will terminate the TLS connections, meaning that it will send decrypted data to the services. @@ -94,33 +94,11 @@ labels: ## Configuration Options -| Field | Description | Default | Required | -|:-----------------------------------------------------------------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:--------|:---------| -| `passthrough` | Defines whether the requests should be forwarded "as is", keeping all data encrypted. | false | No | -| `options` | enables fine-grained control of the TLS parameters. It refers to a [TLS Options](../http/tls/tls-options.md) and will be applied only if a `HostSNI` rule is defined. | "" | No | -| `certResolver` | The name of the certificate resolver to use for automatic certificate generation via ACME providers (such as Let's Encrypt). See the [Certificate Resolver](./#certificate-resolver) section for more details. | "" | No | -| `domains` | List of domains and Subject Alternative Names (SANs) for explicit certificate domain specification. See the [Custom Domains](./#custom-domains) section for more details. | [] | No | - -## Certificate Resolver - -The `tls.certResolver` option allows you to specify a certificate resolver for automatic certificate generation via ACME providers (such as Let's Encrypt). - -When a certificate resolver is configured for a router, -Traefik will automatically obtain and manage TLS certificates for the domains specified in the router's rule (in the `HostSNI` matcher) or in the `tls.domains` configuration (with `tls.domains` taking precedence). - -!!! important "Prerequisites" - - - Certificate resolvers must be defined in the [static configuration](../../install-configuration/tls/certificate-resolvers/acme.md) - - The router must have `tls` enabled - - An ACME challenge type must be configured for the certificate resolver - -## Custom Domains - -When using ACME certificate resolvers, domains are automatically extracted from router rules, -but the `tls.domains` option allows you to explicitly specify the domains and Subject Alternative Names (SANs) for which certificates should be generated. - -This provides fine-grained control over certificate generation and takes precedence over domains automatically extracted from router rules. - -Every domain must have A/AAAA records pointing to Traefik. +| Field | Description | Default | Required | +|:------------------|:--------------------|:-----------------------------------------------|:---------| +|`passthrough`| Defines whether the requests should be forwarded "as is", keeping all data encrypted. | false | No | +|`options`| enables fine-grained control of the TLS parameters. It refers to a [TLS Options](../http/tls/tls-certificates.md#tls-options) and will be applied only if a `HostSNI` rule is defined. | "" | No | +|`domains`| Defines a set of SANs (alternative domains) for each main domain. Every domain must have A/AAAA records pointing to Traefik. Each domain & SAN will lead to a certificate request.| [] | No | +|`certResolver`| If defined, Traefik will try to generate certificates based on routers `Host` & `HostSNI` rules. | "" | No | {!traefik-for-business-applications.md!} diff --git a/docs/content/reference/routing-configuration/udp/routing/rules-priority.md b/docs/content/reference/routing-configuration/udp/router/rules-priority.md similarity index 100% rename from docs/content/reference/routing-configuration/udp/routing/rules-priority.md rename to docs/content/reference/routing-configuration/udp/router/rules-priority.md diff --git a/docs/content/reference/routing-configuration/udp/routing/router.md b/docs/content/reference/routing-configuration/udp/routing/router.md deleted file mode 100644 index 5d4bd57e1..000000000 --- a/docs/content/reference/routing-configuration/udp/routing/router.md +++ /dev/null @@ -1,69 +0,0 @@ ---- -title: "Traefik UDP Routers Documentation" -description: "UDP routers are responsible for connecting incoming UDP packets to the services that can handle them. Read the technical documentation." ---- - -## UDP Router - -A UDP router is in charge of connecting incoming UDP packets to the services that can handle them. Unlike HTTP and TCP routers, UDP routers operate at the transport layer and have unique characteristics due to the connectionless nature of UDP. - -!!! important "UDP Router Characteristics" - - UDP is connectionless, so there is no concept of a request URL path or Host SNI to match against - - UDP routers are essentially load-balancers that distribute packets to backend services - - UDP routers can only target UDP services (not HTTP or TCP services) - - Sessions are tracked with configurable timeouts to maintain state between client and backend - -## Configuration Example - -```yaml tab="Structured (YAML)" -udp: - routers: - my-udp-router: - entryPoints: - - "udp-ep" - - "dns" - service: my-udp-service -``` - -```toml tab="Structured (TOML)" -[udp.routers] - [udp.routers.my-udp-router] - entryPoints = ["udp-ep", "dns"] - service = "my-udp-service" -``` - -```yaml tab="Labels" -labels: - - "traefik.udp.routers.my-udp-router.entrypoints=udp-ep,dns" - - "traefik.udp.routers.my-udp-router.service=my-udp-service" -``` - -```json tab="Tags" -{ - "Tags": [ - "traefik.udp.routers.my-udp-router.entrypoints=udp-ep,dns", - "traefik.udp.routers.my-udp-router.service=my-udp-service" - ] -} -``` - -## Configuration Options - -| Field | Description | Default | Required | -|------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------|----------| -| `entryPoints` | The list of entry points to which the router is attached. If not specified, UDP routers are attached to all UDP entry points. | All UDP entry points | No | -| `service` | The name of the service that will handle the matched UDP packets. UDP services are typically load balancer services that distribute packets to multiple backend servers. See [UDP Service](../service.md) for details. | | Yes | - -## Sessions and Timeout - -Even though UDP is connectionless, Traefik's UDP router implementation relies on sessions to maintain state about ongoing communication between clients and backends. This allows the proxy to know where to forward response packets from backends. - -Each session has an associated timeout that cleans up inactive sessions after a specified duration of inactivity. - -Session timeout can be configured using the `entryPoints.name.udp.timeout` option in the static configuration. See [EntryPoints documentation](../../../install-configuration/entrypoints.md) for details. - -## Router Naming - -- The character `@` is not authorized in the router name -- Router names should be descriptive and follow your naming conventions -- In provider-specific configurations (Docker, Kubernetes), router names are often auto-generated based on service names diff --git a/docs/content/reference/static-configuration/cli-ref.md b/docs/content/reference/static-configuration/cli-ref.md index b06e80721..193f0c65d 100644 --- a/docs/content/reference/static-configuration/cli-ref.md +++ b/docs/content/reference/static-configuration/cli-ref.md @@ -91,7 +91,7 @@ TLS key Defines additional resource attributes (key:value). `--accesslog.otlp.servicename`: -Defines the service name resource attribute. (Default: ```traefik```) +Set the name for this service. (Default: ```traefik```) `--api`: Enable api/dashboard. (Default: ```false```) @@ -129,12 +129,6 @@ Define if the certificates pool must use a copy of the system cert pool. (Defaul `--certificatesresolvers..acme.certificatesduration`: Certificates' duration in hours. (Default: ```2160```) -`--certificatesresolvers..acme.clientresponseheadertimeout`: -Timeout for receiving the response headers when communicating with the ACME server. (Default: ```30```) - -`--certificatesresolvers..acme.clienttimeout`: -Timeout for a complete HTTP transaction with the ACME server. (Default: ```120```) - `--certificatesresolvers..acme.dnschallenge`: Activate DNS-01 Challenge. (Default: ```false```) @@ -180,9 +174,6 @@ CSR email addresses to use. `--certificatesresolvers..acme.httpchallenge`: Activate HTTP-01 Challenge. (Default: ```false```) -`--certificatesresolvers..acme.httpchallenge.delay`: -Delay between the creation of the challenge and the validation. (Default: ```0```) - `--certificatesresolvers..acme.httpchallenge.entrypoint`: HTTP challenge EntryPoint @@ -283,16 +274,13 @@ HTTP/3 configuration. (Default: ```false```) UDP port to advertise, on which HTTP/3 is available. (Default: ```0```) `--entrypoints..observability.accesslogs`: -Enables access-logs for this entryPoint. (Default: ```true```) + (Default: ```true```) `--entrypoints..observability.metrics`: -Enables metrics for this entryPoint. (Default: ```true```) - -`--entrypoints..observability.traceverbosity`: -Defines the tracing verbosity level for this entryPoint. (Default: ```minimal```) + (Default: ```true```) `--entrypoints..observability.tracing`: -Enables tracing for this entryPoint. (Default: ```true```) + (Default: ```true```) `--entrypoints..proxyprotocol`: Proxy-Protocol configuration. (Default: ```false```) @@ -342,9 +330,6 @@ Enable debug mode for the FastProxy implementation. (Default: ```false```) `--experimental.kubernetesgateway`: (Deprecated) Allow the Kubernetes gateway api provider usage. (Default: ```false```) -`--experimental.kubernetesingressnginx`: -Allow the Kubernetes Ingress NGINX provider usage. (Default: ```false```) - `--experimental.localplugins.`: Local plugins configuration. (Default: ```false```) @@ -360,9 +345,6 @@ Environment variables to forward to the wasm guest. `--experimental.localplugins..settings.mounts`: Directory to mount to the wasm guest. -`--experimental.localplugins..settings.useunsafe`: -Allow the plugin to use unsafe package. (Default: ```false```) - `--experimental.otlplogs`: Enables the OpenTelemetry logs integration. (Default: ```false```) @@ -378,9 +360,6 @@ Environment variables to forward to the wasm guest. `--experimental.plugins..settings.mounts`: Directory to mount to the wasm guest. -`--experimental.plugins..settings.useunsafe`: -Allow the plugin to use unsafe package. (Default: ```false```) - `--experimental.plugins..version`: plugin's version. @@ -484,7 +463,7 @@ TLS key Defines additional resource attributes (key:value). `--log.otlp.servicename`: -Defines the service name resource attribute. (Default: ```traefik```) +Set the name for this service. (Default: ```traefik```) `--metrics.addinternals`: Enables metrics for internal services (ping, dashboard, etc...). (Default: ```false```) @@ -603,11 +582,8 @@ TLS key `--metrics.otlp.pushinterval`: Period between calls to collect a checkpoint. (Default: ```10```) -`--metrics.otlp.resourceattributes.`: -Defines additional resource attributes (key:value). - `--metrics.otlp.servicename`: -Defines the service name resource attribute. (Default: ```traefik```) +OTEL service name to use. (Default: ```traefik```) `--metrics.prometheus`: Prometheus metrics exporter type. (Default: ```false```) @@ -654,12 +630,6 @@ Prefix to use for metrics collection. (Default: ```traefik```) `--metrics.statsd.pushinterval`: StatsD push interval. (Default: ```10```) -`--ocsp`: -OCSP configuration. (Default: ```false```) - -`--ocsp.responderoverrides.`: -Defines a map of OCSP responders to replace for querying OCSP servers. - `--ping`: Enable ping. (Default: ```false```) @@ -1062,60 +1032,12 @@ Kubernetes namespaces. `--providers.kubernetesingress.nativelbbydefault`: Defines whether to use Native Kubernetes load-balancing mode by default. (Default: ```false```) -`--providers.kubernetesingress.strictprefixmatching`: -Make prefix matching strictly comply with the Kubernetes Ingress specification (path-element-wise matching instead of character-by-character string matching). (Default: ```false```) - `--providers.kubernetesingress.throttleduration`: Ingress refresh throttle duration (Default: ```0```) `--providers.kubernetesingress.token`: Kubernetes bearer token (not needed for in-cluster client). It accepts either a token value or a file path to the token. -`--providers.kubernetesingressnginx`: -Enable Kubernetes Ingress NGINX provider. (Default: ```false```) - -`--providers.kubernetesingressnginx.certauthfilepath`: -Kubernetes certificate authority file path (not needed for in-cluster client). - -`--providers.kubernetesingressnginx.controllerclass`: -Ingress Class Controller value this controller satisfies. (Default: ```k8s.io/ingress-nginx```) - -`--providers.kubernetesingressnginx.defaultbackendservice`: -Service used to serve HTTP requests not matching any known server name (catch-all). Takes the form 'namespace/name'. - -`--providers.kubernetesingressnginx.disablesvcexternalname`: -Disable support for Services of type ExternalName. (Default: ```false```) - -`--providers.kubernetesingressnginx.endpoint`: -Kubernetes server endpoint (required for external cluster client). - -`--providers.kubernetesingressnginx.ingressclass`: -Name of the ingress class this controller satisfies. (Default: ```nginx```) - -`--providers.kubernetesingressnginx.ingressclassbyname`: -Define if Ingress Controller should watch for Ingress Class by Name together with Controller Class. (Default: ```false```) - -`--providers.kubernetesingressnginx.publishservice`: -Service fronting the Ingress controller. Takes the form 'namespace/name'. - -`--providers.kubernetesingressnginx.publishstatusaddress`: -Customized address (or addresses, separated by comma) to set as the load-balancer status of Ingress objects this controller satisfies. - -`--providers.kubernetesingressnginx.throttleduration`: -Ingress refresh throttle duration. (Default: ```0```) - -`--providers.kubernetesingressnginx.token`: -Kubernetes bearer token (not needed for in-cluster client). It accepts either a token value or a file path to the token. - -`--providers.kubernetesingressnginx.watchingresswithoutclass`: -Define if Ingress Controller should also watch for Ingresses without an IngressClass or the annotation specified. (Default: ```false```) - -`--providers.kubernetesingressnginx.watchnamespace`: -Namespace the controller watches for updates to Kubernetes objects. All namespaces are watched if this parameter is left empty. - -`--providers.kubernetesingressnginx.watchnamespaceselector`: -Selector selects namespaces the controller watches for updates to Kubernetes objects. - `--providers.nomad`: Enable Nomad backend with default settings. (Default: ```false```) @@ -1444,4 +1366,4 @@ Query params to not redact. Sets the rate between 0.0 and 1.0 of requests to trace. (Default: ```1.000000```) `--tracing.servicename`: -Defines the service name resource attribute. (Default: ```traefik```) +Sets the name for this service. (Default: ```traefik```) diff --git a/docs/content/reference/static-configuration/cli.md b/docs/content/reference/static-configuration/cli.md new file mode 100644 index 000000000..85179eade --- /dev/null +++ b/docs/content/reference/static-configuration/cli.md @@ -0,0 +1,9 @@ +--- +title: "Traefik CLI Flags Documentation" +description: "Reference the CLI flags for static configuration in Traefik Proxy. Read the technical documentation." +--- + +# Static Configuration: CLI + +--8<-- "content/reference/static-configuration/cli-ref.md" + diff --git a/docs/content/reference/static-configuration/env-ref.md b/docs/content/reference/static-configuration/env-ref.md index 65c009454..849d058ab 100644 --- a/docs/content/reference/static-configuration/env-ref.md +++ b/docs/content/reference/static-configuration/env-ref.md @@ -91,7 +91,7 @@ TLS key Defines additional resource attributes (key:value). `TRAEFIK_ACCESSLOG_OTLP_SERVICENAME`: -Defines the service name resource attribute. (Default: ```traefik```) +Set the name for this service. (Default: ```traefik```) `TRAEFIK_API`: Enable api/dashboard. (Default: ```false```) @@ -129,12 +129,6 @@ Define if the certificates pool must use a copy of the system cert pool. (Defaul `TRAEFIK_CERTIFICATESRESOLVERS__ACME_CERTIFICATESDURATION`: Certificates' duration in hours. (Default: ```2160```) -`TRAEFIK_CERTIFICATESRESOLVERS__ACME_CLIENTRESPONSEHEADERTIMEOUT`: -Timeout for receiving the response headers when communicating with the ACME server. (Default: ```30```) - -`TRAEFIK_CERTIFICATESRESOLVERS__ACME_CLIENTTIMEOUT`: -Timeout for a complete HTTP transaction with the ACME server. (Default: ```120```) - `TRAEFIK_CERTIFICATESRESOLVERS__ACME_DNSCHALLENGE`: Activate DNS-01 Challenge. (Default: ```false```) @@ -180,9 +174,6 @@ CSR email addresses to use. `TRAEFIK_CERTIFICATESRESOLVERS__ACME_HTTPCHALLENGE`: Activate HTTP-01 Challenge. (Default: ```false```) -`TRAEFIK_CERTIFICATESRESOLVERS__ACME_HTTPCHALLENGE_DELAY`: -Delay between the creation of the challenge and the validation. (Default: ```0```) - `TRAEFIK_CERTIFICATESRESOLVERS__ACME_HTTPCHALLENGE_ENTRYPOINT`: HTTP challenge EntryPoint @@ -283,16 +274,13 @@ Subject alternative names. Default TLS options for the routers linked to the entry point. `TRAEFIK_ENTRYPOINTS__OBSERVABILITY_ACCESSLOGS`: -Enables access-logs for this entryPoint. (Default: ```true```) + (Default: ```true```) `TRAEFIK_ENTRYPOINTS__OBSERVABILITY_METRICS`: -Enables metrics for this entryPoint. (Default: ```true```) - -`TRAEFIK_ENTRYPOINTS__OBSERVABILITY_TRACEVERBOSITY`: -Defines the tracing verbosity level for this entryPoint. (Default: ```minimal```) + (Default: ```true```) `TRAEFIK_ENTRYPOINTS__OBSERVABILITY_TRACING`: -Enables tracing for this entryPoint. (Default: ```true```) + (Default: ```true```) `TRAEFIK_ENTRYPOINTS__PROXYPROTOCOL`: Proxy-Protocol configuration. (Default: ```false```) @@ -342,9 +330,6 @@ Enable debug mode for the FastProxy implementation. (Default: ```false```) `TRAEFIK_EXPERIMENTAL_KUBERNETESGATEWAY`: (Deprecated) Allow the Kubernetes gateway api provider usage. (Default: ```false```) -`TRAEFIK_EXPERIMENTAL_KUBERNETESINGRESSNGINX`: -Allow the Kubernetes Ingress NGINX provider usage. (Default: ```false```) - `TRAEFIK_EXPERIMENTAL_LOCALPLUGINS_`: Local plugins configuration. (Default: ```false```) @@ -360,9 +345,6 @@ Environment variables to forward to the wasm guest. `TRAEFIK_EXPERIMENTAL_LOCALPLUGINS__SETTINGS_MOUNTS`: Directory to mount to the wasm guest. -`TRAEFIK_EXPERIMENTAL_LOCALPLUGINS__SETTINGS_USEUNSAFE`: -Allow the plugin to use unsafe package. (Default: ```false```) - `TRAEFIK_EXPERIMENTAL_OTLPLOGS`: Enables the OpenTelemetry logs integration. (Default: ```false```) @@ -378,9 +360,6 @@ Environment variables to forward to the wasm guest. `TRAEFIK_EXPERIMENTAL_PLUGINS__SETTINGS_MOUNTS`: Directory to mount to the wasm guest. -`TRAEFIK_EXPERIMENTAL_PLUGINS__SETTINGS_USEUNSAFE`: -Allow the plugin to use unsafe package. (Default: ```false```) - `TRAEFIK_EXPERIMENTAL_PLUGINS__VERSION`: plugin's version. @@ -484,7 +463,7 @@ TLS key Defines additional resource attributes (key:value). `TRAEFIK_LOG_OTLP_SERVICENAME`: -Defines the service name resource attribute. (Default: ```traefik```) +Set the name for this service. (Default: ```traefik```) `TRAEFIK_METRICS_ADDINTERNALS`: Enables metrics for internal services (ping, dashboard, etc...). (Default: ```false```) @@ -603,11 +582,8 @@ TLS key `TRAEFIK_METRICS_OTLP_PUSHINTERVAL`: Period between calls to collect a checkpoint. (Default: ```10```) -`TRAEFIK_METRICS_OTLP_RESOURCEATTRIBUTES_`: -Defines additional resource attributes (key:value). - `TRAEFIK_METRICS_OTLP_SERVICENAME`: -Defines the service name resource attribute. (Default: ```traefik```) +OTEL service name to use. (Default: ```traefik```) `TRAEFIK_METRICS_PROMETHEUS`: Prometheus metrics exporter type. (Default: ```false```) @@ -654,12 +630,6 @@ Prefix to use for metrics collection. (Default: ```traefik```) `TRAEFIK_METRICS_STATSD_PUSHINTERVAL`: StatsD push interval. (Default: ```10```) -`TRAEFIK_OCSP`: -OCSP configuration. (Default: ```false```) - -`TRAEFIK_OCSP_RESPONDEROVERRIDES_`: -Defines a map of OCSP responders to replace for querying OCSP servers. - `TRAEFIK_PING`: Enable ping. (Default: ```false```) @@ -1023,51 +993,6 @@ Kubernetes bearer token (not needed for in-cluster client). It accepts either a `TRAEFIK_PROVIDERS_KUBERNETESINGRESS`: Enable Kubernetes backend with default settings. (Default: ```false```) -`TRAEFIK_PROVIDERS_KUBERNETESINGRESSNGINX`: -Enable Kubernetes Ingress NGINX provider. (Default: ```false```) - -`TRAEFIK_PROVIDERS_KUBERNETESINGRESSNGINX_CERTAUTHFILEPATH`: -Kubernetes certificate authority file path (not needed for in-cluster client). - -`TRAEFIK_PROVIDERS_KUBERNETESINGRESSNGINX_CONTROLLERCLASS`: -Ingress Class Controller value this controller satisfies. (Default: ```k8s.io/ingress-nginx```) - -`TRAEFIK_PROVIDERS_KUBERNETESINGRESSNGINX_DEFAULTBACKENDSERVICE`: -Service used to serve HTTP requests not matching any known server name (catch-all). Takes the form 'namespace/name'. - -`TRAEFIK_PROVIDERS_KUBERNETESINGRESSNGINX_DISABLESVCEXTERNALNAME`: -Disable support for Services of type ExternalName. (Default: ```false```) - -`TRAEFIK_PROVIDERS_KUBERNETESINGRESSNGINX_ENDPOINT`: -Kubernetes server endpoint (required for external cluster client). - -`TRAEFIK_PROVIDERS_KUBERNETESINGRESSNGINX_INGRESSCLASS`: -Name of the ingress class this controller satisfies. (Default: ```nginx```) - -`TRAEFIK_PROVIDERS_KUBERNETESINGRESSNGINX_INGRESSCLASSBYNAME`: -Define if Ingress Controller should watch for Ingress Class by Name together with Controller Class. (Default: ```false```) - -`TRAEFIK_PROVIDERS_KUBERNETESINGRESSNGINX_PUBLISHSERVICE`: -Service fronting the Ingress controller. Takes the form 'namespace/name'. - -`TRAEFIK_PROVIDERS_KUBERNETESINGRESSNGINX_PUBLISHSTATUSADDRESS`: -Customized address (or addresses, separated by comma) to set as the load-balancer status of Ingress objects this controller satisfies. - -`TRAEFIK_PROVIDERS_KUBERNETESINGRESSNGINX_THROTTLEDURATION`: -Ingress refresh throttle duration. (Default: ```0```) - -`TRAEFIK_PROVIDERS_KUBERNETESINGRESSNGINX_TOKEN`: -Kubernetes bearer token (not needed for in-cluster client). It accepts either a token value or a file path to the token. - -`TRAEFIK_PROVIDERS_KUBERNETESINGRESSNGINX_WATCHINGRESSWITHOUTCLASS`: -Define if Ingress Controller should also watch for Ingresses without an IngressClass or the annotation specified. (Default: ```false```) - -`TRAEFIK_PROVIDERS_KUBERNETESINGRESSNGINX_WATCHNAMESPACE`: -Namespace the controller watches for updates to Kubernetes objects. All namespaces are watched if this parameter is left empty. - -`TRAEFIK_PROVIDERS_KUBERNETESINGRESSNGINX_WATCHNAMESPACESELECTOR`: -Selector selects namespaces the controller watches for updates to Kubernetes objects. - `TRAEFIK_PROVIDERS_KUBERNETESINGRESS_ALLOWEMPTYSERVICES`: Allow creation of services without endpoints. (Default: ```false```) @@ -1107,9 +1032,6 @@ Kubernetes namespaces. `TRAEFIK_PROVIDERS_KUBERNETESINGRESS_NATIVELBBYDEFAULT`: Defines whether to use Native Kubernetes load-balancing mode by default. (Default: ```false```) -`TRAEFIK_PROVIDERS_KUBERNETESINGRESS_STRICTPREFIXMATCHING`: -Make prefix matching strictly comply with the Kubernetes Ingress specification (path-element-wise matching instead of character-by-character string matching). (Default: ```false```) - `TRAEFIK_PROVIDERS_KUBERNETESINGRESS_THROTTLEDURATION`: Ingress refresh throttle duration (Default: ```0```) @@ -1444,4 +1366,4 @@ Query params to not redact. Sets the rate between 0.0 and 1.0 of requests to trace. (Default: ```1.000000```) `TRAEFIK_TRACING_SERVICENAME`: -Defines the service name resource attribute. (Default: ```traefik```) +Sets the name for this service. (Default: ```traefik```) diff --git a/docs/content/reference/static-configuration/env.md b/docs/content/reference/static-configuration/env.md new file mode 100644 index 000000000..d1758fa18 --- /dev/null +++ b/docs/content/reference/static-configuration/env.md @@ -0,0 +1,27 @@ +--- +title: "Traefik Environment Variables Documentation" +description: "Reference the environment variables for static configuration in Traefik Proxy. Read the technical documentation." +--- + +# 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" diff --git a/docs/content/reference/static-configuration/file.md b/docs/content/reference/static-configuration/file.md new file mode 100644 index 000000000..c2f3174db --- /dev/null +++ b/docs/content/reference/static-configuration/file.md @@ -0,0 +1,14 @@ +--- +title: "Traefik File Static Configuration" +description: "Reference the YAML and TOML files for static configuration in Traefik Proxy. Read the technical documentation." +--- + +# Static Configuration: File + +```yml tab="YAML" +--8<-- "content/reference/static-configuration/file.yaml" +``` + +```toml tab="TOML" +--8<-- "content/reference/static-configuration/file.toml" +``` diff --git a/docs/content/reference/static-configuration/file.toml b/docs/content/reference/static-configuration/file.toml index 7d22522e1..13fd6b2bc 100644 --- a/docs/content/reference/static-configuration/file.toml +++ b/docs/content/reference/static-configuration/file.toml @@ -81,9 +81,8 @@ timeout = "42s" [entryPoints.EntryPoint0.observability] accessLogs = true - metrics = true tracing = true - traceVerbosity = "foobar" + metrics = true [providers] providersThrottleDuration = "42s" @@ -160,26 +159,10 @@ disableIngressClassLookup = true disableClusterScopeResources = true nativeLBByDefault = true - strictPrefixMatching = true [providers.kubernetesIngress.ingressEndpoint] ip = "foobar" hostname = "foobar" publishedService = "foobar" - [providers.kubernetesIngressNGINX] - endpoint = "foobar" - token = "foobar" - certAuthFilePath = "foobar" - throttleDuration = "42s" - watchNamespace = "foobar" - watchNamespaceSelector = "foobar" - ingressClass = "foobar" - controllerClass = "foobar" - watchIngressWithoutClass = true - ingressClassByName = true - publishService = "foobar" - publishStatusAddress = ["foobar", "foobar"] - defaultBackendService = "foobar" - disableSvcExternalName = true [providers.kubernetesCRD] endpoint = "foobar" token = "foobar" @@ -409,9 +392,6 @@ [metrics.otlp.http.headers] name0 = "foobar" name1 = "foobar" - [metrics.otlp.resourceAttributes] - name0 = "foobar" - name1 = "foobar" [ping] entryPoint = "foobar" @@ -552,8 +532,6 @@ storage = "foobar" keyType = "foobar" certificatesDuration = 42 - clientTimeout = "42s" - clientResponseHeaderTimeout = "42s" caCertificates = ["foobar", "foobar"] caSystemCertPool = true caServerName = "foobar" @@ -572,7 +550,6 @@ delayBeforeChecks = "42s" [certificatesResolvers.CertificateResolver0.acme.httpChallenge] entryPoint = "foobar" - delay = "42s" [certificatesResolvers.CertificateResolver0.acme.tlsChallenge] [certificatesResolvers.CertificateResolver0.tailscale] [certificatesResolvers.CertificateResolver1] @@ -585,8 +562,6 @@ storage = "foobar" keyType = "foobar" certificatesDuration = 42 - clientTimeout = "42s" - clientResponseHeaderTimeout = "42s" caCertificates = ["foobar", "foobar"] caSystemCertPool = true caServerName = "foobar" @@ -605,14 +580,12 @@ delayBeforeChecks = "42s" [certificatesResolvers.CertificateResolver1.acme.httpChallenge] entryPoint = "foobar" - delay = "42s" [certificatesResolvers.CertificateResolver1.acme.tlsChallenge] [certificatesResolvers.CertificateResolver1.tailscale] [experimental] abortOnPluginFailure = true otlplogs = true - kubernetesIngressNGINX = true kubernetesGateway = true [experimental.plugins] [experimental.plugins.Descriptor0] @@ -621,27 +594,23 @@ [experimental.plugins.Descriptor0.settings] envs = ["foobar", "foobar"] mounts = ["foobar", "foobar"] - useUnsafe = true [experimental.plugins.Descriptor1] moduleName = "foobar" version = "foobar" [experimental.plugins.Descriptor1.settings] envs = ["foobar", "foobar"] mounts = ["foobar", "foobar"] - useUnsafe = true [experimental.localPlugins] [experimental.localPlugins.LocalDescriptor0] moduleName = "foobar" [experimental.localPlugins.LocalDescriptor0.settings] envs = ["foobar", "foobar"] mounts = ["foobar", "foobar"] - useUnsafe = true [experimental.localPlugins.LocalDescriptor1] moduleName = "foobar" [experimental.localPlugins.LocalDescriptor1.settings] envs = ["foobar", "foobar"] mounts = ["foobar", "foobar"] - useUnsafe = true [experimental.fastProxy] debug = true @@ -650,8 +619,3 @@ [spiffe] workloadAPIAddr = "foobar" - -[ocsp] - [ocsp.responderOverrides] - name0 = "foobar" - name1 = "foobar" diff --git a/docs/content/reference/static-configuration/file.yaml b/docs/content/reference/static-configuration/file.yaml index 64aae401b..4c4fdc109 100644 --- a/docs/content/reference/static-configuration/file.yaml +++ b/docs/content/reference/static-configuration/file.yaml @@ -97,9 +97,8 @@ entryPoints: timeout: 42s observability: accessLogs: true - metrics: true tracing: true - traceVerbosity: foobar + metrics: true providers: providersThrottleDuration: 42s docker: @@ -175,24 +174,6 @@ providers: disableIngressClassLookup: true disableClusterScopeResources: true nativeLBByDefault: true - strictPrefixMatching: true - kubernetesIngressNGINX: - endpoint: foobar - token: foobar - certAuthFilePath: foobar - throttleDuration: 42s - watchNamespace: foobar - watchNamespaceSelector: foobar - ingressClass: foobar - controllerClass: foobar - watchIngressWithoutClass: true - ingressClassByName: true - publishService: foobar - publishStatusAddress: - - foobar - - foobar - defaultBackendService: foobar - disableSvcExternalName: true kubernetesCRD: endpoint: foobar token: foobar @@ -446,9 +427,6 @@ metrics: - 42 pushInterval: 42s serviceName: foobar - resourceAttributes: - name0: foobar - name1: foobar ping: entryPoint: foobar manualRouting: true @@ -596,8 +574,6 @@ certificatesResolvers: kid: foobar hmacEncoded: foobar certificatesDuration: 42 - clientTimeout: 42s - clientResponseHeaderTimeout: 42s caCertificates: - foobar - foobar @@ -617,7 +593,6 @@ certificatesResolvers: disablePropagationCheck: true httpChallenge: entryPoint: foobar - delay: 42s tlsChallenge: {} tailscale: {} CertificateResolver1: @@ -635,8 +610,6 @@ certificatesResolvers: kid: foobar hmacEncoded: foobar certificatesDuration: 42 - clientTimeout: 42s - clientResponseHeaderTimeout: 42s caCertificates: - foobar - foobar @@ -656,7 +629,6 @@ certificatesResolvers: disablePropagationCheck: true httpChallenge: entryPoint: foobar - delay: 42s tlsChallenge: {} tailscale: {} experimental: @@ -671,7 +643,6 @@ experimental: mounts: - foobar - foobar - useUnsafe: true Descriptor1: moduleName: foobar version: foobar @@ -682,7 +653,6 @@ experimental: mounts: - foobar - foobar - useUnsafe: true localPlugins: LocalDescriptor0: moduleName: foobar @@ -693,7 +663,6 @@ experimental: mounts: - foobar - foobar - useUnsafe: true LocalDescriptor1: moduleName: foobar settings: @@ -703,18 +672,12 @@ experimental: mounts: - foobar - foobar - useUnsafe: true abortOnPluginFailure: true fastProxy: debug: true otlplogs: true - kubernetesIngressNGINX: true kubernetesGateway: true core: defaultRuleSyntax: foobar spiffe: workloadAPIAddr: foobar -ocsp: - responderOverrides: - name0: foobar - name1: foobar diff --git a/docs/content/reference/static-configuration/overview.md b/docs/content/reference/static-configuration/overview.md new file mode 100644 index 000000000..853ab0a97 --- /dev/null +++ b/docs/content/reference/static-configuration/overview.md @@ -0,0 +1,10 @@ +--- +title: "Traefik Static Configuration Overview" +description: "Read the official Traefik documentation to get started with static configuration in Traefik Proxy." +--- + +# Static Configuration + +- [File](./file.md) +- [CLI](./cli.md) +- [Environment Variables](./env.md) diff --git a/docs/content/routing/overview.md b/docs/content/routing/overview.md index dd1d0342f..e65bea351 100644 --- a/docs/content/routing/overview.md +++ b/docs/content/routing/overview.md @@ -10,8 +10,8 @@ What's Happening to the Requests? Let's zoom in on Traefik's architecture and talk about the components that enable the routes to be created. -First, when you start Traefik, you define [entrypoints](../entrypoints/) (in their most basic forms, they are port numbers). -Then, connected to these entrypoints, [routers](../routers/) analyze the incoming requests to see if they match a set of [rules](../routers/#rule). +First, when you start Traefik, you define [entrypoints](../entrypoints) (in their most basic forms, they are port numbers). +Then, connected to these entrypoints, [routers](../routers) analyze the incoming requests to see if they match a set of [rules](../routers#rule). If they do, the router might transform the request using pieces of [middleware](../middlewares/overview.md) before forwarding them to your [services](./services/index.md). ![Architecture](../assets/img/architecture-overview.png) @@ -246,7 +246,7 @@ http: Most of what happens to the connection between the clients and Traefik, and then between Traefik and the backend servers, is configured through the -[entrypoints](../entrypoints/) and the [routers](../routers/). +[entrypoints](../entrypoints) and the [routers](../routers). In addition, a few parameters are dedicated to configuring globally what happens with the connections between Traefik and the backends. diff --git a/docs/content/routing/providers/consul-catalog.md b/docs/content/routing/providers/consul-catalog.md index 26c222875..76b326b55 100644 --- a/docs/content/routing/providers/consul-catalog.md +++ b/docs/content/routing/providers/consul-catalog.md @@ -25,7 +25,7 @@ With Consul Catalog, Traefik can leverage tags attached to a service to generate !!! info "tags" - tags are case-insensitive. - - The complete list of tags can be found [the reference page](../../reference/routing-configuration/other-providers/consul-catalog.md) + - The complete list of tags can be found [the reference page](../../reference/dynamic-configuration/consul-catalog.md) ### General @@ -218,14 +218,6 @@ you'd add the tag `traefik.http.services.{name-of-your-choice}.loadbalancer.pass traefik.http.services.myservice.loadbalancer.healthcheck.interval=10 ``` -??? info "`traefik.http.services..loadbalancer.healthcheck.unhealthyinterval`" - - See [health check](../services/index.md#health-check) for more information. - - ```yaml - traefik.http.services.myservice.loadbalancer.healthcheck.unhealthyinterval=10 - ``` - ??? info "`traefik.http.services..loadbalancer.healthcheck.path`" See [health check](../services/index.md#health-check) for more information. diff --git a/docs/content/routing/providers/docker.md b/docs/content/routing/providers/docker.md index c47bc6c60..aedbfe09f 100644 --- a/docs/content/routing/providers/docker.md +++ b/docs/content/routing/providers/docker.md @@ -42,6 +42,7 @@ With Docker, Traefik can leverage labels attached to a container to generate rou Attaching labels to containers (in your docker compose file) ```yaml + version: "3" services: my-container: # ... @@ -54,6 +55,7 @@ With Docker, Traefik can leverage labels attached to a container to generate rou Forward requests for `http://example.com` to `http://:12345`: ```yaml + version: "3" services: my-container: # ... @@ -76,6 +78,7 @@ With Docker, Traefik can leverage labels attached to a container to generate rou In this example, requests are forwarded for `http://example-a.com` to `http://:8000` in addition to `http://example-b.com` forwarding to `http://:9000`: ```yaml + version: "3" services: my-container: # ... @@ -93,7 +96,7 @@ With Docker, Traefik can leverage labels attached to a container to generate rou !!! info "Labels" - Labels are case-insensitive. - - The complete list of labels can be found in [the reference page](../../reference/routing-configuration/other-providers/docker.md). + - The complete list of labels can be found in [the reference page](../../reference/dynamic-configuration/docker.md). ### General @@ -330,14 +333,6 @@ you'd add the label `traefik.http.services..loadbalancer.pa - "traefik.http.services.myservice.loadbalancer.healthcheck.interval=10s" ``` -??? info "`traefik.http.services..loadbalancer.healthcheck.unhealthyinterval`" - - See [health check](../services/index.md#health-check) for more information. - - ```yaml - - "traefik.http.services.myservice.loadbalancer.healthcheck.unhealthyinterval=10s" - ``` - ??? info "`traefik.http.services..loadbalancer.healthcheck.path`" See [health check](../services/index.md#health-check) for more information. diff --git a/docs/content/routing/providers/ecs.md b/docs/content/routing/providers/ecs.md index 4068fdb78..e85975409 100644 --- a/docs/content/routing/providers/ecs.md +++ b/docs/content/routing/providers/ecs.md @@ -23,7 +23,7 @@ With ECS, Traefik can leverage labels attached to a container to generate routin !!! info "labels" - labels are case-insensitive. - - The complete list of labels can be found in [the reference page](../../reference/routing-configuration/other-providers/ecs.md). + - The complete list of labels can be found in [the reference page](../../reference/dynamic-configuration/ecs.md). ### General @@ -220,14 +220,6 @@ you'd add the label `traefik.http.services.{name-of-your-choice}.loadbalancer.pa traefik.http.services.myservice.loadbalancer.healthcheck.interval=10 ``` -??? info "`traefik.http.services..loadbalancer.healthcheck.unhealthyinterval`" - - See [health check](../services/index.md#health-check) for more information. - - ```yaml - traefik.http.services.myservice.loadbalancer.healthcheck.unhealthyinterval=10 - ``` - ??? info "`traefik.http.services..loadbalancer.healthcheck.path`" See [health check](../services/index.md#health-check) for more information. diff --git a/docs/content/routing/providers/kubernetes-crd.md b/docs/content/routing/providers/kubernetes-crd.md index 095691e14..d19c4c66c 100644 --- a/docs/content/routing/providers/kubernetes-crd.md +++ b/docs/content/routing/providers/kubernetes-crd.md @@ -48,7 +48,7 @@ The Kubernetes Ingress Controller, The Custom Resource Way. serviceAccountName: traefik-ingress-controller containers: - name: traefik - image: traefik:v3.5 + image: traefik:v3.4 args: - --log.level=DEBUG - --api @@ -392,7 +392,7 @@ Register the `IngressRoute` [kind](../../reference/dynamic-configuration/kuberne | [13] | `services[n].port` | Defines the port of a [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/). This can be a reference to a named port. | | [14] | `services[n].serversTransport` | Defines the reference to a [ServersTransport](#kind-serverstransport). The ServersTransport namespace is assumed to be the [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) namespace (see [ServersTransport reference](#serverstransport-reference)). | | [15] | `services[n].healthCheck` | Defines the HealthCheck when service references a [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type ExternalName. | -| [16] | `services[n].strategy` | Defines the load-balancing strategy for the load-balancer. Supported values are `wrr` and `p2c`, please refer to the [Load Balancing documentation](../../services/#load-balancing-strategy) for more information. | +| [16] | `services[n].strategy` | Defines the load-balancing strategy for the load-balancer. Supported values are `wrr` and `p2c`, please refer to the [Load Balancing documentation](../routing/services/#load-balancing-strategy) for more information. | | [17] | `services[n].nativeLB` | Controls, when creating the load-balancer, whether the LB's children are directly the pods IPs or if the only child is the Kubernetes Service clusterIP. | | [18] | `services[n].nodePortLB` | Controls, when creating the load-balancer, whether the LB's children are directly the nodes internal IPs using the nodePort when the service type is NodePort. | | [19] | `tls` | Defines [TLS](../routers/index.md#tls) certificate configuration | @@ -1671,7 +1671,7 @@ or referencing TLS options in the [`IngressRoute`](#kind-ingressroute) / [`Ingre | [2] | `minVersion` | Defines the [minimum TLS version](../../https/tls.md#minimum-tls-version) that is acceptable. | | [3] | `maxVersion` | Defines the [maximum TLS version](../../https/tls.md#maximum-tls-version) that is acceptable. | | [4] | `cipherSuites` | list of supported [cipher suites](../../https/tls.md#cipher-suites) for TLS versions up to TLS 1.2. | -| [5] | `curvePreferences` | List of the [elliptic curves references](../../https/tls.md#curve-preferences) that will be used in an ECDHE handshake. | +| [5] | `curvePreferences` | List of the [elliptic curves references](../../https/tls.md#curve-preferences) that will be used in an ECDHE handshake, in preference order. | | [6] | `clientAuth` | determines the server's policy for TLS [Client Authentication](../../https/tls.md#client-authentication-mtls). | | [7] | `clientAuth.secretNames` | list of names of the referenced Kubernetes [Secrets](https://kubernetes.io/docs/concepts/configuration/secret/) (in TLSOption namespace). The secret must contain a certificate under either a `tls.ca` or a `ca.crt` key. | | [8] | `clientAuth.clientAuthType` | defines the client authentication type to apply. The available values are: `NoClientCert`, `RequestClientCert`, `VerifyClientCertIfGiven` and `RequireAndVerifyClientCert`. | diff --git a/docs/content/routing/providers/kubernetes-gateway.md b/docs/content/routing/providers/kubernetes-gateway.md index f658ca4ba..3137db830 100644 --- a/docs/content/routing/providers/kubernetes-gateway.md +++ b/docs/content/routing/providers/kubernetes-gateway.md @@ -8,11 +8,11 @@ description: "The Kubernetes Gateway API can be used as a provider for routing a When using the Kubernetes Gateway API provider, Traefik leverages the Gateway API Custom Resource Definitions (CRDs) to obtain its routing configuration. For detailed information on the Gateway API concepts and resources, refer to the official [documentation](https://gateway-api.sigs.k8s.io/). -The Kubernetes Gateway API provider supports version [v1.3.0](https://github.com/kubernetes-sigs/gateway-api/releases/tag/v1.3.0) of the specification. +The Kubernetes Gateway API provider supports version [v1.2.1](https://github.com/kubernetes-sigs/gateway-api/releases/tag/v1.2.1) of the specification. It fully supports all `HTTPRoute` core and some extended features, like `GRPCRoute`, as well as the `TCPRoute` and `TLSRoute` resources from the [Experimental channel](https://gateway-api.sigs.k8s.io/concepts/versioning/?h=#release-channels). -For more details, check out the conformance [report](https://github.com/kubernetes-sigs/gateway-api/tree/main/conformance/reports/v1.3.0/traefik-traefik). +For more details, check out the conformance [report](https://github.com/kubernetes-sigs/gateway-api/tree/main/conformance/reports/v1.2.1/traefik-traefik). ## Deploying a Gateway diff --git a/docs/content/routing/providers/kubernetes-ingress.md b/docs/content/routing/providers/kubernetes-ingress.md index ff02ae929..5a82d8433 100644 --- a/docs/content/routing/providers/kubernetes-ingress.md +++ b/docs/content/routing/providers/kubernetes-ingress.md @@ -130,7 +130,7 @@ which in turn will create the resulting routers, services, handlers, etc. serviceAccountName: traefik-ingress-controller containers: - name: traefik - image: traefik:v3.5 + image: traefik:v3.4 args: - --entryPoints.web.address=:80 - --providers.kubernetesingress @@ -439,7 +439,7 @@ If the Kubernetes cluster version is 1.18+, the new `pathType` property can be leveraged to define the rules matchers: - `Exact`: This path type forces the rule matcher to `Path` -- `Prefix`: This path type forces the rule matcher to `PathPrefix`. Note that if you want the matching behavior to strictly comply with Kubernetes Ingress specification (request path is matched on an element-by-element basis), consider enabling [`strictPrefixMatching`](../../providers/kubernetes-ingress.md#strictprefixmatching) in the Ingress Provider configuration. +- `Prefix`: This path type forces the rule matcher to `PathPrefix` Please see [this documentation](https://kubernetes.io/docs/concepts/services-networking/ingress/#path-types) for more information. @@ -593,7 +593,7 @@ This way, any Ingress attached to this Entrypoint will have TLS termination by d serviceAccountName: traefik-ingress-controller containers: - name: traefik - image: traefik:v3.5 + image: traefik:v3.4 args: - --entryPoints.websecure.address=:443 - --entryPoints.websecure.http.tls @@ -786,7 +786,7 @@ For more options, please refer to the available [annotations](#on-ingress). serviceAccountName: traefik-ingress-controller containers: - name: traefik - image: traefik:v3.5 + image: traefik:v3.4 args: - --entryPoints.websecure.address=:443 - --providers.kubernetesingress @@ -910,7 +910,7 @@ TLS certificates can be managed in Secrets objects. whether the LB's children are directly the pods IPs or if the only child is the Kubernetes Service clusterIP. One alternative is to use an `ExternalName` service to forward requests to the Kubernetes service through DNS. - To do so, one must [allow external name services](../../../providers/kubernetes-ingress/#allowexternalnameservices "Link to docs about allowing external name services"). + To do so, one must [allow external name services](../providers/kubernetes-ingress/#allowexternalnameservices "Link to docs about allowing external name services"). Traefik automatically requests endpoint information based on the service provided in the ingress spec. Although Traefik will connect directly to the endpoints (pods), diff --git a/docs/content/routing/providers/kv.md b/docs/content/routing/providers/kv.md index a5a5b807b..37e6148f5 100644 --- a/docs/content/routing/providers/kv.md +++ b/docs/content/routing/providers/kv.md @@ -13,7 +13,7 @@ A Story of key & values !!! info "Keys" - Keys are case-insensitive. - - The complete list of keys can be found in [the reference page](../../reference/routing-configuration/other-providers/kv.md). + - The complete list of keys can be found in [the reference page](../../reference/dynamic-configuration/kv.md). ### Routers @@ -180,14 +180,6 @@ A Story of key & values |---------------------------------------------------------------------|-------| | `traefik/http/services/myservice/loadbalancer/healthcheck/interval` | `10` | -??? info "`traefik/http/services//loadbalancer/healthcheck/unhealthyinterval`" - - See [health check](../services/index.md#health-check) for more information. - - | Key (Path) | Value | - |------------------------------------------------------------------------------|-------| - | `traefik/http/services/myservice/loadbalancer/healthcheck/unhealthyinterval` | `10` | - ??? info "`traefik/http/services//loadbalancer/healthcheck/path`" See [health check](../services/index.md#health-check) for more information. diff --git a/docs/content/routing/providers/nomad.md b/docs/content/routing/providers/nomad.md index 4ed3e8c2f..613f35a7b 100644 --- a/docs/content/routing/providers/nomad.md +++ b/docs/content/routing/providers/nomad.md @@ -25,7 +25,7 @@ With Nomad, Traefik can leverage tags attached to a service to generate routing !!! info "tags" - tags are case-insensitive. - - The complete list of tags can be found [the reference page](../../reference/routing-configuration/other-providers/nomad.md) + - The complete list of tags can be found [the reference page](../../reference/dynamic-configuration/nomad.md) ### General @@ -218,14 +218,6 @@ you'd add the tag `traefik.http.services.{name-of-your-choice}.loadbalancer.pass traefik.http.services.myservice.loadbalancer.healthcheck.interval=10 ``` -??? info "`traefik.http.services..loadbalancer.healthcheck.unhealthyinterval`" - - See [health check](../services/index.md#health-check) for more information. - - ```yaml - traefik.http.services.myservice.loadbalancer.healthcheck.unhealthyinterval=10 - ``` - ??? info "`traefik.http.services..loadbalancer.healthcheck.path`" See [health check](../services/index.md#health-check) for more information. diff --git a/docs/content/routing/providers/swarm.md b/docs/content/routing/providers/swarm.md index c628e1b4e..bd9685ef6 100644 --- a/docs/content/routing/providers/swarm.md +++ b/docs/content/routing/providers/swarm.md @@ -55,6 +55,7 @@ With Docker Swarm, Traefik can leverage labels attached to a service to generate then that service is automatically assigned to the router. ```yaml + version: "3" services: my-container: deploy: @@ -73,6 +74,7 @@ With Docker Swarm, Traefik can leverage labels attached to a service to generate Forward requests for `http://example.com` to `http://:12345`: ```yaml + version: "3" services: my-container: # ... @@ -98,6 +100,7 @@ With Docker Swarm, Traefik can leverage labels attached to a service to generate In this example, requests are forwarded for `http://example-a.com` to `http://:8000` in addition to `http://example-b.com` forwarding to `http://:9000`: ```yaml + version: "3" services: my-container: # ... @@ -116,7 +119,7 @@ With Docker Swarm, Traefik can leverage labels attached to a service to generate !!! info "Labels" - Labels are case-insensitive. - - The complete list of labels can be found in [the reference page](../../reference/routing-configuration/other-providers/swarm.md). + - The complete list of labels can be found in [the reference page](../../reference/dynamic-configuration/docker.md). ### General @@ -344,14 +347,6 @@ you'd add the label `traefik.http.services..loadbalancer.pa - "traefik.http.services.myservice.loadbalancer.healthcheck.interval=10s" ``` -??? info "`traefik.http.services..loadbalancer.healthcheck.unhealthyinterval`" - - See [health check](../services/index.md#health-check) for more information. - - ```yaml - - "traefik.http.services.myservice.loadbalancer.healthcheck.unhealthyinterval=10s" - ``` - ??? info "`traefik.http.services..loadbalancer.healthcheck.path`" See [health check](../services/index.md#health-check) for more information. diff --git a/docs/content/routing/routers/index.md b/docs/content/routing/routers/index.md index 06cd3f05b..ef61d56b1 100644 --- a/docs/content/routing/routers/index.md +++ b/docs/content/routing/routers/index.md @@ -527,7 +527,7 @@ A value of `0` for the priority is ignored: `priority = 0` means that the defaul _Optional, Default=""_ -In Traefik v3 a new rule syntax has been introduced ([migration guide](../../migrate/v2-to-v3.md#router-rule-matchers)). +In Traefik v3 a new rule syntax has been introduced ([migration guide](../../migration/v2-to-v3.md#router-rule-matchers)). `ruleSyntax` option allows to configure the rule syntax to be used for parsing the rule on a per-router basis. This allows to have heterogeneous router configurations and ease migration. @@ -1351,7 +1351,7 @@ A value of `0` for the priority is ignored: `priority = 0` means that the defaul _Optional, Default=""_ -In Traefik v3 a new rule syntax has been introduced ([migration guide](../../migrate/v2-to-v3.md#router-rule-matchers)). +In Traefik v3 a new rule syntax has been introduced ([migration guide](../../migration/v2-to-v3.md#router-rule-matchers)). `ruleSyntax` option allows to configure the rule syntax to be used for parsing the rule on a per-router basis. This allows to have heterogeneous router configurations and ease migration. diff --git a/docs/content/routing/services/index.md b/docs/content/routing/services/index.md index 3a4cc5c0a..199fb81a3 100644 --- a/docs/content/routing/services/index.md +++ b/docs/content/routing/services/index.md @@ -415,8 +415,7 @@ Below are the available options for the health check mechanism: - `mode` (default: http), if defined to `grpc`, will use the gRPC health check protocol to probe the server. - `hostname` (optional), sets the value of `hostname` in the `Host` header of the health check request. - `port` (optional), replaces the server URL `port` for the health check endpoint. -- `interval` (default: 30s), defines the frequency of the health check calls for healthy targets. -- `unhealthyInterval` (default: 30s), defines the frequency of the health check calls for unhealthy targets. When not defined, it defaults to the `interval` value. +- `interval` (default: 30s), defines the frequency of the health check calls. - `timeout` (default: 5s), defines the maximum duration Traefik will wait for a health check request before considering the server unhealthy. - `headers` (optional), defines custom headers to be sent to the health check endpoint. - `followRedirects` (default: true), defines whether redirects should be followed during the health check calls. @@ -425,7 +424,7 @@ Below are the available options for the health check mechanism: !!! info "Interval & Timeout Format" - Interval, UnhealthyInterval and Timeout are to be given in a format understood by [time.ParseDuration](https://golang.org/pkg/time/#ParseDuration). + Interval and timeout are to be given in a format understood by [time.ParseDuration](https://golang.org/pkg/time/#ParseDuration). !!! info "Recovering Servers" @@ -1260,10 +1259,6 @@ Please note that by default the whole request is buffered in memory while it is See the maxBodySize option in the example below for how to modify this behaviour. You can also omit the request body by setting the mirrorBody option to `false`. -!!! warning "Default behavior of `percent`" - - When configuring a `mirror` service, if the `percent` field is not set, it defaults to `0`, meaning **no traffic will be sent to the mirror**. - !!! info "Supported Providers" This strategy can be defined currently with the [File](../../providers/file.md) or [IngressRoute](../../providers/kubernetes-crd.md) providers. @@ -1284,8 +1279,6 @@ http: maxBodySize: 1024 mirrors: - name: appv2 - # Percent defines the percentage of requests that should be mirrored. - # Default value is 0, which means no traffic will be sent to the mirror. percent: 10 appv1: @@ -1648,6 +1641,79 @@ The `tls` determines whether to use TLS when dialing with the backend. If no serversTransport is specified, the `default@internal` will be used. The `default@internal` serversTransport is created from the [static configuration](../overview.md#tcp-servers-transports). +#### PROXY Protocol + +Traefik supports [PROXY Protocol](https://www.haproxy.org/download/2.0/doc/proxy-protocol.txt) version 1 and 2 on TCP Services. +It can be enabled by setting `proxyProtocol` on the load balancer. + +Below are the available options for the PROXY protocol: + +- `version` specifies the version of the protocol to be used. Either `1` or `2`. + +!!! info "Version" + + Specifying a version is optional. By default the version 2 will be used. + +??? example "A Service with Proxy Protocol v1 -- Using the [File Provider](../../providers/file.md)" + + ```yaml tab="YAML" + ## Dynamic configuration + tcp: + services: + my-service: + loadBalancer: + proxyProtocol: + version: 1 + ``` + + ```toml tab="TOML" + ## Dynamic configuration + [tcp.services] + [tcp.services.my-service.loadBalancer] + [tcp.services.my-service.loadBalancer.proxyProtocol] + version = 1 + ``` + +#### Termination Delay + +!!! warning + + Deprecated in favor of [`serversTransport.terminationDelay`](#terminationdelay). + Please note that if any `serversTransport` configuration on the servers load balancer is found, + it will take precedence over the servers load balancer `terminationDelay` value, + even if the `serversTransport.terminationDelay` is undefined. + +As a proxy between a client and a server, it can happen that either side (e.g. client side) decides to terminate its writing capability on the connection (i.e. issuance of a FIN packet). +The proxy needs to propagate that intent to the other side, and so when that happens, it also does the same on its connection with the other side (e.g. backend side). + +However, if for some reason (bad implementation, or malicious intent) the other side does not eventually do the same as well, +the connection would stay half-open, which would lock resources for however long. + +To that end, as soon as the proxy enters this termination sequence, it sets a deadline on fully terminating the connections on both sides. + +The termination delay controls that deadline. +It is a duration in milliseconds, defaulting to 100. +A negative value means an infinite deadline (i.e. the connection is never fully terminated by the proxy itself). + +??? example "A Service with a termination delay -- Using the [File Provider](../../providers/file.md)" + + ```yaml tab="YAML" + ## Dynamic configuration + tcp: + services: + my-service: + loadBalancer: + terminationDelay: 200 + ``` + + ```toml tab="TOML" + ## Dynamic configuration + [tcp.services] + [tcp.services.my-service.loadBalancer] + [[tcp.services.my-service.loadBalancer]] + terminationDelay = 200 + ``` + ### Weighted Round Robin The Weighted Round Robin (alias `WRR`) load-balancer of services is in charge of balancing the requests between multiple services based on provided weights. diff --git a/docs/content/secure/secure-api-access-with-jwt.md b/docs/content/secure/secure-api-access-with-jwt.md deleted file mode 100644 index 5e853a637..000000000 --- a/docs/content/secure/secure-api-access-with-jwt.md +++ /dev/null @@ -1,204 +0,0 @@ ---- -title: 'Secure API Access with JWT' -description: 'Traefik Hub API Gateway - Learn how to configure the JWT Authentication middleware for Ingress management.' ---- - -# Secure API Access with JWT - -!!! info "Traefik Hub Feature" - This middleware is available exclusively in [Traefik Hub](https://traefik.io/traefik-hub/). Learn more about [Traefik Hub's advanced features](https://doc.traefik.io/traefik-hub/api-gateway/intro). - -JSON Web Token (JWT) (defined in the [RFC 7519](https://tools.ietf.org/html/rfc7519)) allows -Traefik Hub API Gateway to secure the API access using a token signed using either a private signing secret or a plublic/private key. - -Traefik Hub API Gateway provides many kinds of sources to perform the token validation: - -- Setting a secret value in the middleware configuration (option `signingSecret`). -- Setting a public key: In that case, users should sign their token using a private key, and the public key can be used to verify the signature (option `publicKey`). -- Setting a [JSON Web Key (JWK)](https://datatracker.ietf.org/doc/html/rfc7517) file to define a set of JWK to be used to verify the signature of the incoming JWT (option `jwksFile`). -- Setting a [JSON Web Key (JWK)](https://datatracker.ietf.org/doc/html/rfc7517) URL to define the URL of the host serving a JWK set (option `jwksUrl`). - -!!! note "One single source" - The JWT middleware does not allow you to set more than one way to validate the incoming tokens. - When a Hub API Gateway receives a request that must be validated using the JWT middleware, it verifies the token using the source configured as described above. - If the token is successfully checked, the request is accepted. - -!!! note "Claim Usage" - A JWT can contain metadata in the form of claims (key-value pairs). - The claims contained in the JWT can be used for advanced use-cases such as adding an Authorization layer using the `claims`. - - More information in the [dedicated section](../reference/routing-configuration/http/middlewares/jwt.md#claims). - -## Verify a JWT with a secret - -To allow the Traefik Hub API Gateway to validate a JWT with a secret value stored in a Kubernetes Secret, apply the following configuration: - -```yaml tab="Middleware JWT" -apiVersion: traefik.io/v1alpha1 -kind: Middleware -metadata: - name: test-jwt - namespace: apps -spec: - plugin: - jwt: - signingSecret: "urn:k8s:secret:jwt:signingSecret" -``` - -```yaml tab="Kubernetes Secret" -apiVersion: v1 -kind: Secret -metadata: - name: jwt - namespace: apps -stringData: - signingSecret: mysuperlongsecret -``` - -```yaml tab="IngressRoute" -apiVersion: traefik.io/v1alpha1 -kind: IngressRoute -metadata: - name: my-app - namespace: apps -spec: - entryPoints: - - websecure - routes: - - match: Path(`/my-app`) - kind: Rule - services: - - name: whoami - port: 80 - middlewares: - - name: test-jwt -``` - -```yaml tab="Service & Deployment" -kind: Deployment -apiVersion: apps/v1 -metadata: - name: whoami - namespace: apps -spec: - replicas: 3 - selector: - matchLabels: - app: whoami - template: - metadata: - labels: - app: whoami - spec: - containers: - - name: whoami - image: traefik/whoami - ---- -apiVersion: v1 -kind: Service -metadata: - name: whoami - namespace: apps -spec: - ports: - - port: 80 - name: whoami - selector: - app: whoami -``` - -## Verify a JWT using an Identity Provider - -To allow the Traefik Hub API Gateway to validate a JWT using an Identity Provider, such as Keycloak and Azure AD in the examples below, apply the following configuration: - -```yaml tab="JWKS with Keycloak URL" -apiVersion: traefik.io/v1alpha1 -kind: Middleware -metadata: - name: test-jwt - namespace: apps -spec: - plugin: - jwt: - # Replace KEYCLOAK_URL and REALM_NAME with your values - jwksUrl: https://KEYCLOAK_URL/realms/REALM_NAME/protocol/openid-connect/certs - # Forward the content of the claim grp in the header Group - forwardHeaders: - Group: grp - # Check the value of the claim grp before sending the request to the backend - claims: Equals(`grp`, `admin`) -``` - -```yaml tab="JWKS with Azure AD URL" -apiVersion: traefik.io/v1alpha1 -kind: Middleware -metadata: - name: test-jwt - namespace: apps -spec: - plugin: - jwt: - jwksUrl: https://login.microsoftonline.com/common/discovery/v2.0/keys -``` - -```yaml tab="IngressRoute" -apiVersion: traefik.io/v1alpha1 -kind: IngressRoute -metadata: - name: my-app - namespace: apps -spec: - entryPoints: - - websecure - routes: - - match: Path(`/my-app`) - kind: Rule - services: - - name: whoami - port: 80 - middlewares: - - name: test-jwt -``` - -```yaml tab="Service & Deployment" -kind: Deployment -apiVersion: apps/v1 -metadata: - name: whoami - namespace: apps -spec: - replicas: 3 - selector: - matchLabels: - app: whoami - template: - metadata: - labels: - app: whoami - spec: - containers: - - name: whoami - image: traefik/whoami - ---- -apiVersion: v1 -kind: Service -metadata: - name: whoami - namespace: apps -spec: - ports: - - port: 80 - name: whoami - selector: - app: whoami -``` - -!!! note "Advanced Configuration" - Advanced options are described in the [reference page](../reference/routing-configuration/http/middlewares/jwt.md). - - For example, the metadata recovered from the Identity Provider can be used to restrict the access to the applications. - To do so, you can use the `claims` option, more information in the [dedicated section](../reference/routing-configuration/http/middlewares/jwt.md#claims). - -{!traefik-for-business-applications.md!} diff --git a/docs/content/secure/secure-api-access-with-oidc.md b/docs/content/secure/secure-api-access-with-oidc.md deleted file mode 100644 index f7b18def9..000000000 --- a/docs/content/secure/secure-api-access-with-oidc.md +++ /dev/null @@ -1,110 +0,0 @@ ---- -title: 'Secure API Access with OIDC' -description: 'Traefik Hub API Gateway - The OIDC Authentication middleware secures your applications by delegating the authentication to an external provider.' ---- - -# Secure API Access with OIDC - -!!! info "Traefik Hub Feature" - This middleware is available exclusively in [Traefik Hub](https://traefik.io/traefik-hub/). Learn more about [Traefik Hub's advanced features](https://doc.traefik.io/traefik-hub/api-gateway/intro). - -OpenID Connect Authentication is built on top of the OAuth2 Authorization Code Flow (defined in [OAuth 2.0 RFC 6749, section 4.1](https://tools.ietf.org/html/rfc6749#section-4.1)). -It allows an application to be secured by delegating authentication to an external provider (Keycloak, Okta etc.) -and obtaining the end user's session claims and scopes for authorization purposes. - -To authenticate the user, the middleware redirects through the authentication provider. -Once the authentication is complete, users are redirected back to the middleware before being authorized to access the upstream application, as described in the diagram below: - -![OpenID Connect authentication flow](../assets/img/secure/oidc-auth-flow.png) - -
- -To allow the OIDC Middleware to use the credentials provided by the requests, apply the following configuration: - -```yaml tab="Middleware OIDC" -apiVersion: traefik.io/v1alpha1 -kind: Middleware -metadata: - name: oidc-login - namespace: apps -spec: - plugin: - oidc: - issuer: MY_ISSUER_URL - clientId: "urn:k8s:secret:oidc-client:client_id" - clientSecret: "urn:k8s:secret:oidc-client:client_secret" - redirectUrl: /oidc/callback -``` - -```yaml tab="Kubernetes Secrets" -apiVersion: v1 -kind: Secret -metadata: - name: oidc-client -stringData: - client_id: my-oauth-client-ID # Set your ClientID here - client_secret: my-oauth-client-secret # Set your client secret here -``` - -```yaml tab="IngressRoute" -apiVersion: traefik.io/v1alpha1 -kind: IngressRoute -metadata: - name: secure-applications-apigateway-oauth2-client-credentials - namespace: apps -spec: - entryPoints: - - websecure - routes: - - match: Path(`/my-app`) - kind: Rule - services: - - name: whoami - port: 80 - middlewares: - - name: oidc-login -``` - -```yaml tab="Service & Deployment" -kind: Deployment -apiVersion: apps/v1 -metadata: - name: whoami - namespace: apps -spec: - replicas: 3 - selector: - matchLabels: - app: whoami - template: - metadata: - labels: - app: whoami - spec: - containers: - - name: whoami - image: traefik/whoami - ---- -apiVersion: v1 -kind: Service -metadata: - name: whoami - namespace: apps -spec: - ports: - - port: 80 - name: whoami - selector: - app: whoami -``` - -!!! note "Advanced Configuration" - - Advanced options are described in the [reference page](../reference/routing-configuration/http/middlewares/oidc.md). - - For example, you can find how to customize the session storage: - - Using a cookie ([Options `session`](../reference/routing-configuration/http/middlewares/oidc.md#configuration-options) (default behavior)) - - Using a [Redis store](../reference/routing-configuration/http/middlewares/oidc.md#sessionstore). - -{!traefik-for-business-applications.md!} diff --git a/docs/content/secure/secure-api-access-with-waf.md b/docs/content/secure/secure-api-access-with-waf.md deleted file mode 100644 index 455839b99..000000000 --- a/docs/content/secure/secure-api-access-with-waf.md +++ /dev/null @@ -1,190 +0,0 @@ ---- -title: 'Secure API Access with WAF' -description: 'Traefik Hub API Gateway - Learn how to configure the Coraza Web Application Firewall middleware to protect your applications from common web attacks.' ---- - -# Secure API Access with WAF - -!!! info "Traefik Hub Feature" - This middleware is available exclusively in [Traefik Hub](https://traefik.io/traefik-hub/). Learn more about [Traefik Hub's advanced features](https://doc.traefik.io/traefik-hub/api-gateway/intro). - -The [Coraza Web Application Firewall](https://coraza.io/) middleware in Traefik Hub API Gateway provides comprehensive protection against common web application attacks. The middleware supports the Coraza rule syntax and is compatible with [OWASP Core Rule Set (CRS)](https://coreruleset.org/docs/), allowing you to leverage proven security rules maintained by the security community. - -## Basic WAF Protection - -To protect your applications with custom security rules, apply the following configuration: - -```yaml tab="Middleware WAF" -apiVersion: traefik.io/v1alpha1 -kind: Middleware -metadata: - name: waf-protection - namespace: apps -spec: - plugin: - coraza: - directives: - - SecRuleEngine On - - SecRule REQUEST_URI "@streq /admin" "id:101,phase:1,t:lowercase,log,deny" - - SecRule ARGS "@detectSQLi" "id:102,phase:2,block,msg:'SQL Injection Attack Detected',logdata:'Matched Data: %{MATCHED_VAR} found within %{MATCHED_VAR_NAME}'" -``` - -This configuration implements three security directives that work together to protect an application: - -- **SecRuleEngine On**: Activates the WAF engine to begin processing incoming requests. Without this directive, all other rules remain inactive regardless of their configuration. - -- **Admin Path Protection**: The second rule blocks all access to `/admin` paths by examining the request URI. This completely prevents access to administrative interfaces that often contain sensitive functionality like user management, system configuration, or database administration tools. The rule triggers during phase 1 (request headers processing) and applies lowercase transformation to catch variations like `/Admin` or `/ADMIN`. - -- **SQL Injection Detection**: The third rule scans request parameters (query strings and form data) for SQL injection patterns using Coraza's built-in detection engine. The `ARGS` variable covers query string parameters like `?id=1` and form data from POST requests like `username=admin&password=123`, but does not include cookies. SQL injection attacks attempt to manipulate database queries by injecting malicious SQL code through user inputs. When detected, the rule blocks the request and logs detailed information about the attempted attack, including which parameter contained the malicious payload. - -```yaml tab="IngressRoute" -apiVersion: traefik.io/v1alpha1 -kind: IngressRoute -metadata: - name: protected-app - namespace: apps -spec: - entryPoints: - - websecure - routes: - - match: Path(`/my-app`) - kind: Rule - services: - - name: whoami - port: 80 - middlewares: - - name: waf-protection -``` - -```yaml tab="Service & Deployment" -kind: Deployment -apiVersion: apps/v1 -metadata: - name: whoami - namespace: apps -spec: - replicas: 3 - selector: - matchLabels: - app: whoami - template: - metadata: - labels: - app: whoami - spec: - containers: - - name: whoami - image: traefik/whoami - ---- -apiVersion: v1 -kind: Service -metadata: - name: whoami - namespace: apps -spec: - ports: - - port: 80 - name: whoami - selector: - app: whoami -``` - -## Advanced Protection with OWASP Core Rule Set - -To implement comprehensive protection using the OWASP Core Rule Set, which provides battle-tested rules against common attack patterns, apply the following configuration: - -```yaml tab="Middleware WAF with CRS" -apiVersion: traefik.io/v1alpha1 -kind: Middleware -metadata: - name: waf-crs-protection - namespace: apps -spec: - plugin: - coraza: - crsEnabled: true - directives: - - SecRuleEngine On - - SecDefaultAction "phase:1,log,auditlog,deny,status:403" - - SecDefaultAction "phase:2,log,auditlog,deny,status:403" - - SecAction "id:900110, phase:1, pass, t:none, nolog, setvar:tx.inbound_anomaly_score_threshold=5, setvar:tx.outbound_anomaly_score_threshold=4" - - SecAction "id:900200, phase:1, pass, t:none, nolog, setvar:'tx.allowed_methods=GET POST'" - - Include @owasp_crs/REQUEST-911-METHOD-ENFORCEMENT.conf - - Include @owasp_crs/REQUEST-949-BLOCKING-EVALUATION.conf -``` - -This advanced configuration implements [OWASP Core Rule Set (CRS)](https://coreruleset.org/docs/) protection with anomaly scoring: - -- **SecDefaultAction for Phase 1 & 2**: Sets default behavior for request processing phases. Phase 1 processes request headers while Phase 2 processes request body. When rules match, they log the event to both standard and audit logs, then deny the request with a 403 status code. - -- **Anomaly Score Configuration**: The first `SecAction` sets anomaly score thresholds where `inbound_anomaly_score_threshold=5` means requests scoring 5 or higher are blocked, and `outbound_anomaly_score_threshold=4` applies the same logic to responses. This scoring system allows multiple suspicious patterns to accumulate points rather than blocking on first detection, reducing false positives while maintaining security. - -- **Allowed Methods Configuration**: The second `SecAction` restricts HTTP methods to only `GET` and `POST` requests. This prevents potentially dangerous methods like `PUT`, `DELETE`, `PATCH`, or `OPTIONS` that could modify server resources or reveal system information. - -- **METHOD-ENFORCEMENT Rule Set**: The `REQUEST-911-METHOD-ENFORCEMENT.conf` file enforces the allowed HTTP methods policy defined above. It checks incoming requests against the permitted methods and contributes to the anomaly score for disallowed methods. - -- **BLOCKING-EVALUATION Rule Set**: The `REQUEST-949-BLOCKING-EVALUATION.conf` file evaluates the accumulated anomaly score against the configured thresholds. If the total score exceeds the threshold, it triggers the blocking action, preventing the request from reaching your application. - -```yaml tab="IngressRoute" -apiVersion: traefik.io/v1alpha1 -kind: IngressRoute -metadata: - name: crs-protected-app - namespace: apps -spec: - entryPoints: - - websecure - routes: - - match: Path(`/my-app`) - kind: Rule - services: - - name: whoami - port: 80 - middlewares: - - name: waf-crs-protection -``` - -```yaml tab="Service & Deployment" -kind: Deployment -apiVersion: apps/v1 -metadata: - name: whoami - namespace: apps -spec: - replicas: 3 - selector: - matchLabels: - app: whoami - template: - metadata: - labels: - app: whoami - spec: - containers: - - name: whoami - image: traefik/whoami - ---- -apiVersion: v1 -kind: Service -metadata: - name: whoami - namespace: apps -spec: - ports: - - port: 80 - name: whoami - selector: - app: whoami -``` - -!!! warning - Starting with Traefik Hub v3.11.0, Coraza requires read/write permissions to `/tmp`. This requirement stems from upstream changes in the Coraza engine. - -!!! note "Advanced Configuration" - Advanced options and detailed rule configuration are described in the [reference page](../reference/routing-configuration/http/middlewares/waf.md). - - The WAF middleware supports extensive customization through Coraza directives. You can create custom rules, tune detection thresholds, configure logging levels, and integrate with external threat intelligence feeds. For comprehensive rule writing guidance, consult the [Coraza documentation](https://coraza.io/docs/tutorials/introduction/) and [OWASP CRS documentation](https://coreruleset.org/docs/). - -{!traefik-for-business-applications.md!} diff --git a/docs/content/setup/docker.md b/docs/content/setup/docker.md deleted file mode 100644 index beaf29f9d..000000000 --- a/docs/content/setup/docker.md +++ /dev/null @@ -1,299 +0,0 @@ ---- -title: Setup Traefik Proxy in Docker Standalone -description: "Learn how to Setup Traefik on Docker with HTTP/HTTPS entrypoints, redirects, secure dashboard, basic TLS, metrics, tracing, access‑logs." ---- - -This guide provides an in-depth walkthrough for installing and configuring Traefik Proxy within a Docker container using the official Traefik Docker image & Docker Compose. In this guide, we'll cover the following: - -- Enable the [Docker provider](../reference/install-configuration/providers/docker.md) -- Expose **web** (HTTP :80) and **websecure** (HTTPS :443) entrypoints -- Redirect all HTTP traffic to HTTPS -- Secure the Traefik dashboard with **basic‑auth** -- Terminate TLS with a self‑signed certificate for `*.docker.localhost` -- Deploy the **whoami** demo service -- Enable access‑logs and Prometheus metrics - -## Prerequisites - -- Docker Desktop / Engine -- Docker Compose -- `openssl` -- `htpasswd` from `apache2-utils` - -## Create a self‑signed certificate - -Before Traefik can serve HTTPS locally it needs a certificate. In production you’d use one from a trusted CA, but for a single‑machine stack a quick self‑signed cert is enough. We can create one with openssl by running the following commands: - -```bash -mkdir -p certs -openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ - -keyout certs/local.key -out certs/local.crt \ - -subj "/CN=*.docker.localhost" -``` - -The `certs` folder now holds `local.crt` and `local.key`, which will be mounted read‑only into Traefik. - -## Create the Traefik Dashboard Credentials - -In production, it is advisable to have some form of authentication/security for the Traefik dashboard. Traefik can be secured with the [basic‑auth middleware](../reference/routing-configuration/http/middlewares/basicauth.md). To do this, generate a hashed username / password pair that Traefik’s middleware will validate: - -```bash -htpasswd -nb admin "P@ssw0rd" | sed -e 's/\$/\$\$/g' -``` - -Copy the full output (e.g., admin:$$apr1$$â€Ļ) — we'll need this for the middleware configuration. - -## Create a docker-compose.yaml - -Now define the whole stack in a Compose file. This file declares Traefik, mounts the certificate, sets up a dedicated network, and later hosts the whoami demo service. - -!!! note - You can also choose to use the Docker CLI and a configuration file to run Traefik, but for this tutorial, we'll be using Docker Compose. - -First, create a folder named `dynamic` and create a file named `tls.yaml` for dynamic configuration. Paste the TLS certificate configuration into the file: - -```yaml -tls: - certificates: - - certFile: /certs/local.crt - keyFile: /certs/local.key -``` - -In the same folder as the `dynamic/tls.yaml` file, create a `docker-compose.yaml` file and include the following: - -```yaml -services: - traefik: - image: traefik:v3.4 - container_name: traefik - restart: unless-stopped - security_opt: - - no-new-privileges:true - - networks: - # Connect to the 'traefik_proxy' overlay network for inter-container communication across nodes - - proxy - - ports: - - "80:80" - - "443:443" - - "8080:8080" - - volumes: - - /var/run/docker.sock:/var/run/docker.sock:ro - - ./certs:/certs:ro - - ./dynamic:/dynamic:ro - - command: - # EntryPoints - - "--entrypoints.web.address=:80" - - "--entrypoints.web.http.redirections.entrypoint.to=websecure" - - "--entrypoints.web.http.redirections.entrypoint.scheme=https" - - "--entrypoints.web.http.redirections.entrypoint.permanent=true" - - "--entrypoints.websecure.address=:443" - - "--entrypoints.websecure.http.tls=true" - - # Attach the static configuration tls.yaml file that contains the tls configuration settings - - "--providers.file.filename=/dynamic/tls.yaml" - - # Providers - - "--providers.docker=true" - - "--providers.docker.exposedbydefault=false" - - "--providers.docker.network=proxy" - - # API & Dashboard - - "--api.dashboard=true" - - "--api.insecure=false" - - # Observability - - "--log.level=INFO" - - "--accesslog=true" - - "--metrics.prometheus=true" - - # Traefik Dynamic configuration via Docker labels - labels: - # Enable self‑routing - - "traefik.enable=true" - - # Dashboard router - - "traefik.http.routers.dashboard.rule=Host(`dashboard.docker.localhost`)" - - "traefik.http.routers.dashboard.entrypoints=websecure" - - "traefik.http.routers.dashboard.service=api@internal" - - "traefik.http.routers.dashboard.tls=true" - - # Basic‑auth middleware - - "traefik.http.middlewares.dashboard-auth.basicauth.users=" - - "traefik.http.routers.dashboard.middlewares=dashboard-auth@docker" - -# Whoami application - whoami: - image: traefik/whoami - container_name: whoami - restart: unless-stopped - networks: - - proxy - labels: - - "traefik.enable=true" - - "traefik.http.routers.whoami.rule=Host(`whoami.docker.localhost`)" - - "traefik.http.routers.whoami.entrypoints=websecure" - - "traefik.http.routers.whoami.tls=true" - -networks: - proxy: - name: proxy -``` - -!!! info - - Remember to replace `` with the hash generated earlier. - - The `--api.insecure=false` flag is used to secure the API and prevent the dashboard from being exposed on port 8080. This is done because we are exposing the dashboard with a HTTPS router. - -## Launch the stack - -With the Compose file and supporting assets in place, start the containers and let Docker wire up networking behind the scenes: - -```bash -docker compose up -d -``` - -Traefik will start, read its static configuration from the `command` arguments, connect to the Docker socket, detect its own labels for dynamic configuration (dashboard routing and auth), and begin listening on ports 80 and 443. HTTP requests will be redirected to HTTPS. - -## Access the Dashboard - -Now that Traefik is deployed, you can access the dashboard at [https://dashboard.docker.localhost](https://dashboard.docker.localhost) and it should prompt for the Basic Authentication credentials you configured: - -![Traefik Dashboard](../assets/img/setup/traefik-dashboard-docker.png) - -## Test the whoami Application - -You can test the application using curl: - -```bash -curl -k https://whoami.docker.localhost/ -``` - -```bash -Hostname: whoami-76c9859cfc-k7jzs -IP: 127.0.0.1 -IP: ::1 -IP: 10.42.0.59 -IP: fe80::50d7:a2ff:fed5:2530 -RemoteAddr: 10.42.0.60:54148 -GET / HTTP/1.1 -Host: whoami.docker.localhost -User-Agent: curl/8.7.1 -Accept: */* -Accept-Encoding: gzip -X-Forwarded-For: 10.42.0.1 -X-Forwarded-Host: whoami.docker.localhost -X-Forwarded-Port: 443 -X-Forwarded-Proto: https -X-Forwarded-Server: traefik-644b7c67d9-f2tn9 -X-Real-Ip: 10.42.0.1 -``` - -Making the same request to the HTTP entrypoint will return the following: - -```bash -curl -k http://whoami.docker.localhost - -Moved Permanently -``` - -The above confirms that a redirection has taken place which means our setup works correctly. - -You can also open a browser and navigate to [https://whoami.docker.localhost](https://whoami.docker.localhost) to see a JSON dump from the service: - -![Whoami](../assets/img/setup/whoami-json-dump.png) - -!!! info - You can also navigate to the Traefik Dashboard at [https://dashboard.docker.localhost](https://dashboard.docker.localhost) to see that the route has been created. - -### Other Key Configuration Areas - -Beyond this initial setup, Traefik offers extensive configuration possibilities. Here are brief introductions and minimal examples using Docker Compose `command` arguments or `labels`. Consult the main documentation linked for comprehensive details. - -### TLS Certificate Management (Let's Encrypt) - -To make the `websecure` entry point serve valid HTTPS certificates automatically, enable Let's Encrypt (ACME). - -*Example `command` additions:* - -```yaml -command: - # ... other command arguments ... - - "--certificatesresolvers.le.acme.email=your-email@example.com" - - "--certificatesresolvers.le.acme.storage=/letsencrypt/acme.json" # Path inside container volume - - "--certificatesresolvers.le.acme.httpchallenge.entrypoint=web" - - # - "--certificatesresolvers.le.acme.dnschallenge.provider=your-dns-provider" # Needs provider setup - - # Optionally make 'le' the default resolver for TLS-enabled routers - - "--entrypoints.websecure.http.tls.certresolver=le" -``` - -This defines a resolver named `le`, sets the required email and storage path (within the mounted `/letsencrypt` volume), and enables the HTTP challenge. Refer to the [HTTPS/TLS Documentation](../reference/install-configuration/tls/certificate-resolvers/overview.md) and [Let's Encrypt Documentation](../reference/install-configuration/tls/certificate-resolvers/acme.md) for details on challenges and DNS provider configuration. - -### Metrics (Prometheus) - -You can expose Traefik's internal metrics for monitoring with Prometheus. We already enabled prometheus in our setup but we can further configure it. -*Example `command` additions:* - -```yaml -command: - # If using a dedicated metrics entry point, define it: - - "--entrypoints.metrics.address=:8082" - - # ... other command arguments ... - - "--metrics.prometheus=true" - - # Optionally change the entry point metrics are exposed on (defaults to 'traefik') - - "--metrics.prometheus.entrypoint=metrics" - - # Add labels to metrics for routers/services (can increase cardinality) - - "--metrics.prometheus.addrouterslabels=true" - - "--metrics.prometheus.addserviceslabels=true" -``` - -This enables the `/metrics` endpoint (typically accessed via the internal API port, often 8080 by default if not secured, or via a dedicated entry point). See the [Metrics Documentation](../reference/install-configuration/observability/metrics.md) for options. - -### Tracing (OTel): - -You can enable distributed tracing to follow requests through Traefik. -*Example `command` additions:* - -```yaml -command: - # ... other command arguments ... - - "--tracing.otel=true" - - "--tracing.otel.grpcendpoint=otel-collector:4317" # Adjust endpoint as needed - - "--tracing.otel.httpendpoint=otel-collector.observability:4318" # Adjust endpoint as needed -``` - -!!! note - This option requires a running OTEL collector accessible by Traefik. Consult the [Tracing Documentation](../reference/install-configuration/observability/tracing.md). - -### Access Logs - -You can configure Traefik to log incoming requests for debugging and analysis. -*Example `command` additions:* - -```yaml -command: - # ... other command arguments ... - - "--accesslog=true" # Enable access logs to stdout - - # Optionally change format or output file (requires volume) - - "--accesslog.format=json" - - "--accesslog.filepath=/path/to/access.log" - - # Optionally filter logs - - "--accesslog.filters.statuscodes=400-599" -``` - -This enables access logs to the container's standard output (viewable via `docker compose logs `). See the [Access Logs Documentation](../reference/install-configuration/observability/logs-and-accesslogs.md). - -### Conclusion - -You now have a basic Traefik setup in Docker with secure dashboard access and HTTP-to-HTTPS redirection. - -{!traefik-for-business-applications.md!} diff --git a/docs/content/setup/kubernetes.md b/docs/content/setup/kubernetes.md deleted file mode 100644 index c54853e8c..000000000 --- a/docs/content/setup/kubernetes.md +++ /dev/null @@ -1,400 +0,0 @@ ---- -title: "Setup Traefik on Kubernetes" -description: "Learn how to Setup Traefik on Kubernetes with HTTP/HTTPS entrypoints, redirects, secure dashboard, basic TLS, metrics, tracing, access‑logs." ---- - -This guide provides an in-depth walkthrough for installing and configuring Traefik Proxy within a Kubernetes cluster using the official Helm chart. In this guide, we'll cover the following: - -- Configure standard HTTP (`web`) and HTTPS (`websecure`) entry points, -- Implement automatic redirection from HTTP to HTTPS -- Secure the Traefik Dashboard using Basic Authentication. -- Deploy a demo application to test the setup -- Explore some other key configuration options - -## Prerequisites - -- A Kubernetes cluster -- Helm v3, -- Kubectl - -## Create the Cluster - -If you do not have a Kubernetes cluster already, you can spin up one with K3d: - -```bash -k3d cluster create traefik \ - --port 80:80@loadbalancer \ - --port 443:443@loadbalancer \ - --port 8000:8000@loadbalancer \ - --k3s-arg "--disable=traefik@server:0" -``` - -Ports `80` and `443` reach Traefik from the host, while port `8000` remains free for later demos. The built-in Traefik shipped with k3s is disabled to avoid conflicts. - -Check the context: - -```bash -kubectl cluster-info --context k3d-traefik -``` - -You should see something like this: - -```bash -Kubernetes control plane is running at https://0.0.0.0:56049 -CoreDNS is running at https://0.0.0.0:56049/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy -Metrics-server is running at https://0.0.0.0:56049/api/v1/namespaces/kube-system/services/https:metrics-server:https/proxy - -To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'. -``` - -## Add the chart repo and namespace - -Using Helm streamlines Kubernetes application deployment. Helm packages applications into "charts," which are collections of template files describing Kubernetes resources. We use the official Traefik Helm chart for a managed and customizable installation. - -```bash -helm repo add traefik https://traefik.github.io/charts -helm repo update -kubectl create namespace traefik -``` - -The first command registers the `traefik` repository alias pointing to the official chart location. The second command refreshes your local cache to ensure you have the latest list of charts and versions available from all configured repositories. - -## Create a Local Self‑Signed TLS Secret - -Traefik's Gateway listeners require a certificate whenever a listener uses `protocol: HTTPS`. - -For local development create a throw‑away self‑signed certificate and -store it in a Kubernetes Secret named **local‑selfsigned‑tls**. -The Gateway references this secret to terminate TLS on the `websecure` listener. - -```bash -# 1) Generate a self‑signed certificate valid for *.docker.localhost -openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ - -keyout tls.key -out tls.crt \ - -subj "/CN=*.docker.localhost" - -# 2) Create the TLS secret in the traefik namespace -kubectl create secret tls local-selfsigned-tls \ - --cert=tls.crt --key=tls.key \ - --namespace traefik -``` - -### Why Do We Need To Do This - -The Gateway's HTTPS listener references this secret via `certificateRefs`. -Without it, the helm chart validation fails and the HTTP→HTTPS redirect chain breaks. - -!!! info "Production tip" - The self-signed certificate above is **only for local development**. For production, either store a certificate issued by your organization's CA in a Secret or let an automated issuer such as cert-manager or Traefik's ACME (Let's Encrypt) generate certificates on demand. Update the `certificateRefs` in the `websecure` listener—or use `traefik.io/tls.certresolver`—so clients receive a trusted certificate and no longer see browser warnings. - -## Prepare Helm Chart Configuration Values - -Create a `values.yaml` file with the following content: - -```yaml - -# Configure Network Ports and EntryPoints -# EntryPoints are the network listeners for incoming traffic. -ports: - # Defines the HTTP entry point named 'web' - web: - port: 80 - nodePort: 30000 - # Instructs this entry point to redirect all traffic to the 'websecure' entry point - redirections: - entryPoint: - to: websecure - scheme: https - permanent: true - - # Defines the HTTPS entry point named 'websecure' - websecure: - port: 443 - nodePort: 30001 - -# Enables the dashboard in Secure Mode -api: - dashboard: true - insecure: false - -ingressRoute: - dashboard: - enabled: true - matchRule: Host(`dashboard.docker.localhost`) - entryPoints: - - websecure - middlewares: - - name: dashboard-auth - -# Creates a BasiAuth Middleware and Secret for the Dashboard Security -extraObjects: - - apiVersion: v1 - kind: Secret - metadata: - name: dashboard-auth-secret - type: kubernetes.io/basic-auth - stringData: - username: admin - password: "P@ssw0rd" # Replace with an Actual Password - - apiVersion: traefik.io/v1alpha1 - kind: Middleware - metadata: - name: dashboard-auth - spec: - basicAuth: - secret: dashboard-auth-secret - -# We will route with Gateway API instead. -ingressClass: - enabled: false - -# Enable Gateway API Provider & Disables the KubernetesIngress provider -# Providers tell Traefik where to find routing configuration. -providers: - kubernetesIngress: - enabled: false - kubernetesGateway: - enabled: true - -## Gateway Listeners -gateway: - listeners: - web: # HTTP listener that matches entryPoint `web` - port: 80 - protocol: HTTP - namespacePolicy: - from: All - - websecure: # HTTPS listener that matches entryPoint `websecure` - port: 443 - protocol: HTTPS # TLS terminates inside Traefik - namespacePolicy: - from: All - mode: Terminate - certificateRefs: - - kind: Secret - name: local-selfsigned-tls # the Secret we created before the installation - group: "" - -# Enable Observability -logs: - general: - level: INFO - # This enables access logs, outputting them to Traefik's standard output by default. The [Access Logs Documentation](https://doc.traefik.io/traefik/observability/access-logs/) covers formatting, filtering, and output options. - access: - enabled: true - -# Enables Prometheus for Metrics -metrics: - prometheus: - enabled: true -``` - -## Install the Traefik Using the Helm Values - -Now, apply the configuration using the Helm client. - -```bash -# Install the chart into the 'traefik' namespace -helm install traefik traefik/traefik \ - --namespace traefik \ - --values values.yaml -``` - -**Command Breakdown:** - -- `helm install traefik`: Instructs Helm to install a new release named `traefik`. -- `traefik/traefik`: Specifies the chart to use (`traefik` chart from the `traefik` repository added earlier). -- `--namespace traefik`: Specifies the Kubernetes namespace to install into. Using a dedicated namespace is recommended practice. -- `--values values.yaml`: Applies the custom configuration from your `values.yaml` file. - -## Accessing the Dashboard - -Now that Traefik is deployed, you can access its dashboard at [https://dashboard.docker.localhost/](https://dashboard.docker.localhost/). When you access this link, your browser will prompt for the username and password. Ensure you use the credentials set in the `values.yaml` file to log in. Upon successful login, the dashboard will be displayed as shown below: - -![Traefik Dashboard](../assets/img/setup/traefik-dashboard.png) - -## Deploy a Demo Application - -To test the setup, deploy the [Traefik whoami](https://github.com/traefik/whoami) application in the Kubernetes cluster. Create a file named `whoami.yaml` and paste the following: - -```yaml -apiVersion: apps/v1 -kind: Deployment -metadata: - name: whoami - namespace: traefik -spec: - replicas: 2 - selector: - matchLabels: - app: whoami - template: - metadata: - labels: - app: whoami - spec: - containers: - - name: whoami - image: traefik/whoami - ports: - - containerPort: 80 ---- -apiVersion: v1 -kind: Service -metadata: - name: whoami - namespace: traefik -spec: - selector: - app: whoami - ports: - - port: 80 -``` - -Apply the manifest: - -```bash -kubectl apply -f whoami.yaml -``` - -After deploying the application, you can expose the application by creating a [Gateway API HTTPRoute](https://gateway-api.sigs.k8s.io/api-types/httproute/). To do this, create a file named `whoami-route.yaml` and paste the following: - -```yaml -apiVersion: gateway.networking.k8s.io/v1 -kind: HTTPRoute -metadata: - name: whoami - namespace: traefik -spec: - parentRefs: - - name: traefik-gateway # Name of the Gateway that Traefik creates when you enable the Gateway API provider - hostnames: - - "whoami.docker.localhost" - rules: - - matches: - - path: - type: PathPrefix - value: / - backendRefs: - - name: whoami - port: 80 -``` - -Apply the manifest: - -```bash -kubectl apply -f whoami-route.yaml -``` - -After you apply the manifest, navigate to the Routes in the Traefik Dashboard; you’ll see that the [https://whoami.docker.localhost](https://whoami.docker.localhost) route has been created. - -![Route](../assets/img/setup/route-in-dashboard.png) - -You can test the application using curl: - -```bash -curl -k https://whoami.docker.localhost/ -``` - -```bash -Hostname: whoami-76c9859cfc-k7jzs -IP: 127.0.0.1 -IP: ::1 -IP: 10.42.0.59 -IP: fe80::50d7:a2ff:fed5:2530 -RemoteAddr: 10.42.0.60:54148 -GET / HTTP/1.1 -Host: whoami.docker.localhost -User-Agent: curl/8.7.1 -Accept: */* -Accept-Encoding: gzip -X-Forwarded-For: 10.42.0.1 -X-Forwarded-Host: whoami.docker.localhost -X-Forwarded-Port: 443 -X-Forwarded-Proto: https -X-Forwarded-Server: traefik-644b7c67d9-f2tn9 -X-Real-Ip: 10.42.0.1 -``` - -You can also open a browser and navigate to [https://whoami.docker.localhost](https://whoami.docker.localhost) to see a JSON dump from the service. - -![Whoami](../assets/img/setup/whoami-json-dump.png) - -## Other Key Configuration Areas - -The above setup provides a secure base, but Traefik offers much more. Here's a brief overview of other essential configurations, with minimal examples using Helm `values.yaml` overrides. - -These examples illustrate how to enable features; consult the main documentation for detailed options. - -### TLS Certificate Management (Let's Encrypt) - -On the `websecure` entry point TLS is enabled by default. However, it currently lacks a valid certificate. Traefik can automatically obtain and renew TLS certificates from Let's Encrypt using the ACME protocol. - -*Example `values.yaml` addition:* - -```yaml -additionalArguments: - - "--certificatesresolvers.le.acme.email=your-email@example.com" - - "--certificatesresolvers.le.acme.storage=/data/acme.json" - - "--certificatesresolvers.le.acme.httpchallenge.entrypoint=web" - # - "--certificatesresolvers.le.acme.dnschallenge.provider=your-dns-provider" # Requires provider-specific config, adjust if you control your DNS provider - -# Enable persistence for ACME data (certificates, account) to ensure it survives pod restarts: -persistence: - enabled: true - name: data - size: 1Gi - storageClass: "" -``` - -This enables a certificate resolver named `le`, configures the mandatory email and storage file, and sets up the HTTP challenge (requires port 80 access). Refer to the [HTTPS/TLS Documentation](../reference/install-configuration/tls/certificate-resolvers/overview.md) and [Let's Encrypt Documentation](../reference/install-configuration/tls/certificate-resolvers/acme.md) for full details, including DNS challenge configuration. - -!!!info "Let's Encrypt in Production" - Let's Encrypt can only issue certificates for hostnames that point to a public IP address reachable on ports 80 (HTTP‑01) or via your DNS provider's API (DNS‑01). Replace the `*.docker.localhost` examples with a real domain you control, create the DNS records, and keep ports 80/443 open to your cluster so the validation can succeed. - -### Gateway API & ACME - -Traefik’s built‑in ACME/Let’s Encrypt integration works for IngressRoute and Ingress resources, but it does not issue certificates for Gateway API listeners. -If you’re using the Gateway API, install [cert‑manager](https://cert-manager.io/docs/) (or another certificate controller) and reference the secret it creates in `gateway.listeners.websecure.certificateRefs`. - -### Metrics (Prometheus) - -Traefik can expose detailed metrics in Prometheus format, essential for monitoring its performance and the traffic it handles. - -*Example `values.yaml` addition:* - -```yaml -# Enable metrics endpoint -metrics: - prometheus: - # The entry point metrics will be available on (usually internal/admin) - entryPoint: metrics - # Add standard Prometheus metrics - addRoutersLabels: true - addServicesLabels: true - # ... other options available -``` - -This enables the Prometheus endpoint on a dedicated `metrics` entry point (port 9100). See the [Metrics Documentation](../reference/install-configuration/observability/metrics.md) for configuration details and available metrics. - -### Tracing (OTel) - -Distributed tracing helps understand request latency and flow through your system, including Traefik itself. - -*Example `values.yaml` addition:* - -```yaml -additionalArguments: - - "--tracing.otel=true" - - "--tracing.otel.grpcendpoint=otel-collector.observability:4317" # Adjust endpoint as needed - - "--tracing.otel.httpendpoint=otel-collector.observability:4318" # Adjust endpoint as needed -``` - -This enables OTel tracing and specifies the collector endpoint. Consult the [Tracing Documentation](../reference/install-configuration/observability/tracing.md) for details on OTel tracing. - -## Conclusion - -This setup establishes Traefik with secure dashboard access and HTTPS redirection, along with pointers to enable observability & TLS. - -{!traefik-for-business-applications.md!} diff --git a/docs/content/setup/swarm.md b/docs/content/setup/swarm.md deleted file mode 100644 index 1b097831d..000000000 --- a/docs/content/setup/swarm.md +++ /dev/null @@ -1,330 +0,0 @@ ---- -title: Setup Traefik Proxy in Docker Swarm -description: "Learn how to run Traefik v3 in Docker Swarm with HTTP/HTTPS entrypoints, redirects, a secured dashboard, self‑signed TLS, metrics, tracing, and access‑logs." ---- - -This guide provides an in‑depth walkthrough for installing and configuring Traefik Proxy as a **Swarm service** using `docker stack deploy`. It follows the same structure as the standalone‑Docker tutorial and covers: - -- Enable the [Swarm provider](../reference/install-configuration/providers/swarm.md) -- Expose **web** (HTTP :80) and **websecure** (HTTPS :443) entrypoints -- Redirect all HTTP traffic to HTTPS -- Secure the Traefik dashboard with **basic‑auth** -- Terminate TLS with a self‑signed certificate for `*.swarm.localhost` -- Deploy the **whoami** demo service -- Enable access‑logs and Prometheus metrics - -## Prerequisites - -- Docker Engine with **Swarm mode** initialised (`docker swarm init`) -- Docker Compose -- `openssl` -- `htpasswd` - -## Create a self‑signed certificate - -Before Traefik can serve HTTPS locally it needs a certificate. In production you’d use one from a trusted CA, but for a multi‑node dev swarm a quick self‑signed cert is enough: - -```bash -mkdir -p certs -openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ - -keyout certs/local.key -out certs/local.crt \ - -subj "/CN=*.swarm.localhost" -``` - -## Create the Traefik Dashboard Credentials - -Generate a hashed username / password pair that Traefik’s middleware will validate: - -```bash -htpasswd -nb admin "P@ssw0rd" | sed -e 's/\$/\$\$/g' -``` - -Copy the full output (e.g., `admin:$$apr1$$â€Ļ`) — we’ll paste it into the middleware label. - -## Create a docker‑compose‑swarm.yaml - -!!! note - Swarm uses `docker stack deploy`. The compose file can be named anything; we’ll use `docker‑compose‑swarm.yaml`. - -First, create a folder named **dynamic** and add **tls.yaml** for dynamic TLS configuration: - -```yaml -# dynamic/tls.yaml -tls: - certificates: - - certFile: /certs/local.crt - keyFile: /certs/local.key -``` - -In the same directory, create `docker‑compose‑swarm.yaml`: - -```yaml -services: - traefik: - image: traefik:v3.4 - - networks: - # Connect to the 'traefik_proxy' overlay network for inter-container communication across nodes - - traefik_proxy - - ports: - # Expose Traefik's entry points to the Swarm - # Swarm requires the long syntax for ports. - - target: 80 # Container port (Traefik web entry point) - published: 80 # Host port exposed on the nodes - protocol: tcp - # 'host' mode binds directly to the node's IP where the task runs. - # 'ingress' mode uses Swarm's Routing Mesh (load balances across nodes). - # Choose based on your load balancing strategy. 'host' is often simpler if using an external LB. - mode: host - - target: 443 # Container port ( Traefik websecure entry point) - published: 443 # Host port - protocol: tcp - mode: host - - volumes: - # Mount the Docker socket for the Swarm provider - # This MUST be run from a manager node to access the Swarm API via the socket. - - /var/run/docker.sock:/var/run/docker.sock:ro # Swarm API socket - - ./certs:/certs:ro - - ./dynamic:/dynamic:ro - - # Traefik Static configuration via command-line arguments - command: - # HTTP EntryPoint - - "--entrypoints.web.address=:80" - - # Configure HTTP to HTTPS Redirection - - "--entrypoints.web.http.redirections.entrypoint.to=websecure" - - "--entrypoints.web.http.redirections.entrypoint.scheme=https" - - "--entrypoints.web.http.redirections.entrypoint.permanent=true" - - # HTTPS EntryPoint - - "--entrypoints.websecure.address=:443" - - "--entrypoints.websecure.http.tls=true" - - # Attach dynamic TLS file - - "--providers.file.filename=/dynamic/tls.yaml" - - # Providers - - # Enable the Docker Swarm provider (instead of Docker provider) - - "--providers.swarm.endpoint=unix:///var/run/docker.sock" - - # Watch for Swarm service changes (requires socket access) - - "--providers.swarm.watch=true" - - # Recommended: Don't expose services by default; require explicit labels - - "--providers.swarm.exposedbydefault=false" - - # Specify the default network for Traefik to connect to services - - "--providers.swarm.network=traefik_traefik_proxy" - - # API & Dashboard - - "--api.dashboard=true" # Enable the dashboard - - "--api.insecure=false" # Explicitly disable insecure API mod - - # Observability - - "--log.level=INFO" # Set the Log Level e.g INFO, DEBUG - - "--accesslog=true" # Enable Access Logs - - "--metrics.prometheus=true" # Enable Prometheus - - deploy: - mode: replicated - replicas: 1 - placement: - - # Placement constraints restrict where Traefik tasks can run. - # Running on manager nodes is common for accessing the Swarm API via the socket. - constraints: - - node.role == manager - - # Traefik Dynamic configuration via labels - # In Swarm, labels on the service definition configure Traefik routing for that service. - labels: - - "traefik.enable=true" - - # Dashboard router - - "traefik.http.routers.dashboard.rule=Host(`dashboard.swarm.localhost`)" - - "traefik.http.routers.dashboard.entrypoints=websecure" - - "traefik.http.routers.dashboard.service=api@internal" - - "traefik.http.routers.dashboard.tls=true" - - # Basic‑auth middleware - - "traefik.http.middlewares.dashboard-auth.basicauth.users=" - - "traefik.http.routers.dashboard.middlewares=dashboard-auth@swarm" - - # Service hint - - "traefik.http.services.traefik.loadbalancer.server.port=8080" - - # Deploy the Whoami application - whoami: - image: traefik/whoami - networks: - - traefik_proxy - deploy: - labels: - # Enable Service discovery for Traefik - - "traefik.enable=true" - # Define the WHoami router rule - - "traefik.http.routers.whoami.rule=Host(`whoami.swarm.localhost`)" - # Expose Whoami on the HTTPS entrypoint - - "traefik.http.routers.whoami.entrypoints=websecure" - # Enable TLS - - "traefik.http.routers.whoami.tls=true" - # Expose the whoami port number to Traefik - - traefik.http.services.whoami.loadbalancer.server.port=80 - -# Define the overlay network for Swarm -networks: - traefik_proxy: - driver: overlay - attachable: true -``` - -!!! info - - Replace `` with the escaped hash from the previous step. - - The password hash is stored directly in a service label. This is fine for local development, but anyone with access to the Docker API can view it using `docker service inspect`. For production, use a more secure method to store secrets. - -## Launch the stack - -Create the overlay network once (if it doesn’t exist) and deploy: - -```bash -docker network create --driver overlay --attachable traefik_proxy || true -docker stack deploy -c docker-compose-swarm.yaml traefik -``` - -Swarm schedules the services on a manager node and binds ports 80/443. - -## Access the Dashboard - -Open **https://dashboard.swarm.localhost/** in your browser — the dashboard should prompt for the basic‑auth credentials you configured. - -![Traefik Dashboard](../assets/img/setup/traefik-dashboard-swarm.png) - -## Test the whoami Application - -You can test the application using curl: - -```bash -curl -k https://whoami.swarm.localhost/ -``` - -```bash -Hostname: whoami-76c9859cfc-k7jzs -IP: 127.0.0.1 -IP: ::1 -IP: 10.42.0.59 -IP: fe80::50d7:a2ff:fed5:2530 -RemoteAddr: 10.42.0.60:54148 -GET / HTTP/1.1 -Host: whoami.swarm.localhost -User-Agent: curl/8.7.1 -Accept: */* -Accept-Encoding: gzip -X-Forwarded-For: 10.42.0.1 -X-Forwarded-Host: whoami.swarm.localhost -X-Forwarded-Port: 443 -X-Forwarded-Proto: https -X-Forwarded-Server: traefik-644b7c67d9-f2tn9 -X-Real-Ip: 10.42.0.1 -``` - -Making the same request to the HTTP entrypoint will return the following: - -```bash -curl -k http://whoami.swarm.localhost - -Moved Permanently -``` - -Requesting the HTTP endpoint redirects to HTTPS, confirming the setup works. - -You can also open a browser and navigate to [https://whoami.swarm.localhost](https://whoami.swarm.localhost) to see a JSON dump from the service: - -![Whoami](../assets/img/setup/whoami-json-dump.png) - -### Other Key Configuration Areas - -Beyond this initial setup, Traefik offers extensive configuration possibilities. Here are brief introductions and minimal examples using Docker Compose `command` arguments or `labels`. Consult the main documentation linked for comprehensive details. - -#### TLS Certificate Management (Let’s Encrypt) - -To make the `websecure` entry point serve valid HTTPS certificates automatically, enable Let's Encrypt (ACME). - -```yaml -command: - # ... - - "--certificatesresolvers.le.acme.email=you@example.com" - - "--certificatesresolvers.le.acme.storage=/letsencrypt/acme.json" - - "--certificatesresolvers.le.acme.httpchallenge.entrypoint=web" - - "--entrypoints.websecure.http.tls.certresolver=le" -``` - -This defines a resolver named `le`, sets the required email and storage path (within the mounted `/letsencrypt` volume), and enables the HTTP challenge. Refer to the [HTTPS/TLS Documentation](../reference/install-configuration/tls/certificate-resolvers/overview.md) and [Let's Encrypt Documentation](../reference/install-configuration/tls/certificate-resolvers/acme.md) for details on challenges and DNS provider configuration. - -!!! note - - - Ensure the `/letsencrypt` path is on a **shared volume** or NFS so all nodes can read certificates. - - Ensure to mount the `/letsencrypt` volume in the `traefik` service in the `docker-compose-swarm.yaml` file. - -#### Metrics (Prometheus) - -You can expose Traefik's internal metrics for monitoring with Prometheus. We already enabled prometheus in our setup but we can further configure it. -*Example `command` additions:* - -```yaml -command: - # If using a dedicated metrics entry point, define it: - - "--entrypoints.metrics.address=:8082" - - - "--metrics.prometheus=true" - - # Optionally change the entry point metrics are exposed on (defaults to 'traefik') - - "--metrics.prometheus.entrypoint=metrics" - - # Add labels to metrics for routers/services (can increase cardinality) - - "--metrics.prometheus.addrouterslabels=true" -``` - -This enables the `/metrics` endpoint (typically accessed via the internal API port, often 8080 by default if not secured, or via a dedicated entry point). See the [Metrics Documentation](../reference/install-configuration/observability/metrics.md) for options. - -#### Tracing (OTel) - -You can enable distributed tracing to follow requests through Traefik. -*Example `command` additions:* - -```yaml -command: - # ... - - "--tracing.otel=true" - - "--tracing.otel.grpcendpoint=otel-collector:4317" -``` - -!!! note - This option requires a running OTEL collector accessible by Traefik. Consult the [Tracing Documentation](../reference/install-configuration/observability/tracing.md). - -#### Access Logs - -You can configure Traefik to log incoming requests for debugging and analysis. -*Example `command` additions:* - -```yaml -command: - # ... other command arguments ... - - "--accesslog=true" # Enable access logs to stdout - - # Optionally change format or output file (requires volume) - - "--accesslog.format=json" - - "--accesslog.filepath=/path/to/access.log" - - # Optionally filter logs - - "--accesslog.filters.statuscodes=400-599" -``` - -### Conclusion - -You now have Traefik running on Docker Swarm with HTTPS, a secured dashboard, automatic HTTP → HTTPS redirects, and foundational observability. Expand this stack with Let’s Encrypt, additional middlewares, or multiple Traefik replicas as your Swarm grows. - -{!traefik-for-business-applications.md!} diff --git a/docs/content/user-guides/cert-manager.md b/docs/content/user-guides/cert-manager.md index fbb90d918..f776ea0fa 100644 --- a/docs/content/user-guides/cert-manager.md +++ b/docs/content/user-guides/cert-manager.md @@ -56,11 +56,11 @@ The certificates can then be used in an Ingress / IngressRoute / HTTPRoute. ``` Let's see now how to use it with the various Kubernetes providers of Traefik Proxy. -The enabled providers can be seen on the [dashboard](../reference/install-configuration/api-dashboard.md) of Traefik Proxy and also in the INFO logs when Traefik Proxy starts. +The enabled providers can be seen on the [dashboard](../../operations/dashboard/) of Traefik Proxy and also in the INFO logs when Traefik Proxy starts. ### With an Ingress -To use this certificate with an Ingress, the [Kubernetes Ingress](../providers/kubernetes-ingress.md) provider has to be enabled. +To use this certificate with an Ingress, the [Kubernetes Ingress](../../providers/kubernetes-ingress/) provider has to be enabled. !!! info Traefik Helm Chart @@ -94,7 +94,7 @@ To use this certificate with an Ingress, the [Kubernetes Ingress](../providers/k ### With an IngressRoute -To use this certificate with an IngressRoute, the [Kubernetes CRD](../providers/kubernetes-crd.md) provider has to be enabled. +To use this certificate with an IngressRoute, the [Kubernetes CRD](../../providers/kubernetes-crd) provider has to be enabled. !!! info Traefik Helm Chart @@ -124,7 +124,7 @@ To use this certificate with an IngressRoute, the [Kubernetes CRD](../providers/ ### With an HTTPRoute -To use this certificate with an HTTPRoute, the [Kubernetes Gateway](../routing/providers/kubernetes-gateway.md) provider has to be enabled. +To use this certificate with an HTTPRoute, the [Kubernetes Gateway](../../routing/providers/kubernetes-gateway) provider has to be enabled. !!! info Traefik Helm Chart diff --git a/docs/content/user-guides/crd-acme/03-deployments.yml b/docs/content/user-guides/crd-acme/03-deployments.yml index b2d15b3b7..079d0ab41 100644 --- a/docs/content/user-guides/crd-acme/03-deployments.yml +++ b/docs/content/user-guides/crd-acme/03-deployments.yml @@ -26,7 +26,7 @@ spec: serviceAccountName: traefik-ingress-controller containers: - name: traefik - image: traefik:v3.5 + image: traefik:v3.4 args: - --api.insecure - --accesslog diff --git a/docs/content/user-guides/crd-acme/index.md b/docs/content/user-guides/crd-acme/index.md index 49c9db013..fc2492533 100644 --- a/docs/content/user-guides/crd-acme/index.md +++ b/docs/content/user-guides/crd-acme/index.md @@ -49,10 +49,10 @@ and the RBAC authorization resources which will be referenced through the `servi ```bash # Install Traefik Resource Definitions: -kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.5/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml +kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.4/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml # Install RBAC for Traefik: -kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.5/docs/content/reference/dynamic-configuration/kubernetes-crd-rbac.yml +kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.4/docs/content/reference/dynamic-configuration/kubernetes-crd-rbac.yml ``` ### Services @@ -60,7 +60,7 @@ kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.5/docs/con Then, the services. One for Traefik itself, and one for the app it routes for, i.e. in this case our demo HTTP server: [whoami](https://github.com/traefik/whoami). ```bash -kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.5/docs/content/user-guides/crd-acme/02-services.yml +kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.4/docs/content/user-guides/crd-acme/02-services.yml ``` ```yaml @@ -73,7 +73,7 @@ Next, the deployments, i.e. the actual pods behind the services. Again, one pod for Traefik, and one for the whoami app. ```bash -kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.5/docs/content/user-guides/crd-acme/03-deployments.yml +kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.4/docs/content/user-guides/crd-acme/03-deployments.yml ``` ```yaml @@ -100,7 +100,7 @@ Look it up. We can now finally apply the actual ingressRoutes, with: ```bash -kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.5/docs/content/user-guides/crd-acme/04-ingressroutes.yml +kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.4/docs/content/user-guides/crd-acme/04-ingressroutes.yml ``` ```yaml @@ -126,7 +126,7 @@ Nowadays, TLS v1.0 and v1.1 are deprecated. In order to force TLS v1.2 or later on all your IngressRoute, you can define the `default` TLSOption: ```bash -kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.5/docs/content/user-guides/crd-acme/05-tlsoption.yml +kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.4/docs/content/user-guides/crd-acme/05-tlsoption.yml ``` ```yaml diff --git a/docs/content/user-guides/crd-acme/k3s.yml b/docs/content/user-guides/crd-acme/k3s.yml index 23190c461..10171f561 100644 --- a/docs/content/user-guides/crd-acme/k3s.yml +++ b/docs/content/user-guides/crd-acme/k3s.yml @@ -26,5 +26,5 @@ node: - K3S_CLUSTER_SECRET=somethingtotallyrandom volumes: # this is where you would place a alternative traefik image (saved as a .tar file with - # 'docker save'), if you want to use it, instead of the traefik:v3.5 image. + # 'docker save'), if you want to use it, instead of the traefik:v3.4 image. - /somewhere/on/your/host/custom-image:/var/lib/rancher/k3s/agent/images diff --git a/docs/content/user-guides/docker-compose/acme-dns/docker-compose.yml b/docs/content/user-guides/docker-compose/acme-dns/docker-compose.yml index 2bcb322e6..d276dc41e 100644 --- a/docs/content/user-guides/docker-compose/acme-dns/docker-compose.yml +++ b/docs/content/user-guides/docker-compose/acme-dns/docker-compose.yml @@ -1,7 +1,9 @@ +version: "3.3" + services: traefik: - image: "traefik:v3.5" + image: "traefik:v3.4" container_name: "traefik" command: #- "--log.level=DEBUG" diff --git a/docs/content/user-guides/docker-compose/acme-dns/docker-compose_secrets.yml b/docs/content/user-guides/docker-compose/acme-dns/docker-compose_secrets.yml index 63487e067..ce629d8f2 100644 --- a/docs/content/user-guides/docker-compose/acme-dns/docker-compose_secrets.yml +++ b/docs/content/user-guides/docker-compose/acme-dns/docker-compose_secrets.yml @@ -1,3 +1,5 @@ +version: "3.3" + secrets: ovh_endpoint: file: "./secrets/ovh_endpoint.secret" @@ -11,7 +13,7 @@ secrets: services: traefik: - image: "traefik:v3.5" + image: "traefik:v3.4" container_name: "traefik" command: #- "--log.level=DEBUG" diff --git a/docs/content/user-guides/docker-compose/acme-dns/index.md b/docs/content/user-guides/docker-compose/acme-dns/index.md index 78f25f8d4..e626c633a 100644 --- a/docs/content/user-guides/docker-compose/acme-dns/index.md +++ b/docs/content/user-guides/docker-compose/acme-dns/index.md @@ -6,7 +6,7 @@ description: "Learn how to create a certificate with the Let's Encrypt DNS chall # Docker-compose with Let's Encrypt: DNS Challenge This guide aims to demonstrate how to create a certificate with the Let's Encrypt DNS challenge to use https on a simple service exposed with Traefik. -Please also read the [basic example](../basic-example/) for details on how to expose such a service. +Please also read the [basic example](../basic-example) for details on how to expose such a service. ## Prerequisite diff --git a/docs/content/user-guides/docker-compose/acme-http/docker-compose.yml b/docs/content/user-guides/docker-compose/acme-http/docker-compose.yml index 3deb01ca2..82c248a68 100644 --- a/docs/content/user-guides/docker-compose/acme-http/docker-compose.yml +++ b/docs/content/user-guides/docker-compose/acme-http/docker-compose.yml @@ -1,7 +1,9 @@ +version: "3.3" + services: traefik: - image: "traefik:v3.5" + image: "traefik:v3.4" container_name: "traefik" command: #- "--log.level=DEBUG" diff --git a/docs/content/user-guides/docker-compose/acme-http/index.md b/docs/content/user-guides/docker-compose/acme-http/index.md index 5f60c077f..5f9b90565 100644 --- a/docs/content/user-guides/docker-compose/acme-http/index.md +++ b/docs/content/user-guides/docker-compose/acme-http/index.md @@ -6,7 +6,7 @@ description: "Learn how to create a certificate with the Let's Encrypt HTTP chal # Docker-compose with Let's Encrypt : HTTP Challenge This guide aims to demonstrate how to create a certificate with the Let's Encrypt HTTP challenge to use https on a simple service exposed with Traefik. -Please also read the [basic example](../basic-example/) for details on how to expose such a service. +Please also read the [basic example](../basic-example) for details on how to expose such a service. ## Prerequisite diff --git a/docs/content/user-guides/docker-compose/acme-tls/docker-compose.yml b/docs/content/user-guides/docker-compose/acme-tls/docker-compose.yml index c95bb5eb9..e3faaa3ce 100644 --- a/docs/content/user-guides/docker-compose/acme-tls/docker-compose.yml +++ b/docs/content/user-guides/docker-compose/acme-tls/docker-compose.yml @@ -1,7 +1,9 @@ +version: "3.3" + services: traefik: - image: "traefik:v3.5" + image: "traefik:v3.4" container_name: "traefik" command: #- "--log.level=DEBUG" diff --git a/docs/content/user-guides/docker-compose/acme-tls/index.md b/docs/content/user-guides/docker-compose/acme-tls/index.md index 942d17cc1..382244c62 100644 --- a/docs/content/user-guides/docker-compose/acme-tls/index.md +++ b/docs/content/user-guides/docker-compose/acme-tls/index.md @@ -6,7 +6,7 @@ description: "Learn how to create a certificate with the Let's Encrypt TLS chall # Docker-compose with Let's Encrypt: TLS Challenge This guide aims to demonstrate how to create a certificate with the Let's Encrypt TLS challenge to use https on a simple service exposed with Traefik. -Please also read the [basic example](../basic-example/) for details on how to expose such a service. +Please also read the [basic example](../basic-example) for details on how to expose such a service. ## Prerequisite diff --git a/docs/content/user-guides/docker-compose/basic-example/docker-compose.yml b/docs/content/user-guides/docker-compose/basic-example/docker-compose.yml index 842478ac8..3e9f68fa7 100644 --- a/docs/content/user-guides/docker-compose/basic-example/docker-compose.yml +++ b/docs/content/user-guides/docker-compose/basic-example/docker-compose.yml @@ -1,7 +1,9 @@ +version: "3.3" + services: traefik: - image: "traefik:v3.5" + image: "traefik:v3.4" container_name: "traefik" command: #- "--log.level=DEBUG" diff --git a/docs/content/user-guides/docker-compose/basic-example/index.md b/docs/content/user-guides/docker-compose/basic-example/index.md index 6aef0c804..f6d395b4a 100644 --- a/docs/content/user-guides/docker-compose/basic-example/index.md +++ b/docs/content/user-guides/docker-compose/basic-example/index.md @@ -23,13 +23,15 @@ Create a `docker-compose.yml` file with the following content: You can use a [pre-existing network](https://docs.docker.com/compose/networking/#use-a-pre-existing-network "Link to Docker Compose networking docs") too. ```yaml + version: "3.3" + networks: traefiknet: {} services: traefik: - image: "traefik:v3.5" + image: "traefik:v3.4" ... networks: - traefiknet diff --git a/docs/content/user-guides/websocket.md b/docs/content/user-guides/websocket.md deleted file mode 100644 index cb122d90f..000000000 --- a/docs/content/user-guides/websocket.md +++ /dev/null @@ -1,355 +0,0 @@ ---- -title: "Traefik WebSocket Documentation" -description: "How to configure WebSocket and WebSocket Secure (WSS) connections with Traefik Proxy." ---- - -# WebSocket - -Configuring Traefik to handle WebSocket and WebSocket Secure (WSS) connections. -{: .subtitle } - -## Overview - -WebSocket is a communication protocol that provides full-duplex communication channels over a single TCP connection. -WebSocket Secure (WSS) is the encrypted version of WebSocket, using TLS/SSL encryption. - -Traefik supports WebSocket and WebSocket Secure (WSS) out of the box. This guide will walk through examples of how to configure Traefik for different WebSocket scenarios. - -## Basic WebSocket Configuration - -A basic WebSocket configuration only requires defining a router and a service that points to your WebSocket server. - -```yaml tab="Docker & Swarm" -labels: - - "traefik.http.routers.my-websocket.rule=Host(`ws.example.com`)" - - "traefik.http.routers.my-websocket.service=my-websocket-service" - - "traefik.http.services.my-websocket-service.loadbalancer.server.port=8000" -``` - -```yaml tab="Kubernetes" -apiVersion: traefik.io/v1alpha1 -kind: IngressRoute -metadata: - name: my-websocket-route -spec: - entryPoints: - - web - routes: - - match: Host(`ws.example.com`) - kind: Rule - services: - - name: my-websocket-service - port: 8000 -``` - -```yaml tab="File (YAML)" -http: - routers: - my-websocket: - rule: "Host(`ws.example.com`)" - service: my-websocket-service - - services: - my-websocket-service: - loadBalancer: - servers: - - url: "http://my-websocket-server:8000" -``` - -```toml tab="File (TOML)" -[http.routers] - [http.routers.my-websocket] - rule = "Host(`ws.example.com`)" - service = "my-websocket-service" - -[http.services] - [http.services.my-websocket-service] - [http.services.my-websocket-service.loadBalancer] - [[http.services.my-websocket-service.loadBalancer.servers]] - url = "http://my-websocket-server:8000" -``` - -## WebSocket Secure (WSS) Configuration - -WebSocket Secure (WSS) requires TLS configuration. -The client connects using the `wss://` protocol instead of `ws://`. - -```yaml tab="Docker & Swarm" -labels: - - "traefik.http.routers.my-websocket-secure.rule=Host(`wss.example.com`)" - - "traefik.http.routers.my-websocket-secure.service=my-websocket-service" - - "traefik.http.routers.my-websocket-secure.tls=true" - - "traefik.http.services.my-websocket-service.loadbalancer.server.port=8000" -``` - -```yaml tab="Kubernetes" -apiVersion: traefik.io/v1alpha1 -kind: IngressRoute -metadata: - name: my-websocket-secure-route -spec: - entryPoints: - - websecure - routes: - - match: Host(`wss.example.com`) - kind: Rule - services: - - name: my-websocket-service - port: 8000 - tls: {} -``` - -```yaml tab="File (YAML)" -http: - routers: - my-websocket-secure: - rule: "Host(`wss.example.com`)" - service: my-websocket-service - tls: {} - - services: - my-websocket-service: - loadBalancer: - servers: - - url: "http://my-websocket-server:8000" -``` - -```toml tab="File (TOML)" -[http.routers] - [http.routers.my-websocket-secure] - rule = "Host(`wss.example.com`)" - service = "my-websocket-service" - [http.routers.my-websocket-secure.tls] - -[http.services] - [http.services.my-websocket-service] - [http.services.my-websocket-service.loadBalancer] - [[http.services.my-websocket-service.loadBalancer.servers]] - url = "http://my-websocket-server:8000" -``` - -## SSL Termination for WebSockets - -In this scenario, clients connect to Traefik using WSS (encrypted), but Traefik connects to your backend server using WS (unencrypted). -This is called SSL termination. - -```yaml tab="Docker & Swarm" -labels: - - "traefik.http.routers.my-wss-termination.rule=Host(`wss.example.com`)" - - "traefik.http.routers.my-wss-termination.service=my-ws-service" - - "traefik.http.routers.my-wss-termination.tls=true" - - "traefik.http.services.my-ws-service.loadbalancer.server.port=8000" -``` - -```yaml tab="Kubernetes" -apiVersion: traefik.io/v1alpha1 -kind: IngressRoute -metadata: - name: my-wss-termination-route -spec: - entryPoints: - - websecure - routes: - - match: Host(`wss.example.com`) - kind: Rule - services: - - name: my-ws-service - port: 8000 - tls: {} -``` - -```yaml tab="File (YAML)" -http: - routers: - my-wss-termination: - rule: "Host(`wss.example.com`)" - service: my-ws-service - tls: {} - - services: - my-ws-service: - loadBalancer: - servers: - - url: "http://my-ws-server:8000" -``` - -```toml tab="File (TOML)" -[http.routers] - [http.routers.my-wss-termination] - rule = "Host(`wss.example.com`)" - service = "my-ws-service" - [http.routers.my-wss-termination.tls] - -[http.services] - [http.services.my-ws-service] - [http.services.my-ws-service.loadBalancer] - [[http.services.my-ws-service.loadBalancer.servers]] - url = "http://my-ws-server:8000" -``` - -## End-to-End WebSocket Secure (WSS) - -For end-to-end encryption, Traefik can be configured to connect to your backend using HTTPS. - -```yaml tab="Docker & Swarm" -labels: - - "traefik.http.routers.my-wss-e2e.rule=Host(`wss.example.com`)" - - "traefik.http.routers.my-wss-e2e.service=my-wss-service" - - "traefik.http.routers.my-wss-e2e.tls=true" - - "traefik.http.services.my-wss-service.loadbalancer.server.port=8443" - # If the backend uses a self-signed certificate - - "traefik.http.serversTransports.insecureTransport.insecureSkipVerify=true" - - "traefik.http.services.my-wss-service.loadBalancer.serversTransport=insecureTransport" -``` - -```yaml tab="Kubernetes" -apiVersion: traefik.io/v1alpha1 -kind: ServersTransport -metadata: - name: insecure-transport -spec: - insecureSkipVerify: true - ---- -apiVersion: traefik.io/v1alpha1 -kind: IngressRoute -metadata: - name: my-wss-e2e-route -spec: - entryPoints: - - websecure - routes: - - match: Host(`wss.example.com`) - kind: Rule - services: - - name: my-wss-service - port: 8443 - serversTransport: insecure-transport - tls: {} -``` - -```yaml tab="File (YAML)" -http: - serversTransports: - insecureTransport: - insecureSkipVerify: true - - routers: - my-wss-e2e: - rule: "Host(`wss.example.com`)" - service: my-wss-service - tls: {} - - services: - my-wss-service: - loadBalancer: - serversTransport: insecureTransport - servers: - - url: "https://my-wss-server:8443" -``` - -```toml tab="File (TOML)" -[http.serversTransports] - [http.serversTransports.insecureTransport] - insecureSkipVerify = true - -[http.routers] - [http.routers.my-wss-e2e] - rule = "Host(`wss.example.com`)" - service = "my-wss-service" - [http.routers.my-wss-e2e.tls] - -[http.services] - [http.services.my-wss-service] - [http.services.my-wss-service.loadBalancer] - serversTransport = "insecureTransport" - [[http.services.my-wss-service.loadBalancer.servers]] - url = "https://my-wss-server:8443" -``` - -## EntryPoints Configuration for WebSockets - -In your Traefik static configuration, you'll need to define entryPoints for both WS and WSS: - -```yaml tab="File (YAML)" -entryPoints: - web: - address: ":80" - websecure: - address: ":443" -``` - -```toml tab="File (TOML)" -[entryPoints] - [entryPoints.web] - address = ":80" - [entryPoints.websecure] - address = ":443" -``` - -## Testing WebSocket Connections - -You can test your WebSocket configuration using various tools: - -1. Browser Developer Tools: Most modern browsers include WebSocket debugging in their developer tools. -2. WebSocket client tools like [wscat](https://github.com/websockets/wscat) or online tools like [Piesocket's WebSocket Tester](https://www.piesocket.com/websocket-tester). - -Example wscat commands: - -```bash -# Test standard WebSocket -wscat -c ws://ws.example.com - -# Test WebSocket Secure -wscat -c wss://wss.example.com -``` - -## Common Issues and Solutions - -### Headers and Origin Checks - -Some WebSocket servers implement origin checking. Traefik passes the original headers to your backend, including the `Origin` header. - -If you need to manipulate headers for WebSocket connections, you can use Traefik's Headers middleware: - -```yaml tab="Docker & Swarm" -labels: - - "traefik.http.middlewares.my-headers.headers.customrequestheaders.Origin=https://allowed-origin.com" - - "traefik.http.routers.my-websocket.middlewares=my-headers" -``` - -```yaml tab="Kubernetes" -apiVersion: traefik.io/v1alpha1 -kind: Middleware -metadata: - name: my-headers -spec: - headers: - customRequestHeaders: - Origin: "https://allowed-origin.com" - ---- -apiVersion: traefik.io/v1alpha1 -kind: IngressRoute -metadata: - name: my-websocket-route -spec: - routes: - - match: Host(`ws.example.com`) - kind: Rule - middlewares: - - name: my-headers - services: - - name: my-websocket-service - port: 8000 -``` - -### Certificate Issues with WSS - -If you're experiencing certificate issues with WSS: - -1. Ensure your certificates are valid and not expired -2. For testing with self-signed certificates, configure your clients to accept them -3. When using Let's Encrypt, ensure your domain is properly configured - -For backends with self-signed certificates, use the `insecureSkipVerify` option in the ServersTransport configuration as shown in the examples above. diff --git a/docs/docs.Dockerfile b/docs/docs.Dockerfile index 99e963cf4..e15440a36 100644 --- a/docs/docs.Dockerfile +++ b/docs/docs.Dockerfile @@ -1,4 +1,4 @@ -FROM alpine:3.22 +FROM alpine:3.21 ENV PATH="${PATH}:/venv/bin" diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index dca15910d..fe10e3740 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -33,132 +33,12 @@ extra_javascript: - assets/js/hljs/highlight.pack.js # Download from https://highlightjs.org/download/ and enable YAML, TOML and Dockerfile - assets/js/extra.js -extra_css: - - assets/css/menu-icons.css - plugins: - search - exclude: glob: - "**/include-*.md" - - redirects: - redirect_maps: - # Providers - 'providers/overview.md': 'reference/install-configuration/providers/overview.md' - 'providers/docker.md': 'reference/install-configuration/providers/docker.md' - 'providers/swarm.md': 'reference/install-configuration/providers/swarm.md' - 'providers/kubernetes-crd.md': 'reference/install-configuration/providers/kubernetes/kubernetes-crd.md' - 'providers/kubernetes-ingress.md': 'reference/install-configuration/providers/kubernetes/kubernetes-ingress.md' - 'providers/kubernetes-gateway.md': 'reference/install-configuration/providers/kubernetes/kubernetes-gateway.md' - 'providers/consul-catalog.md': 'reference/install-configuration/providers/hashicorp/consul-catalog.md' - 'providers/nomad.md': 'reference/install-configuration/providers/hashicorp/nomad.md' - 'providers/ecs.md': 'reference/install-configuration/providers/others/ecs.md' - 'providers/file.md': 'reference/install-configuration/providers/others/file.md' - 'providers/consul.md': 'reference/install-configuration/providers/kv/consul.md' - 'providers/etcd.md': 'reference/install-configuration/providers/kv/etcd.md' - 'providers/zookeeper.md': 'reference/install-configuration/providers/kv/zk.md' - 'providers/redis.md': 'reference/install-configuration/providers/kv/redis.md' - 'providers/http.md': 'reference/install-configuration/providers/others/http.md' - # Routing - 'routing/overview.md': 'reference/routing-configuration/dynamic-configuration-methods.md' - 'routing/entrypoints.md': 'reference/install-configuration/entrypoints.md' - 'routing/routers/index.md': 'reference/routing-configuration/http/routing/rules-and-priority.md' - 'routing/services/index.md': 'reference/routing-configuration/http/load-balancing/service.md' - 'routing/providers/docker.md': 'reference/routing-configuration/other-providers/docker.md' - 'routing/providers/swarm.md': 'reference/routing-configuration/other-providers/swarm.md' - 'routing/providers/kubernetes-crd.md': 'reference/routing-configuration/kubernetes/crd/http/ingressroute.md' - 'routing/providers/kubernetes-ingress.md': 'reference/routing-configuration/kubernetes/ingress.md' - 'routing/providers/kubernetes-gateway.md': 'reference/routing-configuration/kubernetes/gateway-api.md' - 'routing/providers/consul-catalog.md': 'reference/routing-configuration/other-providers/consul-catalog.md' - 'routing/providers/nomad.md': 'reference/routing-configuration/other-providers/nomad.md' - 'routing/providers/ecs.md': 'reference/routing-configuration/other-providers/ecs.md' - 'routing/providers/kv.md': 'reference/routing-configuration/other-providers/kv.md' - # Observability - 'observability/overview.md': 'observe/overview.md' - 'observability/logs.md': 'reference/install-configuration/observability/logs-and-accesslogs.md' - 'observability/access-logs.md': 'reference/install-configuration/observability/logs-and-accesslogs.md' - 'observability/metrics/overview.md': 'reference/install-configuration/observability/metrics.md' - 'observability/metrics/datadog.md': 'reference/install-configuration/observability/metrics.md' - 'observability/metrics/influxdb2.md': 'reference/install-configuration/observability/metrics.md' - 'observability/metrics/opentelemetry.md': 'reference/install-configuration/observability/metrics.md' - 'observability/metrics/prometheus.md': 'reference/install-configuration/observability/metrics.md' - 'observability/metrics/statsd.md': 'reference/install-configuration/observability/metrics.md' - 'observability/tracing/overview.md': 'reference/install-configuration/observability/tracing.md' - 'observability/tracing/opentelemetry.md': 'reference/install-configuration/observability/tracing.md' - # Operations - 'operations/cli.md': 'reference/install-configuration/observability/healthcheck.md' - 'operations/dashboard.md': 'reference/install-configuration/api-dashboard.md' - 'operations/api.md': 'reference/install-configuration/api-dashboard.md' - 'operations/ping.md': 'reference/install-configuration/observability/healthcheck.md' - # HTTPS & TLS - 'https/overview.md': 'reference/routing-configuration/http/tls/overview.md' - 'https/tls.md': 'reference/routing-configuration/http/tls/tls-certificates.md' - 'https/acme.md': 'reference/install-configuration/tls/certificate-resolvers/acme.md' - 'https/tailscale.md': 'reference/install-configuration/tls/certificate-resolvers/tailscale.md' - 'https/spiffe.md': 'reference/install-configuration/tls/spiffe.md' - # Middlewares - 'middlewares/overview.md': 'reference/routing-configuration/http/middlewares/overview.md' - # HTTP - 'middlewares/http/overview.md': 'reference/routing-configuration/http/middlewares/overview.md' - 'middlewares/http/addprefix.md': 'reference/routing-configuration/http/middlewares/addprefix.md' - 'middlewares/http/basicauth.md': 'reference/routing-configuration/http/middlewares/basicauth.md' - 'middlewares/http/buffering.md': 'reference/routing-configuration/http/middlewares/buffering.md' - 'middlewares/http/chain.md': 'reference/routing-configuration/http/middlewares/chain.md' - 'middlewares/http/circuitbreaker.md': 'reference/routing-configuration/http/middlewares/circuitbreaker.md' - 'middlewares/http/compress.md': 'reference/routing-configuration/http/middlewares/compress.md' - 'middlewares/http/contenttype.md': 'reference/routing-configuration/http/middlewares/contenttype.md' - 'middlewares/http/digestauth.md': 'reference/routing-configuration/http/middlewares/digestauth.md' - 'middlewares/http/errorpages.md': 'reference/routing-configuration/http/middlewares/errorpages.md' - 'middlewares/http/forwardauth.md': 'reference/routing-configuration/http/middlewares/forwardauth.md' - 'middlewares/http/grpcweb.md': 'reference/routing-configuration/http/middlewares/grpcweb.md' - 'middlewares/http/headers.md': 'reference/routing-configuration/http/middlewares/headers.md' - 'middlewares/http/ipwhitelist.md': 'reference/routing-configuration/http/middlewares/ipallowlist.md' - 'middlewares/http/ipallowlist.md': 'reference/routing-configuration/http/middlewares/ipallowlist.md' - 'middlewares/http/inflightreq.md': 'reference/routing-configuration/http/middlewares/inflightreq.md' - 'middlewares/http/passtlsclientcert.md': 'reference/routing-configuration/http/middlewares/passtlsclientcert.md' - 'middlewares/http/ratelimit.md': 'reference/routing-configuration/http/middlewares/ratelimit.md' - 'middlewares/http/redirectregex.md': 'reference/routing-configuration/http/middlewares/redirectregex.md' - 'middlewares/http/redirectscheme.md': 'reference/routing-configuration/http/middlewares/redirectscheme.md' - 'middlewares/http/replacepath.md': 'reference/routing-configuration/http/middlewares/replacepath.md' - 'middlewares/http/replacepathregex.md': 'reference/routing-configuration/http/middlewares/replacepathregex.md' - 'middlewares/http/retry.md': 'reference/routing-configuration/http/middlewares/retry.md' - 'middlewares/http/stripprefix.md': 'reference/routing-configuration/http/middlewares/stripprefix.md' - 'middlewares/http/stripprefixregex.md': 'reference/routing-configuration/http/middlewares/stripprefixregex.md' - # TCP - 'middlewares/tcp/overview.md': 'reference/routing-configuration/tcp/middlewares/overview.md' - 'middlewares/tcp/inflightconn.md': 'reference/routing-configuration/tcp/middlewares/inflightconn.md' - 'middlewares/tcp/ipwhitelist.md': 'reference/routing-configuration/tcp/middlewares/ipallowlist.md' - 'middlewares/tcp/ipallowlist.md': 'reference/routing-configuration/tcp/middlewares/ipallowlist.md' - ## User Guides - 'user-guides/crd-acme/index.md': 'expose/kubernetes.md' - 'user-guides/cert-manager.md': 'expose/kubernetes.md' - 'user-guides/docker-compose/basic-example/index.md': 'expose/docker.md' - 'user-guides/docker-compose/acme-tls/index.md': 'expose/docker.md' - 'user-guides/docker-compose/acme-http/index.md': 'expose/docker.md' - 'user-guides/docker-compose/acme-dns/index.md': 'expose/docker.md' - # References - # Static Configuration - 'reference/static-configuration/overview.md': 'reference/install-configuration/configuration-options.md' - 'reference/static-configuration/file.md': 'reference/install-configuration/configuration-options.md' - 'reference/static-configuration/cli.md': 'reference/install-configuration/configuration-options.md' - 'reference/static-configuration/env.md': 'reference/install-configuration/configuration-options.md' - # Dynamic Configuration - 'reference/dynamic-configuration/file.md': 'reference/routing-configuration/other-providers/file.md' - 'reference/dynamic-configuration/docker.md': 'reference/routing-configuration/other-providers/docker.md' - 'reference/dynamic-configuration/kubernetes-crd.md': 'reference/routing-configuration/kubernetes/crd/http/ingressroute.md' - 'reference/dynamic-configuration/kubernetes-gateway.md': 'reference/routing-configuration/kubernetes/gateway-api.md' - 'reference/dynamic-configuration/consul-catalog.md': 'reference/routing-configuration/other-providers/consul-catalog.md' - "reference/dynamic-configuration/nomad.md": 'reference/routing-configuration/other-providers/nomad.md' - 'reference/dynamic-configuration/ecs.md': 'reference/routing-configuration/other-providers/ecs.md' - 'reference/dynamic-configuration/kv.md': 'reference/routing-configuration/other-providers/kv.md' - ## Plugins - 'plugins/index.md': "extend/extend-traefik.md" - ## Migration - 'migration/v3.md': 'migrate/v3.md' - 'migration/v2-to-v3.md': 'migrate/v2-to-v3.md' - 'migration/v2-to-v3-details.md': 'migrate/v2-to-v3-details.md' - 'migration/v2.md': 'migrate/v2.md' - 'migration/v1-to-v2.md': 'migrate/v1-to-v2.md' + # https://squidfunk.github.io/mkdocs-material/extensions/admonition/ # https://facelessuser.github.io/pymdown-extensions/ markdown_extensions: @@ -186,38 +66,134 @@ markdown_extensions: nav: - 'What is Traefik': 'index.md' - 'Getting Started': - - 'Overview': 'getting-started/index.md' + - 'Concepts' : 'getting-started/concepts.md' - 'Quick Start': - - 'Kubernetes': 'getting-started/kubernetes.md' - - 'Docker': 'getting-started/docker.md' + - 'Docker': 'getting-started/quick-start.md' + - 'Kubernetes': 'getting-started/quick-start-with-kubernetes.md' - 'Configuration Introduction': 'getting-started/configuration-overview.md' - - 'Setup': - - 'Kubernetes': 'setup/kubernetes.md' - - 'Docker': 'setup/docker.md' - - 'Swarm': 'setup/swarm.md' - - 'Expose': - - 'Overview': 'expose/overview.md' - - 'Kubernetes': 'expose/kubernetes.md' - - 'Docker': 'expose/docker.md' - - 'Swarm': 'expose/swarm.md' - - 'Secure': - - 'Secure Access with JWT Traefik Hub API Gateway': 'secure/secure-api-access-with-jwt.md' - - 'Secure Access with OIDC Traefik Hub API Gateway': 'secure/secure-api-access-with-oidc.md' - - 'Secure Access with a WAF Traefik Hub API Gateway': 'secure/secure-api-access-with-waf.md' - - 'Observe': - - 'Overview': 'observe/overview.md' - - 'Logs & Access Logs': 'observe/logs-and-access-logs.md' - - 'Metrics': 'observe/metrics.md' - - 'Tracing': 'observe/tracing.md' - - 'Extend': 'extend/extend-traefik.md' - - 'Govern Traefik Hub API Gateway': 'govern/index.md' - - 'Migrate': - - 'Traefik v3 minor migrations': 'migrate/v3.md' + - 'Install Traefik': 'getting-started/install-traefik.md' + - 'Frequently Asked Questions': 'getting-started/faq.md' + - 'Configuration Discovery': + - 'Overview': 'providers/overview.md' + - 'Docker': 'providers/docker.md' + - 'Swarm': 'providers/swarm.md' + - 'Kubernetes IngressRoute': 'providers/kubernetes-crd.md' + - 'Kubernetes Ingress': 'providers/kubernetes-ingress.md' + - 'Kubernetes Gateway API': 'providers/kubernetes-gateway.md' + - 'Consul Catalog': 'providers/consul-catalog.md' + - 'Nomad': 'providers/nomad.md' + - 'ECS': 'providers/ecs.md' + - 'File': 'providers/file.md' + - 'Consul': 'providers/consul.md' + - 'Etcd': 'providers/etcd.md' + - 'ZooKeeper': 'providers/zookeeper.md' + - 'Redis': 'providers/redis.md' + - 'HTTP': 'providers/http.md' + - 'Routing & Load Balancing': + - 'Overview': 'routing/overview.md' + - 'EntryPoints': 'routing/entrypoints.md' + - 'Routers': 'routing/routers/index.md' + - 'Services': 'routing/services/index.md' + - 'Providers': + - 'Docker': 'routing/providers/docker.md' + - 'Swarm': 'routing/providers/swarm.md' + - 'Kubernetes IngressRoute': 'routing/providers/kubernetes-crd.md' + - 'Kubernetes Ingress': 'routing/providers/kubernetes-ingress.md' + - 'Kubernetes Gateway API': 'routing/providers/kubernetes-gateway.md' + - 'Consul Catalog': 'routing/providers/consul-catalog.md' + - 'Nomad': 'routing/providers/nomad.md' + - 'ECS': 'routing/providers/ecs.md' + - 'KV': 'routing/providers/kv.md' + - 'HTTPS & TLS': + - 'Overview': 'https/overview.md' + - 'TLS': 'https/tls.md' + - 'Let''s Encrypt': 'https/acme.md' + - 'Tailscale': 'https/tailscale.md' + - 'SPIFFE': 'https/spiffe.md' + - 'Middlewares': + - 'Overview': 'middlewares/overview.md' + - 'HTTP': + - 'Overview': 'middlewares/http/overview.md' + - 'AddPrefix': 'middlewares/http/addprefix.md' + - 'BasicAuth': 'middlewares/http/basicauth.md' + - 'Buffering': 'middlewares/http/buffering.md' + - 'Chain': 'middlewares/http/chain.md' + - 'CircuitBreaker': 'middlewares/http/circuitbreaker.md' + - 'Compress': 'middlewares/http/compress.md' + - 'ContentType': 'middlewares/http/contenttype.md' + - 'DigestAuth': 'middlewares/http/digestauth.md' + - 'Errors': 'middlewares/http/errorpages.md' + - 'ForwardAuth': 'middlewares/http/forwardauth.md' + - 'GrpcWeb': 'middlewares/http/grpcweb.md' + - 'Headers': 'middlewares/http/headers.md' + - 'IPWhiteList': 'middlewares/http/ipwhitelist.md' + - 'IPAllowList': 'middlewares/http/ipallowlist.md' + - 'InFlightReq': 'middlewares/http/inflightreq.md' + - 'PassTLSClientCert': 'middlewares/http/passtlsclientcert.md' + - 'RateLimit': 'middlewares/http/ratelimit.md' + - 'RedirectRegex': 'middlewares/http/redirectregex.md' + - 'RedirectScheme': 'middlewares/http/redirectscheme.md' + - 'ReplacePath': 'middlewares/http/replacepath.md' + - 'ReplacePathRegex': 'middlewares/http/replacepathregex.md' + - 'Retry': 'middlewares/http/retry.md' + - 'StripPrefix': 'middlewares/http/stripprefix.md' + - 'StripPrefixRegex': 'middlewares/http/stripprefixregex.md' + - 'TCP': + - 'Overview': 'middlewares/tcp/overview.md' + - 'InFlightConn': 'middlewares/tcp/inflightconn.md' + - 'IPWhiteList': 'middlewares/tcp/ipwhitelist.md' + - 'IPAllowList': 'middlewares/tcp/ipallowlist.md' + - 'Plugins & Plugin Catalog': 'plugins/index.md' + - 'Operations': + - 'CLI': 'operations/cli.md' + - 'Dashboard' : 'operations/dashboard.md' + - 'API': 'operations/api.md' + - 'Ping': 'operations/ping.md' + - 'Observability': + - 'Overview': 'observability/overview.md' + - 'Logs': 'observability/logs.md' + - 'Access Logs': 'observability/access-logs.md' + - 'Metrics': + - 'Overview': 'observability/metrics/overview.md' + - 'Datadog': 'observability/metrics/datadog.md' + - 'InfluxDB2': 'observability/metrics/influxdb2.md' + - 'OpenTelemetry': 'observability/metrics/opentelemetry.md' + - 'Prometheus': 'observability/metrics/prometheus.md' + - 'StatsD': 'observability/metrics/statsd.md' + - 'Tracing': + - 'Overview': 'observability/tracing/overview.md' + - 'OpenTelemetry': 'observability/tracing/opentelemetry.md' + - 'Security': + - 'Content-Length': 'security/content-length.md' + - 'TLS in Multi-Tenant Kubernetes': 'security/tls-certs-in-multi-tenant-kubernetes.md' + - 'User Guides': + - 'FastProxy': 'user-guides/fastproxy.md' + - 'Kubernetes and Let''s Encrypt': 'user-guides/crd-acme/index.md' + - 'Kubernetes and cert-manager': 'user-guides/cert-manager.md' + - 'gRPC Examples': 'user-guides/grpc.md' + - 'Docker': + - 'Basic Example': 'user-guides/docker-compose/basic-example/index.md' + - 'HTTPS with Let''s Encrypt': + - 'TLS Challenge': 'user-guides/docker-compose/acme-tls/index.md' + - 'HTTP Challenge': 'user-guides/docker-compose/acme-http/index.md' + - 'DNS Challenge': 'user-guides/docker-compose/acme-dns/index.md' + - 'Migration': + - 'Traefik v3 minor migrations': 'migration/v3.md' - 'Traefik v2 to v3': - - 'Migration guide': 'migrate/v2-to-v3.md' - - 'Configuration changes for v3': 'migrate/v2-to-v3-details.md' - - 'Traefik v2 minor migrations': 'migrate/v2.md' - - 'Traefik v1 to v2': 'migrate/v1-to-v2.md' + - 'Migration guide': 'migration/v2-to-v3.md' + - 'Configuration changes for v3': 'migration/v2-to-v3-details.md' + - 'Traefik v2 minor migrations': 'migration/v2.md' + - 'Traefik v1 to v2': 'migration/v1-to-v2.md' + - 'Contributing': + - 'Thank You!': 'contributing/thank-you.md' + - 'Submitting Issues': 'contributing/submitting-issues.md' + - 'Submitting PRs': 'contributing/submitting-pull-requests.md' + - 'Security': 'contributing/submitting-security-issues.md' + - 'Building and Testing': 'contributing/building-testing.md' + - 'Documentation': 'contributing/documentation.md' + - 'Data Collection': 'contributing/data-collection.md' + - 'Advocating': 'contributing/advocating.md' + - 'Maintainers': 'contributing/maintainers.md' - 'Reference': - 'Install Configuration': - 'Boot Environment': 'reference/install-configuration/boot-environment.md' @@ -227,7 +203,6 @@ nav: - 'Kubernetes Gateway API' : 'reference/install-configuration/providers/kubernetes/kubernetes-gateway.md' - 'Kubernetes CRD' : 'reference/install-configuration/providers/kubernetes/kubernetes-crd.md' - 'Kubernetes Ingress' : 'reference/install-configuration/providers/kubernetes/kubernetes-ingress.md' - - 'Kubernetes Ingress NGINX' : 'reference/install-configuration/providers/kubernetes/kubernetes-ingress-nginx.md' - 'Docker': 'reference/install-configuration/providers/docker.md' - 'Swarm': 'reference/install-configuration/providers/swarm.md' - 'Hashicorp': @@ -251,21 +226,19 @@ nav: - "ACME" : 'reference/install-configuration/tls/certificate-resolvers/acme.md' - "Tailscale" : 'reference/install-configuration/tls/certificate-resolvers/tailscale.md' - "SPIFFE" : 'reference/install-configuration/tls/spiffe.md' - - "OCSP" : 'reference/install-configuration/tls/ocsp.md' - 'Observability': - 'Metrics' : 'reference/install-configuration/observability/metrics.md' - 'Tracing': 'reference/install-configuration/observability/tracing.md' - 'Logs & AccessLogs': 'reference/install-configuration/observability/logs-and-accesslogs.md' - 'Health Check (CLI & Ping)': 'reference/install-configuration/observability/healthcheck.md' - - 'Options List': 'reference/install-configuration/configuration-options.md' + # - 'Options List': 'reference/install-configuration/cli-options-list.md' -- Todo - 'Routing Configuration': - - 'Common Configuration' : + - 'General' : - 'Configuration Methods' : 'reference/routing-configuration/dynamic-configuration-methods.md' - 'HTTP' : - - 'Routing' : - - 'Router' : 'reference/routing-configuration/http/routing/router.md' - - 'Rules & Priority' : 'reference/routing-configuration/http/routing/rules-and-priority.md' - - 'Observability': 'reference/routing-configuration/http/routing/observability.md' + - 'Router' : + - 'Rules & Priority' : 'reference/routing-configuration/http/router/rules-and-priority.md' + - 'Observability': 'reference/routing-configuration/http/router/observability.md' - 'Load Balancing' : - 'Service' : 'reference/routing-configuration/http/load-balancing/service.md' - 'ServersTransport' : 'reference/routing-configuration/http/load-balancing/serverstransport.md' @@ -276,7 +249,6 @@ nav: - 'Middlewares' : - 'Overview' : 'reference/routing-configuration/http/middlewares/overview.md' - 'AddPrefix' : 'reference/routing-configuration/http/middlewares/addprefix.md' - - 'APIKey Traefik Hub API Gateway' : 'reference/routing-configuration/http/middlewares/apikey.md' - 'BasicAuth' : 'reference/routing-configuration/http/middlewares/basicauth.md' - 'Buffering': 'reference/routing-configuration/http/middlewares/buffering.md' - 'Chain': 'reference/routing-configuration/http/middlewares/chain.md' @@ -284,20 +256,12 @@ nav: - 'Compress': 'reference/routing-configuration/http/middlewares/compress.md' - 'ContentType': 'reference/routing-configuration/http/middlewares/contenttype.md' - 'DigestAuth': 'reference/routing-configuration/http/middlewares/digestauth.md' - - 'Distributed RateLimit Traefik Hub API Gateway' : 'reference/routing-configuration/http/middlewares/distributed-ratelimit.md' - 'Errors': 'reference/routing-configuration/http/middlewares/errorpages.md' - 'ForwardAuth': 'reference/routing-configuration/http/middlewares/forwardauth.md' - 'GrpcWeb': 'reference/routing-configuration/http/middlewares/grpcweb.md' - 'Headers': 'reference/routing-configuration/http/middlewares/headers.md' - - 'HMAC Traefik Hub API Gateway' : 'reference/routing-configuration/http/middlewares/hmac.md' - 'IPAllowList': 'reference/routing-configuration/http/middlewares/ipallowlist.md' - 'InFlightReq': 'reference/routing-configuration/http/middlewares/inflightreq.md' - - 'JWT Traefik Hub API Gateway' : 'reference/routing-configuration/http/middlewares/jwt.md' - - 'LDAP Traefik Hub API Gateway' : 'reference/routing-configuration/http/middlewares/ldap.md' - - 'Token Introspection Traefik Hub API Gateway' : 'reference/routing-configuration/http/middlewares/oauth2-token-introspection.md' - - 'Client Credentials Traefik Hub API Gateway' : 'reference/routing-configuration/http/middlewares/oauth2-client-credentials.md' - - 'OIDC Traefik Hub API Gateway' : 'reference/routing-configuration/http/middlewares/oidc.md' - - 'OPA Traefik Hub API Gateway' : 'reference/routing-configuration/http/middlewares/opa.md' - 'PassTLSClientCert': 'reference/routing-configuration/http/middlewares/passtlsclientcert.md' - 'RateLimit': 'reference/routing-configuration/http/middlewares/ratelimit.md' - 'RedirectRegex': 'reference/routing-configuration/http/middlewares/redirectregex.md' @@ -307,11 +271,9 @@ nav: - 'Retry': 'reference/routing-configuration/http/middlewares/retry.md' - 'StripPrefix': 'reference/routing-configuration/http/middlewares/stripprefix.md' - 'StripPrefixRegex': 'reference/routing-configuration/http/middlewares/stripprefixregex.md' - - 'WAF Traefik Hub API Gateway' : 'reference/routing-configuration/http/middlewares/waf.md' - 'TCP' : - - 'Routing' : - - 'Router' : 'reference/routing-configuration/tcp/routing/router.md' - - 'Rules & Priority' : 'reference/routing-configuration/tcp/routing/rules-and-priority.md' + - 'Router' : + - 'Rules & Priority' : 'reference/routing-configuration/tcp/router/rules-and-priority.md' - 'Service' : 'reference/routing-configuration/tcp/service.md' - 'ServersTransport' : 'reference/routing-configuration/tcp/serverstransport.md' - 'TLS' : 'reference/routing-configuration/tcp/tls.md' @@ -320,31 +282,28 @@ nav: - 'InFlightConn' : 'reference/routing-configuration/tcp/middlewares/inflightconn.md' - 'IPAllowList' : 'reference/routing-configuration/tcp/middlewares/ipallowlist.md' - 'UDP' : - - 'Routing' : - - 'Router' : 'reference/routing-configuration/udp/routing/router.md' - - 'Rules & Priority' : 'reference/routing-configuration/udp/routing/rules-priority.md' + - 'Router' : + - 'Rules & Priority' : 'reference/routing-configuration/udp/router/rules-priority.md' - 'Service' : 'reference/routing-configuration/udp/service.md' - 'Kubernetes': - 'Gateway API' : 'reference/routing-configuration/kubernetes/gateway-api.md' - 'Kubernetes CRD' : - 'HTTP' : - 'IngressRoute' : 'reference/routing-configuration/kubernetes/crd/http/ingressroute.md' - - 'Service' : 'reference/routing-configuration/kubernetes/crd/http/service.md' - 'TraefikService' : 'reference/routing-configuration/kubernetes/crd/http/traefikservice.md' - 'ServersTransport' : 'reference/routing-configuration/kubernetes/crd/http/serverstransport.md' - 'Middleware' : 'reference/routing-configuration/kubernetes/crd/http/middleware.md' - - 'TLSOption' : 'reference/routing-configuration/kubernetes/crd/tls/tlsoption.md' - - 'TLSStore' : 'reference/routing-configuration/kubernetes/crd/tls/tlsstore.md' + - 'TLSOption' : 'reference/routing-configuration/kubernetes/crd/http/tlsoption.md' + - 'TLSStore' : 'reference/routing-configuration/kubernetes/crd/http/tlsstore.md' - 'TCP' : - 'IngressRouteTCP' : 'reference/routing-configuration/kubernetes/crd/tcp/ingressroutetcp.md' - 'ServersTransportTCP' : 'reference/routing-configuration/kubernetes/crd/tcp/serverstransporttcp.md' - 'MiddlewareTCP' : 'reference/routing-configuration/kubernetes/crd/tcp/middlewaretcp.md' - - 'TLSOption' : 'reference/routing-configuration/kubernetes/crd/tls/tlsoption.md' - - 'TLSStore' : 'reference/routing-configuration/kubernetes/crd/tls/tlsstore.md' + - 'TLSOption' : 'reference/routing-configuration/kubernetes/crd/tcp/tlsoption.md' + - 'TLSStore' : 'reference/routing-configuration/kubernetes/crd/tcp/tlsstore.md' - 'UDP' : - 'IngressRouteUDP' : 'reference/routing-configuration/kubernetes/crd/udp/ingressrouteudp.md' - 'Ingress' : 'reference/routing-configuration/kubernetes/ingress.md' - - 'Ingress NGINX' : 'reference/routing-configuration/kubernetes/ingress-nginx.md' - 'Label & Tag Providers' : - 'Docker' : 'reference/routing-configuration/other-providers/docker.md' - 'Swarm' : 'reference/routing-configuration/other-providers/swarm.md' @@ -352,25 +311,6 @@ nav: - 'Nomad' : 'reference/routing-configuration/other-providers/nomad.md' - 'ECS' : 'reference/routing-configuration/other-providers/ecs.md' - 'KV' : 'reference/routing-configuration/other-providers/kv.md' - - 'File' : 'reference/routing-configuration/other-providers/file.md' - - 'Security': - - 'Content-Length': 'security/content-length.md' - - 'TLS in Multi-Tenant Kubernetes': 'security/tls-certs-in-multi-tenant-kubernetes.md' - - 'Deprecation Notices': - - 'Releases': 'deprecation/releases.md' - - 'Features': 'deprecation/features.md' - - 'User Guides': - - 'FastProxy': 'user-guides/fastproxy.md' - - 'gRPC Examples': 'user-guides/grpc.md' - - 'WebSocket Examples': 'user-guides/websocket.md' - - 'Contributing': - - 'Thank You!': 'contributing/thank-you.md' - - 'Submitting Issues': 'contributing/submitting-issues.md' - - 'Submitting PRs': 'contributing/submitting-pull-requests.md' - - 'Security': 'contributing/submitting-security-issues.md' - - 'Building and Testing': 'contributing/building-testing.md' - - 'Documentation': 'contributing/documentation.md' - - 'Data Collection': 'contributing/data-collection.md' - - 'Advocating': 'contributing/advocating.md' - - 'Maintainers': 'contributing/maintainers.md' - - 'FAQ': 'getting-started/faq.md' + - 'Deprecation Notices': + - 'Releases': 'deprecation/releases.md' + - 'Features': 'deprecation/features.md' diff --git a/docs/requirements.txt b/docs/requirements.txt index d2dff6242..68126a411 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -2,7 +2,6 @@ markdown-include==0.5.1 mkdocs==1.2.4 mkdocs-exclude==1.0.2 mkdocs-traefiklabs>=100.0.7 -mkdocs-redirects==1.2.2 click==8.1.7 colorama==0.4.6 diff --git a/go.mod b/go.mod index 3d00ed55b..493de3354 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/traefik/traefik/v3 -go 1.24.0 +go 1.23.0 require ( github.com/BurntSushi/toml v1.5.0 @@ -8,22 +8,22 @@ require ( github.com/abbot/go-http-auth v0.0.0-00010101000000-000000000000 // No tag on the repo. github.com/andybalholm/brotli v1.1.1 github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 - github.com/aws/aws-sdk-go-v2 v1.39.2 - github.com/aws/aws-sdk-go-v2/config v1.31.12 - github.com/aws/aws-sdk-go-v2/credentials v1.18.16 + github.com/aws/aws-sdk-go-v2 v1.36.3 + github.com/aws/aws-sdk-go-v2/config v1.29.9 + github.com/aws/aws-sdk-go-v2/credentials v1.17.62 github.com/aws/aws-sdk-go-v2/service/ec2 v1.203.1 github.com/aws/aws-sdk-go-v2/service/ecs v1.53.15 github.com/aws/aws-sdk-go-v2/service/ssm v1.56.13 - github.com/aws/smithy-go v1.23.0 + github.com/aws/smithy-go v1.22.2 github.com/cenkalti/backoff/v4 v4.3.0 github.com/containous/alice v0.0.0-20181107144136-d83ebdd94cbd // No tag on the repo. github.com/coreos/go-systemd/v22 v22.5.0 - github.com/docker/cli v28.3.3+incompatible - github.com/docker/docker v28.3.3+incompatible + github.com/docker/cli v27.1.1+incompatible + 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.9.0 - github.com/go-acme/lego/v4 v4.27.0 + github.com/fsnotify/fsnotify v1.8.0 + github.com/go-acme/lego/v4 v4.23.1 github.com/go-kit/kit v0.13.0 github.com/go-kit/log v0.2.1 github.com/golang/protobuf v1.5.4 @@ -33,36 +33,36 @@ 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-retryablehttp v0.7.8 + github.com/hashicorp/go-retryablehttp v0.7.7 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/http-wasm/http-wasm-host-go v0.7.0 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/klauspost/compress v1.18.0 + github.com/klauspost/compress v1.17.11 github.com/kvtools/consul v1.0.2 - github.com/kvtools/etcdv3 v1.0.3 - github.com/kvtools/redis v1.2.0 + 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.68 + github.com/miekg/dns v1.1.64 github.com/mitchellh/copystructure v1.2.0 github.com/mitchellh/hashstructure v1.0.0 - github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c // No tag on the repo. + github.com/mitchellh/mapstructure v1.5.0 github.com/patrickmn/go-cache v2.1.0+incompatible - github.com/pires/go-proxyproto v0.8.1 + github.com/pires/go-proxyproto v0.6.1 github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // No tag on the repo. - github.com/prometheus/client_golang v1.22.0 + github.com/prometheus/client_golang v1.19.1 github.com/prometheus/client_model v0.6.1 - github.com/quic-go/quic-go v0.55.0 - github.com/redis/go-redis/v9 v9.8.0 + github.com/quic-go/quic-go v0.48.2 + github.com/redis/go-redis/v9 v9.7.3 github.com/rs/zerolog v1.33.0 github.com/sirupsen/logrus v1.9.3 - github.com/spiffe/go-spiffe/v2 v2.5.0 + github.com/spiffe/go-spiffe/v2 v2.4.0 github.com/stealthrocket/wasi-go v0.8.0 github.com/stealthrocket/wazergo v0.19.1 - github.com/stretchr/testify v1.11.1 + 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/tailscale/tscert v0.0.0-20230806124524-28a91b69a046 // No tag on the repo. github.com/testcontainers/testcontainers-go v0.32.0 @@ -78,59 +78,58 @@ require ( github.com/vulcand/oxy/v2 v2.0.3 github.com/vulcand/predicate v1.2.0 github.com/yuin/gopher-lua v1.1.1 - go.opentelemetry.io/collector/pdata v1.41.0 - go.opentelemetry.io/contrib/bridges/otellogrus v0.13.0 - go.opentelemetry.io/contrib/propagators/autoprop v0.63.0 - go.opentelemetry.io/otel v1.38.0 - go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.14.0 - go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.14.0 - go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.38.0 - go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.38.0 - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0 - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0 - go.opentelemetry.io/otel/log v0.14.0 - go.opentelemetry.io/otel/metric v1.38.0 - go.opentelemetry.io/otel/sdk v1.38.0 - go.opentelemetry.io/otel/sdk/log v0.14.0 - go.opentelemetry.io/otel/sdk/metric v1.38.0 - go.opentelemetry.io/otel/trace v1.38.0 - golang.org/x/crypto v0.43.0 - golang.org/x/mod v0.28.0 - golang.org/x/net v0.46.0 - golang.org/x/sync v0.17.0 - golang.org/x/sys v0.37.0 - golang.org/x/text v0.30.0 - golang.org/x/time v0.14.0 - golang.org/x/tools v0.37.0 - google.golang.org/grpc v1.75.1 + go.opentelemetry.io/collector/pdata v1.10.0 + go.opentelemetry.io/contrib/bridges/otellogrus v0.7.0 + go.opentelemetry.io/contrib/propagators/autoprop v0.53.0 + go.opentelemetry.io/otel v1.34.0 + go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.8.0 + go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.8.0 + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.28.0 + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.28.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.28.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.28.0 + go.opentelemetry.io/otel/log v0.8.0 + go.opentelemetry.io/otel/metric v1.34.0 + go.opentelemetry.io/otel/sdk v1.34.0 + go.opentelemetry.io/otel/sdk/log v0.8.0 + go.opentelemetry.io/otel/sdk/metric v1.34.0 + go.opentelemetry.io/otel/trace v1.34.0 + golang.org/x/mod v0.23.0 + golang.org/x/net v0.38.0 + golang.org/x/sync v0.12.0 + golang.org/x/sys v0.31.0 + golang.org/x/text v0.23.0 + golang.org/x/time v0.11.0 + golang.org/x/tools v0.30.0 + google.golang.org/grpc v1.71.0 gopkg.in/natefinch/lumberjack.v2 v2.2.1 gopkg.in/yaml.v3 v3.0.1 - k8s.io/api v0.32.3 - k8s.io/apiextensions-apiserver v0.32.3 - k8s.io/apimachinery v0.32.3 - k8s.io/client-go v0.32.3 - k8s.io/utils v0.0.0-20241210054802-24370beab758 // No tag on the repo. + k8s.io/api v0.31.1 + k8s.io/apiextensions-apiserver v0.31.1 + k8s.io/apimachinery v0.31.1 + k8s.io/client-go v0.31.1 + k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // No tag on the repo. mvdan.cc/xurls/v2 v2.5.0 - sigs.k8s.io/controller-runtime v0.20.4 - sigs.k8s.io/gateway-api v1.3.0 + sigs.k8s.io/controller-runtime v0.18.0 + sigs.k8s.io/gateway-api v1.2.1 sigs.k8s.io/yaml v1.4.0 ) require ( - cloud.google.com/go/auth v0.17.0 // indirect - cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect - cloud.google.com/go/compute/metadata v0.9.0 // indirect - dario.cat/mergo v1.0.1 // indirect + cloud.google.com/go/auth v0.15.0 // indirect + cloud.google.com/go/auth/oauth2adapt v0.2.7 // 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.19.1 // indirect - github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.0 // indirect - github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 // indirect + github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.1 // indirect + github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.2 // 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.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-20250102033503-faa5f7b0171c // indirect + github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect github.com/Azure/go-autorest v14.2.0+incompatible // indirect github.com/Azure/go-autorest/autorest v0.11.30 // indirect github.com/Azure/go-autorest/autorest/adal v0.9.22 // indirect @@ -140,47 +139,39 @@ require ( github.com/Azure/go-autorest/autorest/to v0.4.1 // indirect github.com/Azure/go-autorest/logger v0.2.1 // indirect github.com/Azure/go-autorest/tracing v0.6.0 // indirect - github.com/AzureAD/microsoft-authentication-library-for-go v1.5.0 // indirect + github.com/AzureAD/microsoft-authentication-library-for-go v1.3.3 // indirect github.com/HdrHistogram/hdrhistogram-go v1.1.2 // indirect github.com/Masterminds/goutils v1.1.1 // indirect - github.com/Masterminds/semver/v3 v3.3.1 // indirect + github.com/Masterminds/semver/v3 v3.2.1 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect - github.com/Microsoft/hcsshim v0.13.0 // indirect + github.com/Microsoft/hcsshim v0.11.7 // indirect github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87 // indirect github.com/VividCortex/gohistogram v1.0.0 // indirect - github.com/akamai/AkamaiOPEN-edgegrid-golang/v11 v11.1.0 // indirect - github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.5 // indirect - github.com/alibabacloud-go/darabonba-openapi/v2 v2.1.13 // indirect - github.com/alibabacloud-go/debug v1.0.1 // indirect - github.com/alibabacloud-go/tea v1.3.13 // indirect - github.com/alibabacloud-go/tea-utils/v2 v2.0.7 // indirect - github.com/aliyun/credentials-go v1.4.7 // indirect + github.com/akamai/AkamaiOPEN-edgegrid-golang v1.2.2 // indirect + github.com/aliyun/alibaba-cloud-sdk-go v1.63.100 // indirect github.com/armon/go-metrics v0.4.1 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.9 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.9 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.9 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.1 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.9 // indirect - github.com/aws/aws-sdk-go-v2/service/lightsail v1.50.0 // indirect - github.com/aws/aws-sdk-go-v2/service/route53 v1.58.4 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.29.6 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.1 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.38.6 // indirect - github.com/aziontech/azionapi-go-sdk v0.143.0 // indirect - github.com/baidubce/bce-sdk-go v0.9.248 // indirect - github.com/benbjohnson/clock v1.3.5 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 // indirect + github.com/aws/aws-sdk-go-v2/service/lightsail v1.43.1 // indirect + github.com/aws/aws-sdk-go-v2/service/route53 v1.50.0 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.25.1 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.29.1 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.33.17 // indirect + github.com/baidubce/bce-sdk-go v0.9.223 // 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/bytedance/sonic v1.10.0 // indirect - github.com/cenkalti/backoff/v5 v5.0.3 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/clbanning/mxj/v2 v2.7.0 // indirect - github.com/containerd/containerd v1.7.23 // indirect - github.com/containerd/errdefs v1.0.0 // indirect - github.com/containerd/errdefs/pkg v0.3.0 // indirect + github.com/civo/civogo v0.3.11 // indirect + github.com/cloudflare/cloudflare-go v0.115.0 // indirect + github.com/containerd/containerd v1.7.20 // indirect github.com/containerd/log v0.1.0 // indirect - github.com/containerd/platforms v1.0.0-rc.1 // indirect + github.com/containerd/platforms v0.2.1 // indirect github.com/coreos/go-semver v0.3.1 // indirect github.com/cpuguy83/dockercfg v0.3.1 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect @@ -189,54 +180,52 @@ require ( github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/dimchansky/utfbom v1.1.1 // indirect github.com/distribution/reference v0.6.0 // indirect - github.com/dnsimple/dnsimple-go/v4 v4.0.0 // indirect + github.com/dnsimple/dnsimple-go v1.7.0 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/emicklei/go-restful/v3 v3.12.0 // indirect - github.com/evanphx/json-patch/v5 v5.9.11 // indirect - github.com/exoscale/egoscale/v3 v3.1.27 // indirect - github.com/fatih/color v1.18.0 // indirect + github.com/evanphx/json-patch/v5 v5.9.0 // indirect + github.com/exoscale/egoscale/v3 v3.1.13 // indirect + github.com/fatih/color v1.17.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect - github.com/gabriel-vasile/mimetype v1.4.3 // indirect + github.com/gabriel-vasile/mimetype v1.4.2 // indirect github.com/ghodss/yaml v1.0.0 // indirect github.com/gin-gonic/gin v1.9.1 // indirect - github.com/go-acme/alidns-20150109/v4 v4.6.1 // indirect - github.com/go-acme/tencentclouddnspod v1.1.10 // indirect - github.com/go-acme/tencentedgdeone v1.1.19 // indirect github.com/go-errors/errors v1.0.1 // indirect - github.com/go-jose/go-jose/v4 v4.1.3 // indirect + github.com/go-jose/go-jose/v4 v4.0.5 // indirect github.com/go-logfmt/logfmt v0.5.1 // indirect - github.com/go-logr/logr v1.4.3 // indirect + github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-logr/zapr v1.3.0 // indirect github.com/go-ole/go-ole v1.2.6 // indirect github.com/go-openapi/jsonpointer v0.21.0 // indirect github.com/go-openapi/jsonreference v0.21.0 // indirect github.com/go-openapi/swag v0.23.0 // indirect - github.com/go-ozzo/ozzo-validation/v4 v4.3.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.23.0 // indirect + github.com/go-playground/validator/v10 v10.16.0 // indirect github.com/go-resty/resty/v2 v2.16.5 // indirect - github.com/go-viper/mapstructure/v2 v2.4.0 // 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-yaml v1.11.3 // indirect + github.com/goccy/go-json v0.10.5 // 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.2 // indirect - github.com/golang-jwt/jwt/v5 v5.3.0 // indirect + github.com/golang-jwt/jwt/v5 v5.2.2 // indirect github.com/google/gnostic-models v0.6.8 // indirect github.com/google/go-cmp v0.7.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-20240910150728-a0b0bb1d4134 // indirect github.com/google/s2a-go v0.1.9 // indirect github.com/google/uuid v1.6.0 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect - github.com/googleapis/gax-go/v2 v2.15.0 // indirect + github.com/googleapis/gax-go/v2 v2.14.1 // 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/grpc-ecosystem/grpc-gateway/v2 v2.27.2 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.23.0 // indirect github.com/hashicorp/cronexpr v1.1.2 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect @@ -247,157 +236,156 @@ require ( 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.172 // indirect + github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.141 // 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 - github.com/infobloxopen/infoblox-go-client/v2 v2.10.0 // indirect + github.com/infobloxopen/infoblox-go-client/v2 v2.9.0 // indirect + github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/jonboulle/clockwork v0.4.0 // indirect github.com/josharian/intern v1.0.0 // indirect - github.com/json-iterator/go v1.1.13-0.20220915233716-71ac16282d12 // indirect + github.com/json-iterator/go v1.1.12 // indirect github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213 // indirect github.com/klauspost/cpuid/v2 v2.2.5 // indirect github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b // indirect 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/leodido/go-urn v1.4.0 // indirect - github.com/linode/linodego v1.60.0 // indirect + github.com/leodido/go-urn v1.2.4 // indirect + github.com/linode/linodego v1.48.1 // indirect github.com/liquidweb/liquidweb-cli v0.6.9 // indirect github.com/liquidweb/liquidweb-go v1.6.4 // indirect - github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35 // indirect + github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mailgun/minheap v0.0.0-20170619185613-3dbe6c6bf55f // indirect github.com/mailgun/multibuf v0.1.2 // indirect github.com/mailgun/timetools v0.0.0-20141028012446-7e6055773c51 // indirect github.com/mailru/easyjson v0.7.7 // indirect - github.com/mattn/go-colorable v0.1.14 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mimuret/golang-iij-dpf v0.9.1 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/go-ps v1.0.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/moby/docker-image-spec v1.3.1 // indirect - github.com/moby/go-archive v0.1.0 // indirect github.com/moby/patternmatcher v0.6.0 // indirect - github.com/moby/spdystream v0.5.0 // indirect - github.com/moby/sys/atomicwriter v0.1.0 // indirect - github.com/moby/sys/sequential v0.6.0 // indirect - github.com/moby/sys/user v0.4.0 // indirect - github.com/moby/sys/userns v0.1.0 // indirect - github.com/moby/term v0.5.2 // indirect + github.com/moby/spdystream v0.4.0 // indirect + github.com/moby/sys/sequential v0.5.0 // indirect + github.com/moby/sys/user v0.2.0 // indirect + github.com/moby/term v0.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect github.com/morikuni/aec v1.0.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect - github.com/namedotcom/go/v4 v4.0.2 // indirect + 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-20250327222614-988a091fc7ea // indirect - github.com/nrdcg/desec v0.11.0 // indirect + github.com/nrdcg/bunny-go v0.0.0-20240207213615-dde5bf4577a3 // indirect + github.com/nrdcg/desec v0.10.0 // indirect github.com/nrdcg/dnspod-go v0.4.0 // indirect github.com/nrdcg/freemyip v0.3.0 // indirect github.com/nrdcg/goacmedns v0.2.0 // indirect - github.com/nrdcg/goinwx v0.11.0 // indirect + github.com/nrdcg/goinwx v0.10.0 // indirect github.com/nrdcg/mailinabox v0.2.0 // indirect - github.com/nrdcg/namesilo v0.5.0 // indirect + github.com/nrdcg/namesilo v0.2.1 // indirect github.com/nrdcg/nodion v0.1.0 // indirect - github.com/nrdcg/oci-go-sdk/common/v1065 v1065.102.0 // indirect - github.com/nrdcg/oci-go-sdk/dns/v1065 v1065.102.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.20.2 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect - github.com/opencontainers/image-spec v1.1.1 // indirect - github.com/ovh/go-ovh v1.9.0 // indirect - github.com/pelletier/go-toml/v2 v2.2.4 // indirect + github.com/opencontainers/image-spec v1.1.0 // indirect + github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b // indirect + github.com/oracle/oci-go-sdk/v65 v65.87.0 // indirect + github.com/ovh/go-ovh v1.7.0 // indirect + github.com/pelletier/go-toml/v2 v2.1.0 // indirect github.com/peterhellberg/link v1.2.0 // indirect github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect - github.com/pquerna/otp v1.5.0 // indirect - github.com/prometheus/common v0.62.0 // indirect - github.com/prometheus/procfs v0.16.1 // indirect + github.com/power-devops/perfstat v0.0.0-20220216144756-c35f1ee13d7c // indirect + github.com/pquerna/otp v1.4.0 // indirect + github.com/prometheus/common v0.55.0 // indirect + github.com/prometheus/procfs v0.15.1 // indirect github.com/quic-go/qpack v0.5.1 // indirect github.com/regfish/regfish-dnsapi-go v0.1.1 // indirect github.com/rs/cors v1.7.0 // indirect - github.com/sacloud/api-client-go v0.3.3 // indirect - github.com/sacloud/go-http v0.1.9 // indirect - github.com/sacloud/iaas-api-go v1.19.0 // indirect - github.com/sacloud/packages-go v0.0.11 // 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.14.0 // indirect + github.com/sacloud/packages-go v0.0.10 // 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.35 // indirect + github.com/scaleway/scaleway-sdk-go v1.0.0-beta.32 // indirect github.com/selectel/domains-go v1.1.0 // indirect - github.com/selectel/go-selvpcclient/v4 v4.1.0 // indirect + github.com/selectel/go-selvpcclient/v3 v3.2.1 // indirect github.com/shirou/gopsutil/v3 v3.24.4 // 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.2.1 // indirect + github.com/softlayer/softlayer-go v1.1.7 // indirect github.com/softlayer/xmlrpc v0.0.0-20200409220501-5f089df7cb7e // indirect - github.com/sony/gobreaker v1.0.0 // 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.7 // 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/subosito/gotenv v1.6.0 // indirect - github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.1.41 // indirect + github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1128 // indirect + github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1128 // indirect github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.1 // indirect github.com/tjfoc/gmsm v1.4.1 // indirect - github.com/tklauser/go-sysconf v0.3.15 // indirect - github.com/tklauser/numcpus v0.10.0 // indirect - github.com/transip/gotransip/v6 v6.26.1 // indirect - github.com/ultradns/ultradns-go-sdk v1.8.1-20250722213956-faef419 // indirect + github.com/tklauser/go-sysconf v0.3.12 // indirect + github.com/tklauser/numcpus v0.6.1 // indirect + github.com/transip/gotransip/v6 v6.26.0 // indirect + github.com/ultradns/ultradns-go-sdk v1.8.0-20241010134910-243eeec // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/vinyldns/go-vinyldns v0.9.16 // indirect - github.com/volcengine/volc-sdk-golang v1.0.223 // indirect - github.com/vultr/govultr/v3 v3.24.0 // indirect + github.com/volcengine/volc-sdk-golang v1.0.199 // indirect + github.com/vultr/govultr/v3 v3.17.0 // indirect github.com/x448/float16 v0.8.4 // indirect - github.com/yandex-cloud/go-genproto v0.31.0 // indirect - github.com/yandex-cloud/go-sdk/services/dns v0.0.12 // indirect - github.com/yandex-cloud/go-sdk/v2 v2.19.0 // indirect - github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect + github.com/yandex-cloud/go-genproto v0.0.0-20250319153614-fb9d3e5eb01a // indirect + github.com/yandex-cloud/go-sdk v0.0.0-20250320143332-9cbcfc5de4ae // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect - github.com/zeebo/errs v1.4.0 // indirect - go.etcd.io/etcd/api/v3 v3.6.4 // indirect - go.etcd.io/etcd/client/pkg/v3 v3.6.4 // indirect - go.etcd.io/etcd/client/v3 v3.6.4 // indirect + github.com/zeebo/errs v1.3.0 // indirect + go.etcd.io/etcd/api/v3 v3.5.14 // indirect + go.etcd.io/etcd/client/pkg/v3 v3.5.14 // indirect + go.etcd.io/etcd/client/v3 v3.5.14 // indirect go.mongodb.org/mongo-driver v1.13.1 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect - go.opentelemetry.io/collector/featuregate v1.41.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect - go.opentelemetry.io/contrib/propagators/aws v1.38.0 // indirect - go.opentelemetry.io/contrib/propagators/b3 v1.38.0 // indirect - go.opentelemetry.io/contrib/propagators/jaeger v1.38.0 // indirect - go.opentelemetry.io/contrib/propagators/ot v1.38.0 // indirect - go.opentelemetry.io/proto/otlp v1.7.1 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 // indirect + go.opentelemetry.io/contrib/propagators/aws v1.28.0 // indirect + go.opentelemetry.io/contrib/propagators/b3 v1.28.0 // indirect + go.opentelemetry.io/contrib/propagators/jaeger v1.28.0 // indirect + go.opentelemetry.io/contrib/propagators/ot v1.28.0 // indirect + go.opentelemetry.io/proto/otlp v1.3.1 // indirect go.uber.org/atomic v1.11.0 // indirect + go.uber.org/mock v0.4.0 // indirect go.uber.org/multierr v1.11.0 // indirect - go.uber.org/ratelimit v0.3.1 // indirect - go.uber.org/zap v1.27.0 // indirect + go.uber.org/ratelimit v0.3.0 // indirect + go.uber.org/zap v1.26.0 // indirect golang.org/x/arch v0.4.0 // indirect + golang.org/x/crypto v0.36.0 // indirect golang.org/x/exp v0.0.0-20241210194714-1829a127f884 // indirect - golang.org/x/oauth2 v0.32.0 // indirect - golang.org/x/term v0.36.0 // indirect - golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect - google.golang.org/api v0.252.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20251002232023-7c0ddcbb5797 // indirect - google.golang.org/protobuf v1.36.10 // indirect + golang.org/x/oauth2 v0.28.0 // indirect + golang.org/x/term v0.30.0 // indirect + google.golang.org/api v0.227.0 // indirect + google.golang.org/genproto v0.0.0-20241021214115-324edc3d5d38 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4 // indirect + google.golang.org/protobuf v1.36.5 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect + gopkg.in/h2non/gock.v1 v1.0.16 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect - gopkg.in/ns1/ns1-go.v2 v2.15.0 // indirect + gopkg.in/ns1/ns1-go.v2 v2.13.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect k8s.io/klog/v2 v2.130.1 // indirect - k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f // indirect + k8s.io/kube-openapi v0.0.0-20240423202451-8948a665c108 // indirect nhooyr.io/websocket v1.8.7 // indirect - sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.7.0 // indirect + sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect ) // Containous forks diff --git a/go.sum b/go.sum index 01f7b442d..aeff1bfa0 100644 --- a/go.sum +++ b/go.sum @@ -13,18 +13,18 @@ cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKV cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go/auth v0.17.0 h1:74yCm7hCj2rUyyAocqnFzsAYXgJhrG26XCFimrc/Kz4= -cloud.google.com/go/auth v0.17.0/go.mod h1:6wv/t5/6rOPAX4fJiRjKkJCvswLwdet7G8+UGXt7nCQ= -cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc= -cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c= +cloud.google.com/go/auth v0.15.0 h1:Ly0u4aA5vG/fsSsxu98qCQBemXtAtJf+95z9HK+cxps= +cloud.google.com/go/auth v0.15.0/go.mod h1:WJDGqZ1o9E9wKIL+IwStfyn/+s59zl4Bi+1KQNVXLZ8= +cloud.google.com/go/auth/oauth2adapt v0.2.7 h1:/Lc7xODdqcEw8IrZ9SvwnlLX6j9FHQM74z6cBk9Rw6M= +cloud.google.com/go/auth/oauth2adapt v0.2.7/go.mod h1:NTbTTzfvPl1Y3V1nPpOgl2w6d/FjO7NNUQaWSox6ZMc= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs= -cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10= +cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I= +cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= @@ -37,23 +37,23 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= -dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= +dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= +dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk= -github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= github.com/AdamSLevy/jsonrpc2/v14 v14.1.0 h1:Dy3M9aegiI7d7PF1LUdjbVigJReo+QOceYsMyFh9qoE= github.com/AdamSLevy/jsonrpc2/v14 v14.1.0/go.mod h1:ZakZtbCXxCz82NJvq7MoREtiQesnDfrtF6RFUGzQfLo= github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU= github.com/Azure/azure-sdk-for-go v68.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.19.1 h1:5YTBM8QDVIBN3sxBil89WfdAAqDZbyJTgh688DSxX5w= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.19.1/go.mod h1:YD5h/ldMsG0XiIw7PdyNhLxaM317eFh5yNLccNfGdyw= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.0 h1:KpMC6LFL7mqpExyMC9jVOYRiVhLmamjeZfRsUpB7l4s= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.0/go.mod h1:J7MUC/wtRpfGVbQ5sIItY5/FuVWmvzlY21WAOfQnq/I= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.1 h1:DSDNVxqkoXJiko6x8a90zidoYqnYYa6c1MTzDKzKkTo= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.1/go.mod h1:zGqV2R4Cr/k8Uye5w+dgQ06WJtEcbQG/8J7BB6hnCr4= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.2 h1:F0gBpfdPLGsw+nsgk6aqqkZS1jiixa5WwFe3fk/T3Ys= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.2/go.mod h1:SqINnQ9lVVdRlyC8cd1lCI0SdX4n2paeABd2K8ggfnE= github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2 h1:yz1bePFlP5Vws5+8ez6T3HWXPmwOK7Yvq8QxDBD3SKY= github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2/go.mod h1:Pa9ZNPuoNu/GztvBSKk9J1cDJW6vk/n0zLtV4mgd8N8= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 h1:9iefClla7iYpfYWdzPCRDozdmndjTm8DXdpCzPajMgA= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2/go.mod h1:XtLgD3ZD34DAaVIIAyG3objl5DynM3CQ/vMcbBNJZGI= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 h1:ywEEhmNahHBihViHepv3xPBn1663uRv2t2q/ESv9seY= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0/go.mod h1:iZDifYGJTIgIIkYRNWPENUnqx6bJ2xnSDFI2tjwZNuY= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.2.0 h1:lpOxwrQ919lCZoNCd69rVt8u1eLZuMORrGXqy8sNf3c= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.2.0/go.mod h1:fSvRkb8d26z9dbL40Uf/OO6Vo9iExtZK3D0ulRV+8M0= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/internal/v3 v3.1.0 h1:2qsIIvxVT+uE6yrNldntJKlLRgxGbZ85kgtz5SNBhMw= @@ -64,8 +64,8 @@ github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resourcegraph/armresourceg github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resourcegraph/armresourcegraph v0.9.0/go.mod h1:wVEOJfGTj0oPAUGA1JuRAvz/lxXQsWW16axmHPP47Bk= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.2.0 h1:Dd+RhdJn0OTtVGaeDLZpcumkIVCtA/3/Fo42+eoYvVM= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.2.0/go.mod h1:5kakwfW5CjC9KK+Q4wjXAg+ShuIm2mBMua0ZFj2C8PE= -github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg= -github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/Azure/go-autorest/autorest v0.11.28/go.mod h1:MrkzG3Y3AH668QyF9KRk5neJnGgmhQ6krbhR8Q5eMvA= @@ -91,9 +91,10 @@ github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUM github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJTmL004Abzc5wDB5VtZG2PJk5ndYDgVacGqfirKxjM= github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE= -github.com/AzureAD/microsoft-authentication-library-for-go v1.5.0 h1:XkkQbfMyuH2jTSjQjSoihryI8GINRcs4xp8lNawg0FI= -github.com/AzureAD/microsoft-authentication-library-for-go v1.5.0/go.mod h1:HKpQxkWaGLJ+D/5H8QRpyQXA1eKjxkFlOMwck5+33Jk= +github.com/AzureAD/microsoft-authentication-library-for-go v1.3.3 h1:H5xDQaE3XowWfhZRUpnfC+rGZMEVoSiji+b+/HFAPU4= +github.com/AzureAD/microsoft-authentication-library-for-go v1.3.3/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg= github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= @@ -105,15 +106,15 @@ github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= -github.com/Masterminds/semver/v3 v3.3.1 h1:QtNSWtVZ3nBfk8mAOu/B6v7FMJ+NHTIgUPi7rj+4nv4= -github.com/Masterminds/semver/v3 v3.3.1/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= +github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0= +github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA= github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM= github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= -github.com/Microsoft/hcsshim v0.13.0 h1:/BcXOiS6Qi7N9XqUcv27vkIuVOkBEcWstd2pMlWSeaA= -github.com/Microsoft/hcsshim v0.13.0/go.mod h1:9KWJ/8DgU+QzYGupX4tzMhRQE8h6w90lH6HAaclpEok= +github.com/Microsoft/hcsshim v0.11.7 h1:vl/nj3Bar/CvJSYo7gIQPyRWc9f3c6IeSNavBTSZNZQ= +github.com/Microsoft/hcsshim v0.11.7/go.mod h1:MV8xMfmECjl5HdO7U/3/hFVnkmSBjAjmA09d4bExKcU= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87 h1:xPMsUicZ3iosVPSIP7bW5EcGUzjiiMl1OYTe14y/R24= github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87/go.mod h1:iGLljf5n9GjT6kc0HBvyI1nOKnGQbNB66VzSNbK5iks= @@ -125,60 +126,15 @@ github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrd github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= -github.com/akamai/AkamaiOPEN-edgegrid-golang/v11 v11.1.0 h1:h/33OxYLqBk0BYmEbSUy7MlvgQR/m1w1/7OJFKoPL1I= -github.com/akamai/AkamaiOPEN-edgegrid-golang/v11 v11.1.0/go.mod h1:rvh3imDA6EaQi+oM/GQHkQAOHbXPKJ7EWJvfjuw141Q= +github.com/akamai/AkamaiOPEN-edgegrid-golang v1.2.2 h1:F1j7z+/DKEsYqZNoxC6wvfmaiDneLsQOFQmuq9NADSY= +github.com/akamai/AkamaiOPEN-edgegrid-golang v1.2.2/go.mod h1:QlXr/TrICfQ/ANa76sLeQyhAJyNR9sEcfNuZBkY9jgY= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= -github.com/alibabacloud-go/alibabacloud-gateway-pop v0.0.6 h1:eIf+iGJxdU4U9ypaUfbtOWCsZSbTb8AUHvyPrxu6mAA= -github.com/alibabacloud-go/alibabacloud-gateway-pop v0.0.6/go.mod h1:4EUIoxs/do24zMOGGqYVWgw0s9NtiylnJglOeEB5UJo= -github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4/go.mod h1:sCavSAvdzOjul4cEqeVtvlSaSScfNsTQ+46HwlTL1hc= -github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.5 h1:zE8vH9C7JiZLNJJQ5OwjU9mSi4T9ef9u3BURT6LCLC8= -github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.5/go.mod h1:tWnyE9AjF8J8qqLk645oUmVUnFybApTQWklQmi5tY6g= -github.com/alibabacloud-go/darabonba-array v0.1.0 h1:vR8s7b1fWAQIjEjWnuF0JiKsCvclSRTfDzZHTYqfufY= -github.com/alibabacloud-go/darabonba-array v0.1.0/go.mod h1:BLKxr0brnggqOJPqT09DFJ8g3fsDshapUD3C3aOEFaI= -github.com/alibabacloud-go/darabonba-encode-util v0.0.2 h1:1uJGrbsGEVqWcWxrS9MyC2NG0Ax+GpOM5gtupki31XE= -github.com/alibabacloud-go/darabonba-encode-util v0.0.2/go.mod h1:JiW9higWHYXm7F4PKuMgEUETNZasrDM6vqVr/Can7H8= -github.com/alibabacloud-go/darabonba-map v0.0.2 h1:qvPnGB4+dJbJIxOOfawxzF3hzMnIpjmafa0qOTp6udc= -github.com/alibabacloud-go/darabonba-map v0.0.2/go.mod h1:28AJaX8FOE/ym8OUFWga+MtEzBunJwQGceGQlvaPGPc= -github.com/alibabacloud-go/darabonba-openapi/v2 v2.1.12/go.mod h1:f2wDpbM7hK9SvLIH09zSKVU1TsyemUNOqErMscMMl7c= -github.com/alibabacloud-go/darabonba-openapi/v2 v2.1.13 h1:Q00FU3H94Ts0ZIHDmY+fYGgB7dV9D/YX6FGsgorQPgw= -github.com/alibabacloud-go/darabonba-openapi/v2 v2.1.13/go.mod h1:lxFGfobinVsQ49ntjpgWghXmIF0/Sm4+wvBJ1h5RtaE= -github.com/alibabacloud-go/darabonba-signature-util v0.0.7 h1:UzCnKvsjPFzApvODDNEYqBHMFt1w98wC7FOo0InLyxg= -github.com/alibabacloud-go/darabonba-signature-util v0.0.7/go.mod h1:oUzCYV2fcCH797xKdL6BDH8ADIHlzrtKVjeRtunBNTQ= -github.com/alibabacloud-go/darabonba-string v1.0.2 h1:E714wms5ibdzCqGeYJ9JCFywE5nDyvIXIIQbZVFkkqo= -github.com/alibabacloud-go/darabonba-string v1.0.2/go.mod h1:93cTfV3vuPhhEwGGpKKqhVW4jLe7tDpo3LUM0i0g6mA= -github.com/alibabacloud-go/debug v0.0.0-20190504072949-9472017b5c68/go.mod h1:6pb/Qy8c+lqua8cFpEy7g39NRRqOWc3rOwAy8m5Y2BY= -github.com/alibabacloud-go/debug v1.0.0/go.mod h1:8gfgZCCAC3+SCzjWtY053FrOcd4/qlH6IHTI4QyICOc= -github.com/alibabacloud-go/debug v1.0.1 h1:MsW9SmUtbb1Fnt3ieC6NNZi6aEwrXfDksD4QA6GSbPg= -github.com/alibabacloud-go/debug v1.0.1/go.mod h1:8gfgZCCAC3+SCzjWtY053FrOcd4/qlH6IHTI4QyICOc= -github.com/alibabacloud-go/endpoint-util v1.1.0 h1:r/4D3VSw888XGaeNpP994zDUaxdgTSHBbVfZlzf6b5Q= -github.com/alibabacloud-go/endpoint-util v1.1.0/go.mod h1:O5FuCALmCKs2Ff7JFJMudHs0I5EBgecXXxZRyswlEjE= -github.com/alibabacloud-go/openapi-util v0.1.0/go.mod h1:sQuElr4ywwFRlCCberQwKRFhRzIyG4QTP/P4y1CJ6Ws= -github.com/alibabacloud-go/openapi-util v0.1.1 h1:ujGErJjG8ncRW6XtBBMphzHTvCxn4DjrVw4m04HsS28= -github.com/alibabacloud-go/openapi-util v0.1.1/go.mod h1:/UehBSE2cf1gYT43GV4E+RxTdLRzURImCYY0aRmlXpw= -github.com/alibabacloud-go/tea v1.1.0/go.mod h1:IkGyUSX4Ba1V+k4pCtJUc6jDpZLFph9QMy2VUPTwukg= -github.com/alibabacloud-go/tea v1.1.7/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeGOFfwYm3N/p4= -github.com/alibabacloud-go/tea v1.1.8/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeGOFfwYm3N/p4= -github.com/alibabacloud-go/tea v1.1.11/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeGOFfwYm3N/p4= -github.com/alibabacloud-go/tea v1.1.17/go.mod h1:nXxjm6CIFkBhwW4FQkNrolwbfon8Svy6cujmKFUq98A= -github.com/alibabacloud-go/tea v1.1.20/go.mod h1:nXxjm6CIFkBhwW4FQkNrolwbfon8Svy6cujmKFUq98A= -github.com/alibabacloud-go/tea v1.2.2/go.mod h1:CF3vOzEMAG+bR4WOql8gc2G9H3EkH3ZLAQdpmpXMgwk= -github.com/alibabacloud-go/tea v1.3.12/go.mod h1:A560v/JTQ1n5zklt2BEpurJzZTI8TUT+Psg2drWlxRg= -github.com/alibabacloud-go/tea v1.3.13 h1:WhGy6LIXaMbBM6VBYcsDCz6K/TPsT1Ri2hPmmZffZ94= -github.com/alibabacloud-go/tea v1.3.13/go.mod h1:A560v/JTQ1n5zklt2BEpurJzZTI8TUT+Psg2drWlxRg= -github.com/alibabacloud-go/tea-utils v1.3.1/go.mod h1:EI/o33aBfj3hETm4RLiAxF/ThQdSngxrpF8rKUDJjPE= -github.com/alibabacloud-go/tea-utils/v2 v2.0.5/go.mod h1:dL6vbUT35E4F4bFTHL845eUloqaerYBYPsdWR2/jhe4= -github.com/alibabacloud-go/tea-utils/v2 v2.0.7 h1:WDx5qW3Xa5ZgJ1c8NfqJkF6w+AU5wB8835UdhPr6Ax0= -github.com/alibabacloud-go/tea-utils/v2 v2.0.7/go.mod h1:qxn986l+q33J5VkialKMqT/TTs3E+U9MJpd001iWQ9I= -github.com/aliyun/credentials-go v1.1.2/go.mod h1:ozcZaMR5kLM7pwtCMEpVmQ242suV6qTJya2bDq4X1Tw= -github.com/aliyun/credentials-go v1.3.1/go.mod h1:8jKYhQuDawt8x2+fusqa1Y6mPxemTsBEN04dgcAcYz0= -github.com/aliyun/credentials-go v1.3.6/go.mod h1:1LxUuX7L5YrZUWzBrRyk0SwSdH4OmPrib8NVePL3fxM= -github.com/aliyun/credentials-go v1.4.5/go.mod h1:Jm6d+xIgwJVLVWT561vy67ZRP4lPTQxMbEYRuT2Ti1U= -github.com/aliyun/credentials-go v1.4.7 h1:T17dLqEtPUFvjDRRb5giVvLh6dFT8IcNFJJb7MeyCxw= -github.com/aliyun/credentials-go v1.4.7/go.mod h1:Jm6d+xIgwJVLVWT561vy67ZRP4lPTQxMbEYRuT2Ti1U= +github.com/aliyun/alibaba-cloud-sdk-go v1.63.100 h1:yUkCbrSM1cWtgBfRVKMQtdt22KhDvKY7g4V+92eG9wA= +github.com/aliyun/alibaba-cloud-sdk-go v1.63.100/go.mod h1:SOSDHfe1kX91v3W5QiBsWSLqeLxImobbMX1mxrFHsVQ= github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA= github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= @@ -191,24 +147,21 @@ github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= -github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg= -github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= -github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/avast/retry-go v3.0.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY= github.com/aws/aws-sdk-go v1.40.45/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= github.com/aws/aws-sdk-go-v2 v1.9.1/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4= -github.com/aws/aws-sdk-go-v2 v1.39.2 h1:EJLg8IdbzgeD7xgvZ+I8M1e0fL0ptn/M47lianzth0I= -github.com/aws/aws-sdk-go-v2 v1.39.2/go.mod h1:sDioUELIUO9Znk23YVmIk86/9DOpkbyyVb1i/gUNFXY= -github.com/aws/aws-sdk-go-v2/config v1.31.12 h1:pYM1Qgy0dKZLHX2cXslNacbcEFMkDMl+Bcj5ROuS6p8= -github.com/aws/aws-sdk-go-v2/config v1.31.12/go.mod h1:/MM0dyD7KSDPR+39p9ZNVKaHDLb9qnfDurvVS2KAhN8= -github.com/aws/aws-sdk-go-v2/credentials v1.18.16 h1:4JHirI4zp958zC026Sm+V4pSDwW4pwLefKrc0bF2lwI= -github.com/aws/aws-sdk-go-v2/credentials v1.18.16/go.mod h1:qQMtGx9OSw7ty1yLclzLxXCRbrkjWAM7JnObZjmCB7I= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.9 h1:Mv4Bc0mWmv6oDuSWTKnk+wgeqPL5DRFu5bQL9BGPQ8Y= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.9/go.mod h1:IKlKfRppK2a1y0gy1yH6zD+yX5uplJ6UuPlgd48dJiQ= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.9 h1:se2vOWGD3dWQUtfn4wEjRQJb1HK1XsNIt825gskZ970= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.9/go.mod h1:hijCGH2VfbZQxqCDN7bwz/4dzxV+hkyhjawAtdPWKZA= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.9 h1:6RBnKZLkJM4hQ+kN6E7yWFveOTg8NLPHAkqrs4ZPlTU= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.9/go.mod h1:V9rQKRmK7AWuEsOMnHzKj8WyrIir1yUJbZxDuZLFvXI= +github.com/aws/aws-sdk-go-v2 v1.36.3 h1:mJoei2CxPutQVxaATCzDUjcZEjVRdpsiiXi2o38yqWM= +github.com/aws/aws-sdk-go-v2 v1.36.3/go.mod h1:LLXuLpgzEbD766Z5ECcRmi8AzSwfZItDtmABVkRLGzg= +github.com/aws/aws-sdk-go-v2/config v1.29.9 h1:Kg+fAYNaJeGXp1vmjtidss8O2uXIsXwaRqsQJKXVr+0= +github.com/aws/aws-sdk-go-v2/config v1.29.9/go.mod h1:oU3jj2O53kgOU4TXq/yipt6ryiooYjlkqqVaZk7gY/U= +github.com/aws/aws-sdk-go-v2/credentials v1.17.62 h1:fvtQY3zFzYJ9CfixuAQ96IxDrBajbBWGqjNTCa79ocU= +github.com/aws/aws-sdk-go-v2/credentials v1.17.62/go.mod h1:ElETBxIQqcxej++Cs8GyPBbgMys5DgQPTwo7cUPDKt8= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30 h1:x793wxmUWVDhshP8WW2mlnXuFrO4cOd3HLBroh1paFw= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30/go.mod h1:Jpne2tDnYiFascUEs2AWHJL9Yp7A5ZVy3TNyxaAjD6M= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 h1:ZK5jHhnrioRkUNOc+hOgQKlUL5JeC3S6JgLxtQ+Rm0Q= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34/go.mod h1:p4VfIceZokChbA9FzMbRGz5OV+lekcVtHlPKEO0gSZY= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 h1:SZwFm17ZUNNg5Np0ioo/gq8Mn6u9w19Mri8DnJ15Jf0= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34/go.mod h1:dFZsC0BLo346mvKQLWmoJxT+Sjp+qcVR1tRVHQGOH9Q= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 h1:bIqFDwgGXXN1Kpp99pDOdKMTTb5d2KyU5X/BZxjOkRo= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3/go.mod h1:H5O/EsxDWyU+LP/V8i5sm8cxoZgc2fdNR9bxlOFrQTo= github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.8.1/go.mod h1:CM+19rL1+4dFWnOQKwDc7H1KwXTz+h61oUSHyhV0b3o= @@ -216,32 +169,30 @@ github.com/aws/aws-sdk-go-v2/service/ec2 v1.203.1 h1:ZgY9zeVAe+54Qa7o1GXKRNTez79 github.com/aws/aws-sdk-go-v2/service/ec2 v1.203.1/go.mod h1:0naMk66LtdeTmE+1CWQTKwtzOQ2t8mavOhMhR0Pv1m0= github.com/aws/aws-sdk-go-v2/service/ecs v1.53.15 h1:uH0DMwDjLGgjjYMk3M1MXHggk37trTiJIvwyJNP17Ig= github.com/aws/aws-sdk-go-v2/service/ecs v1.53.15/go.mod h1:49tE5yYdlAHqZIO8u5+u9Xy9k8IaV0v5cstZrjnX5+c= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.1 h1:oegbebPEMA/1Jny7kvwejowCaHz1FWZAQ94WXFNCyTM= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.1/go.mod h1:kemo5Myr9ac0U9JfSjMo9yHLtw+pECEHsFtJ9tqCEI8= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.9 h1:5r34CgVOD4WZudeEKZ9/iKpiT6cM1JyEROpXjOcdWv8= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.9/go.mod h1:dB12CEbNWPbzO2uC6QSWHteqOg4JfBVJOojbAoAUb5I= -github.com/aws/aws-sdk-go-v2/service/lightsail v1.50.0 h1:JOLRYFWMMKUABCp94HHfo0JBVQDVTLXOvWWphjpBBiQ= -github.com/aws/aws-sdk-go-v2/service/lightsail v1.50.0/go.mod h1:WEOSRNyfIfvgrD9MuSIGrogKyuFahaVMziVq1pHI0NQ= -github.com/aws/aws-sdk-go-v2/service/route53 v1.58.4 h1:KycXrohD5OxAZ5h02YechO2gevvoHfAPAaJM5l8zqb0= -github.com/aws/aws-sdk-go-v2/service/route53 v1.58.4/go.mod h1:xNLZLn4SusktBQ5moqUOgiDKGz3a7vHwF4W0KD+WBPc= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 h1:eAh2A4b5IzM/lum78bZ590jy36+d/aFLgKF/4Vd1xPE= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3/go.mod h1:0yKJC/kb8sAnmlYa6Zs3QVYqaC8ug2AbnNChv5Ox3uA= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 h1:dM9/92u2F1JbDaGooxTq18wmmFzbJRfXfVfy96/1CXM= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15/go.mod h1:SwFBy2vjtA0vZbjjaFtfN045boopadnoVPhu4Fv66vY= +github.com/aws/aws-sdk-go-v2/service/lightsail v1.43.1 h1:0j58UseBtLuBcP6nY2z4SM1qZEvLF0ylyH6+ggnphLg= +github.com/aws/aws-sdk-go-v2/service/lightsail v1.43.1/go.mod h1:Qy22QnQSdHbZwMZrarsWZBIuK51isPlkD+Z4sztxX0o= +github.com/aws/aws-sdk-go-v2/service/route53 v1.50.0 h1:/nkJHXtJXJeelXHqG0898+fWKgvfaXBhGzbCsSmn9j8= +github.com/aws/aws-sdk-go-v2/service/route53 v1.50.0/go.mod h1:kGYOjvTa0Vw0qxrqrOLut1vMnui6qLxqv/SX3vYeM8Y= github.com/aws/aws-sdk-go-v2/service/ssm v1.56.13 h1:JfPeW7F6Y+VqBg6p+8zQv4wlgceguYu5ZT0USEGZ89g= github.com/aws/aws-sdk-go-v2/service/ssm v1.56.13/go.mod h1:EonGQFn66wZkJJrrKXrryrxoS3V30rcHvaWvc6oGHCI= -github.com/aws/aws-sdk-go-v2/service/sso v1.29.6 h1:A1oRkiSQOWstGh61y4Wc/yQ04sqrQZr1Si/oAXj20/s= -github.com/aws/aws-sdk-go-v2/service/sso v1.29.6/go.mod h1:5PfYspyCU5Vw1wNPsxi15LZovOnULudOQuVxphSflQA= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.1 h1:5fm5RTONng73/QA73LhCNR7UT9RpFH3hR6HWL6bIgVY= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.1/go.mod h1:xBEjWD13h+6nq+z4AkqSfSvqRKFgDIQeaMguAJndOWo= -github.com/aws/aws-sdk-go-v2/service/sts v1.38.6 h1:p3jIvqYwUZgu/XYeI48bJxOhvm47hZb5HUQ0tn6Q9kA= -github.com/aws/aws-sdk-go-v2/service/sts v1.38.6/go.mod h1:WtKK+ppze5yKPkZ0XwqIVWD4beCwv056ZbPQNoeHqM8= +github.com/aws/aws-sdk-go-v2/service/sso v1.25.1 h1:8JdC7Gr9NROg1Rusk25IcZeTO59zLxsKgE0gkh5O6h0= +github.com/aws/aws-sdk-go-v2/service/sso v1.25.1/go.mod h1:qs4a9T5EMLl/Cajiw2TcbNt2UNo/Hqlyp+GiuG4CFDI= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.29.1 h1:KwuLovgQPcdjNMfFt9OhUd9a2OwcOKhxfvF4glTzLuA= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.29.1/go.mod h1:MlYRNmYu/fGPoxBQVvBYr9nyr948aY/WLUvwBMBJubs= +github.com/aws/aws-sdk-go-v2/service/sts v1.33.17 h1:PZV5W8yk4OtH1JAuhV2PXwwO9v5G5Aoj+eMCn4T+1Kc= +github.com/aws/aws-sdk-go-v2/service/sts v1.33.17/go.mod h1:cQnB8CUnxbMU82JvlqjKR2HBOm3fe9pWorWBza6MBJ4= github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= -github.com/aws/smithy-go v1.23.0 h1:8n6I3gXzWJB2DxBDnfxgBaSX6oe0d/t10qGz7OKqMCE= -github.com/aws/smithy-go v1.23.0/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI= -github.com/aziontech/azionapi-go-sdk v0.143.0 h1:4eEBlYT10prgeCVTNR9FIc7f59Crbl2zrH1a4D1BUqU= -github.com/aziontech/azionapi-go-sdk v0.143.0/go.mod h1:cA5DY/VP4X5Eu11LpQNzNn83ziKjja7QVMIl4J45feA= -github.com/baidubce/bce-sdk-go v0.9.248 h1:vB5OMuEC2xnO197M6OWUi24C8mYOZHKXT/8HuKQJUhU= -github.com/baidubce/bce-sdk-go v0.9.248/go.mod h1:zbYJMQwE4IZuyrJiFO8tO8NbtYiKTFTbwh4eIsqjVdg= +github.com/aws/smithy-go v1.22.2 h1:6D9hW43xKFrRx/tXXfAlIZc4JI+yQe6snnWcQyxSyLQ= +github.com/aws/smithy-go v1.22.2/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= +github.com/baidubce/bce-sdk-go v0.9.223 h1:vvDeIemf7ePPP59nLHCntQ/vS++ok2HKbRPgmz1VZKU= +github.com/baidubce/bce-sdk-go v0.9.223/go.mod h1:zbYJMQwE4IZuyrJiFO8tO8NbtYiKTFTbwh4eIsqjVdg= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= -github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o= -github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= +github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= @@ -264,8 +215,6 @@ github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInq github.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= -github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM= -github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= @@ -283,23 +232,21 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= +github.com/civo/civogo v0.3.11 h1:mON/fyrV946Sbk6paRtOSGsN+asCgCmHCgArf5xmGxM= +github.com/civo/civogo v0.3.11/go.mod h1:7+GeeFwc4AYTULaEshpT2vIcl3Qq8HPoxA17viX3l6g= github.com/clbanning/mxj v1.8.4/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng= -github.com/clbanning/mxj/v2 v2.7.0 h1:WA/La7UGCanFe5NpHF0Q3DNtnCsVoxbPKuyBNHWRyME= -github.com/clbanning/mxj/v2 v2.7.0/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cloudflare/cloudflare-go v0.115.0 h1:84/dxeeXweCc0PN5Cto44iTA8AkG1fyT11yPO5ZB7sM= +github.com/cloudflare/cloudflare-go v0.115.0/go.mod h1:Ds6urDwn/TF2uIU24mu7H91xkKP8gSAHxQ44DSZgVmU= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/containerd/containerd v1.7.23 h1:H2CClyUkmpKAGlhQp95g2WXHfLYc7whAuvZGBNYOOwQ= -github.com/containerd/containerd v1.7.23/go.mod h1:7QUzfURqZWCZV7RLNEn1XjUCQLEf0bkaK4GjUaZehxw= -github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI= -github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M= -github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE= -github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk= +github.com/containerd/containerd v1.7.20 h1:Sl6jQYk3TRavaU83h66QMbI2Nqg9Jm6qzwX57Vsn1SQ= +github.com/containerd/containerd v1.7.20/go.mod h1:52GsS5CwquuqPuLncsXwG0t2CiUce+KsNHJZQJvAgR0= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= -github.com/containerd/platforms v1.0.0-rc.1 h1:83KIq4yy1erSRgOVHNk1HYdPvzdJ5CnsWaRoJX4C41E= -github.com/containerd/platforms v1.0.0-rc.1/go.mod h1:J71L7B+aiM5SdIEqmd9wp6THLVRzJGXfNuWCZCllLA4= +github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A= +github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw= github.com/containous/alice v0.0.0-20181107144136-d83ebdd94cbd h1:0n+lFLh5zU0l6KSk3KpnDwfbPGAR44aRLgTbCnhRBHU= github.com/containous/alice v0.0.0-20181107144136-d83ebdd94cbd/go.mod h1:BbQgeDS5i0tNvypwEoF1oNjOJw8knRAE1DnVvjDstcQ= github.com/containous/go-http-auth v0.4.1-0.20200324110947-a37a7636d23e h1:D+uTEzDZc1Fhmd0Pq06c+O9+KkAyExw0eVmu/NOqaHU= @@ -345,12 +292,12 @@ github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= -github.com/dnsimple/dnsimple-go/v4 v4.0.0 h1:nUCICZSyZDiiqimAAL+E8XL+0sKGks5VRki5S8XotRo= -github.com/dnsimple/dnsimple-go/v4 v4.0.0/go.mod h1:AXT2yfAFOntJx6iMeo1J/zKBw0ggXFYBt4e97dqqPnc= -github.com/docker/cli v28.3.3+incompatible h1:fp9ZHAr1WWPGdIWBM1b3zLtgCF+83gRdVMTJsUeiyAo= -github.com/docker/cli v28.3.3+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= -github.com/docker/docker v28.3.3+incompatible h1:Dypm25kh4rmk49v1eiVbsAtpAsYURjYkaKubwuBdxEI= -github.com/docker/docker v28.3.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/dnsimple/dnsimple-go v1.7.0 h1:JKu9xJtZ3SqOC+BuYgAWeab7+EEx0sz422vu8j611ZY= +github.com/dnsimple/dnsimple-go v1.7.0/go.mod h1:EKpuihlWizqYafSnQHGCd/gyvy3HkEQJ7ODB4KdV8T8= +github.com/docker/cli v27.1.1+incompatible h1:goaZxOqs4QKxznZjjBWKONQci/MywhtRv2oNn0GkeZE= +github.com/docker/cli v27.1.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY= +github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= @@ -372,17 +319,18 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.m github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/evanphx/json-patch/v5 v5.9.11 h1:/8HVnzMq13/3x9TPvjG08wUGqBTmZBsCWzjTM0wiaDU= -github.com/evanphx/json-patch/v5 v5.9.11/go.mod h1:3j+LviiESTElxA4p3EMKAB9HXj3/XEtnUf6OZxqIQTM= -github.com/exoscale/egoscale/v3 v3.1.27 h1:vKdWZG8QFDc7rY7lCfcuudO+ovyp5psYjFwKVqmkhCE= -github.com/exoscale/egoscale/v3 v3.1.27/go.mod h1:0iY8OxgHJCS5TKqDNhwOW95JBKCnBZl3YGU4Yt+NqkU= +github.com/evanphx/json-patch v5.7.0+incompatible h1:vgGkfT/9f8zE6tvSCe74nfpAVDQ2tG6yudJd8LBksgI= +github.com/evanphx/json-patch v5.7.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg= +github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ= +github.com/exoscale/egoscale/v3 v3.1.13 h1:CAGC7QRjp2AiGj01agsSD0VKCp4OZmW5f51vV2IguNQ= +github.com/exoscale/egoscale/v3 v3.1.13/go.mod h1:t9+MpSEam94na48O/xgvvPFpQPRiwZ3kBN4/UuQtKco= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= -github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= -github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= -github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= +github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= +github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= @@ -397,12 +345,12 @@ github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7z github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= -github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= -github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= +github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= -github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= -github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= +github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= +github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= github.com/getkin/kin-openapi v0.61.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4= github.com/getkin/kin-openapi v0.87.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= @@ -413,14 +361,8 @@ github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwv github.com/gin-gonic/gin v1.7.4/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY= github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= -github.com/go-acme/alidns-20150109/v4 v4.6.1 h1:Dch3aWRcw4U62+jKPjPQN3iW3TPvgIywATbvHzojXeo= -github.com/go-acme/alidns-20150109/v4 v4.6.1/go.mod h1:RBcqBA5IvUWtlpjx6dC6EkPVyBNLQ+mR18XoaP38BFY= -github.com/go-acme/lego/v4 v4.27.0 h1:cIhWd7Uj4BNFLEF3IpwuMkukVVRs5qjlp4KdUGa75yU= -github.com/go-acme/lego/v4 v4.27.0/go.mod h1:9FfNZHZmg6hf5CWOp4Lzo4gU8aBEvqZvrwdkBboa+4g= -github.com/go-acme/tencentclouddnspod v1.1.10 h1:ERVJ4mc3cT4Nb3+n6H/c1AwZnChGBqLoymE0NVYscKI= -github.com/go-acme/tencentclouddnspod v1.1.10/go.mod h1:Bo/0YQJ/99FM+44HmCQkByuptX1tJsJ9V14MGV/2Qco= -github.com/go-acme/tencentedgdeone v1.1.19 h1:1jdEpMITrDuXHnu7QLOy2hpLW0BlDof70/KuRT+EiTo= -github.com/go-acme/tencentedgdeone v1.1.19/go.mod h1:gpu7HvXfcKWBrq5HAEBowZNkdk7JFPkagRzC/infUY0= +github.com/go-acme/lego/v4 v4.23.1 h1:lZ5fGtGESA2L9FB8dNTvrQUq3/X4QOb8ExkKyY7LSV4= +github.com/go-acme/lego/v4 v4.23.1/go.mod h1:7UMVR7oQbIYw6V7mTgGwi4Er7B6Ww0c+c8feiBM0EgI= github.com/go-chi/chi/v5 v5.0.0/go.mod h1:BBug9lr0cqtdAhsu6R4AAdvufI0/XBzAQSsUqJpoZOs= github.com/go-cmd/cmd v1.0.5/go.mod h1:y8q8qlK5wQibcw63djSl/ntiHUHXHGdCkPk0j4QeW4s= github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w= @@ -428,8 +370,8 @@ github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-jose/go-jose/v4 v4.1.3 h1:CVLmWDhDVRa6Mi/IgCgaopNosCaHz7zrMeF9MlZRkrs= -github.com/go-jose/go-jose/v4 v4.1.3/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08= +github.com/go-jose/go-jose/v4 v4.0.5 h1:M6T8+mKZl/+fNNuFHvGIzDz7BTLQPIounk/b9dw3AaE= +github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JSllnOrmmBOA= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.12.0/go.mod h1:lHd+EkCZPIwYItmGDDRdhinkzX2A1sj+M9biaEaizzs= @@ -445,8 +387,8 @@ github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA= github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= -github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= @@ -461,8 +403,6 @@ github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDsl github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= -github.com/go-ozzo/ozzo-validation/v4 v4.3.0 h1:byhDUpfEwjsVQb1vBunvIjh2BHQ9ead57VkAEY4V+Es= -github.com/go-ozzo/ozzo-validation/v4 v4.3.0/go.mod h1:2NKgrcHl3z6cJs+3Oo940FPRiTzuqKbvfrL2RxCj6Ew= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= @@ -477,17 +417,16 @@ github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91 github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= github.com/go-playground/validator/v10 v10.9.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= -github.com/go-playground/validator/v10 v10.23.0 h1:/PwmTwZhS0dPkav3cdK9kV1FsAmrL8sThn8IHr/sO+o= -github.com/go-playground/validator/v10 v10.23.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= +github.com/go-playground/validator/v10 v10.16.0 h1:x+plE831WK4vaKHO/jpgUGsvLKIqRRkz6M78GuJAfGE= +github.com/go-playground/validator/v10 v10.16.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= github.com/go-resty/resty/v2 v2.16.5 h1:hBKqmWrr7uRc3euHVqmh1HTHcKn99Smr7o5spptdhTM= github.com/go-resty/resty/v2 v2.16.5/go.mod h1:hkJtXbA2iKHzJheXYvQ8snQES5ZLGKMwQ07xAwp/fiA= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= -github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= -github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= +github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss= +github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/go-zookeeper/zk v1.0.2/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw= github.com/go-zookeeper/zk v1.0.3 h1:7M2kwOsc//9VeeFiPtf+uSJlVpU66x9Ba5+8XK7/TDg= github.com/go-zookeeper/zk v1.0.3/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw= @@ -500,26 +439,25 @@ github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6Wezm github.com/gobwas/ws v1.0.2 h1:CoAavW/wd/kulfZmSIBt6p24n4j7tHgNVCjsfHVNUbo= github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= github.com/goccy/go-json v0.7.8/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= -github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= -github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= -github.com/goccy/go-yaml v1.9.8/go.mod h1:JubOolP3gh0HpiBc4BLRD4YmjEjHAmIIB2aaXKkTfoE= -github.com/goccy/go-yaml v1.11.3 h1:B3W9IdWbvrUu2OYQGwvU1nZtvMQJPBKgBUuweJjLj6I= -github.com/goccy/go-yaml v1.11.3/go.mod h1:wKnAMd44+9JAAnGQpWVEgBzGt3YuTaQ4uXoHvE4m7WU= +github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= +github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E= github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A= github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI= github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= -github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo= -github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= +github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8= +github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -559,8 +497,8 @@ github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEW github.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219/go.mod h1:/X8TswGSh1pIozq4ZwCfxS0WA5JGXguxk94ar/4c87Y= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= -github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= +github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= +github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= @@ -599,8 +537,8 @@ github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad h1:a6HEuzUHeKH6hwfN/ZoQgRgVIWFJljSWa/zetS2WTvg= -github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= +github.com/google/pprof v0.0.0-20240910150728-a0b0bb1d4134 h1:c5FlPPgxOn7kJz3VoPLkQYQXGBS3EklQ4Zfi57uOuqQ= +github.com/google/pprof v0.0.0-20240910150728-a0b0bb1d4134/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0= github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM= @@ -613,15 +551,14 @@ github.com/googleapis/enterprise-certificate-proxy v0.3.6 h1:GW/XbdyBFQ8Qe+YAmFU github.com/googleapis/enterprise-certificate-proxy v0.3.6/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/gax-go/v2 v2.15.0 h1:SyjDc1mGgZU5LncH8gimWo9lW1DtIfPibOG81vgd/bo= -github.com/googleapis/gax-go/v2 v2.15.0/go.mod h1:zVVkkxAQHa1RQpg9z2AUCMnKhi0Qld9rcmyfL1OZhoc= +github.com/googleapis/gax-go/v2 v2.14.1 h1:hb0FFeiPaQskmvakKu5EbCbpntQn48jyHuvrkurSS/Q= +github.com/googleapis/gax-go/v2 v2.14.1/go.mod h1:Hb/NubMaVM88SrNkvl8X/o8XWwDJEPqouaLeN2IUxoA= github.com/gophercloud/gophercloud v1.3.0/go.mod h1:aAVqcocTSXh2vYFZ1JTvx4EQmfgzxRcNupUfxZbBNDM= github.com/gophercloud/gophercloud v1.14.1 h1:DTCNaTVGl8/cFu58O1JwWgis9gtISAFONqpMKNg/Vpw= github.com/gophercloud/gophercloud v1.14.1/go.mod h1:aAVqcocTSXh2vYFZ1JTvx4EQmfgzxRcNupUfxZbBNDM= github.com/gophercloud/utils v0.0.0-20231010081019-80377eca5d56 h1:sH7xkTfYzxIEgzq1tDHIMKRh1vThOEOGNsettdEeLbE= github.com/gophercloud/utils v0.0.0-20231010081019-80377eca5d56/go.mod h1:VSalo4adEk+3sNkmVJLnhHoOyOYYS8sTWLG4mv5BKto= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= @@ -635,8 +572,10 @@ github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmg github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 h1:8Tjv8EJ+pM1xP8mK6egEbD1OgnVTyacbefKhmbLhIhU= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2/go.mod h1:pkJQ2tZHJ0aFOVEEot6oZmaVEZcRme73eIFmhiVuRWs= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.23.0 h1:ad0vkEBuk23VJzZR9nkLVG0YAoN9coASF1GusYX6AlU= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.23.0/go.mod h1:igFoXX2ELCW06bol23DWPB5BEWfZISOzSP5K2sbLea0= +github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw= +github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= github.com/hashicorp/consul/api v1.10.1/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M= github.com/hashicorp/consul/api v1.26.1 h1:5oSXOO5fboPZeW5SN+TdGFP/BILDgBm19OrPZ/pICIM= @@ -669,8 +608,8 @@ github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+ github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= -github.com/hashicorp/go-retryablehttp v0.7.8 h1:ylXZWnqa7Lhqpk0L1P1LzDtGcCR0rPVUrx/c8Unxc48= -github.com/hashicorp/go-retryablehttp v0.7.8/go.mod h1:rjiScheydd+CxvumBsIrFKlx3iS0jrZ7LvzFGFmuKbw= +github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU= +github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= @@ -714,8 +653,8 @@ github.com/http-wasm/http-wasm-host-go v0.7.0/go.mod h1:adXKcLmL7yuavH/e0kBAp7b3 github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI= github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= -github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.172 h1:68VUVbLKwBxPh8tjCXwnLO/d8/thdZ+ExpxdFMEdK5A= -github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.172/go.mod h1:M+yna96Fx9o5GbIUnF3OvVvQGjgfVSyeJbV9Yb1z/wI= +github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.141 h1:8i57QAi5u+iPAYze92bkIvZoHiS0J45ndul5glr/NE8= +github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.141/go.mod h1:Y/+YLCFCJtS29i2MbYPTUlNNfwXvkzEsZKR0imY/2aY= github.com/hudl/fargo v1.4.0/go.mod h1:9Ai6uvFy5fQNq6VPKtg+Ceq1+eTY4nKUlR2JElEOcDo= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= @@ -731,18 +670,20 @@ github.com/influxdata/influxdb1-client v0.0.0-20200827194710-b269163b24ab h1:HqW github.com/influxdata/influxdb1-client v0.0.0-20200827194710-b269163b24ab/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839 h1:W9WBk7wlPfJLvMCdtV4zPulc4uCPrlywQOmbFOhgQNU= github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo= -github.com/infobloxopen/infoblox-go-client/v2 v2.10.0 h1:AKsihjFT/t6Y0keEv3p59DACcOuh0inWXdUB0ZOzYH0= -github.com/infobloxopen/infoblox-go-client/v2 v2.10.0/go.mod h1:NeNJpz09efw/edzqkVivGv1bWqBXTomqYBRFbP+XBqg= +github.com/infobloxopen/infoblox-go-client/v2 v2.9.0 h1:wS8kTlQVeVbrepeY83s9X+XdSa6Qah5KO+tdW+zRQXU= +github.com/infobloxopen/infoblox-go-client/v2 v2.9.0/go.mod h1:NeNJpz09efw/edzqkVivGv1bWqBXTomqYBRFbP+XBqg= github.com/jarcoal/httpmock v1.0.8/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik= -github.com/jarcoal/httpmock v1.4.1 h1:0Ju+VCFuARfFlhVXFc2HxlcQkfB+Xq12/EotHko+x2A= -github.com/jarcoal/httpmock v1.4.1/go.mod h1:ftW1xULwo+j0R0JJkJIIi7UKigZUXCLLanykgjwBXL0= +github.com/jarcoal/httpmock v1.3.1 h1:iUx3whfZWVf3jT01hQTO/Eo5sAYtB2/rqaUuOtpInww= +github.com/jarcoal/httpmock v1.3.1/go.mod h1:3yb8rc4BI7TCBhFY8ng0gjuLKJNquuDNiPaZjnENuYg= github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs= github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM= github.com/jcmturner/gofork v1.0.0/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o= github.com/jcmturner/goidentity/v6 v6.0.1/go.mod h1:X1YW3bgtvwAXju7V3LCIMpY0Gbxyjn/mY9zx4tFonSg= github.com/jcmturner/gokrb5/v8 v8.4.2/go.mod h1:sb+Xq/fTY5yktf/VxLsE3wlfPqQjp0aWNYyvBVK62bc= github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc= +github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= @@ -758,9 +699,8 @@ github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/u github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/json-iterator/go v1.1.13-0.20220915233716-71ac16282d12 h1:9Nu54bhS/H/Kgo2/7xNSUuC5G28VR8ljfrLKU2G4IjU= -github.com/json-iterator/go v1.1.13-0.20220915233716-71ac16282d12/go.mod h1:TBzl5BIHNXfS9+C35ZyJaklL7mLDbgUkcgXzSLa8Tk0= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= @@ -769,16 +709,17 @@ github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8 github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213 h1:qGQQKEcAR99REcMpsXCp3lJ03zYT1PkRd3kQGPn9GVg= github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw= -github.com/keybase/go-keychain v0.0.1 h1:way+bWYa6lDppZoZcgMbYsvC7GxljxrskdNInRtuthU= -github.com/keybase/go-keychain v0.0.1/go.mod h1:PdEILRW3i9D8JcdM+FmY6RwkHGnhHxXwkPPMeUgOK1k= +github.com/keybase/go-keychain v0.0.0-20231219164618-57a3676c3af6 h1:IsMZxCuZqKuao2vNdfD82fjjgPLfyHLpR41Z88viRWs= +github.com/keybase/go-keychain v0.0.0-20231219164618-57a3676c3af6/go.mod h1:3VeWNIJaW+O5xpRQbPp0Ybqu1vJd/pm7s2F473HRrkw= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= -github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= +github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= +github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= +github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= @@ -801,10 +742,10 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kvtools/consul v1.0.2 h1:ltPgs4Ld09Xaa7zrOJ/TewBYKAsr11/LRFpErdkb8AA= github.com/kvtools/consul v1.0.2/go.mod h1:bFnzfGJ5ZIRRXCBGBmwhJlLdEWOlrjOcS1WjyAQzaJA= -github.com/kvtools/etcdv3 v1.0.3 h1:bsaGf8Jsi8Xq6h/KVV/D7F/c1IuVQv2f7tuVxeA//fk= -github.com/kvtools/etcdv3 v1.0.3/go.mod h1:ID4AIRgCuCRzzdITo9O5RKUtLwfu/zJvCvosrFcBK4U= -github.com/kvtools/redis v1.2.0 h1:l2wT//fjNPXS66kENuUdIQrSReq4OQxmL4pKH4T65c4= -github.com/kvtools/redis v1.2.0/go.mod h1:EPXcbf7IfiIH05eBDSrR9RKQQmxI83JKL4tsUhPECuk= +github.com/kvtools/etcdv3 v1.0.2 h1:EB0mAtzqe1folE7m7Q6wnCXcGwaOmrYmsVmF3hNsTKI= +github.com/kvtools/etcdv3 v1.0.2/go.mod h1:Xr6DbwqjuCEcXAIWmXxw0DX+N5BhuvablXgN90XeqMM= +github.com/kvtools/redis v1.1.0 h1:nXRAyh2nsaWiJyrX449/qHMc3SvGUqRqRXcrA/MplEo= +github.com/kvtools/redis v1.1.0/go.mod h1:cqg3esJOIYMQ1qy5LVIbPZz9kuiBBcFREP2N5b9+Dn0= github.com/kvtools/valkeyrie v1.0.0 h1:LAITop2wPoYCMitR24GZZsW0b57hmI+ePD18VRTtOf0= github.com/kvtools/valkeyrie v1.0.0/go.mod h1:bDi/OdhJCSbGPMsCgUQl881yuEweKCSItAtTBI+ZjpU= github.com/kvtools/zookeeper v1.0.2 h1:uK0CzQa+mtKGxDDH+DeqXo2HC1Kx4hWXZ7pX/zS4aTo= @@ -821,8 +762,8 @@ github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL github.com/labstack/gommon v0.3.1/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= -github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= -github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= +github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= +github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= github.com/lestrrat-go/backoff/v2 v2.0.8/go.mod h1:rHP/q/r9aT27n24JQLa7JhSQZCKBBOiM/uP402WwN8Y= github.com/lestrrat-go/blackmagic v1.0.0/go.mod h1:TNgH//0vYSs8VXDCfkZLgIrVTTXQELZffUV0tz3MtdQ= github.com/lestrrat-go/codegen v1.0.2/go.mod h1:JhJw6OQAuPEfVKUCLItpaVLumDGWQznd1VaXrBk9TdM= @@ -830,16 +771,15 @@ github.com/lestrrat-go/httpcc v1.0.0/go.mod h1:tGS/u00Vh5N6FHNkExqGGNId8e0Big+++ github.com/lestrrat-go/iter v1.0.1/go.mod h1:zIdgO1mRKhn8l9vrZJZz9TUMMFbQbLeTsbqPDrJ/OJc= github.com/lestrrat-go/jwx v1.2.7/go.mod h1:bw24IXWbavc0R2RsOtpXL7RtMyP589yZ1+L7kd09ZGA= github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= -github.com/linode/linodego v1.60.0 h1:SgsebJFRCi+lSmYy+C40wmKZeJllGGm+W12Qw4+yVdI= -github.com/linode/linodego v1.60.0/go.mod h1:1+Bt0oTz5rBnDOJbGhccxn7LYVytXTIIfAy7QYmijDs= +github.com/linode/linodego v1.48.1 h1:Ojw1S+K5jJr1dggO8/H6r4FINxXnJbOU5GkbpaTfmhU= +github.com/linode/linodego v1.48.1/go.mod h1:fc3t60If8X+yZTFAebhCnNDFrhwQhq9HDU92WnBousQ= github.com/liquidweb/go-lwApi v0.0.0-20190605172801-52a4864d2738/go.mod h1:0sYF9rMXb0vlG+4SzdiGMXHheCZxjguMq+Zb4S2BfBs= github.com/liquidweb/liquidweb-cli v0.6.9 h1:acbIvdRauiwbxIsOCEMXGwF75aSJDbDiyAWPjVnwoYM= github.com/liquidweb/liquidweb-cli v0.6.9/go.mod h1:cE1uvQ+x24NGUL75D0QagOFCG8Wdvmwu8aL9TLmA/eQ= github.com/liquidweb/liquidweb-go v1.6.4 h1:6S0m3hHSpiLqGD7AFSb7lH/W/qr1wx+tKil9fgIbjMc= github.com/liquidweb/liquidweb-go v1.6.4/go.mod h1:B934JPIIcdA+uTq2Nz5PgOtG6CuCaEvQKe/Ge/5GgZ4= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= -github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35 h1:PpXWgLPs+Fqr325bN2FD2ISlRRztXibcX6e8f5FR5Dc= -github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.4/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= @@ -864,9 +804,8 @@ github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= -github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= @@ -889,8 +828,8 @@ github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKju github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4= github.com/miekg/dns v1.1.47/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= -github.com/miekg/dns v1.1.68 h1:jsSRkNozw7G/mnmXULynzMNIsgY2dHC8LO6U6Ij2JEA= -github.com/miekg/dns v1.1.68/go.mod h1:fujopn7TB3Pu3JM69XaawiU0wqjpL9/8xGop5UrTPps= +github.com/miekg/dns v1.1.64 h1:wuZgD9wwCE6XMT05UU/mlSko71eRSXEAm2EbjQXLKnQ= +github.com/miekg/dns v1.1.64/go.mod h1:Dzw9769uoKVaLuODMDZz9M6ynFU6Em65csPuoi8G0ck= github.com/mimuret/golang-iij-dpf v0.9.1 h1:Gj6EhHJkOhr+q2RnvRPJsPMcjuVnWPSccEHyoEehU34= github.com/mimuret/golang-iij-dpf v0.9.1/go.mod h1:sl9KyOkESib9+KRD3HaGpgi1xk7eoN2+d96LCLsME2M= github.com/minio/highwayhash v1.0.1/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= @@ -916,37 +855,30 @@ github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:F github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.4.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c h1:cqn374mizHuIWj+OSJCajGr/phAmuMug9qIX3l9CflE= -github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= -github.com/moby/go-archive v0.1.0 h1:Kk/5rdW/g+H8NHdJW2gsXyZ7UnzvJNOy6VKJqueWdcQ= -github.com/moby/go-archive v0.1.0/go.mod h1:G9B+YoujNohJmrIYFBpSd54GTUB4lt9S+xVQvsJyFuo= github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= -github.com/moby/spdystream v0.5.0 h1:7r0J1Si3QO/kjRitvSLVVFUjxMEb/YLj6S9FF62JBCU= -github.com/moby/spdystream v0.5.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI= -github.com/moby/sys/atomicwriter v0.1.0 h1:kw5D/EqkBwsBFi0ss9v1VG3wIkVhzGvLklJ+w3A14Sw= -github.com/moby/sys/atomicwriter v0.1.0/go.mod h1:Ul8oqv2ZMNHOceF643P6FKPXeCmYtlQMvpizfsSoaWs= -github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU= -github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko= -github.com/moby/sys/user v0.4.0 h1:jhcMKit7SA80hivmFJcbB1vqmw//wU61Zdui2eQXuMs= -github.com/moby/sys/user v0.4.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs= -github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= -github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= -github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ= -github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc= +github.com/moby/spdystream v0.4.0 h1:Vy79D6mHeJJjiPdFEL2yku1kl0chZpJfZcPpb16BRl8= +github.com/moby/spdystream v0.4.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI= +github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc= +github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo= +github.com/moby/sys/user v0.2.0 h1:OnpapJsRp25vkhw8TFG6OLJODNh/3rEwRWtJ3kakwRM= +github.com/moby/sys/user v0.2.0/go.mod h1:RYstrcWOJpVh+6qzUqp2bU3eaRpdiQeKGlKitaH0PM8= +github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= +github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8= -github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= @@ -957,8 +889,8 @@ github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+ github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= -github.com/namedotcom/go/v4 v4.0.2 h1:4gNkPaPRG/2tqFNUUof7jAVsA6vDutFutEOd7ivnDwA= -github.com/namedotcom/go/v4 v4.0.2/go.mod h1:J6sVueHMb0qbarPgdhrzEVhEaYp+R1SCaTGl2s6/J1Q= +github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04 h1:o6uBwrhM5C8Ll3MAAxrQxRHEu7FkapwTuI2WmL1rw4g= +github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04/go.mod h1:5sN+Lt1CaY4wsPvgQH/jsuJi4XO2ssZbdsIizr4CVC8= github.com/nats-io/jwt v1.2.2/go.mod h1:/xX356yQA6LuXI9xWW7mZNpxgF2mBmGecH+Fj34sP5Q= github.com/nats-io/jwt/v2 v2.0.3/go.mod h1:VRP+deawSXyhNjXmxPCHskrR6Mq50BqpEI5SEcNiGlY= github.com/nats-io/nats-server/v2 v2.5.0/go.mod h1:Kj86UtrXAL6LwYRA6H4RqzkHhK0Vcv2ZnKD5WbQ1t3g= @@ -966,31 +898,28 @@ github.com/nats-io/nats.go v1.12.1/go.mod h1:BPko4oXsySz4aSWeFgOHLZs3G4Jq4ZAyE6/ github.com/nats-io/nkeys v0.2.0/go.mod h1:XdZpAbhgyyODYqjTawOnIOI7VlbKSarI9Gfy1tqEu/s= github.com/nats-io/nkeys v0.3.0/go.mod h1:gvUNGjVcM2IPr5rCsRsC6Wb3Hr2CQAm08dsxtV6A5y4= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= +github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nrdcg/auroradns v1.1.0 h1:KekGh8kmf2MNwqZVVYo/fw/ZONt8QMEmbMFOeljteWo= github.com/nrdcg/auroradns v1.1.0/go.mod h1:O7tViUZbAcnykVnrGkXzIJTHoQCHcgalgAe6X1mzHfk= -github.com/nrdcg/bunny-go v0.0.0-20250327222614-988a091fc7ea h1:OSgRS4kqOs/WuxuFOObP2gwrenL4/qiKXQbQugr/Two= -github.com/nrdcg/bunny-go v0.0.0-20250327222614-988a091fc7ea/go.mod h1:IDRRngAngb2eTEaWgpO0hukQFI/vJId46fT1KErMytA= -github.com/nrdcg/desec v0.11.0 h1:XZVHy07sg12y8FozMp+l7XvzPsdzog0AYXuQMaHBsfs= -github.com/nrdcg/desec v0.11.0/go.mod h1:5+4vyhMRTs49V9CNoODF/HwT8Mwxv9DJ6j+7NekUnBs= +github.com/nrdcg/bunny-go v0.0.0-20240207213615-dde5bf4577a3 h1:ouZ2JWDl8IW5k1qugYbmpbmW8hn85Ig6buSMBRlz3KI= +github.com/nrdcg/bunny-go v0.0.0-20240207213615-dde5bf4577a3/go.mod h1:ZwadWt7mVhMHMbAQ1w8IhDqtWO3eWqWq72W7trnaiE8= +github.com/nrdcg/desec v0.10.0 h1:qrEDiqnsvNU9QE7lXIXi/tIHAfyaFXKxF2/8/52O8uM= +github.com/nrdcg/desec v0.10.0/go.mod h1:5+4vyhMRTs49V9CNoODF/HwT8Mwxv9DJ6j+7NekUnBs= github.com/nrdcg/dnspod-go v0.4.0 h1:c/jn1mLZNKF3/osJ6mz3QPxTudvPArXTjpkmYj0uK6U= github.com/nrdcg/dnspod-go v0.4.0/go.mod h1:vZSoFSFeQVm2gWLMkyX61LZ8HI3BaqtHZWgPTGKr6KQ= github.com/nrdcg/freemyip v0.3.0 h1:0D2rXgvLwe2RRaVIjyUcQ4S26+cIS2iFwnhzDsEuuwc= github.com/nrdcg/freemyip v0.3.0/go.mod h1:c1PscDvA0ukBF0dwelU/IwOakNKnVxetpAQ863RMJoM= github.com/nrdcg/goacmedns v0.2.0 h1:ADMbThobzEMnr6kg2ohs4KGa3LFqmgiBA22/6jUWJR0= github.com/nrdcg/goacmedns v0.2.0/go.mod h1:T5o6+xvSLrQpugmwHvrSNkzWht0UGAwj2ACBMhh73Cg= -github.com/nrdcg/goinwx v0.11.0 h1:GER0SE3POub7rxARt3Y3jRy1OON1hwF1LRxHz5xsFBw= -github.com/nrdcg/goinwx v0.11.0/go.mod h1:0BXSC0FxVtU4aTjX0Zw3x0DK32tjugLzeNIAGtwXvPQ= +github.com/nrdcg/goinwx v0.10.0 h1:6W630bjDxQD6OuXKqrFRYVpTt0G/9GXXm3CeOrN0zJM= +github.com/nrdcg/goinwx v0.10.0/go.mod h1:mnMSTi7CXBu2io4DzdOBoGFA1XclD0sEPWJaDhNgkA4= github.com/nrdcg/mailinabox v0.2.0 h1:IKq8mfKiVwNW2hQii/ng1dJ4yYMMv3HAP3fMFIq2CFk= github.com/nrdcg/mailinabox v0.2.0/go.mod h1:0yxqeYOiGyxAu7Sb94eMxHPIOsPYXAjTeA9ZhePhGnc= -github.com/nrdcg/namesilo v0.5.0 h1:6QNxT/XxE+f5B+7QlfWorthNzOzcGlBLRQxqi6YeBrE= -github.com/nrdcg/namesilo v0.5.0/go.mod h1:4UkwlwQfDt74kSGmhLaDylnBrD94IfflnpoEaj6T2qw= +github.com/nrdcg/namesilo v0.2.1 h1:kLjCjsufdW/IlC+iSfAqj0iQGgKjlbUUeDJio5Y6eMg= +github.com/nrdcg/namesilo v0.2.1/go.mod h1:lwMvfQTyYq+BbjJd30ylEG4GPSS6PII0Tia4rRpRiyw= github.com/nrdcg/nodion v0.1.0 h1:zLKaqTn2X0aDuBHHfyA1zFgeZfiCpmu/O9DM73okavw= github.com/nrdcg/nodion v0.1.0/go.mod h1:inbuh3neCtIWlMPZHtEpe43TmRXxHV6+hk97iCZicms= -github.com/nrdcg/oci-go-sdk/common/v1065 v1065.102.0 h1:W28ZizQSS2aRWkFA3iAP9eiZS4OLFaiv35nXtq2lW/s= -github.com/nrdcg/oci-go-sdk/common/v1065 v1065.102.0/go.mod h1:cVbzGjRhtXgrduaQbR1GR1x+VDU60NcXPMZ3+eQuiiY= -github.com/nrdcg/oci-go-sdk/dns/v1065 v1065.102.0 h1:gAOs1dkE7LFoWflzqrDqAhOprc0kF1a0fyV8C4HUPj4= -github.com/nrdcg/oci-go-sdk/dns/v1065 v1065.102.0/go.mod h1:EUBSYwop1K40VpcKy1haIK6kFK/gPT1atEk89OkY0Kg= github.com/nrdcg/porkbun v0.4.0 h1:rWweKlwo1PToQ3H+tEO9gPRW0wzzgmI/Ob3n2Guticw= github.com/nrdcg/porkbun v0.4.0/go.mod h1:/QMskrHEIM0IhC/wY7iTCUgINsxdT2WcOphktJ9+Q54= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= @@ -1008,25 +937,30 @@ github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= github.com/onsi/ginkgo/v2 v2.0.0/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= -github.com/onsi/ginkgo/v2 v2.23.3 h1:edHxnszytJ4lD9D5Jjc4tiDkPBZ3siDeJJkUZJJVkp0= -github.com/onsi/ginkgo/v2 v2.23.3/go.mod h1:zXTP6xIp3U8aVuXN8ENK9IXRaTjFnpVB9mGmaSRvxnM= +github.com/onsi/ginkgo/v2 v2.20.2 h1:7NVCeyIWROIAheY21RLS+3j2bb52W0W82tkberYytp4= +github.com/onsi/ginkgo/v2 v2.20.2/go.mod h1:K9gyxPIlb+aIvnZ8bd9Ak+YP18w3APlR+5coaZoE2ag= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.13.0/go.mod h1:lRk9szgn8TxENtWd0Tp4c3wjlRfMTMH27I+3Je41yGY= github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs= -github.com/onsi/gomega v1.37.0 h1:CdEG8g0S133B4OswTDC/5XPSzE1OeP29QOioj2PID2Y= -github.com/onsi/gomega v1.37.0/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0= +github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= +github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= +github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= -github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= -github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= +github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= +github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= +github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b h1:FfH+VrHHk6Lxt9HdVS0PXzSXFyS2NbZKXv33FYPol0A= +github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b/go.mod h1:AC62GU6hc0BrNm+9RK9VSiwa/EUe1bkIeFORAMcHvJU= github.com/openzipkin/zipkin-go v0.2.5/go.mod h1:KpXfKdgRDnnhsxw4pNIH9Md5lyFqKUa4YDFlwRYAMyE= -github.com/ovh/go-ovh v1.9.0 h1:6K8VoL3BYjVV3In9tPJUdT7qMx9h0GExN9EXx1r2kKE= -github.com/ovh/go-ovh v1.9.0/go.mod h1:cTVDnl94z4tl8pP1uZ/8jlVxntjSIf09bNcQ5TJSC7c= +github.com/oracle/oci-go-sdk/v65 v65.87.0 h1:CeVuK8t0dYODGT3P9IDhz4vyXF8poYE1ijoiO5vrKl0= +github.com/oracle/oci-go-sdk/v65 v65.87.0/go.mod h1:IBEV9l1qBzUpo7zgGaRUhbB05BVfcDGYRFBCPlTcPp0= +github.com/ovh/go-ovh v1.7.0 h1:V14nF7FwDjQrZt9g7jzcvAAQ3HN6DNShRFRMC3jLoPw= +github.com/ovh/go-ovh v1.7.0/go.mod h1:cTVDnl94z4tl8pP1uZ/8jlVxntjSIf09bNcQ5TJSC7c= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= @@ -1034,15 +968,15 @@ github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaR github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= -github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= -github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= +github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= +github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= github.com/performancecopilot/speed/v4 v4.0.0/go.mod h1:qxrSyuDGrTOWfV+uKRFhfxw6h/4HXRGUiZiufxo49BM= github.com/peterhellberg/link v1.2.0 h1:UA5pg3Gp/E0F2WdX7GERiNrPQrM1K6CVJUUWfHa4t6c= github.com/peterhellberg/link v1.2.0/go.mod h1:gYfAh+oJgQu2SrZHg5hROVRQe1ICoK0/HHJTcE0edxc= github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= -github.com/pires/go-proxyproto v0.8.1 h1:9KEixbdJfhrbtjpz/ZwCdWDD2Xem0NZ38qMYaASJgp0= -github.com/pires/go-proxyproto v0.8.1/go.mod h1:ZKAAyp3cgy5Y5Mo4n9AlScrkCZwUy0g3Jf+slqQVcuU= +github.com/pires/go-proxyproto v0.6.1 h1:EBupykFmo22SDjv4fQVQd2J9NOoLPmyZA/15ldOGkPw= +github.com/pires/go-proxyproto v0.6.1/go.mod h1:Odh9VFOZJCf9G8cLW5o435Xf1J95Jw9Gw5rnCjcwzAY= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= @@ -1059,10 +993,10 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= -github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= -github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= -github.com/pquerna/otp v1.5.0 h1:NMMR+WrmaqXU4EzdGJEE1aUUI0AMRzsp96fFFWNPwxs= -github.com/pquerna/otp v1.5.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg= +github.com/power-devops/perfstat v0.0.0-20220216144756-c35f1ee13d7c h1:NRoLoZvkBTKvR5gQLgA3e0hqjkY9u1wm+iOL45VN/qI= +github.com/power-devops/perfstat v0.0.0-20220216144756-c35f1ee13d7c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= +github.com/pquerna/otp v1.4.0 h1:wZvl1TIVxKRThZIBiwOOHOGP/1+nZyWBil9Y2XNEDzg= +github.com/pquerna/otp v1.4.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= @@ -1070,8 +1004,8 @@ github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQ github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q= -github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0= +github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE= +github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= @@ -1086,8 +1020,8 @@ github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8b github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= github.com/prometheus/common v0.30.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io= -github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I= +github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc= +github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= @@ -1096,17 +1030,17 @@ github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+Gx github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= -github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= +github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= +github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI= github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg= -github.com/quic-go/quic-go v0.55.0 h1:zccPQIqYCXDt5NmcEabyYvOnomjs8Tlwl7tISjJh9Mk= -github.com/quic-go/quic-go v0.55.0/go.mod h1:DR51ilwU1uE164KuWXhinFcKWGlEjzys2l8zUl5Ss1U= +github.com/quic-go/quic-go v0.48.2 h1:wsKXZPeGWpMpCGSWqOcqpW2wZYic/8T3aqiOID0/KWE= +github.com/quic-go/quic-go v0.48.2/go.mod h1:yBgs3rWBOADpga7F+jJsb6Ybg1LSYiQvwWlLX+/6HMs= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/redis/go-redis/v9 v9.8.0 h1:q3nRvjrlge/6UD7eTu/DSg2uYiU2mCL0G/uzBWqhicI= -github.com/redis/go-redis/v9 v9.8.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw= +github.com/redis/go-redis/v9 v9.7.3 h1:YpPyAayJV+XErNsatSElgRZZVCwXX9QzkKYNvO7x0wM= +github.com/redis/go-redis/v9 v9.7.3/go.mod h1:bGUrSggJ9X9GUmZpZNEOQKaANxSGgOEBRltRTZHSvrA= github.com/regfish/regfish-dnsapi-go v0.1.1 h1:TJFtbePHkd47q5GZwYl1h3DIYXmoxdLjW/SBsPtB5IE= github.com/regfish/regfish-dnsapi-go v0.1.1/go.mod h1:ubIgXSfqarSnl3XHSn8hIFwFF3h0yrq0ZiWD93Y2VjY= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= @@ -1114,8 +1048,8 @@ github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6L github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= @@ -1123,28 +1057,28 @@ github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8= github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/sacloud/api-client-go v0.3.3 h1:ZpSAyGpITA8UFO3Hq4qMHZLGuNI1FgxAxo4sqBnCKDs= -github.com/sacloud/api-client-go v0.3.3/go.mod h1:0p3ukcWYXRCc2AUWTl1aA+3sXLvurvvDqhRaLZRLBwo= -github.com/sacloud/go-http v0.1.9 h1:Xa5PY8/pb7XWhwG9nAeXSrYXPbtfBWqawgzxD5co3VE= -github.com/sacloud/go-http v0.1.9/go.mod h1:DpDG+MSyxYaBwPJ7l3aKLMzwYdTVtC5Bo63HActcgoE= -github.com/sacloud/iaas-api-go v1.19.0 h1:Bw4uygqukcvlblhWrITp94nikXqy2fnKoAC6929LkIA= -github.com/sacloud/iaas-api-go v1.19.0/go.mod h1:XV995RM1I7k5AHb7UZrCVyDF/8bZXDxa+uk1EXoj/Zs= -github.com/sacloud/packages-go v0.0.11 h1:hrRWLmfPM9w7GBs6xb5/ue6pEMl8t1UuDKyR/KfteHo= -github.com/sacloud/packages-go v0.0.11/go.mod h1:XNF5MCTWcHo9NiqWnYctVbASSSZR3ZOmmQORIzcurJ8= +github.com/sacloud/api-client-go v0.2.10 h1:+rv3jDohD+pkdYwOTBiB+jZsM0xK3AxadXRzhp3q66c= +github.com/sacloud/api-client-go v0.2.10/go.mod h1:Jj3CTy2+O4bcMedVDXlbHuqqche85HEPuVXoQFhLaRc= +github.com/sacloud/go-http v0.1.8 h1:ynreWA/vnM8G2ksbMlmefBHsXURKPz49qlPRqQ9IQdw= +github.com/sacloud/go-http v0.1.8/go.mod h1:7TL7TN1fnPKHsMifIqURDkGujnKViCgEz5Ei/LQdFK8= +github.com/sacloud/iaas-api-go v1.14.0 h1:xjkFWqdo4ilTrKPNNYBNWR/CZ/kVRsJrdAHAad6J/AQ= +github.com/sacloud/iaas-api-go v1.14.0/go.mod h1:C8os2Mnj0TOmMdSllwhaDWKMVG2ysFnpe69kyA4M3V0= +github.com/sacloud/packages-go v0.0.10 h1:UiQGjy8LretewkRhsuna1TBM9Vz/l9FoYpQx+D+AOck= +github.com/sacloud/packages-go v0.0.10/go.mod h1:f8QITBh9z4IZc4yE9j21Q8b0sXEMwRlRmhhjWeDVTYs= github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= -github.com/scaleway/scaleway-sdk-go v1.0.0-beta.35 h1:8xfn1RzeI9yoCUuEwDy08F+No6PcKZGEDOQ6hrRyLts= -github.com/scaleway/scaleway-sdk-go v1.0.0-beta.35/go.mod h1:47B1d/YXmSAxlJxUJxClzHR6b3T4M1WyCvwENPQNBWc= +github.com/scaleway/scaleway-sdk-go v1.0.0-beta.32 h1:4+LP7qmsLSGbmc66m1s5dKRMBwztRppfxFKlYqYte/c= +github.com/scaleway/scaleway-sdk-go v1.0.0-beta.32/go.mod h1:kzh+BSAvpoyHHdHBCDhmSWtBc1NbLMZ2lWHqnBoxFks= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/segmentio/fasthash v1.0.3 h1:EI9+KE1EwvMLBWwjpRDc+fEM+prwxDYbslddQGtrmhM= github.com/segmentio/fasthash v1.0.3/go.mod h1:waKX8l2N8yckOgmSsXJi7x1ZfdKZ4x7KRMzBtS3oedY= github.com/selectel/domains-go v1.1.0 h1:futG50J43ALLKQAnZk9H9yOtLGnSUh7c5hSvuC5gSHo= github.com/selectel/domains-go v1.1.0/go.mod h1:SugRKfq4sTpnOHquslCpzda72wV8u0cMBHx0C0l+bzA= -github.com/selectel/go-selvpcclient/v4 v4.1.0 h1:22lBp+rzg9g2MP4iiGhpVAcCt0kMv7I7uV1W3taLSvQ= -github.com/selectel/go-selvpcclient/v4 v4.1.0/go.mod h1:eFhL1KUW159KOJVeGO7k/Uxl0TYd/sBkWXjuF5WxmYk= +github.com/selectel/go-selvpcclient/v3 v3.2.1 h1:ny6WIAMiHzKxOgOEnwcWE79wIQij1AHHylzPA41MXCw= +github.com/selectel/go-selvpcclient/v3 v3.2.1/go.mod h1:3EfSf8aEWyhspOGbvZ6mvnFg7JN5uckxNyBFPGWsXNQ= github.com/shirou/gopsutil/v3 v3.24.4 h1:dEHgzZXt4LMNm+oYELpzl9YCqV65Yr/6SfrvgRBtXeU= github.com/shirou/gopsutil/v3 v3.24.4/go.mod h1:lTd2mdiOspcqLgAnr9/nGi71NkeMpWKdmhuxm9GusH8= github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= @@ -1164,21 +1098,21 @@ github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/assertions v1.1.0 h1:MkTeG1DMwsrdH7QtLXy5W+fUxWq+vmb6cLmyJ7aRtF0= -github.com/smartystreets/assertions v1.1.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= +github.com/smartystreets/assertions v1.0.1 h1:voD4ITNjPL5jjBfgR/r8fPIIBrliWrWHeiJApdr3r4w= +github.com/smartystreets/assertions v1.0.1/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM= github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9 h1:hp2CYQUINdZMHdvTdXtPOY2ainKl4IoMcpAXEf2xj3Q= github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9/go.mod h1:SnhjPscd9TpLiy1LpzGSKh3bXCfxxXuqd9xmQJy3slM= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/smartystreets/gunit v1.0.4 h1:tpTjnuH7MLlqhoD21vRoMZbMIi5GmBsAJDFyF67GhZA= github.com/smartystreets/gunit v1.0.4/go.mod h1:EH5qMBab2UclzXUcpR8b93eHsIlp9u+pDQIRp5DZNzQ= -github.com/softlayer/softlayer-go v1.2.1 h1:8ucHxn5laVsVPb0/aMGnr6tOMt1I9BgEtU5mn70OGKw= -github.com/softlayer/softlayer-go v1.2.1/go.mod h1:Gz9/ktcmB7Z8EJlu+QEJJpkv8lAmnhYdB9Tc6gedjmo= +github.com/softlayer/softlayer-go v1.1.7 h1:SgTL+pQZt1h+5QkAhVmHORM/7N9c1X0sljJhuOIHxWE= +github.com/softlayer/softlayer-go v1.1.7/go.mod h1:WeJrBLoTJcaT8nO1azeyHyNpo/fDLtbpbvh+pzts+Qw= github.com/softlayer/xmlrpc v0.0.0-20200409220501-5f089df7cb7e h1:3OgWYFw7jxCZPcvAg+4R8A50GZ+CCkARF10lxu2qDsQ= github.com/softlayer/xmlrpc v0.0.0-20200409220501-5f089df7cb7e/go.mod h1:fKZCUVdirrxrBpwd9wb+lSoVixvpwAu8eHzbQB2tums= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= -github.com/sony/gobreaker v1.0.0 h1:feX5fGGXSl3dYd4aHZItw+FpHLvvoaqkawKjVNiFMNQ= -github.com/sony/gobreaker v1.0.0/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= +github.com/sony/gobreaker v0.5.0 h1:dRCvqm0P490vZPmy7ppEk2qCnCieBooFJ+YoXGYB+yg= +github.com/sony/gobreaker v0.5.0/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= @@ -1194,15 +1128,14 @@ github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJ github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M= -github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ= github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk= -github.com/spiffe/go-spiffe/v2 v2.5.0 h1:N2I01KCUkv1FAjZXJMwh95KK1ZIQLYbPfhaxw8WS0hE= -github.com/spiffe/go-spiffe/v2 v2.5.0/go.mod h1:P+NxobPc6wXhVtINNtFjNWGBTreew1GBUCwT2wPmb7g= +github.com/spiffe/go-spiffe/v2 v2.4.0 h1:j/FynG7hi2azrBG5cvjRcnQ4sux/VNj8FAVc99Fl66c= +github.com/spiffe/go-spiffe/v2 v2.4.0/go.mod h1:m5qJ1hGzjxjtrkGHZupoXHo/FDWwCB1MdSyBzfHugx0= github.com/stealthrocket/wasi-go v0.8.0 h1:Hwnv3CUoMhhRyero9vt1vfwaYa9tu/Z5kmCW4WeAmVI= github.com/stealthrocket/wasi-go v0.8.0/go.mod h1:PJ5oVs2E1ciOJnsTnav4nvTtEcJ4D1jUZAewS9pzuZg= github.com/stealthrocket/wazergo v0.19.1 h1:BPrITETPgSFwiytwmToO0MbUC/+RGC39JScz1JmmG6c= @@ -1212,7 +1145,6 @@ github.com/streadway/amqp v1.0.0/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1Sd github.com/streadway/handy v0.0.0-20200128134331-0f66f006fb2e/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= @@ -1225,13 +1157,14 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= +github.com/stretchr/testify v1.7.5/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stvp/go-udp-testing v0.0.0-20191102171040-06b61409b154 h1:XGopsea1Dw7ecQ8JscCNQXDGYAKDiWjDeXnpN/+BY9g= github.com/stvp/go-udp-testing v0.0.0-20191102171040-06b61409b154/go.mod h1:7jxmlfBCDBXRzr0eAQJ48XC1hBu1np4CS5+cHEYfwpc= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= @@ -1239,10 +1172,10 @@ github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8 github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/tailscale/tscert v0.0.0-20230806124524-28a91b69a046 h1:8rUlviSVOEe7TMk7W0gIPrW8MqEzYfZHpsNWSf8s2vg= github.com/tailscale/tscert v0.0.0-20230806124524-28a91b69a046/go.mod h1:kNGUQ3VESx3VZwRwA9MSCUegIl6+saPL8Noq82ozCaU= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.1.10/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.1.19/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.1.41 h1:XQDGrLX6v4McMP+2myhgQcy5JaPqSgwpLM1qa7ngUII= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.1.41/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1128 h1:NGnqDc8FQL0YdiCHgTO4Wkso6ToD8rE3JW9VOzoPBNA= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1128/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1128 h1:mrJ5Fbkd7sZIJ5F6oRfh5zebPQaudPH9Y0+GUmFytYU= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1128/go.mod h1:zbsYIBT+VTX4z4ocjTAdLBIWyNYj3z0BRqd0iPdnjsk= github.com/testcontainers/testcontainers-go v0.32.0 h1:ug1aK08L3gCHdhknlTTwWjPHPS+/alvLJU/DRxTD/ME= github.com/testcontainers/testcontainers-go v0.32.0/go.mod h1:CRHrzHLQhlXUsa5gXjTOfqIEJcrK5+xMDmBr/WMI88E= github.com/testcontainers/testcontainers-go/modules/k3s v0.32.0 h1:Z3DTMveNUqeGJZ+CXZhpvI7OF1BS71Ywi3SwoXLZ4Lc= @@ -1256,15 +1189,12 @@ github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JT github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= -github.com/tjfoc/gmsm v1.3.2/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w= github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho= github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE= +github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= -github.com/tklauser/go-sysconf v0.3.15 h1:VE89k0criAymJ/Os65CSn1IXaol+1wrsFHEB8Ol49K4= -github.com/tklauser/go-sysconf v0.3.15/go.mod h1:Dmjwr6tYFIseJw7a3dRLJfsHAMXZ3nEnL/aZY+0IuI4= +github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= -github.com/tklauser/numcpus v0.10.0 h1:18njr6LDBk1zuna922MgdjQuJFjrdppsZG60sHGfjso= -github.com/tklauser/numcpus v0.10.0/go.mod h1:BiTKazU708GQTYF4mB+cmlpT2Is1gLk7XVuEeem8LsQ= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/traefik/grpc-web v0.16.0 h1:eeUWZaFg6ZU0I9dWOYE2D5qkNzRBmXzzuRlxdltascY= github.com/traefik/grpc-web v0.16.0/go.mod h1:2ttniSv7pTgBWIU2HZLokxRfFX3SA60c/DTmQQgVml4= @@ -1272,11 +1202,15 @@ github.com/traefik/paerser v0.2.2 h1:cpzW/ZrQrBh3mdwD/jnp6aXASiUFKOVr6ldP+keJTcQ github.com/traefik/paerser v0.2.2/go.mod h1:7BBDd4FANoVgaTZG+yh26jI6CA2nds7D/4VTEdIsh24= github.com/traefik/yaegi v0.16.1 h1:f1De3DVJqIDKmnasUF6MwmWv1dSEEat0wcpXhD2On3E= github.com/traefik/yaegi v0.16.1/go.mod h1:4eVhbPb3LnD2VigQjhYbEJ69vDRFdT2HQNrXx8eEwUY= -github.com/transip/gotransip/v6 v6.26.1 h1:MeqIjkTBBsZwWAK6giZyMkqLmKMclVHEuTNmoBdx4MA= -github.com/transip/gotransip/v6 v6.26.1/go.mod h1:x0/RWGRK/zob817O3tfO2xhFoP1vu8YOHORx6Jpk80s= +github.com/transip/gotransip/v6 v6.26.0 h1:Aejfvh8rSp8Mj2GX/RpdBjMCv+Iy/DmgfNgczPDP550= +github.com/transip/gotransip/v6 v6.26.0/go.mod h1:x0/RWGRK/zob817O3tfO2xhFoP1vu8YOHORx6Jpk80s= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= +github.com/uber/jaeger-client-go v2.30.0+incompatible h1:D6wyKGCecFaSRUpo8lCVbaOOb6ThwMmTEbhRwtKR97o= +github.com/uber/jaeger-client-go v2.30.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= +github.com/uber/jaeger-lib v2.4.1+incompatible h1:td4jdvLcExb4cBISKIpHuGoVXh+dVKhn2Um6rjCsSsg= +github.com/uber/jaeger-lib v2.4.1+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= github.com/ugorji/go v1.2.6 h1:tGiWC9HENWE2tqYycIqFTNorMmFRVhNwCpDOpWqnk8E= github.com/ugorji/go v1.2.6/go.mod h1:anCg0y61KIhDlPZmnH+so+RQbysYVyDko0IMgJv0Nn0= @@ -1284,8 +1218,8 @@ github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLY github.com/ugorji/go/codec v1.2.6/go.mod h1:V6TCNZ4PHqoHGFZuSG1W8nrCzzdgA2DozYxWFFpvxTw= github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= -github.com/ultradns/ultradns-go-sdk v1.8.1-20250722213956-faef419 h1:/VaznPrb/b68e3iMvkr27fU7JqPKU4j7tIITZnjQX1k= -github.com/ultradns/ultradns-go-sdk v1.8.1-20250722213956-faef419/go.mod h1:QN0/PdenvYWB0GRMz6JJbPeZz2Lph2iys1p8AFVHm2c= +github.com/ultradns/ultradns-go-sdk v1.8.0-20241010134910-243eeec h1:2s/ghQ8wKE+UzD/hf3P4Gd1j0JI9ncbxv+nsypPoUYI= +github.com/ultradns/ultradns-go-sdk v1.8.0-20241010134910-243eeec/go.mod h1:BZr7Qs3ku1ckpqed8tCRSqTlp8NAeZfAVpfx4OzXMss= github.com/unrolled/render v1.0.2 h1:dGS3EmChQP3yOi1YeFNO/Dx+MbWZhdvhQJTXochM5bs= github.com/unrolled/render v1.0.2/go.mod h1:gN9T0NhL4Bfbwu8ann7Ry/TGHYfosul+J0obPf6NBdM= github.com/unrolled/secure v1.0.9 h1:BWRuEb1vDrBFFDdbCnKkof3gZ35I/bnHGyt0LB0TNyQ= @@ -1301,14 +1235,14 @@ github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPU github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= github.com/vinyldns/go-vinyldns v0.9.16 h1:GZJStDkcCk1F1AcRc64LuuMh+ENL8pHA0CVd4ulRMcQ= github.com/vinyldns/go-vinyldns v0.9.16/go.mod h1:5qIJOdmzAnatKjurI+Tl4uTus7GJKJxb+zitufjHs3Q= -github.com/volcengine/volc-sdk-golang v1.0.223 h1:1EEK6VOUaA2Tu0VBD4VC5iSTTFag+KuNo+Vix469Tz4= -github.com/volcengine/volc-sdk-golang v1.0.223/go.mod h1:zHJlaqiMbIB+0mcrsZPTwOb3FB7S/0MCfqlnO8R7hlM= +github.com/volcengine/volc-sdk-golang v1.0.199 h1:zv9QOqTl/IsLwtfC37GlJtcz6vMAHi+pjq8ILWjLYUc= +github.com/volcengine/volc-sdk-golang v1.0.199/go.mod h1:stZX+EPgv1vF4nZwOlEe8iGcriUPRBKX8zA19gXycOQ= github.com/vulcand/oxy/v2 v2.0.3 h1:CPWVPfW4hVZXzwwiQzpFidbnJKpahjPHezM+7TkZRNw= github.com/vulcand/oxy/v2 v2.0.3/go.mod h1:k3t+xjyqmXVh88FdFDbYmUKMEvNpaejvBW14es6H70A= github.com/vulcand/predicate v1.2.0 h1:uFsW1gcnnR7R+QTID+FVcs0sSYlIGntoGOTb3rQJt50= github.com/vulcand/predicate v1.2.0/go.mod h1:VipoNYXny6c8N381zGUWkjuuNHiRbeAZhE7Qm9c+2GA= -github.com/vultr/govultr/v3 v3.24.0 h1:fTTTj0VBve+Miy+wGhlb90M2NMDfpGFi6Frlj3HVy6M= -github.com/vultr/govultr/v3 v3.24.0/go.mod h1:9WwnWGCKnwDlNjHjtt+j+nP+0QWq6hQXzaHgddqrLWY= +github.com/vultr/govultr/v3 v3.17.0 h1:His5Jh5N8KKqaJxfy3uG6jQbLXy0TmQhNxOiRvkKk00= +github.com/vultr/govultr/v3 v3.17.0/go.mod h1:q34Wd76upKmf+vxFMgaNMH3A8BbsPBmSYZUGC8oZa5w= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= @@ -1316,21 +1250,19 @@ github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+ github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4= github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM= github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= -github.com/yandex-cloud/go-genproto v0.31.0 h1:mFMS5SD1Lt8qErwefR8ChK3d0jg0tvbDLq57IqenpTg= -github.com/yandex-cloud/go-genproto v0.31.0/go.mod h1:0LDD/IZLIUIV4iPH+YcF+jysO3jkSvADFGm4dCAuwQo= -github.com/yandex-cloud/go-sdk/services/dns v0.0.12 h1:c5TNaX7r3DqY37YJFbr7HyQFRcSe1WzCbR81LVwxXyk= -github.com/yandex-cloud/go-sdk/services/dns v0.0.12/go.mod h1:6CRtIkxq6iTSZIOT42EFns54CEr35ncECy4ix9lXUd4= -github.com/yandex-cloud/go-sdk/v2 v2.19.0 h1:Cuzjn6kkOD/KrBF/QyDbKS7b5GAu8fC2ZUjdBjit60A= -github.com/yandex-cloud/go-sdk/v2 v2.19.0/go.mod h1:4SwghU8RB4v2OQzhESgq5SF8XmCXIP80WhgrrNpetJ8= +github.com/yandex-cloud/go-genproto v0.0.0-20250319153614-fb9d3e5eb01a h1:YO8gGyAV4N5SR3NzloZ1128IahSpXWr78oU7aEe7f04= +github.com/yandex-cloud/go-genproto v0.0.0-20250319153614-fb9d3e5eb01a/go.mod h1:0LDD/IZLIUIV4iPH+YcF+jysO3jkSvADFGm4dCAuwQo= +github.com/yandex-cloud/go-sdk v0.0.0-20250320143332-9cbcfc5de4ae h1:x+uGuST05LVlgCxF5TsP8kQCCTW7uIeAQJ1dKtSmWqE= +github.com/yandex-cloud/go-sdk v0.0.0-20250320143332-9cbcfc5de4ae/go.mod h1:V71iJlJnS/NtNNdg/B7SwccBS19aXxwY3fv/wut9D74= github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= -github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM= -github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.30/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= @@ -1339,19 +1271,19 @@ github.com/yuin/gopher-lua v1.1.1 h1:kYKnWBjvbNP4XLT3+bPEwAXJx262OhaHDWDVOPjL46M github.com/yuin/gopher-lua v1.1.1/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= -github.com/zeebo/errs v1.4.0 h1:XNdoD/RRMKP7HD0UhJnIzUy74ISdGGxURlYG8HSWSfM= -github.com/zeebo/errs v1.4.0/go.mod h1:sgbWHsvVuTPHcqJJGQ1WhI5KbWlHYz+2+2C/LSEtCw4= +github.com/zeebo/errs v1.3.0 h1:hmiaKqgYZzcVgRL1Vkc1Mn2914BbzB0IBxs+ebeutGs= +github.com/zeebo/errs v1.3.0/go.mod h1:sgbWHsvVuTPHcqJJGQ1WhI5KbWlHYz+2+2C/LSEtCw4= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= -go.etcd.io/etcd/api/v3 v3.6.4 h1:7F6N7toCKcV72QmoUKa23yYLiiljMrT4xCeBL9BmXdo= -go.etcd.io/etcd/api/v3 v3.6.4/go.mod h1:eFhhvfR8Px1P6SEuLT600v+vrhdDTdcfMzmnxVXXSbk= +go.etcd.io/etcd/api/v3 v3.5.14 h1:vHObSCxyB9zlF60w7qzAdTcGaglbJOpSj1Xj9+WGxq0= +go.etcd.io/etcd/api/v3 v3.5.14/go.mod h1:BmtWcRlQvwa1h3G2jvKYwIQy4PkHlDej5t7uLMUdJUU= go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= -go.etcd.io/etcd/client/pkg/v3 v3.6.4 h1:9HBYrjppeOfFjBjaMTRxT3R7xT0GLK8EJMVC4xg6ok0= -go.etcd.io/etcd/client/pkg/v3 v3.6.4/go.mod h1:sbdzr2cl3HzVmxNw//PH7aLGVtY4QySjQFuaCgcRFAI= +go.etcd.io/etcd/client/pkg/v3 v3.5.14 h1:SaNH6Y+rVEdxfpA2Jr5wkEvN6Zykme5+YnbCkxvuWxQ= +go.etcd.io/etcd/client/pkg/v3 v3.5.14/go.mod h1:8uMgAokyG1czCtIdsq+AGyYQMvpIKnSvPjFMunkgeZI= go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= go.etcd.io/etcd/client/v3 v3.5.0/go.mod h1:AIKXXVX/DQXtfTEqBryiLTUXwON+GuvO6Z7lLS/oTh0= -go.etcd.io/etcd/client/v3 v3.6.4 h1:YOMrCfMhRzY8NgtzUsHl8hC2EBSnuqbR3dh84Uryl7A= -go.etcd.io/etcd/client/v3 v3.6.4/go.mod h1:jaNNHCyg2FdALyKWnd7hxZXZxZANb0+KGY+YQaEMISo= +go.etcd.io/etcd/client/v3 v3.5.14 h1:CWfRs4FDaDoSz81giL7zPpZH2Z35tbOrAJkkjMqOupg= +go.etcd.io/etcd/client/v3 v3.5.14/go.mod h1:k3XfdV/VIHy/97rqWjoUzrj9tk7GgJGH9J8L4dNXmAk= go.mongodb.org/mongo-driver v1.13.1 h1:YIc7HTYsKndGK4RFzJ3covLz1byri52x0IoMB0Pt/vk= go.mongodb.org/mongo-driver v1.13.1/go.mod h1:wcDf1JBCXy2mOW0bWHwO/IOYqdca1MPCwDtFu/Z9+eo= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= @@ -1362,67 +1294,53 @@ go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= -go.opentelemetry.io/collector/featuregate v1.41.0 h1:CL4UMsMQj35nMJC3/jUu8VvYB4MHirbAX4B0Z/fCVLY= -go.opentelemetry.io/collector/featuregate v1.41.0/go.mod h1:A72x92glpH3zxekaUybml1vMSv94BH6jQRn5+/htcjw= -go.opentelemetry.io/collector/pdata v1.41.0 h1:2zurAaY0FkURbLa1x7f7ag6HaNZYZKSmI4wgzDegLgo= -go.opentelemetry.io/collector/pdata v1.41.0/go.mod h1:h0OghaTYe4oRvLxK31Ny7gkyjJ1p8oniM5MiCzluQjc= -go.opentelemetry.io/contrib/bridges/otellogrus v0.13.0 h1:Nzvgkys5xSchtkWEeTQNixr9EVo+cbYCpSey2zMftXw= -go.opentelemetry.io/contrib/bridges/otellogrus v0.13.0/go.mod h1:nvmPavMmeFjktIIxQAsE265cQ9nQ5qhDV2mN5kfdPog= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 h1:q4XOmH/0opmeuJtPsbFNivyl7bCt7yRBbeEm2sC/XtQ= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0/go.mod h1:snMWehoOh2wsEwnvvwtDyFCxVeDAODenXHtn5vzrKjo= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q= -go.opentelemetry.io/contrib/propagators/autoprop v0.63.0 h1:S3+4UwR3Y1tUKklruMwOacAFInNvtuOexz4ZTmJNAyw= -go.opentelemetry.io/contrib/propagators/autoprop v0.63.0/go.mod h1:qpIuOggbbw2T9nKRaO1je/oTRKd4zslAcJonN8LYbTg= -go.opentelemetry.io/contrib/propagators/aws v1.38.0 h1:eRZ7asSbLc5dH7+TBzL6hFKb1dabz0IV51uUUwYRZts= -go.opentelemetry.io/contrib/propagators/aws v1.38.0/go.mod h1:wXqc9NTGcXapBExHBDVLEZlByu6quiQL8w7Tjgv8TCg= -go.opentelemetry.io/contrib/propagators/b3 v1.38.0 h1:uHsCCOSKl0kLrV2dLkFK+8Ywk9iKa/fptkytc6aFFEo= -go.opentelemetry.io/contrib/propagators/b3 v1.38.0/go.mod h1:wMRSZJZcY8ya9mApLLhwIMjqmApy2o/Ml+62lhvxyHU= -go.opentelemetry.io/contrib/propagators/jaeger v1.38.0 h1:nXGeLvT1QtCAhkASkP/ksjkTKZALIaQBIW+JSIw1KIc= -go.opentelemetry.io/contrib/propagators/jaeger v1.38.0/go.mod h1:oMvOXk78ZR3KEuPMBgp/ThAMDy9ku/eyUVztr+3G6Wo= -go.opentelemetry.io/contrib/propagators/ot v1.38.0 h1:k4gSyyohaDXI8F9BDXYC3uO2vr5sRNeQFMsN9Zn0EoI= -go.opentelemetry.io/contrib/propagators/ot v1.38.0/go.mod h1:2hDsuiHRO39SRUMhYGqmj64z/IuMRoxE4bBSFR82Lo8= -go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= -go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= -go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.14.0 h1:OMqPldHt79PqWKOMYIAQs3CxAi7RLgPxwfFSwr4ZxtM= -go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.14.0/go.mod h1:1biG4qiqTxKiUCtoWDPpL3fB3KxVwCiGw81j3nKMuHE= -go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.14.0 h1:QQqYw3lkrzwVsoEX0w//EhH/TCnpRdEenKBOOEIMjWc= -go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.14.0/go.mod h1:gSVQcr17jk2ig4jqJ2DX30IdWH251JcNAecvrqTxH1s= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.38.0 h1:vl9obrcoWVKp/lwl8tRE33853I8Xru9HFbw/skNeLs8= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.38.0/go.mod h1:GAXRxmLJcVM3u22IjTg74zWBrRCKq8BnOqUVLodpcpw= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.38.0 h1:Oe2z/BCg5q7k4iXC3cqJxKYg0ieRiOqF0cecFYdPTwk= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.38.0/go.mod h1:ZQM5lAJpOsKnYagGg/zV2krVqTtaVdYdDkhMoX6Oalg= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 h1:GqRJVj7UmLjCVyVJ3ZFLdPRmhDUp2zFmQe3RHIOsw24= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0/go.mod h1:ri3aaHSmCTVYu2AWv44YMauwAQc0aqI9gHKIcSbI1pU= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0 h1:lwI4Dc5leUqENgGuQImwLo4WnuXFPetmPpkLi2IrX54= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0/go.mod h1:Kz/oCE7z5wuyhPxsXDuaPteSWqjSBD5YaSdbxZYGbGk= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0 h1:aTL7F04bJHUlztTsNGJ2l+6he8c+y/b//eR0jjjemT4= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0/go.mod h1:kldtb7jDTeol0l3ewcmd8SDvx3EmIE7lyvqbasU3QC4= -go.opentelemetry.io/otel/log v0.14.0 h1:2rzJ+pOAZ8qmZ3DDHg73NEKzSZkhkGIua9gXtxNGgrM= -go.opentelemetry.io/otel/log v0.14.0/go.mod h1:5jRG92fEAgx0SU/vFPxmJvhIuDU9E1SUnEQrMlJpOno= -go.opentelemetry.io/otel/log/logtest v0.14.0 h1:BGTqNeluJDK2uIHAY8lRqxjVAYfqgcaTbVk1n3MWe5A= -go.opentelemetry.io/otel/log/logtest v0.14.0/go.mod h1:IuguGt8XVP4XA4d2oEEDMVDBBCesMg8/tSGWDjuKfoA= -go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA= -go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI= -go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E= -go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg= -go.opentelemetry.io/otel/sdk/log v0.14.0 h1:JU/U3O7N6fsAXj0+CXz21Czg532dW2V4gG1HE/e8Zrg= -go.opentelemetry.io/otel/sdk/log v0.14.0/go.mod h1:imQvII+0ZylXfKU7/wtOND8Hn4OpT3YUoIgqJVksUkM= -go.opentelemetry.io/otel/sdk/log/logtest v0.14.0 h1:Ijbtz+JKXl8T2MngiwqBlPaHqc4YCaP/i13Qrow6gAM= -go.opentelemetry.io/otel/sdk/log/logtest v0.14.0/go.mod h1:dCU8aEL6q+L9cYTqcVOk8rM9Tp8WdnHOPLiBgp0SGOA= -go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM= -go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA= -go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= -go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= +go.opentelemetry.io/collector/pdata v1.10.0 h1:oLyPLGvPTQrcRT64ZVruwvmH/u3SHTfNo01pteS4WOE= +go.opentelemetry.io/collector/pdata v1.10.0/go.mod h1:IHxHsp+Jq/xfjORQMDJjSH6jvedOSTOyu3nbxqhWSYE= +go.opentelemetry.io/contrib/bridges/otellogrus v0.7.0 h1:vPSzn6dQvdPq9ZiXFs+jUSJnzoKJkADD9yBdx/a1WgI= +go.opentelemetry.io/contrib/bridges/otellogrus v0.7.0/go.mod h1:yZFNJIjn97IBhuMB3tTGPti9xasYLIdh3ChZIzyhz8A= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 h1:CV7UdSGJt/Ao6Gp4CXckLxVRRsRgDHoI8XjbL3PDl8s= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0/go.mod h1:FRmFuRJfag1IZ2dPkHnEoSFVgTVPUd2qf5Vi69hLb8I= +go.opentelemetry.io/contrib/propagators/autoprop v0.53.0 h1:4zaVLcJ5mvYw0vlk63TX62qS4qty/4jAY1BKZ1usu18= +go.opentelemetry.io/contrib/propagators/autoprop v0.53.0/go.mod h1:RPlvYtxp5D8PKnRzyPM+rwMQrvzdlfA49Sgworkg7aQ= +go.opentelemetry.io/contrib/propagators/aws v1.28.0 h1:acyTl4oyin/iLr5Nz3u7p/PKHUbLh42w/fqg9LblExk= +go.opentelemetry.io/contrib/propagators/aws v1.28.0/go.mod h1:5WgIv6yG9DvLlSY2uIHrYSeVVwCDCqp4jhwinNNyeT4= +go.opentelemetry.io/contrib/propagators/b3 v1.28.0 h1:XR6CFQrQ/ttAYmTBX2loUEFGdk1h17pxYI8828dk/1Y= +go.opentelemetry.io/contrib/propagators/b3 v1.28.0/go.mod h1:DWRkzJONLquRz7OJPh2rRbZ7MugQj62rk7g6HRnEqh0= +go.opentelemetry.io/contrib/propagators/jaeger v1.28.0 h1:xQ3ktSVS128JWIaN1DiPGIjcH+GsvkibIAVRWFjS9eM= +go.opentelemetry.io/contrib/propagators/jaeger v1.28.0/go.mod h1:O9HIyI2kVBrFoEwQZ0IN6PHXykGoit4mZV2aEjkTRH4= +go.opentelemetry.io/contrib/propagators/ot v1.28.0 h1:rmlG+2pc5k5M7Y7izDrxAHZUIwDERdGMTD9oMV7llMk= +go.opentelemetry.io/contrib/propagators/ot v1.28.0/go.mod h1:MNgXIn+UrMbNGpd7xyckyo2LCHIgCdmdjEE7YNZGG+w= +go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY= +go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.8.0 h1:WzNab7hOOLzdDF/EoWCt4glhrbMPVMOO5JYTmpz36Ls= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.8.0/go.mod h1:hKvJwTzJdp90Vh7p6q/9PAOd55dI6WA6sWj62a/JvSs= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.8.0 h1:S+LdBGiQXtJdowoJoQPEtI52syEP/JYBUpjO49EQhV8= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.8.0/go.mod h1:5KXybFvPGds3QinJWQT7pmXf+TN5YIa7CNYObWRkj50= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.28.0 h1:U2guen0GhqH8o/G2un8f/aG/y++OuW6MyCo6hT9prXk= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.28.0/go.mod h1:yeGZANgEcpdx/WK0IvvRFC+2oLiMS2u4L/0Rj2M2Qr0= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.28.0 h1:aLmmtjRke7LPDQ3lvpFz+kNEH43faFhzW7v8BFIEydg= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.28.0/go.mod h1:TC1pyCt6G9Sjb4bQpShH+P5R53pO6ZuGnHuuln9xMeE= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 h1:3Q/xZUyC1BBkualc9ROb4G8qkH90LXEIICcs5zv1OYY= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0/go.mod h1:s75jGIWA9OfCMzF0xr+ZgfrB5FEbbV7UuYo32ahUiFI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.28.0 h1:R3X6ZXmNPRR8ul6i3WgFURCHzaXjHdm0karRG/+dj3s= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.28.0/go.mod h1:QWFXnDavXWwMx2EEcZsf3yxgEKAqsxQ+Syjp+seyInw= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.28.0 h1:j9+03ymgYhPKmeXGk5Zu+cIZOlVzd9Zv7QIiyItjFBU= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.28.0/go.mod h1:Y5+XiUG4Emn1hTfciPzGPJaSI+RpDts6BnCIir0SLqk= +go.opentelemetry.io/otel/log v0.8.0 h1:egZ8vV5atrUWUbnSsHn6vB8R21G2wrKqNiDt3iWertk= +go.opentelemetry.io/otel/log v0.8.0/go.mod h1:M9qvDdUTRCopJcGRKg57+JSQ9LgLBrwwfC32epk5NX8= +go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ= +go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE= +go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A= +go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU= +go.opentelemetry.io/otel/sdk/log v0.8.0 h1:zg7GUYXqxk1jnGF/dTdLPrK06xJdrXgqgFLnI4Crxvs= +go.opentelemetry.io/otel/sdk/log v0.8.0/go.mod h1:50iXr0UVwQrYS45KbruFrEt4LvAdCaWWgIrsN3ZQggo= +go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk= +go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w= +go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k= +go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.opentelemetry.io/proto/otlp v1.7.1 h1:gTOMpGDb0WTBOP8JaO72iL3auEZhVmAQg4ipjOVAtj4= -go.opentelemetry.io/proto/otlp v1.7.1/go.mod h1:b2rVh6rfI/s2pHWNlB7ILJcRALpcNDzKhACevjI+ZnE= -go.opentelemetry.io/proto/slim/otlp v1.7.1 h1:lZ11gEokjIWYM3JWOUrIILr2wcf6RX+rq5SPObV9oyc= -go.opentelemetry.io/proto/slim/otlp v1.7.1/go.mod h1:uZ6LJWa49eNM/EXnnvJGTTu8miokU8RQdnO980LJ57g= -go.opentelemetry.io/proto/slim/otlp/collector/profiles/v1development v0.0.1 h1:Tr/eXq6N7ZFjN+THBF/BtGLUz8dciA7cuzGRsCEkZ88= -go.opentelemetry.io/proto/slim/otlp/collector/profiles/v1development v0.0.1/go.mod h1:riqUmAOJFDFuIAzZu/3V6cOrTyfWzpgNJnG5UwrapCk= -go.opentelemetry.io/proto/slim/otlp/profiles/v1development v0.0.1 h1:z/oMlrCv3Kopwh/dtdRagJy+qsRRPA86/Ux3g7+zFXM= -go.opentelemetry.io/proto/slim/otlp/profiles/v1development v0.0.1/go.mod h1:C7EHYSIiaALi9RnNORCVaPCQDuJgJEn/XxkctaTez1E= +go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= +go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= @@ -1431,20 +1349,20 @@ go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0 go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= -go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko= -go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o= +go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= +go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/ratelimit v0.3.1 h1:K4qVE+byfv/B3tC+4nYWP7v/6SimcO7HzHekoMNBma0= -go.uber.org/ratelimit v0.3.1/go.mod h1:6euWsTB6U/Nb3X++xEUXA8ciPJvr19Q/0h1+oDcJhRk= +go.uber.org/ratelimit v0.3.0 h1:IdZd9wqvFXnvLvSEBo0KPcGfkoBGNkpTHlrE3Rcjkjw= +go.uber.org/ratelimit v0.3.0/go.mod h1:So5LG7CV1zWpY1sHe+DXTJqQvOx+FFPFaAs2SnoyBaI= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI= -go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= -go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= +go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/arch v0.4.0 h1:A8WCeEWhLwPBKNbFi5Wv5UTCBx5zzubnXDlMOFAzFMc= golang.org/x/arch v0.4.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= @@ -1456,10 +1374,8 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191219195013-becbf705a915/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200317142112-1b76d66859c6/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -1479,16 +1395,9 @@ golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0 golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= -golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= -golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= -golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= -golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= -golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= -golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= -golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04= -golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0= +golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= +golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1530,11 +1439,8 @@ golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/mod v0.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U= -golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI= +golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM= +golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1554,6 +1460,7 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191021144547-ec77196f6094/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -1584,30 +1491,22 @@ golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20210913180222-943fd674d43e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210917221730-978cfadd31cf/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= -golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= -golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= -golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= -golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= -golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= -golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= -golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= -golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4= -golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210= +golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= +golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.32.0 h1:jsCblLleRMDrxMN29H3z/k1KliIvpLgCkE6R8FXXNgY= -golang.org/x/oauth2 v0.32.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= +golang.org/x/oauth2 v0.28.0 h1:CrgCKl8PPAVtLnU3c+EDw6x11699EWlsDeWNWKdIOkc= +golang.org/x/oauth2 v0.28.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1621,11 +1520,8 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= -golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= -golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= +golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1669,7 +1565,6 @@ golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200509044756-6aff5f38e54f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1700,7 +1595,6 @@ golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220406163625-3f8b81556e12/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1715,17 +1609,10 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ= -golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= +golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= +golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -1733,16 +1620,9 @@ golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o= -golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= -golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= -golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= -golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= -golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= -golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= -golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= -golang.org/x/term v0.36.0 h1:zMPR+aF8gfksFprF/Nc/rd1wRS1EI6nDBGyWAvDzx2Q= -golang.org/x/term v0.36.0/go.mod h1:Qu394IJq6V6dCBRgwqshf3mPF85AqzYEzofzRdZkWss= +golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y= +golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1757,12 +1637,9 @@ golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= -golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k= -golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM= +golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= +golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1771,8 +1648,8 @@ golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= -golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= +golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= +golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -1813,7 +1690,6 @@ golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjs golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200509030707-2212a7e161a5/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= @@ -1830,20 +1706,15 @@ golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= -golang.org/x/tools v0.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE= -golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w= +golang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY= +golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU= -golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= +gonum.org/v1/gonum v0.8.2 h1:CCXrcPKiGGotvnN6jfUsKk4rRqm7q09/YbKb5xCEvtM= gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= -gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= -gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= @@ -1862,8 +1733,8 @@ google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0M google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/api v0.252.0 h1:xfKJeAJaMwb8OC9fesr369rjciQ704AjU/psjkKURSI= -google.golang.org/api v0.252.0/go.mod h1:dnHOv81x5RAmumZ7BWLShB/u7JZNeyalImxHmtTHxqw= +google.golang.org/api v0.227.0 h1:QvIHF9IuyG6d6ReE+BNd11kIB8hZvjN8Z5xY5t21zYc= +google.golang.org/api v0.227.0/go.mod h1:EIpaG6MbTgQarWF5xJvX0eOJPK9n/5D4Bynb9j2HXvQ= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -1902,12 +1773,12 @@ google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= google.golang.org/genproto v0.0.0-20210917145530-b395a37504d4/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20250603155806-513f23925822 h1:rHWScKit0gvAPuOnu87KpaYtjK5zBMLcULh7gxkCXu4= -google.golang.org/genproto v0.0.0-20250603155806-513f23925822/go.mod h1:HubltRL7rMh0LfnQPkMH4NPDFEWp0jw3vixw7jEM53s= -google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 h1:BIRfGDEjiHRrk0QKZe3Xv2ieMhtgRGeLcZQ0mIVn4EY= -google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5/go.mod h1:j3QtIyytwqGr1JUDtYXwtMXWPKsEa5LtzIFN1Wn5WvE= -google.golang.org/genproto/googleapis/rpc v0.0.0-20251002232023-7c0ddcbb5797 h1:CirRxTOwnRWVLKzDNrs0CXAaVozJoR4G9xvdRecrdpk= -google.golang.org/genproto/googleapis/rpc v0.0.0-20251002232023-7c0ddcbb5797/go.mod h1:HSkG/KdJWusxU1F6CNrwNDjBMgisKxGnc5dAZfT0mjQ= +google.golang.org/genproto v0.0.0-20241021214115-324edc3d5d38 h1:Q3nlH8iSQSRUwOskjbcSMcF2jiYMNiQYZ0c2KEJLKKU= +google.golang.org/genproto v0.0.0-20241021214115-324edc3d5d38/go.mod h1:xBI+tzfqGGN2JBeSebfKXFSdBpWVQ7sLW40PTupVRm4= +google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422 h1:GVIKPyP/kLIyVOgOnTwFOrvQaQUzOzGMCxgFUOEmm24= +google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422/go.mod h1:b6h1vNKhxaSoEI+5jc3PJUCustfli/mRab7295pY7rw= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4 h1:iK2jbkWL86DXjEx0qiHcRE9dE4/Ahua5k6V8OWFb//c= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4/go.mod h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -1925,8 +1796,8 @@ google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.75.1 h1:/ODCNEuf9VghjgO3rqLcfg8fiOP0nSluljWFlDxELLI= -google.golang.org/grpc v1.75.1/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ= +google.golang.org/grpc v1.71.0 h1:kF77BGdPTQ4/JZWMlb9VpJ5pa25aqvVqogsxNHHdeBg= +google.golang.org/grpc v1.71.0/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -1941,8 +1812,8 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= -google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= +google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -1955,18 +1826,22 @@ gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSP gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= +gopkg.in/h2non/gentleman.v1 v1.0.4/go.mod h1:JYuHVdFzS4MKOXe0o+chKJ4hCe6tqKKw9XH9YP6WFrg= +gopkg.in/h2non/gock.v1 v1.0.15/go.mod h1:sX4zAkdYX1TRGJ2JY156cFspQn4yRWn6p9EMdODlynE= +gopkg.in/h2non/gock.v1 v1.0.16 h1:F11k+OafeuFENsjei5t2vMTSTs9L62AdyTe4E1cgdG8= +gopkg.in/h2non/gock.v1 v1.0.16/go.mod h1:XVuDAssexPLwgxCLMvDTWNU5eqklsydR6I5phZ9oPB8= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.56.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= -gopkg.in/ns1/ns1-go.v2 v2.15.0 h1:cE3xSMdSCV8kf9SQldzqgW/Ueh7sv3yO2JwKtYxxz3E= -gopkg.in/ns1/ns1-go.v2 v2.15.0/go.mod h1:pfaU0vECVP7DIOr453z03HXS6dFJpXdNRwOyRzwmPSc= +gopkg.in/ns1/ns1-go.v2 v2.13.0 h1:I5NNqI9Bi1SGK92TVkOvLTwux5LNrix/99H2datVh48= +gopkg.in/ns1/ns1-go.v2 v2.13.0/go.mod h1:pfaU0vECVP7DIOr453z03HXS6dFJpXdNRwOyRzwmPSc= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= @@ -1983,10 +1858,11 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q= -gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA= +gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= +gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -1994,20 +1870,20 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.32.3 h1:Hw7KqxRusq+6QSplE3NYG4MBxZw1BZnq4aP4cJVINls= -k8s.io/api v0.32.3/go.mod h1:2wEDTXADtm/HA7CCMD8D8bK4yuBUptzaRhYcYEEYA3k= -k8s.io/apiextensions-apiserver v0.32.3 h1:4D8vy+9GWerlErCwVIbcQjsWunF9SUGNu7O7hiQTyPY= -k8s.io/apiextensions-apiserver v0.32.3/go.mod h1:8YwcvVRMVzw0r1Stc7XfGAzB/SIVLunqApySV5V7Dss= -k8s.io/apimachinery v0.32.3 h1:JmDuDarhDmA/Li7j3aPrwhpNBA94Nvk5zLeOge9HH1U= -k8s.io/apimachinery v0.32.3/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE= -k8s.io/client-go v0.32.3 h1:RKPVltzopkSgHS7aS98QdscAgtgah/+zmpAogooIqVU= -k8s.io/client-go v0.32.3/go.mod h1:3v0+3k4IcT9bXTc4V2rt+d2ZPPG700Xy6Oi0Gdl2PaY= +k8s.io/api v0.31.1 h1:Xe1hX/fPW3PXYYv8BlozYqw63ytA92snr96zMW9gWTU= +k8s.io/api v0.31.1/go.mod h1:sbN1g6eY6XVLeqNsZGLnI5FwVseTrZX7Fv3O26rhAaI= +k8s.io/apiextensions-apiserver v0.31.1 h1:L+hwULvXx+nvTYX/MKM3kKMZyei+UiSXQWciX/N6E40= +k8s.io/apiextensions-apiserver v0.31.1/go.mod h1:tWMPR3sgW+jsl2xm9v7lAyRF1rYEK71i9G5dRtkknoQ= +k8s.io/apimachinery v0.31.1 h1:mhcUBbj7KUjaVhyXILglcVjuS4nYXiwC+KKFBgIVy7U= +k8s.io/apimachinery v0.31.1/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= +k8s.io/client-go v0.31.1 h1:f0ugtWSbWpxHR7sjVpQwuvw9a3ZKLXX0u0itkFXufb0= +k8s.io/client-go v0.31.1/go.mod h1:sKI8871MJN2OyeqRlmA4W4KM9KBdBUpDLu/43eGemCg= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= -k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f h1:GA7//TjRY9yWGy1poLzYYJJ4JRdzg3+O6e8I+e+8T5Y= -k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f/go.mod h1:R/HEjbvWI0qdfb8viZUeVZm0X6IZnxAydC7YU42CMw4= -k8s.io/utils v0.0.0-20241210054802-24370beab758 h1:sdbE21q2nlQtFh65saZY+rRM6x6aJJI8IUa1AmH/qa0= -k8s.io/utils v0.0.0-20241210054802-24370beab758/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +k8s.io/kube-openapi v0.0.0-20240423202451-8948a665c108 h1:Q8Z7VlGhcJgBHJHYugJ/K/7iB8a2eSxCyxdVjJp+lLY= +k8s.io/kube-openapi v0.0.0-20240423202451-8948a665c108/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98= +k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A= +k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= mvdan.cc/xurls/v2 v2.5.0 h1:lyBNOm8Wo71UknhUs4QTFUNNMyxy2JEIaKKo0RWOh+8= mvdan.cc/xurls/v2 v2.5.0/go.mod h1:yQgaGQ1rFtJUzkmKiHYSSfuQxqfYmd//X6PxvholpeE= nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g= @@ -2017,16 +1893,14 @@ rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8 rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/controller-runtime v0.20.4 h1:X3c+Odnxz+iPTRobG4tp092+CvBU9UK0t/bRf+n0DGU= -sigs.k8s.io/controller-runtime v0.20.4/go.mod h1:xg2XB0K5ShQzAgsoujxuKN4LNXR2LfwwHsPj7Iaw+XY= -sigs.k8s.io/gateway-api v1.3.0 h1:q6okN+/UKDATola4JY7zXzx40WO4VISk7i9DIfOvr9M= -sigs.k8s.io/gateway-api v1.3.0/go.mod h1:d8NV8nJbaRbEKem+5IuxkL8gJGOZ+FJ+NvOIltV8gDk= -sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8= -sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo= -sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016 h1:kXv6kKdoEtedwuqMmkqhbkgvYKeycVbC8+iPCP9j5kQ= -sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= -sigs.k8s.io/structured-merge-diff/v4 v4.7.0 h1:qPeWmscJcXP0snki5IYF79Z8xrl8ETFxgMd7wez1XkI= -sigs.k8s.io/structured-merge-diff/v4 v4.7.0/go.mod h1:dDy58f92j70zLsuZVuUX5Wp9vtxXpaZnkPGWeqDfCps= +sigs.k8s.io/controller-runtime v0.18.0 h1:Z7jKuX784TQSUL1TIyeuF7j8KXZ4RtSX0YgtjKcSTME= +sigs.k8s.io/controller-runtime v0.18.0/go.mod h1:tuAt1+wbVsXIT8lPtk5RURxqAnq7xkpv2Mhttslg7Hw= +sigs.k8s.io/gateway-api v1.2.1 h1:fZZ/+RyRb+Y5tGkwxFKuYuSRQHu9dZtbjenblleOLHM= +sigs.k8s.io/gateway-api v1.2.1/go.mod h1:EpNfEXNjiYfUJypf0eZ0P5iXA9ekSGWaS1WgPaM42X0= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= diff --git a/integration/access_log_test.go b/integration/access_log_test.go index 2f480b524..e1c4003e9 100644 --- a/integration/access_log_test.go +++ b/integration/access_log_test.go @@ -648,6 +648,25 @@ func (s *AccessLogSuite) TestAccessLogDisabledForInternals() { require.Equal(s.T(), 0, count) + // Make some requests on the custom ping router in error. + req, err = http.NewRequest(http.MethodGet, "http://127.0.0.1:8010/ping-error", nil) + require.NoError(s.T(), err) + req.Host = "ping-error.docker.local" + + err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusUnauthorized), try.BodyContains("X-Forwarded-Host: ping-error.docker.local")) + require.NoError(s.T(), err) + err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusUnauthorized), try.BodyContains("X-Forwarded-Host: ping-error.docker.local")) + require.NoError(s.T(), err) + + // Here we verify that the remove of observability doesn't break the metrics for the error page service. + req, err = http.NewRequest(http.MethodGet, "http://127.0.0.1:8080/metrics", nil) + require.NoError(s.T(), err) + + err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.BodyContains("service3")) + require.NoError(s.T(), err) + err = try.Request(req, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK), try.BodyNotContains("service=\"ping")) + require.NoError(s.T(), err) + // Verify no other Traefik problems. s.checkNoOtherTraefikProblems() } diff --git a/integration/conformance-reports/v1.3.0/experimental-v3.5-default-report.yaml b/integration/conformance-reports/v1.2.1/experimental-v3.4-default-report.yaml similarity index 93% rename from integration/conformance-reports/v1.3.0/experimental-v3.5-default-report.yaml rename to integration/conformance-reports/v1.2.1/experimental-v3.4-default-report.yaml index 0facbd714..2025f9c40 100644 --- a/integration/conformance-reports/v1.3.0/experimental-v3.5-default-report.yaml +++ b/integration/conformance-reports/v1.2.1/experimental-v3.4-default-report.yaml @@ -1,14 +1,14 @@ apiVersion: gateway.networking.k8s.io/v1 date: '-' gatewayAPIChannel: experimental -gatewayAPIVersion: v1.3.0 +gatewayAPIVersion: v1.2.1 implementation: contact: - '@traefik/maintainers' organization: traefik project: traefik url: https://traefik.io/ - version: v3.5 + version: v3.4 kind: ConformanceReport mode: default profiles: @@ -46,7 +46,6 @@ profiles: - HTTPRouteResponseHeaderModification - HTTPRouteSchemeRedirect unsupportedFeatures: - - GatewayAddressEmpty - GatewayHTTPListenerIsolation - GatewayInfrastructurePropagation - GatewayStaticAddresses @@ -55,7 +54,6 @@ profiles: - HTTPRouteParentRefPort - HTTPRouteRequestMirror - HTTPRouteRequestMultipleMirrors - - HTTPRouteRequestPercentageMirror - HTTPRouteRequestTimeout name: GATEWAY-HTTP summary: Core tests succeeded. Extended tests succeeded. diff --git a/integration/consul_test.go b/integration/consul_test.go index 31908556e..076fd4560 100644 --- a/integration/consul_test.go +++ b/integration/consul_test.go @@ -2,6 +2,7 @@ package integration import ( "bytes" + "context" "encoding/json" "errors" "fmt" @@ -42,7 +43,7 @@ func (s *ConsulSuite) SetupSuite() { s.consulURL = fmt.Sprintf("http://%s", consulAddr) kv, err := valkeyrie.NewStore( - s.T().Context(), + context.Background(), consul.StoreName, []string{consulAddr}, &consul.Config{ @@ -62,7 +63,7 @@ func (s *ConsulSuite) TearDownSuite() { } func (s *ConsulSuite) TearDownTest() { - err := s.kvClient.DeleteTree(s.T().Context(), "traefik") + err := s.kvClient.DeleteTree(context.Background(), "traefik") if err != nil && !errors.Is(err, store.ErrKeyNotFound) { require.ErrorIs(s.T(), err, store.ErrKeyNotFound) } @@ -117,7 +118,7 @@ func (s *ConsulSuite) TestSimpleConfiguration() { } for k, v := range data { - err := s.kvClient.Put(s.T().Context(), k, []byte(v), nil) + err := s.kvClient.Put(context.Background(), k, []byte(v), nil) require.NoError(s.T(), err) } @@ -177,7 +178,7 @@ func (s *ConsulSuite) TestDeleteRootKey() { file := s.adaptFile("fixtures/consul/simple.toml", struct{ ConsulAddress string }{s.consulURL}) - ctx := s.T().Context() + ctx := context.Background() svcaddr := net.JoinHostPort(s.getComposeServiceIP("whoami"), "80") data := map[string]string{ diff --git a/integration/dual_logging_test.go b/integration/dual_logging_test.go deleted file mode 100644 index fef4cbd54..000000000 --- a/integration/dual_logging_test.go +++ /dev/null @@ -1,134 +0,0 @@ -package integration - -import ( - "compress/gzip" - "encoding/json" - "io" - "net/http" - "net/http/httptest" - "os" - "strings" - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/suite" - "go.opentelemetry.io/collector/pdata/plog/plogotlp" -) - -const traefikTestOTLPLogFile = "traefik_otlp.log" - -// DualLoggingSuite tests that both OTLP and stdout logging can work together. -type DualLoggingSuite struct { - BaseSuite - otlpLogs []string - collector *httptest.Server -} - -func TestDualLoggingSuite(t *testing.T) { - suite.Run(t, new(DualLoggingSuite)) -} - -func (s *DualLoggingSuite) SetupSuite() { - s.BaseSuite.SetupSuite() - - // Clean up any existing log files - os.Remove(traefikTestLogFile) - os.Remove(traefikTestOTLPLogFile) -} - -func (s *DualLoggingSuite) TearDownSuite() { - s.BaseSuite.TearDownSuite() - - // Clean up log files - generatedFiles := []string{ - traefikTestLogFile, - traefikTestOTLPLogFile, - } - - for _, filename := range generatedFiles { - if err := os.Remove(filename); err != nil { - s.T().Logf("Failed to remove %s: %v", filename, err) - } - } -} - -func (s *DualLoggingSuite) SetupTest() { - s.otlpLogs = []string{} - - // Create mock OTLP collector - s.collector = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - defer r.Body.Close() - - gzr, err := gzip.NewReader(r.Body) - if err != nil { - s.T().Logf("Error creating gzip reader: %v", err) - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - defer gzr.Close() - - body, err := io.ReadAll(gzr) - if err != nil { - s.T().Logf("Error reading gzipped body: %v", err) - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - - req := plogotlp.NewExportRequest() - err = req.UnmarshalProto(body) - if err != nil { - s.T().Logf("Error unmarshaling protobuf: %v", err) - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - - marshalledReq, err := json.Marshal(req) - if err != nil { - s.T().Logf("Error marshaling to JSON: %v", err) - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - s.otlpLogs = append(s.otlpLogs, string(marshalledReq)) - - w.WriteHeader(http.StatusOK) - })) -} - -func (s *DualLoggingSuite) TearDownTest() { - if s.collector != nil { - s.collector.Close() - s.collector = nil - } -} - -func (s *DualLoggingSuite) TestOTLPAndStdoutLogging() { - tempObjects := struct { - CollectorURL string - }{ - CollectorURL: s.collector.URL + "/v1/logs", - } - - file := s.adaptFile("fixtures/dual_logging/otlp_and_stdout.toml", tempObjects) - - cmd, display := s.cmdTraefik(withConfigFile(file)) - defer s.displayTraefikLogFile(traefikTestLogFile) - - s.waitForTraefik("dashboard") - - time.Sleep(3 * time.Second) - - s.killCmd(cmd) - time.Sleep(1 * time.Second) - - assert.NotEmpty(s.T(), s.otlpLogs) - - output := display.String() - otlpOutput := strings.Join(s.otlpLogs, "\n") - - foundStdoutLog := strings.Contains(output, "Starting provider") - assert.True(s.T(), foundStdoutLog) - foundOTLPLog := strings.Contains(otlpOutput, "Starting provider") - assert.True(s.T(), foundOTLPLog) -} diff --git a/integration/etcd_test.go b/integration/etcd_test.go index 067c4d61c..01132ba88 100644 --- a/integration/etcd_test.go +++ b/integration/etcd_test.go @@ -2,6 +2,7 @@ package integration import ( "bytes" + "context" "encoding/json" "net" "net/http" @@ -40,7 +41,7 @@ func (s *EtcdSuite) SetupSuite() { var err error s.etcdAddr = net.JoinHostPort(s.getComposeServiceIP("etcd"), "2379") s.kvClient, err = valkeyrie.NewStore( - s.T().Context(), + context.Background(), etcdv3.StoreName, []string{s.etcdAddr}, &etcdv3.Config{ @@ -107,7 +108,7 @@ func (s *EtcdSuite) TestSimpleConfiguration() { } for k, v := range data { - err := s.kvClient.Put(s.T().Context(), k, []byte(v), nil) + err := s.kvClient.Put(context.Background(), k, []byte(v), nil) require.NoError(s.T(), err) } diff --git a/integration/fixtures/dual_logging/otlp_and_stdout.toml b/integration/fixtures/dual_logging/otlp_and_stdout.toml deleted file mode 100644 index 4e31afbf1..000000000 --- a/integration/fixtures/dual_logging/otlp_and_stdout.toml +++ /dev/null @@ -1,24 +0,0 @@ -[global] - checkNewVersion = false - sendAnonymousUsage = false - -[log] - level = "INFO" - -[experimental] - otlpLogs = true - -[log.otlp] - serviceName = "traefik-test" - [log.otlp.http] - endpoint = "{{ .CollectorURL }}" - -[api] - insecure = true - -[entryPoints] - [entryPoints.web] - address = ":8000" - -[providers.docker] - exposedByDefault = false diff --git a/integration/fixtures/k8s-conformance/00-experimental-v1.3.0.yml b/integration/fixtures/k8s-conformance/00-experimental-v1.2.1.yml similarity index 83% rename from integration/fixtures/k8s-conformance/00-experimental-v1.3.0.yml rename to integration/fixtures/k8s-conformance/00-experimental-v1.2.1.yml index 75725cc0f..69b689846 100644 --- a/integration/fixtures/k8s-conformance/00-experimental-v1.3.0.yml +++ b/integration/fixtures/k8s-conformance/00-experimental-v1.2.1.yml @@ -1,4 +1,4 @@ -# Copyright 2025 The Kubernetes Authors. +# Copyright 2024 The Kubernetes Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,6 +17,507 @@ # --- # +# config/crd/experimental/gateway.networking.k8s.io_backendlbpolicies.yaml +# +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/3328 + gateway.networking.k8s.io/bundle-version: v1.2.1 + gateway.networking.k8s.io/channel: experimental + creationTimestamp: null + labels: + gateway.networking.k8s.io/policy: Direct + name: backendlbpolicies.gateway.networking.k8s.io +spec: + group: gateway.networking.k8s.io + names: + categories: + - gateway-api + kind: BackendLBPolicy + listKind: BackendLBPolicyList + plural: backendlbpolicies + shortNames: + - blbpolicy + singular: backendlbpolicy + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha2 + schema: + openAPIV3Schema: + description: |- + BackendLBPolicy provides a way to define load balancing rules + for a backend. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: Spec defines the desired state of BackendLBPolicy. + properties: + sessionPersistence: + description: |- + SessionPersistence defines and configures session persistence + for the backend. + + Support: Extended + properties: + absoluteTimeout: + description: |- + AbsoluteTimeout defines the absolute timeout of the persistent + session. Once the AbsoluteTimeout duration has elapsed, the + session becomes invalid. + + Support: Extended + pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$ + type: string + cookieConfig: + description: |- + CookieConfig provides configuration settings that are specific + to cookie-based session persistence. + + Support: Core + properties: + lifetimeType: + default: Session + description: |- + LifetimeType specifies whether the cookie has a permanent or + session-based lifetime. A permanent cookie persists until its + specified expiry time, defined by the Expires or Max-Age cookie + attributes, while a session cookie is deleted when the current + session ends. + + When set to "Permanent", AbsoluteTimeout indicates the + cookie's lifetime via the Expires or Max-Age cookie attributes + and is required. + + When set to "Session", AbsoluteTimeout indicates the + absolute lifetime of the cookie tracked by the gateway and + is optional. + + Support: Core for "Session" type + + Support: Extended for "Permanent" type + enum: + - Permanent + - Session + type: string + type: object + idleTimeout: + description: |- + IdleTimeout defines the idle timeout of the persistent session. + Once the session has been idle for more than the specified + IdleTimeout duration, the session becomes invalid. + + Support: Extended + pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$ + type: string + sessionName: + description: |- + SessionName defines the name of the persistent session token + which may be reflected in the cookie or the header. Users + should avoid reusing session names to prevent unintended + consequences, such as rejection or unpredictable behavior. + + Support: Implementation-specific + maxLength: 128 + type: string + type: + default: Cookie + description: |- + Type defines the type of session persistence such as through + the use a header or cookie. Defaults to cookie based session + persistence. + + Support: Core for "Cookie" type + + Support: Extended for "Header" type + enum: + - Cookie + - Header + type: string + type: object + x-kubernetes-validations: + - message: AbsoluteTimeout must be specified when cookie lifetimeType + is Permanent + rule: '!has(self.cookieConfig) || !has(self.cookieConfig.lifetimeType) + || self.cookieConfig.lifetimeType != ''Permanent'' || has(self.absoluteTimeout)' + targetRefs: + description: |- + TargetRef identifies an API object to apply policy to. + Currently, Backends (i.e. Service, ServiceImport, or any + implementation-specific backendRef) are the only valid API + target references. + items: + description: |- + LocalPolicyTargetReference identifies an API object to apply a direct or + inherited policy to. This should be used as part of Policy resources + that can target Gateway API resources. For more information on how this + policy attachment model works, and a sample Policy resource, refer to + the policy attachment documentation for Gateway API. + properties: + group: + description: Group is the group of the target resource. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + description: Kind is kind of the target resource. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the target resource. + maxLength: 253 + minLength: 1 + type: string + required: + - group + - kind + - name + type: object + maxItems: 16 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - group + - kind + - name + x-kubernetes-list-type: map + required: + - targetRefs + type: object + status: + description: Status defines the current state of BackendLBPolicy. + properties: + ancestors: + description: |- + Ancestors is a list of ancestor resources (usually Gateways) that are + associated with the policy, and the status of the policy with respect to + each ancestor. When this policy attaches to a parent, the controller that + manages the parent and the ancestors MUST add an entry to this list when + the controller first sees the policy and SHOULD update the entry as + appropriate when the relevant ancestor is modified. + + Note that choosing the relevant ancestor is left to the Policy designers; + an important part of Policy design is designing the right object level at + which to namespace this status. + + Note also that implementations MUST ONLY populate ancestor status for + the Ancestor resources they are responsible for. Implementations MUST + use the ControllerName field to uniquely identify the entries in this list + that they are responsible for. + + Note that to achieve this, the list of PolicyAncestorStatus structs + MUST be treated as a map with a composite key, made up of the AncestorRef + and ControllerName fields combined. + + A maximum of 16 ancestors will be represented in this list. An empty list + means the Policy is not relevant for any ancestors. + + If this slice is full, implementations MUST NOT add further entries. + Instead they MUST consider the policy unimplementable and signal that + on any related resources such as the ancestor that would be referenced + here. For example, if this list was full on BackendTLSPolicy, no + additional Gateways would be able to reference the Service targeted by + the BackendTLSPolicy. + items: + description: |- + PolicyAncestorStatus describes the status of a route with respect to an + associated Ancestor. + + Ancestors refer to objects that are either the Target of a policy or above it + in terms of object hierarchy. For example, if a policy targets a Service, the + Policy's Ancestors are, in order, the Service, the HTTPRoute, the Gateway, and + the GatewayClass. Almost always, in this hierarchy, the Gateway will be the most + useful object to place Policy status on, so we recommend that implementations + SHOULD use Gateway as the PolicyAncestorStatus object unless the designers + have a _very_ good reason otherwise. + + In the context of policy attachment, the Ancestor is used to distinguish which + resource results in a distinct application of this policy. For example, if a policy + targets a Service, it may have a distinct result per attached Gateway. + + Policies targeting the same resource may have different effects depending on the + ancestors of those resources. For example, different Gateways targeting the same + Service may have different capabilities, especially if they have different underlying + implementations. + + For example, in BackendTLSPolicy, the Policy attaches to a Service that is + used as a backend in a HTTPRoute that is itself attached to a Gateway. + In this case, the relevant object for status is the Gateway, and that is the + ancestor object referred to in this status. + + Note that a parent is also an ancestor, so for objects where the parent is the + relevant object for status, this struct SHOULD still be used. + + This struct is intended to be used in a slice that's effectively a map, + with a composite key made up of the AncestorRef and the ControllerName. + properties: + ancestorRef: + description: |- + AncestorRef corresponds with a ParentRef in the spec that this + PolicyAncestorStatus struct describes the status of. + properties: + group: + default: gateway.networking.k8s.io + description: |- + Group is the group of the referent. + When unspecified, "gateway.networking.k8s.io" is inferred. + To set the core API group (such as for a "Service" kind referent), + Group must be explicitly set to "" (empty string). + + Support: Core + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Gateway + description: |- + Kind is kind of the referent. + + There are two kinds of parent resources with "Core" support: + + * Gateway (Gateway conformance profile) + * Service (Mesh conformance profile, ClusterIP Services only) + + Support for other resources is Implementation-Specific. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: |- + Name is the name of the referent. + + Support: Core + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the referent. When unspecified, this refers + to the local namespace of the Route. + + Note that there are specific rules for ParentRefs which cross namespace + boundaries. Cross-namespace references are only valid if they are explicitly + allowed by something in the namespace they are referring to. For example: + Gateway has the AllowedRoutes field, and ReferenceGrant provides a + generic way to enable any other kind of cross-namespace reference. + + + ParentRefs from a Route to a Service in the same namespace are "producer" + routes, which apply default routing rules to inbound connections from + any namespace to the Service. + + ParentRefs from a Route to a Service in a different namespace are + "consumer" routes, and these routing rules are only applied to outbound + connections originating from the same namespace as the Route, for which + the intended destination of the connections are a Service targeted as a + ParentRef of the Route. + + + Support: Core + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: |- + Port is the network port this Route targets. It can be interpreted + differently based on the type of parent resource. + + When the parent resource is a Gateway, this targets all listeners + listening on the specified port that also support this kind of Route(and + select this Route). It's not recommended to set `Port` unless the + networking behaviors specified in a Route must apply to a specific port + as opposed to a listener(s) whose port(s) may be changed. When both Port + and SectionName are specified, the name and port of the selected listener + must match both specified values. + + + When the parent resource is a Service, this targets a specific port in the + Service spec. When both Port (experimental) and SectionName are specified, + the name and port of the selected port must match both specified values. + + + Implementations MAY choose to support other parent resources. + Implementations supporting other types of parent resources MUST clearly + document how/if Port is interpreted. + + For the purpose of status, an attachment is considered successful as + long as the parent resource accepts it partially. For example, Gateway + listeners can restrict which Routes can attach to them by Route kind, + namespace, or hostname. If 1 of 2 Gateway listeners accept attachment + from the referencing Route, the Route MUST be considered successfully + attached. If no Gateway listeners accept attachment from this Route, + the Route MUST be considered detached from the Gateway. + + Support: Extended + format: int32 + maximum: 65535 + minimum: 1 + type: integer + sectionName: + description: |- + SectionName is the name of a section within the target resource. In the + following resources, SectionName is interpreted as the following: + + * Gateway: Listener name. When both Port (experimental) and SectionName + are specified, the name and port of the selected listener must match + both specified values. + * Service: Port name. When both Port (experimental) and SectionName + are specified, the name and port of the selected listener must match + both specified values. + + Implementations MAY choose to support attaching Routes to other resources. + If that is the case, they MUST clearly document how SectionName is + interpreted. + + When unspecified (empty string), this will reference the entire resource. + For the purpose of status, an attachment is considered successful if at + least one section in the parent resource accepts it. For example, Gateway + listeners can restrict which Routes can attach to them by Route kind, + namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from + the referencing Route, the Route MUST be considered successfully + attached. If no Gateway listeners accept attachment from this Route, the + Route MUST be considered detached from the Gateway. + + Support: Core + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - name + type: object + conditions: + description: Conditions describes the status of the Policy with + respect to the given Ancestor. + items: + description: Condition contains details for one aspect of + the current state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, + Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 8 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + controllerName: + description: |- + ControllerName is a domain/path string that indicates the name of the + controller that wrote this status. This corresponds with the + controllerName field on GatewayClass. + + Example: "example.net/gateway-controller". + + The format of this field is DOMAIN "/" PATH, where DOMAIN and PATH are + valid Kubernetes names + (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names). + + Controllers MUST populate this field when writing status. Controllers should ensure that + entries to status populated with their ControllerName are cleaned up when they are no + longer necessary. + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ + type: string + required: + - ancestorRef + - controllerName + type: object + maxItems: 16 + type: array + required: + - ancestors + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: null + storedVersions: null +--- +# # config/crd/experimental/gateway.networking.k8s.io_backendtlspolicies.yaml # apiVersion: apiextensions.k8s.io/v1 @@ -24,7 +525,7 @@ kind: CustomResourceDefinition metadata: annotations: api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/3328 - gateway.networking.k8s.io/bundle-version: v1.3.0 + gateway.networking.k8s.io/bundle-version: v1.2.1 gateway.networking.k8s.io/channel: experimental creationTimestamp: null labels: @@ -106,14 +607,6 @@ spec: by default, but this default may change in the future to provide a more granular application of the policy. - TargetRefs must be _distinct_. This means either that: - - * They select different targets. If this is the case, then targetRef - entries are distinct. In terms of fields, this means that the - multi-part key defined by `group`, `kind`, and `name` must - be unique across all targetRef entries in the BackendTLSPolicy. - * They select different sectionNames in the same target. - Support: Extended for Kubernetes Service Support: Implementation-specific for any other resource @@ -170,20 +663,6 @@ spec: maxItems: 16 minItems: 1 type: array - x-kubernetes-validations: - - message: sectionName must be specified when targetRefs includes - 2 or more references to the same target - rule: 'self.all(p1, self.all(p2, p1.group == p2.group && p1.kind - == p2.kind && p1.name == p2.name ? ((!has(p1.sectionName) || p1.sectionName - == '''') == (!has(p2.sectionName) || p2.sectionName == '''')) - : true))' - - message: sectionName must be unique when targetRefs includes 2 or - more references to the same target - rule: self.all(p1, self.exists_one(p2, p1.group == p2.group && p1.kind - == p2.kind && p1.name == p2.name && (((!has(p1.sectionName) || - p1.sectionName == '') && (!has(p2.sectionName) || p2.sectionName - == '')) || (has(p1.sectionName) && has(p2.sectionName) && p1.sectionName - == p2.sectionName)))) validation: description: Validation contains backend TLS validation configuration. properties: @@ -195,7 +674,7 @@ spec: If CACertificateRefs is empty or unspecified, then WellKnownCACertificates must be specified. Only one of CACertificateRefs or WellKnownCACertificates may be specified, - not both. If CACertificateRefs is empty or unspecified, the configuration for + not both. If CACertifcateRefs is empty or unspecified, the configuration for WellKnownCACertificates MUST be honored instead if supported by the implementation. References to a resource in a different namespace are invalid for the @@ -253,7 +732,7 @@ spec: backends: 1. Hostname MUST be used as the SNI to connect to the backend (RFC 6066). - 2. Hostname MUST be used for authentication and MUST match the certificate served by the matching backend, unless SubjectAltNames is specified. + 2. If SubjectAltNames is not specified, Hostname MUST be used for authentication and MUST match the certificate served by the matching backend. @@ -265,10 +744,10 @@ spec: subjectAltNames: description: |- SubjectAltNames contains one or more Subject Alternative Names. - When specified the certificate served from the backend MUST - have at least one Subject Alternate Name matching one of the specified SubjectAltNames. + When specified, the certificate served from the backend MUST have at least one + Subject Alternate Name matching one of the specified SubjectAltNames. - Support: Extended + Support: Core items: description: SubjectAltName represents Subject Alternative Name. properties: @@ -675,7 +1154,7 @@ kind: CustomResourceDefinition metadata: annotations: api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/3328 - gateway.networking.k8s.io/bundle-version: v1.3.0 + gateway.networking.k8s.io/bundle-version: v1.2.1 gateway.networking.k8s.io/channel: experimental creationTimestamp: null name: gatewayclasses.gateway.networking.k8s.io @@ -910,7 +1389,7 @@ spec: - type x-kubernetes-list-type: map supportedFeatures: - description: |- + description: | SupportedFeatures is the set of features the GatewayClass support. It MUST be sorted in ascending alphabetical order by the Name key. items: @@ -1154,7 +1633,7 @@ spec: - type x-kubernetes-list-type: map supportedFeatures: - description: |- + description: | SupportedFeatures is the set of features the GatewayClass support. It MUST be sorted in ascending alphabetical order by the Name key. items: @@ -1195,7 +1674,7 @@ kind: CustomResourceDefinition metadata: annotations: api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/3328 - gateway.networking.k8s.io/bundle-version: v1.3.0 + gateway.networking.k8s.io/bundle-version: v1.2.1 gateway.networking.k8s.io/channel: experimental creationTimestamp: null name: gateways.gateway.networking.k8s.io @@ -1253,7 +1732,7 @@ spec: description: Spec defines the desired state of Gateway. properties: addresses: - description: |- + description: |+ Addresses requested for this Gateway. This is optional and behavior can depend on the implementation. If a value is set in the spec and the requested address is invalid or unavailable, the implementation MUST @@ -1274,9 +1753,10 @@ spec: GatewayStatus.Addresses. Support: Extended + items: - description: GatewaySpecAddress describes an address that can be - bound to a Gateway. + description: GatewayAddress describes an address that can be bound + to a Gateway. oneOf: - properties: type: @@ -1301,15 +1781,15 @@ spec: type: string value: description: |- - When a value is unspecified, an implementation SHOULD automatically - assign an address matching the requested type if possible. - - If an implementation does not support an empty value, they MUST set the - "Programmed" condition in status to False with a reason of "AddressNotAssigned". + Value of the address. The validity of the values will depend + on the type and support by the controller. Examples: `1.2.3.4`, `128::1`, `my-ip-address`. maxLength: 253 + minLength: 1 type: string + required: + - value type: object x-kubernetes-validations: - message: Hostname value must only contain valid characters (matching @@ -1325,96 +1805,16 @@ spec: - message: Hostname values must be unique rule: 'self.all(a1, a1.type == ''Hostname'' ? self.exists_one(a2, a2.type == a1.type && a2.value == a1.value) : true )' - allowedListeners: - description: |- - AllowedListeners defines which ListenerSets can be attached to this Gateway. - While this feature is experimental, the default value is to allow no ListenerSets. - properties: - namespaces: - default: - from: None - description: |- - Namespaces defines which namespaces ListenerSets can be attached to this Gateway. - While this feature is experimental, the default value is to allow no ListenerSets. - properties: - from: - default: None - description: |- - From indicates where ListenerSets can attach to this Gateway. Possible - values are: - - * Same: Only ListenerSets in the same namespace may be attached to this Gateway. - * Selector: ListenerSets in namespaces selected by the selector may be attached to this Gateway. - * All: ListenerSets in all namespaces may be attached to this Gateway. - * None: Only listeners defined in the Gateway's spec are allowed - - While this feature is experimental, the default value None - enum: - - All - - Selector - - Same - - None - type: string - selector: - description: |- - Selector must be specified when From is set to "Selector". In that case, - only ListenerSets in Namespaces matching this Selector will be selected by this - Gateway. This field is ignored for other values of "From". - properties: - matchExpressions: - description: matchExpressions is a list of label selector - requirements. The requirements are ANDed. - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - properties: - key: - description: key is the label key that the selector - applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - type: object - type: object backendTLS: - description: |- + description: |+ BackendTLS configures TLS settings for when this Gateway is connecting to backends with TLS. Support: Core + properties: clientCertificateRef: - description: |- + description: |+ ClientCertificateRef is a reference to an object that contains a Client Certificate and the associated private key. @@ -1430,6 +1830,7 @@ spec: This setting can be overridden on the service level by use of BackendTLSPolicy. Support: Core + properties: group: default: "" @@ -1564,11 +1965,6 @@ spec: the merging behavior is implementation specific. It is generally recommended that GatewayClass provides defaults that can be overridden by a Gateway. - If the referent cannot be found, refers to an unsupported kind, or when - the data within that resource is malformed, the Gateway SHOULD be - rejected with the "Accepted" status condition set to "False" and an - "InvalidParameters" reason. - Support: Implementation-specific properties: group: @@ -1599,8 +1995,6 @@ spec: logical endpoints that are bound on this Gateway's addresses. At least one Listener MUST be specified. - ## Distinct Listeners - Each Listener in a set of Listeners (for example, in a single Gateway) MUST be _distinct_, in that a traffic flow MUST be able to be assigned to exactly one listener. (This section uses "set of Listeners" rather than @@ -1612,76 +2006,55 @@ spec: combination of Port, Protocol, and, if supported by the protocol, Hostname. Some combinations of port, protocol, and TLS settings are considered - Core support and MUST be supported by implementations based on the objects - they support: + Core support and MUST be supported by implementations based on their + targeted conformance profile: - HTTPRoute + HTTP Profile 1. HTTPRoute, Port: 80, Protocol: HTTP 2. HTTPRoute, Port: 443, Protocol: HTTPS, TLS Mode: Terminate, TLS keypair provided - TLSRoute + TLS Profile 1. TLSRoute, Port: 443, Protocol: TLS, TLS Mode: Passthrough "Distinct" Listeners have the following property: - **The implementation can match inbound requests to a single distinct - Listener**. - - When multiple Listeners share values for fields (for + The implementation can match inbound requests to a single distinct + Listener. When multiple Listeners share values for fields (for example, two Listeners with the same Port value), the implementation can match requests to only one of the Listeners using other Listener fields. - When multiple listeners have the same value for the Protocol field, then - each of the Listeners with matching Protocol values MUST have different - values for other fields. + For example, the following Listener scenarios are distinct: - The set of fields that MUST be different for a Listener differs per protocol. - The following rules define the rules for what fields MUST be considered for - Listeners to be distinct with each protocol currently defined in the - Gateway API spec. + 1. Multiple Listeners with the same Port that all use the "HTTP" + Protocol that all have unique Hostname values. + 2. Multiple Listeners with the same Port that use either the "HTTPS" or + "TLS" Protocol that all have unique Hostname values. + 3. A mixture of "TCP" and "UDP" Protocol Listeners, where no Listener + with the same Protocol has the same Port value. - The set of listeners that all share a protocol value MUST have _different_ - values for _at least one_ of these fields to be distinct: + Some fields in the Listener struct have possible values that affect + whether the Listener is distinct. Hostname is particularly relevant + for HTTP or HTTPS protocols. - * **HTTP, HTTPS, TLS**: Port, Hostname - * **TCP, UDP**: Port + When using the Hostname value to select between same-Port, same-Protocol + Listeners, the Hostname value must be different on each Listener for the + Listener to be distinct. - One **very** important rule to call out involves what happens when an - implementation: - - * Supports TCP protocol Listeners, as well as HTTP, HTTPS, or TLS protocol - Listeners, and - * sees HTTP, HTTPS, or TLS protocols with the same `port` as one with TCP - Protocol. - - In this case all the Listeners that share a port with the - TCP Listener are not distinct and so MUST NOT be accepted. - - If an implementation does not support TCP Protocol Listeners, then the - previous rule does not apply, and the TCP Listeners SHOULD NOT be - accepted. - - Note that the `tls` field is not used for determining if a listener is distinct, because - Listeners that _only_ differ on TLS config will still conflict in all cases. - - ### Listeners that are distinct only by Hostname - - When the Listeners are distinct based only on Hostname, inbound request + When the Listeners are distinct based on Hostname, inbound request hostnames MUST match from the most specific to least specific Hostname values to choose the correct Listener and its associated set of Routes. - Exact matches MUST be processed before wildcard matches, and wildcard - matches MUST be processed before fallback (empty Hostname value) + Exact matches must be processed before wildcard matches, and wildcard + matches must be processed before fallback (empty Hostname value) matches. For example, `"foo.example.com"` takes precedence over `"*.example.com"`, and `"*.example.com"` takes precedence over `""`. Additionally, if there are multiple wildcard entries, more specific wildcard entries must be processed before less specific wildcard entries. For example, `"*.foo.example.com"` takes precedence over `"*.example.com"`. - The precise definition here is that the higher the number of dots in the hostname to the right of the wildcard character, the higher the precedence. @@ -1689,26 +2062,18 @@ spec: the left, however, so `"*.example.com"` will match both `"foo.bar.example.com"` _and_ `"bar.example.com"`. - ## Handling indistinct Listeners - If a set of Listeners contains Listeners that are not distinct, then those - Listeners are _Conflicted_, and the implementation MUST set the "Conflicted" + Listeners are Conflicted, and the implementation MUST set the "Conflicted" condition in the Listener Status to "True". - The words "indistinct" and "conflicted" are considered equivalent for the - purpose of this documentation. - Implementations MAY choose to accept a Gateway with some Conflicted Listeners only if they only accept the partial Listener set that contains - no Conflicted Listeners. - - Specifically, an implementation MAY accept a partial Listener set subject to - the following rules: - - * The implementation MUST NOT pick one conflicting Listener as the winner. - ALL indistinct Listeners must not be accepted for processing. - * At least one distinct Listener MUST be present, or else the Gateway effectively - contains _no_ Listeners, and must be rejected from processing as a whole. + no Conflicted Listeners. To put this another way, implementations may + accept a partial Listener set only if they throw out *all* the conflicting + Listeners. No picking one of the conflicting listeners as the winner. + This also means that the Gateway must have at least one non-conflicting + Listener in this case, otherwise it violates the requirement that at + least one Listener must be present. The implementation MUST set a "ListenersNotValid" condition on the Gateway Status when the Gateway contains Conflicted Listeners whether or @@ -1717,25 +2082,7 @@ spec: Accepted. Additionally, the Listener status for those listeners SHOULD indicate which Listeners are conflicted and not Accepted. - ## General Listener behavior - - Note that, for all distinct Listeners, requests SHOULD match at most one Listener. - For example, if Listeners are defined for "foo.example.com" and "*.example.com", a - request to "foo.example.com" SHOULD only be routed using routes attached - to the "foo.example.com" Listener (and not the "*.example.com" Listener). - - This concept is known as "Listener Isolation", and it is an Extended feature - of Gateway API. Implementations that do not support Listener Isolation MUST - clearly document this, and MUST NOT claim support for the - `GatewayHTTPListenerIsolation` feature. - - Implementations that _do_ support Listener Isolation SHOULD claim support - for the Extended `GatewayHTTPListenerIsolation` feature and pass the associated - conformance tests. - - ## Compatible Listeners - - A Gateway's Listeners are considered _compatible_ if: + A Gateway's Listeners are considered "compatible" if: 1. They are distinct. 2. The implementation can serve them in compliance with the Addresses @@ -1750,11 +2097,16 @@ spec: on the same address, or cannot mix HTTPS and generic TLS listens on the same port would not consider those cases compatible, even though they are distinct. + Note that requests SHOULD match at most one Listener. For example, if + Listeners are defined for "foo.example.com" and "*.example.com", a + request to "foo.example.com" SHOULD only be routed using routes attached + to the "foo.example.com" Listener (and not the "*.example.com" Listener). + This concept is known as "Listener Isolation". Implementations that do + not support Listener Isolation MUST clearly document this. + Implementations MAY merge separate Gateways onto a single set of Addresses if all Listeners across all Gateways are compatible. - In a future release the MinItems=1 requirement MAY be dropped. - Support: Core items: description: |- @@ -1916,31 +2268,10 @@ spec: * TLS: The Listener Hostname MUST match the SNI. * HTTP: The Listener Hostname MUST match the Host header of the request. - * HTTPS: The Listener Hostname SHOULD match both the SNI and Host header. - Note that this does not require the SNI and Host header to be the same. - The semantics of this are described in more detail below. - - To ensure security, Section 11.1 of RFC-6066 emphasizes that server - implementations that rely on SNI hostname matching MUST also verify - hostnames within the application protocol. - - Section 9.1.2 of RFC-7540 provides a mechanism for servers to reject the - reuse of a connection by responding with the HTTP 421 Misdirected Request - status code. This indicates that the origin server has rejected the - request because it appears to have been misdirected. - - To detect misdirected requests, Gateways SHOULD match the authority of - the requests with all the SNI hostname(s) configured across all the - Gateway Listeners on the same port and protocol: - - * If another Listener has an exact match or more specific wildcard entry, - the Gateway SHOULD return a 421. - * If the current Listener (selected by SNI matching during ClientHello) - does not match the Host: - * If another Listener does match the Host the Gateway SHOULD return a - 421. - * If no other Listener matches the Host, the Gateway MUST return a - 404. + * HTTPS: The Listener Hostname SHOULD match at both the TLS and HTTP + protocol layers as described above. If an implementation does not + ensure that both the SNI and Host header match the Listener hostname, + it MUST clearly document that. For HTTPRoute and TLSRoute resources, there is an interaction with the `spec.hostnames` array. When both listener and route specify hostnames, @@ -2080,7 +2411,7 @@ spec: maxItems: 64 type: array frontendValidation: - description: |- + description: |+ FrontendValidation holds configuration information for validating the frontend (client). Setting this field will require clients to send a client certificate required for validation during the TLS handshake. In browsers this may result in a dialog appearing @@ -2088,6 +2419,7 @@ spec: The maximum depth of a certificate chain accepted in verification is Implementation specific. Support: Extended + properties: caCertificateRefs: description: |- @@ -2126,7 +2458,7 @@ spec: group: description: |- Group is the group of the referent. For example, "gateway.networking.k8s.io". - When set to the empty string, core API group is inferred. + When unspecified or empty string, core API group is inferred. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string @@ -2264,7 +2596,7 @@ spec: description: Status defines the current state of Gateway. properties: addresses: - description: |- + description: |+ Addresses lists the network addresses that have been bound to the Gateway. @@ -2274,6 +2606,7 @@ spec: * no addresses are specified, all addresses are dynamically assigned * a combination of specified and dynamic addresses are assigned * a specified address was unusable (e.g. already in use) + items: description: GatewayStatusAddress describes a network address that is bound to a Gateway. @@ -2591,7 +2924,7 @@ spec: description: Spec defines the desired state of Gateway. properties: addresses: - description: |- + description: |+ Addresses requested for this Gateway. This is optional and behavior can depend on the implementation. If a value is set in the spec and the requested address is invalid or unavailable, the implementation MUST @@ -2612,9 +2945,10 @@ spec: GatewayStatus.Addresses. Support: Extended + items: - description: GatewaySpecAddress describes an address that can be - bound to a Gateway. + description: GatewayAddress describes an address that can be bound + to a Gateway. oneOf: - properties: type: @@ -2639,15 +2973,15 @@ spec: type: string value: description: |- - When a value is unspecified, an implementation SHOULD automatically - assign an address matching the requested type if possible. - - If an implementation does not support an empty value, they MUST set the - "Programmed" condition in status to False with a reason of "AddressNotAssigned". + Value of the address. The validity of the values will depend + on the type and support by the controller. Examples: `1.2.3.4`, `128::1`, `my-ip-address`. maxLength: 253 + minLength: 1 type: string + required: + - value type: object x-kubernetes-validations: - message: Hostname value must only contain valid characters (matching @@ -2663,96 +2997,16 @@ spec: - message: Hostname values must be unique rule: 'self.all(a1, a1.type == ''Hostname'' ? self.exists_one(a2, a2.type == a1.type && a2.value == a1.value) : true )' - allowedListeners: - description: |- - AllowedListeners defines which ListenerSets can be attached to this Gateway. - While this feature is experimental, the default value is to allow no ListenerSets. - properties: - namespaces: - default: - from: None - description: |- - Namespaces defines which namespaces ListenerSets can be attached to this Gateway. - While this feature is experimental, the default value is to allow no ListenerSets. - properties: - from: - default: None - description: |- - From indicates where ListenerSets can attach to this Gateway. Possible - values are: - - * Same: Only ListenerSets in the same namespace may be attached to this Gateway. - * Selector: ListenerSets in namespaces selected by the selector may be attached to this Gateway. - * All: ListenerSets in all namespaces may be attached to this Gateway. - * None: Only listeners defined in the Gateway's spec are allowed - - While this feature is experimental, the default value None - enum: - - All - - Selector - - Same - - None - type: string - selector: - description: |- - Selector must be specified when From is set to "Selector". In that case, - only ListenerSets in Namespaces matching this Selector will be selected by this - Gateway. This field is ignored for other values of "From". - properties: - matchExpressions: - description: matchExpressions is a list of label selector - requirements. The requirements are ANDed. - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - properties: - key: - description: key is the label key that the selector - applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - type: object - type: object backendTLS: - description: |- + description: |+ BackendTLS configures TLS settings for when this Gateway is connecting to backends with TLS. Support: Core + properties: clientCertificateRef: - description: |- + description: |+ ClientCertificateRef is a reference to an object that contains a Client Certificate and the associated private key. @@ -2768,6 +3022,7 @@ spec: This setting can be overridden on the service level by use of BackendTLSPolicy. Support: Core + properties: group: default: "" @@ -2902,11 +3157,6 @@ spec: the merging behavior is implementation specific. It is generally recommended that GatewayClass provides defaults that can be overridden by a Gateway. - If the referent cannot be found, refers to an unsupported kind, or when - the data within that resource is malformed, the Gateway SHOULD be - rejected with the "Accepted" status condition set to "False" and an - "InvalidParameters" reason. - Support: Implementation-specific properties: group: @@ -2937,8 +3187,6 @@ spec: logical endpoints that are bound on this Gateway's addresses. At least one Listener MUST be specified. - ## Distinct Listeners - Each Listener in a set of Listeners (for example, in a single Gateway) MUST be _distinct_, in that a traffic flow MUST be able to be assigned to exactly one listener. (This section uses "set of Listeners" rather than @@ -2950,76 +3198,55 @@ spec: combination of Port, Protocol, and, if supported by the protocol, Hostname. Some combinations of port, protocol, and TLS settings are considered - Core support and MUST be supported by implementations based on the objects - they support: + Core support and MUST be supported by implementations based on their + targeted conformance profile: - HTTPRoute + HTTP Profile 1. HTTPRoute, Port: 80, Protocol: HTTP 2. HTTPRoute, Port: 443, Protocol: HTTPS, TLS Mode: Terminate, TLS keypair provided - TLSRoute + TLS Profile 1. TLSRoute, Port: 443, Protocol: TLS, TLS Mode: Passthrough "Distinct" Listeners have the following property: - **The implementation can match inbound requests to a single distinct - Listener**. - - When multiple Listeners share values for fields (for + The implementation can match inbound requests to a single distinct + Listener. When multiple Listeners share values for fields (for example, two Listeners with the same Port value), the implementation can match requests to only one of the Listeners using other Listener fields. - When multiple listeners have the same value for the Protocol field, then - each of the Listeners with matching Protocol values MUST have different - values for other fields. + For example, the following Listener scenarios are distinct: - The set of fields that MUST be different for a Listener differs per protocol. - The following rules define the rules for what fields MUST be considered for - Listeners to be distinct with each protocol currently defined in the - Gateway API spec. + 1. Multiple Listeners with the same Port that all use the "HTTP" + Protocol that all have unique Hostname values. + 2. Multiple Listeners with the same Port that use either the "HTTPS" or + "TLS" Protocol that all have unique Hostname values. + 3. A mixture of "TCP" and "UDP" Protocol Listeners, where no Listener + with the same Protocol has the same Port value. - The set of listeners that all share a protocol value MUST have _different_ - values for _at least one_ of these fields to be distinct: + Some fields in the Listener struct have possible values that affect + whether the Listener is distinct. Hostname is particularly relevant + for HTTP or HTTPS protocols. - * **HTTP, HTTPS, TLS**: Port, Hostname - * **TCP, UDP**: Port + When using the Hostname value to select between same-Port, same-Protocol + Listeners, the Hostname value must be different on each Listener for the + Listener to be distinct. - One **very** important rule to call out involves what happens when an - implementation: - - * Supports TCP protocol Listeners, as well as HTTP, HTTPS, or TLS protocol - Listeners, and - * sees HTTP, HTTPS, or TLS protocols with the same `port` as one with TCP - Protocol. - - In this case all the Listeners that share a port with the - TCP Listener are not distinct and so MUST NOT be accepted. - - If an implementation does not support TCP Protocol Listeners, then the - previous rule does not apply, and the TCP Listeners SHOULD NOT be - accepted. - - Note that the `tls` field is not used for determining if a listener is distinct, because - Listeners that _only_ differ on TLS config will still conflict in all cases. - - ### Listeners that are distinct only by Hostname - - When the Listeners are distinct based only on Hostname, inbound request + When the Listeners are distinct based on Hostname, inbound request hostnames MUST match from the most specific to least specific Hostname values to choose the correct Listener and its associated set of Routes. - Exact matches MUST be processed before wildcard matches, and wildcard - matches MUST be processed before fallback (empty Hostname value) + Exact matches must be processed before wildcard matches, and wildcard + matches must be processed before fallback (empty Hostname value) matches. For example, `"foo.example.com"` takes precedence over `"*.example.com"`, and `"*.example.com"` takes precedence over `""`. Additionally, if there are multiple wildcard entries, more specific wildcard entries must be processed before less specific wildcard entries. For example, `"*.foo.example.com"` takes precedence over `"*.example.com"`. - The precise definition here is that the higher the number of dots in the hostname to the right of the wildcard character, the higher the precedence. @@ -3027,26 +3254,18 @@ spec: the left, however, so `"*.example.com"` will match both `"foo.bar.example.com"` _and_ `"bar.example.com"`. - ## Handling indistinct Listeners - If a set of Listeners contains Listeners that are not distinct, then those - Listeners are _Conflicted_, and the implementation MUST set the "Conflicted" + Listeners are Conflicted, and the implementation MUST set the "Conflicted" condition in the Listener Status to "True". - The words "indistinct" and "conflicted" are considered equivalent for the - purpose of this documentation. - Implementations MAY choose to accept a Gateway with some Conflicted Listeners only if they only accept the partial Listener set that contains - no Conflicted Listeners. - - Specifically, an implementation MAY accept a partial Listener set subject to - the following rules: - - * The implementation MUST NOT pick one conflicting Listener as the winner. - ALL indistinct Listeners must not be accepted for processing. - * At least one distinct Listener MUST be present, or else the Gateway effectively - contains _no_ Listeners, and must be rejected from processing as a whole. + no Conflicted Listeners. To put this another way, implementations may + accept a partial Listener set only if they throw out *all* the conflicting + Listeners. No picking one of the conflicting listeners as the winner. + This also means that the Gateway must have at least one non-conflicting + Listener in this case, otherwise it violates the requirement that at + least one Listener must be present. The implementation MUST set a "ListenersNotValid" condition on the Gateway Status when the Gateway contains Conflicted Listeners whether or @@ -3055,25 +3274,7 @@ spec: Accepted. Additionally, the Listener status for those listeners SHOULD indicate which Listeners are conflicted and not Accepted. - ## General Listener behavior - - Note that, for all distinct Listeners, requests SHOULD match at most one Listener. - For example, if Listeners are defined for "foo.example.com" and "*.example.com", a - request to "foo.example.com" SHOULD only be routed using routes attached - to the "foo.example.com" Listener (and not the "*.example.com" Listener). - - This concept is known as "Listener Isolation", and it is an Extended feature - of Gateway API. Implementations that do not support Listener Isolation MUST - clearly document this, and MUST NOT claim support for the - `GatewayHTTPListenerIsolation` feature. - - Implementations that _do_ support Listener Isolation SHOULD claim support - for the Extended `GatewayHTTPListenerIsolation` feature and pass the associated - conformance tests. - - ## Compatible Listeners - - A Gateway's Listeners are considered _compatible_ if: + A Gateway's Listeners are considered "compatible" if: 1. They are distinct. 2. The implementation can serve them in compliance with the Addresses @@ -3088,11 +3289,16 @@ spec: on the same address, or cannot mix HTTPS and generic TLS listens on the same port would not consider those cases compatible, even though they are distinct. + Note that requests SHOULD match at most one Listener. For example, if + Listeners are defined for "foo.example.com" and "*.example.com", a + request to "foo.example.com" SHOULD only be routed using routes attached + to the "foo.example.com" Listener (and not the "*.example.com" Listener). + This concept is known as "Listener Isolation". Implementations that do + not support Listener Isolation MUST clearly document this. + Implementations MAY merge separate Gateways onto a single set of Addresses if all Listeners across all Gateways are compatible. - In a future release the MinItems=1 requirement MAY be dropped. - Support: Core items: description: |- @@ -3254,31 +3460,10 @@ spec: * TLS: The Listener Hostname MUST match the SNI. * HTTP: The Listener Hostname MUST match the Host header of the request. - * HTTPS: The Listener Hostname SHOULD match both the SNI and Host header. - Note that this does not require the SNI and Host header to be the same. - The semantics of this are described in more detail below. - - To ensure security, Section 11.1 of RFC-6066 emphasizes that server - implementations that rely on SNI hostname matching MUST also verify - hostnames within the application protocol. - - Section 9.1.2 of RFC-7540 provides a mechanism for servers to reject the - reuse of a connection by responding with the HTTP 421 Misdirected Request - status code. This indicates that the origin server has rejected the - request because it appears to have been misdirected. - - To detect misdirected requests, Gateways SHOULD match the authority of - the requests with all the SNI hostname(s) configured across all the - Gateway Listeners on the same port and protocol: - - * If another Listener has an exact match or more specific wildcard entry, - the Gateway SHOULD return a 421. - * If the current Listener (selected by SNI matching during ClientHello) - does not match the Host: - * If another Listener does match the Host the Gateway SHOULD return a - 421. - * If no other Listener matches the Host, the Gateway MUST return a - 404. + * HTTPS: The Listener Hostname SHOULD match at both the TLS and HTTP + protocol layers as described above. If an implementation does not + ensure that both the SNI and Host header match the Listener hostname, + it MUST clearly document that. For HTTPRoute and TLSRoute resources, there is an interaction with the `spec.hostnames` array. When both listener and route specify hostnames, @@ -3418,7 +3603,7 @@ spec: maxItems: 64 type: array frontendValidation: - description: |- + description: |+ FrontendValidation holds configuration information for validating the frontend (client). Setting this field will require clients to send a client certificate required for validation during the TLS handshake. In browsers this may result in a dialog appearing @@ -3426,6 +3611,7 @@ spec: The maximum depth of a certificate chain accepted in verification is Implementation specific. Support: Extended + properties: caCertificateRefs: description: |- @@ -3464,7 +3650,7 @@ spec: group: description: |- Group is the group of the referent. For example, "gateway.networking.k8s.io". - When set to the empty string, core API group is inferred. + When unspecified or empty string, core API group is inferred. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string @@ -3602,7 +3788,7 @@ spec: description: Status defines the current state of Gateway. properties: addresses: - description: |- + description: |+ Addresses lists the network addresses that have been bound to the Gateway. @@ -3612,6 +3798,7 @@ spec: * no addresses are specified, all addresses are dynamically assigned * a combination of specified and dynamic addresses are assigned * a specified address was unusable (e.g. already in use) + items: description: GatewayStatusAddress describes a network address that is bound to a Gateway. @@ -3903,7 +4090,7 @@ kind: CustomResourceDefinition metadata: annotations: api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/3328 - gateway.networking.k8s.io/bundle-version: v1.3.0 + gateway.networking.k8s.io/bundle-version: v1.2.1 gateway.networking.k8s.io/channel: experimental creationTimestamp: null name: grpcroutes.gateway.networking.k8s.io @@ -4052,7 +4239,7 @@ spec: maxItems: 16 type: array parentRefs: - description: |- + description: |+ ParentRefs references the resources (usually Gateways) that a Route wants to be attached to. Note that the referenced parent resource needs to allow this for the attachment to be complete. For Gateways, that means @@ -4114,6 +4301,11 @@ spec: connections originating from the same namespace as the Route, for which the intended destination of the connections are a Service targeted as a ParentRef of the Route. + + + + + items: description: |- ParentReference identifies an API object (usually a Gateway) that can be considered @@ -4287,7 +4479,9 @@ spec: || p2.port == 0)) || (has(p1.port) && has(p2.port) && p1.port == p2.port)))) rules: - description: Rules are a list of GRPC matchers, filters and actions. + description: |+ + Rules are a list of GRPC matchers, filters and actions. + items: description: |- GRPCRouteRule defines the semantics for matching a gRPC request based on @@ -4333,6 +4527,7 @@ spec: namespace's owner to accept the reference. See the ReferenceGrant documentation for details. + When the BackendRef points to a Kubernetes Service, implementations SHOULD honor the appProtocol field if it is set for the target Service Port. @@ -4347,6 +4542,8 @@ spec: If a Route is not able to send traffic to the backend using the specified protocol then the backend is considered invalid. Implementations MUST set the "ResolvedRefs" condition to "False" with the "UnsupportedProtocol" reason. + + properties: filters: description: |- @@ -4432,7 +4629,7 @@ spec: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be - case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries @@ -4507,7 +4704,7 @@ spec: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be - case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries @@ -4535,7 +4732,7 @@ spec: x-kubernetes-list-type: map type: object requestMirror: - description: |- + description: |+ RequestMirror defines a schema for a filter that mirrors requests. Requests are sent to the specified destination, but responses from that destination are ignored. @@ -4545,6 +4742,7 @@ spec: backends. Support: Extended + properties: backendRef: description: |- @@ -4640,12 +4838,13 @@ spec: rule: '(size(self.group) == 0 && self.kind == ''Service'') ? has(self.port) : true' fraction: - description: |- + description: |+ Fraction represents the fraction of requests that should be mirrored to BackendRef. Only one of Fraction or Percent may be specified. If neither field is specified, 100% of requests will be mirrored. + properties: denominator: default: 100 @@ -4664,13 +4863,14 @@ spec: to denominator rule: self.numerator <= self.denominator percent: - description: |- + description: |+ Percent represents the percentage of requests that should be mirrored to BackendRef. Its minimum value is 0 (indicating 0% of requests) and its maximum value is 100 (indicating 100% of requests). Only one of Fraction or Percent may be specified. If neither field is specified, 100% of requests will be mirrored. + format: int32 maximum: 100 minimum: 0 @@ -4715,7 +4915,7 @@ spec: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be - case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries @@ -4790,7 +4990,7 @@ spec: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be - case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries @@ -4818,7 +5018,7 @@ spec: x-kubernetes-list-type: map type: object type: - description: |- + description: |+ Type identifies the type of filter to apply. As with other API fields, types are classified into three conformance levels: @@ -4843,6 +5043,7 @@ spec: If a reference to a custom filter type cannot be resolved, the filter MUST NOT be skipped. Instead, requests that would have been processed by that filter MUST receive a HTTP error response. + enum: - ResponseHeaderModifier - RequestHeaderModifier @@ -4999,7 +5200,7 @@ spec: Specifying the same filter multiple times is not supported unless explicitly indicated in the filter. - If an implementation cannot support a combination of filters, it must clearly + If an implementation can not support a combination of filters, it must clearly document that limitation. In cases where incompatible or unsupported filters are specified and cause the `Accepted` condition to be set to status `False`, implementations may use the `IncompatibleFilters` reason to specify @@ -5082,7 +5283,7 @@ spec: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be - case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries @@ -5156,7 +5357,7 @@ spec: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be - case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries @@ -5184,7 +5385,7 @@ spec: x-kubernetes-list-type: map type: object requestMirror: - description: |- + description: |+ RequestMirror defines a schema for a filter that mirrors requests. Requests are sent to the specified destination, but responses from that destination are ignored. @@ -5194,6 +5395,7 @@ spec: backends. Support: Extended + properties: backendRef: description: |- @@ -5289,12 +5491,13 @@ spec: rule: '(size(self.group) == 0 && self.kind == ''Service'') ? has(self.port) : true' fraction: - description: |- + description: |+ Fraction represents the fraction of requests that should be mirrored to BackendRef. Only one of Fraction or Percent may be specified. If neither field is specified, 100% of requests will be mirrored. + properties: denominator: default: 100 @@ -5313,13 +5516,14 @@ spec: denominator rule: self.numerator <= self.denominator percent: - description: |- + description: |+ Percent represents the percentage of requests that should be mirrored to BackendRef. Its minimum value is 0 (indicating 0% of requests) and its maximum value is 100 (indicating 100% of requests). Only one of Fraction or Percent may be specified. If neither field is specified, 100% of requests will be mirrored. + format: int32 maximum: 100 minimum: 0 @@ -5363,7 +5567,7 @@ spec: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be - case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries @@ -5437,7 +5641,7 @@ spec: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be - case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries @@ -5465,7 +5669,7 @@ spec: x-kubernetes-list-type: map type: object type: - description: |- + description: |+ Type identifies the type of filter to apply. As with other API fields, types are classified into three conformance levels: @@ -5490,6 +5694,7 @@ spec: If a reference to a custom filter type cannot be resolved, the filter MUST NOT be skipped. Instead, requests that would have been processed by that filter MUST receive a HTTP error response. + enum: - ResponseHeaderModifier - RequestHeaderModifier @@ -5705,10 +5910,10 @@ spec: has(self.method) ? self.method.matches(r"""^[A-Za-z_][A-Za-z_0-9]*$"""): true' type: object - maxItems: 64 + maxItems: 8 type: array name: - description: |- + description: | Name is the name of the route rule. This name MUST be unique within a Route if it is set. Support: Extended @@ -5717,11 +5922,12 @@ spec: pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string sessionPersistence: - description: |- + description: |+ SessionPersistence defines and configures session persistence for the route rule. Support: Extended + properties: absoluteTimeout: description: |- @@ -5756,8 +5962,6 @@ spec: absolute lifetime of the cookie tracked by the gateway and is optional. - Defaults to "Session". - Support: Core for "Session" type Support: Extended for "Permanent" type @@ -5875,7 +6079,7 @@ spec: There are a number of cases where the "Accepted" condition may not be set due to lack of controller visibility, that includes when: - * The Route refers to a nonexistent parent. + * The Route refers to a non-existent parent. * The Route is of a type that the controller does not support. * The Route is in a namespace the controller does not have access to. items: @@ -6124,7 +6328,7 @@ kind: CustomResourceDefinition metadata: annotations: api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/3328 - gateway.networking.k8s.io/bundle-version: v1.3.0 + gateway.networking.k8s.io/bundle-version: v1.2.1 gateway.networking.k8s.io/channel: experimental creationTimestamp: null name: httproutes.gateway.networking.k8s.io @@ -6253,7 +6457,7 @@ spec: maxItems: 16 type: array parentRefs: - description: |- + description: |+ ParentRefs references the resources (usually Gateways) that a Route wants to be attached to. Note that the referenced parent resource needs to allow this for the attachment to be complete. For Gateways, that means @@ -6315,6 +6519,11 @@ spec: connections originating from the same namespace as the Route, for which the intended destination of the connections are a Service targeted as a ParentRef of the Route. + + + + + items: description: |- ParentReference identifies an API object (usually a Gateway) that can be considered @@ -6493,7 +6702,9 @@ spec: - path: type: PathPrefix value: / - description: Rules are a list of HTTP matchers, filters and actions. + description: |+ + Rules are a list of HTTP matchers, filters and actions. + items: description: |- HTTPRouteRule defines semantics for matching an HTTP request based on @@ -6546,6 +6757,7 @@ spec: namespace's owner to accept the reference. See the ReferenceGrant documentation for details. + When the BackendRef points to a Kubernetes Service, implementations SHOULD honor the appProtocol field if it is set for the target Service Port. @@ -6560,6 +6772,8 @@ spec: If a Route is not able to send traffic to the backend using the specified protocol then the backend is considered invalid. Implementations MUST set the "ResolvedRefs" condition to "False" with the "UnsupportedProtocol" reason. + + properties: filters: description: |- @@ -6577,289 +6791,6 @@ spec: authentication strategies, rate-limiting, and traffic shaping. API guarantee/conformance is defined based on the type of the filter. properties: - cors: - description: |- - CORS defines a schema for a filter that responds to the - cross-origin request based on HTTP response header. - - Support: Extended - properties: - allowCredentials: - description: |- - AllowCredentials indicates whether the actual cross-origin request allows - to include credentials. - - The only valid value for the `Access-Control-Allow-Credentials` response - header is true (case-sensitive). - - If the credentials are not allowed in cross-origin requests, the gateway - will omit the header `Access-Control-Allow-Credentials` entirely rather - than setting its value to false. - - Support: Extended - enum: - - true - type: boolean - allowHeaders: - description: |- - AllowHeaders indicates which HTTP request headers are supported for - accessing the requested resource. - - Header names are not case sensitive. - - Multiple header names in the value of the `Access-Control-Allow-Headers` - response header are separated by a comma (","). - - When the `AllowHeaders` field is configured with one or more headers, the - gateway must return the `Access-Control-Allow-Headers` response header - which value is present in the `AllowHeaders` field. - - If any header name in the `Access-Control-Request-Headers` request header - is not included in the list of header names specified by the response - header `Access-Control-Allow-Headers`, it will present an error on the - client side. - - If any header name in the `Access-Control-Allow-Headers` response header - does not recognize by the client, it will also occur an error on the - client side. - - A wildcard indicates that the requests with all HTTP headers are allowed. - The `Access-Control-Allow-Headers` response header can only use `*` - wildcard as value when the `AllowCredentials` field is unspecified. - - When the `AllowCredentials` field is specified and `AllowHeaders` field - specified with the `*` wildcard, the gateway must specify one or more - HTTP headers in the value of the `Access-Control-Allow-Headers` response - header. The value of the header `Access-Control-Allow-Headers` is same as - the `Access-Control-Request-Headers` header provided by the client. If - the header `Access-Control-Request-Headers` is not included in the - request, the gateway will omit the `Access-Control-Allow-Headers` - response header, instead of specifying the `*` wildcard. A Gateway - implementation may choose to add implementation-specific default headers. - - Support: Extended - items: - description: |- - HTTPHeaderName is the name of an HTTP header. - - Valid values include: - - * "Authorization" - * "Set-Cookie" - - Invalid values include: - - - ":method" - ":" is an invalid character. This means that HTTP/2 pseudo - headers are not currently supported by this type. - - "/invalid" - "/ " is an invalid character - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - maxItems: 64 - type: array - x-kubernetes-list-type: set - allowMethods: - description: |- - AllowMethods indicates which HTTP methods are supported for accessing the - requested resource. - - Valid values are any method defined by RFC9110, along with the special - value `*`, which represents all HTTP methods are allowed. - - Method names are case sensitive, so these values are also case-sensitive. - (See https://www.rfc-editor.org/rfc/rfc2616#section-5.1.1) - - Multiple method names in the value of the `Access-Control-Allow-Methods` - response header are separated by a comma (","). - - A CORS-safelisted method is a method that is `GET`, `HEAD`, or `POST`. - (See https://fetch.spec.whatwg.org/#cors-safelisted-method) The - CORS-safelisted methods are always allowed, regardless of whether they - are specified in the `AllowMethods` field. - - When the `AllowMethods` field is configured with one or more methods, the - gateway must return the `Access-Control-Allow-Methods` response header - which value is present in the `AllowMethods` field. - - If the HTTP method of the `Access-Control-Request-Method` request header - is not included in the list of methods specified by the response header - `Access-Control-Allow-Methods`, it will present an error on the client - side. - - The `Access-Control-Allow-Methods` response header can only use `*` - wildcard as value when the `AllowCredentials` field is unspecified. - - When the `AllowCredentials` field is specified and `AllowMethods` field - specified with the `*` wildcard, the gateway must specify one HTTP method - in the value of the Access-Control-Allow-Methods response header. The - value of the header `Access-Control-Allow-Methods` is same as the - `Access-Control-Request-Method` header provided by the client. If the - header `Access-Control-Request-Method` is not included in the request, - the gateway will omit the `Access-Control-Allow-Methods` response header, - instead of specifying the `*` wildcard. A Gateway implementation may - choose to add implementation-specific default methods. - - Support: Extended - items: - enum: - - GET - - HEAD - - POST - - PUT - - DELETE - - CONNECT - - OPTIONS - - TRACE - - PATCH - - '*' - type: string - maxItems: 9 - type: array - x-kubernetes-list-type: set - x-kubernetes-validations: - - message: AllowMethods cannot contain '*' alongside - other methods - rule: '!(''*'' in self && self.size() > 1)' - allowOrigins: - description: |- - AllowOrigins indicates whether the response can be shared with requested - resource from the given `Origin`. - - The `Origin` consists of a scheme and a host, with an optional port, and - takes the form `://(:)`. - - Valid values for scheme are: `http` and `https`. - - Valid values for port are any integer between 1 and 65535 (the list of - available TCP/UDP ports). Note that, if not included, port `80` is - assumed for `http` scheme origins, and port `443` is assumed for `https` - origins. This may affect origin matching. - - The host part of the origin may contain the wildcard character `*`. These - wildcard characters behave as follows: - - * `*` is a greedy match to the _left_, including any number of - DNS labels to the left of its position. This also means that - `*` will include any number of period `.` characters to the - left of its position. - * A wildcard by itself matches all hosts. - - An origin value that includes _only_ the `*` character indicates requests - from all `Origin`s are allowed. - - When the `AllowOrigins` field is configured with multiple origins, it - means the server supports clients from multiple origins. If the request - `Origin` matches the configured allowed origins, the gateway must return - the given `Origin` and sets value of the header - `Access-Control-Allow-Origin` same as the `Origin` header provided by the - client. - - The status code of a successful response to a "preflight" request is - always an OK status (i.e., 204 or 200). - - If the request `Origin` does not match the configured allowed origins, - the gateway returns 204/200 response but doesn't set the relevant - cross-origin response headers. Alternatively, the gateway responds with - 403 status to the "preflight" request is denied, coupled with omitting - the CORS headers. The cross-origin request fails on the client side. - Therefore, the client doesn't attempt the actual cross-origin request. - - The `Access-Control-Allow-Origin` response header can only use `*` - wildcard as value when the `AllowCredentials` field is unspecified. - - When the `AllowCredentials` field is specified and `AllowOrigins` field - specified with the `*` wildcard, the gateway must return a single origin - in the value of the `Access-Control-Allow-Origin` response header, - instead of specifying the `*` wildcard. The value of the header - `Access-Control-Allow-Origin` is same as the `Origin` header provided by - the client. - - Support: Extended - items: - description: |- - The AbsoluteURI MUST NOT be a relative URI, and it MUST follow the URI syntax and - encoding rules specified in RFC3986. The AbsoluteURI MUST include both a - scheme (e.g., "http" or "spiffe") and a scheme-specific-part. URIs that - include an authority MUST include a fully qualified domain name or - IP address as the host. - maxLength: 253 - minLength: 1 - pattern: ^(([^:/?#]+):)(//([^/?#]*))([^?#]*)(\?([^#]*))?(#(.*))? - type: string - maxItems: 64 - type: array - x-kubernetes-list-type: set - exposeHeaders: - description: |- - ExposeHeaders indicates which HTTP response headers can be exposed - to client-side scripts in response to a cross-origin request. - - A CORS-safelisted response header is an HTTP header in a CORS response - that it is considered safe to expose to the client scripts. - The CORS-safelisted response headers include the following headers: - `Cache-Control` - `Content-Language` - `Content-Length` - `Content-Type` - `Expires` - `Last-Modified` - `Pragma` - (See https://fetch.spec.whatwg.org/#cors-safelisted-response-header-name) - The CORS-safelisted response headers are exposed to client by default. - - When an HTTP header name is specified using the `ExposeHeaders` field, - this additional header will be exposed as part of the response to the - client. - - Header names are not case sensitive. - - Multiple header names in the value of the `Access-Control-Expose-Headers` - response header are separated by a comma (","). - - A wildcard indicates that the responses with all HTTP headers are exposed - to clients. The `Access-Control-Expose-Headers` response header can only - use `*` wildcard as value when the `AllowCredentials` field is - unspecified. - - Support: Extended - items: - description: |- - HTTPHeaderName is the name of an HTTP header. - - Valid values include: - - * "Authorization" - * "Set-Cookie" - - Invalid values include: - - - ":method" - ":" is an invalid character. This means that HTTP/2 pseudo - headers are not currently supported by this type. - - "/invalid" - "/ " is an invalid character - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - maxItems: 64 - type: array - x-kubernetes-list-type: set - maxAge: - default: 5 - description: |- - MaxAge indicates the duration (in seconds) for the client to cache the - results of a "preflight" request. - - The information provided by the `Access-Control-Allow-Methods` and - `Access-Control-Allow-Headers` response headers can be cached by the - client until the time specified by `Access-Control-Max-Age` elapses. - - The default value of `Access-Control-Max-Age` response header is 5 - (seconds). - format: int32 - minimum: 1 - type: integer - type: object extensionRef: description: |- ExtensionRef is an optional, implementation-specific extension to the @@ -6928,7 +6859,7 @@ spec: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be - case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries @@ -7003,7 +6934,7 @@ spec: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be - case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries @@ -7031,7 +6962,7 @@ spec: x-kubernetes-list-type: map type: object requestMirror: - description: |- + description: |+ RequestMirror defines a schema for a filter that mirrors requests. Requests are sent to the specified destination, but responses from that destination are ignored. @@ -7041,6 +6972,7 @@ spec: backends. Support: Extended + properties: backendRef: description: |- @@ -7136,12 +7068,13 @@ spec: rule: '(size(self.group) == 0 && self.kind == ''Service'') ? has(self.port) : true' fraction: - description: |- + description: |+ Fraction represents the fraction of requests that should be mirrored to BackendRef. Only one of Fraction or Percent may be specified. If neither field is specified, 100% of requests will be mirrored. + properties: denominator: default: 100 @@ -7160,13 +7093,14 @@ spec: to denominator rule: self.numerator <= self.denominator percent: - description: |- + description: |+ Percent represents the percentage of requests that should be mirrored to BackendRef. Its minimum value is 0 (indicating 0% of requests) and its maximum value is 100 (indicating 100% of requests). Only one of Fraction or Percent may be specified. If neither field is specified, 100% of requests will be mirrored. + format: int32 maximum: 100 minimum: 0 @@ -7364,7 +7298,7 @@ spec: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be - case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries @@ -7439,7 +7373,7 @@ spec: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be - case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries @@ -7507,7 +7441,6 @@ spec: - RequestRedirect - URLRewrite - ExtensionRef - - CORS type: string urlRewrite: description: |- @@ -7640,11 +7573,6 @@ spec: - message: filter.extensionRef must be specified for ExtensionRef filter.type rule: '!(!has(self.extensionRef) && self.type == ''ExtensionRef'')' - - message: filter.cors must be nil if the filter.type - is not CORS - rule: '!(has(self.cors) && self.type != ''CORS'')' - - message: filter.cors must be specified for CORS filter.type - rule: '!(!has(self.cors) && self.type == ''CORS'')' maxItems: 16 type: array x-kubernetes-validations: @@ -7766,7 +7694,7 @@ spec: they are specified. Implementations MAY choose to implement this ordering strictly, rejecting - any combination or order of filters that cannot be supported. If implementations + any combination or order of filters that can not be supported. If implementations choose a strict interpretation of filter ordering, they MUST clearly document that behavior. @@ -7788,7 +7716,7 @@ spec: All filters are expected to be compatible with each other except for the URLRewrite and RequestRedirect filters, which may not be combined. If an - implementation cannot support other combinations of filters, they must clearly + implementation can not support other combinations of filters, they must clearly document that limitation. In cases where incompatible or unsupported filters are specified and cause the `Accepted` condition to be set to status `False`, implementations may use the `IncompatibleFilters` reason to specify @@ -7804,289 +7732,6 @@ spec: authentication strategies, rate-limiting, and traffic shaping. API guarantee/conformance is defined based on the type of the filter. properties: - cors: - description: |- - CORS defines a schema for a filter that responds to the - cross-origin request based on HTTP response header. - - Support: Extended - properties: - allowCredentials: - description: |- - AllowCredentials indicates whether the actual cross-origin request allows - to include credentials. - - The only valid value for the `Access-Control-Allow-Credentials` response - header is true (case-sensitive). - - If the credentials are not allowed in cross-origin requests, the gateway - will omit the header `Access-Control-Allow-Credentials` entirely rather - than setting its value to false. - - Support: Extended - enum: - - true - type: boolean - allowHeaders: - description: |- - AllowHeaders indicates which HTTP request headers are supported for - accessing the requested resource. - - Header names are not case sensitive. - - Multiple header names in the value of the `Access-Control-Allow-Headers` - response header are separated by a comma (","). - - When the `AllowHeaders` field is configured with one or more headers, the - gateway must return the `Access-Control-Allow-Headers` response header - which value is present in the `AllowHeaders` field. - - If any header name in the `Access-Control-Request-Headers` request header - is not included in the list of header names specified by the response - header `Access-Control-Allow-Headers`, it will present an error on the - client side. - - If any header name in the `Access-Control-Allow-Headers` response header - does not recognize by the client, it will also occur an error on the - client side. - - A wildcard indicates that the requests with all HTTP headers are allowed. - The `Access-Control-Allow-Headers` response header can only use `*` - wildcard as value when the `AllowCredentials` field is unspecified. - - When the `AllowCredentials` field is specified and `AllowHeaders` field - specified with the `*` wildcard, the gateway must specify one or more - HTTP headers in the value of the `Access-Control-Allow-Headers` response - header. The value of the header `Access-Control-Allow-Headers` is same as - the `Access-Control-Request-Headers` header provided by the client. If - the header `Access-Control-Request-Headers` is not included in the - request, the gateway will omit the `Access-Control-Allow-Headers` - response header, instead of specifying the `*` wildcard. A Gateway - implementation may choose to add implementation-specific default headers. - - Support: Extended - items: - description: |- - HTTPHeaderName is the name of an HTTP header. - - Valid values include: - - * "Authorization" - * "Set-Cookie" - - Invalid values include: - - - ":method" - ":" is an invalid character. This means that HTTP/2 pseudo - headers are not currently supported by this type. - - "/invalid" - "/ " is an invalid character - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - maxItems: 64 - type: array - x-kubernetes-list-type: set - allowMethods: - description: |- - AllowMethods indicates which HTTP methods are supported for accessing the - requested resource. - - Valid values are any method defined by RFC9110, along with the special - value `*`, which represents all HTTP methods are allowed. - - Method names are case sensitive, so these values are also case-sensitive. - (See https://www.rfc-editor.org/rfc/rfc2616#section-5.1.1) - - Multiple method names in the value of the `Access-Control-Allow-Methods` - response header are separated by a comma (","). - - A CORS-safelisted method is a method that is `GET`, `HEAD`, or `POST`. - (See https://fetch.spec.whatwg.org/#cors-safelisted-method) The - CORS-safelisted methods are always allowed, regardless of whether they - are specified in the `AllowMethods` field. - - When the `AllowMethods` field is configured with one or more methods, the - gateway must return the `Access-Control-Allow-Methods` response header - which value is present in the `AllowMethods` field. - - If the HTTP method of the `Access-Control-Request-Method` request header - is not included in the list of methods specified by the response header - `Access-Control-Allow-Methods`, it will present an error on the client - side. - - The `Access-Control-Allow-Methods` response header can only use `*` - wildcard as value when the `AllowCredentials` field is unspecified. - - When the `AllowCredentials` field is specified and `AllowMethods` field - specified with the `*` wildcard, the gateway must specify one HTTP method - in the value of the Access-Control-Allow-Methods response header. The - value of the header `Access-Control-Allow-Methods` is same as the - `Access-Control-Request-Method` header provided by the client. If the - header `Access-Control-Request-Method` is not included in the request, - the gateway will omit the `Access-Control-Allow-Methods` response header, - instead of specifying the `*` wildcard. A Gateway implementation may - choose to add implementation-specific default methods. - - Support: Extended - items: - enum: - - GET - - HEAD - - POST - - PUT - - DELETE - - CONNECT - - OPTIONS - - TRACE - - PATCH - - '*' - type: string - maxItems: 9 - type: array - x-kubernetes-list-type: set - x-kubernetes-validations: - - message: AllowMethods cannot contain '*' alongside - other methods - rule: '!(''*'' in self && self.size() > 1)' - allowOrigins: - description: |- - AllowOrigins indicates whether the response can be shared with requested - resource from the given `Origin`. - - The `Origin` consists of a scheme and a host, with an optional port, and - takes the form `://(:)`. - - Valid values for scheme are: `http` and `https`. - - Valid values for port are any integer between 1 and 65535 (the list of - available TCP/UDP ports). Note that, if not included, port `80` is - assumed for `http` scheme origins, and port `443` is assumed for `https` - origins. This may affect origin matching. - - The host part of the origin may contain the wildcard character `*`. These - wildcard characters behave as follows: - - * `*` is a greedy match to the _left_, including any number of - DNS labels to the left of its position. This also means that - `*` will include any number of period `.` characters to the - left of its position. - * A wildcard by itself matches all hosts. - - An origin value that includes _only_ the `*` character indicates requests - from all `Origin`s are allowed. - - When the `AllowOrigins` field is configured with multiple origins, it - means the server supports clients from multiple origins. If the request - `Origin` matches the configured allowed origins, the gateway must return - the given `Origin` and sets value of the header - `Access-Control-Allow-Origin` same as the `Origin` header provided by the - client. - - The status code of a successful response to a "preflight" request is - always an OK status (i.e., 204 or 200). - - If the request `Origin` does not match the configured allowed origins, - the gateway returns 204/200 response but doesn't set the relevant - cross-origin response headers. Alternatively, the gateway responds with - 403 status to the "preflight" request is denied, coupled with omitting - the CORS headers. The cross-origin request fails on the client side. - Therefore, the client doesn't attempt the actual cross-origin request. - - The `Access-Control-Allow-Origin` response header can only use `*` - wildcard as value when the `AllowCredentials` field is unspecified. - - When the `AllowCredentials` field is specified and `AllowOrigins` field - specified with the `*` wildcard, the gateway must return a single origin - in the value of the `Access-Control-Allow-Origin` response header, - instead of specifying the `*` wildcard. The value of the header - `Access-Control-Allow-Origin` is same as the `Origin` header provided by - the client. - - Support: Extended - items: - description: |- - The AbsoluteURI MUST NOT be a relative URI, and it MUST follow the URI syntax and - encoding rules specified in RFC3986. The AbsoluteURI MUST include both a - scheme (e.g., "http" or "spiffe") and a scheme-specific-part. URIs that - include an authority MUST include a fully qualified domain name or - IP address as the host. - maxLength: 253 - minLength: 1 - pattern: ^(([^:/?#]+):)(//([^/?#]*))([^?#]*)(\?([^#]*))?(#(.*))? - type: string - maxItems: 64 - type: array - x-kubernetes-list-type: set - exposeHeaders: - description: |- - ExposeHeaders indicates which HTTP response headers can be exposed - to client-side scripts in response to a cross-origin request. - - A CORS-safelisted response header is an HTTP header in a CORS response - that it is considered safe to expose to the client scripts. - The CORS-safelisted response headers include the following headers: - `Cache-Control` - `Content-Language` - `Content-Length` - `Content-Type` - `Expires` - `Last-Modified` - `Pragma` - (See https://fetch.spec.whatwg.org/#cors-safelisted-response-header-name) - The CORS-safelisted response headers are exposed to client by default. - - When an HTTP header name is specified using the `ExposeHeaders` field, - this additional header will be exposed as part of the response to the - client. - - Header names are not case sensitive. - - Multiple header names in the value of the `Access-Control-Expose-Headers` - response header are separated by a comma (","). - - A wildcard indicates that the responses with all HTTP headers are exposed - to clients. The `Access-Control-Expose-Headers` response header can only - use `*` wildcard as value when the `AllowCredentials` field is - unspecified. - - Support: Extended - items: - description: |- - HTTPHeaderName is the name of an HTTP header. - - Valid values include: - - * "Authorization" - * "Set-Cookie" - - Invalid values include: - - - ":method" - ":" is an invalid character. This means that HTTP/2 pseudo - headers are not currently supported by this type. - - "/invalid" - "/ " is an invalid character - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - maxItems: 64 - type: array - x-kubernetes-list-type: set - maxAge: - default: 5 - description: |- - MaxAge indicates the duration (in seconds) for the client to cache the - results of a "preflight" request. - - The information provided by the `Access-Control-Allow-Methods` and - `Access-Control-Allow-Headers` response headers can be cached by the - client until the time specified by `Access-Control-Max-Age` elapses. - - The default value of `Access-Control-Max-Age` response header is 5 - (seconds). - format: int32 - minimum: 1 - type: integer - type: object extensionRef: description: |- ExtensionRef is an optional, implementation-specific extension to the @@ -8154,7 +7799,7 @@ spec: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be - case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries @@ -8228,7 +7873,7 @@ spec: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be - case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries @@ -8256,7 +7901,7 @@ spec: x-kubernetes-list-type: map type: object requestMirror: - description: |- + description: |+ RequestMirror defines a schema for a filter that mirrors requests. Requests are sent to the specified destination, but responses from that destination are ignored. @@ -8266,6 +7911,7 @@ spec: backends. Support: Extended + properties: backendRef: description: |- @@ -8361,12 +8007,13 @@ spec: rule: '(size(self.group) == 0 && self.kind == ''Service'') ? has(self.port) : true' fraction: - description: |- + description: |+ Fraction represents the fraction of requests that should be mirrored to BackendRef. Only one of Fraction or Percent may be specified. If neither field is specified, 100% of requests will be mirrored. + properties: denominator: default: 100 @@ -8385,13 +8032,14 @@ spec: denominator rule: self.numerator <= self.denominator percent: - description: |- + description: |+ Percent represents the percentage of requests that should be mirrored to BackendRef. Its minimum value is 0 (indicating 0% of requests) and its maximum value is 100 (indicating 100% of requests). Only one of Fraction or Percent may be specified. If neither field is specified, 100% of requests will be mirrored. + format: int32 maximum: 100 minimum: 0 @@ -8588,7 +8236,7 @@ spec: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be - case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries @@ -8662,7 +8310,7 @@ spec: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be - case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries @@ -8730,7 +8378,6 @@ spec: - RequestRedirect - URLRewrite - ExtensionRef - - CORS type: string urlRewrite: description: |- @@ -8860,11 +8507,6 @@ spec: - message: filter.extensionRef must be specified for ExtensionRef filter.type rule: '!(!has(self.extensionRef) && self.type == ''ExtensionRef'')' - - message: filter.cors must be nil if the filter.type is not - CORS - rule: '!(has(self.cors) && self.type != ''CORS'')' - - message: filter.cors must be specified for CORS filter.type - rule: '!(!has(self.cors) && self.type == ''CORS'')' maxItems: 16 type: array x-kubernetes-validations: @@ -8968,7 +8610,7 @@ spec: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be - case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, only the first entry with an equivalent name MUST be considered for a match. Subsequent @@ -9179,7 +8821,7 @@ spec: maxItems: 64 type: array name: - description: |- + description: | Name is the name of the route rule. This name MUST be unique within a Route if it is set. Support: Extended @@ -9188,14 +8830,15 @@ spec: pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string retry: - description: |- + description: |+ Retry defines the configuration for when to retry an HTTP request. Support: Extended + properties: attempts: description: |- - Attempts specifies the maximum number of times an individual request + Attempts specifies the maxmimum number of times an individual request from the gateway to a backend should be retried. If the maximum number of retries has been attempted without a successful @@ -9269,17 +8912,20 @@ spec: Implementations MAY support specifying discrete values in the 400-499 range, which are often inadvisable to retry. + + maximum: 599 minimum: 400 type: integer type: array type: object sessionPersistence: - description: |- + description: |+ SessionPersistence defines and configures session persistence for the route rule. Support: Extended + properties: absoluteTimeout: description: |- @@ -9314,8 +8960,6 @@ spec: absolute lifetime of the cookie tracked by the gateway and is optional. - Defaults to "Session". - Support: Core for "Session" type Support: Extended for "Permanent" type @@ -9529,7 +9173,7 @@ spec: There are a number of cases where the "Accepted" condition may not be set due to lack of controller visibility, that includes when: - * The Route refers to a nonexistent parent. + * The Route refers to a non-existent parent. * The Route is of a type that the controller does not support. * The Route is in a namespace the controller does not have access to. items: @@ -9879,7 +9523,7 @@ spec: maxItems: 16 type: array parentRefs: - description: |- + description: |+ ParentRefs references the resources (usually Gateways) that a Route wants to be attached to. Note that the referenced parent resource needs to allow this for the attachment to be complete. For Gateways, that means @@ -9941,6 +9585,11 @@ spec: connections originating from the same namespace as the Route, for which the intended destination of the connections are a Service targeted as a ParentRef of the Route. + + + + + items: description: |- ParentReference identifies an API object (usually a Gateway) that can be considered @@ -10119,7 +9768,9 @@ spec: - path: type: PathPrefix value: / - description: Rules are a list of HTTP matchers, filters and actions. + description: |+ + Rules are a list of HTTP matchers, filters and actions. + items: description: |- HTTPRouteRule defines semantics for matching an HTTP request based on @@ -10172,6 +9823,7 @@ spec: namespace's owner to accept the reference. See the ReferenceGrant documentation for details. + When the BackendRef points to a Kubernetes Service, implementations SHOULD honor the appProtocol field if it is set for the target Service Port. @@ -10186,6 +9838,8 @@ spec: If a Route is not able to send traffic to the backend using the specified protocol then the backend is considered invalid. Implementations MUST set the "ResolvedRefs" condition to "False" with the "UnsupportedProtocol" reason. + + properties: filters: description: |- @@ -10203,289 +9857,6 @@ spec: authentication strategies, rate-limiting, and traffic shaping. API guarantee/conformance is defined based on the type of the filter. properties: - cors: - description: |- - CORS defines a schema for a filter that responds to the - cross-origin request based on HTTP response header. - - Support: Extended - properties: - allowCredentials: - description: |- - AllowCredentials indicates whether the actual cross-origin request allows - to include credentials. - - The only valid value for the `Access-Control-Allow-Credentials` response - header is true (case-sensitive). - - If the credentials are not allowed in cross-origin requests, the gateway - will omit the header `Access-Control-Allow-Credentials` entirely rather - than setting its value to false. - - Support: Extended - enum: - - true - type: boolean - allowHeaders: - description: |- - AllowHeaders indicates which HTTP request headers are supported for - accessing the requested resource. - - Header names are not case sensitive. - - Multiple header names in the value of the `Access-Control-Allow-Headers` - response header are separated by a comma (","). - - When the `AllowHeaders` field is configured with one or more headers, the - gateway must return the `Access-Control-Allow-Headers` response header - which value is present in the `AllowHeaders` field. - - If any header name in the `Access-Control-Request-Headers` request header - is not included in the list of header names specified by the response - header `Access-Control-Allow-Headers`, it will present an error on the - client side. - - If any header name in the `Access-Control-Allow-Headers` response header - does not recognize by the client, it will also occur an error on the - client side. - - A wildcard indicates that the requests with all HTTP headers are allowed. - The `Access-Control-Allow-Headers` response header can only use `*` - wildcard as value when the `AllowCredentials` field is unspecified. - - When the `AllowCredentials` field is specified and `AllowHeaders` field - specified with the `*` wildcard, the gateway must specify one or more - HTTP headers in the value of the `Access-Control-Allow-Headers` response - header. The value of the header `Access-Control-Allow-Headers` is same as - the `Access-Control-Request-Headers` header provided by the client. If - the header `Access-Control-Request-Headers` is not included in the - request, the gateway will omit the `Access-Control-Allow-Headers` - response header, instead of specifying the `*` wildcard. A Gateway - implementation may choose to add implementation-specific default headers. - - Support: Extended - items: - description: |- - HTTPHeaderName is the name of an HTTP header. - - Valid values include: - - * "Authorization" - * "Set-Cookie" - - Invalid values include: - - - ":method" - ":" is an invalid character. This means that HTTP/2 pseudo - headers are not currently supported by this type. - - "/invalid" - "/ " is an invalid character - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - maxItems: 64 - type: array - x-kubernetes-list-type: set - allowMethods: - description: |- - AllowMethods indicates which HTTP methods are supported for accessing the - requested resource. - - Valid values are any method defined by RFC9110, along with the special - value `*`, which represents all HTTP methods are allowed. - - Method names are case sensitive, so these values are also case-sensitive. - (See https://www.rfc-editor.org/rfc/rfc2616#section-5.1.1) - - Multiple method names in the value of the `Access-Control-Allow-Methods` - response header are separated by a comma (","). - - A CORS-safelisted method is a method that is `GET`, `HEAD`, or `POST`. - (See https://fetch.spec.whatwg.org/#cors-safelisted-method) The - CORS-safelisted methods are always allowed, regardless of whether they - are specified in the `AllowMethods` field. - - When the `AllowMethods` field is configured with one or more methods, the - gateway must return the `Access-Control-Allow-Methods` response header - which value is present in the `AllowMethods` field. - - If the HTTP method of the `Access-Control-Request-Method` request header - is not included in the list of methods specified by the response header - `Access-Control-Allow-Methods`, it will present an error on the client - side. - - The `Access-Control-Allow-Methods` response header can only use `*` - wildcard as value when the `AllowCredentials` field is unspecified. - - When the `AllowCredentials` field is specified and `AllowMethods` field - specified with the `*` wildcard, the gateway must specify one HTTP method - in the value of the Access-Control-Allow-Methods response header. The - value of the header `Access-Control-Allow-Methods` is same as the - `Access-Control-Request-Method` header provided by the client. If the - header `Access-Control-Request-Method` is not included in the request, - the gateway will omit the `Access-Control-Allow-Methods` response header, - instead of specifying the `*` wildcard. A Gateway implementation may - choose to add implementation-specific default methods. - - Support: Extended - items: - enum: - - GET - - HEAD - - POST - - PUT - - DELETE - - CONNECT - - OPTIONS - - TRACE - - PATCH - - '*' - type: string - maxItems: 9 - type: array - x-kubernetes-list-type: set - x-kubernetes-validations: - - message: AllowMethods cannot contain '*' alongside - other methods - rule: '!(''*'' in self && self.size() > 1)' - allowOrigins: - description: |- - AllowOrigins indicates whether the response can be shared with requested - resource from the given `Origin`. - - The `Origin` consists of a scheme and a host, with an optional port, and - takes the form `://(:)`. - - Valid values for scheme are: `http` and `https`. - - Valid values for port are any integer between 1 and 65535 (the list of - available TCP/UDP ports). Note that, if not included, port `80` is - assumed for `http` scheme origins, and port `443` is assumed for `https` - origins. This may affect origin matching. - - The host part of the origin may contain the wildcard character `*`. These - wildcard characters behave as follows: - - * `*` is a greedy match to the _left_, including any number of - DNS labels to the left of its position. This also means that - `*` will include any number of period `.` characters to the - left of its position. - * A wildcard by itself matches all hosts. - - An origin value that includes _only_ the `*` character indicates requests - from all `Origin`s are allowed. - - When the `AllowOrigins` field is configured with multiple origins, it - means the server supports clients from multiple origins. If the request - `Origin` matches the configured allowed origins, the gateway must return - the given `Origin` and sets value of the header - `Access-Control-Allow-Origin` same as the `Origin` header provided by the - client. - - The status code of a successful response to a "preflight" request is - always an OK status (i.e., 204 or 200). - - If the request `Origin` does not match the configured allowed origins, - the gateway returns 204/200 response but doesn't set the relevant - cross-origin response headers. Alternatively, the gateway responds with - 403 status to the "preflight" request is denied, coupled with omitting - the CORS headers. The cross-origin request fails on the client side. - Therefore, the client doesn't attempt the actual cross-origin request. - - The `Access-Control-Allow-Origin` response header can only use `*` - wildcard as value when the `AllowCredentials` field is unspecified. - - When the `AllowCredentials` field is specified and `AllowOrigins` field - specified with the `*` wildcard, the gateway must return a single origin - in the value of the `Access-Control-Allow-Origin` response header, - instead of specifying the `*` wildcard. The value of the header - `Access-Control-Allow-Origin` is same as the `Origin` header provided by - the client. - - Support: Extended - items: - description: |- - The AbsoluteURI MUST NOT be a relative URI, and it MUST follow the URI syntax and - encoding rules specified in RFC3986. The AbsoluteURI MUST include both a - scheme (e.g., "http" or "spiffe") and a scheme-specific-part. URIs that - include an authority MUST include a fully qualified domain name or - IP address as the host. - maxLength: 253 - minLength: 1 - pattern: ^(([^:/?#]+):)(//([^/?#]*))([^?#]*)(\?([^#]*))?(#(.*))? - type: string - maxItems: 64 - type: array - x-kubernetes-list-type: set - exposeHeaders: - description: |- - ExposeHeaders indicates which HTTP response headers can be exposed - to client-side scripts in response to a cross-origin request. - - A CORS-safelisted response header is an HTTP header in a CORS response - that it is considered safe to expose to the client scripts. - The CORS-safelisted response headers include the following headers: - `Cache-Control` - `Content-Language` - `Content-Length` - `Content-Type` - `Expires` - `Last-Modified` - `Pragma` - (See https://fetch.spec.whatwg.org/#cors-safelisted-response-header-name) - The CORS-safelisted response headers are exposed to client by default. - - When an HTTP header name is specified using the `ExposeHeaders` field, - this additional header will be exposed as part of the response to the - client. - - Header names are not case sensitive. - - Multiple header names in the value of the `Access-Control-Expose-Headers` - response header are separated by a comma (","). - - A wildcard indicates that the responses with all HTTP headers are exposed - to clients. The `Access-Control-Expose-Headers` response header can only - use `*` wildcard as value when the `AllowCredentials` field is - unspecified. - - Support: Extended - items: - description: |- - HTTPHeaderName is the name of an HTTP header. - - Valid values include: - - * "Authorization" - * "Set-Cookie" - - Invalid values include: - - - ":method" - ":" is an invalid character. This means that HTTP/2 pseudo - headers are not currently supported by this type. - - "/invalid" - "/ " is an invalid character - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - maxItems: 64 - type: array - x-kubernetes-list-type: set - maxAge: - default: 5 - description: |- - MaxAge indicates the duration (in seconds) for the client to cache the - results of a "preflight" request. - - The information provided by the `Access-Control-Allow-Methods` and - `Access-Control-Allow-Headers` response headers can be cached by the - client until the time specified by `Access-Control-Max-Age` elapses. - - The default value of `Access-Control-Max-Age` response header is 5 - (seconds). - format: int32 - minimum: 1 - type: integer - type: object extensionRef: description: |- ExtensionRef is an optional, implementation-specific extension to the @@ -10554,7 +9925,7 @@ spec: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be - case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries @@ -10629,7 +10000,7 @@ spec: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be - case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries @@ -10657,7 +10028,7 @@ spec: x-kubernetes-list-type: map type: object requestMirror: - description: |- + description: |+ RequestMirror defines a schema for a filter that mirrors requests. Requests are sent to the specified destination, but responses from that destination are ignored. @@ -10667,6 +10038,7 @@ spec: backends. Support: Extended + properties: backendRef: description: |- @@ -10762,12 +10134,13 @@ spec: rule: '(size(self.group) == 0 && self.kind == ''Service'') ? has(self.port) : true' fraction: - description: |- + description: |+ Fraction represents the fraction of requests that should be mirrored to BackendRef. Only one of Fraction or Percent may be specified. If neither field is specified, 100% of requests will be mirrored. + properties: denominator: default: 100 @@ -10786,13 +10159,14 @@ spec: to denominator rule: self.numerator <= self.denominator percent: - description: |- + description: |+ Percent represents the percentage of requests that should be mirrored to BackendRef. Its minimum value is 0 (indicating 0% of requests) and its maximum value is 100 (indicating 100% of requests). Only one of Fraction or Percent may be specified. If neither field is specified, 100% of requests will be mirrored. + format: int32 maximum: 100 minimum: 0 @@ -10990,7 +10364,7 @@ spec: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be - case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries @@ -11065,7 +10439,7 @@ spec: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be - case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries @@ -11133,7 +10507,6 @@ spec: - RequestRedirect - URLRewrite - ExtensionRef - - CORS type: string urlRewrite: description: |- @@ -11266,11 +10639,6 @@ spec: - message: filter.extensionRef must be specified for ExtensionRef filter.type rule: '!(!has(self.extensionRef) && self.type == ''ExtensionRef'')' - - message: filter.cors must be nil if the filter.type - is not CORS - rule: '!(has(self.cors) && self.type != ''CORS'')' - - message: filter.cors must be specified for CORS filter.type - rule: '!(!has(self.cors) && self.type == ''CORS'')' maxItems: 16 type: array x-kubernetes-validations: @@ -11392,7 +10760,7 @@ spec: they are specified. Implementations MAY choose to implement this ordering strictly, rejecting - any combination or order of filters that cannot be supported. If implementations + any combination or order of filters that can not be supported. If implementations choose a strict interpretation of filter ordering, they MUST clearly document that behavior. @@ -11414,7 +10782,7 @@ spec: All filters are expected to be compatible with each other except for the URLRewrite and RequestRedirect filters, which may not be combined. If an - implementation cannot support other combinations of filters, they must clearly + implementation can not support other combinations of filters, they must clearly document that limitation. In cases where incompatible or unsupported filters are specified and cause the `Accepted` condition to be set to status `False`, implementations may use the `IncompatibleFilters` reason to specify @@ -11430,289 +10798,6 @@ spec: authentication strategies, rate-limiting, and traffic shaping. API guarantee/conformance is defined based on the type of the filter. properties: - cors: - description: |- - CORS defines a schema for a filter that responds to the - cross-origin request based on HTTP response header. - - Support: Extended - properties: - allowCredentials: - description: |- - AllowCredentials indicates whether the actual cross-origin request allows - to include credentials. - - The only valid value for the `Access-Control-Allow-Credentials` response - header is true (case-sensitive). - - If the credentials are not allowed in cross-origin requests, the gateway - will omit the header `Access-Control-Allow-Credentials` entirely rather - than setting its value to false. - - Support: Extended - enum: - - true - type: boolean - allowHeaders: - description: |- - AllowHeaders indicates which HTTP request headers are supported for - accessing the requested resource. - - Header names are not case sensitive. - - Multiple header names in the value of the `Access-Control-Allow-Headers` - response header are separated by a comma (","). - - When the `AllowHeaders` field is configured with one or more headers, the - gateway must return the `Access-Control-Allow-Headers` response header - which value is present in the `AllowHeaders` field. - - If any header name in the `Access-Control-Request-Headers` request header - is not included in the list of header names specified by the response - header `Access-Control-Allow-Headers`, it will present an error on the - client side. - - If any header name in the `Access-Control-Allow-Headers` response header - does not recognize by the client, it will also occur an error on the - client side. - - A wildcard indicates that the requests with all HTTP headers are allowed. - The `Access-Control-Allow-Headers` response header can only use `*` - wildcard as value when the `AllowCredentials` field is unspecified. - - When the `AllowCredentials` field is specified and `AllowHeaders` field - specified with the `*` wildcard, the gateway must specify one or more - HTTP headers in the value of the `Access-Control-Allow-Headers` response - header. The value of the header `Access-Control-Allow-Headers` is same as - the `Access-Control-Request-Headers` header provided by the client. If - the header `Access-Control-Request-Headers` is not included in the - request, the gateway will omit the `Access-Control-Allow-Headers` - response header, instead of specifying the `*` wildcard. A Gateway - implementation may choose to add implementation-specific default headers. - - Support: Extended - items: - description: |- - HTTPHeaderName is the name of an HTTP header. - - Valid values include: - - * "Authorization" - * "Set-Cookie" - - Invalid values include: - - - ":method" - ":" is an invalid character. This means that HTTP/2 pseudo - headers are not currently supported by this type. - - "/invalid" - "/ " is an invalid character - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - maxItems: 64 - type: array - x-kubernetes-list-type: set - allowMethods: - description: |- - AllowMethods indicates which HTTP methods are supported for accessing the - requested resource. - - Valid values are any method defined by RFC9110, along with the special - value `*`, which represents all HTTP methods are allowed. - - Method names are case sensitive, so these values are also case-sensitive. - (See https://www.rfc-editor.org/rfc/rfc2616#section-5.1.1) - - Multiple method names in the value of the `Access-Control-Allow-Methods` - response header are separated by a comma (","). - - A CORS-safelisted method is a method that is `GET`, `HEAD`, or `POST`. - (See https://fetch.spec.whatwg.org/#cors-safelisted-method) The - CORS-safelisted methods are always allowed, regardless of whether they - are specified in the `AllowMethods` field. - - When the `AllowMethods` field is configured with one or more methods, the - gateway must return the `Access-Control-Allow-Methods` response header - which value is present in the `AllowMethods` field. - - If the HTTP method of the `Access-Control-Request-Method` request header - is not included in the list of methods specified by the response header - `Access-Control-Allow-Methods`, it will present an error on the client - side. - - The `Access-Control-Allow-Methods` response header can only use `*` - wildcard as value when the `AllowCredentials` field is unspecified. - - When the `AllowCredentials` field is specified and `AllowMethods` field - specified with the `*` wildcard, the gateway must specify one HTTP method - in the value of the Access-Control-Allow-Methods response header. The - value of the header `Access-Control-Allow-Methods` is same as the - `Access-Control-Request-Method` header provided by the client. If the - header `Access-Control-Request-Method` is not included in the request, - the gateway will omit the `Access-Control-Allow-Methods` response header, - instead of specifying the `*` wildcard. A Gateway implementation may - choose to add implementation-specific default methods. - - Support: Extended - items: - enum: - - GET - - HEAD - - POST - - PUT - - DELETE - - CONNECT - - OPTIONS - - TRACE - - PATCH - - '*' - type: string - maxItems: 9 - type: array - x-kubernetes-list-type: set - x-kubernetes-validations: - - message: AllowMethods cannot contain '*' alongside - other methods - rule: '!(''*'' in self && self.size() > 1)' - allowOrigins: - description: |- - AllowOrigins indicates whether the response can be shared with requested - resource from the given `Origin`. - - The `Origin` consists of a scheme and a host, with an optional port, and - takes the form `://(:)`. - - Valid values for scheme are: `http` and `https`. - - Valid values for port are any integer between 1 and 65535 (the list of - available TCP/UDP ports). Note that, if not included, port `80` is - assumed for `http` scheme origins, and port `443` is assumed for `https` - origins. This may affect origin matching. - - The host part of the origin may contain the wildcard character `*`. These - wildcard characters behave as follows: - - * `*` is a greedy match to the _left_, including any number of - DNS labels to the left of its position. This also means that - `*` will include any number of period `.` characters to the - left of its position. - * A wildcard by itself matches all hosts. - - An origin value that includes _only_ the `*` character indicates requests - from all `Origin`s are allowed. - - When the `AllowOrigins` field is configured with multiple origins, it - means the server supports clients from multiple origins. If the request - `Origin` matches the configured allowed origins, the gateway must return - the given `Origin` and sets value of the header - `Access-Control-Allow-Origin` same as the `Origin` header provided by the - client. - - The status code of a successful response to a "preflight" request is - always an OK status (i.e., 204 or 200). - - If the request `Origin` does not match the configured allowed origins, - the gateway returns 204/200 response but doesn't set the relevant - cross-origin response headers. Alternatively, the gateway responds with - 403 status to the "preflight" request is denied, coupled with omitting - the CORS headers. The cross-origin request fails on the client side. - Therefore, the client doesn't attempt the actual cross-origin request. - - The `Access-Control-Allow-Origin` response header can only use `*` - wildcard as value when the `AllowCredentials` field is unspecified. - - When the `AllowCredentials` field is specified and `AllowOrigins` field - specified with the `*` wildcard, the gateway must return a single origin - in the value of the `Access-Control-Allow-Origin` response header, - instead of specifying the `*` wildcard. The value of the header - `Access-Control-Allow-Origin` is same as the `Origin` header provided by - the client. - - Support: Extended - items: - description: |- - The AbsoluteURI MUST NOT be a relative URI, and it MUST follow the URI syntax and - encoding rules specified in RFC3986. The AbsoluteURI MUST include both a - scheme (e.g., "http" or "spiffe") and a scheme-specific-part. URIs that - include an authority MUST include a fully qualified domain name or - IP address as the host. - maxLength: 253 - minLength: 1 - pattern: ^(([^:/?#]+):)(//([^/?#]*))([^?#]*)(\?([^#]*))?(#(.*))? - type: string - maxItems: 64 - type: array - x-kubernetes-list-type: set - exposeHeaders: - description: |- - ExposeHeaders indicates which HTTP response headers can be exposed - to client-side scripts in response to a cross-origin request. - - A CORS-safelisted response header is an HTTP header in a CORS response - that it is considered safe to expose to the client scripts. - The CORS-safelisted response headers include the following headers: - `Cache-Control` - `Content-Language` - `Content-Length` - `Content-Type` - `Expires` - `Last-Modified` - `Pragma` - (See https://fetch.spec.whatwg.org/#cors-safelisted-response-header-name) - The CORS-safelisted response headers are exposed to client by default. - - When an HTTP header name is specified using the `ExposeHeaders` field, - this additional header will be exposed as part of the response to the - client. - - Header names are not case sensitive. - - Multiple header names in the value of the `Access-Control-Expose-Headers` - response header are separated by a comma (","). - - A wildcard indicates that the responses with all HTTP headers are exposed - to clients. The `Access-Control-Expose-Headers` response header can only - use `*` wildcard as value when the `AllowCredentials` field is - unspecified. - - Support: Extended - items: - description: |- - HTTPHeaderName is the name of an HTTP header. - - Valid values include: - - * "Authorization" - * "Set-Cookie" - - Invalid values include: - - - ":method" - ":" is an invalid character. This means that HTTP/2 pseudo - headers are not currently supported by this type. - - "/invalid" - "/ " is an invalid character - maxLength: 256 - minLength: 1 - pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ - type: string - maxItems: 64 - type: array - x-kubernetes-list-type: set - maxAge: - default: 5 - description: |- - MaxAge indicates the duration (in seconds) for the client to cache the - results of a "preflight" request. - - The information provided by the `Access-Control-Allow-Methods` and - `Access-Control-Allow-Headers` response headers can be cached by the - client until the time specified by `Access-Control-Max-Age` elapses. - - The default value of `Access-Control-Max-Age` response header is 5 - (seconds). - format: int32 - minimum: 1 - type: integer - type: object extensionRef: description: |- ExtensionRef is an optional, implementation-specific extension to the @@ -11780,7 +10865,7 @@ spec: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be - case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries @@ -11854,7 +10939,7 @@ spec: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be - case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries @@ -11882,7 +10967,7 @@ spec: x-kubernetes-list-type: map type: object requestMirror: - description: |- + description: |+ RequestMirror defines a schema for a filter that mirrors requests. Requests are sent to the specified destination, but responses from that destination are ignored. @@ -11892,6 +10977,7 @@ spec: backends. Support: Extended + properties: backendRef: description: |- @@ -11987,12 +11073,13 @@ spec: rule: '(size(self.group) == 0 && self.kind == ''Service'') ? has(self.port) : true' fraction: - description: |- + description: |+ Fraction represents the fraction of requests that should be mirrored to BackendRef. Only one of Fraction or Percent may be specified. If neither field is specified, 100% of requests will be mirrored. + properties: denominator: default: 100 @@ -12011,13 +11098,14 @@ spec: denominator rule: self.numerator <= self.denominator percent: - description: |- + description: |+ Percent represents the percentage of requests that should be mirrored to BackendRef. Its minimum value is 0 (indicating 0% of requests) and its maximum value is 100 (indicating 100% of requests). Only one of Fraction or Percent may be specified. If neither field is specified, 100% of requests will be mirrored. + format: int32 maximum: 100 minimum: 0 @@ -12214,7 +11302,7 @@ spec: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be - case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries @@ -12288,7 +11376,7 @@ spec: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be - case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries @@ -12356,7 +11444,6 @@ spec: - RequestRedirect - URLRewrite - ExtensionRef - - CORS type: string urlRewrite: description: |- @@ -12486,11 +11573,6 @@ spec: - message: filter.extensionRef must be specified for ExtensionRef filter.type rule: '!(!has(self.extensionRef) && self.type == ''ExtensionRef'')' - - message: filter.cors must be nil if the filter.type is not - CORS - rule: '!(has(self.cors) && self.type != ''CORS'')' - - message: filter.cors must be specified for CORS filter.type - rule: '!(!has(self.cors) && self.type == ''CORS'')' maxItems: 16 type: array x-kubernetes-validations: @@ -12594,7 +11676,7 @@ spec: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be - case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, only the first entry with an equivalent name MUST be considered for a match. Subsequent @@ -12805,7 +11887,7 @@ spec: maxItems: 64 type: array name: - description: |- + description: | Name is the name of the route rule. This name MUST be unique within a Route if it is set. Support: Extended @@ -12814,14 +11896,15 @@ spec: pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string retry: - description: |- + description: |+ Retry defines the configuration for when to retry an HTTP request. Support: Extended + properties: attempts: description: |- - Attempts specifies the maximum number of times an individual request + Attempts specifies the maxmimum number of times an individual request from the gateway to a backend should be retried. If the maximum number of retries has been attempted without a successful @@ -12895,17 +11978,20 @@ spec: Implementations MAY support specifying discrete values in the 400-499 range, which are often inadvisable to retry. + + maximum: 599 minimum: 400 type: integer type: array type: object sessionPersistence: - description: |- + description: |+ SessionPersistence defines and configures session persistence for the route rule. Support: Extended + properties: absoluteTimeout: description: |- @@ -12940,8 +12026,6 @@ spec: absolute lifetime of the cookie tracked by the gateway and is optional. - Defaults to "Session". - Support: Core for "Session" type Support: Extended for "Permanent" type @@ -13155,7 +12239,7 @@ spec: There are a number of cases where the "Accepted" condition may not be set due to lack of controller visibility, that includes when: - * The Route refers to a nonexistent parent. + * The Route refers to a non-existent parent. * The Route is of a type that the controller does not support. * The Route is in a namespace the controller does not have access to. items: @@ -13406,7 +12490,7 @@ kind: CustomResourceDefinition metadata: annotations: api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/3328 - gateway.networking.k8s.io/bundle-version: v1.3.0 + gateway.networking.k8s.io/bundle-version: v1.2.1 gateway.networking.k8s.io/channel: experimental creationTimestamp: null name: referencegrants.gateway.networking.k8s.io @@ -13599,7 +12683,7 @@ kind: CustomResourceDefinition metadata: annotations: api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/3328 - gateway.networking.k8s.io/bundle-version: v1.3.0 + gateway.networking.k8s.io/bundle-version: v1.2.1 gateway.networking.k8s.io/channel: experimental creationTimestamp: null name: tcproutes.gateway.networking.k8s.io @@ -13647,7 +12731,7 @@ spec: description: Spec defines the desired state of TCPRoute. properties: parentRefs: - description: |- + description: |+ ParentRefs references the resources (usually Gateways) that a Route wants to be attached to. Note that the referenced parent resource needs to allow this for the attachment to be complete. For Gateways, that means @@ -13709,6 +12793,11 @@ spec: connections originating from the same namespace as the Route, for which the intended destination of the connections are a Service targeted as a ParentRef of the Route. + + + + + items: description: |- ParentReference identifies an API object (usually a Gateway) that can be considered @@ -13882,14 +12971,16 @@ spec: || p2.port == 0)) || (has(p1.port) && has(p2.port) && p1.port == p2.port)))) rules: - description: Rules are a list of TCP matchers and actions. + description: |+ + Rules are a list of TCP matchers and actions. + items: description: TCPRouteRule is the configuration for a given rule. properties: backendRefs: description: |- BackendRefs defines the backend(s) where matching requests should be - sent. If unspecified or invalid (refers to a nonexistent resource or a + sent. If unspecified or invalid (refers to a non-existent resource or a Service with no endpoints), the underlying implementation MUST actively reject connection attempts to this backend. Connection rejections must respect weight; if an invalid backend is requested to have 80% of @@ -13912,6 +13003,7 @@ spec: namespace's owner to accept the reference. See the ReferenceGrant documentation for details. + When the BackendRef points to a Kubernetes Service, implementations SHOULD honor the appProtocol field if it is set for the target Service Port. @@ -13927,6 +13019,7 @@ spec: protocol then the backend is considered invalid. Implementations MUST set the "ResolvedRefs" condition to "False" with the "UnsupportedProtocol" reason. + Note that when the BackendTLSPolicy object is enabled by the implementation, there are some extra rules about validity to consider here. See the fields @@ -14084,7 +13177,7 @@ spec: There are a number of cases where the "Accepted" condition may not be set due to lack of controller visibility, that includes when: - * The Route refers to a nonexistent parent. + * The Route refers to a non-existent parent. * The Route is of a type that the controller does not support. * The Route is in a namespace the controller does not have access to. items: @@ -14335,7 +13428,7 @@ kind: CustomResourceDefinition metadata: annotations: api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/3328 - gateway.networking.k8s.io/bundle-version: v1.3.0 + gateway.networking.k8s.io/bundle-version: v1.2.1 gateway.networking.k8s.io/channel: experimental creationTimestamp: null name: tlsroutes.gateway.networking.k8s.io @@ -14443,7 +13536,7 @@ spec: maxItems: 16 type: array parentRefs: - description: |- + description: |+ ParentRefs references the resources (usually Gateways) that a Route wants to be attached to. Note that the referenced parent resource needs to allow this for the attachment to be complete. For Gateways, that means @@ -14505,6 +13598,11 @@ spec: connections originating from the same namespace as the Route, for which the intended destination of the connections are a Service targeted as a ParentRef of the Route. + + + + + items: description: |- ParentReference identifies an API object (usually a Gateway) that can be considered @@ -14678,14 +13776,16 @@ spec: || p2.port == 0)) || (has(p1.port) && has(p2.port) && p1.port == p2.port)))) rules: - description: Rules are a list of TLS matchers and actions. + description: |+ + Rules are a list of TLS matchers and actions. + items: description: TLSRouteRule is the configuration for a given rule. properties: backendRefs: description: |- BackendRefs defines the backend(s) where matching requests should be - sent. If unspecified or invalid (refers to a nonexistent resource or + sent. If unspecified or invalid (refers to a non-existent resource or a Service with no endpoints), the rule performs no forwarding; if no filters are specified that would result in a response being sent, the underlying implementation must actively reject request attempts to this @@ -14711,6 +13811,7 @@ spec: namespace's owner to accept the reference. See the ReferenceGrant documentation for details. + When the BackendRef points to a Kubernetes Service, implementations SHOULD honor the appProtocol field if it is set for the target Service Port. @@ -14726,6 +13827,7 @@ spec: protocol then the backend is considered invalid. Implementations MUST set the "ResolvedRefs" condition to "False" with the "UnsupportedProtocol" reason. + Note that when the BackendTLSPolicy object is enabled by the implementation, there are some extra rules about validity to consider here. See the fields @@ -14883,7 +13985,7 @@ spec: There are a number of cases where the "Accepted" condition may not be set due to lack of controller visibility, that includes when: - * The Route refers to a nonexistent parent. + * The Route refers to a non-existent parent. * The Route is of a type that the controller does not support. * The Route is in a namespace the controller does not have access to. items: @@ -15134,7 +14236,7 @@ kind: CustomResourceDefinition metadata: annotations: api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/3328 - gateway.networking.k8s.io/bundle-version: v1.3.0 + gateway.networking.k8s.io/bundle-version: v1.2.1 gateway.networking.k8s.io/channel: experimental creationTimestamp: null name: udproutes.gateway.networking.k8s.io @@ -15182,7 +14284,7 @@ spec: description: Spec defines the desired state of UDPRoute. properties: parentRefs: - description: |- + description: |+ ParentRefs references the resources (usually Gateways) that a Route wants to be attached to. Note that the referenced parent resource needs to allow this for the attachment to be complete. For Gateways, that means @@ -15244,6 +14346,11 @@ spec: connections originating from the same namespace as the Route, for which the intended destination of the connections are a Service targeted as a ParentRef of the Route. + + + + + items: description: |- ParentReference identifies an API object (usually a Gateway) that can be considered @@ -15417,14 +14524,16 @@ spec: || p2.port == 0)) || (has(p1.port) && has(p2.port) && p1.port == p2.port)))) rules: - description: Rules are a list of UDP matchers and actions. + description: |+ + Rules are a list of UDP matchers and actions. + items: description: UDPRouteRule is the configuration for a given rule. properties: backendRefs: description: |- BackendRefs defines the backend(s) where matching requests should be - sent. If unspecified or invalid (refers to a nonexistent resource or a + sent. If unspecified or invalid (refers to a non-existent resource or a Service with no endpoints), the underlying implementation MUST actively reject connection attempts to this backend. Packet drops must respect weight; if an invalid backend is requested to have 80% of @@ -15447,6 +14556,7 @@ spec: namespace's owner to accept the reference. See the ReferenceGrant documentation for details. + When the BackendRef points to a Kubernetes Service, implementations SHOULD honor the appProtocol field if it is set for the target Service Port. @@ -15462,6 +14572,7 @@ spec: protocol then the backend is considered invalid. Implementations MUST set the "ResolvedRefs" condition to "False" with the "UnsupportedProtocol" reason. + Note that when the BackendTLSPolicy object is enabled by the implementation, there are some extra rules about validity to consider here. See the fields @@ -15619,7 +14730,7 @@ spec: There are a number of cases where the "Accepted" condition may not be set due to lack of controller visibility, that includes when: - * The Route refers to a nonexistent parent. + * The Route refers to a non-existent parent. * The Route is of a type that the controller does not support. * The Route is in a namespace the controller does not have access to. items: @@ -15861,1458 +14972,3 @@ status: plural: "" conditions: null storedVersions: null ---- -# -# config/crd/experimental/gateway.networking.x-k8s.io_xbackendtrafficpolicies.yaml -# -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/3328 - gateway.networking.k8s.io/bundle-version: v1.3.0 - gateway.networking.k8s.io/channel: experimental - creationTimestamp: null - labels: - gateway.networking.k8s.io/policy: Direct - name: xbackendtrafficpolicies.gateway.networking.x-k8s.io -spec: - group: gateway.networking.x-k8s.io - names: - categories: - - gateway-api - kind: XBackendTrafficPolicy - listKind: XBackendTrafficPolicyList - plural: xbackendtrafficpolicies - shortNames: - - xbtrafficpolicy - singular: xbackendtrafficpolicy - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1alpha1 - schema: - openAPIV3Schema: - description: |- - XBackendTrafficPolicy defines the configuration for how traffic to a - target backend should be handled. - properties: - apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources - type: string - kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds - type: string - metadata: - type: object - spec: - description: Spec defines the desired state of BackendTrafficPolicy. - properties: - retryConstraint: - description: |- - RetryConstraint defines the configuration for when to allow or prevent - further retries to a target backend, by dynamically calculating a 'retry - budget'. This budget is calculated based on the percentage of incoming - traffic composed of retries over a given time interval. Once the budget - is exceeded, additional retries will be rejected. - - For example, if the retry budget interval is 10 seconds, there have been - 1000 active requests in the past 10 seconds, and the allowed percentage - of requests that can be retried is 20% (the default), then 200 of those - requests may be composed of retries. Active requests will only be - considered for the duration of the interval when calculating the retry - budget. Retrying the same original request multiple times within the - retry budget interval will lead to each retry being counted towards - calculating the budget. - - Configuring a RetryConstraint in BackendTrafficPolicy is compatible with - HTTPRoute Retry settings for each HTTPRouteRule that targets the same - backend. While the HTTPRouteRule Retry stanza can specify whether a - request will be retried, and the number of retry attempts each client - may perform, RetryConstraint helps prevent cascading failures such as - retry storms during periods of consistent failures. - - After the retry budget has been exceeded, additional retries to the - backend MUST return a 503 response to the client. - - Additional configurations for defining a constraint on retries MAY be - defined in the future. - - Support: Extended - properties: - budget: - default: - interval: 10s - percent: 20 - description: Budget holds the details of the retry budget configuration. - properties: - interval: - default: 10s - description: |- - Interval defines the duration in which requests will be considered - for calculating the budget for retries. - - Support: Extended - pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$ - type: string - x-kubernetes-validations: - - message: interval can not be greater than one hour or less - than one second - rule: '!(duration(self) < duration(''1s'') || duration(self) - > duration(''1h''))' - percent: - default: 20 - description: |- - Percent defines the maximum percentage of active requests that may - be made up of retries. - - Support: Extended - maximum: 100 - minimum: 0 - type: integer - type: object - minRetryRate: - default: - count: 10 - interval: 1s - description: |- - MinRetryRate defines the minimum rate of retries that will be allowable - over a specified duration of time. - - The effective overall minimum rate of retries targeting the backend - service may be much higher, as there can be any number of clients which - are applying this setting locally. - - This ensures that requests can still be retried during periods of low - traffic, where the budget for retries may be calculated as a very low - value. - - Support: Extended - properties: - count: - description: |- - Count specifies the number of requests per time interval. - - Support: Extended - maximum: 1000000 - minimum: 1 - type: integer - interval: - description: |- - Interval specifies the divisor of the rate of requests, the amount of - time during which the given count of requests occur. - - Support: Extended - pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$ - type: string - x-kubernetes-validations: - - message: interval can not be greater than one hour - rule: '!(duration(self) == duration(''0s'') || duration(self) - > duration(''1h''))' - type: object - type: object - sessionPersistence: - description: |- - SessionPersistence defines and configures session persistence - for the backend. - - Support: Extended - properties: - absoluteTimeout: - description: |- - AbsoluteTimeout defines the absolute timeout of the persistent - session. Once the AbsoluteTimeout duration has elapsed, the - session becomes invalid. - - Support: Extended - pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$ - type: string - cookieConfig: - description: |- - CookieConfig provides configuration settings that are specific - to cookie-based session persistence. - - Support: Core - properties: - lifetimeType: - default: Session - description: |- - LifetimeType specifies whether the cookie has a permanent or - session-based lifetime. A permanent cookie persists until its - specified expiry time, defined by the Expires or Max-Age cookie - attributes, while a session cookie is deleted when the current - session ends. - - When set to "Permanent", AbsoluteTimeout indicates the - cookie's lifetime via the Expires or Max-Age cookie attributes - and is required. - - When set to "Session", AbsoluteTimeout indicates the - absolute lifetime of the cookie tracked by the gateway and - is optional. - - Defaults to "Session". - - Support: Core for "Session" type - - Support: Extended for "Permanent" type - enum: - - Permanent - - Session - type: string - type: object - idleTimeout: - description: |- - IdleTimeout defines the idle timeout of the persistent session. - Once the session has been idle for more than the specified - IdleTimeout duration, the session becomes invalid. - - Support: Extended - pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$ - type: string - sessionName: - description: |- - SessionName defines the name of the persistent session token - which may be reflected in the cookie or the header. Users - should avoid reusing session names to prevent unintended - consequences, such as rejection or unpredictable behavior. - - Support: Implementation-specific - maxLength: 128 - type: string - type: - default: Cookie - description: |- - Type defines the type of session persistence such as through - the use a header or cookie. Defaults to cookie based session - persistence. - - Support: Core for "Cookie" type - - Support: Extended for "Header" type - enum: - - Cookie - - Header - type: string - type: object - x-kubernetes-validations: - - message: AbsoluteTimeout must be specified when cookie lifetimeType - is Permanent - rule: '!has(self.cookieConfig) || !has(self.cookieConfig.lifetimeType) - || self.cookieConfig.lifetimeType != ''Permanent'' || has(self.absoluteTimeout)' - targetRefs: - description: |- - TargetRefs identifies API object(s) to apply this policy to. - Currently, Backends (A grouping of like endpoints such as Service, - ServiceImport, or any implementation-specific backendRef) are the only - valid API target references. - - Currently, a TargetRef can not be scoped to a specific port on a - Service. - items: - description: |- - LocalPolicyTargetReference identifies an API object to apply a direct or - inherited policy to. This should be used as part of Policy resources - that can target Gateway API resources. For more information on how this - policy attachment model works, and a sample Policy resource, refer to - the policy attachment documentation for Gateway API. - properties: - group: - description: Group is the group of the target resource. - maxLength: 253 - pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - kind: - description: Kind is kind of the target resource. - maxLength: 63 - minLength: 1 - pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ - type: string - name: - description: Name is the name of the target resource. - maxLength: 253 - minLength: 1 - type: string - required: - - group - - kind - - name - type: object - maxItems: 16 - minItems: 1 - type: array - x-kubernetes-list-map-keys: - - group - - kind - - name - x-kubernetes-list-type: map - required: - - targetRefs - type: object - status: - description: Status defines the current state of BackendTrafficPolicy. - properties: - ancestors: - description: |- - Ancestors is a list of ancestor resources (usually Gateways) that are - associated with the policy, and the status of the policy with respect to - each ancestor. When this policy attaches to a parent, the controller that - manages the parent and the ancestors MUST add an entry to this list when - the controller first sees the policy and SHOULD update the entry as - appropriate when the relevant ancestor is modified. - - Note that choosing the relevant ancestor is left to the Policy designers; - an important part of Policy design is designing the right object level at - which to namespace this status. - - Note also that implementations MUST ONLY populate ancestor status for - the Ancestor resources they are responsible for. Implementations MUST - use the ControllerName field to uniquely identify the entries in this list - that they are responsible for. - - Note that to achieve this, the list of PolicyAncestorStatus structs - MUST be treated as a map with a composite key, made up of the AncestorRef - and ControllerName fields combined. - - A maximum of 16 ancestors will be represented in this list. An empty list - means the Policy is not relevant for any ancestors. - - If this slice is full, implementations MUST NOT add further entries. - Instead they MUST consider the policy unimplementable and signal that - on any related resources such as the ancestor that would be referenced - here. For example, if this list was full on BackendTLSPolicy, no - additional Gateways would be able to reference the Service targeted by - the BackendTLSPolicy. - items: - description: |- - PolicyAncestorStatus describes the status of a route with respect to an - associated Ancestor. - - Ancestors refer to objects that are either the Target of a policy or above it - in terms of object hierarchy. For example, if a policy targets a Service, the - Policy's Ancestors are, in order, the Service, the HTTPRoute, the Gateway, and - the GatewayClass. Almost always, in this hierarchy, the Gateway will be the most - useful object to place Policy status on, so we recommend that implementations - SHOULD use Gateway as the PolicyAncestorStatus object unless the designers - have a _very_ good reason otherwise. - - In the context of policy attachment, the Ancestor is used to distinguish which - resource results in a distinct application of this policy. For example, if a policy - targets a Service, it may have a distinct result per attached Gateway. - - Policies targeting the same resource may have different effects depending on the - ancestors of those resources. For example, different Gateways targeting the same - Service may have different capabilities, especially if they have different underlying - implementations. - - For example, in BackendTLSPolicy, the Policy attaches to a Service that is - used as a backend in a HTTPRoute that is itself attached to a Gateway. - In this case, the relevant object for status is the Gateway, and that is the - ancestor object referred to in this status. - - Note that a parent is also an ancestor, so for objects where the parent is the - relevant object for status, this struct SHOULD still be used. - - This struct is intended to be used in a slice that's effectively a map, - with a composite key made up of the AncestorRef and the ControllerName. - properties: - ancestorRef: - description: |- - AncestorRef corresponds with a ParentRef in the spec that this - PolicyAncestorStatus struct describes the status of. - properties: - group: - default: gateway.networking.k8s.io - description: |- - Group is the group of the referent. - When unspecified, "gateway.networking.k8s.io" is inferred. - To set the core API group (such as for a "Service" kind referent), - Group must be explicitly set to "" (empty string). - - Support: Core - maxLength: 253 - pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - kind: - default: Gateway - description: |- - Kind is kind of the referent. - - There are two kinds of parent resources with "Core" support: - - * Gateway (Gateway conformance profile) - * Service (Mesh conformance profile, ClusterIP Services only) - - Support for other resources is Implementation-Specific. - maxLength: 63 - minLength: 1 - pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ - type: string - name: - description: |- - Name is the name of the referent. - - Support: Core - maxLength: 253 - minLength: 1 - type: string - namespace: - description: |- - Namespace is the namespace of the referent. When unspecified, this refers - to the local namespace of the Route. - - Note that there are specific rules for ParentRefs which cross namespace - boundaries. Cross-namespace references are only valid if they are explicitly - allowed by something in the namespace they are referring to. For example: - Gateway has the AllowedRoutes field, and ReferenceGrant provides a - generic way to enable any other kind of cross-namespace reference. - - - ParentRefs from a Route to a Service in the same namespace are "producer" - routes, which apply default routing rules to inbound connections from - any namespace to the Service. - - ParentRefs from a Route to a Service in a different namespace are - "consumer" routes, and these routing rules are only applied to outbound - connections originating from the same namespace as the Route, for which - the intended destination of the connections are a Service targeted as a - ParentRef of the Route. - - - Support: Core - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - port: - description: |- - Port is the network port this Route targets. It can be interpreted - differently based on the type of parent resource. - - When the parent resource is a Gateway, this targets all listeners - listening on the specified port that also support this kind of Route(and - select this Route). It's not recommended to set `Port` unless the - networking behaviors specified in a Route must apply to a specific port - as opposed to a listener(s) whose port(s) may be changed. When both Port - and SectionName are specified, the name and port of the selected listener - must match both specified values. - - - When the parent resource is a Service, this targets a specific port in the - Service spec. When both Port (experimental) and SectionName are specified, - the name and port of the selected port must match both specified values. - - - Implementations MAY choose to support other parent resources. - Implementations supporting other types of parent resources MUST clearly - document how/if Port is interpreted. - - For the purpose of status, an attachment is considered successful as - long as the parent resource accepts it partially. For example, Gateway - listeners can restrict which Routes can attach to them by Route kind, - namespace, or hostname. If 1 of 2 Gateway listeners accept attachment - from the referencing Route, the Route MUST be considered successfully - attached. If no Gateway listeners accept attachment from this Route, - the Route MUST be considered detached from the Gateway. - - Support: Extended - format: int32 - maximum: 65535 - minimum: 1 - type: integer - sectionName: - description: |- - SectionName is the name of a section within the target resource. In the - following resources, SectionName is interpreted as the following: - - * Gateway: Listener name. When both Port (experimental) and SectionName - are specified, the name and port of the selected listener must match - both specified values. - * Service: Port name. When both Port (experimental) and SectionName - are specified, the name and port of the selected listener must match - both specified values. - - Implementations MAY choose to support attaching Routes to other resources. - If that is the case, they MUST clearly document how SectionName is - interpreted. - - When unspecified (empty string), this will reference the entire resource. - For the purpose of status, an attachment is considered successful if at - least one section in the parent resource accepts it. For example, Gateway - listeners can restrict which Routes can attach to them by Route kind, - namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from - the referencing Route, the Route MUST be considered successfully - attached. If no Gateway listeners accept attachment from this Route, the - Route MUST be considered detached from the Gateway. - - Support: Core - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - required: - - name - type: object - conditions: - description: Conditions describes the status of the Policy with - respect to the given Ancestor. - items: - description: Condition contains details for one aspect of - the current state of this API Resource. - properties: - lastTransitionTime: - description: |- - lastTransitionTime is the last time the condition transitioned from one status to another. - This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: |- - message is a human readable message indicating details about the transition. - This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: |- - observedGeneration represents the .metadata.generation that the condition was set based upon. - For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date - with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: |- - reason contains a programmatic identifier indicating the reason for the condition's last transition. - Producers of specific condition types may define expected values and meanings for this field, - and whether the values are considered a guaranteed API. - The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, - Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - maxItems: 8 - minItems: 1 - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - controllerName: - description: |- - ControllerName is a domain/path string that indicates the name of the - controller that wrote this status. This corresponds with the - controllerName field on GatewayClass. - - Example: "example.net/gateway-controller". - - The format of this field is DOMAIN "/" PATH, where DOMAIN and PATH are - valid Kubernetes names - (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names). - - Controllers MUST populate this field when writing status. Controllers should ensure that - entries to status populated with their ControllerName are cleaned up when they are no - longer necessary. - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ - type: string - required: - - ancestorRef - - controllerName - type: object - maxItems: 16 - type: array - required: - - ancestors - type: object - required: - - spec - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: null - storedVersions: null ---- -# -# config/crd/experimental/gateway.networking.x-k8s.io_xlistenersets.yaml -# -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/3328 - gateway.networking.k8s.io/bundle-version: v1.3.0 - gateway.networking.k8s.io/channel: experimental - creationTimestamp: null - name: xlistenersets.gateway.networking.x-k8s.io -spec: - group: gateway.networking.x-k8s.io - names: - categories: - - gateway-api - kind: XListenerSet - listKind: XListenerSetList - plural: xlistenersets - shortNames: - - lset - singular: xlistenerset - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.conditions[?(@.type=="Accepted")].status - name: Accepted - type: string - - jsonPath: .status.conditions[?(@.type=="Programmed")].status - name: Programmed - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1alpha1 - schema: - openAPIV3Schema: - description: |- - XListenerSet defines a set of additional listeners - to attach to an existing Gateway. - properties: - apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources - type: string - kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds - type: string - metadata: - type: object - spec: - description: Spec defines the desired state of ListenerSet. - properties: - listeners: - description: |- - Listeners associated with this ListenerSet. Listeners define - logical endpoints that are bound on this referenced parent Gateway's addresses. - - Listeners in a `Gateway` and their attached `ListenerSets` are concatenated - as a list when programming the underlying infrastructure. Each listener - name does not need to be unique across the Gateway and ListenerSets. - See ListenerEntry.Name for more details. - - Implementations MUST treat the parent Gateway as having the merged - list of all listeners from itself and attached ListenerSets using - the following precedence: - - 1. "parent" Gateway - 2. ListenerSet ordered by creation time (oldest first) - 3. ListenerSet ordered alphabetically by “{namespace}/{name}”. - - An implementation MAY reject listeners by setting the ListenerEntryStatus - `Accepted`` condition to False with the Reason `TooManyListeners` - - If a listener has a conflict, this will be reported in the - Status.ListenerEntryStatus setting the `Conflicted` condition to True. - - Implementations SHOULD be cautious about what information from the - parent or siblings are reported to avoid accidentally leaking - sensitive information that the child would not otherwise have access - to. This can include contents of secrets etc. - items: - properties: - allowedRoutes: - default: - namespaces: - from: Same - description: |- - AllowedRoutes defines the types of routes that MAY be attached to a - Listener and the trusted namespaces where those Route resources MAY be - present. - - Although a client request may match multiple route rules, only one rule - may ultimately receive the request. Matching precedence MUST be - determined in order of the following criteria: - - * The most specific match as defined by the Route type. - * The oldest Route based on creation timestamp. For example, a Route with - a creation timestamp of "2020-09-08 01:02:03" is given precedence over - a Route with a creation timestamp of "2020-09-08 01:02:04". - * If everything else is equivalent, the Route appearing first in - alphabetical order (namespace/name) should be given precedence. For - example, foo/bar is given precedence over foo/baz. - - All valid rules within a Route attached to this Listener should be - implemented. Invalid Route rules can be ignored (sometimes that will mean - the full Route). If a Route rule transitions from valid to invalid, - support for that Route rule should be dropped to ensure consistency. For - example, even if a filter specified by a Route rule is invalid, the rest - of the rules within that Route should still be supported. - properties: - kinds: - description: |- - Kinds specifies the groups and kinds of Routes that are allowed to bind - to this Gateway Listener. When unspecified or empty, the kinds of Routes - selected are determined using the Listener protocol. - - A RouteGroupKind MUST correspond to kinds of Routes that are compatible - with the application protocol specified in the Listener's Protocol field. - If an implementation does not support or recognize this resource type, it - MUST set the "ResolvedRefs" condition to False for this Listener with the - "InvalidRouteKinds" reason. - - Support: Core - items: - description: RouteGroupKind indicates the group and kind - of a Route resource. - properties: - group: - default: gateway.networking.k8s.io - description: Group is the group of the Route. - maxLength: 253 - pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - kind: - description: Kind is the kind of the Route. - maxLength: 63 - minLength: 1 - pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ - type: string - required: - - kind - type: object - maxItems: 8 - type: array - namespaces: - default: - from: Same - description: |- - Namespaces indicates namespaces from which Routes may be attached to this - Listener. This is restricted to the namespace of this Gateway by default. - - Support: Core - properties: - from: - default: Same - description: |- - From indicates where Routes will be selected for this Gateway. Possible - values are: - - * All: Routes in all namespaces may be used by this Gateway. - * Selector: Routes in namespaces selected by the selector may be used by - this Gateway. - * Same: Only Routes in the same namespace may be used by this Gateway. - - Support: Core - enum: - - All - - Selector - - Same - type: string - selector: - description: |- - Selector must be specified when From is set to "Selector". In that case, - only Routes in Namespaces matching this Selector will be selected by this - Gateway. This field is ignored for other values of "From". - - Support: Core - properties: - matchExpressions: - description: matchExpressions is a list of label - selector requirements. The requirements are ANDed. - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - properties: - key: - description: key is the label key that the - selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - type: object - type: object - hostname: - description: |- - Hostname specifies the virtual hostname to match for protocol types that - define this concept. When unspecified, all hostnames are matched. This - field is ignored for protocols that don't require hostname based - matching. - - Implementations MUST apply Hostname matching appropriately for each of - the following protocols: - - * TLS: The Listener Hostname MUST match the SNI. - * HTTP: The Listener Hostname MUST match the Host header of the request. - * HTTPS: The Listener Hostname SHOULD match at both the TLS and HTTP - protocol layers as described above. If an implementation does not - ensure that both the SNI and Host header match the Listener hostname, - it MUST clearly document that. - - For HTTPRoute and TLSRoute resources, there is an interaction with the - `spec.hostnames` array. When both listener and route specify hostnames, - there MUST be an intersection between the values for a Route to be - accepted. For more information, refer to the Route specific Hostnames - documentation. - - Hostnames that are prefixed with a wildcard label (`*.`) are interpreted - as a suffix match. That means that a match for `*.example.com` would match - both `test.example.com`, and `foo.test.example.com`, but not `example.com`. - maxLength: 253 - minLength: 1 - pattern: ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - name: - description: |- - Name is the name of the Listener. This name MUST be unique within a - ListenerSet. - - Name is not required to be unique across a Gateway and ListenerSets. - Routes can attach to a Listener by having a ListenerSet as a parentRef - and setting the SectionName - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - port: - description: |- - Port is the network port. Multiple listeners may use the - same port, subject to the Listener compatibility rules. - format: int32 - maximum: 65535 - minimum: 1 - type: integer - protocol: - description: Protocol specifies the network protocol this listener - expects to receive. - maxLength: 255 - minLength: 1 - pattern: ^[a-zA-Z0-9]([-a-zA-Z0-9]*[a-zA-Z0-9])?$|[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9]+$ - type: string - tls: - description: |- - TLS is the TLS configuration for the Listener. This field is required if - the Protocol field is "HTTPS" or "TLS". It is invalid to set this field - if the Protocol field is "HTTP", "TCP", or "UDP". - - The association of SNIs to Certificate defined in GatewayTLSConfig is - defined based on the Hostname field for this listener. - - The GatewayClass MUST use the longest matching SNI out of all - available certificates for any TLS handshake. - properties: - certificateRefs: - description: |- - CertificateRefs contains a series of references to Kubernetes objects that - contains TLS certificates and private keys. These certificates are used to - establish a TLS handshake for requests that match the hostname of the - associated listener. - - A single CertificateRef to a Kubernetes Secret has "Core" support. - Implementations MAY choose to support attaching multiple certificates to - a Listener, but this behavior is implementation-specific. - - References to a resource in different namespace are invalid UNLESS there - is a ReferenceGrant in the target namespace that allows the certificate - to be attached. If a ReferenceGrant does not allow this reference, the - "ResolvedRefs" condition MUST be set to False for this listener with the - "RefNotPermitted" reason. - - This field is required to have at least one element when the mode is set - to "Terminate" (default) and is optional otherwise. - - CertificateRefs can reference to standard Kubernetes resources, i.e. - Secret, or implementation-specific custom resources. - - Support: Core - A single reference to a Kubernetes Secret of type kubernetes.io/tls - - Support: Implementation-specific (More than one reference or other resource types) - items: - description: |- - SecretObjectReference identifies an API object including its namespace, - defaulting to Secret. - - The API object must be valid in the cluster; the Group and Kind must - be registered in the cluster for this reference to be valid. - - References to objects with invalid Group and Kind are not valid, and must - be rejected by the implementation, with appropriate Conditions set - on the containing object. - properties: - group: - default: "" - description: |- - Group is the group of the referent. For example, "gateway.networking.k8s.io". - When unspecified or empty string, core API group is inferred. - maxLength: 253 - pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - kind: - default: Secret - description: Kind is kind of the referent. For example - "Secret". - maxLength: 63 - minLength: 1 - pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ - type: string - name: - description: Name is the name of the referent. - maxLength: 253 - minLength: 1 - type: string - namespace: - description: |- - Namespace is the namespace of the referenced object. When unspecified, the local - namespace is inferred. - - Note that when a namespace different than the local namespace is specified, - a ReferenceGrant object is required in the referent namespace to allow that - namespace's owner to accept the reference. See the ReferenceGrant - documentation for details. - - Support: Core - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - required: - - name - type: object - maxItems: 64 - type: array - frontendValidation: - description: |- - FrontendValidation holds configuration information for validating the frontend (client). - Setting this field will require clients to send a client certificate - required for validation during the TLS handshake. In browsers this may result in a dialog appearing - that requests a user to specify the client certificate. - The maximum depth of a certificate chain accepted in verification is Implementation specific. - - Support: Extended - properties: - caCertificateRefs: - description: |- - CACertificateRefs contains one or more references to - Kubernetes objects that contain TLS certificates of - the Certificate Authorities that can be used - as a trust anchor to validate the certificates presented by the client. - - A single CA certificate reference to a Kubernetes ConfigMap - has "Core" support. - Implementations MAY choose to support attaching multiple CA certificates to - a Listener, but this behavior is implementation-specific. - - Support: Core - A single reference to a Kubernetes ConfigMap - with the CA certificate in a key named `ca.crt`. - - Support: Implementation-specific (More than one reference, or other kinds - of resources). - - References to a resource in a different namespace are invalid UNLESS there - is a ReferenceGrant in the target namespace that allows the certificate - to be attached. If a ReferenceGrant does not allow this reference, the - "ResolvedRefs" condition MUST be set to False for this listener with the - "RefNotPermitted" reason. - items: - description: |- - ObjectReference identifies an API object including its namespace. - - The API object must be valid in the cluster; the Group and Kind must - be registered in the cluster for this reference to be valid. - - References to objects with invalid Group and Kind are not valid, and must - be rejected by the implementation, with appropriate Conditions set - on the containing object. - properties: - group: - description: |- - Group is the group of the referent. For example, "gateway.networking.k8s.io". - When set to the empty string, core API group is inferred. - maxLength: 253 - pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - kind: - description: Kind is kind of the referent. For - example "ConfigMap" or "Service". - maxLength: 63 - minLength: 1 - pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ - type: string - name: - description: Name is the name of the referent. - maxLength: 253 - minLength: 1 - type: string - namespace: - description: |- - Namespace is the namespace of the referenced object. When unspecified, the local - namespace is inferred. - - Note that when a namespace different than the local namespace is specified, - a ReferenceGrant object is required in the referent namespace to allow that - namespace's owner to accept the reference. See the ReferenceGrant - documentation for details. - - Support: Core - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - required: - - group - - kind - - name - type: object - maxItems: 8 - minItems: 1 - type: array - type: object - mode: - default: Terminate - description: |- - Mode defines the TLS behavior for the TLS session initiated by the client. - There are two possible modes: - - - Terminate: The TLS session between the downstream client and the - Gateway is terminated at the Gateway. This mode requires certificates - to be specified in some way, such as populating the certificateRefs - field. - - Passthrough: The TLS session is NOT terminated by the Gateway. This - implies that the Gateway can't decipher the TLS stream except for - the ClientHello message of the TLS protocol. The certificateRefs field - is ignored in this mode. - - Support: Core - enum: - - Terminate - - Passthrough - type: string - options: - additionalProperties: - description: |- - AnnotationValue is the value of an annotation in Gateway API. This is used - for validation of maps such as TLS options. This roughly matches Kubernetes - annotation validation, although the length validation in that case is based - on the entire size of the annotations struct. - maxLength: 4096 - minLength: 0 - type: string - description: |- - Options are a list of key/value pairs to enable extended TLS - configuration for each implementation. For example, configuring the - minimum TLS version or supported cipher suites. - - A set of common keys MAY be defined by the API in the future. To avoid - any ambiguity, implementation-specific definitions MUST use - domain-prefixed names, such as `example.com/my-custom-option`. - Un-prefixed names are reserved for key names defined by Gateway API. - - Support: Implementation-specific - maxProperties: 16 - type: object - type: object - x-kubernetes-validations: - - message: certificateRefs or options must be specified when - mode is Terminate - rule: 'self.mode == ''Terminate'' ? size(self.certificateRefs) - > 0 || size(self.options) > 0 : true' - required: - - name - - port - - protocol - type: object - maxItems: 64 - minItems: 1 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - x-kubernetes-validations: - - message: tls must not be specified for protocols ['HTTP', 'TCP', - 'UDP'] - rule: 'self.all(l, l.protocol in [''HTTP'', ''TCP'', ''UDP''] ? - !has(l.tls) : true)' - - message: tls mode must be Terminate for protocol HTTPS - rule: 'self.all(l, (l.protocol == ''HTTPS'' && has(l.tls)) ? (l.tls.mode - == '''' || l.tls.mode == ''Terminate'') : true)' - - message: hostname must not be specified for protocols ['TCP', 'UDP'] - rule: 'self.all(l, l.protocol in [''TCP'', ''UDP''] ? (!has(l.hostname) - || l.hostname == '''') : true)' - - message: Listener name must be unique within the Gateway - rule: self.all(l1, self.exists_one(l2, l1.name == l2.name)) - - message: Combination of port, protocol and hostname must be unique - for each listener - rule: 'self.all(l1, !has(l1.port) || self.exists_one(l2, has(l2.port) - && l1.port == l2.port && l1.protocol == l2.protocol && (has(l1.hostname) - && has(l2.hostname) ? l1.hostname == l2.hostname : !has(l1.hostname) - && !has(l2.hostname))))' - parentRef: - description: ParentRef references the Gateway that the listeners are - attached to. - properties: - group: - default: gateway.networking.k8s.io - description: Group is the group of the referent. - maxLength: 253 - pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - kind: - default: Gateway - description: Kind is kind of the referent. For example "Gateway". - maxLength: 63 - minLength: 1 - pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ - type: string - name: - description: Name is the name of the referent. - maxLength: 253 - minLength: 1 - type: string - namespace: - description: |- - Namespace is the namespace of the referent. If not present, - the namespace of the referent is assumed to be the same as - the namespace of the referring object. - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - required: - - name - type: object - required: - - listeners - - parentRef - type: object - status: - default: - conditions: - - lastTransitionTime: "1970-01-01T00:00:00Z" - message: Waiting for controller - reason: Pending - status: Unknown - type: Accepted - - lastTransitionTime: "1970-01-01T00:00:00Z" - message: Waiting for controller - reason: Pending - status: Unknown - type: Programmed - description: Status defines the current state of ListenerSet. - properties: - conditions: - default: - - lastTransitionTime: "1970-01-01T00:00:00Z" - message: Waiting for controller - reason: Pending - status: Unknown - type: Accepted - - lastTransitionTime: "1970-01-01T00:00:00Z" - message: Waiting for controller - reason: Pending - status: Unknown - type: Programmed - description: |- - Conditions describe the current conditions of the ListenerSet. - - Implementations MUST express ListenerSet conditions using the - `ListenerSetConditionType` and `ListenerSetConditionReason` - constants so that operators and tools can converge on a common - vocabulary to describe ListenerSet state. - - Known condition types are: - - * "Accepted" - * "Programmed" - items: - description: Condition contains details for one aspect of the current - state of this API Resource. - properties: - lastTransitionTime: - description: |- - lastTransitionTime is the last time the condition transitioned from one status to another. - This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: |- - message is a human readable message indicating details about the transition. - This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: |- - observedGeneration represents the .metadata.generation that the condition was set based upon. - For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date - with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: |- - reason contains a programmatic identifier indicating the reason for the condition's last transition. - Producers of specific condition types may define expected values and meanings for this field, - and whether the values are considered a guaranteed API. - The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - maxItems: 8 - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - listeners: - description: Listeners provide status for each unique listener port - defined in the Spec. - items: - description: ListenerStatus is the status associated with a Listener. - properties: - attachedRoutes: - description: |- - AttachedRoutes represents the total number of Routes that have been - successfully attached to this Listener. - - Successful attachment of a Route to a Listener is based solely on the - combination of the AllowedRoutes field on the corresponding Listener - and the Route's ParentRefs field. A Route is successfully attached to - a Listener when it is selected by the Listener's AllowedRoutes field - AND the Route has a valid ParentRef selecting the whole Gateway - resource or a specific Listener as a parent resource (more detail on - attachment semantics can be found in the documentation on the various - Route kinds ParentRefs fields). Listener or Route status does not impact - successful attachment, i.e. the AttachedRoutes field count MUST be set - for Listeners with condition Accepted: false and MUST count successfully - attached Routes that may themselves have Accepted: false conditions. - - Uses for this field include troubleshooting Route attachment and - measuring blast radius/impact of changes to a Listener. - format: int32 - type: integer - conditions: - description: Conditions describe the current condition of this - listener. - items: - description: Condition contains details for one aspect of - the current state of this API Resource. - properties: - lastTransitionTime: - description: |- - lastTransitionTime is the last time the condition transitioned from one status to another. - This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: |- - message is a human readable message indicating details about the transition. - This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: |- - observedGeneration represents the .metadata.generation that the condition was set based upon. - For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date - with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: |- - reason contains a programmatic identifier indicating the reason for the condition's last transition. - Producers of specific condition types may define expected values and meanings for this field, - and whether the values are considered a guaranteed API. - The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, - Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - maxItems: 8 - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - name: - description: Name is the name of the Listener that this status - corresponds to. - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - port: - description: Port is the network port the listener is configured - to listen on. - format: int32 - maximum: 65535 - minimum: 1 - type: integer - supportedKinds: - description: |- - SupportedKinds is the list indicating the Kinds supported by this - listener. This MUST represent the kinds an implementation supports for - that Listener configuration. - - If kinds are specified in Spec that are not supported, they MUST NOT - appear in this list and an implementation MUST set the "ResolvedRefs" - condition to "False" with the "InvalidRouteKinds" reason. If both valid - and invalid Route kinds are specified, the implementation MUST - reference the valid Route kinds that have been specified. - items: - description: RouteGroupKind indicates the group and kind of - a Route resource. - properties: - group: - default: gateway.networking.k8s.io - description: Group is the group of the Route. - maxLength: 253 - pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - kind: - description: Kind is the kind of the Route. - maxLength: 63 - minLength: 1 - pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ - type: string - required: - - kind - type: object - maxItems: 8 - type: array - required: - - attachedRoutes - - conditions - - name - - port - - supportedKinds - type: object - maxItems: 64 - type: array - x-kubernetes-list-map-keys: - - name - x-kubernetes-list-type: map - type: object - required: - - spec - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: null - storedVersions: null diff --git a/integration/fixtures/k8s/01-traefik-crd.yml b/integration/fixtures/k8s/01-traefik-crd.yml index ea2618637..4a471058f 100644 --- a/integration/fixtures/k8s/01-traefik-crd.yml +++ b/integration/fixtures/k8s/01-traefik-crd.yml @@ -43,7 +43,7 @@ spec: description: |- EntryPoints defines the list of entry point names to bind to. Entry points have to be configured in the static configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/install-configuration/entrypoints/ + More info: https://doc.traefik.io/traefik/v3.4/routing/entrypoints/ Default: all. items: type: string @@ -64,12 +64,12 @@ spec: match: description: |- Match defines the router's rule. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/routing/rules-and-priority/ + More info: https://doc.traefik.io/traefik/v3.4/routing/routers/#rule type: string middlewares: description: |- Middlewares defines the list of references to Middleware resources. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/kubernetes/crd/http/middleware/ + More info: https://doc.traefik.io/traefik/v3.4/routing/providers/kubernetes-crd/#kind-middleware items: description: MiddlewareRef is a reference to a Middleware resource. @@ -89,30 +89,19 @@ spec: observability: description: |- Observability defines the observability configuration for a router. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/routing/observability/ + More info: https://doc.traefik.io/traefik/v3.4/routing/routers/#observability properties: accessLogs: - description: AccessLogs enables access logs for this router. type: boolean metrics: - description: Metrics enables metrics for this router. type: boolean - traceVerbosity: - default: minimal - description: TraceVerbosity defines the verbosity level - of the tracing for this router. - enum: - - minimal - - detailed - type: string tracing: - description: Tracing enables tracing for this router. type: boolean type: object priority: description: |- Priority defines the router's priority. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/routing/rules-and-priority/#priority + More info: https://doc.traefik.io/traefik/v3.4/routing/routers/#priority maximum: 9223372036854775000 type: integer services: @@ -147,7 +136,7 @@ spec: - type: integer - type: string description: |- - Interval defines the frequency of the health check calls for healthy targets. + Interval defines the frequency of the health check calls. Default: 30s x-kubernetes-int-or-string: true method: @@ -183,15 +172,6 @@ spec: Timeout defines the maximum duration Traefik will wait for a health check request before considering the server unhealthy. Default: 5s x-kubernetes-int-or-string: true - unhealthyInterval: - anyOf: - - type: integer - - type: string - description: |- - UnhealthyInterval defines the frequency of the health check calls for unhealthy targets. - When UnhealthyInterval is not defined, it defaults to the Interval value. - Default: 30s - x-kubernetes-int-or-string: true type: object kind: description: Kind defines the kind of the Service. @@ -263,7 +243,7 @@ spec: sticky: description: |- Sticky defines the sticky sessions configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/load-balancing/service/#sticky-sessions + More info: https://doc.traefik.io/traefik/v3.4/routing/services/#sticky-sessions properties: cookie: description: Cookie defines the sticky cookie configuration. @@ -332,7 +312,7 @@ spec: syntax: description: |- Syntax defines the router's rule syntax. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/routing/rules-and-priority/#rulesyntax + More info: https://doc.traefik.io/traefik/v3.4/routing/routers/#rulesyntax Deprecated: Please do not use this field and rewrite the router rules to use the v3 syntax. type: string required: @@ -342,18 +322,18 @@ spec: tls: description: |- TLS defines the TLS configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/routing/router/#tls + More info: https://doc.traefik.io/traefik/v3.4/routing/routers/#tls properties: certResolver: description: |- CertResolver defines the name of the certificate resolver to use. Cert resolvers have to be configured in the static configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/install-configuration/tls/certificate-resolvers/acme/ + More info: https://doc.traefik.io/traefik/v3.4/https/acme/#certificate-resolvers type: string domains: description: |- Domains defines the list of domains that will be used to issue certificates. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/tls/tls-certificates/#domains + More info: https://doc.traefik.io/traefik/v3.4/routing/routers/#domains items: description: Domain holds a domain name with SANs. properties: @@ -372,17 +352,17 @@ spec: description: |- Options defines the reference to a TLSOption, that specifies the parameters of the TLS connection. If not defined, the `default` TLSOption is used. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/tls/tls-options/ + More info: https://doc.traefik.io/traefik/v3.4/https/tls/#tls-options properties: name: description: |- Name defines the name of the referenced TLSOption. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/kubernetes/crd/http/tlsoption/ + More info: https://doc.traefik.io/traefik/v3.4/routing/providers/kubernetes-crd/#kind-tlsoption type: string namespace: description: |- Namespace defines the namespace of the referenced TLSOption. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/kubernetes/crd/http/tlsoption/ + More info: https://doc.traefik.io/traefik/v3.4/routing/providers/kubernetes-crd/#kind-tlsoption type: string required: - name @@ -399,12 +379,12 @@ spec: name: description: |- Name defines the name of the referenced TLSStore. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/kubernetes/crd/http/tlsstore/ + More info: https://doc.traefik.io/traefik/v3.4/routing/providers/kubernetes-crd/#kind-tlsstore type: string namespace: description: |- Namespace defines the namespace of the referenced TLSStore. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/kubernetes/crd/http/tlsstore/ + More info: https://doc.traefik.io/traefik/v3.4/routing/providers/kubernetes-crd/#kind-tlsstore type: string required: - name @@ -464,7 +444,7 @@ spec: description: |- EntryPoints defines the list of entry point names to bind to. Entry points have to be configured in the static configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/install-configuration/entrypoints/ + More info: https://doc.traefik.io/traefik/v3.4/routing/entrypoints/ Default: all. items: type: string @@ -477,7 +457,7 @@ spec: match: description: |- Match defines the router's rule. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/routing/rules-and-priority/ + More info: https://doc.traefik.io/traefik/v3.4/routing/routers/#rule_1 type: string middlewares: description: Middlewares defines the list of references to MiddlewareTCP @@ -501,7 +481,7 @@ spec: priority: description: |- Priority defines the router's priority. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/routing/rules-and-priority/#priority + More info: https://doc.traefik.io/traefik/v3.4/routing/routers/#priority_1 maximum: 9223372036854775000 type: integer services: @@ -543,8 +523,7 @@ spec: proxyProtocol: description: |- ProxyProtocol defines the PROXY protocol configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/service/#proxy-protocol - Deprecated: ProxyProtocol will not be supported in future APIVersions, please use ServersTransport to configure ProxyProtocol instead. + More info: https://doc.traefik.io/traefik/v3.4/routing/services/#proxy-protocol properties: version: description: Version defines the PROXY Protocol version @@ -585,7 +564,7 @@ spec: syntax: description: |- Syntax defines the router's rule syntax. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/routing/rules-and-priority/#rulesyntax + More info: https://doc.traefik.io/traefik/v3.4/routing/routers/#rulesyntax_1 Deprecated: Please do not use this field and rewrite the router rules to use the v3 syntax. enum: - v3 @@ -598,18 +577,18 @@ spec: tls: description: |- TLS defines the TLS configuration on a layer 4 / TCP Route. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/routing/router/#tls + More info: https://doc.traefik.io/traefik/v3.4/routing/routers/#tls_1 properties: certResolver: description: |- CertResolver defines the name of the certificate resolver to use. Cert resolvers have to be configured in the static configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/install-configuration/tls/certificate-resolvers/acme/ + More info: https://doc.traefik.io/traefik/v3.4/https/acme/#certificate-resolvers type: string domains: description: |- Domains defines the list of domains that will be used to issue certificates. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/tls/#domains + More info: https://doc.traefik.io/traefik/v3.4/routing/routers/#domains items: description: Domain holds a domain name with SANs. properties: @@ -628,7 +607,7 @@ spec: description: |- Options defines the reference to a TLSOption, that specifies the parameters of the TLS connection. If not defined, the `default` TLSOption is used. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/tls/#tls-options + More info: https://doc.traefik.io/traefik/v3.4/https/tls/#tls-options properties: name: description: Name defines the name of the referenced Traefik @@ -720,7 +699,7 @@ spec: description: |- EntryPoints defines the list of entry point names to bind to. Entry points have to be configured in the static configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/install-configuration/entrypoints/ + More info: https://doc.traefik.io/traefik/v3.4/routing/entrypoints/ Default: all. items: type: string @@ -808,7 +787,7 @@ spec: openAPIV3Schema: description: |- Middleware is the CRD implementation of a Traefik Middleware. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/overview/ + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/overview/ properties: apiVersion: description: |- @@ -834,7 +813,7 @@ spec: description: |- AddPrefix holds the add prefix middleware configuration. This middleware updates the path of a request before forwarding it. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/addprefix/ + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/addprefix/ properties: prefix: description: |- @@ -849,12 +828,12 @@ spec: description: |- BasicAuth holds the basic auth middleware configuration. This middleware restricts access to your services to known users. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/basicauth/ + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/basicauth/ properties: headerField: description: |- HeaderField defines a header field to store the authenticated user. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/basicauth/#headerfield + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/basicauth/#headerfield type: string realm: description: |- @@ -875,7 +854,7 @@ spec: description: |- Buffering holds the buffering middleware configuration. This middleware retries or limits the size of requests that can be forwarded to backends. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/buffering/#maxrequestbodybytes + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/buffering/#maxrequestbodybytes properties: maxRequestBodyBytes: description: |- @@ -907,14 +886,14 @@ spec: description: |- RetryExpression defines the retry conditions. It is a logical combination of functions with operators AND (&&) and OR (||). - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/buffering/#retryexpression + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/buffering/#retryexpression type: string type: object chain: description: |- Chain holds the configuration of the chain middleware. This middleware enables to define reusable combinations of other pieces of middleware. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/chain/ + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/chain/ properties: middlewares: description: Middlewares is the list of MiddlewareRef which composes @@ -977,7 +956,7 @@ spec: description: |- Compress holds the compress middleware configuration. This middleware compresses responses before sending them to the client, using gzip, brotli, or zstd compression. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/compress/ + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/compress/ properties: defaultEncoding: description: DefaultEncoding specifies the default encoding if @@ -1027,12 +1006,12 @@ spec: description: |- DigestAuth holds the digest auth middleware configuration. This middleware restricts access to your services to known users. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/digestauth/ + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/digestauth/ properties: headerField: description: |- HeaderField defines a header field to store the authenticated user. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/digestauth/#headerfield + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/basicauth/#headerfield type: string realm: description: |- @@ -1052,7 +1031,7 @@ spec: description: |- ErrorPage holds the custom error middleware configuration. This middleware returns a custom page in lieu of the default, according to configured ranges of HTTP Status codes. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/errorpages/ + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/errorpages/ properties: query: description: |- @@ -1064,7 +1043,7 @@ spec: service: description: |- Service defines the reference to a Kubernetes Service that will serve the error page. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/errorpages/#service + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/errorpages/#service properties: healthCheck: description: Healthcheck defines health checks for ExternalName @@ -1090,7 +1069,7 @@ spec: - type: integer - type: string description: |- - Interval defines the frequency of the health check calls for healthy targets. + Interval defines the frequency of the health check calls. Default: 30s x-kubernetes-int-or-string: true method: @@ -1126,15 +1105,6 @@ spec: Timeout defines the maximum duration Traefik will wait for a health check request before considering the server unhealthy. Default: 5s x-kubernetes-int-or-string: true - unhealthyInterval: - anyOf: - - type: integer - - type: string - description: |- - UnhealthyInterval defines the frequency of the health check calls for unhealthy targets. - When UnhealthyInterval is not defined, it defaults to the Interval value. - Default: 30s - x-kubernetes-int-or-string: true type: object kind: description: Kind defines the kind of the Service. @@ -1206,7 +1176,7 @@ spec: sticky: description: |- Sticky defines the sticky sessions configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/load-balancing/service/#sticky-sessions + More info: https://doc.traefik.io/traefik/v3.4/routing/services/#sticky-sessions properties: cookie: description: Cookie defines the sticky cookie configuration. @@ -1293,7 +1263,7 @@ spec: description: |- ForwardAuth holds the forward auth middleware configuration. This middleware delegates the request authentication to a Service. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/forwardauth/ + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/forwardauth/ properties: addAuthCookiesToResponse: description: AddAuthCookiesToResponse defines the list of cookies @@ -1321,7 +1291,7 @@ spec: authResponseHeadersRegex: description: |- AuthResponseHeadersRegex defines the regex to match headers to copy from the authentication server response and set on forwarded request, after stripping all headers that match the regex. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/forwardauth/#authresponseheadersregex + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/forwardauth/#authresponseheadersregex type: string forwardBody: description: ForwardBody defines whether to send the request body @@ -1330,7 +1300,7 @@ spec: headerField: description: |- HeaderField defines a header field to store the authenticated user. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/forwardauth/#headerfield + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/forwardauth/#headerfield type: string maxBodySize: description: MaxBodySize defines the maximum body size in bytes @@ -1392,7 +1362,7 @@ spec: description: |- Headers holds the headers middleware configuration. This middleware manages the requests and responses headers. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/headers/#customrequestheaders + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/headers/#customrequestheaders properties: accessControlAllowCredentials: description: AccessControlAllowCredentials defines whether the @@ -1564,7 +1534,7 @@ spec: description: |- InFlightReq holds the in-flight request middleware configuration. This middleware limits the number of requests being processed and served concurrently. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/inflightreq/ + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/inflightreq/ properties: amount: description: |- @@ -1578,12 +1548,12 @@ spec: SourceCriterion defines what criterion is used to group requests as originating from a common source. If several strategies are defined at the same time, an error will be raised. If none are set, the default is to use the requestHost. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/inflightreq/#sourcecriterion + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/inflightreq/#sourcecriterion properties: ipStrategy: description: |- IPStrategy holds the IP strategy configuration used by Traefik to determine the client IP. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/ipallowlist/#ipstrategy + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/ipallowlist/#ipstrategy properties: depth: description: Depth tells Traefik to use the X-Forwarded-For @@ -1619,12 +1589,12 @@ spec: description: |- IPAllowList holds the IP allowlist middleware configuration. This middleware limits allowed requests based on the client IP. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/ipallowlist/ + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/ipallowlist/ properties: ipStrategy: description: |- IPStrategy holds the IP strategy configuration used by Traefik to determine the client IP. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/ipallowlist/#ipstrategy + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/ipallowlist/#ipstrategy properties: depth: description: Depth tells Traefik to use the X-Forwarded-For @@ -1662,7 +1632,7 @@ spec: ipStrategy: description: |- IPStrategy holds the IP strategy configuration used by Traefik to determine the client IP. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/ipallowlist/#ipstrategy + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/ipallowlist/#ipstrategy properties: depth: description: Depth tells Traefik to use the X-Forwarded-For @@ -1693,7 +1663,7 @@ spec: description: |- PassTLSClientCert holds the pass TLS client cert middleware configuration. This middleware adds the selected data from the passed client TLS certificate to a header. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/passtlsclientcert/ + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/passtlsclientcert/ properties: info: description: Info selects the specific client certificate details @@ -1796,13 +1766,13 @@ spec: x-kubernetes-preserve-unknown-fields: true description: |- Plugin defines the middleware plugin configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/overview/#community-middlewares + More info: https://doc.traefik.io/traefik/plugins/ type: object rateLimit: description: |- RateLimit holds the rate limit configuration. This middleware ensures that services will receive a fair amount of requests, and allows one to define what fair is. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/ratelimit/ + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/ratelimit/ properties: average: description: |- @@ -1921,7 +1891,7 @@ spec: ipStrategy: description: |- IPStrategy holds the IP strategy configuration used by Traefik to determine the client IP. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/ipallowlist/#ipstrategy + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/ipallowlist/#ipstrategy properties: depth: description: Depth tells Traefik to use the X-Forwarded-For @@ -1957,11 +1927,11 @@ spec: description: |- RedirectRegex holds the redirect regex middleware configuration. This middleware redirects a request using regex matching and replacement. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/redirectregex/#regex + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/redirectregex/#regex properties: permanent: description: Permanent defines whether the redirection is permanent - (308). + (301). type: boolean regex: description: Regex defines the regex used to match and capture @@ -1976,11 +1946,11 @@ spec: description: |- RedirectScheme holds the redirect scheme middleware configuration. This middleware redirects requests from a scheme/port to another. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/redirectscheme/ + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/redirectscheme/ properties: permanent: description: Permanent defines whether the redirection is permanent - (308). + (301). type: boolean port: description: Port defines the port of the new URL. @@ -1993,7 +1963,7 @@ spec: description: |- ReplacePath holds the replace path middleware configuration. This middleware replaces the path of the request URL and store the original path in an X-Replaced-Path header. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/replacepath/ + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/replacepath/ properties: path: description: Path defines the path to use as replacement in the @@ -2004,7 +1974,7 @@ spec: description: |- ReplacePathRegex holds the replace path regex middleware configuration. This middleware replaces the path of a URL using regex matching and replacement. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/replacepathregex/ + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/replacepathregex/ properties: regex: description: Regex defines the regular expression used to match @@ -2020,7 +1990,7 @@ spec: Retry holds the retry middleware configuration. This middleware reissues requests a given number of times to a backend server if that server does not reply. As soon as the server answers, the middleware stops retrying, regardless of the response status. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/retry/ + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/retry/ properties: attempts: description: Attempts defines how many times the request should @@ -2044,7 +2014,7 @@ spec: description: |- StripPrefix holds the strip prefix middleware configuration. This middleware removes the specified prefixes from the URL path. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/stripprefix/ + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/stripprefix/ properties: forceSlash: description: |- @@ -2063,7 +2033,7 @@ spec: description: |- StripPrefixRegex holds the strip prefix regex middleware configuration. This middleware removes the matching prefixes from the URL path. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/stripprefixregex/ + More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/stripprefixregex/ properties: regex: description: Regex defines the regular expression to match the @@ -2100,7 +2070,7 @@ spec: openAPIV3Schema: description: |- MiddlewareTCP is the CRD implementation of a Traefik TCP middleware. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/middlewares/overview/ + More info: https://doc.traefik.io/traefik/v3.4/middlewares/overview/ properties: apiVersion: description: |- @@ -2137,7 +2107,7 @@ spec: description: |- IPAllowList defines the IPAllowList middleware configuration. This middleware accepts/refuses connections based on the client IP. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/middlewares/ipallowlist/ + More info: https://doc.traefik.io/traefik/v3.4/middlewares/tcp/ipallowlist/ properties: sourceRange: description: SourceRange defines the allowed IPs (or ranges of @@ -2151,7 +2121,7 @@ spec: IPWhiteList defines the IPWhiteList middleware configuration. This middleware accepts/refuses connections based on the client IP. Deprecated: please use IPAllowList instead. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/middlewares/ipwhitelist/ + More info: https://doc.traefik.io/traefik/v3.4/middlewares/tcp/ipwhitelist/ properties: sourceRange: description: SourceRange defines the allowed IPs (or ranges of @@ -2190,7 +2160,7 @@ spec: ServersTransport is the CRD implementation of a ServersTransport. If no serversTransport is specified, the default@internal will be used. The default@internal serversTransport is created from the static configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/load-balancing/serverstransport/ + More info: https://doc.traefik.io/traefik/v3.4/routing/services/#serverstransport_1 properties: apiVersion: description: |- @@ -2276,7 +2246,7 @@ spec: maxIdleConnsPerHost: description: MaxIdleConnsPerHost controls the maximum idle (keep-alive) to keep per-host. - minimum: -1 + minimum: 0 type: integer peerCertURI: description: PeerCertURI defines the peer cert URI used to match against @@ -2359,7 +2329,7 @@ spec: ServersTransportTCP is the CRD implementation of a TCPServersTransport. If no tcpServersTransport is specified, a default one named default@internal will be used. The default@internal tcpServersTransport can be configured in the static configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/serverstransport/ + More info: https://doc.traefik.io/traefik/v3.4/routing/services/#serverstransport_3 properties: apiVersion: description: |- @@ -2401,15 +2371,6 @@ spec: to a backend server can be established. pattern: ^([0-9]+(ns|us|Âĩs|ms|s|m|h)?)+$ x-kubernetes-int-or-string: true - proxyProtocol: - description: ProxyProtocol holds the PROXY Protocol configuration. - properties: - version: - description: Version defines the PROXY Protocol version to use. - maximum: 2 - minimum: 1 - type: integer - type: object terminationDelay: anyOf: - type: integer @@ -2513,7 +2474,7 @@ spec: openAPIV3Schema: description: |- TLSOption is the CRD implementation of a Traefik TLS Option, allowing to configure some parameters of the TLS connection. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/tls/tls-certificates/#certificates-stores#tls-options + More info: https://doc.traefik.io/traefik/v3.4/https/tls/#tls-options properties: apiVersion: description: |- @@ -2538,14 +2499,14 @@ spec: alpnProtocols: description: |- ALPNProtocols defines the list of supported application level protocols for the TLS handshake, in order of preference. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/tls/tls-certificates/#certificates-stores#alpn-protocols + More info: https://doc.traefik.io/traefik/v3.4/https/tls/#alpn-protocols items: type: string type: array cipherSuites: description: |- CipherSuites defines the list of supported cipher suites for TLS versions up to TLS 1.2. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/tls/tls-certificates/#certificates-stores#cipher-suites + More info: https://doc.traefik.io/traefik/v3.4/https/tls/#cipher-suites items: type: string type: array @@ -2572,8 +2533,8 @@ spec: type: object curvePreferences: description: |- - CurvePreferences defines the preferred elliptic curves. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/tls/tls-certificates/#certificates-stores#curve-preferences + CurvePreferences defines the preferred elliptic curves in a specific order. + More info: https://doc.traefik.io/traefik/v3.4/https/tls/#curve-preferences items: type: string type: array @@ -2633,7 +2594,7 @@ spec: TLSStore is the CRD implementation of a Traefik TLS Store. For the time being, only the TLSStore named default is supported. This means that you cannot have two stores that are named default in different Kubernetes namespaces. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/tls/tls-certificates/#certificates-stores#certificates-stores + More info: https://doc.traefik.io/traefik/v3.4/https/tls/#certificates-stores properties: apiVersion: description: |- @@ -2731,7 +2692,7 @@ spec: TraefikService object allows to: - Apply weight to Services on load-balancing - Mirror traffic on services - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/kubernetes/crd/http/traefikservice/ + More info: https://doc.traefik.io/traefik/v3.4/routing/providers/kubernetes-crd/#kind-traefikservice properties: apiVersion: description: |- @@ -2780,7 +2741,7 @@ spec: - type: integer - type: string description: |- - Interval defines the frequency of the health check calls for healthy targets. + Interval defines the frequency of the health check calls. Default: 30s x-kubernetes-int-or-string: true method: @@ -2816,15 +2777,6 @@ spec: Timeout defines the maximum duration Traefik will wait for a health check request before considering the server unhealthy. Default: 5s x-kubernetes-int-or-string: true - unhealthyInterval: - anyOf: - - type: integer - - type: string - description: |- - UnhealthyInterval defines the frequency of the health check calls for unhealthy targets. - When UnhealthyInterval is not defined, it defaults to the Interval value. - Default: 30s - x-kubernetes-int-or-string: true type: object kind: description: Kind defines the kind of the Service. @@ -2874,7 +2826,7 @@ spec: - type: integer - type: string description: |- - Interval defines the frequency of the health check calls for healthy targets. + Interval defines the frequency of the health check calls. Default: 30s x-kubernetes-int-or-string: true method: @@ -2910,15 +2862,6 @@ spec: Timeout defines the maximum duration Traefik will wait for a health check request before considering the server unhealthy. Default: 5s x-kubernetes-int-or-string: true - unhealthyInterval: - anyOf: - - type: integer - - type: string - description: |- - UnhealthyInterval defines the frequency of the health check calls for unhealthy targets. - When UnhealthyInterval is not defined, it defaults to the Interval value. - Default: 30s - x-kubernetes-int-or-string: true type: object kind: description: Kind defines the kind of the Service. @@ -2995,7 +2938,7 @@ spec: sticky: description: |- Sticky defines the sticky sessions configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/load-balancing/service/#sticky-sessions + More info: https://doc.traefik.io/traefik/v3.4/routing/services/#sticky-sessions properties: cookie: description: Cookie defines the sticky cookie configuration. @@ -3123,7 +3066,7 @@ spec: sticky: description: |- Sticky defines the sticky sessions configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/load-balancing/service/#sticky-sessions + More info: https://doc.traefik.io/traefik/v3.4/routing/services/#sticky-sessions properties: cookie: description: Cookie defines the sticky cookie configuration. @@ -3220,7 +3163,7 @@ spec: - type: integer - type: string description: |- - Interval defines the frequency of the health check calls for healthy targets. + Interval defines the frequency of the health check calls. Default: 30s x-kubernetes-int-or-string: true method: @@ -3256,15 +3199,6 @@ spec: Timeout defines the maximum duration Traefik will wait for a health check request before considering the server unhealthy. Default: 5s x-kubernetes-int-or-string: true - unhealthyInterval: - anyOf: - - type: integer - - type: string - description: |- - UnhealthyInterval defines the frequency of the health check calls for unhealthy targets. - When UnhealthyInterval is not defined, it defaults to the Interval value. - Default: 30s - x-kubernetes-int-or-string: true type: object kind: description: Kind defines the kind of the Service. @@ -3336,7 +3270,7 @@ spec: sticky: description: |- Sticky defines the sticky sessions configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/load-balancing/service/#sticky-sessions + More info: https://doc.traefik.io/traefik/v3.4/routing/services/#sticky-sessions properties: cookie: description: Cookie defines the sticky cookie configuration. @@ -3404,7 +3338,7 @@ spec: sticky: description: |- Sticky defines whether sticky sessions are enabled. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/kubernetes/crd/http/traefikservice/#stickiness-and-load-balancing + More info: https://doc.traefik.io/traefik/v3.4/routing/providers/kubernetes-crd/#stickiness-and-load-balancing properties: cookie: description: Cookie defines the sticky cookie configuration. diff --git a/integration/fixtures/ocsp/ca.crt b/integration/fixtures/ocsp/ca.crt deleted file mode 100644 index bc620de75..000000000 --- a/integration/fixtures/ocsp/ca.crt +++ /dev/null @@ -1,11 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIBhjCCASygAwIBAgIBATAKBggqhkjOPQQDAjASMRAwDgYDVQQDEwdUZXN0IENB -MB4XDTI1MDQyNDEzNTIzOFoXDTM1MDQyMjEzNTIzOFowEjEQMA4GA1UEAxMHVGVz -dCBDQTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABPp8MoNUBbUxp3jW6FcDH+lg -Zft1SIpnGjkMVjLSbW9EzmRQ/oMRHQqJvE7wJbwDs/JUTigRtfZL0vOojnhHcPej -czBxMA4GA1UdDwEB/wQEAwIBBjASBgNVHRMBAf8ECDAGAQH/AgEBMB0GA1UdDgQW -BBQXRlWLK295lmDy+931a4Ha8XVNNjAsBggrBgEFBQcBAQQgMB4wHAYIKwYBBQUH -MAGGEG9jc3AuZXhhbXBsZS5jb20wCgYIKoZIzj0EAwIDSAAwRQIgYH6lnce9jxcp -YIVhY4z55rnOKXqaI/5rUQKwjJ3dRsUCIQDThtkFgOPT/67xOYCTCEVSMSTwh2Gq -jbeucU+4c/InVg== ------END CERTIFICATE----- diff --git a/integration/fixtures/ocsp/ca.key b/integration/fixtures/ocsp/ca.key deleted file mode 100644 index ac7b3aa04..000000000 --- a/integration/fixtures/ocsp/ca.key +++ /dev/null @@ -1,5 +0,0 @@ ------BEGIN EC PRIVATE KEY----- -MHcCAQEEIGV6FPfHeA42xfjVtpnyATG6tKCCu0QoY0OlBR/0xn2toAoGCCqGSM49 -AwEHoUQDQgAE+nwyg1QFtTGneNboVwMf6WBl+3VIimcaOQxWMtJtb0TOZFD+gxEd -Com8TvAlvAOz8lROKBG19kvS86iOeEdw9w== ------END EC PRIVATE KEY----- diff --git a/integration/fixtures/ocsp/default.crt b/integration/fixtures/ocsp/default.crt deleted file mode 100644 index 85a651f66..000000000 --- a/integration/fixtures/ocsp/default.crt +++ /dev/null @@ -1,11 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIBjzCCATWgAwIBAgIIGDlFgswljYAwCgYIKoZIzj0EAwIwEjEQMA4GA1UEAxMH -VGVzdCBDQTAeFw0yNTA0MjQxMzUyMzhaFw0yNjA0MjQxMzUyMzhaMBgxFjAUBgNV -BAMTDWRlZmF1bHQubG9jYWwwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQGABZ/ -zezTMQBwmmw3aifU0OkDQ4ZzxGG7dR93svJPgYnP7TpBVtPrxy0WgVZbbCHv0Srl -PlpO9rFkKf3D4E6Qo28wbTAOBgNVHQ8BAf8EBAMCBaAwDAYDVR0TAQH/BAIwADAf -BgNVHSMEGDAWgBQXRlWLK295lmDy+931a4Ha8XVNNjAsBggrBgEFBQcBAQQgMB4w -HAYIKwYBBQUHMAGGEG9jc3AuZXhhbXBsZS5jb20wCgYIKoZIzj0EAwIDSAAwRQIh -AJMF7RkU0BtNZlHf//PPgpPfDJybnYMIoX1Ek4I8JZ+QAiBpxjzeFE9jwqcJnx5X -KnOJMbgfvJliZZgVSuXBbulzAA== ------END CERTIFICATE----- diff --git a/integration/fixtures/ocsp/default.key b/integration/fixtures/ocsp/default.key deleted file mode 100644 index 96471e10d..000000000 --- a/integration/fixtures/ocsp/default.key +++ /dev/null @@ -1,5 +0,0 @@ ------BEGIN EC PRIVATE KEY----- -MHcCAQEEIO4UluA82wXVkaVH0m6oFGWyC8mzVcc7H9MI0ltXgkNuoAoGCCqGSM49 -AwEHoUQDQgAEBgAWf83s0zEAcJpsN2on1NDpA0OGc8Rhu3Ufd7LyT4GJz+06QVbT -68ctFoFWW2wh79Eq5T5aTvaxZCn9w+BOkA== ------END EC PRIVATE KEY----- diff --git a/integration/fixtures/ocsp/gencert.go b/integration/fixtures/ocsp/gencert.go deleted file mode 100644 index 1c1277641..000000000 --- a/integration/fixtures/ocsp/gencert.go +++ /dev/null @@ -1,100 +0,0 @@ -package main - -import ( - "crypto/ecdsa" - "crypto/elliptic" - "crypto/rand" - "crypto/x509" - "crypto/x509/pkix" - "encoding/pem" - "math/big" - "os" - "time" -) - -func main() { - // generate CA - caKey, caCert := generateCA("Test CA") - saveKeyAndCert("integration/fixtures/ocsp/ca.key", "integration/fixtures/ocsp/ca.crt", caKey, caCert) - - // server certificate - serverKey, serverCert := generateCert("server.local", caKey, caCert) - saveKeyAndCert("integration/fixtures/ocsp/server.key", "integration/fixtures/ocsp/server.crt", serverKey, serverCert) - - // default certificate - defaultKey, defaultCert := generateCert("default.local", caKey, caCert) - saveKeyAndCert("integration/fixtures/ocsp/default.key", "integration/fixtures/ocsp/default.crt", defaultKey, defaultCert) -} - -func generateCA(commonName string) (*ecdsa.PrivateKey, *x509.Certificate) { - // generate a private key for the CA - caKey, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - - // create a self-signed CA certificate - caTemplate := &x509.Certificate{ - SerialNumber: big.NewInt(1), - Subject: pkix.Name{ - CommonName: commonName, - }, - NotBefore: time.Now(), - NotAfter: time.Now().Add(10 * 365 * 24 * time.Hour), // 10 ans - KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign, - BasicConstraintsValid: true, - IsCA: true, - MaxPathLen: 1, - OCSPServer: []string{"ocsp.example.com"}, - } - - caCertDER, _ := x509.CreateCertificate(rand.Reader, caTemplate, caTemplate, &caKey.PublicKey, caKey) - caCert, _ := x509.ParseCertificate(caCertDER) - - return caKey, caCert -} - -func generateCert(commonName string, caKey *ecdsa.PrivateKey, caCert *x509.Certificate) (*ecdsa.PrivateKey, *x509.Certificate) { - // create a private key for the certificate - certKey, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - - // create a certificate signed by the CA - certTemplate := &x509.Certificate{ - SerialNumber: big.NewInt(time.Now().UnixNano()), - Subject: pkix.Name{ - CommonName: commonName, - }, - NotBefore: time.Now(), - NotAfter: time.Now().Add(1 * 365 * 24 * time.Hour), // 1 an - KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment, - BasicConstraintsValid: true, - OCSPServer: []string{"ocsp.example.com"}, - } - - certDER, _ := x509.CreateCertificate(rand.Reader, certTemplate, caCert, &certKey.PublicKey, caKey) - cert, _ := x509.ParseCertificate(certDER) - - return certKey, cert -} - -func saveKeyAndCert(keyFile, certFile string, key *ecdsa.PrivateKey, cert *x509.Certificate) { - // save the private key - keyOut, _ := os.Create(keyFile) - defer keyOut.Close() - - // Marshal the private key to ASN.1 DER format - privateKey, err := x509.MarshalECPrivateKey(key) - if err != nil { - panic(err) - } - - err = pem.Encode(keyOut, &pem.Block{Type: "EC PRIVATE KEY", Bytes: privateKey}) - if err != nil { - panic(err) - } - - // save the certificate - certOut, _ := os.Create(certFile) - defer certOut.Close() - err = pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw}) - if err != nil { - panic(err) - } -} diff --git a/integration/fixtures/ocsp/server.crt b/integration/fixtures/ocsp/server.crt deleted file mode 100644 index 9e05c6e5c..000000000 --- a/integration/fixtures/ocsp/server.crt +++ /dev/null @@ -1,11 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIBjjCCATSgAwIBAgIIGDlFgswgB3AwCgYIKoZIzj0EAwIwEjEQMA4GA1UEAxMH -VGVzdCBDQTAeFw0yNTA0MjQxMzUyMzhaFw0yNjA0MjQxMzUyMzhaMBcxFTATBgNV -BAMTDHNlcnZlci5sb2NhbDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABHpjZoVk -Qh15gTa26KMJfvzfVgGHGicUDg1UYppKAMY83rxSXqRHcVFAFRqWDTgCQRy6hPq+ -6p5OwBziC2X/SOejbzBtMA4GA1UdDwEB/wQEAwIFoDAMBgNVHRMBAf8EAjAAMB8G -A1UdIwQYMBaAFBdGVYsrb3mWYPL73fVrgdrxdU02MCwGCCsGAQUFBwEBBCAwHjAc -BggrBgEFBQcwAYYQb2NzcC5leGFtcGxlLmNvbTAKBggqhkjOPQQDAgNIADBFAiEA -mp5LQixMUFh5h8yF1EtFsi4MKrO+dzD68TqIhq1rKjUCIEbB++M8qO4gtqjv8d06 -AzSLTEfgNCmM574JI46YAKVx ------END CERTIFICATE----- diff --git a/integration/fixtures/ocsp/server.key b/integration/fixtures/ocsp/server.key deleted file mode 100644 index 3cd45737a..000000000 --- a/integration/fixtures/ocsp/server.key +++ /dev/null @@ -1,5 +0,0 @@ ------BEGIN EC PRIVATE KEY----- -MHcCAQEEIFpVKKKxvw6cZe7hwRLHgXIsWiJYUQ66PKzO6iXINUH0oAoGCCqGSM49 -AwEHoUQDQgAEemNmhWRCHXmBNrboowl+/N9WAYcaJxQODVRimkoAxjzevFJepEdx -UUAVGpYNOAJBHLqE+r7qnk7AHOILZf9I5w== ------END EC PRIVATE KEY----- diff --git a/integration/fixtures/ocsp/simple.toml b/integration/fixtures/ocsp/simple.toml deleted file mode 100644 index 13222b694..000000000 --- a/integration/fixtures/ocsp/simple.toml +++ /dev/null @@ -1,27 +0,0 @@ -[global] - checkNewVersion = false - sendAnonymousUsage = false - -[entryPoints] - [entryPoints.web] - address = ":8000" - -[providers.file] - filename = "{{ .SelfFilename }}" - -[ocsp.responderOverrides] - ocsp.example.com = "{{ .ResponderURL }}" - -[log] - level="debug" - -## dynamic configuration ## - -[[tls.certificates]] - certFile = "fixtures/ocsp/server.crt" - keyFile = "fixtures/ocsp/server.key" - -[tls.stores] - [tls.stores.default.defaultCertificate] - certFile = "fixtures/ocsp/default.crt" - keyFile = "fixtures/ocsp/default.key" diff --git a/integration/fixtures/tracing/simple-opentelemetry.toml b/integration/fixtures/tracing/simple-opentelemetry.toml index 61aaebfa8..a8643ebd7 100644 --- a/integration/fixtures/tracing/simple-opentelemetry.toml +++ b/integration/fixtures/tracing/simple-opentelemetry.toml @@ -14,10 +14,6 @@ [entryPoints] [entryPoints.web] address = ":8000" - [entryPoints.web.observability] - traceVerbosity = "detailed" - [entryPoints.web-minimal] - address = ":8001" # Adding metrics to confirm that there is no wrong interaction with tracing. [metrics] @@ -48,13 +44,9 @@ ## dynamic configuration ## [http.routers] - [http.routers.routerBasicMinimal] - Service = "service0" - Rule = "Path(`/basic-minimal`)" - [http.routers.routerBasicMinimal.observability] - traceVerbosity = "minimal" [http.routers.router0] Service = "service0" + Middlewares = [] Rule = "Path(`/basic`)" [http.routers.router1] Service = "service1" diff --git a/integration/grpc_test.go b/integration/grpc_test.go index a624310f2..856b1980d 100644 --- a/integration/grpc_test.go +++ b/integration/grpc_test.go @@ -108,9 +108,7 @@ func getHelloClientGRPCh2c() (helloworld.GreeterClient, func() error, error) { return helloworld.NewGreeterClient(conn), conn.Close, nil } -func callHelloClientGRPC(t *testing.T, name string, secure bool) (string, error) { - t.Helper() - +func callHelloClientGRPC(name string, secure bool) (string, error) { var client helloworld.GreeterClient var closer func() error var err error @@ -125,26 +123,24 @@ func callHelloClientGRPC(t *testing.T, name string, secure bool) (string, error) if err != nil { return "", err } - r, err := client.SayHello(t.Context(), &helloworld.HelloRequest{Name: name}) + r, err := client.SayHello(context.Background(), &helloworld.HelloRequest{Name: name}) if err != nil { return "", err } return r.GetMessage(), nil } -func callStreamExampleClientGRPC(t *testing.T) (helloworld.Greeter_StreamExampleClient, func() error, error) { - t.Helper() - +func callStreamExampleClientGRPC() (helloworld.Greeter_StreamExampleClient, func() error, error) { client, closer, err := getHelloClientGRPC() if err != nil { return nil, closer, err } - s, err := client.StreamExample(t.Context(), &helloworld.StreamExampleRequest{}) + t, err := client.StreamExample(context.Background(), &helloworld.StreamExampleRequest{}) if err != nil { return nil, closer, err } - return s, closer, nil + return t, closer, nil } func (s *GRPCSuite) TestGRPC() { @@ -176,7 +172,7 @@ func (s *GRPCSuite) TestGRPC() { var response string err = try.Do(1*time.Second, func() error { - response, err = callHelloClientGRPC(s.T(), "World", true) + response, err = callHelloClientGRPC("World", true) return err }) assert.NoError(s.T(), err) @@ -208,7 +204,7 @@ func (s *GRPCSuite) TestGRPCh2c() { var response string err = try.Do(1*time.Second, func() error { - response, err = callHelloClientGRPC(s.T(), "World", false) + response, err = callHelloClientGRPC("World", false) return err }) assert.NoError(s.T(), err) @@ -244,7 +240,7 @@ func (s *GRPCSuite) TestGRPCh2cTermination() { var response string err = try.Do(1*time.Second, func() error { - response, err = callHelloClientGRPC(s.T(), "World", true) + response, err = callHelloClientGRPC("World", true) return err }) assert.NoError(s.T(), err) @@ -280,7 +276,7 @@ func (s *GRPCSuite) TestGRPCInsecure() { var response string err = try.Do(1*time.Second, func() error { - response, err = callHelloClientGRPC(s.T(), "World", true) + response, err = callHelloClientGRPC("World", true) return err }) assert.NoError(s.T(), err) @@ -318,7 +314,7 @@ func (s *GRPCSuite) TestGRPCBuffer() { err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("Host(`127.0.0.1`)")) assert.NoError(s.T(), err) var client helloworld.Greeter_StreamExampleClient - client, closer, err := callStreamExampleClientGRPC(s.T()) + client, closer, err := callStreamExampleClientGRPC() defer func() { _ = closer() }() assert.NoError(s.T(), err) @@ -371,7 +367,7 @@ func (s *GRPCSuite) TestGRPCBufferWithFlushInterval() { assert.NoError(s.T(), err) var client helloworld.Greeter_StreamExampleClient - client, closer, err := callStreamExampleClientGRPC(s.T()) + client, closer, err := callStreamExampleClientGRPC() defer func() { _ = closer() stopStreamExample <- true @@ -426,7 +422,7 @@ func (s *GRPCSuite) TestGRPCWithRetry() { var response string err = try.Do(1*time.Second, func() error { - response, err = callHelloClientGRPC(s.T(), "World", true) + response, err = callHelloClientGRPC("World", true) return err }) assert.NoError(s.T(), err) diff --git a/integration/integration_test.go b/integration/integration_test.go index bb0fc2bfd..039f1aae4 100644 --- a/integration/integration_test.go +++ b/integration/integration_test.go @@ -106,7 +106,7 @@ func (s *BaseSuite) displayTraefikLogFile(path string) { } func (s *BaseSuite) SetupSuite() { - if isDockerDesktop(s.T()) { + if isDockerDesktop(context.Background(), s.T()) { _, err := os.Stat(tailscaleSecretFilePath) require.NoError(s.T(), err, "Tailscale need to be configured when running integration tests with Docker Desktop: (https://doc.traefik.io/traefik/v2.11/contributing/building-testing/#testing)") } @@ -116,6 +116,7 @@ func (s *BaseSuite) SetupSuite() { // TODO // stdlog.SetOutput(log.Logger) + ctx := context.Background() // Create docker network // docker network create traefik-test-network --driver bridge --subnet 172.31.42.0/24 var opts []network.NetworkCustomizer @@ -128,18 +129,18 @@ func (s *BaseSuite) SetupSuite() { }, }, })) - dockerNetwork, err := network.New(s.T().Context(), opts...) + dockerNetwork, err := network.New(ctx, opts...) require.NoError(s.T(), err) s.network = dockerNetwork s.hostIP = "172.31.42.1" - if isDockerDesktop(s.T()) { - s.hostIP = getDockerDesktopHostIP(s.T()) + if isDockerDesktop(ctx, s.T()) { + s.hostIP = getDockerDesktopHostIP(ctx, s.T()) s.setupVPN(tailscaleSecretFilePath) } } -func getDockerDesktopHostIP(t *testing.T) string { +func getDockerDesktopHostIP(ctx context.Context, t *testing.T) string { t.Helper() req := testcontainers.ContainerRequest{ @@ -150,13 +151,13 @@ func getDockerDesktopHostIP(t *testing.T) string { Cmd: []string{"getent", "hosts", "host.docker.internal"}, } - con, err := testcontainers.GenericContainer(t.Context(), testcontainers.GenericContainerRequest{ + con, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ ContainerRequest: req, Started: true, }) require.NoError(t, err) - closer, err := con.Logs(t.Context()) + closer, err := con.Logs(ctx) require.NoError(t, err) all, err := io.ReadAll(closer) @@ -169,15 +170,15 @@ func getDockerDesktopHostIP(t *testing.T) string { return matches[0] } -func isDockerDesktop(t *testing.T) bool { +func isDockerDesktop(ctx context.Context, t *testing.T) bool { t.Helper() - cli, err := testcontainers.NewDockerClientWithOpts(t.Context()) + cli, err := testcontainers.NewDockerClientWithOpts(ctx) if err != nil { t.Fatalf("failed to create docker client: %s", err) } - info, err := cli.Info(t.Context()) + info, err := cli.Info(ctx) if err != nil { t.Fatalf("failed to get docker info: %s", err) } @@ -190,7 +191,7 @@ func (s *BaseSuite) TearDownSuite() { err := try.Do(5*time.Second, func() error { if s.network != nil { - err := s.network.Remove(s.T().Context()) + err := s.network.Remove(context.Background()) if err != nil { return err } @@ -217,7 +218,7 @@ func (s *BaseSuite) createComposeProject(name string) { s.containers = map[string]testcontainers.Container{} } - ctx := s.T().Context() + ctx := context.Background() for id, containerConfig := range composeConfigData.Services { var mounts []mount.Mount @@ -272,7 +273,7 @@ func (s *BaseSuite) createContainer(ctx context.Context, containerConfig compose if containerConfig.CapAdd != nil { config.CapAdd = containerConfig.CapAdd } - if !isDockerDesktop(s.T()) { + if !isDockerDesktop(ctx, s.T()) { config.ExtraHosts = append(config.ExtraHosts, "host.docker.internal:"+s.hostIP) } config.Mounts = mounts @@ -291,7 +292,7 @@ func (s *BaseSuite) createContainer(ctx context.Context, containerConfig compose func (s *BaseSuite) composeUp(services ...string) { for name, con := range s.containers { if len(services) == 0 || slices.Contains(services, name) { - err := con.Start(s.T().Context()) + err := con.Start(context.Background()) require.NoError(s.T(), err) } } @@ -302,7 +303,7 @@ func (s *BaseSuite) composeStop(services ...string) { for name, con := range s.containers { if len(services) == 0 || slices.Contains(services, name) { timeout := 10 * time.Second - err := con.Stop(s.T().Context(), &timeout) + err := con.Stop(context.Background(), &timeout) require.NoError(s.T(), err) } } @@ -311,7 +312,7 @@ func (s *BaseSuite) composeStop(services ...string) { // composeDown stops all compose project services and removes the corresponding containers. func (s *BaseSuite) composeDown() { for _, c := range s.containers { - err := c.Terminate(s.T().Context()) + err := c.Terminate(context.Background()) require.NoError(s.T(), err) } s.containers = map[string]testcontainers.Container{} @@ -382,7 +383,7 @@ func (s *BaseSuite) displayLogK3S() { func (s *BaseSuite) displayLogCompose() { for name, ctn := range s.containers { - readCloser, err := ctn.Logs(s.T().Context()) + readCloser, err := ctn.Logs(context.Background()) require.NoError(s.T(), err) for { b := make([]byte, 1024) @@ -450,7 +451,7 @@ func (s *BaseSuite) getComposeServiceIP(name string) string { if !ok { return "" } - ip, err := container.ContainerIP(s.T().Context()) + ip, err := container.ContainerIP(context.Background()) if err != nil { return "" } @@ -500,7 +501,7 @@ func (s *BaseSuite) setupVPN(keyFile string) { func (s *BaseSuite) composeExec(service string, args ...string) string { require.Contains(s.T(), s.containers, service) - _, reader, err := s.containers[service].Exec(s.T().Context(), args) + _, reader, err := s.containers[service].Exec(context.Background(), args) require.NoError(s.T(), err) content, err := io.ReadAll(reader) diff --git a/integration/k8s_conformance_test.go b/integration/k8s_conformance_test.go index 1e03f223b..959443be6 100644 --- a/integration/k8s_conformance_test.go +++ b/integration/k8s_conformance_test.go @@ -1,6 +1,7 @@ package integration import ( + "context" "fmt" "io" "io/fs" @@ -73,7 +74,7 @@ func (s *K8sConformanceSuite) SetupSuite() { s.T().Fatal(err) } - ctx := s.T().Context() + ctx := context.Background() // Ensure image is available locally. images, err := provider.ListImages(ctx) @@ -89,7 +90,7 @@ func (s *K8sConformanceSuite) SetupSuite() { s.k3sContainer, err = k3s.Run(ctx, k3sImage, - k3s.WithManifest("./fixtures/k8s-conformance/00-experimental-v1.3.0.yml"), + k3s.WithManifest("./fixtures/k8s-conformance/00-experimental-v1.2.1.yml"), k3s.WithManifest("./fixtures/k8s-conformance/01-rbac.yml"), k3s.WithManifest("./fixtures/k8s-conformance/02-traefik.yml"), network.WithNetwork(nil, s.network), @@ -145,7 +146,7 @@ func (s *K8sConformanceSuite) SetupSuite() { } func (s *K8sConformanceSuite) TearDownSuite() { - ctx := s.T().Context() + ctx := context.Background() if s.T().Failed() || *showLog { k3sLogs, err := s.k3sContainer.Logs(ctx) @@ -172,7 +173,7 @@ func (s *K8sConformanceSuite) TearDownSuite() { func (s *K8sConformanceSuite) TestK8sGatewayAPIConformance() { // Wait for traefik to start - k3sContainerIP, err := s.k3sContainer.ContainerIP(s.T().Context()) + k3sContainerIP, err := s.k3sContainer.ContainerIP(context.Background()) require.NoError(s.T(), err) err = try.GetRequest("http://"+k3sContainerIP+":9000/api/entrypoints", 10*time.Second, try.BodyContains(`"name":"web"`)) diff --git a/integration/redis_sentinel_test.go b/integration/redis_sentinel_test.go index 542a63728..e229ceb60 100644 --- a/integration/redis_sentinel_test.go +++ b/integration/redis_sentinel_test.go @@ -2,6 +2,7 @@ package integration import ( "bytes" + "context" "encoding/json" "fmt" "net" @@ -50,7 +51,7 @@ func (s *RedisSentinelSuite) SetupSuite() { net.JoinHostPort(s.getComposeServiceIP("sentinel3"), "26379"), } kv, err := valkeyrie.NewStore( - s.T().Context(), + context.Background(), redis.StoreName, s.redisEndpoints, &redis.Config{ @@ -156,7 +157,7 @@ func (s *RedisSentinelSuite) TestSentinelConfiguration() { } for k, v := range data { - err := s.kvClient.Put(s.T().Context(), k, []byte(v), nil) + err := s.kvClient.Put(context.Background(), k, []byte(v), nil) require.NoError(s.T(), err) } diff --git a/integration/redis_test.go b/integration/redis_test.go index e1ed10899..6685b7645 100644 --- a/integration/redis_test.go +++ b/integration/redis_test.go @@ -2,6 +2,7 @@ package integration import ( "bytes" + "context" "encoding/json" "net" "net/http" @@ -42,7 +43,7 @@ func (s *RedisSuite) SetupSuite() { s.redisEndpoints = append(s.redisEndpoints, net.JoinHostPort(s.getComposeServiceIP("redis"), "6379")) kv, err := valkeyrie.NewStore( - s.T().Context(), + context.Background(), redis.StoreName, s.redisEndpoints, &redis.Config{}, @@ -111,7 +112,7 @@ func (s *RedisSuite) TestSimpleConfiguration() { } for k, v := range data { - err := s.kvClient.Put(s.T().Context(), k, []byte(v), nil) + err := s.kvClient.Put(context.Background(), k, []byte(v), nil) require.NoError(s.T(), err) } diff --git a/integration/resources/compose/access_log.yml b/integration/resources/compose/access_log.yml index 1447f4cba..c5be8f9e4 100644 --- a/integration/resources/compose/access_log.yml +++ b/integration/resources/compose/access_log.yml @@ -1,3 +1,4 @@ +version: "3.8" services: server0: image: traefik/whoami @@ -101,3 +102,13 @@ services: traefik.http.routers.ping.entryPoints: ping traefik.http.routers.ping.rule: PathPrefix(`/ping`) traefik.http.routers.ping.service: ping@internal + + traefik.http.routers.ping-error.entryPoints: ping + traefik.http.routers.ping-error.rule: PathPrefix(`/ping-error`) + traefik.http.routers.ping-error.middlewares: errors, basicauth + traefik.http.routers.ping-error.service: ping@internal + traefik.http.middlewares.basicauth.basicauth.users: "test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/" + traefik.http.middlewares.errors.errors.status: 401 + traefik.http.middlewares.errors.errors.service: service3 + traefik.http.middlewares.errors.errors.query: / + traefik.http.services.service3.loadbalancer.server.port: 80 diff --git a/integration/resources/compose/allowlist.yml b/integration/resources/compose/allowlist.yml index f5cd10680..0fd241322 100644 --- a/integration/resources/compose/allowlist.yml +++ b/integration/resources/compose/allowlist.yml @@ -1,3 +1,4 @@ +version: "3.8" services: noOverrideAllowlist: image: traefik/whoami diff --git a/integration/resources/compose/base.yml b/integration/resources/compose/base.yml index 54a5f2f8e..2d6380051 100644 --- a/integration/resources/compose/base.yml +++ b/integration/resources/compose/base.yml @@ -1,3 +1,4 @@ +version: "3.8" services: whoami1: image: traefik/whoami diff --git a/integration/resources/compose/consul.yml b/integration/resources/compose/consul.yml index 0d1f343ba..041ba0b45 100644 --- a/integration/resources/compose/consul.yml +++ b/integration/resources/compose/consul.yml @@ -1,3 +1,4 @@ +version: "3.8" services: consul: image: consul:1.6 diff --git a/integration/resources/compose/consul_catalog.yml b/integration/resources/compose/consul_catalog.yml index 949333a39..6a0fd279c 100644 --- a/integration/resources/compose/consul_catalog.yml +++ b/integration/resources/compose/consul_catalog.yml @@ -1,3 +1,4 @@ +version: "3.8" services: consul: image: consul:1.6.2 diff --git a/integration/resources/compose/docker.yml b/integration/resources/compose/docker.yml index 7ee4492cf..b16571a4b 100644 --- a/integration/resources/compose/docker.yml +++ b/integration/resources/compose/docker.yml @@ -1,3 +1,4 @@ +version: "3.8" services: simple: image: swarm:1.0.0 diff --git a/integration/resources/compose/error_pages.yml b/integration/resources/compose/error_pages.yml index 3f2c40e6b..03cc13f80 100644 --- a/integration/resources/compose/error_pages.yml +++ b/integration/resources/compose/error_pages.yml @@ -1,3 +1,4 @@ +version: "3.8" services: nginx1: image: nginx:1.25.3-alpine3.18 diff --git a/integration/resources/compose/etcd.yml b/integration/resources/compose/etcd.yml index 02529171a..6a3b34fe9 100644 --- a/integration/resources/compose/etcd.yml +++ b/integration/resources/compose/etcd.yml @@ -1,3 +1,4 @@ +version: "3.8" services: etcd: image: quay.io/coreos/etcd:v3.5.14 diff --git a/integration/resources/compose/file.yml b/integration/resources/compose/file.yml index 3e37129b2..52e973e53 100644 --- a/integration/resources/compose/file.yml +++ b/integration/resources/compose/file.yml @@ -1,3 +1,4 @@ +version: "3.8" services: whoami1: image: traefik/whoami diff --git a/integration/resources/compose/healthcheck.yml b/integration/resources/compose/healthcheck.yml index ee9be620f..9419f4bce 100644 --- a/integration/resources/compose/healthcheck.yml +++ b/integration/resources/compose/healthcheck.yml @@ -1,3 +1,4 @@ +version: "3.8" services: whoami1: image: traefik/whoami diff --git a/integration/resources/compose/hostresolver.yml b/integration/resources/compose/hostresolver.yml index 424b832ce..680962006 100644 --- a/integration/resources/compose/hostresolver.yml +++ b/integration/resources/compose/hostresolver.yml @@ -1,3 +1,4 @@ +version: "3.8" services: server1: image: traefik/whoami diff --git a/integration/resources/compose/k8s.yml b/integration/resources/compose/k8s.yml index 51e46b0be..f14b7abdb 100644 --- a/integration/resources/compose/k8s.yml +++ b/integration/resources/compose/k8s.yml @@ -1,3 +1,4 @@ +version: "3.8" services: server: image: rancher/k3s:v1.21.14-k3s1 diff --git a/integration/resources/compose/minimal.yml b/integration/resources/compose/minimal.yml index 911b12d38..acb12804e 100644 --- a/integration/resources/compose/minimal.yml +++ b/integration/resources/compose/minimal.yml @@ -1,3 +1,4 @@ +version: "3.8" services: whoami1: image: traefik/whoami diff --git a/integration/resources/compose/pebble.yml b/integration/resources/compose/pebble.yml index 4e9eac58b..f39dc1378 100644 --- a/integration/resources/compose/pebble.yml +++ b/integration/resources/compose/pebble.yml @@ -1,3 +1,4 @@ +version: "3.8" services: pebble: image: letsencrypt/pebble:v2.3.1 diff --git a/integration/resources/compose/proxy-protocol.yml b/integration/resources/compose/proxy-protocol.yml index 3d8339330..8fa69a9ba 100644 --- a/integration/resources/compose/proxy-protocol.yml +++ b/integration/resources/compose/proxy-protocol.yml @@ -1,3 +1,4 @@ +version: "3.8" services: whoami: image: traefik/whoami diff --git a/integration/resources/compose/ratelimit.yml b/integration/resources/compose/ratelimit.yml index 414aff839..5d9d6ec1e 100644 --- a/integration/resources/compose/ratelimit.yml +++ b/integration/resources/compose/ratelimit.yml @@ -1,3 +1,4 @@ +version: "3.8" services: whoami1: image: traefik/whoami diff --git a/integration/resources/compose/redis.yml b/integration/resources/compose/redis.yml index bc5e657b3..09bbeacad 100644 --- a/integration/resources/compose/redis.yml +++ b/integration/resources/compose/redis.yml @@ -1,3 +1,4 @@ +version: "3.8" services: redis: image: redis:5.0 diff --git a/integration/resources/compose/redis_sentinel.yml b/integration/resources/compose/redis_sentinel.yml index 3f76aa12f..1737c2a1a 100644 --- a/integration/resources/compose/redis_sentinel.yml +++ b/integration/resources/compose/redis_sentinel.yml @@ -1,3 +1,4 @@ +version: "3.8" services: master: image: redis diff --git a/integration/resources/compose/reqacceptgrace.yml b/integration/resources/compose/reqacceptgrace.yml index 3d8339330..8fa69a9ba 100644 --- a/integration/resources/compose/reqacceptgrace.yml +++ b/integration/resources/compose/reqacceptgrace.yml @@ -1,3 +1,4 @@ +version: "3.8" services: whoami: image: traefik/whoami diff --git a/integration/resources/compose/rest.yml b/integration/resources/compose/rest.yml index fd0dedb04..251568165 100644 --- a/integration/resources/compose/rest.yml +++ b/integration/resources/compose/rest.yml @@ -1,3 +1,4 @@ +version: "3.8" services: whoami1: image: traefik/whoami diff --git a/integration/resources/compose/retry.yml b/integration/resources/compose/retry.yml index 3d8339330..8fa69a9ba 100644 --- a/integration/resources/compose/retry.yml +++ b/integration/resources/compose/retry.yml @@ -1,3 +1,4 @@ +version: "3.8" services: whoami: image: traefik/whoami diff --git a/integration/resources/compose/stats.yml b/integration/resources/compose/stats.yml index 206fa5187..599fed311 100644 --- a/integration/resources/compose/stats.yml +++ b/integration/resources/compose/stats.yml @@ -1,3 +1,4 @@ +version: "3.8" services: whoami1: image: traefik/whoami diff --git a/integration/resources/compose/tailscale.yml b/integration/resources/compose/tailscale.yml index 70e5d796c..dbad56561 100644 --- a/integration/resources/compose/tailscale.yml +++ b/integration/resources/compose/tailscale.yml @@ -1,3 +1,4 @@ +version: "3.8" services: tailscaled: hostname: traefik-tests-gw # This will become the tailscale device name diff --git a/integration/resources/compose/tcp.yml b/integration/resources/compose/tcp.yml index 14e2d413c..cd7fc0627 100644 --- a/integration/resources/compose/tcp.yml +++ b/integration/resources/compose/tcp.yml @@ -1,3 +1,4 @@ +version: "3.8" services: whoami-a: image: traefik/whoamitcp diff --git a/integration/resources/compose/timeout.yml b/integration/resources/compose/timeout.yml index de629c4db..6e415b9b0 100644 --- a/integration/resources/compose/timeout.yml +++ b/integration/resources/compose/timeout.yml @@ -1,3 +1,4 @@ +version: "3.8" services: timeoutEndpoint: image: yaman/timeout diff --git a/integration/resources/compose/tlsclientheaders.yml b/integration/resources/compose/tlsclientheaders.yml index ae03ecbae..ef16f5f36 100644 --- a/integration/resources/compose/tlsclientheaders.yml +++ b/integration/resources/compose/tlsclientheaders.yml @@ -1,3 +1,4 @@ +version: "3.8" services: whoami: image: traefik/whoami diff --git a/integration/resources/compose/tracing.yml b/integration/resources/compose/tracing.yml index 8a2a6d514..314dd66dc 100644 --- a/integration/resources/compose/tracing.yml +++ b/integration/resources/compose/tracing.yml @@ -1,3 +1,4 @@ +version: "3.8" services: tempo: hostname: tempo diff --git a/integration/resources/compose/udp.yml b/integration/resources/compose/udp.yml index 0c001c829..ce2633199 100644 --- a/integration/resources/compose/udp.yml +++ b/integration/resources/compose/udp.yml @@ -1,3 +1,4 @@ +version: "3.8" services: whoami-a: image: traefik/whoamiudp:latest diff --git a/integration/resources/compose/whitelist.yml b/integration/resources/compose/whitelist.yml index ee08b935e..790ce52b7 100644 --- a/integration/resources/compose/whitelist.yml +++ b/integration/resources/compose/whitelist.yml @@ -1,3 +1,4 @@ +version: "3.8" services: noOverrideWhitelist: image: traefik/whoami diff --git a/integration/resources/compose/zookeeper.yml b/integration/resources/compose/zookeeper.yml index b086f24f3..9861c1437 100644 --- a/integration/resources/compose/zookeeper.yml +++ b/integration/resources/compose/zookeeper.yml @@ -1,3 +1,4 @@ +version: "3.8" services: zookeeper: image: zookeeper:3.5 diff --git a/integration/simple_test.go b/integration/simple_test.go index 675e21c3d..791ee514c 100644 --- a/integration/simple_test.go +++ b/integration/simple_test.go @@ -3,9 +3,7 @@ package integration import ( "bufio" "bytes" - "crypto" "crypto/rand" - "crypto/tls" "encoding/json" "fmt" "io" @@ -26,7 +24,6 @@ import ( "github.com/stretchr/testify/suite" "github.com/traefik/traefik/v3/integration/try" "github.com/traefik/traefik/v3/pkg/config/dynamic" - "golang.org/x/crypto/ocsp" ) // SimpleSuite tests suite. @@ -1601,132 +1598,6 @@ func (s *SimpleSuite) TestMaxHeaderBytes() { } } -func (s *SimpleSuite) TestSimpleOCSP() { - defaultCert, err := tls.LoadX509KeyPair("fixtures/ocsp/default.crt", "fixtures/ocsp/default.key") - require.NoError(s.T(), err) - - serverCert, err := tls.LoadX509KeyPair("fixtures/ocsp/server.crt", "fixtures/ocsp/server.key") - require.NoError(s.T(), err) - - defaultOCSPResponseTmpl := ocsp.Response{ - SerialNumber: defaultCert.Leaf.SerialNumber, - Status: ocsp.Good, - ThisUpdate: defaultCert.Leaf.NotBefore, - NextUpdate: defaultCert.Leaf.NotAfter, - } - defaultOCSPResponse, err := ocsp.CreateResponse(defaultCert.Leaf, defaultCert.Leaf, defaultOCSPResponseTmpl, defaultCert.PrivateKey.(crypto.Signer)) - require.NoError(s.T(), err) - - serverOCSPResponseTmpl := ocsp.Response{ - SerialNumber: serverCert.Leaf.SerialNumber, - Status: ocsp.Good, - ThisUpdate: serverCert.Leaf.NotBefore, - NextUpdate: serverCert.Leaf.NotAfter, - } - serverOCSPResponse, err := ocsp.CreateResponse(serverCert.Leaf, serverCert.Leaf, serverOCSPResponseTmpl, serverCert.PrivateKey.(crypto.Signer)) - require.NoError(s.T(), err) - - responderCalled := make(chan struct{}) - responder := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { - ct := req.Header.Get("Content-Type") - assert.Equal(s.T(), "application/ocsp-request", ct) - - reqBytes, err := io.ReadAll(req.Body) - require.NoError(s.T(), err) - - ocspReq, err := ocsp.ParseRequest(reqBytes) - require.NoError(s.T(), err) - - var ocspResponse []byte - switch ocspReq.SerialNumber.String() { - case defaultCert.Leaf.SerialNumber.String(): - ocspResponse = defaultOCSPResponse - case serverCert.Leaf.SerialNumber.String(): - ocspResponse = serverOCSPResponse - default: - s.T().Fatalf("Unexpected OCSP request for serial number: %s", ocspReq.SerialNumber) - } - - rw.Header().Set("Content-Type", "application/ocsp-response") - - _, err = rw.Write(ocspResponse) - require.NoError(s.T(), err) - - responderCalled <- struct{}{} - })) - s.T().Cleanup(responder.Close) - - file := s.adaptFile("fixtures/ocsp/simple.toml", struct { - ResponderURL string - }{responder.URL}) - - s.traefikCmd(withConfigFile(file)) - - select { - case <-responderCalled: - case <-time.After(5 * time.Second): - s.T().Fatal("OCSP responder was not called") - } - - select { - case <-responderCalled: - case <-time.After(5 * time.Second): - s.T().Fatal("OCSP responder was not called") - } - - // Check that the response is stapled. - - // Create a TLS client configuration that checks for OCSP stapling for the default cert. - var verifyCallCount int - clientConfig := &tls.Config{ - InsecureSkipVerify: true, - ServerName: "unknown", - VerifyConnection: func(state tls.ConnectionState) error { - s.T().Helper() - - verifyCallCount++ - assert.Equal(s.T(), "default.local", state.PeerCertificates[0].Subject.CommonName) - assert.Equal(s.T(), defaultOCSPResponse, state.OCSPResponse) - return nil - }, - } - - // Connect to the server and verify OCSP stapling. - conn, err := tls.DialWithDialer(&net.Dialer{Timeout: 5 * time.Second}, "tcp", "127.0.0.1:8000", clientConfig) - require.NoError(s.T(), err) - - s.T().Cleanup(func() { - _ = conn.Close() - }) - - assert.Equal(s.T(), 1, verifyCallCount) - - // Create a TLS client configuration that checks for OCSP stapling for a cert in the store. - verifyCallCount = 0 - clientConfig = &tls.Config{ - InsecureSkipVerify: true, - ServerName: "server.local", - VerifyConnection: func(state tls.ConnectionState) error { - s.T().Helper() - - verifyCallCount++ - assert.Equal(s.T(), "server.local", state.PeerCertificates[0].Subject.CommonName) - assert.Equal(s.T(), serverOCSPResponse, state.OCSPResponse) - return nil - }, - } - - // Connect to the server and verify OCSP stapling. - conn, err = tls.DialWithDialer(&net.Dialer{Timeout: 5 * time.Second}, "tcp", "127.0.0.1:8000", clientConfig) - require.NoError(s.T(), err) - - s.T().Cleanup(func() { - _ = conn.Close() - }) - - assert.Equal(s.T(), 1, verifyCallCount) -} - func (s *SimpleSuite) TestSanitizePath() { s.createComposeProject("base") diff --git a/integration/testdata/rawdata-consul.json b/integration/testdata/rawdata-consul.json index 9b27203a7..90806c2ac 100644 --- a/integration/testdata/rawdata-consul.json +++ b/integration/testdata/rawdata-consul.json @@ -14,9 +14,8 @@ "tls": {}, "observability": { "accessLogs": true, - "metrics": true, "tracing": true, - "traceVerbosity": "minimal" + "metrics": true }, "status": "enabled", "using": [ @@ -50,9 +49,8 @@ }, "observability": { "accessLogs": true, - "metrics": true, "tracing": true, - "traceVerbosity": "minimal" + "metrics": true }, "status": "enabled", "using": [ @@ -69,9 +67,8 @@ "priority": 9223372036854775806, "observability": { "accessLogs": true, - "metrics": true, "tracing": true, - "traceVerbosity": "minimal" + "metrics": true }, "status": "enabled", "using": [ @@ -92,9 +89,8 @@ "priority": 9223372036854775805, "observability": { "accessLogs": true, - "metrics": true, "tracing": true, - "traceVerbosity": "minimal" + "metrics": true }, "status": "enabled", "using": [ @@ -104,13 +100,7 @@ }, "middlewares": { "compressor@consul": { - "compress": { - "encodings": [ - "gzip", - "br", - "zstd" - ] - }, + "compress": {}, "status": "enabled", "usedBy": [ "Router0@consul" @@ -183,7 +173,6 @@ "mirror@consul": { "mirroring": { "service": "simplesvc", - "mirrorBody": true, "maxBodySize": -1, "mirrors": [ { diff --git a/integration/testdata/rawdata-crd-label-selector.json b/integration/testdata/rawdata-crd-label-selector.json index 88219a627..8ff23a25a 100644 --- a/integration/testdata/rawdata-crd-label-selector.json +++ b/integration/testdata/rawdata-crd-label-selector.json @@ -9,9 +9,8 @@ "priority": 18, "observability": { "accessLogs": true, - "metrics": true, "tracing": true, - "traceVerbosity": "minimal" + "metrics": true }, "status": "enabled", "using": [ @@ -30,9 +29,8 @@ }, "observability": { "accessLogs": true, - "metrics": true, "tracing": true, - "traceVerbosity": "minimal" + "metrics": true }, "status": "enabled", "using": [ diff --git a/integration/testdata/rawdata-crd.json b/integration/testdata/rawdata-crd.json index 9f1b2d33c..334b6c698 100644 --- a/integration/testdata/rawdata-crd.json +++ b/integration/testdata/rawdata-crd.json @@ -9,9 +9,8 @@ "priority": 18, "observability": { "accessLogs": true, - "metrics": true, "tracing": true, - "traceVerbosity": "minimal" + "metrics": true }, "status": "enabled", "using": [ @@ -30,9 +29,8 @@ }, "observability": { "accessLogs": true, - "metrics": true, "tracing": true, - "traceVerbosity": "minimal" + "metrics": true }, "status": "enabled", "using": [ @@ -51,9 +49,8 @@ "priority": 46, "observability": { "accessLogs": true, - "metrics": true, "tracing": true, - "traceVerbosity": "minimal" + "metrics": true }, "status": "enabled", "using": [ @@ -69,9 +66,8 @@ "priority": 38, "observability": { "accessLogs": true, - "metrics": true, "tracing": true, - "traceVerbosity": "minimal" + "metrics": true }, "status": "enabled", "using": [ @@ -87,9 +83,8 @@ "priority": 50, "observability": { "accessLogs": true, - "metrics": true, "tracing": true, - "traceVerbosity": "minimal" + "metrics": true }, "status": "enabled", "using": [ @@ -105,9 +100,8 @@ "priority": 35, "observability": { "accessLogs": true, - "metrics": true, "tracing": true, - "traceVerbosity": "minimal" + "metrics": true }, "error": [ "the service \"other-ns-wrr3@kubernetescrd\" does not exist" diff --git a/integration/testdata/rawdata-etcd.json b/integration/testdata/rawdata-etcd.json index 360488433..f03d1b67e 100644 --- a/integration/testdata/rawdata-etcd.json +++ b/integration/testdata/rawdata-etcd.json @@ -14,9 +14,8 @@ "tls": {}, "observability": { "accessLogs": true, - "metrics": true, "tracing": true, - "traceVerbosity": "minimal" + "metrics": true }, "status": "enabled", "using": [ @@ -50,9 +49,8 @@ }, "observability": { "accessLogs": true, - "metrics": true, "tracing": true, - "traceVerbosity": "minimal" + "metrics": true }, "status": "enabled", "using": [ @@ -69,9 +67,8 @@ "priority": 9223372036854775806, "observability": { "accessLogs": true, - "metrics": true, "tracing": true, - "traceVerbosity": "minimal" + "metrics": true }, "status": "enabled", "using": [ @@ -92,9 +89,8 @@ "priority": 9223372036854775805, "observability": { "accessLogs": true, - "metrics": true, "tracing": true, - "traceVerbosity": "minimal" + "metrics": true }, "status": "enabled", "using": [ @@ -104,13 +100,7 @@ }, "middlewares": { "compressor@etcd": { - "compress": { - "encodings": [ - "gzip", - "br", - "zstd" - ] - }, + "compress": {}, "status": "enabled", "usedBy": [ "Router0@etcd" @@ -183,7 +173,6 @@ "mirror@etcd": { "mirroring": { "service": "simplesvc", - "mirrorBody": true, "maxBodySize": -1, "mirrors": [ { diff --git a/integration/testdata/rawdata-gateway.json b/integration/testdata/rawdata-gateway.json index 8a6e1bc8c..b6206c121 100644 --- a/integration/testdata/rawdata-gateway.json +++ b/integration/testdata/rawdata-gateway.json @@ -10,9 +10,8 @@ "priority": 9223372036854775806, "observability": { "accessLogs": true, - "metrics": true, "tracing": true, - "traceVerbosity": "minimal" + "metrics": true }, "status": "enabled", "using": [ @@ -33,9 +32,8 @@ "priority": 9223372036854775805, "observability": { "accessLogs": true, - "metrics": true, "tracing": true, - "traceVerbosity": "minimal" + "metrics": true }, "status": "enabled", "using": [ @@ -52,9 +50,8 @@ "priority": 100008, "observability": { "accessLogs": true, - "metrics": true, "tracing": true, - "traceVerbosity": "minimal" + "metrics": true }, "status": "enabled", "using": [ @@ -72,9 +69,8 @@ "tls": {}, "observability": { "accessLogs": true, - "metrics": true, "tracing": true, - "traceVerbosity": "minimal" + "metrics": true }, "status": "enabled", "using": [ diff --git a/integration/testdata/rawdata-ingress-label-selector.json b/integration/testdata/rawdata-ingress-label-selector.json index a4081171c..23305a847 100644 --- a/integration/testdata/rawdata-ingress-label-selector.json +++ b/integration/testdata/rawdata-ingress-label-selector.json @@ -10,9 +10,8 @@ "priority": 9223372036854775806, "observability": { "accessLogs": true, - "metrics": true, "tracing": true, - "traceVerbosity": "minimal" + "metrics": true }, "status": "enabled", "using": [ @@ -33,9 +32,8 @@ "priority": 9223372036854775805, "observability": { "accessLogs": true, - "metrics": true, "tracing": true, - "traceVerbosity": "minimal" + "metrics": true }, "status": "enabled", "using": [ @@ -51,9 +49,8 @@ "priority": 44, "observability": { "accessLogs": true, - "metrics": true, "tracing": true, - "traceVerbosity": "minimal" + "metrics": true }, "status": "enabled", "using": [ diff --git a/integration/testdata/rawdata-ingress.json b/integration/testdata/rawdata-ingress.json index a317099fb..ba2ad706b 100644 --- a/integration/testdata/rawdata-ingress.json +++ b/integration/testdata/rawdata-ingress.json @@ -10,9 +10,8 @@ "priority": 9223372036854775806, "observability": { "accessLogs": true, - "metrics": true, "tracing": true, - "traceVerbosity": "minimal" + "metrics": true }, "status": "enabled", "using": [ @@ -33,9 +32,8 @@ "priority": 9223372036854775805, "observability": { "accessLogs": true, - "metrics": true, "tracing": true, - "traceVerbosity": "minimal" + "metrics": true }, "status": "enabled", "using": [ @@ -51,9 +49,8 @@ "priority": 50, "observability": { "accessLogs": true, - "metrics": true, "tracing": true, - "traceVerbosity": "minimal" + "metrics": true }, "status": "enabled", "using": [ @@ -69,9 +66,8 @@ "priority": 44, "observability": { "accessLogs": true, - "metrics": true, "tracing": true, - "traceVerbosity": "minimal" + "metrics": true }, "status": "enabled", "using": [ @@ -87,9 +83,8 @@ "priority": 47, "observability": { "accessLogs": true, - "metrics": true, "tracing": true, - "traceVerbosity": "minimal" + "metrics": true }, "status": "enabled", "using": [ @@ -105,9 +100,8 @@ "priority": 47, "observability": { "accessLogs": true, - "metrics": true, "tracing": true, - "traceVerbosity": "minimal" + "metrics": true }, "status": "enabled", "using": [ diff --git a/integration/testdata/rawdata-ingressclass-disabled.json b/integration/testdata/rawdata-ingressclass-disabled.json index 5f56a981f..a26369282 100644 --- a/integration/testdata/rawdata-ingressclass-disabled.json +++ b/integration/testdata/rawdata-ingressclass-disabled.json @@ -10,9 +10,8 @@ "priority": 9223372036854775806, "observability": { "accessLogs": true, - "metrics": true, "tracing": true, - "traceVerbosity": "minimal" + "metrics": true }, "status": "enabled", "using": [ @@ -33,9 +32,8 @@ "priority": 9223372036854775805, "observability": { "accessLogs": true, - "metrics": true, "tracing": true, - "traceVerbosity": "minimal" + "metrics": true }, "status": "enabled", "using": [ diff --git a/integration/testdata/rawdata-ingressclass.json b/integration/testdata/rawdata-ingressclass.json index c860c8862..05649bfa1 100644 --- a/integration/testdata/rawdata-ingressclass.json +++ b/integration/testdata/rawdata-ingressclass.json @@ -10,9 +10,8 @@ "priority": 9223372036854775806, "observability": { "accessLogs": true, - "metrics": true, "tracing": true, - "traceVerbosity": "minimal" + "metrics": true }, "status": "enabled", "using": [ @@ -33,9 +32,8 @@ "priority": 9223372036854775805, "observability": { "accessLogs": true, - "metrics": true, "tracing": true, - "traceVerbosity": "minimal" + "metrics": true }, "status": "enabled", "using": [ @@ -51,9 +49,8 @@ "priority": 47, "observability": { "accessLogs": true, - "metrics": true, "tracing": true, - "traceVerbosity": "minimal" + "metrics": true }, "status": "enabled", "using": [ diff --git a/integration/testdata/rawdata-redis.json b/integration/testdata/rawdata-redis.json index 84e094947..381d92df5 100644 --- a/integration/testdata/rawdata-redis.json +++ b/integration/testdata/rawdata-redis.json @@ -14,9 +14,8 @@ "tls": {}, "observability": { "accessLogs": true, - "metrics": true, "tracing": true, - "traceVerbosity": "minimal" + "metrics": true }, "status": "enabled", "using": [ @@ -50,9 +49,8 @@ }, "observability": { "accessLogs": true, - "metrics": true, "tracing": true, - "traceVerbosity": "minimal" + "metrics": true }, "status": "enabled", "using": [ @@ -69,9 +67,8 @@ "priority": 9223372036854775806, "observability": { "accessLogs": true, - "metrics": true, "tracing": true, - "traceVerbosity": "minimal" + "metrics": true }, "status": "enabled", "using": [ @@ -92,9 +89,8 @@ "priority": 9223372036854775805, "observability": { "accessLogs": true, - "metrics": true, "tracing": true, - "traceVerbosity": "minimal" + "metrics": true }, "status": "enabled", "using": [ @@ -104,13 +100,7 @@ }, "middlewares": { "compressor@redis": { - "compress": { - "encodings": [ - "gzip", - "br", - "zstd" - ] - }, + "compress": {}, "status": "enabled", "usedBy": [ "Router0@redis" @@ -183,7 +173,6 @@ "mirror@redis": { "mirroring": { "service": "simplesvc", - "mirrorBody": true, "maxBodySize": -1, "mirrors": [ { @@ -255,7 +244,6 @@ "url": "http://10.0.1.3:8889" } ], - "strategy": "wrr", "passHostHeader": true, "responseForwarding": { "flushInterval": "100ms" diff --git a/integration/testdata/rawdata-zk.json b/integration/testdata/rawdata-zk.json index 3401d4c32..265afbdbc 100644 --- a/integration/testdata/rawdata-zk.json +++ b/integration/testdata/rawdata-zk.json @@ -14,9 +14,8 @@ "tls": {}, "observability": { "accessLogs": true, - "metrics": true, "tracing": true, - "traceVerbosity": "minimal" + "metrics": true }, "status": "enabled", "using": [ @@ -50,9 +49,8 @@ }, "observability": { "accessLogs": true, - "metrics": true, "tracing": true, - "traceVerbosity": "minimal" + "metrics": true }, "status": "enabled", "using": [ @@ -69,9 +67,8 @@ "priority": 9223372036854775806, "observability": { "accessLogs": true, - "metrics": true, "tracing": true, - "traceVerbosity": "minimal" + "metrics": true }, "status": "enabled", "using": [ @@ -92,9 +89,8 @@ "priority": 9223372036854775805, "observability": { "accessLogs": true, - "metrics": true, "tracing": true, - "traceVerbosity": "minimal" + "metrics": true }, "status": "enabled", "using": [ @@ -104,13 +100,7 @@ }, "middlewares": { "compressor@zookeeper": { - "compress": { - "encodings": [ - "gzip", - "br", - "zstd" - ] - }, + "compress": {}, "status": "enabled", "usedBy": [ "Router0@zookeeper" @@ -183,7 +173,6 @@ "mirror@zookeeper": { "mirroring": { "service": "simplesvc", - "mirrorBody": true, "maxBodySize": -1, "mirrors": [ { diff --git a/integration/tracing_test.go b/integration/tracing_test.go index 71e0b1b36..d44ed0d0b 100644 --- a/integration/tracing_test.go +++ b/integration/tracing_test.go @@ -77,104 +77,6 @@ func (s *TracingSuite) TearDownTest() { s.composeStop("tempo") } -func (s *TracingSuite) TestOpenTelemetryBasic_HTTP_router_minimalVerbosity() { - file := s.adaptFile("fixtures/tracing/simple-opentelemetry.toml", TracingTemplate{ - WhoamiIP: s.whoamiIP, - WhoamiPort: s.whoamiPort, - IP: s.otelCollectorIP, - IsHTTP: true, - }) - - s.traefikCmd(withConfigFile(file)) - - // wait for traefik - err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", time.Second, try.BodyContains("basic-auth")) - require.NoError(s.T(), err) - - err = try.GetRequest("http://127.0.0.1:8000/basic-minimal", 500*time.Millisecond, try.StatusCodeIs(http.StatusOK)) - require.NoError(s.T(), err) - - contains := []map[string]string{ - { - "batches.0.scopeSpans.0.scope.name": "github.com/traefik/traefik", - - "batches.0.scopeSpans.0.spans.0.name": "ReverseProxy", - "batches.0.scopeSpans.0.spans.0.kind": "SPAN_KIND_CLIENT", - "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"http.request.method\").value.stringValue": "GET", - "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"network.protocol.version\").value.stringValue": "1.1", - "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"url.full\").value.stringValue": fmt.Sprintf("http://%s/basic-minimal", net.JoinHostPort(s.whoamiIP, "80")), - "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"user_agent.original\").value.stringValue": "Go-http-client/1.1", - "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"network.peer.address\").value.stringValue": s.whoamiIP, - "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"network.peer.port\").value.intValue": "80", - "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"server.address\").value.stringValue": s.whoamiIP, - "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"server.port\").value.intValue": "80", - "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"http.response.status_code\").value.intValue": "200", - - "batches.0.scopeSpans.0.spans.1.name": "GET", - "batches.0.scopeSpans.0.spans.1.kind": "SPAN_KIND_SERVER", - "batches.0.scopeSpans.0.spans.1.attributes.#(key=\"entry_point\").value.stringValue": "web", - "batches.0.scopeSpans.0.spans.1.attributes.#(key=\"http.request.method\").value.stringValue": "GET", - "batches.0.scopeSpans.0.spans.1.attributes.#(key=\"url.path\").value.stringValue": "/basic-minimal", - "batches.0.scopeSpans.0.spans.1.attributes.#(key=\"url.query\").value.stringValue": "", - "batches.0.scopeSpans.0.spans.1.attributes.#(key=\"user_agent.original\").value.stringValue": "Go-http-client/1.1", - "batches.0.scopeSpans.0.spans.1.attributes.#(key=\"server.address\").value.stringValue": "127.0.0.1:8000", - "batches.0.scopeSpans.0.spans.1.attributes.#(key=\"network.peer.address\").value.stringValue": "127.0.0.1", - "batches.0.scopeSpans.0.spans.1.attributes.#(key=\"http.response.status_code\").value.intValue": "200", - }, - } - - s.checkTraceContent(contains) -} - -func (s *TracingSuite) TestOpenTelemetryBasic_HTTP_entrypoint_minimalVerbosity() { - file := s.adaptFile("fixtures/tracing/simple-opentelemetry.toml", TracingTemplate{ - WhoamiIP: s.whoamiIP, - WhoamiPort: s.whoamiPort, - IP: s.otelCollectorIP, - IsHTTP: true, - }) - - s.traefikCmd(withConfigFile(file)) - - // wait for traefik - err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", time.Second, try.BodyContains("basic-auth")) - require.NoError(s.T(), err) - - err = try.GetRequest("http://127.0.0.1:8001/basic", 500*time.Millisecond, try.StatusCodeIs(http.StatusOK)) - require.NoError(s.T(), err) - - contains := []map[string]string{ - { - "batches.0.scopeSpans.0.scope.name": "github.com/traefik/traefik", - - "batches.0.scopeSpans.0.spans.0.name": "ReverseProxy", - "batches.0.scopeSpans.0.spans.0.kind": "SPAN_KIND_CLIENT", - "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"http.request.method\").value.stringValue": "GET", - "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"network.protocol.version\").value.stringValue": "1.1", - "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"url.full\").value.stringValue": fmt.Sprintf("http://%s/basic", net.JoinHostPort(s.whoamiIP, "80")), - "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"user_agent.original\").value.stringValue": "Go-http-client/1.1", - "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"network.peer.address\").value.stringValue": s.whoamiIP, - "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"network.peer.port\").value.intValue": "80", - "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"server.address\").value.stringValue": s.whoamiIP, - "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"server.port\").value.intValue": "80", - "batches.0.scopeSpans.0.spans.0.attributes.#(key=\"http.response.status_code\").value.intValue": "200", - - "batches.0.scopeSpans.0.spans.1.name": "GET", - "batches.0.scopeSpans.0.spans.1.kind": "SPAN_KIND_SERVER", - "batches.0.scopeSpans.0.spans.1.attributes.#(key=\"entry_point\").value.stringValue": "web-minimal", - "batches.0.scopeSpans.0.spans.1.attributes.#(key=\"http.request.method\").value.stringValue": "GET", - "batches.0.scopeSpans.0.spans.1.attributes.#(key=\"url.path\").value.stringValue": "/basic", - "batches.0.scopeSpans.0.spans.1.attributes.#(key=\"url.query\").value.stringValue": "", - "batches.0.scopeSpans.0.spans.1.attributes.#(key=\"user_agent.original\").value.stringValue": "Go-http-client/1.1", - "batches.0.scopeSpans.0.spans.1.attributes.#(key=\"server.address\").value.stringValue": "127.0.0.1:8001", - "batches.0.scopeSpans.0.spans.1.attributes.#(key=\"network.peer.address\").value.stringValue": "127.0.0.1", - "batches.0.scopeSpans.0.spans.1.attributes.#(key=\"http.response.status_code\").value.intValue": "200", - }, - } - - s.checkTraceContent(contains) -} - func (s *TracingSuite) TestOpenTelemetryBasic_HTTP() { file := s.adaptFile("fixtures/tracing/simple-opentelemetry.toml", TracingTemplate{ WhoamiIP: s.whoamiIP, @@ -219,14 +121,14 @@ func (s *TracingSuite) TestOpenTelemetryBasic_HTTP() { "batches.0.scopeSpans.0.spans.3.name": "Router", "batches.0.scopeSpans.0.spans.3.kind": "SPAN_KIND_INTERNAL", "batches.0.scopeSpans.0.spans.3.attributes.#(key=\"traefik.service.name\").value.stringValue": "service0@file", - "batches.0.scopeSpans.0.spans.3.attributes.#(key=\"traefik.router.name\").value.stringValue": "web-router0@file", + "batches.0.scopeSpans.0.spans.3.attributes.#(key=\"traefik.router.name\").value.stringValue": "router0@file", "batches.0.scopeSpans.0.spans.3.attributes.#(key=\"http.route\").value.stringValue": "Path(`/basic`)", "batches.0.scopeSpans.0.spans.4.name": "Metrics", "batches.0.scopeSpans.0.spans.4.kind": "SPAN_KIND_INTERNAL", "batches.0.scopeSpans.0.spans.4.attributes.#(key=\"traefik.middleware.name\").value.stringValue": "metrics-entrypoint", - "batches.0.scopeSpans.0.spans.5.name": "GET", + "batches.0.scopeSpans.0.spans.5.name": "EntryPoint", "batches.0.scopeSpans.0.spans.5.kind": "SPAN_KIND_SERVER", "batches.0.scopeSpans.0.spans.5.attributes.#(key=\"entry_point\").value.stringValue": "web", "batches.0.scopeSpans.0.spans.5.attributes.#(key=\"http.request.method\").value.stringValue": "GET", @@ -287,7 +189,7 @@ func (s *TracingSuite) TestOpenTelemetryBasic_gRPC() { "batches.0.scopeSpans.0.spans.3.name": "Router", "batches.0.scopeSpans.0.spans.3.kind": "SPAN_KIND_INTERNAL", "batches.0.scopeSpans.0.spans.3.attributes.#(key=\"traefik.service.name\").value.stringValue": "service0@file", - "batches.0.scopeSpans.0.spans.3.attributes.#(key=\"traefik.router.name\").value.stringValue": "web-router0@file", + "batches.0.scopeSpans.0.spans.3.attributes.#(key=\"traefik.router.name\").value.stringValue": "router0@file", "batches.0.scopeSpans.0.spans.3.attributes.#(key=\"http.route\").value.stringValue": "Path(`/basic`)", "batches.0.scopeSpans.0.spans.4.name": "Metrics", @@ -349,14 +251,14 @@ func (s *TracingSuite) TestOpenTelemetryRateLimit() { "batches.0.scopeSpans.0.spans.1.name": "Router", "batches.0.scopeSpans.0.spans.1.kind": "SPAN_KIND_INTERNAL", "batches.0.scopeSpans.0.spans.1.attributes.#(key=\"traefik.service.name\").value.stringValue": "service1@file", - "batches.0.scopeSpans.0.spans.1.attributes.#(key=\"traefik.router.name\").value.stringValue": "web-router1@file", + "batches.0.scopeSpans.0.spans.1.attributes.#(key=\"traefik.router.name\").value.stringValue": "router1@file", "batches.0.scopeSpans.0.spans.1.attributes.#(key=\"http.route\").value.stringValue": "Path(`/ratelimit`)", "batches.0.scopeSpans.0.spans.2.name": "Metrics", "batches.0.scopeSpans.0.spans.2.kind": "SPAN_KIND_INTERNAL", "batches.0.scopeSpans.0.spans.2.attributes.#(key=\"traefik.middleware.name\").value.stringValue": "metrics-entrypoint", - "batches.0.scopeSpans.0.spans.3.name": "GET", + "batches.0.scopeSpans.0.spans.3.name": "EntryPoint", "batches.0.scopeSpans.0.spans.3.kind": "SPAN_KIND_SERVER", "batches.0.scopeSpans.0.spans.3.attributes.#(key=\"entry_point\").value.stringValue": "web", "batches.0.scopeSpans.0.spans.3.attributes.#(key=\"http.request.method\").value.stringValue": "GET", @@ -397,14 +299,14 @@ func (s *TracingSuite) TestOpenTelemetryRateLimit() { "batches.0.scopeSpans.0.spans.4.name": "Router", "batches.0.scopeSpans.0.spans.4.kind": "SPAN_KIND_INTERNAL", "batches.0.scopeSpans.0.spans.4.attributes.#(key=\"traefik.service.name\").value.stringValue": "service1@file", - "batches.0.scopeSpans.0.spans.4.attributes.#(key=\"traefik.router.name\").value.stringValue": "web-router1@file", + "batches.0.scopeSpans.0.spans.4.attributes.#(key=\"traefik.router.name\").value.stringValue": "router1@file", "batches.0.scopeSpans.0.spans.4.attributes.#(key=\"http.route\").value.stringValue": "Path(`/ratelimit`)", "batches.0.scopeSpans.0.spans.5.name": "Metrics", "batches.0.scopeSpans.0.spans.5.kind": "SPAN_KIND_INTERNAL", "batches.0.scopeSpans.0.spans.5.attributes.#(key=\"traefik.middleware.name\").value.stringValue": "metrics-entrypoint", - "batches.0.scopeSpans.0.spans.6.name": "GET", + "batches.0.scopeSpans.0.spans.6.name": "EntryPoint", "batches.0.scopeSpans.0.spans.6.kind": "SPAN_KIND_SERVER", "batches.0.scopeSpans.0.spans.6.attributes.#(key=\"entry_point\").value.stringValue": "web", "batches.0.scopeSpans.0.spans.6.attributes.#(key=\"http.request.method\").value.stringValue": "GET", @@ -521,13 +423,13 @@ func (s *TracingSuite) TestOpenTelemetryRetry() { "batches.0.scopeSpans.0.spans.12.name": "Router", "batches.0.scopeSpans.0.spans.12.kind": "SPAN_KIND_INTERNAL", "batches.0.scopeSpans.0.spans.12.attributes.#(key=\"traefik.service.name\").value.stringValue": "service2@file", - "batches.0.scopeSpans.0.spans.12.attributes.#(key=\"traefik.router.name\").value.stringValue": "web-router2@file", + "batches.0.scopeSpans.0.spans.12.attributes.#(key=\"traefik.router.name\").value.stringValue": "router2@file", "batches.0.scopeSpans.0.spans.13.name": "Metrics", "batches.0.scopeSpans.0.spans.13.kind": "SPAN_KIND_INTERNAL", "batches.0.scopeSpans.0.spans.13.attributes.#(key=\"traefik.middleware.name\").value.stringValue": "metrics-entrypoint", - "batches.0.scopeSpans.0.spans.14.name": "GET", + "batches.0.scopeSpans.0.spans.14.name": "EntryPoint", "batches.0.scopeSpans.0.spans.14.kind": "SPAN_KIND_SERVER", "batches.0.scopeSpans.0.spans.14.attributes.#(key=\"entry_point\").value.stringValue": "web", "batches.0.scopeSpans.0.spans.14.attributes.#(key=\"http.request.method\").value.stringValue": "GET", @@ -573,14 +475,14 @@ func (s *TracingSuite) TestOpenTelemetryAuth() { "batches.0.scopeSpans.0.spans.1.name": "Router", "batches.0.scopeSpans.0.spans.1.kind": "SPAN_KIND_INTERNAL", "batches.0.scopeSpans.0.spans.1.attributes.#(key=\"traefik.service.name\").value.stringValue": "service3@file", - "batches.0.scopeSpans.0.spans.1.attributes.#(key=\"traefik.router.name\").value.stringValue": "web-router3@file", + "batches.0.scopeSpans.0.spans.1.attributes.#(key=\"traefik.router.name\").value.stringValue": "router3@file", "batches.0.scopeSpans.0.spans.1.attributes.#(key=\"http.route\").value.stringValue": "Path(`/auth`)", "batches.0.scopeSpans.0.spans.2.name": "Metrics", "batches.0.scopeSpans.0.spans.2.kind": "SPAN_KIND_INTERNAL", "batches.0.scopeSpans.0.spans.2.attributes.#(key=\"traefik.middleware.name\").value.stringValue": "metrics-entrypoint", - "batches.0.scopeSpans.0.spans.3.name": "GET", + "batches.0.scopeSpans.0.spans.3.name": "EntryPoint", "batches.0.scopeSpans.0.spans.3.kind": "SPAN_KIND_SERVER", "batches.0.scopeSpans.0.spans.3.attributes.#(key=\"entry_point\").value.stringValue": "web", "batches.0.scopeSpans.0.spans.3.attributes.#(key=\"http.request.method\").value.stringValue": "GET", @@ -630,14 +532,14 @@ func (s *TracingSuite) TestOpenTelemetryAuthWithRetry() { "batches.0.scopeSpans.0.spans.2.name": "Router", "batches.0.scopeSpans.0.spans.2.kind": "SPAN_KIND_INTERNAL", "batches.0.scopeSpans.0.spans.2.attributes.#(key=\"traefik.service.name\").value.stringValue": "service4@file", - "batches.0.scopeSpans.0.spans.2.attributes.#(key=\"traefik.router.name\").value.stringValue": "web-router4@file", + "batches.0.scopeSpans.0.spans.2.attributes.#(key=\"traefik.router.name\").value.stringValue": "router4@file", "batches.0.scopeSpans.0.spans.2.attributes.#(key=\"http.route\").value.stringValue": "Path(`/retry-auth`)", "batches.0.scopeSpans.0.spans.3.name": "Metrics", "batches.0.scopeSpans.0.spans.3.kind": "SPAN_KIND_INTERNAL", "batches.0.scopeSpans.0.spans.3.attributes.#(key=\"traefik.middleware.name\").value.stringValue": "metrics-entrypoint", - "batches.0.scopeSpans.0.spans.4.name": "GET", + "batches.0.scopeSpans.0.spans.4.name": "EntryPoint", "batches.0.scopeSpans.0.spans.4.kind": "SPAN_KIND_SERVER", "batches.0.scopeSpans.0.spans.4.attributes.#(key=\"entry_point\").value.stringValue": "web", "batches.0.scopeSpans.0.spans.4.attributes.#(key=\"http.request.method\").value.stringValue": "GET", @@ -699,14 +601,14 @@ func (s *TracingSuite) TestOpenTelemetrySafeURL() { "batches.0.scopeSpans.0.spans.4.name": "Router", "batches.0.scopeSpans.0.spans.4.kind": "SPAN_KIND_INTERNAL", "batches.0.scopeSpans.0.spans.4.attributes.#(key=\"traefik.service.name\").value.stringValue": "service3@file", - "batches.0.scopeSpans.0.spans.4.attributes.#(key=\"traefik.router.name\").value.stringValue": "web-router3@file", + "batches.0.scopeSpans.0.spans.4.attributes.#(key=\"traefik.router.name\").value.stringValue": "router3@file", "batches.0.scopeSpans.0.spans.4.attributes.#(key=\"http.route\").value.stringValue": "Path(`/auth`)", "batches.0.scopeSpans.0.spans.5.name": "Metrics", "batches.0.scopeSpans.0.spans.5.kind": "SPAN_KIND_INTERNAL", "batches.0.scopeSpans.0.spans.5.attributes.#(key=\"traefik.middleware.name\").value.stringValue": "metrics-entrypoint", - "batches.0.scopeSpans.0.spans.6.name": "GET", + "batches.0.scopeSpans.0.spans.6.name": "EntryPoint", "batches.0.scopeSpans.0.spans.6.kind": "SPAN_KIND_SERVER", "batches.0.scopeSpans.0.spans.6.attributes.#(key=\"entry_point\").value.stringValue": "web", "batches.0.scopeSpans.0.spans.6.attributes.#(key=\"http.request.method\").value.stringValue": "GET", diff --git a/integration/zk_test.go b/integration/zk_test.go index 8955c530e..f971ca79c 100644 --- a/integration/zk_test.go +++ b/integration/zk_test.go @@ -2,6 +2,7 @@ package integration import ( "bytes" + "context" "encoding/json" "net" "net/http" @@ -42,7 +43,7 @@ func (s *ZookeeperSuite) SetupSuite() { var err error s.kvClient, err = valkeyrie.NewStore( - s.T().Context(), + context.Background(), zookeeper.StoreName, []string{s.zookeeperAddr}, &zookeeper.Config{ @@ -109,7 +110,7 @@ func (s *ZookeeperSuite) TestSimpleConfiguration() { } for k, v := range data { - err := s.kvClient.Put(s.T().Context(), k, []byte(v), nil) + err := s.kvClient.Put(context.Background(), k, []byte(v), nil) require.NoError(s.T(), err) } diff --git a/internal/anchors.go b/internal/anchors.go deleted file mode 100644 index 6e184b97a..000000000 --- a/internal/anchors.go +++ /dev/null @@ -1,281 +0,0 @@ -package main - -import ( - "bufio" - "fmt" - "log" - "os" - "path/filepath" - "regexp" - "strings" -) - -var ( - // detect any existing tag in the cell (case-insensitive). - reExistingAnchor = regexp.MustCompile(`(?i)<\s*a\b[^>]*>.*?`) - // separator cell like --- or :---: (3+ dashes, optional leading/trailing colon). - reSepCell = regexp.MustCompile(`^\s*:?-{3,}:?\s*$`) - // markdown link [text](url) → text (used to strip link wrappers in id). - reMdLink = regexp.MustCompile(`\[(.*?)\]\((.*?)\)`) - // collapse multiple hyphens. - reMultiHyphens = regexp.MustCompile(`-+`) -) - -// splitTableRow splits a markdown table line on pipes, while keeping escaped pipes. -// parts[1] will be the first data cell for lines that start with '|'. -func splitTableRow(line string) []string { - var parts []string - var b strings.Builder - escaped := false - for _, r := range line { - if escaped { - b.WriteRune(r) - escaped = false - continue - } - if r == '\\' { - escaped = true - b.WriteRune(r) - continue - } - if r == '|' { - parts = append(parts, b.String()) - b.Reset() - continue - } - b.WriteRune(r) - } - parts = append(parts, b.String()) - return parts -} - -func isTableRow(line string) bool { - s := strings.TrimSpace(line) - if !strings.HasPrefix(s, "|") { - return false - } - parts := splitTableRow(line) - return len(parts) >= 3 -} - -func isSeparatorRow(line string) bool { - if !isTableRow(line) { - return false - } - parts := splitTableRow(line) - // check all middle cells (skip first and last which are outside pipes) - for i := 1; i < len(parts)-1; i++ { - cell := strings.TrimSpace(parts[i]) - if cell == "" { - continue - } - if !reSepCell.MatchString(cell) { - return false - } - } - return true -} - -// Create ID from cell text, preserving letter case, removing
and markdown decorations. -func makeID(text string) string { - id := strings.TrimSpace(text) - - // remove BR tags (common in table cells) - id = strings.ReplaceAll(id, "
", " ") - id = strings.ReplaceAll(id, "
", " ") - id = strings.ReplaceAll(id, "
", " ") - - // remove the dots - id = strings.ReplaceAll(id, ".", "-") - - // strip markdown link wrappers [text](url) -> text - id = reMdLink.ReplaceAllString(id, "$1") - - // remove inline markdown characters - id = strings.ReplaceAll(id, "`", "") - id = strings.ReplaceAll(id, "*", "") - id = strings.ReplaceAll(id, "~", "") - - // replace spaces/underscores with hyphen - id = strings.ReplaceAll(id, " ", "-") - id = strings.ReplaceAll(id, "_", "-") - - // keep only letters (both cases), digits and hyphens - var clean []rune - for _, r := range id { - if (r >= 'a' && r <= 'z') || - (r >= 'A' && r <= 'Z') || - (r >= '0' && r <= '9') || - r == '-' || r == '.' { - // keep dot as you requested (we won't replace it) - clean = append(clean, r) - } - } - id = string(clean) - - // collapse multiple hyphens and trim - id = reMultiHyphens.ReplaceAllString(id, "-") - id = strings.Trim(id, "-") - if id == "" { - id = "row" - } - return id -} - -// Dedupe ID within a file: if id already seen, append -2, -3... -// Use "opt-" prefix to avoid conflicts with section headings. -func dedupeID(base string, seen map[string]int) string { - if base == "" { - base = "row" - } - - // Add prefix to avoid conflicts with section headings. - optID := "opt-" + base - - count, ok := seen[optID] - if !ok { - seen[optID] = 1 - return optID - } - - seen[optID] = count + 1 - return fmt.Sprintf("%s-%d", optID, count+1) -} - -// Clean existing anchors from cell content. -func cleanExistingAnchors(text string) string { - return reExistingAnchor.ReplaceAllStringFunc(text, func(match string) string { - // Extract content between
tags. - start := strings.Index(match, ">") - end := strings.LastIndex(match, "= 0 && end > start { - return strings.TrimSpace(match[start+1 : end]) - } - return strings.TrimSpace(match) - }) -} - -// Inject clickable link that is also the target (id + href on same element). -func injectClickableFirstCell(line string, seen map[string]int) string { - parts := splitTableRow(line) - // first data cell is index 1 - firstCellRaw := parts[1] - - // Clean any existing anchors first. - firstCellRaw = cleanExistingAnchors(firstCellRaw) - firstTrimmed := strings.TrimSpace(firstCellRaw) - - id := makeID(firstTrimmed) - if id == "" { - return strings.Join(parts, "|") - } - id = dedupeID(id, seen) - - // wrap the visible cell content in a link that is also the target - // keep the cell inner HTML/text (firstCellRaw) as-is inside the - parts[1] = fmt.Sprintf(" %s ", id, id, id, strings.TrimSpace(firstCellRaw)) - return strings.Join(parts, "|") -} - -func processFile(path string) error { - // read file - f, err := os.Open(path) - if err != nil { - return err - } - var lines []string - sc := bufio.NewScanner(f) - for sc.Scan() { - lines = append(lines, sc.Text()) - } - if err := sc.Err(); err != nil { - _ = f.Close() - return err - } - _ = f.Close() - - inFence := false - seen := make(map[string]int) - out := make([]string, len(lines)) - - for i, line := range lines { - trim := strings.TrimSpace(line) - - // toggle code fence (``` or ~~~) - if strings.HasPrefix(trim, "```") || strings.HasPrefix(trim, "~~~") { - inFence = !inFence - out[i] = line - continue - } - if inFence { - out[i] = line - continue - } - - // not a table row -> copy as-is - if !isTableRow(line) { - out[i] = line - continue - } - - // separator row -> copy as-is - if isSeparatorRow(line) { - out[i] = line - continue - } - - // detect header row (the row immediately before a separator) and skip it - isHeader := false - for j := i + 1; j < len(lines); j++ { - if strings.TrimSpace(lines[j]) == "" { - continue - } - if isSeparatorRow(lines[j]) { - isHeader = true - } - break - } - if isHeader { - out[i] = line - continue - } - - // otherwise inject clickable link in first cell - out[i] = injectClickableFirstCell(line, seen) - } - - // overwrite file in place - wf, err := os.Create(path) - if err != nil { - return err - } - bw := bufio.NewWriter(wf) - for _, l := range out { - fmt.Fprintln(bw, l) - } - if err := bw.Flush(); err != nil { - _ = wf.Close() - return err - } - return wf.Close() -} - -func genAnchors() { - root := "./docs/content/reference/" - err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error { - if err != nil { - return err - } - if !info.IsDir() && strings.HasSuffix(strings.ToLower(info.Name()), ".md") { - if perr := processFile(path); perr != nil { - fmt.Printf("âš ī¸ Error processing %s: %v\n", path, perr) - } else { - fmt.Printf("✅ Processed %s\n", path) - } - } - return nil - }) - if err != nil { - log.Fatalf("walk error: %v", err) - } -} diff --git a/internal/gendoc.go b/internal/gendoc.go index 34c9e0002..67eb64003 100644 --- a/internal/gendoc.go +++ b/internal/gendoc.go @@ -5,17 +5,23 @@ import ( "fmt" "io" "os" + "path" + "path/filepath" "reflect" "sort" + "strconv" "strings" "github.com/BurntSushi/toml" "github.com/rs/zerolog/log" + "github.com/traefik/paerser/env" "github.com/traefik/paerser/flag" "github.com/traefik/paerser/generator" + "github.com/traefik/paerser/parser" "github.com/traefik/traefik/v3/cmd" "github.com/traefik/traefik/v3/pkg/collector/hydratation" "github.com/traefik/traefik/v3/pkg/config/dynamic" + "github.com/traefik/traefik/v3/pkg/config/static" "gopkg.in/yaml.v3" ) @@ -24,13 +30,6 @@ var commentGenerated = `## CODE GENERATED AUTOMATICALLY ` func main() { - genRoutingConfDoc() - genInstallConfDoc() - genAnchors() -} - -// Generate the Routing Configuration YAML and TOML files. -func genRoutingConfDoc() { logger := log.With().Logger() dynConf := &dynamic.Configuration{} @@ -47,14 +46,118 @@ func genRoutingConfDoc() { clean(dynConf.TCP.Services) clean(dynConf.UDP.Services) - err = tomlWrite("./docs/content/reference/routing-configuration/other-providers/file.toml", dynConf) + err = tomlWrite("./docs/content/reference/dynamic-configuration/file.toml", dynConf) if err != nil { logger.Fatal().Err(err).Send() } - err = yamlWrite("./docs/content/reference/routing-configuration/other-providers/file.yaml", dynConf) + err = yamlWrite("./docs/content/reference/dynamic-configuration/file.yaml", dynConf) if err != nil { logger.Fatal().Err(err).Send() } + + err = labelsWrite("./docs/content/reference/dynamic-configuration", dynConf) + if err != nil { + logger.Fatal().Err(err).Send() + } + + staticConf := &static.Configuration{} + + err = hydratation.Hydrate(staticConf) + if err != nil { + logger.Fatal().Err(err).Send() + } + + delete(staticConf.EntryPoints, "EntryPoint1") + + err = tomlWrite("./docs/content/reference/static-configuration/file.toml", staticConf) + if err != nil { + logger.Fatal().Err(err).Send() + } + err = yamlWrite("./docs/content/reference/static-configuration/file.yaml", staticConf) + if err != nil { + logger.Fatal().Err(err).Send() + } + + genStaticConfDoc("./docs/content/reference/static-configuration/env-ref.md", "", func(i interface{}) ([]parser.Flat, error) { + return env.Encode(env.DefaultNamePrefix, i) + }) + genStaticConfDoc("./docs/content/reference/static-configuration/cli-ref.md", "--", flag.Encode) + genKVDynConfDoc("./docs/content/reference/dynamic-configuration/kv-ref.md") +} + +func labelsWrite(outputDir string, element *dynamic.Configuration) error { + cleanServers(element) + + etnOpts := parser.EncoderToNodeOpts{OmitEmpty: true, TagName: parser.TagLabel, AllowSliceAsStruct: true} + node, err := parser.EncodeToNode(element, parser.DefaultRootName, etnOpts) + if err != nil { + return err + } + + metaOpts := parser.MetadataOpts{TagName: parser.TagLabel, AllowSliceAsStruct: true} + err = parser.AddMetadata(element, node, metaOpts) + if err != nil { + return err + } + + labels := make(map[string]string) + encodeNode(labels, node.Name, node) + + var keys []string + for k := range labels { + keys = append(keys, k) + } + + sort.Strings(keys) + + dockerLabels, err := os.Create(filepath.Join(outputDir, "docker-labels.yml")) + if err != nil { + return err + } + defer dockerLabels.Close() + + // Write the comment at the beginning of the file + if _, err := dockerLabels.WriteString(commentGenerated); err != nil { + return err + } + + for _, k := range keys { + v := labels[k] + if v != "" { + if v == "42000000000" { + v = "42s" + } + fmt.Fprintln(dockerLabels, `- "`+strings.ToLower(k)+`=`+v+`"`) + } + } + + return nil +} + +func cleanServers(element *dynamic.Configuration) { + for _, svc := range element.HTTP.Services { + if svc.LoadBalancer != nil { + server := svc.LoadBalancer.Servers[0] + svc.LoadBalancer.Servers = nil + svc.LoadBalancer.Servers = append(svc.LoadBalancer.Servers, server) + } + } + + for _, svc := range element.TCP.Services { + if svc.LoadBalancer != nil { + server := svc.LoadBalancer.Servers[0] + svc.LoadBalancer.Servers = nil + svc.LoadBalancer.Servers = append(svc.LoadBalancer.Servers, server) + } + } + + for _, svc := range element.UDP.Services { + if svc.LoadBalancer != nil { + server := svc.LoadBalancer.Servers[0] + svc.LoadBalancer.Servers = nil + svc.LoadBalancer.Servers = append(svc.LoadBalancer.Servers, server) + } + } } func yamlWrite(outputFile string, element any) error { @@ -64,7 +167,7 @@ func yamlWrite(outputFile string, element any) error { } defer file.Close() - // Write the comment at the beginning of the file. + // Write the comment at the beginning of the file if _, err := file.WriteString(commentGenerated); err != nil { return err } @@ -88,7 +191,7 @@ func tomlWrite(outputFile string, element any) error { } defer file.Close() - // Write the comment at the beginning of the file. + // Write the comment at the beginning of the file if _, err := file.WriteString(commentGenerated); err != nil { return err } @@ -126,16 +229,14 @@ func clean(element any) { valSvcs.SetMapIndex(reflect.ValueOf(fmt.Sprintf("%s1", valueSvcRoot.Type().Name())), reflect.Value{}) } -// Generate the Install Configuration in a table. -func genInstallConfDoc() { - outputFile := "./docs/content/reference/install-configuration/configuration-options.md" +func genStaticConfDoc(outputFile, prefix string, encodeFn func(interface{}) ([]parser.Flat, error)) { logger := log.With().Str("file", outputFile).Logger() element := &cmd.NewTraefikConfiguration().Configuration generator.Generate(element) - flats, err := flag.Encode(element) + flats, err := encodeFn(element) if err != nil { logger.Fatal().Err(err).Send() } @@ -158,14 +259,9 @@ func genInstallConfDoc() { CODE GENERATED AUTOMATICALLY THIS FILE MUST NOT BE EDITED BY HAND -->`) - w.writeln(`# Install Configuration Options`) - w.writeln(`## Configuration Options`) + w.writeln() - w.writeln(` -| Field | Description | Default | -|:-------|:------------|:-------|`) - - for _, flat := range flats { + for i, flat := range flats { // TODO must be move into the flats creation. if flat.Name == "experimental.plugins." || flat.Name == "TRAEFIK_EXPERIMENTAL_PLUGINS_" { continue @@ -175,15 +271,21 @@ THIS FILE MUST NOT BE EDITED BY HAND continue } - line := "| " + strings.ReplaceAll(strings.ReplaceAll(flat.Name, "<", "_"), ">", "_") + " | " + flat.Description + " | " - - if flat.Default == "" { - line += "|" + if prefix == "" { + w.writeln("`" + prefix + strings.ReplaceAll(flat.Name, "[0]", "_n") + "`: ") } else { - line += flat.Default + " |" + w.writeln("`" + prefix + strings.ReplaceAll(flat.Name, "[0]", "[n]") + "`: ") } - w.writeln(line) + if flat.Default == "" { + w.writeln(flat.Description) + } else { + w.writeln(flat.Description + " (Default: ```" + flat.Default + "```)") + } + + if i < len(flats)-1 { + w.writeln() + } } if w.err != nil { @@ -203,3 +305,101 @@ func (ew *errWriter) writeln(a ...interface{}) { _, ew.err = fmt.Fprintln(ew.w, a...) } + +func genKVDynConfDoc(outputFile string) { + dynConfPath := "./docs/content/reference/dynamic-configuration/file.toml" + conf := map[string]interface{}{} + _, err := toml.DecodeFile(dynConfPath, &conf) + if err != nil { + log.Fatal().Err(err).Send() + } + + file, err := os.Create(outputFile) + if err != nil { + log.Fatal().Err(err).Send() + } + + store := storeWriter{data: map[string]string{}} + + c := client{store: store} + err = c.load("traefik", conf) + if err != nil { + log.Fatal().Err(err).Send() + } + + var keys []string + for k := range store.data { + keys = append(keys, k) + } + + sort.Strings(keys) + + _, _ = fmt.Fprintf(file, ` +`) + + for _, k := range keys { + _, _ = fmt.Fprintf(file, "| `%s` | `%s` |\n", k, store.data[k]) + } +} + +type storeWriter struct { + data map[string]string +} + +func (f storeWriter) Put(key string, value []byte, _ []string) error { + f.data[key] = string(value) + return nil +} + +type client struct { + store storeWriter +} + +func (c client) load(parentKey string, conf map[string]interface{}) error { + for k, v := range conf { + switch entry := v.(type) { + case map[string]interface{}: + key := path.Join(parentKey, k) + + if len(entry) == 0 { + err := c.store.Put(key, nil, nil) + if err != nil { + return err + } + } else { + err := c.load(key, entry) + if err != nil { + return err + } + } + case []map[string]interface{}: + for i, o := range entry { + key := path.Join(parentKey, k, strconv.Itoa(i)) + + if err := c.load(key, o); err != nil { + return err + } + } + case []interface{}: + for i, o := range entry { + key := path.Join(parentKey, k, strconv.Itoa(i)) + + err := c.store.Put(key, []byte(fmt.Sprintf("%v", o)), nil) + if err != nil { + return err + } + } + default: + key := path.Join(parentKey, k) + + err := c.store.Put(key, []byte(fmt.Sprintf("%v", v)), nil) + if err != nil { + return err + } + } + } + return nil +} diff --git a/internal/parser.go b/internal/parser.go new file mode 100644 index 000000000..3fa85617e --- /dev/null +++ b/internal/parser.go @@ -0,0 +1,66 @@ +package main + +import ( + "fmt" + "reflect" + "strings" + + "github.com/traefik/paerser/parser" +) + +func encodeNode(labels map[string]string, root string, node *parser.Node) { + for _, child := range node.Children { + if child.Disabled { + continue + } + + var sep string + if child.Name[0] != '[' { + sep = "." + } + + childName := root + sep + child.Name + + if child.RawValue != nil { + encodeRawValue(labels, childName, child.RawValue) + continue + } + + if strings.Contains(child.Tag.Get(parser.TagLabel), parser.TagLabelAllowEmpty) { + labels[childName] = "true" + } + + if len(child.Children) > 0 { + encodeNode(labels, childName, child) + } else if len(child.Name) > 0 { + labels[childName] = child.Value + } + } +} + +func encodeRawValue(labels map[string]string, root string, rawValue interface{}) { + if rawValue == nil { + return + } + + tValue := reflect.TypeOf(rawValue) + + if tValue.Kind() == reflect.Map && tValue.Elem().Kind() == reflect.Interface { + r := reflect.ValueOf(rawValue). + Convert(reflect.TypeOf((map[string]interface{})(nil))). + Interface().(map[string]interface{}) + + for k, v := range r { + switch tv := v.(type) { + case string: + labels[root+"."+k] = tv + case []interface{}: + for i, e := range tv { + encodeRawValue(labels, fmt.Sprintf("%s.%s[%d]", root, k, i), e) + } + default: + encodeRawValue(labels, root+"."+k, v) + } + } + } +} diff --git a/internal/testsci/genmatrix.go b/internal/testsci/genmatrix.go deleted file mode 100644 index 01ebdabcd..000000000 --- a/internal/testsci/genmatrix.go +++ /dev/null @@ -1,64 +0,0 @@ -package main - -import ( - "encoding/json" - "fmt" - "os" - "strings" - - "github.com/rs/zerolog/log" - "golang.org/x/tools/go/packages" -) - -const groupCount = 12 - -type group struct { - Group string `json:"group"` -} - -func main() { - cfg := &packages.Config{ - Mode: packages.NeedName, - Dir: ".", - } - - pkgs, err := packages.Load(cfg, "./cmd/...", "./pkg/...") - if err != nil { - log.Fatal().Err(err).Msg("Loading packages") - } - - var packageNames []string - for _, pkg := range pkgs { - if pkg.PkgPath != "" { - packageNames = append(packageNames, pkg.PkgPath) - } - } - - total := len(packageNames) - perGroup := (total + groupCount - 1) / groupCount - - fmt.Fprintf(os.Stderr, "Total packages: %d\n", total) - fmt.Fprintf(os.Stderr, "Packages per group: %d\n", perGroup) - - var matrix []group - for i := range groupCount { - start := i * perGroup - end := start + perGroup - if start >= total { - break - } - if end > total { - end = total - } - g := strings.Join(packageNames[start:end], " ") - matrix = append(matrix, group{Group: g}) - } - - jsonBytes, err := json.Marshal(matrix) - if err != nil { - log.Fatal().Err(err).Msg("Failed to marshal matrix") - } - - // Output for GitHub Actions - fmt.Printf("matrix=%s\n", string(jsonBytes)) -} diff --git a/pkg/api/handler.go b/pkg/api/handler.go index 6c99ca12e..922779e61 100644 --- a/pkg/api/handler.go +++ b/pkg/api/handler.go @@ -156,7 +156,7 @@ func extractType(element interface{}) string { for i := range v.NumField() { field := v.Field(i) - if field.Kind() == reflect.Map && field.Type().Elem() == reflect.TypeFor[dynamic.PluginConf]() { + if field.Kind() == reflect.Map && field.Type().Elem() == reflect.TypeOf(dynamic.PluginConf{}) { if keys := field.MapKeys(); len(keys) == 1 { return keys[0].String() } diff --git a/pkg/api/handler_http_test.go b/pkg/api/handler_http_test.go index e34b1bfe2..715a58676 100644 --- a/pkg/api/handler_http_test.go +++ b/pkg/api/handler_http_test.go @@ -1,6 +1,7 @@ package api import ( + "context" "encoding/json" "fmt" "io" @@ -1003,8 +1004,8 @@ func TestHandler_HTTP(t *testing.T) { rtConf := &test.conf // To lazily initialize the Statuses. rtConf.PopulateUsedBy() - rtConf.GetRoutersByEntryPoints(t.Context(), []string{"web"}, false) - rtConf.GetRoutersByEntryPoints(t.Context(), []string{"web"}, true) + rtConf.GetRoutersByEntryPoints(context.Background(), []string{"web"}, false) + rtConf.GetRoutersByEntryPoints(context.Background(), []string{"web"}, true) handler := New(static.Configuration{API: &static.API{}, Global: &static.Global{}}, rtConf) server := httptest.NewServer(handler.createRouter()) diff --git a/pkg/api/handler_overview.go b/pkg/api/handler_overview.go index 9279370a6..7b15d513e 100644 --- a/pkg/api/handler_overview.go +++ b/pkg/api/handler_overview.go @@ -232,7 +232,7 @@ func getProviders(conf static.Configuration) []string { if !field.IsNil() { providers = append(providers, v.Type().Field(i).Name) } - } else if field.Kind() == reflect.Map && field.Type().Elem() == reflect.TypeFor[static.PluginConf]() { + } else if field.Kind() == reflect.Map && field.Type().Elem() == reflect.TypeOf(static.PluginConf{}) { for _, value := range field.MapKeys() { providers = append(providers, "plugin-"+value.String()) } diff --git a/pkg/api/handler_overview_test.go b/pkg/api/handler_overview_test.go index 7b07106b9..d3ef01f72 100644 --- a/pkg/api/handler_overview_test.go +++ b/pkg/api/handler_overview_test.go @@ -13,12 +13,12 @@ import ( "github.com/traefik/traefik/v3/pkg/config/dynamic" "github.com/traefik/traefik/v3/pkg/config/runtime" "github.com/traefik/traefik/v3/pkg/config/static" - otypes "github.com/traefik/traefik/v3/pkg/observability/types" "github.com/traefik/traefik/v3/pkg/provider/docker" "github.com/traefik/traefik/v3/pkg/provider/file" "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd" "github.com/traefik/traefik/v3/pkg/provider/kubernetes/ingress" "github.com/traefik/traefik/v3/pkg/provider/rest" + "github.com/traefik/traefik/v3/pkg/types" ) func TestHandler_Overview(t *testing.T) { @@ -255,8 +255,8 @@ func TestHandler_Overview(t *testing.T) { confStatic: static.Configuration{ Global: &static.Global{}, API: &static.API{}, - Metrics: &otypes.Metrics{ - Prometheus: &otypes.Prometheus{}, + Metrics: &types.Metrics{ + Prometheus: &types.Prometheus{}, }, Tracing: &static.Tracing{}, }, diff --git a/pkg/api/handler_tcp_test.go b/pkg/api/handler_tcp_test.go index 387c39050..e08897880 100644 --- a/pkg/api/handler_tcp_test.go +++ b/pkg/api/handler_tcp_test.go @@ -1,6 +1,7 @@ package api import ( + "context" "encoding/json" "io" "net/http" @@ -879,7 +880,7 @@ func TestHandler_TCP(t *testing.T) { rtConf := &test.conf // To lazily initialize the Statuses. rtConf.PopulateUsedBy() - rtConf.GetTCPRoutersByEntryPoints(t.Context(), []string{"web"}) + rtConf.GetTCPRoutersByEntryPoints(context.Background(), []string{"web"}) handler := New(static.Configuration{API: &static.API{}, Global: &static.Global{}}, rtConf) server := httptest.NewServer(handler.createRouter()) diff --git a/pkg/api/handler_udp_test.go b/pkg/api/handler_udp_test.go index bd8625dfe..dd3067314 100644 --- a/pkg/api/handler_udp_test.go +++ b/pkg/api/handler_udp_test.go @@ -1,6 +1,7 @@ package api import ( + "context" "encoding/json" "io" "net/http" @@ -569,7 +570,7 @@ func TestHandler_UDP(t *testing.T) { rtConf := &test.conf // To lazily initialize the Statuses. rtConf.PopulateUsedBy() - rtConf.GetUDPRoutersByEntryPoints(t.Context(), []string{"web"}) + rtConf.GetUDPRoutersByEntryPoints(context.Background(), []string{"web"}) handler := New(static.Configuration{API: &static.API{}, Global: &static.Global{}}, rtConf) server := httptest.NewServer(handler.createRouter()) diff --git a/pkg/cli/deprecation.go b/pkg/cli/deprecation.go index c7c262ba7..38b13aba7 100644 --- a/pkg/cli/deprecation.go +++ b/pkg/cli/deprecation.go @@ -195,7 +195,7 @@ func (c *configuration) deprecationNotice(logger zerolog.Logger) bool { if c.Pilot != nil { incompatible = true logger.Error().Msg("Pilot configuration has been removed in v3, please remove all Pilot-related static configuration for Traefik to start." + - " For more information please read the migration guide: https://doc.traefik.io/traefik/v3.5/migration/v2-to-v3/#pilot") + " For more information please read the migration guide: https://doc.traefik.io/traefik/v3.4/migration/v2-to-v3/#pilot") } incompatibleCore := c.Core.deprecationNotice(logger) @@ -213,7 +213,7 @@ func (c *core) deprecationNotice(logger zerolog.Logger) bool { if c != nil && c.DefaultRuleSyntax != "" { logger.Error().Msg("`Core.DefaultRuleSyntax` option has been deprecated in v3.4, and will be removed in the next major version." + " Please consider migrating all router rules to v3 syntax." + - " For more information please read the migration guide: https://doc.traefik.io/traefik/v3.5/migration/v3/#rule-syntax") + " For more information please read the migration guide: https://doc.traefik.io/traefik/v3.4/migration/v3/#rule-syntax") } return false @@ -243,13 +243,13 @@ func (p *providers) deprecationNotice(logger zerolog.Logger) bool { if p.Marathon != nil { incompatible = true logger.Error().Msg("Marathon provider has been removed in v3, please remove all Marathon-related static configuration for Traefik to start." + - " For more information please read the migration guide: https://doc.traefik.io/traefik/v3.5/migration/v2-to-v3/#marathon-provider") + " For more information please read the migration guide: https://doc.traefik.io/traefik/v3.4/migration/v2-to-v3/#marathon-provider") } if p.Rancher != nil { incompatible = true logger.Error().Msg("Rancher provider has been removed in v3, please remove all Rancher-related static configuration for Traefik to start." + - " For more information please read the migration guide: https://doc.traefik.io/traefik/v3.5/migration/v2-to-v3/#rancher-v1-provider") + " For more information please read the migration guide: https://doc.traefik.io/traefik/v3.4/migration/v2-to-v3/#rancher-v1-provider") } dockerIncompatible := p.Docker.deprecationNotice(logger) @@ -291,14 +291,14 @@ func (d *docker) deprecationNotice(logger zerolog.Logger) bool { if d.SwarmMode != nil { incompatible = true logger.Error().Msg("Docker provider `swarmMode` option has been removed in v3, please use the Swarm Provider instead." + - "For more information please read the migration guide: https://doc.traefik.io/traefik/v3.5/migration/v2-to-v3/#docker-docker-swarm") + "For more information please read the migration guide: https://doc.traefik.io/traefik/v3.4/migration/v2-to-v3/#docker-docker-swarm") } if d.TLS != nil && d.TLS.CAOptional != nil { incompatible = true logger.Error().Msg("Docker provider `tls.CAOptional` option has been removed in v3, as TLS client authentication is a server side option (see https://github.com/golang/go/blob/740a490f71d026bb7d2d13cb8fa2d6d6e0572b70/src/crypto/tls/common.go#L634)." + "Please remove all occurrences from the static configuration for Traefik to start." + - "For more information please read the migration guide: https://doc.traefik.io/traefik/v3.5/migration/v2-to-v3/#tlscaoptional") + "For more information please read the migration guide: https://doc.traefik.io/traefik/v3.4/migration/v2-to-v3/#tlscaoptional") } return incompatible @@ -339,7 +339,7 @@ func (e *etcd) deprecationNotice(logger zerolog.Logger) bool { incompatible = true logger.Error().Msg("ETCD provider `tls.CAOptional` option has been removed in v3, as TLS client authentication is a server side option (see https://github.com/golang/go/blob/740a490f71d026bb7d2d13cb8fa2d6d6e0572b70/src/crypto/tls/common.go#L634)." + "Please remove all occurrences from the static configuration for Traefik to start." + - "For more information please read the migration guide: https://doc.traefik.io/traefik/v3.5/migration/v2-to-v3/#tlscaoptional_3") + "For more information please read the migration guide: https://doc.traefik.io/traefik/v3.4/migration/v2-to-v3/#tlscaoptional_3") } return incompatible @@ -360,7 +360,7 @@ func (r *redis) deprecationNotice(logger zerolog.Logger) bool { incompatible = true logger.Error().Msg("Redis provider `tls.CAOptional` option has been removed in v3, as TLS client authentication is a server side option (see https://github.com/golang/go/blob/740a490f71d026bb7d2d13cb8fa2d6d6e0572b70/src/crypto/tls/common.go#L634)." + "Please remove all occurrences from the static configuration for Traefik to start." + - "For more information please read the migration guide: https://doc.traefik.io/traefik/v3.5/migration/v2-to-v3/#tlscaoptional_4") + "For more information please read the migration guide: https://doc.traefik.io/traefik/v3.4/migration/v2-to-v3/#tlscaoptional_4") } return incompatible @@ -381,14 +381,14 @@ func (c *consul) deprecationNotice(logger zerolog.Logger) bool { if c.Namespace != nil { incompatible = true logger.Error().Msg("Consul provider `namespace` option has been removed, please use the `namespaces` option instead." + - "For more information please read the migration guide: https://doc.traefik.io/traefik/v3.5/migration/v2-to-v3/#consul-provider") + "For more information please read the migration guide: https://doc.traefik.io/traefik/v3.4/migration/v2-to-v3/#consul-provider") } if c.TLS != nil && c.TLS.CAOptional != nil { incompatible = true logger.Error().Msg("Consul provider `tls.CAOptional` option has been removed in v3, as TLS client authentication is a server side option (see https://github.com/golang/go/blob/740a490f71d026bb7d2d13cb8fa2d6d6e0572b70/src/crypto/tls/common.go#L634)." + "Please remove all occurrences from the static configuration for Traefik to start." + - "For more information please read the migration guide: https://doc.traefik.io/traefik/v3.5/migration/v2-to-v3/#tlscaoptional_1") + "For more information please read the migration guide: https://doc.traefik.io/traefik/v3.4/migration/v2-to-v3/#tlscaoptional_1") } return incompatible @@ -413,14 +413,14 @@ func (c *consulCatalog) deprecationNotice(logger zerolog.Logger) bool { if c.Namespace != nil { incompatible = true logger.Error().Msg("ConsulCatalog provider `namespace` option has been removed, please use the `namespaces` option instead." + - "For more information please read the migration guide: https://doc.traefik.io/traefik/v3.5/migration/v2-to-v3/#consulcatalog-provider") + "For more information please read the migration guide: https://doc.traefik.io/traefik/v3.4/migration/v2-to-v3/#consulcatalog-provider") } if c.Endpoint != nil && c.Endpoint.TLS != nil && c.Endpoint.TLS.CAOptional != nil { incompatible = true logger.Error().Msg("ConsulCatalog provider `tls.CAOptional` option has been removed in v3, as TLS client authentication is a server side option (see https://github.com/golang/go/blob/740a490f71d026bb7d2d13cb8fa2d6d6e0572b70/src/crypto/tls/common.go#L634)." + "Please remove all occurrences from the static configuration for Traefik to start." + - "For more information please read the migration guide: https://doc.traefik.io/traefik/v3.5/migration/v2-to-v3/#endpointtlscaoptional") + "For more information please read the migration guide: https://doc.traefik.io/traefik/v3.4/migration/v2-to-v3/#endpointtlscaoptional") } return incompatible @@ -441,14 +441,14 @@ func (n *nomad) deprecationNotice(logger zerolog.Logger) bool { if n.Namespace != nil { incompatible = true logger.Error().Msg("Nomad provider `namespace` option has been removed, please use the `namespaces` option instead." + - "For more information please read the migration guide: https://doc.traefik.io/traefik/v3.5/migration/v2-to-v3/#nomad-provider") + "For more information please read the migration guide: https://doc.traefik.io/traefik/v3.4/migration/v2-to-v3/#nomad-provider") } if n.Endpoint != nil && n.Endpoint.TLS != nil && n.Endpoint.TLS.CAOptional != nil { incompatible = true logger.Error().Msg("Nomad provider `tls.CAOptional` option has been removed in v3, as TLS client authentication is a server side option (see https://github.com/golang/go/blob/740a490f71d026bb7d2d13cb8fa2d6d6e0572b70/src/crypto/tls/common.go#L634)." + "Please remove all occurrences from the static configuration for Traefik to start." + - "For more information please read the migration guide: https://doc.traefik.io/traefik/v3.5/migration/v2-to-v3/#endpointtlscaoptional_1") + "For more information please read the migration guide: https://doc.traefik.io/traefik/v3.4/migration/v2-to-v3/#endpointtlscaoptional_1") } return incompatible @@ -469,7 +469,7 @@ func (h *http) deprecationNotice(logger zerolog.Logger) bool { incompatible = true logger.Error().Msg("HTTP provider `tls.CAOptional` option has been removed in v3, as TLS client authentication is a server side option (see https://github.com/golang/go/blob/740a490f71d026bb7d2d13cb8fa2d6d6e0572b70/src/crypto/tls/common.go#L634)." + "Please remove all occurrences from the static configuration for Traefik to start." + - "For more information please read the migration guide: https://doc.traefik.io/traefik/v3.5/migration/v2-to-v3/#tlscaoptional_2") + "For more information please read the migration guide: https://doc.traefik.io/traefik/v3.4/migration/v2-to-v3/#tlscaoptional_2") } return incompatible @@ -487,7 +487,7 @@ func (i *ingress) deprecationNotice(logger zerolog.Logger) { if i.DisableIngressClassLookup != nil { logger.Error().Msg("Kubernetes Ingress provider `disableIngressClassLookup` option has been deprecated in v3.1, and will be removed in the next major version." + "Please use the `disableClusterScopeResources` option instead." + - "For more information please read the migration guide: https://doc.traefik.io/traefik/v3.5/migration/v3/#ingressclasslookup") + "For more information please read the migration guide: https://doc.traefik.io/traefik/v3.4/migration/v3/#ingressclasslookup") } } @@ -504,7 +504,7 @@ func (e *experimental) deprecationNotice(logger zerolog.Logger) bool { if e.HTTP3 != nil { logger.Error().Msg("HTTP3 is not an experimental feature in v3 and the associated enablement has been removed." + "Please remove its usage from the static configuration for Traefik to start." + - "For more information please read the migration guide: https://doc.traefik.io/traefik/v3.5/migration/v2-to-v3-details/#http3") + "For more information please read the migration guide: https://doc.traefik.io/traefik/v3.4/migration/v2-to-v3-details/#http3") return true } @@ -512,7 +512,7 @@ func (e *experimental) deprecationNotice(logger zerolog.Logger) bool { if e.KubernetesGateway != nil { logger.Error().Msg("KubernetesGateway provider is not an experimental feature starting with v3.1." + "Please remove its usage from the static configuration." + - "For more information please read the migration guide: https://doc.traefik.io/traefik/v3.5/migration/v3/#gateway-api-kubernetesgateway-provider") + "For more information please read the migration guide: https://doc.traefik.io/traefik/v3.4/migration/v3/#gateway-api-kubernetesgateway-provider") } return false @@ -539,7 +539,7 @@ func (t *tracing) deprecationNotice(logger zerolog.Logger) bool { if t.SpanNameLimit != nil { incompatible = true logger.Error().Msg("SpanNameLimit option for Tracing has been removed in v3, as Span names are now of a fixed length." + - "For more information please read the migration guide: https://doc.traefik.io/traefik/v3.5/migration/v2-to-v3/#tracing") + "For more information please read the migration guide: https://doc.traefik.io/traefik/v3.4/migration/v2-to-v3/#tracing") } if t.GlobalAttributes != nil { @@ -547,49 +547,49 @@ func (t *tracing) deprecationNotice(logger zerolog.Logger) bool { logger.Error().Msg("`tracing.globalAttributes` option has been deprecated in v3.3, and will be removed in the next major version." + "Please use the `tracing.resourceAttributes` option instead." + - "For more information please read the migration guide: https://doc.traefik.io/traefik/v3.5/migration/v3/#tracing-global-attributes") + "For more information please read the migration guide: https://doc.traefik.io/traefik/v3.4/migration/v3/#tracing-global-attributes") } if t.Jaeger != nil { incompatible = true logger.Error().Msg("Jaeger Tracing backend has been removed in v3, please remove all Jaeger-related Tracing static configuration for Traefik to start." + "In v3, Open Telemetry replaces specific tracing backend implementations, and an collector/exporter can be used to export metrics in a vendor specific format." + - "For more information please read the migration guide: https://doc.traefik.io/traefik/v3.5/migration/v2-to-v3/#tracing") + "For more information please read the migration guide: https://doc.traefik.io/traefik/v3.4/migration/v2-to-v3/#tracing") } if t.Zipkin != nil { incompatible = true logger.Error().Msg("Zipkin Tracing backend has been removed in v3, please remove all Zipkin-related Tracing static configuration for Traefik to start." + "In v3, Open Telemetry replaces specific tracing backend implementations, and an collector/exporter can be used to export metrics in a vendor specific format." + - "For more information please read the migration guide: https://doc.traefik.io/traefik/v3.5/migration/v2-to-v3/#tracing") + "For more information please read the migration guide: https://doc.traefik.io/traefik/v3.4/migration/v2-to-v3/#tracing") } if t.Datadog != nil { incompatible = true logger.Error().Msg("Datadog Tracing backend has been removed in v3, please remove all Datadog-related Tracing static configuration for Traefik to start." + "In v3, Open Telemetry replaces specific tracing backend implementations, and an collector/exporter can be used to export metrics in a vendor specific format." + - "For more information please read the migration guide: https://doc.traefik.io/traefik/v3.5/migration/v2-to-v3/#tracing") + "For more information please read the migration guide: https://doc.traefik.io/traefik/v3.4/migration/v2-to-v3/#tracing") } if t.Instana != nil { incompatible = true logger.Error().Msg("Instana Tracing backend has been removed in v3, please remove all Instana-related Tracing static configuration for Traefik to start." + "In v3, Open Telemetry replaces specific tracing backend implementations, and an collector/exporter can be used to export metrics in a vendor specific format." + - "For more information please read the migration guide: https://doc.traefik.io/traefik/v3.5/migration/v2-to-v3/#tracing") + "For more information please read the migration guide: https://doc.traefik.io/traefik/v3.4/migration/v2-to-v3/#tracing") } if t.Haystack != nil { incompatible = true logger.Error().Msg("Haystack Tracing backend has been removed in v3, please remove all Haystack-related Tracing static configuration for Traefik to start." + "In v3, Open Telemetry replaces specific tracing backend implementations, and an collector/exporter can be used to export metrics in a vendor specific format." + - "For more information please read the migration guide: https://doc.traefik.io/traefik/v3.5/migration/v2-to-v3/#tracing") + "For more information please read the migration guide: https://doc.traefik.io/traefik/v3.4/migration/v2-to-v3/#tracing") } if t.Elastic != nil { incompatible = true logger.Error().Msg("Elastic Tracing backend has been removed in v3, please remove all Elastic-related Tracing static configuration for Traefik to start." + "In v3, Open Telemetry replaces specific tracing backend implementations, and an collector/exporter can be used to export metrics in a vendor specific format." + - "For more information please read the migration guide: https://doc.traefik.io/traefik/v3.5/migration/v2-to-v3/#tracing") + "For more information please read the migration guide: https://doc.traefik.io/traefik/v3.4/migration/v2-to-v3/#tracing") } return incompatible diff --git a/pkg/collector/hydratation/hydration.go b/pkg/collector/hydratation/hydration.go index 74009817a..c45cfcfbb 100644 --- a/pkg/collector/hydratation/hydration.go +++ b/pkg/collector/hydratation/hydration.go @@ -55,7 +55,7 @@ func fill(field reflect.Value) error { setTyped(field, int32(defaultNumber)) case reflect.Int64: switch field.Type() { - case reflect.TypeFor[types.Duration](): + case reflect.TypeOf(types.Duration(time.Second)): setTyped(field, types.Duration(defaultNumber*time.Second)) default: setTyped(field, int64(defaultNumber)) diff --git a/pkg/config/dynamic/http_config.go b/pkg/config/dynamic/http_config.go index 96e16f7b4..d91f60340 100644 --- a/pkg/config/dynamic/http_config.go +++ b/pkg/config/dynamic/http_config.go @@ -5,7 +5,6 @@ import ( "time" ptypes "github.com/traefik/paerser/types" - otypes "github.com/traefik/traefik/v3/pkg/observability/types" traefiktls "github.com/traefik/traefik/v3/pkg/tls" "github.com/traefik/traefik/v3/pkg/types" "google.golang.org/grpc/codes" @@ -89,21 +88,9 @@ type RouterTLSConfig struct { // RouterObservabilityConfig holds the observability configuration for a router. type RouterObservabilityConfig struct { - // AccessLogs enables access logs for this router. AccessLogs *bool `json:"accessLogs,omitempty" toml:"accessLogs,omitempty" yaml:"accessLogs,omitempty" export:"true"` - // Metrics enables metrics for this router. - Metrics *bool `json:"metrics,omitempty" toml:"metrics,omitempty" yaml:"metrics,omitempty" export:"true"` - // Tracing enables tracing for this router. - Tracing *bool `json:"tracing,omitempty" toml:"tracing,omitempty" yaml:"tracing,omitempty" export:"true"` - // TraceVerbosity defines the verbosity level of the tracing for this router. - // +kubebuilder:validation:Enum=minimal;detailed - // +kubebuilder:default=minimal - TraceVerbosity otypes.TracingVerbosity `json:"traceVerbosity,omitempty" toml:"traceVerbosity,omitempty" yaml:"traceVerbosity,omitempty" export:"true"` -} - -// SetDefaults Default values for a RouterObservabilityConfig. -func (r *RouterObservabilityConfig) SetDefaults() { - r.TraceVerbosity = otypes.MinimalVerbosity + Tracing *bool `json:"tracing,omitempty" toml:"tracing,omitempty" yaml:"tracing,omitempty" export:"true"` + Metrics *bool `json:"metrics,omitempty" toml:"metrics,omitempty" yaml:"metrics,omitempty" export:"true"` } // +k8s:deepcopy-gen=true @@ -312,18 +299,17 @@ type Server struct { // ServerHealthCheck holds the HealthCheck configuration. type ServerHealthCheck struct { - Scheme string `json:"scheme,omitempty" toml:"scheme,omitempty" yaml:"scheme,omitempty" export:"true"` - Mode string `json:"mode,omitempty" toml:"mode,omitempty" yaml:"mode,omitempty" export:"true"` - Path string `json:"path,omitempty" toml:"path,omitempty" yaml:"path,omitempty" export:"true"` - Method string `json:"method,omitempty" toml:"method,omitempty" yaml:"method,omitempty" export:"true"` - Status int `json:"status,omitempty" toml:"status,omitempty" yaml:"status,omitempty" export:"true"` - Port int `json:"port,omitempty" toml:"port,omitempty,omitzero" yaml:"port,omitempty" export:"true"` - Interval ptypes.Duration `json:"interval,omitempty" toml:"interval,omitempty" yaml:"interval,omitempty" export:"true"` - UnhealthyInterval *ptypes.Duration `json:"unhealthyInterval,omitempty" toml:"unhealthyInterval,omitempty" yaml:"unhealthyInterval,omitempty" export:"true"` - Timeout ptypes.Duration `json:"timeout,omitempty" toml:"timeout,omitempty" yaml:"timeout,omitempty" export:"true"` - Hostname string `json:"hostname,omitempty" toml:"hostname,omitempty" yaml:"hostname,omitempty"` - FollowRedirects *bool `json:"followRedirects,omitempty" toml:"followRedirects,omitempty" yaml:"followRedirects,omitempty" export:"true"` - Headers map[string]string `json:"headers,omitempty" toml:"headers,omitempty" yaml:"headers,omitempty" export:"true"` + Scheme string `json:"scheme,omitempty" toml:"scheme,omitempty" yaml:"scheme,omitempty" export:"true"` + Mode string `json:"mode,omitempty" toml:"mode,omitempty" yaml:"mode,omitempty" export:"true"` + Path string `json:"path,omitempty" toml:"path,omitempty" yaml:"path,omitempty" export:"true"` + Method string `json:"method,omitempty" toml:"method,omitempty" yaml:"method,omitempty" export:"true"` + Status int `json:"status,omitempty" toml:"status,omitempty" yaml:"status,omitempty" export:"true"` + Port int `json:"port,omitempty" toml:"port,omitempty,omitzero" yaml:"port,omitempty" export:"true"` + Interval ptypes.Duration `json:"interval,omitempty" toml:"interval,omitempty" yaml:"interval,omitempty" export:"true"` + Timeout ptypes.Duration `json:"timeout,omitempty" toml:"timeout,omitempty" yaml:"timeout,omitempty" export:"true"` + Hostname string `json:"hostname,omitempty" toml:"hostname,omitempty" yaml:"hostname,omitempty"` + FollowRedirects *bool `json:"followRedirects,omitempty" toml:"followRedirects,omitempty" yaml:"followRedirects,omitempty" export:"true"` + Headers map[string]string `json:"headers,omitempty" toml:"headers,omitempty" yaml:"headers,omitempty" export:"true"` } // SetDefaults Default values for a HealthCheck. @@ -348,7 +334,7 @@ type ServersTransport struct { InsecureSkipVerify bool `description:"Disables SSL certificate verification." json:"insecureSkipVerify,omitempty" toml:"insecureSkipVerify,omitempty" yaml:"insecureSkipVerify,omitempty" export:"true"` RootCAs []types.FileOrContent `description:"Defines a list of CA certificates used to validate server certificates." json:"rootCAs,omitempty" toml:"rootCAs,omitempty" yaml:"rootCAs,omitempty"` Certificates traefiktls.Certificates `description:"Defines a list of client certificates for mTLS." json:"certificates,omitempty" toml:"certificates,omitempty" yaml:"certificates,omitempty" export:"true"` - MaxIdleConnsPerHost int `description:"If non-zero, controls the maximum idle (keep-alive) to keep per-host. If zero, DefaultMaxIdleConnsPerHost is used. If negative, disables connection reuse." json:"maxIdleConnsPerHost,omitempty" toml:"maxIdleConnsPerHost,omitempty" yaml:"maxIdleConnsPerHost,omitempty" export:"true"` + MaxIdleConnsPerHost int `description:"If non-zero, controls the maximum idle (keep-alive) to keep per-host. If zero, DefaultMaxIdleConnsPerHost is used" json:"maxIdleConnsPerHost,omitempty" toml:"maxIdleConnsPerHost,omitempty" yaml:"maxIdleConnsPerHost,omitempty" export:"true"` ForwardingTimeouts *ForwardingTimeouts `description:"Defines the timeouts for requests forwarded to the backend servers." json:"forwardingTimeouts,omitempty" toml:"forwardingTimeouts,omitempty" yaml:"forwardingTimeouts,omitempty" export:"true"` DisableHTTP2 bool `description:"Disables HTTP/2 for connections with backend servers." json:"disableHTTP2,omitempty" toml:"disableHTTP2,omitempty" yaml:"disableHTTP2,omitempty" export:"true"` PeerCertURI string `description:"Defines the URI used to match against SAN URI during the peer certificate verification." json:"peerCertURI,omitempty" toml:"peerCertURI,omitempty" yaml:"peerCertURI,omitempty" export:"true"` diff --git a/pkg/config/dynamic/middlewares.go b/pkg/config/dynamic/middlewares.go index 02f371948..0f45e62de 100644 --- a/pkg/config/dynamic/middlewares.go +++ b/pkg/config/dynamic/middlewares.go @@ -77,7 +77,7 @@ type ContentType struct { // AddPrefix holds the add prefix middleware configuration. // This middleware updates the path of a request before forwarding it. -// More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/addprefix/ +// More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/addprefix/ type AddPrefix struct { // Prefix is the string to add before the current path in the requested URL. // It should include a leading slash (/). @@ -89,7 +89,7 @@ type AddPrefix struct { // BasicAuth holds the basic auth middleware configuration. // This middleware restricts access to your services to known users. -// More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/basicauth/ +// More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/basicauth/ type BasicAuth struct { // Users is an array of authorized users. // Each user must be declared using the name:hashed-password format. @@ -104,7 +104,7 @@ type BasicAuth struct { // Default: false. RemoveHeader bool `json:"removeHeader,omitempty" toml:"removeHeader,omitempty" yaml:"removeHeader,omitempty" export:"true"` // HeaderField defines a header field to store the authenticated user. - // More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/basicauth/#headerfield + // More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/basicauth/#headerfield HeaderField string `json:"headerField,omitempty" toml:"headerField,omitempty" yaml:"headerField,omitempty" export:"true"` } @@ -112,7 +112,7 @@ type BasicAuth struct { // Buffering holds the buffering middleware configuration. // This middleware retries or limits the size of requests that can be forwarded to backends. -// More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/buffering/#maxrequestbodybytes +// More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/buffering/#maxrequestbodybytes type Buffering struct { // MaxRequestBodyBytes defines the maximum allowed body size for the request (in bytes). // If the request exceeds the allowed size, it is not forwarded to the service, and the client gets a 413 (Request Entity Too Large) response. @@ -130,7 +130,7 @@ type Buffering struct { MemResponseBodyBytes int64 `json:"memResponseBodyBytes,omitempty" toml:"memResponseBodyBytes,omitempty" yaml:"memResponseBodyBytes,omitempty" export:"true"` // RetryExpression defines the retry conditions. // It is a logical combination of functions with operators AND (&&) and OR (||). - // More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/buffering/#retryexpression + // More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/buffering/#retryexpression RetryExpression string `json:"retryExpression,omitempty" toml:"retryExpression,omitempty" yaml:"retryExpression,omitempty" export:"true"` } @@ -147,7 +147,7 @@ type Chain struct { // CircuitBreaker holds the circuit breaker middleware configuration. // This middleware protects the system from stacking requests to unhealthy services, resulting in cascading failures. -// More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/circuitbreaker/ +// More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/circuitbreaker/ type CircuitBreaker struct { // Expression defines the expression that, once matched, opens the circuit breaker and applies the fallback mechanism instead of calling the services. Expression string `json:"expression,omitempty" toml:"expression,omitempty" yaml:"expression,omitempty" export:"true"` @@ -197,7 +197,7 @@ func (c *Compress) SetDefaults() { // DigestAuth holds the digest auth middleware configuration. // This middleware restricts access to your services to known users. -// More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/digestauth/ +// More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/digestauth/ type DigestAuth struct { // Users defines the authorized users. // Each user should be declared using the name:realm:encoded-password format. @@ -210,7 +210,7 @@ type DigestAuth struct { // Default: traefik. Realm string `json:"realm,omitempty" toml:"realm,omitempty" yaml:"realm,omitempty"` // HeaderField defines a header field to store the authenticated user. - // More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/basicauth/#headerfield + // More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/basicauth/#headerfield HeaderField string `json:"headerField,omitempty" toml:"headerField,omitempty" yaml:"headerField,omitempty" export:"true"` } @@ -241,7 +241,7 @@ type ErrorPage struct { // ForwardAuth holds the forward auth middleware configuration. // This middleware delegates the request authentication to a Service. -// More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/forwardauth/ +// More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/forwardauth/ type ForwardAuth struct { // Address defines the authentication server address. Address string `json:"address,omitempty" toml:"address,omitempty" yaml:"address,omitempty"` @@ -252,7 +252,7 @@ type ForwardAuth struct { // AuthResponseHeaders defines the list of headers to copy from the authentication server response and set on forwarded request, replacing any existing conflicting headers. AuthResponseHeaders []string `json:"authResponseHeaders,omitempty" toml:"authResponseHeaders,omitempty" yaml:"authResponseHeaders,omitempty" export:"true"` // AuthResponseHeadersRegex defines the regex to match headers to copy from the authentication server response and set on forwarded request, after stripping all headers that match the regex. - // More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/forwardauth/#authresponseheadersregex + // More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/forwardauth/#authresponseheadersregex AuthResponseHeadersRegex string `json:"authResponseHeadersRegex,omitempty" toml:"authResponseHeadersRegex,omitempty" yaml:"authResponseHeadersRegex,omitempty" export:"true"` // AuthRequestHeaders defines the list of the headers to copy from the request to the authentication server. // If not set or empty then all request headers are passed. @@ -260,7 +260,7 @@ type ForwardAuth struct { // AddAuthCookiesToResponse defines the list of cookies to copy from the authentication server response to the response. AddAuthCookiesToResponse []string `json:"addAuthCookiesToResponse,omitempty" toml:"addAuthCookiesToResponse,omitempty" yaml:"addAuthCookiesToResponse,omitempty" export:"true"` // HeaderField defines a header field to store the authenticated user. - // More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/forwardauth/#headerfield + // More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/forwardauth/#headerfield HeaderField string `json:"headerField,omitempty" toml:"headerField,omitempty" yaml:"headerField,omitempty" export:"true"` // ForwardBody defines whether to send the request body to the authentication server. ForwardBody bool `json:"forwardBody,omitempty" toml:"forwardBody,omitempty" yaml:"forwardBody,omitempty" export:"true"` @@ -295,7 +295,7 @@ type ClientTLS struct { // Headers holds the headers middleware configuration. // This middleware manages the requests and responses headers. -// More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/headers/#customrequestheaders +// More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/headers/#customrequestheaders type Headers struct { // CustomRequestHeaders defines the header names and values to apply to the request. CustomRequestHeaders map[string]string `json:"customRequestHeaders,omitempty" toml:"customRequestHeaders,omitempty" yaml:"customRequestHeaders,omitempty" export:"true"` @@ -425,7 +425,7 @@ func (h *Headers) HasSecureHeadersDefined() bool { // +k8s:deepcopy-gen=true // IPStrategy holds the IP strategy configuration used by Traefik to determine the client IP. -// More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/ipallowlist/#ipstrategy +// More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/ipallowlist/#ipstrategy type IPStrategy struct { // Depth tells Traefik to use the X-Forwarded-For header and take the IP located at the depth position (starting from the right). // +kubebuilder:validation:Minimum=0 @@ -480,7 +480,7 @@ func (s *IPStrategy) Get() (ip.Strategy, error) { // IPWhiteList holds the IP whitelist middleware configuration. // This middleware limits allowed requests based on the client IP. -// More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/ipwhitelist/ +// More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/ipwhitelist/ // Deprecated: please use IPAllowList instead. type IPWhiteList struct { // SourceRange defines the set of allowed IPs (or ranges of allowed IPs by using CIDR notation). Required. @@ -492,7 +492,7 @@ type IPWhiteList struct { // IPAllowList holds the IP allowlist middleware configuration. // This middleware limits allowed requests based on the client IP. -// More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/ipallowlist/ +// More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/ipallowlist/ type IPAllowList struct { // SourceRange defines the set of allowed IPs (or ranges of allowed IPs by using CIDR notation). SourceRange []string `json:"sourceRange,omitempty" toml:"sourceRange,omitempty" yaml:"sourceRange,omitempty"` @@ -506,7 +506,7 @@ type IPAllowList struct { // InFlightReq holds the in-flight request middleware configuration. // This middleware limits the number of requests being processed and served concurrently. -// More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/inflightreq/ +// More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/inflightreq/ type InFlightReq struct { // Amount defines the maximum amount of allowed simultaneous in-flight request. // The middleware responds with HTTP 429 Too Many Requests if there are already amount requests in progress (based on the same sourceCriterion strategy). @@ -515,7 +515,7 @@ type InFlightReq struct { // SourceCriterion defines what criterion is used to group requests as originating from a common source. // If several strategies are defined at the same time, an error will be raised. // If none are set, the default is to use the requestHost. - // More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/inflightreq/#sourcecriterion + // More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/inflightreq/#sourcecriterion SourceCriterion *SourceCriterion `json:"sourceCriterion,omitempty" toml:"sourceCriterion,omitempty" yaml:"sourceCriterion,omitempty" export:"true"` } @@ -523,7 +523,7 @@ type InFlightReq struct { // PassTLSClientCert holds the pass TLS client cert middleware configuration. // This middleware adds the selected data from the passed client TLS certificate to a header. -// More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/passtlsclientcert/ +// More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/passtlsclientcert/ type PassTLSClientCert struct { // PEM sets the X-Forwarded-Tls-Client-Cert header with the certificate. PEM bool `json:"pem,omitempty" toml:"pem,omitempty" yaml:"pem,omitempty" export:"true"` @@ -635,13 +635,13 @@ func (r *Redis) SetDefaults() { // RedirectRegex holds the redirect regex middleware configuration. // This middleware redirects a request using regex matching and replacement. -// More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/redirectregex/#regex +// More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/redirectregex/#regex type RedirectRegex struct { // Regex defines the regex used to match and capture elements from the request URL. Regex string `json:"regex,omitempty" toml:"regex,omitempty" yaml:"regex,omitempty"` // Replacement defines how to modify the URL to have the new target URL. Replacement string `json:"replacement,omitempty" toml:"replacement,omitempty" yaml:"replacement,omitempty"` - // Permanent defines whether the redirection is permanent (308). + // Permanent defines whether the redirection is permanent (301). Permanent bool `json:"permanent,omitempty" toml:"permanent,omitempty" yaml:"permanent,omitempty" export:"true"` } @@ -649,13 +649,13 @@ type RedirectRegex struct { // RedirectScheme holds the redirect scheme middleware configuration. // This middleware redirects requests from a scheme/port to another. -// More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/redirectscheme/ +// More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/redirectscheme/ type RedirectScheme struct { // Scheme defines the scheme of the new URL. Scheme string `json:"scheme,omitempty" toml:"scheme,omitempty" yaml:"scheme,omitempty" export:"true"` // Port defines the port of the new URL. Port string `json:"port,omitempty" toml:"port,omitempty" yaml:"port,omitempty" export:"true"` - // Permanent defines whether the redirection is permanent (308). + // Permanent defines whether the redirection is permanent (301). Permanent bool `json:"permanent,omitempty" toml:"permanent,omitempty" yaml:"permanent,omitempty" export:"true"` } @@ -663,7 +663,7 @@ type RedirectScheme struct { // ReplacePath holds the replace path middleware configuration. // This middleware replaces the path of the request URL and store the original path in an X-Replaced-Path header. -// More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/replacepath/ +// More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/replacepath/ type ReplacePath struct { // Path defines the path to use as replacement in the request URL. Path string `json:"path,omitempty" toml:"path,omitempty" yaml:"path,omitempty" export:"true"` @@ -673,7 +673,7 @@ type ReplacePath struct { // ReplacePathRegex holds the replace path regex middleware configuration. // This middleware replaces the path of a URL using regex matching and replacement. -// More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/replacepathregex/ +// More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/replacepathregex/ type ReplacePathRegex struct { // Regex defines the regular expression used to match and capture the path from the request URL. Regex string `json:"regex,omitempty" toml:"regex,omitempty" yaml:"regex,omitempty" export:"true"` @@ -686,7 +686,7 @@ type ReplacePathRegex struct { // Retry holds the retry middleware configuration. // This middleware reissues requests a given number of times to a backend server if that server does not reply. // As soon as the server answers, the middleware stops retrying, regardless of the response status. -// More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/retry/ +// More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/retry/ type Retry struct { // Attempts defines how many times the request should be retried. Attempts int `json:"attempts,omitempty" toml:"attempts,omitempty" yaml:"attempts,omitempty" export:"true"` @@ -702,7 +702,7 @@ type Retry struct { // StripPrefix holds the strip prefix middleware configuration. // This middleware removes the specified prefixes from the URL path. -// More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/stripprefix/ +// More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/stripprefix/ type StripPrefix struct { // Prefixes defines the prefixes to strip from the request URL. Prefixes []string `json:"prefixes,omitempty" toml:"prefixes,omitempty" yaml:"prefixes,omitempty" export:"true"` @@ -717,7 +717,7 @@ type StripPrefix struct { // StripPrefixRegex holds the strip prefix regex middleware configuration. // This middleware removes the matching prefixes from the URL path. -// More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/stripprefixregex/ +// More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/stripprefixregex/ type StripPrefixRegex struct { // Regex defines the regular expression to match the path prefix from the request URL. Regex []string `json:"regex,omitempty" toml:"regex,omitempty" yaml:"regex,omitempty" export:"true"` diff --git a/pkg/config/dynamic/tcp_config.go b/pkg/config/dynamic/tcp_config.go index 207c90045..eec8c7d38 100644 --- a/pkg/config/dynamic/tcp_config.go +++ b/pkg/config/dynamic/tcp_config.go @@ -84,12 +84,10 @@ type RouterTCPTLSConfig struct { // TCPServersLoadBalancer holds the LoadBalancerService configuration. type TCPServersLoadBalancer struct { - Servers []TCPServer `json:"servers,omitempty" toml:"servers,omitempty" yaml:"servers,omitempty" label-slice-as-struct:"server" export:"true"` - ServersTransport string `json:"serversTransport,omitempty" toml:"serversTransport,omitempty" yaml:"serversTransport,omitempty" export:"true"` + ProxyProtocol *ProxyProtocol `json:"proxyProtocol,omitempty" toml:"proxyProtocol,omitempty" yaml:"proxyProtocol,omitempty" label:"allowEmpty" file:"allowEmpty" kv:"allowEmpty" export:"true"` + Servers []TCPServer `json:"servers,omitempty" toml:"servers,omitempty" yaml:"servers,omitempty" label-slice-as-struct:"server" export:"true"` + ServersTransport string `json:"serversTransport,omitempty" toml:"serversTransport,omitempty" yaml:"serversTransport,omitempty" export:"true"` - // ProxyProtocol holds the PROXY Protocol configuration. - // Deprecated: use ServersTransport to configure ProxyProtocol instead. - ProxyProtocol *ProxyProtocol `json:"proxyProtocol,omitempty" toml:"proxyProtocol,omitempty" yaml:"proxyProtocol,omitempty" label:"allowEmpty" file:"allowEmpty" kv:"allowEmpty" export:"true"` // TerminationDelay, corresponds to the deadline that the proxy sets, after one // of its connected peers indicates it has closed the writing capability of its // connection, to close the reading capability as well, hence fully terminating the @@ -128,12 +126,12 @@ type TCPServer struct { // +k8s:deepcopy-gen=true // ProxyProtocol holds the PROXY Protocol configuration. -// More info: https://doc.traefik.io/traefik/v3.5/routing/services/#proxy-protocol +// More info: https://doc.traefik.io/traefik/v3.4/routing/services/#proxy-protocol type ProxyProtocol struct { // Version defines the PROXY Protocol version to use. // +kubebuilder:validation:Minimum=1 // +kubebuilder:validation:Maximum=2 - Version int `description:"Defines the PROXY Protocol version to use." json:"version,omitempty" toml:"version,omitempty" yaml:"version,omitempty" export:"true"` + Version int `json:"version,omitempty" toml:"version,omitempty" yaml:"version,omitempty" export:"true"` } // SetDefaults Default values for a ProxyProtocol. @@ -147,8 +145,6 @@ func (p *ProxyProtocol) SetDefaults() { type TCPServersTransport struct { DialKeepAlive ptypes.Duration `description:"Defines the interval between keep-alive probes for an active network connection. If zero, keep-alive probes are sent with a default value (currently 15 seconds), if supported by the protocol and operating system. Network protocols or operating systems that do not support keep-alives ignore this field. If negative, keep-alive probes are disabled" json:"dialKeepAlive,omitempty" toml:"dialKeepAlive,omitempty" yaml:"dialKeepAlive,omitempty" export:"true"` DialTimeout ptypes.Duration `description:"Defines the amount of time to wait until a connection to a backend server can be established. If zero, no timeout exists." json:"dialTimeout,omitempty" toml:"dialTimeout,omitempty" yaml:"dialTimeout,omitempty" export:"true"` - // ProxyProtocol holds the PROXY Protocol configuration. - ProxyProtocol *ProxyProtocol `description:"Defines the PROXY Protocol configuration." json:"proxyProtocol,omitempty" toml:"proxyProtocol,omitempty" yaml:"proxyProtocol,omitempty" label:"allowEmpty" file:"allowEmpty" kv:"allowEmpty" export:"true"` // TerminationDelay, corresponds to the deadline that the proxy sets, after one // of its connected peers indicates it has closed the writing capability of its // connection, to close the reading capability as well, hence fully terminating the @@ -158,13 +154,6 @@ type TCPServersTransport struct { TLS *TLSClientConfig `description:"Defines the TLS configuration." json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" label:"allowEmpty" file:"allowEmpty" kv:"allowEmpty" export:"true"` } -// SetDefaults sets the default values for a TCPServersTransport. -func (t *TCPServersTransport) SetDefaults() { - t.DialTimeout = ptypes.Duration(30 * time.Second) - t.DialKeepAlive = ptypes.Duration(15 * time.Second) - t.TerminationDelay = ptypes.Duration(100 * time.Millisecond) -} - // +k8s:deepcopy-gen=true // TLSClientConfig options to configure TLS communication between Traefik and the servers. @@ -176,3 +165,10 @@ type TLSClientConfig struct { PeerCertURI string `description:"Defines the URI used to match against SAN URI during the peer certificate verification." json:"peerCertURI,omitempty" toml:"peerCertURI,omitempty" yaml:"peerCertURI,omitempty" export:"true"` Spiffe *Spiffe `description:"Defines the SPIFFE TLS configuration." json:"spiffe,omitempty" toml:"spiffe,omitempty" yaml:"spiffe,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` } + +// SetDefaults sets the default values for a TCPServersTransport. +func (t *TCPServersTransport) SetDefaults() { + t.DialTimeout = ptypes.Duration(30 * time.Second) + t.DialKeepAlive = ptypes.Duration(15 * time.Second) + t.TerminationDelay = ptypes.Duration(100 * time.Millisecond) +} diff --git a/pkg/config/dynamic/tcp_middlewares.go b/pkg/config/dynamic/tcp_middlewares.go index 3d9624d08..cfa401132 100644 --- a/pkg/config/dynamic/tcp_middlewares.go +++ b/pkg/config/dynamic/tcp_middlewares.go @@ -15,7 +15,7 @@ type TCPMiddleware struct { // TCPInFlightConn holds the TCP InFlightConn middleware configuration. // This middleware prevents services from being overwhelmed with high load, // by limiting the number of allowed simultaneous connections for one IP. -// More info: https://doc.traefik.io/traefik/v3.5/middlewares/tcp/inflightconn/ +// More info: https://doc.traefik.io/traefik/v3.4/middlewares/tcp/inflightconn/ type TCPInFlightConn struct { // Amount defines the maximum amount of allowed simultaneous connections. // The middleware closes the connection if there are already amount connections opened. @@ -36,7 +36,7 @@ type TCPIPWhiteList struct { // TCPIPAllowList holds the TCP IPAllowList middleware configuration. // This middleware limits allowed requests based on the client IP. -// More info: https://doc.traefik.io/traefik/v3.5/middlewares/tcp/ipallowlist/ +// More info: https://doc.traefik.io/traefik/v3.4/middlewares/tcp/ipallowlist/ type TCPIPAllowList struct { // SourceRange defines the allowed IPs (or ranges of allowed IPs by using CIDR notation). SourceRange []string `json:"sourceRange,omitempty" toml:"sourceRange,omitempty" yaml:"sourceRange,omitempty"` diff --git a/pkg/config/dynamic/zz_generated.deepcopy.go b/pkg/config/dynamic/zz_generated.deepcopy.go index fb4cea3e9..2a680897c 100644 --- a/pkg/config/dynamic/zz_generated.deepcopy.go +++ b/pkg/config/dynamic/zz_generated.deepcopy.go @@ -1335,13 +1335,13 @@ func (in *RouterObservabilityConfig) DeepCopyInto(out *RouterObservabilityConfig *out = new(bool) **out = **in } - if in.Metrics != nil { - in, out := &in.Metrics, &out.Metrics + if in.Tracing != nil { + in, out := &in.Tracing, &out.Tracing *out = new(bool) **out = **in } - if in.Tracing != nil { - in, out := &in.Tracing, &out.Tracing + if in.Metrics != nil { + in, out := &in.Metrics, &out.Metrics *out = new(bool) **out = **in } @@ -1428,11 +1428,6 @@ func (in *Server) DeepCopy() *Server { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ServerHealthCheck) DeepCopyInto(out *ServerHealthCheck) { *out = *in - if in.UnhealthyInterval != nil { - in, out := &in.UnhealthyInterval, &out.UnhealthyInterval - *out = new(paersertypes.Duration) - **out = **in - } if in.FollowRedirects != nil { in, out := &in.FollowRedirects, &out.FollowRedirects *out = new(bool) @@ -1929,16 +1924,16 @@ func (in *TCPServer) DeepCopy() *TCPServer { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TCPServersLoadBalancer) DeepCopyInto(out *TCPServersLoadBalancer) { *out = *in - if in.Servers != nil { - in, out := &in.Servers, &out.Servers - *out = make([]TCPServer, len(*in)) - copy(*out, *in) - } if in.ProxyProtocol != nil { in, out := &in.ProxyProtocol, &out.ProxyProtocol *out = new(ProxyProtocol) **out = **in } + if in.Servers != nil { + in, out := &in.Servers, &out.Servers + *out = make([]TCPServer, len(*in)) + copy(*out, *in) + } if in.TerminationDelay != nil { in, out := &in.TerminationDelay, &out.TerminationDelay *out = new(int) @@ -1960,11 +1955,6 @@ func (in *TCPServersLoadBalancer) DeepCopy() *TCPServersLoadBalancer { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TCPServersTransport) DeepCopyInto(out *TCPServersTransport) { *out = *in - if in.ProxyProtocol != nil { - in, out := &in.ProxyProtocol, &out.ProxyProtocol - *out = new(ProxyProtocol) - **out = **in - } if in.TLS != nil { in, out := &in.TLS, &out.TLS *out = new(TLSClientConfig) diff --git a/pkg/config/label/label_test.go b/pkg/config/label/label_test.go index b17222410..bd763ba08 100644 --- a/pkg/config/label/label_test.go +++ b/pkg/config/label/label_test.go @@ -162,7 +162,6 @@ func TestDecodeConfiguration(t *testing.T) { "traefik.http.services.Service0.loadbalancer.healthcheck.headers.name1": "foobar", "traefik.http.services.Service0.loadbalancer.healthcheck.hostname": "foobar", "traefik.http.services.Service0.loadbalancer.healthcheck.interval": "1s", - "traefik.http.services.Service0.loadbalancer.healthcheck.unhealthyinterval": "1s", "traefik.http.services.Service0.loadbalancer.healthcheck.path": "foobar", "traefik.http.services.Service0.loadbalancer.healthcheck.method": "foobar", "traefik.http.services.Service0.loadbalancer.healthcheck.status": "401", @@ -187,7 +186,6 @@ func TestDecodeConfiguration(t *testing.T) { "traefik.http.services.Service1.loadbalancer.healthcheck.headers.name1": "foobar", "traefik.http.services.Service1.loadbalancer.healthcheck.hostname": "foobar", "traefik.http.services.Service1.loadbalancer.healthcheck.interval": "1s", - "traefik.http.services.Service1.loadbalancer.healthcheck.unhealthyinterval": "1s", "traefik.http.services.Service1.loadbalancer.healthcheck.path": "foobar", "traefik.http.services.Service1.loadbalancer.healthcheck.method": "foobar", "traefik.http.services.Service1.loadbalancer.healthcheck.status": "401", @@ -703,16 +701,15 @@ func TestDecodeConfiguration(t *testing.T) { }, }, HealthCheck: &dynamic.ServerHealthCheck{ - Scheme: "foobar", - Mode: "foobar", - Path: "foobar", - Method: "foobar", - Status: 401, - Port: 42, - Interval: ptypes.Duration(time.Second), - UnhealthyInterval: pointer(ptypes.Duration(time.Second)), - Timeout: ptypes.Duration(time.Second), - Hostname: "foobar", + Scheme: "foobar", + Mode: "foobar", + Path: "foobar", + Method: "foobar", + Status: 401, + Port: 42, + Interval: ptypes.Duration(time.Second), + Timeout: ptypes.Duration(time.Second), + Hostname: "foobar", Headers: map[string]string{ "name0": "foobar", "name1": "foobar", @@ -738,16 +735,15 @@ func TestDecodeConfiguration(t *testing.T) { }, }, HealthCheck: &dynamic.ServerHealthCheck{ - Scheme: "foobar", - Mode: "foobar", - Path: "foobar", - Method: "foobar", - Status: 401, - Port: 42, - Interval: ptypes.Duration(time.Second), - UnhealthyInterval: pointer(ptypes.Duration(time.Second)), - Timeout: ptypes.Duration(time.Second), - Hostname: "foobar", + Scheme: "foobar", + Mode: "foobar", + Path: "foobar", + Method: "foobar", + Status: 401, + Port: 42, + Interval: ptypes.Duration(time.Second), + Timeout: ptypes.Duration(time.Second), + Hostname: "foobar", Headers: map[string]string{ "name0": "foobar", "name1": "foobar", @@ -1248,15 +1244,14 @@ func TestEncodeConfiguration(t *testing.T) { }, }, HealthCheck: &dynamic.ServerHealthCheck{ - Scheme: "foobar", - Path: "foobar", - Method: "foobar", - Status: 401, - Port: 42, - Interval: ptypes.Duration(time.Second), - UnhealthyInterval: pointer(ptypes.Duration(time.Second)), - Timeout: ptypes.Duration(time.Second), - Hostname: "foobar", + Scheme: "foobar", + Path: "foobar", + Method: "foobar", + Status: 401, + Port: 42, + Interval: ptypes.Duration(time.Second), + Timeout: ptypes.Duration(time.Second), + Hostname: "foobar", Headers: map[string]string{ "name0": "foobar", "name1": "foobar", @@ -1281,15 +1276,14 @@ func TestEncodeConfiguration(t *testing.T) { }, }, HealthCheck: &dynamic.ServerHealthCheck{ - Scheme: "foobar", - Path: "foobar", - Method: "foobar", - Status: 401, - Port: 42, - Interval: ptypes.Duration(time.Second), - UnhealthyInterval: pointer(ptypes.Duration(time.Second)), - Timeout: ptypes.Duration(time.Second), - Hostname: "foobar", + Scheme: "foobar", + Path: "foobar", + Method: "foobar", + Status: 401, + Port: 42, + Interval: ptypes.Duration(time.Second), + Timeout: ptypes.Duration(time.Second), + Hostname: "foobar", Headers: map[string]string{ "name0": "foobar", "name1": "foobar", @@ -1477,7 +1471,6 @@ func TestEncodeConfiguration(t *testing.T) { "traefik.HTTP.Services.Service0.LoadBalancer.HealthCheck.Headers.name1": "foobar", "traefik.HTTP.Services.Service0.LoadBalancer.HealthCheck.Hostname": "foobar", "traefik.HTTP.Services.Service0.LoadBalancer.HealthCheck.Interval": "1000000000", - "traefik.HTTP.Services.Service0.LoadBalancer.HealthCheck.UnhealthyInterval": "1000000000", "traefik.HTTP.Services.Service0.LoadBalancer.HealthCheck.Path": "foobar", "traefik.HTTP.Services.Service0.LoadBalancer.HealthCheck.Method": "foobar", "traefik.HTTP.Services.Service0.LoadBalancer.HealthCheck.Status": "401", @@ -1502,7 +1495,6 @@ func TestEncodeConfiguration(t *testing.T) { "traefik.HTTP.Services.Service1.LoadBalancer.HealthCheck.Headers.name1": "foobar", "traefik.HTTP.Services.Service1.LoadBalancer.HealthCheck.Hostname": "foobar", "traefik.HTTP.Services.Service1.LoadBalancer.HealthCheck.Interval": "1000000000", - "traefik.HTTP.Services.Service1.LoadBalancer.HealthCheck.UnhealthyInterval": "1000000000", "traefik.HTTP.Services.Service1.LoadBalancer.HealthCheck.Path": "foobar", "traefik.HTTP.Services.Service1.LoadBalancer.HealthCheck.Method": "foobar", "traefik.HTTP.Services.Service1.LoadBalancer.HealthCheck.Status": "401", diff --git a/pkg/config/runtime/runtime.go b/pkg/config/runtime/runtime.go index 7e8db0a5e..d67233887 100644 --- a/pkg/config/runtime/runtime.go +++ b/pkg/config/runtime/runtime.go @@ -6,7 +6,7 @@ import ( "github.com/rs/zerolog/log" "github.com/traefik/traefik/v3/pkg/config/dynamic" - "github.com/traefik/traefik/v3/pkg/observability/logs" + "github.com/traefik/traefik/v3/pkg/logs" ) // Status of the router/service. @@ -26,10 +26,9 @@ const ( type Configuration struct { Routers map[string]*RouterInfo `json:"routers,omitempty"` Middlewares map[string]*MiddlewareInfo `json:"middlewares,omitempty"` - Services map[string]*ServiceInfo `json:"services,omitempty"` - Models map[string]*dynamic.Model `json:"-"` - TCPRouters map[string]*TCPRouterInfo `json:"tcpRouters,omitempty"` TCPMiddlewares map[string]*TCPMiddlewareInfo `json:"tcpMiddlewares,omitempty"` + Services map[string]*ServiceInfo `json:"services,omitempty"` + TCPRouters map[string]*TCPRouterInfo `json:"tcpRouters,omitempty"` TCPServices map[string]*TCPServiceInfo `json:"tcpServices,omitempty"` UDPRouters map[string]*UDPRouterInfo `json:"udpRouters,omitempty"` UDPServices map[string]*UDPServiceInfo `json:"udpServices,omitempty"` @@ -67,8 +66,6 @@ func NewConfig(conf dynamic.Configuration) *Configuration { runtimeConfig.Middlewares[k] = &MiddlewareInfo{Middleware: v, Status: StatusEnabled} } } - - runtimeConfig.Models = conf.HTTP.Models } if conf.TCP != nil { diff --git a/pkg/config/runtime/runtime_http.go b/pkg/config/runtime/runtime_http.go index 69d7e540f..97a89f066 100644 --- a/pkg/config/runtime/runtime_http.go +++ b/pkg/config/runtime/runtime_http.go @@ -10,7 +10,7 @@ import ( "github.com/rs/zerolog/log" "github.com/traefik/traefik/v3/pkg/config/dynamic" - "github.com/traefik/traefik/v3/pkg/observability/logs" + "github.com/traefik/traefik/v3/pkg/logs" ) // GetRoutersByEntryPoints returns all the http routers by entry points name and routers name. diff --git a/pkg/config/runtime/runtime_http_test.go b/pkg/config/runtime/runtime_http_test.go index ad3f44618..57938b616 100644 --- a/pkg/config/runtime/runtime_http_test.go +++ b/pkg/config/runtime/runtime_http_test.go @@ -1,6 +1,7 @@ package runtime import ( + "context" "testing" "github.com/stretchr/testify/assert" @@ -210,7 +211,7 @@ func TestGetRoutersByEntryPoints(t *testing.T) { t.Run(test.desc, func(t *testing.T) { t.Parallel() runtimeConfig := NewConfig(test.conf) - actual := runtimeConfig.GetRoutersByEntryPoints(t.Context(), test.entryPoints, false) + actual := runtimeConfig.GetRoutersByEntryPoints(context.Background(), test.entryPoints, false) assert.Equal(t, test.expected, actual) }) } diff --git a/pkg/config/runtime/runtime_tcp.go b/pkg/config/runtime/runtime_tcp.go index b8bc08981..1c213f7b1 100644 --- a/pkg/config/runtime/runtime_tcp.go +++ b/pkg/config/runtime/runtime_tcp.go @@ -8,7 +8,7 @@ import ( "github.com/rs/zerolog/log" "github.com/traefik/traefik/v3/pkg/config/dynamic" - "github.com/traefik/traefik/v3/pkg/observability/logs" + "github.com/traefik/traefik/v3/pkg/logs" ) // GetTCPRoutersByEntryPoints returns all the tcp routers by entry points name and routers name. diff --git a/pkg/config/runtime/runtime_tcp_test.go b/pkg/config/runtime/runtime_tcp_test.go index 644e57384..c4726ac72 100644 --- a/pkg/config/runtime/runtime_tcp_test.go +++ b/pkg/config/runtime/runtime_tcp_test.go @@ -1,6 +1,7 @@ package runtime import ( + "context" "testing" "github.com/stretchr/testify/assert" @@ -210,7 +211,7 @@ func TestGetTCPRoutersByEntryPoints(t *testing.T) { t.Run(test.desc, func(t *testing.T) { t.Parallel() runtimeConfig := NewConfig(test.conf) - actual := runtimeConfig.GetTCPRoutersByEntryPoints(t.Context(), test.entryPoints) + actual := runtimeConfig.GetTCPRoutersByEntryPoints(context.Background(), test.entryPoints) assert.Equal(t, test.expected, actual) }) } diff --git a/pkg/config/runtime/runtime_udp.go b/pkg/config/runtime/runtime_udp.go index 912f986f1..e1a867506 100644 --- a/pkg/config/runtime/runtime_udp.go +++ b/pkg/config/runtime/runtime_udp.go @@ -8,7 +8,7 @@ import ( "github.com/rs/zerolog/log" "github.com/traefik/traefik/v3/pkg/config/dynamic" - "github.com/traefik/traefik/v3/pkg/observability/logs" + "github.com/traefik/traefik/v3/pkg/logs" ) // GetUDPRoutersByEntryPoints returns all the UDP routers by entry points name and routers name. diff --git a/pkg/config/runtime/runtime_udp_test.go b/pkg/config/runtime/runtime_udp_test.go index 67bcb8602..5531bb934 100644 --- a/pkg/config/runtime/runtime_udp_test.go +++ b/pkg/config/runtime/runtime_udp_test.go @@ -1,6 +1,7 @@ package runtime import ( + "context" "testing" "github.com/stretchr/testify/assert" @@ -191,7 +192,7 @@ func TestGetUDPRoutersByEntryPoints(t *testing.T) { t.Run(test.desc, func(t *testing.T) { t.Parallel() runtimeConfig := NewConfig(test.conf) - actual := runtimeConfig.GetUDPRoutersByEntryPoints(t.Context(), test.entryPoints) + actual := runtimeConfig.GetUDPRoutersByEntryPoints(context.Background(), test.entryPoints) assert.Equal(t, test.expected, actual) }) } diff --git a/pkg/config/static/entrypoints.go b/pkg/config/static/entrypoints.go index ed1eb008b..d98cdb1b3 100644 --- a/pkg/config/static/entrypoints.go +++ b/pkg/config/static/entrypoints.go @@ -7,7 +7,6 @@ import ( "strings" ptypes "github.com/traefik/paerser/types" - otypes "github.com/traefik/traefik/v3/pkg/observability/types" "github.com/traefik/traefik/v3/pkg/types" ) @@ -166,17 +165,15 @@ func (u *UDPConfig) SetDefaults() { // ObservabilityConfig holds the observability configuration for an entry point. type ObservabilityConfig struct { - AccessLogs *bool `description:"Enables access-logs for this entryPoint." json:"accessLogs,omitempty" toml:"accessLogs,omitempty" yaml:"accessLogs,omitempty" export:"true"` - Metrics *bool `description:"Enables metrics for this entryPoint." json:"metrics,omitempty" toml:"metrics,omitempty" yaml:"metrics,omitempty" export:"true"` - Tracing *bool `description:"Enables tracing for this entryPoint." json:"tracing,omitempty" toml:"tracing,omitempty" yaml:"tracing,omitempty" export:"true"` - TraceVerbosity otypes.TracingVerbosity `description:"Defines the tracing verbosity level for this entryPoint." json:"traceVerbosity,omitempty" toml:"traceVerbosity,omitempty" yaml:"traceVerbosity,omitempty" export:"true"` + AccessLogs *bool `json:"accessLogs,omitempty" toml:"accessLogs,omitempty" yaml:"accessLogs,omitempty" export:"true"` + Tracing *bool `json:"tracing,omitempty" toml:"tracing,omitempty" yaml:"tracing,omitempty" export:"true"` + Metrics *bool `json:"metrics,omitempty" toml:"metrics,omitempty" yaml:"metrics,omitempty" export:"true"` } // SetDefaults sets the default values. func (o *ObservabilityConfig) SetDefaults() { defaultValue := true o.AccessLogs = &defaultValue - o.Metrics = &defaultValue o.Tracing = &defaultValue - o.TraceVerbosity = otypes.MinimalVerbosity + o.Metrics = &defaultValue } diff --git a/pkg/config/static/experimental.go b/pkg/config/static/experimental.go index dba89fec8..f88564637 100644 --- a/pkg/config/static/experimental.go +++ b/pkg/config/static/experimental.go @@ -4,12 +4,11 @@ import "github.com/traefik/traefik/v3/pkg/plugins" // Experimental experimental Traefik features. type Experimental struct { - Plugins map[string]plugins.Descriptor `description:"Plugins configuration." json:"plugins,omitempty" toml:"plugins,omitempty" yaml:"plugins,omitempty" export:"true"` - LocalPlugins map[string]plugins.LocalDescriptor `description:"Local plugins configuration." json:"localPlugins,omitempty" toml:"localPlugins,omitempty" yaml:"localPlugins,omitempty" export:"true"` - AbortOnPluginFailure bool `description:"Defines whether all plugins must be loaded successfully for Traefik to start." json:"abortOnPluginFailure,omitempty" toml:"abortOnPluginFailure,omitempty" yaml:"abortOnPluginFailure,omitempty" export:"true"` - FastProxy *FastProxyConfig `description:"Enables the FastProxy implementation." json:"fastProxy,omitempty" toml:"fastProxy,omitempty" yaml:"fastProxy,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` - OTLPLogs bool `description:"Enables the OpenTelemetry logs integration." json:"otlplogs,omitempty" toml:"otlplogs,omitempty" yaml:"otlplogs,omitempty" export:"true"` - KubernetesIngressNGINX bool `description:"Allow the Kubernetes Ingress NGINX provider usage." json:"kubernetesIngressNGINX,omitempty" toml:"kubernetesIngressNGINX,omitempty" yaml:"kubernetesIngressNGINX,omitempty" export:"true"` + Plugins map[string]plugins.Descriptor `description:"Plugins configuration." json:"plugins,omitempty" toml:"plugins,omitempty" yaml:"plugins,omitempty" export:"true"` + LocalPlugins map[string]plugins.LocalDescriptor `description:"Local plugins configuration." json:"localPlugins,omitempty" toml:"localPlugins,omitempty" yaml:"localPlugins,omitempty" export:"true"` + AbortOnPluginFailure bool `description:"Defines whether all plugins must be loaded successfully for Traefik to start." json:"abortOnPluginFailure,omitempty" toml:"abortOnPluginFailure,omitempty" yaml:"abortOnPluginFailure,omitempty" export:"true"` + FastProxy *FastProxyConfig `description:"Enables the FastProxy implementation." json:"fastProxy,omitempty" toml:"fastProxy,omitempty" yaml:"fastProxy,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` + OTLPLogs bool `description:"Enables the OpenTelemetry logs integration." json:"otlplogs,omitempty" toml:"otlplogs,omitempty" yaml:"otlplogs,omitempty" export:"true"` // Deprecated: KubernetesGateway provider is not an experimental feature starting with v3.1. Please remove its usage from the static configuration. KubernetesGateway bool `description:"(Deprecated) Allow the Kubernetes gateway api provider usage." json:"kubernetesGateway,omitempty" toml:"kubernetesGateway,omitempty" yaml:"kubernetesGateway,omitempty" export:"true"` diff --git a/pkg/config/static/static_config.go b/pkg/config/static/static_config.go index e64118963..73f740472 100644 --- a/pkg/config/static/static_config.go +++ b/pkg/config/static/static_config.go @@ -11,8 +11,7 @@ import ( "github.com/rs/zerolog" "github.com/rs/zerolog/log" ptypes "github.com/traefik/paerser/types" - "github.com/traefik/traefik/v3/pkg/observability/logs" - otypes "github.com/traefik/traefik/v3/pkg/observability/types" + "github.com/traefik/traefik/v3/pkg/logs" "github.com/traefik/traefik/v3/pkg/ping" acmeprovider "github.com/traefik/traefik/v3/pkg/provider/acme" "github.com/traefik/traefik/v3/pkg/provider/consulcatalog" @@ -23,14 +22,12 @@ import ( "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd" "github.com/traefik/traefik/v3/pkg/provider/kubernetes/gateway" "github.com/traefik/traefik/v3/pkg/provider/kubernetes/ingress" - ingressnginx "github.com/traefik/traefik/v3/pkg/provider/kubernetes/ingress-nginx" "github.com/traefik/traefik/v3/pkg/provider/kv/consul" "github.com/traefik/traefik/v3/pkg/provider/kv/etcd" "github.com/traefik/traefik/v3/pkg/provider/kv/redis" "github.com/traefik/traefik/v3/pkg/provider/kv/zk" "github.com/traefik/traefik/v3/pkg/provider/nomad" "github.com/traefik/traefik/v3/pkg/provider/rest" - "github.com/traefik/traefik/v3/pkg/tls" "github.com/traefik/traefik/v3/pkg/types" ) @@ -65,13 +62,13 @@ type Configuration struct { EntryPoints EntryPoints `description:"Entry points definition." json:"entryPoints,omitempty" toml:"entryPoints,omitempty" yaml:"entryPoints,omitempty" export:"true"` Providers *Providers `description:"Providers configuration." json:"providers,omitempty" toml:"providers,omitempty" yaml:"providers,omitempty" export:"true"` - API *API `description:"Enable api/dashboard." json:"api,omitempty" toml:"api,omitempty" yaml:"api,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` - Metrics *otypes.Metrics `description:"Enable a metrics exporter." json:"metrics,omitempty" toml:"metrics,omitempty" yaml:"metrics,omitempty" export:"true"` - Ping *ping.Handler `description:"Enable ping." json:"ping,omitempty" toml:"ping,omitempty" yaml:"ping,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` + API *API `description:"Enable api/dashboard." json:"api,omitempty" toml:"api,omitempty" yaml:"api,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` + Metrics *types.Metrics `description:"Enable a metrics exporter." json:"metrics,omitempty" toml:"metrics,omitempty" yaml:"metrics,omitempty" export:"true"` + Ping *ping.Handler `description:"Enable ping." json:"ping,omitempty" toml:"ping,omitempty" yaml:"ping,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` - Log *otypes.TraefikLog `description:"Traefik log settings." json:"log,omitempty" toml:"log,omitempty" yaml:"log,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` - AccessLog *otypes.AccessLog `description:"Access log settings." json:"accessLog,omitempty" toml:"accessLog,omitempty" yaml:"accessLog,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` - Tracing *Tracing `description:"Tracing configuration." json:"tracing,omitempty" toml:"tracing,omitempty" yaml:"tracing,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` + Log *types.TraefikLog `description:"Traefik log settings." json:"log,omitempty" toml:"log,omitempty" yaml:"log,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` + AccessLog *types.AccessLog `description:"Access log settings." json:"accessLog,omitempty" toml:"accessLog,omitempty" yaml:"accessLog,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` + Tracing *Tracing `description:"Tracing configuration." json:"tracing,omitempty" toml:"tracing,omitempty" yaml:"tracing,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` HostResolver *types.HostResolverConfig `description:"Enable CNAME Flattening." json:"hostResolver,omitempty" toml:"hostResolver,omitempty" yaml:"hostResolver,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` @@ -83,8 +80,6 @@ type Configuration struct { Core *Core `description:"Core controls." json:"core,omitempty" toml:"core,omitempty" yaml:"core,omitempty" export:"true"` Spiffe *SpiffeClientConfig `description:"SPIFFE integration configuration." json:"spiffe,omitempty" toml:"spiffe,omitempty" yaml:"spiffe,omitempty" export:"true"` - - OCSP *tls.OCSPConfig `description:"OCSP configuration." json:"ocsp,omitempty" toml:"ocsp,omitempty" yaml:"ocsp,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` } // Core configures Traefik core behavior. @@ -120,7 +115,7 @@ type Global struct { type ServersTransport struct { InsecureSkipVerify bool `description:"Disable SSL certificate verification." json:"insecureSkipVerify,omitempty" toml:"insecureSkipVerify,omitempty" yaml:"insecureSkipVerify,omitempty" export:"true"` RootCAs []types.FileOrContent `description:"Add cert file for self-signed certificate." json:"rootCAs,omitempty" toml:"rootCAs,omitempty" yaml:"rootCAs,omitempty"` - MaxIdleConnsPerHost int `description:"If non-zero, controls the maximum idle (keep-alive) to keep per-host. If zero, DefaultMaxIdleConnsPerHost is used. If negative, disables connection reuse." json:"maxIdleConnsPerHost,omitempty" toml:"maxIdleConnsPerHost,omitempty" yaml:"maxIdleConnsPerHost,omitempty" export:"true"` + MaxIdleConnsPerHost int `description:"If non-zero, controls the maximum idle (keep-alive) to keep per-host. If zero, DefaultMaxIdleConnsPerHost is used" json:"maxIdleConnsPerHost,omitempty" toml:"maxIdleConnsPerHost,omitempty" yaml:"maxIdleConnsPerHost,omitempty" export:"true"` ForwardingTimeouts *ForwardingTimeouts `description:"Timeouts for requests forwarded to the backend servers." json:"forwardingTimeouts,omitempty" toml:"forwardingTimeouts,omitempty" yaml:"forwardingTimeouts,omitempty" export:"true"` Spiffe *Spiffe `description:"Defines the SPIFFE configuration." json:"spiffe,omitempty" toml:"spiffe,omitempty" yaml:"spiffe,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` } @@ -207,14 +202,14 @@ func (a *LifeCycle) SetDefaults() { // Tracing holds the tracing configuration. type Tracing struct { - ServiceName string `description:"Defines the service name resource attribute." json:"serviceName,omitempty" toml:"serviceName,omitempty" yaml:"serviceName,omitempty" export:"true"` - ResourceAttributes map[string]string `description:"Defines additional resource attributes (key:value)." json:"resourceAttributes,omitempty" toml:"resourceAttributes,omitempty" yaml:"resourceAttributes,omitempty" export:"true"` - CapturedRequestHeaders []string `description:"Request headers to add as attributes for server and client spans." json:"capturedRequestHeaders,omitempty" toml:"capturedRequestHeaders,omitempty" yaml:"capturedRequestHeaders,omitempty" export:"true"` - CapturedResponseHeaders []string `description:"Response headers to add as attributes for server and client spans." json:"capturedResponseHeaders,omitempty" toml:"capturedResponseHeaders,omitempty" yaml:"capturedResponseHeaders,omitempty" export:"true"` - SafeQueryParams []string `description:"Query params to not redact." json:"safeQueryParams,omitempty" toml:"safeQueryParams,omitempty" yaml:"safeQueryParams,omitempty" export:"true"` - SampleRate float64 `description:"Sets the rate between 0.0 and 1.0 of requests to trace." json:"sampleRate,omitempty" toml:"sampleRate,omitempty" yaml:"sampleRate,omitempty" export:"true"` - AddInternals bool `description:"Enables tracing for internal services (ping, dashboard, etc...)." json:"addInternals,omitempty" toml:"addInternals,omitempty" yaml:"addInternals,omitempty" export:"true"` - OTLP *otypes.OTelTracing `description:"Settings for OpenTelemetry." json:"otlp,omitempty" toml:"otlp,omitempty" yaml:"otlp,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` + ServiceName string `description:"Sets the name for this service." json:"serviceName,omitempty" toml:"serviceName,omitempty" yaml:"serviceName,omitempty" export:"true"` + ResourceAttributes map[string]string `description:"Defines additional resource attributes (key:value)." json:"resourceAttributes,omitempty" toml:"resourceAttributes,omitempty" yaml:"resourceAttributes,omitempty" export:"true"` + CapturedRequestHeaders []string `description:"Request headers to add as attributes for server and client spans." json:"capturedRequestHeaders,omitempty" toml:"capturedRequestHeaders,omitempty" yaml:"capturedRequestHeaders,omitempty" export:"true"` + CapturedResponseHeaders []string `description:"Response headers to add as attributes for server and client spans." json:"capturedResponseHeaders,omitempty" toml:"capturedResponseHeaders,omitempty" yaml:"capturedResponseHeaders,omitempty" export:"true"` + SafeQueryParams []string `description:"Query params to not redact." json:"safeQueryParams,omitempty" toml:"safeQueryParams,omitempty" yaml:"safeQueryParams,omitempty" export:"true"` + SampleRate float64 `description:"Sets the rate between 0.0 and 1.0 of requests to trace." json:"sampleRate,omitempty" toml:"sampleRate,omitempty" yaml:"sampleRate,omitempty" export:"true"` + AddInternals bool `description:"Enables tracing for internal services (ping, dashboard, etc...)." json:"addInternals,omitempty" toml:"addInternals,omitempty" yaml:"addInternals,omitempty" export:"true"` + OTLP *types.OTelTracing `description:"Settings for OpenTelemetry." json:"otlp,omitempty" toml:"otlp,omitempty" yaml:"otlp,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` // Deprecated: please use ResourceAttributes instead. GlobalAttributes map[string]string `description:"(Deprecated) Defines additional resource attributes (key:value)." json:"globalAttributes,omitempty" toml:"globalAttributes,omitempty" yaml:"globalAttributes,omitempty" export:"true"` @@ -225,7 +220,7 @@ func (t *Tracing) SetDefaults() { t.ServiceName = "traefik" t.SampleRate = 1.0 - t.OTLP = &otypes.OTelTracing{} + t.OTLP = &types.OTelTracing{} t.OTLP.SetDefaults() } @@ -233,22 +228,22 @@ func (t *Tracing) SetDefaults() { type Providers struct { ProvidersThrottleDuration ptypes.Duration `description:"Backends throttle duration: minimum duration between 2 events from providers before applying a new configuration. It avoids unnecessary reloads if multiples events are sent in a short amount of time." json:"providersThrottleDuration,omitempty" toml:"providersThrottleDuration,omitempty" yaml:"providersThrottleDuration,omitempty" export:"true"` - Docker *docker.Provider `description:"Enables Docker provider." json:"docker,omitempty" toml:"docker,omitempty" yaml:"docker,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` - Swarm *docker.SwarmProvider `description:"Enables Docker Swarm provider." json:"swarm,omitempty" toml:"swarm,omitempty" yaml:"swarm,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` - File *file.Provider `description:"Enables File provider." json:"file,omitempty" toml:"file,omitempty" yaml:"file,omitempty" export:"true"` - KubernetesIngress *ingress.Provider `description:"Enables Kubernetes Ingress provider." json:"kubernetesIngress,omitempty" toml:"kubernetesIngress,omitempty" yaml:"kubernetesIngress,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` - KubernetesIngressNGINX *ingressnginx.Provider `description:"Enables Kubernetes Ingress NGINX provider." json:"kubernetesIngressNGINX,omitempty" toml:"kubernetesIngressNGINX,omitempty" yaml:"kubernetesIngressNGINX,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` - KubernetesCRD *crd.Provider `description:"Enables Kubernetes CRD provider." json:"kubernetesCRD,omitempty" toml:"kubernetesCRD,omitempty" yaml:"kubernetesCRD,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` - KubernetesGateway *gateway.Provider `description:"Enables Kubernetes Gateway API provider." json:"kubernetesGateway,omitempty" toml:"kubernetesGateway,omitempty" yaml:"kubernetesGateway,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` - Rest *rest.Provider `description:"Enables Rest provider." json:"rest,omitempty" toml:"rest,omitempty" yaml:"rest,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` - ConsulCatalog *consulcatalog.ProviderBuilder `description:"Enables Consul Catalog provider." json:"consulCatalog,omitempty" toml:"consulCatalog,omitempty" yaml:"consulCatalog,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` - Nomad *nomad.ProviderBuilder `description:"Enables Nomad provider." json:"nomad,omitempty" toml:"nomad,omitempty" yaml:"nomad,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` - Ecs *ecs.Provider `description:"Enables AWS ECS provider." json:"ecs,omitempty" toml:"ecs,omitempty" yaml:"ecs,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` - Consul *consul.ProviderBuilder `description:"Enables Consul provider." json:"consul,omitempty" toml:"consul,omitempty" yaml:"consul,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` - Etcd *etcd.Provider `description:"Enables Etcd provider." json:"etcd,omitempty" toml:"etcd,omitempty" yaml:"etcd,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` - ZooKeeper *zk.Provider `description:"Enables ZooKeeper provider." json:"zooKeeper,omitempty" toml:"zooKeeper,omitempty" yaml:"zooKeeper,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` - Redis *redis.Provider `description:"Enables Redis provider." json:"redis,omitempty" toml:"redis,omitempty" yaml:"redis,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` - HTTP *http.Provider `description:"Enables HTTP provider." json:"http,omitempty" toml:"http,omitempty" yaml:"http,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` + Docker *docker.Provider `description:"Enable Docker backend with default settings." json:"docker,omitempty" toml:"docker,omitempty" yaml:"docker,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` + Swarm *docker.SwarmProvider `description:"Enable Docker Swarm backend with default settings." json:"swarm,omitempty" toml:"swarm,omitempty" yaml:"swarm,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` + + File *file.Provider `description:"Enable File backend with default settings." json:"file,omitempty" toml:"file,omitempty" yaml:"file,omitempty" export:"true"` + KubernetesIngress *ingress.Provider `description:"Enable Kubernetes backend with default settings." json:"kubernetesIngress,omitempty" toml:"kubernetesIngress,omitempty" yaml:"kubernetesIngress,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` + KubernetesCRD *crd.Provider `description:"Enable Kubernetes backend with default settings." json:"kubernetesCRD,omitempty" toml:"kubernetesCRD,omitempty" yaml:"kubernetesCRD,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` + KubernetesGateway *gateway.Provider `description:"Enable Kubernetes gateway api provider with default settings." json:"kubernetesGateway,omitempty" toml:"kubernetesGateway,omitempty" yaml:"kubernetesGateway,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` + Rest *rest.Provider `description:"Enable Rest backend with default settings." json:"rest,omitempty" toml:"rest,omitempty" yaml:"rest,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` + ConsulCatalog *consulcatalog.ProviderBuilder `description:"Enable ConsulCatalog backend with default settings." json:"consulCatalog,omitempty" toml:"consulCatalog,omitempty" yaml:"consulCatalog,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` + Nomad *nomad.ProviderBuilder `description:"Enable Nomad backend with default settings." json:"nomad,omitempty" toml:"nomad,omitempty" yaml:"nomad,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` + Ecs *ecs.Provider `description:"Enable AWS ECS backend with default settings." json:"ecs,omitempty" toml:"ecs,omitempty" yaml:"ecs,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` + Consul *consul.ProviderBuilder `description:"Enable Consul backend with default settings." json:"consul,omitempty" toml:"consul,omitempty" yaml:"consul,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` + Etcd *etcd.Provider `description:"Enable Etcd backend with default settings." json:"etcd,omitempty" toml:"etcd,omitempty" yaml:"etcd,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` + ZooKeeper *zk.Provider `description:"Enable ZooKeeper backend with default settings." json:"zooKeeper,omitempty" toml:"zooKeeper,omitempty" yaml:"zooKeeper,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` + Redis *redis.Provider `description:"Enable Redis backend with default settings." json:"redis,omitempty" toml:"redis,omitempty" yaml:"redis,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` + HTTP *http.Provider `description:"Enable HTTP backend with default settings." json:"http,omitempty" toml:"http,omitempty" yaml:"http,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` Plugin map[string]PluginConf `description:"Plugins configuration." json:"plugin,omitempty" toml:"plugin,omitempty" yaml:"plugin,omitempty"` } @@ -328,15 +323,6 @@ func (c *Configuration) SetEffectiveConfiguration() { continue } - switch resolver.ACME.DNSChallenge.Provider { - case "googledomains", "cloudxns", "brandit": - log.Warn().Msgf("%s DNS provider is deprecated.", resolver.ACME.DNSChallenge.Provider) - case "dnspod": - log.Warn().Msgf("%s provider is deprecated, please use 'tencentcloud' provider instead.", resolver.ACME.DNSChallenge.Provider) - case "azure": - log.Warn().Msgf("%s provider is deprecated, please use 'azuredns' provider instead.", resolver.ACME.DNSChallenge.Provider) - } - if resolver.ACME.DNSChallenge.DisablePropagationCheck { log.Warn().Msgf("disablePropagationCheck is now deprecated, please use propagation.disableChecks instead.") @@ -358,25 +344,6 @@ func (c *Configuration) SetEffectiveConfiguration() { } } - for _, resolver := range c.CertificatesResolvers { - if resolver.ACME == nil { - continue - } - - if resolver.ACME.DNSChallenge == nil { - continue - } - - switch resolver.ACME.DNSChallenge.Provider { - case "googledomains", "cloudxns", "brandit": - log.Warn().Msgf("%s DNS provider is deprecated.", resolver.ACME.DNSChallenge.Provider) - case "dnspod": - log.Warn().Msgf("%s provider is deprecated, please use 'tencentcloud' provider instead.", resolver.ACME.DNSChallenge.Provider) - case "azure": - log.Warn().Msgf("%s provider is deprecated, please use 'azuredns' provider instead.", resolver.ACME.DNSChallenge.Provider) - } - } - c.initACMEProvider() } @@ -422,16 +389,6 @@ func (c *Configuration) ValidateConfiguration() error { } } - if c.Providers != nil && c.Providers.KubernetesIngressNGINX != nil { - if c.Experimental == nil || !c.Experimental.KubernetesIngressNGINX { - return errors.New("the experimental KubernetesIngressNGINX feature must be enabled to use the KubernetesIngressNGINX provider") - } - - if c.Providers.KubernetesIngressNGINX.WatchNamespace != "" && c.Providers.KubernetesIngressNGINX.WatchNamespaceSelector != "" { - return errors.New("watchNamespace and watchNamespaceSelector options are mutually exclusive") - } - } - if c.AccessLog != nil && c.AccessLog.OTLP != nil { if c.Experimental == nil || !c.Experimental.OTLPLogs { return errors.New("the experimental OTLPLogs feature must be enabled to use OTLP access logging") @@ -468,14 +425,6 @@ func (c *Configuration) ValidateConfiguration() error { return errors.New("API basePath must be a valid absolute path") } - if c.OCSP != nil { - for responderURL, url := range c.OCSP.ResponderOverrides { - if url == "" { - return fmt.Errorf("OCSP responder override value for %s cannot be empty", responderURL) - } - } - } - return nil } diff --git a/pkg/healthcheck/healthcheck.go b/pkg/healthcheck/healthcheck.go index e2679d73c..c9d3e7e36 100644 --- a/pkg/healthcheck/healthcheck.go +++ b/pkg/healthcheck/healthcheck.go @@ -40,27 +40,18 @@ type metricsHealthCheck interface { ServiceServerUpGauge() gokitmetrics.Gauge } -type target struct { - targetURL *url.URL - name string -} - type ServiceHealthChecker struct { balancer StatusSetter info *runtime.ServiceInfo - config *dynamic.ServerHealthCheck - interval time.Duration - unhealthyInterval time.Duration - timeout time.Duration + config *dynamic.ServerHealthCheck + interval time.Duration + timeout time.Duration metrics metricsHealthCheck - client *http.Client - - healthyTargets chan target - unhealthyTargets chan target - + client *http.Client + targets map[string]*url.URL serviceName string } @@ -69,26 +60,13 @@ func NewServiceHealthChecker(ctx context.Context, metrics metricsHealthCheck, co interval := time.Duration(config.Interval) if interval <= 0 { - logger.Error().Msg("Health check interval smaller than zero, default value will be used instead.") + logger.Error().Msg("Health check interval smaller than zero") interval = time.Duration(dynamic.DefaultHealthCheckInterval) } - // If the unhealthyInterval option is not set, we use the interval option value, - // to check the unhealthy targets as often as the healthy ones. - var unhealthyInterval time.Duration - if config.UnhealthyInterval == nil { - unhealthyInterval = interval - } else { - unhealthyInterval = time.Duration(*config.UnhealthyInterval) - if unhealthyInterval <= 0 { - logger.Error().Msg("Health check unhealthy interval smaller than zero, default value will be used instead.") - unhealthyInterval = time.Duration(dynamic.DefaultHealthCheckInterval) - } - } - timeout := time.Duration(config.Timeout) if timeout <= 0 { - logger.Error().Msg("Health check timeout smaller than zero, default value will be used instead.") + logger.Error().Msg("Health check timeout smaller than zero") timeout = time.Duration(dynamic.DefaultHealthCheckTimeout) } @@ -102,38 +80,21 @@ func NewServiceHealthChecker(ctx context.Context, metrics metricsHealthCheck, co } } - healthyTargets := make(chan target, len(targets)) - for name, targetURL := range targets { - healthyTargets <- target{ - targetURL: targetURL, - name: name, - } - } - unhealthyTargets := make(chan target, len(targets)) - return &ServiceHealthChecker{ - balancer: service, - info: info, - config: config, - interval: interval, - unhealthyInterval: unhealthyInterval, - timeout: timeout, - healthyTargets: healthyTargets, - unhealthyTargets: unhealthyTargets, - serviceName: serviceName, - client: client, - metrics: metrics, + balancer: service, + info: info, + config: config, + interval: interval, + timeout: timeout, + targets: targets, + serviceName: serviceName, + client: client, + metrics: metrics, } } func (shc *ServiceHealthChecker) Launch(ctx context.Context) { - go shc.healthcheck(ctx, shc.unhealthyTargets, shc.unhealthyInterval) - - shc.healthcheck(ctx, shc.healthyTargets, shc.interval) -} - -func (shc *ServiceHealthChecker) healthcheck(ctx context.Context, targets chan target, interval time.Duration) { - ticker := time.NewTicker(interval) + ticker := time.NewTicker(shc.interval) defer ticker.Stop() for { @@ -142,23 +103,7 @@ func (shc *ServiceHealthChecker) healthcheck(ctx context.Context, targets chan t return case <-ticker.C: - // We collect the targets to check once for all, - // to avoid rechecking a target that has been moved during the health check. - var targetsToCheck []target - hasMoreTargets := true - for hasMoreTargets { - select { - case <-ctx.Done(): - return - case target := <-targets: - targetsToCheck = append(targetsToCheck, target) - default: - hasMoreTargets = false - } - } - - // Now we can check the targets. - for _, target := range targetsToCheck { + for proxyName, target := range shc.targets { select { case <-ctx.Done(): return @@ -168,14 +113,14 @@ func (shc *ServiceHealthChecker) healthcheck(ctx context.Context, targets chan t up := true serverUpMetricValue := float64(1) - if err := shc.executeHealthCheck(ctx, shc.config, target.targetURL); err != nil { + if err := shc.executeHealthCheck(ctx, shc.config, target); err != nil { // The context is canceled when the dynamic configuration is refreshed. if errors.Is(err, context.Canceled) { return } log.Ctx(ctx).Warn(). - Str("targetURL", target.targetURL.String()). + Str("targetURL", target.String()). Err(err). Msg("Health check failed.") @@ -183,21 +128,17 @@ func (shc *ServiceHealthChecker) healthcheck(ctx context.Context, targets chan t serverUpMetricValue = float64(0) } - shc.balancer.SetStatus(ctx, target.name, up) + shc.balancer.SetStatus(ctx, proxyName, up) - var statusStr string + statusStr := runtime.StatusDown if up { statusStr = runtime.StatusUp - shc.healthyTargets <- target - } else { - statusStr = runtime.StatusDown - shc.unhealthyTargets <- target } - shc.info.UpdateServerStatus(target.targetURL.String(), statusStr) + shc.info.UpdateServerStatus(target.String(), statusStr) shc.metrics.ServiceServerUpGauge(). - With("service", shc.serviceName, "url", target.targetURL.String()). + With("service", shc.serviceName, "url", target.String()). Set(serverUpMetricValue) } } diff --git a/pkg/healthcheck/healthcheck_test.go b/pkg/healthcheck/healthcheck_test.go index 056a62a49..a977635d1 100644 --- a/pkg/healthcheck/healthcheck_test.go +++ b/pkg/healthcheck/healthcheck_test.go @@ -66,7 +66,7 @@ func TestNewServiceHealthChecker_durations(t *testing.T) { for _, test := range testCases { t.Run(test.desc, func(t *testing.T) { - healthChecker := NewServiceHealthChecker(t.Context(), nil, test.config, nil, nil, http.DefaultTransport, nil, "") + healthChecker := NewServiceHealthChecker(context.Background(), nil, test.config, nil, nil, http.DefaultTransport, nil, "") assert.Equal(t, test.expInterval, healthChecker.interval) assert.Equal(t, test.expTimeout, healthChecker.timeout) }) @@ -251,7 +251,7 @@ func TestServiceHealthChecker_newRequest(t *testing.T) { shc := ServiceHealthChecker{config: &test.config} u := testhelpers.MustParseURL(test.targetURL) - req, err := shc.newRequest(t.Context(), u) + req, err := shc.newRequest(context.Background(), u) if test.expError { require.Error(t, err) @@ -276,7 +276,7 @@ func TestServiceHealthChecker_checkHealthHTTP_NotFollowingRedirects(t *testing.T })) defer redirectTestServer.Close() - ctx, cancel := context.WithTimeout(t.Context(), time.Duration(dynamic.DefaultHealthCheckTimeout)) + ctx, cancel := context.WithTimeout(context.Background(), time.Duration(dynamic.DefaultHealthCheckTimeout)) defer cancel() server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { @@ -411,7 +411,7 @@ func TestServiceHealthChecker_Launch(t *testing.T) { // The context is passed to the health check and // canonically canceled by the test server once all expected requests have been received. - ctx, cancel := context.WithCancel(t.Context()) + ctx, cancel := context.WithCancel(context.Background()) t.Cleanup(cancel) targetURL, timeout := test.server.Start(t, cancel) @@ -419,12 +419,11 @@ func TestServiceHealthChecker_Launch(t *testing.T) { lb := &testLoadBalancer{RWMutex: &sync.RWMutex{}} config := &dynamic.ServerHealthCheck{ - Mode: test.mode, - Status: test.status, - Path: "/path", - Interval: ptypes.Duration(500 * time.Millisecond), - UnhealthyInterval: pointer(ptypes.Duration(500 * time.Millisecond)), - Timeout: ptypes.Duration(499 * time.Millisecond), + Mode: test.mode, + Status: test.status, + Path: "/path", + Interval: ptypes.Duration(500 * time.Millisecond), + Timeout: ptypes.Duration(499 * time.Millisecond), } gauge := &testhelpers.CollectingGauge{} @@ -457,54 +456,3 @@ func TestServiceHealthChecker_Launch(t *testing.T) { }) } } - -func TestDifferentIntervals(t *testing.T) { - // The context is passed to the health check and - // canonically canceled by the test server once all expected requests have been received. - ctx, cancel := context.WithCancel(t.Context()) - t.Cleanup(cancel) - - healthyServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - w.WriteHeader(http.StatusOK) - })) - healthyURL := testhelpers.MustParseURL(healthyServer.URL) - - unhealthyServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - w.WriteHeader(http.StatusServiceUnavailable) - })) - unhealthyURL := testhelpers.MustParseURL(unhealthyServer.URL) - - lb := &testLoadBalancer{RWMutex: &sync.RWMutex{}} - - config := &dynamic.ServerHealthCheck{ - Mode: "http", - Path: "/path", - Interval: ptypes.Duration(500 * time.Millisecond), - UnhealthyInterval: pointer(ptypes.Duration(50 * time.Millisecond)), - Timeout: ptypes.Duration(499 * time.Millisecond), - } - - gauge := &testhelpers.CollectingGauge{} - serviceInfo := &runtime.ServiceInfo{} - hc := NewServiceHealthChecker(ctx, &MetricsMock{gauge}, config, lb, serviceInfo, http.DefaultTransport, map[string]*url.URL{"healthy": healthyURL, "unhealthy": unhealthyURL}, "foobar") - - wg := sync.WaitGroup{} - wg.Add(1) - - go func() { - hc.Launch(ctx) - wg.Done() - }() - - select { - case <-time.After(2 * time.Second): - break - case <-ctx.Done(): - wg.Wait() - } - - lb.Lock() - defer lb.Unlock() - - assert.Greater(t, lb.numRemovedServers, lb.numUpsertedServers, "removed servers greater than upserted servers") -} diff --git a/pkg/healthcheck/mock_test.go b/pkg/healthcheck/mock_test.go index 483a5385d..ba288cc26 100644 --- a/pkg/healthcheck/mock_test.go +++ b/pkg/healthcheck/mock_test.go @@ -64,10 +64,6 @@ func newGRPCServer(healthSequence ...healthpb.HealthCheckResponse_ServingStatus) return gRPCService } -func (s *GRPCServer) List(_ context.Context, _ *healthpb.HealthListRequest) (*healthpb.HealthListResponse, error) { - return nil, nil -} - func (s *GRPCServer) Check(_ context.Context, _ *healthpb.HealthCheckRequest) (*healthpb.HealthCheckResponse, error) { if s.status.IsEmpty() { s.done() diff --git a/pkg/observability/logs/aws.go b/pkg/logs/aws.go similarity index 100% rename from pkg/observability/logs/aws.go rename to pkg/logs/aws.go diff --git a/pkg/observability/logs/aws_test.go b/pkg/logs/aws_test.go similarity index 100% rename from pkg/observability/logs/aws_test.go rename to pkg/logs/aws_test.go diff --git a/pkg/observability/logs/datadog.go b/pkg/logs/datadog.go similarity index 100% rename from pkg/observability/logs/datadog.go rename to pkg/logs/datadog.go diff --git a/pkg/observability/logs/datadog_test.go b/pkg/logs/datadog_test.go similarity index 100% rename from pkg/observability/logs/datadog_test.go rename to pkg/logs/datadog_test.go diff --git a/pkg/observability/logs/elastic.go b/pkg/logs/elastic.go similarity index 100% rename from pkg/observability/logs/elastic.go rename to pkg/logs/elastic.go diff --git a/pkg/observability/logs/elastic_test.go b/pkg/logs/elastic_test.go similarity index 100% rename from pkg/observability/logs/elastic_test.go rename to pkg/logs/elastic_test.go diff --git a/pkg/observability/logs/fields.go b/pkg/logs/fields.go similarity index 100% rename from pkg/observability/logs/fields.go rename to pkg/logs/fields.go diff --git a/pkg/observability/logs/gokit.go b/pkg/logs/gokit.go similarity index 100% rename from pkg/observability/logs/gokit.go rename to pkg/logs/gokit.go diff --git a/pkg/observability/logs/gokit_test.go b/pkg/logs/gokit_test.go similarity index 100% rename from pkg/observability/logs/gokit_test.go rename to pkg/logs/gokit_test.go diff --git a/pkg/observability/logs/hclog.go b/pkg/logs/hclog.go similarity index 100% rename from pkg/observability/logs/hclog.go rename to pkg/logs/hclog.go diff --git a/pkg/observability/logs/hclog_test.go b/pkg/logs/hclog_test.go similarity index 100% rename from pkg/observability/logs/hclog_test.go rename to pkg/logs/hclog_test.go diff --git a/pkg/observability/logs/instana.go b/pkg/logs/instana.go similarity index 100% rename from pkg/observability/logs/instana.go rename to pkg/logs/instana.go diff --git a/pkg/observability/logs/instana_test.go b/pkg/logs/instana_test.go similarity index 100% rename from pkg/observability/logs/instana_test.go rename to pkg/logs/instana_test.go diff --git a/pkg/observability/logs/log.go b/pkg/logs/log.go similarity index 100% rename from pkg/observability/logs/log.go rename to pkg/logs/log.go diff --git a/pkg/observability/logs/log_test.go b/pkg/logs/log_test.go similarity index 100% rename from pkg/observability/logs/log_test.go rename to pkg/logs/log_test.go diff --git a/pkg/observability/logs/logrus.go b/pkg/logs/logrus.go similarity index 100% rename from pkg/observability/logs/logrus.go rename to pkg/logs/logrus.go diff --git a/pkg/observability/logs/logrus_test.go b/pkg/logs/logrus_test.go similarity index 100% rename from pkg/observability/logs/logrus_test.go rename to pkg/logs/logrus_test.go diff --git a/pkg/observability/logs/otel.go b/pkg/logs/otel.go similarity index 88% rename from pkg/observability/logs/otel.go rename to pkg/logs/otel.go index f56f102f2..bc8f95443 100644 --- a/pkg/observability/logs/otel.go +++ b/pkg/logs/otel.go @@ -1,28 +1,23 @@ package logs import ( - "context" "encoding/json" "fmt" "reflect" "time" "github.com/rs/zerolog" - "github.com/traefik/traefik/v3/pkg/observability" - "github.com/traefik/traefik/v3/pkg/observability/types" + "github.com/traefik/traefik/v3/pkg/types" otellog "go.opentelemetry.io/otel/log" ) // SetupOTelLogger sets up the OpenTelemetry logger. -func SetupOTelLogger(ctx context.Context, logger zerolog.Logger, config *types.OTelLog) (zerolog.Logger, error) { +func SetupOTelLogger(logger zerolog.Logger, config *types.OTelLog) (zerolog.Logger, error) { if config == nil { return logger, nil } - if err := observability.EnsureUserEnvVar(); err != nil { - return zerolog.Logger{}, err - } - provider, err := config.NewLoggerProvider(ctx) + provider, err := config.NewLoggerProvider() if err != nil { return zerolog.Logger{}, fmt.Errorf("setting up OpenTelemetry logger provider: %w", err) } @@ -41,6 +36,9 @@ func (h *otelLoggerHook) Run(e *zerolog.Event, level zerolog.Level, message stri return } + // Discard the event to avoid double logging. + e.Discard() + var record otellog.Record record.SetTimestamp(time.Now().UTC()) record.SetSeverity(otelLogSeverity(level)) diff --git a/pkg/observability/logs/otel_test.go b/pkg/logs/otel_test.go similarity index 95% rename from pkg/observability/logs/otel_test.go rename to pkg/logs/otel_test.go index c51322cdd..60c68cc09 100644 --- a/pkg/observability/logs/otel_test.go +++ b/pkg/logs/otel_test.go @@ -2,6 +2,7 @@ package logs import ( "compress/gzip" + "context" "encoding/json" "io" "net/http" @@ -13,7 +14,7 @@ import ( "github.com/rs/zerolog" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - otypes "github.com/traefik/traefik/v3/pkg/observability/types" + "github.com/traefik/traefik/v3/pkg/types" "go.opentelemetry.io/collector/pdata/plog/plogotlp" "go.opentelemetry.io/otel/trace" ) @@ -160,10 +161,10 @@ func TestLog(t *testing.T) { for _, test := range tests { t.Run(test.desc, func(t *testing.T) { - config := &otypes.OTelLog{ + config := &types.OTelLog{ ServiceName: "test", ResourceAttributes: map[string]string{"resource": "attribute"}, - HTTP: &otypes.OTelHTTP{ + HTTP: &types.OTelHTTP{ Endpoint: collector.URL, }, } @@ -171,10 +172,10 @@ func TestLog(t *testing.T) { out := zerolog.MultiLevelWriter(zerolog.ConsoleWriter{Out: os.Stderr, TimeFormat: time.RFC3339}) logger := zerolog.New(out).With().Caller().Logger() - logger, err := SetupOTelLogger(t.Context(), logger, config) + logger, err := SetupOTelLogger(logger, config) require.NoError(t, err) - ctx := trace.ContextWithSpanContext(t.Context(), trace.NewSpanContext(trace.SpanContextConfig{ + ctx := trace.ContextWithSpanContext(context.Background(), trace.NewSpanContext(trace.SpanContextConfig{ TraceID: trace.TraceID{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8}, SpanID: trace.SpanID{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8}, })) diff --git a/pkg/observability/logs/oxy.go b/pkg/logs/oxy.go similarity index 100% rename from pkg/observability/logs/oxy.go rename to pkg/logs/oxy.go diff --git a/pkg/observability/logs/oxy_test.go b/pkg/logs/oxy_test.go similarity index 100% rename from pkg/observability/logs/oxy_test.go rename to pkg/logs/oxy_test.go diff --git a/pkg/observability/logs/wasm.go b/pkg/logs/wasm.go similarity index 100% rename from pkg/observability/logs/wasm.go rename to pkg/logs/wasm.go diff --git a/pkg/observability/metrics/datadog.go b/pkg/metrics/datadog.go similarity index 95% rename from pkg/observability/metrics/datadog.go rename to pkg/metrics/datadog.go index 6b97f650e..89e7992a1 100644 --- a/pkg/observability/metrics/datadog.go +++ b/pkg/metrics/datadog.go @@ -10,9 +10,9 @@ import ( "github.com/go-kit/kit/util/conn" gokitlog "github.com/go-kit/log" "github.com/rs/zerolog/log" - "github.com/traefik/traefik/v3/pkg/observability/logs" - otypes "github.com/traefik/traefik/v3/pkg/observability/types" + "github.com/traefik/traefik/v3/pkg/logs" "github.com/traefik/traefik/v3/pkg/safe" + "github.com/traefik/traefik/v3/pkg/types" ) const ( @@ -56,7 +56,7 @@ const ( ) // RegisterDatadog registers the metrics pusher if this didn't happen yet and creates a datadog Registry instance. -func RegisterDatadog(ctx context.Context, config *otypes.Datadog) Registry { +func RegisterDatadog(ctx context.Context, config *types.Datadog) Registry { // Ensures there is only one DataDog client sending metrics at any given time. StopDatadog() @@ -109,7 +109,7 @@ func RegisterDatadog(ctx context.Context, config *otypes.Datadog) Registry { return registry } -func initDatadogClient(ctx context.Context, config *otypes.Datadog, logger gokitlog.LoggerFunc) { +func initDatadogClient(ctx context.Context, config *types.Datadog, logger gokitlog.LoggerFunc) { network, address := parseDatadogAddress(config.Address) ctx, datadogLoopCancelFunc = context.WithCancel(ctx) diff --git a/pkg/observability/metrics/datadog_test.go b/pkg/metrics/datadog_test.go similarity index 93% rename from pkg/observability/metrics/datadog_test.go rename to pkg/metrics/datadog_test.go index bf4a044e1..0fc5bd758 100644 --- a/pkg/observability/metrics/datadog_test.go +++ b/pkg/metrics/datadog_test.go @@ -1,6 +1,7 @@ package metrics import ( + "context" "net/http" "strconv" "testing" @@ -9,7 +10,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stvp/go-udp-testing" ptypes "github.com/traefik/paerser/types" - otypes "github.com/traefik/traefik/v3/pkg/observability/types" + "github.com/traefik/traefik/v3/pkg/types" ) func TestDatadog(t *testing.T) { @@ -19,7 +20,7 @@ func TestDatadog(t *testing.T) { // This is needed to make sure that UDP Listener listens for data a bit longer, otherwise it will quit after a millisecond udp.Timeout = 5 * time.Second - datadogRegistry := RegisterDatadog(t.Context(), &otypes.Datadog{Address: ":18125", PushInterval: ptypes.Duration(time.Second), AddEntryPointsLabels: true, AddRoutersLabels: true, AddServicesLabels: true}) + datadogRegistry := RegisterDatadog(context.Background(), &types.Datadog{Address: ":18125", PushInterval: ptypes.Duration(time.Second), AddEntryPointsLabels: true, AddRoutersLabels: true, AddServicesLabels: true}) if !datadogRegistry.IsEpEnabled() || !datadogRegistry.IsRouterEnabled() || !datadogRegistry.IsSvcEnabled() { t.Errorf("DatadogRegistry should return true for IsEnabled(), IsRouterEnabled() and IsSvcEnabled()") @@ -34,7 +35,7 @@ func TestDatadogWithPrefix(t *testing.T) { // This is needed to make sure that UDP Listener listens for data a bit longer, otherwise it will quit after a millisecond udp.Timeout = 5 * time.Second - datadogRegistry := RegisterDatadog(t.Context(), &otypes.Datadog{Prefix: "testPrefix", Address: ":18125", PushInterval: ptypes.Duration(time.Second), AddEntryPointsLabels: true, AddRoutersLabels: true, AddServicesLabels: true}) + datadogRegistry := RegisterDatadog(context.Background(), &types.Datadog{Prefix: "testPrefix", Address: ":18125", PushInterval: ptypes.Duration(time.Second), AddEntryPointsLabels: true, AddRoutersLabels: true, AddServicesLabels: true}) testDatadogRegistry(t, "testPrefix", datadogRegistry) } diff --git a/pkg/observability/metrics/headers.go b/pkg/metrics/headers.go similarity index 100% rename from pkg/observability/metrics/headers.go rename to pkg/metrics/headers.go diff --git a/pkg/observability/metrics/influxdb2.go b/pkg/metrics/influxdb2.go similarity index 95% rename from pkg/observability/metrics/influxdb2.go rename to pkg/metrics/influxdb2.go index aaa159391..270ac03a9 100644 --- a/pkg/observability/metrics/influxdb2.go +++ b/pkg/metrics/influxdb2.go @@ -12,9 +12,9 @@ import ( influxdb2log "github.com/influxdata/influxdb-client-go/v2/log" influxdb "github.com/influxdata/influxdb1-client/v2" "github.com/rs/zerolog/log" - "github.com/traefik/traefik/v3/pkg/observability/logs" - otypes "github.com/traefik/traefik/v3/pkg/observability/types" + "github.com/traefik/traefik/v3/pkg/logs" "github.com/traefik/traefik/v3/pkg/safe" + "github.com/traefik/traefik/v3/pkg/types" ) var ( @@ -52,7 +52,7 @@ const ( ) // RegisterInfluxDB2 creates metrics exporter for InfluxDB2. -func RegisterInfluxDB2(ctx context.Context, config *otypes.InfluxDB2) Registry { +func RegisterInfluxDB2(ctx context.Context, config *types.InfluxDB2) Registry { logger := log.Ctx(ctx) if influxDB2Client == nil { @@ -133,7 +133,7 @@ func StopInfluxDB2() { } // newInfluxDB2Client creates an influxdb2.Client. -func newInfluxDB2Client(config *otypes.InfluxDB2) (influxdb2.Client, error) { +func newInfluxDB2Client(config *types.InfluxDB2) (influxdb2.Client, error) { if config.Token == "" || config.Org == "" || config.Bucket == "" { return nil, errors.New("token, org or bucket property is missing") } diff --git a/pkg/observability/metrics/influxdb2_test.go b/pkg/metrics/influxdb2_test.go similarity index 98% rename from pkg/observability/metrics/influxdb2_test.go rename to pkg/metrics/influxdb2_test.go index daf2d0f96..e75141ff4 100644 --- a/pkg/observability/metrics/influxdb2_test.go +++ b/pkg/metrics/influxdb2_test.go @@ -1,6 +1,7 @@ package metrics import ( + "context" "fmt" "io" "net/http" @@ -11,7 +12,7 @@ import ( "github.com/stretchr/testify/require" ptypes "github.com/traefik/paerser/types" - otypes "github.com/traefik/traefik/v3/pkg/observability/types" + "github.com/traefik/traefik/v3/pkg/types" ) func TestInfluxDB2(t *testing.T) { @@ -25,8 +26,8 @@ func TestInfluxDB2(t *testing.T) { _, _ = fmt.Fprintln(w, "ok") })) - influxDB2Registry := RegisterInfluxDB2(t.Context(), - &otypes.InfluxDB2{ + influxDB2Registry := RegisterInfluxDB2(context.Background(), + &types.InfluxDB2{ Address: ts.URL, Token: "test-token", PushInterval: ptypes.Duration(10 * time.Millisecond), diff --git a/pkg/observability/metrics/metrics.go b/pkg/metrics/metrics.go similarity index 100% rename from pkg/observability/metrics/metrics.go rename to pkg/metrics/metrics.go diff --git a/pkg/observability/metrics/metrics_test.go b/pkg/metrics/metrics_test.go similarity index 100% rename from pkg/observability/metrics/metrics_test.go rename to pkg/metrics/metrics_test.go diff --git a/pkg/observability/metrics/otel.go b/pkg/metrics/otel.go similarity index 88% rename from pkg/observability/metrics/otel.go rename to pkg/metrics/otel.go index eaf95e736..32e48a5d0 100644 --- a/pkg/observability/metrics/otel.go +++ b/pkg/metrics/otel.go @@ -11,8 +11,6 @@ import ( "github.com/go-kit/kit/metrics" "github.com/rs/zerolog/log" - "github.com/traefik/traefik/v3/pkg/observability" - otypes "github.com/traefik/traefik/v3/pkg/observability/types" "github.com/traefik/traefik/v3/pkg/types" "github.com/traefik/traefik/v3/pkg/version" "go.opentelemetry.io/otel" @@ -22,8 +20,7 @@ import ( "go.opentelemetry.io/otel/metric" sdkmetric "go.opentelemetry.io/otel/sdk/metric" "go.opentelemetry.io/otel/sdk/resource" - semconv "go.opentelemetry.io/otel/semconv/v1.37.0" - "go.opentelemetry.io/otel/semconv/v1.37.0/httpconv" + semconv "go.opentelemetry.io/otel/semconv/v1.26.0" "google.golang.org/grpc/credentials" "google.golang.org/grpc/encoding/gzip" ) @@ -42,17 +39,13 @@ func SetMeterProvider(meterProvider *sdkmetric.MeterProvider) { // SemConvMetricsRegistry holds stables semantic conventions metric instruments. type SemConvMetricsRegistry struct { // server metrics - httpServerRequestDuration httpconv.ServerRequestDuration + httpServerRequestDuration metric.Float64Histogram // client metrics - httpClientRequestDuration httpconv.ClientRequestDuration + httpClientRequestDuration metric.Float64Histogram } // NewSemConvMetricRegistry registers all stables semantic conventions metrics. -func NewSemConvMetricRegistry(ctx context.Context, config *otypes.OTLP) (*SemConvMetricsRegistry, error) { - if err := observability.EnsureUserEnvVar(); err != nil { - return nil, err - } - +func NewSemConvMetricRegistry(ctx context.Context, config *types.OTLP) (*SemConvMetricsRegistry, error) { if openTelemetryMeterProvider == nil { var err error if openTelemetryMeterProvider, err = newOpenTelemetryMeterProvider(ctx, config); err != nil { @@ -65,13 +58,17 @@ func NewSemConvMetricRegistry(ctx context.Context, config *otypes.OTLP) (*SemCon meter := otel.Meter("github.com/traefik/traefik", metric.WithInstrumentationVersion(version.Version)) - httpServerRequestDuration, err := httpconv.NewServerRequestDuration(meter, + httpServerRequestDuration, err := meter.Float64Histogram(semconv.HTTPServerRequestDurationName, + metric.WithDescription(semconv.HTTPServerRequestDurationDescription), + metric.WithUnit("s"), metric.WithExplicitBucketBoundaries(config.ExplicitBoundaries...)) if err != nil { return nil, fmt.Errorf("can't build httpServerRequestDuration histogram: %w", err) } - httpClientRequestDuration, err := httpconv.NewClientRequestDuration(meter, + httpClientRequestDuration, err := meter.Float64Histogram(semconv.HTTPClientRequestDurationName, + metric.WithDescription(semconv.HTTPClientRequestDurationDescription), + metric.WithUnit("s"), metric.WithExplicitBucketBoundaries(config.ExplicitBoundaries...)) if err != nil { return nil, fmt.Errorf("can't build httpClientRequestDuration histogram: %w", err) @@ -84,25 +81,25 @@ func NewSemConvMetricRegistry(ctx context.Context, config *otypes.OTLP) (*SemCon } // HTTPServerRequestDuration returns the HTTP server request duration histogram. -func (s *SemConvMetricsRegistry) HTTPServerRequestDuration() httpconv.ServerRequestDuration { +func (s *SemConvMetricsRegistry) HTTPServerRequestDuration() metric.Float64Histogram { if s == nil { - return httpconv.ServerRequestDuration{} + return nil } return s.httpServerRequestDuration } // HTTPClientRequestDuration returns the HTTP client request duration histogram. -func (s *SemConvMetricsRegistry) HTTPClientRequestDuration() httpconv.ClientRequestDuration { +func (s *SemConvMetricsRegistry) HTTPClientRequestDuration() metric.Float64Histogram { if s == nil { - return httpconv.ClientRequestDuration{} + return nil } return s.httpClientRequestDuration } // RegisterOpenTelemetry registers all OpenTelemetry metrics. -func RegisterOpenTelemetry(ctx context.Context, config *otypes.OTLP) Registry { +func RegisterOpenTelemetry(ctx context.Context, config *types.OTLP) Registry { if openTelemetryMeterProvider == nil { var err error if openTelemetryMeterProvider, err = newOpenTelemetryMeterProvider(ctx, config); err != nil { @@ -125,7 +122,7 @@ func RegisterOpenTelemetry(ctx context.Context, config *otypes.OTLP) Registry { configReloadsCounter: newOTLPCounterFrom(meter, configReloadsTotalName, "Config reloads"), lastConfigReloadSuccessGauge: newOTLPGaugeFrom(meter, configLastReloadSuccessName, "Last config reload success", "ms"), openConnectionsGauge: newOTLPGaugeFrom(meter, openConnectionsName, "How many open connections exist, by entryPoint and protocol", "1"), - tlsCertsNotAfterTimestampGauge: newOTLPGaugeFrom(meter, tlsCertsNotAfterTimestampName, "Certificate expiration timestamp", "s"), + tlsCertsNotAfterTimestampGauge: newOTLPGaugeFrom(meter, tlsCertsNotAfterTimestampName, "Certificate expiration timestamp", "ms"), } if config.AddEntryPointsLabels { @@ -195,7 +192,7 @@ func StopOpenTelemetry() { } // newOpenTelemetryMeterProvider creates a new controller.Controller. -func newOpenTelemetryMeterProvider(ctx context.Context, config *otypes.OTLP) (*sdkmetric.MeterProvider, error) { +func newOpenTelemetryMeterProvider(ctx context.Context, config *types.OTLP) (*sdkmetric.MeterProvider, error) { var ( exporter sdkmetric.Exporter err error @@ -209,27 +206,11 @@ func newOpenTelemetryMeterProvider(ctx context.Context, config *otypes.OTLP) (*s return nil, fmt.Errorf("creating exporter: %w", err) } - var resAttrs []attribute.KeyValue - for k, v := range config.ResourceAttributes { - resAttrs = append(resAttrs, attribute.String(k, v)) - } - res, err := resource.New(ctx, - resource.WithContainer(), - resource.WithHost(), - resource.WithOS(), - resource.WithProcess(), - resource.WithTelemetrySDK(), - resource.WithDetectors(types.K8sAttributesDetector{}), - // The following order allows the user to override the service name and version, - // as well as any other attributes set by the above detectors. - resource.WithAttributes( - semconv.ServiceName(config.ServiceName), - semconv.ServiceVersion(version.Version), - ), - resource.WithAttributes(resAttrs...), - // Use the environment variables to allow overriding above resource attributes. + resource.WithAttributes(semconv.ServiceNameKey.String(config.ServiceName)), + resource.WithAttributes(semconv.ServiceVersionKey.String(version.Version)), resource.WithFromEnv(), + resource.WithTelemetrySDK(), ) if err != nil { return nil, fmt.Errorf("building resource: %w", err) @@ -256,7 +237,7 @@ func newOpenTelemetryMeterProvider(ctx context.Context, config *otypes.OTLP) (*s return meterProvider, nil } -func newHTTPExporter(ctx context.Context, config *otypes.OTelHTTP) (sdkmetric.Exporter, error) { +func newHTTPExporter(ctx context.Context, config *types.OTelHTTP) (sdkmetric.Exporter, error) { endpoint, err := url.Parse(config.Endpoint) if err != nil { return nil, fmt.Errorf("invalid collector endpoint %q: %w", config.Endpoint, err) @@ -288,7 +269,7 @@ func newHTTPExporter(ctx context.Context, config *otypes.OTelHTTP) (sdkmetric.Ex return otlpmetrichttp.New(ctx, opts...) } -func newGRPCExporter(ctx context.Context, config *otypes.OTelGRPC) (sdkmetric.Exporter, error) { +func newGRPCExporter(ctx context.Context, config *types.OTelGRPC) (sdkmetric.Exporter, error) { host, port, err := net.SplitHostPort(config.Endpoint) if err != nil { return nil, fmt.Errorf("invalid collector endpoint %q: %w", config.Endpoint, err) diff --git a/pkg/observability/metrics/otel_test.go b/pkg/metrics/otel_test.go similarity index 98% rename from pkg/observability/metrics/otel_test.go rename to pkg/metrics/otel_test.go index abcb1856f..d9af5a090 100644 --- a/pkg/observability/metrics/otel_test.go +++ b/pkg/metrics/otel_test.go @@ -2,6 +2,7 @@ package metrics import ( "compress/gzip" + "context" "encoding/json" "fmt" "io" @@ -15,7 +16,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ptypes "github.com/traefik/paerser/types" - otypes "github.com/traefik/traefik/v3/pkg/observability/types" + "github.com/traefik/traefik/v3/pkg/types" "github.com/traefik/traefik/v3/pkg/version" "go.opentelemetry.io/collector/pdata/pmetric/pmetricotlp" "go.opentelemetry.io/otel/attribute" @@ -323,10 +324,10 @@ func TestOpenTelemetry(t *testing.T) { ts.Close() }) - var cfg otypes.OTLP + var cfg types.OTLP (&cfg).SetDefaults() cfg.AddRoutersLabels = true - cfg.HTTP = &otypes.OTelHTTP{ + cfg.HTTP = &types.OTelHTTP{ Endpoint: ts.URL, } cfg.PushInterval = ptypes.Duration(10 * time.Millisecond) @@ -337,7 +338,7 @@ func TestOpenTelemetry(t *testing.T) { wantServiceName = test.serviceName } - registry := RegisterOpenTelemetry(t.Context(), &cfg) + registry := RegisterOpenTelemetry(context.Background(), &cfg) require.NotNil(t, registry) if !registry.IsEpEnabled() || !registry.IsRouterEnabled() || !registry.IsSvcEnabled() { @@ -365,7 +366,7 @@ func TestOpenTelemetry(t *testing.T) { tryAssertMessage(t, c, expectedConfig) expectedTLSCerts := []string{ - `({"name":"traefik_tls_certs_not_after","description":"Certificate expiration timestamp","unit":"s","gauge":{"dataPoints":\[{"attributes":\[{"key":"key","value":{"stringValue":"value"}}\],"startTimeUnixNano":"[\d]{19}","timeUnixNano":"[\d]{19}","asDouble":1}\]}})`, + `({"name":"traefik_tls_certs_not_after","description":"Certificate expiration timestamp","unit":"ms","gauge":{"dataPoints":\[{"attributes":\[{"key":"key","value":{"stringValue":"value"}}\],"startTimeUnixNano":"[\d]{19}","timeUnixNano":"[\d]{19}","asDouble":1}\]}})`, } registry.TLSCertsNotAfterTimestampGauge().With("key", "value").Set(1) diff --git a/pkg/observability/metrics/prometheus.go b/pkg/metrics/prometheus.go similarity index 99% rename from pkg/observability/metrics/prometheus.go rename to pkg/metrics/prometheus.go index f5a0ff735..608acf16a 100644 --- a/pkg/observability/metrics/prometheus.go +++ b/pkg/metrics/prometheus.go @@ -13,7 +13,7 @@ import ( "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/rs/zerolog/log" "github.com/traefik/traefik/v3/pkg/config/dynamic" - otypes "github.com/traefik/traefik/v3/pkg/observability/types" + "github.com/traefik/traefik/v3/pkg/types" ) const ( @@ -80,7 +80,7 @@ func PrometheusHandler() http.Handler { // RegisterPrometheus registers all Prometheus metrics. // It must be called only once and failing to register the metrics will lead to a panic. -func RegisterPrometheus(ctx context.Context, config *otypes.Prometheus) Registry { +func RegisterPrometheus(ctx context.Context, config *types.Prometheus) Registry { standardRegistry := initStandardRegistry(config) if err := promRegistry.Register(collectors.NewProcessCollector(collectors.ProcessCollectorOpts{})); err != nil { @@ -104,7 +104,7 @@ func RegisterPrometheus(ctx context.Context, config *otypes.Prometheus) Registry return standardRegistry } -func initStandardRegistry(config *otypes.Prometheus) Registry { +func initStandardRegistry(config *types.Prometheus) Registry { buckets := []float64{0.1, 0.3, 1.2, 5.0} if config.Buckets != nil { buckets = config.Buckets diff --git a/pkg/observability/metrics/prometheus_test.go b/pkg/metrics/prometheus_test.go similarity index 96% rename from pkg/observability/metrics/prometheus_test.go rename to pkg/metrics/prometheus_test.go index f3b8c9f49..91e5a56fe 100644 --- a/pkg/observability/metrics/prometheus_test.go +++ b/pkg/metrics/prometheus_test.go @@ -1,6 +1,7 @@ package metrics import ( + "context" "fmt" "net/http" "strconv" @@ -11,8 +12,8 @@ import ( dto "github.com/prometheus/client_model/go" "github.com/stretchr/testify/assert" "github.com/traefik/traefik/v3/pkg/config/dynamic" - otypes "github.com/traefik/traefik/v3/pkg/observability/types" th "github.com/traefik/traefik/v3/pkg/testhelpers" + "github.com/traefik/traefik/v3/pkg/types" ) func TestRegisterPromState(t *testing.T) { @@ -20,42 +21,42 @@ func TestRegisterPromState(t *testing.T) { testCases := []struct { desc string - prometheusSlice []*otypes.Prometheus + prometheusSlice []*types.Prometheus initPromState bool unregisterPromState bool expectedNbRegistries int }{ { desc: "Register once", - prometheusSlice: []*otypes.Prometheus{{}}, + prometheusSlice: []*types.Prometheus{{}}, initPromState: true, unregisterPromState: false, expectedNbRegistries: 1, }, { desc: "Register once with no promState init", - prometheusSlice: []*otypes.Prometheus{{}}, + prometheusSlice: []*types.Prometheus{{}}, initPromState: false, unregisterPromState: false, expectedNbRegistries: 1, }, { desc: "Register twice", - prometheusSlice: []*otypes.Prometheus{{}, {}}, + prometheusSlice: []*types.Prometheus{{}, {}}, initPromState: true, unregisterPromState: false, expectedNbRegistries: 2, }, { desc: "Register twice with no promstate init", - prometheusSlice: []*otypes.Prometheus{{}, {}}, + prometheusSlice: []*types.Prometheus{{}, {}}, initPromState: false, unregisterPromState: false, expectedNbRegistries: 2, }, { desc: "Register twice with unregister", - prometheusSlice: []*otypes.Prometheus{{}, {}}, + prometheusSlice: []*types.Prometheus{{}, {}}, initPromState: true, unregisterPromState: true, expectedNbRegistries: 2, @@ -69,7 +70,7 @@ func TestRegisterPromState(t *testing.T) { if test.initPromState { initStandardRegistry(prom) } - if registerPromState(t.Context()) { + if registerPromState(context.Background()) { actualNbRegistries++ } if test.unregisterPromState { @@ -90,7 +91,7 @@ func TestPrometheus(t *testing.T) { promRegistry = prometheus.NewRegistry() t.Cleanup(promState.reset) - prometheusRegistry := RegisterPrometheus(t.Context(), &otypes.Prometheus{ + prometheusRegistry := RegisterPrometheus(context.Background(), &types.Prometheus{ AddEntryPointsLabels: true, AddRoutersLabels: true, AddServicesLabels: true, @@ -404,7 +405,7 @@ func TestPrometheusMetricRemoval(t *testing.T) { promRegistry = prometheus.NewRegistry() t.Cleanup(promState.reset) - prometheusRegistry := RegisterPrometheus(t.Context(), &otypes.Prometheus{AddEntryPointsLabels: true, AddServicesLabels: true, AddRoutersLabels: true}) + prometheusRegistry := RegisterPrometheus(context.Background(), &types.Prometheus{AddEntryPointsLabels: true, AddServicesLabels: true, AddRoutersLabels: true}) defer promRegistry.Unregister(promState) conf1 := dynamic.Configuration{ @@ -495,7 +496,7 @@ func TestPrometheusMetricRemoveEndpointForRecoveredService(t *testing.T) { promRegistry = prometheus.NewRegistry() t.Cleanup(promState.reset) - prometheusRegistry := RegisterPrometheus(t.Context(), &otypes.Prometheus{AddServicesLabels: true}) + prometheusRegistry := RegisterPrometheus(context.Background(), &types.Prometheus{AddServicesLabels: true}) defer promRegistry.Unregister(promState) conf1 := dynamic.Configuration{ @@ -534,7 +535,7 @@ func TestPrometheusMetricRemoveEndpointForRecoveredService(t *testing.T) { func TestPrometheusRemovedMetricsReset(t *testing.T) { t.Cleanup(promState.reset) - prometheusRegistry := RegisterPrometheus(t.Context(), &otypes.Prometheus{AddEntryPointsLabels: true, AddServicesLabels: true}) + prometheusRegistry := RegisterPrometheus(context.Background(), &types.Prometheus{AddEntryPointsLabels: true, AddServicesLabels: true}) defer promRegistry.Unregister(promState) conf1 := dynamic.Configuration{ diff --git a/pkg/observability/metrics/statsd.go b/pkg/metrics/statsd.go similarity index 94% rename from pkg/observability/metrics/statsd.go rename to pkg/metrics/statsd.go index a3a3acbbe..871300ef6 100644 --- a/pkg/observability/metrics/statsd.go +++ b/pkg/metrics/statsd.go @@ -6,9 +6,9 @@ import ( "github.com/go-kit/kit/metrics/statsd" "github.com/rs/zerolog/log" - "github.com/traefik/traefik/v3/pkg/observability/logs" - otypes "github.com/traefik/traefik/v3/pkg/observability/types" + "github.com/traefik/traefik/v3/pkg/logs" "github.com/traefik/traefik/v3/pkg/safe" + "github.com/traefik/traefik/v3/pkg/types" ) var ( @@ -45,7 +45,7 @@ const ( ) // RegisterStatsd registers the metrics pusher if this didn't happen yet and creates a statsd Registry instance. -func RegisterStatsd(ctx context.Context, config *otypes.Statsd) Registry { +func RegisterStatsd(ctx context.Context, config *types.Statsd) Registry { // just to be sure there is a prefix defined if config.Prefix == "" { config.Prefix = defaultMetricsPrefix @@ -97,7 +97,7 @@ func RegisterStatsd(ctx context.Context, config *otypes.Statsd) Registry { } // initStatsdTicker initializes metrics pusher and creates a statsdClient if not created already. -func initStatsdTicker(ctx context.Context, config *otypes.Statsd) *time.Ticker { +func initStatsdTicker(ctx context.Context, config *types.Statsd) *time.Ticker { address := config.Address if len(address) == 0 { address = "localhost:8125" diff --git a/pkg/observability/metrics/statsd_test.go b/pkg/metrics/statsd_test.go similarity index 91% rename from pkg/observability/metrics/statsd_test.go rename to pkg/metrics/statsd_test.go index ad37314a9..174081db4 100644 --- a/pkg/observability/metrics/statsd_test.go +++ b/pkg/metrics/statsd_test.go @@ -1,6 +1,7 @@ package metrics import ( + "context" "net/http" "strconv" "testing" @@ -8,7 +9,7 @@ import ( "github.com/stvp/go-udp-testing" ptypes "github.com/traefik/paerser/types" - otypes "github.com/traefik/traefik/v3/pkg/observability/types" + "github.com/traefik/traefik/v3/pkg/types" ) func TestStatsD(t *testing.T) { @@ -20,7 +21,7 @@ func TestStatsD(t *testing.T) { // This is needed to make sure that UDP Listener listens for data a bit longer, otherwise it will quit after a millisecond udp.Timeout = 5 * time.Second - statsdRegistry := RegisterStatsd(t.Context(), &otypes.Statsd{Address: ":18125", PushInterval: ptypes.Duration(time.Second), AddEntryPointsLabels: true, AddRoutersLabels: true, AddServicesLabels: true}) + statsdRegistry := RegisterStatsd(context.Background(), &types.Statsd{Address: ":18125", PushInterval: ptypes.Duration(time.Second), AddEntryPointsLabels: true, AddRoutersLabels: true, AddServicesLabels: true}) testRegistry(t, defaultMetricsPrefix, statsdRegistry) } @@ -34,7 +35,7 @@ func TestStatsDWithPrefix(t *testing.T) { // This is needed to make sure that UDP Listener listens for data a bit longer, otherwise it will quit after a millisecond udp.Timeout = 5 * time.Second - statsdRegistry := RegisterStatsd(t.Context(), &otypes.Statsd{Address: ":18125", PushInterval: ptypes.Duration(time.Second), AddEntryPointsLabels: true, AddRoutersLabels: true, AddServicesLabels: true, Prefix: "testPrefix"}) + statsdRegistry := RegisterStatsd(context.Background(), &types.Statsd{Address: ":18125", PushInterval: ptypes.Duration(time.Second), AddEntryPointsLabels: true, AddRoutersLabels: true, AddServicesLabels: true, Prefix: "testPrefix"}) testRegistry(t, "testPrefix", statsdRegistry) } diff --git a/pkg/middlewares/accesslog/field_middleware.go b/pkg/middlewares/accesslog/field_middleware.go index 4d439bc25..d1b39438f 100644 --- a/pkg/middlewares/accesslog/field_middleware.go +++ b/pkg/middlewares/accesslog/field_middleware.go @@ -5,8 +5,8 @@ import ( "time" "github.com/rs/zerolog/log" + "github.com/traefik/traefik/v3/pkg/logs" "github.com/traefik/traefik/v3/pkg/middlewares/capture" - "github.com/traefik/traefik/v3/pkg/observability/logs" "github.com/vulcand/oxy/v2/utils" ) diff --git a/pkg/middlewares/accesslog/logger.go b/pkg/middlewares/accesslog/logger.go index 5f21afc09..6ec8ea4c9 100644 --- a/pkg/middlewares/accesslog/logger.go +++ b/pkg/middlewares/accesslog/logger.go @@ -19,10 +19,8 @@ import ( "github.com/rs/zerolog/log" "github.com/sirupsen/logrus" ptypes "github.com/traefik/paerser/types" + "github.com/traefik/traefik/v3/pkg/logs" "github.com/traefik/traefik/v3/pkg/middlewares/capture" - "github.com/traefik/traefik/v3/pkg/middlewares/observability" - "github.com/traefik/traefik/v3/pkg/observability/logs" - otypes "github.com/traefik/traefik/v3/pkg/observability/types" traefiktls "github.com/traefik/traefik/v3/pkg/tls" "github.com/traefik/traefik/v3/pkg/types" "go.opentelemetry.io/contrib/bridges/otellogrus" @@ -38,9 +36,6 @@ const ( // CommonFormat is the common logging format (CLF). CommonFormat string = "common" - // GenericCLFFormat is the generic CLF format. - GenericCLFFormat string = "genericCLF" - // JSONFormat is the JSON logging format. JSONFormat string = "json" ) @@ -65,7 +60,7 @@ type handlerParams struct { // Handler will write each request and its response to the access log. type Handler struct { - config *otypes.AccessLog + config *types.AccessLog logger *logrus.Logger file io.WriteCloser mu sync.Mutex @@ -74,22 +69,17 @@ type Handler struct { wg sync.WaitGroup } -// AliceConstructor returns an alice.Constructor that wraps the Handler (conditionally) in a middleware chain. -func (h *Handler) AliceConstructor() alice.Constructor { +// WrapHandler Wraps access log handler into an Alice Constructor. +func WrapHandler(handler *Handler) alice.Constructor { return func(next http.Handler) (http.Handler, error) { return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { - if h == nil { - next.ServeHTTP(rw, req) - return - } - - h.ServeHTTP(rw, req, next) + handler.ServeHTTP(rw, req, next) }), nil } } // NewHandler creates a new Handler. -func NewHandler(ctx context.Context, config *otypes.AccessLog) (*Handler, error) { +func NewHandler(config *types.AccessLog) (*Handler, error) { var file io.WriteCloser = noopCloser{os.Stdout} if len(config.FilePath) > 0 { f, err := openAccessLogFile(config.FilePath) @@ -105,8 +95,6 @@ func NewHandler(ctx context.Context, config *otypes.AccessLog) (*Handler, error) switch config.Format { case CommonFormat: formatter = new(CommonLogFormatter) - case GenericCLFFormat: - formatter = new(GenericCLFLogFormatter) case JSONFormat: formatter = new(logrus.JSONFormatter) default: @@ -122,7 +110,7 @@ func NewHandler(ctx context.Context, config *otypes.AccessLog) (*Handler, error) } if config.OTLP != nil { - otelLoggerProvider, err := config.OTLP.NewLoggerProvider(ctx) + otelLoggerProvider, err := config.OTLP.NewLoggerProvider() if err != nil { return nil, fmt.Errorf("setting up OpenTelemetry logger provider: %w", err) } @@ -208,12 +196,6 @@ func GetLogData(req *http.Request) *LogData { } func (h *Handler) ServeHTTP(rw http.ResponseWriter, req *http.Request, next http.Handler) { - if !observability.AccessLogsEnabled(req.Context()) { - next.ServeHTTP(rw, req) - - return - } - now := time.Now().UTC() core := CoreLogData{ @@ -370,63 +352,46 @@ func (h *Handler) logTheRoundTrip(ctx context.Context, logDataTable *LogData) { totalDuration := time.Now().UTC().Sub(core[StartUTC].(time.Time)) core[Duration] = totalDuration - if !h.keepAccessLog(status, retryAttempts, totalDuration) { - return - } - - size := logDataTable.DownstreamResponse.size - core[DownstreamContentSize] = size - if original, ok := core[OriginContentSize]; ok { - o64 := original.(int64) - if size != o64 && size != 0 { - core[GzipRatio] = float64(o64) / float64(size) + if h.keepAccessLog(status, retryAttempts, totalDuration) { + size := logDataTable.DownstreamResponse.size + core[DownstreamContentSize] = size + if original, ok := core[OriginContentSize]; ok { + o64 := original.(int64) + if size != o64 && size != 0 { + core[GzipRatio] = float64(o64) / float64(size) + } } - } - core[Overhead] = totalDuration - if origin, ok := core[OriginDuration]; ok { - core[Overhead] = totalDuration - origin.(time.Duration) - } - - fields := logrus.Fields{} - - for k, v := range logDataTable.Core { - if h.config.Fields.Keep(strings.ToLower(k)) { - fields[k] = v + core[Overhead] = totalDuration + if origin, ok := core[OriginDuration]; ok { + core[Overhead] = totalDuration - origin.(time.Duration) } - } - h.redactHeaders(logDataTable.Request.headers, fields, "request_") - h.redactHeaders(logDataTable.OriginResponse, fields, "origin_") - h.redactHeaders(logDataTable.DownstreamResponse.headers, fields, "downstream_") + fields := logrus.Fields{} - h.mu.Lock() - defer h.mu.Unlock() - - entry := h.logger.WithContext(ctx).WithFields(fields) - - var message string - if h.config.OTLP != nil { - // If the logger is configured to use OpenTelemetry, - // we compute the log body with the formatter. - mBytes, err := h.logger.Formatter.Format(entry) - if err != nil { - message = fmt.Sprintf("Failed to format access log entry: %v", err) - } else { - message = string(mBytes) + for k, v := range logDataTable.Core { + if h.config.Fields.Keep(strings.ToLower(k)) { + fields[k] = v + } } - } - entry.Println(message) + h.redactHeaders(logDataTable.Request.headers, fields, "request_") + h.redactHeaders(logDataTable.OriginResponse, fields, "origin_") + h.redactHeaders(logDataTable.DownstreamResponse.headers, fields, "downstream_") + + h.mu.Lock() + defer h.mu.Unlock() + h.logger.WithContext(ctx).WithFields(fields).Println() + } } func (h *Handler) redactHeaders(headers http.Header, fields logrus.Fields, prefix string) { for k := range headers { v := h.config.Fields.KeepHeader(k) switch v { - case otypes.AccessLogKeep: + case types.AccessLogKeep: fields[prefix+k] = strings.Join(headers.Values(k), ",") - case otypes.AccessLogRedact: + case types.AccessLogRedact: fields[prefix+k] = "REDACTED" } } diff --git a/pkg/middlewares/accesslog/logger_formatters.go b/pkg/middlewares/accesslog/logger_formatters.go index 3da18c239..c714ef146 100644 --- a/pkg/middlewares/accesslog/logger_formatters.go +++ b/pkg/middlewares/accesslog/logger_formatters.go @@ -52,35 +52,6 @@ func (f *CommonLogFormatter) Format(entry *logrus.Entry) ([]byte, error) { return b.Bytes(), err } -// GenericCLFLogFormatter provides formatting in the generic CLF log format. -type GenericCLFLogFormatter struct{} - -// Format formats the log entry in the generic CLF log format. -func (f *GenericCLFLogFormatter) Format(entry *logrus.Entry) ([]byte, error) { - b := &bytes.Buffer{} - - timestamp := defaultValue - if v, ok := entry.Data[StartUTC]; ok { - timestamp = v.(time.Time).Format(commonLogTimeFormat) - } else if v, ok := entry.Data[StartLocal]; ok { - timestamp = v.(time.Time).Local().Format(commonLogTimeFormat) - } - - _, err := fmt.Fprintf(b, "%s - %s [%s] \"%s %s %s\" %v %v %s %s\n", - toLog(entry.Data, ClientHost, defaultValue, false), - toLog(entry.Data, ClientUsername, defaultValue, false), - timestamp, - toLog(entry.Data, RequestMethod, defaultValue, false), - toLog(entry.Data, RequestPath, defaultValue, false), - toLog(entry.Data, RequestProtocol, defaultValue, false), - toLog(entry.Data, DownstreamStatus, defaultValue, true), - toLog(entry.Data, DownstreamContentSize, defaultValue, true), - toLog(entry.Data, "request_Referer", `"-"`, true), - toLog(entry.Data, "request_User-Agent", `"-"`, true)) - - return b.Bytes(), err -} - func toLog(fields logrus.Fields, key, defaultValue string, quoted bool) interface{} { if v, ok := fields[key]; ok { if v == nil { diff --git a/pkg/middlewares/accesslog/logger_formatters_test.go b/pkg/middlewares/accesslog/logger_formatters_test.go index a7e85c5d4..028e3dbf7 100644 --- a/pkg/middlewares/accesslog/logger_formatters_test.go +++ b/pkg/middlewares/accesslog/logger_formatters_test.go @@ -7,7 +7,6 @@ import ( "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" ) func TestCommonLogFormatter_Format(t *testing.T) { @@ -83,100 +82,8 @@ func TestCommonLogFormatter_Format(t *testing.T) { }, } - var err error - time.Local, err = time.LoadLocation("Etc/GMT+9") - require.NoError(t, err) - - for _, test := range testCases { - t.Run(test.name, func(t *testing.T) { - t.Parallel() - - entry := &logrus.Entry{Data: test.data} - - raw, err := clf.Format(entry) - assert.NoError(t, err) - - assert.Equal(t, test.expectedLog, string(raw)) - }) - } -} - -func TestGenericCLFLogFormatter_Format(t *testing.T) { - clf := GenericCLFLogFormatter{} - - testCases := []struct { - name string - data map[string]interface{} - expectedLog string - }{ - { - name: "DownstreamStatus & DownstreamContentSize are nil", - data: map[string]interface{}{ - StartUTC: time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC), - Duration: 123 * time.Second, - ClientHost: "10.0.0.1", - ClientUsername: "Client", - RequestMethod: http.MethodGet, - RequestPath: "/foo", - RequestProtocol: "http", - DownstreamStatus: nil, - DownstreamContentSize: nil, - RequestRefererHeader: "", - RequestUserAgentHeader: "", - RequestCount: 0, - RouterName: "", - ServiceURL: "", - }, - expectedLog: `10.0.0.1 - Client [10/Nov/2009:23:00:00 +0000] "GET /foo http" - - "-" "-" -`, - }, - { - name: "all data", - data: map[string]interface{}{ - StartUTC: time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC), - Duration: 123 * time.Second, - ClientHost: "10.0.0.1", - ClientUsername: "Client", - RequestMethod: http.MethodGet, - RequestPath: "/foo", - RequestProtocol: "http", - DownstreamStatus: 123, - DownstreamContentSize: 132, - RequestRefererHeader: "referer", - RequestUserAgentHeader: "agent", - RequestCount: nil, - RouterName: "foo", - ServiceURL: "http://10.0.0.2/toto", - }, - expectedLog: `10.0.0.1 - Client [10/Nov/2009:23:00:00 +0000] "GET /foo http" 123 132 "referer" "agent" -`, - }, - { - name: "all data with local time", - data: map[string]interface{}{ - StartLocal: time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC), - Duration: 123 * time.Second, - ClientHost: "10.0.0.1", - ClientUsername: "Client", - RequestMethod: http.MethodGet, - RequestPath: "/foo", - RequestProtocol: "http", - DownstreamStatus: 123, - DownstreamContentSize: 132, - RequestRefererHeader: "referer", - RequestUserAgentHeader: "agent", - RequestCount: nil, - RouterName: "foo", - ServiceURL: "http://10.0.0.2/toto", - }, - expectedLog: `10.0.0.1 - Client [10/Nov/2009:14:00:00 -0900] "GET /foo http" 123 132 "referer" "agent" -`, - }, - } - - var err error - time.Local, err = time.LoadLocation("Etc/GMT+9") - require.NoError(t, err) + // Set timezone to Etc/GMT+9 to have a constant behavior + t.Setenv("TZ", "Etc/GMT+9") for _, test := range testCases { t.Run(test.name, func(t *testing.T) { diff --git a/pkg/middlewares/accesslog/logger_test.go b/pkg/middlewares/accesslog/logger_test.go index 2da72991e..6d6a8188f 100644 --- a/pkg/middlewares/accesslog/logger_test.go +++ b/pkg/middlewares/accesslog/logger_test.go @@ -25,8 +25,7 @@ import ( "github.com/stretchr/testify/require" ptypes "github.com/traefik/paerser/types" "github.com/traefik/traefik/v3/pkg/middlewares/capture" - "github.com/traefik/traefik/v3/pkg/middlewares/observability" - otypes "github.com/traefik/traefik/v3/pkg/observability/types" + "github.com/traefik/traefik/v3/pkg/types" "go.opentelemetry.io/collector/pdata/plog/plogotlp" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" @@ -56,131 +55,72 @@ var ( testStart = time.Now() ) -func TestOTelAccessLogWithBody(t *testing.T) { - testCases := []struct { - desc string - format string - bodyCheckFn func(*testing.T, string) - }{ - { - desc: "Common format with log body", - format: CommonFormat, - bodyCheckFn: func(t *testing.T, log string) { - t.Helper() +func TestOTelAccessLog(t *testing.T) { + logCh := make(chan string) + collector := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + gzr, err := gzip.NewReader(r.Body) + require.NoError(t, err) - // For common format, verify the body contains the Traefik common log formatted string - assert.Regexp(t, `"body":{"stringValue":".*- /health -.*200.*[0-9]+ms.*"}`, log) - }, - }, - { - desc: "Generic CLF format with log body", - format: GenericCLFFormat, - bodyCheckFn: func(t *testing.T, log string) { - t.Helper() + body, err := io.ReadAll(gzr) + require.NoError(t, err) - // For generic CLF format, verify the body contains the CLF formatted string - assert.Regexp(t, `"body":{"stringValue":".*- /health -.*200.*"}`, log) - }, - }, - { - desc: "JSON format with log body", - format: JSONFormat, - bodyCheckFn: func(t *testing.T, log string) { - t.Helper() + req := plogotlp.NewExportRequest() + err = req.UnmarshalProto(body) + require.NoError(t, err) - // For JSON format, verify the body contains the JSON formatted string - assert.Regexp(t, `"body":{"stringValue":".*DownstreamStatus.*:200.*"}`, log) + marshalledReq, err := json.Marshal(req) + require.NoError(t, err) + + logCh <- string(marshalledReq) + })) + t.Cleanup(collector.Close) + + config := &types.AccessLog{ + OTLP: &types.OTelLog{ + ServiceName: "test", + ResourceAttributes: map[string]string{"resource": "attribute"}, + HTTP: &types.OTelHTTP{ + Endpoint: collector.URL, }, }, } + logHandler, err := NewHandler(config) + require.NoError(t, err) + t.Cleanup(func() { + err := logHandler.Close() + require.NoError(t, err) + }) - for _, test := range testCases { - t.Run(test.desc, func(t *testing.T) { - t.Parallel() + req := &http.Request{ + Header: map[string][]string{}, + URL: &url.URL{ + Path: testPath, + }, + } + ctx := trace.ContextWithSpanContext(context.Background(), trace.NewSpanContext(trace.SpanContextConfig{ + TraceID: trace.TraceID{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8}, + SpanID: trace.SpanID{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8}, + })) + req = req.WithContext(ctx) - logCh := make(chan string) - collector := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - gzr, err := gzip.NewReader(r.Body) - require.NoError(t, err) + chain := alice.New() + chain = chain.Append(capture.Wrap) + chain = chain.Append(WrapHandler(logHandler)) + handler, err := chain.Then(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + rw.WriteHeader(http.StatusOK) + })) + require.NoError(t, err) + handler.ServeHTTP(httptest.NewRecorder(), req) - body, err := io.ReadAll(gzr) - require.NoError(t, err) + select { + case <-time.After(5 * time.Second): + t.Error("AccessLog not exported") - req := plogotlp.NewExportRequest() - err = req.UnmarshalProto(body) - require.NoError(t, err) - - marshalledReq, err := json.Marshal(req) - require.NoError(t, err) - - logCh <- string(marshalledReq) - })) - t.Cleanup(collector.Close) - - config := &otypes.AccessLog{ - Format: test.format, - OTLP: &otypes.OTelLog{ - ServiceName: "test", - ResourceAttributes: map[string]string{"resource": "attribute"}, - HTTP: &otypes.OTelHTTP{ - Endpoint: collector.URL, - }, - }, - } - logHandler, err := NewHandler(t.Context(), config) - require.NoError(t, err) - t.Cleanup(func() { - err := logHandler.Close() - require.NoError(t, err) - }) - - req := &http.Request{ - Header: map[string][]string{}, - URL: &url.URL{ - Path: "/health", - }, - } - ctx := trace.ContextWithSpanContext(t.Context(), trace.NewSpanContext(trace.SpanContextConfig{ - TraceID: trace.TraceID{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8}, - SpanID: trace.SpanID{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8}, - })) - req = req.WithContext(ctx) - - chain := alice.New() - chain = chain.Append(capture.Wrap) - - // Injection of the observability variables in the request context. - chain = chain.Append(func(next http.Handler) (http.Handler, error) { - return observability.WithObservabilityHandler(next, observability.Observability{ - AccessLogsEnabled: true, - }), nil - }) - - chain = chain.Append(logHandler.AliceConstructor()) - handler, err := chain.Then(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { - rw.WriteHeader(http.StatusOK) - })) - require.NoError(t, err) - handler.ServeHTTP(httptest.NewRecorder(), req) - - select { - case <-time.After(5 * time.Second): - t.Error("AccessLog not exported") - - case log := <-logCh: - // Verify basic OTLP structure - assert.Regexp(t, `{"key":"resource","value":{"stringValue":"attribute"}}`, log) - assert.Regexp(t, `{"key":"service.name","value":{"stringValue":"test"}}`, log) - assert.Regexp(t, `{"key":"DownstreamStatus","value":{"intValue":"200"}}`, log) - assert.Regexp(t, `"traceId":"01020304050607080000000000000000","spanId":"0102030405060708"`, log) - - // Most importantly, verify the log body is populated (not empty) - assert.NotRegexp(t, `"body":{"stringValue":""}`, log, "Log body should not be empty when OTLP is configured") - - // Run format-specific body checks - test.bodyCheckFn(t, log) - } - }) + case log := <-logCh: + assert.Regexp(t, `{"key":"resource","value":{"stringValue":"attribute"}}`, log) + assert.Regexp(t, `{"key":"service.name","value":{"stringValue":"test"}}`, log) + assert.Regexp(t, `{"key":"DownstreamStatus","value":{"intValue":"200"}}`, log) + assert.Regexp(t, `"traceId":"01020304050607080000000000000000","spanId":"0102030405060708"`, log) } } @@ -188,8 +128,8 @@ func TestLogRotation(t *testing.T) { fileName := filepath.Join(t.TempDir(), "traefik.log") rotatedFileName := fileName + ".rotated" - config := &otypes.AccessLog{FilePath: fileName, Format: CommonFormat} - logHandler, err := NewHandler(t.Context(), config) + config := &types.AccessLog{FilePath: fileName, Format: CommonFormat} + logHandler, err := NewHandler(config) require.NoError(t, err) t.Cleanup(func() { err := logHandler.Close() @@ -198,15 +138,7 @@ func TestLogRotation(t *testing.T) { chain := alice.New() chain = chain.Append(capture.Wrap) - - // Injection of the observability variables in the request context. - chain = chain.Append(func(next http.Handler) (http.Handler, error) { - return observability.WithObservabilityHandler(next, observability.Observability{ - AccessLogsEnabled: true, - }), nil - }) - - chain = chain.Append(logHandler.AliceConstructor()) + chain = chain.Append(WrapHandler(logHandler)) handler, err := chain.Then(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { rw.WriteHeader(http.StatusOK) })) @@ -276,18 +208,18 @@ func TestLoggerHeaderFields(t *testing.T) { testCases := []struct { desc string - accessLogFields otypes.AccessLogFields + accessLogFields types.AccessLogFields header string expected string }{ { desc: "with default mode", header: "User-Agent", - expected: otypes.AccessLogDrop, - accessLogFields: otypes.AccessLogFields{ - DefaultMode: otypes.AccessLogDrop, - Headers: &otypes.FieldHeaders{ - DefaultMode: otypes.AccessLogDrop, + expected: types.AccessLogDrop, + accessLogFields: types.AccessLogFields{ + DefaultMode: types.AccessLogDrop, + Headers: &types.FieldHeaders{ + DefaultMode: types.AccessLogDrop, Names: map[string]string{}, }, }, @@ -295,13 +227,13 @@ func TestLoggerHeaderFields(t *testing.T) { { desc: "with exact header name", header: "User-Agent", - expected: otypes.AccessLogKeep, - accessLogFields: otypes.AccessLogFields{ - DefaultMode: otypes.AccessLogDrop, - Headers: &otypes.FieldHeaders{ - DefaultMode: otypes.AccessLogDrop, + expected: types.AccessLogKeep, + accessLogFields: types.AccessLogFields{ + DefaultMode: types.AccessLogDrop, + Headers: &types.FieldHeaders{ + DefaultMode: types.AccessLogDrop, Names: map[string]string{ - "User-Agent": otypes.AccessLogKeep, + "User-Agent": types.AccessLogKeep, }, }, }, @@ -309,13 +241,13 @@ func TestLoggerHeaderFields(t *testing.T) { { desc: "with case-insensitive match on header name", header: "User-Agent", - expected: otypes.AccessLogKeep, - accessLogFields: otypes.AccessLogFields{ - DefaultMode: otypes.AccessLogDrop, - Headers: &otypes.FieldHeaders{ - DefaultMode: otypes.AccessLogDrop, + expected: types.AccessLogKeep, + accessLogFields: types.AccessLogFields{ + DefaultMode: types.AccessLogDrop, + Headers: &types.FieldHeaders{ + DefaultMode: types.AccessLogDrop, Names: map[string]string{ - "user-agent": otypes.AccessLogKeep, + "user-agent": types.AccessLogKeep, }, }, }, @@ -327,13 +259,13 @@ func TestLoggerHeaderFields(t *testing.T) { logFile, err := os.CreateTemp(t.TempDir(), "*.log") require.NoError(t, err) - config := &otypes.AccessLog{ + config := &types.AccessLog{ FilePath: logFile.Name(), Format: CommonFormat, Fields: &test.accessLogFields, } - logger, err := NewHandler(t.Context(), config) + logger, err := NewHandler(config) require.NoError(t, err) t.Cleanup(func() { err := logger.Close() @@ -358,15 +290,7 @@ func TestLoggerHeaderFields(t *testing.T) { chain := alice.New() chain = chain.Append(capture.Wrap) - - // Injection of the observability variables in the request context. - chain = chain.Append(func(next http.Handler) (http.Handler, error) { - return observability.WithObservabilityHandler(next, observability.Observability{ - AccessLogsEnabled: true, - }), nil - }) - - chain = chain.Append(logger.AliceConstructor()) + chain = chain.Append(WrapHandler(logger)) handler, err := chain.Then(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { rw.WriteHeader(http.StatusOK) })) @@ -376,7 +300,7 @@ func TestLoggerHeaderFields(t *testing.T) { logData, err := os.ReadFile(logFile.Name()) require.NoError(t, err) - if test.expected == otypes.AccessLogDrop { + if test.expected == types.AccessLogDrop { assert.NotContains(t, string(logData), strings.Join(expectedValues, ",")) } else { assert.Contains(t, string(logData), strings.Join(expectedValues, ",")) @@ -385,21 +309,21 @@ func TestLoggerHeaderFields(t *testing.T) { } } -func TestCommonLogger(t *testing.T) { +func TestLoggerCLF(t *testing.T) { logFilePath := filepath.Join(t.TempDir(), logFileNameSuffix) - config := &otypes.AccessLog{FilePath: logFilePath, Format: CommonFormat} + config := &types.AccessLog{FilePath: logFilePath, Format: CommonFormat} doLogging(t, config, false) logData, err := os.ReadFile(logFilePath) require.NoError(t, err) expectedLog := ` TestHost - TestUser [13/Apr/2016:07:14:19 -0700] "POST testpath HTTP/0.0" 123 12 "testReferer" "testUserAgent" 1 "testRouter" "http://127.0.0.1/testService" 1ms` - assertValidCommonLogData(t, expectedLog, logData) + assertValidLogData(t, expectedLog, logData) } -func TestCommonLoggerWithBufferingSize(t *testing.T) { +func TestLoggerCLFWithBufferingSize(t *testing.T) { logFilePath := filepath.Join(t.TempDir(), logFileNameSuffix) - config := &otypes.AccessLog{FilePath: logFilePath, Format: CommonFormat, BufferingSize: 1024} + config := &types.AccessLog{FilePath: logFilePath, Format: CommonFormat, BufferingSize: 1024} doLogging(t, config, false) // wait a bit for the buffer to be written in the file. @@ -409,34 +333,7 @@ func TestCommonLoggerWithBufferingSize(t *testing.T) { require.NoError(t, err) expectedLog := ` TestHost - TestUser [13/Apr/2016:07:14:19 -0700] "POST testpath HTTP/0.0" 123 12 "testReferer" "testUserAgent" 1 "testRouter" "http://127.0.0.1/testService" 1ms` - assertValidCommonLogData(t, expectedLog, logData) -} - -func TestLoggerGenericCLF(t *testing.T) { - logFilePath := filepath.Join(t.TempDir(), logFileNameSuffix) - config := &otypes.AccessLog{FilePath: logFilePath, Format: GenericCLFFormat} - doLogging(t, config, false) - - logData, err := os.ReadFile(logFilePath) - require.NoError(t, err) - - expectedLog := ` TestHost - TestUser [13/Apr/2016:07:14:19 -0700] "POST testpath HTTP/0.0" 123 12 "testReferer" "testUserAgent"` - assertValidGenericCLFLogData(t, expectedLog, logData) -} - -func TestLoggerGenericCLFWithBufferingSize(t *testing.T) { - logFilePath := filepath.Join(t.TempDir(), logFileNameSuffix) - config := &otypes.AccessLog{FilePath: logFilePath, Format: GenericCLFFormat, BufferingSize: 1024} - doLogging(t, config, false) - - // wait a bit for the buffer to be written in the file. - time.Sleep(50 * time.Millisecond) - - logData, err := os.ReadFile(logFilePath) - require.NoError(t, err) - - expectedLog := ` TestHost - TestUser [13/Apr/2016:07:14:19 -0700] "POST testpath HTTP/0.0" 123 12 "testReferer" "testUserAgent"` - assertValidGenericCLFLogData(t, expectedLog, logData) + assertValidLogData(t, expectedLog, logData) } func assertString(exp string) func(t *testing.T, actual interface{}) { @@ -474,14 +371,14 @@ func assertFloat64NotZero() func(t *testing.T, actual interface{}) { func TestLoggerJSON(t *testing.T) { testCases := []struct { desc string - config *otypes.AccessLog + config *types.AccessLog tls bool tracing bool expected map[string]func(t *testing.T, value interface{}) }{ { desc: "default config without tracing", - config: &otypes.AccessLog{ + config: &types.AccessLog{ FilePath: "", Format: JSONFormat, }, @@ -520,7 +417,7 @@ func TestLoggerJSON(t *testing.T) { }, { desc: "default config with tracing", - config: &otypes.AccessLog{ + config: &types.AccessLog{ FilePath: "", Format: JSONFormat, }, @@ -562,7 +459,7 @@ func TestLoggerJSON(t *testing.T) { }, { desc: "default config, with TLS request", - config: &otypes.AccessLog{ + config: &types.AccessLog{ FilePath: "", Format: JSONFormat, }, @@ -605,10 +502,10 @@ func TestLoggerJSON(t *testing.T) { }, { desc: "default config drop all fields", - config: &otypes.AccessLog{ + config: &types.AccessLog{ FilePath: "", Format: JSONFormat, - Fields: &otypes.AccessLogFields{ + Fields: &types.AccessLogFields{ DefaultMode: "drop", }, }, @@ -623,12 +520,12 @@ func TestLoggerJSON(t *testing.T) { }, { desc: "default config drop all fields and headers", - config: &otypes.AccessLog{ + config: &types.AccessLog{ FilePath: "", Format: JSONFormat, - Fields: &otypes.AccessLogFields{ + Fields: &types.AccessLogFields{ DefaultMode: "drop", - Headers: &otypes.FieldHeaders{ + Headers: &types.FieldHeaders{ DefaultMode: "drop", }, }, @@ -641,12 +538,12 @@ func TestLoggerJSON(t *testing.T) { }, { desc: "default config drop all fields and redact headers", - config: &otypes.AccessLog{ + config: &types.AccessLog{ FilePath: "", Format: JSONFormat, - Fields: &otypes.AccessLogFields{ + Fields: &types.AccessLogFields{ DefaultMode: "drop", - Headers: &otypes.FieldHeaders{ + Headers: &types.FieldHeaders{ DefaultMode: "redact", }, }, @@ -662,15 +559,15 @@ func TestLoggerJSON(t *testing.T) { }, { desc: "default config drop all fields and headers but kept someone", - config: &otypes.AccessLog{ + config: &types.AccessLog{ FilePath: "", Format: JSONFormat, - Fields: &otypes.AccessLogFields{ + Fields: &types.AccessLogFields{ DefaultMode: "drop", Names: map[string]string{ RequestHost: "keep", }, - Headers: &otypes.FieldHeaders{ + Headers: &types.FieldHeaders{ DefaultMode: "drop", Names: map[string]string{ "Referer": "keep", @@ -688,15 +585,15 @@ func TestLoggerJSON(t *testing.T) { }, { desc: "fields and headers with unconventional letter case", - config: &otypes.AccessLog{ + config: &types.AccessLog{ FilePath: "", Format: JSONFormat, - Fields: &otypes.AccessLogFields{ + Fields: &types.AccessLogFields{ DefaultMode: "drop", Names: map[string]string{ "rEqUeStHoSt": "keep", }, - Headers: &otypes.FieldHeaders{ + Headers: &types.FieldHeaders{ DefaultMode: "drop", Names: map[string]string{ "ReFeReR": "keep", @@ -778,7 +675,7 @@ func TestLogger_AbortedRequest(t *testing.T) { "downstream_Cache-Control": assertString("no-cache"), } - config := &otypes.AccessLog{ + config := &types.AccessLog{ FilePath: filepath.Join(t.TempDir(), logFileNameSuffix), Format: JSONFormat, } @@ -804,12 +701,12 @@ func TestLogger_AbortedRequest(t *testing.T) { func TestNewLogHandlerOutputStdout(t *testing.T) { testCases := []struct { desc string - config *otypes.AccessLog + config *types.AccessLog expectedLog string }{ { desc: "default config", - config: &otypes.AccessLog{ + config: &types.AccessLog{ FilePath: "", Format: CommonFormat, }, @@ -817,19 +714,19 @@ func TestNewLogHandlerOutputStdout(t *testing.T) { }, { desc: "default config with empty filters", - config: &otypes.AccessLog{ + config: &types.AccessLog{ FilePath: "", Format: CommonFormat, - Filters: &otypes.AccessLogFilters{}, + Filters: &types.AccessLogFilters{}, }, expectedLog: `TestHost - TestUser [13/Apr/2016:07:14:19 -0700] "POST testpath HTTP/0.0" 123 12 "testReferer" "testUserAgent" 23 "testRouter" "http://127.0.0.1/testService" 1ms`, }, { desc: "Status code filter not matching", - config: &otypes.AccessLog{ + config: &types.AccessLog{ FilePath: "", Format: CommonFormat, - Filters: &otypes.AccessLogFilters{ + Filters: &types.AccessLogFilters{ StatusCodes: []string{"200"}, }, }, @@ -837,10 +734,10 @@ func TestNewLogHandlerOutputStdout(t *testing.T) { }, { desc: "Status code filter matching", - config: &otypes.AccessLog{ + config: &types.AccessLog{ FilePath: "", Format: CommonFormat, - Filters: &otypes.AccessLogFilters{ + Filters: &types.AccessLogFilters{ StatusCodes: []string{"123"}, }, }, @@ -848,10 +745,10 @@ func TestNewLogHandlerOutputStdout(t *testing.T) { }, { desc: "Duration filter not matching", - config: &otypes.AccessLog{ + config: &types.AccessLog{ FilePath: "", Format: CommonFormat, - Filters: &otypes.AccessLogFilters{ + Filters: &types.AccessLogFilters{ MinDuration: ptypes.Duration(1 * time.Hour), }, }, @@ -859,10 +756,10 @@ func TestNewLogHandlerOutputStdout(t *testing.T) { }, { desc: "Duration filter matching", - config: &otypes.AccessLog{ + config: &types.AccessLog{ FilePath: "", Format: CommonFormat, - Filters: &otypes.AccessLogFilters{ + Filters: &types.AccessLogFilters{ MinDuration: ptypes.Duration(1 * time.Millisecond), }, }, @@ -870,10 +767,10 @@ func TestNewLogHandlerOutputStdout(t *testing.T) { }, { desc: "Retry attempts filter matching", - config: &otypes.AccessLog{ + config: &types.AccessLog{ FilePath: "", Format: CommonFormat, - Filters: &otypes.AccessLogFilters{ + Filters: &types.AccessLogFilters{ RetryAttempts: true, }, }, @@ -881,10 +778,10 @@ func TestNewLogHandlerOutputStdout(t *testing.T) { }, { desc: "Default mode keep", - config: &otypes.AccessLog{ + config: &types.AccessLog{ FilePath: "", Format: CommonFormat, - Fields: &otypes.AccessLogFields{ + Fields: &types.AccessLogFields{ DefaultMode: "keep", }, }, @@ -892,10 +789,10 @@ func TestNewLogHandlerOutputStdout(t *testing.T) { }, { desc: "Default mode keep with override", - config: &otypes.AccessLog{ + config: &types.AccessLog{ FilePath: "", Format: CommonFormat, - Fields: &otypes.AccessLogFields{ + Fields: &types.AccessLogFields{ DefaultMode: "keep", Names: map[string]string{ ClientHost: "drop", @@ -906,10 +803,10 @@ func TestNewLogHandlerOutputStdout(t *testing.T) { }, { desc: "Default mode drop", - config: &otypes.AccessLog{ + config: &types.AccessLog{ FilePath: "", Format: CommonFormat, - Fields: &otypes.AccessLogFields{ + Fields: &types.AccessLogFields{ DefaultMode: "drop", }, }, @@ -917,10 +814,10 @@ func TestNewLogHandlerOutputStdout(t *testing.T) { }, { desc: "Default mode drop with override", - config: &otypes.AccessLog{ + config: &types.AccessLog{ FilePath: "", Format: CommonFormat, - Fields: &otypes.AccessLogFields{ + Fields: &types.AccessLogFields{ DefaultMode: "drop", Names: map[string]string{ ClientHost: "drop", @@ -932,16 +829,16 @@ func TestNewLogHandlerOutputStdout(t *testing.T) { }, { desc: "Default mode drop with header dropped", - config: &otypes.AccessLog{ + config: &types.AccessLog{ FilePath: "", Format: CommonFormat, - Fields: &otypes.AccessLogFields{ + Fields: &types.AccessLogFields{ DefaultMode: "drop", Names: map[string]string{ ClientHost: "drop", ClientUsername: "keep", }, - Headers: &otypes.FieldHeaders{ + Headers: &types.FieldHeaders{ DefaultMode: "drop", }, }, @@ -950,16 +847,16 @@ func TestNewLogHandlerOutputStdout(t *testing.T) { }, { desc: "Default mode drop with header redacted", - config: &otypes.AccessLog{ + config: &types.AccessLog{ FilePath: "", Format: CommonFormat, - Fields: &otypes.AccessLogFields{ + Fields: &types.AccessLogFields{ DefaultMode: "drop", Names: map[string]string{ ClientHost: "drop", ClientUsername: "keep", }, - Headers: &otypes.FieldHeaders{ + Headers: &types.FieldHeaders{ DefaultMode: "redact", }, }, @@ -968,16 +865,16 @@ func TestNewLogHandlerOutputStdout(t *testing.T) { }, { desc: "Default mode drop with header redacted", - config: &otypes.AccessLog{ + config: &types.AccessLog{ FilePath: "", Format: CommonFormat, - Fields: &otypes.AccessLogFields{ + Fields: &types.AccessLogFields{ DefaultMode: "drop", Names: map[string]string{ ClientHost: "drop", ClientUsername: "keep", }, - Headers: &otypes.FieldHeaders{ + Headers: &types.FieldHeaders{ DefaultMode: "keep", Names: map[string]string{ "Referer": "redact", @@ -1000,12 +897,12 @@ func TestNewLogHandlerOutputStdout(t *testing.T) { written, err := os.ReadFile(file.Name()) require.NoError(t, err, "unable to read captured stdout from file") - assertValidCommonLogData(t, test.expectedLog, written) + assertValidLogData(t, test.expectedLog, written) }) } } -func assertValidCommonLogData(t *testing.T, expected string, logData []byte) { +func assertValidLogData(t *testing.T, expected string, logData []byte) { t.Helper() if len(expected) == 0 { @@ -1038,35 +935,6 @@ func assertValidCommonLogData(t *testing.T, expected string, logData []byte) { assert.Regexp(t, `\d*ms`, result[Duration], formatErrMessage) } -func assertValidGenericCLFLogData(t *testing.T, expected string, logData []byte) { - t.Helper() - - if len(expected) == 0 { - assert.Empty(t, logData) - t.Log(string(logData)) - return - } - - result, err := ParseAccessLog(string(logData)) - require.NoError(t, err) - - resultExpected, err := ParseAccessLog(expected) - require.NoError(t, err) - - formatErrMessage := fmt.Sprintf("Expected:\t%q\nActual:\t%q", expected, string(logData)) - - require.Len(t, result, len(resultExpected), formatErrMessage) - assert.Equal(t, resultExpected[ClientHost], result[ClientHost], formatErrMessage) - assert.Equal(t, resultExpected[ClientUsername], result[ClientUsername], formatErrMessage) - assert.Equal(t, resultExpected[RequestMethod], result[RequestMethod], formatErrMessage) - assert.Equal(t, resultExpected[RequestPath], result[RequestPath], formatErrMessage) - assert.Equal(t, resultExpected[RequestProtocol], result[RequestProtocol], formatErrMessage) - assert.Equal(t, resultExpected[OriginStatus], result[OriginStatus], formatErrMessage) - assert.Equal(t, resultExpected[OriginContentSize], result[OriginContentSize], formatErrMessage) - assert.Equal(t, resultExpected[RequestRefererHeader], result[RequestRefererHeader], formatErrMessage) - assert.Equal(t, resultExpected[RequestUserAgentHeader], result[RequestUserAgentHeader], formatErrMessage) -} - func captureStdout(t *testing.T) (out *os.File, restoreStdout func()) { t.Helper() @@ -1084,9 +952,9 @@ func captureStdout(t *testing.T) (out *os.File, restoreStdout func()) { return file, restoreStdout } -func doLoggingTLSOpt(t *testing.T, config *otypes.AccessLog, enableTLS, tracing bool) { +func doLoggingTLSOpt(t *testing.T, config *types.AccessLog, enableTLS, tracing bool) { t.Helper() - logger, err := NewHandler(t.Context(), config) + logger, err := NewHandler(config) require.NoError(t, err) t.Cleanup(func() { err := logger.Close() @@ -1130,28 +998,20 @@ func doLoggingTLSOpt(t *testing.T, config *otypes.AccessLog, enableTLS, tracing chain := alice.New() chain = chain.Append(capture.Wrap) - - // Injection of the observability variables in the request context. - chain = chain.Append(func(next http.Handler) (http.Handler, error) { - return observability.WithObservabilityHandler(next, observability.Observability{ - AccessLogsEnabled: true, - }), nil - }) - - chain = chain.Append(logger.AliceConstructor()) + chain = chain.Append(WrapHandler(logger)) handler, err := chain.Then(http.HandlerFunc(logWriterTestHandlerFunc)) require.NoError(t, err) handler.ServeHTTP(httptest.NewRecorder(), req) } -func doLoggingTLS(t *testing.T, config *otypes.AccessLog, tracing bool) { +func doLoggingTLS(t *testing.T, config *types.AccessLog, tracing bool) { t.Helper() doLoggingTLSOpt(t, config, true, tracing) } -func doLogging(t *testing.T, config *otypes.AccessLog, tracing bool) { +func doLogging(t *testing.T, config *types.AccessLog, tracing bool) { t.Helper() doLoggingTLSOpt(t, config, false, tracing) @@ -1180,10 +1040,10 @@ func logWriterTestHandlerFunc(rw http.ResponseWriter, r *http.Request) { rw.WriteHeader(testStatus) } -func doLoggingWithAbortedStream(t *testing.T, config *otypes.AccessLog) { +func doLoggingWithAbortedStream(t *testing.T, config *types.AccessLog) { t.Helper() - logger, err := NewHandler(t.Context(), config) + logger, err := NewHandler(config) require.NoError(t, err) t.Cleanup(func() { err := logger.Close() @@ -1195,7 +1055,7 @@ func doLoggingWithAbortedStream(t *testing.T, config *otypes.AccessLog) { require.NoError(t, err, "logger should create "+config.FilePath) } - reqContext, cancelRequest := context.WithCancel(t.Context()) + reqContext, cancelRequest := context.WithCancel(context.Background()) req := &http.Request{ Header: map[string][]string{ @@ -1225,15 +1085,7 @@ func doLoggingWithAbortedStream(t *testing.T, config *otypes.AccessLog) { }), nil }) chain = chain.Append(capture.Wrap) - - // Injection of the observability variables in the request context. - chain = chain.Append(func(next http.Handler) (http.Handler, error) { - return observability.WithObservabilityHandler(next, observability.Observability{ - AccessLogsEnabled: true, - }), nil - }) - - chain = chain.Append(logger.AliceConstructor()) + chain = chain.Append(WrapHandler(logger)) service := NewFieldHandler(http.HandlerFunc(streamBackend), ServiceURL, "http://stream", nil) service = NewFieldHandler(service, ServiceAddr, "127.0.0.1", nil) diff --git a/pkg/middlewares/addprefix/add_prefix.go b/pkg/middlewares/addprefix/add_prefix.go index b698cc9ce..46f8d98d0 100644 --- a/pkg/middlewares/addprefix/add_prefix.go +++ b/pkg/middlewares/addprefix/add_prefix.go @@ -7,6 +7,7 @@ import ( "github.com/traefik/traefik/v3/pkg/config/dynamic" "github.com/traefik/traefik/v3/pkg/middlewares" + "go.opentelemetry.io/otel/trace" ) const ( @@ -38,8 +39,8 @@ func New(ctx context.Context, next http.Handler, config dynamic.AddPrefix, name return result, nil } -func (a *addPrefix) GetTracingInformation() (string, string) { - return a.name, typeName +func (a *addPrefix) GetTracingInformation() (string, string, trace.SpanKind) { + return a.name, typeName, trace.SpanKindInternal } func (a *addPrefix) ServeHTTP(rw http.ResponseWriter, req *http.Request) { diff --git a/pkg/middlewares/addprefix/add_prefix_test.go b/pkg/middlewares/addprefix/add_prefix_test.go index 37caf08e2..2bcc2ad7f 100644 --- a/pkg/middlewares/addprefix/add_prefix_test.go +++ b/pkg/middlewares/addprefix/add_prefix_test.go @@ -1,6 +1,7 @@ package addprefix import ( + "context" "net/http" "testing" @@ -33,7 +34,7 @@ func TestNewAddPrefix(t *testing.T) { next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}) - _, err := New(t.Context(), next, test.prefix, "foo-add-prefix") + _, err := New(context.Background(), next, test.prefix, "foo-add-prefix") if test.expectsError { assert.Error(t, err) } else { @@ -86,7 +87,7 @@ func TestAddPrefix(t *testing.T) { req := testhelpers.MustNewRequest(http.MethodGet, "http://localhost"+test.path, nil) - handler, err := New(t.Context(), next, test.prefix, "foo-add-prefix") + handler, err := New(context.Background(), next, test.prefix, "foo-add-prefix") require.NoError(t, err) handler.ServeHTTP(nil, req) diff --git a/pkg/middlewares/auth/basic_auth.go b/pkg/middlewares/auth/basic_auth.go index 863c968f3..e6c175bcb 100644 --- a/pkg/middlewares/auth/basic_auth.go +++ b/pkg/middlewares/auth/basic_auth.go @@ -12,6 +12,7 @@ import ( "github.com/traefik/traefik/v3/pkg/middlewares" "github.com/traefik/traefik/v3/pkg/middlewares/accesslog" "github.com/traefik/traefik/v3/pkg/middlewares/observability" + "go.opentelemetry.io/otel/trace" "golang.org/x/sync/singleflight" ) @@ -60,8 +61,8 @@ func NewBasic(ctx context.Context, next http.Handler, authConfig dynamic.BasicAu return ba, nil } -func (b *basicAuth) GetTracingInformation() (string, string) { - return b.name, typeNameBasic +func (b *basicAuth) GetTracingInformation() (string, string, trace.SpanKind) { + return b.name, typeNameBasic, trace.SpanKindInternal } func (b *basicAuth) ServeHTTP(rw http.ResponseWriter, req *http.Request) { diff --git a/pkg/middlewares/auth/basic_auth_test.go b/pkg/middlewares/auth/basic_auth_test.go index b81a22c03..6a59f0111 100644 --- a/pkg/middlewares/auth/basic_auth_test.go +++ b/pkg/middlewares/auth/basic_auth_test.go @@ -1,6 +1,7 @@ package auth import ( + "context" "fmt" "io" "net/http" @@ -24,13 +25,13 @@ func TestBasicAuthFail(t *testing.T) { auth := dynamic.BasicAuth{ Users: []string{"test"}, } - _, err := NewBasic(t.Context(), next, auth, "authName") + _, err := NewBasic(context.Background(), next, auth, "authName") require.Error(t, err) auth2 := dynamic.BasicAuth{ Users: []string{"test:test"}, } - authMiddleware, err := NewBasic(t.Context(), next, auth2, "authTest") + authMiddleware, err := NewBasic(context.Background(), next, auth2, "authTest") require.NoError(t, err) ts := httptest.NewServer(authMiddleware) @@ -53,7 +54,7 @@ func TestBasicAuthSuccess(t *testing.T) { auth := dynamic.BasicAuth{ Users: []string{"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/"}, } - authMiddleware, err := NewBasic(t.Context(), next, auth, "authName") + authMiddleware, err := NewBasic(context.Background(), next, auth, "authName") require.NoError(t, err) ts := httptest.NewServer(authMiddleware) @@ -84,7 +85,7 @@ func TestBasicAuthUserHeader(t *testing.T) { Users: []string{"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/"}, HeaderField: "X-Webauth-User", } - middleware, err := NewBasic(t.Context(), next, auth, "authName") + middleware, err := NewBasic(context.Background(), next, auth, "authName") require.NoError(t, err) ts := httptest.NewServer(middleware) @@ -115,7 +116,7 @@ func TestBasicAuthHeaderRemoved(t *testing.T) { RemoveHeader: true, Users: []string{"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/"}, } - middleware, err := NewBasic(t.Context(), next, auth, "authName") + middleware, err := NewBasic(context.Background(), next, auth, "authName") require.NoError(t, err) ts := httptest.NewServer(middleware) @@ -146,7 +147,7 @@ func TestBasicAuthHeaderPresent(t *testing.T) { auth := dynamic.BasicAuth{ Users: []string{"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/"}, } - middleware, err := NewBasic(t.Context(), next, auth, "authName") + middleware, err := NewBasic(context.Background(), next, auth, "authName") require.NoError(t, err) ts := httptest.NewServer(middleware) @@ -176,7 +177,7 @@ func TestBasicAuthConcurrentHashOnce(t *testing.T) { Users: []string{"test:$2a$04$.8sTYfcxbSplCtoxt5TdJOgpBYkarKtZYsYfYxQ1edbYRuO1DNi0e"}, } - authMiddleware, err := NewBasic(t.Context(), next, auth, "authName") + authMiddleware, err := NewBasic(context.Background(), next, auth, "authName") require.NoError(t, err) hashCount := 0 @@ -276,7 +277,7 @@ func TestBasicAuthUsersFromFile(t *testing.T) { fmt.Fprintln(w, "traefik") }) - authenticator, err := NewBasic(t.Context(), next, authenticatorConfiguration, "authName") + authenticator, err := NewBasic(context.Background(), next, authenticatorConfiguration, "authName") require.NoError(t, err) ts := httptest.NewServer(authenticator) diff --git a/pkg/middlewares/auth/digest_auth.go b/pkg/middlewares/auth/digest_auth.go index af64a134b..25c22865f 100644 --- a/pkg/middlewares/auth/digest_auth.go +++ b/pkg/middlewares/auth/digest_auth.go @@ -12,6 +12,7 @@ import ( "github.com/traefik/traefik/v3/pkg/middlewares" "github.com/traefik/traefik/v3/pkg/middlewares/accesslog" "github.com/traefik/traefik/v3/pkg/middlewares/observability" + "go.opentelemetry.io/otel/trace" ) const ( @@ -53,8 +54,8 @@ func NewDigest(ctx context.Context, next http.Handler, authConfig dynamic.Digest return da, nil } -func (d *digestAuth) GetTracingInformation() (string, string) { - return d.name, typeNameDigest +func (d *digestAuth) GetTracingInformation() (string, string, trace.SpanKind) { + return d.name, typeNameDigest, trace.SpanKindInternal } func (d *digestAuth) ServeHTTP(rw http.ResponseWriter, req *http.Request) { diff --git a/pkg/middlewares/auth/digest_auth_test.go b/pkg/middlewares/auth/digest_auth_test.go index b0f485b38..ae472e3d0 100644 --- a/pkg/middlewares/auth/digest_auth_test.go +++ b/pkg/middlewares/auth/digest_auth_test.go @@ -1,6 +1,7 @@ package auth import ( + "context" "fmt" "io" "net/http" @@ -22,7 +23,7 @@ func TestDigestAuthError(t *testing.T) { auth := dynamic.DigestAuth{ Users: []string{"test"}, } - _, err := NewDigest(t.Context(), next, auth, "authName") + _, err := NewDigest(context.Background(), next, auth, "authName") assert.Error(t, err) } @@ -34,7 +35,7 @@ func TestDigestAuthFail(t *testing.T) { auth := dynamic.DigestAuth{ Users: []string{"test:traefik:a2688e031edb4be6a3797f3882655c05"}, } - authMiddleware, err := NewDigest(t.Context(), next, auth, "authName") + authMiddleware, err := NewDigest(context.Background(), next, auth, "authName") require.NoError(t, err) assert.NotNil(t, authMiddleware, "this should not be nil") @@ -108,7 +109,7 @@ func TestDigestAuthUsersFromFile(t *testing.T) { fmt.Fprintln(w, "traefik") }) - authenticator, err := NewDigest(t.Context(), next, authenticatorConfiguration, "authName") + authenticator, err := NewDigest(context.Background(), next, authenticatorConfiguration, "authName") require.NoError(t, err) ts := httptest.NewServer(authenticator) diff --git a/pkg/middlewares/auth/forward.go b/pkg/middlewares/auth/forward.go index 4438a486e..0ade40b7d 100644 --- a/pkg/middlewares/auth/forward.go +++ b/pkg/middlewares/auth/forward.go @@ -17,8 +17,7 @@ import ( "github.com/traefik/traefik/v3/pkg/middlewares" "github.com/traefik/traefik/v3/pkg/middlewares/accesslog" "github.com/traefik/traefik/v3/pkg/middlewares/observability" - "github.com/traefik/traefik/v3/pkg/observability/tracing" - "github.com/traefik/traefik/v3/pkg/proxy/httputil" + "github.com/traefik/traefik/v3/pkg/tracing" "github.com/traefik/traefik/v3/pkg/types" "github.com/vulcand/oxy/v2/forward" "github.com/vulcand/oxy/v2/utils" @@ -131,8 +130,8 @@ func NewForward(ctx context.Context, next http.Handler, config dynamic.ForwardAu return fa, nil } -func (fa *forwardAuth) GetTracingInformation() (string, string) { - return fa.name, typeNameForward +func (fa *forwardAuth) GetTracingInformation() (string, string, trace.SpanKind) { + return fa.name, typeNameForward, trace.SpanKindInternal } func (fa *forwardAuth) ServeHTTP(rw http.ResponseWriter, req *http.Request) { @@ -180,7 +179,7 @@ func (fa *forwardAuth) ServeHTTP(rw http.ResponseWriter, req *http.Request) { var forwardSpan trace.Span var tracer *tracing.Tracer - if tracer = tracing.TracerFromContext(req.Context()); tracer != nil && observability.TracingEnabled(req.Context()) { + if tracer = tracing.TracerFromContext(req.Context()); tracer != nil { var tracingCtx context.Context tracingCtx, forwardSpan = tracer.Start(req.Context(), "AuthRequest", trace.WithSpanKind(trace.SpanKindClient)) defer forwardSpan.End() @@ -196,12 +195,7 @@ func (fa *forwardAuth) ServeHTTP(rw http.ResponseWriter, req *http.Request) { logger.Debug().Err(forwardErr).Msgf("Error calling %s", fa.address) observability.SetStatusErrorf(req.Context(), "Error calling %s. Cause: %s", fa.address, forwardErr) - statusCode := http.StatusInternalServerError - if errors.Is(forwardErr, context.Canceled) { - statusCode = httputil.StatusClientClosedRequest - } - - rw.WriteHeader(statusCode) + rw.WriteHeader(http.StatusInternalServerError) return } defer forwardResponse.Body.Close() diff --git a/pkg/middlewares/auth/forward_test.go b/pkg/middlewares/auth/forward_test.go index 95e3d7910..82ccb7346 100644 --- a/pkg/middlewares/auth/forward_test.go +++ b/pkg/middlewares/auth/forward_test.go @@ -11,15 +11,12 @@ import ( "net/url" "strconv" "testing" - "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/traefik/traefik/v3/pkg/config/dynamic" - "github.com/traefik/traefik/v3/pkg/middlewares/observability" - "github.com/traefik/traefik/v3/pkg/observability/tracing" - "github.com/traefik/traefik/v3/pkg/proxy/httputil" "github.com/traefik/traefik/v3/pkg/testhelpers" + "github.com/traefik/traefik/v3/pkg/tracing" "github.com/vulcand/oxy/v2/forward" "go.opentelemetry.io/contrib/propagators/autoprop" "go.opentelemetry.io/otel" @@ -40,7 +37,7 @@ func TestForwardAuthFail(t *testing.T) { })) t.Cleanup(server.Close) - middleware, err := NewForward(t.Context(), next, dynamic.ForwardAuth{ + middleware, err := NewForward(context.Background(), next, dynamic.ForwardAuth{ Address: server.URL, }, "authTest") require.NoError(t, err) @@ -93,7 +90,7 @@ func TestForwardAuthSuccess(t *testing.T) { AuthResponseHeadersRegex: "^Foo-", AddAuthCookiesToResponse: []string{"authCookie"}, } - middleware, err := NewForward(t.Context(), next, auth, "authTest") + middleware, err := NewForward(context.Background(), next, auth, "authTest") require.NoError(t, err) ts := httptest.NewServer(middleware) @@ -138,7 +135,7 @@ func TestForwardAuthForwardBody(t *testing.T) { maxBodySize := int64(len(data)) auth := dynamic.ForwardAuth{Address: server.URL, ForwardBody: true, MaxBodySize: &maxBodySize} - middleware, err := NewForward(t.Context(), next, auth, "authTest") + middleware, err := NewForward(context.Background(), next, auth, "authTest") require.NoError(t, err) ts := httptest.NewServer(middleware) @@ -173,7 +170,7 @@ func TestForwardAuthForwardBodyEmptyBody(t *testing.T) { auth := dynamic.ForwardAuth{Address: server.URL, ForwardBody: true} - middleware, err := NewForward(t.Context(), next, auth, "authTest") + middleware, err := NewForward(context.Background(), next, auth, "authTest") require.NoError(t, err) ts := httptest.NewServer(middleware) @@ -211,7 +208,7 @@ func TestForwardAuthForwardBodySizeLimit(t *testing.T) { maxBodySize := int64(len(data)) - 1 auth := dynamic.ForwardAuth{Address: server.URL, ForwardBody: true, MaxBodySize: &maxBodySize} - middleware, err := NewForward(t.Context(), next, auth, "authTest") + middleware, err := NewForward(context.Background(), next, auth, "authTest") require.NoError(t, err) ts := httptest.NewServer(middleware) @@ -248,7 +245,7 @@ func TestForwardAuthNotForwardBody(t *testing.T) { auth := dynamic.ForwardAuth{Address: server.URL} - middleware, err := NewForward(t.Context(), next, auth, "authTest") + middleware, err := NewForward(context.Background(), next, auth, "authTest") require.NoError(t, err) ts := httptest.NewServer(middleware) @@ -276,7 +273,7 @@ func TestForwardAuthRedirect(t *testing.T) { auth := dynamic.ForwardAuth{Address: authTs.URL} - authMiddleware, err := NewForward(t.Context(), next, auth, "authTest") + authMiddleware, err := NewForward(context.Background(), next, auth, "authTest") require.NoError(t, err) ts := httptest.NewServer(authMiddleware) @@ -327,7 +324,7 @@ func TestForwardAuthRemoveHopByHopHeaders(t *testing.T) { auth := dynamic.ForwardAuth{Address: authTs.URL} - authMiddleware, err := NewForward(t.Context(), next, auth, "authTest") + authMiddleware, err := NewForward(context.Background(), next, auth, "authTest") require.NoError(t, err) ts := httptest.NewServer(authMiddleware) @@ -373,7 +370,7 @@ func TestForwardAuthFailResponseHeaders(t *testing.T) { auth := dynamic.ForwardAuth{ Address: authTs.URL, } - authMiddleware, err := NewForward(t.Context(), next, auth, "authTest") + authMiddleware, err := NewForward(context.Background(), next, auth, "authTest") require.NoError(t, err) ts := httptest.NewServer(authMiddleware) @@ -411,75 +408,6 @@ func TestForwardAuthFailResponseHeaders(t *testing.T) { assert.Equal(t, "Forbidden\n", string(body)) } -func TestForwardAuthClientClosedRequest(t *testing.T) { - requestStarted := make(chan struct{}) - requestCancelled := make(chan struct{}) - responseComplete := make(chan struct{}) - - authTs := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - close(requestStarted) - <-requestCancelled - })) - t.Cleanup(authTs.Close) - - next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - // next should not be called. - t.Fail() - }) - - auth := dynamic.ForwardAuth{ - Address: authTs.URL, - } - authMiddleware, err := NewForward(t.Context(), next, auth, "authTest") - require.NoError(t, err) - - ctx, cancel := context.WithCancel(t.Context()) - req := httptest.NewRequestWithContext(ctx, "GET", "http://foo", http.NoBody) - - recorder := httptest.NewRecorder() - go func() { - authMiddleware.ServeHTTP(recorder, req) - close(responseComplete) - }() - - <-requestStarted - - cancel() - close(requestCancelled) - - <-responseComplete - - assert.Equal(t, httputil.StatusClientClosedRequest, recorder.Result().StatusCode) -} - -func TestForwardAuthForwardError(t *testing.T) { - next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - // next should not be called. - t.Fail() - }) - - auth := dynamic.ForwardAuth{ - Address: "http://non-existing-server", - } - authMiddleware, err := NewForward(t.Context(), next, auth, "authTest") - require.NoError(t, err) - - ctx, cancel := context.WithTimeout(t.Context(), 1*time.Microsecond) - defer cancel() - req := httptest.NewRequestWithContext(ctx, http.MethodGet, "http://foo", nil) - - recorder := httptest.NewRecorder() - responseComplete := make(chan struct{}) - go func() { - authMiddleware.ServeHTTP(recorder, req) - close(responseComplete) - }() - - <-responseComplete - - assert.Equal(t, http.StatusInternalServerError, recorder.Result().StatusCode) -} - func Test_writeHeader(t *testing.T) { testCases := []struct { name string @@ -754,13 +682,9 @@ func TestForwardAuthTracing(t *testing.T) { Address: server.URL, AuthRequestHeaders: []string{"X-Foo"}, } - next, err := NewForward(t.Context(), next, auth, "authTest") + next, err := NewForward(context.Background(), next, auth, "authTest") require.NoError(t, err) - next = observability.WithObservabilityHandler(next, observability.Observability{ - TracingEnabled: true, - }) - req := httptest.NewRequest(http.MethodGet, "http://www.test.com/search?q=Opentelemetry", nil) req.RemoteAddr = "10.0.0.1:1234" req.Header.Set("User-Agent", "forward-test") @@ -801,7 +725,7 @@ func TestForwardAuthPreserveLocationHeader(t *testing.T) { Address: server.URL, PreserveLocationHeader: true, } - middleware, err := NewForward(t.Context(), next, auth, "authTest") + middleware, err := NewForward(context.Background(), next, auth, "authTest") require.NoError(t, err) ts := httptest.NewServer(middleware) @@ -855,7 +779,7 @@ func TestForwardAuthPreserveRequestMethod(t *testing.T) { PreserveRequestMethod: test.preserveRequestMethod, } - middleware, err := NewForward(t.Context(), next, auth, "authTest") + middleware, err := NewForward(context.Background(), next, auth, "authTest") require.NoError(t, err) ts := httptest.NewServer(middleware) diff --git a/pkg/middlewares/buffering/buffering.go b/pkg/middlewares/buffering/buffering.go index 753436add..ec9100a98 100644 --- a/pkg/middlewares/buffering/buffering.go +++ b/pkg/middlewares/buffering/buffering.go @@ -6,9 +6,10 @@ import ( "github.com/rs/zerolog" "github.com/traefik/traefik/v3/pkg/config/dynamic" + "github.com/traefik/traefik/v3/pkg/logs" "github.com/traefik/traefik/v3/pkg/middlewares" - "github.com/traefik/traefik/v3/pkg/observability/logs" oxybuffer "github.com/vulcand/oxy/v2/buffer" + "go.opentelemetry.io/otel/trace" ) const ( @@ -47,8 +48,8 @@ func New(ctx context.Context, next http.Handler, config dynamic.Buffering, name }, nil } -func (b *buffer) GetTracingInformation() (string, string) { - return b.name, typeName +func (b *buffer) GetTracingInformation() (string, string, trace.SpanKind) { + return b.name, typeName, trace.SpanKindInternal } func (b *buffer) ServeHTTP(rw http.ResponseWriter, req *http.Request) { diff --git a/pkg/middlewares/buffering/buffering_test.go b/pkg/middlewares/buffering/buffering_test.go index b070a180f..de6f50170 100644 --- a/pkg/middlewares/buffering/buffering_test.go +++ b/pkg/middlewares/buffering/buffering_test.go @@ -2,6 +2,7 @@ package buffering import ( "bytes" + "context" "crypto/rand" "math" "net/http" @@ -56,7 +57,7 @@ func TestBuffering(t *testing.T) { require.NoError(t, err) }) - buffMiddleware, err := New(t.Context(), next, test.config, "foo") + buffMiddleware, err := New(context.Background(), next, test.config, "foo") require.NoError(t, err) req := httptest.NewRequest(http.MethodPost, "http://localhost", bytes.NewBuffer(test.body)) diff --git a/pkg/middlewares/circuitbreaker/circuit_breaker.go b/pkg/middlewares/circuitbreaker/circuit_breaker.go index 72eecd428..ceaa93de4 100644 --- a/pkg/middlewares/circuitbreaker/circuit_breaker.go +++ b/pkg/middlewares/circuitbreaker/circuit_breaker.go @@ -8,10 +8,11 @@ import ( "github.com/rs/zerolog" "github.com/rs/zerolog/log" "github.com/traefik/traefik/v3/pkg/config/dynamic" + "github.com/traefik/traefik/v3/pkg/logs" "github.com/traefik/traefik/v3/pkg/middlewares" "github.com/traefik/traefik/v3/pkg/middlewares/observability" - "github.com/traefik/traefik/v3/pkg/observability/logs" "github.com/vulcand/oxy/v2/cbreaker" + "go.opentelemetry.io/otel/trace" ) const typeName = "CircuitBreaker" @@ -67,8 +68,8 @@ func New(ctx context.Context, next http.Handler, confCircuitBreaker dynamic.Circ }, nil } -func (c *circuitBreaker) GetTracingInformation() (string, string) { - return c.name, typeName +func (c *circuitBreaker) GetTracingInformation() (string, string, trace.SpanKind) { + return c.name, typeName, trace.SpanKindInternal } func (c *circuitBreaker) ServeHTTP(rw http.ResponseWriter, req *http.Request) { diff --git a/pkg/middlewares/compress/acceptencoding_test.go b/pkg/middlewares/compress/acceptencoding_test.go index 6cacbb951..d3059af6e 100644 --- a/pkg/middlewares/compress/acceptencoding_test.go +++ b/pkg/middlewares/compress/acceptencoding_test.go @@ -1,6 +1,7 @@ package compress import ( + "context" "testing" "github.com/stretchr/testify/assert" @@ -152,7 +153,7 @@ func Test_getCompressionEncoding(t *testing.T) { DefaultEncoding: test.defaultEncoding, } - h, err := New(t.Context(), nil, conf, "test") + h, err := New(context.Background(), nil, conf, "test") require.NoError(t, err) c, ok := h.(*compress) diff --git a/pkg/middlewares/compress/compress.go b/pkg/middlewares/compress/compress.go index 4ab312cc2..0bb3788d2 100644 --- a/pkg/middlewares/compress/compress.go +++ b/pkg/middlewares/compress/compress.go @@ -13,6 +13,7 @@ import ( "github.com/klauspost/compress/zstd" "github.com/traefik/traefik/v3/pkg/config/dynamic" "github.com/traefik/traefik/v3/pkg/middlewares" + "go.opentelemetry.io/otel/trace" ) const typeName = "Compress" @@ -180,8 +181,8 @@ func (c *compress) chooseHandler(typ string, rw http.ResponseWriter, req *http.R } } -func (c *compress) GetTracingInformation() (string, string) { - return c.name, typeName +func (c *compress) GetTracingInformation() (string, string, trace.SpanKind) { + return c.name, typeName, trace.SpanKindInternal } func (c *compress) newGzipHandler() (http.Handler, error) { diff --git a/pkg/middlewares/compress/compress_test.go b/pkg/middlewares/compress/compress_test.go index 9754de238..3c53c4c30 100644 --- a/pkg/middlewares/compress/compress_test.go +++ b/pkg/middlewares/compress/compress_test.go @@ -2,6 +2,7 @@ package compress import ( "compress/gzip" + "context" "io" "net/http" "net/http/httptest" @@ -115,7 +116,7 @@ func TestNegotiation(t *testing.T) { MinResponseBodyBytes: 1, Encodings: defaultSupportedEncodings, } - handler, err := New(t.Context(), next, cfg, "testing") + handler, err := New(context.Background(), next, cfg, "testing") require.NoError(t, err) rw := httptest.NewRecorder() @@ -136,7 +137,7 @@ func TestShouldCompressWhenNoContentEncodingHeader(t *testing.T) { _, err := rw.Write(baseBody) assert.NoError(t, err) }) - handler, err := New(t.Context(), next, dynamic.Compress{Encodings: defaultSupportedEncodings}, "testing") + handler, err := New(context.Background(), next, dynamic.Compress{Encodings: defaultSupportedEncodings}, "testing") require.NoError(t, err) rw := httptest.NewRecorder() @@ -166,7 +167,7 @@ func TestShouldNotCompressWhenContentEncodingHeader(t *testing.T) { http.Error(rw, err.Error(), http.StatusInternalServerError) } }) - handler, err := New(t.Context(), next, dynamic.Compress{Encodings: defaultSupportedEncodings}, "testing") + handler, err := New(context.Background(), next, dynamic.Compress{Encodings: defaultSupportedEncodings}, "testing") require.NoError(t, err) rw := httptest.NewRecorder() @@ -188,7 +189,7 @@ func TestShouldNotCompressWhenNoAcceptEncodingHeader(t *testing.T) { http.Error(rw, err.Error(), http.StatusInternalServerError) } }) - handler, err := New(t.Context(), next, dynamic.Compress{Encodings: defaultSupportedEncodings}, "testing") + handler, err := New(context.Background(), next, dynamic.Compress{Encodings: defaultSupportedEncodings}, "testing") require.NoError(t, err) rw := httptest.NewRecorder() @@ -210,7 +211,7 @@ func TestEmptyAcceptEncoding(t *testing.T) { http.Error(rw, err.Error(), http.StatusInternalServerError) } }) - handler, err := New(t.Context(), next, dynamic.Compress{Encodings: defaultSupportedEncodings}, "testing") + handler, err := New(context.Background(), next, dynamic.Compress{Encodings: defaultSupportedEncodings}, "testing") require.NoError(t, err) rw := httptest.NewRecorder() @@ -237,7 +238,7 @@ func TestShouldNotCompressWhenIdentityAcceptEncodingHeader(t *testing.T) { http.Error(rw, err.Error(), http.StatusInternalServerError) } }) - handler, err := New(t.Context(), next, dynamic.Compress{Encodings: defaultSupportedEncodings}, "testing") + handler, err := New(context.Background(), next, dynamic.Compress{Encodings: defaultSupportedEncodings}, "testing") require.NoError(t, err) rw := httptest.NewRecorder() @@ -264,7 +265,7 @@ func TestShouldNotCompressWhenEmptyAcceptEncodingHeader(t *testing.T) { http.Error(rw, err.Error(), http.StatusInternalServerError) } }) - handler, err := New(t.Context(), next, dynamic.Compress{Encodings: defaultSupportedEncodings}, "testing") + handler, err := New(context.Background(), next, dynamic.Compress{Encodings: defaultSupportedEncodings}, "testing") require.NoError(t, err) rw := httptest.NewRecorder() @@ -286,7 +287,7 @@ func TestShouldNotCompressHeadRequest(t *testing.T) { http.Error(rw, err.Error(), http.StatusInternalServerError) } }) - handler, err := New(t.Context(), next, dynamic.Compress{Encodings: defaultSupportedEncodings}, "testing") + handler, err := New(context.Background(), next, dynamic.Compress{Encodings: defaultSupportedEncodings}, "testing") require.NoError(t, err) rw := httptest.NewRecorder() @@ -376,7 +377,7 @@ func TestShouldNotCompressWhenSpecificContentType(t *testing.T) { } }) - handler, err := New(t.Context(), next, test.conf, "test") + handler, err := New(context.Background(), next, test.conf, "test") require.NoError(t, err) rw := httptest.NewRecorder() @@ -422,7 +423,7 @@ func TestShouldCompressWhenSpecificContentType(t *testing.T) { } }) - handler, err := New(t.Context(), next, test.conf, "test") + handler, err := New(context.Background(), next, test.conf, "test") require.NoError(t, err) rw := httptest.NewRecorder() @@ -472,7 +473,7 @@ func TestIntegrationShouldNotCompress(t *testing.T) { for _, test := range testCases { t.Run(test.name, func(t *testing.T) { - compress, err := New(t.Context(), test.handler, dynamic.Compress{Encodings: defaultSupportedEncodings}, "testing") + compress, err := New(context.Background(), test.handler, dynamic.Compress{Encodings: defaultSupportedEncodings}, "testing") require.NoError(t, err) ts := httptest.NewServer(compress) @@ -507,7 +508,7 @@ func TestShouldWriteHeaderWhenFlush(t *testing.T) { http.Error(rw, err.Error(), http.StatusInternalServerError) } }) - handler, err := New(t.Context(), next, dynamic.Compress{Encodings: defaultSupportedEncodings}, "testing") + handler, err := New(context.Background(), next, dynamic.Compress{Encodings: defaultSupportedEncodings}, "testing") require.NoError(t, err) ts := httptest.NewServer(handler) @@ -558,7 +559,7 @@ func TestIntegrationShouldCompress(t *testing.T) { for _, test := range testCases { t.Run(test.name, func(t *testing.T) { - compress, err := New(t.Context(), test.handler, dynamic.Compress{Encodings: defaultSupportedEncodings}, "testing") + compress, err := New(context.Background(), test.handler, dynamic.Compress{Encodings: defaultSupportedEncodings}, "testing") require.NoError(t, err) ts := httptest.NewServer(compress) @@ -618,7 +619,7 @@ func TestMinResponseBodyBytes(t *testing.T) { MinResponseBodyBytes: test.minResponseBodyBytes, Encodings: defaultSupportedEncodings, } - handler, err := New(t.Context(), next, cfg, "testing") + handler, err := New(context.Background(), next, cfg, "testing") require.NoError(t, err) rw := httptest.NewRecorder() @@ -678,7 +679,7 @@ func Test1xxResponses(t *testing.T) { MinResponseBodyBytes: 1024, Encodings: defaultSupportedEncodings, } - compress, err := New(t.Context(), next, cfg, "testing") + compress, err := New(context.Background(), next, cfg, "testing") require.NoError(t, err) server := httptest.NewServer(compress) @@ -722,7 +723,7 @@ func Test1xxResponses(t *testing.T) { return nil }, } - req, _ := http.NewRequestWithContext(httptrace.WithClientTrace(t.Context(), trace), http.MethodGet, server.URL, nil) + req, _ := http.NewRequestWithContext(httptrace.WithClientTrace(context.Background(), trace), http.MethodGet, server.URL, nil) req.Header.Add(acceptEncodingHeader, test.encoding) res, err := frontendClient.Do(req) @@ -778,7 +779,7 @@ func runCompressionBenchmark(b *testing.B, algorithm string) { _, err := rw.Write(baseBody) assert.NoError(b, err) }) - handler, _ := New(b.Context(), next, dynamic.Compress{}, "testing") + handler, _ := New(context.Background(), next, dynamic.Compress{}, "testing") req, _ := http.NewRequest(http.MethodGet, "/whatever", nil) req.Header.Set("Accept-Encoding", algorithm) diff --git a/pkg/middlewares/contenttype/content_type_test.go b/pkg/middlewares/contenttype/content_type_test.go index 89e3a4866..cfe8ba502 100644 --- a/pkg/middlewares/contenttype/content_type_test.go +++ b/pkg/middlewares/contenttype/content_type_test.go @@ -1,6 +1,7 @@ package contenttype import ( + "context" "net/http" "net/http/httptest" "testing" @@ -59,7 +60,7 @@ func TestAutoDetection(t *testing.T) { if test.autoDetect { var err error - next, err = New(t.Context(), next, dynamic.ContentType{}, "foo-content-type") + next, err = New(context.Background(), next, dynamic.ContentType{}, "foo-content-type") require.NoError(t, err) } diff --git a/pkg/middlewares/customerrors/custom_errors.go b/pkg/middlewares/customerrors/custom_errors.go index 6c1a91c4b..3cd866a74 100644 --- a/pkg/middlewares/customerrors/custom_errors.go +++ b/pkg/middlewares/customerrors/custom_errors.go @@ -15,6 +15,7 @@ import ( "github.com/traefik/traefik/v3/pkg/middlewares/observability" "github.com/traefik/traefik/v3/pkg/types" "github.com/vulcand/oxy/v2/utils" + "go.opentelemetry.io/otel/trace" ) // Compile time validation that the response recorder implements http interfaces correctly. @@ -82,8 +83,8 @@ func New(ctx context.Context, next http.Handler, config dynamic.ErrorPage, servi }, nil } -func (c *customErrors) GetTracingInformation() (string, string) { - return c.name, typeName +func (c *customErrors) GetTracingInformation() (string, string, trace.SpanKind) { + return c.name, typeName, trace.SpanKindInternal } func (c *customErrors) ServeHTTP(rw http.ResponseWriter, req *http.Request) { @@ -122,18 +123,11 @@ func (c *customErrors) ServeHTTP(rw http.ResponseWriter, req *http.Request) { } var query string - - scheme := "http" - if req.TLS != nil { - scheme = "https" - } - orig := &url.URL{Scheme: scheme, Host: req.Host, Path: req.URL.Path, RawPath: req.URL.RawPath, RawQuery: req.URL.RawQuery, Fragment: req.URL.Fragment} - if len(c.backendQuery) > 0 { query = "/" + strings.TrimPrefix(c.backendQuery, "/") query = strings.ReplaceAll(query, "{status}", strconv.Itoa(code)) query = strings.ReplaceAll(query, "{originalStatus}", strconv.Itoa(originalCode)) - query = strings.ReplaceAll(query, "{url}", url.QueryEscape(orig.String())) + query = strings.ReplaceAll(query, "{url}", url.QueryEscape(req.URL.String())) } pageReq, err := newRequest("http://" + req.Host + query) diff --git a/pkg/middlewares/customerrors/custom_errors_test.go b/pkg/middlewares/customerrors/custom_errors_test.go index ec8eff153..37c13e53d 100644 --- a/pkg/middlewares/customerrors/custom_errors_test.go +++ b/pkg/middlewares/customerrors/custom_errors_test.go @@ -170,15 +170,11 @@ func TestHandler(t *testing.T) { } _, _ = fmt.Fprintln(w, http.StatusText(test.backendCode)) }) - errorPageHandler, err := New(t.Context(), handler, *test.errorPage, serviceBuilderMock, "test") + errorPageHandler, err := New(context.Background(), handler, *test.errorPage, serviceBuilderMock, "test") require.NoError(t, err) req := testhelpers.MustNewRequest(http.MethodGet, "http://localhost/test?foo=bar&baz=buz", nil) - // Client like browser and curl will issue a relative HTTP request, which not have a host and scheme in the URL. But the http.NewRequest will set them automatically. - req.URL.Host = "" - req.URL.Scheme = "" - recorder := httptest.NewRecorder() errorPageHandler.ServeHTTP(recorder, req) @@ -209,7 +205,7 @@ func Test1xxResponses(t *testing.T) { config := dynamic.ErrorPage{Service: "error", Query: "/", Status: []string{"200"}} - errorPageHandler, err := New(t.Context(), next, config, serviceBuilderMock, "test") + errorPageHandler, err := New(context.Background(), next, config, serviceBuilderMock, "test") require.NoError(t, err) server := httptest.NewServer(errorPageHandler) @@ -253,7 +249,7 @@ func Test1xxResponses(t *testing.T) { return nil }, } - req, _ := http.NewRequestWithContext(httptrace.WithClientTrace(t.Context(), trace), http.MethodGet, server.URL, nil) + req, _ := http.NewRequestWithContext(httptrace.WithClientTrace(context.Background(), trace), http.MethodGet, server.URL, nil) res, err := frontendClient.Do(req) assert.NoError(t, err) diff --git a/pkg/middlewares/denyrouterrecursion/deny_router_recursion.go b/pkg/middlewares/denyrouterrecursion/deny_router_recursion.go index a33f8c04b..34c6cd12d 100644 --- a/pkg/middlewares/denyrouterrecursion/deny_router_recursion.go +++ b/pkg/middlewares/denyrouterrecursion/deny_router_recursion.go @@ -8,7 +8,7 @@ import ( "github.com/containous/alice" "github.com/rs/zerolog/log" - "github.com/traefik/traefik/v3/pkg/observability/logs" + "github.com/traefik/traefik/v3/pkg/logs" ) const xTraefikRouter = "X-Traefik-Router" diff --git a/pkg/middlewares/gatewayapi/headermodifier/request_header_modifier.go b/pkg/middlewares/gatewayapi/headermodifier/request_header_modifier.go index 8808f3e7e..94d43211a 100644 --- a/pkg/middlewares/gatewayapi/headermodifier/request_header_modifier.go +++ b/pkg/middlewares/gatewayapi/headermodifier/request_header_modifier.go @@ -6,6 +6,7 @@ import ( "github.com/traefik/traefik/v3/pkg/config/dynamic" "github.com/traefik/traefik/v3/pkg/middlewares" + "go.opentelemetry.io/otel/trace" ) const requestHeaderModifierTypeName = "RequestHeaderModifier" @@ -34,8 +35,8 @@ func NewRequestHeaderModifier(ctx context.Context, next http.Handler, config dyn } } -func (r *requestHeaderModifier) GetTracingInformation() (string, string) { - return r.name, requestHeaderModifierTypeName +func (r *requestHeaderModifier) GetTracingInformation() (string, string, trace.SpanKind) { + return r.name, requestHeaderModifierTypeName, trace.SpanKindUnspecified } func (r *requestHeaderModifier) ServeHTTP(rw http.ResponseWriter, req *http.Request) { diff --git a/pkg/middlewares/gatewayapi/headermodifier/request_header_modifier_test.go b/pkg/middlewares/gatewayapi/headermodifier/request_header_modifier_test.go index 36ebbd9db..a9c19980f 100644 --- a/pkg/middlewares/gatewayapi/headermodifier/request_header_modifier_test.go +++ b/pkg/middlewares/gatewayapi/headermodifier/request_header_modifier_test.go @@ -1,6 +1,7 @@ package headermodifier import ( + "context" "net/http" "net/http/httptest" "testing" @@ -102,7 +103,7 @@ func TestRequestHeaderModifier(t *testing.T) { gotHeaders = r.Header }) - handler := NewRequestHeaderModifier(t.Context(), next, test.config, "foo-request-header-modifier") + handler := NewRequestHeaderModifier(context.Background(), next, test.config, "foo-request-header-modifier") req := testhelpers.MustNewRequest(http.MethodGet, "http://localhost", nil) for h, v := range test.requestHeaders { diff --git a/pkg/middlewares/gatewayapi/headermodifier/response_header_modifier.go b/pkg/middlewares/gatewayapi/headermodifier/response_header_modifier.go index e47d23758..2d55b686b 100644 --- a/pkg/middlewares/gatewayapi/headermodifier/response_header_modifier.go +++ b/pkg/middlewares/gatewayapi/headermodifier/response_header_modifier.go @@ -6,6 +6,7 @@ import ( "github.com/traefik/traefik/v3/pkg/config/dynamic" "github.com/traefik/traefik/v3/pkg/middlewares" + "go.opentelemetry.io/otel/trace" ) const responseHeaderModifierTypeName = "ResponseHeaderModifier" @@ -34,8 +35,8 @@ func NewResponseHeaderModifier(ctx context.Context, next http.Handler, config dy } } -func (r *responseHeaderModifier) GetTracingInformation() (string, string) { - return r.name, responseHeaderModifierTypeName +func (r *responseHeaderModifier) GetTracingInformation() (string, string, trace.SpanKind) { + return r.name, responseHeaderModifierTypeName, trace.SpanKindUnspecified } func (r *responseHeaderModifier) ServeHTTP(rw http.ResponseWriter, req *http.Request) { diff --git a/pkg/middlewares/gatewayapi/headermodifier/response_header_modifier_test.go b/pkg/middlewares/gatewayapi/headermodifier/response_header_modifier_test.go index 47a0f3c82..ceea62ca6 100644 --- a/pkg/middlewares/gatewayapi/headermodifier/response_header_modifier_test.go +++ b/pkg/middlewares/gatewayapi/headermodifier/response_header_modifier_test.go @@ -1,6 +1,7 @@ package headermodifier import ( + "context" "net/http" "net/http/httptest" "testing" @@ -103,7 +104,7 @@ func TestResponseHeaderModifier(t *testing.T) { rw.WriteHeader(http.StatusOK) }) - handler := NewResponseHeaderModifier(t.Context(), next, test.config, "foo-response-header-modifier") + handler := NewResponseHeaderModifier(context.Background(), next, test.config, "foo-response-header-modifier") req := testhelpers.MustNewRequest(http.MethodGet, "http://localhost", nil) resp := httptest.NewRecorder() diff --git a/pkg/middlewares/gatewayapi/redirect/request_redirect.go b/pkg/middlewares/gatewayapi/redirect/request_redirect.go index 1305a2372..e2d32e518 100644 --- a/pkg/middlewares/gatewayapi/redirect/request_redirect.go +++ b/pkg/middlewares/gatewayapi/redirect/request_redirect.go @@ -10,6 +10,7 @@ import ( "github.com/traefik/traefik/v3/pkg/config/dynamic" "github.com/traefik/traefik/v3/pkg/middlewares" + "go.opentelemetry.io/otel/trace" ) const typeName = "RequestRedirect" @@ -51,8 +52,8 @@ func NewRequestRedirect(ctx context.Context, next http.Handler, conf dynamic.Req }, nil } -func (r redirect) GetTracingInformation() (string, string) { - return r.name, typeName +func (r redirect) GetTracingInformation() (string, string, trace.SpanKind) { + return r.name, typeName, trace.SpanKindInternal } func (r redirect) ServeHTTP(rw http.ResponseWriter, req *http.Request) { diff --git a/pkg/middlewares/gatewayapi/redirect/request_redirect_test.go b/pkg/middlewares/gatewayapi/redirect/request_redirect_test.go index 530027adb..68eb19bd3 100644 --- a/pkg/middlewares/gatewayapi/redirect/request_redirect_test.go +++ b/pkg/middlewares/gatewayapi/redirect/request_redirect_test.go @@ -1,6 +1,7 @@ package redirect import ( + "context" "net/http" "net/http/httptest" "testing" @@ -184,7 +185,7 @@ func TestRequestRedirectHandler(t *testing.T) { next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}) - handler, err := NewRequestRedirect(t.Context(), next, test.config, "traefikTest") + handler, err := NewRequestRedirect(context.Background(), next, test.config, "traefikTest") if test.wantErr { require.Error(t, err) require.Nil(t, handler) diff --git a/pkg/middlewares/gatewayapi/urlrewrite/url_rewrite.go b/pkg/middlewares/gatewayapi/urlrewrite/url_rewrite.go index bcddb8137..2960a5583 100644 --- a/pkg/middlewares/gatewayapi/urlrewrite/url_rewrite.go +++ b/pkg/middlewares/gatewayapi/urlrewrite/url_rewrite.go @@ -8,6 +8,7 @@ import ( "github.com/traefik/traefik/v3/pkg/config/dynamic" "github.com/traefik/traefik/v3/pkg/middlewares" + "go.opentelemetry.io/otel/trace" ) const ( @@ -37,8 +38,8 @@ func NewURLRewrite(ctx context.Context, next http.Handler, conf dynamic.URLRewri } } -func (u urlRewrite) GetTracingInformation() (string, string) { - return u.name, typeName +func (u urlRewrite) GetTracingInformation() (string, string, trace.SpanKind) { + return u.name, typeName, trace.SpanKindInternal } func (u urlRewrite) ServeHTTP(rw http.ResponseWriter, req *http.Request) { diff --git a/pkg/middlewares/gatewayapi/urlrewrite/url_rewrite_test.go b/pkg/middlewares/gatewayapi/urlrewrite/url_rewrite_test.go index 07b085c65..6b2b9edab 100644 --- a/pkg/middlewares/gatewayapi/urlrewrite/url_rewrite_test.go +++ b/pkg/middlewares/gatewayapi/urlrewrite/url_rewrite_test.go @@ -1,6 +1,7 @@ package urlrewrite import ( + "context" "net/http" "net/http/httptest" "testing" @@ -112,7 +113,7 @@ func TestURLRewriteHandler(t *testing.T) { next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}) - handler := NewURLRewrite(t.Context(), next, test.config, "traefikTest") + handler := NewURLRewrite(context.Background(), next, test.config, "traefikTest") recorder := httptest.NewRecorder() req := httptest.NewRequest(http.MethodGet, test.url, nil) diff --git a/pkg/middlewares/headers/headers.go b/pkg/middlewares/headers/headers.go index cba8b6401..861d1066d 100644 --- a/pkg/middlewares/headers/headers.go +++ b/pkg/middlewares/headers/headers.go @@ -8,6 +8,7 @@ import ( "github.com/traefik/traefik/v3/pkg/config/dynamic" "github.com/traefik/traefik/v3/pkg/middlewares" + "go.opentelemetry.io/otel/trace" ) const ( @@ -57,8 +58,8 @@ func New(ctx context.Context, next http.Handler, cfg dynamic.Headers, name strin }, nil } -func (h *headers) GetTracingInformation() (string, string) { - return h.name, typeName +func (h *headers) GetTracingInformation() (string, string, trace.SpanKind) { + return h.name, typeName, trace.SpanKindInternal } func (h *headers) ServeHTTP(rw http.ResponseWriter, req *http.Request) { diff --git a/pkg/middlewares/headers/headers_test.go b/pkg/middlewares/headers/headers_test.go index df71e6896..194975e36 100644 --- a/pkg/middlewares/headers/headers_test.go +++ b/pkg/middlewares/headers/headers_test.go @@ -3,6 +3,7 @@ package headers // Middleware tests based on https://github.com/unrolled/secure import ( + "context" "io" "net/http" "net/http/httptest" @@ -13,12 +14,13 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/traefik/traefik/v3/pkg/config/dynamic" + "go.opentelemetry.io/otel/trace" ) func TestNew_withoutOptions(t *testing.T) { next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) }) - mid, err := New(t.Context(), next, dynamic.Headers{}, "testing") + mid, err := New(context.Background(), next, dynamic.Headers{}, "testing") require.Errorf(t, err, "headers configuration not valid") assert.Nil(t, mid) @@ -53,7 +55,7 @@ func TestNew_allowedHosts(t *testing.T) { AllowedHosts: []string{"foo.com", "bar.com"}, } - mid, err := New(t.Context(), emptyHandler, cfg, "foo") + mid, err := New(context.Background(), emptyHandler, cfg, "foo") require.NoError(t, err) for _, test := range testCases { @@ -84,7 +86,7 @@ func TestNew_customHeaders(t *testing.T) { }, } - mid, err := New(t.Context(), next, cfg, "testing") + mid, err := New(context.Background(), next, cfg, "testing") require.NoError(t, err) req := httptest.NewRequest(http.MethodGet, "/foo", nil) @@ -106,10 +108,11 @@ func Test_headers_getTracingInformation(t *testing.T) { name: "testing", } - name, typeName := mid.GetTracingInformation() + name, typeName, spanKind := mid.GetTracingInformation() assert.Equal(t, "testing", name) assert.Equal(t, "Headers", typeName) + assert.Equal(t, trace.SpanKindInternal, spanKind) } // This test is an adapted version of net/http/httputil.Test1xxResponses test. @@ -132,7 +135,7 @@ func Test1xxResponses(t *testing.T) { }, } - mid, err := New(t.Context(), next, cfg, "testing") + mid, err := New(context.Background(), next, cfg, "testing") require.NoError(t, err) server := httptest.NewServer(mid) @@ -176,7 +179,7 @@ func Test1xxResponses(t *testing.T) { return nil }, } - req, _ := http.NewRequestWithContext(httptrace.WithClientTrace(t.Context(), trace), http.MethodGet, server.URL, nil) + req, _ := http.NewRequestWithContext(httptrace.WithClientTrace(context.Background(), trace), http.MethodGet, server.URL, nil) res, err := frontendClient.Do(req) assert.NoError(t, err) diff --git a/pkg/middlewares/inflightreq/inflight_req.go b/pkg/middlewares/inflightreq/inflight_req.go index e2df0bcbb..c05193607 100644 --- a/pkg/middlewares/inflightreq/inflight_req.go +++ b/pkg/middlewares/inflightreq/inflight_req.go @@ -7,9 +7,10 @@ import ( "github.com/rs/zerolog" "github.com/traefik/traefik/v3/pkg/config/dynamic" + "github.com/traefik/traefik/v3/pkg/logs" "github.com/traefik/traefik/v3/pkg/middlewares" - "github.com/traefik/traefik/v3/pkg/observability/logs" "github.com/vulcand/oxy/v2/connlimit" + "go.opentelemetry.io/otel/trace" ) const ( @@ -52,8 +53,8 @@ func New(ctx context.Context, next http.Handler, config dynamic.InFlightReq, nam return &inFlightReq{handler: handler, name: name}, nil } -func (i *inFlightReq) GetTracingInformation() (string, string) { - return i.name, typeName +func (i *inFlightReq) GetTracingInformation() (string, string, trace.SpanKind) { + return i.name, typeName, trace.SpanKindInternal } func (i *inFlightReq) ServeHTTP(rw http.ResponseWriter, req *http.Request) { diff --git a/pkg/middlewares/ipallowlist/ip_allowlist.go b/pkg/middlewares/ipallowlist/ip_allowlist.go index 6d9cf0232..46f17c74c 100644 --- a/pkg/middlewares/ipallowlist/ip_allowlist.go +++ b/pkg/middlewares/ipallowlist/ip_allowlist.go @@ -11,6 +11,7 @@ import ( "github.com/traefik/traefik/v3/pkg/ip" "github.com/traefik/traefik/v3/pkg/middlewares" "github.com/traefik/traefik/v3/pkg/middlewares/observability" + "go.opentelemetry.io/otel/trace" ) const ( @@ -64,8 +65,8 @@ func New(ctx context.Context, next http.Handler, config dynamic.IPAllowList, nam }, nil } -func (al *ipAllowLister) GetTracingInformation() (string, string) { - return al.name, typeName +func (al *ipAllowLister) GetTracingInformation() (string, string, trace.SpanKind) { + return al.name, typeName, trace.SpanKindInternal } func (al *ipAllowLister) ServeHTTP(rw http.ResponseWriter, req *http.Request) { diff --git a/pkg/middlewares/ipallowlist/ip_allowlist_test.go b/pkg/middlewares/ipallowlist/ip_allowlist_test.go index fac72dd1c..3dddcc353 100644 --- a/pkg/middlewares/ipallowlist/ip_allowlist_test.go +++ b/pkg/middlewares/ipallowlist/ip_allowlist_test.go @@ -1,6 +1,7 @@ package ipallowlist import ( + "context" "net/http" "net/http/httptest" "testing" @@ -44,7 +45,7 @@ func TestNewIPAllowLister(t *testing.T) { t.Parallel() next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}) - allowLister, err := New(t.Context(), next, test.allowList, "traefikTest") + allowLister, err := New(context.Background(), next, test.allowList, "traefikTest") if test.expectedError { assert.Error(t, err) @@ -104,7 +105,7 @@ func TestIPAllowLister_ServeHTTP(t *testing.T) { t.Parallel() next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}) - allowLister, err := New(t.Context(), next, test.allowList, "traefikTest") + allowLister, err := New(context.Background(), next, test.allowList, "traefikTest") require.NoError(t, err) recorder := httptest.NewRecorder() diff --git a/pkg/middlewares/ipwhitelist/ip_whitelist.go b/pkg/middlewares/ipwhitelist/ip_whitelist.go index 7458b4de6..d8e3dadb3 100644 --- a/pkg/middlewares/ipwhitelist/ip_whitelist.go +++ b/pkg/middlewares/ipwhitelist/ip_whitelist.go @@ -11,6 +11,7 @@ import ( "github.com/traefik/traefik/v3/pkg/ip" "github.com/traefik/traefik/v3/pkg/middlewares" "github.com/traefik/traefik/v3/pkg/middlewares/observability" + "go.opentelemetry.io/otel/trace" ) const ( @@ -54,8 +55,8 @@ func New(ctx context.Context, next http.Handler, config dynamic.IPWhiteList, nam }, nil } -func (wl *ipWhiteLister) GetTracingInformation() (string, string) { - return wl.name, typeName +func (wl *ipWhiteLister) GetTracingInformation() (string, string, trace.SpanKind) { + return wl.name, typeName, trace.SpanKindInternal } func (wl *ipWhiteLister) ServeHTTP(rw http.ResponseWriter, req *http.Request) { diff --git a/pkg/middlewares/ipwhitelist/ip_whitelist_test.go b/pkg/middlewares/ipwhitelist/ip_whitelist_test.go index ce2158fb9..9cf88ef32 100644 --- a/pkg/middlewares/ipwhitelist/ip_whitelist_test.go +++ b/pkg/middlewares/ipwhitelist/ip_whitelist_test.go @@ -1,6 +1,7 @@ package ipwhitelist import ( + "context" "net/http" "net/http/httptest" "testing" @@ -36,7 +37,7 @@ func TestNewIPWhiteLister(t *testing.T) { t.Parallel() next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}) - whiteLister, err := New(t.Context(), next, test.whiteList, "traefikTest") + whiteLister, err := New(context.Background(), next, test.whiteList, "traefikTest") if test.expectedError { assert.Error(t, err) @@ -78,7 +79,7 @@ func TestIPWhiteLister_ServeHTTP(t *testing.T) { t.Parallel() next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}) - whiteLister, err := New(t.Context(), next, test.whiteList, "traefikTest") + whiteLister, err := New(context.Background(), next, test.whiteList, "traefikTest") require.NoError(t, err) recorder := httptest.NewRecorder() diff --git a/pkg/middlewares/metrics/metrics.go b/pkg/middlewares/metrics/metrics.go index 646220a13..e8a1c6dba 100644 --- a/pkg/middlewares/metrics/metrics.go +++ b/pkg/middlewares/metrics/metrics.go @@ -12,12 +12,13 @@ import ( "github.com/containous/alice" gokitmetrics "github.com/go-kit/kit/metrics" "github.com/rs/zerolog/log" + "github.com/traefik/traefik/v3/pkg/metrics" "github.com/traefik/traefik/v3/pkg/middlewares" "github.com/traefik/traefik/v3/pkg/middlewares/capture" "github.com/traefik/traefik/v3/pkg/middlewares/observability" "github.com/traefik/traefik/v3/pkg/middlewares/retry" - "github.com/traefik/traefik/v3/pkg/observability/metrics" traefiktls "github.com/traefik/traefik/v3/pkg/tls" + "go.opentelemetry.io/otel/trace" "google.golang.org/grpc/codes" ) @@ -92,45 +93,33 @@ func NewServiceMiddleware(ctx context.Context, next http.Handler, registry metri } } -// EntryPointMetricsHandler returns the metrics entrypoint handler. -func EntryPointMetricsHandler(ctx context.Context, registry metrics.Registry, entryPointName string) alice.Constructor { +// WrapEntryPointHandler Wraps metrics entrypoint to alice.Constructor. +func WrapEntryPointHandler(ctx context.Context, registry metrics.Registry, entryPointName string) alice.Constructor { return func(next http.Handler) (http.Handler, error) { - if registry == nil || !registry.IsEpEnabled() { - return next, nil - } - return NewEntryPointMiddleware(ctx, next, registry, entryPointName), nil } } -// RouterMetricsHandler returns the metrics router handler. -func RouterMetricsHandler(ctx context.Context, registry metrics.Registry, routerName string, serviceName string) alice.Constructor { +// WrapRouterHandler Wraps metrics router to alice.Constructor. +func WrapRouterHandler(ctx context.Context, registry metrics.Registry, routerName string, serviceName string) alice.Constructor { return func(next http.Handler) (http.Handler, error) { - if registry == nil || !registry.IsRouterEnabled() { - return next, nil - } - return NewRouterMiddleware(ctx, next, registry, routerName, serviceName), nil } } -// ServiceMetricsHandler returns the metrics service handler. -func ServiceMetricsHandler(ctx context.Context, registry metrics.Registry, serviceName string) alice.Constructor { +// WrapServiceHandler Wraps metrics service to alice.Constructor. +func WrapServiceHandler(ctx context.Context, registry metrics.Registry, serviceName string) alice.Constructor { return func(next http.Handler) (http.Handler, error) { - if registry == nil || !registry.IsSvcEnabled() { - return next, nil - } - return NewServiceMiddleware(ctx, next, registry, serviceName), nil } } -func (m *metricsMiddleware) GetTracingInformation() (string, string) { - return m.name, typeName +func (m *metricsMiddleware) GetTracingInformation() (string, string, trace.SpanKind) { + return m.name, typeName, trace.SpanKindInternal } func (m *metricsMiddleware) ServeHTTP(rw http.ResponseWriter, req *http.Request) { - if !observability.MetricsEnabled(req.Context()) { + if val := req.Context().Value(observability.DisableMetricsKey); val != nil { m.next.ServeHTTP(rw, req) return } diff --git a/pkg/middlewares/middleware.go b/pkg/middlewares/middleware.go index 5b7cd6d70..928a9982e 100644 --- a/pkg/middlewares/middleware.go +++ b/pkg/middlewares/middleware.go @@ -5,7 +5,7 @@ import ( "github.com/rs/zerolog" "github.com/rs/zerolog/log" - "github.com/traefik/traefik/v3/pkg/observability/logs" + "github.com/traefik/traefik/v3/pkg/logs" ) // GetLogger creates a logger with the middleware fields. diff --git a/pkg/middlewares/observability/entrypoint.go b/pkg/middlewares/observability/entrypoint.go index e9cd3f10f..64b89b921 100644 --- a/pkg/middlewares/observability/entrypoint.go +++ b/pkg/middlewares/observability/entrypoint.go @@ -8,7 +8,7 @@ import ( "github.com/containous/alice" "github.com/rs/zerolog/log" "github.com/traefik/traefik/v3/pkg/middlewares" - "github.com/traefik/traefik/v3/pkg/observability/tracing" + "github.com/traefik/traefik/v3/pkg/tracing" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/trace" "go.opentelemetry.io/otel/trace/noop" @@ -48,20 +48,11 @@ func newEntryPoint(ctx context.Context, tracer *tracing.Tracer, entryPointName s } func (e *entryPointTracing) ServeHTTP(rw http.ResponseWriter, req *http.Request) { - if e.tracer == nil || !TracingEnabled(req.Context()) { - e.next.ServeHTTP(rw, req) - return - } - tracingCtx := tracing.ExtractCarrierIntoContext(req.Context(), req.Header) start := time.Now() - - // Follow semantic conventions defined by OTEL: https://opentelemetry.io/docs/specs/semconv/http/http-spans/#name - // At the moment this implementation only gets the {method} as there is no guarantee {target} is low cardinality. - tracingCtx, span := e.tracer.Start(tracingCtx, req.Method, trace.WithSpanKind(trace.SpanKindServer), trace.WithTimestamp(start)) + tracingCtx, span := e.tracer.Start(tracingCtx, "EntryPoint", trace.WithSpanKind(trace.SpanKindServer), trace.WithTimestamp(start)) // Associate the request context with the logger. - // This allows the logger to be aware of the tracing context and log accordingly (TraceID, SpanID, etc.). logger := log.Ctx(tracingCtx).With().Ctx(tracingCtx).Logger() loggerCtx := logger.WithContext(tracingCtx) diff --git a/pkg/middlewares/observability/entrypoint_test.go b/pkg/middlewares/observability/entrypoint_test.go index 2e02e15ce..d39d3b842 100644 --- a/pkg/middlewares/observability/entrypoint_test.go +++ b/pkg/middlewares/observability/entrypoint_test.go @@ -1,13 +1,13 @@ package observability import ( + "context" "net/http" "net/http/httptest" "testing" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/traefik/traefik/v3/pkg/observability/tracing" + "github.com/traefik/traefik/v3/pkg/tracing" "go.opentelemetry.io/otel/attribute" ) @@ -20,15 +20,13 @@ func TestEntryPointMiddleware_tracing(t *testing.T) { testCases := []struct { desc string entryPoint string - method string expected expected }{ { - desc: "GET", + desc: "basic test", entryPoint: "test", - method: http.MethodGet, expected: expected{ - name: "GET", + name: "EntryPoint", attributes: []attribute.KeyValue{ attribute.String("span.kind", "server"), attribute.String("entry_point", "test"), @@ -50,38 +48,11 @@ func TestEntryPointMiddleware_tracing(t *testing.T) { }, }, }, - { - desc: "POST", - entryPoint: "test", - method: http.MethodPost, - expected: expected{ - name: "POST", - attributes: []attribute.KeyValue{ - attribute.String("span.kind", "server"), - attribute.String("entry_point", "test"), - attribute.String("http.request.method", "POST"), - attribute.String("network.protocol.version", "1.1"), - attribute.Int64("http.request.body.size", int64(0)), - attribute.String("url.path", "/search"), - attribute.String("url.query", "q=Opentelemetry&token=REDACTED"), - attribute.String("url.scheme", "http"), - attribute.String("user_agent.original", "entrypoint-test"), - attribute.String("server.address", "www.test.com"), - attribute.String("network.peer.address", "10.0.0.1"), - attribute.String("client.address", "10.0.0.1"), - attribute.Int64("client.port", int64(1234)), - attribute.Int64("network.peer.port", int64(1234)), - attribute.StringSlice("http.request.header.x-foo", []string{"foo", "bar"}), - attribute.Int64("http.response.status_code", int64(404)), - attribute.StringSlice("http.response.header.x-bar", []string{"foo", "bar"}), - }, - }, - }, } for _, test := range testCases { t.Run(test.desc, func(t *testing.T) { - req := httptest.NewRequest(test.method, "http://www.test.com/search?q=Opentelemetry&token=123", nil) + req := httptest.NewRequest(http.MethodGet, "http://www.test.com/search?q=Opentelemetry&token=123", nil) rw := httptest.NewRecorder() req.RemoteAddr = "10.0.0.1:1234" req.Header.Set("User-Agent", "entrypoint-test") @@ -97,17 +68,13 @@ func TestEntryPointMiddleware_tracing(t *testing.T) { tracer := &mockTracer{} - // Injection of the observability variables in the request context. - req = req.WithContext(WithObservability(req.Context(), Observability{ - TracingEnabled: true, - })) - - handler := newEntryPoint(t.Context(), tracing.NewTracer(tracer, []string{"X-Foo"}, []string{"X-Bar"}, []string{"q"}), test.entryPoint, next) + handler := newEntryPoint(context.Background(), tracing.NewTracer(tracer, []string{"X-Foo"}, []string{"X-Bar"}, []string{"q"}), test.entryPoint, next) handler.ServeHTTP(rw, req) - require.Len(t, tracer.spans, 1) - assert.Equal(t, test.expected.name, tracer.spans[0].name) - assert.Equal(t, test.expected.attributes, tracer.spans[0].attributes) + for _, span := range tracer.spans { + assert.Equal(t, test.expected.name, span.name) + assert.Equal(t, test.expected.attributes, span.attributes) + } }) } } diff --git a/pkg/middlewares/observability/middleware.go b/pkg/middlewares/observability/middleware.go index 2d677ff82..51e4b6d50 100644 --- a/pkg/middlewares/observability/middleware.go +++ b/pkg/middlewares/observability/middleware.go @@ -6,15 +6,15 @@ import ( "github.com/containous/alice" "github.com/rs/zerolog/log" - "github.com/traefik/traefik/v3/pkg/observability/logs" - "github.com/traefik/traefik/v3/pkg/observability/tracing" + "github.com/traefik/traefik/v3/pkg/logs" + "github.com/traefik/traefik/v3/pkg/tracing" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/trace" ) // Traceable embeds tracing information. type Traceable interface { - GetTracingInformation() (name string, typeName string) + GetTracingInformation() (name string, typeName string, spanKind trace.SpanKind) } // WrapMiddleware adds traceability to an alice.Constructor. @@ -29,20 +29,21 @@ func WrapMiddleware(ctx context.Context, constructor alice.Constructor) alice.Co } if traceableHandler, ok := handler.(Traceable); ok { - name, typeName := traceableHandler.GetTracingInformation() + name, typeName, spanKind := traceableHandler.GetTracingInformation() log.Ctx(ctx).Debug().Str(logs.MiddlewareName, name).Msg("Adding tracing to middleware") - return NewMiddleware(handler, name, typeName), nil + return NewMiddleware(handler, name, typeName, spanKind), nil } return handler, nil } } // NewMiddleware returns a http.Handler struct. -func NewMiddleware(next http.Handler, name string, typeName string) http.Handler { +func NewMiddleware(next http.Handler, name string, typeName string, spanKind trace.SpanKind) http.Handler { return &middlewareTracing{ next: next, name: name, typeName: typeName, + spanKind: spanKind, } } @@ -51,11 +52,12 @@ type middlewareTracing struct { next http.Handler name string typeName string + spanKind trace.SpanKind } func (w *middlewareTracing) ServeHTTP(rw http.ResponseWriter, req *http.Request) { - if tracer := tracing.TracerFromContext(req.Context()); tracer != nil && DetailedTracingEnabled(req.Context()) { - tracingCtx, span := tracer.Start(req.Context(), w.typeName, trace.WithSpanKind(trace.SpanKindInternal)) + if tracer := tracing.TracerFromContext(req.Context()); tracer != nil { + tracingCtx, span := tracer.Start(req.Context(), w.typeName, trace.WithSpanKind(w.spanKind)) defer span.End() req = req.WithContext(tracingCtx) diff --git a/pkg/middlewares/observability/mock_tracing_test.go b/pkg/middlewares/observability/mock_tracing_test.go index 3a2ffd520..4a70b77bc 100644 --- a/pkg/middlewares/observability/mock_tracing_test.go +++ b/pkg/middlewares/observability/mock_tracing_test.go @@ -37,7 +37,7 @@ func (t *mockTracer) Start(ctx context.Context, name string, opts ...trace.SpanS return trace.ContextWithSpan(ctx, span), span } -// mockSpan is an implementation of Span that performs no operations. +// mockSpan is an implementation of Span that preforms no operations. type mockSpan struct { embedded.Span diff --git a/pkg/middlewares/observability/observability.go b/pkg/middlewares/observability/observability.go index 1490a1e8e..1ee9f3b99 100644 --- a/pkg/middlewares/observability/observability.go +++ b/pkg/middlewares/observability/observability.go @@ -3,7 +3,6 @@ package observability import ( "context" "fmt" - "net/http" "go.opentelemetry.io/otel/codes" "go.opentelemetry.io/otel/trace" @@ -11,58 +10,8 @@ import ( type contextKey int -const observabilityKey contextKey = iota - -type Observability struct { - AccessLogsEnabled bool - MetricsEnabled bool - SemConvMetricsEnabled bool - TracingEnabled bool - DetailedTracingEnabled bool -} - -// WithObservabilityHandler sets the observability state in the context for the next handler. -// This is also used for testing purposes to control whether access logs are enabled or not. -func WithObservabilityHandler(next http.Handler, obs Observability) http.Handler { - return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { - next.ServeHTTP(rw, req.WithContext(WithObservability(req.Context(), obs))) - }) -} - -// WithObservability injects the observability state into the context. -func WithObservability(ctx context.Context, obs Observability) context.Context { - return context.WithValue(ctx, observabilityKey, obs) -} - -// AccessLogsEnabled returns whether access-logs are enabled. -func AccessLogsEnabled(ctx context.Context) bool { - obs, ok := ctx.Value(observabilityKey).(Observability) - return ok && obs.AccessLogsEnabled -} - -// MetricsEnabled returns whether metrics are enabled. -func MetricsEnabled(ctx context.Context) bool { - obs, ok := ctx.Value(observabilityKey).(Observability) - return ok && obs.MetricsEnabled -} - -// SemConvMetricsEnabled returns whether metrics are enabled. -func SemConvMetricsEnabled(ctx context.Context) bool { - obs, ok := ctx.Value(observabilityKey).(Observability) - return ok && obs.SemConvMetricsEnabled -} - -// TracingEnabled returns whether tracing is enabled. -func TracingEnabled(ctx context.Context) bool { - obs, ok := ctx.Value(observabilityKey).(Observability) - return ok && obs.TracingEnabled -} - -// DetailedTracingEnabled returns whether detailed tracing is enabled. -func DetailedTracingEnabled(ctx context.Context) bool { - obs, ok := ctx.Value(observabilityKey).(Observability) - return ok && obs.DetailedTracingEnabled -} +// DisableMetricsKey is a context key used to disable the metrics. +const DisableMetricsKey contextKey = iota // SetStatusErrorf flags the span as in error and log an event. func SetStatusErrorf(ctx context.Context, format string, args ...interface{}) { diff --git a/pkg/middlewares/observability/router.go b/pkg/middlewares/observability/router.go index e840a1aa4..5339726ff 100644 --- a/pkg/middlewares/observability/router.go +++ b/pkg/middlewares/observability/router.go @@ -5,11 +5,11 @@ import ( "net/http" "github.com/containous/alice" + "github.com/traefik/traefik/v3/pkg/logs" "github.com/traefik/traefik/v3/pkg/middlewares" - "github.com/traefik/traefik/v3/pkg/observability/logs" - "github.com/traefik/traefik/v3/pkg/observability/tracing" + "github.com/traefik/traefik/v3/pkg/tracing" "go.opentelemetry.io/otel/attribute" - semconv "go.opentelemetry.io/otel/semconv/v1.37.0" + semconv "go.opentelemetry.io/otel/semconv/v1.26.0" "go.opentelemetry.io/otel/trace" ) @@ -45,7 +45,7 @@ func newRouter(ctx context.Context, router, routerRule, service string, next htt } func (f *routerTracing) ServeHTTP(rw http.ResponseWriter, req *http.Request) { - if tracer := tracing.TracerFromContext(req.Context()); tracer != nil && DetailedTracingEnabled(req.Context()) { + if tracer := tracing.TracerFromContext(req.Context()); tracer != nil { tracingCtx, span := tracer.Start(req.Context(), "Router", trace.WithSpanKind(trace.SpanKindInternal)) defer span.End() diff --git a/pkg/middlewares/observability/router_test.go b/pkg/middlewares/observability/router_test.go index b519e6172..23bb46ab9 100644 --- a/pkg/middlewares/observability/router_test.go +++ b/pkg/middlewares/observability/router_test.go @@ -1,6 +1,7 @@ package observability import ( + "context" "net/http" "net/http/httptest" "testing" @@ -30,7 +31,7 @@ func TestNewRouter(t *testing.T) { routerRule: "Path(`/`)", expected: []expected{ { - name: "GET", + name: "EntryPoint", attributes: []attribute.KeyValue{ attribute.String("span.kind", "server"), }, @@ -63,7 +64,7 @@ func TestNewRouter(t *testing.T) { req.Header.Set("User-Agent", "router-test") tracer := &mockTracer{} - tracingCtx, entryPointSpan := tracer.Start(req.Context(), http.MethodGet, trace.WithSpanKind(trace.SpanKindServer)) + tracingCtx, entryPointSpan := tracer.Start(req.Context(), "EntryPoint", trace.WithSpanKind(trace.SpanKindServer)) defer entryPointSpan.End() req = req.WithContext(tracingCtx) @@ -73,7 +74,7 @@ func TestNewRouter(t *testing.T) { rw.WriteHeader(http.StatusNotFound) }) - handler := newRouter(t.Context(), test.router, test.routerRule, test.service, next) + handler := newRouter(context.Background(), test.router, test.routerRule, test.service, next) handler.ServeHTTP(rw, req) for i, span := range tracer.spans { diff --git a/pkg/middlewares/observability/semconv.go b/pkg/middlewares/observability/semconv.go index 41aa52696..51f4480b5 100644 --- a/pkg/middlewares/observability/semconv.go +++ b/pkg/middlewares/observability/semconv.go @@ -10,13 +10,13 @@ import ( "github.com/containous/alice" "github.com/rs/zerolog/log" + "github.com/traefik/traefik/v3/pkg/logs" + "github.com/traefik/traefik/v3/pkg/metrics" "github.com/traefik/traefik/v3/pkg/middlewares" "github.com/traefik/traefik/v3/pkg/middlewares/capture" - "github.com/traefik/traefik/v3/pkg/observability/logs" - "github.com/traefik/traefik/v3/pkg/observability/metrics" "go.opentelemetry.io/otel/attribute" - semconv "go.opentelemetry.io/otel/semconv/v1.37.0" - "go.opentelemetry.io/otel/semconv/v1.37.0/httpconv" + "go.opentelemetry.io/otel/metric" + semconv "go.opentelemetry.io/otel/semconv/v1.26.0" ) const ( @@ -46,7 +46,7 @@ func newServerMetricsSemConv(ctx context.Context, semConvMetricRegistry *metrics } func (e *semConvServerMetrics) ServeHTTP(rw http.ResponseWriter, req *http.Request) { - if e.semConvMetricRegistry == nil || !SemConvMetricsEnabled(req.Context()) { + if e.semConvMetricRegistry == nil || e.semConvMetricRegistry.HTTPServerRequestDuration() == nil { e.next.ServeHTTP(rw, req) return } @@ -70,12 +70,12 @@ func (e *semConvServerMetrics) ServeHTTP(rw http.ResponseWriter, req *http.Reque attrs = append(attrs, attribute.Key("error.type").String(strconv.Itoa(capt.StatusCode()))) } - // Additional optional attributes. + attrs = append(attrs, semconv.HTTPRequestMethodKey.String(req.Method)) attrs = append(attrs, semconv.HTTPResponseStatusCode(capt.StatusCode())) attrs = append(attrs, semconv.NetworkProtocolName(strings.ToLower(req.Proto))) attrs = append(attrs, semconv.NetworkProtocolVersion(Proto(req.Proto))) attrs = append(attrs, semconv.ServerAddress(req.Host)) + attrs = append(attrs, semconv.URLScheme(req.Header.Get("X-Forwarded-Proto"))) - e.semConvMetricRegistry.HTTPServerRequestDuration().Record(req.Context(), end.Sub(start).Seconds(), - httpconv.RequestMethodAttr(req.Method), req.Header.Get("X-Forwarded-Proto"), attrs...) + e.semConvMetricRegistry.HTTPServerRequestDuration().Record(req.Context(), end.Sub(start).Seconds(), metric.WithAttributes(attrs...)) } diff --git a/pkg/middlewares/observability/semconv_test.go b/pkg/middlewares/observability/semconv_test.go index 960d4b98a..08846c4d7 100644 --- a/pkg/middlewares/observability/semconv_test.go +++ b/pkg/middlewares/observability/semconv_test.go @@ -1,6 +1,7 @@ package observability import ( + "context" "net/http" "net/http/httptest" "testing" @@ -8,9 +9,9 @@ import ( "github.com/stretchr/testify/require" ptypes "github.com/traefik/paerser/types" + "github.com/traefik/traefik/v3/pkg/metrics" "github.com/traefik/traefik/v3/pkg/middlewares/capture" - "github.com/traefik/traefik/v3/pkg/observability/metrics" - otypes "github.com/traefik/traefik/v3/pkg/observability/types" + "github.com/traefik/traefik/v3/pkg/types" "go.opentelemetry.io/otel/attribute" sdkmetric "go.opentelemetry.io/otel/sdk/metric" "go.opentelemetry.io/otel/sdk/metric/metricdata" @@ -54,7 +55,7 @@ func TestSemConvServerMetrics(t *testing.T) { t.Run(test.desc, func(t *testing.T) { t.Parallel() - var cfg otypes.OTLP + var cfg types.OTLP (&cfg).SetDefaults() cfg.AddRoutersLabels = true cfg.PushInterval = ptypes.Duration(10 * time.Millisecond) @@ -64,7 +65,7 @@ func TestSemConvServerMetrics(t *testing.T) { // force the meter provider with manual reader to collect metrics for the test. metrics.SetMeterProvider(meterProvider) - semConvMetricRegistry, err := metrics.NewSemConvMetricRegistry(t.Context(), &cfg) + semConvMetricRegistry, err := metrics.NewSemConvMetricRegistry(context.Background(), &cfg) require.NoError(t, err) require.NotNil(t, semConvMetricRegistry) @@ -78,20 +79,15 @@ func TestSemConvServerMetrics(t *testing.T) { rw.WriteHeader(test.statusCode) }) - handler := newServerMetricsSemConv(t.Context(), semConvMetricRegistry, next) + handler := newServerMetricsSemConv(context.Background(), semConvMetricRegistry, next) handler, err = capture.Wrap(handler) require.NoError(t, err) - // Injection of the observability variables in the request context. - handler = WithObservabilityHandler(handler, Observability{ - SemConvMetricsEnabled: true, - }) - handler.ServeHTTP(rw, req) got := metricdata.ResourceMetrics{} - err = rdr.Collect(t.Context(), &got) + err = rdr.Collect(context.Background(), &got) require.NoError(t, err) require.Len(t, got.ScopeMetrics, 1) diff --git a/pkg/middlewares/observability/service.go b/pkg/middlewares/observability/service.go index ef580f9bf..cacd3ef1b 100644 --- a/pkg/middlewares/observability/service.go +++ b/pkg/middlewares/observability/service.go @@ -4,9 +4,9 @@ import ( "context" "net/http" + "github.com/traefik/traefik/v3/pkg/logs" "github.com/traefik/traefik/v3/pkg/middlewares" - "github.com/traefik/traefik/v3/pkg/observability/logs" - "github.com/traefik/traefik/v3/pkg/observability/tracing" + "github.com/traefik/traefik/v3/pkg/tracing" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/trace" ) @@ -32,7 +32,7 @@ func NewService(ctx context.Context, service string, next http.Handler) http.Han } func (t *serviceTracing) ServeHTTP(rw http.ResponseWriter, req *http.Request) { - if tracer := tracing.TracerFromContext(req.Context()); tracer != nil && DetailedTracingEnabled(req.Context()) { + if tracer := tracing.TracerFromContext(req.Context()); tracer != nil { tracingCtx, span := tracer.Start(req.Context(), "Service", trace.WithSpanKind(trace.SpanKindInternal)) defer span.End() diff --git a/pkg/middlewares/observability/service_test.go b/pkg/middlewares/observability/service_test.go index 99197ed9b..db411e718 100644 --- a/pkg/middlewares/observability/service_test.go +++ b/pkg/middlewares/observability/service_test.go @@ -1,6 +1,7 @@ package observability import ( + "context" "net/http" "net/http/httptest" "testing" @@ -26,7 +27,7 @@ func TestNewService(t *testing.T) { service: "myService", expected: []expected{ { - name: "GET", + name: "EntryPoint", attributes: []attribute.KeyValue{ attribute.String("span.kind", "server"), }, @@ -57,7 +58,7 @@ func TestNewService(t *testing.T) { req.Header.Set("User-Agent", "service-test") tracer := &mockTracer{} - tracingCtx, entryPointSpan := tracer.Start(req.Context(), http.MethodGet, trace.WithSpanKind(trace.SpanKindServer)) + tracingCtx, entryPointSpan := tracer.Start(req.Context(), "EntryPoint", trace.WithSpanKind(trace.SpanKindServer)) defer entryPointSpan.End() req = req.WithContext(tracingCtx) @@ -67,7 +68,7 @@ func TestNewService(t *testing.T) { rw.WriteHeader(http.StatusNotFound) }) - handler := NewService(t.Context(), test.service, next) + handler := NewService(context.Background(), test.service, next) handler.ServeHTTP(rw, req) for i, span := range tracer.spans { diff --git a/pkg/middlewares/passtlsclientcert/pass_tls_client_cert.go b/pkg/middlewares/passtlsclientcert/pass_tls_client_cert.go index 6f892a779..cadc375af 100644 --- a/pkg/middlewares/passtlsclientcert/pass_tls_client_cert.go +++ b/pkg/middlewares/passtlsclientcert/pass_tls_client_cert.go @@ -14,6 +14,7 @@ import ( "github.com/rs/zerolog/log" "github.com/traefik/traefik/v3/pkg/config/dynamic" "github.com/traefik/traefik/v3/pkg/middlewares" + "go.opentelemetry.io/otel/trace" ) const typeName = "PassClientTLSCert" @@ -138,8 +139,8 @@ func New(ctx context.Context, next http.Handler, config dynamic.PassTLSClientCer }, nil } -func (p *passTLSClientCert) GetTracingInformation() (string, string) { - return p.name, typeName +func (p *passTLSClientCert) GetTracingInformation() (string, string, trace.SpanKind) { + return p.name, typeName, trace.SpanKindInternal } func (p *passTLSClientCert) ServeHTTP(rw http.ResponseWriter, req *http.Request) { diff --git a/pkg/middlewares/passtlsclientcert/pass_tls_client_cert_test.go b/pkg/middlewares/passtlsclientcert/pass_tls_client_cert_test.go index 7165e9dd3..20b4f087c 100644 --- a/pkg/middlewares/passtlsclientcert/pass_tls_client_cert_test.go +++ b/pkg/middlewares/passtlsclientcert/pass_tls_client_cert_test.go @@ -1,6 +1,7 @@ package passtlsclientcert import ( + "context" "crypto/tls" "crypto/x509" "encoding/pem" @@ -312,7 +313,7 @@ func TestPassTLSClientCert_PEM(t *testing.T) { t.Run(test.desc, func(t *testing.T) { t.Parallel() - tlsClientHeaders, err := New(t.Context(), next, test.config, "foo") + tlsClientHeaders, err := New(context.Background(), next, test.config, "foo") require.NoError(t, err) res := httptest.NewRecorder() @@ -534,7 +535,7 @@ func TestPassTLSClientCert_certInfo(t *testing.T) { t.Run(test.desc, func(t *testing.T) { t.Parallel() - tlsClientHeaders, err := New(t.Context(), next, test.config, "foo") + tlsClientHeaders, err := New(context.Background(), next, test.config, "foo") require.NoError(t, err) res := httptest.NewRecorder() diff --git a/pkg/middlewares/ratelimiter/rate_limiter.go b/pkg/middlewares/ratelimiter/rate_limiter.go index fcc553e15..043974d47 100755 --- a/pkg/middlewares/ratelimiter/rate_limiter.go +++ b/pkg/middlewares/ratelimiter/rate_limiter.go @@ -14,6 +14,7 @@ import ( "github.com/traefik/traefik/v3/pkg/middlewares" "github.com/traefik/traefik/v3/pkg/middlewares/observability" "github.com/vulcand/oxy/v2/utils" + "go.opentelemetry.io/otel/trace" "golang.org/x/time/rate" ) @@ -126,8 +127,8 @@ func New(ctx context.Context, next http.Handler, config dynamic.RateLimit, name }, nil } -func (rl *rateLimiter) GetTracingInformation() (string, string) { - return rl.name, typeName +func (rl *rateLimiter) GetTracingInformation() (string, string, trace.SpanKind) { + return rl.name, typeName, trace.SpanKindInternal } func (rl *rateLimiter) ServeHTTP(rw http.ResponseWriter, req *http.Request) { diff --git a/pkg/middlewares/ratelimiter/rate_limiter_test.go b/pkg/middlewares/ratelimiter/rate_limiter_test.go index a724c6fe6..a741be4e4 100644 --- a/pkg/middlewares/ratelimiter/rate_limiter_test.go +++ b/pkg/middlewares/ratelimiter/rate_limiter_test.go @@ -110,7 +110,7 @@ func TestNewRateLimiter(t *testing.T) { next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}) - h, err := New(t.Context(), next, test.config, "rate-limiter") + h, err := New(context.Background(), next, test.config, "rate-limiter") if test.expectedError != "" { assert.EqualError(t, err, test.expectedError) } else { @@ -274,7 +274,7 @@ func TestInMemoryRateLimit(t *testing.T) { next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { reqCount++ }) - h, err := New(t.Context(), next, test.config, "rate-limiter") + h, err := New(context.Background(), next, test.config, "rate-limiter") require.NoError(t, err) loadPeriod := time.Duration(1e9 / test.incomingLoad) @@ -477,7 +477,7 @@ func TestRedisRateLimit(t *testing.T) { test.config.Redis = &dynamic.Redis{ Endpoints: []string{"localhost:6379"}, } - h, err := New(t.Context(), next, test.config, "rate-limiter") + h, err := New(context.Background(), next, test.config, "rate-limiter") require.NoError(t, err) l := h.(*rateLimiter) diff --git a/pkg/middlewares/recovery/recovery_test.go b/pkg/middlewares/recovery/recovery_test.go index d93cb77eb..1929f0b54 100644 --- a/pkg/middlewares/recovery/recovery_test.go +++ b/pkg/middlewares/recovery/recovery_test.go @@ -1,6 +1,7 @@ package recovery import ( + "context" "errors" "io" "net/http" @@ -46,7 +47,7 @@ func TestRecoverHandler(t *testing.T) { } panic(test.panicErr) } - recovery, err := New(t.Context(), http.HandlerFunc(fn)) + recovery, err := New(context.Background(), http.HandlerFunc(fn)) require.NoError(t, err) server := httptest.NewServer(recovery) diff --git a/pkg/middlewares/redirect/redirect.go b/pkg/middlewares/redirect/redirect.go index d8499d1aa..25ca6a2ac 100644 --- a/pkg/middlewares/redirect/redirect.go +++ b/pkg/middlewares/redirect/redirect.go @@ -6,6 +6,7 @@ import ( "regexp" "github.com/vulcand/oxy/v2/utils" + "go.opentelemetry.io/otel/trace" ) const ( @@ -45,8 +46,8 @@ func newRedirect(next http.Handler, regex, replacement string, permanent bool, r }, nil } -func (r *redirect) GetTracingInformation() (string, string) { - return r.name, typeName +func (r *redirect) GetTracingInformation() (string, string, trace.SpanKind) { + return r.name, typeName, trace.SpanKindInternal } func (r *redirect) ServeHTTP(rw http.ResponseWriter, req *http.Request) { diff --git a/pkg/middlewares/redirect/redirect_regex_test.go b/pkg/middlewares/redirect/redirect_regex_test.go index 78d4fabd0..4239c1ae0 100644 --- a/pkg/middlewares/redirect/redirect_regex_test.go +++ b/pkg/middlewares/redirect/redirect_regex_test.go @@ -1,6 +1,7 @@ package redirect import ( + "context" "crypto/tls" "net/http" "net/http/httptest" @@ -157,7 +158,7 @@ func TestRedirectRegexHandler(t *testing.T) { t.Parallel() next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}) - handler, err := NewRedirectRegex(t.Context(), next, test.config, "traefikTest") + handler, err := NewRedirectRegex(context.Background(), next, test.config, "traefikTest") if test.errorExpected { require.Error(t, err) diff --git a/pkg/middlewares/redirect/redirect_scheme_test.go b/pkg/middlewares/redirect/redirect_scheme_test.go index aa90bddbc..258bf6c04 100644 --- a/pkg/middlewares/redirect/redirect_scheme_test.go +++ b/pkg/middlewares/redirect/redirect_scheme_test.go @@ -1,6 +1,7 @@ package redirect import ( + "context" "crypto/tls" "net/http" "net/http/httptest" @@ -286,7 +287,7 @@ func TestRedirectSchemeHandler(t *testing.T) { t.Parallel() next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}) - handler, err := NewRedirectScheme(t.Context(), next, test.config, "traefikTest") + handler, err := NewRedirectScheme(context.Background(), next, test.config, "traefikTest") if test.errorExpected { require.Error(t, err) diff --git a/pkg/middlewares/replacepath/replace_path.go b/pkg/middlewares/replacepath/replace_path.go index d211b83dd..9c8e404d4 100644 --- a/pkg/middlewares/replacepath/replace_path.go +++ b/pkg/middlewares/replacepath/replace_path.go @@ -8,6 +8,7 @@ import ( "github.com/traefik/traefik/v3/pkg/config/dynamic" "github.com/traefik/traefik/v3/pkg/middlewares" "github.com/traefik/traefik/v3/pkg/middlewares/observability" + "go.opentelemetry.io/otel/trace" ) const ( @@ -34,8 +35,8 @@ func New(ctx context.Context, next http.Handler, config dynamic.ReplacePath, nam }, nil } -func (r *replacePath) GetTracingInformation() (string, string) { - return r.name, typeName +func (r *replacePath) GetTracingInformation() (string, string, trace.SpanKind) { + return r.name, typeName, trace.SpanKindInternal } func (r *replacePath) ServeHTTP(rw http.ResponseWriter, req *http.Request) { diff --git a/pkg/middlewares/replacepath/replace_path_test.go b/pkg/middlewares/replacepath/replace_path_test.go index 9f4923aea..5998579d1 100644 --- a/pkg/middlewares/replacepath/replace_path_test.go +++ b/pkg/middlewares/replacepath/replace_path_test.go @@ -1,6 +1,7 @@ package replacepath import ( + "context" "net/http" "net/http/httptest" "testing" @@ -81,7 +82,7 @@ func TestReplacePath(t *testing.T) { requestURI = r.RequestURI }) - handler, err := New(t.Context(), next, test.config, "foo-replace-path") + handler, err := New(context.Background(), next, test.config, "foo-replace-path") require.NoError(t, err) server := httptest.NewServer(handler) diff --git a/pkg/middlewares/replacepathregex/replace_path_regex.go b/pkg/middlewares/replacepathregex/replace_path_regex.go index 42cd79404..f04e9d9a0 100644 --- a/pkg/middlewares/replacepathregex/replace_path_regex.go +++ b/pkg/middlewares/replacepathregex/replace_path_regex.go @@ -12,6 +12,7 @@ import ( "github.com/traefik/traefik/v3/pkg/middlewares" "github.com/traefik/traefik/v3/pkg/middlewares/observability" "github.com/traefik/traefik/v3/pkg/middlewares/replacepath" + "go.opentelemetry.io/otel/trace" ) const typeName = "ReplacePathRegex" @@ -41,8 +42,8 @@ func New(ctx context.Context, next http.Handler, config dynamic.ReplacePathRegex }, nil } -func (rp *replacePathRegex) GetTracingInformation() (string, string) { - return rp.name, typeName +func (rp *replacePathRegex) GetTracingInformation() (string, string, trace.SpanKind) { + return rp.name, typeName, trace.SpanKindInternal } func (rp *replacePathRegex) ServeHTTP(rw http.ResponseWriter, req *http.Request) { diff --git a/pkg/middlewares/replacepathregex/replace_path_regex_test.go b/pkg/middlewares/replacepathregex/replace_path_regex_test.go index a1be75fcd..065b53d80 100644 --- a/pkg/middlewares/replacepathregex/replace_path_regex_test.go +++ b/pkg/middlewares/replacepathregex/replace_path_regex_test.go @@ -1,6 +1,7 @@ package replacepathregex import ( + "context" "net/http" "net/http/httptest" "testing" @@ -149,7 +150,7 @@ func TestReplacePathRegex(t *testing.T) { requestURI = r.RequestURI }) - handler, err := New(t.Context(), next, test.config, "foo-replace-path-regexp") + handler, err := New(context.Background(), next, test.config, "foo-replace-path-regexp") if test.expectsError { require.Error(t, err) return diff --git a/pkg/middlewares/requestdecorator/hostresolver_test.go b/pkg/middlewares/requestdecorator/hostresolver_test.go index e00228576..f1c8c39a2 100644 --- a/pkg/middlewares/requestdecorator/hostresolver_test.go +++ b/pkg/middlewares/requestdecorator/hostresolver_test.go @@ -1,6 +1,7 @@ package requestdecorator import ( + "context" "testing" "github.com/stretchr/testify/assert" @@ -42,7 +43,7 @@ func TestCNAMEFlatten(t *testing.T) { ResolvDepth: 5, } - flatH := hostResolver.CNAMEFlatten(t.Context(), test.domain) + flatH := hostResolver.CNAMEFlatten(context.Background(), test.domain) assert.Equal(t, test.expectedDomain, flatH) }) } diff --git a/pkg/middlewares/retry/retry.go b/pkg/middlewares/retry/retry.go index de15a21b4..068030777 100644 --- a/pkg/middlewares/retry/retry.go +++ b/pkg/middlewares/retry/retry.go @@ -14,10 +14,9 @@ import ( "github.com/cenkalti/backoff/v4" "github.com/traefik/traefik/v3/pkg/config/dynamic" "github.com/traefik/traefik/v3/pkg/middlewares" - "github.com/traefik/traefik/v3/pkg/middlewares/observability" - "github.com/traefik/traefik/v3/pkg/observability/tracing" + "github.com/traefik/traefik/v3/pkg/tracing" "go.opentelemetry.io/otel/attribute" - semconv "go.opentelemetry.io/otel/semconv/v1.37.0" + semconv "go.opentelemetry.io/otel/semconv/v1.26.0" "go.opentelemetry.io/otel/trace" ) @@ -125,7 +124,7 @@ func (r *retry) ServeHTTP(rw http.ResponseWriter, req *http.Request) { var currentSpan trace.Span operation := func() error { - if tracer != nil && observability.DetailedTracingEnabled(req.Context()) { + if tracer != nil { if currentSpan != nil { currentSpan.End() } diff --git a/pkg/middlewares/retry/retry_test.go b/pkg/middlewares/retry/retry_test.go index a4575b817..6dca141c6 100644 --- a/pkg/middlewares/retry/retry_test.go +++ b/pkg/middlewares/retry/retry_test.go @@ -1,6 +1,7 @@ package retry import ( + "context" "fmt" "io" "net/http" @@ -128,7 +129,7 @@ func TestRetry(t *testing.T) { }) retryListener := &countingRetryListener{} - retry, err := New(t.Context(), next, test.config, retryListener, "traefikTest") + retry, err := New(context.Background(), next, test.config, retryListener, "traefikTest") require.NoError(t, err) recorder := httptest.NewRecorder() @@ -148,7 +149,7 @@ func TestRetryEmptyServerList(t *testing.T) { }) retryListener := &countingRetryListener{} - retry, err := New(t.Context(), next, dynamic.Retry{Attempts: 3}, retryListener, "traefikTest") + retry, err := New(context.Background(), next, dynamic.Retry{Attempts: 3}, retryListener, "traefikTest") require.NoError(t, err) recorder := httptest.NewRecorder() @@ -184,7 +185,7 @@ func TestMultipleRetriesShouldNotLooseHeaders(t *testing.T) { rw.WriteHeader(http.StatusNoContent) }) - retry, err := New(t.Context(), next, dynamic.Retry{Attempts: 3}, &countingRetryListener{}, "traefikTest") + retry, err := New(context.Background(), next, dynamic.Retry{Attempts: 3}, &countingRetryListener{}, "traefikTest") require.NoError(t, err) res := httptest.NewRecorder() @@ -218,7 +219,7 @@ func TestRetryShouldNotLooseHeadersOnWrite(t *testing.T) { require.NoError(t, err) }) - retry, err := New(t.Context(), next, dynamic.Retry{Attempts: 3}, &countingRetryListener{}, "traefikTest") + retry, err := New(context.Background(), next, dynamic.Retry{Attempts: 3}, &countingRetryListener{}, "traefikTest") require.NoError(t, err) res := httptest.NewRecorder() @@ -242,7 +243,7 @@ func TestRetryWithFlush(t *testing.T) { } }) - retry, err := New(t.Context(), next, dynamic.Retry{Attempts: 1}, &countingRetryListener{}, "traefikTest") + retry, err := New(context.Background(), next, dynamic.Retry{Attempts: 1}, &countingRetryListener{}, "traefikTest") require.NoError(t, err) responseRecorder := httptest.NewRecorder() @@ -311,7 +312,7 @@ func TestRetryWebsocket(t *testing.T) { }) retryListener := &countingRetryListener{} - retryH, err := New(t.Context(), next, dynamic.Retry{Attempts: test.maxRequestAttempts}, retryListener, "traefikTest") + retryH, err := New(context.Background(), next, dynamic.Retry{Attempts: test.maxRequestAttempts}, retryListener, "traefikTest") require.NoError(t, err) retryServer := httptest.NewServer(retryH) @@ -344,7 +345,7 @@ func Test1xxResponses(t *testing.T) { }) retryListener := &countingRetryListener{} - retry, err := New(t.Context(), next, dynamic.Retry{Attempts: 1}, retryListener, "traefikTest") + retry, err := New(context.Background(), next, dynamic.Retry{Attempts: 1}, retryListener, "traefikTest") require.NoError(t, err) server := httptest.NewServer(retry) @@ -388,7 +389,7 @@ func Test1xxResponses(t *testing.T) { return nil }, } - req, _ := http.NewRequestWithContext(httptrace.WithClientTrace(t.Context(), trace), http.MethodGet, server.URL, nil) + req, _ := http.NewRequestWithContext(httptrace.WithClientTrace(context.Background(), trace), http.MethodGet, server.URL, nil) res, err := frontendClient.Do(req) assert.NoError(t, err) diff --git a/pkg/middlewares/stripprefix/strip_prefix.go b/pkg/middlewares/stripprefix/strip_prefix.go index 1632814ce..8483f5100 100644 --- a/pkg/middlewares/stripprefix/strip_prefix.go +++ b/pkg/middlewares/stripprefix/strip_prefix.go @@ -7,6 +7,7 @@ import ( "github.com/traefik/traefik/v3/pkg/config/dynamic" "github.com/traefik/traefik/v3/pkg/middlewares" + "go.opentelemetry.io/otel/trace" ) const ( @@ -44,8 +45,8 @@ func New(ctx context.Context, next http.Handler, config dynamic.StripPrefix, nam }, nil } -func (s *stripPrefix) GetTracingInformation() (string, string) { - return s.name, typeName +func (s *stripPrefix) GetTracingInformation() (string, string, trace.SpanKind) { + return s.name, typeName, trace.SpanKindUnspecified } func (s *stripPrefix) ServeHTTP(rw http.ResponseWriter, req *http.Request) { diff --git a/pkg/middlewares/stripprefix/strip_prefix_test.go b/pkg/middlewares/stripprefix/strip_prefix_test.go index 50cee8e28..83310ff8b 100644 --- a/pkg/middlewares/stripprefix/strip_prefix_test.go +++ b/pkg/middlewares/stripprefix/strip_prefix_test.go @@ -1,6 +1,7 @@ package stripprefix import ( + "context" "net/http" "net/http/httptest" "testing" @@ -147,7 +148,7 @@ func TestStripPrefix(t *testing.T) { pointer := func(v bool) *bool { return &v } test.config.ForceSlash = pointer(false) - handler, err := New(t.Context(), next, test.config, "foo-strip-prefix") + handler, err := New(context.Background(), next, test.config, "foo-strip-prefix") require.NoError(t, err) req := testhelpers.MustNewRequest(http.MethodGet, "http://localhost"+test.path, nil) diff --git a/pkg/middlewares/stripprefixregex/strip_prefix_regex.go b/pkg/middlewares/stripprefixregex/strip_prefix_regex.go index a38752659..71de1ad28 100644 --- a/pkg/middlewares/stripprefixregex/strip_prefix_regex.go +++ b/pkg/middlewares/stripprefixregex/strip_prefix_regex.go @@ -9,6 +9,7 @@ import ( "github.com/traefik/traefik/v3/pkg/config/dynamic" "github.com/traefik/traefik/v3/pkg/middlewares" "github.com/traefik/traefik/v3/pkg/middlewares/stripprefix" + "go.opentelemetry.io/otel/trace" ) const ( @@ -42,8 +43,8 @@ func New(ctx context.Context, next http.Handler, config dynamic.StripPrefixRegex return &stripPrefix, nil } -func (s *stripPrefixRegex) GetTracingInformation() (string, string) { - return s.name, typeName +func (s *stripPrefixRegex) GetTracingInformation() (string, string, trace.SpanKind) { + return s.name, typeName, trace.SpanKindInternal } func (s *stripPrefixRegex) ServeHTTP(rw http.ResponseWriter, req *http.Request) { diff --git a/pkg/middlewares/stripprefixregex/strip_prefix_regex_test.go b/pkg/middlewares/stripprefixregex/strip_prefix_regex_test.go index 7b7e3092e..9f2c02548 100644 --- a/pkg/middlewares/stripprefixregex/strip_prefix_regex_test.go +++ b/pkg/middlewares/stripprefixregex/strip_prefix_regex_test.go @@ -1,6 +1,7 @@ package stripprefixregex import ( + "context" "net/http" "net/http/httptest" "testing" @@ -117,7 +118,7 @@ func TestStripPrefixRegex(t *testing.T) { actualHeader = r.Header.Get(stripprefix.ForwardedPrefixHeader) requestURI = r.RequestURI }) - handler, err := New(t.Context(), handlerPath, testPrefixRegex, "foo-strip-prefix-regex") + handler, err := New(context.Background(), handlerPath, testPrefixRegex, "foo-strip-prefix-regex") require.NoError(t, err) req := testhelpers.MustNewRequest(http.MethodGet, "http://localhost"+test.path, nil) diff --git a/pkg/middlewares/tcp/inflightconn/inflight_conn_test.go b/pkg/middlewares/tcp/inflightconn/inflight_conn_test.go index d2f9dc141..28fca80df 100644 --- a/pkg/middlewares/tcp/inflightconn/inflight_conn_test.go +++ b/pkg/middlewares/tcp/inflightconn/inflight_conn_test.go @@ -1,6 +1,7 @@ package inflightconn import ( + "context" "net" "testing" "time" @@ -26,7 +27,7 @@ func TestInFlightConn_ServeTCP(t *testing.T) { finishCh <- struct{}{} }) - middleware, err := New(t.Context(), next, dynamic.TCPInFlightConn{Amount: 1}, "foo") + middleware, err := New(context.Background(), next, dynamic.TCPInFlightConn{Amount: 1}, "foo") require.NoError(t, err) // The first connection should succeed and wait. diff --git a/pkg/middlewares/tcp/ipallowlist/ip_allowlist_test.go b/pkg/middlewares/tcp/ipallowlist/ip_allowlist_test.go index 9e58e6a3a..5c918e8cf 100644 --- a/pkg/middlewares/tcp/ipallowlist/ip_allowlist_test.go +++ b/pkg/middlewares/tcp/ipallowlist/ip_allowlist_test.go @@ -43,7 +43,7 @@ func TestNewIPAllowLister(t *testing.T) { t.Parallel() next := tcp.HandlerFunc(func(conn tcp.WriteCloser) {}) - allowLister, err := New(t.Context(), next, test.allowList, "traefikTest") + allowLister, err := New(context.Background(), next, test.allowList, "traefikTest") if test.expectedError { assert.Error(t, err) @@ -92,7 +92,7 @@ func TestIPAllowLister_ServeHTTP(t *testing.T) { require.NoError(t, err) }) - allowLister, err := New(t.Context(), next, test.allowList, "traefikTest") + allowLister, err := New(context.Background(), next, test.allowList, "traefikTest") require.NoError(t, err) server, client := net.Pipe() diff --git a/pkg/middlewares/tcp/ipwhitelist/ip_whitelist_test.go b/pkg/middlewares/tcp/ipwhitelist/ip_whitelist_test.go index 2fb439714..f0bf631fb 100644 --- a/pkg/middlewares/tcp/ipwhitelist/ip_whitelist_test.go +++ b/pkg/middlewares/tcp/ipwhitelist/ip_whitelist_test.go @@ -43,7 +43,7 @@ func TestNewIPWhiteLister(t *testing.T) { t.Parallel() next := tcp.HandlerFunc(func(conn tcp.WriteCloser) {}) - whiteLister, err := New(t.Context(), next, test.whiteList, "traefikTest") + whiteLister, err := New(context.Background(), next, test.whiteList, "traefikTest") if test.expectedError { assert.Error(t, err) @@ -92,7 +92,7 @@ func TestIPWhiteLister_ServeHTTP(t *testing.T) { require.NoError(t, err) }) - whiteLister, err := New(t.Context(), next, test.whiteList, "traefikTest") + whiteLister, err := New(context.Background(), next, test.whiteList, "traefikTest") require.NoError(t, err) server, client := net.Pipe() diff --git a/pkg/observability/observability.go b/pkg/observability/observability.go deleted file mode 100644 index 4c05f2cbf..000000000 --- a/pkg/observability/observability.go +++ /dev/null @@ -1,15 +0,0 @@ -package observability - -import ( - "fmt" - "os" -) - -func EnsureUserEnvVar() error { - if os.Getenv("USER") == "" { - if err := os.Setenv("USER", "traefik"); err != nil { - return fmt.Errorf("could not set USER environment variable: %w", err) - } - } - return nil -} diff --git a/pkg/observability/types/tracing_test.go b/pkg/observability/types/tracing_test.go deleted file mode 100644 index 77f764871..000000000 --- a/pkg/observability/types/tracing_test.go +++ /dev/null @@ -1,72 +0,0 @@ -package types - -import ( - "testing" - - "github.com/stretchr/testify/require" -) - -func TestTracingVerbosity_Allows(t *testing.T) { - tests := []struct { - desc string - from TracingVerbosity - to TracingVerbosity - allows bool - }{ - { - desc: "minimal vs minimal", - from: MinimalVerbosity, - to: MinimalVerbosity, - allows: true, - }, - { - desc: "minimal vs detailed", - from: MinimalVerbosity, - to: DetailedVerbosity, - allows: false, - }, - { - desc: "detailed vs minimal", - from: DetailedVerbosity, - to: MinimalVerbosity, - allows: true, - }, - { - desc: "detailed vs detailed", - from: DetailedVerbosity, - to: DetailedVerbosity, - allows: true, - }, - { - desc: "unknown vs minimal", - from: TracingVerbosity("unknown"), - to: MinimalVerbosity, - allows: true, - }, - { - desc: "unknown vs detailed", - from: TracingVerbosity("unknown"), - to: DetailedVerbosity, - allows: false, - }, - { - desc: "minimal vs unknown", - from: MinimalVerbosity, - to: TracingVerbosity("unknown"), - allows: false, - }, - { - desc: "detailed vs unknown", - from: DetailedVerbosity, - to: TracingVerbosity("unknown"), - allows: false, - }, - } - - for _, test := range tests { - t.Run(test.desc, func(t *testing.T) { - t.Parallel() - require.Equal(t, test.allows, test.from.Allows(test.to)) - }) - } -} diff --git a/pkg/plugins/builder.go b/pkg/plugins/builder.go index 96a4bf21e..9e47d134b 100644 --- a/pkg/plugins/builder.go +++ b/pkg/plugins/builder.go @@ -28,7 +28,7 @@ type Builder struct { } // NewBuilder creates a new Builder. -func NewBuilder(manager *Manager, plugins map[string]Descriptor, localPlugins map[string]LocalDescriptor) (*Builder, error) { +func NewBuilder(client *Client, plugins map[string]Descriptor, localPlugins map[string]LocalDescriptor) (*Builder, error) { ctx := context.Background() pb := &Builder{ @@ -37,9 +37,9 @@ func NewBuilder(manager *Manager, plugins map[string]Descriptor, localPlugins ma } for pName, desc := range plugins { - manifest, err := manager.ReadManifest(desc.ModuleName) + manifest, err := client.ReadManifest(desc.ModuleName) if err != nil { - _ = manager.ResetAll() + _ = client.ResetAll() return nil, fmt.Errorf("%s: failed to read manifest: %w", desc.ModuleName, err) } @@ -52,7 +52,7 @@ func NewBuilder(manager *Manager, plugins map[string]Descriptor, localPlugins ma switch manifest.Type { case typeMiddleware: - middleware, err := newMiddlewareBuilder(logCtx, manager.GoPath(), manifest, desc.ModuleName, desc.Settings) + middleware, err := newMiddlewareBuilder(logCtx, client.GoPath(), manifest, desc.ModuleName, desc.Settings) if err != nil { return nil, err } @@ -60,7 +60,7 @@ func NewBuilder(manager *Manager, plugins map[string]Descriptor, localPlugins ma pb.middlewareBuilders[pName] = middleware case typeProvider: - pBuilder, err := newProviderBuilder(logCtx, manifest, manager.GoPath(), desc.Settings) + pBuilder, err := newProviderBuilder(logCtx, manifest, client.GoPath()) if err != nil { return nil, fmt.Errorf("%s: %w", desc.ModuleName, err) } @@ -95,7 +95,7 @@ func NewBuilder(manager *Manager, plugins map[string]Descriptor, localPlugins ma pb.middlewareBuilders[pName] = middleware case typeProvider: - builder, err := newProviderBuilder(logCtx, manifest, localGoPath, desc.Settings) + builder, err := newProviderBuilder(logCtx, manifest, localGoPath) if err != nil { return nil, fmt.Errorf("%s: %w", desc.ModuleName, err) } @@ -139,7 +139,7 @@ func newMiddlewareBuilder(ctx context.Context, goPath string, manifest *Manifest return newWasmMiddlewareBuilder(goPath, moduleName, wasmPath, settings) case runtimeYaegi, "": - i, err := newInterpreter(ctx, goPath, manifest, settings) + i, err := newInterpreter(ctx, goPath, manifest.Import) if err != nil { return nil, fmt.Errorf("failed to create Yaegi interpreter: %w", err) } @@ -151,10 +151,10 @@ func newMiddlewareBuilder(ctx context.Context, goPath string, manifest *Manifest } } -func newProviderBuilder(ctx context.Context, manifest *Manifest, goPath string, settings Settings) (providerBuilder, error) { +func newProviderBuilder(ctx context.Context, manifest *Manifest, goPath string) (providerBuilder, error) { switch manifest.Runtime { case runtimeYaegi, "": - i, err := newInterpreter(ctx, goPath, manifest, settings) + i, err := newInterpreter(ctx, goPath, manifest.Import) if err != nil { return providerBuilder{}, err } diff --git a/pkg/plugins/client.go b/pkg/plugins/client.go new file mode 100644 index 000000000..ac4e71bcf --- /dev/null +++ b/pkg/plugins/client.go @@ -0,0 +1,434 @@ +package plugins + +import ( + zipa "archive/zip" + "context" + "crypto/sha256" + "encoding/hex" + "encoding/json" + "errors" + "fmt" + "io" + "net/http" + "net/url" + "os" + "path" + "path/filepath" + "strings" + "time" + + "github.com/hashicorp/go-retryablehttp" + "github.com/rs/zerolog/log" + "github.com/traefik/traefik/v3/pkg/logs" + "golang.org/x/mod/module" + "golang.org/x/mod/zip" + "gopkg.in/yaml.v3" +) + +const ( + sourcesFolder = "sources" + archivesFolder = "archives" + stateFilename = "state.json" + goPathSrc = "src" + pluginManifest = ".traefik.yml" +) + +const pluginsURL = "https://plugins.traefik.io/public/" + +const ( + hashHeader = "X-Plugin-Hash" +) + +// ClientOptions the options of a Traefik plugins client. +type ClientOptions struct { + Output string +} + +// Client a Traefik plugins client. +type Client struct { + HTTPClient *http.Client + baseURL *url.URL + + archives string + stateFile string + goPath string + sources string +} + +// NewClient creates a new Traefik plugins client. +func NewClient(opts ClientOptions) (*Client, error) { + baseURL, err := url.Parse(pluginsURL) + if err != nil { + return nil, err + } + + sourcesRootPath := filepath.Join(filepath.FromSlash(opts.Output), sourcesFolder) + err = resetDirectory(sourcesRootPath) + if err != nil { + return nil, err + } + + goPath, err := os.MkdirTemp(sourcesRootPath, "gop-*") + if err != nil { + return nil, fmt.Errorf("failed to create GoPath: %w", err) + } + + archivesPath := filepath.Join(filepath.FromSlash(opts.Output), archivesFolder) + err = os.MkdirAll(archivesPath, 0o755) + if err != nil { + return nil, fmt.Errorf("failed to create archives directory %s: %w", archivesPath, err) + } + + client := retryablehttp.NewClient() + client.Logger = logs.NewRetryableHTTPLogger(log.Logger) + client.HTTPClient = &http.Client{Timeout: 10 * time.Second} + client.RetryMax = 3 + + return &Client{ + HTTPClient: client.StandardClient(), + baseURL: baseURL, + + archives: archivesPath, + stateFile: filepath.Join(archivesPath, stateFilename), + + goPath: goPath, + sources: filepath.Join(goPath, goPathSrc), + }, nil +} + +// GoPath gets the plugins GoPath. +func (c *Client) GoPath() string { + return c.goPath +} + +// ReadManifest reads a plugin manifest. +func (c *Client) ReadManifest(moduleName string) (*Manifest, error) { + return ReadManifest(c.goPath, moduleName) +} + +// ReadManifest reads a plugin manifest. +func ReadManifest(goPath, moduleName string) (*Manifest, error) { + p := filepath.Join(goPath, goPathSrc, filepath.FromSlash(moduleName), pluginManifest) + + file, err := os.Open(p) + if err != nil { + return nil, fmt.Errorf("failed to open the plugin manifest %s: %w", p, err) + } + + defer func() { _ = file.Close() }() + + m := &Manifest{} + err = yaml.NewDecoder(file).Decode(m) + if err != nil { + return nil, fmt.Errorf("failed to decode the plugin manifest %s: %w", p, err) + } + + return m, nil +} + +// Download downloads a plugin archive. +func (c *Client) Download(ctx context.Context, pName, pVersion string) (string, error) { + filename := c.buildArchivePath(pName, pVersion) + + var hash string + _, err := os.Stat(filename) + if err != nil && !os.IsNotExist(err) { + return "", fmt.Errorf("failed to read archive %s: %w", filename, err) + } + + if err == nil { + hash, err = computeHash(filename) + if err != nil { + return "", fmt.Errorf("failed to compute hash: %w", err) + } + } + + endpoint, err := c.baseURL.Parse(path.Join(c.baseURL.Path, "download", pName, pVersion)) + if err != nil { + return "", fmt.Errorf("failed to parse endpoint URL: %w", err) + } + + req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint.String(), nil) + if err != nil { + return "", fmt.Errorf("failed to create request: %w", err) + } + + if hash != "" { + req.Header.Set(hashHeader, hash) + } + + resp, err := c.HTTPClient.Do(req) + if err != nil { + return "", fmt.Errorf("failed to call service: %w", err) + } + + defer func() { _ = resp.Body.Close() }() + + switch resp.StatusCode { + case http.StatusNotModified: + // noop + return hash, nil + + case http.StatusOK: + err = os.MkdirAll(filepath.Dir(filename), 0o755) + if err != nil { + return "", fmt.Errorf("failed to create directory: %w", err) + } + + var file *os.File + file, err = os.Create(filename) + if err != nil { + return "", fmt.Errorf("failed to create file %q: %w", filename, err) + } + + defer func() { _ = file.Close() }() + + _, err = io.Copy(file, resp.Body) + if err != nil { + return "", fmt.Errorf("failed to write response: %w", err) + } + + hash, err = computeHash(filename) + if err != nil { + return "", fmt.Errorf("failed to compute hash: %w", err) + } + + return hash, nil + + default: + data, _ := io.ReadAll(resp.Body) + return "", fmt.Errorf("error: %d: %s", resp.StatusCode, string(data)) + } +} + +// Check checks the plugin archive integrity. +func (c *Client) Check(ctx context.Context, pName, pVersion, hash string) error { + endpoint, err := c.baseURL.Parse(path.Join(c.baseURL.Path, "validate", pName, pVersion)) + if err != nil { + return fmt.Errorf("failed to parse endpoint URL: %w", err) + } + + req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint.String(), nil) + if err != nil { + return fmt.Errorf("failed to create request: %w", err) + } + + if hash != "" { + req.Header.Set(hashHeader, hash) + } + + resp, err := c.HTTPClient.Do(req) + if err != nil { + return fmt.Errorf("failed to call service: %w", err) + } + + defer func() { _ = resp.Body.Close() }() + + if resp.StatusCode == http.StatusOK { + return nil + } + + return errors.New("plugin integrity check failed") +} + +// Unzip unzip a plugin archive. +func (c *Client) Unzip(pName, pVersion string) error { + err := c.unzipModule(pName, pVersion) + if err == nil { + return nil + } + + return c.unzipArchive(pName, pVersion) +} + +func (c *Client) unzipModule(pName, pVersion string) error { + src := c.buildArchivePath(pName, pVersion) + dest := filepath.Join(c.sources, filepath.FromSlash(pName)) + + return zip.Unzip(dest, module.Version{Path: pName, Version: pVersion}, src) +} + +func (c *Client) unzipArchive(pName, pVersion string) error { + zipPath := c.buildArchivePath(pName, pVersion) + + archive, err := zipa.OpenReader(zipPath) + if err != nil { + return err + } + + defer func() { _ = archive.Close() }() + + dest := filepath.Join(c.sources, filepath.FromSlash(pName)) + + for _, f := range archive.File { + err = unzipFile(f, dest) + if err != nil { + return fmt.Errorf("unable to unzip %s: %w", f.Name, err) + } + } + + return nil +} + +func unzipFile(f *zipa.File, dest string) error { + rc, err := f.Open() + if err != nil { + return err + } + + defer func() { _ = rc.Close() }() + + pathParts := strings.SplitN(f.Name, "/", 2) + + var pp string + if len(pathParts) < 2 { + pp = pathParts[0] + } else { + pp = pathParts[1] + } + + p := filepath.Join(dest, pp) + + if f.FileInfo().IsDir() { + err = os.MkdirAll(p, f.Mode()) + if err != nil { + return fmt.Errorf("unable to create archive directory %s: %w", p, err) + } + + return nil + } + + err = os.MkdirAll(filepath.Dir(p), 0o750) + if err != nil { + return fmt.Errorf("unable to create archive directory %s for file %s: %w", filepath.Dir(p), p, err) + } + + elt, err := os.OpenFile(p, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode()) + if err != nil { + return err + } + + defer func() { _ = elt.Close() }() + + _, err = io.Copy(elt, rc) + if err != nil { + return err + } + + return nil +} + +// CleanArchives cleans plugins archives. +func (c *Client) CleanArchives(plugins map[string]Descriptor) error { + if _, err := os.Stat(c.stateFile); os.IsNotExist(err) { + return nil + } + + stateFile, err := os.Open(c.stateFile) + if err != nil { + return fmt.Errorf("failed to open state file %s: %w", c.stateFile, err) + } + + previous := make(map[string]string) + err = json.NewDecoder(stateFile).Decode(&previous) + if err != nil { + return fmt.Errorf("failed to decode state file %s: %w", c.stateFile, err) + } + + for pName, pVersion := range previous { + for _, desc := range plugins { + if desc.ModuleName == pName && desc.Version != pVersion { + archivePath := c.buildArchivePath(pName, pVersion) + if err = os.RemoveAll(archivePath); err != nil { + return fmt.Errorf("failed to remove archive %s: %w", archivePath, err) + } + } + } + } + + return nil +} + +// WriteState writes the plugins state files. +func (c *Client) WriteState(plugins map[string]Descriptor) error { + m := make(map[string]string) + + for _, descriptor := range plugins { + m[descriptor.ModuleName] = descriptor.Version + } + + mp, err := json.MarshalIndent(m, "", " ") + if err != nil { + return fmt.Errorf("unable to marshal plugin state: %w", err) + } + + return os.WriteFile(c.stateFile, mp, 0o600) +} + +// ResetAll resets all plugins related directories. +func (c *Client) ResetAll() error { + if c.goPath == "" { + return errors.New("goPath is empty") + } + + err := resetDirectory(filepath.Join(c.goPath, "..")) + if err != nil { + return fmt.Errorf("unable to reset plugins GoPath directory %s: %w", c.goPath, err) + } + + err = resetDirectory(c.archives) + if err != nil { + return fmt.Errorf("unable to reset plugins archives directory: %w", err) + } + + return nil +} + +func (c *Client) buildArchivePath(pName, pVersion string) string { + return filepath.Join(c.archives, filepath.FromSlash(pName), pVersion+".zip") +} + +func resetDirectory(dir string) error { + dirPath, err := filepath.Abs(dir) + if err != nil { + return fmt.Errorf("unable to get absolute path of %s: %w", dir, err) + } + + currentPath, err := os.Getwd() + if err != nil { + return fmt.Errorf("unable to get the current directory: %w", err) + } + + if strings.HasPrefix(currentPath, dirPath) { + return fmt.Errorf("cannot be deleted: the directory path %s is the parent of the current path %s", dirPath, currentPath) + } + + err = os.RemoveAll(dir) + if err != nil { + return fmt.Errorf("unable to remove directory %s: %w", dirPath, err) + } + + err = os.MkdirAll(dir, 0o755) + if err != nil { + return fmt.Errorf("unable to create directory %s: %w", dirPath, err) + } + + return nil +} + +func computeHash(filepath string) (string, error) { + file, err := os.Open(filepath) + if err != nil { + return "", err + } + + hash := sha256.New() + + if _, err := io.Copy(hash, file); err != nil { + return "", err + } + + sum := hash.Sum(nil) + + return hex.EncodeToString(sum), nil +} diff --git a/pkg/plugins/downloader.go b/pkg/plugins/downloader.go deleted file mode 100644 index 3dce1df4c..000000000 --- a/pkg/plugins/downloader.go +++ /dev/null @@ -1,160 +0,0 @@ -package plugins - -import ( - "context" - "errors" - "fmt" - "io" - "net/http" - "net/url" - "os" - "path" - "path/filepath" -) - -// PluginDownloader defines the interface for downloading and validating plugins from remote sources. -type PluginDownloader interface { - // Download downloads a plugin archive and returns its hash. - Download(ctx context.Context, pName, pVersion string) (string, error) - // Check checks the plugin archive integrity against a known hash. - Check(ctx context.Context, pName, pVersion, hash string) error -} - -// RegistryDownloaderOptions holds configuration options for creating a RegistryDownloader. -type RegistryDownloaderOptions struct { - HTTPClient *http.Client - ArchivesPath string -} - -// RegistryDownloader implements PluginDownloader for HTTP-based plugin downloads. -type RegistryDownloader struct { - httpClient *http.Client - baseURL *url.URL - archives string -} - -// NewRegistryDownloader creates a new HTTP-based plugin downloader. -func NewRegistryDownloader(opts RegistryDownloaderOptions) (*RegistryDownloader, error) { - baseURL, err := url.Parse(pluginsURL) - if err != nil { - return nil, err - } - - httpClient := opts.HTTPClient - if httpClient == nil { - httpClient = http.DefaultClient - } - - return &RegistryDownloader{ - httpClient: httpClient, - baseURL: baseURL, - archives: opts.ArchivesPath, - }, nil -} - -// Download downloads a plugin archive. -func (d *RegistryDownloader) Download(ctx context.Context, pName, pVersion string) (string, error) { - filename := d.buildArchivePath(pName, pVersion) - - var hash string - _, err := os.Stat(filename) - if err != nil && !os.IsNotExist(err) { - return "", fmt.Errorf("failed to read archive %s: %w", filename, err) - } - - if err == nil { - hash, err = computeHash(filename) - if err != nil { - return "", fmt.Errorf("failed to compute hash: %w", err) - } - } - - endpoint, err := d.baseURL.Parse(path.Join(d.baseURL.Path, "download", pName, pVersion)) - if err != nil { - return "", fmt.Errorf("failed to parse endpoint URL: %w", err) - } - - req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint.String(), nil) - if err != nil { - return "", fmt.Errorf("failed to create request: %w", err) - } - - if hash != "" { - req.Header.Set(hashHeader, hash) - } - - resp, err := d.httpClient.Do(req) - if err != nil { - return "", fmt.Errorf("failed to call service: %w", err) - } - - defer func() { _ = resp.Body.Close() }() - - switch resp.StatusCode { - case http.StatusNotModified: - return hash, nil - case http.StatusOK: - err = os.MkdirAll(filepath.Dir(filename), 0o755) - if err != nil { - return "", fmt.Errorf("failed to create directory: %w", err) - } - - var file *os.File - file, err = os.Create(filename) - if err != nil { - return "", fmt.Errorf("failed to create file %q: %w", filename, err) - } - - defer func() { _ = file.Close() }() - - _, err = io.Copy(file, resp.Body) - if err != nil { - return "", fmt.Errorf("failed to write response: %w", err) - } - - hash, err = computeHash(filename) - if err != nil { - return "", fmt.Errorf("failed to compute hash: %w", err) - } - default: - data, _ := io.ReadAll(resp.Body) - return "", fmt.Errorf("error: %d: %s", resp.StatusCode, string(data)) - } - - return hash, nil -} - -// Check checks the plugin archive integrity. -func (d *RegistryDownloader) Check(ctx context.Context, pName, pVersion, hash string) error { - endpoint, err := d.baseURL.Parse(path.Join(d.baseURL.Path, "validate", pName, pVersion)) - if err != nil { - return fmt.Errorf("failed to parse endpoint URL: %w", err) - } - - req, err := http.NewRequestWithContext(ctx, http.MethodGet, endpoint.String(), nil) - if err != nil { - return fmt.Errorf("failed to create request: %w", err) - } - - if hash != "" { - req.Header.Set(hashHeader, hash) - } - - resp, err := d.httpClient.Do(req) - if err != nil { - return fmt.Errorf("failed to call service: %w", err) - } - - defer func() { _ = resp.Body.Close() }() - - if resp.StatusCode == http.StatusOK { - return nil - } - - return errors.New("plugin integrity check failed") -} - -// buildArchivePath builds the path to a plugin archive file. -func (d *RegistryDownloader) buildArchivePath(pName, pVersion string) string { - return filepath.Join(d.archives, filepath.FromSlash(pName), pVersion+".zip") -} diff --git a/pkg/plugins/downloader_test.go b/pkg/plugins/downloader_test.go deleted file mode 100644 index bcbd89424..000000000 --- a/pkg/plugins/downloader_test.go +++ /dev/null @@ -1,159 +0,0 @@ -package plugins - -import ( - "archive/zip" - "io" - "net/http" - "net/http/httptest" - "net/url" - "os" - "path/filepath" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestHTTPPluginDownloader_Download(t *testing.T) { - tests := []struct { - name string - serverResponse func(w http.ResponseWriter, r *http.Request) - fileAlreadyExists bool - expectError bool - }{ - { - name: "successful download", - serverResponse: func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/zip") - w.WriteHeader(http.StatusOK) - - require.NoError(t, fillDummyZip(w)) - }, - }, - { - name: "not modified response", - serverResponse: func(w http.ResponseWriter, r *http.Request) { - http.Error(w, "", http.StatusNotModified) - }, - fileAlreadyExists: true, - }, - { - name: "server error", - serverResponse: func(w http.ResponseWriter, r *http.Request) { - http.Error(w, "internal server error", http.StatusInternalServerError) - }, - expectError: true, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - server := httptest.NewServer(http.HandlerFunc(test.serverResponse)) - defer server.Close() - - tempDir := t.TempDir() - archivesPath := filepath.Join(tempDir, "archives") - - if test.fileAlreadyExists { - createDummyZip(t, archivesPath) - } - - baseURL, err := url.Parse(server.URL) - require.NoError(t, err) - - downloader := &RegistryDownloader{ - httpClient: server.Client(), - baseURL: baseURL, - archives: archivesPath, - } - - ctx := t.Context() - hash, err := downloader.Download(ctx, "test/plugin", "v1.0.0") - - if test.expectError { - assert.Error(t, err) - } else { - assert.NoError(t, err) - assert.NotEmpty(t, hash) - - // Check if archive file was created - archivePath := downloader.buildArchivePath("test/plugin", "v1.0.0") - assert.FileExists(t, archivePath) - } - }) - } -} - -func TestHTTPPluginDownloader_Check(t *testing.T) { - tests := []struct { - name string - serverResponse func(w http.ResponseWriter, r *http.Request) - expectError require.ErrorAssertionFunc - }{ - { - name: "successful check", - serverResponse: func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusOK) - }, - expectError: require.NoError, - }, - { - name: "failed check", - serverResponse: func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusBadRequest) - }, - expectError: require.Error, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - server := httptest.NewServer(http.HandlerFunc(test.serverResponse)) - defer server.Close() - - tempDir := t.TempDir() - archivesPath := filepath.Join(tempDir, "archives") - - baseURL, err := url.Parse(server.URL) - require.NoError(t, err) - - downloader := &RegistryDownloader{ - httpClient: server.Client(), - baseURL: baseURL, - archives: archivesPath, - } - - ctx := t.Context() - - err = downloader.Check(ctx, "test/plugin", "v1.0.0", "testhash") - test.expectError(t, err) - }) - } -} - -func createDummyZip(t *testing.T, path string) { - t.Helper() - - err := os.MkdirAll(path+"/test/plugin/", 0o755) - require.NoError(t, err) - - zipfile, err := os.Create(path + "/test/plugin/v1.0.0.zip") - require.NoError(t, err) - defer zipfile.Close() - - err = fillDummyZip(zipfile) - require.NoError(t, err) -} - -func fillDummyZip(w io.Writer) error { - writer := zip.NewWriter(w) - - file, err := writer.Create("test.txt") - if err != nil { - return err - } - - _, _ = file.Write([]byte("test content")) - _ = writer.Close() - return nil -} diff --git a/pkg/plugins/manager.go b/pkg/plugins/manager.go deleted file mode 100644 index 2bbb9cbe7..000000000 --- a/pkg/plugins/manager.go +++ /dev/null @@ -1,356 +0,0 @@ -package plugins - -import ( - zipa "archive/zip" - "context" - "crypto/sha256" - "encoding/hex" - "encoding/json" - "errors" - "fmt" - "io" - "os" - "path/filepath" - "strings" - - "golang.org/x/mod/module" - "golang.org/x/mod/zip" - "gopkg.in/yaml.v3" -) - -const ( - sourcesFolder = "sources" - archivesFolder = "archives" - stateFilename = "state.json" - goPathSrc = "src" - pluginManifest = ".traefik.yml" -) - -const pluginsURL = "https://plugins.traefik.io/public/" - -const ( - hashHeader = "X-Plugin-Hash" -) - -// ManagerOptions the options of a Traefik plugins manager. -type ManagerOptions struct { - Output string -} - -// Manager manages Traefik plugins lifecycle operations including storage, and manifest reading. -type Manager struct { - downloader PluginDownloader - - stateFile string - - archives string - sources string - goPath string -} - -// NewManager creates a new Traefik plugins manager. -func NewManager(downloader PluginDownloader, opts ManagerOptions) (*Manager, error) { - sourcesRootPath := filepath.Join(filepath.FromSlash(opts.Output), sourcesFolder) - err := resetDirectory(sourcesRootPath) - if err != nil { - return nil, err - } - - goPath, err := os.MkdirTemp(sourcesRootPath, "gop-*") - if err != nil { - return nil, fmt.Errorf("failed to create GoPath: %w", err) - } - - archivesPath := filepath.Join(filepath.FromSlash(opts.Output), archivesFolder) - err = os.MkdirAll(archivesPath, 0o755) - if err != nil { - return nil, fmt.Errorf("failed to create archives directory %s: %w", archivesPath, err) - } - - return &Manager{ - downloader: downloader, - stateFile: filepath.Join(archivesPath, stateFilename), - archives: archivesPath, - sources: filepath.Join(goPath, goPathSrc), - goPath: goPath, - }, nil -} - -// InstallPlugin download and unzip the given plugin. -func (m *Manager) InstallPlugin(ctx context.Context, plugin Descriptor) error { - hash, err := m.downloader.Download(ctx, plugin.ModuleName, plugin.Version) - if err != nil { - return fmt.Errorf("unable to download plugin %s: %w", plugin.ModuleName, err) - } - - if plugin.Hash != "" { - if plugin.Hash != hash { - return fmt.Errorf("invalid hash for plugin %s, expected %s, got %s", plugin.ModuleName, plugin.Hash, hash) - } - } else { - err = m.downloader.Check(ctx, plugin.ModuleName, plugin.Version, hash) - if err != nil { - return fmt.Errorf("unable to check archive integrity of the plugin %s: %w", plugin.ModuleName, err) - } - } - - if err = m.unzip(plugin.ModuleName, plugin.Version); err != nil { - return fmt.Errorf("unable to unzip plugin %s: %w", plugin.ModuleName, err) - } - - return nil -} - -// GoPath gets the plugins GoPath. -func (m *Manager) GoPath() string { - return m.goPath -} - -// ReadManifest reads a plugin manifest. -func (m *Manager) ReadManifest(moduleName string) (*Manifest, error) { - return ReadManifest(m.goPath, moduleName) -} - -// ReadManifest reads a plugin manifest. -func ReadManifest(goPath, moduleName string) (*Manifest, error) { - p := filepath.Join(goPath, goPathSrc, filepath.FromSlash(moduleName), pluginManifest) - - file, err := os.Open(p) - if err != nil { - return nil, fmt.Errorf("failed to open the plugin manifest %s: %w", p, err) - } - - defer func() { _ = file.Close() }() - - m := &Manifest{} - err = yaml.NewDecoder(file).Decode(m) - if err != nil { - return nil, fmt.Errorf("failed to decode the plugin manifest %s: %w", p, err) - } - - return m, nil -} - -// CleanArchives cleans plugins archives. -func (m *Manager) CleanArchives(plugins map[string]Descriptor) error { - if _, err := os.Stat(m.stateFile); os.IsNotExist(err) { - return nil - } - - stateFile, err := os.Open(m.stateFile) - if err != nil { - return fmt.Errorf("failed to open state file %s: %w", m.stateFile, err) - } - - previous := make(map[string]string) - err = json.NewDecoder(stateFile).Decode(&previous) - if err != nil { - return fmt.Errorf("failed to decode state file %s: %w", m.stateFile, err) - } - - for pName, pVersion := range previous { - for _, desc := range plugins { - if desc.ModuleName == pName && desc.Version != pVersion { - archivePath := m.buildArchivePath(pName, pVersion) - if err = os.RemoveAll(archivePath); err != nil { - return fmt.Errorf("failed to remove archive %s: %w", archivePath, err) - } - } - } - } - - return nil -} - -// WriteState writes the plugins state files. -func (m *Manager) WriteState(plugins map[string]Descriptor) error { - state := make(map[string]string) - - for _, descriptor := range plugins { - state[descriptor.ModuleName] = descriptor.Version - } - - mp, err := json.MarshalIndent(state, "", " ") - if err != nil { - return fmt.Errorf("unable to marshal plugin state: %w", err) - } - - return os.WriteFile(m.stateFile, mp, 0o600) -} - -// ResetAll resets all plugins related directories. -func (m *Manager) ResetAll() error { - if m.goPath == "" { - return errors.New("goPath is empty") - } - - err := resetDirectory(filepath.Join(m.goPath, "..")) - if err != nil { - return fmt.Errorf("unable to reset plugins GoPath directory %s: %w", m.goPath, err) - } - - err = resetDirectory(m.archives) - if err != nil { - return fmt.Errorf("unable to reset plugins archives directory: %w", err) - } - - return nil -} - -func (m *Manager) unzip(pName, pVersion string) error { - err := m.unzipModule(pName, pVersion) - if err == nil { - return nil - } - - // Unzip as a generic archive if the module unzip fails. - // This is useful for plugins that have vendor directories or other structures. - // This is also useful for wasm plugins. - return m.unzipArchive(pName, pVersion) -} - -func (m *Manager) unzipModule(pName, pVersion string) error { - src := m.buildArchivePath(pName, pVersion) - dest := filepath.Join(m.sources, filepath.FromSlash(pName)) - - return zip.Unzip(dest, module.Version{Path: pName, Version: pVersion}, src) -} - -func (m *Manager) unzipArchive(pName, pVersion string) error { - zipPath := m.buildArchivePath(pName, pVersion) - - archive, err := zipa.OpenReader(zipPath) - if err != nil { - return err - } - - defer func() { _ = archive.Close() }() - - dest := filepath.Join(m.sources, filepath.FromSlash(pName)) - - for _, f := range archive.File { - err = m.unzipFile(f, dest) - if err != nil { - return fmt.Errorf("unable to unzip %s: %w", f.Name, err) - } - } - - return nil -} - -func (m *Manager) unzipFile(f *zipa.File, dest string) error { - rc, err := f.Open() - if err != nil { - return err - } - - defer func() { _ = rc.Close() }() - - // Split to discard the first part of the path when the archive is a Yaegi go plugin with vendoring. - // In this case the path starts with `[organization]-[project]-[release commit sha1]/`. - pathParts := strings.SplitN(f.Name, "/", 2) - var fileName string - if len(pathParts) < 2 { - fileName = pathParts[0] - } else { - fileName = pathParts[1] - } - - // Validate and sanitize the file path. - cleanName := filepath.Clean(fileName) - if strings.Contains(cleanName, "..") { - return fmt.Errorf("invalid file path in archive: %s", f.Name) - } - - filePath := filepath.Join(dest, cleanName) - absFilePath, err := filepath.Abs(filePath) - if err != nil { - return fmt.Errorf("resolving file path: %w", err) - } - - absDest, err := filepath.Abs(dest) - if err != nil { - return fmt.Errorf("resolving destination directory: %w", err) - } - - if !strings.HasPrefix(absFilePath, absDest) { - return fmt.Errorf("file path escapes destination directory: %s", absFilePath) - } - - if f.FileInfo().IsDir() { - err = os.MkdirAll(filePath, f.Mode()) - if err != nil { - return fmt.Errorf("unable to create archive directory %s: %w", filePath, err) - } - - return nil - } - - err = os.MkdirAll(filepath.Dir(filePath), 0o750) - if err != nil { - return fmt.Errorf("unable to create archive directory %s for file %s: %w", filepath.Dir(filePath), filePath, err) - } - - elt, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode()) - if err != nil { - return err - } - - defer func() { _ = elt.Close() }() - - _, err = io.Copy(elt, rc) - if err != nil { - return err - } - - return nil -} - -func (m *Manager) buildArchivePath(pName, pVersion string) string { - return filepath.Join(m.archives, filepath.FromSlash(pName), pVersion+".zip") -} - -func resetDirectory(dir string) error { - dirPath, err := filepath.Abs(dir) - if err != nil { - return fmt.Errorf("unable to get absolute path of %s: %w", dir, err) - } - - currentPath, err := os.Getwd() - if err != nil { - return fmt.Errorf("unable to get the current directory: %w", err) - } - - if strings.HasPrefix(currentPath, dirPath) { - return fmt.Errorf("cannot be deleted: the directory path %s is the parent of the current path %s", dirPath, currentPath) - } - - err = os.RemoveAll(dir) - if err != nil { - return fmt.Errorf("unable to remove directory %s: %w", dirPath, err) - } - - err = os.MkdirAll(dir, 0o755) - if err != nil { - return fmt.Errorf("unable to create directory %s: %w", dirPath, err) - } - - return nil -} - -func computeHash(filepath string) (string, error) { - file, err := os.Open(filepath) - if err != nil { - return "", err - } - - hash := sha256.New() - - if _, err := io.Copy(hash, file); err != nil { - return "", err - } - - sum := hash.Sum(nil) - - return hex.EncodeToString(sum), nil -} diff --git a/pkg/plugins/manager_test.go b/pkg/plugins/manager_test.go deleted file mode 100644 index 5100b2a6b..000000000 --- a/pkg/plugins/manager_test.go +++ /dev/null @@ -1,341 +0,0 @@ -package plugins - -import ( - zipa "archive/zip" - "context" - "encoding/json" - "os" - "path/filepath" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "gopkg.in/yaml.v3" -) - -// mockDownloader is a test implementation of PluginDownloader -type mockDownloader struct { - downloadFunc func(ctx context.Context, pName, pVersion string) (string, error) - checkFunc func(ctx context.Context, pName, pVersion, hash string) error -} - -func (m *mockDownloader) Download(ctx context.Context, pName, pVersion string) (string, error) { - if m.downloadFunc != nil { - return m.downloadFunc(ctx, pName, pVersion) - } - return "mockhash", nil -} - -func (m *mockDownloader) Check(ctx context.Context, pName, pVersion, hash string) error { - if m.checkFunc != nil { - return m.checkFunc(ctx, pName, pVersion, hash) - } - return nil -} - -func TestPluginManager_ReadManifest(t *testing.T) { - tempDir := t.TempDir() - opts := ManagerOptions{Output: tempDir} - - downloader := &mockDownloader{} - manager, err := NewManager(downloader, opts) - require.NoError(t, err) - - moduleName := "github.com/test/plugin" - pluginPath := filepath.Join(manager.goPath, "src", moduleName) - err = os.MkdirAll(pluginPath, 0o755) - require.NoError(t, err) - - manifest := &Manifest{ - DisplayName: "Test Plugin", - Type: "middleware", - Import: "github.com/test/plugin", - Summary: "A test plugin", - TestData: map[string]interface{}{ - "test": "data", - }, - } - - manifestPath := filepath.Join(pluginPath, pluginManifest) - manifestData, err := yaml.Marshal(manifest) - require.NoError(t, err) - err = os.WriteFile(manifestPath, manifestData, 0o644) - require.NoError(t, err) - - readManifest, err := manager.ReadManifest(moduleName) - require.NoError(t, err) - assert.Equal(t, manifest.DisplayName, readManifest.DisplayName) - assert.Equal(t, manifest.Type, readManifest.Type) - assert.Equal(t, manifest.Import, readManifest.Import) - assert.Equal(t, manifest.Summary, readManifest.Summary) -} - -func TestPluginManager_ReadManifest_NotFound(t *testing.T) { - tempDir := t.TempDir() - opts := ManagerOptions{Output: tempDir} - - downloader := &mockDownloader{} - manager, err := NewManager(downloader, opts) - require.NoError(t, err) - - _, err = manager.ReadManifest("nonexistent/plugin") - assert.Error(t, err) -} - -func TestPluginManager_CleanArchives(t *testing.T) { - tempDir := t.TempDir() - opts := ManagerOptions{Output: tempDir} - - downloader := &mockDownloader{} - manager, err := NewManager(downloader, opts) - require.NoError(t, err) - - testPlugin1 := "test/plugin1" - testPlugin2 := "test/plugin2" - - archive1Dir := filepath.Join(manager.archives, "test", "plugin1") - archive2Dir := filepath.Join(manager.archives, "test", "plugin2") - err = os.MkdirAll(archive1Dir, 0o755) - require.NoError(t, err) - err = os.MkdirAll(archive2Dir, 0o755) - require.NoError(t, err) - - archive1Old := filepath.Join(archive1Dir, "v1.0.0.zip") - archive1New := filepath.Join(archive1Dir, "v2.0.0.zip") - archive2 := filepath.Join(archive2Dir, "v1.0.0.zip") - - err = os.WriteFile(archive1Old, []byte("old archive"), 0o644) - require.NoError(t, err) - err = os.WriteFile(archive1New, []byte("new archive"), 0o644) - require.NoError(t, err) - err = os.WriteFile(archive2, []byte("archive 2"), 0o644) - require.NoError(t, err) - - state := map[string]string{ - testPlugin1: "v1.0.0", - testPlugin2: "v1.0.0", - } - stateData, err := json.MarshalIndent(state, "", " ") - require.NoError(t, err) - err = os.WriteFile(manager.stateFile, stateData, 0o600) - require.NoError(t, err) - - currentPlugins := map[string]Descriptor{ - "plugin1": { - ModuleName: testPlugin1, - Version: "v2.0.0", - }, - "plugin2": { - ModuleName: testPlugin2, - Version: "v1.0.0", - }, - } - - err = manager.CleanArchives(currentPlugins) - require.NoError(t, err) - - assert.NoFileExists(t, archive1Old) - assert.FileExists(t, archive1New) - assert.FileExists(t, archive2) -} - -func TestPluginManager_WriteState(t *testing.T) { - tempDir := t.TempDir() - opts := ManagerOptions{Output: tempDir} - - downloader := &mockDownloader{} - manager, err := NewManager(downloader, opts) - require.NoError(t, err) - - plugins := map[string]Descriptor{ - "plugin1": { - ModuleName: "test/plugin1", - Version: "v1.0.0", - }, - "plugin2": { - ModuleName: "test/plugin2", - Version: "v2.0.0", - }, - } - - err = manager.WriteState(plugins) - require.NoError(t, err) - - assert.FileExists(t, manager.stateFile) - - data, err := os.ReadFile(manager.stateFile) - require.NoError(t, err) - - var state map[string]string - err = json.Unmarshal(data, &state) - require.NoError(t, err) - - expectedState := map[string]string{ - "test/plugin1": "v1.0.0", - "test/plugin2": "v2.0.0", - } - assert.Equal(t, expectedState, state) -} - -func TestPluginManager_ResetAll(t *testing.T) { - tempDir := t.TempDir() - opts := ManagerOptions{Output: tempDir} - - downloader := &mockDownloader{} - manager, err := NewManager(downloader, opts) - require.NoError(t, err) - - testFile := filepath.Join(manager.GoPath(), "test.txt") - err = os.WriteFile(testFile, []byte("test"), 0o644) - require.NoError(t, err) - - archiveFile := filepath.Join(manager.archives, "test.zip") - err = os.WriteFile(archiveFile, []byte("archive"), 0o644) - require.NoError(t, err) - - err = manager.ResetAll() - require.NoError(t, err) - - assert.DirExists(t, manager.archives) - assert.NoFileExists(t, testFile) - assert.NoFileExists(t, archiveFile) -} - -func TestPluginManager_InstallPlugin(t *testing.T) { - tests := []struct { - name string - plugin Descriptor - downloadFunc func(ctx context.Context, pName, pVersion string) (string, error) - checkFunc func(ctx context.Context, pName, pVersion, hash string) error - setupArchive func(t *testing.T, archivePath string) - expectError bool - errorMsg string - }{ - { - name: "successful installation", - plugin: Descriptor{ - ModuleName: "github.com/test/plugin", - Version: "v1.0.0", - Hash: "expected-hash", - }, - downloadFunc: func(ctx context.Context, pName, pVersion string) (string, error) { - return "expected-hash", nil - }, - checkFunc: func(ctx context.Context, pName, pVersion, hash string) error { - return nil - }, - setupArchive: func(t *testing.T, archivePath string) { - t.Helper() - - // Create a valid zip archive - err := os.MkdirAll(filepath.Dir(archivePath), 0o755) - require.NoError(t, err) - - file, err := os.Create(archivePath) - require.NoError(t, err) - defer file.Close() - - // Write a minimal zip file with a test file - writer := zipa.NewWriter(file) - defer writer.Close() - - fileWriter, err := writer.Create("test-module-v1.0.0/main.go") - require.NoError(t, err) - _, err = fileWriter.Write([]byte("package main\n\nfunc main() {}\n")) - require.NoError(t, err) - }, - expectError: false, - }, - { - name: "download error", - plugin: Descriptor{ - ModuleName: "github.com/test/plugin", - Version: "v1.0.0", - }, - downloadFunc: func(ctx context.Context, pName, pVersion string) (string, error) { - return "", assert.AnError - }, - expectError: true, - errorMsg: "unable to download plugin", - }, - { - name: "check error", - plugin: Descriptor{ - ModuleName: "github.com/test/plugin", - Version: "v1.0.0", - Hash: "expected-hash", - }, - downloadFunc: func(ctx context.Context, pName, pVersion string) (string, error) { - return "actual-hash", nil - }, - checkFunc: func(ctx context.Context, pName, pVersion, hash string) error { - return assert.AnError - }, - expectError: true, - errorMsg: "invalid hash for plugin", - }, - { - name: "unzip error - invalid archive", - plugin: Descriptor{ - ModuleName: "github.com/test/plugin", - Version: "v1.0.0", - }, - downloadFunc: func(ctx context.Context, pName, pVersion string) (string, error) { - return "test-hash", nil - }, - checkFunc: func(ctx context.Context, pName, pVersion, hash string) error { - return nil - }, - setupArchive: func(t *testing.T, archivePath string) { - t.Helper() - - // Create an invalid zip archive - err := os.MkdirAll(filepath.Dir(archivePath), 0o755) - require.NoError(t, err) - err = os.WriteFile(archivePath, []byte("invalid zip content"), 0o644) - require.NoError(t, err) - }, - expectError: true, - errorMsg: "unable to unzip plugin", - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - tempDir := t.TempDir() - opts := ManagerOptions{Output: tempDir} - - downloader := &mockDownloader{ - downloadFunc: test.downloadFunc, - checkFunc: test.checkFunc, - } - - manager, err := NewManager(downloader, opts) - require.NoError(t, err) - - // Setup archive if needed - if test.setupArchive != nil { - archivePath := filepath.Join(manager.archives, - filepath.FromSlash(test.plugin.ModuleName), - test.plugin.Version+".zip") - test.setupArchive(t, archivePath) - } - - ctx := t.Context() - err = manager.InstallPlugin(ctx, test.plugin) - - if test.expectError { - assert.Error(t, err) - if test.errorMsg != "" { - assert.Contains(t, err.Error(), test.errorMsg) - } - } else { - assert.NoError(t, err) - - // Verify that plugin sources were extracted - sourcePath := filepath.Join(manager.sources, filepath.FromSlash(test.plugin.ModuleName)) - assert.DirExists(t, sourcePath) - } - }) - } -} diff --git a/pkg/plugins/middlewarewasm.go b/pkg/plugins/middlewarewasm.go index 48d179426..c33858a9d 100644 --- a/pkg/plugins/middlewarewasm.go +++ b/pkg/plugins/middlewarewasm.go @@ -14,8 +14,8 @@ import ( "github.com/http-wasm/http-wasm-host-go/handler" wasm "github.com/http-wasm/http-wasm-host-go/handler/nethttp" "github.com/tetratelabs/wazero" + "github.com/traefik/traefik/v3/pkg/logs" "github.com/traefik/traefik/v3/pkg/middlewares" - "github.com/traefik/traefik/v3/pkg/observability/logs" ) type wasmMiddlewareBuilder struct { diff --git a/pkg/plugins/middlewarewasm_test.go b/pkg/plugins/middlewarewasm_test.go index faa33942f..38fee4bbe 100644 --- a/pkg/plugins/middlewarewasm_test.go +++ b/pkg/plugins/middlewarewasm_test.go @@ -1,6 +1,7 @@ package plugins import ( + "context" "net/http" "net/http/httptest" "os" @@ -20,7 +21,7 @@ func TestSettingsWithoutSocket(t *testing.T) { zerolog.SetGlobalLevel(zerolog.DebugLevel) - ctx := log.Logger.WithContext(t.Context()) + ctx := log.Logger.WithContext(context.Background()) t.Setenv("PLUGIN_TEST", "MY-TEST") t.Setenv("PLUGIN_TEST_B", "MY-TEST_B") diff --git a/pkg/plugins/middlewareyaegi.go b/pkg/plugins/middlewareyaegi.go index 590938044..452f8713b 100644 --- a/pkg/plugins/middlewareyaegi.go +++ b/pkg/plugins/middlewareyaegi.go @@ -2,7 +2,6 @@ package plugins import ( "context" - "errors" "fmt" "net/http" "os" @@ -13,10 +12,9 @@ import ( "github.com/mitchellh/mapstructure" "github.com/rs/zerolog" "github.com/rs/zerolog/log" - "github.com/traefik/traefik/v3/pkg/observability/logs" + "github.com/traefik/traefik/v3/pkg/logs" "github.com/traefik/yaegi/interp" "github.com/traefik/yaegi/stdlib" - "github.com/traefik/yaegi/stdlib/unsafe" ) type yaegiMiddlewareBuilder struct { @@ -121,7 +119,7 @@ func (m *YaegiMiddleware) NewHandler(ctx context.Context, next http.Handler) (ht return m.builder.newHandler(ctx, next, m.config, m.middlewareName) } -func newInterpreter(ctx context.Context, goPath string, manifest *Manifest, settings Settings) (*interp.Interpreter, error) { +func newInterpreter(ctx context.Context, goPath string, manifestImport string) (*interp.Interpreter, error) { i := interp.New(interp.Options{ GoPath: goPath, Env: os.Environ(), @@ -134,25 +132,14 @@ func newInterpreter(ctx context.Context, goPath string, manifest *Manifest, sett return nil, fmt.Errorf("failed to load symbols: %w", err) } - if manifest.UseUnsafe && !settings.UseUnsafe { - return nil, errors.New("this plugin uses unsafe import. If you want to use it, you need to allow useUnsafe in the settings") - } - - if settings.UseUnsafe && manifest.UseUnsafe { - err := i.Use(unsafe.Symbols) - if err != nil { - return nil, fmt.Errorf("failed to load unsafe symbols: %w", err) - } - } - err = i.Use(ppSymbols()) if err != nil { return nil, fmt.Errorf("failed to load provider symbols: %w", err) } - _, err = i.Eval(fmt.Sprintf(`import "%s"`, manifest.Import)) + _, err = i.Eval(fmt.Sprintf(`import "%s"`, manifestImport)) if err != nil { - return nil, fmt.Errorf("failed to import plugin code %q: %w", manifest.Import, err) + return nil, fmt.Errorf("failed to import plugin code %q: %w", manifestImport, err) } return i, nil diff --git a/pkg/plugins/plugins.go b/pkg/plugins/plugins.go index f7d543154..367b6c46c 100644 --- a/pkg/plugins/plugins.go +++ b/pkg/plugins/plugins.go @@ -13,13 +13,13 @@ import ( const localGoPath = "./plugins-local/" // SetupRemotePlugins setup remote plugins environment. -func SetupRemotePlugins(manager *Manager, plugins map[string]Descriptor) error { +func SetupRemotePlugins(client *Client, plugins map[string]Descriptor) error { err := checkRemotePluginsConfiguration(plugins) if err != nil { return fmt.Errorf("invalid configuration: %w", err) } - err = manager.CleanArchives(plugins) + err = client.CleanArchives(plugins) if err != nil { return fmt.Errorf("unable to clean archives: %w", err) } @@ -27,20 +27,35 @@ func SetupRemotePlugins(manager *Manager, plugins map[string]Descriptor) error { ctx := context.Background() for pAlias, desc := range plugins { - log.Ctx(ctx).Debug().Msgf("Installing plugin: %s: %s@%s", pAlias, desc.ModuleName, desc.Version) + log.Ctx(ctx).Debug().Msgf("Loading of plugin: %s: %s@%s", pAlias, desc.ModuleName, desc.Version) - if err = manager.InstallPlugin(ctx, desc); err != nil { - _ = manager.ResetAll() - return fmt.Errorf("unable to install plugin %s: %w", pAlias, err) + hash, err := client.Download(ctx, desc.ModuleName, desc.Version) + if err != nil { + _ = client.ResetAll() + return fmt.Errorf("unable to download plugin %s: %w", desc.ModuleName, err) + } + + err = client.Check(ctx, desc.ModuleName, desc.Version, hash) + if err != nil { + _ = client.ResetAll() + return fmt.Errorf("unable to check archive integrity of the plugin %s: %w", desc.ModuleName, err) } } - err = manager.WriteState(plugins) + err = client.WriteState(plugins) if err != nil { - _ = manager.ResetAll() + _ = client.ResetAll() return fmt.Errorf("unable to write plugins state: %w", err) } + for _, desc := range plugins { + err = client.Unzip(desc.ModuleName, desc.Version) + if err != nil { + _ = client.ResetAll() + return fmt.Errorf("unable to unzip archive: %w", err) + } + } + return nil } diff --git a/pkg/plugins/providers.go b/pkg/plugins/providers.go index 46c6df8cc..5fdaf03ac 100644 --- a/pkg/plugins/providers.go +++ b/pkg/plugins/providers.go @@ -11,7 +11,7 @@ import ( "github.com/mitchellh/mapstructure" "github.com/rs/zerolog/log" "github.com/traefik/traefik/v3/pkg/config/dynamic" - "github.com/traefik/traefik/v3/pkg/observability/logs" + "github.com/traefik/traefik/v3/pkg/logs" "github.com/traefik/traefik/v3/pkg/provider" "github.com/traefik/traefik/v3/pkg/safe" "github.com/traefik/yaegi/interp" diff --git a/pkg/plugins/types.go b/pkg/plugins/types.go index 75bb589b3..23254f7f7 100644 --- a/pkg/plugins/types.go +++ b/pkg/plugins/types.go @@ -11,9 +11,8 @@ const ( ) type Settings struct { - Envs []string `description:"Environment variables to forward to the wasm guest." json:"envs,omitempty" toml:"envs,omitempty" yaml:"envs,omitempty"` - Mounts []string `description:"Directory to mount to the wasm guest." json:"mounts,omitempty" toml:"mounts,omitempty" yaml:"mounts,omitempty"` - UseUnsafe bool `description:"Allow the plugin to use unsafe package." json:"useUnsafe,omitempty" toml:"useUnsafe,omitempty" yaml:"useUnsafe,omitempty"` + Envs []string `description:"Environment variables to forward to the wasm guest." json:"envs,omitempty" toml:"envs,omitempty" yaml:"envs,omitempty"` + Mounts []string `description:"Directory to mount to the wasm guest." json:"mounts,omitempty" toml:"mounts,omitempty" yaml:"mounts,omitempty"` } // Descriptor The static part of a plugin configuration. @@ -24,9 +23,6 @@ type Descriptor struct { // Version (required) Version string `description:"plugin's version." json:"version,omitempty" toml:"version,omitempty" yaml:"version,omitempty" export:"true"` - // Hash (optional) - Hash string `description:"plugin's hash to validate'" json:"hash,omitempty" toml:"hash,omitempty" yaml:"hash,omitempty" export:"true"` - // Settings (optional) Settings Settings `description:"Plugin's settings (works only for wasm plugins)." json:"settings,omitempty" toml:"settings,omitempty" yaml:"settings,omitempty" export:"true"` } @@ -50,7 +46,6 @@ type Manifest struct { BasePkg string `yaml:"basePkg"` Compatibility string `yaml:"compatibility"` Summary string `yaml:"summary"` - UseUnsafe bool `yaml:"useUnsafe"` TestData map[string]interface{} `yaml:"testData"` } diff --git a/pkg/provider/acme/account.go b/pkg/provider/acme/account.go index 434f58e3a..c0a7458b3 100644 --- a/pkg/provider/acme/account.go +++ b/pkg/provider/acme/account.go @@ -10,7 +10,7 @@ import ( "github.com/go-acme/lego/v4/certcrypto" "github.com/go-acme/lego/v4/registration" "github.com/rs/zerolog/log" - "github.com/traefik/traefik/v3/pkg/observability/logs" + "github.com/traefik/traefik/v3/pkg/logs" ) // Account is used to store lets encrypt registration info. diff --git a/pkg/provider/acme/challenge_http.go b/pkg/provider/acme/challenge_http.go index 81830afaf..37dfd0bc8 100644 --- a/pkg/provider/acme/challenge_http.go +++ b/pkg/provider/acme/challenge_http.go @@ -13,7 +13,7 @@ import ( "github.com/go-acme/lego/v4/challenge/http01" "github.com/rs/zerolog/log" - "github.com/traefik/traefik/v3/pkg/observability/logs" + "github.com/traefik/traefik/v3/pkg/logs" ) // ChallengeHTTP HTTP challenge provider implements challenge.Provider. diff --git a/pkg/provider/acme/challenge_tls.go b/pkg/provider/acme/challenge_tls.go index 9201ba8c4..995f23d18 100644 --- a/pkg/provider/acme/challenge_tls.go +++ b/pkg/provider/acme/challenge_tls.go @@ -9,7 +9,7 @@ import ( "github.com/go-acme/lego/v4/challenge/tlsalpn01" "github.com/rs/zerolog/log" "github.com/traefik/traefik/v3/pkg/config/dynamic" - "github.com/traefik/traefik/v3/pkg/observability/logs" + "github.com/traefik/traefik/v3/pkg/logs" "github.com/traefik/traefik/v3/pkg/safe" traefiktls "github.com/traefik/traefik/v3/pkg/tls" "github.com/traefik/traefik/v3/pkg/types" diff --git a/pkg/provider/acme/local_store.go b/pkg/provider/acme/local_store.go index dab258822..6e50fb153 100644 --- a/pkg/provider/acme/local_store.go +++ b/pkg/provider/acme/local_store.go @@ -8,7 +8,7 @@ import ( "sync" "github.com/rs/zerolog/log" - "github.com/traefik/traefik/v3/pkg/observability/logs" + "github.com/traefik/traefik/v3/pkg/logs" "github.com/traefik/traefik/v3/pkg/safe" ) diff --git a/pkg/provider/acme/local_store_test.go b/pkg/provider/acme/local_store_test.go index 15d592b9a..a3c05fc58 100644 --- a/pkg/provider/acme/local_store_test.go +++ b/pkg/provider/acme/local_store_test.go @@ -1,6 +1,7 @@ package acme import ( + "context" "fmt" "os" "path/filepath" @@ -46,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, safe.NewPool(t.Context())) + s := NewLocalStore(test.filename, safe.NewPool(context.Background())) account, err := s.GetAccount("test") require.NoError(t, err) @@ -59,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, safe.NewPool(t.Context())) + s := NewLocalStore(acmeFile, safe.NewPool(context.Background())) email := "some@email.com" diff --git a/pkg/provider/acme/provider.go b/pkg/provider/acme/provider.go index c818ac7d2..9120ead27 100644 --- a/pkg/provider/acme/provider.go +++ b/pkg/provider/acme/provider.go @@ -11,7 +11,6 @@ import ( "net/url" "os" "reflect" - "slices" "sort" "strconv" "strings" @@ -21,16 +20,15 @@ import ( "github.com/go-acme/lego/v4/certificate" "github.com/go-acme/lego/v4/challenge" "github.com/go-acme/lego/v4/challenge/dns01" - "github.com/go-acme/lego/v4/challenge/http01" "github.com/go-acme/lego/v4/lego" "github.com/go-acme/lego/v4/providers/dns" "github.com/go-acme/lego/v4/registration" "github.com/rs/zerolog/log" ptypes "github.com/traefik/paerser/types" "github.com/traefik/traefik/v3/pkg/config/dynamic" + "github.com/traefik/traefik/v3/pkg/logs" httpmuxer "github.com/traefik/traefik/v3/pkg/muxer/http" tcpmuxer "github.com/traefik/traefik/v3/pkg/muxer/tcp" - "github.com/traefik/traefik/v3/pkg/observability/logs" "github.com/traefik/traefik/v3/pkg/safe" traefiktls "github.com/traefik/traefik/v3/pkg/tls" "github.com/traefik/traefik/v3/pkg/types" @@ -51,9 +49,6 @@ type Configuration struct { EAB *EAB `description:"External Account Binding to use." json:"eab,omitempty" toml:"eab,omitempty" yaml:"eab,omitempty"` CertificatesDuration int `description:"Certificates' duration in hours." json:"certificatesDuration,omitempty" toml:"certificatesDuration,omitempty" yaml:"certificatesDuration,omitempty" export:"true"` - ClientTimeout ptypes.Duration `description:"Timeout for a complete HTTP transaction with the ACME server." json:"clientTimeout,omitempty" toml:"clientTimeout,omitempty" yaml:"clientTimeout,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` - ClientResponseHeaderTimeout ptypes.Duration `description:"Timeout for receiving the response headers when communicating with the ACME server." json:"clientResponseHeaderTimeout,omitempty" toml:"clientResponseHeaderTimeout,omitempty" yaml:"clientResponseHeaderTimeout,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` - CACertificates []string `description:"Specify the paths to PEM encoded CA Certificates that can be used to authenticate an ACME server with an HTTPS certificate not issued by a CA in the system-wide trusted root list." json:"caCertificates,omitempty" toml:"caCertificates,omitempty" yaml:"caCertificates,omitempty"` CASystemCertPool bool `description:"Define if the certificates pool must use a copy of the system cert pool." json:"caSystemCertPool,omitempty" toml:"caSystemCertPool,omitempty" yaml:"caSystemCertPool,omitempty" export:"true"` CAServerName string `description:"Specify the CA server name that can be used to authenticate an ACME server with an HTTPS certificate not issued by a CA in the system-wide trusted root list." json:"caServerName,omitempty" toml:"caServerName,omitempty" yaml:"caServerName,omitempty" export:"true"` @@ -69,8 +64,6 @@ func (a *Configuration) SetDefaults() { a.Storage = "acme.json" a.KeyType = "RSA4096" a.CertificatesDuration = 3 * 30 * 24 // 90 Days - a.ClientTimeout = ptypes.Duration(2 * time.Minute) - a.ClientResponseHeaderTimeout = ptypes.Duration(30 * time.Second) } // CertAndStore allows mapping a TLS certificate to a TLS store. @@ -113,8 +106,7 @@ type Propagation struct { // HTTPChallenge contains HTTP challenge configuration. type HTTPChallenge struct { - EntryPoint string `description:"HTTP challenge EntryPoint" json:"entryPoint,omitempty" toml:"entryPoint,omitempty" yaml:"entryPoint,omitempty" export:"true"` - Delay ptypes.Duration `description:"Delay between the creation of the challenge and the validation." json:"delay,omitempty" toml:"delay,omitempty" yaml:"delay,omitempty" export:"true"` + EntryPoint string `description:"HTTP challenge EntryPoint" json:"entryPoint,omitempty" toml:"entryPoint,omitempty" yaml:"entryPoint,omitempty" export:"true"` } // TLSChallenge contains TLS challenge configuration. @@ -170,10 +162,6 @@ func (p *Provider) Init() error { return errors.New("cannot manage certificates with duration lower than 1 hour") } - if p.ClientTimeout < p.ClientResponseHeaderTimeout { - return errors.New("clientTimeout must be at least clientResponseHeaderTimeout") - } - var err error p.account, err = p.Store.GetAccount(p.ResolverName) if err != nil { @@ -363,7 +351,7 @@ func (p *Provider) getClient() (*lego.Client, error) { if p.HTTPChallenge != nil && len(p.HTTPChallenge.EntryPoint) > 0 { logger.Debug().Msg("Using HTTP Challenge provider.") - err = client.Challenge.SetHTTP01Provider(p.HTTPChallengeProvider, http01.SetDelay(time.Duration(p.HTTPChallenge.Delay))) + err = client.Challenge.SetHTTP01Provider(p.HTTPChallengeProvider) if err != nil { return nil, err } @@ -389,7 +377,7 @@ func (p *Provider) createHTTPClient() (*http.Client, error) { } return &http.Client{ - Timeout: time.Duration(p.ClientTimeout), + Timeout: 2 * time.Minute, Transport: &http.Transport{ Proxy: http.ProxyFromEnvironment, DialContext: (&net.Dialer{ @@ -397,7 +385,7 @@ func (p *Provider) createHTTPClient() (*http.Client, error) { KeepAlive: 30 * time.Second, }).DialContext, TLSHandshakeTimeout: 30 * time.Second, - ResponseHeaderTimeout: time.Duration(p.ClientResponseHeaderTimeout), + ResponseHeaderTimeout: 30 * time.Second, TLSClientConfig: tlsConfig, }, }, nil @@ -652,8 +640,9 @@ func (p *Provider) resolveDefaultCertificate(ctx context.Context, domains []stri p.resolvingDomainsMutex.Lock() - sortedDomains := slices.Clone(domains) - slices.Sort(sortedDomains) + sortedDomains := make([]string, len(domains)) + copy(sortedDomains, domains) + sort.Strings(sortedDomains) domainKey := strings.Join(sortedDomains, ",") diff --git a/pkg/provider/acme/provider_test.go b/pkg/provider/acme/provider_test.go index 477164e3a..020411bd2 100644 --- a/pkg/provider/acme/provider_test.go +++ b/pkg/provider/acme/provider_test.go @@ -1,6 +1,7 @@ package acme import ( + "context" "crypto/tls" "testing" "time" @@ -180,7 +181,7 @@ func TestGetUncheckedCertificates(t *testing.T) { resolvingDomains: test.resolvingDomains, } - domains := acmeProvider.getUncheckedDomains(t.Context(), test.domains, "default") + domains := acmeProvider.getUncheckedDomains(context.Background(), test.domains, "default") assert.Len(t, domains, len(test.expectedDomains), "Unexpected domains.") }) } @@ -244,7 +245,7 @@ func TestProvider_sanitizeDomains(t *testing.T) { acmeProvider := Provider{Configuration: &Configuration{DNSChallenge: test.dnsChallenge}} - domains, err := acmeProvider.sanitizeDomains(t.Context(), test.domains) + domains, err := acmeProvider.sanitizeDomains(context.Background(), test.domains) if len(test.expectedErr) > 0 { assert.EqualError(t, err, test.expectedErr, "Unexpected error.") @@ -423,7 +424,7 @@ func TestDeleteUnnecessaryDomains(t *testing.T) { t.Run(test.desc, func(t *testing.T) { t.Parallel() - domains := deleteUnnecessaryDomains(t.Context(), test.domains) + domains := deleteUnnecessaryDomains(context.Background(), test.domains) assert.Equal(t, test.expectedDomains, domains, "unexpected domain") }) } @@ -496,7 +497,7 @@ func TestIsAccountMatchingCaServer(t *testing.T) { t.Run(test.desc, func(t *testing.T) { t.Parallel() - result := isAccountMatchingCaServer(t.Context(), test.accountURI, test.serverURI) + result := isAccountMatchingCaServer(context.Background(), test.accountURI, test.serverURI) assert.Equal(t, test.expected, result) }) @@ -573,7 +574,7 @@ func TestInitAccount(t *testing.T) { acmeProvider := Provider{account: test.account, Configuration: &Configuration{Email: test.email, KeyType: test.keyType}} - actualAccount, err := acmeProvider.initAccount(t.Context()) + actualAccount, err := acmeProvider.initAccount(context.Background()) assert.NoError(t, err, "Init account in error") assert.Equal(t, test.expectedAccount.Email, actualAccount.Email, "unexpected email account") assert.Equal(t, test.expectedAccount.KeyType, actualAccount.KeyType, "unexpected keyType account") diff --git a/pkg/provider/aggregator/aggregator.go b/pkg/provider/aggregator/aggregator.go index fb2b1a6dc..9f8d21308 100644 --- a/pkg/provider/aggregator/aggregator.go +++ b/pkg/provider/aggregator/aggregator.go @@ -92,10 +92,6 @@ func NewProviderAggregator(conf static.Providers) *ProviderAggregator { p.quietAddProvider(conf.KubernetesIngress) } - if conf.KubernetesIngressNGINX != nil { - p.quietAddProvider(conf.KubernetesIngressNGINX) - } - if conf.KubernetesCRD != nil { p.quietAddProvider(conf.KubernetesCRD) } diff --git a/pkg/provider/aggregator/aggregator_test.go b/pkg/provider/aggregator/aggregator_test.go index 895dc6539..4683a2480 100644 --- a/pkg/provider/aggregator/aggregator_test.go +++ b/pkg/provider/aggregator/aggregator_test.go @@ -1,6 +1,7 @@ package aggregator import ( + "context" "testing" "time" @@ -23,7 +24,7 @@ func TestProviderAggregator_Provide(t *testing.T) { cfgCh := make(chan dynamic.Message) errCh := make(chan error) - pool := safe.NewPool(t.Context()) + pool := safe.NewPool(context.Background()) t.Cleanup(pool.Stop) diff --git a/pkg/provider/configuration.go b/pkg/provider/configuration.go index be18de7ce..70dd14144 100644 --- a/pkg/provider/configuration.go +++ b/pkg/provider/configuration.go @@ -13,7 +13,7 @@ import ( "github.com/Masterminds/sprig/v3" "github.com/rs/zerolog/log" "github.com/traefik/traefik/v3/pkg/config/dynamic" - "github.com/traefik/traefik/v3/pkg/observability/logs" + "github.com/traefik/traefik/v3/pkg/logs" "github.com/traefik/traefik/v3/pkg/tls" ) diff --git a/pkg/provider/consulcatalog/config.go b/pkg/provider/consulcatalog/config.go index 34c9a90dd..513b85298 100644 --- a/pkg/provider/consulcatalog/config.go +++ b/pkg/provider/consulcatalog/config.go @@ -13,7 +13,7 @@ import ( "github.com/rs/zerolog/log" "github.com/traefik/traefik/v3/pkg/config/dynamic" "github.com/traefik/traefik/v3/pkg/config/label" - "github.com/traefik/traefik/v3/pkg/observability/logs" + "github.com/traefik/traefik/v3/pkg/logs" "github.com/traefik/traefik/v3/pkg/provider" "github.com/traefik/traefik/v3/pkg/provider/constraints" ) diff --git a/pkg/provider/consulcatalog/config_test.go b/pkg/provider/consulcatalog/config_test.go index 763179d20..227dd4838 100644 --- a/pkg/provider/consulcatalog/config_test.go +++ b/pkg/provider/consulcatalog/config_test.go @@ -1,6 +1,7 @@ package consulcatalog import ( + "context" "fmt" "testing" "time" @@ -322,7 +323,7 @@ func TestDefaultRule(t *testing.T) { require.NoError(t, err) } - configuration := p.buildConfiguration(t.Context(), test.items, nil) + configuration := p.buildConfiguration(context.Background(), test.items, nil) assert.Equal(t, test.expected, configuration) }) @@ -3601,7 +3602,7 @@ func Test_buildConfiguration(t *testing.T) { test.items[i].Tags = tags } - configuration := p.buildConfiguration(t.Context(), test.items, &connectCert{ + configuration := p.buildConfiguration(context.Background(), test.items, &connectCert{ root: []string{"root"}, leaf: keyPair{ cert: "cert", @@ -4119,7 +4120,7 @@ func TestFilterHealthStatuses(t *testing.T) { require.NoError(t, err) } - configuration := p.buildConfiguration(t.Context(), test.items, nil) + configuration := p.buildConfiguration(context.Background(), test.items, nil) assert.Equal(t, test.expected, configuration) }) diff --git a/pkg/provider/consulcatalog/consul_catalog.go b/pkg/provider/consulcatalog/consul_catalog.go index abdc8648e..55c24edb6 100644 --- a/pkg/provider/consulcatalog/consul_catalog.go +++ b/pkg/provider/consulcatalog/consul_catalog.go @@ -17,7 +17,7 @@ import ( ptypes "github.com/traefik/paerser/types" "github.com/traefik/traefik/v3/pkg/config/dynamic" "github.com/traefik/traefik/v3/pkg/job" - "github.com/traefik/traefik/v3/pkg/observability/logs" + "github.com/traefik/traefik/v3/pkg/logs" "github.com/traefik/traefik/v3/pkg/provider" "github.com/traefik/traefik/v3/pkg/provider/constraints" "github.com/traefik/traefik/v3/pkg/safe" diff --git a/pkg/provider/docker/builder_test.go b/pkg/provider/docker/builder_test.go index c0b4f1d7f..1213b7c01 100644 --- a/pkg/provider/docker/builder_test.go +++ b/pkg/provider/docker/builder_test.go @@ -1,21 +1,22 @@ package docker import ( - containertypes "github.com/docker/docker/api/types/container" - networktypes "github.com/docker/docker/api/types/network" - swarmtypes "github.com/docker/docker/api/types/swarm" + dockertypes "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/container" + "github.com/docker/docker/api/types/network" + "github.com/docker/docker/api/types/swarm" "github.com/docker/go-connections/nat" ) -func containerJSON(ops ...func(*containertypes.InspectResponse)) containertypes.InspectResponse { - c := &containertypes.InspectResponse{ - ContainerJSONBase: &containertypes.ContainerJSONBase{ +func containerJSON(ops ...func(*dockertypes.ContainerJSON)) dockertypes.ContainerJSON { + c := &dockertypes.ContainerJSON{ + ContainerJSONBase: &dockertypes.ContainerJSONBase{ Name: "fake", - HostConfig: &containertypes.HostConfig{}, + HostConfig: &container.HostConfig{}, }, - Config: &containertypes.Config{}, - NetworkSettings: &containertypes.NetworkSettings{ - NetworkSettingsBase: containertypes.NetworkSettingsBase{}, + Config: &container.Config{}, + NetworkSettings: &dockertypes.NetworkSettings{ + NetworkSettingsBase: dockertypes.NetworkSettingsBase{}, }, } @@ -26,50 +27,58 @@ func containerJSON(ops ...func(*containertypes.InspectResponse)) containertypes. return *c } -func name(name string) func(*containertypes.InspectResponse) { - return func(c *containertypes.InspectResponse) { +func name(name string) func(*dockertypes.ContainerJSON) { + return func(c *dockertypes.ContainerJSON) { c.ContainerJSONBase.Name = name } } -func networkMode(mode string) func(*containertypes.InspectResponse) { - return func(c *containertypes.InspectResponse) { - c.ContainerJSONBase.HostConfig.NetworkMode = containertypes.NetworkMode(mode) +func networkMode(mode string) func(*dockertypes.ContainerJSON) { + return func(c *dockertypes.ContainerJSON) { + c.ContainerJSONBase.HostConfig.NetworkMode = container.NetworkMode(mode) } } -func ports(portMap nat.PortMap) func(*containertypes.InspectResponse) { - return func(c *containertypes.InspectResponse) { +func nodeIP(ip string) func(*dockertypes.ContainerJSON) { + return func(c *dockertypes.ContainerJSON) { + c.ContainerJSONBase.Node = &dockertypes.ContainerNode{ + IPAddress: ip, + } + } +} + +func ports(portMap nat.PortMap) func(*dockertypes.ContainerJSON) { + return func(c *dockertypes.ContainerJSON) { c.NetworkSettings.NetworkSettingsBase.Ports = portMap } } -func withNetwork(name string, ops ...func(*networktypes.EndpointSettings)) func(*containertypes.InspectResponse) { - return func(c *containertypes.InspectResponse) { +func withNetwork(name string, ops ...func(*network.EndpointSettings)) func(*dockertypes.ContainerJSON) { + return func(c *dockertypes.ContainerJSON) { if c.NetworkSettings.Networks == nil { - c.NetworkSettings.Networks = map[string]*networktypes.EndpointSettings{} + c.NetworkSettings.Networks = map[string]*network.EndpointSettings{} } - c.NetworkSettings.Networks[name] = &networktypes.EndpointSettings{} + c.NetworkSettings.Networks[name] = &network.EndpointSettings{} for _, op := range ops { op(c.NetworkSettings.Networks[name]) } } } -func ipv4(ip string) func(*networktypes.EndpointSettings) { - return func(s *networktypes.EndpointSettings) { +func ipv4(ip string) func(*network.EndpointSettings) { + return func(s *network.EndpointSettings) { s.IPAddress = ip } } -func ipv6(ip string) func(*networktypes.EndpointSettings) { - return func(s *networktypes.EndpointSettings) { +func ipv6(ip string) func(*network.EndpointSettings) { + return func(s *network.EndpointSettings) { s.GlobalIPv6Address = ip } } -func swarmTask(id string, ops ...func(*swarmtypes.Task)) swarmtypes.Task { - task := &swarmtypes.Task{ +func swarmTask(id string, ops ...func(*swarm.Task)) swarm.Task { + task := &swarm.Task{ ID: id, } @@ -80,28 +89,22 @@ func swarmTask(id string, ops ...func(*swarmtypes.Task)) swarmtypes.Task { return *task } -func taskSlot(slot int) func(*swarmtypes.Task) { - return func(task *swarmtypes.Task) { +func taskSlot(slot int) func(*swarm.Task) { + return func(task *swarm.Task) { task.Slot = slot } } -func taskNodeID(id string) func(*swarmtypes.Task) { - return func(task *swarmtypes.Task) { - task.NodeID = id - } -} - -func taskNetworkAttachment(id, name, driver string, addresses []string) func(*swarmtypes.Task) { - return func(task *swarmtypes.Task) { - task.NetworksAttachments = append(task.NetworksAttachments, swarmtypes.NetworkAttachment{ - Network: swarmtypes.Network{ +func taskNetworkAttachment(id, name, driver string, addresses []string) func(*swarm.Task) { + return func(task *swarm.Task) { + task.NetworksAttachments = append(task.NetworksAttachments, swarm.NetworkAttachment{ + Network: swarm.Network{ ID: id, - Spec: swarmtypes.NetworkSpec{ - Annotations: swarmtypes.Annotations{ + Spec: swarm.NetworkSpec{ + Annotations: swarm.Annotations{ Name: name, }, - DriverConfiguration: &swarmtypes.Driver{ + DriverConfiguration: &swarm.Driver{ Name: driver, }, }, @@ -111,9 +114,9 @@ func taskNetworkAttachment(id, name, driver string, addresses []string) func(*sw } } -func taskStatus(ops ...func(*swarmtypes.TaskStatus)) func(*swarmtypes.Task) { - return func(task *swarmtypes.Task) { - status := &swarmtypes.TaskStatus{} +func taskStatus(ops ...func(*swarm.TaskStatus)) func(*swarm.Task) { + return func(task *swarm.Task) { + status := &swarm.TaskStatus{} for _, op := range ops { op(status) @@ -123,25 +126,25 @@ func taskStatus(ops ...func(*swarmtypes.TaskStatus)) func(*swarmtypes.Task) { } } -func taskState(state swarmtypes.TaskState) func(*swarmtypes.TaskStatus) { - return func(status *swarmtypes.TaskStatus) { +func taskState(state swarm.TaskState) func(*swarm.TaskStatus) { + return func(status *swarm.TaskStatus) { status.State = state } } -func taskContainerStatus(id string) func(*swarmtypes.TaskStatus) { - return func(status *swarmtypes.TaskStatus) { - status.ContainerStatus = &swarmtypes.ContainerStatus{ +func taskContainerStatus(id string) func(*swarm.TaskStatus) { + return func(status *swarm.TaskStatus) { + status.ContainerStatus = &swarm.ContainerStatus{ ContainerID: id, } } } -func swarmService(ops ...func(*swarmtypes.Service)) swarmtypes.Service { - service := &swarmtypes.Service{ +func swarmService(ops ...func(*swarm.Service)) swarm.Service { + service := &swarm.Service{ ID: "serviceID", - Spec: swarmtypes.ServiceSpec{ - Annotations: swarmtypes.Annotations{ + Spec: swarm.ServiceSpec{ + Annotations: swarm.Annotations{ Name: "defaultServiceName", }, }, @@ -154,21 +157,21 @@ func swarmService(ops ...func(*swarmtypes.Service)) swarmtypes.Service { return *service } -func serviceName(name string) func(service *swarmtypes.Service) { - return func(service *swarmtypes.Service) { +func serviceName(name string) func(service *swarm.Service) { + return func(service *swarm.Service) { service.Spec.Annotations.Name = name } } -func serviceLabels(labels map[string]string) func(service *swarmtypes.Service) { - return func(service *swarmtypes.Service) { +func serviceLabels(labels map[string]string) func(service *swarm.Service) { + return func(service *swarm.Service) { service.Spec.Annotations.Labels = labels } } -func withEndpoint(ops ...func(*swarmtypes.Endpoint)) func(*swarmtypes.Service) { - return func(service *swarmtypes.Service) { - endpoint := &swarmtypes.Endpoint{} +func withEndpoint(ops ...func(*swarm.Endpoint)) func(*swarm.Service) { + return func(service *swarm.Service) { + endpoint := &swarm.Endpoint{} for _, op := range ops { op(endpoint) @@ -178,21 +181,21 @@ func withEndpoint(ops ...func(*swarmtypes.Endpoint)) func(*swarmtypes.Service) { } } -func virtualIP(networkID, addr string) func(*swarmtypes.Endpoint) { - return func(endpoint *swarmtypes.Endpoint) { +func virtualIP(networkID, addr string) func(*swarm.Endpoint) { + return func(endpoint *swarm.Endpoint) { if endpoint.VirtualIPs == nil { - endpoint.VirtualIPs = []swarmtypes.EndpointVirtualIP{} + endpoint.VirtualIPs = []swarm.EndpointVirtualIP{} } - endpoint.VirtualIPs = append(endpoint.VirtualIPs, swarmtypes.EndpointVirtualIP{ + endpoint.VirtualIPs = append(endpoint.VirtualIPs, swarm.EndpointVirtualIP{ NetworkID: networkID, Addr: addr, }) } } -func withEndpointSpec(ops ...func(*swarmtypes.EndpointSpec)) func(*swarmtypes.Service) { - return func(service *swarmtypes.Service) { - endpointSpec := &swarmtypes.EndpointSpec{} +func withEndpointSpec(ops ...func(*swarm.EndpointSpec)) func(*swarm.Service) { + return func(service *swarm.Service) { + endpointSpec := &swarm.EndpointSpec{} for _, op := range ops { op(endpointSpec) @@ -202,10 +205,10 @@ func withEndpointSpec(ops ...func(*swarmtypes.EndpointSpec)) func(*swarmtypes.Se } } -func modeDNSRR(spec *swarmtypes.EndpointSpec) { - spec.Mode = swarmtypes.ResolutionModeDNSRR +func modeDNSRR(spec *swarm.EndpointSpec) { + spec.Mode = swarm.ResolutionModeDNSRR } -func modeVIP(spec *swarmtypes.EndpointSpec) { - spec.Mode = swarmtypes.ResolutionModeVIP +func modeVIP(spec *swarm.EndpointSpec) { + spec.Mode = swarm.ResolutionModeVIP } diff --git a/pkg/provider/docker/config.go b/pkg/provider/docker/config.go index 0b322715a..3a6e75bee 100644 --- a/pkg/provider/docker/config.go +++ b/pkg/provider/docker/config.go @@ -8,13 +8,13 @@ import ( "net" "strings" - containertypes "github.com/docker/docker/api/types/container" + dockertypes "github.com/docker/docker/api/types" "github.com/docker/docker/client" "github.com/docker/go-connections/nat" "github.com/rs/zerolog/log" "github.com/traefik/traefik/v3/pkg/config/dynamic" "github.com/traefik/traefik/v3/pkg/config/label" - "github.com/traefik/traefik/v3/pkg/observability/logs" + "github.com/traefik/traefik/v3/pkg/logs" "github.com/traefik/traefik/v3/pkg/provider" "github.com/traefik/traefik/v3/pkg/provider/constraints" ) @@ -138,7 +138,7 @@ func (p *DynConfBuilder) buildTCPServiceConfiguration(ctx context.Context, conta } } - if container.Health != "" && container.Health != containertypes.Healthy { + if container.Health != "" && container.Health != dockertypes.Healthy { return nil } @@ -162,7 +162,7 @@ func (p *DynConfBuilder) buildUDPServiceConfiguration(ctx context.Context, conta } } - if container.Health != "" && container.Health != containertypes.Healthy { + if container.Health != "" && container.Health != dockertypes.Healthy { return nil } @@ -188,7 +188,7 @@ func (p *DynConfBuilder) buildServiceConfiguration(ctx context.Context, containe } } - if container.Health != "" && container.Health != containertypes.Healthy { + if container.Health != "" && container.Health != dockertypes.Healthy { return nil } @@ -220,7 +220,7 @@ func (p *DynConfBuilder) keepContainer(ctx context.Context, container dockerData return false } - if !p.AllowEmptyServices && container.Health != "" && container.Health != containertypes.Healthy { + if !p.AllowEmptyServices && container.Health != "" && container.Health != dockertypes.Healthy { logger.Debug().Msg("Filtering unhealthy or starting container") return false } @@ -368,8 +368,8 @@ func (p *DynConfBuilder) getIPAddress(ctx context.Context, container dockerData) } if container.NetworkSettings.NetworkMode.IsHost() { - if container.NodeIP != "" { - return container.NodeIP + if container.Node != nil && container.Node.IPAddress != "" { + return container.Node.IPAddress } if host, err := net.LookupHost("host.docker.internal"); err == nil { return host[0] diff --git a/pkg/provider/docker/config_test.go b/pkg/provider/docker/config_test.go index e54b980c3..c203fc18a 100644 --- a/pkg/provider/docker/config_test.go +++ b/pkg/provider/docker/config_test.go @@ -1,13 +1,14 @@ package docker import ( + "context" "strconv" "testing" "time" - containertypes "github.com/docker/docker/api/types/container" - networktypes "github.com/docker/docker/api/types/network" - swarmtypes "github.com/docker/docker/api/types/swarm" + docker "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/network" + "github.com/docker/docker/api/types/swarm" "github.com/docker/go-connections/nat" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -420,7 +421,7 @@ func TestDynConfBuilder_DefaultRule(t *testing.T) { require.NoError(t, err) } - configuration := builder.build(t.Context(), test.containers) + configuration := builder.build(context.Background(), test.containers) assert.Equal(t, test.expected, configuration) }) @@ -2746,7 +2747,7 @@ func TestDynConfBuilder_build(t *testing.T) { { ServiceName: "Test", Name: "Test", - Health: containertypes.Unhealthy, + Health: docker.Unhealthy, }, }, expected: &dynamic.Configuration{ @@ -2778,7 +2779,7 @@ func TestDynConfBuilder_build(t *testing.T) { { ServiceName: "Test", Name: "Test", - Health: containertypes.Unhealthy, + Health: docker.Unhealthy, }, }, expected: &dynamic.Configuration{ @@ -2825,7 +2826,7 @@ func TestDynConfBuilder_build(t *testing.T) { { ServiceName: "Test", Name: "Test", - Health: containertypes.Unhealthy, + Health: docker.Unhealthy, Labels: map[string]string{ "traefik.tcp.routers.foo.rule": "HostSNI(`foo.bar`)", }, @@ -2860,7 +2861,7 @@ func TestDynConfBuilder_build(t *testing.T) { { ServiceName: "Test", Name: "Test", - Health: containertypes.Unhealthy, + Health: docker.Unhealthy, Labels: map[string]string{ "traefik.tcp.routers.foo.rule": "HostSNI(`foo.bar`)", }, @@ -2903,7 +2904,7 @@ func TestDynConfBuilder_build(t *testing.T) { { ServiceName: "Test", Name: "Test", - Health: containertypes.Unhealthy, + Health: docker.Unhealthy, Labels: map[string]string{ "traefik.udp.routers.foo": "true", }, @@ -2941,7 +2942,7 @@ func TestDynConfBuilder_build(t *testing.T) { Labels: map[string]string{ "traefik.udp.routers.foo": "true", }, - Health: containertypes.Unhealthy, + Health: docker.Unhealthy, }, }, expected: &dynamic.Configuration{ @@ -3928,7 +3929,7 @@ func TestDynConfBuilder_build(t *testing.T) { require.NoError(t, err) } - configuration := builder.build(t.Context(), test.containers) + configuration := builder.build(context.Background(), test.containers) assert.Equal(t, test.expected, configuration) }) @@ -3944,7 +3945,7 @@ func TestDynConfBuilder_getIPPort_docker(t *testing.T) { testCases := []struct { desc string - container containertypes.InspectResponse + container docker.ContainerJSON serverPort string expected expected }{ @@ -4100,7 +4101,7 @@ func TestDynConfBuilder_getIPPort_docker(t *testing.T) { UseBindPortIP: true, }, nil, false) - actualIP, actualPort, actualError := builder.getIPPort(t.Context(), dData, test.serverPort) + actualIP, actualPort, actualError := builder.getIPPort(context.Background(), dData, test.serverPort) if test.expected.error { require.Error(t, actualError) } else { @@ -4115,9 +4116,8 @@ func TestDynConfBuilder_getIPPort_docker(t *testing.T) { func TestDynConfBuilder_getIPAddress_docker(t *testing.T) { testCases := []struct { desc string - container containertypes.InspectResponse + container docker.ContainerJSON network string - nodeIP string expected string }{ { @@ -4193,10 +4193,10 @@ func TestDynConfBuilder_getIPAddress_docker(t *testing.T) { expected: "127.0.0.1", }, { - desc: "no network, no network label, mode host, node IP", - nodeIP: "10.0.0.5", + desc: "no network, no network label, mode host, node IP", container: containerJSON( networkMode("host"), + nodeIP("10.0.0.5"), ), expected: "10.0.0.5", }, @@ -4211,9 +4211,6 @@ func TestDynConfBuilder_getIPAddress_docker(t *testing.T) { } dData := parseContainer(test.container) - if test.nodeIP != "" { - dData.NodeIP = test.nodeIP - } dData.ExtraConf.Network = conf.Network if len(test.network) > 0 { @@ -4222,7 +4219,7 @@ func TestDynConfBuilder_getIPAddress_docker(t *testing.T) { builder := NewDynConfBuilder(conf, nil, false) - actual := builder.getIPAddress(t.Context(), dData) + actual := builder.getIPAddress(context.Background(), dData) assert.Equal(t, test.expected, actual) }) } @@ -4230,14 +4227,14 @@ func TestDynConfBuilder_getIPAddress_docker(t *testing.T) { func TestDynConfBuilder_getIPAddress_swarm(t *testing.T) { testCases := []struct { - service swarmtypes.Service + service swarm.Service expected string - networks map[string]*networktypes.Summary + networks map[string]*network.Summary }{ { service: swarmService(withEndpointSpec(modeDNSRR)), expected: "", - networks: map[string]*networktypes.Summary{}, + networks: map[string]*network.Summary{}, }, { service: swarmService( @@ -4245,7 +4242,7 @@ func TestDynConfBuilder_getIPAddress_swarm(t *testing.T) { withEndpoint(virtualIP("1", "10.11.12.13/24")), ), expected: "10.11.12.13", - networks: map[string]*networktypes.Summary{ + networks: map[string]*network.Summary{ "1": { Name: "foo", }, @@ -4263,7 +4260,7 @@ func TestDynConfBuilder_getIPAddress_swarm(t *testing.T) { ), ), expected: "10.11.12.99", - networks: map[string]*networktypes.Summary{ + networks: map[string]*network.Summary{ "1": { Name: "foonet", }, @@ -4281,11 +4278,11 @@ func TestDynConfBuilder_getIPAddress_swarm(t *testing.T) { var p SwarmProvider require.NoError(t, p.Init()) - dData, err := p.parseService(t.Context(), test.service, test.networks) + dData, err := p.parseService(context.Background(), test.service, test.networks) require.NoError(t, err) builder := NewDynConfBuilder(p.Shared, nil, false) - actual := builder.getIPAddress(t.Context(), dData) + actual := builder.getIPAddress(context.Background(), dData) assert.Equal(t, test.expected, actual) }) } diff --git a/pkg/provider/docker/data.go b/pkg/provider/docker/data.go index 4c42d396e..509f2431d 100644 --- a/pkg/provider/docker/data.go +++ b/pkg/provider/docker/data.go @@ -1,7 +1,8 @@ package docker import ( - containertypes "github.com/docker/docker/api/types/container" + dockertypes "github.com/docker/docker/api/types" + dockercontainertypes "github.com/docker/docker/api/types/container" "github.com/docker/go-connections/nat" ) @@ -13,13 +14,13 @@ type dockerData struct { Labels map[string]string // List of labels set to container or service NetworkSettings networkSettings Health string - NodeIP string // Only filled in Swarm mode. + Node *dockertypes.ContainerNode ExtraConf configuration } // NetworkSettings holds the networks data to the provider. type networkSettings struct { - NetworkMode containertypes.NetworkMode + NetworkMode dockercontainertypes.NetworkMode Ports nat.PortMap Networks map[string]*networkData } diff --git a/pkg/provider/docker/pdocker.go b/pkg/provider/docker/pdocker.go index 5e88be51b..8deb4f146 100644 --- a/pkg/provider/docker/pdocker.go +++ b/pkg/provider/docker/pdocker.go @@ -9,6 +9,7 @@ import ( "time" "github.com/cenkalti/backoff/v4" + dockertypes "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/container" eventtypes "github.com/docker/docker/api/types/events" "github.com/docker/docker/api/types/filters" @@ -16,7 +17,7 @@ import ( "github.com/rs/zerolog/log" "github.com/traefik/traefik/v3/pkg/config/dynamic" "github.com/traefik/traefik/v3/pkg/job" - "github.com/traefik/traefik/v3/pkg/observability/logs" + "github.com/traefik/traefik/v3/pkg/logs" "github.com/traefik/traefik/v3/pkg/provider" "github.com/traefik/traefik/v3/pkg/safe" ) @@ -110,7 +111,7 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe. if p.Watch { f := filters.NewArgs() f.Add("type", "container") - options := eventtypes.ListOptions{ + options := dockertypes.EventsOptions{ Filters: f, } diff --git a/pkg/provider/docker/pswarm.go b/pkg/provider/docker/pswarm.go index bcb7ded48..07ad3cabc 100644 --- a/pkg/provider/docker/pswarm.go +++ b/pkg/provider/docker/pswarm.go @@ -8,8 +8,8 @@ import ( "time" "github.com/cenkalti/backoff/v4" + dockertypes "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/filters" - networktypes "github.com/docker/docker/api/types/network" swarmtypes "github.com/docker/docker/api/types/swarm" "github.com/docker/docker/api/types/versions" "github.com/docker/docker/client" @@ -17,7 +17,7 @@ import ( ptypes "github.com/traefik/paerser/types" "github.com/traefik/traefik/v3/pkg/config/dynamic" "github.com/traefik/traefik/v3/pkg/job" - "github.com/traefik/traefik/v3/pkg/observability/logs" + "github.com/traefik/traefik/v3/pkg/logs" "github.com/traefik/traefik/v3/pkg/provider" "github.com/traefik/traefik/v3/pkg/safe" ) @@ -161,7 +161,7 @@ func (p *SwarmProvider) Provide(configurationChan chan<- dynamic.Message, pool * func (p *SwarmProvider) listServices(ctx context.Context, dockerClient client.APIClient) ([]dockerData, error) { logger := log.Ctx(ctx) - serviceList, err := dockerClient.ServiceList(ctx, swarmtypes.ServiceListOptions{}) + serviceList, err := dockerClient.ServiceList(ctx, dockertypes.ServiceListOptions{}) if err != nil { return nil, err } @@ -179,13 +179,13 @@ func (p *SwarmProvider) listServices(ctx context.Context, dockerClient client.AP networkListArgs.Add("driver", "overlay") } - networkList, err := dockerClient.NetworkList(ctx, networktypes.ListOptions{Filters: networkListArgs}) + networkList, err := dockerClient.NetworkList(ctx, dockertypes.NetworkListOptions{Filters: networkListArgs}) if err != nil { logger.Debug().Err(err).Msg("Failed to network inspect on client for docker") return nil, err } - networkMap := make(map[string]*networktypes.Summary) + networkMap := make(map[string]*dockertypes.NetworkResource) for _, network := range networkList { networkMap[network.ID] = &network } @@ -218,7 +218,7 @@ func (p *SwarmProvider) listServices(ctx context.Context, dockerClient client.AP return dockerDataList, err } -func (p *SwarmProvider) parseService(ctx context.Context, service swarmtypes.Service, networkMap map[string]*networktypes.Summary) (dockerData, error) { +func (p *SwarmProvider) parseService(ctx context.Context, service swarmtypes.Service, networkMap map[string]*dockertypes.NetworkResource) (dockerData, error) { logger := log.Ctx(ctx) dData := dockerData{ @@ -267,13 +267,13 @@ func (p *SwarmProvider) parseService(ctx context.Context, service swarmtypes.Ser } func listTasks(ctx context.Context, dockerClient client.APIClient, serviceID string, - serviceDockerData dockerData, networkMap map[string]*networktypes.Summary, isGlobalSvc bool, + serviceDockerData dockerData, networkMap map[string]*dockertypes.NetworkResource, isGlobalSvc bool, ) ([]dockerData, error) { serviceIDFilter := filters.NewArgs() serviceIDFilter.Add("service", serviceID) serviceIDFilter.Add("desired-state", "running") - taskList, err := dockerClient.TaskList(ctx, swarmtypes.TaskListOptions{Filters: serviceIDFilter}) + taskList, err := dockerClient.TaskList(ctx, dockertypes.TaskListOptions{Filters: serviceIDFilter}) if err != nil { return nil, err } @@ -283,11 +283,7 @@ func listTasks(ctx context.Context, dockerClient client.APIClient, serviceID str if task.Status.State != swarmtypes.TaskStateRunning { continue } - dData, err := parseTasks(ctx, dockerClient, task, serviceDockerData, networkMap, isGlobalSvc) - if err != nil { - log.Ctx(ctx).Warn().Err(err).Msgf("Error while parsing task %s", getServiceName(dData)) - continue - } + dData := parseTasks(ctx, task, serviceDockerData, networkMap, isGlobalSvc) if len(dData.NetworkSettings.Networks) > 0 { dockerDataList = append(dockerDataList, dData) } @@ -295,9 +291,9 @@ func listTasks(ctx context.Context, dockerClient client.APIClient, serviceID str return dockerDataList, err } -func parseTasks(ctx context.Context, dockerClient client.APIClient, task swarmtypes.Task, serviceDockerData dockerData, - networkMap map[string]*networktypes.Summary, isGlobalSvc bool, -) (dockerData, error) { +func parseTasks(ctx context.Context, task swarmtypes.Task, serviceDockerData dockerData, + networkMap map[string]*dockertypes.NetworkResource, isGlobalSvc bool, +) dockerData { dData := dockerData{ ID: task.ID, ServiceName: serviceDockerData.Name, @@ -311,14 +307,6 @@ func parseTasks(ctx context.Context, dockerClient client.APIClient, task swarmty dData.Name = serviceDockerData.Name + "." + task.ID } - if task.NodeID != "" { - node, _, err := dockerClient.NodeInspectWithRaw(ctx, task.NodeID) - if err != nil { - return dockerData{}, fmt.Errorf("inspecting node %s: %w", task.NodeID, err) - } - dData.NodeIP = node.Status.Addr - } - if task.NetworksAttachments != nil { dData.NetworkSettings.Networks = make(map[string]*networkData) for _, virtualIP := range task.NetworksAttachments { @@ -340,5 +328,5 @@ func parseTasks(ctx context.Context, dockerClient client.APIClient, task swarmty } } } - return dData, nil + return dData } diff --git a/pkg/provider/docker/pswarm_mock_test.go b/pkg/provider/docker/pswarm_mock_test.go index 8793982b9..83eb61b3b 100644 --- a/pkg/provider/docker/pswarm_mock_test.go +++ b/pkg/provider/docker/pswarm_mock_test.go @@ -4,47 +4,35 @@ import ( "context" dockertypes "github.com/docker/docker/api/types" - containertypes "github.com/docker/docker/api/types/container" - networktypes "github.com/docker/docker/api/types/network" - swarmtypes "github.com/docker/docker/api/types/swarm" + "github.com/docker/docker/api/types/swarm" dockerclient "github.com/docker/docker/client" ) type fakeTasksClient struct { dockerclient.APIClient - tasks []swarmtypes.Task - container containertypes.InspectResponse + tasks []swarm.Task + container dockertypes.ContainerJSON err error } -func (c *fakeTasksClient) TaskList(ctx context.Context, options swarmtypes.TaskListOptions) ([]swarmtypes.Task, error) { +func (c *fakeTasksClient) TaskList(ctx context.Context, options dockertypes.TaskListOptions) ([]swarm.Task, error) { return c.tasks, c.err } -func (c *fakeTasksClient) ContainerInspect(ctx context.Context, container string) (containertypes.InspectResponse, error) { +func (c *fakeTasksClient) ContainerInspect(ctx context.Context, container string) (dockertypes.ContainerJSON, error) { return c.container, c.err } type fakeServicesClient struct { dockerclient.APIClient dockerVersion string - networks []networktypes.Summary - nodes []swarmtypes.Node - services []swarmtypes.Service - tasks []swarmtypes.Task + networks []dockertypes.NetworkResource + services []swarm.Service + tasks []swarm.Task err error } -func (c *fakeServicesClient) NodeInspectWithRaw(ctx context.Context, nodeID string) (swarmtypes.Node, []byte, error) { - for _, node := range c.nodes { - if node.ID == nodeID { - return node, nil, nil - } - } - return swarmtypes.Node{}, nil, c.err -} - -func (c *fakeServicesClient) ServiceList(ctx context.Context, options swarmtypes.ServiceListOptions) ([]swarmtypes.Service, error) { +func (c *fakeServicesClient) ServiceList(ctx context.Context, options dockertypes.ServiceListOptions) ([]swarm.Service, error) { return c.services, c.err } @@ -52,10 +40,10 @@ func (c *fakeServicesClient) ServerVersion(ctx context.Context) (dockertypes.Ver return dockertypes.Version{APIVersion: c.dockerVersion}, c.err } -func (c *fakeServicesClient) NetworkList(ctx context.Context, options networktypes.ListOptions) ([]networktypes.Summary, error) { +func (c *fakeServicesClient) NetworkList(ctx context.Context, options dockertypes.NetworkListOptions) ([]dockertypes.NetworkResource, error) { return c.networks, c.err } -func (c *fakeServicesClient) TaskList(ctx context.Context, options swarmtypes.TaskListOptions) ([]swarmtypes.Task, error) { +func (c *fakeServicesClient) TaskList(ctx context.Context, options dockertypes.TaskListOptions) ([]swarm.Task, error) { return c.tasks, c.err } diff --git a/pkg/provider/docker/pswarm_test.go b/pkg/provider/docker/pswarm_test.go index f762f884d..7d6421037 100644 --- a/pkg/provider/docker/pswarm_test.go +++ b/pkg/provider/docker/pswarm_test.go @@ -1,36 +1,37 @@ package docker import ( + "context" "strconv" "testing" "time" - networktypes "github.com/docker/docker/api/types/network" - swarmtypes "github.com/docker/docker/api/types/swarm" + "github.com/docker/docker/api/types/network" + "github.com/docker/docker/api/types/swarm" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestListTasks(t *testing.T) { testCases := []struct { - service swarmtypes.Service - tasks []swarmtypes.Task + service swarm.Service + tasks []swarm.Task isGlobalSVC bool expectedTasks []string - networks map[string]*networktypes.Summary + networks map[string]*network.Summary }{ { service: swarmService(serviceName("container")), - tasks: []swarmtypes.Task{ + tasks: []swarm.Task{ swarmTask("id1", taskSlot(1), taskNetworkAttachment("1", "network1", "overlay", []string{"127.0.0.1"}), - taskStatus(taskState(swarmtypes.TaskStateRunning)), + taskStatus(taskState(swarm.TaskStateRunning)), ), swarmTask("id2", taskSlot(2), taskNetworkAttachment("1", "network1", "overlay", []string{"127.0.0.2"}), - taskStatus(taskState(swarmtypes.TaskStatePending)), + taskStatus(taskState(swarm.TaskStatePending)), ), swarmTask("id3", taskSlot(3), @@ -39,12 +40,12 @@ func TestListTasks(t *testing.T) { swarmTask("id4", taskSlot(4), taskNetworkAttachment("1", "network1", "overlay", []string{"127.0.0.4"}), - taskStatus(taskState(swarmtypes.TaskStateRunning)), + taskStatus(taskState(swarm.TaskStateRunning)), ), swarmTask("id5", taskSlot(5), taskNetworkAttachment("1", "network1", "overlay", []string{"127.0.0.5"}), - taskStatus(taskState(swarmtypes.TaskStateFailed)), + taskStatus(taskState(swarm.TaskStateFailed)), ), }, isGlobalSVC: false, @@ -52,7 +53,7 @@ func TestListTasks(t *testing.T) { "container.1", "container.4", }, - networks: map[string]*networktypes.Summary{ + networks: map[string]*network.Summary{ "1": { Name: "foo", }, @@ -67,11 +68,11 @@ func TestListTasks(t *testing.T) { var p SwarmProvider require.NoError(t, p.Init()) - dockerData, err := p.parseService(t.Context(), test.service, test.networks) + dockerData, err := p.parseService(context.Background(), test.service, test.networks) require.NoError(t, err) dockerClient := &fakeTasksClient{tasks: test.tasks} - taskDockerData, _ := listTasks(t.Context(), dockerClient, test.service.ID, dockerData, test.networks, test.isGlobalSVC) + taskDockerData, _ := listTasks(context.Background(), dockerClient, test.service.ID, dockerData, test.networks, test.isGlobalSVC) if len(test.expectedTasks) != len(taskDockerData) { t.Errorf("expected tasks %v, got %v", test.expectedTasks, taskDockerData) @@ -89,15 +90,15 @@ func TestListTasks(t *testing.T) { func TestSwarmProvider_listServices(t *testing.T) { testCases := []struct { desc string - services []swarmtypes.Service - tasks []swarmtypes.Task + services []swarm.Service + tasks []swarm.Task dockerVersion string - networks []networktypes.Summary + networks []network.Summary expectedServices []string }{ { desc: "Should return no service due to no networks defined", - services: []swarmtypes.Service{ + services: []swarm.Service{ swarmService( serviceName("service1"), serviceLabels(map[string]string{ @@ -118,12 +119,12 @@ func TestSwarmProvider_listServices(t *testing.T) { withEndpointSpec(modeDNSRR)), }, dockerVersion: "1.30", - networks: []networktypes.Summary{}, + networks: []network.Summary{}, expectedServices: []string{}, }, { desc: "Should return only service1", - services: []swarmtypes.Service{ + services: []swarm.Service{ swarmService( serviceName("service1"), serviceLabels(map[string]string{ @@ -144,7 +145,7 @@ func TestSwarmProvider_listServices(t *testing.T) { withEndpointSpec(modeDNSRR)), }, dockerVersion: "1.30", - networks: []networktypes.Summary{ + networks: []network.Summary{ { Name: "network_name", ID: "yk6l57rfwizjzxxzftn4amaot", @@ -156,8 +157,8 @@ func TestSwarmProvider_listServices(t *testing.T) { Ingress: false, ConfigOnly: false, Options: map[string]string{ - "com.docker.networktypes.driver.overlay.vxlanid_list": "4098", - "com.docker.networktypes.enable_ipv6": "false", + "com.docker.network.driver.overlay.vxlanid_list": "4098", + "com.docker.network.enable_ipv6": "false", }, Labels: map[string]string{ "com.docker.stack.namespace": "test", @@ -170,7 +171,7 @@ func TestSwarmProvider_listServices(t *testing.T) { }, { desc: "Should return service1 and service2", - services: []swarmtypes.Service{ + services: []swarm.Service{ swarmService( serviceName("service1"), serviceLabels(map[string]string{ @@ -188,18 +189,18 @@ func TestSwarmProvider_listServices(t *testing.T) { }), withEndpointSpec(modeDNSRR)), }, - tasks: []swarmtypes.Task{ + tasks: []swarm.Task{ swarmTask("id1", taskNetworkAttachment("yk6l57rfwizjzxxzftn4amaot", "network_name", "overlay", []string{"127.0.0.1"}), - taskStatus(taskState(swarmtypes.TaskStateRunning)), + taskStatus(taskState(swarm.TaskStateRunning)), ), swarmTask("id2", taskNetworkAttachment("yk6l57rfwizjzxxzftn4amaot", "network_name", "overlay", []string{"127.0.0.1"}), - taskStatus(taskState(swarmtypes.TaskStateRunning)), + taskStatus(taskState(swarm.TaskStateRunning)), ), }, dockerVersion: "1.30", - networks: []networktypes.Summary{ + networks: []network.Summary{ { Name: "network_name", ID: "yk6l57rfwizjzxxzftn4amaot", @@ -211,8 +212,8 @@ func TestSwarmProvider_listServices(t *testing.T) { Ingress: false, ConfigOnly: false, Options: map[string]string{ - "com.docker.networktypes.driver.overlay.vxlanid_list": "4098", - "com.docker.networktypes.enable_ipv6": "false", + "com.docker.network.driver.overlay.vxlanid_list": "4098", + "com.docker.network.enable_ipv6": "false", }, Labels: map[string]string{ "com.docker.stack.namespace": "test", @@ -237,7 +238,7 @@ func TestSwarmProvider_listServices(t *testing.T) { var p SwarmProvider require.NoError(t, p.Init()) - serviceDockerData, err := p.listServices(t.Context(), dockerClient) + serviceDockerData, err := p.listServices(context.Background(), dockerClient) assert.NoError(t, err) assert.Len(t, serviceDockerData, len(test.expectedServices)) @@ -253,16 +254,15 @@ func TestSwarmProvider_listServices(t *testing.T) { func TestSwarmProvider_parseService_task(t *testing.T) { testCases := []struct { - service swarmtypes.Service - tasks []swarmtypes.Task - nodes []swarmtypes.Node + service swarm.Service + tasks []swarm.Task isGlobalSVC bool expected map[string]dockerData - networks map[string]*networktypes.Summary + networks map[string]*network.Summary }{ { service: swarmService(serviceName("container")), - tasks: []swarmtypes.Task{ + tasks: []swarm.Task{ swarmTask("id1", taskSlot(1)), swarmTask("id2", taskSlot(2)), swarmTask("id3", taskSlot(3)), @@ -279,7 +279,7 @@ func TestSwarmProvider_parseService_task(t *testing.T) { Name: "container.3", }, }, - networks: map[string]*networktypes.Summary{ + networks: map[string]*network.Summary{ "1": { Name: "foo", }, @@ -287,7 +287,7 @@ func TestSwarmProvider_parseService_task(t *testing.T) { }, { service: swarmService(serviceName("container")), - tasks: []swarmtypes.Task{ + tasks: []swarm.Task{ swarmTask("id1"), swarmTask("id2"), swarmTask("id3"), @@ -304,7 +304,7 @@ func TestSwarmProvider_parseService_task(t *testing.T) { Name: "container.id3", }, }, - networks: map[string]*networktypes.Summary{ + networks: map[string]*network.Summary{ "1": { Name: "foo", }, @@ -318,12 +318,12 @@ func TestSwarmProvider_parseService_task(t *testing.T) { virtualIP("1", ""), ), ), - tasks: []swarmtypes.Task{ + tasks: []swarm.Task{ swarmTask( "id1", taskNetworkAttachment("1", "vlan", "macvlan", []string{"127.0.0.1"}), taskStatus( - taskState(swarmtypes.TaskStateRunning), + taskState(swarm.TaskStateRunning), taskContainerStatus("c1"), ), ), @@ -342,40 +342,12 @@ func TestSwarmProvider_parseService_task(t *testing.T) { }, }, }, - networks: map[string]*networktypes.Summary{ + networks: map[string]*network.Summary{ "1": { Name: "vlan", }, }, }, - { - service: swarmService(serviceName("container")), - tasks: []swarmtypes.Task{ - swarmTask("id1", - taskSlot(1), - taskNodeID("id1"), - ), - }, - nodes: []swarmtypes.Node{ - { - ID: "id1", - Status: swarmtypes.NodeStatus{ - Addr: "10.11.12.13", - }, - }, - }, - expected: map[string]dockerData{ - "id1": { - Name: "container.1", - NodeIP: "10.11.12.13", - }, - }, - networks: map[string]*networktypes.Summary{ - "1": { - Name: "foo", - }, - }, - }, } for caseID, test := range testCases { @@ -385,21 +357,13 @@ func TestSwarmProvider_parseService_task(t *testing.T) { var p SwarmProvider require.NoError(t, p.Init()) - dData, err := p.parseService(t.Context(), test.service, test.networks) + dData, err := p.parseService(context.Background(), test.service, test.networks) require.NoError(t, err) - dockerClient := &fakeServicesClient{ - tasks: test.tasks, - nodes: test.nodes, - } - for _, task := range test.tasks { - taskDockerData, err := parseTasks(t.Context(), dockerClient, task, dData, test.networks, test.isGlobalSVC) - require.NoError(t, err) - + taskDockerData := parseTasks(context.Background(), task, dData, test.networks, test.isGlobalSVC) expected := test.expected[task.ID] assert.Equal(t, expected.Name, taskDockerData.Name) - assert.Equal(t, expected.NodeIP, taskDockerData.NodeIP) } }) } diff --git a/pkg/provider/docker/shared.go b/pkg/provider/docker/shared.go index 931c61905..027c398fd 100644 --- a/pkg/provider/docker/shared.go +++ b/pkg/provider/docker/shared.go @@ -9,7 +9,7 @@ import ( "time" "github.com/docker/cli/cli/connhelper" - containertypes "github.com/docker/docker/api/types/container" + dockertypes "github.com/docker/docker/api/types" "github.com/docker/docker/client" "github.com/docker/go-connections/nat" "github.com/docker/go-connections/sockets" @@ -62,7 +62,7 @@ func inspectContainers(ctx context.Context, dockerClient client.ContainerAPIClie return dockerData{} } -func parseContainer(container containertypes.InspectResponse) dockerData { +func parseContainer(container dockertypes.ContainerJSON) dockerData { dData := dockerData{ NetworkSettings: networkSettings{}, } @@ -71,6 +71,7 @@ func parseContainer(container containertypes.InspectResponse) dockerData { dData.ID = container.ContainerJSONBase.ID dData.Name = container.ContainerJSONBase.Name dData.ServiceName = dData.Name // Default ServiceName to be the container's Name. + dData.Node = container.ContainerJSONBase.Node if container.ContainerJSONBase.HostConfig != nil { dData.NetworkSettings.NetworkMode = container.ContainerJSONBase.HostConfig.NetworkMode diff --git a/pkg/provider/docker/shared_test.go b/pkg/provider/docker/shared_test.go index a5cf68def..90022c067 100644 --- a/pkg/provider/docker/shared_test.go +++ b/pkg/provider/docker/shared_test.go @@ -1,12 +1,12 @@ package docker import ( + "context" "strconv" "testing" - containertypes "github.com/docker/docker/api/types/container" - networktypes "github.com/docker/docker/api/types/network" - swarmtypes "github.com/docker/docker/api/types/swarm" + docker "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/swarm" "github.com/docker/go-connections/nat" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -15,7 +15,7 @@ import ( func Test_getPort_docker(t *testing.T) { testCases := []struct { desc string - container containertypes.InspectResponse + container docker.ContainerJSON serverPort string expected string }{ @@ -79,16 +79,16 @@ func Test_getPort_docker(t *testing.T) { func Test_getPort_swarm(t *testing.T) { testCases := []struct { - service swarmtypes.Service + service swarm.Service serverPort string - networks map[string]*networktypes.Summary + networks map[string]*docker.NetworkResource expected string }{ { service: swarmService( withEndpointSpec(modeDNSRR), ), - networks: map[string]*networktypes.Summary{}, + networks: map[string]*docker.NetworkResource{}, serverPort: "8080", expected: "8080", }, @@ -101,7 +101,7 @@ func Test_getPort_swarm(t *testing.T) { var p SwarmProvider require.NoError(t, p.Init()) - dData, err := p.parseService(t.Context(), test.service, test.networks) + dData, err := p.parseService(context.Background(), test.service, test.networks) require.NoError(t, err) actual := getPort(dData, test.serverPort) diff --git a/pkg/provider/ecs/config_test.go b/pkg/provider/ecs/config_test.go index 756168120..8a0b41bab 100644 --- a/pkg/provider/ecs/config_test.go +++ b/pkg/provider/ecs/config_test.go @@ -1,6 +1,7 @@ package ecs import ( + "context" "testing" "time" @@ -389,7 +390,7 @@ func TestDefaultRule(t *testing.T) { require.NoError(t, err) } - configuration := p.buildConfiguration(t.Context(), test.instances) + configuration := p.buildConfiguration(context.Background(), test.instances) assert.Equal(t, test.expected, configuration) }) @@ -3490,7 +3491,7 @@ func Test_buildConfiguration(t *testing.T) { require.NoError(t, err) } - configuration := p.buildConfiguration(t.Context(), test.containers) + configuration := p.buildConfiguration(context.Background(), test.containers) assert.Equal(t, test.expected, configuration) }) diff --git a/pkg/provider/ecs/ecs.go b/pkg/provider/ecs/ecs.go index 084b9b9ca..b6119e24e 100644 --- a/pkg/provider/ecs/ecs.go +++ b/pkg/provider/ecs/ecs.go @@ -24,7 +24,7 @@ import ( "github.com/rs/zerolog/log" "github.com/traefik/traefik/v3/pkg/config/dynamic" "github.com/traefik/traefik/v3/pkg/job" - "github.com/traefik/traefik/v3/pkg/observability/logs" + "github.com/traefik/traefik/v3/pkg/logs" "github.com/traefik/traefik/v3/pkg/provider" "github.com/traefik/traefik/v3/pkg/safe" ) diff --git a/pkg/provider/file/file.go b/pkg/provider/file/file.go index 5f0b08223..7b1b79e99 100644 --- a/pkg/provider/file/file.go +++ b/pkg/provider/file/file.go @@ -18,7 +18,7 @@ import ( "github.com/rs/zerolog/log" "github.com/traefik/paerser/file" "github.com/traefik/traefik/v3/pkg/config/dynamic" - "github.com/traefik/traefik/v3/pkg/observability/logs" + "github.com/traefik/traefik/v3/pkg/logs" "github.com/traefik/traefik/v3/pkg/provider" "github.com/traefik/traefik/v3/pkg/safe" "github.com/traefik/traefik/v3/pkg/tls" diff --git a/pkg/provider/file/file_test.go b/pkg/provider/file/file_test.go index 3aebe1908..3cb4a0490 100644 --- a/pkg/provider/file/file_test.go +++ b/pkg/provider/file/file_test.go @@ -1,6 +1,7 @@ package file import ( + "context" "io" "os" "path/filepath" @@ -66,7 +67,7 @@ func TestTLSCertificateContent(t *testing.T) { require.NoError(t, err) provider := &Provider{} - configuration, err := provider.loadFileConfig(t.Context(), fileConfig.Name(), true) + configuration, err := provider.loadFileConfig(context.Background(), fileConfig.Name(), true) require.NoError(t, err) require.Equal(t, "CONTENT", configuration.TLS.Certificates[0].Certificate.CertFile.String()) @@ -91,7 +92,7 @@ func TestErrorWhenEmptyConfig(t *testing.T) { configChan := make(chan dynamic.Message) errorChan := make(chan struct{}) go func() { - err := provider.Provide(configChan, safe.NewPool(t.Context())) + err := provider.Provide(configChan, safe.NewPool(context.Background())) assert.Error(t, err) close(errorChan) }() @@ -115,7 +116,7 @@ func TestProvideWithoutWatch(t *testing.T) { provider.DebugLogGeneratedTemplate = true go func() { - err := provider.Provide(configChan, safe.NewPool(t.Context())) + err := provider.Provide(configChan, safe.NewPool(context.Background())) assert.NoError(t, err) }() @@ -145,7 +146,7 @@ func TestProvideWithWatch(t *testing.T) { configChan := make(chan dynamic.Message) go func() { - err := provider.Provide(configChan, safe.NewPool(t.Context())) + err := provider.Provide(configChan, safe.NewPool(context.Background())) assert.NoError(t, err) }() diff --git a/pkg/provider/http/http.go b/pkg/provider/http/http.go index 4712e1b0a..1068429b2 100644 --- a/pkg/provider/http/http.go +++ b/pkg/provider/http/http.go @@ -16,7 +16,7 @@ import ( ptypes "github.com/traefik/paerser/types" "github.com/traefik/traefik/v3/pkg/config/dynamic" "github.com/traefik/traefik/v3/pkg/job" - "github.com/traefik/traefik/v3/pkg/observability/logs" + "github.com/traefik/traefik/v3/pkg/logs" "github.com/traefik/traefik/v3/pkg/provider" "github.com/traefik/traefik/v3/pkg/safe" "github.com/traefik/traefik/v3/pkg/tls" diff --git a/pkg/provider/http/http_test.go b/pkg/provider/http/http_test.go index eae98d979..c9f932536 100644 --- a/pkg/provider/http/http_test.go +++ b/pkg/provider/http/http_test.go @@ -1,6 +1,7 @@ package http import ( + "context" "fmt" "net/http" "net/http/httptest" @@ -234,7 +235,7 @@ func TestProvider_Provide(t *testing.T) { }, } - err = provider.Provide(configurationChan, safe.NewPool(t.Context())) + err = provider.Provide(configurationChan, safe.NewPool(context.Background())) require.NoError(t, err) timeout := time.After(time.Second) @@ -268,7 +269,7 @@ func TestProvider_ProvideConfigurationOnlyOnceIfUnchanged(t *testing.T) { configurationChan := make(chan dynamic.Message, 10) - err = provider.Provide(configurationChan, safe.NewPool(t.Context())) + err = provider.Provide(configurationChan, safe.NewPool(context.Background())) require.NoError(t, err) time.Sleep(time.Second) diff --git a/pkg/provider/kubernetes/crd/fixtures/services.yml b/pkg/provider/kubernetes/crd/fixtures/services.yml index 7c33a2c63..86094ef8a 100644 --- a/pkg/provider/kubernetes/crd/fixtures/services.yml +++ b/pkg/provider/kubernetes/crd/fixtures/services.yml @@ -301,38 +301,6 @@ spec: type: ClusterIP clusterIP: 10.10.0.1 ---- -apiVersion: v1 -kind: Service -metadata: - name: native-disabled-svc - namespace: default -spec: - ports: - - name: web - port: 80 - type: ClusterIP - clusterIP: 10.10.0.1 - ---- -kind: EndpointSlice -apiVersion: discovery.k8s.io/v1 -metadata: - name: native-disabled-svc-abc - namespace: default - labels: - kubernetes.io/service-name: native-disabled-svc -addressType: IPv4 -ports: - - name: web - port: 80 -endpoints: - - addresses: - - 10.10.0.20 - - 10.10.0.21 - conditions: - ready: true - --- apiVersion: v1 kind: Service diff --git a/pkg/provider/kubernetes/crd/fixtures/tcp/services.yml b/pkg/provider/kubernetes/crd/fixtures/tcp/services.yml index 05f49a53f..8c29006e8 100644 --- a/pkg/provider/kubernetes/crd/fixtures/tcp/services.yml +++ b/pkg/provider/kubernetes/crd/fixtures/tcp/services.yml @@ -298,46 +298,11 @@ metadata: spec: ports: - - name: tcp - protocol: TCP - port: 9000 + - name: myapp + port: 8000 type: ClusterIP clusterIP: 10.10.0.1 ---- -apiVersion: v1 -kind: Service -metadata: - name: native-disabled-svc-tcp - namespace: default -spec: - ports: - - name: tcp - protocol: TCP - port: 9000 - type: ClusterIP - clusterIP: 10.10.0.1 - ---- -kind: EndpointSlice -apiVersion: discovery.k8s.io/v1 -metadata: - name: native-disabled-tcp-abc - namespace: default - labels: - kubernetes.io/service-name: native-disabled-svc-tcp -addressType: IPv4 -ports: - - name: tcp - protocol: TCP - port: 9000 -endpoints: - - addresses: - - 10.10.0.30 - - 10.10.0.31 - conditions: - ready: true - --- apiVersion: v1 kind: Service diff --git a/pkg/provider/kubernetes/crd/fixtures/tcp/with_global_native_service_lb.yml b/pkg/provider/kubernetes/crd/fixtures/tcp/with_global_native_service_lb.yml index 712119c00..d8832498e 100644 --- a/pkg/provider/kubernetes/crd/fixtures/tcp/with_global_native_service_lb.yml +++ b/pkg/provider/kubernetes/crd/fixtures/tcp/with_global_native_service_lb.yml @@ -12,4 +12,4 @@ spec: - match: HostSNI(`foo.com`) services: - name: native-svc-tcp - port: 9000 + port: 8000 diff --git a/pkg/provider/kubernetes/crd/fixtures/tcp/with_native_service_lb.yml b/pkg/provider/kubernetes/crd/fixtures/tcp/with_native_service_lb.yml index ad8305ac7..c2dbfca31 100644 --- a/pkg/provider/kubernetes/crd/fixtures/tcp/with_native_service_lb.yml +++ b/pkg/provider/kubernetes/crd/fixtures/tcp/with_native_service_lb.yml @@ -12,5 +12,5 @@ spec: - match: HostSNI(`foo.com`) services: - name: native-svc-tcp - port: 9000 + port: 8000 nativeLB: true diff --git a/pkg/provider/kubernetes/crd/fixtures/tcp/with_native_service_lb_disabled.yml b/pkg/provider/kubernetes/crd/fixtures/tcp/with_native_service_lb_disabled.yml deleted file mode 100644 index e54b5b3c0..000000000 --- a/pkg/provider/kubernetes/crd/fixtures/tcp/with_native_service_lb_disabled.yml +++ /dev/null @@ -1,12 +0,0 @@ -apiVersion: traefik.io/v1alpha1 -kind: IngressRouteTCP -metadata: - name: tcp.route.native-disabled - namespace: default -spec: - routes: - - match: HostSNI(`foo.com`) - services: - - name: native-disabled-svc-tcp - port: 9000 - nativeLB: false diff --git a/pkg/provider/kubernetes/crd/fixtures/udp/services.yml b/pkg/provider/kubernetes/crd/fixtures/udp/services.yml index 8993ca084..f77643d36 100644 --- a/pkg/provider/kubernetes/crd/fixtures/udp/services.yml +++ b/pkg/provider/kubernetes/crd/fixtures/udp/services.yml @@ -252,46 +252,11 @@ metadata: spec: ports: - - name: udp - protocol: UDP + - name: myapp port: 8000 type: ClusterIP clusterIP: 10.10.0.1 ---- -apiVersion: v1 -kind: Service -metadata: - name: native-disabled-svc-udp - namespace: default -spec: - ports: - - name: udp - protocol: UDP - port: 8000 - type: ClusterIP - clusterIP: 10.10.0.1 - ---- -kind: EndpointSlice -apiVersion: discovery.k8s.io/v1 -metadata: - name: native-disabled-udp-abc - namespace: default - labels: - kubernetes.io/service-name: native-disabled-svc-udp -addressType: IPv4 -ports: - - name: udp - protocol: UDP - port: 8000 -endpoints: - - addresses: - - 10.10.0.30 - - 10.10.0.31 - conditions: - ready: true - --- apiVersion: v1 kind: Service diff --git a/pkg/provider/kubernetes/crd/fixtures/udp/with_native_service_lb_disabled.yml b/pkg/provider/kubernetes/crd/fixtures/udp/with_native_service_lb_disabled.yml deleted file mode 100644 index f8db343fa..000000000 --- a/pkg/provider/kubernetes/crd/fixtures/udp/with_native_service_lb_disabled.yml +++ /dev/null @@ -1,11 +0,0 @@ -apiVersion: traefik.io/v1alpha1 -kind: IngressRouteUDP -metadata: - name: udp.route.native-disabled - namespace: default -spec: - routes: - - services: - - name: native-disabled-svc-udp - port: 8000 - nativeLB: false diff --git a/pkg/provider/kubernetes/crd/fixtures/with_native_service_lb_disabled.yml b/pkg/provider/kubernetes/crd/fixtures/with_native_service_lb_disabled.yml deleted file mode 100644 index 07a0accf8..000000000 --- a/pkg/provider/kubernetes/crd/fixtures/with_native_service_lb_disabled.yml +++ /dev/null @@ -1,13 +0,0 @@ -apiVersion: traefik.io/v1alpha1 -kind: IngressRoute -metadata: - name: test.route.native-disabled - namespace: default -spec: - routes: - - match: Host(`foo.com`) - kind: Rule - services: - - name: native-disabled-svc - port: 80 - nativeLB: false diff --git a/pkg/provider/kubernetes/crd/kubernetes.go b/pkg/provider/kubernetes/crd/kubernetes.go index b9f5c0aa5..2b374eeba 100644 --- a/pkg/provider/kubernetes/crd/kubernetes.go +++ b/pkg/provider/kubernetes/crd/kubernetes.go @@ -24,7 +24,7 @@ import ( ptypes "github.com/traefik/paerser/types" "github.com/traefik/traefik/v3/pkg/config/dynamic" "github.com/traefik/traefik/v3/pkg/job" - "github.com/traefik/traefik/v3/pkg/observability/logs" + "github.com/traefik/traefik/v3/pkg/logs" "github.com/traefik/traefik/v3/pkg/provider" traefikv1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1" "github.com/traefik/traefik/v3/pkg/provider/kubernetes/gateway" @@ -490,10 +490,6 @@ func (p *Provider) loadConfigurationFromCRD(ctx context.Context, client Client) } } - if serversTransportTCP.Spec.ProxyProtocol != nil { - tcpServerTransport.ProxyProtocol = serversTransportTCP.Spec.ProxyProtocol - } - if serversTransportTCP.Spec.TLS != nil { if len(serversTransportTCP.Spec.TLS.RootCAsSecrets) > 0 { logger.Warn().Msg("RootCAsSecrets option is deprecated, please use the RootCA option instead.") diff --git a/pkg/provider/kubernetes/crd/kubernetes_http.go b/pkg/provider/kubernetes/crd/kubernetes_http.go index 2e907d6c3..9fca7c1f2 100644 --- a/pkg/provider/kubernetes/crd/kubernetes_http.go +++ b/pkg/provider/kubernetes/crd/kubernetes_http.go @@ -9,9 +9,8 @@ import ( "strings" "github.com/rs/zerolog/log" - ptypes "github.com/traefik/paerser/types" "github.com/traefik/traefik/v3/pkg/config/dynamic" - "github.com/traefik/traefik/v3/pkg/observability/logs" + "github.com/traefik/traefik/v3/pkg/logs" "github.com/traefik/traefik/v3/pkg/provider" traefikv1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1" "github.com/traefik/traefik/v3/pkg/provider/kubernetes/k8s" @@ -374,17 +373,6 @@ func (c configBuilder) buildServersLB(namespace string, svc traefikv1alpha1.Load return nil, err } } - // If the UnhealthyInterval option is not set, we use the Interval option value, - // to check the unhealthy targets as often as the healthy ones. - if svc.HealthCheck.UnhealthyInterval == nil { - lb.HealthCheck.UnhealthyInterval = &lb.HealthCheck.Interval - } else { - var unhealthyInterval ptypes.Duration - if err := unhealthyInterval.Set(svc.HealthCheck.UnhealthyInterval.String()); err != nil { - return nil, err - } - lb.HealthCheck.UnhealthyInterval = &unhealthyInterval - } if svc.HealthCheck.Timeout != nil { if err := lb.HealthCheck.Timeout.Set(svc.HealthCheck.Timeout.String()); err != nil { return nil, err diff --git a/pkg/provider/kubernetes/crd/kubernetes_tcp.go b/pkg/provider/kubernetes/crd/kubernetes_tcp.go index c4c3fa6d4..1eb3eed84 100644 --- a/pkg/provider/kubernetes/crd/kubernetes_tcp.go +++ b/pkg/provider/kubernetes/crd/kubernetes_tcp.go @@ -10,7 +10,7 @@ import ( "github.com/rs/zerolog/log" "github.com/traefik/traefik/v3/pkg/config/dynamic" - "github.com/traefik/traefik/v3/pkg/observability/logs" + "github.com/traefik/traefik/v3/pkg/logs" "github.com/traefik/traefik/v3/pkg/provider" traefikv1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1" "github.com/traefik/traefik/v3/pkg/tls" diff --git a/pkg/provider/kubernetes/crd/kubernetes_test.go b/pkg/provider/kubernetes/crd/kubernetes_test.go index ec6651091..93b43fdfb 100644 --- a/pkg/provider/kubernetes/crd/kubernetes_test.go +++ b/pkg/provider/kubernetes/crd/kubernetes_test.go @@ -1,6 +1,7 @@ package crd import ( + "context" "os" "path/filepath" "strings" @@ -1676,7 +1677,7 @@ func TestLoadIngressRouteTCPs(t *testing.T) { AllowEmptyServices: test.allowEmptyServices, } - conf := p.loadConfigurationFromCRD(t.Context(), client) + conf := p.loadConfigurationFromCRD(context.Background(), client) assert.Equal(t, test.expected, conf) }) } @@ -2646,11 +2647,10 @@ func TestLoadIngressRoutes(t *testing.T) { FlushInterval: ptypes.Duration(100 * time.Millisecond), }, HealthCheck: &dynamic.ServerHealthCheck{ - Path: "/health", - Timeout: 5000000000, - Interval: 15000000000, - UnhealthyInterval: pointer(ptypes.Duration(15000000000)), - FollowRedirects: pointer(true), + Path: "/health", + Timeout: 5000000000, + Interval: 15000000000, + FollowRedirects: pointer(true), }, }, }, @@ -2712,11 +2712,10 @@ func TestLoadIngressRoutes(t *testing.T) { FlushInterval: ptypes.Duration(100 * time.Millisecond), }, HealthCheck: &dynamic.ServerHealthCheck{ - Path: "/health1", - Timeout: 5000000000, - Interval: 15000000000, - UnhealthyInterval: pointer(ptypes.Duration(15000000000)), - FollowRedirects: pointer(true), + Path: "/health1", + Timeout: 5000000000, + Interval: 15000000000, + FollowRedirects: pointer(true), }, }, }, @@ -2733,11 +2732,10 @@ func TestLoadIngressRoutes(t *testing.T) { FlushInterval: ptypes.Duration(100 * time.Millisecond), }, HealthCheck: &dynamic.ServerHealthCheck{ - Path: "/health2", - Timeout: 5000000000, - Interval: 20000000000, - UnhealthyInterval: pointer(ptypes.Duration(20000000000)), - FollowRedirects: pointer(true), + Path: "/health2", + Timeout: 5000000000, + Interval: 20000000000, + FollowRedirects: pointer(true), }, }, }, @@ -2778,11 +2776,10 @@ func TestLoadIngressRoutes(t *testing.T) { FlushInterval: ptypes.Duration(100 * time.Millisecond), }, HealthCheck: &dynamic.ServerHealthCheck{ - Path: "/health1", - Timeout: 5000000000, - Interval: 15000000000, - UnhealthyInterval: pointer(ptypes.Duration(15000000000)), - FollowRedirects: pointer(true), + Path: "/health1", + Timeout: 5000000000, + Interval: 15000000000, + FollowRedirects: pointer(true), }, }, }, @@ -5312,7 +5309,7 @@ func TestLoadIngressRoutes(t *testing.T) { AllowEmptyServices: test.allowEmptyServices, } - conf := p.loadConfigurationFromCRD(t.Context(), client) + conf := p.loadConfigurationFromCRD(context.Background(), client) assert.Equal(t, test.expected, conf) }) } @@ -5388,7 +5385,7 @@ func TestLoadIngressRoutes_multipleEndpointAddresses(t *testing.T) { } p := Provider{} - conf := p.loadConfigurationFromCRD(t.Context(), client) + conf := p.loadConfigurationFromCRD(context.Background(), client) service, ok := conf.HTTP.Services["default-test-route-6b204d94623b3df4370c"] require.True(t, ok) @@ -5903,7 +5900,7 @@ func TestLoadIngressRouteUDPs(t *testing.T) { AllowEmptyServices: test.allowEmptyServices, } - conf := p.loadConfigurationFromCRD(t.Context(), client) + conf := p.loadConfigurationFromCRD(context.Background(), client) assert.Equal(t, test.expected, conf) }) } @@ -7401,7 +7398,7 @@ func TestCrossNamespace(t *testing.T) { p := Provider{AllowCrossNamespace: test.allowCrossNamespace} - conf := p.loadConfigurationFromCRD(t.Context(), client) + conf := p.loadConfigurationFromCRD(context.Background(), client) assert.Equal(t, test.expected, conf) }) } @@ -7671,7 +7668,7 @@ func TestExternalNameService(t *testing.T) { p := Provider{AllowExternalNameServices: test.allowExternalNameService} - conf := p.loadConfigurationFromCRD(t.Context(), client) + conf := p.loadConfigurationFromCRD(context.Background(), client) assert.Equal(t, test.expected, conf) }) } @@ -7778,7 +7775,7 @@ func TestNativeLB(t *testing.T) { LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ { - Address: "10.10.0.1:9000", + Address: "10.10.0.1:8000", Port: "", }, }, @@ -7853,7 +7850,7 @@ func TestNativeLB(t *testing.T) { p := Provider{} - conf := p.loadConfigurationFromCRD(t.Context(), client) + conf := p.loadConfigurationFromCRD(context.Background(), client) assert.Equal(t, test.expected, conf) }) } @@ -8121,7 +8118,7 @@ func TestNodePortLB(t *testing.T) { DisableClusterScopeResources: test.disableClusterScope, } - conf := p.loadConfigurationFromCRD(t.Context(), client) + conf := p.loadConfigurationFromCRD(context.Background(), client) assert.Equal(t, test.expected, conf) }) } @@ -8375,52 +8372,6 @@ func TestGlobalNativeLB(t *testing.T) { TLS: &dynamic.TLSConfiguration{}, }, }, - { - desc: "HTTP with global native Service LB but service reference has nativeLB disabled", - paths: []string{"services.yml", "with_native_service_lb_disabled.yml"}, - NativeLBByDefault: true, - expected: &dynamic.Configuration{ - UDP: &dynamic.UDPConfiguration{ - Routers: map[string]*dynamic.UDPRouter{}, - Services: map[string]*dynamic.UDPService{}, - }, - TCP: &dynamic.TCPConfiguration{ - ServersTransports: map[string]*dynamic.TCPServersTransport{}, - Routers: map[string]*dynamic.TCPRouter{}, - Middlewares: map[string]*dynamic.TCPMiddleware{}, - Services: map[string]*dynamic.TCPService{}, - }, - HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, - Routers: map[string]*dynamic.Router{ - "default-test-route-native-disabled-6f97418635c7e18853da": { - Service: "default-test-route-native-disabled-6f97418635c7e18853da", - Rule: "Host(`foo.com`)", - Priority: 0, - }, - }, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{ - "default-test-route-native-disabled-6f97418635c7e18853da": { - LoadBalancer: &dynamic.ServersLoadBalancer{ - Strategy: dynamic.BalancerStrategyWRR, - ResponseForwarding: &dynamic.ResponseForwarding{FlushInterval: dynamic.DefaultFlushInterval}, - Servers: []dynamic.Server{ - { - URL: "http://10.10.0.20:80", - }, - { - URL: "http://10.10.0.21:80", - }, - }, - PassHostHeader: pointer(true), - }, - }, - }, - }, - TLS: &dynamic.TLSConfiguration{}, - }, - }, { desc: "HTTP with native Service LB in ingressroute", paths: []string{"services.yml", "with_native_service_lb.yml"}, @@ -8494,51 +8445,7 @@ func TestGlobalNativeLB(t *testing.T) { LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ { - Address: "10.10.0.1:9000", - Port: "", - }, - }, - }, - }, - }, - }, - TLS: &dynamic.TLSConfiguration{}, - }, - }, - { - desc: "TCP with global native Service LB but service reference has nativeLB disabled", - paths: []string{"tcp/services.yml", "tcp/with_native_service_lb_disabled.yml"}, - NativeLBByDefault: true, - expected: &dynamic.Configuration{ - UDP: &dynamic.UDPConfiguration{ - Routers: map[string]*dynamic.UDPRouter{}, - Services: map[string]*dynamic.UDPService{}, - }, - HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, - }, - TCP: &dynamic.TCPConfiguration{ - ServersTransports: map[string]*dynamic.TCPServersTransport{}, - Routers: map[string]*dynamic.TCPRouter{ - "default-tcp.route.native-disabled-fdd3e9338e47a45efefc": { - Service: "default-tcp.route.native-disabled-fdd3e9338e47a45efefc", - Rule: "HostSNI(`foo.com`)", - }, - }, - Middlewares: map[string]*dynamic.TCPMiddleware{}, - Services: map[string]*dynamic.TCPService{ - "default-tcp.route.native-disabled-fdd3e9338e47a45efefc": { - LoadBalancer: &dynamic.TCPServersLoadBalancer{ - Servers: []dynamic.TCPServer{ - { - Address: "10.10.0.30:9000", - Port: "", - }, - { - Address: "10.10.0.31:9000", + Address: "10.10.0.1:8000", Port: "", }, }, @@ -8578,7 +8485,7 @@ func TestGlobalNativeLB(t *testing.T) { LoadBalancer: &dynamic.TCPServersLoadBalancer{ Servers: []dynamic.TCPServer{ { - Address: "10.10.0.1:9000", + Address: "10.10.0.1:8000", Port: "", }, }, @@ -8668,49 +8575,6 @@ func TestGlobalNativeLB(t *testing.T) { TLS: &dynamic.TLSConfiguration{}, }, }, - { - desc: "UDP with global native Service LB but service reference has nativeLB disabled", - paths: []string{"udp/services.yml", "udp/with_native_service_lb_disabled.yml"}, - NativeLBByDefault: true, - expected: &dynamic.Configuration{ - UDP: &dynamic.UDPConfiguration{ - Routers: map[string]*dynamic.UDPRouter{ - "default-udp.route.native-disabled-0": { - Service: "default-udp.route.native-disabled-0", - }, - }, - Services: map[string]*dynamic.UDPService{ - "default-udp.route.native-disabled-0": { - LoadBalancer: &dynamic.UDPServersLoadBalancer{ - Servers: []dynamic.UDPServer{ - { - Address: "10.10.0.30:8000", - Port: "", - }, - { - Address: "10.10.0.31:8000", - Port: "", - }, - }, - }, - }, - }, - }, - HTTP: &dynamic.HTTPConfiguration{ - ServersTransports: map[string]*dynamic.ServersTransport{}, - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, - }, - TCP: &dynamic.TCPConfiguration{ - ServersTransports: map[string]*dynamic.TCPServersTransport{}, - Routers: map[string]*dynamic.TCPRouter{}, - Middlewares: map[string]*dynamic.TCPMiddleware{}, - Services: map[string]*dynamic.TCPService{}, - }, - TLS: &dynamic.TLSConfiguration{}, - }, - }, } for _, test := range testCases { @@ -8728,6 +8592,8 @@ func TestGlobalNativeLB(t *testing.T) { objects := k8s.MustParseYaml(yamlContent) for _, obj := range objects { switch o := obj.(type) { + case *corev1.Service, *corev1.Endpoints, *corev1.Secret: + k8sObjects = append(k8sObjects, o) case *traefikv1alpha1.IngressRoute: crdObjects = append(crdObjects, o) case *traefikv1alpha1.IngressRouteTCP: @@ -8743,7 +8609,6 @@ func TestGlobalNativeLB(t *testing.T) { case *traefikv1alpha1.TLSStore: crdObjects = append(crdObjects, o) default: - k8sObjects = append(k8sObjects, o) } } } @@ -8765,7 +8630,7 @@ func TestGlobalNativeLB(t *testing.T) { p := Provider{NativeLBByDefault: test.NativeLBByDefault} - conf := p.loadConfigurationFromCRD(t.Context(), client) + conf := p.loadConfigurationFromCRD(context.Background(), client) assert.Equal(t, test.expected, conf) }) } diff --git a/pkg/provider/kubernetes/crd/traefikio/v1alpha1/ingressroute.go b/pkg/provider/kubernetes/crd/traefikio/v1alpha1/ingressroute.go index 6fda0077d..007859260 100644 --- a/pkg/provider/kubernetes/crd/traefikio/v1alpha1/ingressroute.go +++ b/pkg/provider/kubernetes/crd/traefikio/v1alpha1/ingressroute.go @@ -13,18 +13,18 @@ type IngressRouteSpec struct { Routes []Route `json:"routes"` // EntryPoints defines the list of entry point names to bind to. // Entry points have to be configured in the static configuration. - // More info: https://doc.traefik.io/traefik/v3.5/reference/install-configuration/entrypoints/ + // More info: https://doc.traefik.io/traefik/v3.4/routing/entrypoints/ // Default: all. EntryPoints []string `json:"entryPoints,omitempty"` // TLS defines the TLS configuration. - // More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/routing/router/#tls + // More info: https://doc.traefik.io/traefik/v3.4/routing/routers/#tls TLS *TLS `json:"tls,omitempty"` } // Route holds the HTTP route configuration. type Route struct { // Match defines the router's rule. - // More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/routing/rules-and-priority/ + // More info: https://doc.traefik.io/traefik/v3.4/routing/routers/#rule Match string `json:"match"` // Kind defines the kind of the route. // Rule is the only supported kind. @@ -32,62 +32,62 @@ type Route struct { // +kubebuilder:validation:Enum=Rule Kind string `json:"kind,omitempty"` // Priority defines the router's priority. - // More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/routing/rules-and-priority/#priority + // More info: https://doc.traefik.io/traefik/v3.4/routing/routers/#priority // +kubebuilder:validation:Maximum=9223372036854774807 Priority int `json:"priority,omitempty"` // Syntax defines the router's rule syntax. - // More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/routing/rules-and-priority/#rulesyntax + // More info: https://doc.traefik.io/traefik/v3.4/routing/routers/#rulesyntax // Deprecated: Please do not use this field and rewrite the router rules to use the v3 syntax. Syntax string `json:"syntax,omitempty"` // Services defines the list of Service. // It can contain any combination of TraefikService and/or reference to a Kubernetes Service. Services []Service `json:"services,omitempty"` // Middlewares defines the list of references to Middleware resources. - // More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/kubernetes/crd/http/middleware/ + // More info: https://doc.traefik.io/traefik/v3.4/routing/providers/kubernetes-crd/#kind-middleware Middlewares []MiddlewareRef `json:"middlewares,omitempty"` // Observability defines the observability configuration for a router. - // More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/routing/observability/ + // More info: https://doc.traefik.io/traefik/v3.4/routing/routers/#observability Observability *dynamic.RouterObservabilityConfig `json:"observability,omitempty"` } // TLS holds the TLS configuration. -// More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/tls/overview/ +// More info: https://doc.traefik.io/traefik/v3.4/routing/routers/#tls type TLS struct { // SecretName is the name of the referenced Kubernetes Secret to specify the certificate details. SecretName string `json:"secretName,omitempty"` // Options defines the reference to a TLSOption, that specifies the parameters of the TLS connection. // If not defined, the `default` TLSOption is used. - // More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/tls/tls-options/ + // More info: https://doc.traefik.io/traefik/v3.4/https/tls/#tls-options Options *TLSOptionRef `json:"options,omitempty"` // Store defines the reference to the TLSStore, that will be used to store certificates. // Please note that only `default` TLSStore can be used. Store *TLSStoreRef `json:"store,omitempty"` // CertResolver defines the name of the certificate resolver to use. // Cert resolvers have to be configured in the static configuration. - // More info: https://doc.traefik.io/traefik/v3.5/reference/install-configuration/tls/certificate-resolvers/acme/ + // More info: https://doc.traefik.io/traefik/v3.4/https/acme/#certificate-resolvers CertResolver string `json:"certResolver,omitempty"` // Domains defines the list of domains that will be used to issue certificates. - // More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/tls/tls-certificates/#domains + // More info: https://doc.traefik.io/traefik/v3.4/routing/routers/#domains Domains []types.Domain `json:"domains,omitempty"` } // TLSOptionRef is a reference to a TLSOption resource. type TLSOptionRef struct { // Name defines the name of the referenced TLSOption. - // More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/kubernetes/crd/http/tlsoption/ + // More info: https://doc.traefik.io/traefik/v3.4/routing/providers/kubernetes-crd/#kind-tlsoption Name string `json:"name"` // Namespace defines the namespace of the referenced TLSOption. - // More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/kubernetes/crd/http/tlsoption/ + // More info: https://doc.traefik.io/traefik/v3.4/routing/providers/kubernetes-crd/#kind-tlsoption Namespace string `json:"namespace,omitempty"` } // TLSStoreRef is a reference to a TLSStore resource. type TLSStoreRef struct { // Name defines the name of the referenced TLSStore. - // More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/kubernetes/crd/http/tlsstore/ + // More info: https://doc.traefik.io/traefik/v3.4/routing/providers/kubernetes-crd/#kind-tlsstore Name string `json:"name"` // Namespace defines the namespace of the referenced TLSStore. - // More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/kubernetes/crd/http/tlsstore/ + // More info: https://doc.traefik.io/traefik/v3.4/routing/providers/kubernetes-crd/#kind-tlsstore Namespace string `json:"namespace,omitempty"` } @@ -104,7 +104,7 @@ type LoadBalancerSpec struct { // Namespace defines the namespace of the referenced Kubernetes Service or TraefikService. Namespace string `json:"namespace,omitempty"` // Sticky defines the sticky sessions configuration. - // More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/load-balancing/service/#sticky-sessions + // More info: https://doc.traefik.io/traefik/v3.4/routing/services/#sticky-sessions Sticky *dynamic.Sticky `json:"sticky,omitempty"` // Port defines the port of a Kubernetes Service. // This can be a reference to a named port. @@ -170,13 +170,9 @@ type ServerHealthCheck struct { Status int `json:"status,omitempty"` // Port defines the server URL port for the health check endpoint. Port int `json:"port,omitempty"` - // Interval defines the frequency of the health check calls for healthy targets. + // Interval defines the frequency of the health check calls. // Default: 30s Interval *intstr.IntOrString `json:"interval,omitempty"` - // UnhealthyInterval defines the frequency of the health check calls for unhealthy targets. - // When UnhealthyInterval is not defined, it defaults to the Interval value. - // Default: 30s - UnhealthyInterval *intstr.IntOrString `json:"unhealthyInterval,omitempty"` // Timeout defines the maximum duration Traefik will wait for a health check request before considering the server unhealthy. // Default: 5s Timeout *intstr.IntOrString `json:"timeout,omitempty"` diff --git a/pkg/provider/kubernetes/crd/traefikio/v1alpha1/ingressroutetcp.go b/pkg/provider/kubernetes/crd/traefikio/v1alpha1/ingressroutetcp.go index 5ef08dc02..4e04249f0 100644 --- a/pkg/provider/kubernetes/crd/traefikio/v1alpha1/ingressroutetcp.go +++ b/pkg/provider/kubernetes/crd/traefikio/v1alpha1/ingressroutetcp.go @@ -13,25 +13,25 @@ type IngressRouteTCPSpec struct { Routes []RouteTCP `json:"routes"` // EntryPoints defines the list of entry point names to bind to. // Entry points have to be configured in the static configuration. - // More info: https://doc.traefik.io/traefik/v3.5/reference/install-configuration/entrypoints/ + // More info: https://doc.traefik.io/traefik/v3.4/routing/entrypoints/ // Default: all. EntryPoints []string `json:"entryPoints,omitempty"` // TLS defines the TLS configuration on a layer 4 / TCP Route. - // More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/routing/router/#tls + // More info: https://doc.traefik.io/traefik/v3.4/routing/routers/#tls_1 TLS *TLSTCP `json:"tls,omitempty"` } // RouteTCP holds the TCP route configuration. type RouteTCP struct { // Match defines the router's rule. - // More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/routing/rules-and-priority/ + // More info: https://doc.traefik.io/traefik/v3.4/routing/routers/#rule_1 Match string `json:"match"` // Priority defines the router's priority. - // More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/routing/rules-and-priority/#priority + // More info: https://doc.traefik.io/traefik/v3.4/routing/routers/#priority_1 // +kubebuilder:validation:Maximum=9223372036854774807 Priority int `json:"priority,omitempty"` // Syntax defines the router's rule syntax. - // More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/routing/rules-and-priority/#rulesyntax + // More info: https://doc.traefik.io/traefik/v3.4/routing/routers/#rulesyntax_1 // +kubebuilder:validation:Enum=v3;v2 // Deprecated: Please do not use this field and rewrite the router rules to use the v3 syntax. Syntax string `json:"syntax,omitempty"` @@ -42,7 +42,7 @@ type RouteTCP struct { } // TLSTCP holds the TLS configuration for an IngressRouteTCP. -// More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/tls/ +// More info: https://doc.traefik.io/traefik/v3.4/routing/routers/#tls_1 type TLSTCP struct { // SecretName is the name of the referenced Kubernetes Secret to specify the certificate details. SecretName string `json:"secretName,omitempty"` @@ -50,17 +50,17 @@ type TLSTCP struct { Passthrough bool `json:"passthrough,omitempty"` // Options defines the reference to a TLSOption, that specifies the parameters of the TLS connection. // If not defined, the `default` TLSOption is used. - // More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/tls/#tls-options + // More info: https://doc.traefik.io/traefik/v3.4/https/tls/#tls-options Options *ObjectReference `json:"options,omitempty"` // Store defines the reference to the TLSStore, that will be used to store certificates. // Please note that only `default` TLSStore can be used. Store *ObjectReference `json:"store,omitempty"` // CertResolver defines the name of the certificate resolver to use. // Cert resolvers have to be configured in the static configuration. - // More info: https://doc.traefik.io/traefik/v3.5/reference/install-configuration/tls/certificate-resolvers/acme/ + // More info: https://doc.traefik.io/traefik/v3.4/https/acme/#certificate-resolvers CertResolver string `json:"certResolver,omitempty"` // Domains defines the list of domains that will be used to issue certificates. - // More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/tls/#domains + // More info: https://doc.traefik.io/traefik/v3.4/routing/routers/#domains Domains []types.Domain `json:"domains,omitempty"` } @@ -85,8 +85,7 @@ type ServiceTCP struct { // Deprecated: TerminationDelay will not be supported in future APIVersions, please use ServersTransport to configure the TerminationDelay instead. TerminationDelay *int `json:"terminationDelay,omitempty"` // ProxyProtocol defines the PROXY protocol configuration. - // More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/service/#proxy-protocol - // Deprecated: ProxyProtocol will not be supported in future APIVersions, please use ServersTransport to configure ProxyProtocol instead. + // More info: https://doc.traefik.io/traefik/v3.4/routing/services/#proxy-protocol ProxyProtocol *dynamic.ProxyProtocol `json:"proxyProtocol,omitempty"` // ServersTransport defines the name of ServersTransportTCP resource to use. // It allows to configure the transport between Traefik and your servers. diff --git a/pkg/provider/kubernetes/crd/traefikio/v1alpha1/ingressrouteudp.go b/pkg/provider/kubernetes/crd/traefikio/v1alpha1/ingressrouteudp.go index 6e0038843..3df06d203 100644 --- a/pkg/provider/kubernetes/crd/traefikio/v1alpha1/ingressrouteudp.go +++ b/pkg/provider/kubernetes/crd/traefikio/v1alpha1/ingressrouteudp.go @@ -11,7 +11,7 @@ type IngressRouteUDPSpec struct { Routes []RouteUDP `json:"routes"` // EntryPoints defines the list of entry point names to bind to. // Entry points have to be configured in the static configuration. - // More info: https://doc.traefik.io/traefik/v3.5/reference/install-configuration/entrypoints/ + // More info: https://doc.traefik.io/traefik/v3.4/routing/entrypoints/ // Default: all. EntryPoints []string `json:"entryPoints,omitempty"` } diff --git a/pkg/provider/kubernetes/crd/traefikio/v1alpha1/middleware.go b/pkg/provider/kubernetes/crd/traefikio/v1alpha1/middleware.go index 32354e2b5..a64ae9579 100644 --- a/pkg/provider/kubernetes/crd/traefikio/v1alpha1/middleware.go +++ b/pkg/provider/kubernetes/crd/traefikio/v1alpha1/middleware.go @@ -12,7 +12,7 @@ import ( // +kubebuilder:storageversion // Middleware is the CRD implementation of a Traefik Middleware. -// More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/overview/ +// More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/overview/ type Middleware struct { metav1.TypeMeta `json:",inline"` // Standard object's metadata. @@ -52,7 +52,7 @@ type MiddlewareSpec struct { ContentType *dynamic.ContentType `json:"contentType,omitempty"` GrpcWeb *dynamic.GrpcWeb `json:"grpcWeb,omitempty"` // Plugin defines the middleware plugin configuration. - // More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/overview/#community-middlewares + // More info: https://doc.traefik.io/traefik/plugins/ Plugin map[string]apiextensionv1.JSON `json:"plugin,omitempty"` } @@ -60,7 +60,7 @@ type MiddlewareSpec struct { // ErrorPage holds the custom error middleware configuration. // This middleware returns a custom page in lieu of the default, according to configured ranges of HTTP Status codes. -// More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/errorpages/ +// More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/errorpages/ type ErrorPage struct { // Status defines which status or range of statuses should result in an error page. // It can be either a status code as a number (500), @@ -73,7 +73,7 @@ type ErrorPage struct { // For example: "418": 404 or "410-418": 404 StatusRewrites map[string]int `json:"statusRewrites,omitempty"` // Service defines the reference to a Kubernetes Service that will serve the error page. - // More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/errorpages/#service + // More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/errorpages/#service Service Service `json:"service,omitempty"` // Query defines the URL for the error page (hosted by service). // The {status} variable can be used in order to insert the status code in the URL. @@ -108,7 +108,7 @@ type CircuitBreaker struct { // Chain holds the configuration of the chain middleware. // This middleware enables to define reusable combinations of other pieces of middleware. -// More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/chain/ +// More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/chain/ type Chain struct { // Middlewares is the list of MiddlewareRef which composes the chain. Middlewares []MiddlewareRef `json:"middlewares,omitempty"` @@ -118,7 +118,7 @@ type Chain struct { // BasicAuth holds the basic auth middleware configuration. // This middleware restricts access to your services to known users. -// More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/basicauth/ +// More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/basicauth/ type BasicAuth struct { // Secret is the name of the referenced Kubernetes Secret containing user credentials. Secret string `json:"secret,omitempty"` @@ -129,7 +129,7 @@ type BasicAuth struct { // Default: false. RemoveHeader bool `json:"removeHeader,omitempty"` // HeaderField defines a header field to store the authenticated user. - // More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/basicauth/#headerfield + // More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/basicauth/#headerfield HeaderField string `json:"headerField,omitempty"` } @@ -137,7 +137,7 @@ type BasicAuth struct { // DigestAuth holds the digest auth middleware configuration. // This middleware restricts access to your services to known users. -// More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/digestauth/ +// More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/digestauth/ type DigestAuth struct { // Secret is the name of the referenced Kubernetes Secret containing user credentials. Secret string `json:"secret,omitempty"` @@ -147,7 +147,7 @@ type DigestAuth struct { // Default: traefik. Realm string `json:"realm,omitempty"` // HeaderField defines a header field to store the authenticated user. - // More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/digestauth/#headerfield + // More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/basicauth/#headerfield HeaderField string `json:"headerField,omitempty"` } @@ -155,7 +155,7 @@ type DigestAuth struct { // ForwardAuth holds the forward auth middleware configuration. // This middleware delegates the request authentication to a Service. -// More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/forwardauth/ +// More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/forwardauth/ type ForwardAuth struct { // Address defines the authentication server address. Address string `json:"address,omitempty"` @@ -164,7 +164,7 @@ type ForwardAuth struct { // AuthResponseHeaders defines the list of headers to copy from the authentication server response and set on forwarded request, replacing any existing conflicting headers. AuthResponseHeaders []string `json:"authResponseHeaders,omitempty"` // AuthResponseHeadersRegex defines the regex to match headers to copy from the authentication server response and set on forwarded request, after stripping all headers that match the regex. - // More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/forwardauth/#authresponseheadersregex + // More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/forwardauth/#authresponseheadersregex AuthResponseHeadersRegex string `json:"authResponseHeadersRegex,omitempty"` // AuthRequestHeaders defines the list of the headers to copy from the request to the authentication server. // If not set or empty then all request headers are passed. @@ -174,7 +174,7 @@ type ForwardAuth struct { // AddAuthCookiesToResponse defines the list of cookies to copy from the authentication server response to the response. AddAuthCookiesToResponse []string `json:"addAuthCookiesToResponse,omitempty"` // HeaderField defines a header field to store the authenticated user. - // More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/forwardauth/#headerfield + // More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/forwardauth/#headerfield HeaderField string `json:"headerField,omitempty"` // ForwardBody defines whether to send the request body to the authentication server. ForwardBody bool `json:"forwardBody,omitempty"` @@ -201,7 +201,7 @@ type ClientTLSWithCAOptional struct { // RateLimit holds the rate limit configuration. // This middleware ensures that services will receive a fair amount of requests, and allows one to define what fair is. -// More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/ratelimit/ +// More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/ratelimit/ type RateLimit struct { // Average is the maximum rate, by default in requests/s, allowed for the given source. // It defaults to 0, which means no rate limiting. @@ -286,7 +286,7 @@ type ClientTLS struct { // Compress holds the compress middleware configuration. // This middleware compresses responses before sending them to the client, using gzip, brotli, or zstd compression. -// More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/compress/ +// More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/compress/ type Compress struct { // ExcludedContentTypes defines the list of content types to compare the Content-Type header of the incoming requests and responses before compressing. // `application/grpc` is always excluded. @@ -308,7 +308,7 @@ type Compress struct { // Retry holds the retry middleware configuration. // This middleware reissues requests a given number of times to a backend server if that server does not reply. // As soon as the server answers, the middleware stops retrying, regardless of the response status. -// More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/retry/ +// More info: https://doc.traefik.io/traefik/v3.4/middlewares/http/retry/ type Retry struct { // Attempts defines how many times the request should be retried. // +kubebuilder:validation:Minimum=0 diff --git a/pkg/provider/kubernetes/crd/traefikio/v1alpha1/middlewaretcp.go b/pkg/provider/kubernetes/crd/traefikio/v1alpha1/middlewaretcp.go index 49309b02c..544976bca 100644 --- a/pkg/provider/kubernetes/crd/traefikio/v1alpha1/middlewaretcp.go +++ b/pkg/provider/kubernetes/crd/traefikio/v1alpha1/middlewaretcp.go @@ -9,7 +9,7 @@ import ( // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // MiddlewareTCP is the CRD implementation of a Traefik TCP middleware. -// More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/middlewares/overview/ +// More info: https://doc.traefik.io/traefik/v3.4/middlewares/overview/ type MiddlewareTCP struct { metav1.TypeMeta `json:",inline"` // Standard object's metadata. @@ -28,11 +28,11 @@ type MiddlewareTCPSpec struct { // IPWhiteList defines the IPWhiteList middleware configuration. // This middleware accepts/refuses connections based on the client IP. // Deprecated: please use IPAllowList instead. - // More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/middlewares/ipwhitelist/ + // More info: https://doc.traefik.io/traefik/v3.4/middlewares/tcp/ipwhitelist/ IPWhiteList *dynamic.TCPIPWhiteList `json:"ipWhiteList,omitempty"` // IPAllowList defines the IPAllowList middleware configuration. // This middleware accepts/refuses connections based on the client IP. - // More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/middlewares/ipallowlist/ + // More info: https://doc.traefik.io/traefik/v3.4/middlewares/tcp/ipallowlist/ IPAllowList *dynamic.TCPIPAllowList `json:"ipAllowList,omitempty"` } diff --git a/pkg/provider/kubernetes/crd/traefikio/v1alpha1/serverstransport.go b/pkg/provider/kubernetes/crd/traefikio/v1alpha1/serverstransport.go index f797671fc..5c7773895 100644 --- a/pkg/provider/kubernetes/crd/traefikio/v1alpha1/serverstransport.go +++ b/pkg/provider/kubernetes/crd/traefikio/v1alpha1/serverstransport.go @@ -13,7 +13,7 @@ import ( // ServersTransport is the CRD implementation of a ServersTransport. // If no serversTransport is specified, the default@internal will be used. // The default@internal serversTransport is created from the static configuration. -// More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/load-balancing/serverstransport/ +// More info: https://doc.traefik.io/traefik/v3.4/routing/services/#serverstransport_1 type ServersTransport struct { metav1.TypeMeta `json:",inline"` // Standard object's metadata. @@ -39,7 +39,7 @@ type ServersTransportSpec struct { // CertificatesSecrets defines a list of secret storing client certificates for mTLS. CertificatesSecrets []string `json:"certificatesSecrets,omitempty"` // MaxIdleConnsPerHost controls the maximum idle (keep-alive) to keep per-host. - // +kubebuilder:validation:Minimum=-1 + // +kubebuilder:validation:Minimum=0 MaxIdleConnsPerHost int `json:"maxIdleConnsPerHost,omitempty"` // ForwardingTimeouts defines the timeouts for requests forwarded to the backend servers. ForwardingTimeouts *ForwardingTimeouts `json:"forwardingTimeouts,omitempty"` diff --git a/pkg/provider/kubernetes/crd/traefikio/v1alpha1/serverstransporttcp.go b/pkg/provider/kubernetes/crd/traefikio/v1alpha1/serverstransporttcp.go index dfb2299be..e4b3d6744 100644 --- a/pkg/provider/kubernetes/crd/traefikio/v1alpha1/serverstransporttcp.go +++ b/pkg/provider/kubernetes/crd/traefikio/v1alpha1/serverstransporttcp.go @@ -13,7 +13,7 @@ import ( // ServersTransportTCP is the CRD implementation of a TCPServersTransport. // If no tcpServersTransport is specified, a default one named default@internal will be used. // The default@internal tcpServersTransport can be configured in the static configuration. -// More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/serverstransport/ +// More info: https://doc.traefik.io/traefik/v3.4/routing/services/#serverstransport_3 type ServersTransportTCP struct { metav1.TypeMeta `json:",inline"` // Standard object's metadata. @@ -35,14 +35,12 @@ type ServersTransportTCPSpec struct { // +kubebuilder:validation:Pattern="^([0-9]+(ns|us|Âĩs|ms|s|m|h)?)+$" // +kubebuilder:validation:XIntOrString DialKeepAlive *intstr.IntOrString `json:"dialKeepAlive,omitempty"` - // ProxyProtocol holds the PROXY Protocol configuration. - ProxyProtocol *dynamic.ProxyProtocol `json:"proxyProtocol,omitempty"` // TerminationDelay defines the delay to wait before fully terminating the connection, after one connected peer has closed its writing capability. // +kubebuilder:validation:Pattern="^([0-9]+(ns|us|Âĩs|ms|s|m|h)?)+$" // +kubebuilder:validation:XIntOrString TerminationDelay *intstr.IntOrString `json:"terminationDelay,omitempty"` // TLS defines the TLS configuration - TLS *TLSClientConfig `json:"tls,omitempty"` + TLS *TLSClientConfig `description:"Defines the TLS configuration." json:"tls,omitempty"` } // TLSClientConfig defines the desired state of a TLSClientConfig. diff --git a/pkg/provider/kubernetes/crd/traefikio/v1alpha1/service.go b/pkg/provider/kubernetes/crd/traefikio/v1alpha1/service.go index 8adce08ef..009da340e 100644 --- a/pkg/provider/kubernetes/crd/traefikio/v1alpha1/service.go +++ b/pkg/provider/kubernetes/crd/traefikio/v1alpha1/service.go @@ -13,7 +13,7 @@ import ( // TraefikService object allows to: // - Apply weight to Services on load-balancing // - Mirror traffic on services -// More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/kubernetes/crd/http/traefikservice/ +// More info: https://doc.traefik.io/traefik/v3.4/routing/providers/kubernetes-crd/#kind-traefikservice type TraefikService struct { metav1.TypeMeta `json:",inline"` // Standard object's metadata. @@ -49,7 +49,7 @@ type TraefikServiceSpec struct { // +k8s:deepcopy-gen=true // Mirroring holds the mirroring service configuration. -// More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/load-balancing/service/#mirroring +// More info: https://doc.traefik.io/traefik/v3.4/routing/services/#mirroring-service type Mirroring struct { LoadBalancerSpec `json:",inline"` @@ -78,11 +78,11 @@ type MirrorService struct { // +k8s:deepcopy-gen=true // WeightedRoundRobin holds the weighted round-robin configuration. -// More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/load-balancing/service/#weighted-round-robin-wrr +// More info: https://doc.traefik.io/traefik/v3.4/routing/services/#weighted-round-robin-service type WeightedRoundRobin struct { // Services defines the list of Kubernetes Service and/or TraefikService to load-balance, with weight. Services []Service `json:"services,omitempty"` // Sticky defines whether sticky sessions are enabled. - // More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/kubernetes/crd/http/traefikservice/#stickiness-and-load-balancing + // More info: https://doc.traefik.io/traefik/v3.4/routing/providers/kubernetes-crd/#stickiness-and-load-balancing Sticky *dynamic.Sticky `json:"sticky,omitempty"` } diff --git a/pkg/provider/kubernetes/crd/traefikio/v1alpha1/tlsoption.go b/pkg/provider/kubernetes/crd/traefikio/v1alpha1/tlsoption.go index 07ab9954d..af8aceeb5 100644 --- a/pkg/provider/kubernetes/crd/traefikio/v1alpha1/tlsoption.go +++ b/pkg/provider/kubernetes/crd/traefikio/v1alpha1/tlsoption.go @@ -9,7 +9,7 @@ import ( // +kubebuilder:storageversion // TLSOption is the CRD implementation of a Traefik TLS Option, allowing to configure some parameters of the TLS connection. -// More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/tls/tls-certificates/#certificates-stores#tls-options +// More info: https://doc.traefik.io/traefik/v3.4/https/tls/#tls-options type TLSOption struct { metav1.TypeMeta `json:",inline"` // Standard object's metadata. @@ -32,17 +32,17 @@ type TLSOptionSpec struct { // Default: None. MaxVersion string `json:"maxVersion,omitempty"` // CipherSuites defines the list of supported cipher suites for TLS versions up to TLS 1.2. - // More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/tls/tls-certificates/#certificates-stores#cipher-suites + // More info: https://doc.traefik.io/traefik/v3.4/https/tls/#cipher-suites CipherSuites []string `json:"cipherSuites,omitempty"` - // CurvePreferences defines the preferred elliptic curves. - // More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/tls/tls-certificates/#certificates-stores#curve-preferences + // CurvePreferences defines the preferred elliptic curves in a specific order. + // More info: https://doc.traefik.io/traefik/v3.4/https/tls/#curve-preferences CurvePreferences []string `json:"curvePreferences,omitempty"` // ClientAuth defines the server's policy for TLS Client Authentication. ClientAuth ClientAuth `json:"clientAuth,omitempty"` // SniStrict defines whether Traefik allows connections from clients connections that do not specify a server_name extension. SniStrict bool `json:"sniStrict,omitempty"` // ALPNProtocols defines the list of supported application level protocols for the TLS handshake, in order of preference. - // More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/tls/tls-certificates/#certificates-stores#alpn-protocols + // More info: https://doc.traefik.io/traefik/v3.4/https/tls/#alpn-protocols ALPNProtocols []string `json:"alpnProtocols,omitempty"` // DisableSessionTickets disables TLS session resumption via session tickets. DisableSessionTickets bool `json:"disableSessionTickets,omitempty"` diff --git a/pkg/provider/kubernetes/crd/traefikio/v1alpha1/tlsstore.go b/pkg/provider/kubernetes/crd/traefikio/v1alpha1/tlsstore.go index 23a5d1a9a..eac007778 100644 --- a/pkg/provider/kubernetes/crd/traefikio/v1alpha1/tlsstore.go +++ b/pkg/provider/kubernetes/crd/traefikio/v1alpha1/tlsstore.go @@ -12,7 +12,7 @@ import ( // TLSStore is the CRD implementation of a Traefik TLS Store. // For the time being, only the TLSStore named default is supported. // This means that you cannot have two stores that are named default in different Kubernetes namespaces. -// More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/tls/tls-certificates/#certificates-stores#certificates-stores +// More info: https://doc.traefik.io/traefik/v3.4/https/tls/#certificates-stores type TLSStore struct { metav1.TypeMeta `json:",inline"` // Standard object's metadata. diff --git a/pkg/provider/kubernetes/crd/traefikio/v1alpha1/zz_generated.deepcopy.go b/pkg/provider/kubernetes/crd/traefikio/v1alpha1/zz_generated.deepcopy.go index c4f898635..f975a8bfb 100644 --- a/pkg/provider/kubernetes/crd/traefikio/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/provider/kubernetes/crd/traefikio/v1alpha1/zz_generated.deepcopy.go @@ -1280,11 +1280,6 @@ func (in *ServerHealthCheck) DeepCopyInto(out *ServerHealthCheck) { *out = new(intstr.IntOrString) **out = **in } - if in.UnhealthyInterval != nil { - in, out := &in.UnhealthyInterval, &out.UnhealthyInterval - *out = new(intstr.IntOrString) - **out = **in - } if in.Timeout != nil { in, out := &in.Timeout, &out.Timeout *out = new(intstr.IntOrString) @@ -1491,11 +1486,6 @@ func (in *ServersTransportTCPSpec) DeepCopyInto(out *ServersTransportTCPSpec) { *out = new(intstr.IntOrString) **out = **in } - if in.ProxyProtocol != nil { - in, out := &in.ProxyProtocol, &out.ProxyProtocol - *out = new(dynamic.ProxyProtocol) - **out = **in - } if in.TerminationDelay != nil { in, out := &in.TerminationDelay, &out.TerminationDelay *out = new(intstr.IntOrString) diff --git a/pkg/provider/kubernetes/gateway/annotations.go b/pkg/provider/kubernetes/gateway/annotations.go index b17cc3cb1..8e82ae603 100644 --- a/pkg/provider/kubernetes/gateway/annotations.go +++ b/pkg/provider/kubernetes/gateway/annotations.go @@ -16,7 +16,7 @@ type ServiceConfig struct { // Service is the service's configuration from annotations. type Service struct { - NativeLB *bool `json:"nativeLB"` + NativeLB bool `json:"nativeLB"` } func parseServiceAnnotations(annotations map[string]string) (ServiceConfig, error) { diff --git a/pkg/provider/kubernetes/gateway/annotations_test.go b/pkg/provider/kubernetes/gateway/annotations_test.go index 5ca8c994d..80537526a 100644 --- a/pkg/provider/kubernetes/gateway/annotations_test.go +++ b/pkg/provider/kubernetes/gateway/annotations_test.go @@ -5,7 +5,6 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "k8s.io/utils/ptr" ) func Test_parseServiceConfig(t *testing.T) { @@ -23,7 +22,7 @@ func Test_parseServiceConfig(t *testing.T) { }, expected: ServiceConfig{ Service: Service{ - NativeLB: ptr.To(true), + NativeLB: true, }, }, }, diff --git a/pkg/provider/kubernetes/gateway/client.go b/pkg/provider/kubernetes/gateway/client.go index 109a0f9d7..6c704e99e 100644 --- a/pkg/provider/kubernetes/gateway/client.go +++ b/pkg/provider/kubernetes/gateway/client.go @@ -10,7 +10,6 @@ import ( "time" "github.com/rs/zerolog/log" - "github.com/traefik/traefik/v3/pkg/provider/kubernetes/k8s" "github.com/traefik/traefik/v3/pkg/types" corev1 "k8s.io/api/core/v1" discoveryv1 "k8s.io/api/discovery/v1" @@ -34,6 +33,22 @@ import ( const resyncPeriod = 10 * time.Minute +type resourceEventHandler struct { + ev chan<- interface{} +} + +func (reh *resourceEventHandler) OnAdd(obj interface{}, _ bool) { + eventHandlerFunc(reh.ev, obj) +} + +func (reh *resourceEventHandler) OnUpdate(_, newObj interface{}) { + eventHandlerFunc(reh.ev, newObj) +} + +func (reh *resourceEventHandler) OnDelete(obj interface{}) { + eventHandlerFunc(reh.ev, obj) +} + type clientWrapper struct { csGateway gateclientset.Interface csKube kclientset.Interface @@ -130,7 +145,7 @@ func newExternalClusterClient(endpoint, caFilePath string, token types.FileOrCon // WatchAll starts namespace-specific controllers for all relevant kinds. func (c *clientWrapper) WatchAll(namespaces []string, stopCh <-chan struct{}) (<-chan interface{}, error) { eventCh := make(chan interface{}, 1) - eventHandler := &k8s.ResourceEventHandler{Ev: eventCh} + eventHandler := &resourceEventHandler{ev: eventCh} if len(namespaces) == 0 { namespaces = []string{metav1.NamespaceAll} @@ -800,6 +815,16 @@ func (c *clientWrapper) isWatchedNamespace(namespace string) bool { return slices.Contains(c.watchedNamespaces, namespace) } +// eventHandlerFunc will pass the obj on to the events channel or drop it. +// This is so passing the events along won't block in the case of high volume. +// The events are only used for signaling anyway so dropping a few is ok. +func eventHandlerFunc(events chan<- interface{}, obj interface{}) { + select { + case events <- obj: + default: + } +} + // translateNotFoundError will translate a "not found" error to a boolean return // value which indicates if the resource exists and a nil error. func translateNotFoundError(err error) (bool, error) { diff --git a/pkg/provider/kubernetes/gateway/fixtures/httproute/simple_nativelb_disabled.yml b/pkg/provider/kubernetes/gateway/fixtures/httproute/simple_nativelb_disabled.yml deleted file mode 100644 index 989de4446..000000000 --- a/pkg/provider/kubernetes/gateway/fixtures/httproute/simple_nativelb_disabled.yml +++ /dev/null @@ -1,51 +0,0 @@ ---- -kind: GatewayClass -apiVersion: gateway.networking.k8s.io/v1 -metadata: - name: my-gateway-class -spec: - controllerName: traefik.io/gateway-controller - ---- -kind: Gateway -apiVersion: gateway.networking.k8s.io/v1 -metadata: - name: my-gateway - namespace: default -spec: - gatewayClassName: my-gateway-class - listeners: # Use GatewayClass defaults for listener definition. - - name: http - protocol: HTTP - port: 80 - allowedRoutes: - kinds: - - kind: HTTPRoute - group: gateway.networking.k8s.io - namespaces: - from: Same - ---- -kind: HTTPRoute -apiVersion: gateway.networking.k8s.io/v1 -metadata: - name: http-app-1 - namespace: default -spec: - parentRefs: - - name: my-gateway - kind: Gateway - group: gateway.networking.k8s.io - hostnames: - - "foo.com" - rules: - - matches: - - path: - type: Exact - value: /bar - backendRefs: - - name: whoami-native-disabled - port: 80 - weight: 1 - kind: Service - group: "" diff --git a/pkg/provider/kubernetes/gateway/fixtures/httproute/with_app_protocol_service.yml b/pkg/provider/kubernetes/gateway/fixtures/httproute/with_app_protocol_service.yml index 4f51360a0..c364c8f52 100644 --- a/pkg/provider/kubernetes/gateway/fixtures/httproute/with_app_protocol_service.yml +++ b/pkg/provider/kubernetes/gateway/fixtures/httproute/with_app_protocol_service.yml @@ -59,13 +59,3 @@ spec: weight: 1 kind: Service group: "" - - name: whoami-HTTP - port: 80 - weight: 1 - kind: Service - group: "" - - name: whoami-HTTPS - port: 443 - weight: 1 - kind: Service - group: "" diff --git a/pkg/provider/kubernetes/gateway/fixtures/services.yml b/pkg/provider/kubernetes/gateway/fixtures/services.yml index 143dc8656..65376431b 100644 --- a/pkg/provider/kubernetes/gateway/fixtures/services.yml +++ b/pkg/provider/kubernetes/gateway/fixtures/services.yml @@ -430,80 +430,6 @@ spec: name: wss appProtocol: kubernetes.io/wss ---- -kind: EndpointSlice -apiVersion: discovery.k8s.io/v1 -metadata: - name: whoami-HTTPS-abc - namespace: default - labels: - kubernetes.io/service-name: whoami-HTTPS - -addressType: IPv4 -ports: - - name: websecure - port: 8443 -endpoints: - - addresses: - - 10.10.0.16 - conditions: - ready: true - ---- -apiVersion: v1 -kind: Service -metadata: - name: whoami-HTTPS - namespace: default - -spec: - ports: - - name: websecure - protocol: TCP - appProtocol: HTTPS - port: 443 - targetPort: websecure - selector: - app: containous - task: whoami-HTTPS - ---- -kind: EndpointSlice -apiVersion: discovery.k8s.io/v1 -metadata: - name: whoami-HTTP-abc - namespace: default - labels: - kubernetes.io/service-name: whoami-HTTP - -addressType: IPv4 -ports: - - name: web - port: 8080 -endpoints: - - addresses: - - 10.10.0.17 - conditions: - ready: true - ---- -apiVersion: v1 -kind: Service -metadata: - name: whoami-HTTP - namespace: default - -spec: - ports: - - name: web - protocol: TCP - port: 80 - appProtocol: HTTP - targetPort: web - selector: - app: containous - task: whoami-HTTP - --- apiVersion: v1 kind: Service @@ -545,82 +471,3 @@ spec: - protocol: TCP port: 10000 name: tcp-2 - ---- -apiVersion: v1 -kind: Service -metadata: - name: whoami-native-disabled - namespace: default - annotations: - traefik.io/service.nativelb: "false" -spec: - clusterIP: 10.10.10.2 - ports: - - name: web - protocol: TCP - port: 80 - targetPort: web - selector: - app: containous - task: whoami - ---- -kind: EndpointSlice -apiVersion: discovery.k8s.io/v1 -metadata: - name: whoami-native-disabled-abc - namespace: default - labels: - kubernetes.io/service-name: whoami-native-disabled -addressType: IPv4 -ports: - - name: web - port: 80 -endpoints: - - addresses: - - 10.10.0.20 - - 10.10.0.21 - conditions: - ready: true - ---- -apiVersion: v1 -kind: Service -metadata: - name: whoamitcp-native-disabled - namespace: default - annotations: - traefik.io/service.nativelb: "false" -spec: - clusterIP: 10.10.10.3 - ports: - - protocol: TCP - port: 9000 - name: tcp-1 - - protocol: TCP - port: 10000 - name: tcp-2 - ---- -kind: EndpointSlice -apiVersion: discovery.k8s.io/v1 -metadata: - name: whoamitcp-native-disabled-abc - namespace: default - labels: - kubernetes.io/service-name: whoamitcp-native-disabled -addressType: IPv4 -ports: - - name: tcp-1 - protocol: TCP - port: 9000 - - name: tcp-2 - protocol: TCP - port: 10000 -endpoints: - - addresses: - - 10.10.0.30 - - 10.10.0.31 - conditions: - ready: true diff --git a/pkg/provider/kubernetes/gateway/fixtures/tcproute/simple_nativelb_disabled.yml b/pkg/provider/kubernetes/gateway/fixtures/tcproute/simple_nativelb_disabled.yml deleted file mode 100644 index 15f2518fd..000000000 --- a/pkg/provider/kubernetes/gateway/fixtures/tcproute/simple_nativelb_disabled.yml +++ /dev/null @@ -1,46 +0,0 @@ ---- -kind: GatewayClass -apiVersion: gateway.networking.k8s.io/v1 -metadata: - name: my-gateway-class - namespace: default -spec: - controllerName: traefik.io/gateway-controller - ---- -kind: Gateway -apiVersion: gateway.networking.k8s.io/v1 -metadata: - name: my-tcp-gateway - namespace: default -spec: - gatewayClassName: my-gateway-class - listeners: # Use GatewayClass defaults for listener definition. - - name: tcp - protocol: TCP - port: 9000 - allowedRoutes: - namespaces: - from: Same - kinds: - - kind: TCPRoute - group: gateway.networking.k8s.io - ---- -kind: TCPRoute -apiVersion: gateway.networking.k8s.io/v1alpha2 -metadata: - name: tcp-app-1 - namespace: default -spec: - parentRefs: - - name: my-tcp-gateway - kind: Gateway - group: gateway.networking.k8s.io - rules: - - backendRefs: - - name: whoamitcp-native-disabled - port: 9000 - weight: 1 - kind: Service - group: "" \ No newline at end of file diff --git a/pkg/provider/kubernetes/gateway/grpcroute.go b/pkg/provider/kubernetes/gateway/grpcroute.go index 39b84181a..ecf208568 100644 --- a/pkg/provider/kubernetes/gateway/grpcroute.go +++ b/pkg/provider/kubernetes/gateway/grpcroute.go @@ -430,7 +430,7 @@ func getGRPCServiceProtocol(portSpec corev1.ServicePort) (string, error) { return schemeH2C, nil } - switch ap := strings.ToLower(*portSpec.AppProtocol); ap { + switch ap := *portSpec.AppProtocol; ap { case appProtocolH2C: return schemeH2C, nil case appProtocolHTTPS: diff --git a/pkg/provider/kubernetes/gateway/httproute.go b/pkg/provider/kubernetes/gateway/httproute.go index ebee5fd1f..8af4cadd4 100644 --- a/pkg/provider/kubernetes/gateway/httproute.go +++ b/pkg/provider/kubernetes/gateway/httproute.go @@ -817,7 +817,7 @@ func getHTTPServiceProtocol(portSpec corev1.ServicePort) (string, error) { return protocol, nil } - switch ap := strings.ToLower(*portSpec.AppProtocol); ap { + switch ap := *portSpec.AppProtocol; ap { case appProtocolH2C: return schemeH2C, nil case appProtocolHTTP, appProtocolWS: diff --git a/pkg/provider/kubernetes/gateway/kubernetes.go b/pkg/provider/kubernetes/gateway/kubernetes.go index 3010f6bc0..1f31045ca 100644 --- a/pkg/provider/kubernetes/gateway/kubernetes.go +++ b/pkg/provider/kubernetes/gateway/kubernetes.go @@ -19,7 +19,7 @@ import ( ptypes "github.com/traefik/paerser/types" "github.com/traefik/traefik/v3/pkg/config/dynamic" "github.com/traefik/traefik/v3/pkg/job" - "github.com/traefik/traefik/v3/pkg/observability/logs" + "github.com/traefik/traefik/v3/pkg/logs" traefikv1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1" "github.com/traefik/traefik/v3/pkg/provider/kubernetes/k8s" "github.com/traefik/traefik/v3/pkg/safe" @@ -918,12 +918,7 @@ func (p *Provider) getBackendAddresses(namespace string, ref gatev1.BackendRef) return nil, corev1.ServicePort{}, fmt.Errorf("parsing service annotations config: %w", err) } - nativeLB := p.NativeLBByDefault - if annotationsConfig.Service.NativeLB != nil { - nativeLB = *annotationsConfig.Service.NativeLB - } - - if nativeLB { + if p.NativeLBByDefault || annotationsConfig.Service.NativeLB { if service.Spec.ClusterIP == "" || service.Spec.ClusterIP == "None" { return nil, corev1.ServicePort{}, fmt.Errorf("no clusterIP found for service: %s/%s", service.Namespace, service.Name) } diff --git a/pkg/provider/kubernetes/gateway/kubernetes_test.go b/pkg/provider/kubernetes/gateway/kubernetes_test.go index 90e93f64f..70000cfd2 100644 --- a/pkg/provider/kubernetes/gateway/kubernetes_test.go +++ b/pkg/provider/kubernetes/gateway/kubernetes_test.go @@ -1,6 +1,7 @@ package gateway import ( + "context" "errors" "net/http" "os" @@ -74,14 +75,14 @@ func TestGatewayClassLabelSelector(t *testing.T) { client: client, } - _ = p.loadConfigurationFromGateways(t.Context()) + _ = p.loadConfigurationFromGateways(context.Background()) - gw, err := gwClient.GatewayV1().Gateways("default").Get(t.Context(), "traefik-external", metav1.GetOptions{}) + gw, err := gwClient.GatewayV1().Gateways("default").Get(context.Background(), "traefik-external", metav1.GetOptions{}) require.NoError(t, err) assert.Empty(t, gw.Status.Addresses) - gw, err = gwClient.GatewayV1().Gateways("default").Get(t.Context(), "traefik-internal", metav1.GetOptions{}) + gw, err = gwClient.GatewayV1().Gateways("default").Get(context.Background(), "traefik-internal", metav1.GetOptions{}) require.NoError(t, err) require.Len(t, gw.Status.Addresses, 1) require.NotNil(t, gw.Status.Addresses[0].Type) @@ -2522,69 +2523,6 @@ func TestLoadHTTPRoutes(t *testing.T) { TLS: &dynamic.TLSConfiguration{}, }, }, - { - desc: "Simple HTTPRoute with NativeLBByDefault enabled but service has disabled nativelb", - paths: []string{"services.yml", "httproute/simple_nativelb_disabled.yml"}, - nativeLB: true, - entryPoints: map[string]Entrypoint{"web": { - Address: ":80", - }}, - expected: &dynamic.Configuration{ - UDP: &dynamic.UDPConfiguration{ - Routers: map[string]*dynamic.UDPRouter{}, - Services: map[string]*dynamic.UDPService{}, - }, - TCP: &dynamic.TCPConfiguration{ - Routers: map[string]*dynamic.TCPRouter{}, - Middlewares: map[string]*dynamic.TCPMiddleware{}, - Services: map[string]*dynamic.TCPService{}, - ServersTransports: map[string]*dynamic.TCPServersTransport{}, - }, - HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{ - "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-1c0cf64bde37d9d0df06": { - EntryPoints: []string{"web"}, - Service: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-1c0cf64bde37d9d0df06-wrr", - Rule: "Host(`foo.com`) && Path(`/bar`)", - Priority: 100008, - RuleSyntax: "default", - }, - }, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{ - "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-1c0cf64bde37d9d0df06-wrr": { - Weighted: &dynamic.WeightedRoundRobin{ - Services: []dynamic.WRRService{ - { - Name: "default-whoami-native-disabled-http-80", - Weight: ptr.To(1), - }, - }, - }, - }, - "default-whoami-native-disabled-http-80": { - LoadBalancer: &dynamic.ServersLoadBalancer{ - Strategy: dynamic.BalancerStrategyWRR, - Servers: []dynamic.Server{ - { - URL: "http://10.10.0.20:80", - }, - { - URL: "http://10.10.0.21:80", - }, - }, - PassHostHeader: ptr.To(true), - ResponseForwarding: &dynamic.ResponseForwarding{ - FlushInterval: ptypes.Duration(100 * time.Millisecond), - }, - }, - }, - }, - ServersTransports: map[string]*dynamic.ServersTransport{}, - }, - TLS: &dynamic.TLSConfiguration{}, - }, - }, } for _, test := range testCases { @@ -2618,7 +2556,7 @@ func TestLoadHTTPRoutes(t *testing.T) { client: client, } - conf := p.loadConfigurationFromGateways(t.Context()) + conf := p.loadConfigurationFromGateways(context.Background()) assert.Equal(t, test.expected, conf) }) } @@ -2959,14 +2897,6 @@ func TestLoadHTTPRoutes_backendExtensionRef(t *testing.T) { Name: "default-whoami-wss-http-80", Weight: ptr.To(1), }, - { - Name: "default-whoami-HTTP-http-80", - Weight: ptr.To(1), - }, - { - Name: "default-whoami-HTTPS-http-443", - Weight: ptr.To(1), - }, }, }, }, @@ -3012,34 +2942,6 @@ func TestLoadHTTPRoutes_backendExtensionRef(t *testing.T) { }, }, }, - "default-whoami-HTTPS-http-443": { - LoadBalancer: &dynamic.ServersLoadBalancer{ - Strategy: dynamic.BalancerStrategyWRR, - Servers: []dynamic.Server{ - { - URL: "https://10.10.0.16:8443", - }, - }, - PassHostHeader: ptr.To(true), - ResponseForwarding: &dynamic.ResponseForwarding{ - FlushInterval: ptypes.Duration(100 * time.Millisecond), - }, - }, - }, - "default-whoami-HTTP-http-80": { - LoadBalancer: &dynamic.ServersLoadBalancer{ - Strategy: dynamic.BalancerStrategyWRR, - Servers: []dynamic.Server{ - { - URL: "http://10.10.0.17:8080", - }, - }, - PassHostHeader: ptr.To(true), - ResponseForwarding: &dynamic.ResponseForwarding{ - FlushInterval: ptypes.Duration(100 * time.Millisecond), - }, - }, - }, }, ServersTransports: map[string]*dynamic.ServersTransport{}, }, @@ -3081,7 +2983,7 @@ func TestLoadHTTPRoutes_backendExtensionRef(t *testing.T) { p.RegisterBackendFuncs(group, kind, backendFunc) } } - conf := p.loadConfigurationFromGateways(t.Context()) + conf := p.loadConfigurationFromGateways(context.Background()) assert.Equal(t, test.expected, conf) }) } @@ -3367,7 +3269,7 @@ func TestLoadHTTPRoutes_filterExtensionRef(t *testing.T) { p.RegisterFilterFuncs(group, kind, filterFunc) } } - conf := p.loadConfigurationFromGateways(t.Context()) + conf := p.loadConfigurationFromGateways(context.Background()) assert.Equal(t, test.expected, conf) }) } @@ -3659,7 +3561,7 @@ func TestLoadGRPCRoutes_filterExtensionRef(t *testing.T) { p.RegisterFilterFuncs(group, kind, filterFunc) } } - conf := p.loadConfigurationFromGateways(t.Context()) + conf := p.loadConfigurationFromGateways(context.Background()) assert.Equal(t, test.expected, conf) }) } @@ -4528,63 +4430,6 @@ func TestLoadTCPRoutes(t *testing.T) { TLS: &dynamic.TLSConfiguration{}, }, }, - { - desc: "Simple TCPRoute with NativeLBByDefault enabled but service has disabled nativelb", - paths: []string{"services.yml", "tcproute/simple_nativelb_disabled.yml"}, - nativeLB: true, - entryPoints: map[string]Entrypoint{ - "tcp": {Address: ":9000"}, - }, - expected: &dynamic.Configuration{ - UDP: &dynamic.UDPConfiguration{ - Routers: map[string]*dynamic.UDPRouter{}, - Services: map[string]*dynamic.UDPService{}, - }, - TCP: &dynamic.TCPConfiguration{ - Routers: map[string]*dynamic.TCPRouter{ - "tcproute-default-tcp-app-1-gw-default-my-tcp-gateway-ep-tcp-0-e3b0c44298fc1c149afb": { - EntryPoints: []string{"tcp"}, - Service: "tcproute-default-tcp-app-1-gw-default-my-tcp-gateway-ep-tcp-0-e3b0c44298fc1c149afb-wrr", - Rule: "HostSNI(`*`)", - RuleSyntax: "default", - }, - }, - Middlewares: map[string]*dynamic.TCPMiddleware{}, - Services: map[string]*dynamic.TCPService{ - "tcproute-default-tcp-app-1-gw-default-my-tcp-gateway-ep-tcp-0-e3b0c44298fc1c149afb-wrr": { - Weighted: &dynamic.TCPWeightedRoundRobin{ - Services: []dynamic.TCPWRRService{ - { - Name: "default-whoamitcp-native-disabled-9000", - Weight: ptr.To(1), - }, - }, - }, - }, - "default-whoamitcp-native-disabled-9000": { - LoadBalancer: &dynamic.TCPServersLoadBalancer{ - Servers: []dynamic.TCPServer{ - { - Address: "10.10.0.30:9000", - }, - { - Address: "10.10.0.31:9000", - }, - }, - }, - }, - }, - ServersTransports: map[string]*dynamic.TCPServersTransport{}, - }, - HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, - ServersTransports: map[string]*dynamic.ServersTransport{}, - }, - TLS: &dynamic.TLSConfiguration{}, - }, - }, } for _, test := range testCases { @@ -4618,7 +4463,7 @@ func TestLoadTCPRoutes(t *testing.T) { client: client, } - conf := p.loadConfigurationFromGateways(t.Context()) + conf := p.loadConfigurationFromGateways(context.Background()) assert.Equal(t, test.expected, conf) }) } @@ -5897,7 +5742,7 @@ func TestLoadTLSRoutes(t *testing.T) { client: client, } - conf := p.loadConfigurationFromGateways(t.Context()) + conf := p.loadConfigurationFromGateways(context.Background()) assert.Equal(t, test.expected, conf) }) } @@ -6953,7 +6798,7 @@ func TestLoadMixedRoutes(t *testing.T) { client: client, } - conf := p.loadConfigurationFromGateways(t.Context()) + conf := p.loadConfigurationFromGateways(context.Background()) assert.Equal(t, test.expected, conf) }) } @@ -7289,7 +7134,7 @@ func TestLoadRoutesWithReferenceGrants(t *testing.T) { client: client, } - conf := p.loadConfigurationFromGateways(t.Context()) + conf := p.loadConfigurationFromGateways(context.Background()) assert.Equal(t, test.expected, conf) }) } @@ -8341,7 +8186,7 @@ func newGatewaySimpleClientSet(t *testing.T, objects ...runtime.Object) *gatefak continue } - _, err := client.GatewayV1().Gateways(gateway.Namespace).Create(t.Context(), gateway, metav1.CreateOptions{}) + _, err := client.GatewayV1().Gateways(gateway.Namespace).Create(context.Background(), gateway, metav1.CreateOptions{}) require.NoError(t, err) } diff --git a/pkg/provider/kubernetes/ingress-nginx/annotations.go b/pkg/provider/kubernetes/ingress-nginx/annotations.go deleted file mode 100644 index 6536abed5..000000000 --- a/pkg/provider/kubernetes/ingress-nginx/annotations.go +++ /dev/null @@ -1,115 +0,0 @@ -package ingressnginx - -import ( - "errors" - "reflect" - "strconv" - "strings" - - netv1 "k8s.io/api/networking/v1" -) - -type ingressConfig struct { - AuthType *string `annotation:"nginx.ingress.kubernetes.io/auth-type"` - AuthSecret *string `annotation:"nginx.ingress.kubernetes.io/auth-secret"` - AuthRealm *string `annotation:"nginx.ingress.kubernetes.io/auth-realm"` - AuthSecretType *string `annotation:"nginx.ingress.kubernetes.io/auth-secret-type"` - - AuthURL *string `annotation:"nginx.ingress.kubernetes.io/auth-url"` - AuthResponseHeaders *string `annotation:"nginx.ingress.kubernetes.io/auth-response-headers"` - - ForceSSLRedirect *bool `annotation:"nginx.ingress.kubernetes.io/force-ssl-redirect"` - SSLRedirect *bool `annotation:"nginx.ingress.kubernetes.io/ssl-redirect"` - - SSLPassthrough *bool `annotation:"nginx.ingress.kubernetes.io/ssl-passthrough"` - - UseRegex *bool `annotation:"nginx.ingress.kubernetes.io/use-regex"` - - Affinity *string `annotation:"nginx.ingress.kubernetes.io/affinity"` - SessionCookieName *string `annotation:"nginx.ingress.kubernetes.io/session-cookie-name"` - SessionCookieSecure *bool `annotation:"nginx.ingress.kubernetes.io/session-cookie-secure"` - SessionCookiePath *string `annotation:"nginx.ingress.kubernetes.io/session-cookie-path"` - SessionCookieDomain *string `annotation:"nginx.ingress.kubernetes.io/session-cookie-domain"` - SessionCookieSameSite *string `annotation:"nginx.ingress.kubernetes.io/session-cookie-samesite"` - SessionCookieMaxAge *int `annotation:"nginx.ingress.kubernetes.io/session-cookie-max-age"` - - ServiceUpstream *bool `annotation:"nginx.ingress.kubernetes.io/service-upstream"` - - BackendProtocol *string `annotation:"nginx.ingress.kubernetes.io/backend-protocol"` - - ProxySSLSecret *string `annotation:"nginx.ingress.kubernetes.io/proxy-ssl-secret"` - ProxySSLVerify *string `annotation:"nginx.ingress.kubernetes.io/proxy-ssl-verify"` - ProxySSLName *string `annotation:"nginx.ingress.kubernetes.io/proxy-ssl-name"` - ProxySSLServerName *string `annotation:"nginx.ingress.kubernetes.io/proxy-ssl-server-name"` - - EnableCORS *bool `annotation:"nginx.ingress.kubernetes.io/enable-cors"` - EnableCORSAllowCredentials *bool `annotation:"nginx.ingress.kubernetes.io/cors-allow-credentials"` - CORSExposeHeaders *[]string `annotation:"nginx.ingress.kubernetes.io/cors-expose-headers"` - CORSAllowHeaders *[]string `annotation:"nginx.ingress.kubernetes.io/cors-allow-headers"` - CORSAllowMethods *[]string `annotation:"nginx.ingress.kubernetes.io/cors-allow-methods"` - CORSAllowOrigin *[]string `annotation:"nginx.ingress.kubernetes.io/cors-allow-origin"` - CORSMaxAge *int `annotation:"nginx.ingress.kubernetes.io/cors-max-age"` -} - -// parseIngressConfig parses the annotations from an Ingress object into an ingressConfig struct. -func parseIngressConfig(ing *netv1.Ingress) (ingressConfig, error) { - cfg := ingressConfig{} - cfgType := reflect.TypeOf(cfg) - cfgValue := reflect.ValueOf(&cfg).Elem() - - for i := range cfgType.NumField() { - field := cfgType.Field(i) - annotation := field.Tag.Get("annotation") - if annotation == "" { - continue - } - - val, ok := ing.GetAnnotations()[annotation] - if !ok { - continue - } - - switch field.Type.Elem().Kind() { - case reflect.String: - cfgValue.Field(i).Set(reflect.ValueOf(&val)) - case reflect.Bool: - parsed, err := strconv.ParseBool(val) - if err == nil { - cfgValue.Field(i).Set(reflect.ValueOf(&parsed)) - } - case reflect.Int: - parsed, err := strconv.Atoi(val) - if err == nil { - cfgValue.Field(i).Set(reflect.ValueOf(&parsed)) - } - case reflect.Slice: - if field.Type.Elem().Elem().Kind() == reflect.String { - // Handle slice of strings - var slice []string - elements := strings.Split(val, ",") - for _, elt := range elements { - slice = append(slice, strings.TrimSpace(elt)) - } - cfgValue.Field(i).Set(reflect.ValueOf(&slice)) - } else { - return cfg, errors.New("unsupported slice type in annotations") - } - default: - return cfg, errors.New("unsupported kind") - } - } - - return cfg, nil -} - -// parseBackendProtocol parses the backend protocol annotation and returns the corresponding protocol string. -func parseBackendProtocol(bp string) string { - switch strings.ToUpper(bp) { - case "HTTPS", "GRPCS": - return "https" - case "GRPC": - return "h2c" - default: - return "http" - } -} diff --git a/pkg/provider/kubernetes/ingress-nginx/annotations_test.go b/pkg/provider/kubernetes/ingress-nginx/annotations_test.go deleted file mode 100644 index 36ce89434..000000000 --- a/pkg/provider/kubernetes/ingress-nginx/annotations_test.go +++ /dev/null @@ -1,76 +0,0 @@ -package ingressnginx - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - netv1 "k8s.io/api/networking/v1" - "k8s.io/utils/ptr" -) - -func Test_parseIngressConfig(t *testing.T) { - tests := []struct { - desc string - annotations map[string]string - expected ingressConfig - }{ - { - desc: "all fields set", - annotations: map[string]string{ - "nginx.ingress.kubernetes.io/ssl-passthrough": "true", - "nginx.ingress.kubernetes.io/affinity": "cookie", - "nginx.ingress.kubernetes.io/session-cookie-name": "mycookie", - "nginx.ingress.kubernetes.io/session-cookie-secure": "true", - "nginx.ingress.kubernetes.io/session-cookie-path": "/foo", - "nginx.ingress.kubernetes.io/session-cookie-domain": "example.com", - "nginx.ingress.kubernetes.io/session-cookie-samesite": "Strict", - "nginx.ingress.kubernetes.io/session-cookie-max-age": "3600", - "nginx.ingress.kubernetes.io/backend-protocol": "HTTPS", - "nginx.ingress.kubernetes.io/cors-expose-headers": "foo, bar", - }, - expected: ingressConfig{ - SSLPassthrough: ptr.To(true), - Affinity: ptr.To("cookie"), - SessionCookieName: ptr.To("mycookie"), - SessionCookieSecure: ptr.To(true), - SessionCookiePath: ptr.To("/foo"), - SessionCookieDomain: ptr.To("example.com"), - SessionCookieSameSite: ptr.To("Strict"), - SessionCookieMaxAge: ptr.To(3600), - BackendProtocol: ptr.To("HTTPS"), - CORSExposeHeaders: ptr.To([]string{"foo", "bar"}), - }, - }, - { - desc: "missing fields", - annotations: map[string]string{ - "nginx.ingress.kubernetes.io/ssl-passthrough": "false", - }, - expected: ingressConfig{ - SSLPassthrough: ptr.To(false), - }, - }, - { - desc: "invalid bool and int", - annotations: map[string]string{ - "nginx.ingress.kubernetes.io/ssl-passthrough": "notabool", - "nginx.ingress.kubernetes.io/session-cookie-max-age (in seconds)": "notanint", - }, - }, - } - - for _, test := range tests { - t.Run(test.desc, func(t *testing.T) { - t.Parallel() - - var ing netv1.Ingress - ing.SetAnnotations(test.annotations) - - cfg, err := parseIngressConfig(&ing) - require.NoError(t, err) - - assert.Equal(t, test.expected, cfg) - }) - } -} diff --git a/pkg/provider/kubernetes/ingress-nginx/client.go b/pkg/provider/kubernetes/ingress-nginx/client.go deleted file mode 100644 index 93eae4a09..000000000 --- a/pkg/provider/kubernetes/ingress-nginx/client.go +++ /dev/null @@ -1,384 +0,0 @@ -package ingressnginx - -import ( - "context" - "errors" - "fmt" - "os" - "path/filepath" - "runtime" - "slices" - "time" - - "github.com/rs/zerolog/log" - "github.com/traefik/traefik/v3/pkg/provider/kubernetes/k8s" - "github.com/traefik/traefik/v3/pkg/types" - traefikversion "github.com/traefik/traefik/v3/pkg/version" - corev1 "k8s.io/api/core/v1" - discoveryv1 "k8s.io/api/discovery/v1" - netv1 "k8s.io/api/networking/v1" - kerror "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/apimachinery/pkg/selection" - kinformers "k8s.io/client-go/informers" - kclientset "k8s.io/client-go/kubernetes" - "k8s.io/client-go/rest" - "k8s.io/client-go/tools/clientcmd" -) - -const ( - resyncPeriod = 10 * time.Minute - defaultTimeout = 5 * time.Second -) - -type clientWrapper struct { - clientset kclientset.Interface - clusterScopeFactory kinformers.SharedInformerFactory - factoriesKube map[string]kinformers.SharedInformerFactory - factoriesSecret map[string]kinformers.SharedInformerFactory - factoriesIngress map[string]kinformers.SharedInformerFactory - isNamespaceAll bool - watchedNamespaces []string - - ignoreIngressClasses bool -} - -// newInClusterClient returns a new Provider client that is expected to run -// inside the cluster. -func newInClusterClient(endpoint string) (*clientWrapper, error) { - config, err := rest.InClusterConfig() - if err != nil { - return nil, fmt.Errorf("failed to create in-cluster configuration: %w", err) - } - - if endpoint != "" { - config.Host = endpoint - } - - return createClientFromConfig(config) -} - -func newExternalClusterClientFromFile(file string) (*clientWrapper, error) { - configFromFlags, err := clientcmd.BuildConfigFromFlags("", file) - if err != nil { - return nil, err - } - return createClientFromConfig(configFromFlags) -} - -// newExternalClusterClient returns a new Provider client that may run outside -// of the cluster. -// The endpoint parameter must not be empty. -func newExternalClusterClient(endpoint, caFilePath string, token types.FileOrContent) (*clientWrapper, error) { - if endpoint == "" { - return nil, errors.New("endpoint missing for external cluster client") - } - - tokenData, err := token.Read() - if err != nil { - return nil, fmt.Errorf("read token: %w", err) - } - - config := &rest.Config{ - Host: endpoint, - BearerToken: string(tokenData), - } - - if caFilePath != "" { - caData, err := os.ReadFile(caFilePath) - if err != nil { - return nil, fmt.Errorf("failed to read CA file %s: %w", caFilePath, err) - } - - config.TLSClientConfig = rest.TLSClientConfig{CAData: caData} - } - return createClientFromConfig(config) -} - -func createClientFromConfig(c *rest.Config) (*clientWrapper, error) { - c.UserAgent = fmt.Sprintf( - "%s/%s (%s/%s) kubernetes/ingress", - filepath.Base(os.Args[0]), - traefikversion.Version, - runtime.GOOS, - runtime.GOARCH, - ) - - clientset, err := kclientset.NewForConfig(c) - if err != nil { - return nil, err - } - - return newClient(clientset), nil -} - -func newClient(clientSet kclientset.Interface) *clientWrapper { - return &clientWrapper{ - clientset: clientSet, - factoriesSecret: make(map[string]kinformers.SharedInformerFactory), - factoriesIngress: make(map[string]kinformers.SharedInformerFactory), - factoriesKube: make(map[string]kinformers.SharedInformerFactory), - } -} - -// WatchAll starts namespace-specific controllers for all relevant kinds. -func (c *clientWrapper) WatchAll(ctx context.Context, namespace, namespaceSelector string) (<-chan interface{}, error) { - stopCh := ctx.Done() - eventCh := make(chan interface{}, 1) - eventHandler := &k8s.ResourceEventHandler{Ev: eventCh} - - c.ignoreIngressClasses = false - _, err := c.clientset.NetworkingV1().IngressClasses().List(ctx, metav1.ListOptions{Limit: 1}) - if err != nil { - if !kerror.IsNotFound(err) { - if kerror.IsForbidden(err) { - c.ignoreIngressClasses = true - } - } - } - - if namespaceSelector != "" { - ns, err := c.clientset.CoreV1().Namespaces().List(ctx, metav1.ListOptions{LabelSelector: namespaceSelector}) - if err != nil { - return nil, fmt.Errorf("listing namespaces: %w", err) - } - for _, item := range ns.Items { - c.watchedNamespaces = append(c.watchedNamespaces, item.Name) - } - } else { - c.isNamespaceAll = namespace == metav1.NamespaceAll - c.watchedNamespaces = []string{namespace} - } - - notOwnedByHelm := func(opts *metav1.ListOptions) { - opts.LabelSelector = "owner!=helm" - } - - for _, ns := range c.watchedNamespaces { - factoryIngress := kinformers.NewSharedInformerFactoryWithOptions(c.clientset, resyncPeriod, kinformers.WithNamespace(ns)) - - _, err := factoryIngress.Networking().V1().Ingresses().Informer().AddEventHandler(eventHandler) - if err != nil { - return nil, err - } - - c.factoriesIngress[ns] = factoryIngress - - factoryKube := kinformers.NewSharedInformerFactoryWithOptions(c.clientset, resyncPeriod, kinformers.WithNamespace(ns)) - _, err = factoryKube.Core().V1().Services().Informer().AddEventHandler(eventHandler) - if err != nil { - return nil, err - } - _, err = factoryKube.Discovery().V1().EndpointSlices().Informer().AddEventHandler(eventHandler) - if err != nil { - return nil, err - } - c.factoriesKube[ns] = factoryKube - - factorySecret := kinformers.NewSharedInformerFactoryWithOptions(c.clientset, resyncPeriod, kinformers.WithNamespace(ns), kinformers.WithTweakListOptions(notOwnedByHelm)) - _, err = factorySecret.Core().V1().Secrets().Informer().AddEventHandler(eventHandler) - if err != nil { - return nil, err - } - c.factoriesSecret[ns] = factorySecret - } - - for _, ns := range c.watchedNamespaces { - c.factoriesIngress[ns].Start(stopCh) - c.factoriesKube[ns].Start(stopCh) - c.factoriesSecret[ns].Start(stopCh) - } - - for _, ns := range c.watchedNamespaces { - for t, ok := range c.factoriesIngress[ns].WaitForCacheSync(stopCh) { - if !ok { - return nil, fmt.Errorf("timed out waiting for controller caches to sync %s in namespace %q", t.String(), ns) - } - } - - for t, ok := range c.factoriesKube[ns].WaitForCacheSync(stopCh) { - if !ok { - return nil, fmt.Errorf("timed out waiting for controller caches to sync %s in namespace %q", t.String(), ns) - } - } - - for t, ok := range c.factoriesSecret[ns].WaitForCacheSync(stopCh) { - if !ok { - return nil, fmt.Errorf("timed out waiting for controller caches to sync %s in namespace %q", t.String(), ns) - } - } - } - - c.clusterScopeFactory = kinformers.NewSharedInformerFactory(c.clientset, resyncPeriod) - - if !c.ignoreIngressClasses { - _, err = c.clusterScopeFactory.Networking().V1().IngressClasses().Informer().AddEventHandler(eventHandler) - if err != nil { - return nil, err - } - } - - c.clusterScopeFactory.Start(stopCh) - - for t, ok := range c.clusterScopeFactory.WaitForCacheSync(stopCh) { - if !ok { - return nil, fmt.Errorf("timed out waiting for controller caches to sync %s", t.String()) - } - } - - return eventCh, nil -} - -func (c *clientWrapper) ListIngressClasses() ([]*netv1.IngressClass, error) { - if c.ignoreIngressClasses { - return []*netv1.IngressClass{}, nil - } - - return c.clusterScopeFactory.Networking().V1().IngressClasses().Lister().List(labels.Everything()) -} - -// ListIngresses returns all Ingresses for observed namespaces in the cluster. -func (c *clientWrapper) ListIngresses() []*netv1.Ingress { - var results []*netv1.Ingress - - for ns, factory := range c.factoriesIngress { - // networking - listNew, err := factory.Networking().V1().Ingresses().Lister().List(labels.Everything()) - if err != nil { - log.Error().Err(err).Msgf("Failed to list ingresses in namespace %s", ns) - continue - } - - results = append(results, listNew...) - } - - return results -} - -// UpdateIngressStatus updates an Ingress with a provided status. -func (c *clientWrapper) UpdateIngressStatus(src *netv1.Ingress, ingStatus []netv1.IngressLoadBalancerIngress) error { - if !c.isWatchedNamespace(src.Namespace) { - return fmt.Errorf("failed to get ingress %s/%s: namespace is not within watched namespaces", src.Namespace, src.Name) - } - - ing, err := c.factoriesIngress[c.lookupNamespace(src.Namespace)].Networking().V1().Ingresses().Lister().Ingresses(src.Namespace).Get(src.Name) - if err != nil { - return fmt.Errorf("failed to get ingress %s/%s: %w", src.Namespace, src.Name, err) - } - - logger := log.With().Str("namespace", ing.Namespace).Str("ingress", ing.Name).Logger() - - if isLoadBalancerIngressEquals(ing.Status.LoadBalancer.Ingress, ingStatus) { - logger.Debug().Msg("Skipping ingress status update") - return nil - } - - ingCopy := ing.DeepCopy() - ingCopy.Status = netv1.IngressStatus{LoadBalancer: netv1.IngressLoadBalancerStatus{Ingress: ingStatus}} - - ctx, cancel := context.WithTimeout(context.Background(), defaultTimeout) - defer cancel() - - _, err = c.clientset.NetworkingV1().Ingresses(ingCopy.Namespace).UpdateStatus(ctx, ingCopy, metav1.UpdateOptions{}) - if err != nil { - return fmt.Errorf("failed to update ingress status %s/%s: %w", src.Namespace, src.Name, err) - } - - logger.Info().Msg("Updated ingress status") - return nil -} - -// GetService returns the named service from the given namespace. -func (c *clientWrapper) GetService(namespace, name string) (*corev1.Service, error) { - if !c.isWatchedNamespace(namespace) { - return nil, fmt.Errorf("failed to get service %s/%s: namespace is not within watched namespaces", namespace, name) - } - - return c.factoriesKube[c.lookupNamespace(namespace)].Core().V1().Services().Lister().Services(namespace).Get(name) -} - -// GetEndpointSlicesForService returns the EndpointSlices for the given service name in the given namespace. -func (c *clientWrapper) GetEndpointSlicesForService(namespace, serviceName string) ([]*discoveryv1.EndpointSlice, error) { - if !c.isWatchedNamespace(namespace) { - return nil, fmt.Errorf("failed to get endpointslices for service %s/%s: namespace is not within watched namespaces", namespace, serviceName) - } - - serviceLabelRequirement, err := labels.NewRequirement(discoveryv1.LabelServiceName, selection.Equals, []string{serviceName}) - if err != nil { - return nil, fmt.Errorf("failed to create service label selector requirement: %w", err) - } - serviceSelector := labels.NewSelector() - serviceSelector = serviceSelector.Add(*serviceLabelRequirement) - - return c.factoriesKube[c.lookupNamespace(namespace)].Discovery().V1().EndpointSlices().Lister().EndpointSlices(namespace).List(serviceSelector) -} - -// GetSecret returns the named secret from the given namespace. -func (c *clientWrapper) GetSecret(namespace, name string) (*corev1.Secret, error) { - if !c.isWatchedNamespace(namespace) { - return nil, fmt.Errorf("failed to get secret %s/%s: namespace is not within watched namespaces", namespace, name) - } - - return c.factoriesSecret[c.lookupNamespace(namespace)].Core().V1().Secrets().Lister().Secrets(namespace).Get(name) -} - -// lookupNamespace returns the lookup namespace key for the given namespace. -// When listening on all namespaces, it returns the client-go identifier ("") -// for all-namespaces. Otherwise, it returns the given namespace. -// The distinction is necessary because we index all informers on the special -// identifier iff all-namespaces are requested but receive specific namespace -// identifiers from the Kubernetes API, so we have to bridge this gap. -func (c *clientWrapper) lookupNamespace(ns string) string { - if c.isNamespaceAll { - return metav1.NamespaceAll - } - return ns -} - -// isWatchedNamespace checks to ensure that the namespace is being watched before we request -// it to ensure we don't panic by requesting an out-of-watch object. -func (c *clientWrapper) isWatchedNamespace(ns string) bool { - if c.isNamespaceAll { - return true - } - - return slices.Contains(c.watchedNamespaces, ns) -} - -// isLoadBalancerIngressEquals returns true if the given slices are equal, false otherwise. -func isLoadBalancerIngressEquals(aSlice, bSlice []netv1.IngressLoadBalancerIngress) bool { - if len(aSlice) != len(bSlice) { - return false - } - - aMap := make(map[string]struct{}) - for _, aIngress := range aSlice { - aMap[aIngress.Hostname+aIngress.IP] = struct{}{} - } - - for _, bIngress := range bSlice { - if _, exists := aMap[bIngress.Hostname+bIngress.IP]; !exists { - return false - } - } - - return true -} - -// filterIngressClass return a slice containing IngressClass matching either the annotation name or the controller. -func filterIngressClass(ingressClasses []*netv1.IngressClass, ingressClassByName bool, ingressClass, controllerClass string) []*netv1.IngressClass { - var filteredIngressClasses []*netv1.IngressClass - for _, ic := range ingressClasses { - if ingressClassByName && ic.Name == ingressClass { - return append(filteredIngressClasses, ic) - } - - if ic.Spec.Controller == controllerClass { - filteredIngressClasses = append(filteredIngressClasses, ic) - continue - } - } - - return filteredIngressClasses -} diff --git a/pkg/provider/kubernetes/ingress-nginx/client_test.go b/pkg/provider/kubernetes/ingress-nginx/client_test.go deleted file mode 100644 index e6ed09596..000000000 --- a/pkg/provider/kubernetes/ingress-nginx/client_test.go +++ /dev/null @@ -1,270 +0,0 @@ -package ingressnginx - -import ( - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - corev1 "k8s.io/api/core/v1" - discoveryv1 "k8s.io/api/discovery/v1" - netv1 "k8s.io/api/networking/v1" - kerror "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - kversion "k8s.io/apimachinery/pkg/version" - discoveryfake "k8s.io/client-go/discovery/fake" - kubefake "k8s.io/client-go/kubernetes/fake" -) - -func TestIsLoadBalancerIngressEquals(t *testing.T) { - testCases := []struct { - desc string - aSlice []netv1.IngressLoadBalancerIngress - bSlice []netv1.IngressLoadBalancerIngress - expectedEqual bool - }{ - { - desc: "both slices are empty", - expectedEqual: true, - }, - { - desc: "not the same length", - bSlice: []netv1.IngressLoadBalancerIngress{ - {IP: "192.168.1.1", Hostname: "traefik"}, - }, - expectedEqual: false, - }, - { - desc: "same ordered content", - aSlice: []netv1.IngressLoadBalancerIngress{ - {IP: "192.168.1.1", Hostname: "traefik"}, - }, - bSlice: []netv1.IngressLoadBalancerIngress{ - {IP: "192.168.1.1", Hostname: "traefik"}, - }, - expectedEqual: true, - }, - { - desc: "same unordered content", - aSlice: []netv1.IngressLoadBalancerIngress{ - {IP: "192.168.1.1", Hostname: "traefik"}, - {IP: "192.168.1.2", Hostname: "traefik2"}, - }, - bSlice: []netv1.IngressLoadBalancerIngress{ - {IP: "192.168.1.2", Hostname: "traefik2"}, - {IP: "192.168.1.1", Hostname: "traefik"}, - }, - expectedEqual: true, - }, - { - desc: "different ordered content", - aSlice: []netv1.IngressLoadBalancerIngress{ - {IP: "192.168.1.1", Hostname: "traefik"}, - {IP: "192.168.1.2", Hostname: "traefik2"}, - }, - bSlice: []netv1.IngressLoadBalancerIngress{ - {IP: "192.168.1.1", Hostname: "traefik"}, - {IP: "192.168.1.2", Hostname: "traefik"}, - }, - expectedEqual: false, - }, - { - desc: "different unordered content", - aSlice: []netv1.IngressLoadBalancerIngress{ - {IP: "192.168.1.1", Hostname: "traefik"}, - {IP: "192.168.1.2", Hostname: "traefik2"}, - }, - bSlice: []netv1.IngressLoadBalancerIngress{ - {IP: "192.168.1.2", Hostname: "traefik3"}, - {IP: "192.168.1.1", Hostname: "traefik"}, - }, - expectedEqual: false, - }, - } - - for _, test := range testCases { - t.Run(test.desc, func(t *testing.T) { - t.Parallel() - - gotEqual := isLoadBalancerIngressEquals(test.aSlice, test.bSlice) - assert.Equal(t, test.expectedEqual, gotEqual) - }) - } -} - -func TestClientIgnoresHelmOwnedSecrets(t *testing.T) { - secret := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: "default", - Name: "secret", - }, - } - helmSecret := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: "default", - Name: "helm-secret", - Labels: map[string]string{ - "owner": "helm", - }, - }, - } - - kubeClient := kubefake.NewClientset(helmSecret, secret) - - discovery, _ := kubeClient.Discovery().(*discoveryfake.FakeDiscovery) - discovery.FakedServerVersion = &kversion.Info{ - GitVersion: "v1.19", - } - - client := newClient(kubeClient) - - eventCh, err := client.WatchAll(t.Context(), "", "") - require.NoError(t, err) - - select { - case event := <-eventCh: - secret, ok := event.(*corev1.Secret) - require.True(t, ok) - - assert.NotEqual(t, "helm-secret", secret.Name) - case <-time.After(50 * time.Millisecond): - assert.Fail(t, "expected to receive event for secret") - } - - select { - case <-eventCh: - assert.Fail(t, "received more than one event") - case <-time.After(50 * time.Millisecond): - } - - _, err = client.GetSecret("default", "secret") - require.NoError(t, err) - - _, err = client.GetSecret("default", "helm-secret") - assert.True(t, kerror.IsNotFound(err)) -} - -func TestClientIgnoresEmptyEndpointSliceUpdates(t *testing.T) { - emptyEndpointSlice := &discoveryv1.EndpointSlice{ - ObjectMeta: metav1.ObjectMeta{ - Name: "empty-endpointslice", - Namespace: "test", - ResourceVersion: "1244", - Annotations: map[string]string{ - "test-annotation": "_", - }, - }, - } - - samplePortName := "testing" - samplePortNumber := int32(1337) - samplePortProtocol := corev1.ProtocolTCP - sampleAddressReady := true - filledEndpointSlice := &discoveryv1.EndpointSlice{ - ObjectMeta: metav1.ObjectMeta{ - Name: "filled-endpointslice", - Namespace: "test", - ResourceVersion: "1234", - }, - AddressType: discoveryv1.AddressTypeIPv4, - Endpoints: []discoveryv1.Endpoint{{ - Addresses: []string{"10.13.37.1"}, - Conditions: discoveryv1.EndpointConditions{ - Ready: &sampleAddressReady, - }, - }}, - Ports: []discoveryv1.EndpointPort{{ - Name: &samplePortName, - Port: &samplePortNumber, - Protocol: &samplePortProtocol, - }}, - } - - kubeClient := kubefake.NewClientset(emptyEndpointSlice, filledEndpointSlice) - - discovery, _ := kubeClient.Discovery().(*discoveryfake.FakeDiscovery) - discovery.FakedServerVersion = &kversion.Info{ - GitVersion: "v1.19", - } - - client := newClient(kubeClient) - - eventCh, err := client.WatchAll(t.Context(), "", "") - require.NoError(t, err) - - select { - case event := <-eventCh: - ep, ok := event.(*discoveryv1.EndpointSlice) - require.True(t, ok) - - assert.True(t, ep.Name == "empty-endpointslice" || ep.Name == "filled-endpointslice") - case <-time.After(50 * time.Millisecond): - assert.Fail(t, "expected to receive event for endpointslices") - } - - emptyEndpointSlice, err = kubeClient.DiscoveryV1().EndpointSlices("test").Get(t.Context(), "empty-endpointslice", metav1.GetOptions{}) - assert.NoError(t, err) - - // Update endpoint annotation and resource version (apparently not done by fake client itself) - // to show an update that should not trigger an update event on our eventCh. - // This reflects the behavior of kubernetes controllers which use endpoint annotations for leader election. - emptyEndpointSlice.Annotations["test-annotation"] = "___" - emptyEndpointSlice.ResourceVersion = "1245" - _, err = kubeClient.DiscoveryV1().EndpointSlices("test").Update(t.Context(), emptyEndpointSlice, metav1.UpdateOptions{}) - require.NoError(t, err) - - select { - case event := <-eventCh: - ep, ok := event.(*discoveryv1.EndpointSlice) - require.True(t, ok) - - assert.Fail(t, "didn't expect to receive event for empty endpointslice update", ep.Name) - case <-time.After(50 * time.Millisecond): - } - - filledEndpointSlice, err = kubeClient.DiscoveryV1().EndpointSlices("test").Get(t.Context(), "filled-endpointslice", metav1.GetOptions{}) - assert.NoError(t, err) - - filledEndpointSlice.Endpoints[0].Addresses[0] = "10.13.37.2" - filledEndpointSlice.ResourceVersion = "1235" - _, err = kubeClient.DiscoveryV1().EndpointSlices("test").Update(t.Context(), filledEndpointSlice, metav1.UpdateOptions{}) - require.NoError(t, err) - - select { - case event := <-eventCh: - ep, ok := event.(*discoveryv1.EndpointSlice) - require.True(t, ok) - - assert.Equal(t, "filled-endpointslice", ep.Name) - case <-time.After(50 * time.Millisecond): - assert.Fail(t, "expected to receive event for filled endpointslice") - } - - select { - case <-eventCh: - assert.Fail(t, "received more than one event") - case <-time.After(50 * time.Millisecond): - } - - newPortNumber := int32(42) - filledEndpointSlice.Ports[0].Port = &newPortNumber - filledEndpointSlice.ResourceVersion = "1236" - _, err = kubeClient.DiscoveryV1().EndpointSlices("test").Update(t.Context(), filledEndpointSlice, metav1.UpdateOptions{}) - require.NoError(t, err) - - select { - case event := <-eventCh: - ep, ok := event.(*discoveryv1.EndpointSlice) - require.True(t, ok) - - assert.Equal(t, "filled-endpointslice", ep.Name) - case <-time.After(50 * time.Millisecond): - assert.Fail(t, "expected to receive event for filled endpointslice") - } - - select { - case <-eventCh: - assert.Fail(t, "received more than one event") - case <-time.After(50 * time.Millisecond): - } -} diff --git a/pkg/provider/kubernetes/ingress-nginx/convert.go b/pkg/provider/kubernetes/ingress-nginx/convert.go deleted file mode 100644 index f2a27e034..000000000 --- a/pkg/provider/kubernetes/ingress-nginx/convert.go +++ /dev/null @@ -1,69 +0,0 @@ -package ingressnginx - -import ( - "errors" - - corev1 "k8s.io/api/core/v1" - netv1 "k8s.io/api/networking/v1" -) - -type marshaler interface { - Marshal() ([]byte, error) -} - -type unmarshaler interface { - Unmarshal(data []byte) error -} - -type LoadBalancerIngress interface { - corev1.LoadBalancerIngress | netv1.IngressLoadBalancerIngress -} - -// convertSlice converts slice of LoadBalancerIngress to slice of LoadBalancerIngress. -// O (Bar), I (Foo) => []Bar. -func convertSlice[O LoadBalancerIngress, I LoadBalancerIngress](loadBalancerIngresses []I) ([]O, error) { - var results []O - - for _, loadBalancerIngress := range loadBalancerIngresses { - mar, ok := any(&loadBalancerIngress).(marshaler) - if !ok { - // All the pointer of types related to the interface LoadBalancerIngress are compatible with the interface marshaler. - continue - } - - um, err := convert[O](mar) - if err != nil { - return nil, err - } - - v, ok := any(*um).(O) - if !ok { - continue - } - - results = append(results, v) - } - - return results, nil -} - -// convert must only be used with unmarshaler and marshaler compatible types. -func convert[T any](input marshaler) (*T, error) { - data, err := input.Marshal() - if err != nil { - return nil, err - } - - var output T - um, ok := any(&output).(unmarshaler) - if !ok { - return nil, errors.New("the output type doesn't implement unmarshaler interface") - } - - err = um.Unmarshal(data) - if err != nil { - return nil, err - } - - return &output, nil -} diff --git a/pkg/provider/kubernetes/ingress-nginx/convert_test.go b/pkg/provider/kubernetes/ingress-nginx/convert_test.go deleted file mode 100644 index a0bff8834..000000000 --- a/pkg/provider/kubernetes/ingress-nginx/convert_test.go +++ /dev/null @@ -1,77 +0,0 @@ -package ingressnginx - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - corev1 "k8s.io/api/core/v1" - netv1 "k8s.io/api/networking/v1" - "k8s.io/utils/ptr" -) - -func Test_convertSlice_corev1_to_networkingv1(t *testing.T) { - g := []corev1.LoadBalancerIngress{ - { - IP: "132456", - Hostname: "foo", - Ports: []corev1.PortStatus{ - { - Port: 123, - Protocol: "https", - Error: ptr.To("test"), - }, - }, - }, - } - - actual, err := convertSlice[netv1.IngressLoadBalancerIngress](g) - require.NoError(t, err) - - expected := []netv1.IngressLoadBalancerIngress{ - { - IP: "132456", - Hostname: "foo", - Ports: []netv1.IngressPortStatus{ - { - Port: 123, - Protocol: "https", - Error: ptr.To("test"), - }, - }, - }, - } - - assert.Equal(t, expected, actual) -} - -func Test_convert(t *testing.T) { - g := &corev1.LoadBalancerIngress{ - IP: "132456", - Hostname: "foo", - Ports: []corev1.PortStatus{ - { - Port: 123, - Protocol: "https", - Error: ptr.To("test"), - }, - }, - } - - actual, err := convert[netv1.IngressLoadBalancerIngress](g) - require.NoError(t, err) - - expected := &netv1.IngressLoadBalancerIngress{ - IP: "132456", - Hostname: "foo", - Ports: []netv1.IngressPortStatus{ - { - Port: 123, - Protocol: "https", - Error: ptr.To("test"), - }, - }, - } - - assert.Equal(t, expected, actual) -} diff --git a/pkg/provider/kubernetes/ingress-nginx/fixtures/ingressclasses.yml b/pkg/provider/kubernetes/ingress-nginx/fixtures/ingressclasses.yml deleted file mode 100644 index 33e88a25d..000000000 --- a/pkg/provider/kubernetes/ingress-nginx/fixtures/ingressclasses.yml +++ /dev/null @@ -1,7 +0,0 @@ ---- -apiVersion: networking.k8s.io/v1 -kind: IngressClass -metadata: - name: nginx -spec: - controller: k8s.io/ingress-nginx \ No newline at end of file diff --git a/pkg/provider/kubernetes/ingress-nginx/fixtures/ingresses/01-ingress-with-basicauth.yml b/pkg/provider/kubernetes/ingress-nginx/fixtures/ingresses/01-ingress-with-basicauth.yml deleted file mode 100644 index cd750a9f1..000000000 --- a/pkg/provider/kubernetes/ingress-nginx/fixtures/ingresses/01-ingress-with-basicauth.yml +++ /dev/null @@ -1,37 +0,0 @@ ---- -kind: Ingress -apiVersion: networking.k8s.io/v1 -metadata: - name: ingress-with-basicauth - namespace: default - annotations: - # Configuration basic authentication for the Ingress - nginx.ingress.kubernetes.io/auth-type: "basic" - nginx.ingress.kubernetes.io/auth-secret-type: "auth-file" - nginx.ingress.kubernetes.io/auth-secret: "default/basic-auth" - nginx.ingress.kubernetes.io/auth-realm: "Authentication Required" - -spec: - ingressClassName: nginx - rules: - - host: whoami.localhost - http: - paths: - - path: /basicauth - pathType: Exact - backend: - service: - name: whoami - port: - number: 80 - ---- -kind: Secret -apiVersion: v1 -metadata: - name: basic-auth - namespace: default -type: Opaque -data: - # user:password - auth: dXNlcjp7U0hBfVc2cGg1TW01UHo4R2dpVUxiUGd6RzM3bWo5Zz0= \ No newline at end of file diff --git a/pkg/provider/kubernetes/ingress-nginx/fixtures/ingresses/02-ingress-with-forwardauth.yml b/pkg/provider/kubernetes/ingress-nginx/fixtures/ingresses/02-ingress-with-forwardauth.yml deleted file mode 100644 index 220499792..000000000 --- a/pkg/provider/kubernetes/ingress-nginx/fixtures/ingresses/02-ingress-with-forwardauth.yml +++ /dev/null @@ -1,24 +0,0 @@ ---- -kind: Ingress -apiVersion: networking.k8s.io/v1 -metadata: - name: ingress-with-forwardauth - namespace: default - annotations: - nginx.ingress.kubernetes.io/auth-url: "http://whoami.default.svc/" - nginx.ingress.kubernetes.io/auth-method: "GET" - nginx.ingress.kubernetes.io/auth-response-headers: "X-Foo" - -spec: - ingressClassName: nginx - rules: - - host: whoami.localhost - http: - paths: - - path: /forwardauth - pathType: Exact - backend: - service: - name: whoami - port: - number: 80 diff --git a/pkg/provider/kubernetes/ingress-nginx/fixtures/ingresses/03-ingress-with-ssl-redirect.yml b/pkg/provider/kubernetes/ingress-nginx/fixtures/ingresses/03-ingress-with-ssl-redirect.yml deleted file mode 100644 index 57f8dd420..000000000 --- a/pkg/provider/kubernetes/ingress-nginx/fixtures/ingresses/03-ingress-with-ssl-redirect.yml +++ /dev/null @@ -1,75 +0,0 @@ ---- -kind: Ingress -apiVersion: networking.k8s.io/v1 -metadata: - name: ingress-with-ssl-redirect - namespace: default - - -spec: - ingressClassName: nginx - rules: - - host: sslredirect.localhost - http: - paths: - - path: / - pathType: Exact - backend: - service: - name: whoami - port: - number: 80 - tls: - - hosts: - - sslredirect.localhost - secretName: whoami-tls - ---- -kind: Ingress -apiVersion: networking.k8s.io/v1 -metadata: - name: ingress-without-ssl-redirect - namespace: default - annotations: - nginx.ingress.kubernetes.io/ssl-redirect: "false" - -spec: - ingressClassName: nginx - rules: - - host: withoutsslredirect.localhost - http: - paths: - - path: / - pathType: Exact - backend: - service: - name: whoami - port: - number: 80 - tls: - - hosts: - - withoutsslredirect.localhost - secretName: whoami-tls - ---- -kind: Ingress -apiVersion: networking.k8s.io/v1 -metadata: - name: ingress-with-force-ssl-redirect - namespace: default - annotations: - nginx.ingress.kubernetes.io/force-ssl-redirect: "true" - -spec: - ingressClassName: nginx - rules: - - host: forcesslredirect.localhost - http: - paths: - - path: / - pathType: Exact - backend: - service: - name: whoami - port: - number: 80 diff --git a/pkg/provider/kubernetes/ingress-nginx/fixtures/ingresses/04-ingress-with-ssl-passthrough.yml b/pkg/provider/kubernetes/ingress-nginx/fixtures/ingresses/04-ingress-with-ssl-passthrough.yml deleted file mode 100644 index 70f531c7c..000000000 --- a/pkg/provider/kubernetes/ingress-nginx/fixtures/ingresses/04-ingress-with-ssl-passthrough.yml +++ /dev/null @@ -1,22 +0,0 @@ ---- -kind: Ingress -apiVersion: networking.k8s.io/v1 -metadata: - name: ingress-with-ssl-passthrough - namespace: default - annotations: - nginx.ingress.kubernetes.io/ssl-passthrough: "true" - -spec: - ingressClassName: nginx - rules: - - host: passthrough.whoami.localhost - http: - paths: - - path: / - pathType: Prefix - backend: - service: - name: whoami-tls - port: - number: 443 \ No newline at end of file diff --git a/pkg/provider/kubernetes/ingress-nginx/fixtures/ingresses/05-ingress-with-default-backend.yml b/pkg/provider/kubernetes/ingress-nginx/fixtures/ingresses/05-ingress-with-default-backend.yml deleted file mode 100644 index 8809e7164..000000000 --- a/pkg/provider/kubernetes/ingress-nginx/fixtures/ingresses/05-ingress-with-default-backend.yml +++ /dev/null @@ -1,24 +0,0 @@ ---- -kind: Ingress -apiVersion: networking.k8s.io/v1 -metadata: - name: ingress-with-default-backend - namespace: default - -spec: - defaultBackend: - service: - name: whoami-default - port: - number: 80 - - rules: - - http: - paths: - - path: / - pathType: Exact - backend: - service: - name: whoami - port: - number: 80 diff --git a/pkg/provider/kubernetes/ingress-nginx/fixtures/ingresses/05-ingress-with-default-backend2.yml b/pkg/provider/kubernetes/ingress-nginx/fixtures/ingresses/05-ingress-with-default-backend2.yml deleted file mode 100644 index a9c350168..000000000 --- a/pkg/provider/kubernetes/ingress-nginx/fixtures/ingresses/05-ingress-with-default-backend2.yml +++ /dev/null @@ -1,108 +0,0 @@ - ---- -kind: Ingress -apiVersion: networking.k8s.io/v1 -metadata: - name: ingress-with-default-backend2 - namespace: default -# annotations: -# nginx.ingress.kubernetes.io/force-ssl-redirect: "true" - -# annotations: -## Configuration basic authentication for the Ingress -# nginx.ingress.kubernetes.io/auth-type: "basic" -# nginx.ingress.kubernetes.io/auth-secret-type: "auth-file" -# nginx.ingress.kubernetes.io/auth-secret: "default/basic-auth" -# nginx.ingress.kubernetes.io/auth-realm: "Authentication Required" - -spec: - defaultBackend: - service: - name: whoami-default2 - port: - number: 80 - - rules: - - host: dd.localhost - http: - paths: - - path: / - pathType: Exact - backend: - service: - name: whoami - port: - number: 443 -# -# tls: -# - hosts: -# - dd.localhost -# secretName: whoami-tls -# -#--- -#kind: Secret -#apiVersion: v1 -#metadata: -# name: whoami-tls -# namespace: default -# -#type: opaque -#stringData: -# tls.crt: | -# -----BEGIN CERTIFICATE----- -# MIIEXjCCAsagAwIBAgIQAJmtU2qHBlD9D2HZFZLMeDANBgkqhkiG9w0BAQsFADCB -# jzEeMBwGA1UEChMVbWtjZXJ0IGRldmVsb3BtZW50IENBMTIwMAYDVQQLDClyb21h -# aW5AY29udGFpbm91cy5ob21lIChSb21haW4gVHJpYm90dMOpKTE5MDcGA1UEAwww -# bWtjZXJ0IHJvbWFpbkBjb250YWlub3VzLmhvbWUgKFJvbWFpbiBUcmlib3R0w6kp -# MB4XDTI1MDYxMDE1NDE0NFoXDTI3MDkxMDE1NDE0NFowXzEnMCUGA1UEChMebWtj -# ZXJ0IGRldmVsb3BtZW50IGNlcnRpZmljYXRlMTQwMgYDVQQLDCtyb21haW5ATWFj -# Qm9vay1Qcm8ubG9jYWwgKFJvbWFpbiBUcmlib3R0w6kpMIIBIjANBgkqhkiG9w0B -# AQEFAAOCAQ8AMIIBCgKCAQEAq3dajz+RgY+VUXvKKtHFFVd+0URcpDRgN+SJOxP/ -# 1uZG2U57DMvTiVy6zfpYo7QPzyEAUwbRTMMgxZV5oy1JPkGzV5kc08GUT3Lh1Azf -# LVPX/K1nA+k7p9+kuMsfkHVABMawRpnWo215T9pjGaTKERA2EaNvrSdq73k6raVn -# DnnmvUgWGPvxTetaLu0AVQscGyrTfQNMB8BwC+JEQJKocenJ0ve5l9/yv9543P2G -# 6UcOv71lDOBNPyltrc4sXfGC2vB1APbp80BVfkZDiF+8Gr8wGJrkd75Esp/xetFV -# yZ6NKO9ZsGZ2E14/qxfvASHGNFNQJafqhnuGbmky8AeaawIDAQABo2UwYzAOBgNV -# HQ8BAf8EBAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwHwYDVR0jBBgwFoAUjIHl -# 1gcu+iVVHCicC14yHQiRojgwGwYDVR0RBBQwEoIQd2hvYW1pLmxvY2FsaG9zdDAN -# BgkqhkiG9w0BAQsFAAOCAYEAo/f0ADJwnkOakCHcYCNSqRY/VzRIQSQK3wfDq3bD -# 8EDxGrGPYHOIL+u/Up4RO2/9vLEnFpWb30A8z/qZTKKD+rMuU3qTcCJ2tsB3DAIV -# T+b2GmJYjURf1gqe/NNXnzZqgkoP+bHx6iNvDr1kmc3pZshayz+FxzNmjgpbKl2G -# SgfFLnJDm7hwTC9JFoPyzb586Q0OGQKCJpDMy6pi1MAQl2RWiKyrgo1mhYnSxQmI -# qbJbxYlegRRQQPD6YEJcL5lwILVW3TXcGrK+zuMD+xWznDTBg2BxbF2umG8jmXPH -# 04gRfjlMNLEYSrNEU8EOa/lXebcxnlz6meFOgfYKmSHxL+kwjTUuppDV/qP9U+VS -# /ozJ85VS8iEx1obqZGgqgwcBKMRYzuRnW1XEScGUOK9/cs9mGoXG9uafKb7ekFQc -# wU0j0FoUVzc50WWEjCGFU/dS/2HXUXU/Rcf+uULC10ORplwrB5XdXZYxowh7T//U -# yh86E4+M0LHyZH0vUwDoBk/1 -# -----END CERTIFICATE----- -# -# -# tls.key: | -# -----BEGIN PRIVATE KEY----- -# MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCrd1qPP5GBj5VR -# e8oq0cUVV37RRFykNGA35Ik7E//W5kbZTnsMy9OJXLrN+lijtA/PIQBTBtFMwyDF -# lXmjLUk+QbNXmRzTwZRPcuHUDN8tU9f8rWcD6Tun36S4yx+QdUAExrBGmdajbXlP -# 2mMZpMoREDYRo2+tJ2rveTqtpWcOeea9SBYY+/FN61ou7QBVCxwbKtN9A0wHwHAL -# 4kRAkqhx6cnS97mX3/K/3njc/YbpRw6/vWUM4E0/KW2tzixd8YLa8HUA9unzQFV+ -# RkOIX7wavzAYmuR3vkSyn/F60VXJno0o71mwZnYTXj+rF+8BIcY0U1Alp+qGe4Zu -# aTLwB5prAgMBAAECggEAVNpLxnf+2c7kZd6MvYPxtA4IhCcAcYI5228NOl87TG3I -# weFEo6B6no91IlmxY9HHwQjj0DKfgQ1POnguKcJPbK+2wLLUwTYa3vZLK1TzXMsR -# J8noINda3kiei5R5mlNryvFIaqfWwCl8zzeTsy0JkkgjebcXnOjU0o17rFMeHNsH -# A3iFWWnHtJkn2OaVtOOgsyjJ9oAnGX0AE4cVp7ZZTerpaYTXzkCphbwRi00IEbCk -# 1bn7gPcBQRoxs12GJUUuy/sopQRA51PE//CnV2pkGuDFWBhFBBKYdsHaTUwmTb5P -# l6S5CuCtw44NkTPetTe2sn9DpOIlR7PmojQndmKkgQKBgQDJ6/RDueJCBxSYS6bh -# 7dTPRphJvntoJHs9Q/NNjKdQhxv0vIIdtRk88Q2qhjjlCzHb1RtD5Jsl+D+TxOdG -# wR1/E8+hdbRKv+WACywa38aBPuZSEj89bnyPyQfzs5TtzD5JsdUHT4l5Eudth6Gv -# w14dFKria8WiEd7X2GodnlZX/wKBgQDZY1QBNjAHsi7QJJSvbPKwK8RygvdNJEem -# FYxhjtHzOfUttjyDXDSGheY3/VzKi2rGgVAHLi+qbvwkURn4qT3xtV5Lpi+BLWHP -# Gwepisd9P5TrN0DGQojWjzYatN9MYRzX0JynIB+alabN2bG7kfPPsHikAA7pRxLH -# 7EwMBDGdlQKBgBqd9uoCk9e+VTGqL0py7m2QUbzO1jepL3GpBmZ/lwKffMjrHH/M -# ApKs9+81mERhEGZ5FgoCFY2Qxti0yQPjqv64XtNaz7RWzWrujhbQzrr0zqmc7Cct -# 7E+L4Xd3gbdDCCbwwTMgge+q1UTz7xVbPIm60rfcGwY9MtHjHkHfQGSDAoGBAIA/ -# CAT6+dTgepuSqSDg7j+eYnOH7etVlutVVQ8M2bFbJNiF5Sc900L1ZX7seryHCUP4 -# b8T8q2Qpu5iVO/QlrASXkfyhGu9jXYt4D8omtE+gnfMyEoWkJOQncqzIvd9qf0CW -# soQqAFsLJG/WmPLmRObm3hUqb6GRq3PEZIzGQJsNAoGBAJEN0ZkrIkNK+Jjd1oNB -# AnwgLA0qyAHqJxPig45Nudhb6Jw4ub/hKG9bCrLpcBM57Lue535e2HtQ5Ed22Pim -# 0m7bQkvrIQYjflW99RsfkiH5qJsiTy9O92iKgGtJAJ80vTkIggAbsnzOHlZvR0Fr -# +GhYvMt0TxpugicUqguSSUZp -# -----END PRIVATE KEY----- diff --git a/pkg/provider/kubernetes/ingress-nginx/fixtures/ingresses/06-ingress-with-sticky.yml b/pkg/provider/kubernetes/ingress-nginx/fixtures/ingresses/06-ingress-with-sticky.yml deleted file mode 100644 index ac56745cc..000000000 --- a/pkg/provider/kubernetes/ingress-nginx/fixtures/ingresses/06-ingress-with-sticky.yml +++ /dev/null @@ -1,28 +0,0 @@ ---- -kind: Ingress -apiVersion: networking.k8s.io/v1 -metadata: - name: ingress-with-sticky - namespace: default - annotations: - nginx.ingress.kubernetes.io/affinity: cookie - nginx.ingress.kubernetes.io/session-cookie-name: foobar - nginx.ingress.kubernetes.io/session-cookie-secure: "true" - nginx.ingress.kubernetes.io/session-cookie-path: "/foobar" - nginx.ingress.kubernetes.io/session-cookie-domain: "foo.localhost" - nginx.ingress.kubernetes.io/session-cookie-samesite: "None" - nginx.ingress.kubernetes.io/session-cookie-max-age: "42" - -spec: - ingressClassName: nginx - rules: - - host: sticky.localhost - http: - paths: - - path: / - pathType: Exact - backend: - service: - name: whoami - port: - number: 80 diff --git a/pkg/provider/kubernetes/ingress-nginx/fixtures/ingresses/07-ingress-with-proxy-ssl.yml b/pkg/provider/kubernetes/ingress-nginx/fixtures/ingresses/07-ingress-with-proxy-ssl.yml deleted file mode 100644 index 4d3f4dc63..000000000 --- a/pkg/provider/kubernetes/ingress-nginx/fixtures/ingresses/07-ingress-with-proxy-ssl.yml +++ /dev/null @@ -1,37 +0,0 @@ ---- -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: ingress-with-proxy-ssl - namespace: default - annotations: - nginx.ingress.kubernetes.io/backend-protocol: "HTTPS" # HTTP, HTTPS, AUTO_HTTP, GRPC, GRPCS and FCGI - nginx.ingress.kubernetes.io/proxy-ssl-secret: "default/ingress-with-proxy-ssl" - nginx.ingress.kubernetes.io/proxy-ssl-verify: "on" - nginx.ingress.kubernetes.io/proxy-ssl-verify-depth: "1" - nginx.ingress.kubernetes.io/proxy-ssl-server-name: "whoami.localhost" - nginx.ingress.kubernetes.io/proxy-ssl-name: "whoami.localhost" - -spec: - ingressClassName: nginx - rules: - - host: proxy-ssl.localhost - http: - paths: - - path: / - pathType: Exact - backend: - service: - name: whoami-tls - port: - number: 443 - ---- -kind: Secret -apiVersion: v1 -metadata: - namespace: default - name: ingress-with-proxy-ssl - -data: - ca.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0t diff --git a/pkg/provider/kubernetes/ingress-nginx/fixtures/ingresses/08-ingress-with-cors.yml b/pkg/provider/kubernetes/ingress-nginx/fixtures/ingresses/08-ingress-with-cors.yml deleted file mode 100644 index b862b9bf6..000000000 --- a/pkg/provider/kubernetes/ingress-nginx/fixtures/ingresses/08-ingress-with-cors.yml +++ /dev/null @@ -1,28 +0,0 @@ ---- -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: ingress-with-cors - namespace: default - annotations: - nginx.ingress.kubernetes.io/enable-cors: "true" - nginx.ingress.kubernetes.io/cors-allow-credentials: "true" - nginx.ingress.kubernetes.io/cors-expose-headers: "X-Forwarded-For, X-Forwarded-Host" - nginx.ingress.kubernetes.io/cors-allow-headers: "X-Foo" - nginx.ingress.kubernetes.io/cors-allow-methods: "PUT, GET, POST, OPTIONS" - nginx.ingress.kubernetes.io/cors-allow-origin: "*" - nginx.ingress.kubernetes.io/cors-max-age: "42" - -spec: - ingressClassName: nginx - rules: - - host: cors.localhost - http: - paths: - - path: / - pathType: Exact - backend: - service: - name: whoami - port: - number: 80 diff --git a/pkg/provider/kubernetes/ingress-nginx/fixtures/ingresses/09-ingress-with-service-upstream.yml b/pkg/provider/kubernetes/ingress-nginx/fixtures/ingresses/09-ingress-with-service-upstream.yml deleted file mode 100644 index 2a2adcec2..000000000 --- a/pkg/provider/kubernetes/ingress-nginx/fixtures/ingresses/09-ingress-with-service-upstream.yml +++ /dev/null @@ -1,22 +0,0 @@ ---- -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: ingress-with-service-upstream - namespace: default - annotations: - nginx.ingress.kubernetes.io/service-upstream: "true" - -spec: - ingressClassName: nginx - rules: - - host: service-upstream.localhost - http: - paths: - - path: / - pathType: Exact - backend: - service: - name: whoami - port: - number: 80 diff --git a/pkg/provider/kubernetes/ingress-nginx/fixtures/secrets.yml b/pkg/provider/kubernetes/ingress-nginx/fixtures/secrets.yml deleted file mode 100644 index 7c53f7fb0..000000000 --- a/pkg/provider/kubernetes/ingress-nginx/fixtures/secrets.yml +++ /dev/null @@ -1,9 +0,0 @@ -kind: Secret -apiVersion: v1 -metadata: - namespace: default - name: whoami-tls - -data: - tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0t - tls.key: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0t diff --git a/pkg/provider/kubernetes/ingress-nginx/fixtures/services.yml b/pkg/provider/kubernetes/ingress-nginx/fixtures/services.yml deleted file mode 100644 index b658083aa..000000000 --- a/pkg/provider/kubernetes/ingress-nginx/fixtures/services.yml +++ /dev/null @@ -1,80 +0,0 @@ -kind: Service -apiVersion: v1 -metadata: - name: whoami - namespace: default - -spec: - clusterIP: 10.10.10.1 - ports: - - name: web2 - protocol: TCP - port: 8000 - targetPort: web2 - - name: web - protocol: TCP - port: 80 - targetPort: web - selector: - app: whoami - task: whoami - ---- -kind: EndpointSlice -apiVersion: discovery.k8s.io/v1 -metadata: - name: whoami - namespace: default - labels: - kubernetes.io/service-name: whoami - -addressType: IPv4 -ports: - - name: web - port: 80 - - name: web2 - port: 8000 -endpoints: - - addresses: - - 10.10.0.1 - - 10.10.0.2 - conditions: - ready: true - ---- -apiVersion: v1 -kind: Service -metadata: - name: whoami-tls - namespace: default - -spec: - ports: - - name: websecure - protocol: TCP - appProtocol: https - port: 443 - targetPort: websecure - selector: - app: whoami-tls - task: whoami - ---- -kind: EndpointSlice -apiVersion: discovery.k8s.io/v1 -metadata: - name: whoami-tls - namespace: default - labels: - kubernetes.io/service-name: whoami-tls - -addressType: IPv4 -ports: - - name: websecure - port: 8443 -endpoints: - - addresses: - - 10.10.0.5 - - 10.10.0.6 - conditions: - ready: true diff --git a/pkg/provider/kubernetes/ingress-nginx/kubernetes.go b/pkg/provider/kubernetes/ingress-nginx/kubernetes.go deleted file mode 100644 index e8f583ba0..000000000 --- a/pkg/provider/kubernetes/ingress-nginx/kubernetes.go +++ /dev/null @@ -1,1118 +0,0 @@ -package ingressnginx - -import ( - "context" - "errors" - "fmt" - "maps" - "math" - "net" - "os" - "regexp" - "slices" - "strconv" - "strings" - "time" - - "github.com/cenkalti/backoff/v4" - "github.com/mitchellh/hashstructure" - "github.com/rs/zerolog/log" - ptypes "github.com/traefik/paerser/types" - "github.com/traefik/traefik/v3/pkg/config/dynamic" - "github.com/traefik/traefik/v3/pkg/job" - "github.com/traefik/traefik/v3/pkg/observability/logs" - "github.com/traefik/traefik/v3/pkg/provider" - "github.com/traefik/traefik/v3/pkg/provider/kubernetes/k8s" - "github.com/traefik/traefik/v3/pkg/safe" - "github.com/traefik/traefik/v3/pkg/tls" - "github.com/traefik/traefik/v3/pkg/types" - corev1 "k8s.io/api/core/v1" - netv1 "k8s.io/api/networking/v1" - "k8s.io/utils/ptr" -) - -const ( - providerName = "kubernetesingressnginx" - - annotationIngressClass = "kubernetes.io/ingress.class" - - defaultControllerName = "k8s.io/ingress-nginx" - defaultAnnotationValue = "nginx" - - defaultBackendName = "default-backend" - defaultBackendTLSName = "default-backend-tls" -) - -type backendAddress struct { - Address string - Fenced bool -} - -type namedServersTransport struct { - Name string - ServersTransport *dynamic.ServersTransport -} - -type certBlocks struct { - CA *types.FileOrContent - Certificate *tls.Certificate -} - -// Provider holds configurations of the provider. -type Provider struct { - Endpoint string `description:"Kubernetes server endpoint (required for external cluster client)." json:"endpoint,omitempty" toml:"endpoint,omitempty" yaml:"endpoint,omitempty"` - Token types.FileOrContent `description:"Kubernetes bearer token (not needed for in-cluster client). It accepts either a token value or a file path to the token." json:"token,omitempty" toml:"token,omitempty" yaml:"token,omitempty" loggable:"false"` - CertAuthFilePath string `description:"Kubernetes certificate authority file path (not needed for in-cluster client)." json:"certAuthFilePath,omitempty" toml:"certAuthFilePath,omitempty" yaml:"certAuthFilePath,omitempty"` - ThrottleDuration ptypes.Duration `description:"Ingress refresh throttle duration." json:"throttleDuration,omitempty" toml:"throttleDuration,omitempty" yaml:"throttleDuration,omitempty" export:"true"` - - WatchNamespace string `description:"Namespace the controller watches for updates to Kubernetes objects. All namespaces are watched if this parameter is left empty." json:"watchNamespace,omitempty" toml:"watchNamespace,omitempty" yaml:"watchNamespace,omitempty" export:"true"` - WatchNamespaceSelector string `description:"Selector selects namespaces the controller watches for updates to Kubernetes objects." json:"watchNamespaceSelector,omitempty" toml:"watchNamespaceSelector,omitempty" yaml:"watchNamespaceSelector,omitempty" export:"true"` - - IngressClass string `description:"Name of the ingress class this controller satisfies." json:"ingressClass,omitempty" toml:"ingressClass,omitempty" yaml:"ingressClass,omitempty" export:"true"` - ControllerClass string `description:"Ingress Class Controller value this controller satisfies." json:"controllerClass,omitempty" toml:"controllerClass,omitempty" yaml:"controllerClass,omitempty" export:"true"` - WatchIngressWithoutClass bool `description:"Define if Ingress Controller should also watch for Ingresses without an IngressClass or the annotation specified." json:"watchIngressWithoutClass,omitempty" toml:"watchIngressWithoutClass,omitempty" yaml:"watchIngressWithoutClass,omitempty" export:"true"` - IngressClassByName bool `description:"Define if Ingress Controller should watch for Ingress Class by Name together with Controller Class." json:"ingressClassByName,omitempty" toml:"ingressClassByName,omitempty" yaml:"ingressClassByName,omitempty" export:"true"` - - // TODO: support report-node-internal-ip-address and update-status. - PublishService string `description:"Service fronting the Ingress controller. Takes the form 'namespace/name'." json:"publishService,omitempty" toml:"publishService,omitempty" yaml:"publishService,omitempty" export:"true"` - PublishStatusAddress []string `description:"Customized address (or addresses, separated by comma) to set as the load-balancer status of Ingress objects this controller satisfies." json:"publishStatusAddress,omitempty" toml:"publishStatusAddress,omitempty" yaml:"publishStatusAddress,omitempty"` - - DefaultBackendService string `description:"Service used to serve HTTP requests not matching any known server name (catch-all). Takes the form 'namespace/name'." json:"defaultBackendService,omitempty" toml:"defaultBackendService,omitempty" yaml:"defaultBackendService,omitempty" export:"true"` - DisableSvcExternalName bool `description:"Disable support for Services of type ExternalName." json:"disableSvcExternalName,omitempty" toml:"disableSvcExternalName,omitempty" yaml:"disableSvcExternalName,omitempty" export:"true"` - - defaultBackendServiceNamespace string - defaultBackendServiceName string - - k8sClient *clientWrapper - lastConfiguration safe.Safe -} - -func (p *Provider) SetDefaults() { - p.IngressClass = defaultAnnotationValue - p.ControllerClass = defaultControllerName -} - -// Init the provider. -func (p *Provider) Init() error { - // Validates and parses the default backend configuration. - if p.DefaultBackendService != "" { - parts := strings.Split(p.DefaultBackendService, "/") - if len(parts) != 2 { - return fmt.Errorf("invalid default backend service format: %s, expected 'namespace/name'", p.DefaultBackendService) - } - p.defaultBackendServiceNamespace = parts[0] - p.defaultBackendServiceName = parts[1] - } - - // Initializes Kubernetes client. - var err error - p.k8sClient, err = p.newK8sClient() - if err != nil { - return fmt.Errorf("creating kubernetes client: %w", err) - } - - return nil -} - -// Provide allows the k8s provider to provide configurations to traefik using the given configuration channel. -func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.Pool) error { - logger := log.With().Str(logs.ProviderName, providerName).Logger() - ctxLog := logger.WithContext(context.Background()) - - pool.GoCtx(func(ctxPool context.Context) { - operation := func() error { - eventsChan, err := p.k8sClient.WatchAll(ctxPool, p.WatchNamespace, p.WatchNamespaceSelector) - if err != nil { - logger.Error().Err(err).Msg("Error watching kubernetes events") - timer := time.NewTimer(1 * time.Second) - select { - case <-timer.C: - return err - case <-ctxPool.Done(): - return nil - } - } - - throttleDuration := time.Duration(p.ThrottleDuration) - throttledChan := throttleEvents(ctxLog, throttleDuration, pool, eventsChan) - if throttledChan != nil { - eventsChan = throttledChan - } - - for { - select { - case <-ctxPool.Done(): - return nil - case event := <-eventsChan: - // Note that event is the *first* event that came in during this - // throttling interval -- if we're hitting our throttle, we may have - // dropped events. This is fine, because we don't treat different - // event types differently. But if we do in the future, we'll need to - // track more information about the dropped events. - conf := p.loadConfiguration(ctxLog) - - confHash, err := hashstructure.Hash(conf, nil) - switch { - case err != nil: - logger.Error().Msg("Unable to hash the configuration") - case p.lastConfiguration.Get() == confHash: - logger.Debug().Msgf("Skipping Kubernetes event kind %T", event) - default: - p.lastConfiguration.Set(confHash) - configurationChan <- dynamic.Message{ - ProviderName: providerName, - Configuration: conf, - } - } - - // If we're throttling, we sleep here for the throttle duration to - // enforce that we don't refresh faster than our throttle. time.Sleep - // returns immediately if p.ThrottleDuration is 0 (no throttle). - time.Sleep(throttleDuration) - } - } - } - - notify := func(err error, time time.Duration) { - logger.Error().Err(err).Msgf("Provider error, retrying in %s", time) - } - - err := backoff.RetryNotify(safe.OperationWithRecover(operation), backoff.WithContext(job.NewBackOff(backoff.NewExponentialBackOff()), ctxPool), notify) - if err != nil { - logger.Error().Err(err).Msg("Cannot retrieve data") - } - }) - - return nil -} - -func (p *Provider) newK8sClient() (*clientWrapper, error) { - withEndpoint := "" - if p.Endpoint != "" { - withEndpoint = fmt.Sprintf(" with endpoint %v", p.Endpoint) - } - - switch { - case os.Getenv("KUBERNETES_SERVICE_HOST") != "" && os.Getenv("KUBERNETES_SERVICE_PORT") != "": - log.Info().Msgf("Creating in-cluster Provider client%s", withEndpoint) - return newInClusterClient(p.Endpoint) - case os.Getenv("KUBECONFIG") != "": - log.Info().Msgf("Creating cluster-external Provider client from KUBECONFIG %s", os.Getenv("KUBECONFIG")) - return newExternalClusterClientFromFile(os.Getenv("KUBECONFIG")) - default: - log.Info().Msgf("Creating cluster-external Provider client%s", withEndpoint) - return newExternalClusterClient(p.Endpoint, p.CertAuthFilePath, p.Token) - } -} - -func (p *Provider) loadConfiguration(ctx context.Context) *dynamic.Configuration { - conf := &dynamic.Configuration{ - HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, - ServersTransports: map[string]*dynamic.ServersTransport{}, - }, - TCP: &dynamic.TCPConfiguration{ - Routers: map[string]*dynamic.TCPRouter{}, - Services: map[string]*dynamic.TCPService{}, - }, - } - - // We configure the default backend when it is configured at the provider level. - if p.defaultBackendServiceNamespace != "" && p.defaultBackendServiceName != "" { - ib := netv1.IngressBackend{Service: &netv1.IngressServiceBackend{Name: p.defaultBackendServiceName}} - svc, err := p.buildService(p.defaultBackendServiceNamespace, ib, ingressConfig{}) - if err != nil { - log.Ctx(ctx).Error().Err(err).Msg("Cannot build default backend service") - return conf - } - - // Add the default backend service router to the configuration. - conf.HTTP.Routers[defaultBackendName] = &dynamic.Router{ - Rule: "PathPrefix(`/`)", - // "default" stands for the default rule syntax in Traefik v3, i.e. the v3 syntax. - RuleSyntax: "default", - Priority: math.MinInt32, - Service: defaultBackendName, - } - - conf.HTTP.Routers[defaultBackendTLSName] = &dynamic.Router{ - Rule: "PathPrefix(`/`)", - // "default" stands for the default rule syntax in Traefik v3, i.e. the v3 syntax. - RuleSyntax: "default", - Priority: math.MinInt32, - Service: defaultBackendName, - TLS: &dynamic.RouterTLSConfig{}, - } - - conf.HTTP.Services[defaultBackendName] = svc - } - - var ingressClasses []*netv1.IngressClass - ics, err := p.k8sClient.ListIngressClasses() - if err != nil { - log.Ctx(ctx).Warn().Err(err).Msg("Failed to list ingress classes") - } - ingressClasses = filterIngressClass(ics, p.IngressClassByName, p.IngressClass, p.ControllerClass) - - ingresses := p.k8sClient.ListIngresses() - - uniqCerts := make(map[string]*tls.CertAndStores) - for _, ingress := range ingresses { - logger := log.Ctx(ctx).With().Str("ingress", ingress.Name).Str("namespace", ingress.Namespace).Logger() - ctxIngress := logger.WithContext(ctx) - - if !p.shouldProcessIngress(ingress, ingressClasses) { - continue - } - - ingressConfig, err := parseIngressConfig(ingress) - if err != nil { - logger.Error().Err(err).Msg("Error parsing ingress configuration") - continue - } - - if err := p.updateIngressStatus(ingress); err != nil { - logger.Error().Err(err).Msg("Error while updating ingress status") - } - - var hasTLS bool - if len(ingress.Spec.TLS) > 0 { - hasTLS = true - if err := p.loadCertificates(ctxIngress, ingress, uniqCerts); err != nil { - logger.Error().Err(err).Msg("Error configuring TLS") - continue - } - } - - namedServersTransport, err := p.buildServersTransport(ingress.Namespace, ingress.Name, ingressConfig) - if err != nil { - logger.Error().Err(err).Msg("Ignoring Ingress cannot create proxy SSL configuration") - continue - } - - var defaultBackendService *dynamic.Service - if ingress.Spec.DefaultBackend != nil && ingress.Spec.DefaultBackend.Service != nil { - var err error - defaultBackendService, err = p.buildService(ingress.Namespace, *ingress.Spec.DefaultBackend, ingressConfig) - if err != nil { - logger.Error(). - Str("serviceName", ingress.Spec.DefaultBackend.Service.Name). - Str("servicePort", ingress.Spec.DefaultBackend.Service.Port.String()). - Err(err). - Msg("Cannot create default backend service") - } - } - - if defaultBackendService != nil && len(ingress.Spec.Rules) == 0 { - rt := &dynamic.Router{ - Rule: "PathPrefix(`/`)", - // "default" stands for the default rule syntax in Traefik v3, i.e. the v3 syntax. - RuleSyntax: "default", - Priority: math.MinInt32, - Service: defaultBackendName, - } - - if err := p.applyMiddlewares(ingress.Namespace, defaultBackendName, ingressConfig, hasTLS, rt, conf); err != nil { - logger.Error().Err(err).Msg("Error applying middlewares") - } - - conf.HTTP.Routers[defaultBackendName] = rt - - rtTLS := &dynamic.Router{ - Rule: "PathPrefix(`/`)", - // "default" stands for the default rule syntax in Traefik v3, i.e. the v3 syntax. - RuleSyntax: "default", - Priority: math.MinInt32, - Service: defaultBackendName, - TLS: &dynamic.RouterTLSConfig{}, - } - - if err := p.applyMiddlewares(ingress.Namespace, defaultBackendTLSName, ingressConfig, false, rtTLS, conf); err != nil { - logger.Error().Err(err).Msg("Error applying middlewares") - } - - conf.HTTP.Routers[defaultBackendTLSName] = rtTLS - - if namedServersTransport != nil && defaultBackendService.LoadBalancer != nil { - defaultBackendService.LoadBalancer.ServersTransport = namedServersTransport.Name - conf.HTTP.ServersTransports[namedServersTransport.Name] = namedServersTransport.ServersTransport - } - conf.HTTP.Services[defaultBackendName] = defaultBackendService - } - - for ri, rule := range ingress.Spec.Rules { - if ptr.Deref(ingressConfig.SSLPassthrough, false) { - if rule.Host == "" { - logger.Error().Err(err).Msg("Cannot process ssl-passthrough for rule without host") - continue - } - - var backend *netv1.IngressBackend - if rule.HTTP != nil { - for _, path := range rule.HTTP.Paths { - if path.Path == "/" { - backend = &path.Backend - break - } - } - } else if ingress.Spec.DefaultBackend != nil { - // Passthrough with the default backend if no HTTP section. - backend = ingress.Spec.DefaultBackend - } - - if backend == nil { - logger.Error().Msgf("No backend found for ssl-passthrough for rule with host %q", rule.Host) - continue - } - - service, err := p.buildPassthroughService(ingress.Namespace, *backend, ingressConfig) - if err != nil { - logger.Error().Err(err).Msgf("Cannot create passthrough service for %s", backend.Service.Name) - continue - } - - port := backend.Service.Port.Name - if len(backend.Service.Port.Name) == 0 { - port = strconv.Itoa(int(backend.Service.Port.Number)) - } - - serviceName := provider.Normalize(ingress.Namespace + "-" + backend.Service.Name + "-" + port) - conf.TCP.Services[serviceName] = service - - routerKey := strings.TrimPrefix(provider.Normalize(ingress.Namespace+"-"+ingress.Name+"-"+rule.Host), "-") - - conf.TCP.Routers[routerKey] = &dynamic.TCPRouter{ - Rule: fmt.Sprintf("HostSNI(`%s`)", rule.Host), - // "default" stands for the default rule syntax in Traefik v3, i.e. the v3 syntax. - RuleSyntax: "default", - Service: serviceName, - TLS: &dynamic.RouterTCPTLSConfig{ - Passthrough: true, - }, - } - - continue - } - - if defaultBackendService != nil && rule.Host != "" { - key := provider.Normalize(ingress.Namespace + "-" + ingress.Name + "-default-backend") - - rt := &dynamic.Router{ - Rule: buildHostRule(rule.Host), - // "default" stands for the default rule syntax in Traefik v3, i.e. the v3 syntax. - RuleSyntax: "default", - Service: key, - } - - if err := p.applyMiddlewares(ingress.Namespace, key, ingressConfig, hasTLS, rt, conf); err != nil { - logger.Error().Err(err).Msg("Error applying middlewares") - } - - conf.HTTP.Routers[key] = rt - - rtTLS := &dynamic.Router{ - Rule: buildHostRule(rule.Host), - // "default" stands for the default rule syntax in Traefik v3, i.e. the v3 syntax. - RuleSyntax: "default", - Service: key, - TLS: &dynamic.RouterTLSConfig{}, - } - - if err := p.applyMiddlewares(ingress.Namespace, key+"-tls", ingressConfig, false, rtTLS, conf); err != nil { - logger.Error().Err(err).Msg("Error applying middlewares") - } - - conf.HTTP.Routers[key+"-tls"] = rtTLS - - if namedServersTransport != nil && defaultBackendService.LoadBalancer != nil { - defaultBackendService.LoadBalancer.ServersTransport = namedServersTransport.Name - conf.HTTP.ServersTransports[namedServersTransport.Name] = namedServersTransport.ServersTransport - } - - conf.HTTP.Services[key] = defaultBackendService - } - - if rule.HTTP == nil { - continue - } - - for pi, pa := range rule.HTTP.Paths { - // As NGINX we are ignoring resource backend. - // An Ingress backend must have se service or a resource definition. - if pa.Backend.Service == nil { - logger.Error().Str("path", pa.Path). - Err(err).Msg("Ignoring path with no service backend") - continue - } - - portString := pa.Backend.Service.Port.Name - if len(pa.Backend.Service.Port.Name) == 0 { - portString = strconv.Itoa(int(pa.Backend.Service.Port.Number)) - } - - // TODO: if no service, do not add middlewares and 503. - serviceName := provider.Normalize(ingress.Namespace + "-" + pa.Backend.Service.Name + "-" + portString) - - service, err := p.buildService(ingress.Namespace, pa.Backend, ingressConfig) - if err != nil { - logger.Error(). - Str("serviceName", pa.Backend.Service.Name). - Str("servicePort", pa.Backend.Service.Port.String()). - Err(err). - Msg("Cannot create service") - continue - } - - rt := &dynamic.Router{ - Rule: buildRule(rule.Host, pa, ingressConfig), - // "default" stands for the default rule syntax in Traefik v3, i.e. the v3 syntax. - RuleSyntax: "default", - Service: serviceName, - } - if hasTLS { - rt.TLS = &dynamic.RouterTLSConfig{} - } - - routerKey := provider.Normalize(fmt.Sprintf("%s-%s-rule-%d-path-%d", ingress.Namespace, ingress.Name, ri, pi)) - - conf.HTTP.Routers[routerKey] = rt - conf.HTTP.Services[serviceName] = service - - if namedServersTransport != nil && service.LoadBalancer != nil { - service.LoadBalancer.ServersTransport = namedServersTransport.Name - conf.HTTP.ServersTransports[namedServersTransport.Name] = namedServersTransport.ServersTransport - } - - if err := p.applyMiddlewares(ingress.Namespace, routerKey, ingressConfig, hasTLS, rt, conf); err != nil { - logger.Error().Err(err).Msg("Error applying middlewares") - } - } - } - } - - conf.TLS = &dynamic.TLSConfiguration{ - Certificates: slices.Collect(maps.Values(uniqCerts)), - } - - return conf -} - -func (p *Provider) buildServersTransport(namespace, name string, cfg ingressConfig) (*namedServersTransport, error) { - scheme := parseBackendProtocol(ptr.Deref(cfg.BackendProtocol, "HTTP")) - if scheme != "https" { - return nil, nil - } - - nst := &namedServersTransport{ - Name: provider.Normalize(namespace + "-" + name), - ServersTransport: &dynamic.ServersTransport{ - ServerName: ptr.Deref(cfg.ProxySSLName, ptr.Deref(cfg.ProxySSLServerName, "")), - InsecureSkipVerify: strings.ToLower(ptr.Deref(cfg.ProxySSLVerify, "off")) == "on", - }, - } - - if sslSecret := ptr.Deref(cfg.ProxySSLSecret, ""); sslSecret != "" { - parts := strings.Split(sslSecret, "/") - if len(parts) != 2 { - return nil, fmt.Errorf("malformed proxy SSL secret: %s, expected namespace/name", sslSecret) - } - - blocks, err := p.certificateBlocks(parts[0], parts[1]) - if err != nil { - return nil, fmt.Errorf("getting certificate blocks: %w", err) - } - - if blocks.CA != nil { - nst.ServersTransport.RootCAs = []types.FileOrContent{*blocks.CA} - } - - if blocks.Certificate != nil { - nst.ServersTransport.Certificates = []tls.Certificate{*blocks.Certificate} - } - } - - return nst, nil -} - -func (p *Provider) buildService(namespace string, backend netv1.IngressBackend, cfg ingressConfig) (*dynamic.Service, error) { - backendAddresses, err := p.getBackendAddresses(namespace, backend, cfg) - if err != nil { - return nil, fmt.Errorf("getting backend addresses: %w", err) - } - - lb := &dynamic.ServersLoadBalancer{} - lb.SetDefaults() - - if ptr.Deref(cfg.Affinity, "") != "" { - lb.Sticky = &dynamic.Sticky{ - Cookie: &dynamic.Cookie{ - Name: ptr.Deref(cfg.SessionCookieName, "INGRESSCOOKIE"), - Secure: ptr.Deref(cfg.SessionCookieSecure, false), - HTTPOnly: true, // Default value in Nginx. - SameSite: strings.ToLower(ptr.Deref(cfg.SessionCookieSameSite, "")), - MaxAge: ptr.Deref(cfg.SessionCookieMaxAge, 0), - Path: ptr.To(ptr.Deref(cfg.SessionCookiePath, "/")), - Domain: ptr.Deref(cfg.SessionCookieDomain, ""), - }, - } - } - - scheme := parseBackendProtocol(ptr.Deref(cfg.BackendProtocol, "HTTP")) - - svc := &dynamic.Service{LoadBalancer: lb} - for _, addr := range backendAddresses { - svc.LoadBalancer.Servers = append(svc.LoadBalancer.Servers, dynamic.Server{ - URL: fmt.Sprintf("%s://%s", scheme, addr.Address), - }) - } - - return svc, nil -} - -func (p *Provider) buildPassthroughService(namespace string, backend netv1.IngressBackend, cfg ingressConfig) (*dynamic.TCPService, error) { - backendAddresses, err := p.getBackendAddresses(namespace, backend, cfg) - if err != nil { - return nil, fmt.Errorf("getting backend addresses: %w", err) - } - - lb := &dynamic.TCPServersLoadBalancer{} - for _, addr := range backendAddresses { - lb.Servers = append(lb.Servers, dynamic.TCPServer{ - Address: addr.Address, - }) - } - - return &dynamic.TCPService{LoadBalancer: lb}, nil -} - -func (p *Provider) getBackendAddresses(namespace string, backend netv1.IngressBackend, cfg ingressConfig) ([]backendAddress, error) { - service, err := p.k8sClient.GetService(namespace, backend.Service.Name) - if err != nil { - return nil, fmt.Errorf("getting service: %w", err) - } - - if p.DisableSvcExternalName && service.Spec.Type == corev1.ServiceTypeExternalName { - return nil, errors.New("externalName services not allowed") - } - - var portName string - var portSpec corev1.ServicePort - var match bool - for _, p := range service.Spec.Ports { - // A port with number 0 or an empty name is not allowed, this case is there for the default backend service. - if (backend.Service.Port.Number == 0 && backend.Service.Port.Name == "") || - (backend.Service.Port.Number == p.Port || (backend.Service.Port.Name == p.Name && len(p.Name) > 0)) { - portName = p.Name - portSpec = p - match = true - break - } - } - if !match { - return nil, errors.New("service port not found") - } - - if service.Spec.Type == corev1.ServiceTypeExternalName { - return []backendAddress{{Address: net.JoinHostPort(service.Spec.ExternalName, strconv.Itoa(int(portSpec.Port)))}}, nil - } - - // When service upstream is set to true we return the service ClusterIP as the backend address. - if ptr.Deref(cfg.ServiceUpstream, false) { - return []backendAddress{{Address: net.JoinHostPort(service.Spec.ClusterIP, strconv.Itoa(int(portSpec.Port)))}}, nil - } - - endpointSlices, err := p.k8sClient.GetEndpointSlicesForService(namespace, backend.Service.Name) - if err != nil { - return nil, fmt.Errorf("getting endpointslices: %w", err) - } - - var addresses []backendAddress - uniqAddresses := map[string]struct{}{} - for _, endpointSlice := range endpointSlices { - var port int32 - for _, p := range endpointSlice.Ports { - if portName == *p.Name { - port = *p.Port - break - } - } - if port == 0 { - continue - } - - for _, endpoint := range endpointSlice.Endpoints { - if !k8s.EndpointServing(endpoint) { - continue - } - - for _, address := range endpoint.Addresses { - if _, ok := uniqAddresses[address]; ok { - continue - } - - uniqAddresses[address] = struct{}{} - addresses = append(addresses, backendAddress{ - Address: net.JoinHostPort(address, strconv.Itoa(int(port))), - Fenced: ptr.Deref(endpoint.Conditions.Terminating, false) && ptr.Deref(endpoint.Conditions.Serving, false), - }) - } - } - } - - return addresses, nil -} - -func (p *Provider) updateIngressStatus(ing *netv1.Ingress) error { - if p.PublishService == "" && len(p.PublishStatusAddress) == 0 { - // Nothing to do, no PublishService or PublishStatusAddress defined. - return nil - } - - if len(p.PublishStatusAddress) > 0 { - ingStatus := make([]netv1.IngressLoadBalancerIngress, 0, len(p.PublishStatusAddress)) - for _, nameOrIP := range p.PublishStatusAddress { - if net.ParseIP(nameOrIP) != nil { - ingStatus = append(ingStatus, netv1.IngressLoadBalancerIngress{IP: nameOrIP}) - continue - } - - ingStatus = append(ingStatus, netv1.IngressLoadBalancerIngress{Hostname: nameOrIP}) - } - - return p.k8sClient.UpdateIngressStatus(ing, ingStatus) - } - - serviceInfo := strings.Split(p.PublishService, "/") - if len(serviceInfo) != 2 { - return fmt.Errorf("parsing publishService, 'namespace/service' format expected: %s", p.PublishService) - } - - serviceNamespace, serviceName := serviceInfo[0], serviceInfo[1] - - service, err := p.k8sClient.GetService(serviceNamespace, serviceName) - if err != nil { - return fmt.Errorf("getting service: %w", err) - } - - var ingressStatus []netv1.IngressLoadBalancerIngress - - switch service.Spec.Type { - case corev1.ServiceTypeExternalName: - ingressStatus = []netv1.IngressLoadBalancerIngress{{ - Hostname: service.Spec.ExternalName, - }} - - case corev1.ServiceTypeClusterIP: - ingressStatus = []netv1.IngressLoadBalancerIngress{{ - IP: service.Spec.ClusterIP, - }} - - case corev1.ServiceTypeNodePort: - if service.Spec.ExternalIPs == nil { - ingressStatus = []netv1.IngressLoadBalancerIngress{{ - IP: service.Spec.ClusterIP, - }} - } else { - ingressStatus = make([]netv1.IngressLoadBalancerIngress, 0, len(service.Spec.ExternalIPs)) - for _, ip := range service.Spec.ExternalIPs { - ingressStatus = append(ingressStatus, netv1.IngressLoadBalancerIngress{IP: ip}) - } - } - - case corev1.ServiceTypeLoadBalancer: - ingressStatus, err = convertSlice[netv1.IngressLoadBalancerIngress](service.Status.LoadBalancer.Ingress) - if err != nil { - return fmt.Errorf("converting ingress loadbalancer status: %w", err) - } - for _, ip := range service.Spec.ExternalIPs { - // Avoid duplicates in the ingress status. - var found bool - for _, status := range ingressStatus { - if status.IP == ip || status.Hostname == ip { - found = true - continue - } - } - if !found { - ingressStatus = append(ingressStatus, netv1.IngressLoadBalancerIngress{IP: ip}) - } - } - } - - return p.k8sClient.UpdateIngressStatus(ing, ingressStatus) -} - -func (p *Provider) shouldProcessIngress(ingress *netv1.Ingress, ingressClasses []*netv1.IngressClass) bool { - if len(ingressClasses) > 0 && ingress.Spec.IngressClassName != nil { - return slices.ContainsFunc(ingressClasses, func(ic *netv1.IngressClass) bool { - return *ingress.Spec.IngressClassName == ic.ObjectMeta.Name - }) - } - - if class, ok := ingress.Annotations[annotationIngressClass]; ok { - return class == p.IngressClass - } - - return p.WatchIngressWithoutClass -} - -func (p *Provider) loadCertificates(ctx context.Context, ingress *netv1.Ingress, uniqCerts map[string]*tls.CertAndStores) error { - for _, t := range ingress.Spec.TLS { - if t.SecretName == "" { - log.Ctx(ctx).Debug().Msg("Skipping TLS sub-section: No secret name provided") - continue - } - - certKey := ingress.Namespace + "-" + t.SecretName - if _, certExists := uniqCerts[certKey]; !certExists { - blocks, err := p.certificateBlocks(ingress.Namespace, t.SecretName) - if err != nil { - return fmt.Errorf("getting certificate blocks: %w", err) - } - - if blocks.Certificate == nil { - return fmt.Errorf("no keypair found in secret %s/%s", ingress.Namespace, t.SecretName) - } - - uniqCerts[certKey] = &tls.CertAndStores{ - Certificate: *blocks.Certificate, - } - } - } - - return nil -} - -func (p *Provider) applyMiddlewares(namespace, routerKey string, ingressConfig ingressConfig, hasTLS bool, rt *dynamic.Router, conf *dynamic.Configuration) error { - if err := p.applyBasicAuthConfiguration(namespace, routerKey, ingressConfig, rt, conf); err != nil { - return fmt.Errorf("applying basic auth configuration: %w", err) - } - - if err := applyForwardAuthConfiguration(routerKey, ingressConfig, rt, conf); err != nil { - return fmt.Errorf("applying forward auth configuration: %w", err) - } - - applyCORSConfiguration(routerKey, ingressConfig, rt, conf) - - // Apply SSL redirect is mandatory to be applied after all other middlewares. - // TODO: check how to remove this, and create the HTTP router elsewhere. - applySSLRedirectConfiguration(routerKey, ingressConfig, hasTLS, rt, conf) - - return nil -} - -func (p *Provider) applyBasicAuthConfiguration(namespace, routerName string, ingressConfig ingressConfig, rt *dynamic.Router, conf *dynamic.Configuration) error { - if ingressConfig.AuthType == nil { - return nil - } - - authType := ptr.Deref(ingressConfig.AuthType, "") - if authType != "basic" && authType != "digest" { - return fmt.Errorf("invalid auth-type %q, must be 'basic' or 'digest'", authType) - } - - authSecret := ptr.Deref(ingressConfig.AuthSecret, "") - if authSecret == "" { - return fmt.Errorf("invalid auth-secret %q, must not be empty", authSecret) - } - - authSecretParts := strings.Split(authSecret, "/") - if len(authSecretParts) > 2 { - return fmt.Errorf("invalid auth secret %q", authSecret) - } - - secretName := authSecretParts[0] - secretNamespace := namespace - if len(authSecretParts) == 2 { - secretNamespace = authSecretParts[0] - secretName = authSecretParts[1] - } - - secret, err := p.k8sClient.GetSecret(secretNamespace, secretName) - if err != nil { - return fmt.Errorf("getting secret %s: %w", authSecret, err) - } - - authSecretType := ptr.Deref(ingressConfig.AuthSecretType, "auth-file") - if authSecretType != "auth-file" && authSecretType != "auth-map" { - return fmt.Errorf("invalid auth-secret-type %q, must be 'auth-file' or 'auth-map'", authSecretType) - } - - users, err := basicAuthUsers(secret, authSecretType) - if err != nil { - return fmt.Errorf("getting users from secret %s: %w", authSecret, err) - } - - realm := ptr.Deref(ingressConfig.AuthRealm, "") - - switch authType { - case "basic": - basicMiddlewareName := routerName + "-basic-auth" - conf.HTTP.Middlewares[basicMiddlewareName] = &dynamic.Middleware{ - BasicAuth: &dynamic.BasicAuth{ - Users: users, - Realm: realm, - RemoveHeader: false, - }, - } - rt.Middlewares = append(rt.Middlewares, basicMiddlewareName) - - case "digest": - digestMiddlewareName := routerName + "-digest-auth" - conf.HTTP.Middlewares[digestMiddlewareName] = &dynamic.Middleware{ - DigestAuth: &dynamic.DigestAuth{ - Users: users, - Realm: realm, - RemoveHeader: false, - }, - } - rt.Middlewares = append(rt.Middlewares, digestMiddlewareName) - } - - return nil -} - -func (p *Provider) certificateBlocks(namespace, name string) (*certBlocks, error) { - secret, err := p.k8sClient.GetSecret(namespace, name) - if err != nil { - return nil, fmt.Errorf("fetching secret %s/%s: %w", namespace, name, err) - } - - certBytes, hasCert := secret.Data[corev1.TLSCertKey] - keyBytes, hasKey := secret.Data[corev1.TLSPrivateKeyKey] - caBytes, hasCA := secret.Data[corev1.ServiceAccountRootCAKey] - - if !hasCert && !hasKey && !hasCA { - return nil, errors.New("secret does not contain a keypair or CA certificate") - } - - var blocks certBlocks - if hasCA { - if len(caBytes) == 0 { - return nil, errors.New("secret contains an empty CA certificate") - } - - ca := types.FileOrContent(caBytes) - blocks.CA = &ca - } - - if hasKey && hasCert { - if len(certBytes) == 0 { - return nil, errors.New("secret contains an empty certificate") - } - if len(keyBytes) == 0 { - return nil, errors.New("secret contains an empty key") - } - blocks.Certificate = &tls.Certificate{ - CertFile: types.FileOrContent(certBytes), - KeyFile: types.FileOrContent(keyBytes), - } - } - - return &blocks, nil -} - -func applyCORSConfiguration(routerName string, ingressConfig ingressConfig, rt *dynamic.Router, conf *dynamic.Configuration) { - if !ptr.Deref(ingressConfig.EnableCORS, false) { - return - } - - corsMiddlewareName := routerName + "-cors" - conf.HTTP.Middlewares[corsMiddlewareName] = &dynamic.Middleware{ - Headers: &dynamic.Headers{ - AccessControlAllowCredentials: ptr.Deref(ingressConfig.EnableCORSAllowCredentials, true), - AccessControlExposeHeaders: ptr.Deref(ingressConfig.CORSExposeHeaders, []string{}), - AccessControlAllowHeaders: ptr.Deref(ingressConfig.CORSAllowHeaders, []string{"DNT", "Keep-Alive", "User-Agent", "X-Requested-With", "If-Modified-Since", "Cache-Control", "Content-Type", "Range,Authorization"}), - AccessControlAllowMethods: ptr.Deref(ingressConfig.CORSAllowMethods, []string{"GET", "PUT", "POST", "DELETE", "PATCH", "OPTIONS"}), - AccessControlAllowOriginList: ptr.Deref(ingressConfig.CORSAllowOrigin, []string{"*"}), - AccessControlMaxAge: int64(ptr.Deref(ingressConfig.CORSMaxAge, 1728000)), - }, - } - - rt.Middlewares = append(rt.Middlewares, corsMiddlewareName) -} - -func applySSLRedirectConfiguration(routerName string, ingressConfig ingressConfig, hasTLS bool, rt *dynamic.Router, conf *dynamic.Configuration) { - var forceSSLRedirect bool - if ingressConfig.ForceSSLRedirect != nil { - forceSSLRedirect = *ingressConfig.ForceSSLRedirect - } - - sslRedirect := ptr.Deref(ingressConfig.SSLRedirect, hasTLS) - - if !forceSSLRedirect && !sslRedirect { - if hasTLS { - httpRouter := &dynamic.Router{ - Rule: rt.Rule, - // "default" stands for the default rule syntax in Traefik v3, i.e. the v3 syntax. - RuleSyntax: "default", - Middlewares: rt.Middlewares, - Service: rt.Service, - } - - conf.HTTP.Routers[routerName+"-http"] = httpRouter - } - - return - } - - redirectRouter := &dynamic.Router{ - Rule: rt.Rule, - // "default" stands for the default rule syntax in Traefik v3, i.e. the v3 syntax. - RuleSyntax: "default", - Service: "noop@internal", - } - - redirectMiddlewareName := routerName + "-redirect-scheme" - conf.HTTP.Middlewares[redirectMiddlewareName] = &dynamic.Middleware{ - RedirectScheme: &dynamic.RedirectScheme{ - Scheme: "https", - Permanent: true, - }, - } - redirectRouter.Middlewares = append(redirectRouter.Middlewares, redirectMiddlewareName) - - conf.HTTP.Routers[routerName+"-redirect"] = redirectRouter -} - -func applyForwardAuthConfiguration(routerName string, ingressConfig ingressConfig, rt *dynamic.Router, conf *dynamic.Configuration) error { - if ingressConfig.AuthURL == nil { - return nil - } - - if *ingressConfig.AuthURL == "" { - return errors.New("empty auth-url found in ingress annotations") - } - - authResponseHeaders := strings.Split(ptr.Deref(ingressConfig.AuthResponseHeaders, ""), ",") - - forwardMiddlewareName := routerName + "-forward-auth" - conf.HTTP.Middlewares[forwardMiddlewareName] = &dynamic.Middleware{ - ForwardAuth: &dynamic.ForwardAuth{ - Address: *ingressConfig.AuthURL, - AuthResponseHeaders: authResponseHeaders, - }, - } - rt.Middlewares = append(rt.Middlewares, forwardMiddlewareName) - - return nil -} - -func basicAuthUsers(secret *corev1.Secret, authSecretType string) (dynamic.Users, error) { - var users dynamic.Users - if authSecretType == "auth-map" { - if len(secret.Data) == 0 { - return nil, fmt.Errorf("secret %s/%s does not contain any user credentials", secret.Namespace, secret.Name) - } - - for user, pass := range secret.Data { - users = append(users, user+":"+string(pass)) - } - - return users, nil - } - - // Default to auth-file type. - authFileContent, ok := secret.Data["auth"] - if !ok { - return nil, fmt.Errorf("secret %s/%s does not contain auth-file content key `auth`", secret.Namespace, secret.Name) - } - - // Trim lines and filter out blanks - rawLines := strings.Split(string(authFileContent), "\n") - for _, rawLine := range rawLines { - line := strings.TrimSpace(rawLine) - if line != "" && !strings.HasPrefix(line, "#") { - users = append(users, line) - } - } - - return users, nil -} - -func buildRule(host string, pa netv1.HTTPIngressPath, config ingressConfig) string { - var rules []string - if len(host) > 0 { - rules = append(rules, buildHostRule(host)) - } - - if len(pa.Path) > 0 { - pathType := ptr.Deref(pa.PathType, netv1.PathTypePrefix) - if pathType == netv1.PathTypeImplementationSpecific { - pathType = netv1.PathTypePrefix - } - - switch pathType { - case netv1.PathTypeExact: - rules = append(rules, fmt.Sprintf("Path(`%s`)", pa.Path)) - case netv1.PathTypePrefix: - if ptr.Deref(config.UseRegex, false) { - rules = append(rules, fmt.Sprintf("PathRegexp(`^%s`)", regexp.QuoteMeta(pa.Path))) - } else { - rules = append(rules, buildPrefixRule(pa.Path)) - } - } - } - - return strings.Join(rules, " && ") -} - -func buildHostRule(host string) string { - if strings.HasPrefix(host, "*.") { - host = strings.Replace(regexp.QuoteMeta(host), `\*\.`, `[a-zA-Z0-9-]+\.`, 1) - return fmt.Sprintf("HostRegexp(`^%s$`)", host) - } - - return fmt.Sprintf("Host(`%s`)", host) -} - -// buildPrefixRule is a helper function to build a path prefix rule that matches path prefix split by `/`. -// For example, the paths `/abc`, `/abc/`, and `/abc/def` would all match the prefix `/abc`, -// but the path `/abcd` would not. See TestStrictPrefixMatchingRule() for more examples. -// -// "PathPrefix" in Kubernetes Gateway API is semantically equivalent to the "Prefix" path type in the -// Kubernetes Ingress API. -func buildPrefixRule(path string) string { - if path == "/" { - return "PathPrefix(`/`)" - } - - path = strings.TrimSuffix(path, "/") - return fmt.Sprintf("(Path(`%[1]s`) || PathPrefix(`%[1]s/`))", path) -} - -func throttleEvents(ctx context.Context, throttleDuration time.Duration, pool *safe.Pool, eventsChan <-chan interface{}) chan interface{} { - if throttleDuration == 0 { - return nil - } - - // Create a buffered channel to hold the pending event (if we're delaying processing the event due to throttling). - eventsChanBuffered := make(chan interface{}, 1) - - // Run a goroutine that reads events from eventChan and does a - // non-blocking write to pendingEvent. This guarantees that writing to - // eventChan will never block, and that pendingEvent will have - // something in it if there's been an event since we read from that channel. - pool.GoCtx(func(ctxPool context.Context) { - for { - select { - case <-ctxPool.Done(): - return - case nextEvent := <-eventsChan: - select { - case eventsChanBuffered <- nextEvent: - default: - // We already have an event in eventsChanBuffered, so we'll - // do a refresh as soon as our throttle allows us to. It's fine - // to drop the event and keep whatever's in the buffer -- we - // don't do different things for different events. - log.Ctx(ctx).Debug().Msgf("Dropping event kind %T due to throttling", nextEvent) - } - } - } - }) - - return eventsChanBuffered -} diff --git a/pkg/provider/kubernetes/ingress-nginx/kubernetes_test.go b/pkg/provider/kubernetes/ingress-nginx/kubernetes_test.go deleted file mode 100644 index 37f819300..000000000 --- a/pkg/provider/kubernetes/ingress-nginx/kubernetes_test.go +++ /dev/null @@ -1,605 +0,0 @@ -package ingressnginx - -import ( - "math" - "os" - "path/filepath" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/traefik/traefik/v3/pkg/config/dynamic" - "github.com/traefik/traefik/v3/pkg/provider/kubernetes/k8s" - "github.com/traefik/traefik/v3/pkg/tls" - "github.com/traefik/traefik/v3/pkg/types" - "k8s.io/apimachinery/pkg/runtime" - kubefake "k8s.io/client-go/kubernetes/fake" - "k8s.io/utils/ptr" -) - -func TestLoadIngresses(t *testing.T) { - testCases := []struct { - desc string - ingressClass string - defaultBackendServiceName string - defaultBackendServiceNamespace string - paths []string - expected *dynamic.Configuration - }{ - { - desc: "Empty, no IngressClass", - paths: []string{ - "services.yml", - "ingresses/01-ingress-with-basicauth.yml", - }, - expected: &dynamic.Configuration{ - TCP: &dynamic.TCPConfiguration{ - Routers: map[string]*dynamic.TCPRouter{}, - Services: map[string]*dynamic.TCPService{}, - }, - HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, - ServersTransports: map[string]*dynamic.ServersTransport{}, - }, - TLS: &dynamic.TLSConfiguration{}, - }, - }, - { - desc: "Basic Auth", - paths: []string{ - "services.yml", - "ingressclasses.yml", - "ingresses/01-ingress-with-basicauth.yml", - }, - expected: &dynamic.Configuration{ - TCP: &dynamic.TCPConfiguration{ - Routers: map[string]*dynamic.TCPRouter{}, - Services: map[string]*dynamic.TCPService{}, - }, - HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{ - "default-ingress-with-basicauth-rule-0-path-0": { - Rule: "Host(`whoami.localhost`) && Path(`/basicauth`)", - RuleSyntax: "default", - Middlewares: []string{"default-ingress-with-basicauth-rule-0-path-0-basic-auth"}, - Service: "default-whoami-80", - }, - }, - Middlewares: map[string]*dynamic.Middleware{ - "default-ingress-with-basicauth-rule-0-path-0-basic-auth": { - BasicAuth: &dynamic.BasicAuth{ - Users: dynamic.Users{ - "user:{SHA}W6ph5Mm5Pz8GgiULbPgzG37mj9g=", - }, - Realm: "Authentication Required", - }, - }, - }, - Services: map[string]*dynamic.Service{ - "default-whoami-80": { - LoadBalancer: &dynamic.ServersLoadBalancer{ - Servers: []dynamic.Server{ - { - URL: "http://10.10.0.1:80", - }, - { - URL: "http://10.10.0.2:80", - }, - }, - Strategy: "wrr", - PassHostHeader: ptr.To(true), - ResponseForwarding: &dynamic.ResponseForwarding{ - FlushInterval: dynamic.DefaultFlushInterval, - }, - }, - }, - }, - ServersTransports: map[string]*dynamic.ServersTransport{}, - }, - TLS: &dynamic.TLSConfiguration{}, - }, - }, - { - desc: "Forward Auth", - paths: []string{ - "services.yml", - "ingressclasses.yml", - "ingresses/02-ingress-with-forwardauth.yml", - }, - expected: &dynamic.Configuration{ - TCP: &dynamic.TCPConfiguration{ - Routers: map[string]*dynamic.TCPRouter{}, - Services: map[string]*dynamic.TCPService{}, - }, - HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{ - "default-ingress-with-forwardauth-rule-0-path-0": { - Rule: "Host(`whoami.localhost`) && Path(`/forwardauth`)", - RuleSyntax: "default", - Middlewares: []string{"default-ingress-with-forwardauth-rule-0-path-0-forward-auth"}, - Service: "default-whoami-80", - }, - }, - Middlewares: map[string]*dynamic.Middleware{ - "default-ingress-with-forwardauth-rule-0-path-0-forward-auth": { - ForwardAuth: &dynamic.ForwardAuth{ - Address: "http://whoami.default.svc/", - AuthResponseHeaders: []string{"X-Foo"}, - }, - }, - }, - Services: map[string]*dynamic.Service{ - "default-whoami-80": { - LoadBalancer: &dynamic.ServersLoadBalancer{ - Servers: []dynamic.Server{ - { - URL: "http://10.10.0.1:80", - }, - { - URL: "http://10.10.0.2:80", - }, - }, - Strategy: "wrr", - PassHostHeader: ptr.To(true), - ResponseForwarding: &dynamic.ResponseForwarding{ - FlushInterval: dynamic.DefaultFlushInterval, - }, - }, - }, - }, - ServersTransports: map[string]*dynamic.ServersTransport{}, - }, - TLS: &dynamic.TLSConfiguration{}, - }, - }, - { - desc: "SSL Redirect", - paths: []string{ - "services.yml", - "secrets.yml", - "ingressclasses.yml", - "ingresses/03-ingress-with-ssl-redirect.yml", - }, - expected: &dynamic.Configuration{ - TCP: &dynamic.TCPConfiguration{ - Routers: map[string]*dynamic.TCPRouter{}, - Services: map[string]*dynamic.TCPService{}, - }, - HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{ - "default-ingress-with-ssl-redirect-rule-0-path-0": { - Rule: "Host(`sslredirect.localhost`) && Path(`/`)", - RuleSyntax: "default", - TLS: &dynamic.RouterTLSConfig{}, - Service: "default-whoami-80", - }, - "default-ingress-with-ssl-redirect-rule-0-path-0-redirect": { - Rule: "Host(`sslredirect.localhost`) && Path(`/`)", - RuleSyntax: "default", - Middlewares: []string{"default-ingress-with-ssl-redirect-rule-0-path-0-redirect-scheme"}, - Service: "noop@internal", - }, - "default-ingress-without-ssl-redirect-rule-0-path-0-http": { - Rule: "Host(`withoutsslredirect.localhost`) && Path(`/`)", - RuleSyntax: "default", - Service: "default-whoami-80", - }, - "default-ingress-without-ssl-redirect-rule-0-path-0": { - Rule: "Host(`withoutsslredirect.localhost`) && Path(`/`)", - RuleSyntax: "default", - TLS: &dynamic.RouterTLSConfig{}, - Service: "default-whoami-80", - }, - "default-ingress-with-force-ssl-redirect-rule-0-path-0": { - Rule: "Host(`forcesslredirect.localhost`) && Path(`/`)", - RuleSyntax: "default", - Service: "default-whoami-80", - }, - "default-ingress-with-force-ssl-redirect-rule-0-path-0-redirect": { - Rule: "Host(`forcesslredirect.localhost`) && Path(`/`)", - RuleSyntax: "default", - Middlewares: []string{"default-ingress-with-force-ssl-redirect-rule-0-path-0-redirect-scheme"}, - Service: "noop@internal", - }, - }, - Middlewares: map[string]*dynamic.Middleware{ - "default-ingress-with-ssl-redirect-rule-0-path-0-redirect-scheme": { - RedirectScheme: &dynamic.RedirectScheme{ - Scheme: "https", - Permanent: true, - }, - }, - "default-ingress-with-force-ssl-redirect-rule-0-path-0-redirect-scheme": { - RedirectScheme: &dynamic.RedirectScheme{ - Scheme: "https", - Permanent: true, - }, - }, - }, - Services: map[string]*dynamic.Service{ - "default-whoami-80": { - LoadBalancer: &dynamic.ServersLoadBalancer{ - Servers: []dynamic.Server{ - { - URL: "http://10.10.0.1:80", - }, - { - URL: "http://10.10.0.2:80", - }, - }, - Strategy: "wrr", - PassHostHeader: ptr.To(true), - ResponseForwarding: &dynamic.ResponseForwarding{ - FlushInterval: dynamic.DefaultFlushInterval, - }, - }, - }, - }, - ServersTransports: map[string]*dynamic.ServersTransport{}, - }, - TLS: &dynamic.TLSConfiguration{ - Certificates: []*tls.CertAndStores{ - { - Certificate: tls.Certificate{ - CertFile: "-----BEGIN CERTIFICATE-----", - KeyFile: "-----BEGIN CERTIFICATE-----", - }, - }, - }, - }, - }, - }, - { - desc: "SSL Passthrough", - paths: []string{ - "services.yml", - "secrets.yml", - "ingressclasses.yml", - "ingresses/04-ingress-with-ssl-passthrough.yml", - }, - expected: &dynamic.Configuration{ - TCP: &dynamic.TCPConfiguration{ - Routers: map[string]*dynamic.TCPRouter{ - "default-ingress-with-ssl-passthrough-passthrough-whoami-localhost": { - Rule: "HostSNI(`passthrough.whoami.localhost`)", - RuleSyntax: "default", - TLS: &dynamic.RouterTCPTLSConfig{ - Passthrough: true, - }, - Service: "default-whoami-tls-443", - }, - }, - Services: map[string]*dynamic.TCPService{ - "default-whoami-tls-443": { - LoadBalancer: &dynamic.TCPServersLoadBalancer{ - Servers: []dynamic.TCPServer{ - { - Address: "10.10.0.5:8443", - }, - { - Address: "10.10.0.6:8443", - }, - }, - }, - }, - }, - }, - HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{}, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{}, - ServersTransports: map[string]*dynamic.ServersTransport{}, - }, - TLS: &dynamic.TLSConfiguration{}, - }, - }, - { - desc: "Sticky Sessions", - paths: []string{ - "services.yml", - "secrets.yml", - "ingressclasses.yml", - "ingresses/06-ingress-with-sticky.yml", - }, - expected: &dynamic.Configuration{ - TCP: &dynamic.TCPConfiguration{ - Routers: map[string]*dynamic.TCPRouter{}, - Services: map[string]*dynamic.TCPService{}, - }, - HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{ - "default-ingress-with-sticky-rule-0-path-0": { - Rule: "Host(`sticky.localhost`) && Path(`/`)", - RuleSyntax: "default", - Service: "default-whoami-80", - }, - }, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{ - "default-whoami-80": { - LoadBalancer: &dynamic.ServersLoadBalancer{ - Servers: []dynamic.Server{ - { - URL: "http://10.10.0.1:80", - }, - { - URL: "http://10.10.0.2:80", - }, - }, - Strategy: "wrr", - PassHostHeader: ptr.To(true), - ResponseForwarding: &dynamic.ResponseForwarding{ - FlushInterval: dynamic.DefaultFlushInterval, - }, - Sticky: &dynamic.Sticky{ - Cookie: &dynamic.Cookie{ - Name: "foobar", - Domain: "foo.localhost", - HTTPOnly: true, - MaxAge: 42, - Path: ptr.To("/foobar"), - SameSite: "none", - Secure: true, - }, - }, - }, - }, - }, - ServersTransports: map[string]*dynamic.ServersTransport{}, - }, - TLS: &dynamic.TLSConfiguration{}, - }, - }, - { - desc: "Proxy SSL", - paths: []string{ - "services.yml", - "secrets.yml", - "ingressclasses.yml", - "ingresses/07-ingress-with-proxy-ssl.yml", - }, - expected: &dynamic.Configuration{ - TCP: &dynamic.TCPConfiguration{ - Routers: map[string]*dynamic.TCPRouter{}, - Services: map[string]*dynamic.TCPService{}, - }, - HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{ - "default-ingress-with-proxy-ssl-rule-0-path-0": { - Rule: "Host(`proxy-ssl.localhost`) && Path(`/`)", - RuleSyntax: "default", - Service: "default-whoami-tls-443", - }, - }, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{ - "default-whoami-tls-443": { - LoadBalancer: &dynamic.ServersLoadBalancer{ - Servers: []dynamic.Server{ - { - URL: "https://10.10.0.5:8443", - }, - { - URL: "https://10.10.0.6:8443", - }, - }, - Strategy: "wrr", - PassHostHeader: ptr.To(true), - ResponseForwarding: &dynamic.ResponseForwarding{ - FlushInterval: dynamic.DefaultFlushInterval, - }, - ServersTransport: "default-ingress-with-proxy-ssl", - }, - }, - }, - ServersTransports: map[string]*dynamic.ServersTransport{ - "default-ingress-with-proxy-ssl": { - ServerName: "whoami.localhost", - InsecureSkipVerify: true, - RootCAs: []types.FileOrContent{"-----BEGIN CERTIFICATE-----"}, - }, - }, - }, - TLS: &dynamic.TLSConfiguration{}, - }, - }, - { - desc: "CORS", - paths: []string{ - "services.yml", - "ingressclasses.yml", - "ingresses/08-ingress-with-cors.yml", - }, - expected: &dynamic.Configuration{ - TCP: &dynamic.TCPConfiguration{ - Routers: map[string]*dynamic.TCPRouter{}, - Services: map[string]*dynamic.TCPService{}, - }, - HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{ - "default-ingress-with-cors-rule-0-path-0": { - Rule: "Host(`cors.localhost`) && Path(`/`)", - RuleSyntax: "default", - Middlewares: []string{"default-ingress-with-cors-rule-0-path-0-cors"}, - Service: "default-whoami-80", - }, - }, - Middlewares: map[string]*dynamic.Middleware{ - "default-ingress-with-cors-rule-0-path-0-cors": { - Headers: &dynamic.Headers{ - AccessControlAllowCredentials: true, - AccessControlAllowHeaders: []string{"X-Foo"}, - AccessControlAllowMethods: []string{"PUT", "GET", "POST", "OPTIONS"}, - AccessControlAllowOriginList: []string{"*"}, - AccessControlExposeHeaders: []string{"X-Forwarded-For", "X-Forwarded-Host"}, - AccessControlMaxAge: 42, - }, - }, - }, - Services: map[string]*dynamic.Service{ - "default-whoami-80": { - LoadBalancer: &dynamic.ServersLoadBalancer{ - Servers: []dynamic.Server{ - { - URL: "http://10.10.0.1:80", - }, - { - URL: "http://10.10.0.2:80", - }, - }, - Strategy: "wrr", - PassHostHeader: ptr.To(true), - ResponseForwarding: &dynamic.ResponseForwarding{ - FlushInterval: dynamic.DefaultFlushInterval, - }, - }, - }, - }, - ServersTransports: map[string]*dynamic.ServersTransport{}, - }, - TLS: &dynamic.TLSConfiguration{}, - }, - }, - { - desc: "Service Upstream", - paths: []string{ - "services.yml", - "ingressclasses.yml", - "ingresses/09-ingress-with-service-upstream.yml", - }, - expected: &dynamic.Configuration{ - TCP: &dynamic.TCPConfiguration{ - Routers: map[string]*dynamic.TCPRouter{}, - Services: map[string]*dynamic.TCPService{}, - }, - HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{ - "default-ingress-with-service-upstream-rule-0-path-0": { - Rule: "Host(`service-upstream.localhost`) && Path(`/`)", - RuleSyntax: "default", - Service: "default-whoami-80", - }, - }, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{ - "default-whoami-80": { - LoadBalancer: &dynamic.ServersLoadBalancer{ - Servers: []dynamic.Server{ - { - URL: "http://10.10.10.1:80", - }, - }, - Strategy: "wrr", - PassHostHeader: ptr.To(true), - ResponseForwarding: &dynamic.ResponseForwarding{ - FlushInterval: dynamic.DefaultFlushInterval, - }, - }, - }, - }, - ServersTransports: map[string]*dynamic.ServersTransport{}, - }, - TLS: &dynamic.TLSConfiguration{}, - }, - }, - { - desc: "Default Backend", - defaultBackendServiceName: "whoami", - defaultBackendServiceNamespace: "default", - paths: []string{ - "services.yml", - }, - expected: &dynamic.Configuration{ - TCP: &dynamic.TCPConfiguration{ - Routers: map[string]*dynamic.TCPRouter{}, - Services: map[string]*dynamic.TCPService{}, - }, - HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{ - "default-backend": { - Rule: "PathPrefix(`/`)", - RuleSyntax: "default", - Priority: math.MinInt32, - Service: "default-backend", - }, - "default-backend-tls": { - Rule: "PathPrefix(`/`)", - RuleSyntax: "default", - Priority: math.MinInt32, - TLS: &dynamic.RouterTLSConfig{}, - Service: "default-backend", - }, - }, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{ - "default-backend": { - LoadBalancer: &dynamic.ServersLoadBalancer{ - Servers: []dynamic.Server{ - { - URL: "http://10.10.0.1:8000", - }, - { - URL: "http://10.10.0.2:8000", - }, - }, - Strategy: "wrr", - PassHostHeader: ptr.To(true), - ResponseForwarding: &dynamic.ResponseForwarding{ - FlushInterval: dynamic.DefaultFlushInterval, - }, - }, - }, - }, - ServersTransports: map[string]*dynamic.ServersTransport{}, - }, - TLS: &dynamic.TLSConfiguration{}, - }, - }, - } - - for _, test := range testCases { - t.Run(test.desc, func(t *testing.T) { - t.Parallel() - - k8sObjects := readResources(t, test.paths) - kubeClient := kubefake.NewClientset(k8sObjects...) - client := newClient(kubeClient) - - eventCh, err := client.WatchAll(t.Context(), "", "") - require.NoError(t, err) - - if len(k8sObjects) > 0 { - // just wait for the first event - <-eventCh - } - - p := Provider{ - k8sClient: client, - defaultBackendServiceName: test.defaultBackendServiceName, - defaultBackendServiceNamespace: test.defaultBackendServiceNamespace, - } - p.SetDefaults() - - conf := p.loadConfiguration(t.Context()) - assert.Equal(t, test.expected, conf) - }) - } -} - -func readResources(t *testing.T, paths []string) []runtime.Object { - t.Helper() - - var k8sObjects []runtime.Object - for _, path := range paths { - yamlContent, err := os.ReadFile(filepath.FromSlash("./fixtures/" + path)) - if err != nil { - panic(err) - } - - k8sObjects = append(k8sObjects, k8s.MustParseYaml(yamlContent)...) - } - - return k8sObjects -} diff --git a/pkg/provider/kubernetes/ingress/annotations_test.go b/pkg/provider/kubernetes/ingress/annotations_test.go index 1f86f51c7..bda404889 100644 --- a/pkg/provider/kubernetes/ingress/annotations_test.go +++ b/pkg/provider/kubernetes/ingress/annotations_test.go @@ -6,7 +6,6 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/traefik/traefik/v3/pkg/config/dynamic" - otypes "github.com/traefik/traefik/v3/pkg/observability/types" "github.com/traefik/traefik/v3/pkg/types" ) @@ -59,10 +58,9 @@ func Test_parseRouterConfig(t *testing.T) { Options: "foobar", }, Observability: &dynamic.RouterObservabilityConfig{ - AccessLogs: pointer(true), - Tracing: pointer(true), - Metrics: pointer(true), - TraceVerbosity: otypes.MinimalVerbosity, + AccessLogs: pointer(true), + Tracing: pointer(true), + Metrics: pointer(true), }, }, }, diff --git a/pkg/provider/kubernetes/ingress/client_test.go b/pkg/provider/kubernetes/ingress/client_test.go index 9014265bd..24240f277 100644 --- a/pkg/provider/kubernetes/ingress/client_test.go +++ b/pkg/provider/kubernetes/ingress/client_test.go @@ -1,6 +1,7 @@ package ingress import ( + "context" "errors" "testing" "time" @@ -248,7 +249,7 @@ func TestClientIgnoresEmptyEndpointSliceUpdates(t *testing.T) { assert.Fail(t, "expected to receive event for endpointslices") } - emptyEndpointSlice, err = kubeClient.DiscoveryV1().EndpointSlices("test").Get(t.Context(), "empty-endpointslice", metav1.GetOptions{}) + emptyEndpointSlice, err = kubeClient.DiscoveryV1().EndpointSlices("test").Get(context.TODO(), "empty-endpointslice", metav1.GetOptions{}) assert.NoError(t, err) // Update endpoint annotation and resource version (apparently not done by fake client itself) @@ -256,7 +257,7 @@ func TestClientIgnoresEmptyEndpointSliceUpdates(t *testing.T) { // This reflects the behavior of kubernetes controllers which use endpoint annotations for leader election. emptyEndpointSlice.Annotations["test-annotation"] = "___" emptyEndpointSlice.ResourceVersion = "1245" - _, err = kubeClient.DiscoveryV1().EndpointSlices("test").Update(t.Context(), emptyEndpointSlice, metav1.UpdateOptions{}) + _, err = kubeClient.DiscoveryV1().EndpointSlices("test").Update(context.TODO(), emptyEndpointSlice, metav1.UpdateOptions{}) require.NoError(t, err) select { @@ -268,12 +269,12 @@ func TestClientIgnoresEmptyEndpointSliceUpdates(t *testing.T) { case <-time.After(50 * time.Millisecond): } - filledEndpointSlice, err = kubeClient.DiscoveryV1().EndpointSlices("test").Get(t.Context(), "filled-endpointslice", metav1.GetOptions{}) + filledEndpointSlice, err = kubeClient.DiscoveryV1().EndpointSlices("test").Get(context.TODO(), "filled-endpointslice", metav1.GetOptions{}) assert.NoError(t, err) filledEndpointSlice.Endpoints[0].Addresses[0] = "10.13.37.2" filledEndpointSlice.ResourceVersion = "1235" - _, err = kubeClient.DiscoveryV1().EndpointSlices("test").Update(t.Context(), filledEndpointSlice, metav1.UpdateOptions{}) + _, err = kubeClient.DiscoveryV1().EndpointSlices("test").Update(context.TODO(), filledEndpointSlice, metav1.UpdateOptions{}) require.NoError(t, err) select { @@ -295,7 +296,7 @@ func TestClientIgnoresEmptyEndpointSliceUpdates(t *testing.T) { newPortNumber := int32(42) filledEndpointSlice.Ports[0].Port = &newPortNumber filledEndpointSlice.ResourceVersion = "1236" - _, err = kubeClient.DiscoveryV1().EndpointSlices("test").Update(t.Context(), filledEndpointSlice, metav1.UpdateOptions{}) + _, err = kubeClient.DiscoveryV1().EndpointSlices("test").Update(context.TODO(), filledEndpointSlice, metav1.UpdateOptions{}) require.NoError(t, err) select { diff --git a/pkg/provider/kubernetes/ingress/fixtures/Ingress-with-native-lb-by-default-but-service-has-disabled-nativelb.yml b/pkg/provider/kubernetes/ingress/fixtures/Ingress-with-native-lb-by-default-but-service-has-disabled-nativelb.yml deleted file mode 100644 index a076f3c3e..000000000 --- a/pkg/provider/kubernetes/ingress/fixtures/Ingress-with-native-lb-by-default-but-service-has-disabled-nativelb.yml +++ /dev/null @@ -1,53 +0,0 @@ -kind: Ingress -apiVersion: networking.k8s.io/v1 -metadata: - name: global-native-lb - namespace: default -spec: - rules: - - host: traefik.tchouk - http: - paths: - - path: /bar - backend: - service: - name: native-disabled-svc - port: - name: web - number: 8080 - pathType: Prefix - ---- -kind: Service -apiVersion: v1 -metadata: - name: native-disabled-svc - namespace: default - annotations: - traefik.ingress.kubernetes.io/service.nativelb: "false" -spec: - ports: - - name: web - port: 8080 - clusterIP: 10.0.0.1 - type: ClusterIP - externalName: traefik.wtf - ---- -kind: EndpointSlice -apiVersion: discovery.k8s.io/v1 -metadata: - name: native-disabled-svc-abc - namespace: default - labels: - kubernetes.io/service-name: native-disabled-svc -addressType: IPv4 -ports: - - name: web - port: 8080 -endpoints: - - addresses: - - 10.10.0.20 - - 10.10.0.21 - conditions: - ready: true \ No newline at end of file diff --git a/pkg/provider/kubernetes/ingress/fixtures/Ingress-with-strict-prefix-matching.yml b/pkg/provider/kubernetes/ingress/fixtures/Ingress-with-strict-prefix-matching.yml deleted file mode 100644 index c52467d71..000000000 --- a/pkg/provider/kubernetes/ingress/fixtures/Ingress-with-strict-prefix-matching.yml +++ /dev/null @@ -1,49 +0,0 @@ -kind: Ingress -apiVersion: networking.k8s.io/v1 -metadata: - name: "" - namespace: testing - -spec: - rules: - - http: - paths: - - path: /bar - backend: - service: - name: service1 - port: - number: 80 - pathType: Prefix - ---- -kind: Service -apiVersion: v1 -metadata: - name: service1 - namespace: testing - -spec: - ports: - - port: 80 - clusterIP: 10.0.0.1 - ---- -kind: EndpointSlice -apiVersion: discovery.k8s.io/v1 -metadata: - name: service1-abc - namespace: testing - labels: - kubernetes.io/service-name: service1 - -addressType: IPv4 -ports: - - port: 8080 - name: "" -endpoints: - - addresses: - - 10.10.0.1 - - 10.21.0.1 - conditions: - ready: true diff --git a/pkg/provider/kubernetes/ingress/kubernetes.go b/pkg/provider/kubernetes/ingress/kubernetes.go index 7a1919b3e..591bdcf55 100644 --- a/pkg/provider/kubernetes/ingress/kubernetes.go +++ b/pkg/provider/kubernetes/ingress/kubernetes.go @@ -21,7 +21,7 @@ import ( ptypes "github.com/traefik/paerser/types" "github.com/traefik/traefik/v3/pkg/config/dynamic" "github.com/traefik/traefik/v3/pkg/job" - "github.com/traefik/traefik/v3/pkg/observability/logs" + "github.com/traefik/traefik/v3/pkg/logs" "github.com/traefik/traefik/v3/pkg/provider" "github.com/traefik/traefik/v3/pkg/provider/kubernetes/k8s" "github.com/traefik/traefik/v3/pkg/safe" @@ -56,7 +56,6 @@ type Provider struct { DisableIngressClassLookup bool `description:"Disables the lookup of IngressClasses (Deprecated, please use DisableClusterScopeResources)." json:"disableIngressClassLookup,omitempty" toml:"disableIngressClassLookup,omitempty" yaml:"disableIngressClassLookup,omitempty" export:"true"` DisableClusterScopeResources bool `description:"Disables the lookup of cluster scope resources (incompatible with IngressClasses and NodePortLB enabled services)." json:"disableClusterScopeResources,omitempty" toml:"disableClusterScopeResources,omitempty" yaml:"disableClusterScopeResources,omitempty" export:"true"` NativeLBByDefault bool `description:"Defines whether to use Native Kubernetes load-balancing mode by default." json:"nativeLBByDefault,omitempty" toml:"nativeLBByDefault,omitempty" yaml:"nativeLBByDefault,omitempty" export:"true"` - StrictPrefixMatching bool `description:"Make prefix matching strictly comply with the Kubernetes Ingress specification (path-element-wise matching instead of character-by-character string matching)." json:"strictPrefixMatching,omitempty" toml:"strictPrefixMatching,omitempty" yaml:"strictPrefixMatching,omitempty" export:"true"` // The default rule syntax is initialized with the configuration defined by the user with the core.DefaultRuleSyntax option. DefaultRuleSyntax string `json:"-" toml:"-" yaml:"-" label:"-" file:"-"` @@ -701,7 +700,7 @@ func (p *Provider) loadRouter(rule netv1.IngressRule, pa netv1.HTTPIngressPath, matcher = "Path" } - rules = append(rules, buildRule(p.StrictPrefixMatching, matcher, pa.Path)) + rules = append(rules, fmt.Sprintf("%s(`%s`)", matcher, pa.Path)) } rt.Rule = strings.Join(rules, " && ") @@ -847,41 +846,6 @@ func makeRouterKeyWithHash(key, rule string) (string, error) { return dupKey, nil } -func buildRule(strictPrefixMatching bool, matcher string, path string) string { - // When enabled, strictPrefixMatching ensures that prefix matching follows - // the Kubernetes Ingress spec (path-element-wise instead of character-wise). - if strictPrefixMatching && matcher == "PathPrefix" { - // According to - // https://kubernetes.io/docs/concepts/services-networking/ingress/#examples, - // "/v12" should not match "/v1". - // - // Traefik's default PathPrefix matcher performs a character-wise prefix match, - // unlike Kubernetes which matches path elements. To mimic Kubernetes behavior, - // we will use Path and PathPrefix to replicate element-wise behavior. - // - // "PathPrefix" in Kubernetes Gateway API is semantically equivalent to the "Prefix" path type in the - // Kubernetes Ingress API. - return buildStrictPrefixMatchingRule(path) - } - - return fmt.Sprintf("%s(`%s`)", matcher, path) -} - -// buildStrictPrefixMatchingRule is a helper function to build a path prefix rule that matches path prefix split by `/`. -// For example, the paths `/abc`, `/abc/`, and `/abc/def` would all match the prefix `/abc`, -// but the path `/abcd` would not. See TestStrictPrefixMatchingRule() for more examples. -// -// "PathPrefix" in Kubernetes Gateway API is semantically equivalent to the "Prefix" path type in the -// Kubernetes Ingress API. -func buildStrictPrefixMatchingRule(path string) string { - if path == "/" { - return "PathPrefix(`/`)" - } - - path = strings.TrimSuffix(path, "/") - return fmt.Sprintf("(Path(`%[1]s`) || PathPrefix(`%[1]s/`))", path) -} - func throttleEvents(ctx context.Context, throttleDuration time.Duration, pool *safe.Pool, eventsChan <-chan interface{}) chan interface{} { if throttleDuration == 0 { return nil diff --git a/pkg/provider/kubernetes/ingress/kubernetes_test.go b/pkg/provider/kubernetes/ingress/kubernetes_test.go index d965e68a2..548fc4124 100644 --- a/pkg/provider/kubernetes/ingress/kubernetes_test.go +++ b/pkg/provider/kubernetes/ingress/kubernetes_test.go @@ -1,11 +1,9 @@ package ingress import ( + "context" "errors" - "fmt" "math" - "net/http" - "net/http/httptest" "os" "path/filepath" "strings" @@ -16,8 +14,6 @@ import ( "github.com/stretchr/testify/require" ptypes "github.com/traefik/paerser/types" "github.com/traefik/traefik/v3/pkg/config/dynamic" - traefikhttp "github.com/traefik/traefik/v3/pkg/muxer/http" - otypes "github.com/traefik/traefik/v3/pkg/observability/types" "github.com/traefik/traefik/v3/pkg/provider" "github.com/traefik/traefik/v3/pkg/provider/kubernetes/k8s" "github.com/traefik/traefik/v3/pkg/tls" @@ -42,7 +38,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { disableIngressClassLookup bool disableClusterScopeResources bool defaultRuleSyntax string - strictPrefixMatching bool }{ { desc: "Empty ingresses", @@ -125,10 +120,9 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { Options: "foobar", }, Observability: &dynamic.RouterObservabilityConfig{ - AccessLogs: pointer(true), - Tracing: pointer(true), - Metrics: pointer(true), - TraceVerbosity: otypes.MinimalVerbosity, + AccessLogs: pointer(true), + Tracing: pointer(true), + Metrics: pointer(true), }, }, }, @@ -1649,40 +1643,6 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { }, }, }, - { - desc: "Ingress with strict prefix matching", - expected: &dynamic.Configuration{ - HTTP: &dynamic.HTTPConfiguration{ - Middlewares: map[string]*dynamic.Middleware{}, - Routers: map[string]*dynamic.Router{ - "testing-bar": { - Rule: "(Path(`/bar`) || PathPrefix(`/bar/`))", - Service: "testing-service1-80", - }, - }, - Services: map[string]*dynamic.Service{ - "testing-service1-80": { - LoadBalancer: &dynamic.ServersLoadBalancer{ - Strategy: dynamic.BalancerStrategyWRR, - PassHostHeader: pointer(true), - ResponseForwarding: &dynamic.ResponseForwarding{ - FlushInterval: ptypes.Duration(100 * time.Millisecond), - }, - Servers: []dynamic.Server{ - { - URL: "http://10.10.0.1:8080", - }, - { - URL: "http://10.21.0.1:8080", - }, - }, - }, - }, - }, - }, - }, - strictPrefixMatching: true, - }, } for _, test := range testCases { @@ -1696,9 +1656,8 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { DisableIngressClassLookup: test.disableIngressClassLookup, DisableClusterScopeResources: test.disableClusterScopeResources, DefaultRuleSyntax: test.defaultRuleSyntax, - StrictPrefixMatching: test.strictPrefixMatching, } - conf := p.loadConfigurationFromIngresses(t.Context(), clientMock) + conf := p.loadConfigurationFromIngresses(context.Background(), clientMock) assert.Equal(t, test.expected, conf) }) @@ -1824,7 +1783,7 @@ func TestLoadConfigurationFromIngressesWithExternalNameServices(t *testing.T) { p := Provider{IngressClass: test.ingressClass} p.AllowExternalNameServices = test.allowExternalNameServices - conf := p.loadConfigurationFromIngresses(t.Context(), clientMock) + conf := p.loadConfigurationFromIngresses(context.Background(), clientMock) assert.Equal(t, test.expected, conf) }) @@ -1874,7 +1833,7 @@ func TestLoadConfigurationFromIngressesWithNativeLB(t *testing.T) { clientMock := newClientMock(generateTestFilename(test.desc)) p := Provider{IngressClass: test.ingressClass} - conf := p.loadConfigurationFromIngresses(t.Context(), clientMock) + conf := p.loadConfigurationFromIngresses(context.Background(), clientMock) assert.Equal(t, test.expected, conf) }) @@ -1935,7 +1894,7 @@ func TestLoadConfigurationFromIngressesWithNodePortLB(t *testing.T) { clientMock := newClientMock(generateTestFilename(test.desc)) p := Provider{DisableClusterScopeResources: test.clusterScopeDisabled} - conf := p.loadConfigurationFromIngresses(t.Context(), clientMock) + conf := p.loadConfigurationFromIngresses(context.Background(), clientMock) assert.Equal(t, test.expected, conf) }) @@ -2107,7 +2066,7 @@ func TestGetCertificates(t *testing.T) { t.Parallel() tlsConfigs := map[string]*tls.CertAndStores{} - err := getCertificates(t.Context(), test.ingress, test.client, tlsConfigs) + err := getCertificates(context.Background(), test.ingress, test.client, tlsConfigs) if test.errResult != "" { assert.EqualError(t, err, test.errResult) @@ -2181,37 +2140,6 @@ func TestLoadConfigurationFromIngressesWithNativeLBByDefault(t *testing.T) { }, }, }, - { - desc: "Ingress with native lb by default but service has disabled nativelb", - expected: &dynamic.Configuration{ - HTTP: &dynamic.HTTPConfiguration{ - Middlewares: map[string]*dynamic.Middleware{}, - Routers: map[string]*dynamic.Router{ - "default-global-native-lb-traefik-tchouk-bar": { - Rule: "Host(`traefik.tchouk`) && PathPrefix(`/bar`)", - Service: "default-native-disabled-svc-web", - }, - }, - Services: map[string]*dynamic.Service{ - "default-native-disabled-svc-web": { - LoadBalancer: &dynamic.ServersLoadBalancer{ - Strategy: dynamic.BalancerStrategyWRR, - ResponseForwarding: &dynamic.ResponseForwarding{FlushInterval: dynamic.DefaultFlushInterval}, - PassHostHeader: pointer(true), - Servers: []dynamic.Server{ - { - URL: "http://10.10.0.20:8080", - }, - { - URL: "http://10.10.0.21:8080", - }, - }, - }, - }, - }, - }, - }, - }, } for _, test := range testCases { @@ -2224,7 +2152,7 @@ func TestLoadConfigurationFromIngressesWithNativeLBByDefault(t *testing.T) { IngressClass: test.ingressClass, NativeLBByDefault: true, } - conf := p.loadConfigurationFromIngresses(t.Context(), clientMock) + conf := p.loadConfigurationFromIngresses(context.Background(), clientMock) assert.Equal(t, test.expected, conf) }) @@ -2324,9 +2252,9 @@ func TestIngressEndpointPublishedService(t *testing.T) { PublishedService: "default/published-service", }, } - p.loadConfigurationFromIngresses(t.Context(), client) + p.loadConfigurationFromIngresses(context.Background(), client) - ingress, err := kubeClient.NetworkingV1().Ingresses(metav1.NamespaceDefault).Get(t.Context(), "foo", metav1.GetOptions{}) + ingress, err := kubeClient.NetworkingV1().Ingresses(metav1.NamespaceDefault).Get(context.Background(), "foo", metav1.GetOptions{}) require.NoError(t, err) assert.Equal(t, test.expected, ingress.Status.LoadBalancer.Ingress) @@ -2350,108 +2278,3 @@ func readResources(t *testing.T, paths []string) []runtime.Object { return k8sObjects } - -func TestStrictPrefixMatchingRule(t *testing.T) { - tests := []struct { - path string - requestPath string - match bool - }{ // The tests are taken from https://kubernetes.io/docs/concepts/services-networking/ingress/#examples - { - path: "/foo", - requestPath: "/foo", - match: true, - }, - { - path: "/foo", - requestPath: "/foo/", - match: true, - }, - { - path: "/foo/", - requestPath: "/foo", - match: true, - }, - { - path: "/foo/", - requestPath: "/foo/", - match: true, - }, - { - path: "/aaa/bb", - requestPath: "/aaa/bbb", - match: false, - }, - { - path: "/aaa/bbb", - requestPath: "/aaa/bbb", - match: true, - }, - { - path: "/aaa/bbb/", - requestPath: "/aaa/bbb", - match: true, - }, - { - path: "/aaa/bbb", - requestPath: "/aaa/bbb/", - match: true, - }, - { - path: "/aaa/bbb", - requestPath: "/aaa/bbb/ccc", - match: true, - }, - { - path: "/aaa/bbb", - requestPath: "/aaa/bbbxyz", - match: false, - }, - { - path: "/", - requestPath: "/aaa/ccc", - match: true, - }, - { - path: "/aaa", - requestPath: "/aaa/ccc", - match: true, - }, - { - path: "/...", - requestPath: "/aaa", - match: false, - }, - { - path: "/...", - requestPath: "/.../", - match: true, - }, - } - - for _, tt := range tests { - t.Run(fmt.Sprintf("Prefix match case %s", tt.path), func(t *testing.T) { - t.Parallel() - - handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}) - parser, err := traefikhttp.NewSyntaxParser() - require.NoError(t, err) - - muxer := traefikhttp.NewMuxer(parser) - - rule := buildStrictPrefixMatchingRule(tt.path) - err = muxer.AddRoute(rule, "", 0, handler) - require.NoError(t, err) - - w := httptest.NewRecorder() - req := httptest.NewRequest(http.MethodGet, tt.requestPath, http.NoBody) - muxer.ServeHTTP(w, req) - - if tt.match { - assert.Equal(t, http.StatusOK, w.Code) - } else { - assert.Equal(t, http.StatusNotFound, w.Code) - } - }) - } -} diff --git a/pkg/provider/kubernetes/k8s/event_handler.go b/pkg/provider/kubernetes/k8s/event_handler.go index 9b3babaa9..1de67ec5e 100644 --- a/pkg/provider/kubernetes/k8s/event_handler.go +++ b/pkg/provider/kubernetes/k8s/event_handler.go @@ -11,7 +11,7 @@ type ResourceEventHandler struct { } // OnAdd is called on Add Events. -func (reh *ResourceEventHandler) OnAdd(obj interface{}, _ bool) { +func (reh *ResourceEventHandler) OnAdd(obj interface{}, isInInitialList bool) { eventHandlerFunc(reh.Ev, obj) } diff --git a/pkg/provider/kv/kv.go b/pkg/provider/kv/kv.go index a03d4e711..613320617 100644 --- a/pkg/provider/kv/kv.go +++ b/pkg/provider/kv/kv.go @@ -14,7 +14,7 @@ import ( "github.com/traefik/traefik/v3/pkg/config/dynamic" "github.com/traefik/traefik/v3/pkg/config/kv" "github.com/traefik/traefik/v3/pkg/job" - "github.com/traefik/traefik/v3/pkg/observability/logs" + "github.com/traefik/traefik/v3/pkg/logs" "github.com/traefik/traefik/v3/pkg/safe" ) @@ -55,7 +55,7 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe. ctx := logger.WithContext(context.Background()) operation := func() error { - if _, err := p.kvClient.Exists(ctx, path.Join(p.RootKey, "qmslkjdfmqlskdjfmqlksjazcueznbvbwzlkajzebvkwjdcqmlsfj"), nil); err != nil { + if _, err := p.kvClient.Exists(ctx, path.Join(p.RootKey, "qmslkjdfmqlskdjfmqlksjazçueznbvbwzlkajzebvkwjdcqmlsfj"), nil); err != nil { return fmt.Errorf("KV store connection error: %w", err) } return nil diff --git a/pkg/provider/kv/kv_test.go b/pkg/provider/kv/kv_test.go index c8dc84bbc..b439f38ae 100644 --- a/pkg/provider/kv/kv_test.go +++ b/pkg/provider/kv/kv_test.go @@ -1,6 +1,7 @@ package kv import ( + "context" "errors" "testing" "time" @@ -44,7 +45,6 @@ func Test_buildConfiguration(t *testing.T) { "traefik/http/services/Service01/loadBalancer/healthCheck/path": "foobar", "traefik/http/services/Service01/loadBalancer/healthCheck/port": "42", "traefik/http/services/Service01/loadBalancer/healthCheck/interval": "1s", - "traefik/http/services/Service01/loadBalancer/healthCheck/unhealthyinterval": "1s", "traefik/http/services/Service01/loadBalancer/healthCheck/timeout": "1s", "traefik/http/services/Service01/loadBalancer/healthCheck/hostname": "foobar", "traefik/http/services/Service01/loadBalancer/healthCheck/headers/name0": "foobar", @@ -297,7 +297,7 @@ func Test_buildConfiguration(t *testing.T) { "traefik/tls/certificates/1/stores/1": "foobar", })) - cfg, err := provider.buildConfiguration(t.Context()) + cfg, err := provider.buildConfiguration(context.Background()) require.NoError(t, err) expected := &dynamic.Configuration{ @@ -665,15 +665,14 @@ func Test_buildConfiguration(t *testing.T) { }, }, HealthCheck: &dynamic.ServerHealthCheck{ - Scheme: "foobar", - Mode: "foobar", - Path: "foobar", - Port: 42, - Interval: ptypes.Duration(time.Second), - UnhealthyInterval: pointer(ptypes.Duration(time.Second)), - Timeout: ptypes.Duration(time.Second), - Hostname: "foobar", - FollowRedirects: pointer(true), + Scheme: "foobar", + Mode: "foobar", + Path: "foobar", + Port: 42, + Interval: ptypes.Duration(time.Second), + Timeout: ptypes.Duration(time.Second), + Hostname: "foobar", + FollowRedirects: pointer(true), Headers: map[string]string{ "name0": "foobar", "name1": "foobar", @@ -957,7 +956,7 @@ func Test_buildConfiguration_KV_error(t *testing.T) { }, } - cfg, err := provider.buildConfiguration(t.Context()) + cfg, err := provider.buildConfiguration(context.Background()) require.Error(t, err) assert.Nil(t, cfg) } @@ -976,7 +975,7 @@ func TestKvWatchTree(t *testing.T) { configChan := make(chan dynamic.Message) go func() { - err := provider.watchKv(t.Context(), configChan) + err := provider.watchKv(context.Background(), configChan) require.NoError(t, err) }() diff --git a/pkg/provider/kv/redis/redis.go b/pkg/provider/kv/redis/redis.go index c6e357f03..874a6b1a1 100644 --- a/pkg/provider/kv/redis/redis.go +++ b/pkg/provider/kv/redis/redis.go @@ -21,14 +21,14 @@ type Provider struct { Username string `description:"Username for authentication." json:"username,omitempty" toml:"username,omitempty" yaml:"username,omitempty" loggable:"false"` Password string `description:"Password for authentication." json:"password,omitempty" toml:"password,omitempty" yaml:"password,omitempty" loggable:"false"` DB int `description:"Database to be selected after connecting to the server." json:"db,omitempty" toml:"db,omitempty" yaml:"db,omitempty"` - Sentinel *Sentinel `description:"Enable Sentinel support." json:"sentinel,omitempty" toml:"sentinel,omitempty" yaml:"sentinel,omitempty" export:"true"` + Sentinel *Sentinel `description:"Enable Sentinel support." json:"sentinel,omitempty" toml:"sentinel,omitempty" yaml:"sentinel,omitempty"` } // Sentinel holds the Redis Sentinel configuration. type Sentinel struct { MasterName string `description:"Name of the master." json:"masterName,omitempty" toml:"masterName,omitempty" yaml:"masterName,omitempty" export:"true"` - Username string `description:"Username for Sentinel authentication." json:"username,omitempty" toml:"username,omitempty" yaml:"username,omitempty" loggable:"false"` - Password string `description:"Password for Sentinel authentication." json:"password,omitempty" toml:"password,omitempty" yaml:"password,omitempty" loggable:"false"` + Username string `description:"Username for Sentinel authentication." json:"username,omitempty" toml:"username,omitempty" yaml:"username,omitempty" export:"true"` + Password string `description:"Password for Sentinel authentication." json:"password,omitempty" toml:"password,omitempty" yaml:"password,omitempty" export:"true"` LatencyStrategy bool `description:"Defines whether to route commands to the closest master or replica nodes (mutually exclusive with RandomStrategy and ReplicaStrategy)." json:"latencyStrategy,omitempty" toml:"latencyStrategy,omitempty" yaml:"latencyStrategy,omitempty" export:"true"` RandomStrategy bool `description:"Defines whether to route commands randomly to master or replica nodes (mutually exclusive with LatencyStrategy and ReplicaStrategy)." json:"randomStrategy,omitempty" toml:"randomStrategy,omitempty" yaml:"randomStrategy,omitempty" export:"true"` diff --git a/pkg/provider/nomad/config.go b/pkg/provider/nomad/config.go index a6fb4343b..4a2192ef0 100644 --- a/pkg/provider/nomad/config.go +++ b/pkg/provider/nomad/config.go @@ -13,7 +13,7 @@ import ( "github.com/rs/zerolog/log" "github.com/traefik/traefik/v3/pkg/config/dynamic" "github.com/traefik/traefik/v3/pkg/config/label" - "github.com/traefik/traefik/v3/pkg/observability/logs" + "github.com/traefik/traefik/v3/pkg/logs" "github.com/traefik/traefik/v3/pkg/provider" "github.com/traefik/traefik/v3/pkg/provider/constraints" ) diff --git a/pkg/provider/nomad/config_test.go b/pkg/provider/nomad/config_test.go index efeabe66e..e74ccddb5 100644 --- a/pkg/provider/nomad/config_test.go +++ b/pkg/provider/nomad/config_test.go @@ -1,6 +1,7 @@ package nomad import ( + "context" "testing" "time" @@ -250,7 +251,8 @@ func Test_defaultRule(t *testing.T) { err := p.Init() require.NoError(t, err) - config := p.buildConfig(t.Context(), test.items) + ctx := context.TODO() + config := p.buildConfig(ctx, test.items) require.Equal(t, test.expected, config) }) } @@ -3075,7 +3077,8 @@ func Test_buildConfig(t *testing.T) { err := p.Init() require.NoError(t, err) - c := p.buildConfig(t.Context(), test.items) + ctx := context.TODO() + c := p.buildConfig(ctx, test.items) require.Equal(t, test.expected, c) }) } @@ -3243,7 +3246,8 @@ func Test_buildConfigAllowEmptyServicesTrue(t *testing.T) { err := p.Init() require.NoError(t, err) - c := p.buildConfig(t.Context(), test.items) + ctx := context.TODO() + c := p.buildConfig(ctx, test.items) require.Equal(t, test.expected, c) }) } @@ -3375,7 +3379,8 @@ func Test_buildConfigAllowEmptyServicesFalseDefault(t *testing.T) { err := p.Init() require.NoError(t, err) - c := p.buildConfig(t.Context(), test.items) + ctx := context.TODO() + c := p.buildConfig(ctx, test.items) require.Equal(t, test.expected, c) }) } @@ -3423,8 +3428,8 @@ func Test_keepItem(t *testing.T) { p := new(Provider) p.SetDefaults() p.Constraints = test.constraints - - result := p.keepItem(t.Context(), test.i) + ctx := context.TODO() + result := p.keepItem(ctx, test.i) require.Equal(t, test.exp, result) }) } diff --git a/pkg/provider/nomad/nomad.go b/pkg/provider/nomad/nomad.go index 77876468e..6f0b4774e 100644 --- a/pkg/provider/nomad/nomad.go +++ b/pkg/provider/nomad/nomad.go @@ -15,7 +15,7 @@ import ( ptypes "github.com/traefik/paerser/types" "github.com/traefik/traefik/v3/pkg/config/dynamic" "github.com/traefik/traefik/v3/pkg/job" - "github.com/traefik/traefik/v3/pkg/observability/logs" + "github.com/traefik/traefik/v3/pkg/logs" "github.com/traefik/traefik/v3/pkg/provider" "github.com/traefik/traefik/v3/pkg/provider/constraints" "github.com/traefik/traefik/v3/pkg/safe" diff --git a/pkg/provider/nomad/nomad_test.go b/pkg/provider/nomad/nomad_test.go index d5442d50f..2794fab0c 100644 --- a/pkg/provider/nomad/nomad_test.go +++ b/pkg/provider/nomad/nomad_test.go @@ -1,6 +1,7 @@ package nomad import ( + "context" "fmt" "net/http" "net/http/httptest" @@ -169,7 +170,7 @@ func Test_getNomadServiceDataWithEmptyServices_GroupService_Scaling1(t *testing. require.NoError(t, err) // make the query for services - items, err := p.getNomadServiceDataWithEmptyServices(t.Context()) + items, err := p.getNomadServiceDataWithEmptyServices(context.TODO()) require.NoError(t, err) require.Len(t, items, 1) } @@ -199,7 +200,7 @@ func Test_getNomadServiceDataWithEmptyServices_GroupService_Scaling0(t *testing. require.NoError(t, err) // make the query for services - items, err := p.getNomadServiceDataWithEmptyServices(t.Context()) + items, err := p.getNomadServiceDataWithEmptyServices(context.TODO()) require.NoError(t, err) require.Len(t, items, 1) } @@ -229,7 +230,7 @@ func Test_getNomadServiceDataWithEmptyServices_GroupService_ScalingDisabled(t *t require.NoError(t, err) // make the query for services - items, err := p.getNomadServiceDataWithEmptyServices(t.Context()) + items, err := p.getNomadServiceDataWithEmptyServices(context.TODO()) require.NoError(t, err) require.Len(t, items, 1) } @@ -259,7 +260,7 @@ func Test_getNomadServiceDataWithEmptyServices_GroupService_ScalingDisabled_Stop require.NoError(t, err) // make the query for services - items, err := p.getNomadServiceDataWithEmptyServices(t.Context()) + items, err := p.getNomadServiceDataWithEmptyServices(context.TODO()) require.NoError(t, err) // Should not be listed as job is stopped @@ -293,7 +294,7 @@ func Test_getNomadServiceDataWithEmptyServices_GroupTaskService_Scaling1(t *test require.NoError(t, err) // make the query for services - items, err := p.getNomadServiceDataWithEmptyServices(t.Context()) + items, err := p.getNomadServiceDataWithEmptyServices(context.TODO()) require.NoError(t, err) require.Len(t, items, 2) } @@ -325,7 +326,7 @@ func Test_getNomadServiceDataWithEmptyServices_GroupTaskService_Scaling0(t *test require.NoError(t, err) // make the query for services - items, err := p.getNomadServiceDataWithEmptyServices(t.Context()) + items, err := p.getNomadServiceDataWithEmptyServices(context.TODO()) require.NoError(t, err) require.Len(t, items, 2) } @@ -355,7 +356,7 @@ func Test_getNomadServiceDataWithEmptyServices_TCP(t *testing.T) { require.NoError(t, err) // make the query for services - items, err := p.getNomadServiceDataWithEmptyServices(t.Context()) + items, err := p.getNomadServiceDataWithEmptyServices(context.TODO()) require.NoError(t, err) require.Len(t, items, 1) } @@ -385,7 +386,7 @@ func Test_getNomadServiceDataWithEmptyServices_UDP(t *testing.T) { require.NoError(t, err) // make the query for services - items, err := p.getNomadServiceDataWithEmptyServices(t.Context()) + items, err := p.getNomadServiceDataWithEmptyServices(context.TODO()) require.NoError(t, err) require.Len(t, items, 1) } @@ -415,7 +416,7 @@ func Test_getNomadServiceDataWithEmptyServices_ScalingEnabled_Stopped(t *testing require.NoError(t, err) // make the query for services - items, err := p.getNomadServiceDataWithEmptyServices(t.Context()) + items, err := p.getNomadServiceDataWithEmptyServices(context.TODO()) require.NoError(t, err) // Should not be listed as job is stopped @@ -464,7 +465,7 @@ func Test_getNomadServiceData(t *testing.T) { require.NoError(t, err) // make the query for services - items, err := p.getNomadServiceData(t.Context()) + items, err := p.getNomadServiceData(context.TODO()) require.NoError(t, err) require.Len(t, items, 2) } diff --git a/pkg/provider/tailscale/provider.go b/pkg/provider/tailscale/provider.go index 5e84912e4..8e795e5be 100644 --- a/pkg/provider/tailscale/provider.go +++ b/pkg/provider/tailscale/provider.go @@ -12,9 +12,9 @@ import ( "github.com/rs/zerolog/log" "github.com/tailscale/tscert" "github.com/traefik/traefik/v3/pkg/config/dynamic" + "github.com/traefik/traefik/v3/pkg/logs" "github.com/traefik/traefik/v3/pkg/muxer/http" "github.com/traefik/traefik/v3/pkg/muxer/tcp" - "github.com/traefik/traefik/v3/pkg/observability/logs" "github.com/traefik/traefik/v3/pkg/safe" traefiktls "github.com/traefik/traefik/v3/pkg/tls" "github.com/traefik/traefik/v3/pkg/types" diff --git a/pkg/provider/tailscale/provider_test.go b/pkg/provider/tailscale/provider_test.go index 279299b3c..2e6e1b962 100644 --- a/pkg/provider/tailscale/provider_test.go +++ b/pkg/provider/tailscale/provider_test.go @@ -1,6 +1,7 @@ package tailscale import ( + "context" "testing" "github.com/stretchr/testify/assert" @@ -124,7 +125,7 @@ func TestProvider_findDomains(t *testing.T) { p := Provider{ResolverName: "foo"} - got := p.findDomains(t.Context(), test.config) + got := p.findDomains(context.TODO(), test.config) assert.Equal(t, test.want, got) }) } @@ -229,7 +230,7 @@ func Test_sanitizeDomains(t *testing.T) { t.Run(test.desc, func(t *testing.T) { t.Parallel() - got := sanitizeDomains(t.Context(), test.domains) + got := sanitizeDomains(context.TODO(), test.domains) assert.Equal(t, test.want, got) }) } diff --git a/pkg/provider/traefik/internal.go b/pkg/provider/traefik/internal.go index 73d17d63f..22efe451d 100644 --- a/pkg/provider/traefik/internal.go +++ b/pkg/provider/traefik/internal.go @@ -11,7 +11,7 @@ import ( "github.com/rs/zerolog/log" "github.com/traefik/traefik/v3/pkg/config/dynamic" "github.com/traefik/traefik/v3/pkg/config/static" - "github.com/traefik/traefik/v3/pkg/observability/logs" + "github.com/traefik/traefik/v3/pkg/logs" "github.com/traefik/traefik/v3/pkg/provider" "github.com/traefik/traefik/v3/pkg/safe" "github.com/traefik/traefik/v3/pkg/tls" @@ -242,10 +242,9 @@ func (i *Provider) entryPointModels(cfg *dynamic.Configuration) { if ep.Observability != nil { httpModel.Observability = dynamic.RouterObservabilityConfig{ - AccessLogs: ep.Observability.AccessLogs, - Metrics: ep.Observability.Metrics, - Tracing: ep.Observability.Tracing, - TraceVerbosity: ep.Observability.TraceVerbosity, + AccessLogs: ep.Observability.AccessLogs, + Tracing: ep.Observability.Tracing, + Metrics: ep.Observability.Metrics, } } diff --git a/pkg/provider/traefik/internal_test.go b/pkg/provider/traefik/internal_test.go index e0697dd8d..3ff89a727 100644 --- a/pkg/provider/traefik/internal_test.go +++ b/pkg/provider/traefik/internal_test.go @@ -1,6 +1,7 @@ package traefik import ( + "context" "encoding/json" "flag" "os" @@ -10,7 +11,6 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/traefik/traefik/v3/pkg/config/static" - otypes "github.com/traefik/traefik/v3/pkg/observability/types" "github.com/traefik/traefik/v3/pkg/ping" "github.com/traefik/traefik/v3/pkg/provider/rest" "github.com/traefik/traefik/v3/pkg/types" @@ -42,8 +42,8 @@ func Test_createConfiguration(t *testing.T) { Insecure: true, }, }, - Metrics: &otypes.Metrics{ - Prometheus: &otypes.Prometheus{ + Metrics: &types.Metrics{ + Prometheus: &types.Prometheus{ EntryPoint: "test", ManualRouting: false, }, @@ -66,8 +66,8 @@ func Test_createConfiguration(t *testing.T) { Insecure: false, }, }, - Metrics: &otypes.Metrics{ - Prometheus: &otypes.Prometheus{ + Metrics: &types.Metrics{ + Prometheus: &types.Prometheus{ EntryPoint: "test", ManualRouting: true, }, @@ -151,8 +151,8 @@ func Test_createConfiguration(t *testing.T) { { desc: "prometheus_simple.json", staticCfg: static.Configuration{ - Metrics: &otypes.Metrics{ - Prometheus: &otypes.Prometheus{ + Metrics: &types.Metrics{ + Prometheus: &types.Prometheus{ EntryPoint: "test", ManualRouting: false, }, @@ -162,8 +162,8 @@ func Test_createConfiguration(t *testing.T) { { desc: "prometheus_custom.json", staticCfg: static.Configuration{ - Metrics: &otypes.Metrics{ - Prometheus: &otypes.Prometheus{ + Metrics: &types.Metrics{ + Prometheus: &types.Prometheus{ EntryPoint: "test", ManualRouting: true, }, @@ -269,7 +269,7 @@ func Test_createConfiguration(t *testing.T) { provider := Provider{staticCfg: test.staticCfg} - cfg := provider.createConfiguration(t.Context()) + cfg := provider.createConfiguration(context.Background()) filename := filepath.Join("fixtures", test.desc) diff --git a/pkg/proxy/fast/proxy_test.go b/pkg/proxy/fast/proxy_test.go index 70b1256aa..e26c67f63 100644 --- a/pkg/proxy/fast/proxy_test.go +++ b/pkg/proxy/fast/proxy_test.go @@ -20,6 +20,7 @@ import ( "github.com/traefik/traefik/v3/pkg/config/dynamic" "github.com/traefik/traefik/v3/pkg/config/static" "github.com/traefik/traefik/v3/pkg/testhelpers" + "github.com/traefik/traefik/v3/pkg/tls/generate" ) const ( @@ -124,17 +125,9 @@ func TestProxyFromEnvironment(t *testing.T) { for _, test := range testCases { t.Run(test.desc, func(t *testing.T) { - var backendServer *httptest.Server - if test.tls { - backendServer = httptest.NewTLSServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { - _, _ = rw.Write([]byte("backendTLS")) - })) - } else { - backendServer = httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { - _, _ = rw.Write([]byte("backend")) - })) - } - t.Cleanup(backendServer.Close) + backendURL, backendCert := newBackendServer(t, test.tls, http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + _, _ = rw.Write([]byte("backend")) + })) var proxyCalled bool proxyHandler := http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { @@ -162,21 +155,8 @@ func TestProxyFromEnvironment(t *testing.T) { connHj, _, err := hj.Hijack() require.NoError(t, err) - defer func() { - _ = connHj.Close() - _ = conn.Close() - }() - - errCh := make(chan error, 1) - go func() { - _, err = io.Copy(connHj, conn) - errCh <- err - }() - go func() { - _, err = io.Copy(conn, connHj) - errCh <- err - }() - <-errCh // Wait for one of the copy operations to finish + go func() { _, _ = io.Copy(connHj, conn) }() + _, _ = io.Copy(conn, connHj) }) var proxyURL string @@ -218,7 +198,7 @@ func TestProxyFromEnvironment(t *testing.T) { proxyURL = proxyServer.URL case proxyHTTPS: - proxyServer := httptest.NewTLSServer(proxyHandler) + proxyServer := httptest.NewServer(proxyHandler) t.Cleanup(proxyServer.Close) proxyURL = proxyServer.URL @@ -229,8 +209,11 @@ func TestProxyFromEnvironment(t *testing.T) { if proxyCert != nil { certPool.AddCert(proxyCert) } - if backendServer.Certificate() != nil { - certPool.AddCert(backendServer.Certificate()) + if backendCert != nil { + cert, err := x509.ParseCertificate(backendCert.Certificate[0]) + require.NoError(t, err) + + certPool.AddCert(cert) } builder := NewProxyBuilder(&transportManagerMock{tlsConfig: &tls.Config{RootCAs: certPool}}, static.FastProxyConfig{}) @@ -247,7 +230,7 @@ func TestProxyFromEnvironment(t *testing.T) { return u, nil } - reverseProxy, err := builder.Build("foo", testhelpers.MustParseURL(backendServer.URL), false, false) + reverseProxy, err := builder.Build("foo", testhelpers.MustParseURL(backendURL), false, false) require.NoError(t, err) reverseProxyServer := httptest.NewServer(reverseProxy) @@ -263,11 +246,7 @@ func TestProxyFromEnvironment(t *testing.T) { body, err := io.ReadAll(resp.Body) require.NoError(t, err) - if test.tls { - assert.Equal(t, "backendTLS", string(body)) - } else { - assert.Equal(t, "backend", string(body)) - } + assert.Equal(t, "backend", string(body)) assert.True(t, proxyCalled) }) } @@ -406,6 +385,52 @@ func TestTransferEncodingChunked(t *testing.T) { assert.Equal(t, "chunk 0\nchunk 1\nchunk 2\n", string(body)) } +func newCertificate(t *testing.T, domain string) *tls.Certificate { + t.Helper() + + certPEM, keyPEM, err := generate.KeyPair(domain, time.Time{}) + require.NoError(t, err) + + certificate, err := tls.X509KeyPair(certPEM, keyPEM) + require.NoError(t, err) + + return &certificate +} + +func newBackendServer(t *testing.T, isTLS bool, handler http.Handler) (string, *tls.Certificate) { + t.Helper() + + var ln net.Listener + var err error + var cert *tls.Certificate + + scheme := "http" + domain := "backend.localhost" + if isTLS { + scheme = "https" + + cert = newCertificate(t, domain) + + ln, err = tls.Listen("tcp", ":0", &tls.Config{Certificates: []tls.Certificate{*cert}}) + require.NoError(t, err) + } else { + ln, err = net.Listen("tcp", ":0") + require.NoError(t, err) + } + + srv := &http.Server{Handler: handler} + go func() { _ = srv.Serve(ln) }() + + t.Cleanup(func() { _ = srv.Close() }) + + _, port, err := net.SplitHostPort(ln.Addr().String()) + require.NoError(t, err) + + backendURL := fmt.Sprintf("%s://%s:%s", scheme, domain, port) + + return backendURL, cert +} + type transportManagerMock struct { tlsConfig *tls.Config } diff --git a/pkg/proxy/httputil/builder.go b/pkg/proxy/httputil/builder.go index 1a4b04a0f..64360517a 100644 --- a/pkg/proxy/httputil/builder.go +++ b/pkg/proxy/httputil/builder.go @@ -8,7 +8,7 @@ import ( "time" "github.com/traefik/traefik/v3/pkg/config/dynamic" - "github.com/traefik/traefik/v3/pkg/observability/metrics" + "github.com/traefik/traefik/v3/pkg/metrics" ) // TransportManager manages transport used for backend communications. @@ -38,15 +38,17 @@ func NewProxyBuilder(transportManager TransportManager, semConvMetricsRegistry * func (r *ProxyBuilder) Update(_ map[string]*dynamic.ServersTransport) {} // Build builds a new httputil.ReverseProxy with the given configuration. -func (r *ProxyBuilder) Build(cfgName string, targetURL *url.URL, passHostHeader, preservePath bool, flushInterval time.Duration) (http.Handler, error) { +func (r *ProxyBuilder) Build(cfgName string, targetURL *url.URL, shouldObserve, passHostHeader, preservePath bool, flushInterval time.Duration) (http.Handler, error) { roundTripper, err := r.transportManager.GetRoundTripper(cfgName) if err != nil { return nil, fmt.Errorf("getting RoundTripper: %w", err) } - // Wrapping the roundTripper with the Tracing roundTripper, - // to create, if necessary, the reverseProxy client span and the semConv client metric. - roundTripper = newObservabilityRoundTripper(r.semConvMetricsRegistry, roundTripper) + if shouldObserve { + // Wrapping the roundTripper with the Tracing roundTripper, + // to handle the reverseProxy client span creation. + roundTripper = newObservabilityRoundTripper(r.semConvMetricsRegistry, roundTripper) + } return buildSingleHostProxy(targetURL, passHostHeader, preservePath, flushInterval, roundTripper, r.bufferPool), nil } diff --git a/pkg/proxy/httputil/builder_test.go b/pkg/proxy/httputil/builder_test.go index e45a871b6..f7ff93902 100644 --- a/pkg/proxy/httputil/builder_test.go +++ b/pkg/proxy/httputil/builder_test.go @@ -23,7 +23,7 @@ func TestEscapedPath(t *testing.T) { roundTrippers: map[string]http.RoundTripper{"default": &http.Transport{}}, } - p, err := NewProxyBuilder(transportManager, nil).Build("default", testhelpers.MustParseURL(srv.URL), true, false, 0) + p, err := NewProxyBuilder(transportManager, nil).Build("default", testhelpers.MustParseURL(srv.URL), false, true, false, 0) require.NoError(t, err) proxy := httptest.NewServer(http.HandlerFunc(p.ServeHTTP)) diff --git a/pkg/proxy/httputil/observability.go b/pkg/proxy/httputil/observability.go index 23ff5acdf..8fa3382e3 100644 --- a/pkg/proxy/httputil/observability.go +++ b/pkg/proxy/httputil/observability.go @@ -9,12 +9,12 @@ import ( "strings" "time" + "github.com/traefik/traefik/v3/pkg/metrics" "github.com/traefik/traefik/v3/pkg/middlewares/observability" - "github.com/traefik/traefik/v3/pkg/observability/metrics" - "github.com/traefik/traefik/v3/pkg/observability/tracing" + "github.com/traefik/traefik/v3/pkg/tracing" "go.opentelemetry.io/otel/attribute" - semconv "go.opentelemetry.io/otel/semconv/v1.37.0" - "go.opentelemetry.io/otel/semconv/v1.37.0/httpconv" + "go.opentelemetry.io/otel/metric" + semconv "go.opentelemetry.io/otel/semconv/v1.26.0" "go.opentelemetry.io/otel/trace" ) @@ -35,7 +35,7 @@ func (t *wrapper) RoundTrip(req *http.Request) (*http.Response, error) { var span trace.Span var tracingCtx context.Context var tracer *tracing.Tracer - if tracer = tracing.TracerFromContext(req.Context()); tracer != nil && observability.TracingEnabled(req.Context()) { + if tracer = tracing.TracerFromContext(req.Context()); tracer != nil { tracingCtx, span = tracer.Start(req.Context(), "ReverseProxy", trace.WithSpanKind(trace.SpanKindClient)) defer span.End() @@ -68,43 +68,38 @@ func (t *wrapper) RoundTrip(req *http.Request) (*http.Response, error) { span.End(trace.WithTimestamp(end)) } - if !observability.SemConvMetricsEnabled(req.Context()) || t.semConvMetricRegistry == nil { - return response, err - } + if req.Context().Value(observability.DisableMetricsKey) == nil && t.semConvMetricRegistry != nil && t.semConvMetricRegistry.HTTPClientRequestDuration() != nil { + var attrs []attribute.KeyValue - var attrs []attribute.KeyValue - - if statusCode < 100 || statusCode >= 600 { - attrs = append(attrs, attribute.Key("error.type").String(fmt.Sprintf("Invalid HTTP status code %d", statusCode))) - } else if statusCode >= 400 { - attrs = append(attrs, attribute.Key("error.type").String(strconv.Itoa(statusCode))) - } - - attrs = append(attrs, semconv.HTTPRequestMethodKey.String(req.Method)) - attrs = append(attrs, semconv.HTTPResponseStatusCode(statusCode)) - attrs = append(attrs, semconv.NetworkProtocolName(strings.ToLower(req.Proto))) - attrs = append(attrs, semconv.NetworkProtocolVersion(observability.Proto(req.Proto))) - - var serverPort int - _, port, splitErr := net.SplitHostPort(req.URL.Host) - if splitErr != nil { - switch req.URL.Scheme { - case "http": - serverPort = 80 - attrs = append(attrs, semconv.ServerPort(serverPort)) - case "https": - serverPort = 443 - attrs = append(attrs, semconv.ServerPort(serverPort)) + if statusCode < 100 || statusCode >= 600 { + attrs = append(attrs, attribute.Key("error.type").String(fmt.Sprintf("Invalid HTTP status code %d", statusCode))) + } else if statusCode >= 400 { + attrs = append(attrs, attribute.Key("error.type").String(strconv.Itoa(statusCode))) } - } else { - serverPort, _ := strconv.Atoi(port) - attrs = append(attrs, semconv.ServerPort(serverPort)) + + attrs = append(attrs, semconv.HTTPRequestMethodKey.String(req.Method)) + attrs = append(attrs, semconv.HTTPResponseStatusCode(statusCode)) + attrs = append(attrs, semconv.NetworkProtocolName(strings.ToLower(req.Proto))) + attrs = append(attrs, semconv.NetworkProtocolVersion(observability.Proto(req.Proto))) + attrs = append(attrs, semconv.ServerAddress(req.URL.Host)) + + _, port, err := net.SplitHostPort(req.URL.Host) + if err != nil { + switch req.URL.Scheme { + case "http": + attrs = append(attrs, semconv.ServerPort(80)) + case "https": + attrs = append(attrs, semconv.ServerPort(443)) + } + } else { + intPort, _ := strconv.Atoi(port) + attrs = append(attrs, semconv.ServerPort(intPort)) + } + + attrs = append(attrs, semconv.URLScheme(req.Header.Get("X-Forwarded-Proto"))) + + t.semConvMetricRegistry.HTTPClientRequestDuration().Record(req.Context(), end.Sub(start).Seconds(), metric.WithAttributes(attrs...)) } - attrs = append(attrs, semconv.URLScheme(req.Header.Get("X-Forwarded-Proto"))) - - t.semConvMetricRegistry.HTTPClientRequestDuration().Record(req.Context(), end.Sub(start).Seconds(), - httpconv.RequestMethodAttr(req.Method), req.URL.Host, serverPort, attrs...) - return response, err } diff --git a/pkg/proxy/httputil/observability_test.go b/pkg/proxy/httputil/observability_test.go index c0ef0e7a3..ea0721e07 100644 --- a/pkg/proxy/httputil/observability_test.go +++ b/pkg/proxy/httputil/observability_test.go @@ -1,6 +1,7 @@ package httputil import ( + "context" "net/http" "net/http/httptest" "testing" @@ -8,9 +9,8 @@ import ( "github.com/stretchr/testify/require" ptypes "github.com/traefik/paerser/types" - "github.com/traefik/traefik/v3/pkg/middlewares/observability" - "github.com/traefik/traefik/v3/pkg/observability/metrics" - otypes "github.com/traefik/traefik/v3/pkg/observability/types" + "github.com/traefik/traefik/v3/pkg/metrics" + "github.com/traefik/traefik/v3/pkg/types" "go.opentelemetry.io/otel/attribute" sdkmetric "go.opentelemetry.io/otel/sdk/metric" "go.opentelemetry.io/otel/sdk/metric/metricdata" @@ -59,7 +59,7 @@ func TestObservabilityRoundTripper_metrics(t *testing.T) { t.Run(test.desc, func(t *testing.T) { t.Parallel() - var cfg otypes.OTLP + var cfg types.OTLP (&cfg).SetDefaults() cfg.AddRoutersLabels = true cfg.PushInterval = ptypes.Duration(10 * time.Millisecond) @@ -69,7 +69,7 @@ func TestObservabilityRoundTripper_metrics(t *testing.T) { // force the meter provider with manual reader to collect metrics for the test. metrics.SetMeterProvider(meterProvider) - semConvMetricRegistry, err := metrics.NewSemConvMetricRegistry(t.Context(), &cfg) + semConvMetricRegistry, err := metrics.NewSemConvMetricRegistry(context.Background(), &cfg) require.NoError(t, err) require.NotNil(t, semConvMetricRegistry) @@ -78,17 +78,12 @@ func TestObservabilityRoundTripper_metrics(t *testing.T) { req.Header.Set("User-Agent", "rt-test") req.Header.Set("X-Forwarded-Proto", "http") - // Injection of the observability variables in the request context. - req = req.WithContext(observability.WithObservability(req.Context(), observability.Observability{ - SemConvMetricsEnabled: true, - })) - ort := newObservabilityRoundTripper(semConvMetricRegistry, mockRoundTripper{statusCode: test.statusCode}) _, err = ort.RoundTrip(req) require.NoError(t, err) got := metricdata.ResourceMetrics{} - err = rdr.Collect(t.Context(), &got) + err = rdr.Collect(context.Background(), &got) require.NoError(t, err) require.Len(t, got.ScopeMetrics, 1) diff --git a/pkg/proxy/httputil/proxy.go b/pkg/proxy/httputil/proxy.go index a6c93cade..7d4c52c9d 100644 --- a/pkg/proxy/httputil/proxy.go +++ b/pkg/proxy/httputil/proxy.go @@ -15,7 +15,7 @@ import ( "github.com/rs/zerolog" "github.com/rs/zerolog/log" - "github.com/traefik/traefik/v3/pkg/observability/logs" + "github.com/traefik/traefik/v3/pkg/logs" "golang.org/x/net/http/httpguts" ) diff --git a/pkg/proxy/httputil/proxy_websocket_test.go b/pkg/proxy/httputil/proxy_websocket_test.go index 7abfc39f6..48296f955 100644 --- a/pkg/proxy/httputil/proxy_websocket_test.go +++ b/pkg/proxy/httputil/proxy_websocket_test.go @@ -301,7 +301,7 @@ func TestWebSocketRequestWithHeadersInResponseWriter(t *testing.T) { }, } - p, err := NewProxyBuilder(transportManager, nil).Build("default@internal", testhelpers.MustParseURL(srv.URL), true, false, 0) + p, err := NewProxyBuilder(transportManager, nil).Build("default@internal", testhelpers.MustParseURL(srv.URL), false, true, false, 0) require.NoError(t, err) proxy := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { req.URL = testhelpers.MustParseURL(srv.URL) @@ -357,7 +357,7 @@ func TestWebSocketUpgradeFailed(t *testing.T) { }, } - p, err := NewProxyBuilder(transportManager, nil).Build("default@internal", testhelpers.MustParseURL(srv.URL), true, false, 0) + p, err := NewProxyBuilder(transportManager, nil).Build("default@internal", testhelpers.MustParseURL(srv.URL), false, true, false, 0) require.NoError(t, err) proxy := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { path := req.URL.Path // keep the original path @@ -618,7 +618,7 @@ func createProxyWithForwarder(t *testing.T, uri string, transport http.RoundTrip roundTrippers: map[string]http.RoundTripper{"fwd": transport}, } - p, err := NewProxyBuilder(transportManager, nil).Build("fwd", u, true, false, 0) + p, err := NewProxyBuilder(transportManager, nil).Build("fwd", u, false, true, false, 0) require.NoError(t, err) srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { diff --git a/pkg/proxy/smart_builder.go b/pkg/proxy/smart_builder.go index ad9d14d56..08b247c53 100644 --- a/pkg/proxy/smart_builder.go +++ b/pkg/proxy/smart_builder.go @@ -45,7 +45,7 @@ func (b *SmartBuilder) Update(newConfigs map[string]*dynamic.ServersTransport) { } // Build builds an HTTP proxy for the given URL using the ServersTransport with the given name. -func (b *SmartBuilder) Build(configName string, targetURL *url.URL, passHostHeader, preservePath bool, flushInterval time.Duration) (http.Handler, error) { +func (b *SmartBuilder) Build(configName string, targetURL *url.URL, shouldObserve, passHostHeader, preservePath bool, flushInterval time.Duration) (http.Handler, error) { serversTransport, err := b.transportManager.Get(configName) if err != nil { return nil, fmt.Errorf("getting ServersTransport: %w", err) @@ -55,7 +55,7 @@ func (b *SmartBuilder) Build(configName string, targetURL *url.URL, passHostHead // For the https scheme we cannot guess if the backend communication will use HTTP2, // thus we check if HTTP/2 is disabled to use the fast proxy implementation when this is possible. if targetURL.Scheme == "h2c" || (targetURL.Scheme == "https" && !serversTransport.DisableHTTP2) { - return b.proxyBuilder.Build(configName, targetURL, passHostHeader, preservePath, flushInterval) + return b.proxyBuilder.Build(configName, targetURL, shouldObserve, passHostHeader, preservePath, flushInterval) } return b.fastProxyBuilder.Build(configName, targetURL, passHostHeader, preservePath) } diff --git a/pkg/proxy/smart_builder_test.go b/pkg/proxy/smart_builder_test.go index d1c29ddd8..c03bd19f3 100644 --- a/pkg/proxy/smart_builder_test.go +++ b/pkg/proxy/smart_builder_test.go @@ -101,7 +101,7 @@ func TestSmartBuilder_Build(t *testing.T) { httpProxyBuilder := httputil.NewProxyBuilder(transportManager, nil) proxyBuilder := NewSmartBuilder(transportManager, httpProxyBuilder, test.fastProxyConfig) - proxyHandler, err := proxyBuilder.Build("test", targetURL, false, false, time.Second) + proxyHandler, err := proxyBuilder.Build("test", targetURL, false, false, false, time.Second) require.NoError(t, err) rw := httptest.NewRecorder() diff --git a/pkg/redactor/redactor.go b/pkg/redactor/redactor.go index cbe4a6c5c..c28feaa1a 100644 --- a/pkg/redactor/redactor.go +++ b/pkg/redactor/redactor.go @@ -70,7 +70,7 @@ func doOnJSON(input string) string { } func doOnStruct(field reflect.Value, tag string, redactByDefault bool) error { - if field.Type().AssignableTo(reflect.TypeFor[dynamic.PluginConf]()) { + if field.Type().AssignableTo(reflect.TypeOf(dynamic.PluginConf{})) { resetPlugin(field) return nil } @@ -164,7 +164,7 @@ func reset(field reflect.Value, name string) error { } case reflect.String: if field.String() != "" { - if field.Type().AssignableTo(reflect.TypeFor[types.FileOrContent]()) { + if field.Type().AssignableTo(reflect.TypeOf(types.FileOrContent(""))) { field.Set(reflect.ValueOf(types.FileOrContent(maskShort))) } else { field.Set(reflect.ValueOf(maskShort)) diff --git a/pkg/redactor/redactor_config_test.go b/pkg/redactor/redactor_config_test.go index 9edf2363f..b086ccd6a 100644 --- a/pkg/redactor/redactor_config_test.go +++ b/pkg/redactor/redactor_config_test.go @@ -12,7 +12,6 @@ import ( ptypes "github.com/traefik/paerser/types" "github.com/traefik/traefik/v3/pkg/config/dynamic" "github.com/traefik/traefik/v3/pkg/config/static" - otypes "github.com/traefik/traefik/v3/pkg/observability/types" "github.com/traefik/traefik/v3/pkg/ping" "github.com/traefik/traefik/v3/pkg/plugins" "github.com/traefik/traefik/v3/pkg/provider/acme" @@ -799,21 +798,21 @@ func TestDo_staticConfiguration(t *testing.T) { Debug: true, } - config.Metrics = &otypes.Metrics{ - Prometheus: &otypes.Prometheus{ + config.Metrics = &types.Metrics{ + Prometheus: &types.Prometheus{ Buckets: []float64{0.1, 0.3, 1.2, 5}, AddEntryPointsLabels: true, AddServicesLabels: true, EntryPoint: "MyEntryPoint", ManualRouting: true, }, - Datadog: &otypes.Datadog{ + Datadog: &types.Datadog{ Address: "localhost:8181", PushInterval: 42, AddEntryPointsLabels: true, AddServicesLabels: true, }, - StatsD: &otypes.Statsd{ + StatsD: &types.Statsd{ Address: "localhost:8182", PushInterval: 42, AddEntryPointsLabels: true, @@ -828,7 +827,7 @@ func TestDo_staticConfiguration(t *testing.T) { TerminatingStatusCode: 42, } - config.Log = &otypes.TraefikLog{ + config.Log = &types.TraefikLog{ Level: "Level", Format: "json", FilePath: "/foo/path", @@ -836,19 +835,19 @@ func TestDo_staticConfiguration(t *testing.T) { MaxAge: 3, MaxBackups: 4, Compress: true, - OTLP: &otypes.OTelLog{ + OTLP: &types.OTelLog{ ServiceName: "foobar", ResourceAttributes: map[string]string{ "foobar": "foobar", }, - GRPC: &otypes.OTelGRPC{ + GRPC: &types.OTelGRPC{ Endpoint: "foobar", Insecure: true, Headers: map[string]string{ "foobar": "foobar", }, }, - HTTP: &otypes.OTelHTTP{ + HTTP: &types.OTelHTTP{ Endpoint: "foobar", Headers: map[string]string{ "foobar": "foobar", @@ -857,20 +856,20 @@ func TestDo_staticConfiguration(t *testing.T) { }, } - config.AccessLog = &otypes.AccessLog{ + config.AccessLog = &types.AccessLog{ FilePath: "AccessLog FilePath", Format: "AccessLog Format", - Filters: &otypes.AccessLogFilters{ + Filters: &types.AccessLogFilters{ StatusCodes: []string{"200", "500"}, RetryAttempts: true, MinDuration: 42, }, - Fields: &otypes.AccessLogFields{ + Fields: &types.AccessLogFields{ DefaultMode: "drop", Names: map[string]string{ "RequestHost": "keep", }, - Headers: &otypes.FieldHeaders{ + Headers: &types.FieldHeaders{ DefaultMode: "drop", Names: map[string]string{ "Referer": "keep", @@ -878,19 +877,19 @@ func TestDo_staticConfiguration(t *testing.T) { }, }, BufferingSize: 42, - OTLP: &otypes.OTelLog{ + OTLP: &types.OTelLog{ ServiceName: "foobar", ResourceAttributes: map[string]string{ "foobar": "foobar", }, - GRPC: &otypes.OTelGRPC{ + GRPC: &types.OTelGRPC{ Endpoint: "foobar", Insecure: true, Headers: map[string]string{ "foobar": "foobar", }, }, - HTTP: &otypes.OTelHTTP{ + HTTP: &types.OTelHTTP{ Endpoint: "foobar", Headers: map[string]string{ "foobar": "foobar", @@ -908,14 +907,14 @@ func TestDo_staticConfiguration(t *testing.T) { "foobar": "foobar", }, SampleRate: 42, - OTLP: &otypes.OTelTracing{ - HTTP: &otypes.OTelHTTP{ + OTLP: &types.OTelTracing{ + HTTP: &types.OTelHTTP{ Endpoint: "foobar", Headers: map[string]string{ "foobar": "foobar", }, }, - GRPC: &otypes.OTelGRPC{ + GRPC: &types.OTelGRPC{ Endpoint: "foobar", Insecure: true, Headers: map[string]string{ diff --git a/pkg/safe/routine_test.go b/pkg/safe/routine_test.go index 3fa403e78..12875972c 100644 --- a/pkg/safe/routine_test.go +++ b/pkg/safe/routine_test.go @@ -15,7 +15,7 @@ func TestNewPoolContext(t *testing.T) { testKey := testKeyType("test") - ctx := context.WithValue(t.Context(), testKey, "test") + ctx := context.WithValue(context.Background(), testKey, "test") p := NewPool(ctx) p.GoCtx(func(ctx context.Context) { @@ -66,7 +66,7 @@ func TestPoolWithCtx(t *testing.T) { t.Run(test.desc, func(t *testing.T) { // These subtests cannot be run in parallel, since the testRoutine // is shared across the subtests. - p := NewPool(t.Context()) + p := NewPool(context.Background()) timer := time.NewTimer(500 * time.Millisecond) defer timer.Stop() @@ -93,7 +93,7 @@ func TestPoolWithCtx(t *testing.T) { } func TestPoolCleanupWithGoPanicking(t *testing.T) { - p := NewPool(t.Context()) + p := NewPool(context.Background()) timer := time.NewTimer(500 * time.Millisecond) defer timer.Stop() diff --git a/pkg/server/aggregator.go b/pkg/server/aggregator.go index 8f22474fe..2a0c1bd4f 100644 --- a/pkg/server/aggregator.go +++ b/pkg/server/aggregator.go @@ -7,8 +7,7 @@ import ( "github.com/go-acme/lego/v4/challenge/tlsalpn01" "github.com/rs/zerolog/log" "github.com/traefik/traefik/v3/pkg/config/dynamic" - "github.com/traefik/traefik/v3/pkg/observability/logs" - otypes "github.com/traefik/traefik/v3/pkg/observability/types" + "github.com/traefik/traefik/v3/pkg/logs" "github.com/traefik/traefik/v3/pkg/server/provider" "github.com/traefik/traefik/v3/pkg/tls" ) @@ -209,10 +208,6 @@ func applyModel(cfg dynamic.Configuration) dynamic.Configuration { cp.Observability.Tracing = m.Observability.Tracing } - if cp.Observability.TraceVerbosity == "" { - cp.Observability.TraceVerbosity = m.Observability.TraceVerbosity - } - rtName := name if len(eps) > 1 { rtName = epName + "-" + name @@ -229,7 +224,7 @@ func applyModel(cfg dynamic.Configuration) dynamic.Configuration { cfg.HTTP.Routers = rts } - // Apply the default observability model to HTTP routers. + // Apply default observability model to HTTP routers. applyDefaultObservabilityModel(cfg) if cfg.TCP == nil || len(cfg.TCP.Models) == 0 { @@ -261,16 +256,14 @@ func applyModel(cfg dynamic.Configuration) dynamic.Configuration { // and make sure it is serialized and available in the API. // We could have introduced a "default" model, but it would have been more complex to manage for now. // This could be generalized in the future. -// TODO: check if we can remove this and rely on the SetDefaults instead. func applyDefaultObservabilityModel(cfg dynamic.Configuration) { if cfg.HTTP != nil { for _, router := range cfg.HTTP.Routers { if router.Observability == nil { router.Observability = &dynamic.RouterObservabilityConfig{ - AccessLogs: pointer(true), - Metrics: pointer(true), - Tracing: pointer(true), - TraceVerbosity: otypes.MinimalVerbosity, + AccessLogs: pointer(true), + Metrics: pointer(true), + Tracing: pointer(true), } continue @@ -280,16 +273,12 @@ func applyDefaultObservabilityModel(cfg dynamic.Configuration) { router.Observability.AccessLogs = pointer(true) } - if router.Observability.Metrics == nil { - router.Observability.Metrics = pointer(true) - } - if router.Observability.Tracing == nil { router.Observability.Tracing = pointer(true) } - if router.Observability.TraceVerbosity == "" { - router.Observability.TraceVerbosity = otypes.MinimalVerbosity + if router.Observability.Metrics == nil { + router.Observability.Metrics = pointer(true) } } } diff --git a/pkg/server/aggregator_test.go b/pkg/server/aggregator_test.go index e33ef7ae1..182815a39 100644 --- a/pkg/server/aggregator_test.go +++ b/pkg/server/aggregator_test.go @@ -6,7 +6,6 @@ import ( "github.com/go-acme/lego/v4/challenge/tlsalpn01" "github.com/stretchr/testify/assert" "github.com/traefik/traefik/v3/pkg/config/dynamic" - otypes "github.com/traefik/traefik/v3/pkg/observability/types" "github.com/traefik/traefik/v3/pkg/tls" ) @@ -522,10 +521,9 @@ func Test_applyModel(t *testing.T) { Routers: map[string]*dynamic.Router{ "test": { Observability: &dynamic.RouterObservabilityConfig{ - AccessLogs: pointer(true), - Metrics: pointer(true), - Tracing: pointer(true), - TraceVerbosity: otypes.MinimalVerbosity, + AccessLogs: pointer(true), + Metrics: pointer(true), + Tracing: pointer(true), }, }, }, @@ -591,10 +589,9 @@ func Test_applyModel(t *testing.T) { Middlewares: []string{"test"}, TLS: &dynamic.RouterTLSConfig{}, Observability: &dynamic.RouterObservabilityConfig{ - AccessLogs: pointer(true), - Metrics: pointer(true), - Tracing: pointer(true), - TraceVerbosity: otypes.MinimalVerbosity, + AccessLogs: pointer(true), + Metrics: pointer(true), + Tracing: pointer(true), }, }, }, @@ -625,10 +622,9 @@ func Test_applyModel(t *testing.T) { Middlewares: []string{"test"}, TLS: &dynamic.RouterTLSConfig{}, Observability: dynamic.RouterObservabilityConfig{ - AccessLogs: pointer(true), - Tracing: pointer(true), - Metrics: pointer(true), - TraceVerbosity: otypes.MinimalVerbosity, + AccessLogs: pointer(true), + Tracing: pointer(true), + Metrics: pointer(true), }, }, }, @@ -642,10 +638,9 @@ func Test_applyModel(t *testing.T) { Middlewares: []string{"test"}, TLS: &dynamic.RouterTLSConfig{}, Observability: &dynamic.RouterObservabilityConfig{ - AccessLogs: pointer(true), - Tracing: pointer(true), - Metrics: pointer(true), - TraceVerbosity: otypes.MinimalVerbosity, + AccessLogs: pointer(true), + Tracing: pointer(true), + Metrics: pointer(true), }, }, }, @@ -656,10 +651,9 @@ func Test_applyModel(t *testing.T) { Middlewares: []string{"test"}, TLS: &dynamic.RouterTLSConfig{}, Observability: dynamic.RouterObservabilityConfig{ - AccessLogs: pointer(true), - Tracing: pointer(true), - Metrics: pointer(true), - TraceVerbosity: otypes.MinimalVerbosity, + AccessLogs: pointer(true), + Tracing: pointer(true), + Metrics: pointer(true), }, }, }, @@ -694,10 +688,9 @@ func Test_applyModel(t *testing.T) { Middlewares: []string{"test"}, TLS: &dynamic.RouterTLSConfig{CertResolver: "router"}, Observability: &dynamic.RouterObservabilityConfig{ - AccessLogs: pointer(true), - Metrics: pointer(true), - Tracing: pointer(true), - TraceVerbosity: otypes.MinimalVerbosity, + AccessLogs: pointer(true), + Metrics: pointer(true), + Tracing: pointer(true), }, }, }, @@ -737,10 +730,9 @@ func Test_applyModel(t *testing.T) { "test": { EntryPoints: []string{"web"}, Observability: &dynamic.RouterObservabilityConfig{ - AccessLogs: pointer(true), - Metrics: pointer(true), - Tracing: pointer(true), - TraceVerbosity: otypes.MinimalVerbosity, + AccessLogs: pointer(true), + Metrics: pointer(true), + Tracing: pointer(true), }, }, "websecure-test": { @@ -748,10 +740,9 @@ func Test_applyModel(t *testing.T) { Middlewares: []string{"test"}, TLS: &dynamic.RouterTLSConfig{}, Observability: &dynamic.RouterObservabilityConfig{ - AccessLogs: pointer(true), - Metrics: pointer(true), - Tracing: pointer(true), - TraceVerbosity: otypes.MinimalVerbosity, + AccessLogs: pointer(true), + Metrics: pointer(true), + Tracing: pointer(true), }, }, }, diff --git a/pkg/server/configurationwatcher.go b/pkg/server/configurationwatcher.go index 8142f8359..5071f2498 100644 --- a/pkg/server/configurationwatcher.go +++ b/pkg/server/configurationwatcher.go @@ -8,7 +8,7 @@ import ( "github.com/rs/zerolog" "github.com/rs/zerolog/log" "github.com/traefik/traefik/v3/pkg/config/dynamic" - "github.com/traefik/traefik/v3/pkg/observability/logs" + "github.com/traefik/traefik/v3/pkg/logs" "github.com/traefik/traefik/v3/pkg/provider" "github.com/traefik/traefik/v3/pkg/safe" "github.com/traefik/traefik/v3/pkg/tls" diff --git a/pkg/server/configurationwatcher_test.go b/pkg/server/configurationwatcher_test.go index 99a196008..7baf233d0 100644 --- a/pkg/server/configurationwatcher_test.go +++ b/pkg/server/configurationwatcher_test.go @@ -57,7 +57,7 @@ func (p *mockProvider) Init() error { } func TestNewConfigurationWatcher(t *testing.T) { - routinesPool := safe.NewPool(t.Context()) + routinesPool := safe.NewPool(context.Background()) t.Cleanup(routinesPool.Stop) pvd := &mockProvider{ @@ -117,7 +117,7 @@ func TestNewConfigurationWatcher(t *testing.T) { } func TestWaitForRequiredProvider(t *testing.T) { - routinesPool := safe.NewPool(t.Context()) + routinesPool := safe.NewPool(context.Background()) pvdAggregator := &mockProvider{ wait: 5 * time.Millisecond, @@ -165,7 +165,7 @@ func TestWaitForRequiredProvider(t *testing.T) { } func TestIgnoreTransientConfiguration(t *testing.T) { - routinesPool := safe.NewPool(t.Context()) + routinesPool := safe.NewPool(context.Background()) config := &dynamic.Configuration{ HTTP: th.BuildConfiguration( @@ -305,7 +305,7 @@ func TestIgnoreTransientConfiguration(t *testing.T) { } func TestListenProvidersThrottleProviderConfigReload(t *testing.T) { - routinesPool := safe.NewPool(t.Context()) + routinesPool := safe.NewPool(context.Background()) pvd := &mockProvider{ wait: 10 * time.Millisecond, @@ -350,7 +350,7 @@ func TestListenProvidersThrottleProviderConfigReload(t *testing.T) { } func TestListenProvidersSkipsEmptyConfigs(t *testing.T) { - routinesPool := safe.NewPool(t.Context()) + routinesPool := safe.NewPool(context.Background()) pvd := &mockProvider{ messages: []dynamic.Message{{ProviderName: "mock"}}, @@ -371,7 +371,7 @@ func TestListenProvidersSkipsEmptyConfigs(t *testing.T) { } func TestListenProvidersSkipsSameConfigurationForProvider(t *testing.T) { - routinesPool := safe.NewPool(t.Context()) + routinesPool := safe.NewPool(context.Background()) message := dynamic.Message{ ProviderName: "mock", @@ -405,7 +405,7 @@ func TestListenProvidersSkipsSameConfigurationForProvider(t *testing.T) { } func TestListenProvidersDoesNotSkipFlappingConfiguration(t *testing.T) { - routinesPool := safe.NewPool(t.Context()) + routinesPool := safe.NewPool(context.Background()) configuration := &dynamic.Configuration{ HTTP: th.BuildConfiguration( @@ -475,7 +475,7 @@ func TestListenProvidersDoesNotSkipFlappingConfiguration(t *testing.T) { } func TestListenProvidersIgnoreSameConfig(t *testing.T) { - routinesPool := safe.NewPool(t.Context()) + routinesPool := safe.NewPool(context.Background()) configuration := &dynamic.Configuration{ HTTP: th.BuildConfiguration( @@ -568,7 +568,7 @@ func TestListenProvidersIgnoreSameConfig(t *testing.T) { } func TestApplyConfigUnderStress(t *testing.T) { - routinesPool := safe.NewPool(t.Context()) + routinesPool := safe.NewPool(context.Background()) watcher := NewConfigurationWatcher(routinesPool, &mockProvider{}, []string{}, "") @@ -611,7 +611,7 @@ func TestApplyConfigUnderStress(t *testing.T) { } func TestListenProvidersIgnoreIntermediateConfigs(t *testing.T) { - routinesPool := safe.NewPool(t.Context()) + routinesPool := safe.NewPool(context.Background()) configuration := &dynamic.Configuration{ HTTP: th.BuildConfiguration( @@ -704,7 +704,7 @@ func TestListenProvidersIgnoreIntermediateConfigs(t *testing.T) { } func TestListenProvidersPublishesConfigForEachProvider(t *testing.T) { - routinesPool := safe.NewPool(t.Context()) + routinesPool := safe.NewPool(context.Background()) configuration := &dynamic.Configuration{ HTTP: th.BuildConfiguration( @@ -771,7 +771,7 @@ func TestListenProvidersPublishesConfigForEachProvider(t *testing.T) { } func TestPublishConfigUpdatedByProvider(t *testing.T) { - routinesPool := safe.NewPool(t.Context()) + routinesPool := safe.NewPool(context.Background()) pvdConfiguration := dynamic.Configuration{ TCP: &dynamic.TCPConfiguration{ @@ -817,7 +817,7 @@ func TestPublishConfigUpdatedByProvider(t *testing.T) { } func TestPublishConfigUpdatedByConfigWatcherListener(t *testing.T) { - routinesPool := safe.NewPool(t.Context()) + routinesPool := safe.NewPool(context.Background()) pvd := &mockProvider{ wait: 10 * time.Millisecond, diff --git a/pkg/server/middleware/middlewares.go b/pkg/server/middleware/middlewares.go index 624a3c9c9..9eebfd31c 100644 --- a/pkg/server/middleware/middlewares.go +++ b/pkg/server/middleware/middlewares.go @@ -428,5 +428,8 @@ func (b *Builder) buildConstructor(ctx context.Context, middlewareName string) ( return nil, fmt.Errorf("invalid middleware %q configuration: invalid middleware type or middleware does not exist", middlewareName) } + // The tracing middleware is a NOOP if tracing is not setup on the middleware chain. + // Hence, regarding internal resources' observability deactivation, + // this would not enable tracing. return observability.WrapMiddleware(ctx, middleware), nil } diff --git a/pkg/server/middleware/middlewares_test.go b/pkg/server/middleware/middlewares_test.go index 92cebc050..89ea08963 100644 --- a/pkg/server/middleware/middlewares_test.go +++ b/pkg/server/middleware/middlewares_test.go @@ -1,6 +1,7 @@ package middleware import ( + "context" "errors" "net/http" "net/http/httptest" @@ -19,7 +20,7 @@ func TestBuilder_BuildChainNilConfig(t *testing.T) { } middlewaresBuilder := NewBuilder(testConfig, nil, nil) - chain := middlewaresBuilder.BuildChain(t.Context(), []string{"empty"}) + chain := middlewaresBuilder.BuildChain(context.Background(), []string{"empty"}) _, err := chain.Then(nil) require.Error(t, err) } @@ -30,7 +31,7 @@ func TestBuilder_BuildChainNonExistentChain(t *testing.T) { } middlewaresBuilder := NewBuilder(testConfig, nil, nil) - chain := middlewaresBuilder.BuildChain(t.Context(), []string{"empty"}) + chain := middlewaresBuilder.BuildChain(context.Background(), []string{"empty"}) _, err := chain.Then(nil) require.Error(t, err) } @@ -258,7 +259,7 @@ func TestBuilder_BuildChainWithContext(t *testing.T) { t.Run(test.desc, func(t *testing.T) { t.Parallel() - ctx := t.Context() + ctx := context.Background() if len(test.contextProvider) > 0 { ctx = provider.AddInContext(ctx, "foobar@"+test.contextProvider) } @@ -365,7 +366,7 @@ func TestBuilder_buildConstructor(t *testing.T) { t.Run(test.desc, func(t *testing.T) { t.Parallel() - constructor, err := middlewaresBuilder.buildConstructor(t.Context(), test.middlewareID) + constructor, err := middlewaresBuilder.buildConstructor(context.Background(), test.middlewareID) require.NoError(t, err) middleware, err2 := constructor(http.HandlerFunc(func(_ http.ResponseWriter, _ *http.Request) {})) diff --git a/pkg/server/middleware/observability.go b/pkg/server/middleware/observability.go index 0ffe34165..d279be902 100644 --- a/pkg/server/middleware/observability.go +++ b/pkg/server/middleware/observability.go @@ -4,19 +4,19 @@ import ( "context" "io" "net/http" + "strings" "github.com/containous/alice" "github.com/rs/zerolog/log" "github.com/traefik/traefik/v3/pkg/config/dynamic" "github.com/traefik/traefik/v3/pkg/config/static" + "github.com/traefik/traefik/v3/pkg/logs" + "github.com/traefik/traefik/v3/pkg/metrics" "github.com/traefik/traefik/v3/pkg/middlewares/accesslog" "github.com/traefik/traefik/v3/pkg/middlewares/capture" mmetrics "github.com/traefik/traefik/v3/pkg/middlewares/metrics" "github.com/traefik/traefik/v3/pkg/middlewares/observability" - "github.com/traefik/traefik/v3/pkg/observability/logs" - "github.com/traefik/traefik/v3/pkg/observability/metrics" - "github.com/traefik/traefik/v3/pkg/observability/tracing" - otypes "github.com/traefik/traefik/v3/pkg/observability/types" + "github.com/traefik/traefik/v3/pkg/tracing" ) // ObservabilityMgr is a manager for observability (AccessLogs, Metrics and Tracing) enablement. @@ -42,44 +42,111 @@ func NewObservabilityMgr(config static.Configuration, metricsRegistry metrics.Re } // BuildEPChain an observability middleware chain by entry point. -func (o *ObservabilityMgr) BuildEPChain(ctx context.Context, entryPointName string, internal bool, config dynamic.RouterObservabilityConfig) alice.Chain { +func (o *ObservabilityMgr) BuildEPChain(ctx context.Context, entryPointName string, resourceName string, observabilityConfig *dynamic.RouterObservabilityConfig) alice.Chain { chain := alice.New() if o == nil { return chain } - // Injection of the observability variables in the request context. - // This injection must be the first step in order for other observability middlewares to rely on it. - chain = chain.Append(func(next http.Handler) (http.Handler, error) { - return o.observabilityContextHandler(next, internal, config), nil - }) - - // Capture middleware for accessLogs or metrics. - if o.shouldAccessLog(internal, config) || o.shouldMeter(internal, config) || o.shouldMeterSemConv(internal, config) { - chain = chain.Append(capture.Wrap) + if o.accessLoggerMiddleware != nil || o.metricsRegistry != nil && (o.metricsRegistry.IsEpEnabled() || o.metricsRegistry.IsRouterEnabled() || o.metricsRegistry.IsSvcEnabled()) { + if o.ShouldAddAccessLogs(resourceName, observabilityConfig) || o.ShouldAddMetrics(resourceName, observabilityConfig) { + chain = chain.Append(capture.Wrap) + } } // As the Entry point observability middleware ensures that the tracing is added to the request and logger context, // it needs to be added before the access log middleware to ensure that the trace ID is logged. - chain = chain.Append(observability.EntryPointHandler(ctx, o.tracer, entryPointName)) + if o.tracer != nil && o.ShouldAddTracing(resourceName, observabilityConfig) { + chain = chain.Append(observability.EntryPointHandler(ctx, o.tracer, entryPointName)) + } - // Access log handlers. - chain = chain.Append(o.accessLoggerMiddleware.AliceConstructor()) - chain = chain.Append(func(next http.Handler) (http.Handler, error) { - return accesslog.NewFieldHandler(next, logs.EntryPointName, entryPointName, accesslog.InitServiceFields), nil - }) - - // Entrypoint metrics handler. - metricsHandler := mmetrics.EntryPointMetricsHandler(ctx, o.metricsRegistry, entryPointName) - chain = chain.Append(observability.WrapMiddleware(ctx, metricsHandler)) + if o.accessLoggerMiddleware != nil && o.ShouldAddAccessLogs(resourceName, observabilityConfig) { + chain = chain.Append(accesslog.WrapHandler(o.accessLoggerMiddleware)) + chain = chain.Append(func(next http.Handler) (http.Handler, error) { + return accesslog.NewFieldHandler(next, logs.EntryPointName, entryPointName, accesslog.InitServiceFields), nil + }) + } // Semantic convention server metrics handler. - chain = chain.Append(observability.SemConvServerMetricsHandler(ctx, o.semConvMetricRegistry)) + if o.semConvMetricRegistry != nil && o.ShouldAddMetrics(resourceName, observabilityConfig) { + chain = chain.Append(observability.SemConvServerMetricsHandler(ctx, o.semConvMetricRegistry)) + } + + if o.metricsRegistry != nil && o.metricsRegistry.IsEpEnabled() && o.ShouldAddMetrics(resourceName, observabilityConfig) { + metricsHandler := mmetrics.WrapEntryPointHandler(ctx, o.metricsRegistry, entryPointName) + + if o.tracer != nil && o.ShouldAddTracing(resourceName, observabilityConfig) { + chain = chain.Append(observability.WrapMiddleware(ctx, metricsHandler)) + } else { + chain = chain.Append(metricsHandler) + } + } + + // Inject context keys to control whether to produce metrics further downstream (services, round-tripper), + // because the router configuration cannot be evaluated during build time for services. + if observabilityConfig != nil && observabilityConfig.Metrics != nil && !*observabilityConfig.Metrics { + chain = chain.Append(func(next http.Handler) (http.Handler, error) { + return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + next.ServeHTTP(rw, req.WithContext(context.WithValue(req.Context(), observability.DisableMetricsKey, true))) + }), nil + }) + } return chain } +// ShouldAddAccessLogs returns whether the access logs should be enabled for the given serviceName and the observability config. +func (o *ObservabilityMgr) ShouldAddAccessLogs(serviceName string, observabilityConfig *dynamic.RouterObservabilityConfig) bool { + if o == nil { + return false + } + + if o.config.AccessLog == nil { + return false + } + + if strings.HasSuffix(serviceName, "@internal") && !o.config.AccessLog.AddInternals { + return false + } + + return observabilityConfig == nil || observabilityConfig.AccessLogs == nil || *observabilityConfig.AccessLogs +} + +// ShouldAddMetrics returns whether the metrics should be enabled for the given resource and the observability config. +func (o *ObservabilityMgr) ShouldAddMetrics(serviceName string, observabilityConfig *dynamic.RouterObservabilityConfig) bool { + if o == nil { + return false + } + + if o.config.Metrics == nil { + return false + } + + if strings.HasSuffix(serviceName, "@internal") && !o.config.Metrics.AddInternals { + return false + } + + return observabilityConfig == nil || observabilityConfig.Metrics == nil || *observabilityConfig.Metrics +} + +// ShouldAddTracing returns whether the tracing should be enabled for the given serviceName and the observability config. +func (o *ObservabilityMgr) ShouldAddTracing(serviceName string, observabilityConfig *dynamic.RouterObservabilityConfig) bool { + if o == nil { + return false + } + + if o.config.Tracing == nil { + return false + } + + if strings.HasSuffix(serviceName, "@internal") && !o.config.Tracing.AddInternals { + return false + } + + return observabilityConfig == nil || observabilityConfig.Tracing == nil || *observabilityConfig.Tracing +} + // MetricsRegistry is an accessor to the metrics registry. func (o *ObservabilityMgr) MetricsRegistry() metrics.Registry { if o == nil { @@ -124,89 +191,3 @@ func (o *ObservabilityMgr) RotateAccessLogs() error { return o.accessLoggerMiddleware.Rotate() } - -func (o *ObservabilityMgr) observabilityContextHandler(next http.Handler, internal bool, config dynamic.RouterObservabilityConfig) http.Handler { - return observability.WithObservabilityHandler(next, observability.Observability{ - AccessLogsEnabled: o.shouldAccessLog(internal, config), - MetricsEnabled: o.shouldMeter(internal, config), - SemConvMetricsEnabled: o.shouldMeterSemConv(internal, config), - TracingEnabled: o.shouldTrace(internal, config, otypes.MinimalVerbosity), - DetailedTracingEnabled: o.shouldTrace(internal, config, otypes.DetailedVerbosity), - }) -} - -// shouldAccessLog returns whether the access logs should be enabled for the given serviceName and the observability config. -func (o *ObservabilityMgr) shouldAccessLog(internal bool, observabilityConfig dynamic.RouterObservabilityConfig) bool { - if o == nil { - return false - } - - if o.config.AccessLog == nil { - return false - } - - if internal && !o.config.AccessLog.AddInternals { - return false - } - - return observabilityConfig.AccessLogs == nil || *observabilityConfig.AccessLogs -} - -// shouldMeter returns whether the metrics should be enabled for the given serviceName and the observability config. -func (o *ObservabilityMgr) shouldMeter(internal bool, observabilityConfig dynamic.RouterObservabilityConfig) bool { - if o == nil || o.metricsRegistry == nil { - return false - } - - if !o.metricsRegistry.IsEpEnabled() && !o.metricsRegistry.IsRouterEnabled() && !o.metricsRegistry.IsSvcEnabled() { - return false - } - - if o.config.Metrics == nil { - return false - } - - if internal && !o.config.Metrics.AddInternals { - return false - } - - return observabilityConfig.Metrics == nil || *observabilityConfig.Metrics -} - -// shouldMeterSemConv returns whether the OTel semantic convention metrics should be enabled for the given serviceName and the observability config. -func (o *ObservabilityMgr) shouldMeterSemConv(internal bool, observabilityConfig dynamic.RouterObservabilityConfig) bool { - if o == nil || o.semConvMetricRegistry == nil { - return false - } - - if o.config.Metrics == nil { - return false - } - - if internal && !o.config.Metrics.AddInternals { - return false - } - - return observabilityConfig.Metrics == nil || *observabilityConfig.Metrics -} - -// shouldTrace returns whether the tracing should be enabled for the given serviceName and the observability config. -func (o *ObservabilityMgr) shouldTrace(internal bool, observabilityConfig dynamic.RouterObservabilityConfig, verbosity otypes.TracingVerbosity) bool { - if o == nil { - return false - } - - if o.config.Tracing == nil { - return false - } - - if internal && !o.config.Tracing.AddInternals { - return false - } - - if !observabilityConfig.TraceVerbosity.Allows(verbosity) { - return false - } - - return observabilityConfig.Tracing == nil || *observabilityConfig.Tracing -} diff --git a/pkg/server/middleware/plugins.go b/pkg/server/middleware/plugins.go index 0529ca190..eacf8fba1 100644 --- a/pkg/server/middleware/plugins.go +++ b/pkg/server/middleware/plugins.go @@ -7,6 +7,7 @@ import ( "github.com/traefik/traefik/v3/pkg/config/dynamic" "github.com/traefik/traefik/v3/pkg/plugins" + "go.opentelemetry.io/otel/trace" ) const typeName = "Plugin" @@ -54,6 +55,6 @@ func (s *traceablePlugin) ServeHTTP(rw http.ResponseWriter, req *http.Request) { s.h.ServeHTTP(rw, req) } -func (s *traceablePlugin) GetTracingInformation() (string, string) { - return s.name, typeName +func (s *traceablePlugin) GetTracingInformation() (string, string, trace.SpanKind) { + return s.name, typeName, trace.SpanKindInternal } diff --git a/pkg/server/provider/provider_test.go b/pkg/server/provider/provider_test.go index f4a0b27e3..7686d1bb5 100644 --- a/pkg/server/provider/provider_test.go +++ b/pkg/server/provider/provider_test.go @@ -16,31 +16,31 @@ func TestAddInContext(t *testing.T) { }{ { desc: "without provider information", - ctx: t.Context(), + ctx: context.Background(), name: "test", expected: "", }, { desc: "provider name embedded in element name", - ctx: t.Context(), + ctx: context.Background(), name: "test@foo", expected: "foo", }, { desc: "provider name in context", - ctx: context.WithValue(t.Context(), key, "foo"), + ctx: context.WithValue(context.Background(), key, "foo"), name: "test", expected: "foo", }, { desc: "provider name in context and different provider name embedded in element name", - ctx: context.WithValue(t.Context(), key, "foo"), + ctx: context.WithValue(context.Background(), key, "foo"), name: "test@fii", expected: "fii", }, { desc: "provider name in context and same provider name embedded in element name", - ctx: context.WithValue(t.Context(), key, "foo"), + ctx: context.WithValue(context.Background(), key, "foo"), name: "test@foo", expected: "foo", }, @@ -71,31 +71,31 @@ func TestGetQualifiedName(t *testing.T) { }{ { desc: "empty name", - ctx: t.Context(), + ctx: context.Background(), name: "", expected: "", }, { desc: "without provider", - ctx: t.Context(), + ctx: context.Background(), name: "test", expected: "test", }, { desc: "with explicit provider", - ctx: t.Context(), + ctx: context.Background(), name: "test@foo", expected: "test@foo", }, { desc: "with provider in context", - ctx: context.WithValue(t.Context(), key, "foo"), + ctx: context.WithValue(context.Background(), key, "foo"), name: "test", expected: "test@foo", }, { desc: "with provider in context and explicit name", - ctx: context.WithValue(t.Context(), key, "foo"), + ctx: context.WithValue(context.Background(), key, "foo"), name: "test@fii", expected: "test@fii", }, diff --git a/pkg/server/router/router.go b/pkg/server/router/router.go index bac2d5c48..3abd239e5 100644 --- a/pkg/server/router/router.go +++ b/pkg/server/router/router.go @@ -10,15 +10,14 @@ import ( "github.com/containous/alice" "github.com/rs/zerolog/log" - "github.com/traefik/traefik/v3/pkg/config/dynamic" "github.com/traefik/traefik/v3/pkg/config/runtime" + "github.com/traefik/traefik/v3/pkg/logs" "github.com/traefik/traefik/v3/pkg/middlewares/accesslog" "github.com/traefik/traefik/v3/pkg/middlewares/denyrouterrecursion" metricsMiddle "github.com/traefik/traefik/v3/pkg/middlewares/metrics" "github.com/traefik/traefik/v3/pkg/middlewares/observability" "github.com/traefik/traefik/v3/pkg/middlewares/recovery" httpmuxer "github.com/traefik/traefik/v3/pkg/muxer/http" - "github.com/traefik/traefik/v3/pkg/observability/logs" "github.com/traefik/traefik/v3/pkg/server/middleware" "github.com/traefik/traefik/v3/pkg/server/provider" "github.com/traefik/traefik/v3/pkg/tls" @@ -71,22 +70,11 @@ func (m *Manager) getHTTPRouters(ctx context.Context, entryPoints []string, tls func (m *Manager) BuildHandlers(rootCtx context.Context, entryPoints []string, tls bool) map[string]http.Handler { entryPointHandlers := make(map[string]http.Handler) - defaultObsConfig := dynamic.RouterObservabilityConfig{} - defaultObsConfig.SetDefaults() - for entryPointName, routers := range m.getHTTPRouters(rootCtx, entryPoints, tls) { logger := log.Ctx(rootCtx).With().Str(logs.EntryPointName, entryPointName).Logger() ctx := logger.WithContext(rootCtx) - // TODO: Improve this part. Relying on models is a shortcut to get the entrypoint observability configuration. Maybe we should pass down the static configuration. - // When the entry point has no observability configuration no model is produced, - // and we need to create the default configuration is this case. - epObsConfig := defaultObsConfig - if model, ok := m.conf.Models[entryPointName+"@internal"]; ok && model != nil { - epObsConfig = model.Observability - } - - handler, err := m.buildEntryPointHandler(ctx, entryPointName, routers, epObsConfig) + handler, err := m.buildEntryPointHandler(ctx, entryPointName, routers) if err != nil { logger.Error().Err(err).Send() continue @@ -105,15 +93,7 @@ func (m *Manager) BuildHandlers(rootCtx context.Context, entryPoints []string, t continue } - // TODO: Improve this part. Relying on models is a shortcut to get the entrypoint observability configuration. Maybe we should pass down the static configuration. - // When the entry point has no observability configuration no model is produced, - // and we need to create the default configuration is this case. - epObsConfig := defaultObsConfig - if model, ok := m.conf.Models[entryPointName+"@internal"]; ok && model != nil { - epObsConfig = model.Observability - } - - defaultHandler, err := m.observabilityMgr.BuildEPChain(ctx, entryPointName, false, epObsConfig).Then(http.NotFoundHandler()) + defaultHandler, err := m.observabilityMgr.BuildEPChain(ctx, entryPointName, "", nil).Then(BuildDefaultHTTPRouter()) if err != nil { logger.Error().Err(err).Send() continue @@ -124,10 +104,10 @@ func (m *Manager) BuildHandlers(rootCtx context.Context, entryPoints []string, t return entryPointHandlers } -func (m *Manager) buildEntryPointHandler(ctx context.Context, entryPointName string, configs map[string]*runtime.RouterInfo, config dynamic.RouterObservabilityConfig) (http.Handler, error) { +func (m *Manager) buildEntryPointHandler(ctx context.Context, entryPointName string, configs map[string]*runtime.RouterInfo) (http.Handler, error) { muxer := httpmuxer.NewMuxer(m.parser) - defaultHandler, err := m.observabilityMgr.BuildEPChain(ctx, entryPointName, false, config).Then(http.NotFoundHandler()) + defaultHandler, err := m.observabilityMgr.BuildEPChain(ctx, entryPointName, "", nil).Then(http.NotFoundHandler()) if err != nil { return nil, err } @@ -156,11 +136,7 @@ func (m *Manager) buildEntryPointHandler(ctx context.Context, entryPointName str continue } - if routerConfig.Observability != nil { - config = *routerConfig.Observability - } - - observabilityChain := m.observabilityMgr.BuildEPChain(ctxRouter, entryPointName, strings.HasSuffix(routerConfig.Service, "@internal"), config) + observabilityChain := m.observabilityMgr.BuildEPChain(ctx, entryPointName, routerConfig.Service, routerConfig.Observability) handler, err = observabilityChain.Then(handler) if err != nil { routerConfig.AddError(err, true) @@ -204,7 +180,22 @@ func (m *Manager) buildRouterHandler(ctx context.Context, routerName string, rou return nil, err } - m.routerHandlers[routerName] = handler + // Prevents from enabling observability for internal resources. + if !m.observabilityMgr.ShouldAddAccessLogs(provider.GetQualifiedName(ctx, routerConfig.Service), routerConfig.Observability) { + m.routerHandlers[routerName] = handler + return m.routerHandlers[routerName], nil + } + + handlerWithAccessLog, err := alice.New(func(next http.Handler) (http.Handler, error) { + return accesslog.NewFieldHandler(next, accesslog.RouterName, routerName, nil), nil + }).Then(handler) + if err != nil { + log.Ctx(ctx).Error().Err(err).Send() + m.routerHandlers[routerName] = handler + } else { + m.routerHandlers[routerName] = handlerWithAccessLog + } + return m.routerHandlers[routerName], nil } @@ -219,29 +210,40 @@ func (m *Manager) buildHTTPHandler(ctx context.Context, router *runtime.RouterIn return nil, errors.New("the service is missing on the router") } - qualifiedService := provider.GetQualifiedName(ctx, router.Service) + sHandler, err := m.serviceManager.BuildHTTP(ctx, router.Service) + if err != nil { + return nil, err + } + + mHandler := m.middlewaresBuilder.BuildChain(ctx, router.Middlewares) chain := alice.New() + if m.observabilityMgr.MetricsRegistry() != nil && m.observabilityMgr.MetricsRegistry().IsRouterEnabled() && + m.observabilityMgr.ShouldAddMetrics(provider.GetQualifiedName(ctx, router.Service), router.Observability) { + chain = chain.Append(metricsMiddle.WrapRouterHandler(ctx, m.observabilityMgr.MetricsRegistry(), routerName, provider.GetQualifiedName(ctx, router.Service))) + } + + // Prevents from enabling tracing for internal resources. + if !m.observabilityMgr.ShouldAddTracing(provider.GetQualifiedName(ctx, router.Service), router.Observability) { + return chain.Extend(*mHandler).Then(sHandler) + } + + chain = chain.Append(observability.WrapRouterHandler(ctx, routerName, router.Rule, provider.GetQualifiedName(ctx, router.Service))) + + if m.observabilityMgr.MetricsRegistry() != nil && m.observabilityMgr.MetricsRegistry().IsRouterEnabled() { + metricsHandler := metricsMiddle.WrapRouterHandler(ctx, m.observabilityMgr.MetricsRegistry(), routerName, provider.GetQualifiedName(ctx, router.Service)) + chain = chain.Append(observability.WrapMiddleware(ctx, metricsHandler)) + } + if router.DefaultRule { chain = chain.Append(denyrouterrecursion.WrapHandler(routerName)) } - // Access logs, metrics, and tracing middlewares are idempotent if the associated signal is disabled. - chain = chain.Append(observability.WrapRouterHandler(ctx, routerName, router.Rule, qualifiedService)) - metricsHandler := metricsMiddle.RouterMetricsHandler(ctx, m.observabilityMgr.MetricsRegistry(), routerName, qualifiedService) - - chain = chain.Append(observability.WrapMiddleware(ctx, metricsHandler)) - chain = chain.Append(func(next http.Handler) (http.Handler, error) { - return accesslog.NewFieldHandler(next, accesslog.RouterName, routerName, nil), nil - }) - - mHandler := m.middlewaresBuilder.BuildChain(ctx, router.Middlewares) - - sHandler, err := m.serviceManager.BuildHTTP(ctx, qualifiedService) - if err != nil { - return nil, err - } - return chain.Extend(*mHandler).Then(sHandler) } + +// BuildDefaultHTTPRouter creates a default HTTP router. +func BuildDefaultHTTPRouter() http.Handler { + return http.NotFoundHandler() +} diff --git a/pkg/server/router/router_test.go b/pkg/server/router/router_test.go index 1458b2e99..b73f3df5a 100644 --- a/pkg/server/router/router_test.go +++ b/pkg/server/router/router_test.go @@ -1,6 +1,7 @@ package router import ( + "context" "crypto/tls" "io" "math" @@ -325,14 +326,14 @@ func TestRouterManager_Get(t *testing.T) { serviceManager := service.NewManager(rtConf.Services, nil, nil, transportManager, proxyBuilderMock{}) middlewaresBuilder := middleware.NewBuilder(rtConf.Middlewares, serviceManager, nil) - tlsManager := traefiktls.NewManager(nil) + tlsManager := traefiktls.NewManager() parser, err := httpmuxer.NewSyntaxParser() require.NoError(t, err) routerManager := NewManager(rtConf, serviceManager, middlewaresBuilder, nil, tlsManager, parser) - handlers := routerManager.BuildHandlers(t.Context(), test.entryPoints, false) + handlers := routerManager.BuildHandlers(context.Background(), test.entryPoints, false) w := httptest.NewRecorder() req := testhelpers.MustNewRequest(http.MethodGet, "http://foo.bar/", nil) @@ -712,16 +713,16 @@ func TestRuntimeConfiguration(t *testing.T) { serviceManager := service.NewManager(rtConf.Services, nil, nil, transportManager, proxyBuilderMock{}) middlewaresBuilder := middleware.NewBuilder(rtConf.Middlewares, serviceManager, nil) - tlsManager := traefiktls.NewManager(nil) - tlsManager.UpdateConfigs(t.Context(), nil, test.tlsOptions, nil) + tlsManager := traefiktls.NewManager() + tlsManager.UpdateConfigs(context.Background(), nil, test.tlsOptions, nil) parser, err := httpmuxer.NewSyntaxParser() require.NoError(t, err) routerManager := NewManager(rtConf, serviceManager, middlewaresBuilder, nil, tlsManager, parser) - _ = routerManager.BuildHandlers(t.Context(), entryPoints, false) - _ = routerManager.BuildHandlers(t.Context(), entryPoints, true) + _ = routerManager.BuildHandlers(context.Background(), entryPoints, false) + _ = routerManager.BuildHandlers(context.Background(), entryPoints, true) // even though rtConf was passed by argument to the manager builders above, // it's ok to use it as the result we check, because everything worth checking @@ -794,14 +795,14 @@ func TestProviderOnMiddlewares(t *testing.T) { serviceManager := service.NewManager(rtConf.Services, nil, nil, transportManager, nil) middlewaresBuilder := middleware.NewBuilder(rtConf.Middlewares, serviceManager, nil) - tlsManager := traefiktls.NewManager(nil) + tlsManager := traefiktls.NewManager() parser, err := httpmuxer.NewSyntaxParser() require.NoError(t, err) routerManager := NewManager(rtConf, serviceManager, middlewaresBuilder, nil, tlsManager, parser) - _ = routerManager.BuildHandlers(t.Context(), entryPoints, false) + _ = routerManager.BuildHandlers(context.Background(), entryPoints, false) assert.Equal(t, []string{"chain@file", "m1@file"}, rtConf.Routers["router@file"].Middlewares) assert.Equal(t, []string{"m1@file", "m2@file", "m1@file"}, rtConf.Middlewares["chain@file"].Chain.Middlewares) @@ -873,14 +874,14 @@ func BenchmarkRouterServe(b *testing.B) { serviceManager := service.NewManager(rtConf.Services, nil, nil, staticTransportManager{res}, nil) middlewaresBuilder := middleware.NewBuilder(rtConf.Middlewares, serviceManager, nil) - tlsManager := traefiktls.NewManager(nil) + tlsManager := traefiktls.NewManager() parser, err := httpmuxer.NewSyntaxParser() require.NoError(b, err) routerManager := NewManager(rtConf, serviceManager, middlewaresBuilder, nil, tlsManager, parser) - handlers := routerManager.BuildHandlers(b.Context(), entryPoints, false) + handlers := routerManager.BuildHandlers(context.Background(), entryPoints, false) w := httptest.NewRecorder() req := testhelpers.MustNewRequest(http.MethodGet, "http://foo.bar/", nil) @@ -920,7 +921,7 @@ func BenchmarkService(b *testing.B) { w := httptest.NewRecorder() req := testhelpers.MustNewRequest(http.MethodGet, "http://foo.bar/", nil) - handler, _ := serviceManager.BuildHTTP(b.Context(), "foo-service") + handler, _ := serviceManager.BuildHTTP(context.Background(), "foo-service") b.ReportAllocs() for range b.N { handler.ServeHTTP(w, req) @@ -929,7 +930,7 @@ func BenchmarkService(b *testing.B) { type proxyBuilderMock struct{} -func (p proxyBuilderMock) Build(_ string, _ *url.URL, _, _ bool, _ time.Duration) (http.Handler, error) { +func (p proxyBuilderMock) Build(_ string, _ *url.URL, _, _, _ bool, _ time.Duration) (http.Handler, error) { return http.HandlerFunc(func(responseWriter http.ResponseWriter, req *http.Request) {}), nil } diff --git a/pkg/server/router/tcp/manager.go b/pkg/server/router/tcp/manager.go index 788bf7cb0..5966462cc 100644 --- a/pkg/server/router/tcp/manager.go +++ b/pkg/server/router/tcp/manager.go @@ -11,10 +11,10 @@ import ( "github.com/rs/zerolog/log" "github.com/traefik/traefik/v3/pkg/config/runtime" + "github.com/traefik/traefik/v3/pkg/logs" "github.com/traefik/traefik/v3/pkg/middlewares/snicheck" httpmuxer "github.com/traefik/traefik/v3/pkg/muxer/http" tcpmuxer "github.com/traefik/traefik/v3/pkg/muxer/tcp" - "github.com/traefik/traefik/v3/pkg/observability/logs" "github.com/traefik/traefik/v3/pkg/server/provider" tcpservice "github.com/traefik/traefik/v3/pkg/server/service/tcp" "github.com/traefik/traefik/v3/pkg/tcp" diff --git a/pkg/server/router/tcp/manager_test.go b/pkg/server/router/tcp/manager_test.go index 32b3218e0..97cec0444 100644 --- a/pkg/server/router/tcp/manager_test.go +++ b/pkg/server/router/tcp/manager_test.go @@ -1,6 +1,7 @@ package tcp import ( + "context" "crypto/tls" "math" "net/http" @@ -347,9 +348,9 @@ func TestRuntimeConfiguration(t *testing.T) { dialerManager := tcp2.NewDialerManager(nil) dialerManager.Update(map[string]*dynamic.TCPServersTransport{"default@internal": {}}) serviceManager := tcp.NewManager(conf, dialerManager) - tlsManager := traefiktls.NewManager(nil) + tlsManager := traefiktls.NewManager() tlsManager.UpdateConfigs( - t.Context(), + context.Background(), map[string]traefiktls.Store{}, map[string]traefiktls.Options{ "default": { @@ -369,7 +370,7 @@ func TestRuntimeConfiguration(t *testing.T) { routerManager := NewManager(conf, serviceManager, middlewaresBuilder, nil, nil, tlsManager) - _ = routerManager.BuildHandlers(t.Context(), entryPoints) + _ = routerManager.BuildHandlers(context.Background(), entryPoints) // even though conf was passed by argument to the manager builders above, // it's ok to use it as the result we check, because everything worth checking @@ -659,8 +660,8 @@ func TestDomainFronting(t *testing.T) { serviceManager := tcp.NewManager(conf, tcp2.NewDialerManager(nil)) - tlsManager := traefiktls.NewManager(nil) - tlsManager.UpdateConfigs(t.Context(), map[string]traefiktls.Store{}, test.tlsOptions, []*traefiktls.CertAndStores{}) + tlsManager := traefiktls.NewManager() + tlsManager.UpdateConfigs(context.Background(), map[string]traefiktls.Store{}, test.tlsOptions, []*traefiktls.CertAndStores{}) httpsHandler := map[string]http.Handler{ "web": http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {}), @@ -670,7 +671,7 @@ func TestDomainFronting(t *testing.T) { routerManager := NewManager(conf, serviceManager, middlewaresBuilder, nil, httpsHandler, tlsManager) - routers := routerManager.BuildHandlers(t.Context(), entryPoints) + routers := routerManager.BuildHandlers(context.Background(), entryPoints) router, ok := routers["web"] require.True(t, ok) diff --git a/pkg/server/router/tcp/router_test.go b/pkg/server/router/tcp/router_test.go index 1c2875776..16f633443 100644 --- a/pkg/server/router/tcp/router_test.go +++ b/pkg/server/router/tcp/router_test.go @@ -2,6 +2,7 @@ package tcp import ( "bytes" + "context" "crypto/tls" "errors" "fmt" @@ -172,9 +173,9 @@ func Test_Routing(t *testing.T) { require.NoError(t, err) // Creates the tlsManager and defines the TLS 1.0 and 1.2 TLSOptions. - tlsManager := traefiktls.NewManager(nil) + tlsManager := traefiktls.NewManager() tlsManager.UpdateConfigs( - t.Context(), + context.Background(), map[string]traefiktls.Store{ tlsalpn01.ACMETLS1Protocol: {}, }, @@ -605,7 +606,7 @@ func Test_Routing(t *testing.T) { router(dynConf) } - router, err := manager.buildEntryPointHandler(t.Context(), dynConf.TCPRouters, dynConf.Routers, nil, nil) + router, err := manager.buildEntryPointHandler(context.Background(), dynConf.TCPRouters, dynConf.Routers, nil, nil) require.NoError(t, err) if test.allowACMETLSPassthrough { diff --git a/pkg/server/router/udp/router.go b/pkg/server/router/udp/router.go index 910efa499..8fb0f70d6 100644 --- a/pkg/server/router/udp/router.go +++ b/pkg/server/router/udp/router.go @@ -7,7 +7,7 @@ import ( "github.com/rs/zerolog/log" "github.com/traefik/traefik/v3/pkg/config/runtime" - "github.com/traefik/traefik/v3/pkg/observability/logs" + "github.com/traefik/traefik/v3/pkg/logs" "github.com/traefik/traefik/v3/pkg/server/provider" udpservice "github.com/traefik/traefik/v3/pkg/server/service/udp" "github.com/traefik/traefik/v3/pkg/udp" diff --git a/pkg/server/router/udp/router_test.go b/pkg/server/router/udp/router_test.go index 9dfcaacab..eaa99e174 100644 --- a/pkg/server/router/udp/router_test.go +++ b/pkg/server/router/udp/router_test.go @@ -1,6 +1,7 @@ package udp import ( + "context" "testing" "github.com/stretchr/testify/assert" @@ -117,7 +118,7 @@ func TestRuntimeConfiguration(t *testing.T) { serviceManager := udp.NewManager(conf) routerManager := NewManager(conf, serviceManager) - _ = routerManager.BuildHandlers(t.Context(), entryPoints) + _ = routerManager.BuildHandlers(context.Background(), entryPoints) // even though conf was passed by argument to the manager builders above, // it's ok to use it as the result we check, because everything worth checking diff --git a/pkg/server/routerfactory_test.go b/pkg/server/routerfactory_test.go index ba77f5e31..648b24e8f 100644 --- a/pkg/server/routerfactory_test.go +++ b/pkg/server/routerfactory_test.go @@ -55,7 +55,7 @@ func TestReuseService(t *testing.T) { transportManager.Update(map[string]*dynamic.ServersTransport{"default@internal": {}}) managerFactory := service.NewManagerFactory(staticConfig, nil, nil, transportManager, proxyBuilderMock{}, nil) - tlsManager := tls.NewManager(nil) + tlsManager := tls.NewManager() dialerManager := tcp.NewDialerManager(nil) dialerManager.Update(map[string]*dynamic.TCPServersTransport{"default@internal": {}}) @@ -193,7 +193,7 @@ func TestServerResponseEmptyBackend(t *testing.T) { transportManager.Update(map[string]*dynamic.ServersTransport{"default@internal": {}}) managerFactory := service.NewManagerFactory(staticConfig, nil, nil, transportManager, proxyBuilderMock{}, nil) - tlsManager := tls.NewManager(nil) + tlsManager := tls.NewManager() dialerManager := tcp.NewDialerManager(nil) dialerManager.Update(map[string]*dynamic.TCPServersTransport{"default@internal": {}}) @@ -239,7 +239,7 @@ func TestInternalServices(t *testing.T) { transportManager.Update(map[string]*dynamic.ServersTransport{"default@internal": {}}) managerFactory := service.NewManagerFactory(staticConfig, nil, nil, transportManager, nil, nil) - tlsManager := tls.NewManager(nil) + tlsManager := tls.NewManager() dialerManager := tcp.NewDialerManager(nil) dialerManager.Update(map[string]*dynamic.TCPServersTransport{"default@internal": {}}) @@ -258,7 +258,7 @@ func TestInternalServices(t *testing.T) { type proxyBuilderMock struct{} -func (p proxyBuilderMock) Build(_ string, _ *url.URL, _, _ bool, _ time.Duration) (http.Handler, error) { +func (p proxyBuilderMock) Build(_ string, _ *url.URL, _, _, _ bool, _ time.Duration) (http.Handler, error) { return http.HandlerFunc(func(responseWriter http.ResponseWriter, req *http.Request) {}), nil } diff --git a/pkg/server/server.go b/pkg/server/server.go index 72af729e6..9c96bdcc6 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -8,7 +8,7 @@ import ( "time" "github.com/rs/zerolog/log" - "github.com/traefik/traefik/v3/pkg/observability/metrics" + "github.com/traefik/traefik/v3/pkg/metrics" "github.com/traefik/traefik/v3/pkg/safe" "github.com/traefik/traefik/v3/pkg/server/middleware" ) diff --git a/pkg/server/server_entrypoint_listenconfig_other_test.go b/pkg/server/server_entrypoint_listenconfig_other_test.go index 45fa5ec47..b143db1c6 100644 --- a/pkg/server/server_entrypoint_listenconfig_other_test.go +++ b/pkg/server/server_entrypoint_listenconfig_other_test.go @@ -16,12 +16,12 @@ func TestNewListenConfig(t *testing.T) { require.Nil(t, listenConfig.Control) require.Zero(t, listenConfig.KeepAlive) - l1, err := listenConfig.Listen(t.Context(), "tcp", ep.Address) + l1, err := listenConfig.Listen(context.Background(), "tcp", ep.Address) require.NoError(t, err) require.NotNil(t, l1) defer l1.Close() - l2, err := listenConfig.Listen(t.Context(), "tcp", l1.Addr().String()) + l2, err := listenConfig.Listen(context.Background(), "tcp", l1.Addr().String()) require.Error(t, err) require.ErrorContains(t, err, "address already in use") require.Nil(t, l2) @@ -31,12 +31,12 @@ func TestNewListenConfig(t *testing.T) { require.Nil(t, listenConfig.Control) require.Zero(t, listenConfig.KeepAlive) - l3, err := listenConfig.Listen(t.Context(), "tcp", ep.Address) + l3, err := listenConfig.Listen(context.Background(), "tcp", ep.Address) require.NoError(t, err) require.NotNil(t, l3) defer l3.Close() - l4, err := listenConfig.Listen(t.Context(), "tcp", l3.Addr().String()) + l4, err := listenConfig.Listen(context.Background(), "tcp", l3.Addr().String()) require.Error(t, err) require.ErrorContains(t, err, "address already in use") require.Nil(t, l4) diff --git a/pkg/server/server_entrypoint_listenconfig_unix_test.go b/pkg/server/server_entrypoint_listenconfig_unix_test.go index 98aab2fea..a5f7dda0a 100644 --- a/pkg/server/server_entrypoint_listenconfig_unix_test.go +++ b/pkg/server/server_entrypoint_listenconfig_unix_test.go @@ -3,6 +3,7 @@ package server import ( + "context" "net" "testing" @@ -16,12 +17,12 @@ func TestNewListenConfig(t *testing.T) { require.Nil(t, listenConfig.Control) require.Zero(t, listenConfig.KeepAlive) - l1, err := listenConfig.Listen(t.Context(), "tcp", ep.Address) + l1, err := listenConfig.Listen(context.Background(), "tcp", ep.Address) require.NoError(t, err) require.NotNil(t, l1) defer l1.Close() - l2, err := listenConfig.Listen(t.Context(), "tcp", l1.Addr().String()) + l2, err := listenConfig.Listen(context.Background(), "tcp", l1.Addr().String()) require.Error(t, err) require.ErrorContains(t, err, "address already in use") require.Nil(t, l2) @@ -31,24 +32,24 @@ func TestNewListenConfig(t *testing.T) { require.NotNil(t, listenConfig.Control) require.Zero(t, listenConfig.KeepAlive) - l3, err := listenConfig.Listen(t.Context(), "tcp", ep.Address) + l3, err := listenConfig.Listen(context.Background(), "tcp", ep.Address) require.NoError(t, err) require.NotNil(t, l3) defer l3.Close() - l4, err := listenConfig.Listen(t.Context(), "tcp", l3.Addr().String()) + l4, err := listenConfig.Listen(context.Background(), "tcp", l3.Addr().String()) require.NoError(t, err) require.NotNil(t, l4) defer l4.Close() _, l3Port, err := net.SplitHostPort(l3.Addr().String()) require.NoError(t, err) - l5, err := listenConfig.Listen(t.Context(), "tcp", "127.0.0.1:"+l3Port) + l5, err := listenConfig.Listen(context.Background(), "tcp", "127.0.0.1:"+l3Port) require.NoError(t, err) require.NotNil(t, l5) defer l5.Close() - l6, err := listenConfig.Listen(t.Context(), "tcp", l1.Addr().String()) + l6, err := listenConfig.Listen(context.Background(), "tcp", l1.Addr().String()) require.Error(t, err) require.ErrorContains(t, err, "address already in use") require.Nil(t, l6) diff --git a/pkg/server/server_entrypoint_tcp.go b/pkg/server/server_entrypoint_tcp.go index aef3954bf..039d06659 100644 --- a/pkg/server/server_entrypoint_tcp.go +++ b/pkg/server/server_entrypoint_tcp.go @@ -12,7 +12,6 @@ import ( "os" "strings" "sync" - "sync/atomic" "syscall" "time" @@ -23,17 +22,20 @@ import ( "github.com/rs/zerolog/log" "github.com/traefik/traefik/v3/pkg/config/static" "github.com/traefik/traefik/v3/pkg/ip" + "github.com/traefik/traefik/v3/pkg/logs" + "github.com/traefik/traefik/v3/pkg/metrics" "github.com/traefik/traefik/v3/pkg/middlewares" "github.com/traefik/traefik/v3/pkg/middlewares/contenttype" "github.com/traefik/traefik/v3/pkg/middlewares/forwardedheaders" "github.com/traefik/traefik/v3/pkg/middlewares/requestdecorator" - "github.com/traefik/traefik/v3/pkg/observability/logs" - "github.com/traefik/traefik/v3/pkg/observability/metrics" "github.com/traefik/traefik/v3/pkg/safe" + "github.com/traefik/traefik/v3/pkg/server/router" tcprouter "github.com/traefik/traefik/v3/pkg/server/router/tcp" "github.com/traefik/traefik/v3/pkg/server/service" "github.com/traefik/traefik/v3/pkg/tcp" "github.com/traefik/traefik/v3/pkg/types" + "golang.org/x/net/http2" + "golang.org/x/net/http2/h2c" ) type key string @@ -57,19 +59,15 @@ type connState struct { type httpForwarder struct { net.Listener - - connChan chan net.Conn - errChan chan error - closeChan chan struct{} - closeOnce sync.Once + connChan chan net.Conn + errChan chan error } func newHTTPForwarder(ln net.Listener) *httpForwarder { return &httpForwarder{ - Listener: ln, - connChan: make(chan net.Conn), - errChan: make(chan error), - closeChan: make(chan struct{}), + Listener: ln, + connChan: make(chan net.Conn), + errChan: make(chan error), } } @@ -81,8 +79,6 @@ func (h *httpForwarder) ServeTCP(conn tcp.WriteCloser) { // Accept retrieves a served connection in ServeTCP. func (h *httpForwarder) Accept() (net.Conn, error) { select { - case <-h.closeChan: - return nil, errors.New("listener closed") case conn := <-h.connChan: return conn, nil case err := <-h.errChan: @@ -90,14 +86,6 @@ func (h *httpForwarder) Accept() (net.Conn, error) { } } -// Close closes the wrapped listener and unblocks Accept. -func (h *httpForwarder) Close() error { - h.closeOnce.Do(func() { - close(h.closeChan) - }) - return h.Listener.Close() -} - // TCPEntryPoints holds a map of TCPEntryPoint (the entrypoint names being the keys). type TCPEntryPoints map[string]*TCPEntryPoint @@ -177,9 +165,8 @@ type TCPEntryPoint struct { tracker *connectionTracker httpServer *httpServer httpsServer *httpServer - http3Server *http3server - // inShutdown reports whether the Shutdown method has been called. - inShutdown atomic.Bool + + http3Server *http3server } // NewTCPEntryPoint creates a new TCPEntryPoint. @@ -188,31 +175,31 @@ func NewTCPEntryPoint(ctx context.Context, name string, config *static.EntryPoin listener, err := buildListener(ctx, name, config) if err != nil { - return nil, fmt.Errorf("building listener: %w", err) + return nil, fmt.Errorf("error preparing server: %w", err) } rt, err := tcprouter.NewRouter() if err != nil { - return nil, fmt.Errorf("creating TCP router: %w", err) + return nil, fmt.Errorf("error preparing tcp router: %w", err) } reqDecorator := requestdecorator.New(hostResolverConfig) - httpServer, err := newHTTPServer(ctx, listener, config, true, reqDecorator) + httpServer, err := createHTTPServer(ctx, listener, config, true, reqDecorator) if err != nil { - return nil, fmt.Errorf("creating HTTP server: %w", err) + return nil, fmt.Errorf("error preparing http server: %w", err) } rt.SetHTTPForwarder(httpServer.Forwarder) - httpsServer, err := newHTTPServer(ctx, listener, config, false, reqDecorator) + httpsServer, err := createHTTPServer(ctx, listener, config, false, reqDecorator) if err != nil { - return nil, fmt.Errorf("creating HTTPS server: %w", err) + return nil, fmt.Errorf("error preparing https server: %w", err) } h3Server, err := newHTTP3Server(ctx, name, config, httpsServer) if err != nil { - return nil, fmt.Errorf("creating HTTP3 server: %w", err) + return nil, fmt.Errorf("error preparing http3 server: %w", err) } rt.SetHTTPSForwarder(httpsServer.Forwarder) @@ -242,11 +229,6 @@ func (e *TCPEntryPoint) Start(ctx context.Context) { for { conn, err := e.listener.Accept() - // As the Shutdown method has been called, an error is expected. - // Thus, it is not necessary to log it. - if err != nil && e.inShutdown.Load() { - return - } if err != nil { logger.Error().Err(err).Send() @@ -298,8 +280,6 @@ func (e *TCPEntryPoint) Start(ctx context.Context) { func (e *TCPEntryPoint) Shutdown(ctx context.Context) { logger := log.Ctx(ctx) - e.inShutdown.Store(true) - reqAcceptGraceTimeOut := time.Duration(e.transportConfiguration.LifeCycle.RequestAcceptGraceTimeout) if reqAcceptGraceTimeOut > 0 { logger.Info().Msgf("Waiting %s for incoming requests to cease", reqAcceptGraceTimeOut) @@ -373,7 +353,7 @@ func (e *TCPEntryPoint) SwitchRouter(rt *tcprouter.Router) { httpHandler := rt.GetHTTPHandler() if httpHandler == nil { - httpHandler = http.NotFoundHandler() + httpHandler = router.BuildDefaultHTTPRouter() } e.httpServer.Switcher.UpdateHandler(httpHandler) @@ -382,7 +362,7 @@ func (e *TCPEntryPoint) SwitchRouter(rt *tcprouter.Router) { httpsHandler := rt.GetHTTPSHandler() if httpsHandler == nil { - httpsHandler = http.NotFoundHandler() + httpsHandler = router.BuildDefaultHTTPRouter() } e.httpsServer.Switcher.UpdateHandler(httpsHandler) @@ -484,18 +464,6 @@ func buildProxyProtocolListener(ctx context.Context, entryPoint *static.EntryPoi return proxyListener, nil } -type onceCloseListener struct { - net.Listener - - once sync.Once - closeErr error -} - -func (oc *onceCloseListener) Close() error { - oc.once.Do(func() { oc.closeErr = oc.Listener.Close() }) - return oc.closeErr -} - func buildListener(ctx context.Context, name string, config *static.EntryPoint) (net.Listener, error) { var listener net.Listener var err error @@ -510,13 +478,6 @@ func buildListener(ctx context.Context, name string, config *static.EntryPoint) if listener == nil { listenConfig := newListenConfig(config) - - // TODO: Look into configuring keepAlive period through listenConfig instead of our custom tcpKeepAliveListener, to reactivate MultipathTCP? - // MultipathTCP is not supported on all platforms, and is notably unsupported in combination with TCP keep-alive. - if !strings.Contains(os.Getenv("GODEBUG"), "multipathtcp") { - listenConfig.SetMultipathTCP(false) - } - listener, err = listenConfig.Listen(ctx, "tcp", config.GetAddress()) if err != nil { return nil, fmt.Errorf("error opening listener: %w", err) @@ -531,7 +492,7 @@ func buildListener(ctx context.Context, name string, config *static.EntryPoint) return nil, fmt.Errorf("error creating proxy protocol listener: %w", err) } } - return &onceCloseListener{Listener: listener}, nil + return listener, nil } func newConnectionTracker(openConnectionsGauge gokitmetrics.Gauge) *connectionTracker { @@ -627,12 +588,12 @@ type httpServer struct { Switcher *middlewares.HTTPHandlerSwitcher } -func newHTTPServer(ctx context.Context, ln net.Listener, configuration *static.EntryPoint, withH2c bool, reqDecorator *requestdecorator.RequestDecorator) (*httpServer, error) { +func createHTTPServer(ctx context.Context, ln net.Listener, configuration *static.EntryPoint, withH2c bool, reqDecorator *requestdecorator.RequestDecorator) (*httpServer, error) { if configuration.HTTP2.MaxConcurrentStreams < 0 { return nil, errors.New("max concurrent streams value must be greater than or equal to zero") } - httpSwitcher := middlewares.NewHandlerSwitcher(http.NotFoundHandler()) + httpSwitcher := middlewares.NewHandlerSwitcher(router.BuildDefaultHTTPRouter()) next, err := alice.New(requestdecorator.WrapHandler(reqDecorator)).Then(httpSwitcher) if err != nil { @@ -654,12 +615,11 @@ func newHTTPServer(ctx context.Context, ln net.Listener, configuration *static.E handler = newKeepAliveMiddleware(handler, configuration.Transport.KeepAliveMaxRequests, configuration.Transport.KeepAliveMaxTime) } - var protocols http.Protocols - protocols.SetHTTP1(true) - protocols.SetHTTP2(true) - - // With the addition of UnencryptedHTTP2 in http.Server#Protocols in go1.24 setting the h2c handler is not necessary anymore. - protocols.SetUnencryptedHTTP2(withH2c) + if withH2c { + handler = h2c.NewHandler(handler, &http2.Server{ + MaxConcurrentStreams: uint32(configuration.HTTP2.MaxConcurrentStreams), + }) + } handler = contenttype.DisableAutoDetection(handler) @@ -680,16 +640,12 @@ func newHTTPServer(ctx context.Context, ln net.Listener, configuration *static.E handler = denyFragment(handler) serverHTTP := &http.Server{ - Protocols: &protocols, Handler: handler, ErrorLog: stdlog.New(logs.NoLevel(log.Logger, zerolog.DebugLevel), "", 0), ReadTimeout: time.Duration(configuration.Transport.RespondingTimeouts.ReadTimeout), WriteTimeout: time.Duration(configuration.Transport.RespondingTimeouts.WriteTimeout), IdleTimeout: time.Duration(configuration.Transport.RespondingTimeouts.IdleTimeout), MaxHeaderBytes: configuration.HTTP.MaxHeaderBytes, - HTTP2: &http.HTTP2Config{ - MaxConcurrentStreams: int(configuration.HTTP2.MaxConcurrentStreams), - }, } if debugConnection || (configuration.Transport != nil && (configuration.Transport.KeepAliveMaxTime > 0 || configuration.Transport.KeepAliveMaxRequests > 0)) { serverHTTP.ConnContext = func(ctx context.Context, c net.Conn) context.Context { @@ -723,11 +679,24 @@ func newHTTPServer(ctx context.Context, ln net.Listener, configuration *static.E return ctx } + // ConfigureServer configures HTTP/2 with the MaxConcurrentStreams option for the given server. + // Also keeping behavior the same as + // https://cs.opensource.google/go/go/+/refs/tags/go1.17.7:src/net/http/server.go;l=3262 + if !strings.Contains(os.Getenv("GODEBUG"), "http2server=0") { + err = http2.ConfigureServer(serverHTTP, &http2.Server{ + MaxConcurrentStreams: uint32(configuration.HTTP2.MaxConcurrentStreams), + NewWriteScheduler: func() http2.WriteScheduler { return http2.NewPriorityWriteScheduler(nil) }, + }) + if err != nil { + return nil, fmt.Errorf("configure HTTP/2 server: %w", err) + } + } + listener := newHTTPForwarder(ln) go func() { err := serverHTTP.Serve(listener) if err != nil && !errors.Is(err, http.ErrServerClosed) { - log.Ctx(ctx).Error().Err(err).Msg("Error while running HTTP server") + log.Ctx(ctx).Error().Err(err).Msg("Error while starting server") } }() return &httpServer{ diff --git a/pkg/server/server_entrypoint_tcp_http3_test.go b/pkg/server/server_entrypoint_tcp_http3_test.go index 746821958..cc6b18082 100644 --- a/pkg/server/server_entrypoint_tcp_http3_test.go +++ b/pkg/server/server_entrypoint_tcp_http3_test.go @@ -2,6 +2,7 @@ package server import ( "bufio" + "context" "crypto/tls" "crypto/x509" "net/http" @@ -86,7 +87,7 @@ func TestHTTP3AdvertisedPort(t *testing.T) { epConfig := &static.EntryPointsTransport{} epConfig.SetDefaults() - entryPoint, err := NewTCPEntryPoint(t.Context(), "foo", &static.EntryPoint{ + entryPoint, err := NewTCPEntryPoint(context.Background(), "foo", &static.EntryPoint{ Address: "127.0.0.1:0", Transport: epConfig, ForwardedHeaders: &static.ForwardedHeaders{}, @@ -107,7 +108,7 @@ func TestHTTP3AdvertisedPort(t *testing.T) { rw.WriteHeader(http.StatusOK) }), nil) - ctx := t.Context() + ctx := context.Background() go entryPoint.Start(ctx) entryPoint.SwitchRouter(router) @@ -150,7 +151,7 @@ func TestHTTP30RTT(t *testing.T) { epConfig := &static.EntryPointsTransport{} epConfig.SetDefaults() - entryPoint, err := NewTCPEntryPoint(t.Context(), "foo", &static.EntryPoint{ + entryPoint, err := NewTCPEntryPoint(context.Background(), "foo", &static.EntryPoint{ Address: "127.0.0.1:8090", Transport: epConfig, ForwardedHeaders: &static.ForwardedHeaders{}, @@ -169,7 +170,7 @@ func TestHTTP30RTT(t *testing.T) { rw.WriteHeader(http.StatusOK) }), nil) - ctx := t.Context() + ctx := context.Background() go entryPoint.Start(ctx) entryPoint.SwitchRouter(router) @@ -192,7 +193,7 @@ func TestHTTP30RTT(t *testing.T) { tlsConf.ClientSessionCache = cache // This first DialAddrEarly connection is here to populate the cache. - earlyConnection, err := quic.DialAddrEarly(t.Context(), "127.0.0.1:8090", tlsConf, &quic.Config{}) + earlyConnection, err := quic.DialAddrEarly(context.Background(), "127.0.0.1:8090", tlsConf, &quic.Config{}) require.NoError(t, err) t.Cleanup(func() { @@ -206,7 +207,7 @@ func TestHTTP30RTT(t *testing.T) { // 0RTT is always false on the first connection. require.False(t, earlyConnection.ConnectionState().Used0RTT) - earlyConnection, err = quic.DialAddrEarly(t.Context(), "127.0.0.1:8090", tlsConf, &quic.Config{}) + earlyConnection, err = quic.DialAddrEarly(context.Background(), "127.0.0.1:8090", tlsConf, &quic.Config{}) require.NoError(t, err) <-earlyConnection.HandshakeComplete() diff --git a/pkg/server/server_entrypoint_tcp_test.go b/pkg/server/server_entrypoint_tcp_test.go index 8170234a7..ecb0c06ae 100644 --- a/pkg/server/server_entrypoint_tcp_test.go +++ b/pkg/server/server_entrypoint_tcp_test.go @@ -80,7 +80,7 @@ func testShutdown(t *testing.T, router *tcprouter.Router) { epConfig.RespondingTimeouts.ReadTimeout = ptypes.Duration(5 * time.Second) epConfig.RespondingTimeouts.WriteTimeout = ptypes.Duration(5 * time.Second) - entryPoint, err := NewTCPEntryPoint(t.Context(), "", &static.EntryPoint{ + entryPoint, err := NewTCPEntryPoint(context.Background(), "", &static.EntryPoint{ // We explicitly use an IPV4 address because on Alpine, with an IPV6 address // there seems to be shenanigans related to properly cleaning up file descriptors Address: "127.0.0.1:0", @@ -90,7 +90,7 @@ func testShutdown(t *testing.T, router *tcprouter.Router) { }, nil, nil) require.NoError(t, err) - conn, err := startEntrypoint(t, entryPoint, router) + conn, err := startEntrypoint(entryPoint, router) require.NoError(t, err) t.Cleanup(func() { _ = conn.Close() }) @@ -113,7 +113,7 @@ func testShutdown(t *testing.T, router *tcprouter.Router) { _, err = reader.Peek(1) require.NoError(t, err) - go entryPoint.Shutdown(t.Context()) + go entryPoint.Shutdown(context.Background()) // Make sure that new connections are not permitted anymore. // Note that this should be true not only after Shutdown has returned, @@ -144,10 +144,8 @@ func testShutdown(t *testing.T, router *tcprouter.Router) { assert.Equal(t, http.StatusOK, resp.StatusCode) } -func startEntrypoint(t *testing.T, entryPoint *TCPEntryPoint, router *tcprouter.Router) (net.Conn, error) { - t.Helper() - - go entryPoint.Start(t.Context()) +func startEntrypoint(entryPoint *TCPEntryPoint, router *tcprouter.Router) (net.Conn, error) { + go entryPoint.Start(context.Background()) entryPoint.SwitchRouter(router) @@ -169,7 +167,7 @@ func TestReadTimeoutWithoutFirstByte(t *testing.T) { epConfig.SetDefaults() epConfig.RespondingTimeouts.ReadTimeout = ptypes.Duration(2 * time.Second) - entryPoint, err := NewTCPEntryPoint(t.Context(), "", &static.EntryPoint{ + entryPoint, err := NewTCPEntryPoint(context.Background(), "", &static.EntryPoint{ Address: ":0", Transport: epConfig, ForwardedHeaders: &static.ForwardedHeaders{}, @@ -184,7 +182,7 @@ func TestReadTimeoutWithoutFirstByte(t *testing.T) { rw.WriteHeader(http.StatusOK) })) - conn, err := startEntrypoint(t, entryPoint, router) + conn, err := startEntrypoint(entryPoint, router) require.NoError(t, err) errChan := make(chan error) @@ -208,7 +206,7 @@ func TestReadTimeoutWithFirstByte(t *testing.T) { epConfig.SetDefaults() epConfig.RespondingTimeouts.ReadTimeout = ptypes.Duration(2 * time.Second) - entryPoint, err := NewTCPEntryPoint(t.Context(), "", &static.EntryPoint{ + entryPoint, err := NewTCPEntryPoint(context.Background(), "", &static.EntryPoint{ Address: ":0", Transport: epConfig, ForwardedHeaders: &static.ForwardedHeaders{}, @@ -223,7 +221,7 @@ func TestReadTimeoutWithFirstByte(t *testing.T) { rw.WriteHeader(http.StatusOK) })) - conn, err := startEntrypoint(t, entryPoint, router) + conn, err := startEntrypoint(entryPoint, router) require.NoError(t, err) _, err = conn.Write([]byte("GET /some HTTP/1.1\r\n")) @@ -250,7 +248,7 @@ func TestKeepAliveMaxRequests(t *testing.T) { epConfig.SetDefaults() epConfig.KeepAliveMaxRequests = 3 - entryPoint, err := NewTCPEntryPoint(t.Context(), "", &static.EntryPoint{ + entryPoint, err := NewTCPEntryPoint(context.Background(), "", &static.EntryPoint{ Address: ":0", Transport: epConfig, ForwardedHeaders: &static.ForwardedHeaders{}, @@ -265,7 +263,7 @@ func TestKeepAliveMaxRequests(t *testing.T) { rw.WriteHeader(http.StatusOK) })) - conn, err := startEntrypoint(t, entryPoint, router) + conn, err := startEntrypoint(entryPoint, router) require.NoError(t, err) http.DefaultClient.Transport = &http.Transport{ @@ -298,7 +296,7 @@ func TestKeepAliveMaxTime(t *testing.T) { epConfig.SetDefaults() epConfig.KeepAliveMaxTime = ptypes.Duration(time.Millisecond) - entryPoint, err := NewTCPEntryPoint(t.Context(), "", &static.EntryPoint{ + entryPoint, err := NewTCPEntryPoint(context.Background(), "", &static.EntryPoint{ Address: ":0", Transport: epConfig, ForwardedHeaders: &static.ForwardedHeaders{}, @@ -313,7 +311,7 @@ func TestKeepAliveMaxTime(t *testing.T) { rw.WriteHeader(http.StatusOK) })) - conn, err := startEntrypoint(t, entryPoint, router) + conn, err := startEntrypoint(entryPoint, router) require.NoError(t, err) http.DefaultClient.Transport = &http.Transport{ @@ -342,7 +340,7 @@ func TestKeepAliveH2c(t *testing.T) { epConfig.SetDefaults() epConfig.KeepAliveMaxRequests = 1 - entryPoint, err := NewTCPEntryPoint(t.Context(), "", &static.EntryPoint{ + entryPoint, err := NewTCPEntryPoint(context.Background(), "", &static.EntryPoint{ Address: ":0", Transport: epConfig, ForwardedHeaders: &static.ForwardedHeaders{}, @@ -357,7 +355,7 @@ func TestKeepAliveH2c(t *testing.T) { rw.WriteHeader(http.StatusOK) })) - conn, err := startEntrypoint(t, entryPoint, router) + conn, err := startEntrypoint(entryPoint, router) require.NoError(t, err) http2Transport := &http2.Transport{ @@ -511,7 +509,7 @@ func TestNormalizePath_malformedPercentEncoding(t *testing.T) { } } -// TestPathOperations tests the whole behavior of normalizePath, and sanitizePath combined through the use of the newHTTPServer func. +// TestPathOperations tests the whole behavior of normalizePath, and sanitizePath combined through the use of the createHTTPServer func. // It aims to guarantee the server entrypoint handler is secure regarding a large variety of cases that could lead to path traversal attacks. func TestPathOperations(t *testing.T) { // Create a listener for the server. @@ -525,8 +523,8 @@ func TestPathOperations(t *testing.T) { configuration := &static.EntryPoint{} configuration.SetDefaults() - // Create the HTTP server using newHTTPServer. - server, err := newHTTPServer(t.Context(), ln, configuration, false, requestdecorator.New(nil)) + // Create the HTTP server using createHTTPServer. + server, err := createHTTPServer(context.Background(), ln, configuration, false, requestdecorator.New(nil)) require.NoError(t, err) server.Switcher.UpdateHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { diff --git a/pkg/server/server_entrypoint_udp.go b/pkg/server/server_entrypoint_udp.go index 2d7250496..00a798f22 100644 --- a/pkg/server/server_entrypoint_udp.go +++ b/pkg/server/server_entrypoint_udp.go @@ -8,7 +8,7 @@ import ( "github.com/rs/zerolog/log" "github.com/traefik/traefik/v3/pkg/config/static" - "github.com/traefik/traefik/v3/pkg/observability/logs" + "github.com/traefik/traefik/v3/pkg/logs" "github.com/traefik/traefik/v3/pkg/udp" ) diff --git a/pkg/server/server_entrypoint_udp_test.go b/pkg/server/server_entrypoint_udp_test.go index c66a7d9dc..dea497938 100644 --- a/pkg/server/server_entrypoint_udp_test.go +++ b/pkg/server/server_entrypoint_udp_test.go @@ -1,6 +1,7 @@ package server import ( + "context" "io" "net" "testing" @@ -26,7 +27,7 @@ func TestShutdownUDPConn(t *testing.T) { entryPoint, err := NewUDPEntryPoint(&ep, "") require.NoError(t, err) - go entryPoint.Start(t.Context()) + go entryPoint.Start(context.Background()) entryPoint.Switch(udp.HandlerFunc(func(conn *udp.Conn) { for { b := make([]byte, 1024*1024) @@ -53,18 +54,12 @@ func TestShutdownUDPConn(t *testing.T) { // Start sending packets, to create a "session" with the server. requireEcho(t, "TEST", conn, time.Second) - shutdownStartedChan := make(chan struct{}) doneChan := make(chan struct{}) go func() { - close(shutdownStartedChan) - entryPoint.Shutdown(t.Context()) + entryPoint.Shutdown(context.Background()) close(doneChan) }() - // Wait until shutdown has started, and hopefully after 100 ms the listener has stopped accepting new sessions. - <-shutdownStartedChan - time.Sleep(100 * time.Millisecond) - // Make sure that our session is still live even after the shutdown. requireEcho(t, "TEST2", conn, time.Second) diff --git a/pkg/server/service/loadbalancer/failover/failover_test.go b/pkg/server/service/loadbalancer/failover/failover_test.go index 3d357017e..a2f7f89f0 100644 --- a/pkg/server/service/loadbalancer/failover/failover_test.go +++ b/pkg/server/service/loadbalancer/failover/failover_test.go @@ -1,6 +1,7 @@ package failover import ( + "context" "net/http" "net/http/httptest" "testing" @@ -50,7 +51,7 @@ func TestFailover(t *testing.T) { assert.Equal(t, []int{200}, recorder.status) assert.True(t, status) - failover.SetHandlerStatus(t.Context(), false) + failover.SetHandlerStatus(context.Background(), false) recorder = &responseRecorder{ResponseRecorder: httptest.NewRecorder(), save: map[string]int{}} failover.ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/", nil)) @@ -60,7 +61,7 @@ func TestFailover(t *testing.T) { assert.Equal(t, []int{200}, recorder.status) assert.True(t, status) - failover.SetFallbackHandlerStatus(t.Context(), false) + failover.SetFallbackHandlerStatus(context.Background(), false) recorder = &responseRecorder{ResponseRecorder: httptest.NewRecorder(), save: map[string]int{}} failover.ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/", nil)) @@ -91,7 +92,7 @@ func TestFailoverDownThenUp(t *testing.T) { assert.Equal(t, 0, recorder.save["fallback"]) assert.Equal(t, []int{200}, recorder.status) - failover.SetHandlerStatus(t.Context(), false) + failover.SetHandlerStatus(context.Background(), false) recorder = &responseRecorder{ResponseRecorder: httptest.NewRecorder(), save: map[string]int{}} failover.ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/", nil)) @@ -100,7 +101,7 @@ func TestFailoverDownThenUp(t *testing.T) { assert.Equal(t, 1, recorder.save["fallback"]) assert.Equal(t, []int{200}, recorder.status) - failover.SetHandlerStatus(t.Context(), true) + failover.SetHandlerStatus(context.Background(), true) recorder = &responseRecorder{ResponseRecorder: httptest.NewRecorder(), save: map[string]int{}} failover.ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/", nil)) @@ -128,7 +129,7 @@ func TestFailoverPropagate(t *testing.T) { rw.WriteHeader(http.StatusOK) })) err := failover.RegisterStatusUpdater(func(up bool) { - topFailover.SetHandlerStatus(t.Context(), up) + topFailover.SetHandlerStatus(context.Background(), up) }) require.NoError(t, err) @@ -140,7 +141,7 @@ func TestFailoverPropagate(t *testing.T) { assert.Equal(t, 0, recorder.save["topFailover"]) assert.Equal(t, []int{200}, recorder.status) - failover.SetHandlerStatus(t.Context(), false) + failover.SetHandlerStatus(context.Background(), false) recorder = &responseRecorder{ResponseRecorder: httptest.NewRecorder(), save: map[string]int{}} topFailover.ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/", nil)) @@ -150,7 +151,7 @@ func TestFailoverPropagate(t *testing.T) { assert.Equal(t, 0, recorder.save["topFailover"]) assert.Equal(t, []int{200}, recorder.status) - failover.SetFallbackHandlerStatus(t.Context(), false) + failover.SetFallbackHandlerStatus(context.Background(), false) recorder = &responseRecorder{ResponseRecorder: httptest.NewRecorder(), save: map[string]int{}} topFailover.ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/", nil)) diff --git a/pkg/server/service/loadbalancer/mirror/mirror_test.go b/pkg/server/service/loadbalancer/mirror/mirror_test.go index 515bafdb7..94a4c62a0 100644 --- a/pkg/server/service/loadbalancer/mirror/mirror_test.go +++ b/pkg/server/service/loadbalancer/mirror/mirror_test.go @@ -2,6 +2,7 @@ package mirror import ( "bytes" + "context" "io" "net/http" "net/http/httptest" @@ -19,7 +20,7 @@ func TestMirroringOn100(t *testing.T) { handler := http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { rw.WriteHeader(http.StatusOK) }) - pool := safe.NewPool(t.Context()) + pool := safe.NewPool(context.Background()) mirror := New(handler, pool, true, defaultMaxBodySize, nil) err := mirror.AddMirror(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { atomic.AddInt32(&countMirror1, 1) @@ -48,7 +49,7 @@ func TestMirroringOn10(t *testing.T) { handler := http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { rw.WriteHeader(http.StatusOK) }) - pool := safe.NewPool(t.Context()) + pool := safe.NewPool(context.Background()) mirror := New(handler, pool, true, defaultMaxBodySize, nil) err := mirror.AddMirror(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { atomic.AddInt32(&countMirror1, 1) @@ -73,7 +74,7 @@ func TestMirroringOn10(t *testing.T) { } func TestInvalidPercent(t *testing.T) { - mirror := New(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {}), safe.NewPool(t.Context()), true, defaultMaxBodySize, nil) + mirror := New(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {}), safe.NewPool(context.Background()), true, defaultMaxBodySize, nil) err := mirror.AddMirror(nil, -1) assert.Error(t, err) @@ -91,7 +92,7 @@ func TestHijack(t *testing.T) { handler := http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { rw.WriteHeader(http.StatusOK) }) - pool := safe.NewPool(t.Context()) + pool := safe.NewPool(context.Background()) mirror := New(handler, pool, true, defaultMaxBodySize, nil) var mirrorRequest bool @@ -115,7 +116,7 @@ func TestFlush(t *testing.T) { handler := http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { rw.WriteHeader(http.StatusOK) }) - pool := safe.NewPool(t.Context()) + pool := safe.NewPool(context.Background()) mirror := New(handler, pool, true, defaultMaxBodySize, nil) var mirrorRequest bool @@ -143,7 +144,7 @@ func TestMirroringWithBody(t *testing.T) { body = []byte(`body`) ) - pool := safe.NewPool(t.Context()) + pool := safe.NewPool(context.Background()) handler := http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { assert.NotNil(t, r.Body) @@ -185,7 +186,7 @@ func TestMirroringWithIgnoredBody(t *testing.T) { emptyBody = []byte(``) ) - pool := safe.NewPool(t.Context()) + pool := safe.NewPool(context.Background()) handler := http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { assert.NotNil(t, r.Body) diff --git a/pkg/server/service/loadbalancer/p2c/p2c.go b/pkg/server/service/loadbalancer/p2c/p2c.go index 3bf06a512..d6d31413c 100644 --- a/pkg/server/service/loadbalancer/p2c/p2c.go +++ b/pkg/server/service/loadbalancer/p2c/p2c.go @@ -43,7 +43,6 @@ type rnd interface { type Balancer struct { wantsHealthCheck bool - // handlersMu is a mutex to protect the handlers slice, the status and the fenced maps. handlersMu sync.RWMutex handlers []*namedHandler // status is a record of which child services of the Balancer are healthy, keyed @@ -51,12 +50,11 @@ type Balancer struct { // created via Add, and it is later removed or added to the map as needed, // through the SetStatus method. status map[string]struct{} - // fenced is the list of terminating yet still serving child services. - fenced map[string]struct{} - // updaters is the list of hooks that are run (to update the Balancer // parent(s)), whenever the Balancer status changes. updaters []func(bool) + // fenced is the list of terminating yet still serving child services. + fenced map[string]struct{} sticky *loadbalancer.Sticky @@ -183,10 +181,7 @@ func (b *Balancer) ServeHTTP(rw http.ResponseWriter, req *http.Request) { if err != nil { log.Error().Err(err).Msg("Error while getting sticky handler") } else if h != nil { - b.handlersMu.RLock() - _, ok := b.status[h.Name] - b.handlersMu.RUnlock() - if ok { + if _, ok := b.status[h.Name]; ok { if rewrite { if err := b.sticky.WriteStickyCookie(rw, h.Name); err != nil { log.Error().Err(err).Msg("Writing sticky cookie") diff --git a/pkg/server/service/loadbalancer/p2c/p2c_test.go b/pkg/server/service/loadbalancer/p2c/p2c_test.go index bce6cc456..b89420de2 100644 --- a/pkg/server/service/loadbalancer/p2c/p2c_test.go +++ b/pkg/server/service/loadbalancer/p2c/p2c_test.go @@ -1,6 +1,7 @@ package p2c import ( + "context" "net/http" "net/http/httptest" "strconv" @@ -206,7 +207,7 @@ func TestBalancerPropagate(t *testing.T) { assert.Equal(t, http.StatusOK, recorder.Code) // two gets downed, but balancer still up since first is still up. - balancer.SetStatus(t.Context(), "second", false) + balancer.SetStatus(context.Background(), "second", false) assert.Equal(t, 0, calls) recorder = httptest.NewRecorder() @@ -215,7 +216,7 @@ func TestBalancerPropagate(t *testing.T) { assert.Equal(t, "first", recorder.Header().Get("server")) // first gets downed, balancer is down. - balancer.SetStatus(t.Context(), "first", false) + balancer.SetStatus(context.Background(), "first", false) assert.Equal(t, 1, calls) recorder = httptest.NewRecorder() @@ -223,7 +224,7 @@ func TestBalancerPropagate(t *testing.T) { assert.Equal(t, http.StatusServiceUnavailable, recorder.Code) // two gets up, balancer up. - balancer.SetStatus(t.Context(), "second", true) + balancer.SetStatus(context.Background(), "second", true) assert.Equal(t, 2, calls) recorder = httptest.NewRecorder() diff --git a/pkg/server/service/loadbalancer/wrr/wrr.go b/pkg/server/service/loadbalancer/wrr/wrr.go index 2c9bff0ee..8206b97aa 100644 --- a/pkg/server/service/loadbalancer/wrr/wrr.go +++ b/pkg/server/service/loadbalancer/wrr/wrr.go @@ -27,7 +27,6 @@ type namedHandler struct { type Balancer struct { wantsHealthCheck bool - // handlersMu is a mutex to protect the handlers slice, the status and the fenced maps. handlersMu sync.RWMutex handlers []*namedHandler // status is a record of which child services of the Balancer are healthy, keyed @@ -35,12 +34,11 @@ type Balancer struct { // created via Add, and it is later removed or added to the map as needed, // through the SetStatus method. status map[string]struct{} - // fenced is the list of terminating yet still serving child services. - fenced map[string]struct{} - // updaters is the list of hooks that are run (to update the Balancer // parent(s)), whenever the Balancer status changes. updaters []func(bool) + // fenced is the list of terminating yet still serving child services. + fenced map[string]struct{} sticky *loadbalancer.Sticky @@ -182,10 +180,7 @@ func (b *Balancer) ServeHTTP(rw http.ResponseWriter, req *http.Request) { if err != nil { log.Error().Err(err).Msg("Error while getting sticky handler") } else if h != nil { - b.handlersMu.RLock() - _, ok := b.status[h.Name] - b.handlersMu.RUnlock() - if ok { + if _, ok := b.status[h.Name]; ok { if rewrite { if err := b.sticky.WriteStickyCookie(rw, h.Name); err != nil { log.Error().Err(err).Msg("Writing sticky cookie") diff --git a/pkg/server/service/loadbalancer/wrr/wrr_test.go b/pkg/server/service/loadbalancer/wrr/wrr_test.go index 5260623be..7e9c3bc04 100644 --- a/pkg/server/service/loadbalancer/wrr/wrr_test.go +++ b/pkg/server/service/loadbalancer/wrr/wrr_test.go @@ -76,8 +76,8 @@ func TestBalancerNoServiceUp(t *testing.T) { rw.WriteHeader(http.StatusInternalServerError) }), pointer(1), false) - balancer.SetStatus(context.WithValue(t.Context(), serviceName, "parent"), "first", false) - balancer.SetStatus(context.WithValue(t.Context(), serviceName, "parent"), "second", false) + balancer.SetStatus(context.WithValue(context.Background(), serviceName, "parent"), "first", false) + balancer.SetStatus(context.WithValue(context.Background(), serviceName, "parent"), "second", false) recorder := httptest.NewRecorder() balancer.ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/", nil)) @@ -96,7 +96,7 @@ func TestBalancerOneServerDown(t *testing.T) { balancer.Add("second", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { rw.WriteHeader(http.StatusInternalServerError) }), pointer(1), false) - balancer.SetStatus(context.WithValue(t.Context(), serviceName, "parent"), "second", false) + balancer.SetStatus(context.WithValue(context.Background(), serviceName, "parent"), "second", false) recorder := &responseRecorder{ResponseRecorder: httptest.NewRecorder(), save: map[string]int{}} for range 3 { @@ -118,7 +118,7 @@ func TestBalancerDownThenUp(t *testing.T) { rw.Header().Set("server", "second") rw.WriteHeader(http.StatusOK) }), pointer(1), false) - balancer.SetStatus(context.WithValue(t.Context(), serviceName, "parent"), "second", false) + balancer.SetStatus(context.WithValue(context.Background(), serviceName, "parent"), "second", false) recorder := &responseRecorder{ResponseRecorder: httptest.NewRecorder(), save: map[string]int{}} for range 3 { @@ -126,7 +126,7 @@ func TestBalancerDownThenUp(t *testing.T) { } assert.Equal(t, 3, recorder.save["first"]) - balancer.SetStatus(context.WithValue(t.Context(), serviceName, "parent"), "second", true) + balancer.SetStatus(context.WithValue(context.Background(), serviceName, "parent"), "second", true) recorder = &responseRecorder{ResponseRecorder: httptest.NewRecorder(), save: map[string]int{}} for range 2 { balancer.ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/", nil)) @@ -160,13 +160,13 @@ func TestBalancerPropagate(t *testing.T) { topBalancer := New(nil, true) topBalancer.Add("balancer1", balancer1, pointer(1), false) _ = balancer1.RegisterStatusUpdater(func(up bool) { - topBalancer.SetStatus(context.WithValue(t.Context(), serviceName, "top"), "balancer1", up) + topBalancer.SetStatus(context.WithValue(context.Background(), serviceName, "top"), "balancer1", up) // TODO(mpl): if test gets flaky, add channel or something here to signal that // propagation is done, and wait on it before sending request. }) topBalancer.Add("balancer2", balancer2, pointer(1), false) _ = balancer2.RegisterStatusUpdater(func(up bool) { - topBalancer.SetStatus(context.WithValue(t.Context(), serviceName, "top"), "balancer2", up) + topBalancer.SetStatus(context.WithValue(context.Background(), serviceName, "top"), "balancer2", up) }) recorder := &responseRecorder{ResponseRecorder: httptest.NewRecorder(), save: map[string]int{}} @@ -181,7 +181,7 @@ func TestBalancerPropagate(t *testing.T) { assert.Equal(t, wantStatus, recorder.status) // fourth gets downed, but balancer2 still up since third is still up. - balancer2.SetStatus(context.WithValue(t.Context(), serviceName, "top"), "fourth", false) + balancer2.SetStatus(context.WithValue(context.Background(), serviceName, "top"), "fourth", false) recorder = &responseRecorder{ResponseRecorder: httptest.NewRecorder(), save: map[string]int{}} for range 8 { topBalancer.ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/", nil)) @@ -195,7 +195,7 @@ func TestBalancerPropagate(t *testing.T) { // third gets downed, and the propagation triggers balancer2 to be marked as // down as well for topBalancer. - balancer2.SetStatus(context.WithValue(t.Context(), serviceName, "top"), "third", false) + balancer2.SetStatus(context.WithValue(context.Background(), serviceName, "top"), "third", false) recorder = &responseRecorder{ResponseRecorder: httptest.NewRecorder(), save: map[string]int{}} for range 8 { topBalancer.ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/", nil)) diff --git a/pkg/server/service/managerfactory.go b/pkg/server/service/managerfactory.go index a850ec745..19da05d7b 100644 --- a/pkg/server/service/managerfactory.go +++ b/pkg/server/service/managerfactory.go @@ -9,7 +9,7 @@ import ( "github.com/traefik/traefik/v3/pkg/api/dashboard" "github.com/traefik/traefik/v3/pkg/config/runtime" "github.com/traefik/traefik/v3/pkg/config/static" - "github.com/traefik/traefik/v3/pkg/observability/metrics" + "github.com/traefik/traefik/v3/pkg/metrics" "github.com/traefik/traefik/v3/pkg/safe" "github.com/traefik/traefik/v3/pkg/server/middleware" ) diff --git a/pkg/server/service/service.go b/pkg/server/service/service.go index a8e00cd1d..82cc96e36 100644 --- a/pkg/server/service/service.go +++ b/pkg/server/service/service.go @@ -17,11 +17,12 @@ import ( "github.com/traefik/traefik/v3/pkg/config/dynamic" "github.com/traefik/traefik/v3/pkg/config/runtime" "github.com/traefik/traefik/v3/pkg/healthcheck" + "github.com/traefik/traefik/v3/pkg/logs" "github.com/traefik/traefik/v3/pkg/middlewares/accesslog" + "github.com/traefik/traefik/v3/pkg/middlewares/capture" metricsMiddle "github.com/traefik/traefik/v3/pkg/middlewares/metrics" "github.com/traefik/traefik/v3/pkg/middlewares/observability" "github.com/traefik/traefik/v3/pkg/middlewares/retry" - "github.com/traefik/traefik/v3/pkg/observability/logs" "github.com/traefik/traefik/v3/pkg/proxy/httputil" "github.com/traefik/traefik/v3/pkg/safe" "github.com/traefik/traefik/v3/pkg/server/cookie" @@ -36,7 +37,7 @@ import ( // ProxyBuilder builds reverse proxy handlers. type ProxyBuilder interface { - Build(cfgName string, targetURL *url.URL, passHostHeader, preservePath bool, flushInterval time.Duration) (http.Handler, error) + Build(cfgName string, targetURL *url.URL, shouldObserve, passHostHeader, preservePath bool, flushInterval time.Duration) (http.Handler, error) Update(configs map[string]*dynamic.ServersTransport) } @@ -363,32 +364,50 @@ func (m *Manager) getLoadBalancerServiceHandler(ctx context.Context, serviceName qualifiedSvcName := provider.GetQualifiedName(ctx, serviceName) - proxy, err := m.proxyBuilder.Build(service.ServersTransport, target, passHostHeader, server.PreservePath, flushInterval) + shouldObserve := m.observabilityMgr.ShouldAddTracing(qualifiedSvcName, nil) || m.observabilityMgr.ShouldAddMetrics(qualifiedSvcName, nil) + proxy, err := m.proxyBuilder.Build(service.ServersTransport, target, shouldObserve, passHostHeader, server.PreservePath, flushInterval) if err != nil { return nil, fmt.Errorf("error building proxy for server URL %s: %w", server.URL, err) } - // The retry wrapping must be done just before the proxy handler, // to make sure that the retry will not be triggered/disabled by // middlewares in the chain. proxy = retry.WrapHandler(proxy) - // Access logs, metrics, and tracing middlewares are idempotent if the associated signal is disabled. - proxy = accesslog.NewFieldHandler(proxy, accesslog.ServiceURL, target.String(), nil) - proxy = accesslog.NewFieldHandler(proxy, accesslog.ServiceAddr, target.Host, nil) - proxy = accesslog.NewFieldHandler(proxy, accesslog.ServiceName, qualifiedSvcName, accesslog.AddServiceFields) + // Prevents from enabling observability for internal resources. - metricsHandler := metricsMiddle.ServiceMetricsHandler(ctx, m.observabilityMgr.MetricsRegistry(), qualifiedSvcName) - metricsHandler = observability.WrapMiddleware(ctx, metricsHandler) - - proxy, err = alice.New(). - Append(metricsHandler). - Then(proxy) - if err != nil { - return nil, fmt.Errorf("error wrapping metrics handler: %w", err) + if m.observabilityMgr.ShouldAddAccessLogs(qualifiedSvcName, nil) { + proxy = accesslog.NewFieldHandler(proxy, accesslog.ServiceURL, target.String(), nil) + proxy = accesslog.NewFieldHandler(proxy, accesslog.ServiceAddr, target.Host, nil) + proxy = accesslog.NewFieldHandler(proxy, accesslog.ServiceName, serviceName, accesslog.AddServiceFields) } - proxy = observability.NewService(ctx, qualifiedSvcName, proxy) + if m.observabilityMgr.MetricsRegistry() != nil && m.observabilityMgr.MetricsRegistry().IsSvcEnabled() && + m.observabilityMgr.ShouldAddMetrics(qualifiedSvcName, nil) { + metricsHandler := metricsMiddle.WrapServiceHandler(ctx, m.observabilityMgr.MetricsRegistry(), serviceName) + + proxy, err = alice.New(). + Append(observability.WrapMiddleware(ctx, metricsHandler)). + Then(proxy) + if err != nil { + return nil, fmt.Errorf("error wrapping metrics handler: %w", err) + } + } + + if m.observabilityMgr.ShouldAddTracing(qualifiedSvcName, nil) { + proxy = observability.NewService(ctx, serviceName, proxy) + } + + if m.observabilityMgr.ShouldAddAccessLogs(qualifiedSvcName, nil) || m.observabilityMgr.ShouldAddMetrics(qualifiedSvcName, nil) { + // Some piece of middleware, like the ErrorPage, are relying on this serviceBuilder to get the handler for a given service, + // to re-target the request to it. + // Those pieces of middleware can be configured on routes that expose a Traefik internal service. + // In such a case, observability for internals being optional, the capture probe could be absent from context (no wrap via the entrypoint). + // But if the service targeted by this piece of middleware is not an internal one, + // and requires observability, we still want the capture probe to be present in the request context. + // Makes sure a capture probe is in the request context. + proxy, _ = capture.Wrap(proxy) + } lb.AddServer(server.URL, proxy, server) diff --git a/pkg/server/service/service_test.go b/pkg/server/service/service_test.go index 7d00a69fb..1a31a9e75 100644 --- a/pkg/server/service/service_test.go +++ b/pkg/server/service/service_test.go @@ -74,7 +74,7 @@ func TestGetLoadBalancer(t *testing.T) { t.Parallel() serviceInfo := &runtime.ServiceInfo{Service: &dynamic.Service{LoadBalancer: test.service}} - handler, err := sm.getLoadBalancerServiceHandler(t.Context(), test.serviceName, serviceInfo) + handler, err := sm.getLoadBalancerServiceHandler(context.Background(), test.serviceName, serviceInfo) if test.expectError { require.Error(t, err) assert.Nil(t, handler) @@ -321,7 +321,7 @@ func TestGetLoadBalancerServiceHandler(t *testing.T) { for _, test := range testCases { t.Run(test.desc, func(t *testing.T) { serviceInfo := &runtime.ServiceInfo{Service: &dynamic.Service{LoadBalancer: test.service}} - handler, err := sm.getLoadBalancerServiceHandler(t.Context(), test.serviceName, serviceInfo) + handler, err := sm.getLoadBalancerServiceHandler(context.Background(), test.serviceName, serviceInfo) assert.NoError(t, err) assert.NotNil(t, handler) @@ -402,7 +402,7 @@ func Test1xxResponses(t *testing.T) { }, } - handler, err := sm.getLoadBalancerServiceHandler(t.Context(), "foobar", info) + handler, err := sm.getLoadBalancerServiceHandler(context.Background(), "foobar", info) assert.NoError(t, err) frontend := httptest.NewServer(handler) @@ -446,7 +446,7 @@ func Test1xxResponses(t *testing.T) { return nil }, } - req, _ := http.NewRequestWithContext(httptrace.WithClientTrace(t.Context(), trace), http.MethodGet, frontend.URL, nil) + req, _ := http.NewRequestWithContext(httptrace.WithClientTrace(context.Background(), trace), http.MethodGet, frontend.URL, nil) res, err := frontendClient.Do(req) assert.NoError(t, err) @@ -496,15 +496,15 @@ func TestManager_ServiceBuilders(t *testing.T) { return nil, nil })) - h, err := manager.BuildHTTP(t.Context(), "test@internal") + h, err := manager.BuildHTTP(context.Background(), "test@internal") require.NoError(t, err) assert.Equal(t, internalHandler, h) - h, err = manager.BuildHTTP(t.Context(), "test@test") + h, err = manager.BuildHTTP(context.Background(), "test@test") require.NoError(t, err) assert.NotNil(t, h) - _, err = manager.BuildHTTP(t.Context(), "wrong@test") + _, err = manager.BuildHTTP(context.Background(), "wrong@test") assert.Error(t, err) } @@ -563,7 +563,7 @@ func TestManager_Build(t *testing.T) { manager := NewManager(test.configs, nil, nil, &transportManagerMock{}, nil) - ctx := t.Context() + ctx := context.Background() if len(test.providerName) > 0 { ctx = provider.AddInContext(ctx, "foobar@"+test.providerName) } @@ -586,7 +586,7 @@ func TestMultipleTypeOnBuildHTTP(t *testing.T) { manager := NewManager(services, nil, nil, &transportManagerMock{}, nil) - _, err := manager.BuildHTTP(t.Context(), "test@file") + _, err := manager.BuildHTTP(context.Background(), "test@file") assert.Error(t, err, "cannot create service: multi-types service not supported, consider declaring two different pieces of service instead") } diff --git a/pkg/server/service/tcp/service.go b/pkg/server/service/tcp/service.go index 9ff454fad..64c173a51 100644 --- a/pkg/server/service/tcp/service.go +++ b/pkg/server/service/tcp/service.go @@ -10,9 +10,10 @@ import ( "github.com/rs/zerolog/log" "github.com/traefik/traefik/v3/pkg/config/runtime" - "github.com/traefik/traefik/v3/pkg/observability/logs" + "github.com/traefik/traefik/v3/pkg/logs" "github.com/traefik/traefik/v3/pkg/server/provider" "github.com/traefik/traefik/v3/pkg/tcp" + "golang.org/x/net/proxy" ) // Manager is the TCPHandlers factory. @@ -57,10 +58,6 @@ func (m *Manager) BuildTCP(rootCtx context.Context, serviceName string) (tcp.Han log.Ctx(ctx).Warn().Msgf("Service %q load balancer uses `TerminationDelay`, but this option is deprecated, please use ServersTransport configuration instead.", serviceName) } - if conf.LoadBalancer.ProxyProtocol != nil { - log.Ctx(ctx).Warn().Msgf("Service %q load balancer uses `ProxyProtocol`, but this option is deprecated, please use ServersTransport configuration instead.", serviceName) - } - if len(conf.LoadBalancer.ServersTransport) > 0 { conf.LoadBalancer.ServersTransport = provider.GetQualifiedName(ctx, conf.LoadBalancer.ServersTransport) } @@ -75,12 +72,20 @@ func (m *Manager) BuildTCP(rootCtx context.Context, serviceName string) (tcp.Han continue } - dialer, err := m.dialerManager.Build(conf.LoadBalancer, server.TLS) + dialer, err := m.dialerManager.Get(conf.LoadBalancer.ServersTransport, server.TLS) if err != nil { return nil, err } - handler, err := tcp.NewProxy(server.Address, dialer) + // Handle TerminationDelay deprecated option. + if conf.LoadBalancer.ServersTransport == "" && conf.LoadBalancer.TerminationDelay != nil { + dialer = &dialerWrapper{ + Dialer: dialer, + terminationDelay: time.Duration(*conf.LoadBalancer.TerminationDelay), + } + } + + handler, err := tcp.NewProxy(server.Address, conf.LoadBalancer.ProxyProtocol, dialer) if err != nil { srvLogger.Error().Err(err).Msg("Failed to create server") continue @@ -121,3 +126,13 @@ func shuffle[T any](values []T, r *rand.Rand) []T { return shuffled } + +// dialerWrapper is only used to handle TerminationDelay deprecated option on TCPServersLoadBalancer. +type dialerWrapper struct { + proxy.Dialer + terminationDelay time.Duration +} + +func (d dialerWrapper) TerminationDelay() time.Duration { + return d.terminationDelay +} diff --git a/pkg/server/service/tcp/service_test.go b/pkg/server/service/tcp/service_test.go index 8dd0b7393..6f06d296c 100644 --- a/pkg/server/service/tcp/service_test.go +++ b/pkg/server/service/tcp/service_test.go @@ -1,6 +1,7 @@ package tcp import ( + "context" "testing" "github.com/stretchr/testify/assert" @@ -231,7 +232,7 @@ func TestManager_BuildTCP(t *testing.T) { }, }, providerName: "provider-1", - expectedError: "no transport configuration found for \"myServersTransport@provider-1\"", + expectedError: "TCP dialer not found myServersTransport@provider-1", }, } @@ -248,7 +249,7 @@ func TestManager_BuildTCP(t *testing.T) { TCPServices: test.configs, }, dialerManager) - ctx := t.Context() + ctx := context.Background() if len(test.providerName) > 0 { ctx = provider.AddInContext(ctx, "foobar@"+test.providerName) } diff --git a/pkg/server/service/transport_test.go b/pkg/server/service/transport_test.go index 0fd3a8ab8..d27f293f2 100644 --- a/pkg/server/service/transport_test.go +++ b/pkg/server/service/transport_test.go @@ -1,6 +1,7 @@ package service import ( + "context" "crypto/rand" "crypto/rsa" "crypto/tls" @@ -606,7 +607,7 @@ func TestKerberosRoundTripper(t *testing.T) { }), } - ctx := AddTransportOnContext(t.Context()) + ctx := AddTransportOnContext(context.Background()) for _, expected := range test.expectedStatusCode { req, err := http.NewRequestWithContext(ctx, http.MethodGet, "http://127.0.0.1", http.NoBody) require.NoError(t, err) diff --git a/pkg/server/service/udp/service.go b/pkg/server/service/udp/service.go index d0d3028b4..1772c1954 100644 --- a/pkg/server/service/udp/service.go +++ b/pkg/server/service/udp/service.go @@ -10,7 +10,7 @@ import ( "github.com/rs/zerolog/log" "github.com/traefik/traefik/v3/pkg/config/runtime" - "github.com/traefik/traefik/v3/pkg/observability/logs" + "github.com/traefik/traefik/v3/pkg/logs" "github.com/traefik/traefik/v3/pkg/server/provider" "github.com/traefik/traefik/v3/pkg/udp" ) diff --git a/pkg/server/service/udp/service_test.go b/pkg/server/service/udp/service_test.go index 3a744c6e6..ec1794bc1 100644 --- a/pkg/server/service/udp/service_test.go +++ b/pkg/server/service/udp/service_test.go @@ -1,6 +1,7 @@ package udp import ( + "context" "testing" "github.com/stretchr/testify/assert" @@ -180,7 +181,7 @@ func TestManager_BuildUDP(t *testing.T) { UDPServices: test.configs, }) - ctx := t.Context() + ctx := context.Background() if len(test.providerName) > 0 { ctx = provider.AddInContext(ctx, "foobar@"+test.providerName) } diff --git a/pkg/tcp/dialer.go b/pkg/tcp/dialer.go index bc4855b5b..ca08f7d2e 100644 --- a/pkg/tcp/dialer.go +++ b/pkg/tcp/dialer.go @@ -9,85 +9,33 @@ import ( "sync" "time" - "github.com/pires/go-proxyproto" "github.com/rs/zerolog/log" "github.com/spiffe/go-spiffe/v2/bundle/x509bundle" "github.com/spiffe/go-spiffe/v2/spiffeid" "github.com/spiffe/go-spiffe/v2/spiffetls/tlsconfig" "github.com/spiffe/go-spiffe/v2/svid/x509svid" - ptypes "github.com/traefik/paerser/types" "github.com/traefik/traefik/v3/pkg/config/dynamic" traefiktls "github.com/traefik/traefik/v3/pkg/tls" "github.com/traefik/traefik/v3/pkg/types" + "golang.org/x/net/proxy" ) -// ClientConn is the interface that provides information about the client connection. -type ClientConn interface { - // LocalAddr returns the local network address, if known. - LocalAddr() net.Addr - - // RemoteAddr returns the remote network address, if known. - RemoteAddr() net.Addr -} - -// Dialer is an interface to dial a network connection, with support for PROXY protocol and termination delay. type Dialer interface { - Dial(network, addr string, clientConn ClientConn) (c net.Conn, err error) + proxy.Dialer + TerminationDelay() time.Duration } type tcpDialer struct { - dialer *net.Dialer + proxy.Dialer terminationDelay time.Duration - proxyProtocol *dynamic.ProxyProtocol } -// TerminationDelay returns the termination delay duration. func (d tcpDialer) TerminationDelay() time.Duration { return d.terminationDelay } -// Dial dials a network connection and optionally sends a PROXY protocol header. -func (d tcpDialer) Dial(network, addr string, clientConn ClientConn) (net.Conn, error) { - conn, err := d.dialer.Dial(network, addr) - if err != nil { - return nil, err - } - - if d.proxyProtocol != nil && clientConn != nil && d.proxyProtocol.Version > 0 && d.proxyProtocol.Version < 3 { - header := proxyproto.HeaderProxyFromAddrs(byte(d.proxyProtocol.Version), clientConn.RemoteAddr(), clientConn.LocalAddr()) - if _, err := header.WriteTo(conn); err != nil { - _ = conn.Close() - return nil, fmt.Errorf("writing PROXY Protocol header: %w", err) - } - } - - return conn, nil -} - -type tcpTLSDialer struct { - tcpDialer - tlsConfig *tls.Config -} - -// Dial dials a network connection with the wrapped tcpDialer and performs a TLS handshake. -func (d tcpTLSDialer) Dial(network, addr string, clientConn ClientConn) (net.Conn, error) { - conn, err := d.tcpDialer.Dial(network, addr, clientConn) - if err != nil { - return nil, err - } - - // Now perform TLS handshake on the connection - tlsConn := tls.Client(conn, d.tlsConfig) - if err := tlsConn.Handshake(); err != nil { - _ = conn.Close() - return nil, fmt.Errorf("TLS handshake failed: %w", err) - } - - return tlsConn, nil -} - -// SpiffeX509Source allows retrieving a x509 SVID and bundle. +// SpiffeX509Source allows to retrieve a x509 SVID and bundle. type SpiffeX509Source interface { x509svid.Source x509bundle.Source @@ -95,106 +43,118 @@ type SpiffeX509Source interface { // DialerManager handles dialer for the reverse proxy. type DialerManager struct { - serversTransportsMu sync.RWMutex - serversTransports map[string]*dynamic.TCPServersTransport - spiffeX509Source SpiffeX509Source + rtLock sync.RWMutex + dialers map[string]Dialer + dialersTLS map[string]Dialer + spiffeX509Source SpiffeX509Source } // NewDialerManager creates a new DialerManager. func NewDialerManager(spiffeX509Source SpiffeX509Source) *DialerManager { return &DialerManager{ - serversTransports: make(map[string]*dynamic.TCPServersTransport), - spiffeX509Source: spiffeX509Source, + dialers: make(map[string]Dialer), + dialersTLS: make(map[string]Dialer), + spiffeX509Source: spiffeX509Source, } } -// Update updates the TCP serversTransport configurations. +// Update updates the dialers configurations. func (d *DialerManager) Update(configs map[string]*dynamic.TCPServersTransport) { - d.serversTransportsMu.Lock() - defer d.serversTransportsMu.Unlock() + d.rtLock.Lock() + defer d.rtLock.Unlock() - d.serversTransports = configs + d.dialers = make(map[string]Dialer) + d.dialersTLS = make(map[string]Dialer) + for configName, config := range configs { + if err := d.createDialers(configName, config); err != nil { + log.Debug(). + Str("dialer", configName). + Err(err). + Msg("Create TCP Dialer") + } + } } -// Build builds a dialer by name. -func (d *DialerManager) Build(config *dynamic.TCPServersLoadBalancer, isTLS bool) (Dialer, error) { - name := "default@internal" - if config.ServersTransport != "" { - name = config.ServersTransport +// Get gets a dialer by name. +func (d *DialerManager) Get(name string, tls bool) (Dialer, error) { + if len(name) == 0 { + name = "default@internal" } - var st *dynamic.TCPServersTransport - d.serversTransportsMu.RLock() - st, ok := d.serversTransports[name] - d.serversTransportsMu.RUnlock() - if !ok || st == nil { - return nil, fmt.Errorf("no transport configuration found for %q", name) + d.rtLock.RLock() + defer d.rtLock.RUnlock() + + if tls { + if rt, ok := d.dialersTLS[name]; ok { + return rt, nil + } + + return nil, fmt.Errorf("TCP dialer not found %s", name) } - // Handle TerminationDelay and ProxyProtocol deprecated options. - var terminationDelay ptypes.Duration - if config.TerminationDelay != nil { - terminationDelay = ptypes.Duration(*config.TerminationDelay) - } - proxyProtocol := config.ProxyProtocol - - if config.ServersTransport != "" { - terminationDelay = st.TerminationDelay - proxyProtocol = st.ProxyProtocol + if rt, ok := d.dialers[name]; ok { + return rt, nil } - if proxyProtocol != nil && (proxyProtocol.Version < 1 || proxyProtocol.Version > 2) { - return nil, fmt.Errorf("unknown proxyProtocol version: %d", proxyProtocol.Version) + return nil, fmt.Errorf("TCP dialer not found %s", name) +} + +// createDialers creates the dialers according to the TCPServersTransport configuration. +func (d *DialerManager) createDialers(name string, cfg *dynamic.TCPServersTransport) error { + if cfg == nil { + return errors.New("no transport configuration given") + } + + dialer := &net.Dialer{ + Timeout: time.Duration(cfg.DialTimeout), + KeepAlive: time.Duration(cfg.DialKeepAlive), } var tlsConfig *tls.Config - if st.TLS != nil { - if st.TLS.Spiffe != nil { + + if cfg.TLS != nil { + if cfg.TLS.Spiffe != nil { if d.spiffeX509Source == nil { - return nil, errors.New("SPIFFE is enabled for this transport, but not configured") + return errors.New("SPIFFE is enabled for this transport, but not configured") } - authorizer, err := buildSpiffeAuthorizer(st.TLS.Spiffe) + authorizer, err := buildSpiffeAuthorizer(cfg.TLS.Spiffe) if err != nil { - return nil, fmt.Errorf("unable to build SPIFFE authorizer: %w", err) + return fmt.Errorf("unable to build SPIFFE authorizer: %w", err) } tlsConfig = tlsconfig.MTLSClientConfig(d.spiffeX509Source, d.spiffeX509Source, authorizer) } - if st.TLS.InsecureSkipVerify || len(st.TLS.RootCAs) > 0 || len(st.TLS.ServerName) > 0 || len(st.TLS.Certificates) > 0 || st.TLS.PeerCertURI != "" { + if cfg.TLS.InsecureSkipVerify || len(cfg.TLS.RootCAs) > 0 || len(cfg.TLS.ServerName) > 0 || len(cfg.TLS.Certificates) > 0 || cfg.TLS.PeerCertURI != "" { if tlsConfig != nil { - return nil, errors.New("TLS and SPIFFE configuration cannot be defined at the same time") + return errors.New("TLS and SPIFFE configuration cannot be defined at the same time") } tlsConfig = &tls.Config{ - ServerName: st.TLS.ServerName, - InsecureSkipVerify: st.TLS.InsecureSkipVerify, - RootCAs: createRootCACertPool(st.TLS.RootCAs), - Certificates: st.TLS.Certificates.GetCertificates(), + ServerName: cfg.TLS.ServerName, + InsecureSkipVerify: cfg.TLS.InsecureSkipVerify, + RootCAs: createRootCACertPool(cfg.TLS.RootCAs), + Certificates: cfg.TLS.Certificates.GetCertificates(), } - if st.TLS.PeerCertURI != "" { + if cfg.TLS.PeerCertURI != "" { tlsConfig.VerifyPeerCertificate = func(rawCerts [][]byte, _ [][]*x509.Certificate) error { - return traefiktls.VerifyPeerCertificate(st.TLS.PeerCertURI, tlsConfig, rawCerts) + return traefiktls.VerifyPeerCertificate(cfg.TLS.PeerCertURI, tlsConfig, rawCerts) } } } } - dialer := tcpDialer{ - dialer: &net.Dialer{ - Timeout: time.Duration(st.DialTimeout), - KeepAlive: time.Duration(st.DialKeepAlive), - }, - terminationDelay: time.Duration(terminationDelay), - proxyProtocol: proxyProtocol, + tlsDialer := &tls.Dialer{ + NetDialer: dialer, + Config: tlsConfig, } - if !isTLS { - return dialer, nil - } - return tcpTLSDialer{dialer, tlsConfig}, nil + d.dialers[name] = tcpDialer{dialer, time.Duration(cfg.TerminationDelay)} + d.dialersTLS[name] = tcpDialer{tlsDialer, time.Duration(cfg.TerminationDelay)} + + return nil } func createRootCACertPool(rootCAs []types.FileOrContent) *x509.CertPool { diff --git a/pkg/tcp/dialer_test.go b/pkg/tcp/dialer_test.go index 07d8a7b12..97ae5d8df 100644 --- a/pkg/tcp/dialer_test.go +++ b/pkg/tcp/dialer_test.go @@ -7,7 +7,6 @@ import ( "crypto/tls" "crypto/x509" "crypto/x509/pkix" - "errors" "io" "math/big" "net" @@ -15,7 +14,6 @@ import ( "testing" "time" - "github.com/pires/go-proxyproto" "github.com/spiffe/go-spiffe/v2/bundle/x509bundle" "github.com/spiffe/go-spiffe/v2/spiffeid" "github.com/spiffe/go-spiffe/v2/spiffetls/tlsconfig" @@ -133,7 +131,7 @@ func TestConflictingConfig(t *testing.T) { dialerManager.Update(dynamicConf) - _, err := dialerManager.Build(&dynamic.TCPServersLoadBalancer{ServersTransport: "test"}, false) + _, err := dialerManager.Get("test", false) require.Error(t, err) } @@ -142,7 +140,7 @@ func TestNoTLS(t *testing.T) { require.NoError(t, err) defer backendListener.Close() - go fakeServer(t, backendListener) + go fakeRedis(t, backendListener) _, port, err := net.SplitHostPort(backendListener.Addr().String()) require.NoError(t, err) @@ -157,10 +155,10 @@ func TestNoTLS(t *testing.T) { dialerManager.Update(dynamicConf) - dialer, err := dialerManager.Build(&dynamic.TCPServersLoadBalancer{ServersTransport: "test"}, false) + dialer, err := dialerManager.Get("test", false) require.NoError(t, err) - conn, err := dialer.Dial("tcp", ":"+port, nil) + conn, err := dialer.Dial("tcp", ":"+port) require.NoError(t, err) _, err = conn.Write([]byte("ping\n")) @@ -188,7 +186,7 @@ func TestTLS(t *testing.T) { tlsListener := tls.NewListener(backendListener, &tls.Config{Certificates: []tls.Certificate{cert}}) defer tlsListener.Close() - go fakeServer(t, tlsListener) + go fakeRedis(t, tlsListener) _, port, err := net.SplitHostPort(tlsListener.Addr().String()) require.NoError(t, err) @@ -206,10 +204,10 @@ func TestTLS(t *testing.T) { dialerManager.Update(dynamicConf) - dialer, err := dialerManager.Build(&dynamic.TCPServersLoadBalancer{ServersTransport: "test"}, true) + dialer, err := dialerManager.Get("test", true) require.NoError(t, err) - conn, err := dialer.Dial("tcp", ":"+port, nil) + conn, err := dialer.Dial("tcp", ":"+port) require.NoError(t, err) _, err = conn.Write([]byte("ping\n")) @@ -238,7 +236,7 @@ func TestTLSWithInsecureSkipVerify(t *testing.T) { tlsListener := tls.NewListener(backendListener, &tls.Config{Certificates: []tls.Certificate{cert}}) defer tlsListener.Close() - go fakeServer(t, tlsListener) + go fakeRedis(t, tlsListener) _, port, err := net.SplitHostPort(tlsListener.Addr().String()) require.NoError(t, err) @@ -257,10 +255,10 @@ func TestTLSWithInsecureSkipVerify(t *testing.T) { dialerManager.Update(dynamicConf) - dialer, err := dialerManager.Build(&dynamic.TCPServersLoadBalancer{ServersTransport: "test"}, true) + dialer, err := dialerManager.Get("test", true) require.NoError(t, err) - conn, err := dialer.Dial("tcp", ":"+port, nil) + conn, err := dialer.Dial("tcp", ":"+port) require.NoError(t, err) _, err = conn.Write([]byte("ping\n")) @@ -299,7 +297,7 @@ func TestMTLS(t *testing.T) { }) defer tlsListener.Close() - go fakeServer(t, tlsListener) + go fakeRedis(t, tlsListener) _, port, err := net.SplitHostPort(tlsListener.Addr().String()) require.NoError(t, err) @@ -326,10 +324,10 @@ func TestMTLS(t *testing.T) { dialerManager.Update(dynamicConf) - dialer, err := dialerManager.Build(&dynamic.TCPServersLoadBalancer{ServersTransport: "test"}, true) + dialer, err := dialerManager.Get("test", true) require.NoError(t, err) - conn, err := dialer.Dial("tcp", ":"+port, nil) + conn, err := dialer.Dial("tcp", ":"+port) require.NoError(t, err) _, err = conn.Write([]byte("ping\n")) @@ -446,7 +444,7 @@ func TestSpiffeMTLS(t *testing.T) { for _, test := range testCases { t.Run(test.desc, func(t *testing.T) { - go fakeServer(t, tlsListener) + go fakeRedis(t, tlsListener) dialerManager := NewDialerManager(test.clientSource) @@ -460,10 +458,10 @@ func TestSpiffeMTLS(t *testing.T) { dialerManager.Update(dynamicConf) - dialer, err := dialerManager.Build(&dynamic.TCPServersLoadBalancer{ServersTransport: "test"}, true) + dialer, err := dialerManager.Get("test", true) require.NoError(t, err) - conn, err := dialer.Dial("tcp", ":"+port, nil) + conn, err := dialer.Dial("tcp", ":"+port) if test.wantError { require.Error(t, err) @@ -489,271 +487,6 @@ func TestSpiffeMTLS(t *testing.T) { } } -func TestProxyProtocol(t *testing.T) { - testCases := []struct { - desc string - version int - }{ - { - desc: "proxy protocol v1", - version: 1, - }, - { - desc: "proxy protocol v2", - version: 2, - }, - } - - for _, test := range testCases { - t.Run(test.desc, func(t *testing.T) { - backendListener, err := net.Listen("tcp", ":0") - require.NoError(t, err) - - var version int - var localAddr, remoteAddr string - proxyBackendListener := proxyproto.Listener{ - Listener: backendListener, - ValidateHeader: func(h *proxyproto.Header) error { - version = int(h.Version) - localAddr = h.DestinationAddr.String() - remoteAddr = h.SourceAddr.String() - return nil - }, - Policy: func(upstream net.Addr) (proxyproto.Policy, error) { - switch test.version { - case 1, 2: - return proxyproto.USE, nil - default: - return proxyproto.REQUIRE, errors.New("unsupported version") - } - }, - } - defer proxyBackendListener.Close() - - go fakeServer(t, &proxyBackendListener) - - _, port, err := net.SplitHostPort(backendListener.Addr().String()) - require.NoError(t, err) - - dialerManager := NewDialerManager(nil) - dialerManager.Update(map[string]*dynamic.TCPServersTransport{ - "test": { - ProxyProtocol: &dynamic.ProxyProtocol{ - Version: test.version, - }, - }, - }) - - dialer, err := dialerManager.Build(&dynamic.TCPServersLoadBalancer{ServersTransport: "test"}, false) - require.NoError(t, err) - - clientConn := &fakeClientConn{ - localAddr: &net.TCPAddr{ - IP: net.ParseIP("2.2.2.2"), - Port: 12345, - }, - remoteAddr: &net.TCPAddr{ - IP: net.ParseIP("1.1.1.1"), - Port: 12345, - }, - } - - conn, err := dialer.Dial("tcp", ":"+port, clientConn) - require.NoError(t, err) - defer conn.Close() - - _, err = conn.Write([]byte("ping")) - require.NoError(t, err) - - buf := make([]byte, 64) - n, err := conn.Read(buf) - require.NoError(t, err) - - assert.Equal(t, 4, n) - assert.Equal(t, "PONG", string(buf[:4])) - assert.Equal(t, test.version, version) - assert.Equal(t, "2.2.2.2:12345", localAddr) - assert.Equal(t, "1.1.1.1:12345", remoteAddr) - }) - } -} - -func TestProxyProtocolWithTLS(t *testing.T) { - testCases := []struct { - desc string - version int - }{ - { - desc: "proxy protocol v1", - version: 1, - }, - { - desc: "proxy protocol v2", - version: 2, - }, - } - - for _, test := range testCases { - t.Run(test.desc, func(t *testing.T) { - cert, err := tls.X509KeyPair(LocalhostCert, LocalhostKey) - require.NoError(t, err) - - backendListener, err := net.Listen("tcp", ":0") - require.NoError(t, err) - - var version int - var localAddr, remoteAddr string - proxyBackendListener := proxyproto.Listener{ - Listener: backendListener, - ValidateHeader: func(h *proxyproto.Header) error { - version = int(h.Version) - localAddr = h.DestinationAddr.String() - remoteAddr = h.SourceAddr.String() - return nil - }, - Policy: func(upstream net.Addr) (proxyproto.Policy, error) { - switch test.version { - case 1, 2: - return proxyproto.USE, nil - default: - return proxyproto.REQUIRE, errors.New("unsupported version") - } - }, - } - defer proxyBackendListener.Close() - - go func() { - conn, err := proxyBackendListener.Accept() - require.NoError(t, err) - defer conn.Close() - - // Now wrap with TLS and perform handshake - tlsConn := tls.Server(conn, &tls.Config{Certificates: []tls.Certificate{cert}}) - defer tlsConn.Close() - - err = tlsConn.Handshake() - require.NoError(t, err) - - buf := make([]byte, 64) - n, err := tlsConn.Read(buf) - require.NoError(t, err) - - if bytes.Equal(buf[:n], []byte("ping")) { - _, _ = tlsConn.Write([]byte("PONG")) - } - }() - - _, port, err := net.SplitHostPort(backendListener.Addr().String()) - require.NoError(t, err) - - dialerManager := NewDialerManager(nil) - dialerManager.Update(map[string]*dynamic.TCPServersTransport{ - "test": { - TLS: &dynamic.TLSClientConfig{ - ServerName: "example.com", - RootCAs: []types.FileOrContent{types.FileOrContent(LocalhostCert)}, - InsecureSkipVerify: true, - }, - ProxyProtocol: &dynamic.ProxyProtocol{ - Version: test.version, - }, - }, - }) - - dialer, err := dialerManager.Build(&dynamic.TCPServersLoadBalancer{ - ServersTransport: "test", - }, true) - require.NoError(t, err) - - clientConn := &fakeClientConn{ - localAddr: &net.TCPAddr{ - IP: net.ParseIP("2.2.2.2"), - Port: 12345, - }, - remoteAddr: &net.TCPAddr{ - IP: net.ParseIP("1.1.1.1"), - Port: 12345, - }, - } - - conn, err := dialer.Dial("tcp", ":"+port, clientConn) - require.NoError(t, err) - defer conn.Close() - - _, err = conn.Write([]byte("ping")) - require.NoError(t, err) - - buf := make([]byte, 64) - n, err := conn.Read(buf) - require.NoError(t, err) - - assert.Equal(t, 4, n) - assert.Equal(t, "PONG", string(buf[:4])) - assert.Equal(t, test.version, version) - assert.Equal(t, "2.2.2.2:12345", localAddr) - assert.Equal(t, "1.1.1.1:12345", remoteAddr) - }) - } -} - -func TestProxyProtocolDisabled(t *testing.T) { - backendListener, err := net.Listen("tcp", ":0") - require.NoError(t, err) - defer backendListener.Close() - - go func() { - conn, err := backendListener.Accept() - require.NoError(t, err) - defer conn.Close() - - buf := make([]byte, 64) - n, err := conn.Read(buf) - require.NoError(t, err) - - if bytes.Equal(buf[:n], []byte("ping")) { - _, _ = conn.Write([]byte("PONG")) - } - }() - - _, port, err := net.SplitHostPort(backendListener.Addr().String()) - require.NoError(t, err) - - // No proxy protocol configuration. - dialerManager := NewDialerManager(nil) - dialerManager.Update(map[string]*dynamic.TCPServersTransport{ - "test": {}, - }) - - dialer, err := dialerManager.Build(&dynamic.TCPServersLoadBalancer{ServersTransport: "test"}, false) - require.NoError(t, err) - - conn, err := dialer.Dial("tcp", ":"+port, nil) - require.NoError(t, err) - - _, err = conn.Write([]byte("ping")) - require.NoError(t, err) - - buf := make([]byte, 64) - n, err := conn.Read(buf) - require.NoError(t, err) - - assert.Equal(t, 4, n) - assert.Equal(t, "PONG", string(buf[:4])) -} - -type fakeClientConn struct { - remoteAddr *net.TCPAddr - localAddr *net.TCPAddr -} - -func (f fakeClientConn) LocalAddr() net.Addr { - return f.localAddr -} - -func (f fakeClientConn) RemoteAddr() net.Addr { - return f.remoteAddr -} - // fakeSpiffePKI simulates a SPIFFE aware PKI and allows generating multiple valid SVIDs. type fakeSpiffePKI struct { caPrivateKey *rsa.PrivateKey diff --git a/pkg/tcp/proxy.go b/pkg/tcp/proxy.go index aa979d303..33b9acd8b 100644 --- a/pkg/tcp/proxy.go +++ b/pkg/tcp/proxy.go @@ -2,25 +2,34 @@ package tcp import ( "errors" + "fmt" "io" "net" "syscall" "time" + "github.com/pires/go-proxyproto" "github.com/rs/zerolog/log" + "github.com/traefik/traefik/v3/pkg/config/dynamic" ) // Proxy forwards a TCP request to a TCP service. type Proxy struct { - address string - dialer Dialer + address string + proxyProtocol *dynamic.ProxyProtocol + dialer Dialer } // NewProxy creates a new Proxy. -func NewProxy(address string, dialer Dialer) (*Proxy, error) { +func NewProxy(address string, proxyProtocol *dynamic.ProxyProtocol, dialer Dialer) (*Proxy, error) { + if proxyProtocol != nil && (proxyProtocol.Version < 1 || proxyProtocol.Version > 2) { + return nil, fmt.Errorf("unknown proxyProtocol version: %d", proxyProtocol.Version) + } + return &Proxy{ - address: address, - dialer: dialer, + address: address, + proxyProtocol: proxyProtocol, + dialer: dialer, }, nil } @@ -34,7 +43,7 @@ func (p *Proxy) ServeTCP(conn WriteCloser) { // needed because of e.g. server.trackedConnection defer conn.Close() - connBackend, err := p.dialBackend(conn) + connBackend, err := p.dialBackend() if err != nil { log.Error().Err(err).Msg("Error while dialing backend") return @@ -44,6 +53,14 @@ func (p *Proxy) ServeTCP(conn WriteCloser) { defer connBackend.Close() errChan := make(chan error) + if p.proxyProtocol != nil && p.proxyProtocol.Version > 0 && p.proxyProtocol.Version < 3 { + header := proxyproto.HeaderProxyFromAddrs(byte(p.proxyProtocol.Version), conn.RemoteAddr(), conn.LocalAddr()) + if _, err := header.WriteTo(connBackend); err != nil { + log.Error().Err(err).Msg("Error while writing TCP proxy protocol headers to backend connection") + return + } + } + go p.connCopy(conn, connBackend, errChan) go p.connCopy(connBackend, conn, errChan) @@ -62,10 +79,8 @@ func (p *Proxy) ServeTCP(conn WriteCloser) { <-errChan } -func (p *Proxy) dialBackend(clientConn net.Conn) (WriteCloser, error) { - // The clientConn is passed to the dialer so that it can use information from it if needed, - // to build a PROXY protocol header. - conn, err := p.dialer.Dial("tcp", p.address, clientConn) +func (p *Proxy) dialBackend() (WriteCloser, error) { + conn, err := p.dialer.Dial("tcp", p.address) if err != nil { return nil, err } diff --git a/pkg/tcp/proxy_test.go b/pkg/tcp/proxy_test.go index 34bd6cdfe..e2874e8aa 100644 --- a/pkg/tcp/proxy_test.go +++ b/pkg/tcp/proxy_test.go @@ -2,25 +2,59 @@ package tcp import ( "bytes" + "errors" "io" "net" "testing" "time" + "github.com/pires/go-proxyproto" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/traefik/traefik/v3/pkg/config/dynamic" ) +func fakeRedis(t *testing.T, listener net.Listener) { + t.Helper() + + for { + conn, err := listener.Accept() + require.NoError(t, err) + + for { + withErr := false + buf := make([]byte, 64) + if _, err := conn.Read(buf); err != nil { + withErr = true + } + + if string(buf[:4]) == "ping" { + time.Sleep(1 * time.Millisecond) + if _, err := conn.Write([]byte("PONG")); err != nil { + _ = conn.Close() + return + } + } + + if withErr { + _ = conn.Close() + return + } + } + } +} + func TestCloseWrite(t *testing.T) { backendListener, err := net.Listen("tcp", ":0") require.NoError(t, err) - go fakeServer(t, backendListener) + go fakeRedis(t, backendListener) _, port, err := net.SplitHostPort(backendListener.Addr().String()) require.NoError(t, err) - dialer := tcpDialer{&net.Dialer{}, 10 * time.Millisecond, nil} + dialer := tcpDialer{&net.Dialer{}, 10 * time.Millisecond} - proxy, err := NewProxy(":"+port, dialer) + proxy, err := NewProxy(":"+port, nil, dialer) require.NoError(t, err) proxyListener, err := net.Listen("tcp", ":0") @@ -50,37 +84,90 @@ func TestCloseWrite(t *testing.T) { buffer := bytes.NewBuffer(buf) n, err := io.Copy(buffer, conn) require.NoError(t, err) - require.Equal(t, int64(4), n) require.Equal(t, "PONG", buffer.String()) } -func fakeServer(t *testing.T, listener net.Listener) { - t.Helper() +func TestProxyProtocol(t *testing.T) { + testCases := []struct { + desc string + version int + }{ + { + desc: "PROXY protocol v1", + version: 1, + }, + { + desc: "PROXY protocol v2", + version: 2, + }, + } - for { - conn, err := listener.Accept() - require.NoError(t, err) + for _, test := range testCases { + t.Run(test.desc, func(t *testing.T) { + backendListener, err := net.Listen("tcp", ":0") + require.NoError(t, err) - for { - withErr := false - buf := make([]byte, 64) - if _, err := conn.Read(buf); err != nil { - withErr = true + var version int + proxyBackendListener := proxyproto.Listener{ + Listener: backendListener, + ValidateHeader: func(h *proxyproto.Header) error { + version = int(h.Version) + return nil + }, + Policy: func(upstream net.Addr) (proxyproto.Policy, error) { + switch test.version { + case 1, 2: + return proxyproto.USE, nil + default: + return proxyproto.REQUIRE, errors.New("unsupported version") + } + }, } + defer proxyBackendListener.Close() - if string(buf[:4]) == "ping" { - time.Sleep(1 * time.Millisecond) - if _, err := conn.Write([]byte("PONG")); err != nil { - _ = conn.Close() - return + go fakeRedis(t, &proxyBackendListener) + + _, port, err := net.SplitHostPort(proxyBackendListener.Addr().String()) + require.NoError(t, err) + + dialer := tcpDialer{&net.Dialer{}, 10 * time.Millisecond} + + proxy, err := NewProxy(":"+port, &dynamic.ProxyProtocol{Version: test.version}, dialer) + require.NoError(t, err) + + proxyListener, err := net.Listen("tcp", ":0") + require.NoError(t, err) + + go func() { + for { + conn, err := proxyListener.Accept() + require.NoError(t, err) + proxy.ServeTCP(conn.(*net.TCPConn)) } - } + }() - if withErr { - _ = conn.Close() - return - } - } + _, port, err = net.SplitHostPort(proxyListener.Addr().String()) + require.NoError(t, err) + + conn, err := net.Dial("tcp", ":"+port) + require.NoError(t, err) + + _, err = conn.Write([]byte("ping\n")) + require.NoError(t, err) + + err = conn.(*net.TCPConn).CloseWrite() + require.NoError(t, err) + + var buf []byte + buffer := bytes.NewBuffer(buf) + n, err := io.Copy(buffer, conn) + require.NoError(t, err) + + assert.Equal(t, int64(4), n) + assert.Equal(t, "PONG", buffer.String()) + + assert.Equal(t, test.version, version) + }) } } diff --git a/pkg/testhelpers/config.go b/pkg/testhelpers/config.go index afd298599..ee3dbc975 100644 --- a/pkg/testhelpers/config.go +++ b/pkg/testhelpers/config.go @@ -2,7 +2,6 @@ package testhelpers import ( "github.com/traefik/traefik/v3/pkg/config/dynamic" - otypes "github.com/traefik/traefik/v3/pkg/observability/types" ) // BuildConfiguration is a helper to create a configuration. @@ -58,10 +57,9 @@ func WithServiceName(serviceName string) func(*dynamic.Router) { func WithObservability() func(*dynamic.Router) { return func(r *dynamic.Router) { r.Observability = &dynamic.RouterObservabilityConfig{ - AccessLogs: pointer(true), - Metrics: pointer(true), - Tracing: pointer(true), - TraceVerbosity: otypes.MinimalVerbosity, + AccessLogs: pointer(true), + Metrics: pointer(true), + Tracing: pointer(true), } } } diff --git a/pkg/tls/certificate.go b/pkg/tls/certificate.go index f99796783..e1e11191c 100644 --- a/pkg/tls/certificate.go +++ b/pkg/tls/certificate.go @@ -7,6 +7,7 @@ import ( "fmt" "net/url" "os" + "sort" "strings" "github.com/rs/zerolog/log" @@ -34,16 +35,14 @@ var ( // Available CurveIDs defined at https://godoc.org/crypto/tls#CurveID, // also allowing rfc names defined at https://tools.ietf.org/html/rfc8446#section-4.2.7 CurveIDs = map[string]tls.CurveID{ - `secp256r1`: tls.CurveP256, - `CurveP256`: tls.CurveP256, - `secp384r1`: tls.CurveP384, - `CurveP384`: tls.CurveP384, - `secp521r1`: tls.CurveP521, - `CurveP521`: tls.CurveP521, - `x25519`: tls.X25519, - `X25519`: tls.X25519, - `x25519mlkem768`: tls.X25519MLKEM768, - `X25519MLKEM768`: tls.X25519MLKEM768, + `secp256r1`: tls.CurveP256, + `CurveP256`: tls.CurveP256, + `secp384r1`: tls.CurveP384, + `CurveP384`: tls.CurveP384, + `secp521r1`: tls.CurveP521, + `CurveP521`: tls.CurveP521, + `x25519`: tls.X25519, + `X25519`: tls.X25519, } ) @@ -75,6 +74,68 @@ type Certificate struct { KeyFile types.FileOrContent `json:"keyFile,omitempty" toml:"keyFile,omitempty" yaml:"keyFile,omitempty" loggable:"false"` } +// AppendCertificate appends a Certificate to a certificates map keyed by store name. +func (c *Certificate) AppendCertificate(certs map[string]map[string]*tls.Certificate, storeName string) error { + certContent, err := c.CertFile.Read() + if err != nil { + return fmt.Errorf("unable to read CertFile : %w", err) + } + + keyContent, err := c.KeyFile.Read() + if err != nil { + return fmt.Errorf("unable to read KeyFile : %w", err) + } + tlsCert, err := tls.X509KeyPair(certContent, keyContent) + if err != nil { + return fmt.Errorf("unable to generate TLS certificate : %w", err) + } + + parsedCert, _ := x509.ParseCertificate(tlsCert.Certificate[0]) + + var SANs []string + if parsedCert.Subject.CommonName != "" { + SANs = append(SANs, strings.ToLower(parsedCert.Subject.CommonName)) + } + if parsedCert.DNSNames != nil { + for _, dnsName := range parsedCert.DNSNames { + if dnsName != parsedCert.Subject.CommonName { + SANs = append(SANs, strings.ToLower(dnsName)) + } + } + } + if parsedCert.IPAddresses != nil { + for _, ip := range parsedCert.IPAddresses { + if ip.String() != parsedCert.Subject.CommonName { + SANs = append(SANs, strings.ToLower(ip.String())) + } + } + } + + // Guarantees the order to produce a unique cert key. + sort.Strings(SANs) + certKey := strings.Join(SANs, ",") + + certExists := false + if certs[storeName] == nil { + certs[storeName] = make(map[string]*tls.Certificate) + } else { + for domains := range certs[storeName] { + if domains == certKey { + certExists = true + break + } + } + } + if certExists { + log.Debug().Msgf("Skipping addition of certificate for domain(s) %q, to TLS Store %s, as it already exists for this store.", certKey, storeName) + } else { + log.Debug().Msgf("Adding certificate for domain(s) %s", certKey) + certs[storeName][certKey] = &tlsCert + } + + return err +} + // GetCertificate returns a tls.Certificate matching the configured CertFile and KeyFile. func (c *Certificate) GetCertificate() (tls.Certificate, error) { certContent, err := c.CertFile.Read() @@ -106,6 +167,24 @@ func (c *Certificate) GetCertificateFromBytes() (tls.Certificate, error) { return cert, nil } +// Set is the method to set the flag value, part of the flag.Value interface. +// Set's argument is a string to be parsed to set the flag. +// It's a comma-separated list, so we split it. +func (c *Certificates) Set(value string) error { + certificates := strings.Split(value, ";") + for _, certificate := range certificates { + files := strings.Split(certificate, ",") + if len(files) != 2 { + return fmt.Errorf("bad certificates format: %s", value) + } + *c = append(*c, Certificate{ + CertFile: types.FileOrContent(files[0]), + KeyFile: types.FileOrContent(files[1]), + }) + } + return nil +} + // GetTruncatedCertificateName truncates the certificate name. func (c *Certificate) GetTruncatedCertificateName() string { certName := c.CertFile.String() diff --git a/pkg/tls/certificate_store.go b/pkg/tls/certificate_store.go index 57979f45d..2ead96ccf 100644 --- a/pkg/tls/certificate_store.go +++ b/pkg/tls/certificate_store.go @@ -2,7 +2,7 @@ package tls import ( "crypto/tls" - "fmt" + "crypto/x509" "net" "sort" "strings" @@ -13,40 +13,57 @@ import ( "github.com/traefik/traefik/v3/pkg/safe" ) -// CertificateData holds runtime data for runtime TLS certificate handling. -type CertificateData struct { - Hash string - Certificate *tls.Certificate -} - // CertificateStore store for dynamic certificates. type CertificateStore struct { DynamicCerts *safe.Safe - DefaultCertificate *CertificateData + DefaultCertificate *tls.Certificate CertCache *cache.Cache - - ocspStapler *ocspStapler } // NewCertificateStore create a store for dynamic certificates. -func NewCertificateStore(ocspStapler *ocspStapler) *CertificateStore { - var dynamicCerts safe.Safe - dynamicCerts.Set(make(map[string]*CertificateData)) +func NewCertificateStore() *CertificateStore { + s := &safe.Safe{} + s.Set(make(map[string]*tls.Certificate)) return &CertificateStore{ - DynamicCerts: &dynamicCerts, + DynamicCerts: s, CertCache: cache.New(1*time.Hour, 10*time.Minute), - ocspStapler: ocspStapler, } } +func (c *CertificateStore) getDefaultCertificateDomains() []string { + var allCerts []string + + if c.DefaultCertificate == nil { + return allCerts + } + + x509Cert, err := x509.ParseCertificate(c.DefaultCertificate.Certificate[0]) + if err != nil { + log.Error().Err(err).Msg("Could not parse default certificate") + return allCerts + } + + if len(x509Cert.Subject.CommonName) > 0 { + allCerts = append(allCerts, x509Cert.Subject.CommonName) + } + + allCerts = append(allCerts, x509Cert.DNSNames...) + + for _, ipSan := range x509Cert.IPAddresses { + allCerts = append(allCerts, ipSan.String()) + } + + return allCerts +} + // GetAllDomains return a slice with all the certificate domain. func (c *CertificateStore) GetAllDomains() []string { allDomains := c.getDefaultCertificateDomains() // Get dynamic certificates if c.DynamicCerts != nil && c.DynamicCerts.Get() != nil { - for domain := range c.DynamicCerts.Get().(map[string]*CertificateData) { + for domain := range c.DynamicCerts.Get().(map[string]*tls.Certificate) { allDomains = append(allDomains, domain) } } @@ -54,23 +71,6 @@ func (c *CertificateStore) GetAllDomains() []string { return allDomains } -// GetDefaultCertificate returns the default certificate. -func (c *CertificateStore) GetDefaultCertificate() *tls.Certificate { - if c == nil { - return nil - } - - if c.ocspStapler != nil && c.DefaultCertificate.Hash != "" { - if staple, ok := c.ocspStapler.GetStaple(c.DefaultCertificate.Hash); ok { - // We are updating the OCSPStaple of the certificate without any synchronization - // as this should not cause any issue. - c.DefaultCertificate.Certificate.OCSPStaple = staple - } - } - - return c.DefaultCertificate.Certificate -} - // GetBestCertificate returns the best match certificate, and caches the response. func (c *CertificateStore) GetBestCertificate(clientHello *tls.ClientHelloInfo) *tls.Certificate { if c == nil { @@ -87,21 +87,12 @@ func (c *CertificateStore) GetBestCertificate(clientHello *tls.ClientHelloInfo) } if cert, ok := c.CertCache.Get(serverName); ok { - certificateData := cert.(*CertificateData) - if c.ocspStapler != nil && certificateData.Hash != "" { - if staple, ok := c.ocspStapler.GetStaple(certificateData.Hash); ok { - // We are updating the OCSPStaple of the certificate without any synchronization - // as this should not cause any issue. - certificateData.Certificate.OCSPStaple = staple - } - } - - return certificateData.Certificate + return cert.(*tls.Certificate) } - matchedCerts := map[string]*CertificateData{} + matchedCerts := map[string]*tls.Certificate{} if c.DynamicCerts != nil && c.DynamicCerts.Get() != nil { - for domains, cert := range c.DynamicCerts.Get().(map[string]*CertificateData) { + for domains, cert := range c.DynamicCerts.Get().(map[string]*tls.Certificate) { for _, certDomain := range strings.Split(domains, ",") { if matchDomain(serverName, certDomain) { matchedCerts[certDomain] = cert @@ -119,25 +110,15 @@ func (c *CertificateStore) GetBestCertificate(clientHello *tls.ClientHelloInfo) sort.Strings(keys) // cache best match - certificateData := matchedCerts[keys[len(keys)-1]] - c.CertCache.SetDefault(serverName, certificateData) - - if c.ocspStapler != nil && certificateData.Hash != "" { - if staple, ok := c.ocspStapler.GetStaple(certificateData.Hash); ok { - // We are updating the OCSPStaple of the certificate without any synchronization - // as this should not cause any issue. - certificateData.Certificate.OCSPStaple = staple - } - } - - return certificateData.Certificate + c.CertCache.SetDefault(serverName, matchedCerts[keys[len(keys)-1]]) + return matchedCerts[keys[len(keys)-1]] } return nil } // GetCertificate returns the first certificate matching all the given domains. -func (c *CertificateStore) GetCertificate(domains []string) *CertificateData { +func (c *CertificateStore) GetCertificate(domains []string) *tls.Certificate { if c == nil { return nil } @@ -146,11 +127,11 @@ func (c *CertificateStore) GetCertificate(domains []string) *CertificateData { domainsKey := strings.Join(domains, ",") if cert, ok := c.CertCache.Get(domainsKey); ok { - return cert.(*CertificateData) + return cert.(*tls.Certificate) } if c.DynamicCerts != nil && c.DynamicCerts.Get() != nil { - for certDomains, cert := range c.DynamicCerts.Get().(map[string]*CertificateData) { + for certDomains, cert := range c.DynamicCerts.Get().(map[string]*tls.Certificate) { if domainsKey == certDomains { c.CertCache.SetDefault(domainsKey, cert) return cert @@ -182,91 +163,6 @@ func (c *CertificateStore) ResetCache() { } } -func (c *CertificateStore) getDefaultCertificateDomains() []string { - if c.DefaultCertificate == nil { - return nil - } - - defaultCert := c.DefaultCertificate.Certificate.Leaf - - var allCerts []string - if len(defaultCert.Subject.CommonName) > 0 { - allCerts = append(allCerts, defaultCert.Subject.CommonName) - } - - allCerts = append(allCerts, defaultCert.DNSNames...) - - for _, ipSan := range defaultCert.IPAddresses { - allCerts = append(allCerts, ipSan.String()) - } - - return allCerts -} - -// appendCertificate appends a Certificate to a certificates map keyed by store name. -func appendCertificate(certs map[string]map[string]*CertificateData, subjectAltNames []string, storeName string, cert *CertificateData) { - // Guarantees the order to produce a unique cert key. - sort.Strings(subjectAltNames) - certKey := strings.Join(subjectAltNames, ",") - - certExists := false - if certs[storeName] == nil { - certs[storeName] = make(map[string]*CertificateData) - } else { - for domains := range certs[storeName] { - if domains == certKey { - certExists = true - break - } - } - } - if certExists { - log.Debug().Msgf("Skipping addition of certificate for domain(s) %q, to TLS Store %s, as it already exists for this store.", certKey, storeName) - } else { - log.Debug().Msgf("Adding certificate for domain(s) %s", certKey) - - certs[storeName][certKey] = cert - } -} - -func parseCertificate(cert *Certificate) (tls.Certificate, []string, error) { - certContent, err := cert.CertFile.Read() - if err != nil { - return tls.Certificate{}, nil, fmt.Errorf("unable to read CertFile: %w", err) - } - - keyContent, err := cert.KeyFile.Read() - if err != nil { - return tls.Certificate{}, nil, fmt.Errorf("unable to read KeyFile: %w", err) - } - - tlsCert, err := tls.X509KeyPair(certContent, keyContent) - if err != nil { - return tls.Certificate{}, nil, fmt.Errorf("unable to generate TLS certificate: %w", err) - } - - var SANs []string - if tlsCert.Leaf.Subject.CommonName != "" { - SANs = append(SANs, strings.ToLower(tlsCert.Leaf.Subject.CommonName)) - } - if tlsCert.Leaf.DNSNames != nil { - for _, dnsName := range tlsCert.Leaf.DNSNames { - if dnsName != tlsCert.Leaf.Subject.CommonName { - SANs = append(SANs, strings.ToLower(dnsName)) - } - } - } - if tlsCert.Leaf.IPAddresses != nil { - for _, ip := range tlsCert.Leaf.IPAddresses { - if ip.String() != tlsCert.Leaf.Subject.CommonName { - SANs = append(SANs, strings.ToLower(ip.String())) - } - } - } - - return tlsCert, SANs, err -} - // matchDomain returns whether the server name matches the cert domain. // The server name, from TLS SNI, must not have trailing dots (https://datatracker.ietf.org/doc/html/rfc6066#section-3). // This is enforced by https://github.com/golang/go/blob/d3d7998756c33f69706488cade1cd2b9b10a4c7f/src/crypto/tls/handshake_messages.go#L423-L427. diff --git a/pkg/tls/certificate_store_test.go b/pkg/tls/certificate_store_test.go index ef4fd3885..cbc668bd6 100644 --- a/pkg/tls/certificate_store_test.go +++ b/pkg/tls/certificate_store_test.go @@ -58,12 +58,12 @@ func TestGetBestCertificate(t *testing.T) { for _, test := range testCases { t.Run(test.desc, func(t *testing.T) { t.Parallel() - dynamicMap := map[string]*CertificateData{} + dynamicMap := map[string]*tls.Certificate{} if test.dynamicCert != "" { cert, err := loadTestCert(test.dynamicCert, test.uppercase) require.NoError(t, err) - dynamicMap[strings.ToLower(test.dynamicCert)] = &CertificateData{Certificate: cert} + dynamicMap[strings.ToLower(test.dynamicCert)] = cert } store := &CertificateStore{ diff --git a/pkg/tls/ocsp.go b/pkg/tls/ocsp.go deleted file mode 100644 index 2df703fb0..000000000 --- a/pkg/tls/ocsp.go +++ /dev/null @@ -1,208 +0,0 @@ -package tls - -import ( - "bytes" - "context" - "crypto/x509" - "errors" - "fmt" - "io" - "net/http" - "time" - - "github.com/patrickmn/go-cache" - "github.com/rs/zerolog/log" - "golang.org/x/crypto/ocsp" -) - -const defaultCacheDuration = 24 * time.Hour - -type ocspEntry struct { - leaf *x509.Certificate - issuer *x509.Certificate - responders []string - nextUpdate time.Time - staple []byte -} - -// ocspStapler retrieves staples from OCSP responders and store them in an in-memory cache. -// It also updates the staples on a regular basis and before they expire. -type ocspStapler struct { - client *http.Client - cache cache.Cache - forceStapleUpdates chan struct{} - responderOverrides map[string]string -} - -// newOCSPStapler creates a new ocspStapler cache. -func newOCSPStapler(responderOverrides map[string]string) *ocspStapler { - return &ocspStapler{ - client: &http.Client{Timeout: 10 * time.Second}, - cache: *cache.New(defaultCacheDuration, 5*time.Minute), - forceStapleUpdates: make(chan struct{}, 1), - responderOverrides: responderOverrides, - } -} - -// Run updates the OCSP staples every hours. -func (o *ocspStapler) Run(ctx context.Context) { - ticker := time.NewTicker(time.Hour) - defer ticker.Stop() - - for { - select { - case <-ctx.Done(): - return - - case <-o.forceStapleUpdates: - o.updateStaples(ctx) - - case <-ticker.C: - o.updateStaples(ctx) - } - } -} - -// ForceStapleUpdates triggers staple updates in the background instead of waiting for the Run routine to update them. -func (o *ocspStapler) ForceStapleUpdates() { - select { - case o.forceStapleUpdates <- struct{}{}: - default: - } -} - -// GetStaple retrieves the OCSP staple for the corresponding to the given key (public certificate hash). -func (o *ocspStapler) GetStaple(key string) ([]byte, bool) { - if item, ok := o.cache.Get(key); ok && item != nil { - if entry, ok := item.(*ocspEntry); ok { - return entry.staple, true - } - } - return nil, false -} - -// Upsert creates a new entry for the given certificate. -// The ocspStapler will then be responsible from retrieving and updating the corresponding OCSP obtainStaple. -func (o *ocspStapler) Upsert(key string, leaf, issuer *x509.Certificate) error { - if len(leaf.OCSPServer) == 0 { - return errors.New("leaf certificate does not contain an OCSP server") - } - - if item, ok := o.cache.Get(key); ok { - o.cache.Set(key, item, cache.NoExpiration) - return nil - } - - var responders []string - for _, url := range leaf.OCSPServer { - if len(o.responderOverrides) > 0 { - if newURL, ok := o.responderOverrides[url]; ok { - url = newURL - } - } - responders = append(responders, url) - } - - o.cache.Set(key, &ocspEntry{ - leaf: leaf, - issuer: issuer, - responders: responders, - }, cache.NoExpiration) - - return nil -} - -// ResetTTL resets the expiration time for all items having no expiration. -// This allows setting a TTL for certificates that do not exist anymore in the dynamic configuration. -// For certificates that are still provided by the dynamic configuration, -// their expiration time will be unset when calling the Upsert method. -func (o *ocspStapler) ResetTTL() { - for key, item := range o.cache.Items() { - if item.Expiration > 0 { - continue - } - - o.cache.Set(key, item.Object, defaultCacheDuration) - } -} - -func (o *ocspStapler) updateStaples(ctx context.Context) { - for _, item := range o.cache.Items() { - select { - case <-ctx.Done(): - return - default: - } - - entry := item.Object.(*ocspEntry) - - if entry.staple != nil && time.Now().Before(entry.nextUpdate) { - continue - } - - if err := o.updateStaple(ctx, entry); err != nil { - log.Error().Err(err).Msgf("Unable to retieve OCSP staple for: %s", entry.leaf.Subject.CommonName) - continue - } - } -} - -// obtainStaple obtains the OCSP stable for the given leaf certificate. -func (o *ocspStapler) updateStaple(ctx context.Context, entry *ocspEntry) error { - ocspReq, err := ocsp.CreateRequest(entry.leaf, entry.issuer, nil) - if err != nil { - return fmt.Errorf("creating OCSP request: %w", err) - } - - for _, responder := range entry.responders { - logger := log.With().Str("responder", responder).Logger() - - req, err := http.NewRequestWithContext(ctx, http.MethodPost, responder, bytes.NewReader(ocspReq)) - if err != nil { - return fmt.Errorf("creating OCSP request: %w", err) - } - - req.Header.Set("Content-Type", "application/ocsp-request") - - res, err := o.client.Do(req) - if err != nil && ctx.Err() != nil { - return ctx.Err() - } - if err != nil { - logger.Debug().Err(err).Msg("Unable to obtain OCSP response") - continue - } - defer res.Body.Close() - - if res.StatusCode/100 != 2 { - logger.Debug().Msgf("Unable to obtain OCSP response due to status code: %d", res.StatusCode) - continue - } - - ocspResBytes, err := io.ReadAll(res.Body) - if err != nil { - logger.Debug().Err(err).Msg("Unable to read OCSP response bytes") - continue - } - - ocspRes, err := ocsp.ParseResponseForCert(ocspResBytes, entry.leaf, entry.issuer) - if err != nil { - logger.Debug().Err(err).Msg("Unable to parse OCSP response") - continue - } - - entry.staple = ocspResBytes - - // As per RFC 6960, the nextUpdate field is optional. - if ocspRes.NextUpdate.IsZero() { - // NextUpdate is not set, the staple should be updated on the next update. - entry.nextUpdate = time.Now() - } else { - entry.nextUpdate = ocspRes.ThisUpdate.Add(ocspRes.NextUpdate.Sub(ocspRes.ThisUpdate) / 2) - } - - return nil - } - - return errors.New("no OCSP staple obtained from any responders") -} diff --git a/pkg/tls/ocsp_test.go b/pkg/tls/ocsp_test.go deleted file mode 100644 index 0cbb69a89..000000000 --- a/pkg/tls/ocsp_test.go +++ /dev/null @@ -1,485 +0,0 @@ -package tls - -import ( - "crypto" - "crypto/tls" - "io" - "net/http" - "net/http/httptest" - "testing" - "time" - - "github.com/patrickmn/go-cache" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "golang.org/x/crypto/ocsp" -) - -const certWithOCSPServer = `-----BEGIN CERTIFICATE----- -MIIBgjCCASegAwIBAgICIAAwCgYIKoZIzj0EAwIwEjEQMA4GA1UEAxMHVGVzdCBD -QTAeFw0yMzAxMDExMjAwMDBaFw0yMzAyMDExMjAwMDBaMCAxHjAcBgNVBAMTFU9D -U1AgVGVzdCBDZXJ0aWZpY2F0ZTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABIoe -I/bjo34qony8LdRJD+Jhuk8/S8YHXRHl6rH9t5VFCFtX8lIPN/Ll1zCrQ2KB3Wlb -fxSgiQyLrCpZyrdhVPSjXzBdMAwGA1UdEwEB/wQCMAAwHwYDVR0jBBgwFoAU+Eo3 -5sST4LRrwS4dueIdGBZ5d7IwLAYIKwYBBQUHAQEEIDAeMBwGCCsGAQUFBzABhhBv -Y3NwLmV4YW1wbGUuY29tMAoGCCqGSM49BAMCA0kAMEYCIQDg94xY/+/VepESdvTT -ykCwiWOS2aCpjyryrKpwMKkR0AIhAPc/+ZEz4W10OENxC1t+NUTvS8JbEGOwulkZ -z9yfaLuD ------END CERTIFICATE-----` - -const certWithoutOCSPServer = `-----BEGIN CERTIFICATE----- -MIIBUzCB+aADAgECAgIgADAKBggqhkjOPQQDAjASMRAwDgYDVQQDEwdUZXN0IENB -MB4XDTIzMDEwMTEyMDAwMFoXDTIzMDIwMTEyMDAwMFowIDEeMBwGA1UEAxMVT0NT -UCBUZXN0IENlcnRpZmljYXRlMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEih4j -9uOjfiqifLwt1EkP4mG6Tz9LxgddEeXqsf23lUUIW1fyUg838uXXMKtDYoHdaVt/ -FKCJDIusKlnKt2FU9KMxMC8wDAYDVR0TAQH/BAIwADAfBgNVHSMEGDAWgBT4Sjfm -xJPgtGvBLh254h0YFnl3sjAKBggqhkjOPQQDAgNJADBGAiEA3rWetLGblfSuNZKf -5CpZxhj3A0BjEocEh+2P+nAgIdUCIQDIgptabR1qTLQaF2u0hJsEX2IKuIUvYWH3 -6Lb92+zIHg== ------END CERTIFICATE-----` - -// certKey is the private key for both certWithOCSPServer and certWithoutOCSPServer. -const certKey = `-----BEGIN EC PRIVATE KEY----- -MHcCAQEEINnVcgrSNh4HlThWlZpegq14M8G/p9NVDtdVjZrseUGLoAoGCCqGSM49 -AwEHoUQDQgAEih4j9uOjfiqifLwt1EkP4mG6Tz9LxgddEeXqsf23lUUIW1fyUg83 -8uXXMKtDYoHdaVt/FKCJDIusKlnKt2FU9A== ------END EC PRIVATE KEY-----` - -// caCert is the issuing certificate for certWithOCSPServer and certWithoutOCSPServer. -const caCert = `-----BEGIN CERTIFICATE----- -MIIBazCCARGgAwIBAgICEAAwCgYIKoZIzj0EAwIwEjEQMA4GA1UEAxMHVGVzdCBD -QTAeFw0yMzAxMDExMjAwMDBaFw0yMzAyMDExMjAwMDBaMBIxEDAOBgNVBAMTB1Rl -c3QgQ0EwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASdKexSor/aeazDM57UHhAX -rCkJxUeF2BWf0lZYCRxc3f0GdrEsVvjJW8+/E06eAzDCGSdM/08Nvun1nb6AmAlt -o1cwVTAOBgNVHQ8BAf8EBAMCAQYwEwYDVR0lBAwwCgYIKwYBBQUHAwkwDwYDVR0T -AQH/BAUwAwEB/zAdBgNVHQ4EFgQU+Eo35sST4LRrwS4dueIdGBZ5d7IwCgYIKoZI -zj0EAwIDSAAwRQIgGbA39+kETTB/YMLBFoC2fpZe1cDWfFB7TUdfINUqdH4CIQCR -ByUFC8A+hRNkK5YNH78bgjnKk/88zUQF5ONy4oPGdQ== ------END CERTIFICATE-----` - -const caKey = `-----BEGIN EC PRIVATE KEY----- -MHcCAQEEIDJ59ptjq3MzILH4zn5IKoH1sYn+zrUeq2kD8+DD2x+OoAoGCCqGSM49 -AwEHoUQDQgAEnSnsUqK/2nmswzOe1B4QF6wpCcVHhdgVn9JWWAkcXN39BnaxLFb4 -yVvPvxNOngMwwhknTP9PDb7p9Z2+gJgJbQ== ------END EC PRIVATE KEY-----` - -func TestOCSPStapler_Upsert(t *testing.T) { - ocspStapler := newOCSPStapler(nil) - - issuerCert, err := tls.X509KeyPair([]byte(caCert), []byte(caKey)) - require.NoError(t, err) - - leafCert, err := tls.X509KeyPair([]byte(certWithOCSPServer), []byte(certKey)) - require.NoError(t, err) - - // Upsert a certificate without an OCSP server should raise an error. - leafCertWithoutOCSPServer, err := tls.X509KeyPair([]byte(certWithoutOCSPServer), []byte(certKey)) - require.NoError(t, err) - - err = ocspStapler.Upsert("foo", leafCertWithoutOCSPServer.Leaf, issuerCert.Leaf) - require.Error(t, err) - - // Upsert a certificate with an OCSP server. - err = ocspStapler.Upsert("foo", leafCert.Leaf, issuerCert.Leaf) - require.NoError(t, err) - - i, ok := ocspStapler.cache.Get("foo") - require.True(t, ok) - - e, ok := i.(*ocspEntry) - require.True(t, ok) - - assert.Equal(t, leafCert.Leaf, e.leaf) - assert.Equal(t, issuerCert.Leaf, e.issuer) - assert.Nil(t, e.staple) - assert.Equal(t, []string{"ocsp.example.com"}, e.responders) - assert.Equal(t, int64(0), ocspStapler.cache.Items()["foo"].Expiration) - - // Upsert an existing entry to make sure that the existing staple is preserved. - e.staple = []byte("foo") - e.nextUpdate = time.Now() - e.responders = []string{"foo.com"} - - err = ocspStapler.Upsert("foo", leafCert.Leaf, issuerCert.Leaf) - require.NoError(t, err) - - i, ok = ocspStapler.cache.Get("foo") - require.True(t, ok) - - e, ok = i.(*ocspEntry) - require.True(t, ok) - - assert.Equal(t, leafCert.Leaf, e.leaf) - assert.Equal(t, issuerCert.Leaf, e.issuer) - assert.Equal(t, []byte("foo"), e.staple) - assert.NotZero(t, e.nextUpdate) - assert.Equal(t, []string{"foo.com"}, e.responders) - assert.Equal(t, int64(0), ocspStapler.cache.Items()["foo"].Expiration) -} - -func TestOCSPStapler_Upsert_withResponderOverrides(t *testing.T) { - ocspStapler := newOCSPStapler(map[string]string{ - "ocsp.example.com": "foo.com", - }) - - issuerCert, err := tls.X509KeyPair([]byte(caCert), []byte(caKey)) - require.NoError(t, err) - - leafCert, err := tls.X509KeyPair([]byte(certWithOCSPServer), []byte(certKey)) - require.NoError(t, err) - - err = ocspStapler.Upsert("foo", leafCert.Leaf, issuerCert.Leaf) - require.NoError(t, err) - - i, ok := ocspStapler.cache.Get("foo") - require.True(t, ok) - - e, ok := i.(*ocspEntry) - require.True(t, ok) - - assert.Equal(t, leafCert.Leaf, e.leaf) - assert.Equal(t, issuerCert.Leaf, e.issuer) - assert.Nil(t, e.staple) - assert.Equal(t, []string{"foo.com"}, e.responders) -} - -func TestOCSPStapler_ResetTTL(t *testing.T) { - ocspStapler := newOCSPStapler(nil) - - issuerCert, err := tls.X509KeyPair([]byte(caCert), []byte(caKey)) - require.NoError(t, err) - - leafCert, err := tls.X509KeyPair([]byte(certWithOCSPServer), []byte(certKey)) - require.NoError(t, err) - - ocspStapler.cache.Set("foo", &ocspEntry{ - leaf: leafCert.Leaf, - issuer: issuerCert.Leaf, - responders: []string{"foo.com"}, - nextUpdate: time.Now(), - staple: []byte("foo"), - }, cache.NoExpiration) - - ocspStapler.cache.Set("bar", &ocspEntry{ - leaf: leafCert.Leaf, - issuer: issuerCert.Leaf, - responders: []string{"bar.com"}, - nextUpdate: time.Now(), - staple: []byte("bar"), - }, time.Hour) - - wantBarExpiration := ocspStapler.cache.Items()["bar"].Expiration - - ocspStapler.ResetTTL() - - item, ok := ocspStapler.cache.Items()["foo"] - require.True(t, ok) - - e, ok := item.Object.(*ocspEntry) - require.True(t, ok) - - assert.Positive(t, item.Expiration) - assert.Equal(t, leafCert.Leaf, e.leaf) - assert.Equal(t, issuerCert.Leaf, e.issuer) - assert.Equal(t, []byte("foo"), e.staple) - assert.NotZero(t, e.nextUpdate) - assert.Equal(t, []string{"foo.com"}, e.responders) - - item, ok = ocspStapler.cache.Items()["bar"] - require.True(t, ok) - - e, ok = item.Object.(*ocspEntry) - require.True(t, ok) - - assert.Equal(t, wantBarExpiration, item.Expiration) - assert.Equal(t, leafCert.Leaf, e.leaf) - assert.Equal(t, issuerCert.Leaf, e.issuer) - assert.Equal(t, []byte("bar"), e.staple) - assert.NotZero(t, e.nextUpdate) - assert.Equal(t, []string{"bar.com"}, e.responders) -} - -func TestOCSPStapler_GetStaple(t *testing.T) { - ocspStapler := newOCSPStapler(nil) - - // Get an un-existing staple. - staple, exists := ocspStapler.GetStaple("foo") - - assert.False(t, exists) - assert.Nil(t, staple) - - // Get an existing staple. - ocspStapler.cache.Set("foo", &ocspEntry{staple: []byte("foo")}, cache.NoExpiration) - - staple, exists = ocspStapler.GetStaple("foo") - - assert.True(t, exists) - assert.Equal(t, []byte("foo"), staple) -} - -func TestOCSPStapler_updateStaple(t *testing.T) { - leafCert, err := tls.X509KeyPair([]byte(certWithOCSPServer), []byte(certKey)) - require.NoError(t, err) - - issuerCert, err := tls.X509KeyPair([]byte(caCert), []byte(caKey)) - require.NoError(t, err) - - thisUpdate, err := time.Parse("2006-01-02", "2025-01-01") - require.NoError(t, err) - nextUpdate, err := time.Parse("2006-01-02", "2025-01-02") - require.NoError(t, err) - stapleUpdate := thisUpdate.Add(nextUpdate.Sub(thisUpdate) / 2) - - ocspResponseTmpl := ocsp.Response{ - SerialNumber: leafCert.Leaf.SerialNumber, - TBSResponseData: []byte("foo"), - ThisUpdate: thisUpdate, - NextUpdate: nextUpdate, - } - - ocspResponse, err := ocsp.CreateResponse(leafCert.Leaf, leafCert.Leaf, ocspResponseTmpl, issuerCert.PrivateKey.(crypto.Signer)) - require.NoError(t, err) - - handler := func(rw http.ResponseWriter, req *http.Request) { - ct := req.Header.Get("Content-Type") - assert.Equal(t, "application/ocsp-request", ct) - - reqBytes, err := io.ReadAll(req.Body) - require.NoError(t, err) - - _, err = ocsp.ParseRequest(reqBytes) - require.NoError(t, err) - - rw.Header().Set("Content-Type", "application/ocsp-response") - - _, err = rw.Write(ocspResponse) - require.NoError(t, err) - } - - responder := httptest.NewServer(http.HandlerFunc(handler)) - t.Cleanup(responder.Close) - - responderStatusNotOK := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusNotFound) - })) - t.Cleanup(responderStatusNotOK.Close) - - testCases := []struct { - desc string - entry *ocspEntry - expectError bool - }{ - { - desc: "no responder", - entry: &ocspEntry{ - leaf: leafCert.Leaf, - issuer: issuerCert.Leaf, - }, - expectError: true, - }, - { - desc: "wrong responder", - entry: &ocspEntry{ - leaf: leafCert.Leaf, - issuer: issuerCert.Leaf, - responders: []string{"http://foo.bar"}, - }, - expectError: true, - }, - { - desc: "not ok status responder", - entry: &ocspEntry{ - leaf: leafCert.Leaf, - issuer: issuerCert.Leaf, - responders: []string{responderStatusNotOK.URL}, - }, - expectError: true, - }, - { - desc: "one wrong responder, one ok", - entry: &ocspEntry{ - leaf: leafCert.Leaf, - issuer: issuerCert.Leaf, - responders: []string{"http://foo.bar", responder.URL}, - }, - }, - { - desc: "ok responder", - entry: &ocspEntry{ - leaf: leafCert.Leaf, - issuer: issuerCert.Leaf, - responders: []string{responder.URL}, - }, - }, - } - - for _, test := range testCases { - t.Run(test.desc, func(t *testing.T) { - t.Parallel() - - ocspStapler := newOCSPStapler(nil) - ocspStapler.client = &http.Client{Timeout: time.Second} - - err = ocspStapler.updateStaple(t.Context(), test.entry) - if test.expectError { - require.Error(t, err) - return - } - - require.NoError(t, err) - - assert.Equal(t, ocspResponse, test.entry.staple) - assert.Equal(t, stapleUpdate.UTC(), test.entry.nextUpdate) - }) - } -} - -func TestOCSPStapler_updateStaple_withoutNextUpdate(t *testing.T) { - leafCert, err := tls.X509KeyPair([]byte(certWithOCSPServer), []byte(certKey)) - require.NoError(t, err) - - issuerCert, err := tls.X509KeyPair([]byte(caCert), []byte(caKey)) - require.NoError(t, err) - - thisUpdate, err := time.Parse("2006-01-02", "2025-01-01") - require.NoError(t, err) - - ocspResponseTmpl := ocsp.Response{ - SerialNumber: leafCert.Leaf.SerialNumber, - TBSResponseData: []byte("foo"), - ThisUpdate: thisUpdate, - } - - ocspResponse, err := ocsp.CreateResponse(leafCert.Leaf, leafCert.Leaf, ocspResponseTmpl, issuerCert.PrivateKey.(crypto.Signer)) - require.NoError(t, err) - - handler := func(rw http.ResponseWriter, req *http.Request) { - ct := req.Header.Get("Content-Type") - assert.Equal(t, "application/ocsp-request", ct) - - reqBytes, err := io.ReadAll(req.Body) - require.NoError(t, err) - - _, err = ocsp.ParseRequest(reqBytes) - require.NoError(t, err) - - rw.Header().Set("Content-Type", "application/ocsp-response") - - _, err = rw.Write(ocspResponse) - require.NoError(t, err) - } - - responder := httptest.NewServer(http.HandlerFunc(handler)) - t.Cleanup(responder.Close) - - responderStatusNotOK := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusNotFound) - })) - t.Cleanup(responderStatusNotOK.Close) - - ocspStapler := newOCSPStapler(nil) - ocspStapler.client = &http.Client{Timeout: time.Second} - - entry := &ocspEntry{ - leaf: leafCert.Leaf, - issuer: issuerCert.Leaf, - responders: []string{responder.URL}, - } - err = ocspStapler.updateStaple(t.Context(), entry) - require.NoError(t, err) - - assert.Equal(t, ocspResponse, entry.staple) - assert.NotZero(t, entry.nextUpdate) - assert.Greater(t, time.Now(), entry.nextUpdate) -} - -func TestOCSPStapler_updateStaples(t *testing.T) { - leafCert, err := tls.X509KeyPair([]byte(certWithOCSPServer), []byte(certKey)) - require.NoError(t, err) - - issuerCert, err := tls.X509KeyPair([]byte(caCert), []byte(caKey)) - require.NoError(t, err) - - thisUpdate, err := time.Parse("2006-01-02", "2025-01-01") - require.NoError(t, err) - nextUpdate, err := time.Parse("2006-01-02", "2025-01-02") - require.NoError(t, err) - stapleUpdate := thisUpdate.Add(nextUpdate.Sub(thisUpdate) / 2) - - ocspResponseTmpl := ocsp.Response{ - SerialNumber: leafCert.Leaf.SerialNumber, - TBSResponseData: []byte("foo"), - ThisUpdate: thisUpdate, - NextUpdate: nextUpdate, - } - - ocspResponse, err := ocsp.CreateResponse(leafCert.Leaf, leafCert.Leaf, ocspResponseTmpl, issuerCert.PrivateKey.(crypto.Signer)) - require.NoError(t, err) - - handler := func(rw http.ResponseWriter, req *http.Request) { - ct := req.Header.Get("Content-Type") - assert.Equal(t, "application/ocsp-request", ct) - - reqBytes, err := io.ReadAll(req.Body) - require.NoError(t, err) - - _, err = ocsp.ParseRequest(reqBytes) - require.NoError(t, err) - - rw.Header().Set("Content-Type", "application/ocsp-response") - - _, err = rw.Write(ocspResponse) - require.NoError(t, err) - } - - responder := httptest.NewServer(http.HandlerFunc(handler)) - t.Cleanup(responder.Close) - - ocspStapler := newOCSPStapler(nil) - ocspStapler.client = &http.Client{Timeout: time.Second} - - // nil staple entry - ocspStapler.cache.Set("nilStaple", &ocspEntry{ - leaf: leafCert.Leaf, - issuer: issuerCert.Leaf, - responders: []string{responder.URL}, - nextUpdate: time.Now().Add(-time.Hour), - }, cache.NoExpiration) - // staple entry with nextUpdate in the past - ocspStapler.cache.Set("toUpdate", &ocspEntry{ - leaf: leafCert.Leaf, - issuer: issuerCert.Leaf, - responders: []string{responder.URL}, - staple: []byte("foo"), - nextUpdate: time.Now().Add(-time.Hour), - }, cache.NoExpiration) - // staple entry with nextUpdate in the future - inOneHour := time.Now().Add(time.Hour) - ocspStapler.cache.Set("noUpdate", &ocspEntry{ - leaf: leafCert.Leaf, - issuer: issuerCert.Leaf, - responders: []string{responder.URL}, - staple: []byte("foo"), - nextUpdate: inOneHour, - }, cache.NoExpiration) - - ocspStapler.updateStaples(t.Context()) - - nilStaple, ok := ocspStapler.cache.Get("nilStaple") - require.True(t, ok) - - assert.Equal(t, ocspResponse, nilStaple.(*ocspEntry).staple) - assert.Equal(t, stapleUpdate.UTC(), nilStaple.(*ocspEntry).nextUpdate) - - toUpdate, ok := ocspStapler.cache.Get("toUpdate") - require.True(t, ok) - - assert.Equal(t, ocspResponse, toUpdate.(*ocspEntry).staple) - assert.Equal(t, stapleUpdate.UTC(), nilStaple.(*ocspEntry).nextUpdate) - - noUpdate, ok := ocspStapler.cache.Get("noUpdate") - require.True(t, ok) - - assert.Equal(t, []byte("foo"), noUpdate.(*ocspEntry).staple) - assert.Equal(t, inOneHour, noUpdate.(*ocspEntry).nextUpdate) -} diff --git a/pkg/tls/tlsmanager.go b/pkg/tls/tlsmanager.go index 54d2c4cbf..7fcbeab76 100644 --- a/pkg/tls/tlsmanager.go +++ b/pkg/tls/tlsmanager.go @@ -6,16 +6,14 @@ import ( "crypto/x509" "errors" "fmt" - "hash/fnv" "slices" - "strconv" "strings" "sync" "github.com/go-acme/lego/v4/challenge/dns01" "github.com/go-acme/lego/v4/challenge/tlsalpn01" "github.com/rs/zerolog/log" - "github.com/traefik/traefik/v3/pkg/observability/logs" + "github.com/traefik/traefik/v3/pkg/logs" "github.com/traefik/traefik/v3/pkg/tls/generate" "github.com/traefik/traefik/v3/pkg/types" ) @@ -45,11 +43,6 @@ func getCipherSuites() []string { return ciphers } -// OCSPConfig contains the OCSP configuration. -type OCSPConfig struct { - ResponderOverrides map[string]string `description:"Defines a map of OCSP responders to replace for querying OCSP servers." json:"responderOverrides,omitempty" toml:"responderOverrides,omitempty" yaml:"responderOverrides,omitempty"` -} - // Manager is the TLS option/store/configuration factory. type Manager struct { lock sync.RWMutex @@ -57,33 +50,16 @@ type Manager struct { stores map[string]*CertificateStore configs map[string]Options certs []*CertAndStores - - // As of today, the TLS manager contains and is responsible for creating/starting the OCSP ocspStapler. - // It would likely have been a Configuration listener but this implies that certs are re-parsed. - // But this would probably have impact on resource consumption. - ocspStapler *ocspStapler } // NewManager creates a new Manager. -func NewManager(ocspConfig *OCSPConfig) *Manager { - manager := &Manager{ +func NewManager() *Manager { + return &Manager{ stores: map[string]*CertificateStore{}, configs: map[string]Options{ "default": DefaultTLSOptions, }, } - - if ocspConfig != nil { - manager.ocspStapler = newOCSPStapler(ocspConfig.ResponderOverrides) - } - - return manager -} - -func (m *Manager) Run(ctx context.Context) { - if m.ocspStapler != nil { - m.ocspStapler.Run(ctx) - } } // UpdateConfigs updates the TLS* configuration options. @@ -115,14 +91,7 @@ func (m *Manager) UpdateConfigs(ctx context.Context, stores map[string]Store, co m.storesConfig[tlsalpn01.ACMETLS1Protocol] = Store{} } - storesCertificates := make(map[string]map[string]*CertificateData) - - // Define the TTL for all the cache entries with no TTL. - // This will discard entries that are not used anymore. - if m.ocspStapler != nil { - m.ocspStapler.ResetTTL() - } - + storesCertificates := make(map[string]map[string]*tls.Certificate) for _, conf := range certs { if len(conf.Stores) == 0 { log.Ctx(ctx).Debug().MsgFunc(func() string { @@ -132,49 +101,24 @@ func (m *Manager) UpdateConfigs(ctx context.Context, stores map[string]Store, co conf.Stores = []string{DefaultTLSStoreName} } - cert, SANs, err := parseCertificate(&conf.Certificate) - if err != nil { - log.Ctx(ctx).Error().Err(err).Msgf("Unable to parse certificate %s", conf.Certificate.GetTruncatedCertificateName()) - continue - } - - var certHash string - if m.ocspStapler != nil && len(cert.Leaf.OCSPServer) > 0 { - certHash = hashRawCert(cert.Leaf.Raw) - - issuer := cert.Leaf - if len(cert.Certificate) > 1 { - issuer, err = x509.ParseCertificate(cert.Certificate[1]) - if err != nil { - log.Ctx(ctx).Error().Err(err).Msgf("Unable to parse issuer certificate %s", conf.Certificate.GetTruncatedCertificateName()) - continue - } - } - - if err := m.ocspStapler.Upsert(certHash, cert.Leaf, issuer); err != nil { - log.Ctx(ctx).Error().Err(err).Msgf("Unable to upsert OCSP certificate %s", conf.Certificate.GetTruncatedCertificateName()) - continue - } - } - - certData := &CertificateData{ - Certificate: &cert, - Hash: certHash, - } - for _, store := range conf.Stores { + logger := log.Ctx(ctx).With().Str(logs.TLSStoreName, store).Logger() + if _, ok := m.storesConfig[store]; !ok { m.storesConfig[store] = Store{} } - appendCertificate(storesCertificates, SANs, store, certData) + err := conf.Certificate.AppendCertificate(storesCertificates, store) + if err != nil { + logger.Error().Err(err).Msgf("Unable to append certificate %s to store", conf.Certificate.GetTruncatedCertificateName()) + } } } m.stores = make(map[string]*CertificateStore) for storeName, storeConfig := range m.storesConfig { - st := NewCertificateStore(m.ocspStapler) + st := NewCertificateStore() m.stores[storeName] = st if certs, ok := storesCertificates[storeName]; ok { @@ -189,17 +133,13 @@ func (m *Manager) UpdateConfigs(ctx context.Context, stores map[string]Store, co logger := log.Ctx(ctx).With().Str(logs.TLSStoreName, storeName).Logger() ctxStore := logger.WithContext(ctx) - certificate, err := m.getDefaultCertificate(ctxStore, storeConfig, st) + certificate, err := getDefaultCertificate(ctxStore, storeConfig, st) if err != nil { logger.Error().Err(err).Msg("Error while creating certificate store") } st.DefaultCertificate = certificate } - - if m.ocspStapler != nil { - m.ocspStapler.ForceStapleUpdates() - } } // sanitizeDomains sanitizes the domain definition Main and SANS, @@ -286,8 +226,7 @@ func (m *Manager) Get(storeName, configName string) (*tls.Config, error) { } log.Debug().Msgf("Serving default certificate for request: %q", domainToCheck) - - return store.GetDefaultCertificate(), nil + return store.DefaultCertificate, nil } return tlsConfig, err @@ -306,8 +245,8 @@ func (m *Manager) GetServerCertificates() []*x509.Certificate { // We iterate over all the certificates. if defaultStore.DynamicCerts != nil && defaultStore.DynamicCerts.Get() != nil { - for _, cert := range defaultStore.DynamicCerts.Get().(map[string]*CertificateData) { - x509Cert, err := x509.ParseCertificate(cert.Certificate.Certificate[0]) + for _, cert := range defaultStore.DynamicCerts.Get().(map[string]*tls.Certificate) { + x509Cert, err := x509.ParseCertificate(cert.Certificate[0]) if err != nil { continue } @@ -317,7 +256,7 @@ func (m *Manager) GetServerCertificates() []*x509.Certificate { } if defaultStore.DefaultCertificate != nil { - x509Cert, err := x509.ParseCertificate(defaultStore.DefaultCertificate.Certificate.Certificate[0]) + x509Cert, err := x509.ParseCertificate(defaultStore.DefaultCertificate.Certificate[0]) if err != nil { return certificates } @@ -350,9 +289,9 @@ func (m *Manager) GetStore(storeName string) *CertificateStore { return m.getStore(storeName) } -func (m *Manager) getDefaultCertificate(ctx context.Context, tlsStore Store, st *CertificateStore) (*CertificateData, error) { +func getDefaultCertificate(ctx context.Context, tlsStore Store, st *CertificateStore) (*tls.Certificate, error) { if tlsStore.DefaultCertificate != nil { - cert, err := m.buildDefaultCertificate(tlsStore.DefaultCertificate) + cert, err := buildDefaultCertificate(tlsStore.DefaultCertificate) if err != nil { return nil, err } @@ -365,65 +304,22 @@ func (m *Manager) getDefaultCertificate(ctx context.Context, tlsStore Store, st return nil, err } - defaultCertificate := &CertificateData{ - Certificate: defaultCert, - } - if tlsStore.DefaultGeneratedCert != nil && tlsStore.DefaultGeneratedCert.Domain != nil && tlsStore.DefaultGeneratedCert.Resolver != "" { domains, err := sanitizeDomains(*tlsStore.DefaultGeneratedCert.Domain) if err != nil { - return defaultCertificate, fmt.Errorf("falling back to the internal generated certificate because invalid domains: %w", err) + return defaultCert, fmt.Errorf("falling back to the internal generated certificate because invalid domains: %w", err) } defaultACMECert := st.GetCertificate(domains) if defaultACMECert == nil { - return defaultCertificate, fmt.Errorf("unable to find certificate for domains %q: falling back to the internal generated certificate", strings.Join(domains, ",")) + return defaultCert, fmt.Errorf("unable to find certificate for domains %q: falling back to the internal generated certificate", strings.Join(domains, ",")) } return defaultACMECert, nil } log.Ctx(ctx).Debug().Msg("No default certificate, fallback to the internal generated certificate") - return defaultCertificate, nil -} - -func (m *Manager) buildDefaultCertificate(defaultCertificate *Certificate) (*CertificateData, error) { - certFile, err := defaultCertificate.CertFile.Read() - if err != nil { - return nil, fmt.Errorf("failed to get cert file content: %w", err) - } - - keyFile, err := defaultCertificate.KeyFile.Read() - if err != nil { - return nil, fmt.Errorf("failed to get key file content: %w", err) - } - - cert, err := tls.X509KeyPair(certFile, keyFile) - if err != nil { - return nil, fmt.Errorf("failed to load X509 key pair: %w", err) - } - - var certHash string - if m.ocspStapler != nil && len(cert.Leaf.OCSPServer) > 0 { - certHash = hashRawCert(cert.Leaf.Raw) - - issuer := cert.Leaf - if len(cert.Certificate) > 1 { - issuer, err = x509.ParseCertificate(cert.Certificate[1]) - if err != nil { - return nil, fmt.Errorf("parsing issuer certificate %s: %w", defaultCertificate.GetTruncatedCertificateName(), err) - } - } - - if err := m.ocspStapler.Upsert(certHash, cert.Leaf, issuer); err != nil { - return nil, fmt.Errorf("upserting OCSP certificate %s: %w", defaultCertificate.GetTruncatedCertificateName(), err) - } - } - - return &CertificateData{ - Certificate: &cert, - Hash: certHash, - }, nil + return defaultCert, nil } // creates a TLS config that allows terminating HTTPS for multiple domains using SNI. @@ -516,10 +412,20 @@ func buildTLSConfig(tlsOption Options) (*tls.Config, error) { return conf, nil } -func hashRawCert(rawCert []byte) string { - hasher := fnv.New64() +func buildDefaultCertificate(defaultCertificate *Certificate) (*tls.Certificate, error) { + certFile, err := defaultCertificate.CertFile.Read() + if err != nil { + return nil, fmt.Errorf("failed to get cert file content: %w", err) + } - // purposely ignoring the error, as no error can be returned from the implementation. - _, _ = hasher.Write(rawCert) - return strconv.FormatUint(hasher.Sum64(), 16) + keyFile, err := defaultCertificate.KeyFile.Read() + if err != nil { + return nil, fmt.Errorf("failed to get key file content: %w", err) + } + + cert, err := tls.X509KeyPair(certFile, keyFile) + if err != nil { + return nil, fmt.Errorf("failed to load X509 key pair: %w", err) + } + return &cert, nil } diff --git a/pkg/tls/tlsmanager_test.go b/pkg/tls/tlsmanager_test.go index 1cee20276..f6df9c3c8 100644 --- a/pkg/tls/tlsmanager_test.go +++ b/pkg/tls/tlsmanager_test.go @@ -2,21 +2,14 @@ package tls import ( "context" - "crypto" "crypto/tls" "crypto/x509" "encoding/pem" - "io" - "net/http" - "net/http/httptest" "testing" - "time" - "github.com/patrickmn/go-cache" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/traefik/traefik/v3/pkg/types" - "golang.org/x/crypto/ocsp" ) // LocalhostCert is a PEM-encoded TLS cert with SAN IPs @@ -84,10 +77,10 @@ func TestTLSInStore(t *testing.T) { }, }} - tlsManager := NewManager(nil) - tlsManager.UpdateConfigs(t.Context(), nil, nil, dynamicConfigs) + tlsManager := NewManager() + tlsManager.UpdateConfigs(context.Background(), nil, nil, dynamicConfigs) - certs := tlsManager.GetStore("default").DynamicCerts.Get().(map[string]*CertificateData) + certs := tlsManager.GetStore("default").DynamicCerts.Get().(map[string]*tls.Certificate) if len(certs) == 0 { t.Fatal("got error: default store must have TLS certificates.") } @@ -101,8 +94,8 @@ func TestTLSInvalidStore(t *testing.T) { }, }} - tlsManager := NewManager(nil) - tlsManager.UpdateConfigs(t.Context(), + tlsManager := NewManager() + tlsManager.UpdateConfigs(context.Background(), map[string]Store{ "default": { DefaultCertificate: &Certificate{ @@ -112,7 +105,7 @@ func TestTLSInvalidStore(t *testing.T) { }, }, nil, dynamicConfigs) - certs := tlsManager.GetStore("default").DynamicCerts.Get().(map[string]*CertificateData) + certs := tlsManager.GetStore("default").DynamicCerts.Get().(map[string]*tls.Certificate) if len(certs) == 0 { t.Fatal("got error: default store must have TLS certificates.") } @@ -165,8 +158,8 @@ func TestManager_Get(t *testing.T) { }, } - tlsManager := NewManager(nil) - tlsManager.UpdateConfigs(t.Context(), nil, tlsConfigs, dynamicConfigs) + tlsManager := NewManager() + tlsManager.UpdateConfigs(context.Background(), nil, tlsConfigs, dynamicConfigs) for _, test := range testCases { t.Run(test.desc, func(t *testing.T) { @@ -304,8 +297,8 @@ func TestClientAuth(t *testing.T) { }, } - tlsManager := NewManager(nil) - tlsManager.UpdateConfigs(t.Context(), nil, tlsConfigs, nil) + tlsManager := NewManager() + tlsManager.UpdateConfigs(context.Background(), nil, tlsConfigs, nil) for _, test := range testCases { t.Run(test.desc, func(t *testing.T) { @@ -331,108 +324,8 @@ func TestClientAuth(t *testing.T) { } } -func TestManager_UpdateConfigs_OCSPConfig(t *testing.T) { - leafCert, err := tls.X509KeyPair([]byte(certWithOCSPServer), []byte(certKey)) - require.NoError(t, err) - - issuerCert, err := tls.X509KeyPair([]byte(caCert), []byte(caKey)) - require.NoError(t, err) - - thisUpdate, err := time.Parse("2006-01-02", "2025-01-01") - require.NoError(t, err) - nextUpdate, err := time.Parse("2006-01-02", "2025-01-02") - require.NoError(t, err) - - ocspResponseTmpl := ocsp.Response{ - SerialNumber: leafCert.Leaf.SerialNumber, - TBSResponseData: []byte("foo"), - ThisUpdate: thisUpdate, - NextUpdate: nextUpdate, - } - - ocspResponse, err := ocsp.CreateResponse(leafCert.Leaf, leafCert.Leaf, ocspResponseTmpl, issuerCert.PrivateKey.(crypto.Signer)) - require.NoError(t, err) - - responderCall := make(chan struct{}) - - handler := func(rw http.ResponseWriter, req *http.Request) { - ct := req.Header.Get("Content-Type") - assert.Equal(t, "application/ocsp-request", ct) - - reqBytes, err := io.ReadAll(req.Body) - require.NoError(t, err) - - _, err = ocsp.ParseRequest(reqBytes) - require.NoError(t, err) - - rw.Header().Set("Content-Type", "application/ocsp-response") - - _, err = rw.Write(ocspResponse) - require.NoError(t, err) - - responderCall <- struct{}{} - } - - responder := httptest.NewServer(http.HandlerFunc(handler)) - t.Cleanup(responder.Close) - - testContext, cancel := context.WithCancel(t.Context()) - t.Cleanup(cancel) - - tlsManager := NewManager(&OCSPConfig{ - ResponderOverrides: map[string]string{ - "ocsp.example.com": responder.URL, - }, - }) - - go tlsManager.Run(testContext) - - tlsManager.ocspStapler.cache.Set("existing", &ocspEntry{ - leaf: leafCert.Leaf, - issuer: issuerCert.Leaf, - staple: []byte("foo"), - nextUpdate: time.Now().Add(time.Hour), - }, cache.NoExpiration) - tlsManager.ocspStapler.cache.Set("existingWithTTL", &ocspEntry{ - leaf: leafCert.Leaf, - issuer: issuerCert.Leaf, - staple: []byte("foo"), - nextUpdate: time.Now().Add(time.Hour), - }, 2*defaultCacheDuration) - - tlsManager.UpdateConfigs(testContext, nil, nil, []*CertAndStores{ - { - Certificate: Certificate{ - CertFile: certWithOCSPServer, - KeyFile: certKey, - }, - }, - }) - - // Asserting that UpdateConfigs resets the expiration for existing entries. - _, expiration, ok := tlsManager.ocspStapler.cache.GetWithExpiration("existing") - require.True(t, ok) - assert.Greater(t, expiration, time.Now()) - // But not for entries with TTL already set. - _, expiration, ok = tlsManager.ocspStapler.cache.GetWithExpiration("existingWithTTL") - require.True(t, ok) - assert.Greater(t, expiration, time.Now().Add(defaultCacheDuration)) - - select { - case <-responderCall: - case <-time.After(3 * time.Second): - t.Fatal("Timeout waiting for OCSP responder call") - } - - assert.Len(t, tlsManager.ocspStapler.cache.Items(), 3) - - certHash := hashRawCert(leafCert.Leaf.Raw) - _, ok = tlsManager.ocspStapler.cache.Get(certHash) - require.True(t, ok) -} - func TestManager_Get_DefaultValues(t *testing.T) { - tlsManager := NewManager(nil) + tlsManager := NewManager() // Ensures we won't break things for Traefik users when updating Go config, _ := tlsManager.Get("default", "default") diff --git a/pkg/observability/tracing/tracing.go b/pkg/tracing/tracing.go similarity index 89% rename from pkg/observability/tracing/tracing.go rename to pkg/tracing/tracing.go index bb38bc654..76a3d3302 100644 --- a/pkg/observability/tracing/tracing.go +++ b/pkg/tracing/tracing.go @@ -13,24 +13,23 @@ import ( "github.com/rs/zerolog/log" "github.com/traefik/traefik/v3/pkg/config/static" - "github.com/traefik/traefik/v3/pkg/observability" - otypes "github.com/traefik/traefik/v3/pkg/observability/types" + "github.com/traefik/traefik/v3/pkg/types" "go.opentelemetry.io/contrib/propagators/autoprop" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" "go.opentelemetry.io/otel/propagation" - semconv "go.opentelemetry.io/otel/semconv/v1.37.0" + semconv "go.opentelemetry.io/otel/semconv/v1.26.0" "go.opentelemetry.io/otel/trace" ) // Backend is an abstraction for tracking backend (OpenTelemetry, ...). type Backend interface { - Setup(ctx context.Context, serviceName string, sampleRate float64, resourceAttributes map[string]string) (trace.Tracer, io.Closer, error) + Setup(serviceName string, sampleRate float64, resourceAttributes map[string]string) (trace.Tracer, io.Closer, error) } // NewTracing Creates a Tracing. -func NewTracing(ctx context.Context, conf *static.Tracing) (*Tracer, io.Closer, error) { +func NewTracing(conf *static.Tracing) (*Tracer, io.Closer, error) { var backend Backend if conf.OTLP != nil { @@ -39,17 +38,13 @@ func NewTracing(ctx context.Context, conf *static.Tracing) (*Tracer, io.Closer, if backend == nil { log.Debug().Msg("Could not initialize tracing, using OpenTelemetry by default") - defaultBackend := &otypes.OTelTracing{} + defaultBackend := &types.OTelTracing{} backend = defaultBackend } - if err := observability.EnsureUserEnvVar(); err != nil { - return nil, nil, err - } - otel.SetTextMapPropagator(autoprop.NewTextMapPropagator()) - tr, closer, err := backend.Setup(ctx, conf.ServiceName, conf.SampleRate, conf.ResourceAttributes) + tr, closer, err := backend.Setup(conf.ServiceName, conf.SampleRate, conf.ResourceAttributes) if err != nil { return nil, nil, err } @@ -89,6 +84,13 @@ func InjectContextIntoCarrier(req *http.Request) { propagator.Inject(req.Context(), propagation.HeaderCarrier(req.Header)) } +// SetStatusErrorf flags the span as in error and log an event. +func SetStatusErrorf(ctx context.Context, format string, args ...interface{}) { + if span := trace.SpanFromContext(ctx); span != nil { + span.SetStatus(codes.Error, fmt.Sprintf(format, args...)) + } +} + // Span is trace.Span wrapping the Traefik TracerProvider. type Span struct { trace.Span @@ -132,8 +134,8 @@ func NewTracer(tracer trace.Tracer, capturedRequestHeaders, capturedResponseHead return &Tracer{ Tracer: tracer, safeQueryParams: safeQueryParams, - capturedRequestHeaders: canonicalizeHeaders(capturedRequestHeaders), - capturedResponseHeaders: canonicalizeHeaders(capturedResponseHeaders), + capturedRequestHeaders: capturedRequestHeaders, + capturedResponseHeaders: capturedResponseHeaders, } } @@ -351,18 +353,3 @@ func defaultStatus(code int) (codes.Code, string) { } return codes.Unset, "" } - -// canonicalizeHeaders converts a slice of header keys to their canonical form. -// It uses http.CanonicalHeaderKey to ensure that the headers are in a consistent format. -func canonicalizeHeaders(headers []string) []string { - if headers == nil { - return nil - } - - canonicalHeaders := make([]string, len(headers)) - for i, header := range headers { - canonicalHeaders[i] = http.CanonicalHeaderKey(header) - } - - return canonicalHeaders -} diff --git a/pkg/observability/tracing/tracing_test.go b/pkg/tracing/tracing_test.go similarity index 61% rename from pkg/observability/tracing/tracing_test.go rename to pkg/tracing/tracing_test.go index 91dc19e60..b4d92dca8 100644 --- a/pkg/observability/tracing/tracing_test.go +++ b/pkg/tracing/tracing_test.go @@ -2,6 +2,8 @@ package tracing import ( "compress/gzip" + "context" + "encoding/json" "io" "net/http" "net/http/httptest" @@ -13,12 +15,9 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/traefik/traefik/v3/pkg/config/static" - otypes "github.com/traefik/traefik/v3/pkg/observability/types" - "go.opentelemetry.io/collector/pdata/pcommon" - "go.opentelemetry.io/collector/pdata/ptrace" + "github.com/traefik/traefik/v3/pkg/types" "go.opentelemetry.io/collector/pdata/ptrace/ptraceotlp" "go.opentelemetry.io/otel/trace" - "go.opentelemetry.io/otel/trace/noop" ) func Test_safeFullURL(t *testing.T) { @@ -77,16 +76,15 @@ func TestTracing(t *testing.T) { headers map[string]string resourceAttributes map[string]string wantServiceHeadersFn func(t *testing.T, headers http.Header) - assertFn func(*testing.T, ptrace.Traces) + assertFn func(*testing.T, string) }{ { desc: "service name and version", - assertFn: func(t *testing.T, traces ptrace.Traces) { + assertFn: func(t *testing.T, trace string) { t.Helper() - attributes := resourceAttributes(traces) - assert.Equal(t, "traefik", attributes["service.name"]) - assert.Equal(t, "dev", attributes["service.version"]) + assert.Regexp(t, `({"key":"service.name","value":{"stringValue":"traefik"}})`, trace) + assert.Regexp(t, `({"key":"service.version","value":{"stringValue":"dev"}})`, trace) }, }, { @@ -94,11 +92,10 @@ func TestTracing(t *testing.T) { resourceAttributes: map[string]string{ "service.environment": "custom", }, - assertFn: func(t *testing.T, traces ptrace.Traces) { + assertFn: func(t *testing.T, trace string) { t.Helper() - attributes := resourceAttributes(traces) - assert.Equal(t, "custom", attributes["service.environment"]) + assert.Regexp(t, `({"key":"service.environment","value":{"stringValue":"custom"}})`, trace) }, }, { @@ -114,13 +111,12 @@ func TestTracing(t *testing.T) { assert.Regexp(t, `(00-00000000000000000000000000000001-\w{16}-01)`, headers["Traceparent"][0]) assert.Equal(t, []string{"foo=bar"}, headers["Tracestate"]) }, - assertFn: func(t *testing.T, traces ptrace.Traces) { + assertFn: func(t *testing.T, trace string) { t.Helper() - span := mainSpan(traces) - assert.Equal(t, "00000000000000000000000000000001", span.TraceID().String()) - assert.Equal(t, "0000000000000001", span.ParentSpanID().String()) - assert.Equal(t, "foo=bar", span.TraceState().AsRaw()) + assert.Regexp(t, `("traceId":"00000000000000000000000000000001")`, trace) + assert.Regexp(t, `("parentSpanId":"0000000000000001")`, trace) + assert.Regexp(t, `("traceState":"foo=bar")`, trace) }, }, { @@ -131,12 +127,11 @@ func TestTracing(t *testing.T) { assert.Regexp(t, `(00-\w{32}-\w{16}-01)`, headers["Traceparent"][0]) }, - assertFn: func(t *testing.T, traces ptrace.Traces) { + assertFn: func(t *testing.T, trace string) { t.Helper() - span := mainSpan(traces) - assert.Len(t, span.TraceID().String(), 32) - assert.Empty(t, span.ParentSpanID().String()) + assert.Regexp(t, `("traceId":"\w{32}")`, trace) + assert.Regexp(t, `("parentSpanId":"\w{16}")`, trace) }, }, { @@ -150,12 +145,11 @@ func TestTracing(t *testing.T) { assert.Regexp(t, `(00000000000000000000000000000001-\w{16}-1)`, headers["B3"][0]) }, - assertFn: func(t *testing.T, traces ptrace.Traces) { + assertFn: func(t *testing.T, trace string) { t.Helper() - span := mainSpan(traces) - assert.Equal(t, "00000000000000000000000000000001", span.TraceID().String()) - assert.Equal(t, "0000000000000002", span.ParentSpanID().String()) + assert.Regexp(t, `("traceId":"00000000000000000000000000000001")`, trace) + assert.Regexp(t, `("parentSpanId":"0000000000000002")`, trace) }, }, { @@ -166,12 +160,11 @@ func TestTracing(t *testing.T) { assert.Regexp(t, `(\w{32}-\w{16}-1)`, headers["B3"][0]) }, - assertFn: func(t *testing.T, traces ptrace.Traces) { + assertFn: func(t *testing.T, trace string) { t.Helper() - span := mainSpan(traces) - assert.Len(t, span.TraceID().String(), 32) - assert.Empty(t, span.ParentSpanID().String()) + assert.Regexp(t, `("traceId":"\w{32}")`, trace) + assert.Regexp(t, `("parentSpanId":"\w{16}")`, trace) }, }, { @@ -191,12 +184,11 @@ func TestTracing(t *testing.T) { assert.Equal(t, "1", headers["X-B3-Sampled"][0]) assert.Len(t, headers["X-B3-Spanid"][0], 16) }, - assertFn: func(t *testing.T, traces ptrace.Traces) { + assertFn: func(t *testing.T, trace string) { t.Helper() - span := mainSpan(traces) - assert.Equal(t, "00000000000000000000000000000001", span.TraceID().String()) - assert.Equal(t, "0000000000000002", span.ParentSpanID().String()) + assert.Regexp(t, `("traceId":"00000000000000000000000000000001")`, trace) + assert.Regexp(t, `("parentSpanId":"0000000000000002")`, trace) }, }, { @@ -209,12 +201,11 @@ func TestTracing(t *testing.T) { assert.Equal(t, "1", headers["X-B3-Sampled"][0]) assert.Regexp(t, `(\w{16})`, headers["X-B3-Spanid"][0]) }, - assertFn: func(t *testing.T, traces ptrace.Traces) { + assertFn: func(t *testing.T, trace string) { t.Helper() - span := mainSpan(traces) - assert.Len(t, span.TraceID().String(), 32) - assert.Empty(t, span.ParentSpanID().String()) + assert.Regexp(t, `("traceId":"\w{32}")`, trace) + assert.Regexp(t, `("parentSpanId":"")`, trace) }, }, { @@ -240,12 +231,11 @@ func TestTracing(t *testing.T) { assert.Regexp(t, `(00000000000000000000000000000001:\w{16}:0:1)`, headers["Uber-Trace-Id"][0]) }, - assertFn: func(t *testing.T, traces ptrace.Traces) { + assertFn: func(t *testing.T, trace string) { t.Helper() - span := mainSpan(traces) - assert.Equal(t, "00000000000000000000000000000001", span.TraceID().String()) - assert.Len(t, span.ParentSpanID().String(), 16) + assert.Regexp(t, `("traceId":"00000000000000000000000000000001")`, trace) + assert.Regexp(t, `("parentSpanId":"\w{16}")`, trace) }, }, { @@ -256,12 +246,11 @@ func TestTracing(t *testing.T) { assert.Regexp(t, `(\w{32}:\w{16}:0:1)`, headers["Uber-Trace-Id"][0]) }, - assertFn: func(t *testing.T, traces ptrace.Traces) { + assertFn: func(t *testing.T, trace string) { t.Helper() - span := mainSpan(traces) - assert.Len(t, span.TraceID().String(), 32) - assert.Empty(t, span.ParentSpanID().String()) + assert.Regexp(t, `("traceId":"\w{32}")`, trace) + assert.Regexp(t, `("parentSpanId":"\w{16}")`, trace) }, }, { @@ -275,12 +264,11 @@ func TestTracing(t *testing.T) { assert.Regexp(t, `(Root=1-5759e988-bd862e3fe1be46a994272793;Parent=\w{16};Sampled=1)`, headers["X-Amzn-Trace-Id"][0]) }, - assertFn: func(t *testing.T, traces ptrace.Traces) { + assertFn: func(t *testing.T, trace string) { t.Helper() - span := mainSpan(traces) - assert.Equal(t, "5759e988bd862e3fe1be46a994272793", span.TraceID().String()) - assert.Len(t, span.ParentSpanID().String(), 16) + assert.Regexp(t, `("traceId":"5759e988bd862e3fe1be46a994272793")`, trace) + assert.Regexp(t, `("parentSpanId":"\w{16}")`, trace) }, }, { @@ -291,12 +279,11 @@ func TestTracing(t *testing.T) { assert.Regexp(t, `(Root=1-\w{8}-\w{24};Parent=\w{16};Sampled=1)`, headers["X-Amzn-Trace-Id"][0]) }, - assertFn: func(t *testing.T, traces ptrace.Traces) { + assertFn: func(t *testing.T, trace string) { t.Helper() - span := mainSpan(traces) - assert.Len(t, span.TraceID().String(), 32) - assert.Empty(t, span.ParentSpanID().String()) + assert.Regexp(t, `("traceId":"\w{32}")`, trace) + assert.Regexp(t, `("parentSpanId":"\w{16}")`, trace) }, }, { @@ -307,17 +294,16 @@ func TestTracing(t *testing.T) { assert.Empty(t, headers) }, - assertFn: func(t *testing.T, traces ptrace.Traces) { + assertFn: func(t *testing.T, trace string) { t.Helper() - span := mainSpan(traces) - assert.Len(t, span.TraceID().String(), 32) - assert.Empty(t, span.ParentSpanID().String()) + assert.Regexp(t, `("traceId":"\w{32}")`, trace) + assert.Regexp(t, `("parentSpanId":"\w{16}")`, trace) }, }, } - traceCh := make(chan ptrace.Traces) + traceCh := make(chan string) collector := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { gzr, err := gzip.NewReader(r.Body) require.NoError(t, err) @@ -329,7 +315,10 @@ func TestTracing(t *testing.T) { err = req.UnmarshalProto(body) require.NoError(t, err) - traceCh <- req.Traces() + marshalledReq, err := json.Marshal(req) + require.NoError(t, err) + + traceCh <- string(marshalledReq) })) t.Cleanup(collector.Close) @@ -355,14 +344,14 @@ func TestTracing(t *testing.T) { ServiceName: "traefik", SampleRate: 1.0, ResourceAttributes: test.resourceAttributes, - OTLP: &otypes.OTelTracing{ - HTTP: &otypes.OTelHTTP{ + OTLP: &types.OTelTracing{ + HTTP: &types.OTelHTTP{ Endpoint: collector.URL, }, }, } - tracer, closer, err := NewTracing(t.Context(), tracingConfig) + tracer, closer, err := NewTracing(tracingConfig) require.NoError(t, err) t.Cleanup(func() { _ = closer.Close() @@ -395,10 +384,10 @@ func TestTracing(t *testing.T) { case <-time.After(10 * time.Second): t.Error("Trace not exported") - case traces := <-traceCh: + case trace := <-traceCh: assert.Equal(t, http.StatusOK, rw.Code) if test.assertFn != nil { - test.assertFn(t, traces) + test.assertFn(t, trace) } } }) @@ -410,95 +399,19 @@ func TestTracing(t *testing.T) { func TestTracerProvider(t *testing.T) { t.Parallel() - otlpConfig := &otypes.OTelTracing{} + otlpConfig := &types.OTelTracing{} otlpConfig.SetDefaults() config := &static.Tracing{OTLP: otlpConfig} - tracer, closer, err := NewTracing(t.Context(), config) + tracer, closer, err := NewTracing(config) if err != nil { t.Fatal(err) } closer.Close() - _, span := tracer.Start(t.Context(), "test") + _, span := tracer.Start(context.Background(), "test") defer span.End() span.TracerProvider().Tracer("github.com/traefik/traefik") span.TracerProvider().Tracer("other") } - -// TestNewTracer_HeadersCanonicalization tests that NewTracer properly canonicalizes headers. -func TestNewTracer_HeadersCanonicalization(t *testing.T) { - testCases := []struct { - desc string - inputHeaders []string - expectedCanonicalHeaders []string - }{ - { - desc: "Empty headers", - inputHeaders: []string{}, - expectedCanonicalHeaders: []string{}, - }, - { - desc: "Already canonical headers", - inputHeaders: []string{"Content-Type", "User-Agent", "Accept-Encoding"}, - expectedCanonicalHeaders: []string{"Content-Type", "User-Agent", "Accept-Encoding"}, - }, - { - desc: "Lowercase headers", - inputHeaders: []string{"content-type", "user-agent", "accept-encoding"}, - expectedCanonicalHeaders: []string{"Content-Type", "User-Agent", "Accept-Encoding"}, - }, - { - desc: "Mixed case headers", - inputHeaders: []string{"CoNtEnT-tYpE", "uSeR-aGeNt", "aCcEpT-eNcOdInG"}, - expectedCanonicalHeaders: []string{"Content-Type", "User-Agent", "Accept-Encoding"}, - }, - } - - for _, test := range testCases { - t.Run(test.desc, func(t *testing.T) { - t.Parallel() - - // Create a mock tracer using a no-op tracer from OpenTelemetry - mockTracer := noop.NewTracerProvider().Tracer("test") - - // Test capturedRequestHeaders - tracer := NewTracer(mockTracer, test.inputHeaders, nil, nil) - assert.Equal(t, test.expectedCanonicalHeaders, tracer.capturedRequestHeaders) - assert.Nil(t, tracer.capturedResponseHeaders) - - // Test capturedResponseHeaders - tracer = NewTracer(mockTracer, nil, test.inputHeaders, nil) - assert.Equal(t, test.expectedCanonicalHeaders, tracer.capturedResponseHeaders) - assert.Nil(t, tracer.capturedRequestHeaders) - }) - } -} - -// resourceAttributes extracts resource attributes as a map. -func resourceAttributes(traces ptrace.Traces) map[string]string { - attributes := make(map[string]string) - if traces.ResourceSpans().Len() > 0 { - resource := traces.ResourceSpans().At(0).Resource() - resource.Attributes().Range(func(k string, v pcommon.Value) bool { - if v.Type() == pcommon.ValueTypeStr { - attributes[k] = v.Str() - } - return true - }) - } - return attributes -} - -// mainSpan gets the main span from traces (assumes single span for testing). -func mainSpan(traces ptrace.Traces) ptrace.Span { - for _, resourceSpans := range traces.ResourceSpans().All() { - for _, scopeSpans := range resourceSpans.ScopeSpans().All() { - if scopeSpans.Spans().Len() > 0 { - return scopeSpans.Spans().At(0) - } - } - } - return ptrace.NewSpan() -} diff --git a/pkg/types/k8sdetector.go b/pkg/types/k8sdetector.go deleted file mode 100644 index 3031783ee..000000000 --- a/pkg/types/k8sdetector.go +++ /dev/null @@ -1,70 +0,0 @@ -package types - -import ( - "context" - "errors" - "fmt" - "os" - "strings" - - "github.com/rs/zerolog/log" - "go.opentelemetry.io/otel/sdk/resource" - semconv "go.opentelemetry.io/otel/semconv/v1.37.0" - kerror "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - kclientset "k8s.io/client-go/kubernetes" - "k8s.io/client-go/rest" -) - -// K8sAttributesDetector detects the metadata of the Traefik pod running in a Kubernetes cluster. -// It reads the pod name from the hostname file and the namespace from the service account namespace file and queries the Kubernetes API to get the pod's UID. -type K8sAttributesDetector struct{} - -func (K8sAttributesDetector) Detect(ctx context.Context) (*resource.Resource, error) { - attrs := os.Getenv("OTEL_RESOURCE_ATTRIBUTES") - if strings.Contains(attrs, string(semconv.K8SPodNameKey)) || strings.Contains(attrs, string(semconv.K8SPodUIDKey)) { - return resource.Empty(), nil - } - - // The InClusterConfig function returns a config for the Kubernetes API server - // when it is running inside a Kubernetes cluster. - config, err := rest.InClusterConfig() - if err != nil && errors.Is(err, rest.ErrNotInCluster) { - return resource.Empty(), nil - } - if err != nil { - return nil, fmt.Errorf("creating in cluster config: %w", err) - } - - client, err := kclientset.NewForConfig(config) - if err != nil { - return nil, fmt.Errorf("creating Kubernetes client: %w", err) - } - - podName, err := os.Hostname() - if err != nil { - return nil, fmt.Errorf("getting pod name: %w", err) - } - - podNamespaceBytes, err := os.ReadFile("/var/run/secrets/kubernetes.io/serviceaccount/namespace") - if err != nil { - return nil, fmt.Errorf("getting pod namespace: %w", err) - } - podNamespace := string(podNamespaceBytes) - - pod, err := client.CoreV1().Pods(podNamespace).Get(ctx, podName, metav1.GetOptions{}) - if err != nil && (kerror.IsForbidden(err) || kerror.IsNotFound(err)) { - log.Error().Err(err).Msg("Unable to build K8s resource attributes for Traefik pod") - return resource.Empty(), nil - } - if err != nil { - return nil, fmt.Errorf("getting pod metadata: %w", err) - } - - // To avoid version conflicts with other detectors, we use a Schemaless resource. - return resource.NewSchemaless( - semconv.K8SPodUID(string(pod.UID)), - semconv.K8SPodName(pod.Name), - semconv.K8SNamespaceName(podNamespace), - ), nil -} diff --git a/pkg/observability/types/logs.go b/pkg/types/logs.go similarity index 90% rename from pkg/observability/types/logs.go rename to pkg/types/logs.go index 8bc108ddc..87aef4e40 100644 --- a/pkg/observability/types/logs.go +++ b/pkg/types/logs.go @@ -7,14 +7,13 @@ import ( "net/url" "github.com/traefik/paerser/types" - ttypes "github.com/traefik/traefik/v3/pkg/types" "github.com/traefik/traefik/v3/pkg/version" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc" "go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp" otelsdk "go.opentelemetry.io/otel/sdk/log" "go.opentelemetry.io/otel/sdk/resource" - semconv "go.opentelemetry.io/otel/semconv/v1.37.0" + semconv "go.opentelemetry.io/otel/semconv/v1.27.0" "google.golang.org/grpc/credentials" "google.golang.org/grpc/encoding/gzip" ) @@ -59,7 +58,7 @@ func (l *TraefikLog) SetDefaults() { // AccessLog holds the configuration settings for the access logger (middlewares/accesslog). type AccessLog struct { FilePath string `description:"Access log file path. Stdout is used when omitted or empty." json:"filePath,omitempty" toml:"filePath,omitempty" yaml:"filePath,omitempty"` - Format string `description:"Access log format: json, common, or genericCLF" json:"format,omitempty" toml:"format,omitempty" yaml:"format,omitempty" export:"true"` + Format string `description:"Access log format: json | common" json:"format,omitempty" toml:"format,omitempty" yaml:"format,omitempty" export:"true"` Filters *AccessLogFilters `description:"Access log filters, used to keep only specific access logs." json:"filters,omitempty" toml:"filters,omitempty" yaml:"filters,omitempty" export:"true"` Fields *AccessLogFields `description:"AccessLogFields." json:"fields,omitempty" toml:"fields,omitempty" yaml:"fields,omitempty" export:"true"` BufferingSize int64 `description:"Number of access log lines to process in a buffered way." json:"bufferingSize,omitempty" toml:"bufferingSize,omitempty" yaml:"bufferingSize,omitempty" export:"true"` @@ -151,7 +150,7 @@ func checkFieldHeaderValue(value, defaultValue string) string { // OTelLog provides configuration settings for the open-telemetry logger. type OTelLog struct { - ServiceName string `description:"Defines the service name resource attribute." json:"serviceName,omitempty" toml:"serviceName,omitempty" yaml:"serviceName,omitempty" export:"true"` + ServiceName string `description:"Set the name for this service." json:"serviceName,omitempty" toml:"serviceName,omitempty" yaml:"serviceName,omitempty" export:"true"` ResourceAttributes map[string]string `description:"Defines additional resource attributes (key:value)." json:"resourceAttributes,omitempty" toml:"resourceAttributes,omitempty" yaml:"resourceAttributes,omitempty"` GRPC *OTelGRPC `description:"gRPC configuration for the OpenTelemetry collector." json:"grpc,omitempty" toml:"grpc,omitempty" yaml:"grpc,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` HTTP *OTelHTTP `description:"HTTP configuration for the OpenTelemetry collector." json:"http,omitempty" toml:"http,omitempty" yaml:"http,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` @@ -165,7 +164,7 @@ func (o *OTelLog) SetDefaults() { } // NewLoggerProvider creates a new OpenTelemetry logger provider. -func (o *OTelLog) NewLoggerProvider(ctx context.Context) (*otelsdk.LoggerProvider, error) { +func (o *OTelLog) NewLoggerProvider() (*otelsdk.LoggerProvider, error) { var ( err error exporter otelsdk.Exporter @@ -179,27 +178,21 @@ func (o *OTelLog) NewLoggerProvider(ctx context.Context) (*otelsdk.LoggerProvide return nil, fmt.Errorf("setting up exporter: %w", err) } - var resAttrs []attribute.KeyValue - for k, v := range o.ResourceAttributes { - resAttrs = append(resAttrs, attribute.String(k, v)) + attr := []attribute.KeyValue{ + semconv.ServiceNameKey.String(o.ServiceName), + semconv.ServiceVersionKey.String(version.Version), } - res, err := resource.New(ctx, - resource.WithContainer(), - resource.WithHost(), - resource.WithOS(), - resource.WithProcess(), - resource.WithTelemetrySDK(), - resource.WithDetectors(ttypes.K8sAttributesDetector{}), - // The following order allows the user to override the service name and version, - // as well as any other attributes set by the above detectors. - resource.WithAttributes( - semconv.ServiceName(o.ServiceName), - semconv.ServiceVersion(version.Version), - ), - resource.WithAttributes(resAttrs...), - // Use the environment variables to allow overriding above resource attributes. + for k, v := range o.ResourceAttributes { + attr = append(attr, attribute.String(k, v)) + } + + res, err := resource.New(context.Background(), + resource.WithAttributes(attr...), resource.WithFromEnv(), + resource.WithTelemetrySDK(), + resource.WithOSType(), + resource.WithProcessCommandArgs(), ) if err != nil { return nil, fmt.Errorf("building resource: %w", err) diff --git a/pkg/observability/types/metrics.go b/pkg/types/metrics.go similarity index 86% rename from pkg/observability/types/metrics.go rename to pkg/types/metrics.go index 331544513..8ca8dec68 100644 --- a/pkg/observability/types/metrics.go +++ b/pkg/types/metrics.go @@ -111,13 +111,12 @@ type OTLP struct { GRPC *OTelGRPC `description:"gRPC configuration for the OpenTelemetry collector." json:"grpc,omitempty" toml:"grpc,omitempty" yaml:"grpc,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` HTTP *OTelHTTP `description:"HTTP configuration for the OpenTelemetry collector." json:"http,omitempty" toml:"http,omitempty" yaml:"http,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` - AddEntryPointsLabels bool `description:"Enable metrics on entry points." json:"addEntryPointsLabels,omitempty" toml:"addEntryPointsLabels,omitempty" yaml:"addEntryPointsLabels,omitempty" export:"true"` - AddRoutersLabels bool `description:"Enable metrics on routers." json:"addRoutersLabels,omitempty" toml:"addRoutersLabels,omitempty" yaml:"addRoutersLabels,omitempty" export:"true"` - AddServicesLabels bool `description:"Enable metrics on services." json:"addServicesLabels,omitempty" toml:"addServicesLabels,omitempty" yaml:"addServicesLabels,omitempty" export:"true"` - ExplicitBoundaries []float64 `description:"Boundaries for latency metrics." json:"explicitBoundaries,omitempty" toml:"explicitBoundaries,omitempty" yaml:"explicitBoundaries,omitempty" export:"true"` - PushInterval types.Duration `description:"Period between calls to collect a checkpoint." json:"pushInterval,omitempty" toml:"pushInterval,omitempty" yaml:"pushInterval,omitempty" export:"true"` - ServiceName string `description:"Defines the service name resource attribute." json:"serviceName,omitempty" toml:"serviceName,omitempty" yaml:"serviceName,omitempty" export:"true"` - ResourceAttributes map[string]string `description:"Defines additional resource attributes (key:value)." json:"resourceAttributes,omitempty" toml:"resourceAttributes,omitempty" yaml:"resourceAttributes,omitempty" export:"true"` + AddEntryPointsLabels bool `description:"Enable metrics on entry points." json:"addEntryPointsLabels,omitempty" toml:"addEntryPointsLabels,omitempty" yaml:"addEntryPointsLabels,omitempty" export:"true"` + AddRoutersLabels bool `description:"Enable metrics on routers." json:"addRoutersLabels,omitempty" toml:"addRoutersLabels,omitempty" yaml:"addRoutersLabels,omitempty" export:"true"` + AddServicesLabels bool `description:"Enable metrics on services." json:"addServicesLabels,omitempty" toml:"addServicesLabels,omitempty" yaml:"addServicesLabels,omitempty" export:"true"` + ExplicitBoundaries []float64 `description:"Boundaries for latency metrics." json:"explicitBoundaries,omitempty" toml:"explicitBoundaries,omitempty" yaml:"explicitBoundaries,omitempty" export:"true"` + PushInterval types.Duration `description:"Period between calls to collect a checkpoint." json:"pushInterval,omitempty" toml:"pushInterval,omitempty" yaml:"pushInterval,omitempty" export:"true"` + ServiceName string `description:"OTEL service name to use." json:"serviceName,omitempty" toml:"serviceName,omitempty" yaml:"serviceName,omitempty" export:"true"` } // SetDefaults sets the default values. diff --git a/pkg/observability/types/otel.go b/pkg/types/otel.go similarity index 89% rename from pkg/observability/types/otel.go rename to pkg/types/otel.go index 7e6f9cf28..281d83e1d 100644 --- a/pkg/observability/types/otel.go +++ b/pkg/types/otel.go @@ -1,12 +1,10 @@ package types -import "github.com/traefik/traefik/v3/pkg/types" - // OTelGRPC provides configuration settings for the gRPC open-telemetry. type OTelGRPC struct { Endpoint string `description:"Sets the gRPC endpoint (host:port) of the collector." json:"endpoint,omitempty" toml:"endpoint,omitempty" yaml:"endpoint,omitempty"` Insecure bool `description:"Disables client transport security for the exporter." json:"insecure,omitempty" toml:"insecure,omitempty" yaml:"insecure,omitempty" export:"true"` - TLS *types.ClientTLS `description:"Defines client transport security parameters." json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" export:"true"` + TLS *ClientTLS `description:"Defines client transport security parameters." json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" export:"true"` Headers map[string]string `description:"Headers sent with payload." json:"headers,omitempty" toml:"headers,omitempty" yaml:"headers,omitempty"` } @@ -18,7 +16,7 @@ func (o *OTelGRPC) SetDefaults() { // OTelHTTP provides configuration settings for the HTTP open-telemetry. type OTelHTTP struct { Endpoint string `description:"Sets the HTTP endpoint (scheme://host:port/path) of the collector." json:"endpoint,omitempty" toml:"endpoint,omitempty" yaml:"endpoint,omitempty"` - TLS *types.ClientTLS `description:"Defines client transport security parameters." json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" export:"true"` + TLS *ClientTLS `description:"Defines client transport security parameters." json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" export:"true"` Headers map[string]string `description:"Headers sent with payload." json:"headers,omitempty" toml:"headers,omitempty" yaml:"headers,omitempty"` } diff --git a/pkg/types/tls_test.go b/pkg/types/tls_test.go index 615267644..b123aba53 100644 --- a/pkg/types/tls_test.go +++ b/pkg/types/tls_test.go @@ -1,6 +1,7 @@ package types import ( + "context" "testing" "github.com/stretchr/testify/assert" @@ -104,7 +105,7 @@ func TestClientTLS_CreateTLSConfig(t *testing.T) { for _, test := range tests { t.Run(test.desc, func(t *testing.T) { - tlsConfig, err := test.clientTLS.CreateTLSConfig(t.Context()) + tlsConfig, err := test.clientTLS.CreateTLSConfig(context.Background()) if test.wantErr { require.Error(t, err) return diff --git a/pkg/observability/types/tracing.go b/pkg/types/tracing.go similarity index 77% rename from pkg/observability/types/tracing.go rename to pkg/types/tracing.go index 9f525f9c2..c232ad37a 100644 --- a/pkg/observability/types/tracing.go +++ b/pkg/types/tracing.go @@ -9,7 +9,6 @@ import ( "time" "github.com/rs/zerolog/log" - ttypes "github.com/traefik/traefik/v3/pkg/types" "github.com/traefik/traefik/v3/pkg/version" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" @@ -18,28 +17,12 @@ import ( "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp" "go.opentelemetry.io/otel/sdk/resource" sdktrace "go.opentelemetry.io/otel/sdk/trace" - semconv "go.opentelemetry.io/otel/semconv/v1.37.0" + semconv "go.opentelemetry.io/otel/semconv/v1.27.0" "go.opentelemetry.io/otel/trace" "google.golang.org/grpc/credentials" "google.golang.org/grpc/encoding/gzip" ) -type TracingVerbosity string - -const ( - MinimalVerbosity TracingVerbosity = "minimal" - DetailedVerbosity TracingVerbosity = "detailed" -) - -func (v TracingVerbosity) Allows(verbosity TracingVerbosity) bool { - switch v { - case DetailedVerbosity: - return verbosity == DetailedVerbosity || verbosity == MinimalVerbosity - default: - return verbosity == MinimalVerbosity - } -} - // OTelTracing provides configuration settings for the open-telemetry tracer. type OTelTracing struct { GRPC *OTelGRPC `description:"gRPC configuration for the OpenTelemetry collector." json:"grpc,omitempty" toml:"grpc,omitempty" yaml:"grpc,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` @@ -53,7 +36,7 @@ func (c *OTelTracing) SetDefaults() { } // Setup sets up the tracer. -func (c *OTelTracing) Setup(ctx context.Context, serviceName string, sampleRate float64, resourceAttributes map[string]string) (trace.Tracer, io.Closer, error) { +func (c *OTelTracing) Setup(serviceName string, sampleRate float64, resourceAttributes map[string]string) (trace.Tracer, io.Closer, error) { var ( err error exporter *otlptrace.Exporter @@ -67,27 +50,21 @@ func (c *OTelTracing) Setup(ctx context.Context, serviceName string, sampleRate return nil, nil, fmt.Errorf("setting up exporter: %w", err) } - var resAttrs []attribute.KeyValue - for k, v := range resourceAttributes { - resAttrs = append(resAttrs, attribute.String(k, v)) + attr := []attribute.KeyValue{ + semconv.ServiceNameKey.String(serviceName), + semconv.ServiceVersionKey.String(version.Version), } - res, err := resource.New(ctx, - resource.WithContainer(), - resource.WithHost(), - resource.WithOS(), - resource.WithProcess(), - resource.WithTelemetrySDK(), - resource.WithDetectors(ttypes.K8sAttributesDetector{}), - // The following order allows the user to override the service name and version, - // as well as any other attributes set by the above detectors. - resource.WithAttributes( - semconv.ServiceName(serviceName), - semconv.ServiceVersion(version.Version), - ), - resource.WithAttributes(resAttrs...), - // Use the environment variables to allow overriding above resource attributes. + for k, v := range resourceAttributes { + attr = append(attr, attribute.String(k, v)) + } + + res, err := resource.New(context.Background(), + resource.WithAttributes(attr...), resource.WithFromEnv(), + resource.WithTelemetrySDK(), + resource.WithOSType(), + resource.WithProcessCommandArgs(), ) if err != nil { return nil, nil, fmt.Errorf("building resource: %w", err) diff --git a/pkg/udp/conn.go b/pkg/udp/conn.go index 36778f648..69ba8ebe2 100644 --- a/pkg/udp/conn.go +++ b/pkg/udp/conn.go @@ -34,7 +34,7 @@ type Listener struct { timeout time.Duration } -// ListenPacketConn creates a new listener from PacketConn. +// Creates a new listener from PacketConn. func ListenPacketConn(packetConn net.PacketConn, timeout time.Duration) (*Listener, error) { if timeout <= 0 { return nil, errors.New("timeout should be greater than zero") diff --git a/pkg/udp/conn_test.go b/pkg/udp/conn_test.go index 3e9459102..fbcd080b3 100644 --- a/pkg/udp/conn_test.go +++ b/pkg/udp/conn_test.go @@ -1,9 +1,11 @@ package udp import ( + "crypto/rand" "errors" "io" "net" + "runtime" "testing" "time" @@ -250,19 +252,13 @@ func TestShutdown(t *testing.T) { // Start sending packets, to create a "session" with the server. requireEcho(t, "TEST", conn, time.Second) - shutdownStartedChan := make(chan struct{}) doneChan := make(chan struct{}) go func() { - close(shutdownStartedChan) err := l.Shutdown(5 * time.Second) require.NoError(t, err) close(doneChan) }() - // Wait until shutdown has started, and hopefully after 100 ms the listener has stopped accepting new sessions. - <-shutdownStartedChan - time.Sleep(100 * time.Millisecond) - // Make sure that our session is still live even after the shutdown. requireEcho(t, "TEST2", conn, time.Second) @@ -307,6 +303,58 @@ func TestShutdown(t *testing.T) { } } +func TestReadLoopMaxDataSize(t *testing.T) { + if runtime.GOOS == "darwin" { + // sudo sysctl -w net.inet.udp.maxdgram=65507 + t.Skip("Skip test on darwin as the maximum dgram size is set to 9216 bytes by default") + } + + // Theoretical maximum size of data in a UDP datagram. + // 65535 − 8 (UDP header) − 20 (IP header). + dataSize := 65507 + + doneCh := make(chan struct{}) + + l, err := Listen(net.ListenConfig{}, "udp", ":0", 3*time.Second) + require.NoError(t, err) + + defer func() { + err := l.Close() + require.NoError(t, err) + }() + + go func() { + defer close(doneCh) + + conn, err := l.Accept() + require.NoError(t, err) + + buffer := make([]byte, dataSize) + + n, err := conn.Read(buffer) + require.NoError(t, err) + + assert.Equal(t, dataSize, n) + }() + + c, err := net.Dial("udp", l.Addr().String()) + require.NoError(t, err) + + data := make([]byte, dataSize) + + _, err = rand.Read(data) + require.NoError(t, err) + + _, err = c.Write(data) + require.NoError(t, err) + + select { + case <-doneCh: + case <-time.Tick(5 * time.Second): + t.Fatal("Timeout waiting for datagram read") + } +} + // requireEcho tests that the conn session is live and functional, // by writing data through it, and expecting the same data as a response when reading on it. // It fatals if the read blocks longer than timeout, diff --git a/script/gcg/traefik-bugfix.toml b/script/gcg/traefik-bugfix.toml index 36d6242a3..5e4a617a5 100644 --- a/script/gcg/traefik-bugfix.toml +++ b/script/gcg/traefik-bugfix.toml @@ -4,11 +4,11 @@ RepositoryName = "traefik" OutputType = "file" FileName = "traefik_changelog.md" -# example new bugfix v3.5.4 -CurrentRef = "v3.5" -PreviousRef = "v3.5.3" -BaseBranch = "v3.5" -FutureCurrentRefName = "v3.5.4" +# example new bugfix v3.4.1 +CurrentRef = "v3.4" +PreviousRef = "v3.4.0" +BaseBranch = "v3.4" +FutureCurrentRefName = "v3.4.1" ThresholdPreviousRef = 10 ThresholdCurrentRef = 10 diff --git a/script/gcg/traefik-final-release-part1.toml b/script/gcg/traefik-final-release-part1.toml index 95291aea5..edba19179 100644 --- a/script/gcg/traefik-final-release-part1.toml +++ b/script/gcg/traefik-final-release-part1.toml @@ -4,11 +4,11 @@ RepositoryName = "traefik" OutputType = "file" FileName = "traefik_changelog.md" -# example final release of v3.5.0 -CurrentRef = "v3.5" -PreviousRef = "v3.5.0-rc1" -BaseBranch = "v3.5" -FutureCurrentRefName = "v3.5.0" +# example final release of v3.4.0 +CurrentRef = "v3.4" +PreviousRef = "v3.4.0-rc1" +BaseBranch = "v3.4" +FutureCurrentRefName = "v3.4.0" ThresholdPreviousRef = 10 ThresholdCurrentRef = 10 diff --git a/script/gcg/traefik-final-release-part2.toml b/script/gcg/traefik-final-release-part2.toml index ceaa118de..453ff650f 100644 --- a/script/gcg/traefik-final-release-part2.toml +++ b/script/gcg/traefik-final-release-part2.toml @@ -4,11 +4,11 @@ RepositoryName = "traefik" OutputType = "file" FileName = "traefik_changelog.md" -# example final release of v3.5.0 -CurrentRef = "v3.5.0-rc1" -PreviousRef = "v3.4.0-rc1" +# example final release of v3.4.0 +CurrentRef = "v3.4.0-rc1" +PreviousRef = "v3.3.0-rc1" BaseBranch = "master" -FutureCurrentRefName = "v3.5.0" +FutureCurrentRefName = "v3.4.0" ThresholdPreviousRef = 10 ThresholdCurrentRef = 10 diff --git a/script/gcg/traefik-rc-first.toml b/script/gcg/traefik-rc-first.toml index a946c438d..9d3afbc12 100644 --- a/script/gcg/traefik-rc-first.toml +++ b/script/gcg/traefik-rc-first.toml @@ -4,11 +4,11 @@ RepositoryName = "traefik" OutputType = "file" FileName = "traefik_changelog.md" -# example RC1 of v3.5.0-rc1 +# example RC1 of v3.4.0-rc1 CurrentRef = "master" -PreviousRef = "v3.4.0-rc1" +PreviousRef = "v3.3.0-rc1" BaseBranch = "master" -FutureCurrentRefName = "v3.5.0-rc1" +FutureCurrentRefName = "v3.4.0-rc1" ThresholdPreviousRef = 10 ThresholdCurrentRef = 10 diff --git a/script/gcg/traefik-rc-new.toml b/script/gcg/traefik-rc-new.toml index 70a89e7cd..252218964 100644 --- a/script/gcg/traefik-rc-new.toml +++ b/script/gcg/traefik-rc-new.toml @@ -4,11 +4,11 @@ RepositoryName = "traefik" OutputType = "file" FileName = "traefik_changelog.md" -# example rc2 of v3.5.0 -CurrentRef = "v3.5" -PreviousRef = "v3.5.0-rc1" -BaseBranch = "v3.5" -FutureCurrentRefName = "v3.5.0-rc2" +# example rc2 of v3.4.0 +CurrentRef = "v3.4" +PreviousRef = "v3.4.0-rc1" +BaseBranch = "v3.4" +FutureCurrentRefName = "v3.4.0-rc2" ThresholdPreviousRef = 10 ThresholdCurrentRef = 10 diff --git a/script/release-packages.sh b/script/release-packages.sh new file mode 100755 index 000000000..18ed92d3b --- /dev/null +++ b/script/release-packages.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash +set -e + +if [ -n "${SEMAPHORE_GIT_TAG_NAME}" ]; then + echo "Releasing packages..." +else + echo "Skipping release" + exit 0 +fi + +rm -rf dist + +for os in linux darwin windows freebsd openbsd; do + goreleaser release --skip=publish -p 2 --timeout="90m" --config "$(go run ./internal/release "$os")" + go clean -cache +done + +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/ diff --git a/traefik.sample.toml b/traefik.sample.toml index a88793976..7cf8ed2bf 100644 --- a/traefik.sample.toml +++ b/traefik.sample.toml @@ -81,16 +81,12 @@ # # filePath = "/path/to/log/log.txt" - # Format is either "json", "common", or "genericCLF". - # - "common": Traefik's extended CLF format (default) - # - "genericCLF": Standard CLF format compatible with standard log analyzers - # - "json": JSON format for structured logging + # Format is either "json" or "common". # # Optional # Default: "common" # # format = "json" - # format = "genericCLF" ################################################################ # API and dashboard configuration diff --git a/traefik.sample.yml b/traefik.sample.yml index bcfa53441..c13ebcd42 100644 --- a/traefik.sample.yml +++ b/traefik.sample.yml @@ -79,16 +79,12 @@ entryPoints: # # filePath: /path/to/log/log.txt - # Format is either "json", "common", or "genericCLF". - # - "common": Traefik's extended CLF format (default) - # - "genericCLF": Standard CLF format compatible with standard log analyzers - # - "json": JSON format for structured logging + # Format is either "json" or "common". # # Optional # Default: "common" # # format: json -# format: genericCLF ################################################################ # API and dashboard configuration diff --git a/webui/.dockerignore b/webui/.dockerignore new file mode 100644 index 000000000..8e8fdce7b --- /dev/null +++ b/webui/.dockerignore @@ -0,0 +1,5 @@ +# compiled output +/dist + +# dependencies +/node_modules diff --git a/webui/.editorconfig b/webui/.editorconfig index cb5b5bbaa..9d08a1a82 100644 --- a/webui/.editorconfig +++ b/webui/.editorconfig @@ -1,22 +1,9 @@ -# Editor configuration, see http://editorconfig.org root = true [*] charset = utf-8 - -[*.{js,ts,tsx}] indent_style = space indent_size = 2 +end_of_line = lf insert_final_newline = true trim_trailing_whitespace = true - -[*.md] -max_line_length = off -trim_trailing_whitespace = false - -[Makefile] -indent_style = tab - -[{package.json}] -indent_style = space -indent_size = 2 \ No newline at end of file diff --git a/webui/.env.sample b/webui/.env.sample deleted file mode 100644 index 0fe8436ee..000000000 --- a/webui/.env.sample +++ /dev/null @@ -1,2 +0,0 @@ -VITE_APP_BASE_API_URL=/api -VITE_APP_BASE_URL= diff --git a/webui/.eslintignore b/webui/.eslintignore new file mode 100644 index 000000000..9f81cf845 --- /dev/null +++ b/webui/.eslintignore @@ -0,0 +1,7 @@ +/dist +/src-capacitor +/src-cordova +/.quasar +/node_modules +.eslintrc.cjs +/quasar.config.*.temporary.compiled* diff --git a/webui/.eslintrc.cjs b/webui/.eslintrc.cjs new file mode 100644 index 000000000..331093689 --- /dev/null +++ b/webui/.eslintrc.cjs @@ -0,0 +1,68 @@ +module.exports = { + root: true, + + parserOptions: { + parser: '@babel/eslint-parser', + ecmaVersion: 2021, // Allows for the parsing of modern ECMAScript features + }, + + env: { + node: true, + browser: true, + 'vue/setup-compiler-macros': true + }, + + extends: [ + // https://github.com/vuejs/eslint-plugin-vue#priority-a-essential-error-prevention + // consider switching to `plugin:vue/strongly-recommended` or `plugin:vue/recommended` for stricter rules. + 'plugin:vue/vue3-essential', + 'plugin:vue/vue3-recommended', + 'standard' + ], + + // required to lint *.vue files + plugins: [ + 'vue', + ], + + globals: { + ga: 'readonly', // Google Analytics + cordova: 'readonly', + __statics: 'readonly', + __QUASAR_SSR__: 'readonly', + __QUASAR_SSR_SERVER__: 'readonly', + __QUASAR_SSR_CLIENT__: 'readonly', + __QUASAR_SSR_PWA__: 'readonly', + process: 'readonly', + Capacitor: 'readonly', + chrome: 'readonly' + }, + + // add your custom rules here + rules: { + // allow async-await + 'generator-star-spacing': 'off', + // allow paren-less arrow functions + 'arrow-parens': 'off', + 'one-var': 'off', + 'no-void': 'off', + 'multiline-ternary': 'off', + + 'import/first': 'off', + 'import/named': 'error', + 'import/namespace': 'error', + 'import/default': 'error', + 'import/export': 'error', + 'import/extensions': 'off', + 'import/no-unresolved': 'off', + 'import/no-extraneous-dependencies': 'off', + 'prefer-promise-reject-errors': 'off', + 'vue/multi-word-component-names': 'off', + + // allow console.log during development only + //'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off', + // allow debugger during development only + //'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off' + } +} + diff --git a/webui/.gitignore b/webui/.gitignore index 541d4f563..db32ba46c 100644 --- a/webui/.gitignore +++ b/webui/.gitignore @@ -1,62 +1,32 @@ -# See http://help.github.com/ignore-files/ for more about ignoring files. - -# compiled output -/dist -/build -/dist-server -/tmp -/out-tsc - -# dependencies -/node_modules -.yalc - -# IDEs and editors -/.idea -.project -.classpath -.c9/ -*.launch -.settings/ -*.sublime-workspace - -# IDE - VSCode -.vscode/* -!.vscode/settings.json -!.vscode/tasks.json -!.vscode/launch.json -!.vscode/extensions.json - -# misc -/.sass-cache -/connect.lock -/coverage -/libpeerconnection.log -npm-debug.log -yarn-error.log -testem.log -/typings - -# e2e -/e2e/*.js -/e2e/*.map - -# System Files +.quasar .DS_Store -Thumbs.db +.thumbs.db +node_modules +/dist +/dev_local +/src-cordova/node_modules +/src-cordova/platforms +/src-cordova/plugins +/src-cordova/www -# env -.env +# Log files +npm-debug.log* +yarn-debug.log* +yarn-error.log* -# yarn berry with no zero-installs -.pnp.* -.yarn/* -!.yarn/patches -!.yarn/plugins -!.yarn/releases -!.yarn/sdks -!.yarn/versions +# Editor directories and files +.idea +.vscode +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? -# static assets +# local env files +.env.local +.env.*.local + +# static assets (ignore all except the DO NOT EDIT file) static/* !static/DONT-EDIT-FILES-IN-THIS-DIRECTORY.md diff --git a/webui/.nvmrc b/webui/.nvmrc index 26600046d..8b0beab16 100644 --- a/webui/.nvmrc +++ b/webui/.nvmrc @@ -1 +1 @@ -v22.15.1 +20.11.0 diff --git a/webui/.postcssrc.cjs b/webui/.postcssrc.cjs new file mode 100644 index 000000000..1174fe52b --- /dev/null +++ b/webui/.postcssrc.cjs @@ -0,0 +1,8 @@ +// https://github.com/michael-ciniawsky/postcss-load-config + +module.exports = { + plugins: [ + // to edit target browsers: use "browserslist" field in package.json + require('autoprefixer') + ] +} diff --git a/webui/.prettierrc.json b/webui/.prettierrc.json deleted file mode 100644 index 7933dbd7b..000000000 --- a/webui/.prettierrc.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "semi": false, - "trailingComma": "all", - "singleQuote": true, - "printWidth": 120 -} \ No newline at end of file diff --git a/webui/.stylintrc b/webui/.stylintrc new file mode 100644 index 000000000..ce38d777e --- /dev/null +++ b/webui/.stylintrc @@ -0,0 +1,35 @@ +{ + "blocks": "never", + "brackets": "never", + "colons": "never", + "colors": "always", + "commaSpace": "always", + "commentSpace": "always", + "cssLiteral": "never", + "depthLimit": false, + "duplicates": true, + "efficient": "always", + "extendPref": false, + "globalDupe": true, + "indentPref": 2, + "leadingZero": "never", + "maxErrors": false, + "maxWarnings": false, + "mixed": false, + "namingConvention": false, + "namingConventionStrict": false, + "none": "never", + "noImportant": false, + "parenSpace": "never", + "placeholder": false, + "prefixVarsWithDollar": "always", + "quotePref": "single", + "semicolons": "never", + "sortOrder": false, + "stackedProperties": "never", + "trailingWhitespace": "never", + "universal": "never", + "valid": true, + "zeroUnits": "never", + "zIndexNormalize": false +} diff --git a/webui/.yarnrc.yml b/webui/.yarnrc.yml deleted file mode 100644 index 3186f3f07..000000000 --- a/webui/.yarnrc.yml +++ /dev/null @@ -1 +0,0 @@ -nodeLinker: node-modules diff --git a/webui/Dockerfile b/webui/Dockerfile new file mode 100644 index 000000000..283101452 --- /dev/null +++ b/webui/Dockerfile @@ -0,0 +1,17 @@ +FROM node:22.9-alpine3.20 +# Current Active LTS release according to (https://nodejs.org/en/about/releases/) + +ENV WEBUI_DIR=/src/webui +RUN mkdir -p $WEBUI_DIR + +COPY package.json $WEBUI_DIR/ +COPY yarn.lock $WEBUI_DIR/ + +WORKDIR $WEBUI_DIR +RUN yarn install + +COPY . $WEBUI_DIR/ + +EXPOSE 8080 + +RUN yarn lint diff --git a/webui/babel.config.cjs b/webui/babel.config.cjs new file mode 100644 index 000000000..cd63d3933 --- /dev/null +++ b/webui/babel.config.cjs @@ -0,0 +1,16 @@ +/* eslint-disable */ + +module.exports = api => { + return { + presets: [ + [ + '@quasar/babel-preset-app', + api.caller(caller => caller && caller.target === 'node') + ? { targets: { node: 'current' } } + : {} + ] + ] + } +} + + diff --git a/webui/buildx.Dockerfile b/webui/buildx.Dockerfile deleted file mode 100644 index 09b65ab81..000000000 --- a/webui/buildx.Dockerfile +++ /dev/null @@ -1,18 +0,0 @@ -FROM node:22.15.1-alpine3.20 - -ENV WEBUI_DIR=/src/webui -RUN mkdir -p $WEBUI_DIR - -COPY package.json yarn.lock .yarnrc.yml $WEBUI_DIR/ - -ENV VITE_APP_BASE_URL="" -ENV VITE_APP_BASE_API_URL="/api" - -WORKDIR $WEBUI_DIR - -RUN corepack enable -RUN yarn workspaces focus --all --production - -COPY . $WEBUI_DIR/ - -EXPOSE 8080 diff --git a/webui/dev/scripts/transfer.js b/webui/dev/scripts/transfer.js new file mode 100644 index 000000000..eaa02bceb --- /dev/null +++ b/webui/dev/scripts/transfer.js @@ -0,0 +1,17 @@ +const fs = require('fs-extra') + +const folder = process.argv[2] + +async function execute () { + try { + await fs.emptyDir('./static') + await fs.outputFile('./static/DONT-EDIT-FILES-IN-THIS-DIRECTORY.md', 'For more information see `webui/readme.md`') + console.log('Deleted static folder contents!') + await fs.copy(`./dist/${folder}`, './static', { overwrite: true }) + console.log('Installed new files in static folder!') + } catch (err) { + console.error(err) + } +} + +execute() diff --git a/webui/eslint.config.mjs b/webui/eslint.config.mjs deleted file mode 100644 index 51edbb887..000000000 --- a/webui/eslint.config.mjs +++ /dev/null @@ -1,57 +0,0 @@ -import js from '@eslint/js' -import eslintConfigPrettier from 'eslint-config-prettier' -import importPlugin from 'eslint-plugin-import' -import jsxA11y from 'eslint-plugin-jsx-a11y' -import react from 'eslint-plugin-react' -import reactHooks from 'eslint-plugin-react-hooks' -import globals from 'globals' -import tseslint from 'typescript-eslint' - -export default tseslint.config( - { ignores: ['dist'] }, - { - extends: [js.configs.recommended, ...tseslint.configs.recommended], - files: ['**/*.{ts,tsx}'], - languageOptions: { - ecmaVersion: 2020, - globals: globals.browser, - parserOptions: { - ecmaFeatures: { - jsx: true, - }, - }, - }, - plugins: { - react: react, - 'react-hooks': reactHooks, - }, - rules: { - ...reactHooks.configs.recommended.rules, - "@typescript-eslint/no-explicit-any": "warn", - }, - }, - eslintConfigPrettier, - { - files: ['**/*.{ts,tsx}'], - extends: [importPlugin.flatConfigs.recommended, importPlugin.flatConfigs.typescript], - rules: { - 'import/order': [ - 'error', - { - alphabetize: { - order: 'asc', - caseInsensitive: true, - }, - 'newlines-between': 'always', - }, - ], - }, - settings: { - 'import/resolver': { - typescript: true, - node: true, - }, - }, - }, - jsxA11y.flatConfigs.recommended, -) diff --git a/webui/index.dev.html b/webui/index.dev.html deleted file mode 100644 index 215a1a4a8..000000000 --- a/webui/index.dev.html +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - - - Traefik Proxy - - - -
- - - diff --git a/webui/index.html b/webui/index.html index 4b5b2e2a0..654437a27 100644 --- a/webui/index.html +++ b/webui/index.html @@ -1,31 +1,32 @@ - + {{if .APIUrl}} {{end}} - - - - - - - - Traefik Proxy + <%= productName %> + + + + + + + + + + + + + + + + - -
- + diff --git a/webui/jsconfig.json b/webui/jsconfig.json new file mode 100644 index 000000000..456944a5e --- /dev/null +++ b/webui/jsconfig.json @@ -0,0 +1,39 @@ +{ + "compilerOptions": { + "baseUrl": ".", + "paths": { + "src/*": [ + "src/*" + ], + "app/*": [ + "*" + ], + "components/*": [ + "src/components/*" + ], + "layouts/*": [ + "src/layouts/*" + ], + "pages/*": [ + "src/pages/*" + ], + "assets/*": [ + "src/assets/*" + ], + "boot/*": [ + "src/boot/*" + ], + "stores/*": [ + "src/stores/*" + ], + "vue$": [ + "node_modules/vue/dist/vue.runtime.esm-bundler.js" + ] + } + }, + "exclude": [ + "dist", + ".quasar", + "node_modules" + ] +} \ No newline at end of file diff --git a/webui/package.json b/webui/package.json index 3721a8ae1..7cd74012b 100644 --- a/webui/package.json +++ b/webui/package.json @@ -1,102 +1,62 @@ { - "name": "traefik-proxy-dashboard", - "version": "0.1.0", + "name": "traefik-ui", + "version": "2.0.0", + "description": "Traefik UI", + "productName": "Traefik", + "cordovaId": "io.traefik.traefik", "private": true, - "homepage": ".", "scripts": { - "build": "vite build", - "build:prod": "yarn test && yarn tsc && yarn lint && yarn build", - "dev": "vite", - "format": "prettier './src/**/*.{ts,tsx}' --config .prettierrc.json --write", - "lint": "eslint './src/**/*.{ts,tsx}'", - "lint:fix": "eslint --fix './src/**/*.{ts,tsx}'", - "preview": "vite preview", - "test": "vitest run", - "test:coverage": "vitest run --coverage", - "test:watch": "vitest", + "transfer": "node dev/scripts/transfer.js", + "lint": "eslint src/**/*.{js,vue}", + "dev": "APP_ENV=development quasar dev", + "build-quasar": "quasar build", + "build-staging": "NODE_ENV=production APP_ENV=development yarn build-quasar", + "build": "NODE_ENV=production APP_ENV=production yarn build-quasar && yarn transfer spa", + "build:nc": "yarn build", + "test": "echo \"See package.json => scripts for available tests.\" && exit 0", + "test:unit": "vitest", "test:unit:ci": "vitest run" }, - "lint-staged": { - "**/*.{ts,tsx}": [ - "yarn format", - "eslint --fix", - "git add" - ] - }, - "husky": { - "hooks": { - "pre-commit": "lint-staged" - } - }, - "browserslist": { - "production": [ - ">0.2%", - "not dead", - "not op_mini all" - ], - "development": [ - "last 1 chrome version", - "last 1 firefox version", - "last 1 safari version" - ] - }, - "type": "module", "dependencies": { - "@eslint/js": "^9.32.0", - "@testing-library/dom": "^10.4.1", - "@testing-library/jest-dom": "^6.4.2", - "@testing-library/react": "^14.2.1", - "@testing-library/user-event": "^14.5.2", - "@traefiklabs/faency": "11.1.4", - "@types/lodash": "^4.17.16", - "@types/node": "^22.15.18", - "@types/react": "^18.2.0", - "@types/react-dom": "^18.2.19", - "@types/react-router-dom": "^5.1.3", - "@typescript-eslint/parser": "^8.38.0", - "@vitejs/plugin-react": "^4.7.0", - "@vitest/coverage-v8": "^3.2.4", + "@quasar/extras": "^1.16.12", + "axios": "^1.7.4", "chart.js": "^4.4.1", - "eslint": "^9.32.0", - "eslint-config-prettier": "^10.1.8", - "eslint-import-resolver-typescript": "^4.4.4", - "eslint-plugin-import": "^2.32.0", - "eslint-plugin-jsx-a11y": "^6.10.2", - "eslint-plugin-react": "^7.37.5", - "eslint-plugin-react-hooks": "^5.2.0", - "framer-motion": "^11.18.2", - "globals": "^16.0.0", - "jest-extended": "^4.0.2", - "jsdom": "^24.0.0", - "lodash": "^4.17.21", - "msw": "^2.1.7", - "query-string": "^6.9.0", - "react": "^18.2.0", - "react-chartjs-2": "^5.2.0", - "react-dom": "^18.2.0", - "react-error-boundary": "^4.0.12", - "react-helmet-async": "^2.0.4", - "react-icons": "^5.0.1", - "react-infinite-scroll-hook": "^4.1.1", - "react-router-dom": "6.22.1", - "swr": "^2.2.4", - "typescript": "^5.2.2", - "typescript-eslint": "^8.38.0", - "usehooks-ts": "^2.14.0", - "vite": "^5.4.19", - "vite-tsconfig-paths": "^5.1.4", - "vitest": "^3.2.4", - "vitest-canvas-mock": "^0.3.3" + "core-js": "^3.35.1", + "dot-prop": "^8.0.2", + "lodash.isequal": "4.5.0", + "moment": "^2.30.1", + "quasar": "^2.16.6", + "query-string": "^8.1.0", + "vue": "^3.0.0", + "vue-chartjs": "^5.3.0", + "vue-router": "^4.0.12", + "vuex": "^4.1.0", + "vuex-map-fields": "^1.4.1" }, "devDependencies": { - "husky": "^3.1.0", - "lint-staged": "^9.5.0", - "prettier": "^3.5.3" + "@babel/core": "^7.23.9", + "@babel/eslint-parser": "^7.23.10", + "@quasar/app-vite": "^2.0.0-beta.15", + "@quasar/babel-preset-app": "^2.0.3", + "@quasar/quasar-app-extension-testing-unit-vitest": "^1.0.0", + "@vue/test-utils": "^2.4.4", + "autoprefixer": "^10.4.2", + "eslint": "^8.11.0", + "eslint-config-standard": "^17.0.0", + "eslint-plugin-import": "^2.19.1", + "eslint-plugin-n": "^16.6.2", + "eslint-plugin-promise": "^6.0.0", + "eslint-plugin-vue": "^9.0.0", + "postcss": "^8.4.14", + "vitest": "^1.6.0" }, - "msw": { - "workerDirectory": [ - "public" - ] + "resolutions": { + "cookie": "^0.7.0" }, - "packageManager": "yarn@4.9.1" + "engines": { + "node": "^22 || ^20 || ^18 || ^16", + "npm": ">= 6.13.4", + "yarn": ">= 1.22.22" + }, + "packageManager": "yarn@1.22.22" } diff --git a/webui/postcss.config.cjs b/webui/postcss.config.cjs new file mode 100644 index 000000000..94b7b1c85 --- /dev/null +++ b/webui/postcss.config.cjs @@ -0,0 +1,27 @@ +/* eslint-disable */ +// https://github.com/michael-ciniawsky/postcss-load-config + +module.exports = { + plugins: [ + // https://github.com/postcss/autoprefixer + require('autoprefixer')({ + overrideBrowserslist: [ + 'last 4 Chrome versions', + 'last 4 Firefox versions', + 'last 4 Edge versions', + 'last 4 Safari versions', + 'last 4 Android versions', + 'last 4 ChromeAndroid versions', + 'last 4 FirefoxAndroid versions', + 'last 4 iOS versions' + ] + }) + + // https://github.com/elchininet/postcss-rtlcss + // If you want to support RTL css, then + // 1. yarn/npm install postcss-rtlcss + // 2. optionally set quasar.config.js > framework > lang to an RTL language + // 3. uncomment the following line: + // require('postcss-rtlcss') + ] +} diff --git a/webui/public/app-logo-128x128.png b/webui/public/app-logo-128x128.png new file mode 100755 index 000000000..af9348b10 Binary files /dev/null and b/webui/public/app-logo-128x128.png differ diff --git a/webui/public/browserconfig.xml b/webui/public/browserconfig.xml deleted file mode 100644 index 4c3e081e5..000000000 --- a/webui/public/browserconfig.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - #ddee6d - - - diff --git a/webui/public/favicon-16x16.png b/webui/public/favicon-16x16.png deleted file mode 100644 index 3d57b6754..000000000 Binary files a/webui/public/favicon-16x16.png and /dev/null differ diff --git a/webui/public/favicon-32x32.png b/webui/public/favicon-32x32.png deleted file mode 100644 index ae98b9bf0..000000000 Binary files a/webui/public/favicon-32x32.png and /dev/null differ diff --git a/webui/public/favicon.ico b/webui/public/favicon.ico deleted file mode 100644 index 95ed61ba4..000000000 Binary files a/webui/public/favicon.ico and /dev/null differ diff --git a/webui/public/manifest.json b/webui/public/manifest.json deleted file mode 100644 index 73d155b68..000000000 --- a/webui/public/manifest.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "short_name": "Traefik Proxy", - "name": "Traefik Proxy", - "icons": [ - { - "src": "favicon-16x16.png", - "sizes": "16x16", - "type": "image/png" - }, - { - "src": "favicon-32x32.png", - "sizes": "32x32", - "type": "image/png" - }, - { - "src": "favicon-96x96.png", - "sizes": "96x96", - "type": "image/png" - } - ], - "start_url": ".", - "display": "standalone", - "theme_color": "#ddee6d", - "background_color": "#091827" -} diff --git a/webui/public/mockServiceWorker.js b/webui/public/mockServiceWorker.js deleted file mode 100644 index 34057e898..000000000 --- a/webui/public/mockServiceWorker.js +++ /dev/null @@ -1,307 +0,0 @@ -/* eslint-disable */ -/* tslint:disable */ - -/** - * Mock Service Worker. - * @see https://github.com/mswjs/msw - * - Please do NOT modify this file. - * - Please do NOT serve this file on production. - */ - -const PACKAGE_VERSION = '2.7.3' -const INTEGRITY_CHECKSUM = '00729d72e3b82faf54ca8b9621dbb96f' -const IS_MOCKED_RESPONSE = Symbol('isMockedResponse') -const activeClientIds = new Set() - -self.addEventListener('install', function () { - self.skipWaiting() -}) - -self.addEventListener('activate', function (event) { - event.waitUntil(self.clients.claim()) -}) - -self.addEventListener('message', async function (event) { - const clientId = event.source.id - - if (!clientId || !self.clients) { - return - } - - const client = await self.clients.get(clientId) - - if (!client) { - return - } - - const allClients = await self.clients.matchAll({ - type: 'window', - }) - - switch (event.data) { - case 'KEEPALIVE_REQUEST': { - sendToClient(client, { - type: 'KEEPALIVE_RESPONSE', - }) - break - } - - case 'INTEGRITY_CHECK_REQUEST': { - sendToClient(client, { - type: 'INTEGRITY_CHECK_RESPONSE', - payload: { - packageVersion: PACKAGE_VERSION, - checksum: INTEGRITY_CHECKSUM, - }, - }) - break - } - - case 'MOCK_ACTIVATE': { - activeClientIds.add(clientId) - - sendToClient(client, { - type: 'MOCKING_ENABLED', - payload: { - client: { - id: client.id, - frameType: client.frameType, - }, - }, - }) - break - } - - case 'MOCK_DEACTIVATE': { - activeClientIds.delete(clientId) - break - } - - case 'CLIENT_CLOSED': { - activeClientIds.delete(clientId) - - const remainingClients = allClients.filter((client) => { - return client.id !== clientId - }) - - // Unregister itself when there are no more clients - if (remainingClients.length === 0) { - self.registration.unregister() - } - - break - } - } -}) - -self.addEventListener('fetch', function (event) { - const { request } = event - - // Bypass navigation requests. - if (request.mode === 'navigate') { - return - } - - // Opening the DevTools triggers the "only-if-cached" request - // that cannot be handled by the worker. Bypass such requests. - if (request.cache === 'only-if-cached' && request.mode !== 'same-origin') { - return - } - - // Bypass all requests when there are no active clients. - // Prevents the self-unregistered worked from handling requests - // after it's been deleted (still remains active until the next reload). - if (activeClientIds.size === 0) { - return - } - - // Generate unique request ID. - const requestId = crypto.randomUUID() - event.respondWith(handleRequest(event, requestId)) -}) - -async function handleRequest(event, requestId) { - const client = await resolveMainClient(event) - const response = await getResponse(event, client, requestId) - - // Send back the response clone for the "response:*" life-cycle events. - // Ensure MSW is active and ready to handle the message, otherwise - // this message will pend indefinitely. - if (client && activeClientIds.has(client.id)) { - ;(async function () { - const responseClone = response.clone() - - sendToClient( - client, - { - type: 'RESPONSE', - payload: { - requestId, - isMockedResponse: IS_MOCKED_RESPONSE in response, - type: responseClone.type, - status: responseClone.status, - statusText: responseClone.statusText, - body: responseClone.body, - headers: Object.fromEntries(responseClone.headers.entries()), - }, - }, - [responseClone.body], - ) - })() - } - - return response -} - -// Resolve the main client for the given event. -// Client that issues a request doesn't necessarily equal the client -// that registered the worker. It's with the latter the worker should -// communicate with during the response resolving phase. -async function resolveMainClient(event) { - const client = await self.clients.get(event.clientId) - - if (activeClientIds.has(event.clientId)) { - return client - } - - if (client?.frameType === 'top-level') { - return client - } - - const allClients = await self.clients.matchAll({ - type: 'window', - }) - - return allClients - .filter((client) => { - // Get only those clients that are currently visible. - return client.visibilityState === 'visible' - }) - .find((client) => { - // Find the client ID that's recorded in the - // set of clients that have registered the worker. - return activeClientIds.has(client.id) - }) -} - -async function getResponse(event, client, requestId) { - const { request } = event - - // Clone the request because it might've been already used - // (i.e. its body has been read and sent to the client). - const requestClone = request.clone() - - function passthrough() { - // Cast the request headers to a new Headers instance - // so the headers can be manipulated with. - const headers = new Headers(requestClone.headers) - - // Remove the "accept" header value that marked this request as passthrough. - // This prevents request alteration and also keeps it compliant with the - // user-defined CORS policies. - const acceptHeader = headers.get('accept') - if (acceptHeader) { - const values = acceptHeader.split(',').map((value) => value.trim()) - const filteredValues = values.filter( - (value) => value !== 'msw/passthrough', - ) - - if (filteredValues.length > 0) { - headers.set('accept', filteredValues.join(', ')) - } else { - headers.delete('accept') - } - } - - return fetch(requestClone, { headers }) - } - - // Bypass mocking when the client is not active. - if (!client) { - return passthrough() - } - - // Bypass initial page load requests (i.e. static assets). - // The absence of the immediate/parent client in the map of the active clients - // means that MSW hasn't dispatched the "MOCK_ACTIVATE" event yet - // and is not ready to handle requests. - if (!activeClientIds.has(client.id)) { - return passthrough() - } - - // Notify the client that a request has been intercepted. - const requestBuffer = await request.arrayBuffer() - const clientMessage = await sendToClient( - client, - { - type: 'REQUEST', - payload: { - id: requestId, - url: request.url, - mode: request.mode, - method: request.method, - headers: Object.fromEntries(request.headers.entries()), - cache: request.cache, - credentials: request.credentials, - destination: request.destination, - integrity: request.integrity, - redirect: request.redirect, - referrer: request.referrer, - referrerPolicy: request.referrerPolicy, - body: requestBuffer, - keepalive: request.keepalive, - }, - }, - [requestBuffer], - ) - - switch (clientMessage.type) { - case 'MOCK_RESPONSE': { - return respondWithMock(clientMessage.data) - } - - case 'PASSTHROUGH': { - return passthrough() - } - } - - return passthrough() -} - -function sendToClient(client, message, transferrables = []) { - return new Promise((resolve, reject) => { - const channel = new MessageChannel() - - channel.port1.onmessage = (event) => { - if (event.data && event.data.error) { - return reject(event.data.error) - } - - resolve(event.data) - } - - client.postMessage( - message, - [channel.port2].concat(transferrables.filter(Boolean)), - ) - }) -} - -async function respondWithMock(response) { - // Setting response status code to 0 is a no-op. - // However, when responding with a "Response.error()", the produced Response - // instance will have status code set to 0. Since it's not possible to create - // a Response instance with status code 0, handle that use-case separately. - if (response.status === 0) { - return Response.error() - } - - const mockedResponse = new Response(response.body, response) - - Reflect.defineProperty(mockedResponse, IS_MOCKED_RESPONSE, { - value: true, - enumerable: true, - }) - - return mockedResponse -} diff --git a/webui/public/providers/consul.svg b/webui/public/providers/consul.svg new file mode 100644 index 000000000..b9b33d374 --- /dev/null +++ b/webui/public/providers/consul.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/webui/public/providers/consulcatalog.svg b/webui/public/providers/consulcatalog.svg new file mode 100644 index 000000000..a692dede2 --- /dev/null +++ b/webui/public/providers/consulcatalog.svg @@ -0,0 +1,10 @@ + + + + + + + + + diff --git a/webui/public/providers/docker.svg b/webui/public/providers/docker.svg new file mode 100644 index 000000000..db4a729e6 --- /dev/null +++ b/webui/public/providers/docker.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/webui/public/providers/ecs.svg b/webui/public/providers/ecs.svg new file mode 100644 index 000000000..aad9305b5 --- /dev/null +++ b/webui/public/providers/ecs.svg @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/webui/public/providers/etcd.svg b/webui/public/providers/etcd.svg new file mode 100644 index 000000000..3c270f632 --- /dev/null +++ b/webui/public/providers/etcd.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/webui/public/providers/file.svg b/webui/public/providers/file.svg new file mode 100644 index 000000000..bf4d1cae1 --- /dev/null +++ b/webui/public/providers/file.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/webui/public/providers/http.svg b/webui/public/providers/http.svg new file mode 100644 index 000000000..338e1afca --- /dev/null +++ b/webui/public/providers/http.svg @@ -0,0 +1,11 @@ + + + + + + + + + \ No newline at end of file diff --git a/webui/public/providers/hub.svg b/webui/public/providers/hub.svg new file mode 100644 index 000000000..1df28d7a8 --- /dev/null +++ b/webui/public/providers/hub.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/webui/public/providers/internal.svg b/webui/public/providers/internal.svg new file mode 100644 index 000000000..ce0fc3496 --- /dev/null +++ b/webui/public/providers/internal.svg @@ -0,0 +1,4 @@ + + + + diff --git a/webui/public/providers/kubernetes.svg b/webui/public/providers/kubernetes.svg new file mode 100644 index 000000000..b6670fe5d --- /dev/null +++ b/webui/public/providers/kubernetes.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/webui/public/providers/kubernetescrd.svg b/webui/public/providers/kubernetescrd.svg new file mode 100644 index 000000000..b6670fe5d --- /dev/null +++ b/webui/public/providers/kubernetescrd.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/webui/public/providers/kubernetesgateway.svg b/webui/public/providers/kubernetesgateway.svg new file mode 100644 index 000000000..b6670fe5d --- /dev/null +++ b/webui/public/providers/kubernetesgateway.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/webui/public/providers/kubernetesingress.svg b/webui/public/providers/kubernetesingress.svg new file mode 100644 index 000000000..b6670fe5d --- /dev/null +++ b/webui/public/providers/kubernetesingress.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/webui/public/providers/marathon.svg b/webui/public/providers/marathon.svg new file mode 100644 index 000000000..0d65d89b6 --- /dev/null +++ b/webui/public/providers/marathon.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/webui/public/providers/nomad.svg b/webui/public/providers/nomad.svg new file mode 100755 index 000000000..e71d75007 --- /dev/null +++ b/webui/public/providers/nomad.svg @@ -0,0 +1,7 @@ + + + + + + diff --git a/webui/public/providers/plugin.svg b/webui/public/providers/plugin.svg new file mode 100644 index 000000000..5a6a63769 --- /dev/null +++ b/webui/public/providers/plugin.svg @@ -0,0 +1,10 @@ + + + plugin + + + + + + + \ No newline at end of file diff --git a/webui/public/providers/rancher.svg b/webui/public/providers/rancher.svg new file mode 100644 index 000000000..7d9a4e776 --- /dev/null +++ b/webui/public/providers/rancher.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/webui/public/providers/redis.svg b/webui/public/providers/redis.svg new file mode 100644 index 000000000..e0944a282 --- /dev/null +++ b/webui/public/providers/redis.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/webui/public/providers/rest.svg b/webui/public/providers/rest.svg new file mode 100644 index 000000000..9a877e0d4 --- /dev/null +++ b/webui/public/providers/rest.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/webui/public/providers/swarm.svg b/webui/public/providers/swarm.svg new file mode 100644 index 000000000..db4a729e6 --- /dev/null +++ b/webui/public/providers/swarm.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/webui/public/providers/zookeeper.svg b/webui/public/providers/zookeeper.svg new file mode 100644 index 000000000..0b9f1be63 --- /dev/null +++ b/webui/public/providers/zookeeper.svg @@ -0,0 +1,128 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/webui/public/robots.txt b/webui/public/robots.txt deleted file mode 100644 index 1f53798bb..000000000 --- a/webui/public/robots.txt +++ /dev/null @@ -1,2 +0,0 @@ -User-agent: * -Disallow: / diff --git a/webui/public/traefiklabs-hub-button-app/main-v1.js b/webui/public/traefiklabs-hub-button-app/main-v1.js index e140dab34..2c912be53 100644 --- a/webui/public/traefiklabs-hub-button-app/main-v1.js +++ b/webui/public/traefiklabs-hub-button-app/main-v1.js @@ -1,23 +1,3 @@ /* eslint-disable */ -(()=>{var e={110:(e,t,n)=>{"use strict";var r=n(309),a={childContextTypes:!0,contextType:!0,contextTypes:!0,defaultProps:!0,displayName:!0,getDefaultProps:!0,getDerivedStateFromError:!0,getDerivedStateFromProps:!0,mixins:!0,propTypes:!0,type:!0},l={name:!0,length:!0,prototype:!0,caller:!0,callee:!0,arguments:!0,arity:!0},o={$$typeof:!0,compare:!0,defaultProps:!0,displayName:!0,propTypes:!0,type:!0},i={};function u(e){return r.isMemo(e)?o:i[e.$$typeof]||a}i[r.ForwardRef]={$$typeof:!0,render:!0,defaultProps:!0,displayName:!0,propTypes:!0},i[r.Memo]=o;var s=Object.defineProperty,c=Object.getOwnPropertyNames,f=Object.getOwnPropertySymbols,d=Object.getOwnPropertyDescriptor,p=Object.getPrototypeOf,h=Object.prototype;e.exports=function e(t,n,r){if("string"!==typeof n){if(h){var a=p(n);a&&a!==h&&e(t,a,r)}var o=c(n);f&&(o=o.concat(f(n)));for(var i=u(t),m=u(n),g=0;g{"use strict";var n="function"===typeof Symbol&&Symbol.for,r=n?Symbol.for("react.element"):60103,a=n?Symbol.for("react.portal"):60106,l=n?Symbol.for("react.fragment"):60107,o=n?Symbol.for("react.strict_mode"):60108,i=n?Symbol.for("react.profiler"):60114,u=n?Symbol.for("react.provider"):60109,s=n?Symbol.for("react.context"):60110,c=n?Symbol.for("react.async_mode"):60111,f=n?Symbol.for("react.concurrent_mode"):60111,d=n?Symbol.for("react.forward_ref"):60112,p=n?Symbol.for("react.suspense"):60113,h=n?Symbol.for("react.suspense_list"):60120,m=n?Symbol.for("react.memo"):60115,g=n?Symbol.for("react.lazy"):60116,v=n?Symbol.for("react.block"):60121,y=n?Symbol.for("react.fundamental"):60117,b=n?Symbol.for("react.responder"):60118,S=n?Symbol.for("react.scope"):60119;function k(e){if("object"===typeof e&&null!==e){var t=e.$$typeof;switch(t){case r:switch(e=e.type){case c:case f:case l:case i:case o:case p:return e;default:switch(e=e&&e.$$typeof){case s:case d:case g:case m:case u:return e;default:return t}}case a:return t}}}function w(e){return k(e)===f}t.AsyncMode=c,t.ConcurrentMode=f,t.ContextConsumer=s,t.ContextProvider=u,t.Element=r,t.ForwardRef=d,t.Fragment=l,t.Lazy=g,t.Memo=m,t.Portal=a,t.Profiler=i,t.StrictMode=o,t.Suspense=p,t.isAsyncMode=function(e){return w(e)||k(e)===c},t.isConcurrentMode=w,t.isContextConsumer=function(e){return k(e)===s},t.isContextProvider=function(e){return k(e)===u},t.isElement=function(e){return"object"===typeof e&&null!==e&&e.$$typeof===r},t.isForwardRef=function(e){return k(e)===d},t.isFragment=function(e){return k(e)===l},t.isLazy=function(e){return k(e)===g},t.isMemo=function(e){return k(e)===m},t.isPortal=function(e){return k(e)===a},t.isProfiler=function(e){return k(e)===i},t.isStrictMode=function(e){return k(e)===o},t.isSuspense=function(e){return k(e)===p},t.isValidElementType=function(e){return"string"===typeof e||"function"===typeof e||e===l||e===f||e===i||e===o||e===p||e===h||"object"===typeof e&&null!==e&&(e.$$typeof===g||e.$$typeof===m||e.$$typeof===u||e.$$typeof===s||e.$$typeof===d||e.$$typeof===y||e.$$typeof===b||e.$$typeof===S||e.$$typeof===v)},t.typeOf=k},309:(e,t,n)=>{"use strict";e.exports=n(746)},463:(e,t,n)=>{"use strict";var r=n(791),a=n(296);function l(e){for(var t="https://reactjs.org/docs/error-decoder.html?invariant="+e,n=1;n