Merge current v2.6 into master
This commit is contained in:
commit
a79868fadc
22 changed files with 566 additions and 221 deletions
17
CHANGELOG.md
17
CHANGELOG.md
|
@ -1,3 +1,20 @@
|
||||||
|
## [v2.6.1](https://github.com/traefik/traefik/tree/v2.6.1) (2022-02-14)
|
||||||
|
[All Commits](https://github.com/traefik/traefik/compare/v2.6.0...v2.6.1)
|
||||||
|
|
||||||
|
**Bug fixes:**
|
||||||
|
- **[acme]** Add domain to HTTP challenge errors ([#8740](https://github.com/traefik/traefik/pull/8740) by [ldez](https://github.com/ldez))
|
||||||
|
- **[metrics]** Fix metrics bucket key high cardinality ([#8761](https://github.com/traefik/traefik/pull/8761) by [tomMoulard](https://github.com/tomMoulard))
|
||||||
|
- **[middleware,tls]** Use CNAME for SNI check on host header ([#8773](https://github.com/traefik/traefik/pull/8773) by [ldez](https://github.com/ldez))
|
||||||
|
- **[middleware,tracing]** Rename Datadog span tags ([#8323](https://github.com/traefik/traefik/pull/8323) by [luckielordie](https://github.com/luckielordie))
|
||||||
|
- **[tls]** Apply the same approach as the rules system on the TLS configuration choice ([#8764](https://github.com/traefik/traefik/pull/8764) by [ldez](https://github.com/ldez))
|
||||||
|
|
||||||
|
**Documentation:**
|
||||||
|
- **[acme]** Add Hurricane Electric to acme documentation ([#8746](https://github.com/traefik/traefik/pull/8746) by [vladshub](https://github.com/vladshub))
|
||||||
|
- **[acme]** Clarify that ACME challenge is mandatory ([#8739](https://github.com/traefik/traefik/pull/8739) by [mpl](https://github.com/mpl))
|
||||||
|
- **[http3]** Explain a bit more around enabling HTTP3 ([#8731](https://github.com/traefik/traefik/pull/8731) by [SantoDE](https://github.com/SantoDE))
|
||||||
|
- **[metrics]** Fix mixups in metrics documentation ([#8752](https://github.com/traefik/traefik/pull/8752) by [tomMoulard](https://github.com/tomMoulard))
|
||||||
|
- **[middleware,k8s/crd]** Fix Kubernetes TCP examples ([#8759](https://github.com/traefik/traefik/pull/8759) by [sylr](https://github.com/sylr))
|
||||||
|
|
||||||
## [v2.6.0](https://github.com/traefik/traefik/tree/v2.6.0) (2022-01-24)
|
## [v2.6.0](https://github.com/traefik/traefik/tree/v2.6.0) (2022-01-24)
|
||||||
[All Commits](https://github.com/traefik/traefik/compare/v2.5.0-rc1...v2.6.0)
|
[All Commits](https://github.com/traefik/traefik/compare/v2.5.0-rc1...v2.6.0)
|
||||||
|
|
||||||
|
|
|
@ -190,7 +190,7 @@ func setupServer(staticConfiguration *static.Configuration) (*server.Server, err
|
||||||
|
|
||||||
// Entrypoints
|
// Entrypoints
|
||||||
|
|
||||||
serverEntryPointsTCP, err := server.NewTCPEntryPoints(staticConfiguration.EntryPoints)
|
serverEntryPointsTCP, err := server.NewTCPEntryPoints(staticConfiguration.EntryPoints, staticConfiguration.HostResolver)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ labels:
|
||||||
|
|
||||||
```yaml tab="Kubernetes"
|
```yaml tab="Kubernetes"
|
||||||
apiVersion: traefik.containo.us/v1alpha1
|
apiVersion: traefik.containo.us/v1alpha1
|
||||||
kind: Middleware
|
kind: MiddlewareTCP
|
||||||
metadata:
|
metadata:
|
||||||
name: test-inflightconn
|
name: test-inflightconn
|
||||||
spec:
|
spec:
|
||||||
|
|
|
@ -36,7 +36,7 @@ spec:
|
||||||
|
|
||||||
---
|
---
|
||||||
apiVersion: traefik.containo.us/v1alpha1
|
apiVersion: traefik.containo.us/v1alpha1
|
||||||
kind: Middleware
|
kind: MiddlewareTCP
|
||||||
metadata:
|
metadata:
|
||||||
name: foo-ip-whitelist
|
name: foo-ip-whitelist
|
||||||
spec:
|
spec:
|
||||||
|
@ -47,7 +47,7 @@ spec:
|
||||||
|
|
||||||
---
|
---
|
||||||
apiVersion: traefik.containo.us/v1alpha1
|
apiVersion: traefik.containo.us/v1alpha1
|
||||||
kind: IngressRoute
|
kind: IngressRouteTCP
|
||||||
metadata:
|
metadata:
|
||||||
name: ingressroute
|
name: ingressroute
|
||||||
spec:
|
spec:
|
||||||
|
|
|
@ -438,3 +438,17 @@ To enable HTTP/3 on an EntryPoint, please check out the [HTTP/3 configuration](.
|
||||||
In `v2.6`, the [Kubernetes Gateway API provider](../providers/kubernetes-gateway.md) now only supports the version [v1alpha2](https://gateway-api.sigs.k8s.io/v1alpha2/guides/getting-started/) of the specification and
|
In `v2.6`, the [Kubernetes Gateway API provider](../providers/kubernetes-gateway.md) now only supports the version [v1alpha2](https://gateway-api.sigs.k8s.io/v1alpha2/guides/getting-started/) of the specification and
|
||||||
[route namespaces](https://gateway-api.sigs.k8s.io/v1alpha2/references/spec/#gateway.networking.k8s.io/v1alpha2.RouteNamespaces) selectors, which requires Traefik to fetch and watch the cluster namespaces.
|
[route namespaces](https://gateway-api.sigs.k8s.io/v1alpha2/references/spec/#gateway.networking.k8s.io/v1alpha2.RouteNamespaces) selectors, which requires Traefik to fetch and watch the cluster namespaces.
|
||||||
Therefore, the [RBAC](../reference/dynamic-configuration/kubernetes-gateway.md#rbac) and [CRD](../reference/dynamic-configuration/kubernetes-gateway.md#definitions) definitions must be updated.
|
Therefore, the [RBAC](../reference/dynamic-configuration/kubernetes-gateway.md#rbac) and [CRD](../reference/dynamic-configuration/kubernetes-gateway.md#definitions) definitions must be updated.
|
||||||
|
|
||||||
|
## v2.6.0 to v2.6.1
|
||||||
|
|
||||||
|
### Metrics
|
||||||
|
|
||||||
|
In `v2.6.1`, the metrics system does not support any more custom HTTP method verbs to prevent potential metrics cardinality overhead.
|
||||||
|
In consequence, for metrics having the method label,
|
||||||
|
if the HTTP method verb of a request is not one defined in the set of common methods for [`HTTP/1.1`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods)
|
||||||
|
or the [`PRI`](https://datatracker.ietf.org/doc/html/rfc7540#section-11.6) verb (for `HTTP/2`),
|
||||||
|
the value for the method label becomes `EXTENSION_METHOD`, instead of the request's one.
|
||||||
|
|
||||||
|
### Tracing
|
||||||
|
|
||||||
|
In `v2.6.1`, the Datadog tags added to a span changed from `service.name` to `traefik.service.name` and from `router.name` to `traefik.router.name`.
|
||||||
|
|
|
@ -62,7 +62,7 @@ traefik_config_last_reload_success
|
||||||
|
|
||||||
The expiration date of certificates.
|
The expiration date of certificates.
|
||||||
|
|
||||||
Available labels: `cn`, `sans`, `serial`.
|
[Labels](#labels): `cn`, `sans`, `serial`.
|
||||||
|
|
||||||
```dd tab="Datadog"
|
```dd tab="Datadog"
|
||||||
tls.certs.notAfterTimestamp
|
tls.certs.notAfterTimestamp
|
||||||
|
@ -94,7 +94,7 @@ traefik_tls_certs_not_after
|
||||||
|
|
||||||
The total count of HTTP requests received by an entrypoint.
|
The total count of HTTP requests received by an entrypoint.
|
||||||
|
|
||||||
Available labels: `code`, `method`, `protocol`, `entrypoint`.
|
[Labels](#labels): `code`, `method`, `protocol`, `entrypoint`.
|
||||||
|
|
||||||
```dd tab="Datadog"
|
```dd tab="Datadog"
|
||||||
entrypoint.request.total
|
entrypoint.request.total
|
||||||
|
@ -117,7 +117,7 @@ traefik_entrypoint_requests_total
|
||||||
|
|
||||||
The total count of HTTPS requests received by an entrypoint.
|
The total count of HTTPS requests received by an entrypoint.
|
||||||
|
|
||||||
Available labels: `tls_version`, `tls_cipher`, `entrypoint`.
|
[Labels](#labels): `tls_version`, `tls_cipher`, `entrypoint`.
|
||||||
|
|
||||||
```dd tab="Datadog"
|
```dd tab="Datadog"
|
||||||
entrypoint.request.tls.total
|
entrypoint.request.tls.total
|
||||||
|
@ -140,7 +140,7 @@ traefik_entrypoint_requests_tls_total
|
||||||
|
|
||||||
Request processing duration histogram on an entrypoint.
|
Request processing duration histogram on an entrypoint.
|
||||||
|
|
||||||
Available labels: `code`, `method`, `protocol`, `entrypoint`.
|
[Labels](#labels): `code`, `method`, `protocol`, `entrypoint`.
|
||||||
|
|
||||||
```dd tab="Datadog"
|
```dd tab="Datadog"
|
||||||
entrypoint.request.duration
|
entrypoint.request.duration
|
||||||
|
@ -163,7 +163,7 @@ traefik_entrypoint_request_duration_seconds
|
||||||
|
|
||||||
The current count of open connections on an entrypoint.
|
The current count of open connections on an entrypoint.
|
||||||
|
|
||||||
Available labels: `method`, `protocol`, `entrypoint`.
|
[Labels](#labels): `method`, `protocol`, `entrypoint`.
|
||||||
|
|
||||||
```dd tab="Datadog"
|
```dd tab="Datadog"
|
||||||
entrypoint.connections.open
|
entrypoint.connections.open
|
||||||
|
@ -195,7 +195,7 @@ traefik_entrypoint_open_connections
|
||||||
|
|
||||||
The total count of HTTP requests handled by a router.
|
The total count of HTTP requests handled by a router.
|
||||||
|
|
||||||
Available labels: `code`, `method`, `protocol`, `router`, `service`.
|
[Labels](#labels): `code`, `method`, `protocol`, `router`, `service`.
|
||||||
|
|
||||||
```dd tab="Datadog"
|
```dd tab="Datadog"
|
||||||
router.request.total
|
router.request.total
|
||||||
|
@ -218,7 +218,7 @@ traefik_router_requests_total
|
||||||
|
|
||||||
The total count of HTTPS requests handled by a router.
|
The total count of HTTPS requests handled by a router.
|
||||||
|
|
||||||
Available labels: `tls_version`, `tls_cipher`, `router`, `service`.
|
[Labels](#labels): `tls_version`, `tls_cipher`, `router`, `service`.
|
||||||
|
|
||||||
```dd tab="Datadog"
|
```dd tab="Datadog"
|
||||||
router.request.tls.total
|
router.request.tls.total
|
||||||
|
@ -241,7 +241,7 @@ traefik_router_requests_tls_total
|
||||||
|
|
||||||
Request processing duration histogram on a router.
|
Request processing duration histogram on a router.
|
||||||
|
|
||||||
Available labels: `code`, `method`, `protocol`, `router`, `service`.
|
[Labels](#labels): `code`, `method`, `protocol`, `router`, `service`.
|
||||||
|
|
||||||
```dd tab="Datadog"
|
```dd tab="Datadog"
|
||||||
router.request.duration
|
router.request.duration
|
||||||
|
@ -264,7 +264,7 @@ traefik_router_request_duration_seconds
|
||||||
|
|
||||||
The current count of open connections on a router.
|
The current count of open connections on a router.
|
||||||
|
|
||||||
Available labels: `method`, `protocol`, `router`, `service`.
|
[Labels](#labels): `method`, `protocol`, `router`, `service`.
|
||||||
|
|
||||||
```dd tab="Datadog"
|
```dd tab="Datadog"
|
||||||
router.connections.open
|
router.connections.open
|
||||||
|
@ -298,7 +298,7 @@ traefik_router_open_connections
|
||||||
|
|
||||||
The total count of HTTP requests processed on a service.
|
The total count of HTTP requests processed on a service.
|
||||||
|
|
||||||
Available labels: `code`, `method`, `protocol`, `service`.
|
[Labels](#labels): `code`, `method`, `protocol`, `service`.
|
||||||
|
|
||||||
```dd tab="Datadog"
|
```dd tab="Datadog"
|
||||||
service.request.total
|
service.request.total
|
||||||
|
@ -321,7 +321,7 @@ traefik_service_requests_total
|
||||||
|
|
||||||
The total count of HTTPS requests processed on a service.
|
The total count of HTTPS requests processed on a service.
|
||||||
|
|
||||||
Available labels: `tls_version`, `tls_cipher`, `service`.
|
[Labels](#labels): `tls_version`, `tls_cipher`, `service`.
|
||||||
|
|
||||||
```dd tab="Datadog"
|
```dd tab="Datadog"
|
||||||
router.service.tls.total
|
router.service.tls.total
|
||||||
|
@ -344,7 +344,7 @@ traefik_service_requests_tls_total
|
||||||
|
|
||||||
Request processing duration histogram on a service.
|
Request processing duration histogram on a service.
|
||||||
|
|
||||||
Available labels: `code`, `method`, `protocol`, `service`.
|
[Labels](#labels): `code`, `method`, `protocol`, `service`.
|
||||||
|
|
||||||
```dd tab="Datadog"
|
```dd tab="Datadog"
|
||||||
service.request.duration
|
service.request.duration
|
||||||
|
@ -367,7 +367,7 @@ traefik_service_request_duration_seconds
|
||||||
|
|
||||||
The current count of open connections on a service.
|
The current count of open connections on a service.
|
||||||
|
|
||||||
Available labels: `method`, `protocol`, `service`.
|
[Labels](#labels): `method`, `protocol`, `service`.
|
||||||
|
|
||||||
```dd tab="Datadog"
|
```dd tab="Datadog"
|
||||||
service.connections.open
|
service.connections.open
|
||||||
|
@ -390,7 +390,7 @@ traefik_service_open_connections
|
||||||
|
|
||||||
The count of requests retries on a service.
|
The count of requests retries on a service.
|
||||||
|
|
||||||
Available labels: `service`.
|
[Labels](#labels): `service`.
|
||||||
|
|
||||||
```dd tab="Datadog"
|
```dd tab="Datadog"
|
||||||
service.retries.total
|
service.retries.total
|
||||||
|
@ -413,7 +413,7 @@ traefik_service_retries_total
|
||||||
|
|
||||||
Current service's server status, described by a gauge with a value of 0 for a down server or a value of 1 for an up server.
|
Current service's server status, described by a gauge with a value of 0 for a down server or a value of 1 for an up server.
|
||||||
|
|
||||||
Available labels: `service`, `url`.
|
[Labels](#labels): `service`, `url`.
|
||||||
|
|
||||||
```dd tab="Datadog"
|
```dd tab="Datadog"
|
||||||
service.server.up
|
service.server.up
|
||||||
|
@ -431,3 +431,28 @@ traefik_service_server_up
|
||||||
# Default prefix: "traefik"
|
# Default prefix: "traefik"
|
||||||
{prefix}.service.server.up
|
{prefix}.service.server.up
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Labels
|
||||||
|
|
||||||
|
Here is a comprehensive list of labels that are provided by the metrics:
|
||||||
|
|
||||||
|
| Label | Description | example |
|
||||||
|
|---------------|---------------------------------------|----------------------------|
|
||||||
|
| `cn` | Certificate Common Name | "example.com" |
|
||||||
|
| `code` | Request code | "200" |
|
||||||
|
| `entrypoint` | Entrypoint that handled the request | "example_entrypoint" |
|
||||||
|
| `method` | Request Method | "GET" |
|
||||||
|
| `protocol` | Request protocol | "http" |
|
||||||
|
| `router` | Router that handled the request | "example_router" |
|
||||||
|
| `sans` | Certificate Subject Alternative NameS | "example.com" |
|
||||||
|
| `serial` | Certificate Serial Number | "123..." |
|
||||||
|
| `service` | Service that handled the request | "example_service@provider" |
|
||||||
|
| `tls_cipher` | TLS cipher used for the request | "TLS_FALLBACK_SCSV" |
|
||||||
|
| `tls_version` | TLS version used for the request | "1.0" |
|
||||||
|
| `url` | Service server url | "http://example.com" |
|
||||||
|
|
||||||
|
!!! info "`method` label value"
|
||||||
|
|
||||||
|
If the HTTP method verb on a request is not one defined in the set of common methods for [`HTTP/1.1`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods)
|
||||||
|
or the [`PRI`](https://datatracker.ietf.org/doc/html/rfc7540#section-11.6) verb (for `HTTP/2`),
|
||||||
|
then the value for the method label becomes `EXTENSION_METHOD`.
|
||||||
|
|
|
@ -9,18 +9,24 @@
|
||||||
# Use certificate in net/internal/testcert.go
|
# Use certificate in net/internal/testcert.go
|
||||||
rootCAs = [ """
|
rootCAs = [ """
|
||||||
-----BEGIN CERTIFICATE-----
|
-----BEGIN CERTIFICATE-----
|
||||||
MIICEzCCAXygAwIBAgIQMIMChMLGrR+QvmQvpwAU6zANBgkqhkiG9w0BAQsFADAS
|
MIIDOTCCAiGgAwIBAgIQSRJrEpBGFc7tNb1fb5pKFzANBgkqhkiG9w0BAQsFADAS
|
||||||
MRAwDgYDVQQKEwdBY21lIENvMCAXDTcwMDEwMTAwMDAwMFoYDzIwODQwMTI5MTYw
|
MRAwDgYDVQQKEwdBY21lIENvMCAXDTcwMDEwMTAwMDAwMFoYDzIwODQwMTI5MTYw
|
||||||
MDAwWjASMRAwDgYDVQQKEwdBY21lIENvMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCB
|
MDAwWjASMRAwDgYDVQQKEwdBY21lIENvMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
|
||||||
iQKBgQDuLnQAI3mDgey3VBzWnB2L39JUU4txjeVE6myuDqkM/uGlfjb9SjY1bIw4
|
MIIBCgKCAQEA6Gba5tHV1dAKouAaXO3/ebDUU4rvwCUg/CNaJ2PT5xLD4N1Vcb8r
|
||||||
iA5sBBZzHi3z0h1YV8QPuxEbi4nW91IJm2gsvvZhIrCHS3l6afab4pZBl2+XsDul
|
bFSW2HXKq+MPfVdwIKR/1DczEoAGf/JWQTW7EgzlXrCd3rlajEX2D73faWJekD0U
|
||||||
rKBxKKtD1rGxlG4LjncdabFn9gvLZad2bSysqz/qTAUStTvqJQIDAQABo2gwZjAO
|
aUgz5vtrTXZ90BQL7WvRICd7FlEZ6FPOcPlumiyNmzUqtwGhO+9ad1W5BqJaRI6P
|
||||||
BgNVHQ8BAf8EBAMCAqQwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDwYDVR0TAQH/BAUw
|
YfouNkwR6Na4TzSj5BrqUfP0FwDizKSJ0XXmh8g8G9mtwxOSN3Ru1QFc61Xyeluk
|
||||||
AwEB/zAuBgNVHREEJzAlggtleGFtcGxlLmNvbYcEfwAAAYcQAAAAAAAAAAAAAAAA
|
POGKBV/q6RBNklTNe0gI8usUMlYyoC7ytppNMW7X2vodAelSu25jgx2anj9fDVZu
|
||||||
AAAAATANBgkqhkiG9w0BAQsFAAOBgQCEcetwO59EWk7WiJsG4x8SY+UIAA+flUI9
|
h7AXF5+4nJS4AAt0n1lNY7nGSsdZas8PbQIDAQABo4GIMIGFMA4GA1UdDwEB/wQE
|
||||||
tyC4lNhbcF2Idq9greZwbYCqTTTr2XiRNSMLCOjKyI7ukPoPjo16ocHj+P3vZGfs
|
AwICpDATBgNVHSUEDDAKBggrBgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud
|
||||||
h1fIw3cSS2OolhloGw/XM6RWPWtPAlGykKLciQrBru5NAPvCMsb/I1DAceTiotQM
|
DgQWBBStsdjh3/JCXXYlQryOrL4Sh7BW5TAuBgNVHREEJzAlggtleGFtcGxlLmNv
|
||||||
fblo6RBxUQ==
|
bYcEfwAAAYcQAAAAAAAAAAAAAAAAAAAAATANBgkqhkiG9w0BAQsFAAOCAQEAxWGI
|
||||||
|
5NhpF3nwwy/4yB4i/CwwSpLrWUa70NyhvprUBC50PxiXav1TeDzwzLx/o5HyNwsv
|
||||||
|
cxv3HdkLW59i/0SlJSrNnWdfZ19oTcS+6PtLoVyISgtyN6DpkKpdG1cOkW3Cy2P2
|
||||||
|
+tK/tKHRP1Y/Ra0RiDpOAmqn0gCOFGz8+lqDIor/T7MTpibL3IxqWfPrvfVRHL3B
|
||||||
|
grw/ZQTTIVjjh4JBSW3WyWgNo/ikC1lrVxzl4iPUGptxT36Cr7Zk2Bsg0XqwbOvK
|
||||||
|
5d+NTDREkSnUbie4GeutujmX3Dsx88UiV6UY/4lHJa6I5leHUNOHahRbpbWeOfs/
|
||||||
|
WkBKOclmOV2xlTVuPw==
|
||||||
-----END CERTIFICATE-----
|
-----END CERTIFICATE-----
|
||||||
"""]
|
"""]
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,20 @@
|
||||||
-----BEGIN CERTIFICATE-----
|
-----BEGIN CERTIFICATE-----
|
||||||
MIICEzCCAXygAwIBAgIQMIMChMLGrR+QvmQvpwAU6zANBgkqhkiG9w0BAQsFADAS
|
MIIDOTCCAiGgAwIBAgIQSRJrEpBGFc7tNb1fb5pKFzANBgkqhkiG9w0BAQsFADAS
|
||||||
MRAwDgYDVQQKEwdBY21lIENvMCAXDTcwMDEwMTAwMDAwMFoYDzIwODQwMTI5MTYw
|
MRAwDgYDVQQKEwdBY21lIENvMCAXDTcwMDEwMTAwMDAwMFoYDzIwODQwMTI5MTYw
|
||||||
MDAwWjASMRAwDgYDVQQKEwdBY21lIENvMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCB
|
MDAwWjASMRAwDgYDVQQKEwdBY21lIENvMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
|
||||||
iQKBgQDuLnQAI3mDgey3VBzWnB2L39JUU4txjeVE6myuDqkM/uGlfjb9SjY1bIw4
|
MIIBCgKCAQEA6Gba5tHV1dAKouAaXO3/ebDUU4rvwCUg/CNaJ2PT5xLD4N1Vcb8r
|
||||||
iA5sBBZzHi3z0h1YV8QPuxEbi4nW91IJm2gsvvZhIrCHS3l6afab4pZBl2+XsDul
|
bFSW2HXKq+MPfVdwIKR/1DczEoAGf/JWQTW7EgzlXrCd3rlajEX2D73faWJekD0U
|
||||||
rKBxKKtD1rGxlG4LjncdabFn9gvLZad2bSysqz/qTAUStTvqJQIDAQABo2gwZjAO
|
aUgz5vtrTXZ90BQL7WvRICd7FlEZ6FPOcPlumiyNmzUqtwGhO+9ad1W5BqJaRI6P
|
||||||
BgNVHQ8BAf8EBAMCAqQwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDwYDVR0TAQH/BAUw
|
YfouNkwR6Na4TzSj5BrqUfP0FwDizKSJ0XXmh8g8G9mtwxOSN3Ru1QFc61Xyeluk
|
||||||
AwEB/zAuBgNVHREEJzAlggtleGFtcGxlLmNvbYcEfwAAAYcQAAAAAAAAAAAAAAAA
|
POGKBV/q6RBNklTNe0gI8usUMlYyoC7ytppNMW7X2vodAelSu25jgx2anj9fDVZu
|
||||||
AAAAATANBgkqhkiG9w0BAQsFAAOBgQCEcetwO59EWk7WiJsG4x8SY+UIAA+flUI9
|
h7AXF5+4nJS4AAt0n1lNY7nGSsdZas8PbQIDAQABo4GIMIGFMA4GA1UdDwEB/wQE
|
||||||
tyC4lNhbcF2Idq9greZwbYCqTTTr2XiRNSMLCOjKyI7ukPoPjo16ocHj+P3vZGfs
|
AwICpDATBgNVHSUEDDAKBggrBgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud
|
||||||
h1fIw3cSS2OolhloGw/XM6RWPWtPAlGykKLciQrBru5NAPvCMsb/I1DAceTiotQM
|
DgQWBBStsdjh3/JCXXYlQryOrL4Sh7BW5TAuBgNVHREEJzAlggtleGFtcGxlLmNv
|
||||||
fblo6RBxUQ==
|
bYcEfwAAAYcQAAAAAAAAAAAAAAAAAAAAATANBgkqhkiG9w0BAQsFAAOCAQEAxWGI
|
||||||
|
5NhpF3nwwy/4yB4i/CwwSpLrWUa70NyhvprUBC50PxiXav1TeDzwzLx/o5HyNwsv
|
||||||
|
cxv3HdkLW59i/0SlJSrNnWdfZ19oTcS+6PtLoVyISgtyN6DpkKpdG1cOkW3Cy2P2
|
||||||
|
+tK/tKHRP1Y/Ra0RiDpOAmqn0gCOFGz8+lqDIor/T7MTpibL3IxqWfPrvfVRHL3B
|
||||||
|
grw/ZQTTIVjjh4JBSW3WyWgNo/ikC1lrVxzl4iPUGptxT36Cr7Zk2Bsg0XqwbOvK
|
||||||
|
5d+NTDREkSnUbie4GeutujmX3Dsx88UiV6UY/4lHJa6I5leHUNOHahRbpbWeOfs/
|
||||||
|
WkBKOclmOV2xlTVuPw==
|
||||||
-----END CERTIFICATE-----
|
-----END CERTIFICATE-----
|
||||||
|
|
|
@ -159,12 +159,27 @@ func containsHeader(req *http.Request, name, value string) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getMethod returns the request's method.
|
||||||
|
// It checks whether the method is a valid UTF-8 string.
|
||||||
|
// To restrict the (potentially infinite) number of accepted values for the method,
|
||||||
|
// and avoid unbounded memory issues,
|
||||||
|
// values that are not part of the set of HTTP verbs are replaced with EXTENSION_METHOD.
|
||||||
|
// See https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods
|
||||||
|
// https://datatracker.ietf.org/doc/html/rfc2616/#section-5.1.1.
|
||||||
func getMethod(r *http.Request) string {
|
func getMethod(r *http.Request) string {
|
||||||
if !utf8.ValidString(r.Method) {
|
if !utf8.ValidString(r.Method) {
|
||||||
log.Warnf("Invalid HTTP method encoding: %s", r.Method)
|
log.WithoutContext().Warnf("Invalid HTTP method encoding: %s", r.Method)
|
||||||
return "NON_UTF8_HTTP_METHOD"
|
return "NON_UTF8_HTTP_METHOD"
|
||||||
}
|
}
|
||||||
return r.Method
|
|
||||||
|
switch r.Method {
|
||||||
|
case "HEAD", "GET", "POST", "PUT", "DELETE", "CONNECT", "OPTIONS", "TRACE", // https://datatracker.ietf.org/doc/html/rfc7231#section-4
|
||||||
|
"PATCH", // https://datatracker.ietf.org/doc/html/rfc5789#section-2
|
||||||
|
"PRI": // https://datatracker.ietf.org/doc/html/rfc7540#section-11.6
|
||||||
|
return r.Method
|
||||||
|
default:
|
||||||
|
return "EXTENSION_METHOD"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type retryMetrics interface {
|
type retryMetrics interface {
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/go-kit/kit/metrics"
|
"github.com/go-kit/kit/metrics"
|
||||||
|
@ -98,3 +99,33 @@ func TestCloseNotifier(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_getMethod(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
method string
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
method: http.MethodGet,
|
||||||
|
expected: http.MethodGet,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
method: strings.ToLower(http.MethodGet),
|
||||||
|
expected: "EXTENSION_METHOD",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
method: "THIS_IS_NOT_A_VALID_METHOD",
|
||||||
|
expected: "EXTENSION_METHOD",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range testCases {
|
||||||
|
test := test
|
||||||
|
t.Run(test.method, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
request := httptest.NewRequest(test.method, "http://example.com", nil)
|
||||||
|
assert.Equal(t, test.expected, getMethod(request))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
107
pkg/middlewares/snicheck/snicheck.go
Normal file
107
pkg/middlewares/snicheck/snicheck.go
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
package snicheck
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/traefik/traefik/v2/pkg/log"
|
||||||
|
"github.com/traefik/traefik/v2/pkg/middlewares/requestdecorator"
|
||||||
|
traefiktls "github.com/traefik/traefik/v2/pkg/tls"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SNICheck is an HTTP handler that checks whether the TLS configuration for the server name is the same as for the host header.
|
||||||
|
type SNICheck struct {
|
||||||
|
next http.Handler
|
||||||
|
tlsOptionsForHost map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates a new SNICheck.
|
||||||
|
func New(tlsOptionsForHost map[string]string, next http.Handler) *SNICheck {
|
||||||
|
return &SNICheck{next: next, tlsOptionsForHost: tlsOptionsForHost}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s SNICheck) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||||
|
if req.TLS == nil {
|
||||||
|
s.next.ServeHTTP(rw, req)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
host := getHost(req)
|
||||||
|
serverName := strings.TrimSpace(req.TLS.ServerName)
|
||||||
|
|
||||||
|
// Domain Fronting
|
||||||
|
if !strings.EqualFold(host, serverName) {
|
||||||
|
tlsOptionHeader := findTLSOptionName(s.tlsOptionsForHost, host, true)
|
||||||
|
tlsOptionSNI := findTLSOptionName(s.tlsOptionsForHost, serverName, false)
|
||||||
|
|
||||||
|
if tlsOptionHeader != tlsOptionSNI {
|
||||||
|
log.WithoutContext().
|
||||||
|
WithField("host", host).
|
||||||
|
WithField("req.Host", req.Host).
|
||||||
|
WithField("req.TLS.ServerName", req.TLS.ServerName).
|
||||||
|
Debugf("TLS options difference: SNI:%s, Header:%s", tlsOptionSNI, tlsOptionHeader)
|
||||||
|
http.Error(rw, http.StatusText(http.StatusMisdirectedRequest), http.StatusMisdirectedRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s.next.ServeHTTP(rw, req)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getHost(req *http.Request) string {
|
||||||
|
h := requestdecorator.GetCNAMEFlatten(req.Context())
|
||||||
|
if h != "" {
|
||||||
|
return h
|
||||||
|
}
|
||||||
|
|
||||||
|
h = requestdecorator.GetCanonizedHost(req.Context())
|
||||||
|
if h != "" {
|
||||||
|
return h
|
||||||
|
}
|
||||||
|
|
||||||
|
host, _, err := net.SplitHostPort(req.Host)
|
||||||
|
if err != nil {
|
||||||
|
host = req.Host
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.TrimSpace(host)
|
||||||
|
}
|
||||||
|
|
||||||
|
func findTLSOptionName(tlsOptionsForHost map[string]string, host string, fqdn bool) string {
|
||||||
|
name := findTLSOptName(tlsOptionsForHost, host, fqdn)
|
||||||
|
if name != "" {
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
|
name = findTLSOptName(tlsOptionsForHost, strings.ToLower(host), fqdn)
|
||||||
|
if name != "" {
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
|
return traefiktls.DefaultTLSConfigName
|
||||||
|
}
|
||||||
|
|
||||||
|
func findTLSOptName(tlsOptionsForHost map[string]string, host string, fqdn bool) string {
|
||||||
|
if tlsOptions, ok := tlsOptionsForHost[host]; ok {
|
||||||
|
return tlsOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
if !fqdn {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if last := len(host) - 1; last >= 0 && host[last] == '.' {
|
||||||
|
if tlsOptions, ok := tlsOptionsForHost[host[:last]]; ok {
|
||||||
|
return tlsOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if tlsOptions, ok := tlsOptionsForHost[host+"."]; ok {
|
||||||
|
return tlsOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
60
pkg/middlewares/snicheck/snicheck_test.go
Normal file
60
pkg/middlewares/snicheck/snicheck_test.go
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
package snicheck
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSNICheck_ServeHTTP(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
desc string
|
||||||
|
tlsOptionsForHost map[string]string
|
||||||
|
host string
|
||||||
|
expected int
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
desc: "no TLS options",
|
||||||
|
expected: http.StatusOK,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "with TLS options",
|
||||||
|
tlsOptionsForHost: map[string]string{
|
||||||
|
"example.com": "foo",
|
||||||
|
},
|
||||||
|
expected: http.StatusOK,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "server name and host doesn't have the same TLS configuration",
|
||||||
|
tlsOptionsForHost: map[string]string{
|
||||||
|
"example.com": "foo",
|
||||||
|
},
|
||||||
|
host: "example.com",
|
||||||
|
expected: http.StatusMisdirectedRequest,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range testCases {
|
||||||
|
test := test
|
||||||
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
next := http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {})
|
||||||
|
|
||||||
|
sniCheck := New(test.tlsOptionsForHost, next)
|
||||||
|
|
||||||
|
req := httptest.NewRequest(http.MethodGet, "https://localhost", nil)
|
||||||
|
if test.host != "" {
|
||||||
|
req.Host = test.host
|
||||||
|
}
|
||||||
|
|
||||||
|
recorder := httptest.NewRecorder()
|
||||||
|
|
||||||
|
sniCheck.ServeHTTP(recorder, req)
|
||||||
|
|
||||||
|
assert.Equal(t, test.expected, recorder.Code)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -43,8 +43,8 @@ func (f *forwarderMiddleware) ServeHTTP(rw http.ResponseWriter, req *http.Reques
|
||||||
span, req, finish := tr.StartSpanf(req, ext.SpanKindRPCClientEnum, "forward", opParts, "/")
|
span, req, finish := tr.StartSpanf(req, ext.SpanKindRPCClientEnum, "forward", opParts, "/")
|
||||||
defer finish()
|
defer finish()
|
||||||
|
|
||||||
span.SetTag("service.name", f.service)
|
span.SetTag("traefik.service.name", f.service)
|
||||||
span.SetTag("router.name", f.router)
|
span.SetTag("traefik.router.name", f.router)
|
||||||
ext.HTTPMethod.Set(span, req.Method)
|
ext.HTTPMethod.Set(span, req.Method)
|
||||||
ext.HTTPUrl.Set(span, req.URL.String())
|
ext.HTTPUrl.Set(span, req.URL.String())
|
||||||
span.SetTag("http.host", req.Host)
|
span.SetTag("http.host", req.Host)
|
||||||
|
|
|
@ -36,12 +36,12 @@ func TestNewForwarder(t *testing.T) {
|
||||||
router: "some-service.domain.tld",
|
router: "some-service.domain.tld",
|
||||||
expected: expected{
|
expected: expected{
|
||||||
Tags: map[string]interface{}{
|
Tags: map[string]interface{}{
|
||||||
"http.host": "www.test.com",
|
"http.host": "www.test.com",
|
||||||
"http.method": "GET",
|
"http.method": "GET",
|
||||||
"http.url": "http://www.test.com/toto",
|
"http.url": "http://www.test.com/toto",
|
||||||
"service.name": "some-service.domain.tld",
|
"traefik.service.name": "some-service.domain.tld",
|
||||||
"router.name": "some-service.domain.tld",
|
"traefik.router.name": "some-service.domain.tld",
|
||||||
"span.kind": ext.SpanKindRPCClientEnum,
|
"span.kind": ext.SpanKindRPCClientEnum,
|
||||||
},
|
},
|
||||||
OperationName: "forward some-service.domain.tld/some-service.domain.tld",
|
OperationName: "forward some-service.domain.tld/some-service.domain.tld",
|
||||||
},
|
},
|
||||||
|
@ -56,12 +56,12 @@ func TestNewForwarder(t *testing.T) {
|
||||||
router: "some-service-100.slug.namespace.environment.domain.tld",
|
router: "some-service-100.slug.namespace.environment.domain.tld",
|
||||||
expected: expected{
|
expected: expected{
|
||||||
Tags: map[string]interface{}{
|
Tags: map[string]interface{}{
|
||||||
"http.host": "www.test.com",
|
"http.host": "www.test.com",
|
||||||
"http.method": "GET",
|
"http.method": "GET",
|
||||||
"http.url": "http://www.test.com/toto",
|
"http.url": "http://www.test.com/toto",
|
||||||
"service.name": "some-service-100.slug.namespace.environment.domain.tld",
|
"traefik.service.name": "some-service-100.slug.namespace.environment.domain.tld",
|
||||||
"router.name": "some-service-100.slug.namespace.environment.domain.tld",
|
"traefik.router.name": "some-service-100.slug.namespace.environment.domain.tld",
|
||||||
"span.kind": ext.SpanKindRPCClientEnum,
|
"span.kind": ext.SpanKindRPCClientEnum,
|
||||||
},
|
},
|
||||||
OperationName: "forward some-service-100.slug.namespace.enviro.../some-service-100.slug.namespace.enviro.../bc4a0d48",
|
OperationName: "forward some-service-100.slug.namespace.enviro.../some-service-100.slug.namespace.enviro.../bc4a0d48",
|
||||||
},
|
},
|
||||||
|
@ -76,12 +76,12 @@ func TestNewForwarder(t *testing.T) {
|
||||||
router: "some-service1.namespace.environment.domain.tld",
|
router: "some-service1.namespace.environment.domain.tld",
|
||||||
expected: expected{
|
expected: expected{
|
||||||
Tags: map[string]interface{}{
|
Tags: map[string]interface{}{
|
||||||
"http.host": "www.test.com",
|
"http.host": "www.test.com",
|
||||||
"http.method": "GET",
|
"http.method": "GET",
|
||||||
"http.url": "http://www.test.com/toto",
|
"http.url": "http://www.test.com/toto",
|
||||||
"service.name": "some-service1.namespace.environment.domain.tld",
|
"traefik.service.name": "some-service1.namespace.environment.domain.tld",
|
||||||
"router.name": "some-service1.namespace.environment.domain.tld",
|
"traefik.router.name": "some-service1.namespace.environment.domain.tld",
|
||||||
"span.kind": ext.SpanKindRPCClientEnum,
|
"span.kind": ext.SpanKindRPCClientEnum,
|
||||||
},
|
},
|
||||||
OperationName: "forward some-service1.namespace.environment.domain.tld/some-service1.namespace.environment.domain.tld",
|
OperationName: "forward some-service1.namespace.environment.domain.tld/some-service1.namespace.environment.domain.tld",
|
||||||
},
|
},
|
||||||
|
@ -96,12 +96,12 @@ func TestNewForwarder(t *testing.T) {
|
||||||
router: "some-service1.backend.namespace.environment.domain.tld",
|
router: "some-service1.backend.namespace.environment.domain.tld",
|
||||||
expected: expected{
|
expected: expected{
|
||||||
Tags: map[string]interface{}{
|
Tags: map[string]interface{}{
|
||||||
"http.host": "www.test.com",
|
"http.host": "www.test.com",
|
||||||
"http.method": "GET",
|
"http.method": "GET",
|
||||||
"http.url": "http://www.test.com/toto",
|
"http.url": "http://www.test.com/toto",
|
||||||
"service.name": "some-service1.frontend.namespace.environment.domain.tld",
|
"traefik.service.name": "some-service1.frontend.namespace.environment.domain.tld",
|
||||||
"router.name": "some-service1.backend.namespace.environment.domain.tld",
|
"traefik.router.name": "some-service1.backend.namespace.environment.domain.tld",
|
||||||
"span.kind": ext.SpanKindRPCClientEnum,
|
"span.kind": ext.SpanKindRPCClientEnum,
|
||||||
},
|
},
|
||||||
OperationName: "forward some-service1.frontend.namespace.envir.../some-service1.backend.namespace.enviro.../fa49dd23",
|
OperationName: "forward some-service1.frontend.namespace.envir.../some-service1.backend.namespace.enviro.../fa49dd23",
|
||||||
},
|
},
|
||||||
|
|
|
@ -9,7 +9,6 @@ import (
|
||||||
"github.com/traefik/traefik/v2/pkg/metrics"
|
"github.com/traefik/traefik/v2/pkg/metrics"
|
||||||
"github.com/traefik/traefik/v2/pkg/middlewares/accesslog"
|
"github.com/traefik/traefik/v2/pkg/middlewares/accesslog"
|
||||||
metricsmiddleware "github.com/traefik/traefik/v2/pkg/middlewares/metrics"
|
metricsmiddleware "github.com/traefik/traefik/v2/pkg/middlewares/metrics"
|
||||||
"github.com/traefik/traefik/v2/pkg/middlewares/requestdecorator"
|
|
||||||
mTracing "github.com/traefik/traefik/v2/pkg/middlewares/tracing"
|
mTracing "github.com/traefik/traefik/v2/pkg/middlewares/tracing"
|
||||||
"github.com/traefik/traefik/v2/pkg/tracing"
|
"github.com/traefik/traefik/v2/pkg/tracing"
|
||||||
"github.com/traefik/traefik/v2/pkg/tracing/jaeger"
|
"github.com/traefik/traefik/v2/pkg/tracing/jaeger"
|
||||||
|
@ -20,7 +19,6 @@ type ChainBuilder struct {
|
||||||
metricsRegistry metrics.Registry
|
metricsRegistry metrics.Registry
|
||||||
accessLoggerMiddleware *accesslog.Handler
|
accessLoggerMiddleware *accesslog.Handler
|
||||||
tracer *tracing.Tracing
|
tracer *tracing.Tracing
|
||||||
requestDecorator *requestdecorator.RequestDecorator
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewChainBuilder Creates a new ChainBuilder.
|
// NewChainBuilder Creates a new ChainBuilder.
|
||||||
|
@ -29,7 +27,6 @@ func NewChainBuilder(staticConfiguration static.Configuration, metricsRegistry m
|
||||||
metricsRegistry: metricsRegistry,
|
metricsRegistry: metricsRegistry,
|
||||||
accessLoggerMiddleware: accessLoggerMiddleware,
|
accessLoggerMiddleware: accessLoggerMiddleware,
|
||||||
tracer: setupTracing(staticConfiguration.Tracing),
|
tracer: setupTracing(staticConfiguration.Tracing),
|
||||||
requestDecorator: requestdecorator.New(staticConfiguration.HostResolver),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,7 +46,7 @@ func (c *ChainBuilder) Build(ctx context.Context, entryPointName string) alice.C
|
||||||
chain = chain.Append(metricsmiddleware.WrapEntryPointHandler(ctx, c.metricsRegistry, entryPointName))
|
chain = chain.Append(metricsmiddleware.WrapEntryPointHandler(ctx, c.metricsRegistry, entryPointName))
|
||||||
}
|
}
|
||||||
|
|
||||||
return chain.Append(requestdecorator.WrapHandler(c.requestDecorator))
|
return chain
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close accessLogger and tracer.
|
// Close accessLogger and tracer.
|
||||||
|
|
|
@ -5,12 +5,11 @@ import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/traefik/traefik/v2/pkg/config/runtime"
|
"github.com/traefik/traefik/v2/pkg/config/runtime"
|
||||||
"github.com/traefik/traefik/v2/pkg/log"
|
"github.com/traefik/traefik/v2/pkg/log"
|
||||||
|
"github.com/traefik/traefik/v2/pkg/middlewares/snicheck"
|
||||||
"github.com/traefik/traefik/v2/pkg/rules"
|
"github.com/traefik/traefik/v2/pkg/rules"
|
||||||
"github.com/traefik/traefik/v2/pkg/server/provider"
|
"github.com/traefik/traefik/v2/pkg/server/provider"
|
||||||
tcpservice "github.com/traefik/traefik/v2/pkg/server/service/tcp"
|
tcpservice "github.com/traefik/traefik/v2/pkg/server/service/tcp"
|
||||||
|
@ -161,38 +160,7 @@ func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sniCheck := http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
sniCheck := snicheck.New(tlsOptionsForHost, handlerHTTPS)
|
||||||
if req.TLS == nil {
|
|
||||||
handlerHTTPS.ServeHTTP(rw, req)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
host, _, err := net.SplitHostPort(req.Host)
|
|
||||||
if err != nil {
|
|
||||||
host = req.Host
|
|
||||||
}
|
|
||||||
|
|
||||||
host = strings.TrimSpace(host)
|
|
||||||
serverName := strings.TrimSpace(req.TLS.ServerName)
|
|
||||||
|
|
||||||
// Domain Fronting
|
|
||||||
if !strings.EqualFold(host, serverName) {
|
|
||||||
tlsOptionSNI := findTLSOptionName(tlsOptionsForHost, serverName)
|
|
||||||
tlsOptionHeader := findTLSOptionName(tlsOptionsForHost, host)
|
|
||||||
|
|
||||||
if tlsOptionHeader != tlsOptionSNI {
|
|
||||||
log.WithoutContext().
|
|
||||||
WithField("host", host).
|
|
||||||
WithField("req.Host", req.Host).
|
|
||||||
WithField("req.TLS.ServerName", req.TLS.ServerName).
|
|
||||||
Debugf("TLS options difference: SNI=%s, Header:%s", tlsOptionSNI, tlsOptionHeader)
|
|
||||||
http.Error(rw, http.StatusText(http.StatusMisdirectedRequest), http.StatusMisdirectedRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
handlerHTTPS.ServeHTTP(rw, req)
|
|
||||||
})
|
|
||||||
|
|
||||||
router.HTTPSHandler(sniCheck, defaultTLSConf)
|
router.HTTPSHandler(sniCheck, defaultTLSConf)
|
||||||
|
|
||||||
|
@ -321,17 +289,3 @@ func (m *Manager) buildTCPHandler(ctx context.Context, router *runtime.TCPRouter
|
||||||
|
|
||||||
return tcp.NewChain().Extend(*mHandler).Then(sHandler)
|
return tcp.NewChain().Extend(*mHandler).Then(sHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
func findTLSOptionName(tlsOptionsForHost map[string]string, host string) string {
|
|
||||||
tlsOptions, ok := tlsOptionsForHost[host]
|
|
||||||
if ok {
|
|
||||||
return tlsOptions
|
|
||||||
}
|
|
||||||
|
|
||||||
tlsOptions, ok = tlsOptionsForHost[strings.ToLower(host)]
|
|
||||||
if ok {
|
|
||||||
return tlsOptions
|
|
||||||
}
|
|
||||||
|
|
||||||
return traefiktls.DefaultTLSConfigName
|
|
||||||
}
|
|
||||||
|
|
|
@ -59,7 +59,6 @@ func TestRuntimeConfiguration(t *testing.T) {
|
||||||
},
|
},
|
||||||
"bar": {
|
"bar": {
|
||||||
TCPRouter: &dynamic.TCPRouter{
|
TCPRouter: &dynamic.TCPRouter{
|
||||||
|
|
||||||
EntryPoints: []string{"web"},
|
EntryPoints: []string{"web"},
|
||||||
Service: "foo-service",
|
Service: "foo-service",
|
||||||
Rule: "HostSNI(`foo.bar`)",
|
Rule: "HostSNI(`foo.bar`)",
|
||||||
|
@ -136,7 +135,6 @@ func TestRuntimeConfiguration(t *testing.T) {
|
||||||
},
|
},
|
||||||
"bar": {
|
"bar": {
|
||||||
Router: &dynamic.Router{
|
Router: &dynamic.Router{
|
||||||
|
|
||||||
EntryPoints: []string{"web"},
|
EntryPoints: []string{"web"},
|
||||||
Service: "foo-service",
|
Service: "foo-service",
|
||||||
Rule: "Host(`bar.foo`) && PathPrefix(`/path`)",
|
Rule: "Host(`bar.foo`) && PathPrefix(`/path`)",
|
||||||
|
@ -240,7 +238,6 @@ func TestRuntimeConfiguration(t *testing.T) {
|
||||||
},
|
},
|
||||||
"bar": {
|
"bar": {
|
||||||
TCPRouter: &dynamic.TCPRouter{
|
TCPRouter: &dynamic.TCPRouter{
|
||||||
|
|
||||||
EntryPoints: []string{"web"},
|
EntryPoints: []string{"web"},
|
||||||
Service: "foo-service",
|
Service: "foo-service",
|
||||||
Rule: "HostSNI(`foo.bar`)",
|
Rule: "HostSNI(`foo.bar`)",
|
||||||
|
@ -340,9 +337,26 @@ func TestRuntimeConfiguration(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDomainFronting(t *testing.T) {
|
func TestDomainFronting(t *testing.T) {
|
||||||
|
tlsOptionsBase := map[string]traefiktls.Options{
|
||||||
|
"default": {
|
||||||
|
MinVersion: "VersionTLS10",
|
||||||
|
},
|
||||||
|
"host1@file": {
|
||||||
|
MinVersion: "VersionTLS12",
|
||||||
|
},
|
||||||
|
"host1@crd": {
|
||||||
|
MinVersion: "VersionTLS12",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
entryPoints := []string{"web"}
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
desc string
|
desc string
|
||||||
routers map[string]*runtime.RouterInfo
|
routers map[string]*runtime.RouterInfo
|
||||||
|
tlsOptions map[string]traefiktls.Options
|
||||||
|
host string
|
||||||
|
ServerName string
|
||||||
expectedStatus int
|
expectedStatus int
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
|
@ -350,7 +364,7 @@ func TestDomainFronting(t *testing.T) {
|
||||||
routers: map[string]*runtime.RouterInfo{
|
routers: map[string]*runtime.RouterInfo{
|
||||||
"router-1@file": {
|
"router-1@file": {
|
||||||
Router: &dynamic.Router{
|
Router: &dynamic.Router{
|
||||||
EntryPoints: []string{"web"},
|
EntryPoints: entryPoints,
|
||||||
Rule: "Host(`host1.local`)",
|
Rule: "Host(`host1.local`)",
|
||||||
TLS: &dynamic.RouterTLSConfig{
|
TLS: &dynamic.RouterTLSConfig{
|
||||||
Options: "host1",
|
Options: "host1",
|
||||||
|
@ -359,12 +373,15 @@ func TestDomainFronting(t *testing.T) {
|
||||||
},
|
},
|
||||||
"router-2@file": {
|
"router-2@file": {
|
||||||
Router: &dynamic.Router{
|
Router: &dynamic.Router{
|
||||||
EntryPoints: []string{"web"},
|
EntryPoints: entryPoints,
|
||||||
Rule: "Host(`host2.local`)",
|
Rule: "Host(`host2.local`)",
|
||||||
TLS: &dynamic.RouterTLSConfig{},
|
TLS: &dynamic.RouterTLSConfig{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
tlsOptions: tlsOptionsBase,
|
||||||
|
host: "host1.local",
|
||||||
|
ServerName: "host2.local",
|
||||||
expectedStatus: http.StatusMisdirectedRequest,
|
expectedStatus: http.StatusMisdirectedRequest,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -372,7 +389,7 @@ func TestDomainFronting(t *testing.T) {
|
||||||
routers: map[string]*runtime.RouterInfo{
|
routers: map[string]*runtime.RouterInfo{
|
||||||
"router-1@file": {
|
"router-1@file": {
|
||||||
Router: &dynamic.Router{
|
Router: &dynamic.Router{
|
||||||
EntryPoints: []string{"web"},
|
EntryPoints: entryPoints,
|
||||||
Rule: "Host(`host1.local`)",
|
Rule: "Host(`host1.local`)",
|
||||||
TLS: &dynamic.RouterTLSConfig{
|
TLS: &dynamic.RouterTLSConfig{
|
||||||
Options: "host1",
|
Options: "host1",
|
||||||
|
@ -381,7 +398,7 @@ func TestDomainFronting(t *testing.T) {
|
||||||
},
|
},
|
||||||
"router-2@file": {
|
"router-2@file": {
|
||||||
Router: &dynamic.Router{
|
Router: &dynamic.Router{
|
||||||
EntryPoints: []string{"web"},
|
EntryPoints: entryPoints,
|
||||||
Rule: "Host(`host2.local`)",
|
Rule: "Host(`host2.local`)",
|
||||||
TLS: &dynamic.RouterTLSConfig{
|
TLS: &dynamic.RouterTLSConfig{
|
||||||
Options: "host1",
|
Options: "host1",
|
||||||
|
@ -389,6 +406,9 @@ func TestDomainFronting(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
tlsOptions: tlsOptionsBase,
|
||||||
|
host: "host1.local",
|
||||||
|
ServerName: "host2.local",
|
||||||
expectedStatus: http.StatusOK,
|
expectedStatus: http.StatusOK,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -396,7 +416,7 @@ func TestDomainFronting(t *testing.T) {
|
||||||
routers: map[string]*runtime.RouterInfo{
|
routers: map[string]*runtime.RouterInfo{
|
||||||
"router-1@file": {
|
"router-1@file": {
|
||||||
Router: &dynamic.Router{
|
Router: &dynamic.Router{
|
||||||
EntryPoints: []string{"web"},
|
EntryPoints: entryPoints,
|
||||||
Rule: "Host(`host1.local`)",
|
Rule: "Host(`host1.local`)",
|
||||||
TLS: &dynamic.RouterTLSConfig{
|
TLS: &dynamic.RouterTLSConfig{
|
||||||
Options: "host1",
|
Options: "host1",
|
||||||
|
@ -405,7 +425,7 @@ func TestDomainFronting(t *testing.T) {
|
||||||
},
|
},
|
||||||
"router-2@file": {
|
"router-2@file": {
|
||||||
Router: &dynamic.Router{
|
Router: &dynamic.Router{
|
||||||
EntryPoints: []string{"web"},
|
EntryPoints: entryPoints,
|
||||||
Rule: "Host(`host1.local`) && PathPrefix(`/foo`)",
|
Rule: "Host(`host1.local`) && PathPrefix(`/foo`)",
|
||||||
TLS: &dynamic.RouterTLSConfig{
|
TLS: &dynamic.RouterTLSConfig{
|
||||||
Options: "default",
|
Options: "default",
|
||||||
|
@ -414,7 +434,7 @@ func TestDomainFronting(t *testing.T) {
|
||||||
},
|
},
|
||||||
"router-3@file": {
|
"router-3@file": {
|
||||||
Router: &dynamic.Router{
|
Router: &dynamic.Router{
|
||||||
EntryPoints: []string{"web"},
|
EntryPoints: entryPoints,
|
||||||
Rule: "Host(`host2.local`)",
|
Rule: "Host(`host2.local`)",
|
||||||
TLS: &dynamic.RouterTLSConfig{
|
TLS: &dynamic.RouterTLSConfig{
|
||||||
Options: "host1",
|
Options: "host1",
|
||||||
|
@ -422,6 +442,9 @@ func TestDomainFronting(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
tlsOptions: tlsOptionsBase,
|
||||||
|
host: "host1.local",
|
||||||
|
ServerName: "host2.local",
|
||||||
expectedStatus: http.StatusMisdirectedRequest,
|
expectedStatus: http.StatusMisdirectedRequest,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -429,7 +452,7 @@ func TestDomainFronting(t *testing.T) {
|
||||||
routers: map[string]*runtime.RouterInfo{
|
routers: map[string]*runtime.RouterInfo{
|
||||||
"router-1@file": {
|
"router-1@file": {
|
||||||
Router: &dynamic.Router{
|
Router: &dynamic.Router{
|
||||||
EntryPoints: []string{"web"},
|
EntryPoints: entryPoints,
|
||||||
Rule: "Host(`host1.local`)",
|
Rule: "Host(`host1.local`)",
|
||||||
TLS: &dynamic.RouterTLSConfig{
|
TLS: &dynamic.RouterTLSConfig{
|
||||||
Options: "host1",
|
Options: "host1",
|
||||||
|
@ -438,7 +461,7 @@ func TestDomainFronting(t *testing.T) {
|
||||||
},
|
},
|
||||||
"router-2@file": {
|
"router-2@file": {
|
||||||
Router: &dynamic.Router{
|
Router: &dynamic.Router{
|
||||||
EntryPoints: []string{"web"},
|
EntryPoints: entryPoints,
|
||||||
Rule: "Host(`host1.local`) && PathPrefix(`/bar`)",
|
Rule: "Host(`host1.local`) && PathPrefix(`/bar`)",
|
||||||
TLS: &dynamic.RouterTLSConfig{
|
TLS: &dynamic.RouterTLSConfig{
|
||||||
Options: "host1",
|
Options: "host1",
|
||||||
|
@ -447,7 +470,7 @@ func TestDomainFronting(t *testing.T) {
|
||||||
},
|
},
|
||||||
"router-3@file": {
|
"router-3@file": {
|
||||||
Router: &dynamic.Router{
|
Router: &dynamic.Router{
|
||||||
EntryPoints: []string{"web"},
|
EntryPoints: entryPoints,
|
||||||
Rule: "Host(`host2.local`)",
|
Rule: "Host(`host2.local`)",
|
||||||
TLS: &dynamic.RouterTLSConfig{
|
TLS: &dynamic.RouterTLSConfig{
|
||||||
Options: "host1",
|
Options: "host1",
|
||||||
|
@ -455,6 +478,9 @@ func TestDomainFronting(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
tlsOptions: tlsOptionsBase,
|
||||||
|
host: "host1.local",
|
||||||
|
ServerName: "host2.local",
|
||||||
expectedStatus: http.StatusOK,
|
expectedStatus: http.StatusOK,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -462,7 +488,7 @@ func TestDomainFronting(t *testing.T) {
|
||||||
routers: map[string]*runtime.RouterInfo{
|
routers: map[string]*runtime.RouterInfo{
|
||||||
"router-1@file": {
|
"router-1@file": {
|
||||||
Router: &dynamic.Router{
|
Router: &dynamic.Router{
|
||||||
EntryPoints: []string{"web"},
|
EntryPoints: entryPoints,
|
||||||
Rule: "Host(`host1.local`)",
|
Rule: "Host(`host1.local`)",
|
||||||
TLS: &dynamic.RouterTLSConfig{
|
TLS: &dynamic.RouterTLSConfig{
|
||||||
Options: "host1",
|
Options: "host1",
|
||||||
|
@ -471,7 +497,7 @@ func TestDomainFronting(t *testing.T) {
|
||||||
},
|
},
|
||||||
"router-2@crd": {
|
"router-2@crd": {
|
||||||
Router: &dynamic.Router{
|
Router: &dynamic.Router{
|
||||||
EntryPoints: []string{"web"},
|
EntryPoints: entryPoints,
|
||||||
Rule: "Host(`host2.local`)",
|
Rule: "Host(`host2.local`)",
|
||||||
TLS: &dynamic.RouterTLSConfig{
|
TLS: &dynamic.RouterTLSConfig{
|
||||||
Options: "host1",
|
Options: "host1",
|
||||||
|
@ -479,6 +505,9 @@ func TestDomainFronting(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
tlsOptions: tlsOptionsBase,
|
||||||
|
host: "host1.local",
|
||||||
|
ServerName: "host2.local",
|
||||||
expectedStatus: http.StatusMisdirectedRequest,
|
expectedStatus: http.StatusMisdirectedRequest,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -486,7 +515,7 @@ func TestDomainFronting(t *testing.T) {
|
||||||
routers: map[string]*runtime.RouterInfo{
|
routers: map[string]*runtime.RouterInfo{
|
||||||
"router-1@file": {
|
"router-1@file": {
|
||||||
Router: &dynamic.Router{
|
Router: &dynamic.Router{
|
||||||
EntryPoints: []string{"web"},
|
EntryPoints: entryPoints,
|
||||||
Rule: "Host(`host1.local`)",
|
Rule: "Host(`host1.local`)",
|
||||||
TLS: &dynamic.RouterTLSConfig{
|
TLS: &dynamic.RouterTLSConfig{
|
||||||
Options: "host1@crd",
|
Options: "host1@crd",
|
||||||
|
@ -495,7 +524,7 @@ func TestDomainFronting(t *testing.T) {
|
||||||
},
|
},
|
||||||
"router-2@crd": {
|
"router-2@crd": {
|
||||||
Router: &dynamic.Router{
|
Router: &dynamic.Router{
|
||||||
EntryPoints: []string{"web"},
|
EntryPoints: entryPoints,
|
||||||
Rule: "Host(`host2.local`)",
|
Rule: "Host(`host2.local`)",
|
||||||
TLS: &dynamic.RouterTLSConfig{
|
TLS: &dynamic.RouterTLSConfig{
|
||||||
Options: "host1@crd",
|
Options: "host1@crd",
|
||||||
|
@ -503,25 +532,63 @@ func TestDomainFronting(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
tlsOptions: tlsOptionsBase,
|
||||||
|
host: "host1.local",
|
||||||
|
ServerName: "host2.local",
|
||||||
expectedStatus: http.StatusOK,
|
expectedStatus: http.StatusOK,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
desc: "Request is misdirected when server name is empty and the host name is an FQDN, but router's rule is not",
|
||||||
|
routers: map[string]*runtime.RouterInfo{
|
||||||
|
"router-1@file": {
|
||||||
|
Router: &dynamic.Router{
|
||||||
|
EntryPoints: entryPoints,
|
||||||
|
Rule: "Host(`host1.local`)",
|
||||||
|
TLS: &dynamic.RouterTLSConfig{
|
||||||
|
Options: "host1@file",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
tlsOptions: map[string]traefiktls.Options{
|
||||||
|
"default": {
|
||||||
|
MinVersion: "VersionTLS13",
|
||||||
|
},
|
||||||
|
"host1@file": {
|
||||||
|
MinVersion: "VersionTLS12",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
host: "host1.local.",
|
||||||
|
expectedStatus: http.StatusMisdirectedRequest,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Request is misdirected when server name is empty and the host name is not FQDN, but router's rule is",
|
||||||
|
routers: map[string]*runtime.RouterInfo{
|
||||||
|
"router-1@file": {
|
||||||
|
Router: &dynamic.Router{
|
||||||
|
EntryPoints: entryPoints,
|
||||||
|
Rule: "Host(`host1.local.`)",
|
||||||
|
TLS: &dynamic.RouterTLSConfig{
|
||||||
|
Options: "host1@file",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
tlsOptions: map[string]traefiktls.Options{
|
||||||
|
"default": {
|
||||||
|
MinVersion: "VersionTLS13",
|
||||||
|
},
|
||||||
|
"host1@file": {
|
||||||
|
MinVersion: "VersionTLS12",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
host: "host1.local",
|
||||||
|
expectedStatus: http.StatusMisdirectedRequest,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
t.Run(test.desc, func(t *testing.T) {
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
entryPoints := []string{"web"}
|
|
||||||
tlsOptions := map[string]traefiktls.Options{
|
|
||||||
"default": {
|
|
||||||
MinVersion: "VersionTLS10",
|
|
||||||
},
|
|
||||||
"host1@file": {
|
|
||||||
MinVersion: "VersionTLS12",
|
|
||||||
},
|
|
||||||
"host1@crd": {
|
|
||||||
MinVersion: "VersionTLS12",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
conf := &runtime.Configuration{
|
conf := &runtime.Configuration{
|
||||||
Routers: test.routers,
|
Routers: test.routers,
|
||||||
}
|
}
|
||||||
|
@ -529,7 +596,7 @@ func TestDomainFronting(t *testing.T) {
|
||||||
serviceManager := tcp.NewManager(conf)
|
serviceManager := tcp.NewManager(conf)
|
||||||
|
|
||||||
tlsManager := traefiktls.NewManager()
|
tlsManager := traefiktls.NewManager()
|
||||||
tlsManager.UpdateConfigs(context.Background(), map[string]traefiktls.Store{}, tlsOptions, []*traefiktls.CertAndStores{})
|
tlsManager.UpdateConfigs(context.Background(), map[string]traefiktls.Store{}, test.tlsOptions, []*traefiktls.CertAndStores{})
|
||||||
|
|
||||||
httpsHandler := map[string]http.Handler{
|
httpsHandler := map[string]http.Handler{
|
||||||
"web": http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {}),
|
"web": http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {}),
|
||||||
|
@ -545,9 +612,9 @@ func TestDomainFronting(t *testing.T) {
|
||||||
require.True(t, ok)
|
require.True(t, ok)
|
||||||
|
|
||||||
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
||||||
req.Host = "host1.local"
|
req.Host = test.host
|
||||||
req.TLS = &tls.ConnectionState{
|
req.TLS = &tls.ConnectionState{
|
||||||
ServerName: "host2.local",
|
ServerName: test.ServerName,
|
||||||
}
|
}
|
||||||
|
|
||||||
rw := httptest.NewRecorder()
|
rw := httptest.NewRecorder()
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/containous/alice"
|
||||||
"github.com/pires/go-proxyproto"
|
"github.com/pires/go-proxyproto"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/traefik/traefik/v2/pkg/config/static"
|
"github.com/traefik/traefik/v2/pkg/config/static"
|
||||||
|
@ -18,9 +19,11 @@ import (
|
||||||
"github.com/traefik/traefik/v2/pkg/log"
|
"github.com/traefik/traefik/v2/pkg/log"
|
||||||
"github.com/traefik/traefik/v2/pkg/middlewares"
|
"github.com/traefik/traefik/v2/pkg/middlewares"
|
||||||
"github.com/traefik/traefik/v2/pkg/middlewares/forwardedheaders"
|
"github.com/traefik/traefik/v2/pkg/middlewares/forwardedheaders"
|
||||||
|
"github.com/traefik/traefik/v2/pkg/middlewares/requestdecorator"
|
||||||
"github.com/traefik/traefik/v2/pkg/safe"
|
"github.com/traefik/traefik/v2/pkg/safe"
|
||||||
"github.com/traefik/traefik/v2/pkg/server/router"
|
"github.com/traefik/traefik/v2/pkg/server/router"
|
||||||
"github.com/traefik/traefik/v2/pkg/tcp"
|
"github.com/traefik/traefik/v2/pkg/tcp"
|
||||||
|
"github.com/traefik/traefik/v2/pkg/types"
|
||||||
"golang.org/x/net/http2"
|
"golang.org/x/net/http2"
|
||||||
"golang.org/x/net/http2/h2c"
|
"golang.org/x/net/http2/h2c"
|
||||||
)
|
)
|
||||||
|
@ -60,7 +63,7 @@ func (h *httpForwarder) Accept() (net.Conn, error) {
|
||||||
type TCPEntryPoints map[string]*TCPEntryPoint
|
type TCPEntryPoints map[string]*TCPEntryPoint
|
||||||
|
|
||||||
// NewTCPEntryPoints creates a new TCPEntryPoints.
|
// NewTCPEntryPoints creates a new TCPEntryPoints.
|
||||||
func NewTCPEntryPoints(entryPointsConfig static.EntryPoints) (TCPEntryPoints, error) {
|
func NewTCPEntryPoints(entryPointsConfig static.EntryPoints, hostResolverConfig *types.HostResolverConfig) (TCPEntryPoints, error) {
|
||||||
serverEntryPointsTCP := make(TCPEntryPoints)
|
serverEntryPointsTCP := make(TCPEntryPoints)
|
||||||
for entryPointName, config := range entryPointsConfig {
|
for entryPointName, config := range entryPointsConfig {
|
||||||
protocol, err := config.GetProtocol()
|
protocol, err := config.GetProtocol()
|
||||||
|
@ -74,7 +77,7 @@ func NewTCPEntryPoints(entryPointsConfig static.EntryPoints) (TCPEntryPoints, er
|
||||||
|
|
||||||
ctx := log.With(context.Background(), log.Str(log.EntryPointName, entryPointName))
|
ctx := log.With(context.Background(), log.Str(log.EntryPointName, entryPointName))
|
||||||
|
|
||||||
serverEntryPointsTCP[entryPointName], err = NewTCPEntryPoint(ctx, config)
|
serverEntryPointsTCP[entryPointName], err = NewTCPEntryPoint(ctx, config, hostResolverConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error while building entryPoint %s: %w", entryPointName, err)
|
return nil, fmt.Errorf("error while building entryPoint %s: %w", entryPointName, err)
|
||||||
}
|
}
|
||||||
|
@ -130,7 +133,7 @@ type TCPEntryPoint struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewTCPEntryPoint creates a new TCPEntryPoint.
|
// NewTCPEntryPoint creates a new TCPEntryPoint.
|
||||||
func NewTCPEntryPoint(ctx context.Context, configuration *static.EntryPoint) (*TCPEntryPoint, error) {
|
func NewTCPEntryPoint(ctx context.Context, configuration *static.EntryPoint, hostResolverConfig *types.HostResolverConfig) (*TCPEntryPoint, error) {
|
||||||
tracker := newConnectionTracker()
|
tracker := newConnectionTracker()
|
||||||
|
|
||||||
listener, err := buildListener(ctx, configuration)
|
listener, err := buildListener(ctx, configuration)
|
||||||
|
@ -140,14 +143,16 @@ func NewTCPEntryPoint(ctx context.Context, configuration *static.EntryPoint) (*T
|
||||||
|
|
||||||
rt := &tcp.Router{}
|
rt := &tcp.Router{}
|
||||||
|
|
||||||
httpServer, err := createHTTPServer(ctx, listener, configuration, true)
|
reqDecorator := requestdecorator.New(hostResolverConfig)
|
||||||
|
|
||||||
|
httpServer, err := createHTTPServer(ctx, listener, configuration, true, reqDecorator)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error preparing httpServer: %w", err)
|
return nil, fmt.Errorf("error preparing httpServer: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
rt.HTTPForwarder(httpServer.Forwarder)
|
rt.HTTPForwarder(httpServer.Forwarder)
|
||||||
|
|
||||||
httpsServer, err := createHTTPServer(ctx, listener, configuration, false)
|
httpsServer, err := createHTTPServer(ctx, listener, configuration, false, reqDecorator)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error preparing httpsServer: %w", err)
|
return nil, fmt.Errorf("error preparing httpsServer: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -500,16 +505,19 @@ type httpServer struct {
|
||||||
Switcher *middlewares.HTTPHandlerSwitcher
|
Switcher *middlewares.HTTPHandlerSwitcher
|
||||||
}
|
}
|
||||||
|
|
||||||
func createHTTPServer(ctx context.Context, ln net.Listener, configuration *static.EntryPoint, withH2c bool) (*httpServer, error) {
|
func createHTTPServer(ctx context.Context, ln net.Listener, configuration *static.EntryPoint, withH2c bool, reqDecorator *requestdecorator.RequestDecorator) (*httpServer, error) {
|
||||||
httpSwitcher := middlewares.NewHandlerSwitcher(router.BuildDefaultHTTPRouter())
|
httpSwitcher := middlewares.NewHandlerSwitcher(router.BuildDefaultHTTPRouter())
|
||||||
|
|
||||||
|
next, err := alice.New(requestdecorator.WrapHandler(reqDecorator)).Then(httpSwitcher)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
var handler http.Handler
|
var handler http.Handler
|
||||||
var err error
|
|
||||||
handler, err = forwardedheaders.NewXForwarded(
|
handler, err = forwardedheaders.NewXForwarded(
|
||||||
configuration.ForwardedHeaders.Insecure,
|
configuration.ForwardedHeaders.Insecure,
|
||||||
configuration.ForwardedHeaders.TrustedIPs,
|
configuration.ForwardedHeaders.TrustedIPs,
|
||||||
httpSwitcher)
|
next)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,38 +17,57 @@ import (
|
||||||
// LocalhostCert is a PEM-encoded TLS cert with SAN IPs
|
// LocalhostCert is a PEM-encoded TLS cert with SAN IPs
|
||||||
// "127.0.0.1" and "[::1]", expiring at Jan 29 16:00:00 2084 GMT.
|
// "127.0.0.1" and "[::1]", expiring at Jan 29 16:00:00 2084 GMT.
|
||||||
// generated from src/crypto/tls:
|
// generated from src/crypto/tls:
|
||||||
// go run generate_cert.go --rsa-bits 1024 --host 127.0.0.1,::1,example.com --ca --start-date "Jan 1 00:00:00 1970" --duration=1000000h
|
// go run generate_cert.go --rsa-bits 2048 --host 127.0.0.1,::1,example.com --ca --start-date "Jan 1 00:00:00 1970" --duration=1000000h
|
||||||
var (
|
var (
|
||||||
localhostCert = traefiktls.FileOrContent(`-----BEGIN CERTIFICATE-----
|
localhostCert = traefiktls.FileOrContent(`-----BEGIN CERTIFICATE-----
|
||||||
MIICEzCCAXygAwIBAgIQMIMChMLGrR+QvmQvpwAU6zANBgkqhkiG9w0BAQsFADAS
|
MIIDOTCCAiGgAwIBAgIQSRJrEpBGFc7tNb1fb5pKFzANBgkqhkiG9w0BAQsFADAS
|
||||||
MRAwDgYDVQQKEwdBY21lIENvMCAXDTcwMDEwMTAwMDAwMFoYDzIwODQwMTI5MTYw
|
MRAwDgYDVQQKEwdBY21lIENvMCAXDTcwMDEwMTAwMDAwMFoYDzIwODQwMTI5MTYw
|
||||||
MDAwWjASMRAwDgYDVQQKEwdBY21lIENvMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCB
|
MDAwWjASMRAwDgYDVQQKEwdBY21lIENvMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
|
||||||
iQKBgQDuLnQAI3mDgey3VBzWnB2L39JUU4txjeVE6myuDqkM/uGlfjb9SjY1bIw4
|
MIIBCgKCAQEA6Gba5tHV1dAKouAaXO3/ebDUU4rvwCUg/CNaJ2PT5xLD4N1Vcb8r
|
||||||
iA5sBBZzHi3z0h1YV8QPuxEbi4nW91IJm2gsvvZhIrCHS3l6afab4pZBl2+XsDul
|
bFSW2HXKq+MPfVdwIKR/1DczEoAGf/JWQTW7EgzlXrCd3rlajEX2D73faWJekD0U
|
||||||
rKBxKKtD1rGxlG4LjncdabFn9gvLZad2bSysqz/qTAUStTvqJQIDAQABo2gwZjAO
|
aUgz5vtrTXZ90BQL7WvRICd7FlEZ6FPOcPlumiyNmzUqtwGhO+9ad1W5BqJaRI6P
|
||||||
BgNVHQ8BAf8EBAMCAqQwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDwYDVR0TAQH/BAUw
|
YfouNkwR6Na4TzSj5BrqUfP0FwDizKSJ0XXmh8g8G9mtwxOSN3Ru1QFc61Xyeluk
|
||||||
AwEB/zAuBgNVHREEJzAlggtleGFtcGxlLmNvbYcEfwAAAYcQAAAAAAAAAAAAAAAA
|
POGKBV/q6RBNklTNe0gI8usUMlYyoC7ytppNMW7X2vodAelSu25jgx2anj9fDVZu
|
||||||
AAAAATANBgkqhkiG9w0BAQsFAAOBgQCEcetwO59EWk7WiJsG4x8SY+UIAA+flUI9
|
h7AXF5+4nJS4AAt0n1lNY7nGSsdZas8PbQIDAQABo4GIMIGFMA4GA1UdDwEB/wQE
|
||||||
tyC4lNhbcF2Idq9greZwbYCqTTTr2XiRNSMLCOjKyI7ukPoPjo16ocHj+P3vZGfs
|
AwICpDATBgNVHSUEDDAKBggrBgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud
|
||||||
h1fIw3cSS2OolhloGw/XM6RWPWtPAlGykKLciQrBru5NAPvCMsb/I1DAceTiotQM
|
DgQWBBStsdjh3/JCXXYlQryOrL4Sh7BW5TAuBgNVHREEJzAlggtleGFtcGxlLmNv
|
||||||
fblo6RBxUQ==
|
bYcEfwAAAYcQAAAAAAAAAAAAAAAAAAAAATANBgkqhkiG9w0BAQsFAAOCAQEAxWGI
|
||||||
|
5NhpF3nwwy/4yB4i/CwwSpLrWUa70NyhvprUBC50PxiXav1TeDzwzLx/o5HyNwsv
|
||||||
|
cxv3HdkLW59i/0SlJSrNnWdfZ19oTcS+6PtLoVyISgtyN6DpkKpdG1cOkW3Cy2P2
|
||||||
|
+tK/tKHRP1Y/Ra0RiDpOAmqn0gCOFGz8+lqDIor/T7MTpibL3IxqWfPrvfVRHL3B
|
||||||
|
grw/ZQTTIVjjh4JBSW3WyWgNo/ikC1lrVxzl4iPUGptxT36Cr7Zk2Bsg0XqwbOvK
|
||||||
|
5d+NTDREkSnUbie4GeutujmX3Dsx88UiV6UY/4lHJa6I5leHUNOHahRbpbWeOfs/
|
||||||
|
WkBKOclmOV2xlTVuPw==
|
||||||
-----END CERTIFICATE-----`)
|
-----END CERTIFICATE-----`)
|
||||||
|
|
||||||
// LocalhostKey is the private key for localhostCert.
|
// LocalhostKey is the private key for localhostCert.
|
||||||
localhostKey = traefiktls.FileOrContent(`-----BEGIN RSA PRIVATE KEY-----
|
localhostKey = traefiktls.FileOrContent(`-----BEGIN RSA PRIVATE KEY-----
|
||||||
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
|
||||||
|
ZboOWVe3icTy64BT3OQhmg==
|
||||||
-----END RSA PRIVATE KEY-----`)
|
-----END RSA PRIVATE KEY-----`)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -72,7 +91,7 @@ func TestHTTP3AdvertisedPort(t *testing.T) {
|
||||||
HTTP3: &static.HTTP3Config{
|
HTTP3: &static.HTTP3Config{
|
||||||
AdvertisedPort: 8080,
|
AdvertisedPort: 8080,
|
||||||
},
|
},
|
||||||
})
|
}, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
router := &tcp.Router{}
|
router := &tcp.Router{}
|
||||||
|
|
|
@ -79,7 +79,7 @@ func testShutdown(t *testing.T, router *tcp.Router) {
|
||||||
Address: "127.0.0.1:0",
|
Address: "127.0.0.1:0",
|
||||||
Transport: epConfig,
|
Transport: epConfig,
|
||||||
ForwardedHeaders: &static.ForwardedHeaders{},
|
ForwardedHeaders: &static.ForwardedHeaders{},
|
||||||
})
|
}, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
conn, err := startEntrypoint(entryPoint, router)
|
conn, err := startEntrypoint(entryPoint, router)
|
||||||
|
@ -162,7 +162,7 @@ func TestReadTimeoutWithoutFirstByte(t *testing.T) {
|
||||||
Address: ":0",
|
Address: ":0",
|
||||||
Transport: epConfig,
|
Transport: epConfig,
|
||||||
ForwardedHeaders: &static.ForwardedHeaders{},
|
ForwardedHeaders: &static.ForwardedHeaders{},
|
||||||
})
|
}, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
router := &tcp.Router{}
|
router := &tcp.Router{}
|
||||||
|
@ -198,7 +198,7 @@ func TestReadTimeoutWithFirstByte(t *testing.T) {
|
||||||
Address: ":0",
|
Address: ":0",
|
||||||
Transport: epConfig,
|
Transport: epConfig,
|
||||||
ForwardedHeaders: &static.ForwardedHeaders{},
|
ForwardedHeaders: &static.ForwardedHeaders{},
|
||||||
})
|
}, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
router := &tcp.Router{}
|
router := &tcp.Router{}
|
||||||
|
|
|
@ -17,35 +17,54 @@ import (
|
||||||
// go run generate_cert.go --rsa-bits 1024 --host 127.0.0.1,::1,example.com --ca --start-date "Jan 1 00:00:00 1970" --duration=1000000h
|
// go run generate_cert.go --rsa-bits 1024 --host 127.0.0.1,::1,example.com --ca --start-date "Jan 1 00:00:00 1970" --duration=1000000h
|
||||||
var (
|
var (
|
||||||
localhostCert = FileOrContent(`-----BEGIN CERTIFICATE-----
|
localhostCert = FileOrContent(`-----BEGIN CERTIFICATE-----
|
||||||
MIICEzCCAXygAwIBAgIQMIMChMLGrR+QvmQvpwAU6zANBgkqhkiG9w0BAQsFADAS
|
MIIDOTCCAiGgAwIBAgIQSRJrEpBGFc7tNb1fb5pKFzANBgkqhkiG9w0BAQsFADAS
|
||||||
MRAwDgYDVQQKEwdBY21lIENvMCAXDTcwMDEwMTAwMDAwMFoYDzIwODQwMTI5MTYw
|
MRAwDgYDVQQKEwdBY21lIENvMCAXDTcwMDEwMTAwMDAwMFoYDzIwODQwMTI5MTYw
|
||||||
MDAwWjASMRAwDgYDVQQKEwdBY21lIENvMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCB
|
MDAwWjASMRAwDgYDVQQKEwdBY21lIENvMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
|
||||||
iQKBgQDuLnQAI3mDgey3VBzWnB2L39JUU4txjeVE6myuDqkM/uGlfjb9SjY1bIw4
|
MIIBCgKCAQEA6Gba5tHV1dAKouAaXO3/ebDUU4rvwCUg/CNaJ2PT5xLD4N1Vcb8r
|
||||||
iA5sBBZzHi3z0h1YV8QPuxEbi4nW91IJm2gsvvZhIrCHS3l6afab4pZBl2+XsDul
|
bFSW2HXKq+MPfVdwIKR/1DczEoAGf/JWQTW7EgzlXrCd3rlajEX2D73faWJekD0U
|
||||||
rKBxKKtD1rGxlG4LjncdabFn9gvLZad2bSysqz/qTAUStTvqJQIDAQABo2gwZjAO
|
aUgz5vtrTXZ90BQL7WvRICd7FlEZ6FPOcPlumiyNmzUqtwGhO+9ad1W5BqJaRI6P
|
||||||
BgNVHQ8BAf8EBAMCAqQwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDwYDVR0TAQH/BAUw
|
YfouNkwR6Na4TzSj5BrqUfP0FwDizKSJ0XXmh8g8G9mtwxOSN3Ru1QFc61Xyeluk
|
||||||
AwEB/zAuBgNVHREEJzAlggtleGFtcGxlLmNvbYcEfwAAAYcQAAAAAAAAAAAAAAAA
|
POGKBV/q6RBNklTNe0gI8usUMlYyoC7ytppNMW7X2vodAelSu25jgx2anj9fDVZu
|
||||||
AAAAATANBgkqhkiG9w0BAQsFAAOBgQCEcetwO59EWk7WiJsG4x8SY+UIAA+flUI9
|
h7AXF5+4nJS4AAt0n1lNY7nGSsdZas8PbQIDAQABo4GIMIGFMA4GA1UdDwEB/wQE
|
||||||
tyC4lNhbcF2Idq9greZwbYCqTTTr2XiRNSMLCOjKyI7ukPoPjo16ocHj+P3vZGfs
|
AwICpDATBgNVHSUEDDAKBggrBgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud
|
||||||
h1fIw3cSS2OolhloGw/XM6RWPWtPAlGykKLciQrBru5NAPvCMsb/I1DAceTiotQM
|
DgQWBBStsdjh3/JCXXYlQryOrL4Sh7BW5TAuBgNVHREEJzAlggtleGFtcGxlLmNv
|
||||||
fblo6RBxUQ==
|
bYcEfwAAAYcQAAAAAAAAAAAAAAAAAAAAATANBgkqhkiG9w0BAQsFAAOCAQEAxWGI
|
||||||
|
5NhpF3nwwy/4yB4i/CwwSpLrWUa70NyhvprUBC50PxiXav1TeDzwzLx/o5HyNwsv
|
||||||
|
cxv3HdkLW59i/0SlJSrNnWdfZ19oTcS+6PtLoVyISgtyN6DpkKpdG1cOkW3Cy2P2
|
||||||
|
+tK/tKHRP1Y/Ra0RiDpOAmqn0gCOFGz8+lqDIor/T7MTpibL3IxqWfPrvfVRHL3B
|
||||||
|
grw/ZQTTIVjjh4JBSW3WyWgNo/ikC1lrVxzl4iPUGptxT36Cr7Zk2Bsg0XqwbOvK
|
||||||
|
5d+NTDREkSnUbie4GeutujmX3Dsx88UiV6UY/4lHJa6I5leHUNOHahRbpbWeOfs/
|
||||||
|
WkBKOclmOV2xlTVuPw==
|
||||||
-----END CERTIFICATE-----`)
|
-----END CERTIFICATE-----`)
|
||||||
|
|
||||||
// LocalhostKey is the private key for localhostCert.
|
// LocalhostKey is the private key for localhostCert.
|
||||||
localhostKey = FileOrContent(`-----BEGIN RSA PRIVATE KEY-----
|
localhostKey = FileOrContent(`-----BEGIN RSA PRIVATE KEY-----
|
||||||
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
|
||||||
|
ZboOWVe3icTy64BT3OQhmg==
|
||||||
-----END RSA PRIVATE KEY-----`)
|
-----END RSA PRIVATE KEY-----`)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -4,11 +4,11 @@ RepositoryName = "traefik"
|
||||||
OutputType = "file"
|
OutputType = "file"
|
||||||
FileName = "traefik_changelog.md"
|
FileName = "traefik_changelog.md"
|
||||||
|
|
||||||
# example new bugfix v2.5.7
|
# example new bugfix v2.6.1
|
||||||
CurrentRef = "v2.5"
|
CurrentRef = "v2.6"
|
||||||
PreviousRef = "v2.5.6"
|
PreviousRef = "v2.6.0"
|
||||||
BaseBranch = "v2.5"
|
BaseBranch = "v2.6"
|
||||||
FutureCurrentRefName = "v2.5.7"
|
FutureCurrentRefName = "v2.6.1"
|
||||||
|
|
||||||
ThresholdPreviousRef = 10
|
ThresholdPreviousRef = 10
|
||||||
ThresholdCurrentRef = 10
|
ThresholdCurrentRef = 10
|
||||||
|
|
Loading…
Reference in a new issue