Merge branch v3.4 into master
This commit is contained in:
commit
289d6e5dca
2
.github/workflows/build.yaml
vendored
2
.github/workflows/build.yaml
vendored
@ -10,7 +10,7 @@ on:
|
||||
- 'script/gcg/**'
|
||||
|
||||
env:
|
||||
GO_VERSION: '1.23'
|
||||
GO_VERSION: '1.24'
|
||||
CGO_ENABLED: 0
|
||||
|
||||
jobs:
|
||||
|
2
.github/workflows/experimental.yaml
vendored
2
.github/workflows/experimental.yaml
vendored
@ -7,7 +7,7 @@ on:
|
||||
- v*
|
||||
|
||||
env:
|
||||
GO_VERSION: '1.23'
|
||||
GO_VERSION: '1.24'
|
||||
CGO_ENABLED: 0
|
||||
|
||||
jobs:
|
||||
|
2
.github/workflows/release.yaml
vendored
2
.github/workflows/release.yaml
vendored
@ -6,7 +6,7 @@ on:
|
||||
- 'v*.*.*'
|
||||
|
||||
env:
|
||||
GO_VERSION: '1.23'
|
||||
GO_VERSION: '1.24'
|
||||
CGO_ENABLED: 0
|
||||
VERSION: ${{ github.ref_name }}
|
||||
TRAEFIKER_EMAIL: "traefiker@traefik.io"
|
||||
|
2
.github/workflows/test-integration.yaml
vendored
2
.github/workflows/test-integration.yaml
vendored
@ -10,7 +10,7 @@ on:
|
||||
- 'script/gcg/**'
|
||||
|
||||
env:
|
||||
GO_VERSION: '1.23'
|
||||
GO_VERSION: '1.24'
|
||||
CGO_ENABLED: 0
|
||||
|
||||
jobs:
|
||||
|
33
.github/workflows/test-unit.yaml
vendored
33
.github/workflows/test-unit.yaml
vendored
@ -10,11 +10,39 @@ on:
|
||||
- 'script/gcg/**'
|
||||
|
||||
env:
|
||||
GO_VERSION: '1.23'
|
||||
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
|
||||
@ -34,7 +62,8 @@ jobs:
|
||||
touch webui/static/index.html
|
||||
|
||||
- name: Tests
|
||||
run: make test-unit
|
||||
run: |
|
||||
go test -v -parallel 8 ${{ matrix.package.group }}
|
||||
|
||||
test-ui-unit:
|
||||
runs-on: ubuntu-latest
|
||||
|
2
.github/workflows/validate.yaml
vendored
2
.github/workflows/validate.yaml
vendored
@ -6,7 +6,7 @@ on:
|
||||
- '*'
|
||||
|
||||
env:
|
||||
GO_VERSION: '1.23'
|
||||
GO_VERSION: '1.24'
|
||||
GOLANGCI_LINT_VERSION: v2.0.2
|
||||
MISSPELL_VERSION: v0.6.0
|
||||
|
||||
|
36
CHANGELOG.md
36
CHANGELOG.md
@ -1,3 +1,39 @@
|
||||
## [v3.4.1](https://github.com/traefik/traefik/tree/v3.4.1) (2025-05-27)
|
||||
[All Commits](https://github.com/traefik/traefik/compare/v3.4.0...v3.4.1)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[docker]** Do not warn network missing if connected to a container network ([#11698](https://github.com/traefik/traefik/pull/11698) by [holysoles](https://github.com/holysoles))
|
||||
- **[k8s/crd]** Fix CEL validation for RootCA in ServersTransport ([#11775](https://github.com/traefik/traefik/pull/11775) by [rtribotte](https://github.com/rtribotte))
|
||||
- **[middleware]** Scope the rate limit counter key by source and by middleware ([#11753](https://github.com/traefik/traefik/pull/11753) by [aromeyer](https://github.com/aromeyer))
|
||||
- **[server]** Use routing path in v3 matchers ([#11790](https://github.com/traefik/traefik/pull/11790) by [kevinpollet](https://github.com/kevinpollet))
|
||||
- **[service]** Make P2C strategy thread-safe ([#11762](https://github.com/traefik/traefik/pull/11762) by [lbenguigui](https://github.com/lbenguigui))
|
||||
- **[webui]** Do not display RemoveHeader option when not defined ([#11782](https://github.com/traefik/traefik/pull/11782) by [kevinpollet](https://github.com/kevinpollet))
|
||||
|
||||
**Documentation:**
|
||||
- **[acme]** Fix ambiguous wording in ACME page ([#11789](https://github.com/traefik/traefik/pull/11789) by [joshka](https://github.com/joshka))
|
||||
- **[k8s]** Fix incorrect case and missing rbac in documentation ([#11742](https://github.com/traefik/traefik/pull/11742) by [mmatur](https://github.com/mmatur))
|
||||
- **[middleware]** Match encoded certificate to example data for TLS passthrough ([#11759](https://github.com/traefik/traefik/pull/11759) by [holysoles](https://github.com/holysoles))
|
||||
|
||||
**Misc:**
|
||||
- Merge branch v2.11 into v3.4 ([#11799](https://github.com/traefik/traefik/pull/11799) by [kevinpollet](https://github.com/kevinpollet))
|
||||
- Merge branch v2.11 into v3.4 ([#11796](https://github.com/traefik/traefik/pull/11796) by [kevinpollet](https://github.com/kevinpollet))
|
||||
- Merge branch v2.11 into v3.4 ([#11783](https://github.com/traefik/traefik/pull/11783) by [kevinpollet](https://github.com/kevinpollet))
|
||||
- Merge branch v2.11 into v3.4 ([#11757](https://github.com/traefik/traefik/pull/11757) by [mmatur](https://github.com/mmatur))
|
||||
- Merge v2.11 into v3.4 ([#11751](https://github.com/traefik/traefik/pull/11751) by [mmatur](https://github.com/mmatur))
|
||||
|
||||
## [v2.11.25](https://github.com/traefik/traefik/tree/v2.11.25) (2025-05-27)
|
||||
[All Commits](https://github.com/traefik/traefik/compare/v2.11.24...v2.11.25)
|
||||
|
||||
**Bug fixes:**
|
||||
- **[k8s/ingress]** Fix panic for ingress with backend resource ([#11777](https://github.com/traefik/traefik/pull/11777) by [rtribotte](https://github.com/rtribotte))
|
||||
- **[server]** Normalize request path ([#11768](https://github.com/traefik/traefik/pull/11768) by [kevinpollet](https://github.com/kevinpollet))
|
||||
|
||||
**Documentation:**
|
||||
- **[middleware,k8s]** Add multi-tenant TLS guidance to the docs ([#11724](https://github.com/traefik/traefik/pull/11724) by [sheddy-traefik](https://github.com/sheddy-traefik))
|
||||
- **[service]** Add a note about how to disable connection reuse with backends ([#11716](https://github.com/traefik/traefik/pull/11716) by [rtribotte](https://github.com/rtribotte))
|
||||
- Fix broken link in documentation ([#11761](https://github.com/traefik/traefik/pull/11761) by [sheddy-traefik](https://github.com/sheddy-traefik))
|
||||
- Change version for path sanitization migration guide ([#11702](https://github.com/traefik/traefik/pull/11702) by [rtribotte](https://github.com/rtribotte))
|
||||
|
||||
## [v3.4.0](https://github.com/traefik/traefik/tree/v3.4.0) (2025-05-05)
|
||||
[All Commits](https://github.com/traefik/traefik/compare/v3.3.0-rc1...v3.4.0)
|
||||
|
||||
|
@ -301,7 +301,10 @@ func setupServer(staticConfiguration *static.Configuration) (*server.Server, err
|
||||
|
||||
// Router factory
|
||||
|
||||
routerFactory := server.NewRouterFactory(*staticConfiguration, managerFactory, tlsManager, observabilityMgr, pluginBuilder, dialerManager)
|
||||
routerFactory, err := server.NewRouterFactory(*staticConfiguration, managerFactory, tlsManager, observabilityMgr, pluginBuilder, dialerManager)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("creating router factory: %w", err)
|
||||
}
|
||||
|
||||
// Watcher
|
||||
|
||||
|
@ -15,8 +15,6 @@ A Use Case Using Docker
|
||||
Create a `docker-compose.yml` file where you will define a `reverse-proxy` service that uses the official Traefik image:
|
||||
|
||||
```yaml
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
reverse-proxy:
|
||||
# The official v3 Traefik docker image
|
||||
@ -50,8 +48,6 @@ Now that you have a Traefik instance up and running, you will deploy new service
|
||||
Edit your `docker-compose.yml` file and add the following at the end of your file.
|
||||
|
||||
```yaml
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
|
||||
...
|
||||
|
@ -13,7 +13,7 @@ You can configure Traefik to use an ACME provider (like Let's Encrypt) for autom
|
||||
!!! warning "Let's Encrypt and Rate Limiting"
|
||||
Note that Let's Encrypt API has [rate limiting](https://letsencrypt.org/docs/rate-limits). These last up to **one week**, and cannot be overridden.
|
||||
|
||||
When running Traefik in a container this file should be persisted across restarts.
|
||||
When running Traefik in a container the `acme.json` file should be persisted across restarts.
|
||||
If Traefik requests new certificates each time it starts up, a crash-looping container can quickly reach Let's Encrypt's ratelimits.
|
||||
To configure where certificates are stored, please take a look at the [storage](#storage) configuration.
|
||||
|
||||
@ -795,6 +795,8 @@ docker run -v "/my/host/acme:/etc/traefik/acme" traefik
|
||||
|
||||
_Optional, Default=2160_
|
||||
|
||||
`certificatesDuration` specifies the duration (in hours) of the certificates issued by the CA server. It is used to determine when to renew the certificate, but it **doesn't** define the duration of the certificates, that is up to the CA server.
|
||||
|
||||
`certificatesDuration` is used to calculate two durations:
|
||||
|
||||
- `Renew Period`: the period before the end of the certificate duration, during which the certificate should be renewed.
|
||||
|
@ -677,3 +677,32 @@ it can lead to unsafe routing when the `sanitizePath` option is set to `false`.
|
||||
|
||||
Setting the `sanitizePath` option to `false` is not safe.
|
||||
Ensure every request is properly url encoded instead.
|
||||
|
||||
## v2.11.25
|
||||
|
||||
### Request Path Normalization
|
||||
|
||||
Since `v2.11.25`, the request path is now normalized by decoding unreserved characters in the request path,
|
||||
and also uppercasing the percent-encoded characters.
|
||||
This follows [RFC 3986 percent-encoding normalization](https://datatracker.ietf.org/doc/html/rfc3986#section-6.2.2.2),
|
||||
and [RFC 3986 case normalization](https://datatracker.ietf.org/doc/html/rfc3986#section-6.2.2.1).
|
||||
|
||||
The normalization happens before the request path is sanitized,
|
||||
and cannot be disabled.
|
||||
This notably helps with encoded dots characters (which are unreserved characters) to be sanitized properly.
|
||||
|
||||
### Routing Path
|
||||
|
||||
Since `v2.11.25`, the reserved characters [(as per RFC 3986)](https://datatracker.ietf.org/doc/html/rfc3986#section-2.2) are kept encoded in the request path when matching the router rules.
|
||||
Those characters, when decoded, change the meaning of the request path for routing purposes,
|
||||
and Traefik now keeps them encoded to avoid any ambiguity.
|
||||
|
||||
### Request Path Matching Examples
|
||||
|
||||
| Request Path | Router Rule | Traefik v2.11.24 | Traefik v2.11.25 |
|
||||
|-------------------|------------------------|------------------|------------------|
|
||||
| `/foo%2Fbar` | PathPrefix(`/foo/bar`) | Match | No match |
|
||||
| `/foo/../bar` | PathPrefix(`/foo`) | No match | No match |
|
||||
| `/foo/../bar` | PathPrefix(`/bar`) | Match | Match |
|
||||
| `/foo/%2E%2E/bar` | PathPrefix(`/foo`) | Match | No match |
|
||||
| `/foo/%2E%2E/bar` | PathPrefix(`/bar`) | No match | Match |
|
||||
|
@ -290,3 +290,32 @@ and to help with the migration from v2 to v3.
|
||||
The `ruleSyntax` router's option was used to override the default rule syntax for a specific router.
|
||||
|
||||
In preparation for the next major release, please remove any use of these two options and use the v3 syntax for writing the router's rules.
|
||||
|
||||
## v3.4.1
|
||||
|
||||
### Request Path Normalization
|
||||
|
||||
Since `v3.4.1`, the request path is now normalized by decoding unreserved characters in the request path,
|
||||
and also uppercasing the percent-encoded characters.
|
||||
This follows [RFC 3986 percent-encoding normalization](https://datatracker.ietf.org/doc/html/rfc3986#section-6.2.2.2),
|
||||
and [RFC 3986 case normalization](https://datatracker.ietf.org/doc/html/rfc3986#section-6.2.2.1).
|
||||
|
||||
The normalization happens before the request path is sanitized,
|
||||
and cannot be disabled.
|
||||
This notably helps with encoded dots characters (which are unreserved characters) to be sanitized properly.
|
||||
|
||||
### Routing Path
|
||||
|
||||
Since `v3.4.1`, the reserved characters [(as per RFC 3986)](https://datatracker.ietf.org/doc/html/rfc3986#section-2.2) are kept encoded in the request path when matching the router rules.
|
||||
Those characters, when decoded, change the meaning of the request path for routing purposes,
|
||||
and Traefik now keeps them encoded to avoid any ambiguity.
|
||||
|
||||
### Request Path Matching Examples
|
||||
|
||||
| Request Path | Router Rule | Traefik v3.4.0 | Traefik v3.4.1 |
|
||||
|-------------------|------------------------|----------------|----------------|
|
||||
| `/foo%2Fbar` | PathPrefix(`/foo/bar`) | Match | No match |
|
||||
| `/foo/../bar` | PathPrefix(`/foo`) | No match | No match |
|
||||
| `/foo/../bar` | PathPrefix(`/bar`) | Match | Match |
|
||||
| `/foo/%2E%2E/bar` | PathPrefix(`/foo`) | Match | No match |
|
||||
| `/foo/%2E%2E/bar` | PathPrefix(`/bar`) | No match | Match |
|
||||
|
@ -288,8 +288,6 @@ It is possible to configure the Traefik to timestamp in a specific timezone by e
|
||||
Example utilizing Docker Compose:
|
||||
|
||||
```yaml
|
||||
version: "3.7"
|
||||
|
||||
services:
|
||||
traefik:
|
||||
image: traefik:v3.4
|
||||
|
@ -40,7 +40,6 @@ This provider works with [Docker (standalone) Engine](https://docs.docker.com/en
|
||||
Attaching labels to containers (in your docker compose file)
|
||||
|
||||
```yaml
|
||||
version: "3"
|
||||
services:
|
||||
my-container:
|
||||
# ...
|
||||
@ -162,8 +161,6 @@ See the [Docker API Access](#docker-api-access) section for more information.
|
||||
The docker-compose file shares the docker sock with the Traefik container
|
||||
|
||||
```yaml
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
traefik:
|
||||
image: traefik:v3.4 # The official v3 Traefik docker image
|
||||
|
@ -53,7 +53,6 @@ This provider works with [Docker Swarm Mode](https://docs.docker.com/engine/swar
|
||||
then that service is automatically assigned to the router.
|
||||
|
||||
```yaml
|
||||
version: "3"
|
||||
services:
|
||||
my-container:
|
||||
deploy:
|
||||
@ -176,8 +175,6 @@ docker service create \
|
||||
```
|
||||
|
||||
```yml tab="With Docker Compose"
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
traefik:
|
||||
# ...
|
||||
@ -208,8 +205,6 @@ See the [Docker Swarm API Access](#docker-api-access) section for more informati
|
||||
The docker-compose file shares the docker sock with the Traefik container
|
||||
|
||||
```yaml
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
traefik:
|
||||
image: traefik:v3.4 # The official v3 Traefik docker image
|
||||
|
@ -2291,7 +2291,7 @@ spec:
|
||||
type: object
|
||||
x-kubernetes-validations:
|
||||
- message: RootCA cannot have both Secret and ConfigMap defined.
|
||||
rule: has(self.secret) && has(self.configMap)
|
||||
rule: '!has(self.secret) || !has(self.configMap)'
|
||||
type: array
|
||||
rootCAsSecrets:
|
||||
description: |-
|
||||
@ -2436,7 +2436,7 @@ spec:
|
||||
type: object
|
||||
x-kubernetes-validations:
|
||||
- message: RootCA cannot have both Secret and ConfigMap defined.
|
||||
rule: has(self.secret) && has(self.configMap)
|
||||
rule: '!has(self.secret) || !has(self.configMap)'
|
||||
type: array
|
||||
rootCAsSecrets:
|
||||
description: |-
|
||||
|
@ -134,7 +134,7 @@ spec:
|
||||
type: object
|
||||
x-kubernetes-validations:
|
||||
- message: RootCA cannot have both Secret and ConfigMap defined.
|
||||
rule: has(self.secret) && has(self.configMap)
|
||||
rule: '!has(self.secret) || !has(self.configMap)'
|
||||
type: array
|
||||
rootCAsSecrets:
|
||||
description: |-
|
||||
|
@ -110,7 +110,7 @@ spec:
|
||||
type: object
|
||||
x-kubernetes-validations:
|
||||
- message: RootCA cannot have both Secret and ConfigMap defined.
|
||||
rule: has(self.secret) && has(self.configMap)
|
||||
rule: '!has(self.secret) || !has(self.configMap)'
|
||||
type: array
|
||||
rootCAsSecrets:
|
||||
description: |-
|
||||
|
@ -199,8 +199,6 @@ It is possible to configure the Traefik to timestamp in a specific timezone by e
|
||||
Example utilizing Docker Compose:
|
||||
|
||||
```yaml
|
||||
version: "3.7"
|
||||
|
||||
services:
|
||||
traefik:
|
||||
image: traefik:v3.4
|
||||
|
@ -29,7 +29,6 @@ providers:
|
||||
Attach labels to containers (in your Docker compose file)
|
||||
|
||||
```yaml
|
||||
version: "3"
|
||||
services:
|
||||
my-container:
|
||||
# ...
|
||||
@ -67,8 +66,6 @@ See the [Docker API Access](#docker-api-access) section for more information.
|
||||
The docker-compose file shares the docker sock with the Traefik container
|
||||
|
||||
```yaml
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
traefik:
|
||||
image: traefik:v3.1 # The official v3 Traefik docker image
|
||||
|
@ -33,7 +33,6 @@ When there is only one service, and the router does not specify a service,
|
||||
then that service is automatically assigned to the router.
|
||||
|
||||
```yaml tab="Labels"
|
||||
version: "3"
|
||||
services:
|
||||
my-container:
|
||||
deploy:
|
||||
@ -73,8 +72,6 @@ See the [Docker Swarm API Access](#docker-api-access) section for more informati
|
||||
The docker-compose file shares the docker sock with the Traefik container
|
||||
|
||||
```yaml
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
traefik:
|
||||
image: traefik:v3.1 # The official v3 Traefik docker image
|
||||
@ -405,8 +402,6 @@ docker service create \
|
||||
```
|
||||
|
||||
```yml tab="With Docker Compose"
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
traefik:
|
||||
# ...
|
||||
|
@ -72,8 +72,6 @@ When using Docker or Amazon ECS, you can define routing configuration using cont
|
||||
When deploying a Docker container, you can specify labels to define routing rules and services:
|
||||
|
||||
```yaml
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
my-service:
|
||||
image: my-image
|
||||
|
@ -202,109 +202,6 @@ spec:
|
||||
- `X-Forwarded-Tls-Client-Cert-Info` header value is a string that has been escaped in order to be a valid URL query.
|
||||
- These options only work accordingly to the MutualTLS configuration. i.e, only the certificates that match the `clientAuth.clientAuthType` policy are passed.
|
||||
|
||||
??? example "Example of a complete certificate and explaining each of the middleware options"
|
||||
|
||||
```txt
|
||||
Certificate:
|
||||
Data:
|
||||
Version: 3 (0x2)
|
||||
Serial Number: 1 (0x1)
|
||||
Signature Algorithm: sha1WithRSAEncryption
|
||||
Issuer: DC=org, DC=cheese, O=Cheese, O=Cheese 2, OU=Simple Signing Section, OU=Simple Signing Section 2, CN=Simple Signing CA, CN=Simple Signing CA 2, C=FR, C=US, L=TOULOUSE, L=LYON, ST=Signing State, ST=Signing State 2/emailAddress=simple@signing.com/emailAddress=simple2@signing.com
|
||||
Validity
|
||||
Not Before: Dec 6 11:10:16 2018 GMT
|
||||
Not After : Dec 5 11:10:16 2020 GMT
|
||||
Subject: DC=org, DC=cheese, O=Cheese, O=Cheese 2, OU=Simple Signing Section, OU=Simple Signing Section 2, CN=*.example.org, CN=*.example.com, C=FR, C=US, L=TOULOUSE, L=LYON, ST=Cheese org state, ST=Cheese com statemailAddress=cert@example.org/emailAddress=cert@sexample.com
|
||||
Subject Public Key Info:
|
||||
Public Key Algorithm: rsaEncryption
|
||||
RSA Public-Key: (2048 bit)
|
||||
Modulus:
|
||||
00:de:77:fa:8d:03:70:30:39:dd:51:1b:cc:60:db:
|
||||
a9:5a:13:b1:af:fe:2c:c6:38:9b:88:0a:0f:8e:d9:
|
||||
1b:a1:1d:af:0d:66:e4:13:5b:bc:5d:36:92:d7:5e:
|
||||
d0:fa:88:29:d3:78:e1:81:de:98:b2:a9:22:3f:bf:
|
||||
8a:af:12:92:63:d4:a9:c3:f2:e4:7e:d2:dc:a2:c5:
|
||||
39:1c:7a:eb:d7:12:70:63:2e:41:47:e0:f0:08:e8:
|
||||
dc:be:09:01:ec:28:09:af:35:d7:79:9c:50:35:d1:
|
||||
6b:e5:87:7b:34:f6:d2:31:65:1d:18:42:69:6c:04:
|
||||
11:83:fe:44:ae:90:92:2d:0b:75:39:57:62:e6:17:
|
||||
2f:47:2b:c7:53:dd:10:2d:c9:e3:06:13:d2:b9:ba:
|
||||
63:2e:3c:7d:83:6b:d6:89:c9:cc:9d:4d:bf:9f:e8:
|
||||
a3:7b:da:c8:99:2b:ba:66:d6:8e:f8:41:41:a0:c9:
|
||||
d0:5e:c8:11:a4:55:4a:93:83:87:63:04:63:41:9c:
|
||||
fb:68:04:67:c2:71:2f:f2:65:1d:02:5d:15:db:2c:
|
||||
d9:04:69:85:c2:7d:0d:ea:3b:ac:85:f8:d4:8f:0f:
|
||||
c5:70:b2:45:e1:ec:b2:54:0b:e9:f7:82:b4:9b:1b:
|
||||
2d:b9:25:d4:ab:ca:8f:5b:44:3e:15:dd:b8:7f:b7:
|
||||
ee:f9
|
||||
Exponent: 65537 (0x10001)
|
||||
X509v3 extensions:
|
||||
X509v3 Key Usage: critical
|
||||
Digital Signature, Key Encipherment
|
||||
X509v3 Basic Constraints:
|
||||
CA:FALSE
|
||||
X509v3 Extended Key Usage:
|
||||
TLS Web Server Authentication, TLS Web Client Authentication
|
||||
X509v3 Subject Key Identifier:
|
||||
94:BA:73:78:A2:87:FB:58:28:28:CF:98:3B:C2:45:70:16:6E:29:2F
|
||||
X509v3 Authority Key Identifier:
|
||||
keyid:1E:52:A2:E8:54:D5:37:EB:D5:A8:1D:E4:C2:04:1D:37:E2:F7:70:03
|
||||
|
||||
X509v3 Subject Alternative Name:
|
||||
DNS:*.example.org, DNS:*.example.net, DNS:*.example.com, IP Address:10.0.1.0, IP Address:10.0.1.2, email:test@example.org, email:test@example.net
|
||||
Signature Algorithm: sha1WithRSAEncryption
|
||||
76:6b:05:b0:0e:34:11:b1:83:99:91:dc:ae:1b:e2:08:15:8b:
|
||||
16:b2:9b:27:1c:02:ac:b5:df:1b:d0:d0:75:a4:2b:2c:5c:65:
|
||||
ed:99:ab:f7:cd:fe:38:3f:c3:9a:22:31:1b:ac:8c:1c:c2:f9:
|
||||
5d:d4:75:7a:2e:72:c7:85:a9:04:af:9f:2a:cc:d3:96:75:f0:
|
||||
8e:c7:c6:76:48:ac:45:a4:b9:02:1e:2f:c0:15:c4:07:08:92:
|
||||
cb:27:50:67:a1:c8:05:c5:3a:b3:a6:48:be:eb:d5:59:ab:a2:
|
||||
1b:95:30:71:13:5b:0a:9a:73:3b:60:cc:10:d0:6a:c7:e5:d7:
|
||||
8b:2f:f9:2e:98:f2:ff:81:14:24:09:e3:4b:55:57:09:1a:22:
|
||||
74:f1:f6:40:13:31:43:89:71:0a:96:1a:05:82:1f:83:3a:87:
|
||||
9b:17:25:ef:5a:55:f2:2d:cd:0d:4d:e4:81:58:b6:e3:8d:09:
|
||||
62:9a:0c:bd:e4:e5:5c:f0:95:da:cb:c7:34:2c:34:5f:6d:fc:
|
||||
60:7b:12:5b:86:fd:df:21:89:3b:48:08:30:bf:67:ff:8c:e6:
|
||||
9b:53:cc:87:36:47:70:40:3b:d9:90:2a:d2:d2:82:c6:9c:f5:
|
||||
d1:d8:e0:e6:fd:aa:2f:95:7e:39:ac:fc:4e:d4:ce:65:b3:ec:
|
||||
c6:98:8a:31
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIGWjCCBUKgAwIBAgIBATANBgkqhkiG9w0BAQUFADCCAYQxEzARBgoJkiaJk/Is
|
||||
ZAEZFgNvcmcxFjAUBgoJkiaJk/IsZAEZFgZjaGVlc2UxDzANBgNVBAoMBkNoZWVz
|
||||
ZTERMA8GA1UECgwIQ2hlZXNlIDIxHzAdBgNVBAsMFlNpbXBsZSBTaWduaW5nIFNl
|
||||
Y3Rpb24xITAfBgNVBAsMGFNpbXBsZSBTaWduaW5nIFNlY3Rpb24gMjEaMBgGA1UE
|
||||
AwwRU2ltcGxlIFNpZ25pbmcgQ0ExHDAaBgNVBAMME1NpbXBsZSBTaWduaW5nIENB
|
||||
IDIxCzAJBgNVBAYTAkZSMQswCQYDVQQGEwJVUzERMA8GA1UEBwwIVE9VTE9VU0Ux
|
||||
DTALBgNVBAcMBExZT04xFjAUBgNVBAgMDVNpZ25pbmcgU3RhdGUxGDAWBgNVBAgM
|
||||
D1NpZ25pbmcgU3RhdGUgMjEhMB8GCSqGSIb3DQEJARYSc2ltcGxlQHNpZ25pbmcu
|
||||
Y29tMSIwIAYJKoZIhvcNAQkBFhNzaW1wbGUyQHNpZ25pbmcuY29tMB4XDTE4MTIw
|
||||
NjExMTAxNloXDTIwMTIwNTExMTAxNlowggF2MRMwEQYKCZImiZPyLGQBGRYDb3Jn
|
||||
MRYwFAYKCZImiZPyLGQBGRYGY2hlZXNlMQ8wDQYDVQQKDAZDaGVlc2UxETAPBgNV
|
||||
BAoMCENoZWVzZSAyMR8wHQYDVQQLDBZTaW1wbGUgU2lnbmluZyBTZWN0aW9uMSEw
|
||||
HwYDVQQLDBhTaW1wbGUgU2lnbmluZyBTZWN0aW9uIDIxFTATBgNVBAMMDCouY2hl
|
||||
ZXNlLm9yZzEVMBMGA1UEAwwMKi5jaGVlc2UuY29tMQswCQYDVQQGEwJGUjELMAkG
|
||||
A1UEBhMCVVMxETAPBgNVBAcMCFRPVUxPVVNFMQ0wCwYDVQQHDARMWU9OMRkwFwYD
|
||||
VQQIDBBDaGVlc2Ugb3JnIHN0YXRlMRkwFwYDVQQIDBBDaGVlc2UgY29tIHN0YXRl
|
||||
MR4wHAYJKoZIhvcNAQkBFg9jZXJ0QGNoZWVzZS5vcmcxHzAdBgkqhkiG9w0BCQEW
|
||||
EGNlcnRAc2NoZWVzZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
|
||||
AQDed/qNA3AwOd1RG8xg26laE7Gv/izGOJuICg+O2RuhHa8NZuQTW7xdNpLXXtD6
|
||||
iCnTeOGB3piyqSI/v4qvEpJj1KnD8uR+0tyixTkceuvXEnBjLkFH4PAI6Ny+CQHs
|
||||
KAmvNdd5nFA10Wvlh3s09tIxZR0YQmlsBBGD/kSukJItC3U5V2LmFy9HK8dT3RAt
|
||||
yeMGE9K5umMuPH2Da9aJycydTb+f6KN72siZK7pm1o74QUGgydBeyBGkVUqTg4dj
|
||||
BGNBnPtoBGfCcS/yZR0CXRXbLNkEaYXCfQ3qO6yF+NSPD8VwskXh7LJUC+n3grSb
|
||||
Gy25JdSryo9bRD4V3bh/t+75AgMBAAGjgeAwgd0wDgYDVR0PAQH/BAQDAgWgMAkG
|
||||
A1UdEwQCMAAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMB0GA1UdDgQW
|
||||
BBSUunN4oof7WCgoz5g7wkVwFm4pLzAfBgNVHSMEGDAWgBQeUqLoVNU369WoHeTC
|
||||
BB034vdwAzBhBgNVHREEWjBYggwqLmNoZWVzZS5vcmeCDCouY2hlZXNlLm5ldIIM
|
||||
Ki5jaGVlc2UuY29thwQKAAEAhwQKAAECgQ90ZXN0QGNoZWVzZS5vcmeBD3Rlc3RA
|
||||
Y2hlZXNlLm5ldDANBgkqhkiG9w0BAQUFAAOCAQEAdmsFsA40EbGDmZHcrhviCBWL
|
||||
FrKbJxwCrLXfG9DQdaQrLFxl7Zmr983+OD/DmiIxG6yMHML5XdR1ei5yx4WpBK+f
|
||||
KszTlnXwjsfGdkisRaS5Ah4vwBXEBwiSyydQZ6HIBcU6s6ZIvuvVWauiG5UwcRNb
|
||||
CppzO2DMENBqx+XXiy/5Lpjy/4EUJAnjS1VXCRoidPH2QBMxQ4lxCpYaBYIfgzqH
|
||||
mxcl71pV8i3NDU3kgVi2440JYpoMveTlXPCV2svHNCw0X238YHsSW4b93yGJO0gI
|
||||
-----END CERTIFICATE-----
|
||||
```
|
||||
|
||||
## Configuration Options
|
||||
|
||||
| Field | Description | Default | Required |
|
||||
@ -362,5 +259,5 @@ If there are more than one certificate, they are separated by a `,`.
|
||||
The following example shows such a concatenation, when all the available fields are selected:
|
||||
|
||||
```text
|
||||
Subject="DC=org,DC=cheese,C=FR,C=US,ST=Cheese org state,ST=Cheese com state,L=TOULOUSE,L=LYON,O=Cheese,O=Cheese 2,CN=*.example.com";Issuer="DC=org,DC=cheese,C=FR,C=US,ST=Signing State,ST=Signing State 2,L=TOULOUSE,L=LYON,O=Cheese,O=Cheese 2,CN=Simple Signing CA 2";NB="1544094616";NA="1607166616";SAN="*.example.org,*.example.net,*.example.com,test@example.org,test@example.net,10.0.1.0,10.0.1.2"
|
||||
Subject="DC=org,DC=cheese,C=FR,C=US,ST=Cheese org state,ST=Cheese com state,L=TOULOUSE,L=LYON,O=Cheese,O=Cheese 2,CN=*.example.com";Issuer="DC=org,DC=cheese,C=FR,C=US,ST=Signing State,ST=Signing State 2,L=TOULOUSE,L=LYON,O=Cheese,O=Cheese 2,CN=Simple Signing CA 2";NB="1747282426";NA="1778818426"SAN="*.example.org,*.example.net,*.example.com,test@example.org,test@example.net,10.0.1.0,10.0.1.2"
|
||||
```
|
||||
|
@ -35,7 +35,6 @@ With Docker, Traefik can leverage labels attached to a container to generate rou
|
||||
Attaching labels to containers (in your docker compose file)
|
||||
|
||||
```yaml
|
||||
version: "3"
|
||||
services:
|
||||
my-container:
|
||||
# ...
|
||||
@ -48,7 +47,6 @@ With Docker, Traefik can leverage labels attached to a container to generate rou
|
||||
Forward requests for `http://example.com` to `http://<private IP of container>:12345`:
|
||||
|
||||
```yaml
|
||||
version: "3"
|
||||
services:
|
||||
my-container:
|
||||
# ...
|
||||
@ -71,7 +69,6 @@ With Docker, Traefik can leverage labels attached to a container to generate rou
|
||||
In this example, requests are forwarded for `http://example-a.com` to `http://<private IP of container>:8000` in addition to `http://example-b.com` forwarding to `http://<private IP of container>:9000`:
|
||||
|
||||
```yaml
|
||||
version: "3"
|
||||
services:
|
||||
my-container:
|
||||
# ...
|
||||
|
@ -48,7 +48,6 @@ With Docker Swarm, Traefik can leverage labels attached to a service to generate
|
||||
then that service is automatically assigned to the router.
|
||||
|
||||
```yaml
|
||||
version: "3"
|
||||
services:
|
||||
my-container:
|
||||
deploy:
|
||||
@ -67,7 +66,6 @@ With Docker Swarm, Traefik can leverage labels attached to a service to generate
|
||||
Forward requests for `http://example.com` to `http://<private IP of container>:12345`:
|
||||
|
||||
```yaml
|
||||
version: "3"
|
||||
services:
|
||||
my-container:
|
||||
# ...
|
||||
@ -93,7 +91,6 @@ With Docker Swarm, Traefik can leverage labels attached to a service to generate
|
||||
In this example, requests are forwarded for `http://example-a.com` to `http://<private IP of container>:8000` in addition to `http://example-b.com` forwarding to `http://<private IP of container>:9000`:
|
||||
|
||||
```yaml
|
||||
version: "3"
|
||||
services:
|
||||
my-container:
|
||||
# ...
|
||||
|
@ -42,7 +42,6 @@ With Docker, Traefik can leverage labels attached to a container to generate rou
|
||||
Attaching labels to containers (in your docker compose file)
|
||||
|
||||
```yaml
|
||||
version: "3"
|
||||
services:
|
||||
my-container:
|
||||
# ...
|
||||
@ -55,7 +54,6 @@ With Docker, Traefik can leverage labels attached to a container to generate rou
|
||||
Forward requests for `http://example.com` to `http://<private IP of container>:12345`:
|
||||
|
||||
```yaml
|
||||
version: "3"
|
||||
services:
|
||||
my-container:
|
||||
# ...
|
||||
@ -78,7 +76,6 @@ With Docker, Traefik can leverage labels attached to a container to generate rou
|
||||
In this example, requests are forwarded for `http://example-a.com` to `http://<private IP of container>:8000` in addition to `http://example-b.com` forwarding to `http://<private IP of container>:9000`:
|
||||
|
||||
```yaml
|
||||
version: "3"
|
||||
services:
|
||||
my-container:
|
||||
# ...
|
||||
|
@ -55,7 +55,6 @@ With Docker Swarm, Traefik can leverage labels attached to a service to generate
|
||||
then that service is automatically assigned to the router.
|
||||
|
||||
```yaml
|
||||
version: "3"
|
||||
services:
|
||||
my-container:
|
||||
deploy:
|
||||
@ -74,7 +73,6 @@ With Docker Swarm, Traefik can leverage labels attached to a service to generate
|
||||
Forward requests for `http://example.com` to `http://<private IP of container>:12345`:
|
||||
|
||||
```yaml
|
||||
version: "3"
|
||||
services:
|
||||
my-container:
|
||||
# ...
|
||||
@ -100,7 +98,6 @@ With Docker Swarm, Traefik can leverage labels attached to a service to generate
|
||||
In this example, requests are forwarded for `http://example-a.com` to `http://<private IP of container>:8000` in addition to `http://example-b.com` forwarding to `http://<private IP of container>:9000`:
|
||||
|
||||
```yaml
|
||||
version: "3"
|
||||
services:
|
||||
my-container:
|
||||
# ...
|
||||
|
@ -5,7 +5,7 @@ description: "Enforce strict Content‑Length validation in Traefik by streaming
|
||||
|
||||
Traefik acts as a streaming proxy. By default, it checks each chunk of data against the `Content-Length` header as it passes it on to the backend or client. This live check blocks truncated or over‑long streams without holding the entire message.
|
||||
|
||||
If you need Traefik to read and verify the full body before any data moves on, add the [buffering middleware](../../reference/routing-configuration/http/middlewares/buffering.md):
|
||||
If you need Traefik to read and verify the full body before any data moves on, add the [buffering middleware](../middlewares/http/buffering.md):
|
||||
|
||||
```yaml
|
||||
http:
|
||||
|
@ -1,5 +1,3 @@
|
||||
version: "3.3"
|
||||
|
||||
services:
|
||||
|
||||
traefik:
|
||||
|
@ -1,5 +1,3 @@
|
||||
version: "3.3"
|
||||
|
||||
secrets:
|
||||
ovh_endpoint:
|
||||
file: "./secrets/ovh_endpoint.secret"
|
||||
|
@ -1,5 +1,3 @@
|
||||
version: "3.3"
|
||||
|
||||
services:
|
||||
|
||||
traefik:
|
||||
|
@ -1,5 +1,3 @@
|
||||
version: "3.3"
|
||||
|
||||
services:
|
||||
|
||||
traefik:
|
||||
|
@ -1,5 +1,3 @@
|
||||
version: "3.3"
|
||||
|
||||
services:
|
||||
|
||||
traefik:
|
||||
|
@ -23,8 +23,6 @@ Create a `docker-compose.yml` file with the following content:
|
||||
You can use a [pre-existing network](https://docs.docker.com/compose/networking/#use-a-pre-existing-network "Link to Docker Compose networking docs") too.
|
||||
|
||||
```yaml
|
||||
version: "3.3"
|
||||
|
||||
networks:
|
||||
traefiknet: {}
|
||||
|
||||
|
355
docs/content/user-guides/websocket.md
Normal file
355
docs/content/user-guides/websocket.md
Normal file
@ -0,0 +1,355 @@
|
||||
---
|
||||
title: "Traefik WebSocket Documentation"
|
||||
description: "How to configure WebSocket and WebSocket Secure (WSS) connections with Traefik Proxy."
|
||||
---
|
||||
|
||||
# WebSocket
|
||||
|
||||
Configuring Traefik to handle WebSocket and WebSocket Secure (WSS) connections.
|
||||
{: .subtitle }
|
||||
|
||||
## Overview
|
||||
|
||||
WebSocket is a communication protocol that provides full-duplex communication channels over a single TCP connection.
|
||||
WebSocket Secure (WSS) is the encrypted version of WebSocket, using TLS/SSL encryption.
|
||||
|
||||
Traefik supports WebSocket and WebSocket Secure (WSS) out of the box. This guide will walk through examples of how to configure Traefik for different WebSocket scenarios.
|
||||
|
||||
## Basic WebSocket Configuration
|
||||
|
||||
A basic WebSocket configuration only requires defining a router and a service that points to your WebSocket server.
|
||||
|
||||
```yaml tab="Docker & Swarm"
|
||||
labels:
|
||||
- "traefik.http.routers.my-websocket.rule=Host(`ws.example.com`)"
|
||||
- "traefik.http.routers.my-websocket.service=my-websocket-service"
|
||||
- "traefik.http.services.my-websocket-service.loadbalancer.server.port=8000"
|
||||
```
|
||||
|
||||
```yaml tab="Kubernetes"
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: IngressRoute
|
||||
metadata:
|
||||
name: my-websocket-route
|
||||
spec:
|
||||
entryPoints:
|
||||
- web
|
||||
routes:
|
||||
- match: Host(`ws.example.com`)
|
||||
kind: Rule
|
||||
services:
|
||||
- name: my-websocket-service
|
||||
port: 8000
|
||||
```
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
http:
|
||||
routers:
|
||||
my-websocket:
|
||||
rule: "Host(`ws.example.com`)"
|
||||
service: my-websocket-service
|
||||
|
||||
services:
|
||||
my-websocket-service:
|
||||
loadBalancer:
|
||||
servers:
|
||||
- url: "http://my-websocket-server:8000"
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[http.routers]
|
||||
[http.routers.my-websocket]
|
||||
rule = "Host(`ws.example.com`)"
|
||||
service = "my-websocket-service"
|
||||
|
||||
[http.services]
|
||||
[http.services.my-websocket-service]
|
||||
[http.services.my-websocket-service.loadBalancer]
|
||||
[[http.services.my-websocket-service.loadBalancer.servers]]
|
||||
url = "http://my-websocket-server:8000"
|
||||
```
|
||||
|
||||
## WebSocket Secure (WSS) Configuration
|
||||
|
||||
WebSocket Secure (WSS) requires TLS configuration.
|
||||
The client connects using the `wss://` protocol instead of `ws://`.
|
||||
|
||||
```yaml tab="Docker & Swarm"
|
||||
labels:
|
||||
- "traefik.http.routers.my-websocket-secure.rule=Host(`wss.example.com`)"
|
||||
- "traefik.http.routers.my-websocket-secure.service=my-websocket-service"
|
||||
- "traefik.http.routers.my-websocket-secure.tls=true"
|
||||
- "traefik.http.services.my-websocket-service.loadbalancer.server.port=8000"
|
||||
```
|
||||
|
||||
```yaml tab="Kubernetes"
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: IngressRoute
|
||||
metadata:
|
||||
name: my-websocket-secure-route
|
||||
spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: Host(`wss.example.com`)
|
||||
kind: Rule
|
||||
services:
|
||||
- name: my-websocket-service
|
||||
port: 8000
|
||||
tls: {}
|
||||
```
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
http:
|
||||
routers:
|
||||
my-websocket-secure:
|
||||
rule: "Host(`wss.example.com`)"
|
||||
service: my-websocket-service
|
||||
tls: {}
|
||||
|
||||
services:
|
||||
my-websocket-service:
|
||||
loadBalancer:
|
||||
servers:
|
||||
- url: "http://my-websocket-server:8000"
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[http.routers]
|
||||
[http.routers.my-websocket-secure]
|
||||
rule = "Host(`wss.example.com`)"
|
||||
service = "my-websocket-service"
|
||||
[http.routers.my-websocket-secure.tls]
|
||||
|
||||
[http.services]
|
||||
[http.services.my-websocket-service]
|
||||
[http.services.my-websocket-service.loadBalancer]
|
||||
[[http.services.my-websocket-service.loadBalancer.servers]]
|
||||
url = "http://my-websocket-server:8000"
|
||||
```
|
||||
|
||||
## SSL Termination for WebSockets
|
||||
|
||||
In this scenario, clients connect to Traefik using WSS (encrypted), but Traefik connects to your backend server using WS (unencrypted).
|
||||
This is called SSL termination.
|
||||
|
||||
```yaml tab="Docker & Swarm"
|
||||
labels:
|
||||
- "traefik.http.routers.my-wss-termination.rule=Host(`wss.example.com`)"
|
||||
- "traefik.http.routers.my-wss-termination.service=my-ws-service"
|
||||
- "traefik.http.routers.my-wss-termination.tls=true"
|
||||
- "traefik.http.services.my-ws-service.loadbalancer.server.port=8000"
|
||||
```
|
||||
|
||||
```yaml tab="Kubernetes"
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: IngressRoute
|
||||
metadata:
|
||||
name: my-wss-termination-route
|
||||
spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: Host(`wss.example.com`)
|
||||
kind: Rule
|
||||
services:
|
||||
- name: my-ws-service
|
||||
port: 8000
|
||||
tls: {}
|
||||
```
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
http:
|
||||
routers:
|
||||
my-wss-termination:
|
||||
rule: "Host(`wss.example.com`)"
|
||||
service: my-ws-service
|
||||
tls: {}
|
||||
|
||||
services:
|
||||
my-ws-service:
|
||||
loadBalancer:
|
||||
servers:
|
||||
- url: "http://my-ws-server:8000"
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[http.routers]
|
||||
[http.routers.my-wss-termination]
|
||||
rule = "Host(`wss.example.com`)"
|
||||
service = "my-ws-service"
|
||||
[http.routers.my-wss-termination.tls]
|
||||
|
||||
[http.services]
|
||||
[http.services.my-ws-service]
|
||||
[http.services.my-ws-service.loadBalancer]
|
||||
[[http.services.my-ws-service.loadBalancer.servers]]
|
||||
url = "http://my-ws-server:8000"
|
||||
```
|
||||
|
||||
## End-to-End WebSocket Secure (WSS)
|
||||
|
||||
For end-to-end encryption, Traefik can be configured to connect to your backend using HTTPS.
|
||||
|
||||
```yaml tab="Docker & Swarm"
|
||||
labels:
|
||||
- "traefik.http.routers.my-wss-e2e.rule=Host(`wss.example.com`)"
|
||||
- "traefik.http.routers.my-wss-e2e.service=my-wss-service"
|
||||
- "traefik.http.routers.my-wss-e2e.tls=true"
|
||||
- "traefik.http.services.my-wss-service.loadbalancer.server.port=8443"
|
||||
# If the backend uses a self-signed certificate
|
||||
- "traefik.http.serversTransports.insecureTransport.insecureSkipVerify=true"
|
||||
- "traefik.http.services.my-wss-service.loadBalancer.serversTransport=insecureTransport"
|
||||
```
|
||||
|
||||
```yaml tab="Kubernetes"
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: ServersTransport
|
||||
metadata:
|
||||
name: insecure-transport
|
||||
spec:
|
||||
insecureSkipVerify: true
|
||||
|
||||
---
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: IngressRoute
|
||||
metadata:
|
||||
name: my-wss-e2e-route
|
||||
spec:
|
||||
entryPoints:
|
||||
- websecure
|
||||
routes:
|
||||
- match: Host(`wss.example.com`)
|
||||
kind: Rule
|
||||
services:
|
||||
- name: my-wss-service
|
||||
port: 8443
|
||||
serversTransport: insecure-transport
|
||||
tls: {}
|
||||
```
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
http:
|
||||
serversTransports:
|
||||
insecureTransport:
|
||||
insecureSkipVerify: true
|
||||
|
||||
routers:
|
||||
my-wss-e2e:
|
||||
rule: "Host(`wss.example.com`)"
|
||||
service: my-wss-service
|
||||
tls: {}
|
||||
|
||||
services:
|
||||
my-wss-service:
|
||||
loadBalancer:
|
||||
serversTransport: insecureTransport
|
||||
servers:
|
||||
- url: "https://my-wss-server:8443"
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[http.serversTransports]
|
||||
[http.serversTransports.insecureTransport]
|
||||
insecureSkipVerify = true
|
||||
|
||||
[http.routers]
|
||||
[http.routers.my-wss-e2e]
|
||||
rule = "Host(`wss.example.com`)"
|
||||
service = "my-wss-service"
|
||||
[http.routers.my-wss-e2e.tls]
|
||||
|
||||
[http.services]
|
||||
[http.services.my-wss-service]
|
||||
[http.services.my-wss-service.loadBalancer]
|
||||
serversTransport = "insecureTransport"
|
||||
[[http.services.my-wss-service.loadBalancer.servers]]
|
||||
url = "https://my-wss-server:8443"
|
||||
```
|
||||
|
||||
## EntryPoints Configuration for WebSockets
|
||||
|
||||
In your Traefik static configuration, you'll need to define entryPoints for both WS and WSS:
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
entryPoints:
|
||||
web:
|
||||
address: ":80"
|
||||
websecure:
|
||||
address: ":443"
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
[entryPoints]
|
||||
[entryPoints.web]
|
||||
address = ":80"
|
||||
[entryPoints.websecure]
|
||||
address = ":443"
|
||||
```
|
||||
|
||||
## Testing WebSocket Connections
|
||||
|
||||
You can test your WebSocket configuration using various tools:
|
||||
|
||||
1. Browser Developer Tools: Most modern browsers include WebSocket debugging in their developer tools.
|
||||
2. WebSocket client tools like [wscat](https://github.com/websockets/wscat) or online tools like [Piesocket's WebSocket Tester](https://www.piesocket.com/websocket-tester).
|
||||
|
||||
Example wscat commands:
|
||||
|
||||
```bash
|
||||
# Test standard WebSocket
|
||||
wscat -c ws://ws.example.com
|
||||
|
||||
# Test WebSocket Secure
|
||||
wscat -c wss://wss.example.com
|
||||
```
|
||||
|
||||
## Common Issues and Solutions
|
||||
|
||||
### Headers and Origin Checks
|
||||
|
||||
Some WebSocket servers implement origin checking. Traefik passes the original headers to your backend, including the `Origin` header.
|
||||
|
||||
If you need to manipulate headers for WebSocket connections, you can use Traefik's Headers middleware:
|
||||
|
||||
```yaml tab="Docker & Swarm"
|
||||
labels:
|
||||
- "traefik.http.middlewares.my-headers.headers.customrequestheaders.Origin=https://allowed-origin.com"
|
||||
- "traefik.http.routers.my-websocket.middlewares=my-headers"
|
||||
```
|
||||
|
||||
```yaml tab="Kubernetes"
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: Middleware
|
||||
metadata:
|
||||
name: my-headers
|
||||
spec:
|
||||
headers:
|
||||
customRequestHeaders:
|
||||
Origin: "https://allowed-origin.com"
|
||||
|
||||
---
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: IngressRoute
|
||||
metadata:
|
||||
name: my-websocket-route
|
||||
spec:
|
||||
routes:
|
||||
- match: Host(`ws.example.com`)
|
||||
kind: Rule
|
||||
middlewares:
|
||||
- name: my-headers
|
||||
services:
|
||||
- name: my-websocket-service
|
||||
port: 8000
|
||||
```
|
||||
|
||||
### Certificate Issues with WSS
|
||||
|
||||
If you're experiencing certificate issues with WSS:
|
||||
|
||||
1. Ensure your certificates are valid and not expired
|
||||
2. For testing with self-signed certificates, configure your clients to accept them
|
||||
3. When using Let's Encrypt, ensure your domain is properly configured
|
||||
|
||||
For backends with self-signed certificates, use the `insecureSkipVerify` option in the ServersTransport configuration as shown in the examples above.
|
@ -171,6 +171,7 @@ nav:
|
||||
- 'Kubernetes and Let''s Encrypt': 'user-guides/crd-acme/index.md'
|
||||
- 'Kubernetes and cert-manager': 'user-guides/cert-manager.md'
|
||||
- 'gRPC Examples': 'user-guides/grpc.md'
|
||||
- 'WebSocket Examples': 'user-guides/websocket.md'
|
||||
- 'Docker':
|
||||
- 'Basic Example': 'user-guides/docker-compose/basic-example/index.md'
|
||||
- 'HTTPS with Let''s Encrypt':
|
||||
|
4
go.mod
4
go.mod
@ -1,6 +1,6 @@
|
||||
module github.com/traefik/traefik/v3
|
||||
|
||||
go 1.23.0
|
||||
go 1.24.0
|
||||
|
||||
require (
|
||||
github.com/BurntSushi/toml v1.5.0
|
||||
@ -391,7 +391,7 @@ require (
|
||||
// Containous forks
|
||||
replace (
|
||||
github.com/abbot/go-http-auth => github.com/containous/go-http-auth v0.4.1-0.20200324110947-a37a7636d23e
|
||||
github.com/gorilla/mux => github.com/containous/mux v0.0.0-20220627093034-b2dd784e613f
|
||||
github.com/gorilla/mux => github.com/containous/mux v0.0.0-20250523120546-41b6ec3aed59
|
||||
github.com/mailgun/minheap => github.com/containous/minheap v0.0.0-20190809180810-6e71eb837595
|
||||
)
|
||||
|
||||
|
4
go.sum
4
go.sum
@ -253,8 +253,8 @@ github.com/containous/go-http-auth v0.4.1-0.20200324110947-a37a7636d23e h1:D+uTE
|
||||
github.com/containous/go-http-auth v0.4.1-0.20200324110947-a37a7636d23e/go.mod h1:s8kLgBQolDbsJOPVIGCEEv9zGAKUUf/685Gi0Qqg8z8=
|
||||
github.com/containous/minheap v0.0.0-20190809180810-6e71eb837595 h1:aPspFRO6b94To3gl4yTDOEtpjFwXI7V2W+z0JcNljQ4=
|
||||
github.com/containous/minheap v0.0.0-20190809180810-6e71eb837595/go.mod h1:+lHFbEasIiQVGzhVDVw/cn0ZaOzde2OwNncp1NhXV4c=
|
||||
github.com/containous/mux v0.0.0-20220627093034-b2dd784e613f h1:1uEtynq2C0ljy3630jt7EAxg8jZY2gy6YHdGwdqEpWw=
|
||||
github.com/containous/mux v0.0.0-20220627093034-b2dd784e613f/go.mod h1:z8WW7n06n8/1xF9Jl9WmuDeZuHAhfL+bwarNjsciwwg=
|
||||
github.com/containous/mux v0.0.0-20250523120546-41b6ec3aed59 h1:lJUOWjGohYjLKEfAz2nyI/dpzfKNPQLi5GLH7aaOZkw=
|
||||
github.com/containous/mux v0.0.0-20250523120546-41b6ec3aed59/go.mod h1:z8WW7n06n8/1xF9Jl9WmuDeZuHAhfL+bwarNjsciwwg=
|
||||
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
|
@ -2,7 +2,6 @@ package integration
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
@ -43,7 +42,7 @@ func (s *ConsulSuite) SetupSuite() {
|
||||
s.consulURL = fmt.Sprintf("http://%s", consulAddr)
|
||||
|
||||
kv, err := valkeyrie.NewStore(
|
||||
context.Background(),
|
||||
s.T().Context(),
|
||||
consul.StoreName,
|
||||
[]string{consulAddr},
|
||||
&consul.Config{
|
||||
@ -63,7 +62,7 @@ func (s *ConsulSuite) TearDownSuite() {
|
||||
}
|
||||
|
||||
func (s *ConsulSuite) TearDownTest() {
|
||||
err := s.kvClient.DeleteTree(context.Background(), "traefik")
|
||||
err := s.kvClient.DeleteTree(s.T().Context(), "traefik")
|
||||
if err != nil && !errors.Is(err, store.ErrKeyNotFound) {
|
||||
require.ErrorIs(s.T(), err, store.ErrKeyNotFound)
|
||||
}
|
||||
@ -118,7 +117,7 @@ func (s *ConsulSuite) TestSimpleConfiguration() {
|
||||
}
|
||||
|
||||
for k, v := range data {
|
||||
err := s.kvClient.Put(context.Background(), k, []byte(v), nil)
|
||||
err := s.kvClient.Put(s.T().Context(), k, []byte(v), nil)
|
||||
require.NoError(s.T(), err)
|
||||
}
|
||||
|
||||
@ -178,7 +177,7 @@ func (s *ConsulSuite) TestDeleteRootKey() {
|
||||
|
||||
file := s.adaptFile("fixtures/consul/simple.toml", struct{ ConsulAddress string }{s.consulURL})
|
||||
|
||||
ctx := context.Background()
|
||||
ctx := s.T().Context()
|
||||
svcaddr := net.JoinHostPort(s.getComposeServiceIP("whoami"), "80")
|
||||
|
||||
data := map[string]string{
|
||||
|
@ -2,7 +2,6 @@ package integration
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net"
|
||||
"net/http"
|
||||
@ -41,7 +40,7 @@ func (s *EtcdSuite) SetupSuite() {
|
||||
var err error
|
||||
s.etcdAddr = net.JoinHostPort(s.getComposeServiceIP("etcd"), "2379")
|
||||
s.kvClient, err = valkeyrie.NewStore(
|
||||
context.Background(),
|
||||
s.T().Context(),
|
||||
etcdv3.StoreName,
|
||||
[]string{s.etcdAddr},
|
||||
&etcdv3.Config{
|
||||
@ -108,7 +107,7 @@ func (s *EtcdSuite) TestSimpleConfiguration() {
|
||||
}
|
||||
|
||||
for k, v := range data {
|
||||
err := s.kvClient.Put(context.Background(), k, []byte(v), nil)
|
||||
err := s.kvClient.Put(s.T().Context(), k, []byte(v), nil)
|
||||
require.NoError(s.T(), err)
|
||||
}
|
||||
|
||||
|
28
integration/fixtures/https/max_concurrent_stream.toml
Normal file
28
integration/fixtures/https/max_concurrent_stream.toml
Normal file
@ -0,0 +1,28 @@
|
||||
[global]
|
||||
checkNewVersion = false
|
||||
sendAnonymousUsage = false
|
||||
|
||||
[log]
|
||||
level = "DEBUG"
|
||||
|
||||
[serversTransport]
|
||||
insecureSkipVerify=true
|
||||
|
||||
[entryPoints]
|
||||
[entryPoints.web]
|
||||
address = ":8000"
|
||||
[entryPoints.web.http2]
|
||||
maxConcurrentStreams = 42
|
||||
|
||||
[api]
|
||||
insecure = true
|
||||
|
||||
[providers.file]
|
||||
filename = "{{ .SelfFilename }}"
|
||||
|
||||
## dynamic configuration ##
|
||||
|
||||
[tls.stores]
|
||||
[tls.stores.default.defaultCertificate]
|
||||
certFile = "resources/tls/local.cert"
|
||||
keyFile = "resources/tls/local.key"
|
@ -2291,7 +2291,7 @@ spec:
|
||||
type: object
|
||||
x-kubernetes-validations:
|
||||
- message: RootCA cannot have both Secret and ConfigMap defined.
|
||||
rule: has(self.secret) && has(self.configMap)
|
||||
rule: '!has(self.secret) || !has(self.configMap)'
|
||||
type: array
|
||||
rootCAsSecrets:
|
||||
description: |-
|
||||
@ -2436,7 +2436,7 @@ spec:
|
||||
type: object
|
||||
x-kubernetes-validations:
|
||||
- message: RootCA cannot have both Secret and ConfigMap defined.
|
||||
rule: has(self.secret) && has(self.configMap)
|
||||
rule: '!has(self.secret) || !has(self.configMap)'
|
||||
type: array
|
||||
rootCAsSecrets:
|
||||
description: |-
|
||||
|
@ -19,6 +19,9 @@
|
||||
[providers.file]
|
||||
filename = "{{ .SelfFilename }}"
|
||||
|
||||
[core]
|
||||
defaultRuleSyntax = "{{ .DefaultRuleSyntax }}"
|
||||
|
||||
# dynamic configuration
|
||||
[http.routers]
|
||||
[http.routers.without]
|
@ -108,7 +108,9 @@ func getHelloClientGRPCh2c() (helloworld.GreeterClient, func() error, error) {
|
||||
return helloworld.NewGreeterClient(conn), conn.Close, nil
|
||||
}
|
||||
|
||||
func callHelloClientGRPC(name string, secure bool) (string, error) {
|
||||
func callHelloClientGRPC(t *testing.T, name string, secure bool) (string, error) {
|
||||
t.Helper()
|
||||
|
||||
var client helloworld.GreeterClient
|
||||
var closer func() error
|
||||
var err error
|
||||
@ -123,24 +125,26 @@ func callHelloClientGRPC(name string, secure bool) (string, error) {
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
r, err := client.SayHello(context.Background(), &helloworld.HelloRequest{Name: name})
|
||||
r, err := client.SayHello(t.Context(), &helloworld.HelloRequest{Name: name})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return r.GetMessage(), nil
|
||||
}
|
||||
|
||||
func callStreamExampleClientGRPC() (helloworld.Greeter_StreamExampleClient, func() error, error) {
|
||||
func callStreamExampleClientGRPC(t *testing.T) (helloworld.Greeter_StreamExampleClient, func() error, error) {
|
||||
t.Helper()
|
||||
|
||||
client, closer, err := getHelloClientGRPC()
|
||||
if err != nil {
|
||||
return nil, closer, err
|
||||
}
|
||||
t, err := client.StreamExample(context.Background(), &helloworld.StreamExampleRequest{})
|
||||
s, err := client.StreamExample(t.Context(), &helloworld.StreamExampleRequest{})
|
||||
if err != nil {
|
||||
return nil, closer, err
|
||||
}
|
||||
|
||||
return t, closer, nil
|
||||
return s, closer, nil
|
||||
}
|
||||
|
||||
func (s *GRPCSuite) TestGRPC() {
|
||||
@ -172,7 +176,7 @@ func (s *GRPCSuite) TestGRPC() {
|
||||
|
||||
var response string
|
||||
err = try.Do(1*time.Second, func() error {
|
||||
response, err = callHelloClientGRPC("World", true)
|
||||
response, err = callHelloClientGRPC(s.T(), "World", true)
|
||||
return err
|
||||
})
|
||||
assert.NoError(s.T(), err)
|
||||
@ -204,7 +208,7 @@ func (s *GRPCSuite) TestGRPCh2c() {
|
||||
|
||||
var response string
|
||||
err = try.Do(1*time.Second, func() error {
|
||||
response, err = callHelloClientGRPC("World", false)
|
||||
response, err = callHelloClientGRPC(s.T(), "World", false)
|
||||
return err
|
||||
})
|
||||
assert.NoError(s.T(), err)
|
||||
@ -240,7 +244,7 @@ func (s *GRPCSuite) TestGRPCh2cTermination() {
|
||||
|
||||
var response string
|
||||
err = try.Do(1*time.Second, func() error {
|
||||
response, err = callHelloClientGRPC("World", true)
|
||||
response, err = callHelloClientGRPC(s.T(), "World", true)
|
||||
return err
|
||||
})
|
||||
assert.NoError(s.T(), err)
|
||||
@ -276,7 +280,7 @@ func (s *GRPCSuite) TestGRPCInsecure() {
|
||||
|
||||
var response string
|
||||
err = try.Do(1*time.Second, func() error {
|
||||
response, err = callHelloClientGRPC("World", true)
|
||||
response, err = callHelloClientGRPC(s.T(), "World", true)
|
||||
return err
|
||||
})
|
||||
assert.NoError(s.T(), err)
|
||||
@ -314,7 +318,7 @@ func (s *GRPCSuite) TestGRPCBuffer() {
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("Host(`127.0.0.1`)"))
|
||||
assert.NoError(s.T(), err)
|
||||
var client helloworld.Greeter_StreamExampleClient
|
||||
client, closer, err := callStreamExampleClientGRPC()
|
||||
client, closer, err := callStreamExampleClientGRPC(s.T())
|
||||
defer func() { _ = closer() }()
|
||||
assert.NoError(s.T(), err)
|
||||
|
||||
@ -367,7 +371,7 @@ func (s *GRPCSuite) TestGRPCBufferWithFlushInterval() {
|
||||
assert.NoError(s.T(), err)
|
||||
|
||||
var client helloworld.Greeter_StreamExampleClient
|
||||
client, closer, err := callStreamExampleClientGRPC()
|
||||
client, closer, err := callStreamExampleClientGRPC(s.T())
|
||||
defer func() {
|
||||
_ = closer()
|
||||
stopStreamExample <- true
|
||||
@ -422,7 +426,7 @@ func (s *GRPCSuite) TestGRPCWithRetry() {
|
||||
|
||||
var response string
|
||||
err = try.Do(1*time.Second, func() error {
|
||||
response, err = callHelloClientGRPC("World", true)
|
||||
response, err = callHelloClientGRPC(s.T(), "World", true)
|
||||
return err
|
||||
})
|
||||
assert.NoError(s.T(), err)
|
||||
|
@ -3,6 +3,7 @@ package integration
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
@ -19,6 +20,7 @@ import (
|
||||
"github.com/traefik/traefik/v3/pkg/config/dynamic"
|
||||
traefiktls "github.com/traefik/traefik/v3/pkg/tls"
|
||||
"github.com/traefik/traefik/v3/pkg/types"
|
||||
"golang.org/x/net/http2"
|
||||
)
|
||||
|
||||
// HTTPSSuite tests suite.
|
||||
@ -1174,3 +1176,38 @@ func (s *HTTPSSuite) TestWithInvalidTLSOption() {
|
||||
assert.Nil(s.T(), conn)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SimpleSuite) TestMaxConcurrentStream() {
|
||||
file := s.adaptFile("fixtures/https/max_concurrent_stream.toml", struct{}{})
|
||||
|
||||
s.traefikCmd(withConfigFile(file), "--log.level=DEBUG", "--accesslog")
|
||||
|
||||
// Wait for traefik.
|
||||
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", time.Second, try.BodyContains("api@internal"))
|
||||
require.NoError(s.T(), err)
|
||||
|
||||
// Add client self-signed cert.
|
||||
roots := x509.NewCertPool()
|
||||
certContent, err := os.ReadFile("./resources/tls/local.cert")
|
||||
require.NoError(s.T(), err)
|
||||
|
||||
roots.AppendCertsFromPEM(certContent)
|
||||
|
||||
// Open a connection to inspect SettingsFrame.
|
||||
conn, err := tls.Dial("tcp", "127.0.0.1:8000", &tls.Config{
|
||||
RootCAs: roots,
|
||||
NextProtos: []string{"h2"},
|
||||
})
|
||||
require.NoError(s.T(), err)
|
||||
|
||||
framer := http2.NewFramer(nil, conn)
|
||||
frame, err := framer.ReadFrame()
|
||||
require.NoError(s.T(), err)
|
||||
|
||||
fr, ok := frame.(*http2.SettingsFrame)
|
||||
require.True(s.T(), ok)
|
||||
|
||||
maxConcurrentStream, ok := fr.Value(http2.SettingMaxConcurrentStreams)
|
||||
assert.True(s.T(), ok)
|
||||
assert.Equal(s.T(), uint32(42), maxConcurrentStream)
|
||||
}
|
||||
|
@ -106,7 +106,7 @@ func (s *BaseSuite) displayTraefikLogFile(path string) {
|
||||
}
|
||||
|
||||
func (s *BaseSuite) SetupSuite() {
|
||||
if isDockerDesktop(context.Background(), s.T()) {
|
||||
if isDockerDesktop(s.T()) {
|
||||
_, err := os.Stat(tailscaleSecretFilePath)
|
||||
require.NoError(s.T(), err, "Tailscale need to be configured when running integration tests with Docker Desktop: (https://doc.traefik.io/traefik/v2.11/contributing/building-testing/#testing)")
|
||||
}
|
||||
@ -116,7 +116,6 @@ func (s *BaseSuite) SetupSuite() {
|
||||
// TODO
|
||||
// stdlog.SetOutput(log.Logger)
|
||||
|
||||
ctx := context.Background()
|
||||
// Create docker network
|
||||
// docker network create traefik-test-network --driver bridge --subnet 172.31.42.0/24
|
||||
var opts []network.NetworkCustomizer
|
||||
@ -129,18 +128,18 @@ func (s *BaseSuite) SetupSuite() {
|
||||
},
|
||||
},
|
||||
}))
|
||||
dockerNetwork, err := network.New(ctx, opts...)
|
||||
dockerNetwork, err := network.New(s.T().Context(), opts...)
|
||||
require.NoError(s.T(), err)
|
||||
|
||||
s.network = dockerNetwork
|
||||
s.hostIP = "172.31.42.1"
|
||||
if isDockerDesktop(ctx, s.T()) {
|
||||
s.hostIP = getDockerDesktopHostIP(ctx, s.T())
|
||||
if isDockerDesktop(s.T()) {
|
||||
s.hostIP = getDockerDesktopHostIP(s.T())
|
||||
s.setupVPN(tailscaleSecretFilePath)
|
||||
}
|
||||
}
|
||||
|
||||
func getDockerDesktopHostIP(ctx context.Context, t *testing.T) string {
|
||||
func getDockerDesktopHostIP(t *testing.T) string {
|
||||
t.Helper()
|
||||
|
||||
req := testcontainers.ContainerRequest{
|
||||
@ -151,13 +150,13 @@ func getDockerDesktopHostIP(ctx context.Context, t *testing.T) string {
|
||||
Cmd: []string{"getent", "hosts", "host.docker.internal"},
|
||||
}
|
||||
|
||||
con, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
|
||||
con, err := testcontainers.GenericContainer(t.Context(), testcontainers.GenericContainerRequest{
|
||||
ContainerRequest: req,
|
||||
Started: true,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
closer, err := con.Logs(ctx)
|
||||
closer, err := con.Logs(t.Context())
|
||||
require.NoError(t, err)
|
||||
|
||||
all, err := io.ReadAll(closer)
|
||||
@ -170,15 +169,15 @@ func getDockerDesktopHostIP(ctx context.Context, t *testing.T) string {
|
||||
return matches[0]
|
||||
}
|
||||
|
||||
func isDockerDesktop(ctx context.Context, t *testing.T) bool {
|
||||
func isDockerDesktop(t *testing.T) bool {
|
||||
t.Helper()
|
||||
|
||||
cli, err := testcontainers.NewDockerClientWithOpts(ctx)
|
||||
cli, err := testcontainers.NewDockerClientWithOpts(t.Context())
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create docker client: %s", err)
|
||||
}
|
||||
|
||||
info, err := cli.Info(ctx)
|
||||
info, err := cli.Info(t.Context())
|
||||
if err != nil {
|
||||
t.Fatalf("failed to get docker info: %s", err)
|
||||
}
|
||||
@ -191,7 +190,7 @@ func (s *BaseSuite) TearDownSuite() {
|
||||
|
||||
err := try.Do(5*time.Second, func() error {
|
||||
if s.network != nil {
|
||||
err := s.network.Remove(context.Background())
|
||||
err := s.network.Remove(s.T().Context())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -218,7 +217,7 @@ func (s *BaseSuite) createComposeProject(name string) {
|
||||
s.containers = map[string]testcontainers.Container{}
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
ctx := s.T().Context()
|
||||
|
||||
for id, containerConfig := range composeConfigData.Services {
|
||||
var mounts []mount.Mount
|
||||
@ -273,7 +272,7 @@ func (s *BaseSuite) createContainer(ctx context.Context, containerConfig compose
|
||||
if containerConfig.CapAdd != nil {
|
||||
config.CapAdd = containerConfig.CapAdd
|
||||
}
|
||||
if !isDockerDesktop(ctx, s.T()) {
|
||||
if !isDockerDesktop(s.T()) {
|
||||
config.ExtraHosts = append(config.ExtraHosts, "host.docker.internal:"+s.hostIP)
|
||||
}
|
||||
config.Mounts = mounts
|
||||
@ -292,7 +291,7 @@ func (s *BaseSuite) createContainer(ctx context.Context, containerConfig compose
|
||||
func (s *BaseSuite) composeUp(services ...string) {
|
||||
for name, con := range s.containers {
|
||||
if len(services) == 0 || slices.Contains(services, name) {
|
||||
err := con.Start(context.Background())
|
||||
err := con.Start(s.T().Context())
|
||||
require.NoError(s.T(), err)
|
||||
}
|
||||
}
|
||||
@ -303,7 +302,7 @@ func (s *BaseSuite) composeStop(services ...string) {
|
||||
for name, con := range s.containers {
|
||||
if len(services) == 0 || slices.Contains(services, name) {
|
||||
timeout := 10 * time.Second
|
||||
err := con.Stop(context.Background(), &timeout)
|
||||
err := con.Stop(s.T().Context(), &timeout)
|
||||
require.NoError(s.T(), err)
|
||||
}
|
||||
}
|
||||
@ -312,7 +311,7 @@ func (s *BaseSuite) composeStop(services ...string) {
|
||||
// composeDown stops all compose project services and removes the corresponding containers.
|
||||
func (s *BaseSuite) composeDown() {
|
||||
for _, c := range s.containers {
|
||||
err := c.Terminate(context.Background())
|
||||
err := c.Terminate(s.T().Context())
|
||||
require.NoError(s.T(), err)
|
||||
}
|
||||
s.containers = map[string]testcontainers.Container{}
|
||||
@ -383,7 +382,7 @@ func (s *BaseSuite) displayLogK3S() {
|
||||
|
||||
func (s *BaseSuite) displayLogCompose() {
|
||||
for name, ctn := range s.containers {
|
||||
readCloser, err := ctn.Logs(context.Background())
|
||||
readCloser, err := ctn.Logs(s.T().Context())
|
||||
require.NoError(s.T(), err)
|
||||
for {
|
||||
b := make([]byte, 1024)
|
||||
@ -451,7 +450,7 @@ func (s *BaseSuite) getComposeServiceIP(name string) string {
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
ip, err := container.ContainerIP(context.Background())
|
||||
ip, err := container.ContainerIP(s.T().Context())
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
@ -501,7 +500,7 @@ func (s *BaseSuite) setupVPN(keyFile string) {
|
||||
func (s *BaseSuite) composeExec(service string, args ...string) string {
|
||||
require.Contains(s.T(), s.containers, service)
|
||||
|
||||
_, reader, err := s.containers[service].Exec(context.Background(), args)
|
||||
_, reader, err := s.containers[service].Exec(s.T().Context(), args)
|
||||
require.NoError(s.T(), err)
|
||||
|
||||
content, err := io.ReadAll(reader)
|
||||
|
@ -1,7 +1,6 @@
|
||||
package integration
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
@ -74,7 +73,7 @@ func (s *K8sConformanceSuite) SetupSuite() {
|
||||
s.T().Fatal(err)
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
ctx := s.T().Context()
|
||||
|
||||
// Ensure image is available locally.
|
||||
images, err := provider.ListImages(ctx)
|
||||
@ -146,7 +145,7 @@ func (s *K8sConformanceSuite) SetupSuite() {
|
||||
}
|
||||
|
||||
func (s *K8sConformanceSuite) TearDownSuite() {
|
||||
ctx := context.Background()
|
||||
ctx := s.T().Context()
|
||||
|
||||
if s.T().Failed() || *showLog {
|
||||
k3sLogs, err := s.k3sContainer.Logs(ctx)
|
||||
@ -173,7 +172,7 @@ func (s *K8sConformanceSuite) TearDownSuite() {
|
||||
|
||||
func (s *K8sConformanceSuite) TestK8sGatewayAPIConformance() {
|
||||
// Wait for traefik to start
|
||||
k3sContainerIP, err := s.k3sContainer.ContainerIP(context.Background())
|
||||
k3sContainerIP, err := s.k3sContainer.ContainerIP(s.T().Context())
|
||||
require.NoError(s.T(), err)
|
||||
|
||||
err = try.GetRequest("http://"+k3sContainerIP+":9000/api/entrypoints", 10*time.Second, try.BodyContains(`"name":"web"`))
|
||||
|
@ -2,7 +2,6 @@ package integration
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
@ -51,7 +50,7 @@ func (s *RedisSentinelSuite) SetupSuite() {
|
||||
net.JoinHostPort(s.getComposeServiceIP("sentinel3"), "26379"),
|
||||
}
|
||||
kv, err := valkeyrie.NewStore(
|
||||
context.Background(),
|
||||
s.T().Context(),
|
||||
redis.StoreName,
|
||||
s.redisEndpoints,
|
||||
&redis.Config{
|
||||
@ -157,7 +156,7 @@ func (s *RedisSentinelSuite) TestSentinelConfiguration() {
|
||||
}
|
||||
|
||||
for k, v := range data {
|
||||
err := s.kvClient.Put(context.Background(), k, []byte(v), nil)
|
||||
err := s.kvClient.Put(s.T().Context(), k, []byte(v), nil)
|
||||
require.NoError(s.T(), err)
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,6 @@ package integration
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net"
|
||||
"net/http"
|
||||
@ -43,7 +42,7 @@ func (s *RedisSuite) SetupSuite() {
|
||||
s.redisEndpoints = append(s.redisEndpoints, net.JoinHostPort(s.getComposeServiceIP("redis"), "6379"))
|
||||
|
||||
kv, err := valkeyrie.NewStore(
|
||||
context.Background(),
|
||||
s.T().Context(),
|
||||
redis.StoreName,
|
||||
s.redisEndpoints,
|
||||
&redis.Config{},
|
||||
@ -112,7 +111,7 @@ func (s *RedisSuite) TestSimpleConfiguration() {
|
||||
}
|
||||
|
||||
for k, v := range data {
|
||||
err := s.kvClient.Put(context.Background(), k, []byte(v), nil)
|
||||
err := s.kvClient.Put(s.T().Context(), k, []byte(v), nil)
|
||||
require.NoError(s.T(), err)
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
server0:
|
||||
image: traefik/whoami
|
||||
|
@ -1,4 +1,3 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
noOverrideAllowlist:
|
||||
image: traefik/whoami
|
||||
|
@ -1,4 +1,3 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
whoami1:
|
||||
image: traefik/whoami
|
||||
|
@ -1,4 +1,3 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
consul:
|
||||
image: consul:1.6
|
||||
|
@ -1,4 +1,3 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
consul:
|
||||
image: consul:1.6.2
|
||||
|
@ -1,4 +1,3 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
simple:
|
||||
image: swarm:1.0.0
|
||||
|
@ -1,4 +1,3 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
nginx1:
|
||||
image: nginx:1.25.3-alpine3.18
|
||||
|
@ -1,4 +1,3 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
etcd:
|
||||
image: quay.io/coreos/etcd:v3.5.14
|
||||
|
@ -1,4 +1,3 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
whoami1:
|
||||
image: traefik/whoami
|
||||
|
@ -1,4 +1,3 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
whoami1:
|
||||
image: traefik/whoami
|
||||
|
@ -1,4 +1,3 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
server1:
|
||||
image: traefik/whoami
|
||||
|
@ -1,4 +1,3 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
server:
|
||||
image: rancher/k3s:v1.21.14-k3s1
|
||||
|
@ -1,4 +1,3 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
whoami1:
|
||||
image: traefik/whoami
|
||||
|
@ -1,4 +1,3 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
pebble:
|
||||
image: letsencrypt/pebble:v2.3.1
|
||||
|
@ -1,4 +1,3 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
whoami:
|
||||
image: traefik/whoami
|
||||
|
@ -1,4 +1,3 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
whoami1:
|
||||
image: traefik/whoami
|
||||
|
@ -1,4 +1,3 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
redis:
|
||||
image: redis:5.0
|
||||
|
@ -1,4 +1,3 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
master:
|
||||
image: redis
|
||||
|
@ -1,4 +1,3 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
whoami:
|
||||
image: traefik/whoami
|
||||
|
@ -1,4 +1,3 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
whoami1:
|
||||
image: traefik/whoami
|
||||
|
@ -1,4 +1,3 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
whoami:
|
||||
image: traefik/whoami
|
||||
|
@ -1,4 +1,3 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
whoami1:
|
||||
image: traefik/whoami
|
||||
|
@ -1,4 +1,3 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
tailscaled:
|
||||
hostname: traefik-tests-gw # This will become the tailscale device name
|
||||
|
@ -1,4 +1,3 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
whoami-a:
|
||||
image: traefik/whoamitcp
|
||||
|
@ -1,4 +1,3 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
timeoutEndpoint:
|
||||
image: yaman/timeout
|
||||
|
@ -1,4 +1,3 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
whoami:
|
||||
image: traefik/whoami
|
||||
|
@ -1,4 +1,3 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
tempo:
|
||||
hostname: tempo
|
||||
|
@ -1,4 +1,3 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
whoami-a:
|
||||
image: traefik/whoamiudp:latest
|
||||
|
@ -1,4 +1,3 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
noOverrideWhitelist:
|
||||
image: traefik/whoami
|
||||
|
@ -1,4 +1,3 @@
|
||||
version: "3.8"
|
||||
services:
|
||||
zookeeper:
|
||||
image: zookeeper:3.5
|
||||
|
@ -711,11 +711,11 @@ func (s *SimpleSuite) TestWithDefaultRuleSyntax() {
|
||||
require.NoError(s.T(), err)
|
||||
|
||||
// router2 has an error because it uses the wrong rule syntax (v3 instead of v2)
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/http/routers/router2@file", 1*time.Second, try.BodyContains("error while parsing rule QueryRegexp(`foo`, `bar`): unsupported function: QueryRegexp"))
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/http/routers/router2@file", 1*time.Second, try.BodyContains("parsing rule QueryRegexp(`foo`, `bar`): unsupported function: QueryRegexp"))
|
||||
require.NoError(s.T(), err)
|
||||
|
||||
// router3 has an error because it uses the wrong rule syntax (v2 instead of v3)
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/http/routers/router3@file", 1*time.Second, try.BodyContains("error while adding rule PathPrefix: unexpected number of parameters; got 2, expected one of [1]"))
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/http/routers/router3@file", 1*time.Second, try.BodyContains("adding rule PathPrefix: unexpected number of parameters; got 2, expected one of [1]"))
|
||||
require.NoError(s.T(), err)
|
||||
}
|
||||
|
||||
@ -741,11 +741,11 @@ func (s *SimpleSuite) TestWithoutDefaultRuleSyntax() {
|
||||
require.NoError(s.T(), err)
|
||||
|
||||
// router2 has an error because it uses the wrong rule syntax (v3 instead of v2)
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/http/routers/router2@file", 1*time.Second, try.BodyContains("error while adding rule PathPrefix: unexpected number of parameters; got 2, expected one of [1]"))
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/http/routers/router2@file", 1*time.Second, try.BodyContains("adding rule PathPrefix: unexpected number of parameters; got 2, expected one of [1]"))
|
||||
require.NoError(s.T(), err)
|
||||
|
||||
// router2 has an error because it uses the wrong rule syntax (v2 instead of v3)
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/http/routers/router3@file", 1*time.Second, try.BodyContains("error while parsing rule QueryRegexp(`foo`, `bar`): unsupported function: QueryRegexp"))
|
||||
err = try.GetRequest("http://127.0.0.1:8080/api/http/routers/router3@file", 1*time.Second, try.BodyContains("parsing rule QueryRegexp(`foo`, `bar`): unsupported function: QueryRegexp"))
|
||||
require.NoError(s.T(), err)
|
||||
}
|
||||
|
||||
@ -1606,9 +1606,10 @@ func (s *SimpleSuite) TestSanitizePath() {
|
||||
|
||||
whoami1URL := "http://" + net.JoinHostPort(s.getComposeServiceIP("whoami1"), "80")
|
||||
|
||||
file := s.adaptFile("fixtures/simple_clean_path.toml", struct {
|
||||
file := s.adaptFile("fixtures/simple_sanitize_path.toml", struct {
|
||||
Server1 string
|
||||
}{whoami1URL})
|
||||
DefaultRuleSyntax string
|
||||
}{whoami1URL, "v3"})
|
||||
|
||||
s.traefikCmd(withConfigFile(file))
|
||||
|
||||
@ -1641,6 +1642,116 @@ func (s *SimpleSuite) TestSanitizePath() {
|
||||
target: "127.0.0.1:8000",
|
||||
expected: http.StatusFound,
|
||||
},
|
||||
{
|
||||
desc: "Implicit encoded dot dots call to the route with a middleware",
|
||||
request: "GET /without/%2E%2E/with HTTP/1.1\r\nHost: other.localhost\r\n\r\n",
|
||||
target: "127.0.0.1:8000",
|
||||
expected: http.StatusFound,
|
||||
},
|
||||
{
|
||||
desc: "Implicit with encoded unreserved character call to the route with a middleware",
|
||||
request: "GET /%77ith HTTP/1.1\r\nHost: other.localhost\r\n\r\n",
|
||||
target: "127.0.0.1:8000",
|
||||
expected: http.StatusFound,
|
||||
},
|
||||
{
|
||||
desc: "Explicit call to the route with a middleware, and disable path sanitization",
|
||||
request: "GET /with HTTP/1.1\r\nHost: other.localhost\r\n\r\n",
|
||||
target: "127.0.0.1:8001",
|
||||
expected: http.StatusFound,
|
||||
},
|
||||
{
|
||||
desc: "Explicit call to the route without a middleware, and disable path sanitization",
|
||||
request: "GET /without HTTP/1.1\r\nHost: other.localhost\r\n\r\n",
|
||||
target: "127.0.0.1:8001",
|
||||
expected: http.StatusOK,
|
||||
body: "GET /without HTTP/1.1",
|
||||
},
|
||||
{
|
||||
desc: "Implicit call to the route with a middleware, and disable path sanitization",
|
||||
request: "GET /without/../with HTTP/1.1\r\nHost: other.localhost\r\n\r\n",
|
||||
target: "127.0.0.1:8001",
|
||||
// The whoami is redirecting to /with, but the path is not sanitized.
|
||||
expected: http.StatusMovedPermanently,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
conn, err := net.Dial("tcp", test.target)
|
||||
require.NoError(s.T(), err)
|
||||
|
||||
_, err = conn.Write([]byte(test.request))
|
||||
require.NoError(s.T(), err)
|
||||
|
||||
resp, err := http.ReadResponse(bufio.NewReader(conn), nil)
|
||||
require.NoError(s.T(), err)
|
||||
|
||||
assert.Equalf(s.T(), test.expected, resp.StatusCode, "%s failed with %d instead of %d", test.desc, resp.StatusCode, test.expected)
|
||||
|
||||
if test.body != "" {
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
require.NoError(s.T(), err)
|
||||
assert.Contains(s.T(), string(body), test.body)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SimpleSuite) TestSanitizePathSyntaxV2() {
|
||||
s.createComposeProject("base")
|
||||
|
||||
s.composeUp()
|
||||
defer s.composeDown()
|
||||
|
||||
whoami1URL := "http://" + net.JoinHostPort(s.getComposeServiceIP("whoami1"), "80")
|
||||
|
||||
file := s.adaptFile("fixtures/simple_sanitize_path.toml", struct {
|
||||
Server1 string
|
||||
DefaultRuleSyntax string
|
||||
}{whoami1URL, "v2"})
|
||||
|
||||
s.traefikCmd(withConfigFile(file))
|
||||
|
||||
err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("PathPrefix(`/with`)"))
|
||||
require.NoError(s.T(), err)
|
||||
|
||||
testCases := []struct {
|
||||
desc string
|
||||
request string
|
||||
target string
|
||||
body string
|
||||
expected int
|
||||
}{
|
||||
{
|
||||
desc: "Explicit call to the route with a middleware",
|
||||
request: "GET /with HTTP/1.1\r\nHost: other.localhost\r\n\r\n",
|
||||
target: "127.0.0.1:8000",
|
||||
expected: http.StatusFound,
|
||||
},
|
||||
{
|
||||
desc: "Explicit call to the route without a middleware",
|
||||
request: "GET /without HTTP/1.1\r\nHost: other.localhost\r\n\r\n",
|
||||
target: "127.0.0.1:8000",
|
||||
expected: http.StatusOK,
|
||||
body: "GET /without HTTP/1.1",
|
||||
},
|
||||
{
|
||||
desc: "Implicit call to the route with a middleware",
|
||||
request: "GET /without/../with HTTP/1.1\r\nHost: other.localhost\r\n\r\n",
|
||||
target: "127.0.0.1:8000",
|
||||
expected: http.StatusFound,
|
||||
},
|
||||
{
|
||||
desc: "Implicit encoded dot dots call to the route with a middleware",
|
||||
request: "GET /without/%2E%2E/with HTTP/1.1\r\nHost: other.localhost\r\n\r\n",
|
||||
target: "127.0.0.1:8000",
|
||||
expected: http.StatusFound,
|
||||
},
|
||||
{
|
||||
desc: "Implicit with encoded unreserved character call to the route with a middleware",
|
||||
request: "GET /%77ith HTTP/1.1\r\nHost: other.localhost\r\n\r\n",
|
||||
target: "127.0.0.1:8000",
|
||||
expected: http.StatusFound,
|
||||
},
|
||||
{
|
||||
desc: "Explicit call to the route with a middleware, and disable path sanitization",
|
||||
request: "GET /with HTTP/1.1\r\nHost: other.localhost\r\n\r\n",
|
||||
|
@ -2,7 +2,6 @@ package integration
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net"
|
||||
"net/http"
|
||||
@ -43,7 +42,7 @@ func (s *ZookeeperSuite) SetupSuite() {
|
||||
|
||||
var err error
|
||||
s.kvClient, err = valkeyrie.NewStore(
|
||||
context.Background(),
|
||||
s.T().Context(),
|
||||
zookeeper.StoreName,
|
||||
[]string{s.zookeeperAddr},
|
||||
&zookeeper.Config{
|
||||
@ -110,7 +109,7 @@ func (s *ZookeeperSuite) TestSimpleConfiguration() {
|
||||
}
|
||||
|
||||
for k, v := range data {
|
||||
err := s.kvClient.Put(context.Background(), k, []byte(v), nil)
|
||||
err := s.kvClient.Put(s.T().Context(), k, []byte(v), nil)
|
||||
require.NoError(s.T(), err)
|
||||
}
|
||||
|
||||
|
64
internal/testsci/genmatrix.go
Normal file
64
internal/testsci/genmatrix.go
Normal file
@ -0,0 +1,64 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
"golang.org/x/tools/go/packages"
|
||||
)
|
||||
|
||||
const groupCount = 12
|
||||
|
||||
type group struct {
|
||||
Group string `json:"group"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
cfg := &packages.Config{
|
||||
Mode: packages.NeedName,
|
||||
Dir: ".",
|
||||
}
|
||||
|
||||
pkgs, err := packages.Load(cfg, "./cmd/...", "./pkg/...")
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Loading packages")
|
||||
}
|
||||
|
||||
var packageNames []string
|
||||
for _, pkg := range pkgs {
|
||||
if pkg.PkgPath != "" {
|
||||
packageNames = append(packageNames, pkg.PkgPath)
|
||||
}
|
||||
}
|
||||
|
||||
total := len(packageNames)
|
||||
perGroup := (total + groupCount - 1) / groupCount
|
||||
|
||||
fmt.Fprintf(os.Stderr, "Total packages: %d\n", total)
|
||||
fmt.Fprintf(os.Stderr, "Packages per group: %d\n", perGroup)
|
||||
|
||||
var matrix []group
|
||||
for i := range groupCount {
|
||||
start := i * perGroup
|
||||
end := start + perGroup
|
||||
if start >= total {
|
||||
break
|
||||
}
|
||||
if end > total {
|
||||
end = total
|
||||
}
|
||||
g := strings.Join(packageNames[start:end], " ")
|
||||
matrix = append(matrix, group{Group: g})
|
||||
}
|
||||
|
||||
jsonBytes, err := json.Marshal(matrix)
|
||||
if err != nil {
|
||||
log.Fatal().Err(err).Msg("Failed to marshal matrix")
|
||||
}
|
||||
|
||||
// Output for GitHub Actions
|
||||
fmt.Printf("matrix=%s\n", string(jsonBytes))
|
||||
}
|
@ -1,7 +1,6 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
@ -1004,8 +1003,8 @@ func TestHandler_HTTP(t *testing.T) {
|
||||
rtConf := &test.conf
|
||||
// To lazily initialize the Statuses.
|
||||
rtConf.PopulateUsedBy()
|
||||
rtConf.GetRoutersByEntryPoints(context.Background(), []string{"web"}, false)
|
||||
rtConf.GetRoutersByEntryPoints(context.Background(), []string{"web"}, true)
|
||||
rtConf.GetRoutersByEntryPoints(t.Context(), []string{"web"}, false)
|
||||
rtConf.GetRoutersByEntryPoints(t.Context(), []string{"web"}, true)
|
||||
|
||||
handler := New(static.Configuration{API: &static.API{}, Global: &static.Global{}}, rtConf)
|
||||
server := httptest.NewServer(handler.createRouter())
|
||||
|
@ -1,7 +1,6 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
@ -880,7 +879,7 @@ func TestHandler_TCP(t *testing.T) {
|
||||
rtConf := &test.conf
|
||||
// To lazily initialize the Statuses.
|
||||
rtConf.PopulateUsedBy()
|
||||
rtConf.GetTCPRoutersByEntryPoints(context.Background(), []string{"web"})
|
||||
rtConf.GetTCPRoutersByEntryPoints(t.Context(), []string{"web"})
|
||||
|
||||
handler := New(static.Configuration{API: &static.API{}, Global: &static.Global{}}, rtConf)
|
||||
server := httptest.NewServer(handler.createRouter())
|
||||
|
@ -1,7 +1,6 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
@ -570,7 +569,7 @@ func TestHandler_UDP(t *testing.T) {
|
||||
rtConf := &test.conf
|
||||
// To lazily initialize the Statuses.
|
||||
rtConf.PopulateUsedBy()
|
||||
rtConf.GetUDPRoutersByEntryPoints(context.Background(), []string{"web"})
|
||||
rtConf.GetUDPRoutersByEntryPoints(t.Context(), []string{"web"})
|
||||
|
||||
handler := New(static.Configuration{API: &static.API{}, Global: &static.Global{}}, rtConf)
|
||||
server := httptest.NewServer(handler.createRouter())
|
||||
|
@ -1,7 +1,6 @@
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
@ -211,7 +210,7 @@ func TestGetRoutersByEntryPoints(t *testing.T) {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
runtimeConfig := NewConfig(test.conf)
|
||||
actual := runtimeConfig.GetRoutersByEntryPoints(context.Background(), test.entryPoints, false)
|
||||
actual := runtimeConfig.GetRoutersByEntryPoints(t.Context(), test.entryPoints, false)
|
||||
assert.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
@ -211,7 +210,7 @@ func TestGetTCPRoutersByEntryPoints(t *testing.T) {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
runtimeConfig := NewConfig(test.conf)
|
||||
actual := runtimeConfig.GetTCPRoutersByEntryPoints(context.Background(), test.entryPoints)
|
||||
actual := runtimeConfig.GetTCPRoutersByEntryPoints(t.Context(), test.entryPoints)
|
||||
assert.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
@ -192,7 +191,7 @@ func TestGetUDPRoutersByEntryPoints(t *testing.T) {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
runtimeConfig := NewConfig(test.conf)
|
||||
actual := runtimeConfig.GetUDPRoutersByEntryPoints(context.Background(), test.entryPoints)
|
||||
actual := runtimeConfig.GetUDPRoutersByEntryPoints(t.Context(), test.entryPoints)
|
||||
assert.Equal(t, test.expected, actual)
|
||||
})
|
||||
}
|
||||
|
@ -66,7 +66,7 @@ func TestNewServiceHealthChecker_durations(t *testing.T) {
|
||||
|
||||
for _, test := range testCases {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
healthChecker := NewServiceHealthChecker(context.Background(), nil, test.config, nil, nil, http.DefaultTransport, nil, "")
|
||||
healthChecker := NewServiceHealthChecker(t.Context(), nil, test.config, nil, nil, http.DefaultTransport, nil, "")
|
||||
assert.Equal(t, test.expInterval, healthChecker.interval)
|
||||
assert.Equal(t, test.expTimeout, healthChecker.timeout)
|
||||
})
|
||||
@ -251,7 +251,7 @@ func TestServiceHealthChecker_newRequest(t *testing.T) {
|
||||
shc := ServiceHealthChecker{config: &test.config}
|
||||
|
||||
u := testhelpers.MustParseURL(test.targetURL)
|
||||
req, err := shc.newRequest(context.Background(), u)
|
||||
req, err := shc.newRequest(t.Context(), u)
|
||||
|
||||
if test.expError {
|
||||
require.Error(t, err)
|
||||
@ -276,7 +276,7 @@ func TestServiceHealthChecker_checkHealthHTTP_NotFollowingRedirects(t *testing.T
|
||||
}))
|
||||
defer redirectTestServer.Close()
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(dynamic.DefaultHealthCheckTimeout))
|
||||
ctx, cancel := context.WithTimeout(t.Context(), time.Duration(dynamic.DefaultHealthCheckTimeout))
|
||||
defer cancel()
|
||||
|
||||
server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||
@ -411,7 +411,7 @@ func TestServiceHealthChecker_Launch(t *testing.T) {
|
||||
|
||||
// The context is passed to the health check and
|
||||
// canonically canceled by the test server once all expected requests have been received.
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(t.Context())
|
||||
t.Cleanup(cancel)
|
||||
|
||||
targetURL, timeout := test.server.Start(t, cancel)
|
||||
@ -461,7 +461,7 @@ func TestServiceHealthChecker_Launch(t *testing.T) {
|
||||
func TestDifferentIntervals(t *testing.T) {
|
||||
// The context is passed to the health check and
|
||||
// canonically canceled by the test server once all expected requests have been received.
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
ctx, cancel := context.WithCancel(t.Context())
|
||||
t.Cleanup(cancel)
|
||||
|
||||
healthyServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
|
@ -2,7 +2,6 @@ package logs
|
||||
|
||||
import (
|
||||
"compress/gzip"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
@ -175,7 +174,7 @@ func TestLog(t *testing.T) {
|
||||
logger, err := SetupOTelLogger(logger, config)
|
||||
require.NoError(t, err)
|
||||
|
||||
ctx := trace.ContextWithSpanContext(context.Background(), trace.NewSpanContext(trace.SpanContextConfig{
|
||||
ctx := trace.ContextWithSpanContext(t.Context(), trace.NewSpanContext(trace.SpanContextConfig{
|
||||
TraceID: trace.TraceID{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8},
|
||||
SpanID: trace.SpanID{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8},
|
||||
}))
|
||||
|
@ -1,7 +1,6 @@
|
||||
package metrics
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"testing"
|
||||
@ -20,7 +19,7 @@ func TestDatadog(t *testing.T) {
|
||||
// This is needed to make sure that UDP Listener listens for data a bit longer, otherwise it will quit after a millisecond
|
||||
udp.Timeout = 5 * time.Second
|
||||
|
||||
datadogRegistry := RegisterDatadog(context.Background(), &types.Datadog{Address: ":18125", PushInterval: ptypes.Duration(time.Second), AddEntryPointsLabels: true, AddRoutersLabels: true, AddServicesLabels: true})
|
||||
datadogRegistry := RegisterDatadog(t.Context(), &types.Datadog{Address: ":18125", PushInterval: ptypes.Duration(time.Second), AddEntryPointsLabels: true, AddRoutersLabels: true, AddServicesLabels: true})
|
||||
|
||||
if !datadogRegistry.IsEpEnabled() || !datadogRegistry.IsRouterEnabled() || !datadogRegistry.IsSvcEnabled() {
|
||||
t.Errorf("DatadogRegistry should return true for IsEnabled(), IsRouterEnabled() and IsSvcEnabled()")
|
||||
@ -35,7 +34,7 @@ func TestDatadogWithPrefix(t *testing.T) {
|
||||
// This is needed to make sure that UDP Listener listens for data a bit longer, otherwise it will quit after a millisecond
|
||||
udp.Timeout = 5 * time.Second
|
||||
|
||||
datadogRegistry := RegisterDatadog(context.Background(), &types.Datadog{Prefix: "testPrefix", Address: ":18125", PushInterval: ptypes.Duration(time.Second), AddEntryPointsLabels: true, AddRoutersLabels: true, AddServicesLabels: true})
|
||||
datadogRegistry := RegisterDatadog(t.Context(), &types.Datadog{Prefix: "testPrefix", Address: ":18125", PushInterval: ptypes.Duration(time.Second), AddEntryPointsLabels: true, AddRoutersLabels: true, AddServicesLabels: true})
|
||||
|
||||
testDatadogRegistry(t, "testPrefix", datadogRegistry)
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
package metrics
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
@ -26,7 +25,7 @@ func TestInfluxDB2(t *testing.T) {
|
||||
_, _ = fmt.Fprintln(w, "ok")
|
||||
}))
|
||||
|
||||
influxDB2Registry := RegisterInfluxDB2(context.Background(),
|
||||
influxDB2Registry := RegisterInfluxDB2(t.Context(),
|
||||
&types.InfluxDB2{
|
||||
Address: ts.URL,
|
||||
Token: "test-token",
|
||||
|
@ -2,7 +2,6 @@ package metrics
|
||||
|
||||
import (
|
||||
"compress/gzip"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
@ -338,7 +337,7 @@ func TestOpenTelemetry(t *testing.T) {
|
||||
wantServiceName = test.serviceName
|
||||
}
|
||||
|
||||
registry := RegisterOpenTelemetry(context.Background(), &cfg)
|
||||
registry := RegisterOpenTelemetry(t.Context(), &cfg)
|
||||
require.NotNil(t, registry)
|
||||
|
||||
if !registry.IsEpEnabled() || !registry.IsRouterEnabled() || !registry.IsSvcEnabled() {
|
||||
|
@ -1,7 +1,6 @@
|
||||
package metrics
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
@ -70,7 +69,7 @@ func TestRegisterPromState(t *testing.T) {
|
||||
if test.initPromState {
|
||||
initStandardRegistry(prom)
|
||||
}
|
||||
if registerPromState(context.Background()) {
|
||||
if registerPromState(t.Context()) {
|
||||
actualNbRegistries++
|
||||
}
|
||||
if test.unregisterPromState {
|
||||
@ -91,7 +90,7 @@ func TestPrometheus(t *testing.T) {
|
||||
promRegistry = prometheus.NewRegistry()
|
||||
t.Cleanup(promState.reset)
|
||||
|
||||
prometheusRegistry := RegisterPrometheus(context.Background(), &types.Prometheus{
|
||||
prometheusRegistry := RegisterPrometheus(t.Context(), &types.Prometheus{
|
||||
AddEntryPointsLabels: true,
|
||||
AddRoutersLabels: true,
|
||||
AddServicesLabels: true,
|
||||
@ -405,7 +404,7 @@ func TestPrometheusMetricRemoval(t *testing.T) {
|
||||
promRegistry = prometheus.NewRegistry()
|
||||
t.Cleanup(promState.reset)
|
||||
|
||||
prometheusRegistry := RegisterPrometheus(context.Background(), &types.Prometheus{AddEntryPointsLabels: true, AddServicesLabels: true, AddRoutersLabels: true})
|
||||
prometheusRegistry := RegisterPrometheus(t.Context(), &types.Prometheus{AddEntryPointsLabels: true, AddServicesLabels: true, AddRoutersLabels: true})
|
||||
defer promRegistry.Unregister(promState)
|
||||
|
||||
conf1 := dynamic.Configuration{
|
||||
@ -496,7 +495,7 @@ func TestPrometheusMetricRemoveEndpointForRecoveredService(t *testing.T) {
|
||||
promRegistry = prometheus.NewRegistry()
|
||||
t.Cleanup(promState.reset)
|
||||
|
||||
prometheusRegistry := RegisterPrometheus(context.Background(), &types.Prometheus{AddServicesLabels: true})
|
||||
prometheusRegistry := RegisterPrometheus(t.Context(), &types.Prometheus{AddServicesLabels: true})
|
||||
defer promRegistry.Unregister(promState)
|
||||
|
||||
conf1 := dynamic.Configuration{
|
||||
@ -535,7 +534,7 @@ func TestPrometheusMetricRemoveEndpointForRecoveredService(t *testing.T) {
|
||||
func TestPrometheusRemovedMetricsReset(t *testing.T) {
|
||||
t.Cleanup(promState.reset)
|
||||
|
||||
prometheusRegistry := RegisterPrometheus(context.Background(), &types.Prometheus{AddEntryPointsLabels: true, AddServicesLabels: true})
|
||||
prometheusRegistry := RegisterPrometheus(t.Context(), &types.Prometheus{AddEntryPointsLabels: true, AddServicesLabels: true})
|
||||
defer promRegistry.Unregister(promState)
|
||||
|
||||
conf1 := dynamic.Configuration{
|
||||
|
@ -1,7 +1,6 @@
|
||||
package metrics
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"testing"
|
||||
@ -21,7 +20,7 @@ func TestStatsD(t *testing.T) {
|
||||
// This is needed to make sure that UDP Listener listens for data a bit longer, otherwise it will quit after a millisecond
|
||||
udp.Timeout = 5 * time.Second
|
||||
|
||||
statsdRegistry := RegisterStatsd(context.Background(), &types.Statsd{Address: ":18125", PushInterval: ptypes.Duration(time.Second), AddEntryPointsLabels: true, AddRoutersLabels: true, AddServicesLabels: true})
|
||||
statsdRegistry := RegisterStatsd(t.Context(), &types.Statsd{Address: ":18125", PushInterval: ptypes.Duration(time.Second), AddEntryPointsLabels: true, AddRoutersLabels: true, AddServicesLabels: true})
|
||||
|
||||
testRegistry(t, defaultMetricsPrefix, statsdRegistry)
|
||||
}
|
||||
@ -35,7 +34,7 @@ func TestStatsDWithPrefix(t *testing.T) {
|
||||
// This is needed to make sure that UDP Listener listens for data a bit longer, otherwise it will quit after a millisecond
|
||||
udp.Timeout = 5 * time.Second
|
||||
|
||||
statsdRegistry := RegisterStatsd(context.Background(), &types.Statsd{Address: ":18125", PushInterval: ptypes.Duration(time.Second), AddEntryPointsLabels: true, AddRoutersLabels: true, AddServicesLabels: true, Prefix: "testPrefix"})
|
||||
statsdRegistry := RegisterStatsd(t.Context(), &types.Statsd{Address: ":18125", PushInterval: ptypes.Duration(time.Second), AddEntryPointsLabels: true, AddRoutersLabels: true, AddServicesLabels: true, Prefix: "testPrefix"})
|
||||
|
||||
testRegistry(t, "testPrefix", statsdRegistry)
|
||||
}
|
||||
|
@ -97,7 +97,7 @@ func TestOTelAccessLog(t *testing.T) {
|
||||
Path: testPath,
|
||||
},
|
||||
}
|
||||
ctx := trace.ContextWithSpanContext(context.Background(), trace.NewSpanContext(trace.SpanContextConfig{
|
||||
ctx := trace.ContextWithSpanContext(t.Context(), trace.NewSpanContext(trace.SpanContextConfig{
|
||||
TraceID: trace.TraceID{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8},
|
||||
SpanID: trace.SpanID{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8},
|
||||
}))
|
||||
@ -1055,7 +1055,7 @@ func doLoggingWithAbortedStream(t *testing.T, config *types.AccessLog) {
|
||||
require.NoError(t, err, "logger should create "+config.FilePath)
|
||||
}
|
||||
|
||||
reqContext, cancelRequest := context.WithCancel(context.Background())
|
||||
reqContext, cancelRequest := context.WithCancel(t.Context())
|
||||
|
||||
req := &http.Request{
|
||||
Header: map[string][]string{
|
||||
|
@ -1,7 +1,6 @@
|
||||
package addprefix
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
@ -34,7 +33,7 @@ func TestNewAddPrefix(t *testing.T) {
|
||||
|
||||
next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
|
||||
|
||||
_, err := New(context.Background(), next, test.prefix, "foo-add-prefix")
|
||||
_, err := New(t.Context(), next, test.prefix, "foo-add-prefix")
|
||||
if test.expectsError {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
@ -87,7 +86,7 @@ func TestAddPrefix(t *testing.T) {
|
||||
|
||||
req := testhelpers.MustNewRequest(http.MethodGet, "http://localhost"+test.path, nil)
|
||||
|
||||
handler, err := New(context.Background(), next, test.prefix, "foo-add-prefix")
|
||||
handler, err := New(t.Context(), next, test.prefix, "foo-add-prefix")
|
||||
require.NoError(t, err)
|
||||
|
||||
handler.ServeHTTP(nil, req)
|
||||
|
@ -1,7 +1,6 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
@ -25,13 +24,13 @@ func TestBasicAuthFail(t *testing.T) {
|
||||
auth := dynamic.BasicAuth{
|
||||
Users: []string{"test"},
|
||||
}
|
||||
_, err := NewBasic(context.Background(), next, auth, "authName")
|
||||
_, err := NewBasic(t.Context(), next, auth, "authName")
|
||||
require.Error(t, err)
|
||||
|
||||
auth2 := dynamic.BasicAuth{
|
||||
Users: []string{"test:test"},
|
||||
}
|
||||
authMiddleware, err := NewBasic(context.Background(), next, auth2, "authTest")
|
||||
authMiddleware, err := NewBasic(t.Context(), next, auth2, "authTest")
|
||||
require.NoError(t, err)
|
||||
|
||||
ts := httptest.NewServer(authMiddleware)
|
||||
@ -54,7 +53,7 @@ func TestBasicAuthSuccess(t *testing.T) {
|
||||
auth := dynamic.BasicAuth{
|
||||
Users: []string{"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/"},
|
||||
}
|
||||
authMiddleware, err := NewBasic(context.Background(), next, auth, "authName")
|
||||
authMiddleware, err := NewBasic(t.Context(), next, auth, "authName")
|
||||
require.NoError(t, err)
|
||||
|
||||
ts := httptest.NewServer(authMiddleware)
|
||||
@ -85,7 +84,7 @@ func TestBasicAuthUserHeader(t *testing.T) {
|
||||
Users: []string{"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/"},
|
||||
HeaderField: "X-Webauth-User",
|
||||
}
|
||||
middleware, err := NewBasic(context.Background(), next, auth, "authName")
|
||||
middleware, err := NewBasic(t.Context(), next, auth, "authName")
|
||||
require.NoError(t, err)
|
||||
|
||||
ts := httptest.NewServer(middleware)
|
||||
@ -116,7 +115,7 @@ func TestBasicAuthHeaderRemoved(t *testing.T) {
|
||||
RemoveHeader: true,
|
||||
Users: []string{"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/"},
|
||||
}
|
||||
middleware, err := NewBasic(context.Background(), next, auth, "authName")
|
||||
middleware, err := NewBasic(t.Context(), next, auth, "authName")
|
||||
require.NoError(t, err)
|
||||
|
||||
ts := httptest.NewServer(middleware)
|
||||
@ -147,7 +146,7 @@ func TestBasicAuthHeaderPresent(t *testing.T) {
|
||||
auth := dynamic.BasicAuth{
|
||||
Users: []string{"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/"},
|
||||
}
|
||||
middleware, err := NewBasic(context.Background(), next, auth, "authName")
|
||||
middleware, err := NewBasic(t.Context(), next, auth, "authName")
|
||||
require.NoError(t, err)
|
||||
|
||||
ts := httptest.NewServer(middleware)
|
||||
@ -177,7 +176,7 @@ func TestBasicAuthConcurrentHashOnce(t *testing.T) {
|
||||
Users: []string{"test:$2a$04$.8sTYfcxbSplCtoxt5TdJOgpBYkarKtZYsYfYxQ1edbYRuO1DNi0e"},
|
||||
}
|
||||
|
||||
authMiddleware, err := NewBasic(context.Background(), next, auth, "authName")
|
||||
authMiddleware, err := NewBasic(t.Context(), next, auth, "authName")
|
||||
require.NoError(t, err)
|
||||
|
||||
hashCount := 0
|
||||
@ -277,7 +276,7 @@ func TestBasicAuthUsersFromFile(t *testing.T) {
|
||||
fmt.Fprintln(w, "traefik")
|
||||
})
|
||||
|
||||
authenticator, err := NewBasic(context.Background(), next, authenticatorConfiguration, "authName")
|
||||
authenticator, err := NewBasic(t.Context(), next, authenticatorConfiguration, "authName")
|
||||
require.NoError(t, err)
|
||||
|
||||
ts := httptest.NewServer(authenticator)
|
||||
|
@ -1,7 +1,6 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
@ -23,7 +22,7 @@ func TestDigestAuthError(t *testing.T) {
|
||||
auth := dynamic.DigestAuth{
|
||||
Users: []string{"test"},
|
||||
}
|
||||
_, err := NewDigest(context.Background(), next, auth, "authName")
|
||||
_, err := NewDigest(t.Context(), next, auth, "authName")
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
@ -35,7 +34,7 @@ func TestDigestAuthFail(t *testing.T) {
|
||||
auth := dynamic.DigestAuth{
|
||||
Users: []string{"test:traefik:a2688e031edb4be6a3797f3882655c05"},
|
||||
}
|
||||
authMiddleware, err := NewDigest(context.Background(), next, auth, "authName")
|
||||
authMiddleware, err := NewDigest(t.Context(), next, auth, "authName")
|
||||
require.NoError(t, err)
|
||||
assert.NotNil(t, authMiddleware, "this should not be nil")
|
||||
|
||||
@ -109,7 +108,7 @@ func TestDigestAuthUsersFromFile(t *testing.T) {
|
||||
fmt.Fprintln(w, "traefik")
|
||||
})
|
||||
|
||||
authenticator, err := NewDigest(context.Background(), next, authenticatorConfiguration, "authName")
|
||||
authenticator, err := NewDigest(t.Context(), next, authenticatorConfiguration, "authName")
|
||||
require.NoError(t, err)
|
||||
|
||||
ts := httptest.NewServer(authenticator)
|
||||
|
@ -37,7 +37,7 @@ func TestForwardAuthFail(t *testing.T) {
|
||||
}))
|
||||
t.Cleanup(server.Close)
|
||||
|
||||
middleware, err := NewForward(context.Background(), next, dynamic.ForwardAuth{
|
||||
middleware, err := NewForward(t.Context(), next, dynamic.ForwardAuth{
|
||||
Address: server.URL,
|
||||
}, "authTest")
|
||||
require.NoError(t, err)
|
||||
@ -90,7 +90,7 @@ func TestForwardAuthSuccess(t *testing.T) {
|
||||
AuthResponseHeadersRegex: "^Foo-",
|
||||
AddAuthCookiesToResponse: []string{"authCookie"},
|
||||
}
|
||||
middleware, err := NewForward(context.Background(), next, auth, "authTest")
|
||||
middleware, err := NewForward(t.Context(), next, auth, "authTest")
|
||||
require.NoError(t, err)
|
||||
|
||||
ts := httptest.NewServer(middleware)
|
||||
@ -135,7 +135,7 @@ func TestForwardAuthForwardBody(t *testing.T) {
|
||||
maxBodySize := int64(len(data))
|
||||
auth := dynamic.ForwardAuth{Address: server.URL, ForwardBody: true, MaxBodySize: &maxBodySize}
|
||||
|
||||
middleware, err := NewForward(context.Background(), next, auth, "authTest")
|
||||
middleware, err := NewForward(t.Context(), next, auth, "authTest")
|
||||
require.NoError(t, err)
|
||||
|
||||
ts := httptest.NewServer(middleware)
|
||||
@ -170,7 +170,7 @@ func TestForwardAuthForwardBodyEmptyBody(t *testing.T) {
|
||||
|
||||
auth := dynamic.ForwardAuth{Address: server.URL, ForwardBody: true}
|
||||
|
||||
middleware, err := NewForward(context.Background(), next, auth, "authTest")
|
||||
middleware, err := NewForward(t.Context(), next, auth, "authTest")
|
||||
require.NoError(t, err)
|
||||
|
||||
ts := httptest.NewServer(middleware)
|
||||
@ -208,7 +208,7 @@ func TestForwardAuthForwardBodySizeLimit(t *testing.T) {
|
||||
maxBodySize := int64(len(data)) - 1
|
||||
auth := dynamic.ForwardAuth{Address: server.URL, ForwardBody: true, MaxBodySize: &maxBodySize}
|
||||
|
||||
middleware, err := NewForward(context.Background(), next, auth, "authTest")
|
||||
middleware, err := NewForward(t.Context(), next, auth, "authTest")
|
||||
require.NoError(t, err)
|
||||
|
||||
ts := httptest.NewServer(middleware)
|
||||
@ -245,7 +245,7 @@ func TestForwardAuthNotForwardBody(t *testing.T) {
|
||||
|
||||
auth := dynamic.ForwardAuth{Address: server.URL}
|
||||
|
||||
middleware, err := NewForward(context.Background(), next, auth, "authTest")
|
||||
middleware, err := NewForward(t.Context(), next, auth, "authTest")
|
||||
require.NoError(t, err)
|
||||
|
||||
ts := httptest.NewServer(middleware)
|
||||
@ -273,7 +273,7 @@ func TestForwardAuthRedirect(t *testing.T) {
|
||||
|
||||
auth := dynamic.ForwardAuth{Address: authTs.URL}
|
||||
|
||||
authMiddleware, err := NewForward(context.Background(), next, auth, "authTest")
|
||||
authMiddleware, err := NewForward(t.Context(), next, auth, "authTest")
|
||||
require.NoError(t, err)
|
||||
|
||||
ts := httptest.NewServer(authMiddleware)
|
||||
@ -324,7 +324,7 @@ func TestForwardAuthRemoveHopByHopHeaders(t *testing.T) {
|
||||
|
||||
auth := dynamic.ForwardAuth{Address: authTs.URL}
|
||||
|
||||
authMiddleware, err := NewForward(context.Background(), next, auth, "authTest")
|
||||
authMiddleware, err := NewForward(t.Context(), next, auth, "authTest")
|
||||
require.NoError(t, err)
|
||||
|
||||
ts := httptest.NewServer(authMiddleware)
|
||||
@ -370,7 +370,7 @@ func TestForwardAuthFailResponseHeaders(t *testing.T) {
|
||||
auth := dynamic.ForwardAuth{
|
||||
Address: authTs.URL,
|
||||
}
|
||||
authMiddleware, err := NewForward(context.Background(), next, auth, "authTest")
|
||||
authMiddleware, err := NewForward(t.Context(), next, auth, "authTest")
|
||||
require.NoError(t, err)
|
||||
|
||||
ts := httptest.NewServer(authMiddleware)
|
||||
@ -682,7 +682,7 @@ func TestForwardAuthTracing(t *testing.T) {
|
||||
Address: server.URL,
|
||||
AuthRequestHeaders: []string{"X-Foo"},
|
||||
}
|
||||
next, err := NewForward(context.Background(), next, auth, "authTest")
|
||||
next, err := NewForward(t.Context(), next, auth, "authTest")
|
||||
require.NoError(t, err)
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "http://www.test.com/search?q=Opentelemetry", nil)
|
||||
@ -725,7 +725,7 @@ func TestForwardAuthPreserveLocationHeader(t *testing.T) {
|
||||
Address: server.URL,
|
||||
PreserveLocationHeader: true,
|
||||
}
|
||||
middleware, err := NewForward(context.Background(), next, auth, "authTest")
|
||||
middleware, err := NewForward(t.Context(), next, auth, "authTest")
|
||||
require.NoError(t, err)
|
||||
|
||||
ts := httptest.NewServer(middleware)
|
||||
@ -779,7 +779,7 @@ func TestForwardAuthPreserveRequestMethod(t *testing.T) {
|
||||
PreserveRequestMethod: test.preserveRequestMethod,
|
||||
}
|
||||
|
||||
middleware, err := NewForward(context.Background(), next, auth, "authTest")
|
||||
middleware, err := NewForward(t.Context(), next, auth, "authTest")
|
||||
require.NoError(t, err)
|
||||
|
||||
ts := httptest.NewServer(middleware)
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user