1
0
mirror of https://github.com/containous/traefik.git synced 2025-09-06 05:44:21 +03:00

Compare commits

..

7 Commits
master ... v2.2

Author SHA1 Message Date
Fernandez Ludovic
06af6ea7f3 chore: fix PyYAML version 2023-07-19 21:53:09 +02:00
Fernandez Ludovic
60ca8fb43a fix: doc requirements 2022-07-18 11:57:38 +02:00
Fernandez Ludovic
a68faf1fd1 fix: doc requirements 2022-03-24 23:57:04 +01:00
Joker
34babb8eeb Fix incorrect syntax in yaml config examples 2020-09-23 11:36:04 +02:00
Fernandez Ludovic
01f84bffae fix: remove old mixtus call. 2020-09-23 11:05:31 +02:00
Andrii Dembitskyi
f730e46bf8 Fix syntax in yaml config examples 2020-09-23 10:52:03 +02:00
Romain
7fc42ebb02 chore: apply new documentation style.
Co-authored-by: jbdoumenjou <jb.doumenjou@gmail.com>
2020-09-23 10:43:07 +02:00
2220 changed files with 94505 additions and 279347 deletions

View File

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

3
.github/FUNDING.yml vendored
View File

@@ -1,3 +0,0 @@
# These are supported funding model platforms
github: traefik

82
.github/ISSUE_TEMPLATE/Bug_report.md vendored Normal file
View File

@@ -0,0 +1,82 @@
---
name: Bug report
about: Create a report to help us improve
---
<!-- PLEASE FOLLOW THE ISSUE TEMPLATE TO HELP TRIAGE AND SUPPORT! -->
### Do you want to request a *feature* or report a *bug*?
<!--
DO NOT FILE ISSUES FOR GENERAL SUPPORT QUESTIONS.
The issue tracker is for reporting bugs and feature requests only.
For end-user related support questions, please refer to one of the following:
- the Traefik community forum: https://community.traefik.io/
-->
Bug
<!--
The configurations between 1.X and 2.X are NOT compatible.
Please have a look here https://doc.traefik.io/traefik/getting-started/configuration-overview/.
-->
### What did you do?
<!--
HOW TO WRITE A GOOD BUG REPORT?
- Respect the issue template as much as possible.
- The title should be short and descriptive.
- Explain the conditions which led you to report this issue: the context.
- The context should lead to something, an idea or a problem that youre facing.
- Remain clear and concise.
- Format your messages to help the reader focus on what matters and understand the structure of your message, use Markdown syntax https://help.github.com/articles/github-flavored-markdown
-->
### What did you expect to see?
### What did you see instead?
### Output of `traefik version`: (_What version of Traefik are you using?_)
<!--
`latest` is not considered as a valid version.
For the Traefik Docker image:
docker run [IMAGE] version
ex: docker run traefik version
-->
```
(paste your output here)
```
### What is your environment & configuration (arguments, toml, provider, platform, ...)?
```toml
# (paste your configuration here)
```
<!--
Add more configuration information here.
-->
### If applicable, please paste the log output in DEBUG level (`--log.level=DEBUG` switch)
```
(paste your output here)
```

View File

@@ -0,0 +1,35 @@
---
name: Feature request
about: Suggest an idea for this project
---
<!-- PLEASE FOLLOW THE ISSUE TEMPLATE TO HELP TRIAGE AND SUPPORT! -->
### Do you want to request a *feature* or report a *bug*?
<!--
DO NOT FILE ISSUES FOR GENERAL SUPPORT QUESTIONS.
The issue tracker is for reporting bugs and feature requests only.
For end-user related support questions, please refer to one of the following:
- the Traefik community forum: https://community.traefik.io/
-->
Feature
### What did you expect to see?
<!--
HOW TO WRITE A GOOD ISSUE?
- Respect the issue template as much as possible.
- The title should be short and descriptive.
- Explain the conditions which led you to report this issue: the context.
- The context should lead to something, an idea or a problem that youre facing.
- Remain clear and concise.
- Format your messages to help the reader focus on what matters and understand the structure of your message, use Markdown syntax https://help.github.com/articles/github-flavored-markdown
-->

View File

@@ -1,82 +0,0 @@
name: Bug Report (Traefik)
description: Create a report to help us improve.
body:
- type: checkboxes
id: terms
attributes:
label: Welcome!
description: |
The issue tracker is for reporting bugs and feature requests only.
For end-user related support questions, please use the [Traefik community forum](https://community.traefik.io/).
All new/updated issues are triaged regularly by the maintainers.
All issues closed by a bot are subsequently double-checked by the maintainers.
DO NOT FILE ISSUES FOR GENERAL SUPPORT QUESTIONS.
options:
- label: Yes, I've searched similar issues on [GitHub](https://github.com/traefik/traefik/issues) and didn't find any.
required: true
- label: Yes, I've searched similar issues on the [Traefik community forum](https://community.traefik.io) and didn't find any.
required: true
- type: textarea
attributes:
label: What did you do?
description: |
How to write a good bug report?
- Respect the issue template as much as possible.
- The title should be short and descriptive.
- Explain the conditions which led you to report this issue: the context.
- The context should lead to something, an idea or a problem that youre facing.
- Remain clear and concise.
- Format your messages to help the reader focus on what matters and understand the structure of your message, use [Markdown syntax](https://help.github.com/articles/github-flavored-markdown)
placeholder: What did you do?
validations:
required: true
- type: textarea
attributes:
label: What did you see instead?
placeholder: What did you see instead?
validations:
required: true
- type: textarea
attributes:
label: What version of Traefik are you using?
description: |
`latest` is not considered as a valid version.
Output of `traefik version`.
For the Traefik Docker image (`docker run [IMAGE] version`), example:
```console
$ docker run traefik version
```
placeholder: Paste your output here.
validations:
required: true
- type: textarea
attributes:
label: What is your environment & configuration?
description: arguments, toml, provider, platform, ...
placeholder: Add information here.
value: |
```yaml
# (paste your configuration here)
```
Add more configuration information here.
validations:
required: true
- type: textarea
attributes:
label: If applicable, please paste the log output in DEBUG level
description: "`--log.level=DEBUG` switch."
placeholder: Paste your output here.
validations:
required: false

View File

@@ -1,8 +0,0 @@
blank_issues_enabled: false
contact_links:
- name: Traefik Community Support
url: https://community.traefik.io/
about: If you have a question, or are looking for advice, please post on our Discuss forum! The community loves to chime in to help. Happy Coding!
- name: Traefik Helm Chart Issues
url: https://github.com/traefik/traefik-helm-chart
about: Are you submitting an issue or feature enhancement for the Traefik helm chart? Please post in the traefik-helm-chart GitHub Issues.

View File

@@ -1,33 +0,0 @@
name: Feature Request (Traefik)
description: Suggest an idea for this project.
body:
- type: checkboxes
id: terms
attributes:
label: Welcome!
description: |
The issue tracker is for reporting bugs and feature requests only. For end-user related support questions, please refer to one of the following:
- the Traefik community forum: https://community.traefik.io/
DO NOT FILE ISSUES FOR GENERAL SUPPORT QUESTIONS.
options:
- label: Yes, I've searched similar issues on [GitHub](https://github.com/traefik/traefik/issues) and didn't find any.
required: true
- label: Yes, I've searched similar issues on the [Traefik community forum](https://community.traefik.io) and didn't find any.
required: true
- type: textarea
attributes:
label: What did you expect to see?
description: |
How to write a good issue?
- Respect the issue template as much as possible.
- The title should be short and descriptive.
- Explain the conditions which led you to report this issue: the context.
- The context should lead to something, an idea or a problem that youre facing.
- Remain clear and concise.
- Format your messages to help the reader focus on what matters and understand the structure of your message, use [Markdown syntax](https://help.github.com/articles/github-flavored-markdown)
placeholder: What did you expect to see?
validations:
required: true

View File

@@ -1,16 +1,17 @@
<!--
PLEASE READ THIS MESSAGE.
Documentation:
- for Traefik v2: use branch v2.11 (fixes only)
- for Traefik v3: use branch v3.5
Documentation fixes or enhancements:
- for Traefik v1: use branch v1.7
- for Traefik v2: use branch v2.2
Bug:
- for Traefik v2: use branch v2.11 (security fixes only)
- for Traefik v3: use branch v3.5
Bug fixes:
- for Traefik v1: use branch v1.7
- for Traefik v2: use branch v2.2
Enhancements:
- use branch master
- for Traefik v1: we only accept bug fixes
- for Traefik v2: use branch master
HOW TO WRITE A GOOD PULL REQUEST? https://doc.traefik.io/traefik/contributing/submitting-pull-requests/

View File

@@ -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@v4
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

View File

@@ -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@v4
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 }}

View File

@@ -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@v4
- 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}}"

View File

@@ -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@v4
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 }}

View File

@@ -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@v4
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##*/}

View File

@@ -1,138 +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@v4
with:
fetch-depth: 0
- name: Set up Go ${{ env.GO_VERSION }}
uses: actions/setup-go@v5
env:
# Ensure cache consistency on Linux, see https://github.com/actions/setup-go/pull/383
ImageOS: ${{ matrix.os }}
with:
go-version: ${{ env.GO_VERSION }}
check-latest: true
- name: Artifact webui
uses: actions/download-artifact@v4
with:
name: webui.tar.gz
- name: Untar webui
run: |
tar xvf webui.tar.gz
rm webui.tar.gz
- name: Go generate
run: go generate
- name: Generate goreleaser file
run: |
GORELEASER_CONFIG_FILE_PATH=$(go run ./internal/release "${{ matrix.os }}")
echo "GORELEASER_CONFIG_FILE_PATH=$GORELEASER_CONFIG_FILE_PATH" >> $GITHUB_ENV
- name: Build with goreleaser
uses: goreleaser/goreleaser-action@v6
with:
distribution: goreleaser
# 'latest', 'nightly', or a semver
version: '~> v2'
args: release --clean --timeout="90m" --config "${{ env.GORELEASER_CONFIG_FILE_PATH }}"
- name: Artifact binaries
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.os }}-binaries
path: |
dist/**/*_checksums.txt
dist/**/*.tar.gz
dist/**/*.zip
retention-days: 1
release:
if: github.ref_type == 'tag' && github.repository == 'traefik/traefik'
runs-on: ubuntu-latest
needs:
- build
steps:
- name: Check out code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Artifact webui
uses: actions/download-artifact@v4
with:
name: webui.tar.gz
- name: Untar webui
run: |
tar xvf webui.tar.gz
rm webui.tar.gz
- name: Retrieve the secret and decode it to a file
env:
TRAEFIKER_RSA: ${{ secrets.TRAEFIKER_RSA }}
run: |
mkdir -p ~/.ssh
echo "${TRAEFIKER_RSA}" | base64 --decode > ~/.ssh/traefiker_rsa
- name: Download All Artifacts
uses: actions/download-artifact@v4
with:
path: dist/
pattern: "*-binaries"
merge-multiple: true
- name: Publish Release
env:
GH_TOKEN: ${{ github.token }}
run: |
cat dist/**/*_checksums.txt >> "dist/traefik_${VERSION}_checksums.txt"
rm dist/**/*_checksums.txt
tar cfz "dist/traefik-${VERSION}.src.tar.gz" \
--exclude-vcs \
--exclude .idea \
--exclude .travis \
--exclude .semaphoreci \
--exclude .github \
--exclude dist .
chown -R "$(id -u)":"$(id -g)" dist/
gh release create ${VERSION} ./dist/**/traefik*.{zip,tar.gz} ./dist/traefik*.{tar.gz,txt} --repo traefik/traefik --title ${VERSION} --notes ${VERSION} --latest=false
./script/deploy.sh

View File

@@ -1,26 +0,0 @@
name: Sync Docker Images
on:
workflow_dispatch:
schedule:
- cron: "0 0 * * *" # Run every day
jobs:
sync:
runs-on: ubuntu-latest
permissions:
packages: write
contents: read
if: github.repository == 'traefik/traefik'
steps:
- uses: actions/checkout@v4
- uses: imjasonh/setup-crane@v0.4
- name: Sync
run: |
EXCLUDED_TAGS="1.7.9-alpine v1.0.0-beta.392 v1.0.0-beta.404 v1.0.0-beta.704 v1.0.0-rc1 v1.7.9-alpine"
EXCLUDED_REGEX=$(echo $EXCLUDED_TAGS | sed 's/ /|/g')
diff <(crane ls traefik) <(crane ls ghcr.io/traefik/traefik) | grep '^<' | awk '{print $2}' | while read -r tag; do [[ "$tag" =~ ^($EXCLUDED_REGEX)$ ]] || (echo "Processing image: traefik:$tag"; crane cp "traefik:$tag" "ghcr.io/traefik/traefik:$tag"); done
crane cp traefik:latest ghcr.io/traefik/traefik:latest

View File

@@ -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@v4
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

View File

@@ -1,41 +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: Avoid generating webui
run: |
mkdir webui/static
touch webui/static/index.html
- name: K8s Gateway API conformance test and report
run: |
make test-gateway-api-conformance
git diff --exit-code

View File

@@ -1,109 +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@v4
with:
fetch-depth: 0
- name: Set up Go ${{ env.GO_VERSION }}
uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}
check-latest: true
- name: Avoid generating webui
run: |
mkdir webui/static
touch webui/static/index.html
- 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@v4
with:
fetch-depth: 0
- name: Set up Go ${{ env.GO_VERSION }}
uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}
check-latest: true
- name: Avoid generating webui
run: |
mkdir webui/static
touch webui/static/index.html
- 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

View File

@@ -1,93 +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@v4
with:
fetch-depth: 0
- name: Set up Go ${{ env.GO_VERSION }}
uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}
check-latest: true
- name: 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@v4
with:
fetch-depth: 0
- name: Set up Go ${{ env.GO_VERSION }}
uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}
check-latest: true
- name: Avoid generating webui
run: |
mkdir webui/static
touch webui/static/index.html
- 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@v4
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

View File

@@ -1,94 +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@v4
with:
fetch-depth: 0
- name: Set up Go ${{ env.GO_VERSION }}
uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}
check-latest: true
- name: Avoid generating webui
run: |
mkdir webui/static
touch webui/static/index.html
- 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@v4
with:
fetch-depth: 0
- name: Set up Go ${{ env.GO_VERSION }}
uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}
check-latest: true
- name: 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: Avoid generating webui
run: |
mkdir webui/static
touch webui/static/index.html
- name: Validate
run: make validate-files
validate-generate:
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Go ${{ env.GO_VERSION }}
uses: actions/setup-go@v5
with:
go-version: ${{ env.GO_VERSION }}
check-latest: true
- name: 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

6
.gitignore vendored
View File

@@ -7,6 +7,7 @@
/webui/.tmp/
/site/
/docs/site/
/static/
/autogen/
/traefik
/traefik.toml
@@ -15,8 +16,3 @@
*.exe
cover.out
vendor/
plugins-storage/
plugins-local/
traefik_changelog.md
integration/tailscale.secret
integration/conformance-reports/**/experimental-dev-default-report.yaml

115
.golangci.toml Normal file
View File

@@ -0,0 +1,115 @@
[run]
timeout = "10m"
skip-files = []
skip-dirs = [
"pkg/provider/kubernetes/crd/generated/",
]
[linters-settings]
[linters-settings.govet]
check-shadowing = false
[linters-settings.golint]
min-confidence = 0.0
[linters-settings.gocyclo]
min-complexity = 14.0
[linters-settings.maligned]
suggest-new = true
[linters-settings.goconst]
min-len = 3.0
min-occurrences = 4.0
[linters-settings.misspell]
locale = "US"
[linters-settings.funlen]
lines = 230 # default 60
statements = 120 # default 40
[linters]
enable-all = true
disable = [
"gocyclo", # FIXME must be fixed
"gosec",
"dupl",
"maligned",
"lll",
"unparam",
"prealloc",
"scopelint",
"gochecknoinits",
"gochecknoglobals",
"godox",
"gocognit",
"bodyclose", # Too many false-positive and panics.
"wsl", # Too strict
"gomnd", # Too strict
"stylecheck", # skip because report issues related to some generated files.
"testpackage", # Too strict
"goerr113", # Too strict
"nestif", # Too many false-positive.
"noctx", # Too strict
"exhaustive", # Too strict
]
[issues]
exclude-use-default = false
max-per-linter = 0
max-same-issues = 0
exclude = [
"SA1019: http.CloseNotifier is deprecated: the CloseNotifier interface predates Go's context package. New code should use Request.Context instead.", # FIXME must be fixed
"Error return value of .((os\\.)?std(out|err)\\..*|.*Close|.*Flush|os\\.Remove(All)?|.*printf?|os\\.(Un)?Setenv). is not checked",
"should have a package comment, unless it's in another file for this package",
]
[[issues.exclude-rules]]
path = "(.+)_test.go"
linters = ["goconst", "funlen", "godot"]
[[issues.exclude-rules]]
path = "integration/.+_test.go"
text = "Error return value of `cmd\\.Process\\.Kill` is not checked"
[[issues.exclude-rules]]
path = "integration/(consul_catalog_test|constraint_test).go"
text = "Error return value of `(s.deregisterService|s.deregisterAgentService)` is not checked"
[[issues.exclude-rules]]
path = "integration/grpc_test.go"
text = "Error return value of `closer` is not checked"
[[issues.exclude-rules]]
path = "pkg/h2c/h2c.go"
text = "Error return value of `rw.Write` is not checked"
[[issues.exclude-rules]]
path = "pkg/middlewares/recovery/recovery.go"
text = "`logger` can be `github.com/stretchr/testify/assert.TestingT`"
[[issues.exclude-rules]]
path = "pkg/provider/docker/builder_test.go"
text = "(U1000: func )?`(.+)` is unused"
[[issues.exclude-rules]]
path = "pkg/provider/kubernetes/builder_(endpoint|service)_test.go"
text = "(U1000: func )?`(.+)` is unused"
[[issues.exclude-rules]]
path = "pkg/config/parser/.+_test.go"
text = "U1000: field `(foo|fuu)` is unused"
[[issues.exclude-rules]]
path = "pkg/server/service/bufferpool.go"
text = "SA6002: argument should be pointer-like to avoid allocations"
[[issues.exclude-rules]]
path = "cmd/configuration.go"
text = "string `traefik` has (\\d) occurrences, make it a constant"
[[issues.exclude-rules]]
path = "pkg/server/middleware/middlewares.go"
text = "Function 'buildConstructor' is too long \\(\\d+ > 230\\)"
[[issues.exclude-rules]] # FIXME must be fixed
path = "cmd/context.go"
text = "S1000: should use a simple channel send/receive instead of `select` with a single case"
[[issues.exclude-rules]]
path = "pkg/tracing/haystack/logger.go"
linters = ["goprintffuncname"]
[[issues.exclude-rules]]
path = "pkg/tracing/tracing.go"
text = "printf-like formatting function 'SetErrorWithEvent' should be named 'SetErrorWithEventf'"
[[issues.exclude-rules]]
path = "pkg/log/deprecated.go"
linters = ["godot"]

View File

@@ -1,328 +0,0 @@
version: "2"
formatters:
enable:
- gci
- gofumpt
exclusions:
generated: lax
paths:
- pkg/provider/kubernetes/crd/generated/
linters:
default: all
disable:
- bodyclose # too many false-positive
- containedctx # too many false-positive
- contextcheck # too many false-positive
- cyclop # duplicate of gocyclo
- dupl # Too strict
- err113 # Too strict
- exhaustive # Not relevant
- exhaustruct # Not relevant
- forcetypeassert # Too strict
- gochecknoglobals
- gochecknoinits
- gocognit # Too strict
- gocyclo # FIXME must be fixed
- gosec # Too strict
- gosmopolitan # not relevant
- ireturn # Not relevant
- lll # Not relevant
- maintidx # kind of duplicate of gocyclo
- makezero # Not relevant
- mnd # Too strict
- nestif # Too many false-positive.
- nilnil # Not relevant
- nlreturn # Not relevant
- noctx # Too strict
- nonamedreturns # Too strict
- paralleltest # Not relevant
- prealloc # Too many false-positive.
- rowserrcheck # not relevant (SQL)
- sqlclosecheck # not relevant (SQL)
- tagliatelle # Too strict
- testpackage # Too strict
- tparallel # Not relevant
- varnamelen # Not relevant
- wrapcheck # Too strict
- wsl # Too strict
settings:
depguard:
rules:
main:
deny:
- pkg: github.com/instana/testify
desc: not allowed
- pkg: github.com/pkg/errors
desc: Should be replaced by standard lib errors package
errcheck:
exclude-functions:
- fmt.Fprintln
forbidigo:
forbid:
- pattern: ^print(ln)?$
- pattern: ^spew\.Print(f|ln)?$
- pattern: ^spew\.Dump$
funlen:
lines: -1
statements: 120
goconst:
min-len: 3
min-occurrences: 4
gocyclo:
min-complexity: 14
godox:
keywords:
- FIXME
gomoddirectives:
toolchain-pattern: go1\.\d+\.\d+$
tool-forbidden: true
go-version-pattern: ^1\.\d+(\.0)?$
replace-allow-list:
- github.com/abbot/go-http-auth
- github.com/gorilla/mux
- github.com/mailgun/minheap
- github.com/mailgun/multibuf
- github.com/jaguilar/vt100
- github.com/cucumber/godog
govet:
enable-all: true
disable:
- shadow
- fieldalignment
importas:
no-unaliased: true
alias:
- pkg: github.com/docker/compose/v2/pkg/api
alias: composeapi
# Standard Kubernetes rewrites:
- pkg: k8s.io/api/core/v1
alias: corev1
- pkg: k8s.io/api/networking/v1
alias: netv1
- pkg: k8s.io/api/networking/v1beta1
alias: netv1beta1
- pkg: k8s.io/api/admission/v1
alias: admv1
- pkg: k8s.io/api/admission/v1beta1
alias: admv1beta1
- pkg: k8s.io/api/extensions/v1beta1
alias: extv1beta1
- pkg: k8s.io/apimachinery/pkg/apis/meta/v1
alias: metav1
- pkg: k8s.io/apimachinery/pkg/types
alias: ktypes
- pkg: k8s.io/apimachinery/pkg/api/errors
alias: kerror
- pkg: k8s.io/client-go/kubernetes
alias: kclientset
- pkg: k8s.io/client-go/informers
alias: kinformers
- pkg: k8s.io/client-go/testing
alias: ktesting
- pkg: k8s.io/apimachinery/pkg/runtime/schema
alias: kschema
- pkg: k8s.io/client-go/kubernetes/scheme
alias: kscheme
- pkg: k8s.io/apimachinery/pkg/version
alias: kversion
- pkg: k8s.io/client-go/kubernetes/fake
alias: kubefake
- pkg: k8s.io/client-go/discovery/fake
alias: discoveryfake
# Kubernetes Gateway rewrites:
- pkg: sigs.k8s.io/gateway-api/pkg/client/clientset/gateway/versioned
alias: gateclientset
- pkg: sigs.k8s.io/gateway-api/pkg/client/informers/gateway/externalversions
alias: gateinformers
- pkg: sigs.k8s.io/gateway-api/apis/v1alpha2
alias: gatev1alpha2
# Traefik Kubernetes rewrites:
- pkg: github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1
alias: traefikv1alpha1
- pkg: github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/clientset/versioned
alias: traefikclientset
- pkg: github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/informers/externalversions
alias: traefikinformers
- pkg: github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/clientset/versioned/scheme
alias: traefikscheme
- pkg: github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/clientset/versioned/fake
alias: traefikcrdfake
misspell:
locale: US
revive:
rules:
- name: struct-tag
- name: blank-imports
- name: context-as-argument
- name: context-keys-type
- name: dot-imports
- name: error-return
- name: error-strings
- name: error-naming
- name: exported
disabled: true
- name: if-return
- name: increment-decrement
- name: var-naming
- name: var-declaration
- name: package-comments
disabled: true
- name: range
- name: receiver-naming
- name: time-naming
- name: unexported-return
- name: indent-error-flow
- name: errorf
- name: empty-block
- name: superfluous-else
- name: unused-parameter
disabled: true
- name: unreachable-code
- name: redefines-builtin-id
tagalign:
align: false
sort: true
order:
- description
- json
- toml
- yaml
- yml
- label
- label-slice-as-struct
- file
- kv
- export
testifylint:
disable:
- suite-dont-use-pkg
- require-error
- go-require
perfsprint:
err-error: true
errorf: true
sprintf1: true
strconcat: false
staticcheck:
checks:
- all
- '-SA1019'
- '-ST1000'
- '-ST1003'
- '-ST1016'
- '-ST1020'
- '-ST1021'
- '-ST1022'
- '-QF1001'
- '-QF1008' # TODO must be fixed
exclusions:
generated: lax
presets:
- comments
- std-error-handling
rules:
- path: (.+)_test.go
linters:
- canonicalheader
- fatcontext
- funlen
- goconst
- godot
- path: (.+)_test.go
text: ' always receives '
linters:
- unparam
- path: pkg/server/service/bufferpool.go
text: 'SA6002: argument should be pointer-like to avoid allocations'
- path: pkg/server/middleware/middlewares.go
text: Function 'buildConstructor' has too many statements
linters:
- funlen
- path: pkg/provider/kubernetes/ingress-nginx/kubernetes.go
text: Function 'loadConfiguration' has too many statements
linters:
- funlen
- path: pkg/tracing/haystack/logger.go
linters:
- goprintffuncname
- path: pkg/tracing/tracing.go
text: printf-like formatting function 'SetErrorWithEvent' should be named 'SetErrorWithEventf'
linters:
- goprintffuncname
- path: pkg/tls/tlsmanager_test.go
text: 'SA1019: config.ClientCAs.Subjects has been deprecated since Go 1.18'
- path: pkg/types/tls_test.go
text: 'SA1019: tlsConfig.RootCAs.Subjects has been deprecated since Go 1.18'
- path: pkg/provider/kubernetes/(crd|gateway)/client.go
linters:
- interfacebloat
- path: pkg/metrics/metrics.go
linters:
- interfacebloat
- path: integration/healthcheck_test.go
text: Duplicate words \(wsp2,\) found
linters:
- dupword
- path: pkg/types/domain_test.go
text: Duplicate words \(sub\) found
linters:
- dupword
- path: pkg/provider/kubernetes/gateway/client_mock_test.go
text: 'unusedwrite: unused write to field'
linters:
- govet
- path: pkg/provider/acme/local_store.go
linters:
- musttag
- path: pkg/tls/certificate.go
text: the methods of "Certificates" use pointer receiver and non-pointer receiver.
linters:
- recvcheck
- path: pkg/config/static/static_config.go
source: 'errors.New\("Consul Catalog provider'
text: 'ST1005: error strings should not be capitalized'
- path: pkg/config/static/static_config.go
source: 'errors.New\("Consul provider'
text: 'ST1005: error strings should not be capitalized'
- path: pkg/config/static/static_config.go
source: 'errors.New\("Nomad provider'
text: 'ST1005: error strings should not be capitalized'
- path: (.+)\.go
text: 'struct-tag: unknown option ''inline'' in JSON tag'
linters:
- revive
- path: (.+)\.go
text: 'struct-tag: unknown option ''omitzero'' in TOML tag'
linters:
- revive
- path: (.+)\.go$
text: 'SA1019: http.CloseNotifier has been deprecated' # FIXME must be fixed
- path: (.+)\.go$
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:
- funlen
- path: pkg/plugins/middlewarewasm.go
text: 'the methods of "wasmMiddlewareBuilder" use pointer receiver and non-pointer receiver.'
linters:
- recvcheck
- path: pkg/proxy/httputil/bufferpool.go
text: 'SA6002: argument should be pointer-like to avoid allocations'
paths:
- pkg/provider/kubernetes/crd/generated/
issues:
max-issues-per-linter: 0
max-same-issues: 0

View File

@@ -1,55 +1,46 @@
project_name: traefik
version: 2
[[if .GOARCH]]
dist: "./dist/[[ .GOOS ]]-[[ .GOARCH ]]"
[[else]]
dist: "./dist/[[ .GOOS ]]"
[[end]]
before:
hooks:
- go generate
builds:
- binary: traefik
main: ./cmd/traefik/
main: ./cmd/traefik/traefik.go
env:
- CGO_ENABLED=0
ldflags:
- -s -w -X github.com/traefik/traefik/v3/pkg/version.Version={{.Version}} -X github.com/traefik/traefik/v3/pkg/version.Codename={{.Env.CODENAME}} -X github.com/traefik/traefik/v3/pkg/version.BuildDate={{.Date}}
flags:
- -trimpath
- -s -w -X github.com/containous/traefik/v2/pkg/version.Version={{.Version}} -X github.com/containous/traefik/v2/pkg/version.Codename={{.Env.CODENAME}} -X github.com/containous/traefik/v2/pkg/version.BuildDate={{.Date}}
goos:
- "[[ .GOOS ]]"
- linux
- darwin
- windows
- freebsd
- openbsd
goarch:
[[if .GOARCH]]
- "[[ .GOARCH ]]"
[[else]]
- amd64
- '386'
- 386
- arm
- arm64
- ppc64le
- s390x
- riscv64
[[end]]
goarm:
- '7'
- '6'
- 7
- 6
- 5
ignore:
- goos: darwin
goarch: '386'
goarch: 386
- goos: openbsd
goarch: arm
- goos: openbsd
goarch: arm64
- goos: freebsd
goarch: arm
- goos: freebsd
goarch: arm64
- goos: windows
goarch: arm
changelog:
disable: true
skip: true
archives:
- id: traefik

View File

@@ -1,13 +0,0 @@
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"

4
.semaphoreci/cleanup.sh Executable file
View File

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

20
.semaphoreci/golang.sh Executable file
View File

@@ -0,0 +1,20 @@
#!/usr/bin/env bash
set -e
curl -O https://dl.google.com/go/go"${GO_VERSION}".linux-amd64.tar.gz
tar -xvf go"${GO_VERSION}".linux-amd64.tar.gz
rm -rf go"${GO_VERSION}".linux-amd64.tar.gz
sudo mkdir -p /usr/local/golang/"${GO_VERSION}"/go
sudo mv go /usr/local/golang/"${GO_VERSION}"/
sudo rm /usr/local/bin/go
sudo chmod +x /usr/local/golang/"${GO_VERSION}"/go/bin/go
sudo ln -s /usr/local/golang/"${GO_VERSION}"/go/bin/go /usr/local/bin/go
export GOROOT="/usr/local/golang/${GO_VERSION}/go"
export GOTOOLDIR="/usr/local/golang/${GO_VERSION}/go/pkg/tool/linux_amd64"
go version

6
.semaphoreci/job1.sh Executable file
View File

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

8
.semaphoreci/job2.sh Executable file
View File

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

35
.semaphoreci/setup.sh Executable file
View File

@@ -0,0 +1,35 @@
# For personnal CI
# mv /home/runner/workspace/src/github.com/<username>/ /home/runner/workspace/src/github.com/containous/
# cd /home/runner/workspace/src/github.com/containous/traefik/
for s in apache2 cassandra elasticsearch memcached mysql mongod postgresql sphinxsearch rethinkdb rabbitmq-server redis-server; do sudo service $s stop; done
sudo swapoff -a
sudo dd if=/dev/zero of=/swapfile bs=1M count=3072
sudo mkswap /swapfile
sudo swapon /swapfile
sudo rm -rf /home/runner/.rbenv
sudo rm -rf /usr/local/golang/{1.4.3,1.5.4,1.6.4,1.7.6,1.8.6,1.9.7,1.10.3,1.11}
#export DOCKER_VERSION=18.06.3
source .semaphoreci/vars
if [ -z "${PULL_REQUEST_NUMBER}" ]; then SHOULD_TEST="-*-"; else TEMP_STORAGE=$(curl --silent https://patch-diff.githubusercontent.com/raw/containous/traefik/pull/${PULL_REQUEST_NUMBER}.diff | patch --dry-run -p1 -R || true); fi
echo ${SHOULD_TEST}
if [ -n "$TEMP_STORAGE" ]; then SHOULD_TEST=$(echo "$TEMP_STORAGE" | grep -Ev '(.md|.yaml|.yml)' || :); fi
echo ${TEMP_STORAGE}
echo ${SHOULD_TEST}
#if [ -n "$SHOULD_TEST" ]; then sudo -E apt-get -yq update; fi
#if [ -n "$SHOULD_TEST" ]; then sudo -E apt-get -yq --no-install-suggests --no-install-recommends --force-yes install docker-ce=${DOCKER_VERSION}*; fi
if [ -n "$SHOULD_TEST" ]; then docker version; fi
export GO_VERSION=1.13
if [ -f "./go.mod" ]; then GO_VERSION="$(grep '^go .*' go.mod | awk '{print $2}')"; export GO_VERSION; fi
#if [ "${GO_VERSION}" == '1.14' ]; then export GO_VERSION=1.14rc2; fi
echo "Selected Go version: ${GO_VERSION}"
if [ -f "./.semaphoreci/golang.sh" ]; then ./.semaphoreci/golang.sh; fi
if [ -f "./.semaphoreci/golang.sh" ]; then export GOROOT="/usr/local/golang/${GO_VERSION}/go"; fi
if [ -f "./.semaphoreci/golang.sh" ]; then export GOTOOLDIR="/usr/local/golang/${GO_VERSION}/go/pkg/tool/linux_amd64"; fi
go version
if [ -f "./go.mod" ]; then export GO111MODULE=on; fi
if [ -f "./go.mod" ]; then export GOPROXY=https://proxy.golang.org; fi
if [ -f "./go.mod" ]; then go mod download; fi
df

36
.semaphoreci/vars Normal file
View File

@@ -0,0 +1,36 @@
#!/usr/bin/env bash
set -e
export REPO='containous/traefik'
if VERSION=$(git describe --exact-match --abbrev=0 --tags);
then
export VERSION
else
export VERSION=''
fi
export CODENAME=chevrotin
export N_MAKE_JOBS=2
function ci_retry {
local NRETRY=3
local NSLEEP=5
local n=0
until [ $n -ge $NRETRY ]
do
"$@" && break
n=$((n+1))
echo "${*} failed, attempt ${n}/${NRETRY}"
sleep $NSLEEP
done
[ $n -lt $NRETRY ]
}
export -f ci_retry

58
.travis.yml Normal file
View File

@@ -0,0 +1,58 @@
sudo: required
dist: trusty
git:
depth: false
services:
- docker
env:
global:
- REPO=$TRAVIS_REPO_SLUG
- VERSION=$TRAVIS_TAG
- CODENAME=chevrotin
- GO111MODULE=on
script:
- echo "Skipping tests... (Tests are executed on SemaphoreCI)"
- if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then make docs; fi
before_deploy:
- >
if ! [ "$BEFORE_DEPLOY_RUN" ]; then
export BEFORE_DEPLOY_RUN=1;
sudo -E apt-get -yq update;
sudo -E apt-get -yq --no-install-suggests --no-install-recommends --force-yes install docker-ce=${DOCKER_VERSION}*;
docker version;
make build-image;
if [ "$TRAVIS_TAG" ]; then
make release-packages;
fi;
curl -sfL https://raw.githubusercontent.com/containous/structor/master/godownloader.sh | bash -s -- -b "${GOPATH}/bin" ${STRUCTOR_VERSION}
curl -sSfL https://raw.githubusercontent.com/traefik/mixtus/master/godownloader.sh | sh -s -- -b "${GOPATH}/bin" ${MIXTUS_VERSION}
structor -o containous -r traefik --dockerfile-url="https://raw.githubusercontent.com/containous/traefik/v1.7/docs.Dockerfile" --menu.js-url="https://raw.githubusercontent.com/containous/structor/master/traefik-menu.js.gotmpl" --rqts-url="https://raw.githubusercontent.com/containous/structor/master/requirements-override.txt" --force-edit-url --exp-branch=master --debug;
fi
deploy:
- provider: releases
api_key: ${GITHUB_TOKEN}
file: dist/traefik*
skip_cleanup: true
file_glob: true
on:
repo: traefik/traefik
tags: true
- provider: script
script: sh script/deploy.sh
skip_cleanup: true
on:
repo: traefik/traefik
tags: true
- provider: script
script: 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=containous --src-repo-name=traefik
skip_cleanup: true
on:
repo: traefik/traefik
all_branches: true

BIN
.travis/traefiker_rsa.enc Normal file

Binary file not shown.

11463
CHANGELOG.md

File diff suppressed because it is too large Load Diff

View File

@@ -2,7 +2,7 @@
## Our Pledge
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience,nationality, personal appearance, race, religion, or sexual identity and orientation.
## Our Standards
@@ -30,38 +30,22 @@ Project maintainers have the right and responsibility to remove, edit, or reject
## Scope
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or our community.
Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event.
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community.
Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event.
Representation of a project may be further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at contact@traefik.io
All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances.
The project team is obligated to maintain confidentiality with regard to the reporter of an incident.
All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances.
The project team is obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
When an inappropriate behavior is reported, maintainers will discuss on the Maintainer's Discord before marking the message as "abuse".
This conversation beforehand avoids one-sided decisions.
The first message will be edited and marked as abuse.
The second edited message and marked as abuse results in a 7-day ban.
The third edited message and marked as abuse results in a permanent ban.
The content of edited messages is:
`Dear user, we want traefik to provide a welcoming and respectful environment. Your [comment/issue/PR] has been reported and marked as abuse according to our [Code of Conduct](./CODE_OF_CONDUCT.md). Thank you.`
The [report must be resolved](https://docs.github.com/en/communities/moderating-comments-and-conversations/managing-reported-content-in-your-organizations-repository#resolving-a-report) accordingly.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/
[version]: http://contributor-covenant.org/version/1/4/

View File

@@ -1,11 +1,4 @@
# Contributing
Here are some guidelines that should help to start contributing to the project.
- [Submitting pull Requests](https://doc.traefik.io/traefik/contributing/submitting-pull-requests/)
- [Submitting issues](https://doc.traefik.io/traefik/contributing/submitting-issues/)
- [Submitting security issues](https://doc.traefik.io/traefik/contributing/submitting-security-issues/)
- [Advocating for Traefik](https://doc.traefik.io/traefik/contributing/advocating/)
- [Triage Process](https://github.com/traefik/contributors-guide/blob/master/issue_triage.md)
If you are willing to become a maintainer of the project, please take a look at the [maintainers guidelines](docs/content/contributing/maintainers-guidelines.md).
- https://doc.traefik.io/traefik/contributing/submitting-pull-requests/
- https://doc.traefik.io/traefik/contributing/submitting-issues/

View File

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

View File

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

266
Makefile
View File

@@ -1,198 +1,152 @@
.PHONY: all docs docs-serve
SRCS = $(shell git ls-files '*.go' | grep -v '^vendor/')
TAG_NAME := $(shell git describe --abbrev=0 --tags --exact-match)
TAG_NAME := $(shell git tag -l --contains HEAD)
SHA := $(shell git rev-parse HEAD)
VERSION_GIT := $(if $(TAG_NAME),$(TAG_NAME),$(SHA))
VERSION := $(if $(VERSION),$(VERSION),$(VERSION_GIT))
BIN_NAME := traefik
CODENAME ?= cheddar
BIND_DIR := "dist"
DATE := $(shell date -u '+%Y-%m-%d_%I:%M:%S%p')
GIT_BRANCH := $(subst heads/,,$(shell git rev-parse --abbrev-ref HEAD 2>/dev/null))
TRAEFIK_DEV_IMAGE := traefik-dev$(if $(GIT_BRANCH),:$(subst /,-,$(GIT_BRANCH)))
# Default build target
GOOS := $(shell go env GOOS)
GOARCH := $(shell go env GOARCH)
GOGC ?=
REPONAME := $(shell echo $(REPO) | tr '[:upper:]' '[:lower:]')
TRAEFIK_IMAGE := $(if $(REPONAME),$(REPONAME),"containous/traefik")
LINT_EXECUTABLES = misspell shellcheck
INTEGRATION_OPTS := $(if $(MAKE_DOCKER_HOST),-e "DOCKER_HOST=$(MAKE_DOCKER_HOST)", -e "TEST_CONTAINER=1" -v "/var/run/docker.sock:/var/run/docker.sock")
DOCKER_BUILD_ARGS := $(if $(DOCKER_VERSION), "--build-arg=DOCKER_VERSION=$(DOCKER_VERSION)",)
DOCKER_BUILD_PLATFORMS ?= linux/amd64,linux/arm64
TRAEFIK_ENVS := \
-e OS_ARCH_ARG \
-e OS_PLATFORM_ARG \
-e TESTFLAGS \
-e VERBOSE \
-e VERSION \
-e CODENAME \
-e TESTDIRS \
-e CI \
-e CONTAINER=DOCKER # Indicator for integration tests that we are running inside a container.
.PHONY: default
#? default: Run `make generate` and `make binary`
default: generate binary
TRAEFIK_MOUNT := -v "$(CURDIR)/$(BIND_DIR):/go/src/github.com/containous/traefik/$(BIND_DIR)"
DOCKER_RUN_OPTS := $(TRAEFIK_ENVS) $(TRAEFIK_MOUNT) "$(TRAEFIK_DEV_IMAGE)"
DOCKER_NON_INTERACTIVE ?= false
DOCKER_RUN_TRAEFIK := docker run --add-host=host.docker.internal:127.0.0.1 $(INTEGRATION_OPTS) $(if $(DOCKER_NON_INTERACTIVE), , -it) $(DOCKER_RUN_OPTS)
DOCKER_RUN_TRAEFIK_NOTTY := docker run $(INTEGRATION_OPTS) $(if $(DOCKER_NON_INTERACTIVE), , -i) $(DOCKER_RUN_OPTS)
#? dist: Create the "dist" directory
PRE_TARGET ?= build-dev-image
default: binary
## Build Dev Docker image
build-dev-image: dist
docker build $(DOCKER_BUILD_ARGS) -t "$(TRAEFIK_DEV_IMAGE)" -f build.Dockerfile .
## Build Dev Docker image without cache
build-dev-image-no-cache: dist
docker build --no-cache -t "$(TRAEFIK_DEV_IMAGE)" -f build.Dockerfile .
## Create the "dist" directory
dist:
mkdir -p dist
mkdir dist
.PHONY: build-webui-image
#? build-webui-image: Build WebUI Docker 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
clean-webui:
rm -rf webui/static
## Generate WebUI
generate-webui: build-webui-image
if [ ! -d "static" ]; then \
mkdir -p static; \
docker run --rm -v "$$PWD/static":'/src/static' traefik-webui npm run build:nc; \
docker run --rm -v "$$PWD/static":'/src/static' traefik-webui chown -R $(shell id -u):$(shell id -g) ../static; \
echo 'For more informations show `webui/readme.md`' > $$PWD/static/DONT-EDIT-FILES-IN-THIS-DIRECTORY.md; \
fi
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 chown -R $(shell id -u):$(shell id -g) ./static
## Build the linux binary
binary: generate-webui $(PRE_TARGET)
$(if $(PRE_TARGET),$(DOCKER_RUN_TRAEFIK)) ./script/make.sh generate binary
.PHONY: generate-webui
#? generate-webui: Generate WebUI
generate-webui: webui/static/index.html
## Build the binary for the standard plaforms (linux, darwin, windows)
crossbinary-default: generate-webui build-dev-image
$(DOCKER_RUN_TRAEFIK_NOTTY) ./script/make.sh generate crossbinary-default
.PHONY: generate
#? generate: Generate code (Dynamic and Static configuration documentation reference files)
generate:
go generate
## Build the binary for the standard plaforms (linux, darwin, windows) in parallel
crossbinary-default-parallel:
$(MAKE) generate-webui
$(MAKE) build-dev-image crossbinary-default
.PHONY: binary
#? binary: Build the binary
binary: generate-webui dist
@echo SHA: $(VERSION) $(CODENAME) $(DATE)
CGO_ENABLED=0 GOGC=${GOGC} GOOS=${GOOS} GOARCH=${GOARCH} go build ${FLAGS[*]} -ldflags "-s -w \
-X github.com/traefik/traefik/v3/pkg/version.Version=$(VERSION) \
-X github.com/traefik/traefik/v3/pkg/version.Codename=$(CODENAME) \
-X github.com/traefik/traefik/v3/pkg/version.BuildDate=$(DATE)" \
-installsuffix nocgo -o "./dist/${GOOS}/${GOARCH}/$(BIN_NAME)" ./cmd/traefik
## Run the unit and integration tests
test: build-dev-image
$(DOCKER_RUN_TRAEFIK) ./script/make.sh generate test-unit binary test-integration
binary-linux-arm64: export GOOS := linux
binary-linux-arm64: export GOARCH := arm64
binary-linux-arm64:
@$(MAKE) binary
## Run the unit tests
test-unit: $(PRE_TARGET)
$(if $(PRE_TARGET),$(DOCKER_RUN_TRAEFIK)) ./script/make.sh generate test-unit
binary-linux-amd64: export GOOS := linux
binary-linux-amd64: export GOARCH := amd64
binary-linux-amd64:
@$(MAKE) binary
binary-windows-amd64: export GOOS := windows
binary-windows-amd64: export GOARCH := amd64
binary-windows-amd64: export BIN_NAME := traefik.exe
binary-windows-amd64:
@$(MAKE) binary
.PHONY: crossbinary-default
#? crossbinary-default: Build the binary for the standard platforms (linux, darwin, windows)
crossbinary-default: generate generate-webui
$(CURDIR)/script/crossbinary-default.sh
.PHONY: test
#? test: Run the unit and integration tests
test: test-ui-unit test-unit test-integration
.PHONY: test-unit
#? test-unit: Run the unit tests
test-unit:
GOOS=$(GOOS) GOARCH=$(GOARCH) go test -cover "-coverprofile=cover.out" -v $(TESTFLAGS) ./pkg/... ./cmd/...
.PHONY: test-integration
#? test-integration: Run the integration tests
test-integration:
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)
.PHONY: test-ui-unit
#? test-ui-unit: Run the unit tests for the webui
test-ui-unit:
$(MAKE) build-webui-image
docker run --rm -v "$(PWD)/webui/static":'/src/webui/static' traefik-webui yarn --cwd webui install
docker run --rm -v "$(PWD)/webui/static":'/src/webui/static' traefik-webui yarn --cwd webui test:unit:ci
.PHONY: pull-images
#? pull-images: Pull all Docker images to avoid timeout during integration tests
## Pull all images for integration tests
pull-images:
grep --no-filename -E '^\s+image:' ./integration/resources/compose/*.yml \
| awk '{print $$2}' \
| sort \
| uniq \
| xargs -P 6 -n 1 docker pull
grep --no-filename -E '^\s+image:' ./integration/resources/compose/*.yml | awk '{print $$2}' | sort | uniq | xargs -P 6 -n 1 docker pull
.PHONY: lint
#? lint: Run golangci-lint
lint:
golangci-lint run
## Run the integration tests
test-integration: $(PRE_TARGET)
$(if $(PRE_TARGET),$(DOCKER_RUN_TRAEFIK),TEST_CONTAINER=1) ./script/make.sh generate binary test-integration
TEST_HOST=1 ./script/make.sh test-integration
.PHONY: validate-files
#? validate-files: Validate code and docs
validate-files:
$(foreach exec,$(LINT_EXECUTABLES),\
$(if $(shell which $(exec)),,$(error "No $(exec) in PATH")))
$(CURDIR)/script/validate-vendor.sh
$(CURDIR)/script/validate-misspell.sh
$(CURDIR)/script/validate-shell-script.sh
## Validate code and docs
validate-files: $(PRE_TARGET)
$(if $(PRE_TARGET),$(DOCKER_RUN_TRAEFIK)) ./script/make.sh generate validate-lint validate-misspell
bash $(CURDIR)/script/validate-shell-script.sh
.PHONY: validate
#? validate: Validate code, docs, and vendor
validate: lint validate-files
## Validate code, docs, and vendor
validate: $(PRE_TARGET)
$(if $(PRE_TARGET),$(DOCKER_RUN_TRAEFIK)) ./script/make.sh generate validate-lint validate-misspell validate-vendor
bash $(CURDIR)/script/validate-shell-script.sh
# Target for building images for multiple architectures.
.PHONY: multi-arch-image-%
multi-arch-image-%: binary-linux-amd64 binary-linux-arm64
docker buildx build $(DOCKER_BUILDX_ARGS) -t traefik/traefik:$* --platform=$(DOCKER_BUILD_PLATFORMS) -f Dockerfile .
## Clean up static directory and build a Docker Traefik image
build-image: binary
rm -rf static
docker build -t $(TRAEFIK_IMAGE) .
## Build a Docker Traefik image
build-image-dirty: binary
docker build -t $(TRAEFIK_IMAGE) .
.PHONY: build-image
#? build-image: Clean up static directory and build a Docker Traefik image
build-image: export DOCKER_BUILDX_ARGS := --load
build-image: export DOCKER_BUILD_PLATFORMS := linux/$(GOARCH)
build-image: clean-webui
@$(MAKE) multi-arch-image-latest
## Start a shell inside the build env
shell: build-dev-image
$(DOCKER_RUN_TRAEFIK) /bin/bash
.PHONY: build-image-dirty
#? build-image-dirty: Build a Docker Traefik image without re-building the webui when it's already built
build-image-dirty: export DOCKER_BUILDX_ARGS := --load
build-image-dirty: export DOCKER_BUILD_PLATFORMS := linux/$(GOARCH)
build-image-dirty:
@$(MAKE) multi-arch-image-latest
.PHONY: docs
#? docs: Build documentation site
## Build documentation site
docs:
make -C ./docs docs
.PHONY: docs-serve
#? docs-serve: Serve the documentation site locally
## Serve the documentation site localy
docs-serve:
make -C ./docs docs-serve
.PHONY: docs-pull-images
#? docs-pull-images: Pull image for doc building
docs-pull-images:
make -C ./docs docs-pull-images
.PHONY: generate-crd
#? generate-crd: Generate CRD clientset and CRD manifests
## Generate CRD clientset
generate-crd:
@$(CURDIR)/script/code-gen.sh
./script/update-generated-crd-code.sh
.PHONY: generate-genconf
#? generate-genconf: Generate code from dynamic configuration github.com/traefik/genconf
generate-genconf:
go run ./cmd/internal/gen/
## Create packages for the release
release-packages: generate-webui build-dev-image
rm -rf dist
$(DOCKER_RUN_TRAEFIK_NOTTY) goreleaser release --skip-publish --timeout="60m"
$(DOCKER_RUN_TRAEFIK_NOTTY) tar cfz dist/traefik-${VERSION}.src.tar.gz \
--exclude-vcs \
--exclude .idea \
--exclude .travis \
--exclude .semaphoreci \
--exclude .github \
--exclude dist .
$(DOCKER_RUN_TRAEFIK_NOTTY) chown -R $(shell id -u):$(shell id -g) dist/
.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
## Format the Code
fmt:
gofmt -s -l -w $(SRCS)
.PHONY: help
#? help: Get more info on make commands
help: Makefile
@echo " Choose a command run in traefik:"
@sed -n 's/^#?//p' $< | column -t -s ':' | sort | sed -e 's/^/ /'
run-dev:
go generate
GO111MODULE=on go build ./cmd/traefik
./traefik

View File

@@ -1,21 +1,19 @@
<p align="center">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="docs/content/assets/img/traefik.logo-dark.png">
<source media="(prefers-color-scheme: light)" srcset="docs/content/assets/img/traefik.logo.png">
<img alt="Traefik" title="Traefik" src="docs/content/assets/img/traefik.logo.png">
</picture>
<img src="docs/content/assets/img/traefik.logo.png" alt="Traefik" title="Traefik" />
</p>
[![Build Status SemaphoreCI](https://traefik-oss.semaphoreci.com/badges/traefik/branches/master.svg?style=shields)](https://traefik-oss.semaphoreci.com/projects/traefik)
[![Build Status SemaphoreCI](https://semaphoreci.com/api/v1/containous/traefik/branches/master/shields_badge.svg)](https://semaphoreci.com/containous/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)
[![Go Report Card](https://goreportcard.com/badge/containous/traefik)](https://goreportcard.com/report/containous/traefik)
[![](https://images.microbadger.com/badges/image/traefik.svg)](https://microbadger.com/images/traefik)
[![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/containous/traefik/blob/master/LICENSE.md)
[![Join the community support forum at https://community.traefik.io/](https://img.shields.io/badge/style-register-green.svg?style=social&label=Discourse)](https://community.traefik.io/)
[![Twitter](https://img.shields.io/twitter/follow/traefik.svg?style=social)](https://twitter.com/intent/follow?screen_name=traefik)
Traefik (pronounced _traffic_) is a modern HTTP reverse proxy and load balancer that makes deploying microservices easy.
Traefik integrates with your existing infrastructure components ([Docker](https://www.docker.com/), [Swarm mode](https://docs.docker.com/engine/swarm/), [Kubernetes](https://kubernetes.io), [Consul](https://www.consul.io/), [Etcd](https://coreos.com/etcd/), [Rancher v2](https://rancher.com), [Amazon ECS](https://aws.amazon.com/ecs), ...) and configures itself automatically and dynamically.
Traefik integrates with your existing infrastructure components ([Docker](https://www.docker.com/), [Swarm mode](https://docs.docker.com/engine/swarm/), [Kubernetes](https://kubernetes.io), [Marathon](https://mesosphere.github.io/marathon/), [Consul](https://www.consul.io/), [Etcd](https://coreos.com/etcd/), [Rancher](https://rancher.com), [Amazon ECS](https://aws.amazon.com/ecs), ...) and configures itself automatically and dynamically.
Pointing Traefik at your orchestrator should be the _only_ configuration step you need.
---
@@ -35,8 +33,7 @@ Pointing Traefik at your orchestrator should be the _only_ configuration step yo
---
:warning: When migrating to a new major version of Traefik, please refer to the [migration guide](https://doc.traefik.io/traefik/migrate/v2-to-v3/) to ensure a smooth transition and to be aware of any breaking changes.
:warning: Please be aware that the old configurations for Traefik v1.x are NOT compatible with the v2.x config as of now. If you're running v2, please ensure you are using a [v2 configuration](https://doc.traefik.io/traefik/).
## Overview
@@ -59,21 +56,23 @@ _(But if you'd rather configure some of your routes manually, Traefik supports t
- Continuously updates its configuration (No restarts!)
- Supports multiple load balancing algorithms
- Provides HTTPS to your microservices by leveraging [Let's Encrypt](https://letsencrypt.org) (wildcard certificates support)
- Provides HTTPS to your microservices by leveraging [Let's Encrypt](https://letsencrypt.org) (wildcard certificates support)
- Circuit breakers, retry
- See the magic through its clean web UI
- WebSocket, HTTP/2, gRPC ready
- Provides metrics (Rest, Prometheus, Datadog, Statsd, InfluxDB 2.X)
- Websocket, HTTP/2, GRPC ready
- Provides metrics (Rest, Prometheus, Datadog, Statsd, InfluxDB)
- Keeps access logs (JSON, CLF)
- Fast
- Exposes a Rest API
- Packaged as a single binary file (made with :heart: with go) and available as an [official](https://hub.docker.com/r/_/traefik/) docker image
- Packaged as a single binary file (made with :heart: with go) and available as a [tiny](https://microbadger.com/images/traefik) [official](https://hub.docker.com/r/_/traefik/) docker image
## Supported Backends
- [Docker](https://doc.traefik.io/traefik/providers/docker/) / [Swarm mode](https://doc.traefik.io/traefik/providers/docker/)
- [Kubernetes](https://doc.traefik.io/traefik/providers/kubernetes-crd/)
- [ECS](https://doc.traefik.io/traefik/providers/ecs/)
- [Marathon](https://doc.traefik.io/traefik/providers/marathon/)
- [Rancher](https://doc.traefik.io/traefik/providers/rancher/) (Metadata)
- [File](https://doc.traefik.io/traefik/providers/file/)
## Quickstart
@@ -88,25 +87,28 @@ You can access the simple HTML frontend of Traefik.
## Documentation
You can find the complete documentation of Traefik v3 at [https://doc.traefik.io/traefik/](https://doc.traefik.io/traefik/).
You can find the complete documentation of Traefik v2 at [https://doc.traefik.io/traefik/](https://doc.traefik.io/traefik/).
If you are using Traefik v1, you can find the complete documentation at [https://doc.traefik.io/traefik/v1.7/](https://doc.traefik.io/traefik/v1.7/).
A collection of contributions around Traefik can be found at [https://awesome.traefik.io](https://awesome.traefik.io).
## Support
To get community support, you can:
- join the Traefik community forum: [![Join the chat at https://community.traefik.io/](https://img.shields.io/badge/style-register-green.svg?style=social&label=Discourse)](https://community.traefik.io/)
If you need commercial support, please contact [Traefik.io](https://traefik.io) by mail: <mailto:support@traefik.io>.
## Download
- Grab the latest binary from the [releases](https://github.com/traefik/traefik/releases) page and run it with the [sample configuration file](https://raw.githubusercontent.com/traefik/traefik/master/traefik.sample.toml):
- Grab the latest binary from the [releases](https://github.com/containous/traefik/releases) page and run it with the [sample configuration file](https://raw.githubusercontent.com/containous/traefik/master/traefik.sample.toml):
```shell
./traefik --configFile=traefik.toml
```
- Or use the official tiny Docker image and run it with the [sample configuration file](https://raw.githubusercontent.com/traefik/traefik/master/traefik.sample.toml):
- Or use the official tiny Docker image and run it with the [sample configuration file](https://raw.githubusercontent.com/containous/traefik/master/traefik.sample.toml):
```shell
docker run -d -p 8080:8080 -p 80:80 -v $PWD/traefik.toml:/etc/traefik/traefik.toml traefik
@@ -115,7 +117,7 @@ docker run -d -p 8080:8080 -p 80:80 -v $PWD/traefik.toml:/etc/traefik/traefik.to
- Or get the sources:
```shell
git clone https://github.com/traefik/traefik
git clone https://github.com/containous/traefik
```
## Introductory Videos
@@ -124,9 +126,7 @@ You can find high level and deep dive videos on [videos.traefik.io](https://vide
## Maintainers
We are strongly promoting a philosophy of openness and sharing, and firmly standing against the elitist closed approach. Being part of the core team should be accessible to anyone who is motivated and want to be part of that journey!
This [document](docs/content/contributing/maintainers-guidelines.md) describes how to be part of the [maintainers' team](docs/content/contributing/maintainers.md) as well as various responsibilities and guidelines for Traefik maintainers.
You can also find more information on our process to review pull requests and manage issues [in this document](https://github.com/traefik/contributors-guide/blob/master/issue_triage.md).
[Information about process and maintainers](docs/content/contributing/maintainers.md)
## Contributing
@@ -137,7 +137,7 @@ By participating in this project, you agree to abide by its terms.
## Release Cycle
- We usually release 3/4 new versions (e.g. 1.1.0, 1.2.0, 1.3.0) per year.
- We release a new version (e.g. 1.1.0, 1.2.0, 1.3.0) every other month.
- Release Candidates are available before the release (e.g. 1.1.0-rc1, 1.1.0-rc2, 1.1.0-rc3, 1.1.0-rc4, before 1.1.0).
- Bug-fixes (e.g. 1.1.1, 1.1.2, 1.2.1, 1.2.3) are released as needed (no additional features are delivered in those versions, bug-fixes only).
@@ -152,9 +152,9 @@ 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 logo ![logo](docs/content/assets/img/traefik.icon.png).
The gopher's logo of Traefik is licensed under the Creative Commons 3.0 Attributions license.
Traefik's logo is licensed under the Creative Commons 3.0 Attributions license.
The gopher's logo of Traefik was inspired by the gopher stickers made by [Takuya Ueda](https://twitter.com/tenntenn).
Traefik's logo was inspired by the gopher stickers made by [Takuya Ueda](https://twitter.com/tenntenn).
The original Go gopher was designed by [Renee French](https://reneefrench.blogspot.com/).

View File

@@ -1,30 +0,0 @@
# Security Policy
You can join our security mailing list to be aware of the latest announcements from our security team.
You can subscribe by sending an email to security+subscribe@traefik.io or on [the online viewer](https://groups.google.com/a/traefik.io/forum/#!forum/security).
Reported vulnerabilities can be found on [cve.mitre.org](https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=traefik).
## Supported Versions
- We usually release 3/4 new versions (e.g. 1.1.0, 1.2.0, 1.3.0) per year.
- Release Candidates are available before the release (e.g. 1.1.0-rc1, 1.1.0-rc2, 1.1.0-rc3, 1.1.0-rc4, before 1.1.0).
- Bug-fixes (e.g. 1.1.1, 1.1.2, 1.2.1, 1.2.3) are released as needed (no additional features are delivered in those versions, bug-fixes only).
Each version is supported until the next one is released (e.g. 1.1.x will be supported until 1.2.0 is out).
We use [Semantic Versioning](https://semver.org/).
| Version | Supported |
|-----------|--------------------|
| `2.2.x` | :white_check_mark: |
| `< 2.2.x` | :x: |
| `1.7.x` | :white_check_mark: |
| `< 1.7.x` | :x: |
## Reporting a Vulnerability
We want to keep Traefik safe for everyone.
If you've discovered a security vulnerability in Traefik,
we appreciate your help in disclosing it to us in a responsible manner,
by creating a [security advisory](https://github.com/traefik/traefik/security/advisories).

37
build.Dockerfile Normal file
View File

@@ -0,0 +1,37 @@
FROM golang:1.14-alpine
RUN apk --update upgrade \
&& apk --no-cache --no-progress add git mercurial bash gcc musl-dev curl tar ca-certificates tzdata \
&& update-ca-certificates \
&& rm -rf /var/cache/apk/*
# Which docker version to test on
ARG DOCKER_VERSION=18.09.7
# Download docker
RUN mkdir -p /usr/local/bin \
&& curl -fL https://download.docker.com/linux/static/stable/x86_64/docker-${DOCKER_VERSION}.tgz \
| tar -xzC /usr/local/bin --transform 's#^.+/##x'
# Download go-bindata binary to bin folder in $GOPATH
RUN mkdir -p /usr/local/bin \
&& curl -fsSL -o /usr/local/bin/go-bindata https://github.com/containous/go-bindata/releases/download/v1.0.0/go-bindata \
&& chmod +x /usr/local/bin/go-bindata
# Download golangci-lint binary to bin folder in $GOPATH
RUN curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | bash -s -- -b $GOPATH/bin v1.28.0
# Download misspell binary to bin folder in $GOPATH
RUN curl -sfL https://raw.githubusercontent.com/client9/misspell/master/install-misspell.sh | bash -s -- -b $GOPATH/bin v0.3.4
# Download goreleaser binary to bin folder in $GOPATH
RUN curl -sfL https://install.goreleaser.com/github.com/goreleaser/goreleaser.sh | sh
WORKDIR /go/src/github.com/containous/traefik
# Download go modules
COPY go.mod .
COPY go.sum .
RUN GO111MODULE=on GOPROXY=https://proxy.golang.org go mod download
COPY . /go/src/github.com/containous/traefik

View File

@@ -3,8 +3,8 @@ package cmd
import (
"time"
ptypes "github.com/traefik/paerser/types"
"github.com/traefik/traefik/v3/pkg/config/static"
"github.com/containous/traefik/v2/pkg/config/static"
"github.com/containous/traefik/v2/pkg/types"
)
// TraefikCmdConfiguration wraps the static configuration and extra parameters.
@@ -23,15 +23,11 @@ func NewTraefikConfiguration() *TraefikCmdConfiguration {
},
EntryPoints: make(static.EntryPoints),
Providers: &static.Providers{
ProvidersThrottleDuration: ptypes.Duration(2 * time.Second),
ProvidersThrottleDuration: types.Duration(2 * time.Second),
},
ServersTransport: &static.ServersTransport{
MaxIdleConnsPerHost: 200,
},
TCPServersTransport: &static.TCPServersTransport{
DialTimeout: ptypes.Duration(30 * time.Second),
DialKeepAlive: ptypes.Duration(15 * time.Second),
},
},
ConfigFile: "",
}

22
cmd/context.go Normal file
View File

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

View File

@@ -7,8 +7,8 @@ import (
"os"
"time"
"github.com/traefik/paerser/cli"
"github.com/traefik/traefik/v3/pkg/config/static"
"github.com/containous/traefik/v2/pkg/cli"
"github.com/containous/traefik/v2/pkg/config/static"
)
// NewCmd builds a new HealthCheck command.
@@ -64,7 +64,7 @@ func Do(staticConfiguration static.Configuration) (*http.Response, error) {
client := &http.Client{Timeout: 5 * time.Second}
protocol := "http"
// TODO Handle TLS on ping etc...
// FIXME Handle TLS on ping etc...
// if pingEntryPoint.TLS != nil {
// protocol = "https"
// tr := &http.Transport{

View File

@@ -1,332 +0,0 @@
package main
import (
"bytes"
"fmt"
"go/format"
"go/importer"
"go/token"
"go/types"
"io"
"log"
"os"
"path"
"path/filepath"
"reflect"
"slices"
"sort"
"strings"
"golang.org/x/tools/imports"
)
// File a kind of AST element that represents a file.
type File struct {
Package string
Imports []string
Elements []Element
}
// Element is a simplified version of a symbol.
type Element struct {
Name string
Value string
}
// Centrifuge a centrifuge.
// Generate Go Structures from Go structures.
type Centrifuge struct {
IncludedImports []string
ExcludedTypes []string
ExcludedFiles []string
TypeCleaner func(types.Type, string) string
PackageCleaner func(string) string
rootPkg string
fileSet *token.FileSet
pkg *types.Package
}
// NewCentrifuge creates a new Centrifuge.
func NewCentrifuge(rootPkg string) (*Centrifuge, error) {
fileSet := token.NewFileSet()
pkg, err := importer.ForCompiler(fileSet, "source", nil).Import(rootPkg)
if err != nil {
return nil, err
}
return &Centrifuge{
fileSet: fileSet,
pkg: pkg,
rootPkg: rootPkg,
TypeCleaner: func(typ types.Type, _ string) string {
return typ.String()
},
PackageCleaner: func(s string) string {
return s
},
}, nil
}
// Run runs the code extraction and the code generation.
func (c Centrifuge) Run(dest string, pkgName string) error {
files := c.run(c.pkg.Scope(), c.rootPkg, pkgName)
err := fileWriter{baseDir: dest}.Write(files)
if err != nil {
return err
}
for _, p := range c.pkg.Imports() {
if slices.Contains(c.IncludedImports, p.Path()) {
fls := c.run(p.Scope(), p.Path(), p.Name())
err = fileWriter{baseDir: filepath.Join(dest, p.Name())}.Write(fls)
if err != nil {
return err
}
}
}
return err
}
func (c Centrifuge) run(sc *types.Scope, rootPkg string, pkgName string) map[string]*File {
files := map[string]*File{}
for _, name := range sc.Names() {
if slices.Contains(c.ExcludedTypes, name) {
continue
}
o := sc.Lookup(name)
if !o.Exported() {
continue
}
filename := filepath.Base(c.fileSet.File(o.Pos()).Name())
if slices.Contains(c.ExcludedFiles, path.Join(rootPkg, filename)) {
continue
}
fl, ok := files[filename]
if !ok {
files[filename] = &File{Package: pkgName}
fl = files[filename]
}
elt := Element{
Name: name,
}
switch ob := o.(type) {
case *types.TypeName:
switch obj := ob.Type().(*types.Named).Underlying().(type) {
case *types.Struct:
elt.Value = c.writeStruct(name, obj, rootPkg, fl)
case *types.Map:
elt.Value = fmt.Sprintf("type %s map[%s]%s\n", name, obj.Key().String(), c.TypeCleaner(obj.Elem(), rootPkg))
case *types.Slice:
elt.Value = fmt.Sprintf("type %s []%v\n", name, c.TypeCleaner(obj.Elem(), rootPkg))
case *types.Basic:
elt.Value = fmt.Sprintf("type %s %v\n", name, obj.Name())
default:
log.Printf("OTHER TYPE::: %s %T\n", name, o.Type().(*types.Named).Underlying())
continue
}
default:
log.Printf("OTHER::: %s %T\n", name, o)
continue
}
if len(elt.Value) > 0 {
fl.Elements = append(fl.Elements, elt)
}
}
return files
}
func (c Centrifuge) writeStruct(name string, obj *types.Struct, rootPkg string, elt *File) string {
b := strings.Builder{}
b.WriteString(fmt.Sprintf("type %s struct {\n", name))
for i := range obj.NumFields() {
field := obj.Field(i)
if !field.Exported() {
continue
}
fPkg := c.PackageCleaner(extractPackage(field.Type()))
if fPkg != "" && fPkg != rootPkg {
elt.Imports = append(elt.Imports, fPkg)
}
fType := c.TypeCleaner(field.Type(), rootPkg)
if field.Embedded() {
b.WriteString(fmt.Sprintf("\t%s\n", fType))
continue
}
values, ok := lookupTagValue(obj.Tag(i), "json")
if len(values) > 0 && values[0] == "-" {
continue
}
b.WriteString(fmt.Sprintf("\t%s %s", field.Name(), fType))
if ok {
b.WriteString(fmt.Sprintf(" `json:\"%s\"`", strings.Join(values, ",")))
}
b.WriteString("\n")
}
b.WriteString("}\n")
return b.String()
}
func lookupTagValue(raw, key string) ([]string, bool) {
value, ok := reflect.StructTag(raw).Lookup(key)
if !ok {
return nil, ok
}
values := strings.Split(value, ",")
if len(values) < 1 {
return nil, true
}
return values, true
}
func extractPackage(t types.Type) string {
switch tu := t.(type) {
case *types.Named:
return tu.Obj().Pkg().Path()
case *types.Slice:
if v, ok := tu.Elem().(*types.Named); ok {
return v.Obj().Pkg().Path()
}
return ""
case *types.Map:
if v, ok := tu.Elem().(*types.Named); ok {
return v.Obj().Pkg().Path()
}
return ""
case *types.Pointer:
return extractPackage(tu.Elem())
default:
return ""
}
}
type fileWriter struct {
baseDir string
}
func (f fileWriter) Write(files map[string]*File) error {
err := os.MkdirAll(f.baseDir, 0o755)
if err != nil {
return err
}
for name, file := range files {
err = f.writeFile(name, file)
if err != nil {
return err
}
}
return nil
}
func (f fileWriter) writeFile(name string, desc *File) error {
if len(desc.Elements) == 0 {
return nil
}
filename := filepath.Join(f.baseDir, name)
file, err := os.Create(filename)
if err != nil {
return fmt.Errorf("failed to create file: %w", err)
}
defer func() { _ = file.Close() }()
b := bytes.NewBufferString("package ")
b.WriteString(desc.Package)
b.WriteString("\n")
b.WriteString("// Code generated by centrifuge. DO NOT EDIT.\n")
b.WriteString("\n")
f.writeImports(b, desc.Imports)
b.WriteString("\n")
for _, elt := range desc.Elements {
b.WriteString(elt.Value)
b.WriteString("\n")
}
// gofmt
source, err := format.Source(b.Bytes())
if err != nil {
log.Println(b.String())
return fmt.Errorf("failed to format sources: %w", err)
}
// goimports
process, err := imports.Process(filename, source, nil)
if err != nil {
log.Println(string(source))
return fmt.Errorf("failed to format imports: %w", err)
}
_, err = file.Write(process)
if err != nil {
return err
}
return nil
}
func (f fileWriter) writeImports(b io.StringWriter, imports []string) {
if len(imports) == 0 {
return
}
uniq := map[string]struct{}{}
sort.Strings(imports)
_, _ = b.WriteString("import (\n")
for _, s := range imports {
if _, exist := uniq[s]; exist {
continue
}
uniq[s] = struct{}{}
_, _ = b.WriteString(fmt.Sprintf(` "%s"`+"\n", s))
}
_, _ = b.WriteString(")\n")
}

View File

@@ -1,124 +0,0 @@
package main
import (
"fmt"
"go/build"
"go/types"
"log"
"os"
"path"
"path/filepath"
"strings"
)
const rootPkg = "github.com/traefik/traefik/v3/pkg/config/dynamic"
const (
destModuleName = "github.com/traefik/genconf"
destPkg = "dynamic"
)
const marsh = `package %s
import "encoding/json"
type JSONPayload struct {
*Configuration
}
func (c JSONPayload) MarshalJSON() ([]byte, error) {
if c.Configuration == nil {
return nil, nil
}
return json.Marshal(c.Configuration)
}
`
// main generate Go Structures from Go structures.
// Allows to create an external module (destModuleName) used by the plugin's providers
// that contains Go structs of the dynamic configuration and nothing else.
// These Go structs do not have any non-exported fields and do not rely on any external dependencies.
func main() {
dest := filepath.Join(path.Join(build.Default.GOPATH, "src"), destModuleName, destPkg)
log.Println("Output:", dest)
err := run(dest)
if err != nil {
log.Fatal(err)
}
}
func run(dest string) error {
centrifuge, err := NewCentrifuge(rootPkg)
if err != nil {
return err
}
centrifuge.IncludedImports = []string{
"github.com/traefik/traefik/v3/pkg/tls",
"github.com/traefik/traefik/v3/pkg/types",
}
centrifuge.ExcludedTypes = []string{
// tls
"CertificateStore", "Manager",
// dynamic
"Message", "Configurations",
// types
"HTTPCodeRanges", "HostResolverConfig",
}
centrifuge.ExcludedFiles = []string{
"github.com/traefik/traefik/v3/pkg/types/logs.go",
"github.com/traefik/traefik/v3/pkg/types/metrics.go",
}
centrifuge.TypeCleaner = cleanType
centrifuge.PackageCleaner = cleanPackage
err = centrifuge.Run(dest, destPkg)
if err != nil {
return err
}
return os.WriteFile(filepath.Join(dest, "marshaler.go"), []byte(fmt.Sprintf(marsh, destPkg)), 0o666)
}
func cleanType(typ types.Type, base string) string {
if typ.String() == "github.com/traefik/traefik/v3/pkg/types.FileOrContent" {
return "string"
}
if typ.String() == "[]github.com/traefik/traefik/v3/pkg/types.FileOrContent" {
return "[]string"
}
if typ.String() == "github.com/traefik/paerser/types.Duration" {
return "string"
}
if strings.Contains(typ.String(), base) {
return strings.ReplaceAll(typ.String(), base+".", "")
}
if strings.Contains(typ.String(), "github.com/traefik/traefik/v3/pkg/") {
return strings.ReplaceAll(typ.String(), "github.com/traefik/traefik/v3/pkg/", "")
}
return typ.String()
}
func cleanPackage(src string) string {
switch src {
case "github.com/traefik/paerser/types":
return ""
case "github.com/traefik/traefik/v3/pkg/tls":
return path.Join(destModuleName, destPkg, "tls")
case "github.com/traefik/traefik/v3/pkg/types":
return path.Join(destModuleName, destPkg, "types")
default:
return src
}
}

View File

@@ -1,114 +0,0 @@
package main
import (
"context"
"errors"
"fmt"
"io"
stdlog "log"
"os"
"strings"
"time"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"github.com/sirupsen/logrus"
"github.com/traefik/traefik/v3/pkg/config/static"
"github.com/traefik/traefik/v3/pkg/logs"
"gopkg.in/natefinch/lumberjack.v2"
)
func init() {
// hide the first logs before the setup of the logger.
zerolog.SetGlobalLevel(zerolog.ErrorLevel)
}
func setupLogger(ctx context.Context, 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.
if staticConfiguration.Log != nil && staticConfiguration.Log.OTLP != nil &&
(staticConfiguration.Experimental == nil || !staticConfiguration.Experimental.OTLPLogs) {
return errors.New("the experimental OTLPLogs feature must be enabled to use OTLP logging")
}
// configure log format
w := getLogWriter(staticConfiguration)
// configure log level
logLevel := getLogLevel(staticConfiguration)
zerolog.SetGlobalLevel(logLevel)
// create logger
logger := zerolog.New(w).With().Timestamp()
if logLevel <= zerolog.DebugLevel {
logger = logger.Caller()
}
log.Logger = logger.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)
if err != nil {
return fmt.Errorf("setting up OpenTelemetry logger: %w", err)
}
}
zerolog.DefaultContextLogger = &log.Logger
// Global logrus replacement (related to lib like go-rancher-metadata, docker, etc.)
logrus.StandardLogger().Out = logs.NoLevel(log.Logger, zerolog.DebugLevel)
// configure default standard log.
stdlog.SetFlags(stdlog.Lshortfile | stdlog.LstdFlags)
stdlog.SetOutput(logs.NoLevel(log.Logger, zerolog.DebugLevel))
return nil
}
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)
w = &lumberjack.Logger{
Filename: staticConfiguration.Log.FilePath,
MaxSize: staticConfiguration.Log.MaxSize,
MaxBackups: staticConfiguration.Log.MaxBackups,
MaxAge: staticConfiguration.Log.MaxAge,
Compress: true,
}
}
if staticConfiguration.Log == nil || staticConfiguration.Log.Format != "json" {
w = zerolog.ConsoleWriter{
Out: w,
TimeFormat: time.RFC3339,
NoColor: staticConfiguration.Log != nil && (staticConfiguration.Log.NoColor || len(staticConfiguration.Log.FilePath) > 0),
}
}
return w
}
func getLogLevel(staticConfiguration *static.Configuration) zerolog.Level {
levelStr := "error"
if staticConfiguration.Log != nil && staticConfiguration.Log.Level != "" {
levelStr = strings.ToLower(staticConfiguration.Log.Level)
}
logLevel, err := zerolog.ParseLevel(strings.ToLower(levelStr))
if err != nil {
log.Error().Err(err).
Str("logLevel", levelStr).
Msg("Unspecified or invalid log level, setting the level to default (ERROR)...")
logLevel = zerolog.ErrorLevel
}
return logLevel
}

View File

@@ -1,83 +0,0 @@
package main
import (
"fmt"
"github.com/traefik/traefik/v3/pkg/config/static"
"github.com/traefik/traefik/v3/pkg/plugins"
)
const outputDir = "./plugins-storage/"
func createPluginBuilder(staticConfiguration *static.Configuration) (*plugins.Builder, error) {
client, plgs, localPlgs, err := initPlugins(staticConfiguration)
if err != nil {
return nil, err
}
return plugins.NewBuilder(client, plgs, localPlgs)
}
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 client *plugins.Client
plgs := map[string]plugins.Descriptor{}
if hasPlugins(staticCfg) {
opts := plugins.ClientOptions{
Output: outputDir,
}
var err error
client, err = plugins.NewClient(opts)
if err != nil {
return nil, nil, nil, fmt.Errorf("unable to create plugins client: %w", err)
}
err = plugins.SetupRemotePlugins(client, staticCfg.Experimental.Plugins)
if err != nil {
return nil, nil, nil, fmt.Errorf("unable to set up plugins environment: %w", err)
}
plgs = staticCfg.Experimental.Plugins
}
localPlgs := map[string]plugins.LocalDescriptor{}
if hasLocalPlugins(staticCfg) {
err := plugins.SetupLocalPlugins(staticCfg.Experimental.LocalPlugins)
if err != nil {
return nil, nil, nil, err
}
localPlgs = staticCfg.Experimental.LocalPlugins
}
return client, plgs, localPlgs, nil
}
func checkUniquePluginNames(e *static.Experimental) error {
if e == nil {
return nil
}
for s := range e.LocalPlugins {
if _, ok := e.Plugins[s]; ok {
return fmt.Errorf("the plugin's name %q must be unique", s)
}
}
return nil
}
func hasPlugins(staticCfg *static.Configuration) bool {
return staticCfg.Experimental != nil && len(staticCfg.Experimental.Plugins) > 0
}
func hasLocalPlugins(staticCfg *static.Configuration) bool {
return staticCfg.Experimental != nil && len(staticCfg.Experimental.LocalPlugins) > 0
}

View File

@@ -2,60 +2,47 @@ package main
import (
"context"
"crypto/x509"
"fmt"
"io"
"encoding/json"
stdlog "log"
"maps"
"net/http"
"os"
"os/signal"
"slices"
"path/filepath"
"sort"
"strings"
"syscall"
"time"
"github.com/coreos/go-systemd/v22/daemon"
"github.com/go-acme/lego/v4/challenge"
gokitmetrics "github.com/go-kit/kit/metrics"
"github.com/rs/zerolog/log"
"github.com/containous/traefik/v2/autogen/genstatic"
"github.com/containous/traefik/v2/cmd"
"github.com/containous/traefik/v2/cmd/healthcheck"
cmdVersion "github.com/containous/traefik/v2/cmd/version"
"github.com/containous/traefik/v2/pkg/cli"
"github.com/containous/traefik/v2/pkg/collector"
"github.com/containous/traefik/v2/pkg/config/dynamic"
"github.com/containous/traefik/v2/pkg/config/static"
"github.com/containous/traefik/v2/pkg/log"
"github.com/containous/traefik/v2/pkg/metrics"
"github.com/containous/traefik/v2/pkg/middlewares/accesslog"
"github.com/containous/traefik/v2/pkg/provider/acme"
"github.com/containous/traefik/v2/pkg/provider/aggregator"
"github.com/containous/traefik/v2/pkg/provider/traefik"
"github.com/containous/traefik/v2/pkg/safe"
"github.com/containous/traefik/v2/pkg/server"
"github.com/containous/traefik/v2/pkg/server/middleware"
"github.com/containous/traefik/v2/pkg/server/service"
traefiktls "github.com/containous/traefik/v2/pkg/tls"
"github.com/containous/traefik/v2/pkg/types"
"github.com/containous/traefik/v2/pkg/version"
"github.com/coreos/go-systemd/daemon"
assetfs "github.com/elazarl/go-bindata-assetfs"
"github.com/sirupsen/logrus"
"github.com/spiffe/go-spiffe/v2/workloadapi"
"github.com/traefik/paerser/cli"
"github.com/traefik/traefik/v3/cmd"
"github.com/traefik/traefik/v3/cmd/healthcheck"
cmdVersion "github.com/traefik/traefik/v3/cmd/version"
tcli "github.com/traefik/traefik/v3/pkg/cli"
"github.com/traefik/traefik/v3/pkg/collector"
"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/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/vulcand/oxy/roundrobin"
)
func main() {
// traefik config inits
tConfig := cmd.NewTraefikConfiguration()
loaders := []cli.ResourceLoader{&tcli.DeprecationLoader{}, &tcli.FileLoader{}, &tcli.FlagLoader{}, &tcli.EnvLoader{}}
loaders := []cli.ResourceLoader{&cli.FileLoader{}, &cli.FlagLoader{}, &cli.EnvLoader{}}
cmdTraefik := &cli.Command{
Name: "traefik",
@@ -82,7 +69,7 @@ Complete documentation is available at https://traefik.io`,
err = cli.Execute(cmdTraefik)
if err != nil {
log.Error().Err(err).Msg("Command error")
stdlog.Println(err)
logrus.Exit(1)
}
@@ -90,28 +77,31 @@ 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 {
return fmt.Errorf("setting up logger: %w", err)
}
configureLogging(staticConfiguration)
http.DefaultTransport.(*http.Transport).Proxy = http.ProxyFromEnvironment
if err := roundrobin.SetDefaultWeight(0); err != nil {
log.WithoutContext().Errorf("Could not set round robin default weight: %v", err)
}
staticConfiguration.SetEffectiveConfiguration()
if err := staticConfiguration.ValidateConfiguration(); err != nil {
return err
}
log.Info().Str("version", version.Version).
Msgf("Traefik version %s built on %s", version.Version, version.BuildDate)
log.WithoutContext().Infof("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.WithoutContext().Errorf("Could not marshal static configuration: %v", err)
log.WithoutContext().Debugf("Static configuration loaded [struct] %#v", staticConfiguration)
} else {
log.Debug().RawJSON("staticConfiguration", []byte(redactedStaticConfiguration)).Msg("Static configuration loaded [json]")
log.WithoutContext().Debugf("Static configuration loaded %s", string(jsonConf))
}
if staticConfiguration.API != nil && staticConfiguration.API.Dashboard {
staticConfiguration.API.DashboardAssets = &assetfs.AssetFS{Asset: genstatic.Asset, AssetInfo: genstatic.AssetInfo, AssetDir: genstatic.AssetDir, Prefix: "static"}
}
if staticConfiguration.Global.CheckNewVersion {
@@ -125,6 +115,8 @@ func runCmd(staticConfiguration *static.Configuration) error {
return err
}
ctx := cmd.ContextWithSignal(context.Background())
if staticConfiguration.Ping != nil {
staticConfiguration.Ping.WithContext(ctx)
}
@@ -134,16 +126,16 @@ func runCmd(staticConfiguration *static.Configuration) error {
sent, err := daemon.SdNotify(false, "READY=1")
if !sent && err != nil {
log.Error().Err(err).Msg("Failed to notify")
log.WithoutContext().Errorf("Failed to notify: %v", err)
}
t, err := daemon.SdWatchdogEnabled(false)
if err != nil {
log.Error().Err(err).Msg("Could not enable Watchdog")
log.WithoutContext().Errorf("Could not enable Watchdog: %v", err)
} else if t != 0 {
// Send a ping each half time given
t /= 2
log.Info().Msgf("Watchdog activated with timer duration %s", t)
log.WithoutContext().Infof("Watchdog activated with timer duration %s", t)
safe.Go(func() {
tick := time.Tick(t)
for range tick {
@@ -154,69 +146,34 @@ func runCmd(staticConfiguration *static.Configuration) error {
if staticConfiguration.Ping == nil || errHealthCheck == nil {
if ok, _ := daemon.SdNotify(false, "WATCHDOG=1"); !ok {
log.Error().Msg("Fail to tick watchdog")
log.WithoutContext().Error("Fail to tick watchdog")
}
} else {
log.Error().Err(errHealthCheck).Send()
log.WithoutContext().Error(errHealthCheck)
}
}
})
}
svr.Wait()
log.Info().Msg("Shutting down")
log.WithoutContext().Info("Shutting down")
return nil
}
func setupServer(staticConfiguration *static.Configuration) (*server.Server, error) {
providerAggregator := aggregator.NewProviderAggregator(*staticConfiguration.Providers)
ctx := context.Background()
routinesPool := safe.NewPool(ctx)
// adds internal provider
err := providerAggregator.AddProvider(traefik.New(*staticConfiguration))
if err != nil {
return nil, err
}
// ACME
tlsManager := traefiktls.NewManager()
tlsManager := traefiktls.NewManager(staticConfiguration.OCSP)
routinesPool.GoCtx(tlsManager.Run)
acmeProviders := initACMEProvider(staticConfiguration, &providerAggregator, tlsManager)
httpChallengeProvider := acme.NewChallengeHTTP()
tlsChallengeProvider := acme.NewChallengeTLSALPN()
err = providerAggregator.AddProvider(tlsChallengeProvider)
if err != nil {
return nil, err
}
acmeProviders := initACMEProvider(staticConfiguration, providerAggregator, tlsManager, httpChallengeProvider, tlsChallengeProvider, routinesPool)
// Tailscale
tsProviders := initTailscaleProviders(staticConfiguration, providerAggregator)
// Observability
metricRegistries := registerMetricClients(staticConfiguration.Metrics)
var semConvMetricRegistry *metrics.SemConvMetricsRegistry
if staticConfiguration.Metrics != nil && staticConfiguration.Metrics.OTLP != nil {
semConvMetricRegistry, err = metrics.NewSemConvMetricRegistry(ctx, staticConfiguration.Metrics.OTLP)
if err != nil {
return nil, fmt.Errorf("unable to create SemConv metric registry: %w", err)
}
}
metricsRegistry := metrics.NewMultiRegistry(metricRegistries)
accessLog := setupAccessLog(ctx, staticConfiguration.AccessLog)
tracer, tracerCloser := setupTracing(ctx, staticConfiguration.Tracing)
observabilityMgr := middleware.NewObservabilityMgr(*staticConfiguration, metricsRegistry, semConvMetricRegistry, accessLog, tracer, tracerCloser)
// Entrypoints
serverEntryPointsTCP, err := server.NewTCPEntryPoints(staticConfiguration.EntryPoints, staticConfiguration.HostResolver, metricsRegistry)
serverEntryPointsTCP, err := server.NewTCPEntryPoints(staticConfiguration.EntryPoints)
if err != nil {
return nil, err
}
@@ -226,205 +183,21 @@ func setupServer(staticConfiguration *static.Configuration) (*server.Server, err
return nil, err
}
if staticConfiguration.API != nil {
version.DisableDashboardAd = staticConfiguration.API.DisableDashboardAd
}
ctx := context.Background()
routinesPool := safe.NewPool(ctx)
// Plugins
pluginLogger := log.Ctx(ctx).With().Logger()
hasPlugins := staticConfiguration.Experimental != nil && (staticConfiguration.Experimental.Plugins != nil || staticConfiguration.Experimental.LocalPlugins != nil)
if hasPlugins {
pluginsList := slices.Collect(maps.Keys(staticConfiguration.Experimental.Plugins))
pluginsList = append(pluginsList, slices.Collect(maps.Keys(staticConfiguration.Experimental.LocalPlugins))...)
metricsRegistry := registerMetricClients(staticConfiguration.Metrics)
accessLog := setupAccessLog(staticConfiguration.AccessLog)
chainBuilder := middleware.NewChainBuilder(*staticConfiguration, metricsRegistry, accessLog)
managerFactory := service.NewManagerFactory(*staticConfiguration, routinesPool, metricsRegistry)
routerFactory := server.NewRouterFactory(*staticConfiguration, managerFactory, tlsManager, chainBuilder)
pluginLogger = pluginLogger.With().Strs("plugins", pluginsList).Logger()
pluginLogger.Info().Msg("Loading plugins...")
}
pluginBuilder, err := createPluginBuilder(staticConfiguration)
if err != nil && staticConfiguration.Experimental != nil && staticConfiguration.Experimental.AbortOnPluginFailure {
return nil, fmt.Errorf("plugin: failed to create plugin builder: %w", err)
}
if err != nil {
pluginLogger.Err(err).Msg("Plugins are disabled because an error has occurred.")
} else if hasPlugins {
pluginLogger.Info().Msg("Plugins loaded.")
}
// Providers plugins
for name, conf := range staticConfiguration.Providers.Plugin {
if pluginBuilder == nil {
break
}
p, err := pluginBuilder.BuildProvider(name, conf)
if err != nil {
return nil, fmt.Errorf("plugin: failed to build provider: %w", err)
}
err = providerAggregator.AddProvider(p)
if err != nil {
return nil, fmt.Errorf("plugin: failed to add provider: %w", err)
}
}
// Service manager factory
var spiffeX509Source *workloadapi.X509Source
if staticConfiguration.Spiffe != nil && staticConfiguration.Spiffe.WorkloadAPIAddr != "" {
log.Info().Str("workloadAPIAddr", staticConfiguration.Spiffe.WorkloadAPIAddr).
Msg("Waiting on SPIFFE SVID delivery")
spiffeX509Source, err = workloadapi.NewX509Source(
ctx,
workloadapi.WithClientOptions(
workloadapi.WithAddr(
staticConfiguration.Spiffe.WorkloadAPIAddr,
),
),
)
if err != nil {
return nil, fmt.Errorf("unable to create SPIFFE x509 source: %w", err)
}
log.Info().Msg("Successfully obtained SPIFFE SVID.")
}
transportManager := service.NewTransportManager(spiffeX509Source)
var proxyBuilder service.ProxyBuilder = httputil.NewProxyBuilder(transportManager, semConvMetricRegistry)
if staticConfiguration.Experimental != nil && staticConfiguration.Experimental.FastProxy != nil {
proxyBuilder = proxy.NewSmartBuilder(transportManager, proxyBuilder, *staticConfiguration.Experimental.FastProxy)
}
dialerManager := tcp.NewDialerManager(spiffeX509Source)
acmeHTTPHandler := getHTTPChallengeHandler(acmeProviders, httpChallengeProvider)
managerFactory := service.NewManagerFactory(*staticConfiguration, routinesPool, observabilityMgr, transportManager, proxyBuilder, acmeHTTPHandler)
// Router factory
routerFactory, err := server.NewRouterFactory(*staticConfiguration, managerFactory, tlsManager, observabilityMgr, pluginBuilder, dialerManager)
if err != nil {
return nil, fmt.Errorf("creating router factory: %w", err)
}
// Watcher
watcher := server.NewConfigurationWatcher(
routinesPool,
providerAggregator,
getDefaultsEntrypoints(staticConfiguration),
"internal",
)
// TLS
watcher.AddListener(func(conf dynamic.Configuration) {
ctx := context.Background()
tlsManager.UpdateConfigs(ctx, conf.TLS.Stores, conf.TLS.Options, conf.TLS.Certificates)
gauge := metricsRegistry.TLSCertsNotAfterTimestampGauge()
for _, certificate := range tlsManager.GetServerCertificates() {
appendCertMetric(gauge, certificate)
}
})
// Metrics
watcher.AddListener(func(_ dynamic.Configuration) {
metricsRegistry.ConfigReloadsCounter().Add(1)
metricsRegistry.LastConfigReloadSuccessGauge().Set(float64(time.Now().Unix()))
})
// Server Transports
watcher.AddListener(func(conf dynamic.Configuration) {
transportManager.Update(conf.HTTP.ServersTransports)
proxyBuilder.Update(conf.HTTP.ServersTransports)
dialerManager.Update(conf.TCP.ServersTransports)
})
// Switch router
watcher.AddListener(switchRouter(routerFactory, serverEntryPointsTCP, serverEntryPointsUDP))
// Metrics
if metricsRegistry.IsEpEnabled() || metricsRegistry.IsRouterEnabled() || metricsRegistry.IsSvcEnabled() {
var eps []string
for key := range serverEntryPointsTCP {
eps = append(eps, key)
}
watcher.AddListener(func(conf dynamic.Configuration) {
metrics.OnConfigurationUpdate(conf, eps)
})
}
// TLS challenge
watcher.AddListener(tlsChallengeProvider.ListenConfiguration)
// Certificate Resolvers
resolverNames := map[string]struct{}{}
// ACME
for _, p := range acmeProviders {
resolverNames[p.ResolverName] = struct{}{}
watcher.AddListener(p.ListenConfiguration)
}
// Tailscale
for _, p := range tsProviders {
resolverNames[p.ResolverName] = struct{}{}
watcher.AddListener(p.HandleConfigUpdate)
}
// Certificate resolver logs
watcher.AddListener(func(config dynamic.Configuration) {
for rtName, rt := range config.HTTP.Routers {
if rt.TLS == nil || rt.TLS.CertResolver == "" {
continue
}
if _, ok := resolverNames[rt.TLS.CertResolver]; !ok {
log.Error().Err(err).Str(logs.RouterName, rtName).Str("certificateResolver", rt.TLS.CertResolver).
Msg("Router uses a nonexistent certificate resolver")
}
}
})
return server.NewServer(routinesPool, serverEntryPointsTCP, serverEntryPointsUDP, watcher, observabilityMgr), nil
}
func getHTTPChallengeHandler(acmeProviders []*acme.Provider, httpChallengeProvider http.Handler) http.Handler {
var acmeHTTPHandler http.Handler
for _, p := range acmeProviders {
if p != nil && p.HTTPChallenge != nil {
acmeHTTPHandler = httpChallengeProvider
break
}
}
return acmeHTTPHandler
}
func getDefaultsEntrypoints(staticConfiguration *static.Configuration) []string {
var defaultEntryPoints []string
// Determines if at least one EntryPoint is configured to be used by default.
var hasDefinedDefaults bool
for _, ep := range staticConfiguration.EntryPoints {
if ep.AsDefault {
hasDefinedDefaults = true
break
}
}
for name, cfg := range staticConfiguration.EntryPoints {
// By default all entrypoints are considered.
// If at least one is flagged, then only flagged entrypoints are included.
if hasDefinedDefaults && !cfg.AsDefault {
continue
}
protocol, err := cfg.GetProtocol()
if err != nil {
// Should never happen because Traefik should not start if protocol is invalid.
log.Error().Err(err).Msg("Invalid protocol")
log.WithoutContext().Errorf("Invalid protocol: %v", err)
}
if protocol != "udp" && name != static.DefaultInternalEntryPointName {
@@ -432,186 +205,218 @@ func getDefaultsEntrypoints(staticConfiguration *static.Configuration) []string
}
}
slices.Sort(defaultEntryPoints)
return defaultEntryPoints
sort.Strings(defaultEntryPoints)
watcher := server.NewConfigurationWatcher(
routinesPool,
providerAggregator,
time.Duration(staticConfiguration.Providers.ProvidersThrottleDuration),
defaultEntryPoints,
)
watcher.AddListener(func(conf dynamic.Configuration) {
ctx := context.Background()
tlsManager.UpdateConfigs(ctx, conf.TLS.Stores, conf.TLS.Options, conf.TLS.Certificates)
})
watcher.AddListener(func(_ dynamic.Configuration) {
metricsRegistry.ConfigReloadsCounter().Add(1)
metricsRegistry.LastConfigReloadSuccessGauge().Set(float64(time.Now().Unix()))
})
watcher.AddListener(switchRouter(routerFactory, acmeProviders, serverEntryPointsTCP, serverEntryPointsUDP))
watcher.AddListener(func(conf dynamic.Configuration) {
if metricsRegistry.IsEpEnabled() || metricsRegistry.IsSvcEnabled() {
var eps []string
for key := range serverEntryPointsTCP {
eps = append(eps, key)
}
metrics.OnConfigurationUpdate(conf, eps)
}
})
resolverNames := map[string]struct{}{}
for _, p := range acmeProviders {
resolverNames[p.ResolverName] = struct{}{}
watcher.AddListener(p.ListenConfiguration)
}
watcher.AddListener(func(config dynamic.Configuration) {
for rtName, rt := range config.HTTP.Routers {
if rt.TLS == nil || rt.TLS.CertResolver == "" {
continue
}
if _, ok := resolverNames[rt.TLS.CertResolver]; !ok {
log.WithoutContext().Errorf("the router %s uses a non-existent resolver: %s", rtName, rt.TLS.CertResolver)
}
}
})
return server.NewServer(routinesPool, serverEntryPointsTCP, serverEntryPointsUDP, watcher, chainBuilder, accessLog), nil
}
func switchRouter(routerFactory *server.RouterFactory, serverEntryPointsTCP server.TCPEntryPoints, serverEntryPointsUDP server.UDPEntryPoints) func(conf dynamic.Configuration) {
func switchRouter(routerFactory *server.RouterFactory, acmeProviders []*acme.Provider, serverEntryPointsTCP server.TCPEntryPoints, serverEntryPointsUDP server.UDPEntryPoints) func(conf dynamic.Configuration) {
return func(conf dynamic.Configuration) {
rtConf := runtime.NewConfig(conf)
routers, udpRouters := routerFactory.CreateRouters(rtConf)
routers, udpRouters := routerFactory.CreateRouters(conf)
for entryPointName, rt := range routers {
for _, p := range acmeProviders {
if p != nil && p.HTTPChallenge != nil && p.HTTPChallenge.EntryPoint == entryPointName {
rt.HTTPHandler(p.CreateHandler(rt.GetHTTPHandler()))
break
}
}
}
serverEntryPointsTCP.Switch(routers)
serverEntryPointsUDP.Switch(udpRouters)
}
}
// initACMEProvider creates and registers acme.Provider instances corresponding to the configured ACME certificate resolvers.
func initACMEProvider(c *static.Configuration, providerAggregator *aggregator.ProviderAggregator, tlsManager *traefiktls.Manager, httpChallengeProvider, tlsChallengeProvider challenge.Provider, routinesPool *safe.Pool) []*acme.Provider {
// initACMEProvider creates an acme provider from the ACME part of globalConfiguration.
func initACMEProvider(c *static.Configuration, providerAggregator *aggregator.ProviderAggregator, tlsManager *traefiktls.Manager) []*acme.Provider {
challengeStore := acme.NewLocalChallengeStore()
localStores := map[string]*acme.LocalStore{}
var resolvers []*acme.Provider
for name, resolver := range c.CertificatesResolvers {
if resolver.ACME == nil {
continue
if resolver.ACME != nil {
if localStores[resolver.ACME.Storage] == nil {
localStores[resolver.ACME.Storage] = acme.NewLocalStore(resolver.ACME.Storage)
}
p := &acme.Provider{
Configuration: resolver.ACME,
Store: localStores[resolver.ACME.Storage],
ChallengeStore: challengeStore,
ResolverName: name,
}
if err := providerAggregator.AddProvider(p); err != nil {
log.WithoutContext().Errorf("The ACME resolver %q is skipped from the resolvers list because: %v", name, err)
continue
}
p.SetTLSManager(tlsManager)
if p.TLSChallenge != nil {
tlsManager.TLSAlpnGetter = p.GetTLSALPNCertificate
}
p.SetConfigListenerChan(make(chan dynamic.Configuration))
resolvers = append(resolvers, p)
}
if localStores[resolver.ACME.Storage] == nil {
localStores[resolver.ACME.Storage] = acme.NewLocalStore(resolver.ACME.Storage, routinesPool)
}
p := &acme.Provider{
Configuration: resolver.ACME,
Store: localStores[resolver.ACME.Storage],
ResolverName: name,
HTTPChallengeProvider: httpChallengeProvider,
TLSChallengeProvider: tlsChallengeProvider,
}
if err := providerAggregator.AddProvider(p); err != nil {
log.Error().Err(err).Str("resolver", name).Msg("The ACME resolve is skipped from the resolvers list")
continue
}
p.SetTLSManager(tlsManager)
p.SetConfigListenerChan(make(chan dynamic.Configuration))
resolvers = append(resolvers, p)
}
return resolvers
}
// initTailscaleProviders creates and registers tailscale.Provider instances corresponding to the configured Tailscale certificate resolvers.
func initTailscaleProviders(cfg *static.Configuration, providerAggregator *aggregator.ProviderAggregator) []*tailscale.Provider {
var providers []*tailscale.Provider
for name, resolver := range cfg.CertificatesResolvers {
if resolver.Tailscale == nil {
continue
}
tsProvider := &tailscale.Provider{ResolverName: name}
if err := providerAggregator.AddProvider(tsProvider); err != nil {
log.Error().Err(err).Str(logs.ProviderName, name).Msg("Unable to create Tailscale provider")
continue
}
providers = append(providers, tsProvider)
}
return providers
}
func registerMetricClients(metricsConfig *types.Metrics) []metrics.Registry {
func registerMetricClients(metricsConfig *types.Metrics) metrics.Registry {
if metricsConfig == nil {
return nil
return metrics.NewVoidRegistry()
}
var registries []metrics.Registry
if metricsConfig.Prometheus != nil {
logger := log.With().Str(logs.MetricsProviderName, "prometheus").Logger()
prometheusRegister := metrics.RegisterPrometheus(logger.WithContext(context.Background()), metricsConfig.Prometheus)
ctx := log.With(context.Background(), log.Str(log.MetricsProviderName, "prometheus"))
prometheusRegister := metrics.RegisterPrometheus(ctx, metricsConfig.Prometheus)
if prometheusRegister != nil {
registries = append(registries, prometheusRegister)
logger.Debug().Msg("Configured Prometheus metrics")
log.FromContext(ctx).Debug("Configured Prometheus metrics")
}
}
if metricsConfig.Datadog != nil {
logger := log.With().Str(logs.MetricsProviderName, "datadog").Logger()
registries = append(registries, metrics.RegisterDatadog(logger.WithContext(context.Background()), metricsConfig.Datadog))
logger.Debug().
Str("address", metricsConfig.Datadog.Address).
Str("pushInterval", metricsConfig.Datadog.PushInterval.String()).
Msgf("Configured Datadog metrics")
ctx := log.With(context.Background(), log.Str(log.MetricsProviderName, "datadog"))
registries = append(registries, metrics.RegisterDatadog(ctx, metricsConfig.Datadog))
log.FromContext(ctx).Debugf("Configured Datadog metrics: pushing to %s once every %s",
metricsConfig.Datadog.Address, metricsConfig.Datadog.PushInterval)
}
if metricsConfig.StatsD != nil {
logger := log.With().Str(logs.MetricsProviderName, "statsd").Logger()
registries = append(registries, metrics.RegisterStatsd(logger.WithContext(context.Background()), metricsConfig.StatsD))
logger.Debug().
Str("address", metricsConfig.StatsD.Address).
Str("pushInterval", metricsConfig.StatsD.PushInterval.String()).
Msg("Configured StatsD metrics")
ctx := log.With(context.Background(), log.Str(log.MetricsProviderName, "statsd"))
registries = append(registries, metrics.RegisterStatsd(ctx, metricsConfig.StatsD))
log.FromContext(ctx).Debugf("Configured StatsD metrics: pushing to %s once every %s",
metricsConfig.StatsD.Address, metricsConfig.StatsD.PushInterval)
}
if metricsConfig.InfluxDB2 != nil {
logger := log.With().Str(logs.MetricsProviderName, "influxdb2").Logger()
influxDB2Register := metrics.RegisterInfluxDB2(logger.WithContext(context.Background()), metricsConfig.InfluxDB2)
if influxDB2Register != nil {
registries = append(registries, influxDB2Register)
logger.Debug().
Str("address", metricsConfig.InfluxDB2.Address).
Str("bucket", metricsConfig.InfluxDB2.Bucket).
Str("organization", metricsConfig.InfluxDB2.Org).
Str("pushInterval", metricsConfig.InfluxDB2.PushInterval.String()).
Msg("Configured InfluxDB v2 metrics")
}
if metricsConfig.InfluxDB != nil {
ctx := log.With(context.Background(), log.Str(log.MetricsProviderName, "influxdb"))
registries = append(registries, metrics.RegisterInfluxDB(ctx, metricsConfig.InfluxDB))
log.FromContext(ctx).Debugf("Configured InfluxDB metrics: pushing to %s once every %s",
metricsConfig.InfluxDB.Address, metricsConfig.InfluxDB.PushInterval)
}
if metricsConfig.OTLP != nil {
logger := log.With().Str(logs.MetricsProviderName, "openTelemetry").Logger()
openTelemetryRegistry := metrics.RegisterOpenTelemetry(logger.WithContext(context.Background()), metricsConfig.OTLP)
if openTelemetryRegistry != nil {
registries = append(registries, openTelemetryRegistry)
logger.Debug().
Str("pushInterval", metricsConfig.OTLP.PushInterval.String()).
Msg("Configured OpenTelemetry metrics")
}
}
return registries
return metrics.NewMultiRegistry(registries)
}
func appendCertMetric(gauge gokitmetrics.Gauge, certificate *x509.Certificate) {
slices.Sort(certificate.DNSNames)
labels := []string{
"cn", certificate.Subject.CommonName,
"serial", certificate.SerialNumber.String(),
"sans", strings.Join(certificate.DNSNames, ","),
}
notAfter := float64(certificate.NotAfter.Unix())
gauge.With(labels...).Set(notAfter)
}
func setupAccessLog(ctx context.Context, conf *types.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")
log.WithoutContext().Warnf("Unable to create access logger : %v", err)
return nil
}
return accessLoggerMiddleware
}
func setupTracing(ctx context.Context, conf *static.Tracing) (*tracing.Tracer, io.Closer) {
if conf == nil {
return nil, nil
func configureLogging(staticConfiguration *static.Configuration) {
// configure default log flags
stdlog.SetFlags(stdlog.Lshortfile | stdlog.LstdFlags)
// configure log level
// an explicitly defined log level always has precedence. if none is
// given and debug mode is disabled, the default is ERROR, and DEBUG
// otherwise.
levelStr := "error"
if staticConfiguration.Log != nil && staticConfiguration.Log.Level != "" {
levelStr = strings.ToLower(staticConfiguration.Log.Level)
}
tracer, closer, err := tracing.NewTracing(ctx, conf)
level, err := logrus.ParseLevel(levelStr)
if err != nil {
log.Warn().Err(err).Msg("Unable to create tracer")
return nil, nil
log.WithoutContext().Errorf("Error getting level: %v", err)
}
log.SetLevel(level)
var logFile string
if staticConfiguration.Log != nil && len(staticConfiguration.Log.FilePath) > 0 {
logFile = staticConfiguration.Log.FilePath
}
return tracer, closer
// configure log format
var formatter logrus.Formatter
if staticConfiguration.Log != nil && staticConfiguration.Log.Format == "json" {
formatter = &logrus.JSONFormatter{}
} else {
disableColors := len(logFile) > 0
formatter = &logrus.TextFormatter{DisableColors: disableColors, FullTimestamp: true, DisableSorting: true}
}
log.SetFormatter(formatter)
if len(logFile) > 0 {
dir := filepath.Dir(logFile)
if err := os.MkdirAll(dir, 0o755); err != nil {
log.WithoutContext().Errorf("Failed to create log path %s: %s", dir, err)
}
err = log.OpenFile(logFile)
logrus.RegisterExitHandler(func() {
if err := log.CloseFile(); err != nil {
log.WithoutContext().Errorf("Error while closing log: %v", err)
}
})
if err != nil {
log.WithoutContext().Errorf("Error while opening log file %s: %v", logFile, err)
}
}
}
func checkNewVersion() {
@@ -624,16 +429,16 @@ func checkNewVersion() {
}
func stats(staticConfiguration *static.Configuration) {
logger := log.With().Logger()
logger := log.WithoutContext()
if staticConfiguration.Global.SendAnonymousUsage {
logger.Info().Msg(`Stats collection is enabled.`)
logger.Info().Msg(`Many thanks for contributing to Traefik's improvement by allowing us to receive anonymous information from your configuration.`)
logger.Info().Msg(`Help us improve Traefik by leaving this feature on :)`)
logger.Info().Msg(`More details on: https://doc.traefik.io/traefik/contributing/data-collection/`)
logger.Info(`Stats collection is enabled.`)
logger.Info(`Many thanks for contributing to Traefik's improvement by allowing us to receive anonymous information from your configuration.`)
logger.Info(`Help us improve Traefik by leaving this feature on :)`)
logger.Info(`More details on: https://doc.traefik.io/traefik/contributing/data-collection/`)
collect(staticConfiguration)
} else {
logger.Info().Msg(`
logger.Info(`
Stats collection is disabled.
Help us improve Traefik by turning this feature on :)
More details on: https://doc.traefik.io/traefik/contributing/data-collection/
@@ -646,7 +451,7 @@ func collect(staticConfiguration *static.Configuration) {
safe.Go(func() {
for time.Sleep(10 * time.Minute); ; <-ticker {
if err := collector.Collect(staticConfiguration); err != nil {
log.Debug().Err(err).Send()
log.WithoutContext().Debug(err)
}
}
})

View File

@@ -1,186 +0,0 @@
package main
import (
"crypto/x509"
"encoding/pem"
"strings"
"testing"
"github.com/go-kit/kit/metrics"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/traefik/traefik/v3/pkg/config/static"
)
// FooCert is a PEM-encoded TLS cert.
// generated from src/crypto/tls:
// go run generate_cert.go --rsa-bits 1024 --host foo.org,foo.com --ca --start-date "Jan 1 00:00:00 1970" --duration=1000000h
const fooCert = `-----BEGIN CERTIFICATE-----
MIICHzCCAYigAwIBAgIQXQFLeYRwc5X21t457t2xADANBgkqhkiG9w0BAQsFADAS
MRAwDgYDVQQKEwdBY21lIENvMCAXDTcwMDEwMTAwMDAwMFoYDzIwODQwMTI5MTYw
MDAwWjASMRAwDgYDVQQKEwdBY21lIENvMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCB
iQKBgQDCjn67GSs/khuGC4GNN+tVo1S+/eSHwr/hWzhfMqO7nYiXkFzmxi+u14CU
Pda6WOeps7T2/oQEFMxKKg7zYOqkLSbjbE0ZfosopaTvEsZm/AZHAAvoOrAsIJOn
SEiwy8h0tLA4z1SNR6rmIVQWyqBZEPAhBTQM1z7tFp48FakCFwIDAQABo3QwcjAO
BgNVHQ8BAf8EBAMCAqQwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDwYDVR0TAQH/BAUw
AwEB/zAdBgNVHQ4EFgQUDHG3ASzeUezElup9zbPpBn/vjogwGwYDVR0RBBQwEoIH
Zm9vLm9yZ4IHZm9vLmNvbTANBgkqhkiG9w0BAQsFAAOBgQBT+VLMbB9u27tBX8Aw
ZrGY3rbNdBGhXVTksrjiF+6ZtDpD3iI56GH9zLxnqvXkgn3u0+Ard5TqF/xmdwVw
NY0V/aWYfcL2G2auBCQrPvM03ozRnVUwVfP23eUzX2ORNHCYhd2ObQx4krrhs7cJ
SWxtKwFlstoXY3K2g9oRD9UxdQ==
-----END CERTIFICATE-----`
// BarCert is a PEM-encoded TLS cert.
// generated from src/crypto/tls:
// go run generate_cert.go --rsa-bits 1024 --host bar.org,bar.com --ca --start-date "Jan 1 00:00:00 1970" --duration=10000h
const barCert = `-----BEGIN CERTIFICATE-----
MIICHTCCAYagAwIBAgIQcuIcNEXzBHPoxna5S6wG4jANBgkqhkiG9w0BAQsFADAS
MRAwDgYDVQQKEwdBY21lIENvMB4XDTcwMDEwMTAwMDAwMFoXDTcxMDIyMTE2MDAw
MFowEjEQMA4GA1UEChMHQWNtZSBDbzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkC
gYEAqtcrP+KA7D6NjyztGNIPMup9KiBMJ8QL+preog/YHR7SQLO3kGFhpS3WKMab
SzMypC3ZX1PZjBP5ZzwaV3PFbuwlCkPlyxR2lOWmullgI7mjY0TBeYLDIclIzGRp
mpSDDSpkW1ay2iJDSpXjlhmwZr84hrCU7BRTQJo91fdsRTsCAwEAAaN0MHIwDgYD
VR0PAQH/BAQDAgKkMBMGA1UdJQQMMAoGCCsGAQUFBwMBMA8GA1UdEwEB/wQFMAMB
Af8wHQYDVR0OBBYEFK8jnzFQvBAgWtfzOyXY4VSkwrTXMBsGA1UdEQQUMBKCB2Jh
ci5vcmeCB2Jhci5jb20wDQYJKoZIhvcNAQELBQADgYEAJz0ifAExisC/ZSRhWuHz
7qs1i6Nd4+YgEVR8dR71MChP+AMxucY1/ajVjb9xlLys3GPE90TWSdVppabEVjZY
Oq11nPKc50ItTt8dMku6t0JHBmzoGdkN0V4zJCBqdQJxhop8JpYJ0S9CW0eT93h3
ipYQSsmIINGtMXJ8VkP/MlM=
-----END CERTIFICATE-----`
type gaugeMock struct {
metrics map[string]float64
labels string
}
func (g gaugeMock) With(labelValues ...string) metrics.Gauge {
g.labels = strings.Join(labelValues, ",")
return g
}
func (g gaugeMock) Set(value float64) {
g.metrics[g.labels] = value
}
func (g gaugeMock) Add(delta float64) {
panic("implement me")
}
func TestAppendCertMetric(t *testing.T) {
testCases := []struct {
desc string
certs []string
expected map[string]float64
}{
{
desc: "No certs",
certs: []string{},
expected: map[string]float64{},
},
{
desc: "One cert",
certs: []string{fooCert},
expected: map[string]float64{
"cn,,serial,123624926713171615935660664614975025408,sans,foo.com,foo.org": 3.6e+09,
},
},
{
desc: "Two certs",
certs: []string{fooCert, barCert},
expected: map[string]float64{
"cn,,serial,123624926713171615935660664614975025408,sans,foo.com,foo.org": 3.6e+09,
"cn,,serial,152706022658490889223053211416725817058,sans,bar.com,bar.org": 3.6e+07,
},
},
}
for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
gauge := &gaugeMock{
metrics: map[string]float64{},
}
for _, cert := range test.certs {
block, _ := pem.Decode([]byte(cert))
parsedCert, err := x509.ParseCertificate(block.Bytes)
require.NoError(t, err)
appendCertMetric(gauge, parsedCert)
}
assert.Equal(t, test.expected, gauge.metrics)
})
}
}
func TestGetDefaultsEntrypoints(t *testing.T) {
testCases := []struct {
desc string
entrypoints static.EntryPoints
expected []string
}{
{
desc: "Skips special names",
entrypoints: map[string]*static.EntryPoint{
"web": {
Address: ":80",
},
"traefik": {
Address: ":8080",
},
},
expected: []string{"web"},
},
{
desc: "Two EntryPoints not attachable",
entrypoints: map[string]*static.EntryPoint{
"web": {
Address: ":80",
},
"websecure": {
Address: ":443",
},
},
expected: []string{"web", "websecure"},
},
{
desc: "Two EntryPoints only one attachable",
entrypoints: map[string]*static.EntryPoint{
"web": {
Address: ":80",
},
"websecure": {
Address: ":443",
AsDefault: true,
},
},
expected: []string{"websecure"},
},
{
desc: "Two attachable EntryPoints",
entrypoints: map[string]*static.EntryPoint{
"web": {
Address: ":80",
AsDefault: true,
},
"websecure": {
Address: ":443",
AsDefault: true,
},
},
expected: []string{"web", "websecure"},
},
}
for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) {
actual := getDefaultsEntrypoints(&static.Configuration{
EntryPoints: test.entrypoints,
})
assert.ElementsMatch(t, test.expected, actual)
})
}
}

View File

@@ -7,8 +7,8 @@ import (
"runtime"
"text/template"
"github.com/traefik/paerser/cli"
"github.com/traefik/traefik/v3/pkg/version"
"github.com/containous/traefik/v2/pkg/cli"
"github.com/containous/traefik/v2/pkg/version"
)
var versionTemplate = `Version: {{.Version}}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
[Unit]
Description=Traefik
Documentation=https://doc.traefik.io/traefik/
Documentation=https://doc.traefik.io
#After=network-online.target
#AssertFileIsExecutable=/usr/bin/traefik
#AssertPathExists=/etc/traefik/traefik.toml

View File

@@ -4,7 +4,6 @@
"MD009": false,
"MD013": false,
"MD024": false,
"MD025": false,
"MD026": false,
"MD033": false,
"MD034": false,

View File

@@ -1,3 +1,4 @@
#######
# This Makefile contains all targets related to the documentation
#######
@@ -11,55 +12,41 @@ TRAEFIK_DOCS_CHECK_IMAGE ?= $(TRAEFIK_DOCS_BUILD_IMAGE)-check
SITE_DIR := $(CURDIR)/site
DOCKER_RUN_DOC_PORT := 8000
DOCKER_RUN_DOC_MOUNTS := -v $(CURDIR):/mkdocs
DOCKER_RUN_DOC_MOUNTS := -v $(CURDIR):/mkdocs
DOCKER_RUN_DOC_OPTS := --rm $(DOCKER_RUN_DOC_MOUNTS) -p $(DOCKER_RUN_DOC_PORT):8000
# Default: generates the documentation into $(SITE_DIR)
.PHONY: docs
docs: docs-clean docs-image docs-lint docs-build docs-verify
# Writer Mode: build and serve docs on http://localhost:8000 with livereload
.PHONY: docs-serve
docs-serve: docs-image
docker run $(DOCKER_RUN_DOC_OPTS) $(TRAEFIK_DOCS_BUILD_IMAGE) mkdocs serve
## Pull image for doc building
.PHONY: docs-pull-images
docs-pull-images:
grep --no-filename -E '^FROM' ./*.Dockerfile \
| awk '{print $$2}' \
| sort \
| uniq \
| xargs -P 6 -n 1 docker pull
# Utilities Targets for each step
.PHONY: docs-image
docs-image:
docker build -t $(TRAEFIK_DOCS_BUILD_IMAGE) -f docs.Dockerfile ./
.PHONY: docs-build
docs-build: docs-image
docker run $(DOCKER_RUN_DOC_OPTS) $(TRAEFIK_DOCS_BUILD_IMAGE) sh -c "mkdocs build \
&& chown -R $(shell id -u):$(shell id -g) ./site"
.PHONY: docs-verify
docs-verify: docs-build
ifneq ("$(DOCS_VERIFY_SKIP)", "true")
docker build -t $(TRAEFIK_DOCS_CHECK_IMAGE) -f check.Dockerfile ./
docker run --rm -v $(CURDIR):/app $(TRAEFIK_DOCS_CHECK_IMAGE) /verify.sh
else
echo "DOCS_VERIFY_SKIP is true: no verification done."
endif
@if [ "$(DOCS_VERIFY_SKIP)" != "true" ]; then \
docker build -t $(TRAEFIK_DOCS_CHECK_IMAGE) -f check.Dockerfile ./; \
docker run --rm -v $(CURDIR):/app $(TRAEFIK_DOCS_CHECK_IMAGE) /verify.sh; \
else \
echo "DOCS_VERIFY_SKIP is true: no verification done."; \
fi
.PHONY: docs-lint
docs-lint:
ifneq ("$(DOCS_LINT_SKIP)", "true")
docker build -t $(TRAEFIK_DOCS_CHECK_IMAGE) -f check.Dockerfile ./
docker run --rm -v $(CURDIR):/app $(TRAEFIK_DOCS_CHECK_IMAGE) /lint.sh
else
echo "DOCS_LINT_SKIP is true: no linting done."
endif
@if [ "$(DOCS_LINT_SKIP)" != "true" ]; then \
docker build -t $(TRAEFIK_DOCS_CHECK_IMAGE) -f check.Dockerfile ./ && \
docker run --rm -v $(CURDIR):/app $(TRAEFIK_DOCS_CHECK_IMAGE) /lint.sh; \
else \
echo "DOCS_LINT_SKIP is true: no linting done."; \
fi
.PHONY: docs-clean
docs-clean:
rm -rf $(SITE_DIR)
.PHONY: all docs-verify docs docs-clean docs-build docs-lint

View File

@@ -1,19 +1,14 @@
FROM alpine:3.22
FROM alpine:3.15 as alpine
RUN apk --no-cache --no-progress add \
build-base \
gcompat \
libcurl \
libxml2-dev \
libxslt-dev \
ruby \
ruby-bigdecimal \
ruby-dev \
ruby-etc \
ruby-ffi \
zlib-dev
RUN gem install nokogiri --version 1.18.6 --no-document -- --use-system-libraries
RUN gem install html-proofer --version 5.0.10 --no-document -- --use-system-libraries
ruby-json \
ruby-nokogiri
RUN gem install html-proofer --version 3.19.3 --no-document -- --use-system-libraries
# After Ruby, some NodeJS YAY!
RUN apk --no-cache --no-progress add \
@@ -21,9 +16,12 @@ RUN apk --no-cache --no-progress add \
nodejs \
npm
# To handle 'not get uid/gid'
RUN npm config set unsafe-perm true
RUN npm install --global \
markdownlint@0.29.0 \
markdownlint-cli@0.35.0
markdownlint@0.17.2 \
markdownlint-cli@0.19.0
# Finally the shell tools we need for later
# tini helps to terminate properly all the parallelized tasks when sending CTRL-C

View File

@@ -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;
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 520 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 610 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 878 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 791 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 603 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 966 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 482 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 731 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 715 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 733 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 358 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1010 KiB

After

Width:  |  Height:  |  Size: 452 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 241 KiB

After

Width:  |  Height:  |  Size: 125 KiB

View File

@@ -0,0 +1,96 @@
/*
Atom One Light by Daniel Gamage
Original One Light Syntax theme from https://github.com/atom/one-light-syntax
base: #fafafa
mono-1: #383a42
mono-2: #686b77
mono-3: #a0a1a7
hue-1: #0184bb
hue-2: #4078f2
hue-3: #a626a4
hue-4: #50a14f
hue-5: #e45649
hue-5-2: #c91243
hue-6: #986801
hue-6-2: #c18401
*/
.hljs {
display: block;
overflow-x: auto;
padding: 0.5em;
color: #383a42;
background: #fafafa;
}
.hljs-comment,
.hljs-quote {
color: #a0a1a7;
font-style: italic;
}
.hljs-doctag,
.hljs-keyword,
.hljs-formula {
color: #a626a4;
}
.hljs-section,
.hljs-name,
.hljs-selector-tag,
.hljs-deletion,
.hljs-subst {
color: #e45649;
}
.hljs-literal {
color: #0184bb;
}
.hljs-string,
.hljs-regexp,
.hljs-addition,
.hljs-attribute,
.hljs-meta-string {
color: #50a14f;
}
.hljs-built_in,
.hljs-class .hljs-title {
color: #c18401;
}
.hljs-attr,
.hljs-variable,
.hljs-template-variable,
.hljs-type,
.hljs-selector-class,
.hljs-selector-attr,
.hljs-selector-pseudo,
.hljs-number {
color: #986801;
}
.hljs-symbol,
.hljs-bullet,
.hljs-link,
.hljs-meta,
.hljs-selector-id,
.hljs-title {
color: #4078f2;
}
.hljs-emphasis {
font-style: italic;
}
.hljs-strong {
font-weight: bold;
}
.hljs-link {
text-decoration: underline;
}

View File

@@ -0,0 +1,70 @@
.md-container {
padding-top: 0;
}
.md-content h1 {
color: var(--dark) !important;
font-weight: bold !important;
}
.md-content a {
color: var(--blue) !important;
}
.md-content a:hover {
font-weight: bold !important;
}
.md-typeset p code,
.md-typeset .codehilite,
.md-typeset .highlight {
background-color: var(--light-blue) !important;
}
.md-typeset table:not([class]) th {
background: var(--dark) !important;
color: white !important;
}
/* Front page image size */
img[src$='#small'] {
width: 150px;
}
img[src$='#medium'] {
width: 300px;
}
/* Center table and objects */
.center,
img,
.md-typeset__table {
display: block !important;
margin: 0 auto;
}
.md-typeset table:not([class]) tr td:first-child {
text-align: left;
}
.md-typeset table:not([class]) th:not([align]),
.md-typeset table:not([class]) td:not([align]) {
text-align: center;
}
article p:not([class]),
article ul:not([class]),
article ol:not([class]) {
padding-left: 0.8em !important;
}
/* Fix for Chrome */
.md-typeset__table td code {
word-break: unset;
}
.md-typeset__table tr :nth-child(1) {
word-wrap: break-word;
max-width: 30em;
}
p {
text-align: justify;
}

View File

@@ -0,0 +1,10 @@
.md-footer-meta {
background-color: var(--dark);
}
.md-footer-privacy-policy {
margin: 0 .6rem;
padding: .4rem 0;
color: hsla(0,0%,100%,.3);
font-size: .64rem;
}

View File

@@ -0,0 +1,484 @@
@import url('https://fonts.googleapis.com/css?family=Rubik:300i,400,400i,500,500i,700&amp;display=swap');
.wrapper-1200 {
width: 100%;
max-width: 61rem;
margin: 0 auto;
padding: 0 .6rem;
}
@media (max-width: 700px) {
.wrapper-1200 {
padding: 0 20px;
}
}
.btn-type-1 {
outline: none;
border: none;
background-color: #1e54d5;
line-height: 1em;
border-radius: 8px;
padding: 12px 15px;
text-transform: uppercase;
letter-spacing: 0.05em;
font-size: 1.25rem;
background-image: linear-gradient(to top, rgba(0, 0, 0, 0.28) 1%, #1e54d5 99%);
font-weight: 500;
text-align: center;
color: white;
transition: all 0.2s;
}
.button--secondary {
outline: none;
border: 2px solid #1e54d5 !important;
background: transparent;
line-height: 1em;
border-radius: 8px;
padding: 9px 13px;
letter-spacing: 0;
font-size: 1.3rem;
font-weight: 500;
text-align: center;
color: #1e54d5;
transition: all 0.2s;
display: inline-block;
}
.button--secondary:hover {
color: white !important;
background: #1e54d5;
}
.button--secondary:focus {
color: white !important;
background: #1e54d5;
}
.site-header-and-placeholder-wrapper {
position: relative;
height: 64px;
}
.site-header {
position: fixed;
width: 100%;
top: 0;
left: 0;
transition: height 0.1s;
z-index: 100;
background: white;
box-shadow: 0 0 7px 0 #00000021;
border-bottom: 1px solid #e2e2e2;
height: 64px;
display: flex;
align-items: center;
font-size: 10px;
font-family: 'Rubik', -apple-system, 'BlinkMacSystemFont', 'Segoe UI',
'Helvetica Neue', sans-serif;
color: #06102a;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
.site-header.scrolled {
box-shadow: 0 0 5px 0 #00000028;
position: fixed;
top: 0;
height: 52px;
}
.site-header.scrolled .site-header__title a {
font-size: 2.2em;
}
.header-placeholder {
background: none;
width: 100%;
height: 64px;
position: absolute;
}
.header-placeholder.active {
display: block;
}
.site-header .wrapper-1200 {
display: flex;
justify-content: space-between;
align-items: center;
}
.site-header .wrapper-1200 .left {
display: flex;
align-items: center;
justify-content: flex-start;
}
.site-header__logo {
max-width: 145px;
}
.site-header__title a {
color: #06102a;
font-size: 2.2em;
font-weight: 500;
transition: all 0.2s;
text-transform: uppercase;
letter-spacing: 0.02em;
}
/* Navigation */
.site-header__nav .menu-item-wrapper {
display: inline-block;
padding-left: 30px;
}
.site-header__nav .menu-item {
color: #06102a;
transition: all 0.05s;
font-size: 1.45em;
line-height: 1em;
font-weight: 500;
}
.site-header__nav .menu-item:hover {
color: #8a959e;
}
.site-header__nav .menu-item--with-icon {
display: flex;
align-items: center;
justify-content: flex-start;
}
.site-header__nav .menu-item--with-icon .title {
margin-right: 3px;
}
.site-header__nav .menu-item--with-icon .icon {
width: 20px;
height: 20px;
transition: all 0.1s;
}
.site-header__nav .menu-item--with-icon .icon svg {
stroke-width: 2.5 !important;
width: 100%;
height: 100%;
}
.site-header__nav .menu-item-wrapper--dropdown {
position: relative;
}
.site-header__nav .menu-item-wrapper--dropdown:hover .nav-dropdown-menu {
display: block;
}
.site-header__nav .nav-dropdown-menu {
display: none;
}
.nav-dropdown-menu {
position: absolute;
z-index: 500;
background: transparent;
}
.nav-dropdown-menu-wrapper {
border-radius: 8px;
box-shadow: 0 12px 40px 0 rgba(1, 10, 32, 0.24);
background: white;
margin: 8px 0;
overflow: hidden;
}
/* Products, Solutions dropdown menu */
.nav-dropdown-menu--products,
.nav-dropdown-menu--solutions {
width: 500px;
}
.nav-dropdown-menu--products .nav-dropdown-menu-wrapper,
.nav-dropdown-menu--solutions .nav-dropdown-menu-wrapper {
padding: 20px;
}
.nav-dropdown-menu--products .nav-dropdown-menu-wrapper {
height: 430px;
}
.nav-dropdown-menu--products .dm-header,
.nav-dropdown-menu--solutions .dm-header {
font-size: 1.1em;
font-weight: 500;
font-stretch: normal;
font-style: normal;
line-height: normal;
letter-spacing: 3.67px;
color: #505769;
margin-bottom: 20px;
text-transform: uppercase;
}
.nav-dropdown-menu--products .dm-item,
.nav-dropdown-menu--solutions .dm-item {
border: none;
margin: 0 0 24px;
color: #06102a;
transition: all 0.1s;
position: relative;
width: 100%;
}
.nav-dropdown-menu--products .dm-item:last-child,
.nav-dropdown-menu--solutions .dm-item:last-child {
margin-bottom: 0;
}
.nav-dropdown-menu--products .dm-item .dmi-image {
width: 104px;
height: 72px;
position: absolute;
/*background: #f4f4f4;*/
display: flex;
align-items: center;
justify-content: center;
padding: 20px;
border-radius: 4px;
z-index: 0;
}
.nav-dropdown-menu--solutions .dm-item .dmi-image {
width: 65px;
padding: 10px;
background: white;
height: auto;
position: absolute;
z-index: 0;
}
.nav-dropdown-menu--solutions .dm-item .dmi-image img {
width: 100%;
}
.nav-dropdown-menu--products .dm-item .dmi-details,
.nav-dropdown-menu--solutions .dm-item .dmi-details {
padding: 6px 0 0 127px;
width: 100%;
background: transparent;
display: block;
color: #06102a;
position: relative;
z-index: 1;
}
.nav-dropdown-menu--products .dm-item .dmi-details:hover,
.nav-dropdown-menu--solutions .dm-item .dmi-details:hover {
color: #1e54d5;
}
.nav-dropdown-menu--products .dm-item .dmi-title,
.nav-dropdown-menu--solutions .dm-item .dmi-title {
font-size: 1.6em;
font-weight: 500;
margin: 0 0 2px;
}
.nav-dropdown-menu--products .dm-item .dmi-description,
.nav-dropdown-menu--solutions .dm-item .dmi-description {
font-size: 1.4em;
opacity: 0.7;
line-height: 1.6em;
}
.nav-dropdown-menu--products .dm-item--traefikee .dmi-image img,
.nav-dropdown-menu--solutions .dm-item--traefikee .dmi-image img {
transform: scale(1.1);
}
.nav-dropdown-menu--solutions .dm-item .dmi-details {
padding: 5px 0 0 80px;
}
.nav-dropdown-menu--solutions .dm-item:last-child {
margin-bottom: 10px;
}
/* Dropdown menu: Learn and Company */
.nav-dropdown-menu--learn {
width: 250px;
}
.nav-dropdown-menu--company {
width: 500px;
}
.nav-dropdown-menu--company .nav-dropdown-menu-wrapper {
display: grid;
grid-template-columns: 50% 50%;
}
.nav-dropdown-menu--learn .dm-left,
.nav-dropdown-menu--company .dm-left {
padding: 25px;
}
.nav-dropdown-menu--learn .dm-header,
.nav-dropdown-menu--company .dm-header {
font-size: 1.1em;
font-weight: 500;
font-stretch: normal;
font-style: normal;
line-height: normal;
letter-spacing: 3.67px;
color: #505769;
margin-bottom: 20px;
text-transform: uppercase;
}
.nav-dropdown-menu--learn .dm-item,
.nav-dropdown-menu--company .dm-item {
display: block;
font-size: 1.6em;
font-weight: 500;
color: #06102a;
margin-bottom: 15px;
}
.nav-dropdown-menu--learn .dm-item:last-child,
.nav-dropdown-menu--company .dm-item:last-child {
margin-bottom: 0;
}
.nav-dropdown-menu--learn .dm-item:hover,
.nav-dropdown-menu--company .dm-item:hover {
color: #1e54d5;
}
.dm-preview {
background: #edeff4;
overflow: hidden;
height: 100%;
display: flex;
flex-direction: column;
}
.dm-preview__feature-image {
overflow: hidden;
display: block;
}
.dm-preview__feature-image img {
width: 100%;
height: 145px;
background: #ffffff no-repeat 50%;
object-fit: cover;
vertical-align: middle;
}
.dm-preview__content {
padding: 15px;
display: flex;
justify-content: flex-start;
align-items: flex-start;
flex-direction: column;
flex: 1;
position: relative;
}
.dm-preview__tag {
display: block;
font-size: 1.2em;
color: #db7d11;
letter-spacing: 2.5px;
font-weight: 500;
margin: 0 0;
text-transform: uppercase;
}
.dm-preview__title {
font-size: 1.6em;
font-weight: 500;
line-height: 1.6em;
margin: 0;
color: #06102a;
display: block;
flex: 1;
position: relative;
z-index: 1;
padding-bottom: 20px;
}
.dm-preview .arrow-link {
justify-content: flex-start;
font-size: 1.4em;
position: absolute;
bottom: 12px;
z-index: 0;
}
/* Dropdown menu: Company */
.nav-dropdown-menu--company {
width: 450px;
}
.nav-dropdown-menu--company .dm-right {
background: #06102a;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
color: white;
padding: 20px;
}
.nav-dropdown-menu--company .dm-right p {
font-size: 1.6em;
font-weight: 500;
margin: 0 0 15px;
text-align: center;
}
.nav-dropdown-menu--company .dm-right a {
text-transform: uppercase;
line-height: 1.5em;
padding: 9px 12px;
font-size: 1.2em;
}
/* Demo */
.site-header__demo-button .button--secondary {
font-size: 1.4em;
padding: 8px 12px;
border-radius: 6px;
}
/* Drawer */
.site-header .drawer {
display: none;
}
@media (max-width: 1219px) {
.site-header__nav .menu-item-wrapper {
padding-left: 20px;
}
}
@media (max-width: 980px) {
.site-header__nav {
display: none;
}
.site-header .drawer {
display: block;
}
.site-header .right .site-header__demo-button {
display: none;
}
html [data-md-color-primary=indigo] .md-nav--primary .md-nav__title--site {
background-color: #06102a;
}
html .md-nav--primary .md-nav__title {
padding: 64px .8rem .2rem;
}
.md-search__inner {
top: 64px;
right: 0;
}
}
.md-header .md-search {
margin-right: 12.1rem;
}
.site-header__main {
display: flex;
align-items: center;
}
.dmi-image.proxy {
background-color: #24a1c1;
}
.dmi-image.mesh {
background-color: #9d0fb0;
}
.dmi-image.enterprise {
background-color: #0060e0;
}
.dmi-image.pilot {
background-color: #db7d11;
}

View File

@@ -0,0 +1,101 @@
.md-nav__link {
margin-left: -0.4rem;
padding: 0 0.4rem;
line-height: 32px;
color: var(--dark) !important;
}
.md-nav__link::after {
font-size: 16px;
vertical-align: -.25em;
}
.md-nav__toggle:checked + .md-nav__link,
.md-nav__link--active,
.md-nav__link:hover {
border-radius: 8px;
background-color: var(--light-blue) !important;
color: var(--dark) !important;
transition: background-color 0.3s ease;
}
.md-nav__link--active {
color: var(--blue) !important;
font-weight: bold;
}
.md-sidebar--primary {
background-color: white;
}
.md-sidebar--secondary .md-nav__title {
font-size: 12px;
text-transform: uppercase;
margin-bottom: 0.4rem;
padding: 0;
}
.md-sidebar--secondary .md-sidebar__scrollwrap {
border-radius: 8px;
background-color: var(--light-blue) !important;
}
.md-sidebar--secondary .md-nav__title {
padding: 0.8rem 0.4rem 0.8rem;
}
.md-sidebar--secondary .md-nav__list {
padding: 0 0.4rem 0.8rem 1.2rem;
}
.md-sidebar--secondary .md-sidebar__scrollwrap .md-nav__link {
font-weight: 300;
}
.md-sidebar--secondary
.md-sidebar__scrollwrap
.md-nav__link[data-md-state='blur'],
.md-sidebar--secondary .md-sidebar__scrollwrap .md-nav__link:hover {
color: var(--blue) !important;
font-weight: bold;
}
.md-sidebar--secondary .md-nav__item {
padding: 0 0 0 0.4rem;
}
.md-sidebar--secondary .md-nav__link {
margin-top: 0.225em;
padding: 0.1rem 0.2rem;
}
.md-sidebar--secondary li {
list-style-type: disc;
}
.md-sidebar--secondary .repo_url {
padding: 10px 0 14px 0;
}
.md-search__inner {
width: inherit;
float: inherit;
}
.md-search__input {
margin-bottom: 10px;
border-radius: 4px;
background-color: inherit;
border: 1px solid rgba(0,0,0,.07);
}
.md-search__input::placeholder {
color: rgba(0,0,0,.07);
}
@media only screen and (min-width: 60em) {
[data-md-toggle=search]:checked~.md-header .md-search__inner {
margin-top: 100px;
}
}

View File

@@ -0,0 +1,24 @@
.product-switcher {
font-size: 10px;
font-family: 'Rubik', -apple-system, 'BlinkMacSystemFont', 'Segoe UI',
'Helvetica Neue', sans-serif;
color: #06102a;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
.product-switcher .menu-item-wrapper {
padding-left: 15px;
}
.product-switcher img {
margin-right: 10px;
}
.product-switcher img.gopher {
margin: 0 2px 0 16px;
}
.product-switcher .nav-dropdown-menu--products .nav-dropdown-menu-wrapper {
width: auto;
height: auto;
}

View File

@@ -0,0 +1,10 @@
:root {
--dark: #06102a;
--blue: #04B5D1;
--light-blue: #E4F7FA;
--input-bg-color: white;
--input-color: black;
--input-placeholder-color: #bbb;
--input-border-color: #dcdcdc;
}

View File

@@ -1,31 +1,10 @@
---
title: "Traefik Advocation Documentation"
description: "There are many ways to contribute to Traefik Proxy. Let us know if youre talking about Traefik, and we'll promote your enthusiasm!"
---
# Advocating
Spread the Love & Tell Us About It
Spread the Love & Tell Us about It
{: .subtitle }
Traefik Proxy was started by the community and for the community.
You can contribute to the Traefik community in three main ways:
There are many ways to contribute to the project, and there is one that always spark joy: when we see/read about users talking about how Traefik helps them solve their problems.
**Spread the word!** Guides, videos, blog posts, how-to articles, and showing off your network design all help spread the word about Traefik Proxy
and teach others in the community how to best implement it.
It always sparks joy when users share how Traefik Proxy helps them solve their problems.
If you are talking about Traefik Proxy, [let us know](https://traefik.io/submit-my-contribution/) and we will promote your work and reward your enthusiasm!
If you are giving a talk that includes or is about Traefik Proxy, [let us know](https://traefik.io/submit-my-contribution/) and we will send you swag and stickers for your time at the conference.
If you have written about Traefik or shared useful information you would like to promote, feel free to add links to the [dedicated wiki page on GitHub](https://github.com/traefik/traefik/wiki/Awesome-Traefik).
If you're talking about Traefik, [let us know](https://blog.traefik.io/spread-the-love-ba5a40aa72e7) and we'll promote your enthusiasm!
**Help community members!** Everyone needs a place to share their cool innovations or get help with that pesky bug that only a different pair of eyes seems to be able to see.
Join our [Community Forum](https://community.traefik.io/) where you can ask questions, help out other users, and share your neat configuration examples or snippets.
Top contributors will be asked to join the Ambassador program and get unique swag to celebrate!
**Build cool solutions!** Traefik Proxy would be so much better if only it had…
We love all the wonderful ideas that our users come up with, but we can only build so much.
Luckily, as an open source community, our users can help by [building awesome features](https://github.com/orgs/traefik/projects/9/views/7), enhancements, or bug fixes.
We are a big community, so we do need to prioritize a bit.
That is why we use the tag `contributor/wanted` to let you know which pull requests will make it to the front of the queue for design support and review.
Feel free to grab one of these and run with it.
Top contributors get unique swag to celebrate.
Also, if you've written about Traefik or shared useful information you'd like to promote, feel free to add links in the [dedicated wiki page on Github](https://github.com/traefik/traefik/wiki/Awesome-Traefik).

View File

@@ -1,25 +1,70 @@
---
title: "Traefik Building & Testing Documentation"
description: "Compile and test your own Traefik Proxy! Learn how to build your own Traefik binary from the sources, and read the technical documentation."
---
# Building and Testing
Compile and Test Your Own Traefik!
{: .subtitle }
You want to build your own Traefik binary from the sources?
So you want to build your own Traefik binary from the sources?
Let's see how.
## Building
You need:
- [Docker](https://github.com/docker/docker "Link to website of Docker")
- `make`
- [Go](https://go.dev/ "Link to website of Go")
- [misspell](https://github.com/golangci/misspell)
- [shellcheck](https://github.com/koalaman/shellcheck)
- [Tailscale](https://tailscale.com/) if you are using Docker Desktop
You need either [Docker](https://github.com/docker/docker) and `make` (Method 1), or `go` (Method 2) in order to build Traefik.
For changes to its dependencies, the `dep` dependency management tool is required.
### Method 1: Using `Docker` and `Makefile`
Run make with the `binary` target.
This will create binaries for the Linux platform in the `dist` folder.
In case when you run build on CI, you may probably want to run docker in non-interactive mode. To achieve that define `DOCKER_NON_INTERACTIVE=true` environment variable.
```bash
$ make binary
docker build -t traefik-webui -f webui/Dockerfile webui
Sending build context to Docker daemon 2.686MB
Step 1/11 : FROM node:8.15.0
---> 1f6c34f7921c
[...]
Successfully built ce4ff439c06a
Successfully tagged traefik-webui:latest
[...]
docker build -t "traefik-dev:4475--feature-documentation" -f build.Dockerfile .
Sending build context to Docker daemon 279MB
Step 1/10 : FROM golang:1.14-alpine
---> f4bfb3d22bda
[...]
Successfully built 5c3c1a911277
Successfully tagged traefik-dev:4475--feature-documentation
docker run -e "TEST_CONTAINER=1" -v "/var/run/docker.sock:/var/run/docker.sock" -it -e OS_ARCH_ARG -e OS_PLATFORM_ARG -e TESTFLAGS -e VERBOSE -e VERSION -e CODENAME -e TESTDIRS -e CI -e CONTAINER=DOCKER -v "/home/ldez/sources/go/src/github.com/traefik/traefik/"dist":/go/src/github.com/traefik/traefik/"dist"" "traefik-dev:4475--feature-documentation" ./script/make.sh generate binary
---> Making bundle: generate (in .)
removed 'autogen/genstatic/gen.go'
---> Making bundle: binary (in .)
$ ls dist/
traefik*
```
The following targets can be executed outside Docker by setting the variable `PRE_TARGET` to an empty string (we don't recommend that):
- `test-unit`
- `test-integration`
- `validate`
- `binary` (the webUI is still generated by using Docker)
ex:
```bash
PRE_TARGET= make test-unit
```
### Method 2: Using `go`
Requirements:
- `go` v1.14+
- environment variable `GO111MODULE=on`
- [go-bindata](https://github.com/containous/go-bindata) `GO111MODULE=off go get -u github.com/containous/go-bindata/...`
!!! tip "Source Directory"
@@ -52,34 +97,52 @@ You need:
## ... and the list goes on
```
### Build Traefik
#### Build Traefik
Once you've set up your go environment and cloned the source repository, you can build Traefik.
```bash
$ make binary
SHA: 8fddfe118288bb5280eb5e77fa952f52def360b4 cheddar 2024-01-11_03:14:57PM
CGO_ENABLED=0 GOGC=off GOOS=darwin GOARCH=arm64 go build -ldflags "-s -w \
-X github.com/traefik/traefik/v2/pkg/version.Version=8fddfe118288bb5280eb5e77fa952f52def360b4 \
-X github.com/traefik/traefik/v2/pkg/version.Codename=cheddar \
-X github.com/traefik/traefik/v2/pkg/version.BuildDate=2024-01-11_03:14:57PM" \
-installsuffix nocgo -o "./dist/darwin/arm64/traefik" ./cmd/traefik
Beforehand, you need to get [go-bindata](https://github.com/containous/go-bindata) (the first time) in order to be able to use the `go generate` command (which is part of the build process).
$ ls dist/
traefik*
```bash
cd ~/go/src/github.com/traefik/traefik
# Get go-bindata. (Important: the ellipses are required.)
GO111MODULE=off go get github.com/containous/go-bindata/...
```
You will find the Traefik executable (`traefik`) in the `./dist` directory.
```bash
# Generate UI static files
rm -rf static/ autogen/; make generate-webui
# required to merge non-code components into the final binary,
# such as the web dashboard/UI
go generate
```
```bash
# Standard go build
go build ./cmd/traefik
```
You will find the Traefik executable (`traefik`) in the `~/go/src/github.com/traefik/traefik` directory.
## Testing
### Method 1: `Docker` and `make`
Run unit tests using the `test-unit` target.
Run integration tests using the `test-integration` target.
Run all tests (unit and integration) using the `test` target.
```bash
$ make test-unit
GOOS=darwin GOARCH=arm64 go test -cover "-coverprofile=cover.out" -v ./pkg/... ./cmd/...
docker build -t "traefik-dev:your-feature-branch" -f build.Dockerfile .
# […]
docker run --rm -it -e OS_ARCH_ARG -e OS_PLATFORM_ARG -e TESTFLAGS -v "/home/user/go/src/github/traefik/traefik/dist:/go/src/github.com/traefik/traefik/dist" "traefik-dev:your-feature-branch" ./script/make.sh generate test-unit
---> Making bundle: generate (in .)
removed 'gen.go'
---> Making bundle: test-unit (in .)
+ go test -cover -coverprofile=cover.out .
ok github.com/traefik/traefik 0.005s coverage: 4.1% of statements
@@ -88,30 +151,28 @@ Test success
For development purposes, you can specify which tests to run by using (only works the `test-integration` target):
??? note "Configuring Tailscale for Docker Desktop user"
Create `tailscale.secret` file in `integration` directory.
This file needs to contain a [Tailscale auth key](https://tailscale.com/kb/1085/auth-keys)
(an ephemeral, but reusable, one is recommended).
Add this section to your tailscale ACLs to auto-approve the routes for the
containers in the docker subnet:
```json
"autoApprovers": {
// Allow myself to automatically
// advertize routes for docker networks
"routes": {
"172.31.42.0/24": ["your_tailscale_identity"],
},
},
```
```bash
# Run every tests in the MyTest suite
TESTFLAGS="-test.run TestAccessLogSuite" make test-integration
TESTFLAGS="-check.f MyTestSuite" make test-integration
# Run the test "MyTest" in the MyTest suite
TESTFLAGS="-test.run TestAccessLogSuite -testify.m ^TestAccessLog$" make test-integration
TESTFLAGS="-check.f MyTestSuite.MyTest" make test-integration
# Run every tests starting with "My", in the MyTest suite
TESTFLAGS="-check.f MyTestSuite.My" make test-integration
# Run every tests ending with "Test", in the MyTest suite
TESTFLAGS="-check.f MyTestSuite.*Test" make test-integration
```
More: https://labix.org/gocheck
### Method 2: `go`
Unit tests can be run from the cloned directory using `$ go test ./...` which should return `ok`, similar to:
```test
ok _/home/user/go/src/github/traefik/traefik 0.004s
```
Integration tests must be run from the `integration/` directory and require the `-integration` switch: `$ cd integration && go test -integration ./...`.

View File

@@ -1,8 +1,3 @@
---
title: "Traefik Data Collection Documentation"
description: "To learn more about how Traefik is being used and improve it, we collect anonymous usage statistics from running instances. Read the technical documentation."
---
# Data Collection
Understanding How Traefik is Being Used
@@ -10,23 +5,23 @@ Understanding How Traefik is Being Used
## Configuration Example
Understanding how you use Traefik is very important to us: it helps us improve the solution in many different ways.
For this very reason, the sendAnonymousUsage option is mandatory: we want you to take time to consider whether or not you wish to share anonymous data with us, so we can benefit from your experience and use cases.
Understanding how you use Traefik is very important to us: it helps us improve the solution in many different ways.
For this very reason, the sendAnonymousUsage option is mandatory: we want you to take time to consider whether or not you wish to share anonymous data with us so we can benefit from your experience and use cases.
!!! example "Enabling Data Collection"
```yaml tab="File (YAML)"
global:
# Send anonymous usage data
sendAnonymousUsage: true
```
```toml tab="File (TOML)"
[global]
# Send anonymous usage data
sendAnonymousUsage = true
```
```yaml tab="File (YAML)"
global:
# Send anonymous usage data
sendAnonymousUsage: true
```
```bash tab="CLI"
# Send anonymous usage data
--global.sendAnonymousUsage
@@ -34,7 +29,7 @@ For this very reason, the sendAnonymousUsage option is mandatory: we want you to
## Collected Data
This feature comes from this [public proposal](https://github.com/traefik/traefik/issues/2369).
This feature comes from the public proposal [here](https://github.com/traefik/traefik/issues/2369).
In order to help us learn more about how Traefik is being used and improve it, we collect anonymous usage statistics from running instances.
Those data help us prioritize our developments and focus on what's important for our users (for example, which provider is popular, and which is not).
@@ -45,56 +40,56 @@ Once a day (the first call begins 10 minutes after the start of Traefik), we col
- the Traefik version number
- a hash of the configuration
- an **anonymized version** of the static configuration (token, username, password, URL, IP, domain, email, etc., are removed).
- an **anonymized version** of the static configuration (token, user name, password, URL, IP, domain, email, etc, are removed).
!!! info
- We do not collect the dynamic configuration information (routers & services).
- We do not collect this data to run advertising programs.
- We do not sell this data to third-parties.
### Example of Collected Data
```yaml tab="Original configuration"
entryPoints:
web:
address: ":80"
```toml tab="Original configuration"
[entryPoints]
[entryPoints.web]
address = ":80"
api: {}
[api]
providers:
docker:
endpoint: "tcp://10.10.10.10:2375"
exposedByDefault: true
[providers.docker]
endpoint = "tcp://10.10.10.10:2375"
exposedByDefault = true
swarmMode = true
tls:
ca: dockerCA
cert: dockerCert
key: dockerKey
insecureSkipVerify: true
[providers.docker.TLS]
ca = "dockerCA"
cert = "dockerCert"
key = "dockerKey"
insecureSkipVerify = true
```
```yaml tab="Resulting Obfuscated Configuration"
entryPoints:
web:
address: ":80"
```toml tab="Resulting Obfuscated Configuration"
[entryPoints]
[entryPoints.web]
address = ":80"
api: {}
[api]
providers:
docker:
endpoint: "xxxx"
exposedByDefault: true
[providers.docker]
endpoint = "xxxx"
exposedByDefault = true
swarmMode = true
tls:
ca: xxxx
cert: xxxx
key: xxxx
insecureSkipVerify: true
[providers.docker.TLS]
ca = "xxxx"
cert = "xxxx"
key = "xxxx"
insecureSkipVerify = true
```
## The Code for Data Collection
If you want to dig into more details, here is the source code of the collecting system: [collector.go](https://github.com/traefik/traefik/blob/master/pkg/collector/collector.go)
By default, we anonymize all configuration fields, except fields tagged with `export=true`.
By default we anonymize all configuration fields, except fields tagged with `export=true`.

View File

@@ -1,8 +1,3 @@
---
title: "Traefik Contribution Documentation"
description: "Found something unclear in the Traefik Proxy documentation and want to give a try at explaining it better? Read the guide to building documentation."
---
# Documentation
Features Are Better When You Know How to Use Them
@@ -15,18 +10,14 @@ Let's see how.
### General
This [documentation](../../ "Link to the official Traefik documentation") is built with [MkDocs](https://mkdocs.org/ "Link to the website of MkDocs").
This [documentation](https://doc.traefik.io/traefik/) is built with [mkdocs](https://mkdocs.org/).
### Method 1: `Docker` and `make`
Please make sure you have the following requirements installed:
- [Docker](https://www.docker.com/ "Link to the website of Docker")
You can build the documentation and test it locally (with live reloading), using the `docs-serve` target:
You can build the documentation and test it locally (with live reloading), using the `docs` target:
```bash
$ make docs-serve
$ make docs
docker build -t traefik-docs -f docs.Dockerfile .
# […]
docker run --rm -v /home/user/go/github/traefik/traefik:/mkdocs -p 8000:8000 traefik-docs mkdocs serve
@@ -38,7 +29,7 @@ docker run --rm -v /home/user/go/github/traefik/traefik:/mkdocs -p 8000:8000 tr
!!! tip "Default URL"
Your local documentation server will run by default on <http://127.0.0.1:8000>.
Your local documentation server will run by default on [http://127.0.0.1:8000](http://127.0.0.1:8000).
If you only want to build the documentation without serving it locally, you can use the following command:
@@ -47,12 +38,9 @@ $ make docs-build
...
```
### Method 2: `MkDocs`
### Method 2: `mkdocs`
Please make sure you have the following requirements installed:
- [Python](https://www.python.org/ "Link to the website of Python")
- [pip](https://pypi.org/project/pip/ "Link to the website of pip on PyPI")
First, make sure you have `python` and `pip` installed.
```bash
$ python --version
@@ -61,7 +49,7 @@ $ pip --version
pip 1.5.2
```
Then, install MkDocs with `pip`.
Then, install mkdocs with `pip`.
```bash
pip install --user -r requirements.txt
@@ -94,19 +82,17 @@ Running ["HtmlCheck", "ImageCheck", "ScriptCheck", "LinkCheck"] on /app/site/bas
!!! note "Clean & Verify"
If you've made changes to the documentation, it's safer to clean it before verifying it.
If you've made changes to the documentation, it's safter to clean it before verifying it.
```bash
$ make docs
$ make docs-clean docs-verify
...
```
Will perform all necessary steps for you.
!!! note "Disabling Documentation Verification"
Verification can be disabled by setting the environment variable `DOCS_VERIFY_SKIP` to `true`:
```shell
DOCS_VERIFY_SKIP=true make docs-verify
...

View File

@@ -1,127 +0,0 @@
---
title: "Traefik Maintainer's Guidelines Documentation"
description: "Interested in contributing more to the community and becoming a Traefik Proxy maintainer? Read the guide to becoming a part of the core team."
---
# Maintainer's Guidelines
![Maintainer's Guidelines](../assets/img/maintainers-guidelines.png)
Welcome to the Traefik Community.
We are strongly promoting a philosophy of openness and sharing,
and firmly standing against the elitist closed approach.
Being part of the core team should be accessible to anyone motivated
and wants to be part of that journey!
## Becoming a Maintainer
Before a contributor becomes a maintainer, they should meet the following requirements:
- The contributor enabled [2FA](https://docs.github.com/en/authentication/securing-your-account-with-two-factor-authentication-2fa/configuring-two-factor-authentication) on their GitHub account
- The contributor showed a consistent pattern of helpful, non-threatening, and friendly behavior towards other community members in the past.
- The contributor has read and accepted the maintainer's guidelines.
The contributor should also meet one or several of the following requirements:
- The contributor has opened and successfully run medium to large PRs in the past 6 months.
- The contributor has participated in multiple code reviews of other PRs,
including those of other maintainers and contributors.
- The contributor is active on Traefik Community forums
or other technical forums/boards, such as K8S Slack, Reddit, StackOverflow, and Hacker News.
Any existing active maintainer can create an issue to discuss promoting a contributor to maintainer.
Other maintainers can vote on the issue, and if the quorum is reached, the contributor is promoted to maintainer.
If the quorum is not reached within one month after the issue is created, it is closed.
## Maintainer's Responsibilities and Privileges
As a maintainer, you are granted a vote for the following:
- [PR review](https://github.com/traefik/contributors-guide/blob/master/pr_guidelines.md).
- [Design review](https://github.com/traefik/contributors-guide/blob/master/proposals.md).
- [Proposals](https://github.com/traefik/contributors-guide/blob/master/proposals.md).
Maintainers are also added to the maintainer's Discord server where happens the [issue triage](https://github.com/traefik/contributors-guide/blob/master/issue_triage.md)
and appear on the [Maintainers](maintainers.md) page.
As a maintainer, you should:
- Prioritize PR reviews, design reviews, and issue triage above any other task.
Making sure contributors and community members are listened to and have an impact on the project is essential to keeping the project active and develop a thriving community.
- Prioritize helping contributors reaching the expecting quality level over rewriting contributions.
Any triage activity on issues and PRs (e.g. labels, marking messages as off-topic, refusing, marking duplicates) should result from a collective decision to ensure knowledge is shared among maintainers.
## Communicating
- All of our maintainers are added to the Traefik Maintainers Discord server that belongs to Traefik labs.
Having the team in one place helps us to communicate effectively.
Maintainers can discuss issues, pull requests, enhancements more efficiently
and get the feedback almost immediately.
Fewer blockers mean more fun and engaging work.
- Every decision made on the discord server among maintainers is documented so it's visible to the rest of the community.
- Maintainers express their opinions on issues and reviews.
It is fine to have different point of views.
We encourage active and open conversations which goals are to improve Traefik.
- When discussing issues and proposals, maintainers should share as much information as possible to help solve the issue.
## Maintainers Activity
In order to keep the core team efficient and dynamic,
maintainers' activity and involvement will be reviewed on a regular basis.
- Has the maintainer engaged with the team and the community by meeting two or more of these benchmarks in the past six months?
- Has the maintainer participated in at least two or three maintainer meetings?
- Substantial review of at least one or two PRs from either contributors or maintainers.
- Opened at least one or two bug fixes or feature request PRs
that were eventually merged (or on a trajectory for merge).
- Substantial participation in the Help Wanted program (answered questions, helped identify issues, applied guidelines from the Help Wanted guide to open issues).
- Substantial participation with the community in general.
- Has the maintainer shown a consistent pattern of helpful,
non-threatening,
and friendly behavior towards other people on the maintainer team and with our community?
## Additional Comments for Maintainers (that should apply to any contributor)
- Be respectful with other maintainers and other community members.
- Be open minded when participating in conversations: try to put yourself in others shoes.
- Keep the communication public -
if anyone tries to communicate with you directly,
ask politely to move the conversation to a public communication channel.
- Stay away from defensive comments.
- Please try to express your thoughts clearly enough
and note that some of us are not native English speakers.
Try to rephrase your sentences, avoiding mental shortcuts;
none of us is able to predict anyone's thoughts.
- Be proactive.
- Emoji are fine,
but if you express yourself clearly enough they are not necessary.
They will not replace good communication.
- Embrace mentorship: help others grow and match the quality level we strive for.
- Keep in mind that we all have the same goal: improve the project.

View File

@@ -1,40 +1,176 @@
---
title: "Traefik Maintainers Documentation"
description: "Traefik Proxy is an open source software with a thriving community of contributors and maintainers. Read the list of maintainers on this page."
---
# Maintainers
## Active Maintainers
## The team
* Emile Vauge [@emilevauge](https://github.com/emilevauge)
* Vincent Demeester [@vdemeester](https://github.com/vdemeester)
* Ed Robinson [@errm](https://github.com/errm)
* Daniel Tomcej [@dtomcej](https://github.com/dtomcej)
* Manuel Zapf [@SantoDE](https://github.com/SantoDE)
* Timo Reimann [@timoreimann](https://github.com/timoreimann)
* Ludovic Fernandez [@ldez](https://github.com/ldez)
* Julien Salleyron [@juliens](https://github.com/juliens)
* Nicolas Mengin [@nmengin](https://github.com/nmengin)
* Marco Jantke [@marco-jantke](https://github.com/marco-jantke)
* Michaël Matur [@mmatur](https://github.com/mmatur)
* Gérald Croës [@geraldcroes](https://github.com/geraldcroes)
* Jean-Baptiste Doumenjou [@jbdoumenjou](https://github.com/jbdoumenjou)
* Mathieu Lonjaret [@mpl](https://github.com/mpl)
* Romain Tribotté [@rtribotte](https://github.com/rtribotte)
* Kevin Pollet [@kevinpollet](https://github.com/kevinpollet)
* Harold Ozouf [@jspdown](https://github.com/jspdown)
* Tom Moulard [@tommoulard](https://github.com/tommoulard)
* Landry Benguigui [@lbenguigui](https://github.com/lbenguigui)
* Simon Delicata [@sdelicata](https://github.com/sdelicata)
* Baptiste Mayelle [@youkoulayley](https://github.com/youkoulayley)
* Jesper Noordsij [@jnoordsij](https://github.com/jnoordsij)
## Past Maintainers
## Contributions Daily Meeting
People who have had an incredibly positive impact on the project, and are now focusing on other projects.
* 3 Maintainers should attend to a Contributions Daily Meeting where we sort and label new issues ([is:issue label:status/0-needs-triage](https://github.com/traefik/traefik/issues?utf8=%E2%9C%93&q=is%3Aissue+label%3Astatus%2F0-needs-triage+)), and review every Pull Requests
* Every pull request should be checked during the Contributions Daily Meeting
* Even if its already assigned
* Even PR labelled with `contributor/waiting-for-corrections` or `contributor/waiting-for-feedback`
* Issues labeled with `priority/P0` and `priority/P1` should be assigned.
* Modifying an issue or a pull request (labels, assignees, milestone) is only possible:
* During the Contributions Daily Meeting
* By an assigned maintainer
* In case of emergency, if a change proposal is approved by 2 other maintainers (on Slack, Discord, Discourse, etc)
* Vincent Demeester [@vdemeester](https://github.com/vdemeester)
* Ed Robinson [@errm](https://github.com/errm)
* Daniel Tomcej [@dtomcej](https://github.com/dtomcej)
* Timo Reimann [@timoreimann](https://github.com/timoreimann)
* Marco Jantke [@mjantke](https://github.com/mjeri)
* Ludovic Fernandez [@ldez](https://github.com/ldez)
## PR review process:
## Maintainer's Guidelines
* The status `needs-design-review` is only used in complex/heavy/tricky PRs.
* From `1` to `2`: 1 comment that says “design LGTM” (by a senior maintainer).
* From `2` to `3`: 3 LGTM approvals by any maintainer.
* If needed, a specific maintainer familiar with a particular domain can be requested for the review.
* If a PR has been implemented in pair programming, one peer's LGTM goes into the review for free
* Amending someone else's pull request is authorized only in emergency, if a rebase is needed, or if the initial contributor is silent
Please read the [maintainer's guidelines](maintainers-guidelines.md).
We use [PRM](https://github.com/ldez/prm) to manage locally pull requests.
## Bots
### [Myrmica Lobicornis](https://github.com/traefik/lobicornis/)
Update and Merge Pull Request.
The maintainer giving the final LGTM must add the `status/3-needs-merge` label to trigger the merge bot.
By default, a squash-rebase merge will be carried out.
To preserve commits, add `bot/merge-method-rebase` before `status/3-needs-merge`.
The status `status/4-merge-in-progress` is only used by the bot.
If the bot is not able to perform the merge, the label `bot/need-human-merge` is added.
In such a situation, solve the conflicts/CI/... and then remove the label `bot/need-human-merge`.
To prevent the bot from automatically merging a PR, add the label `bot/no-merge`.
The label `bot/light-review` decreases the number of required LGTM from 3 to 1.
This label is used when:
* Updating the vendors from previously reviewed PRs
* Merging branches into the master
* Preparing the release
### [Myrmica Bibikoffi](https://github.com/traefik/bibikoffi/)
* closes stale issues [cron]
* use some criterion as number of days between creation, last update, labels, ...
### [Myrmica Aloba](https://github.com/traefik/aloba)
Manage GitHub labels.
* Add labels on new PR [GitHub WebHook]
* Add milestone to a new PR based on a branch version (1.4, 1.3, ...) [GitHub WebHook]
* Add and remove `contributor/waiting-for-corrections` label when a review request changes [GitHub WebHook]
* Weekly report of PR status on Slack (CaptainPR) [cron]
## Labels
A maintainer that looks at an issue/PR must define its `kind/*`, `area/*`, and `status/*`.
### Status - Workflow
The `status/*` labels represent the desired state in the workflow.
* `status/0-needs-triage`: all the new issues and PRs have this status. _[bot only]_
* `status/1-needs-design-review`: needs a design review. **(only for PR)**
* `status/2-needs-review`: needs a code/documentation review. **(only for PR)**
* `status/3-needs-merge`: ready to merge. **(only for PR)**
* `status/4-merge-in-progress`: merge is in progress. _[bot only]_
### Contributor
* `contributor/need-more-information`: we need more information from the contributor in order to analyze a problem.
* `contributor/waiting-for-feedback`: we need the contributor to give us feedback.
* `contributor/waiting-for-corrections`: we need the contributor to take actions in order to move forward with a PR. **(only for PR)** _[bot, humans]_
* `contributor/needs-resolve-conflicts`: use it only when there is some conflicts (and an automatic rebase is not possible). **(only for PR)** _[bot, humans]_
### Kind
* `kind/enhancement`: a new or improved feature.
* `kind/question`: a question. **(only for issue)**
* `kind/proposal`: a proposal that needs to be discussed.
* _Proposal issues_ are design proposals
* _Proposal PRs_ are technical prototypes that need to be refined with multiple contributors.
* `kind/bug/possible`: a possible bug that needs analysis before it is confirmed or fixed. **(only for issues)**
* `kind/bug/confirmed`: a confirmed bug (reproducible). **(only for issues)**
* `kind/bug/fix`: a bug fix. **(only for PR)**
### Resolution
* `resolution/duplicate`: a duplicate issue/PR.
* `resolution/declined`: declined (Rule #1 of open-source: no is temporary, yes is forever).
* `WIP`: Work In Progress. **(only for PR)**
### Platform
* `platform/windows`: Windows related.
### Area
* `area/acme`: ACME related.
* `area/api`: Traefik API related.
* `area/authentication`: Authentication related.
* `area/cluster`: Traefik clustering related.
* `area/documentation`: Documentation related.
* `area/infrastructure`: CI or Traefik building scripts related.
* `area/healthcheck`: Health-check related.
* `area/logs`: Logs related.
* `area/middleware`: Middleware related.
* `area/middleware/metrics`: Metrics related. (Prometheus, StatsD, ...)
* `area/middleware/tracing`: Tracing related. (Jaeger, Zipkin, ...)
* `area/oxy`: Oxy related.
* `area/provider`: related to all providers.
* `area/provider/boltdb`: Boltd DB related.
* `area/provider/consul`: Consul related.
* `area/provider/docker`: Docker and Swarm related.
* `area/provider/ecs`: ECS related.
* `area/provider/etcd`: Etcd related.
* `area/provider/eureka`: Eureka related.
* `area/provider/file`: file provider related.
* `area/provider/k8s`: Kubernetes related.
* `area/provider/kv`: KV related.
* `area/provider/marathon`: Marathon related.
* `area/provider/mesos`: Mesos related.
* `area/provider/rancher`: Rancher related.
* `area/provider/servicefabric`: Azure service fabric related.
* `area/provider/zk`: Zoo Keeper related.
* `area/rules`: Rules related.
* `area/server`: Server related.
* `area/sticky-session`: Sticky session related.
* `area/tls`: TLS related.
* `area/websocket`: WebSocket related.
* `area/webui`: Web UI related.
### Issues Priority
* `priority/P0`: needs hot fix.
* `priority/P1`: need to be fixed in next release.
* `priority/P2`: need to be fixed in the future.
* `priority/P3`: maybe.
### PR size
Automatically set by a bot.
* `size/S`: small PR.
* `size/M`: medium PR.
* `size/L`: Large PR.

View File

@@ -1,63 +1,44 @@
---
title: "Traefik Submitting Issues Documentation"
description: "Help us help you! Learn how to submit an issue, following the guidelines, so the Traefik Proxy team can help. Read the technical documentation."
---
# Submitting Issues
Help Us Help You!
{: .subtitle }
Issues are perfect for requesting a feature/enhancement or reporting a suspected bug.
We use the [GitHub issue tracker](https://github.com/traefik/traefik/issues) to keep track of issues in Traefik.
We use the [GitHub issue tracker](https://github.com/traefik/traefik/issues) to keep track of issues in Traefik.
The process of sorting and checking the issues is a daunting task, and requires a lot of work.
To help maintainers (and other community members) quickly and effortlessly understand what you need,
be sure to follow the guidelines below.
The process of sorting and checking the issues is a daunting task, and requires a lot of work (more than an hour a day ... just for sorting).
To save us some time and get quicker feedback, be sure to follow the guide lines below.
!!! important "Getting Help Vs Reporting an Issue"
The issue tracker is not a general support forum, but a place to report bugs and asks for new features.
For end-user related support questions, try using the [Traefik Community Forum](https://community.traefik.io/)
[![Join the chat at https://community.traefik.io/](https://img.shields.io/badge/style-register-green.svg?style=social&label=Traefik%20Community%20Forum)](https://community.traefik.io/)
For end-user related support questions, try using first:
- the Traefik community forum: [![Join the chat at https://community.traefik.io/](https://img.shields.io/badge/style-register-green.svg?style=social&label=Discourse)](https://community.traefik.io/)
## Issue Title
The title must be short and descriptive. (~60 characters)
Examples:
## Description
* Bug: Duplicate requests in access logs
* Feature: Support TCP
Follow the [issue template](https://github.com/traefik/traefik/blob/master/.github/ISSUE_TEMPLATE.md) as much as possible.
Explain us in which conditions you encountered the issue, what is your context.
Remain as clear and concise as possible
Take time to polish the format of your message so we'll enjoy reading it and working on it.
Help the readers focus on what matters, and help them understand the structure of your message (see the [Github Markdown Syntax](https://help.github.com/articles/github-flavored-markdown)).
## Feature Request
Traefik is an open source project and aims to be the best edge router possible.
Traefik is an open-source project and aims to be the best edge router possible.
Remember when asking for new features that these must be useful to the majority (and not only useful in edge case scenarios, or hack-like setups).
Follow the [issue template](https://github.com/traefik/traefik/blob/master/.github/ISSUE_TEMPLATE/feature-request.yml) as much as possible.
Do your best to explain what you're looking for, and why it would improve Traefik for everyone.
Be detailed and share the use-case(s) to allow us to see the value of your feature request as quickly as possible.
Features with a lot of positive interaction (claps, +1s, conversation about how this would impact them) indicate higher community interest and help us to prioritize.
If you are interested in creating a PR for your feature request, let us know in the issue, so we can work with you.
It can take a lot of work to make sure a PR can integrate with our existing code and planning with the team ahead of time can make sure that your PR can be accepted and merged quickly.
## Issues or Possible Bug Reports
Follow the [issue template](https://github.com/traefik/traefik/blob/master/.github/ISSUE_TEMPLATE/bug_report.yml) as much as possible.
Explain the conditions in which you encountered the issue; what is your context?
Share any logs you may have, and make sure to share the steps it takes to reproduce your issue or bug.
Remain as clear and concise as possible.
Take time to polish the format of your message, so we'll enjoy reading it and working on it.
Help your readers focus on what matters and help them understand the structure of your message (see the [GitHub Markdown Syntax](https://docs.github.com/en/get-started/writing-on-github)).
Do you best to explain what you're looking for, and why it would improve Traefik for everyone.
## International English
Every maintainer / Traefik user is not a native English speaker, so if you sometimes feel that some messages sound rude, remember that it probably is a language barrier problem from someone willing to help you.
Every maintainer / Traefik user is not a native English speaker, so if you feel sometimes that some messages sound rude, remember that it probably is a language barrier problem from someone willing to help you.

View File

@@ -1,232 +1,45 @@
---
title: "Traefik Pull Requests Documentation"
description: "Looking to contribute to Traefik Proxy? This guide will show you the guidelines for submitting a PR in our contributors guide repository."
---
# Submitting Pull Requests
# Before You Submit a Pull Request
A Quick Guide for Efficient Contributions
{: .subtitle }
This guide is for contributors who already have a pull request to submit.
If you are looking for information on setting up your developer environment
and creating code to contribute to Traefik Proxy or related projects,
see the [development guide](https://docs.traefik.io/contributing/building-testing/).
So you've decided to improve Traefik?
Thank You!
Now the last step is to submit your Pull Request in a way that makes sure it gets the attention it deserves.
Looking for a way to contribute to Traefik Proxy?
Check out this list of [Priority Issues](https://github.com/traefik/traefik/labels/contributor%2Fwanted),
the [Good First Issue](https://github.com/traefik/traefik/labels/contributor%2Fgood-first-issue) list,
or the list of [confirmed bugs](https://github.com/traefik/traefik/labels/kind%2Fbug%2Fconfirmed) waiting to be remedied.
Let's go through the classic pitfalls to make sure everything is right.
## How We Prioritize
## Title
We wish we could review every pull request right away, but because it's a time-consuming operation, it's not always possible.
The title must be short and descriptive. (~60 characters)
The PRs we are able to handle the fastest are:
## Description
* Documentation updates.
* Bug fixes.
* Enhancements and Features with a `contributor/wanted` tag.
Follow the [pull request template](https://github.com/traefik/traefik/blob/master/.github/PULL_REQUEST_TEMPLATE.md) as much as possible.
PRs that take more time to address include:
Explain the conditions which led you to write this PR: give us context.
The context should lead to something, an idea or a problem that youre facing.
* Enhancements or Features without the `contributor/wanted` tag.
Remain clear and concise.
If you have an idea for an enhancement or feature that you would like to build,
[create an issue](https://github.com/traefik/traefik/issues/new/choose) for it first
and tell us you are interested in writing the PR.
If an issue already exists, definitely comment on it to tell us you are interested in creating a PR.
Take time to polish the format of your message so we'll enjoy reading it and working on it.
Help the readers focus on what matters, and help them understand the structure of your message (see the [Github Markdown Syntax](https://help.github.com/articles/github-flavored-markdown)).
This will allow us to communicate directly and let you know if it is something we would accept.
## PR Content
It also allows us to make sure you have all the information you need during the design phase
so that it can be reviewed and merged quickly.
- Make it small.
- One feature per Pull Request.
- Write useful descriptions and titles.
- Avoid re-formatting code that is not on the path of your PR.
- Make sure the [code builds](building-testing.md).
- Make sure [all tests pass](building-testing.md).
- Add tests.
- Address review comments in terms of additional commits (and don't amend/squash existing ones unless the PR is trivial).
Read more about the [Triage process](https://github.com/traefik/contributors-guide/blob/master/issue_triage.md) in the docs.
!!! note "Third-Party Dependencies"
## The Pull Request Submit Process
If a PR involves changes to third-party dependencies, the commits pertaining to the vendor folder and the manifest/lock file(s) should be committed separated.
Merging a PR requires the following steps to be completed before it is merged automatically.
!!! tip "10 Tips for Better Pull Requests"
* Make sure your pull request adheres to our best practices. These include:
* [Following project conventions](https://github.com/traefik/traefik/blob/master/docs/content/contributing/maintainers-guidelines.md); including using the PR Template.
* Make small pull requests.
* Solve only one problem at a time.
* Comment thoroughly.
* Do not open the PR from an organization repository.
* Keep "allows edit from maintainer" checked.
* Use semantic line breaks for documentation.
* Ensure your PR is not a draft. We do not review drafts, but do answer questions and confer with developers on them as needed.
* Ensure that the dependencies in the `go.mod` file reference a tag. If referencing a tag is not possible, add a comment explaining why.
* Pass the validation check.
* Pass all tests.
* Receive 2 approving reviews from maintainers.
## Pull Request Review Cycle
Learn about our [Triage Process](https://github.com/traefik/contributors-guide/blob/master/issue_triage.md),
in short, it looks like this:
* We triage every new PR or comment before entering it into the review process.
* We ensure that all prerequisites for review have been met.
* We check to make sure the use case meets our needs.
* We assign reviewers.
* Design Review.
* This takes longer than other parts of the process.
* We review that there are no obvious conflicts with our codebase.
* Code Review.
* We review the code in-depth and run tests.
* We may ask for changes here.
* During code review, we ask that you be reasonably responsive,
if a PR languishes in code review it is at risk of rejection,
or we may take ownership of the PR and the contributor will become a co-author.
* Merge.
* Success!
!!! note
Occasionally, we may freeze our codebase when working towards a specific feature or goal that could impact other development.
During this time, your pull request could remain unmerged while the release work is completed.
## Run Local Verifications
You must run these local verifications before you submit your pull request to predict the pass or failure of continuous integration.
Your PR will not be reviewed until these are green on the CI.
* `make generate`
* `make generate-crd`
* `make test-gateway-api-conformance`
* `make validate`
* `make pull-images`
* `make test`
## The Testing and Merge Workflow
Pull Requests are managed by the bot [Myrmica Lobicornis](https://github.com/traefik/lobicornis).
This bot is responsible for verifying GitHub Checks (CI, Tests, etc), mergability, and minimum reviews.
In addition, it rebases or merges with the base PR branch if needed.
It performs several other housekeeping items
and you can read more about those on the [README](https://github.com/traefik/lobicornis) for Lobicornis.
The maintainer giving the final LGTM must add the `status/3-needs-merge` label to trigger the merge bot.
By default, a squash-rebase merge will be carried out.
The status `status/4-merge-in-progress` is only used by the bot.
If the bot is not able to perform the merge, the label `bot/need-human-merge` is added.
In such a situation, solve the conflicts/CI/... and then remove the label `bot/need-human-merge`.
To prevent the bot from automatically merging a PR, add the label `bot/no-merge`.
The label `bot/light-review` decreases the number of required LGTM from 2 to 1.
This label can be used when:
* Updating a dependency.
* Merging branches back into the next version branch.
* Submitting minor documentation changes.
* Submitting changelog PRs.
## Why Was My Pull Request Closed?
Traefik Proxy is made by the community for the community,
as such the goal is to engage the community to make Traefik the best reverse proxy available.
Part of this goal is maintaining a lean codebase and ensuring code velocity.
Unfortunately, this means that sometimes we will not be able to merge a pull request.
Because we respect the work you did, you will always be told why we are closing your pull request.
If you do not agree with our decision, do not worry; closed pull requests are effortless to recreate,
and little work is lost by closing a pull request that subsequently needs to be reopened.
Your pull request might be closed if:
* Your PR's design conflicts with our existing codebase in such a way that merging is not an option
and the work needed to make your pull request usable is too high.
* To prevent this, make sure you created an issue first
and think about including Traefik Proxy maintainers in your design phase to minimize conflicts.
* Your PR is for an enhancement or feature that we will not use.
* Please remember to create an issue for any pull request **before** you create a PR
to ensure that your goal is something we can merge and that you have any design insight you might need from the team.
* Your PR has been waiting for feedback from the contributor for over 90 days.
## Why is My Pull Request Not Getting Reviewed
A few factors affect how long your pull request might wait for review.
We must prioritize which PRs we focus on.
Our first priority is PRs we have identified as having high community engagement and broad applicability.
We put our top priorities on our roadmap, and you can identify them by the `contributor/wanted` tag.
These PRs will enter our review process the fastest.
Our second priority is bug fixes.
Especially for bugs that have already been tagged with `bug/confirmed`.
These reviews enter the process quickly.
If your PR does not meet the criteria above,
it will take longer for us to review, as any PRs that do meet the criteria above will be prioritized.
Additionally, during the last few weeks of a milestone, we stop reviewing PRs to reduce churn and stabilize.
We will resume after the release.
The second major reason that we deprioritize your PR is that you are not following best practices.
The most common failures to follow best practices are:
* You did not create an issue for the PR you wish to make.
If you do not create an issue before submitting your PR,
we will not be able to answer any design questions and let you know how likely your PR is to be merged.
* You created pull requests that are too large to review.
* Break your pull requests up.
If you can extract whole ideas from your pull request and send those as pull requests of their own,
you should do that instead.
It is better to have many pull requests addressing one thing than one pull request addressing many things.
* Traefik Proxy is a fast-moving codebase — lock in your changes ASAP with your small pull request,
and make merges be someone else's problem.
We want every pull request to be useful on its own,
so use your best judgment on what should be a pull request vs. a commit.
* You did not comment well.
* Comment everything.
Please remember that we are working internationally, cross-culturally, and with different use-cases.
Your reviewer will not intuitively understand the problem the same way you do or solve it the same way you would.
This is why every change you make must be explained, and your strategy for coding must also be explained.
* Your tests were inadequate or absent.
* If you do not know how to test your PR, please ask!
We will be happy to help you or suggest appropriate test cases.
If you have already followed the best practices and your PR still has not received a response,
here are some things you can do to move the process along:
* If you have fixed all the issues from a review,
remember to re-request a review (using the designated button) to let your reviewer know that you are ready.
You can choose to comment with the changes you made.
* Kindly comment on the pull request. Doing so will automatically give your PR visibility during the triage process.
For more information on best practices, try these links:
* [How to Write a Git Commit Message - Chris Beams](https://chris.beams.io/posts/git-commit/)
* [Distributed Git - Contributing to a Project (Commit Guidelines)](https://git-scm.com/book/en/v2/Distributed-Git-Contributing-to-a-Project)
* [Whats with the 50/72 rule? - Preslav Rachev](https://preslav.me/2015/02/21/what-s-with-the-50-72-rule/)
* [A Note About Git Commit Messages - Tim Pope](https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html)
## It's OK to Push Back
Sometimes reviewers make mistakes.
It is OK to push back on changes your reviewer requested.
If you have a good reason for doing something a certain way, you are absolutely allowed to debate the merits of a requested change.
Both the reviewer and reviewee should strive to discuss these issues in a polite and respectful manner.
You might be overruled, but you might also prevail.
We are pretty reasonable people.
Another phenomenon of open-source projects (where anyone can comment on any issue) is the dog-pile -
your pull request gets so many comments from so many people it becomes hard to follow.
In this situation, you can ask the primary reviewer (assignee) whether they want you to fork a new pull request
to clear out all the comments.
You do not have to fix every issue raised by every person who feels like commenting,
but you should answer reasonable comments with an explanation.
## Common Sense and Courtesy
No document can take the place of common sense and good taste.
Use your best judgment, while you put a bit of thought into how your work can be made easier to review.
If you do these things, your pull requests will get merged with less friction.
We enjoyed this article, maybe you will too! [10 tips for better pull requests](https://blog.ploeh.dk/2015/01/15/10-tips-for-better-pull-requests/).

View File

@@ -1,23 +1,16 @@
---
title: "Traefik Security Documentation"
description: "Security is a key part of Traefik Proxy. Read the technical documentation to learn about security advisories, CVE, and how to report a vulnerability."
---
# Security
## Security Advisories
We strongly advise you to join our mailing list to be aware of the latest announcements from our security team.
You can subscribe by sending an email to security+subscribe@traefik.io or on [the online viewer](https://groups.google.com/a/traefik.io/forum/#!forum/security).
You can subscribe sending a mail to security+subscribe@traefik.io or on [the online viewer](https://groups.google.com/a/traefik.io/forum/#!forum/security).
## CVE
Reported vulnerabilities can be found on
Reported vulnerabilities can be found on
[cve.mitre.org](https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=traefik).
## Report a Vulnerability
We want to keep Traefik safe for everyone.
If you've discovered a security vulnerability in Traefik,
we appreciate your help in disclosing it to us in a responsible manner,
by creating a [security advisory](https://github.com/traefik/traefik/security/advisories).
If you've discovered a security vulnerability in Traefik, we appreciate your help in disclosing it to us in a responsible manner, using [this form](https://security.traefik.io).

View File

@@ -1,32 +1,27 @@
---
title: "Traefik Contribution Documentation"
description: "Thank you to all those who have contributed! Traefik Proxy is an open-source project that thrives with the support of our passionate community."
---
# Thank You!
_You_ Made It
{: .subtitle}
Traefik Proxy truly is an [open-source project](https://github.com/traefik/traefik/),
and wouldn't have become what it is today without the help of our [many contributors](https://github.com/traefik/traefik/graphs/contributors),
not accounting for people having helped with issues, tests, comments, articles, ... or just enjoy using Traefik Proxy and share with others.
Traefik truly is an [open-source project](https://github.com/traefik/traefik/),
and wouldn't have become what it is today without the help of our [many contributors](https://github.com/traefik/traefik/graphs/contributors) (at the time of writing this),
not accounting for people having helped with issues, tests, comments, articles, ... or just enjoying it and letting others know.
So once again, thank you for your invaluable help in making Traefik such a good product!
So once again, thank you for your invaluable help on making Traefik such a good product.
!!! question "Where to Go Next?"
If you want to:
- Propose an idea, request a feature, or report a bug,
then read [Submitting Issues](./submitting-issues.md).
- Propose and idea, request a feature a report a bug,
read the page [Submitting Issues](./submitting-issues.md).
- Discover how to make an efficient contribution,
then read [Submitting Pull Requests](./submitting-pull-requests.md).
read the page [Submitting Pull Requests](./submitting-pull-requests.md).
- Learn how to build and test Traefik,
then the page [Building and Testing](./building-testing.md) is for you.
the page [Building and Testing](./building-testing.md) is for you.
- Contribute to the documentation,
then read the page about [Documentation](./documentation.md).
read the related page [Documentation](./documentation.md).
- Understand how do we learn about Traefik usage,
read the [Data Collection](./data-collection.md) page.
- Spread the love about Traefik, please check the [Advocating](./advocating.md) page.
- Learn about who are the maintainers and how they work on the project,
read the [Maintainers](./maintainers.md) and [Maintainer Guidelines](./maintainers-guidelines.md) pages.
read the [Maintainers](./maintainers.md) page.

View File

@@ -1,20 +0,0 @@
# Feature Deprecation Notices
This page is maintained and updated periodically to reflect our roadmap and any decisions around feature deprecation.
| Feature | Deprecated | End of Support | Removal |
|----------------------------------------------------------------------------------------------------------------------|------------|----------------|---------|
| [Kubernetes Ingress API Version `networking.k8s.io/v1beta1`](#kubernetes-ingress-api-version-networkingk8siov1beta1) | N/A | N/A | 3.0 |
| [CRD API Version `apiextensions.k8s.io/v1beta1`](#kubernetes-ingress-api-version-networkingk8siov1beta1) | N/A | N/A | 3.0 |
## Impact
### Kubernetes Ingress API Version `networking.k8s.io/v1beta1`
The Kubernetes Ingress API Version `networking.k8s.io/v1beta1` support is removed in v3.
Please use the API Group `networking.k8s.io/v1` instead.
### Traefik CRD Definitions API Version `apiextensions.k8s.io/v1beta1`
The Traefik CRD definitions API Version `apiextensions.k8s.io/v1beta1` support is removed in v3.
Please use the API Group `apiextensions.k8s.io/v1` instead.

View File

@@ -1,49 +0,0 @@
# Releases
## Versions
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.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.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 |
| 2.7 | May 24, 2022 | Ended Jun 29, 2022 | No |
| 2.6 | Jan 24, 2022 | Ended May 24, 2022 | No |
| 2.5 | Aug 17, 2021 | Ended Jan 24, 2022 | No |
| 2.4 | Jan 19, 2021 | Ended Aug 17, 2021 | No |
| 2.3 | Sep 23, 2020 | Ended Jan 19, 2021 | No |
| 2.2 | Mar 25, 2020 | Ended Sep 23, 2020 | No |
| 2.1 | Dec 11, 2019 | Ended Mar 25, 2020 | No |
| 2.0 | Sep 16, 2019 | Ended Dec 11, 2019 | No |
| 1.7 | Sep 24, 2018 | Ended Dec 31, 2021 | No |
??? example "Active Support / Security Support"
- **Active support**: Receives any bug fixes.
- **Security support**: Receives only critical bug and security fixes.
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).
!!! important "All target dates for end of support or feature removal announcements may be subject to change."
## Versioning Scheme
The Traefik Proxy project follows the [semantic versioning](https://semver.org/) scheme and maintains a separate branch for each minor version. The main branch always represents the next upcoming minor or major version.
And these are our guiding rules for version support:
- **Only the latest `minor`** will be on active support at any given time
- **The last `minor` after releasing a new `major`** will be supported for 1 year following the `major` release
- **Previous rules are subject to change** and in such cases an announcement will be made publicly, [here](https://traefik.io/blog/traefik-2-1-in-the-wild/) is an example extending v1.x branch support.

View File

@@ -1,462 +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/router/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"
```
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/router/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

File diff suppressed because it is too large Load Diff

View File

@@ -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)

View File

@@ -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/router/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/router/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

View File

@@ -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).

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