mirror of
https://github.com/traefik/traefik
synced 2026-02-03 08:50:32 +00:00
Add encodedCharacters middleware
This commit is contained in:
@@ -1105,6 +1105,39 @@ spec:
|
||||
containing user credentials.
|
||||
type: string
|
||||
type: object
|
||||
encodedCharacters:
|
||||
description: EncodedCharacters configures which encoded characters
|
||||
are allowed in the request path.
|
||||
properties:
|
||||
allowEncodedBackSlash:
|
||||
description: AllowEncodedBackSlash defines whether requests with
|
||||
encoded back slash characters in the path are allowed.
|
||||
type: boolean
|
||||
allowEncodedHash:
|
||||
description: AllowEncodedHash defines whether requests with encoded
|
||||
hash characters in the path are allowed.
|
||||
type: boolean
|
||||
allowEncodedNullCharacter:
|
||||
description: AllowEncodedNullCharacter defines whether requests
|
||||
with encoded null characters in the path are allowed.
|
||||
type: boolean
|
||||
allowEncodedPercent:
|
||||
description: AllowEncodedPercent defines whether requests with
|
||||
encoded percent characters in the path are allowed.
|
||||
type: boolean
|
||||
allowEncodedQuestionMark:
|
||||
description: AllowEncodedQuestionMark defines whether requests
|
||||
with encoded question mark characters in the path are allowed.
|
||||
type: boolean
|
||||
allowEncodedSemicolon:
|
||||
description: AllowEncodedSemicolon defines whether requests with
|
||||
encoded semicolon characters in the path are allowed.
|
||||
type: boolean
|
||||
allowEncodedSlash:
|
||||
description: AllowEncodedSlash defines whether requests with encoded
|
||||
slash characters in the path are allowed.
|
||||
type: boolean
|
||||
type: object
|
||||
errors:
|
||||
description: |-
|
||||
ErrorPage holds the custom error middleware configuration.
|
||||
|
||||
@@ -260,6 +260,39 @@ spec:
|
||||
containing user credentials.
|
||||
type: string
|
||||
type: object
|
||||
encodedCharacters:
|
||||
description: EncodedCharacters configures which encoded characters
|
||||
are allowed in the request path.
|
||||
properties:
|
||||
allowEncodedBackSlash:
|
||||
description: AllowEncodedBackSlash defines whether requests with
|
||||
encoded back slash characters in the path are allowed.
|
||||
type: boolean
|
||||
allowEncodedHash:
|
||||
description: AllowEncodedHash defines whether requests with encoded
|
||||
hash characters in the path are allowed.
|
||||
type: boolean
|
||||
allowEncodedNullCharacter:
|
||||
description: AllowEncodedNullCharacter defines whether requests
|
||||
with encoded null characters in the path are allowed.
|
||||
type: boolean
|
||||
allowEncodedPercent:
|
||||
description: AllowEncodedPercent defines whether requests with
|
||||
encoded percent characters in the path are allowed.
|
||||
type: boolean
|
||||
allowEncodedQuestionMark:
|
||||
description: AllowEncodedQuestionMark defines whether requests
|
||||
with encoded question mark characters in the path are allowed.
|
||||
type: boolean
|
||||
allowEncodedSemicolon:
|
||||
description: AllowEncodedSemicolon defines whether requests with
|
||||
encoded semicolon characters in the path are allowed.
|
||||
type: boolean
|
||||
allowEncodedSlash:
|
||||
description: AllowEncodedSlash defines whether requests with encoded
|
||||
slash characters in the path are allowed.
|
||||
type: boolean
|
||||
type: object
|
||||
errors:
|
||||
description: |-
|
||||
ErrorPage holds the custom error middleware configuration.
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
---
|
||||
title: "Traefik EncodedCharacters Documentation"
|
||||
description: "In Traefik Proxy, the EncodedCharacters middleware controls which ambiguous reserved encoded characters are allowed in the request path. Read the technical documentation."
|
||||
---
|
||||
|
||||
The EncodedCharacters middleware controls which ambiguous reserved encoded characters are allowed in the request path.
|
||||
|
||||
When you use this middleware, by default, potentially dangerous encoded characters are rejected for security enhancement.
|
||||
|
||||
## Configuration Examples
|
||||
|
||||
```yaml tab="Docker & Swarm"
|
||||
# Allow encoded slash in the request path.
|
||||
labels:
|
||||
- "traefik.http.middlewares.test-encodedchars.encodedcharacters.allowencodedslash=true"
|
||||
```
|
||||
|
||||
```yaml tab="Kubernetes"
|
||||
# Allow encoded slash in the request path.
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: Middleware
|
||||
metadata:
|
||||
name: test-encodedchars
|
||||
spec:
|
||||
encodedCharacters:
|
||||
allowEncodedSlash: true
|
||||
```
|
||||
|
||||
```yaml tab="Consul Catalog"
|
||||
# Allow encoded slash in the request path.
|
||||
- "traefik.http.middlewares.test-encodedchars.encodedcharacters.allowencodedslash=true"
|
||||
```
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
# Allow encoded slash in the request path.
|
||||
http:
|
||||
middlewares:
|
||||
test-encodedchars:
|
||||
encodedCharacters:
|
||||
allowEncodedSlash: true
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
# Allow encoded slash in the request path.
|
||||
[http.middlewares]
|
||||
[http.middlewares.test-encodedchars.encodedCharacters]
|
||||
allowEncodedSlash = true
|
||||
```
|
||||
|
||||
## Configuration Options
|
||||
|
||||
When you are configuring these options, check if your backend is fully compliant with [RFC 3986](https://datatracker.ietf.org/doc/html/rfc3986).
|
||||
This helps avoid split-view situation, where Traefik and your backend interpret the same URL differently.
|
||||
|
||||
| Field | Description | Default | Required |
|
||||
|-------------------------|--------------------------------------------------------------------|---------| -------- |
|
||||
| <a id="opt-allowEncodedSlash" href="#opt-allowEncodedSlash" title="#opt-allowEncodedSlash">`allowEncodedSlash`</a> | Allow encoded slash (`%2F` and `%2f`) in the request path. | `false` | No |
|
||||
| <a id="opt-allowEncodedBackSlash" href="#opt-allowEncodedBackSlash" title="#opt-allowEncodedBackSlash">`allowEncodedBackSlash`</a> | Allow encoded backslash (`%5C` and `%5c`) in the request path. | `false` | No |
|
||||
| <a id="opt-allowEncodedSemicolon" href="#opt-allowEncodedSemicolon" title="#opt-allowEncodedSemicolon">`allowEncodedSemicolon`</a> | Allow encoded semicolon (`%3B` and `%3b`) in the request path. | `false` | No |
|
||||
| <a id="opt-allowEncodedPercent" href="#opt-allowEncodedPercent" title="#opt-allowEncodedPercent">`allowEncodedPercent`</a> | Allow encoded percent (`%25`) in the request path. | `false` | No |
|
||||
| <a id="opt-allowEncodedQuestionMark" href="#opt-allowEncodedQuestionMark" title="#opt-allowEncodedQuestionMark">`allowEncodedQuestionMark`</a> | Allow encoded question mark (`%3F` and `%3f`) in the request path. | `false` | No |
|
||||
| <a id="opt-allowEncodedHash" href="#opt-allowEncodedHash" title="#opt-allowEncodedHash">`allowEncodedHash`</a> | Allow encoded hash (`%23`) in the request path. | `false` | No |
|
||||
@@ -18,8 +18,8 @@ Middlewares that use the same protocol can be combined into chains to fit every
|
||||
|
||||
## Available HTTP Middlewares
|
||||
|
||||
| Middleware | Purpose | Area |
|
||||
|-------------------------------------------|---------------------------------------------------|-----------------------------|
|
||||
| Middleware | Purpose | Area |
|
||||
|------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------|-----------------------------|
|
||||
| <a id="opt-AddPrefix" href="#opt-AddPrefix" title="#opt-AddPrefix">[AddPrefix](addprefix.md)</a> | Adds a Path Prefix | Path Modifier |
|
||||
| <a id="opt-BasicAuth" href="#opt-BasicAuth" title="#opt-BasicAuth">[BasicAuth](basicauth.md)</a> | Adds Basic Authentication | Security, Authentication |
|
||||
| <a id="opt-Buffering" href="#opt-Buffering" title="#opt-Buffering">[Buffering](buffering.md)</a> | Buffers the request/response | Request Lifecycle |
|
||||
@@ -28,6 +28,7 @@ Middlewares that use the same protocol can be combined into chains to fit every
|
||||
| <a id="opt-Compress" href="#opt-Compress" title="#opt-Compress">[Compress](compress.md)</a> | Compresses the response | Content Modifier |
|
||||
| <a id="opt-ContentType" href="#opt-ContentType" title="#opt-ContentType">[ContentType](contenttype.md)</a> | Handles Content-Type auto-detection | Misc |
|
||||
| <a id="opt-DigestAuth" href="#opt-DigestAuth" title="#opt-DigestAuth">[DigestAuth](digestauth.md)</a> | Adds Digest Authentication | Security, Authentication |
|
||||
| <a id="opt-EncodedCharacters" href="#opt-EncodedCharacters" title="#opt-EncodedCharacters">[EncodedCharacters](encodedcharacters.md)</a> | Defines allowed reserved encoded characters in the request path | Security, Request Lifecycle |
|
||||
| <a id="opt-Errors" href="#opt-Errors" title="#opt-Errors">[Errors](errorpages.md)</a> | Defines custom error pages | Request Lifecycle |
|
||||
| <a id="opt-ForwardAuth" href="#opt-ForwardAuth" title="#opt-ForwardAuth">[ForwardAuth](forwardauth.md)</a> | Delegates Authentication | Security, Authentication |
|
||||
| <a id="opt-GrpcWeb" href="#opt-GrpcWeb" title="#opt-GrpcWeb">[GrpcWeb](grpcweb.md)</a> | Converts gRPC Web requests to HTTP/2 gRPC requests. | Request |
|
||||
|
||||
@@ -191,15 +191,24 @@
|
||||
realm = "foobar"
|
||||
headerField = "foobar"
|
||||
[http.middlewares.Middleware09]
|
||||
[http.middlewares.Middleware09.errors]
|
||||
[http.middlewares.Middleware09.encodedCharacters]
|
||||
allowEncodedSlash = true
|
||||
allowEncodedBackSlash = true
|
||||
allowEncodedNullCharacter = true
|
||||
allowEncodedSemicolon = true
|
||||
allowEncodedPercent = true
|
||||
allowEncodedQuestionMark = true
|
||||
allowEncodedHash = true
|
||||
[http.middlewares.Middleware10]
|
||||
[http.middlewares.Middleware10.errors]
|
||||
status = ["foobar", "foobar"]
|
||||
service = "foobar"
|
||||
query = "foobar"
|
||||
[http.middlewares.Middleware09.errors.statusRewrites]
|
||||
[http.middlewares.Middleware10.errors.statusRewrites]
|
||||
name0 = 42
|
||||
name1 = 42
|
||||
[http.middlewares.Middleware10]
|
||||
[http.middlewares.Middleware10.forwardAuth]
|
||||
[http.middlewares.Middleware11]
|
||||
[http.middlewares.Middleware11.forwardAuth]
|
||||
address = "foobar"
|
||||
trustForwardHeader = true
|
||||
authResponseHeaders = ["foobar", "foobar"]
|
||||
@@ -211,17 +220,17 @@
|
||||
maxBodySize = 42
|
||||
preserveLocationHeader = true
|
||||
preserveRequestMethod = true
|
||||
[http.middlewares.Middleware10.forwardAuth.tls]
|
||||
[http.middlewares.Middleware11.forwardAuth.tls]
|
||||
ca = "foobar"
|
||||
cert = "foobar"
|
||||
key = "foobar"
|
||||
insecureSkipVerify = true
|
||||
caOptional = true
|
||||
[http.middlewares.Middleware11]
|
||||
[http.middlewares.Middleware11.grpcWeb]
|
||||
allowOrigins = ["foobar", "foobar"]
|
||||
[http.middlewares.Middleware12]
|
||||
[http.middlewares.Middleware12.headers]
|
||||
[http.middlewares.Middleware12.grpcWeb]
|
||||
allowOrigins = ["foobar", "foobar"]
|
||||
[http.middlewares.Middleware13]
|
||||
[http.middlewares.Middleware13.headers]
|
||||
accessControlAllowCredentials = true
|
||||
accessControlAllowHeaders = ["foobar", "foobar"]
|
||||
accessControlAllowMethods = ["foobar", "foobar"]
|
||||
@@ -252,49 +261,49 @@
|
||||
sslTemporaryRedirect = true
|
||||
sslHost = "foobar"
|
||||
sslForceHost = true
|
||||
[http.middlewares.Middleware12.headers.customRequestHeaders]
|
||||
[http.middlewares.Middleware13.headers.customRequestHeaders]
|
||||
name0 = "foobar"
|
||||
name1 = "foobar"
|
||||
[http.middlewares.Middleware12.headers.customResponseHeaders]
|
||||
[http.middlewares.Middleware13.headers.customResponseHeaders]
|
||||
name0 = "foobar"
|
||||
name1 = "foobar"
|
||||
[http.middlewares.Middleware12.headers.sslProxyHeaders]
|
||||
[http.middlewares.Middleware13.headers.sslProxyHeaders]
|
||||
name0 = "foobar"
|
||||
name1 = "foobar"
|
||||
[http.middlewares.Middleware13]
|
||||
[http.middlewares.Middleware13.ipAllowList]
|
||||
[http.middlewares.Middleware14]
|
||||
[http.middlewares.Middleware14.ipAllowList]
|
||||
sourceRange = ["foobar", "foobar"]
|
||||
rejectStatusCode = 42
|
||||
[http.middlewares.Middleware13.ipAllowList.ipStrategy]
|
||||
depth = 42
|
||||
excludedIPs = ["foobar", "foobar"]
|
||||
ipv6Subnet = 42
|
||||
[http.middlewares.Middleware14]
|
||||
[http.middlewares.Middleware14.ipWhiteList]
|
||||
sourceRange = ["foobar", "foobar"]
|
||||
[http.middlewares.Middleware14.ipWhiteList.ipStrategy]
|
||||
[http.middlewares.Middleware14.ipAllowList.ipStrategy]
|
||||
depth = 42
|
||||
excludedIPs = ["foobar", "foobar"]
|
||||
ipv6Subnet = 42
|
||||
[http.middlewares.Middleware15]
|
||||
[http.middlewares.Middleware15.inFlightReq]
|
||||
[http.middlewares.Middleware15.ipWhiteList]
|
||||
sourceRange = ["foobar", "foobar"]
|
||||
[http.middlewares.Middleware15.ipWhiteList.ipStrategy]
|
||||
depth = 42
|
||||
excludedIPs = ["foobar", "foobar"]
|
||||
ipv6Subnet = 42
|
||||
[http.middlewares.Middleware16]
|
||||
[http.middlewares.Middleware16.inFlightReq]
|
||||
amount = 42
|
||||
[http.middlewares.Middleware15.inFlightReq.sourceCriterion]
|
||||
[http.middlewares.Middleware16.inFlightReq.sourceCriterion]
|
||||
requestHeaderName = "foobar"
|
||||
requestHost = true
|
||||
[http.middlewares.Middleware15.inFlightReq.sourceCriterion.ipStrategy]
|
||||
[http.middlewares.Middleware16.inFlightReq.sourceCriterion.ipStrategy]
|
||||
depth = 42
|
||||
excludedIPs = ["foobar", "foobar"]
|
||||
ipv6Subnet = 42
|
||||
[http.middlewares.Middleware16]
|
||||
[http.middlewares.Middleware16.passTLSClientCert]
|
||||
[http.middlewares.Middleware17]
|
||||
[http.middlewares.Middleware17.passTLSClientCert]
|
||||
pem = true
|
||||
[http.middlewares.Middleware16.passTLSClientCert.info]
|
||||
[http.middlewares.Middleware17.passTLSClientCert.info]
|
||||
notAfter = true
|
||||
notBefore = true
|
||||
sans = true
|
||||
serialNumber = true
|
||||
[http.middlewares.Middleware16.passTLSClientCert.info.subject]
|
||||
[http.middlewares.Middleware17.passTLSClientCert.info.subject]
|
||||
country = true
|
||||
province = true
|
||||
locality = true
|
||||
@@ -303,7 +312,7 @@
|
||||
commonName = true
|
||||
serialNumber = true
|
||||
domainComponent = true
|
||||
[http.middlewares.Middleware16.passTLSClientCert.info.issuer]
|
||||
[http.middlewares.Middleware17.passTLSClientCert.info.issuer]
|
||||
country = true
|
||||
province = true
|
||||
locality = true
|
||||
@@ -311,27 +320,27 @@
|
||||
commonName = true
|
||||
serialNumber = true
|
||||
domainComponent = true
|
||||
[http.middlewares.Middleware17]
|
||||
[http.middlewares.Middleware17.plugin]
|
||||
[http.middlewares.Middleware17.plugin.PluginConf0]
|
||||
name0 = "foobar"
|
||||
name1 = "foobar"
|
||||
[http.middlewares.Middleware17.plugin.PluginConf1]
|
||||
name0 = "foobar"
|
||||
name1 = "foobar"
|
||||
[http.middlewares.Middleware18]
|
||||
[http.middlewares.Middleware18.rateLimit]
|
||||
[http.middlewares.Middleware18.plugin]
|
||||
[http.middlewares.Middleware18.plugin.PluginConf0]
|
||||
name0 = "foobar"
|
||||
name1 = "foobar"
|
||||
[http.middlewares.Middleware18.plugin.PluginConf1]
|
||||
name0 = "foobar"
|
||||
name1 = "foobar"
|
||||
[http.middlewares.Middleware19]
|
||||
[http.middlewares.Middleware19.rateLimit]
|
||||
average = 42
|
||||
period = "42s"
|
||||
burst = 42
|
||||
[http.middlewares.Middleware18.rateLimit.sourceCriterion]
|
||||
[http.middlewares.Middleware19.rateLimit.sourceCriterion]
|
||||
requestHeaderName = "foobar"
|
||||
requestHost = true
|
||||
[http.middlewares.Middleware18.rateLimit.sourceCriterion.ipStrategy]
|
||||
[http.middlewares.Middleware19.rateLimit.sourceCriterion.ipStrategy]
|
||||
depth = 42
|
||||
excludedIPs = ["foobar", "foobar"]
|
||||
ipv6Subnet = 42
|
||||
[http.middlewares.Middleware18.rateLimit.redis]
|
||||
[http.middlewares.Middleware19.rateLimit.redis]
|
||||
endpoints = ["foobar", "foobar"]
|
||||
username = "foobar"
|
||||
password = "foobar"
|
||||
@@ -342,38 +351,38 @@
|
||||
readTimeout = "42s"
|
||||
writeTimeout = "42s"
|
||||
dialTimeout = "42s"
|
||||
[http.middlewares.Middleware18.rateLimit.redis.tls]
|
||||
[http.middlewares.Middleware19.rateLimit.redis.tls]
|
||||
ca = "foobar"
|
||||
cert = "foobar"
|
||||
key = "foobar"
|
||||
insecureSkipVerify = true
|
||||
[http.middlewares.Middleware19]
|
||||
[http.middlewares.Middleware19.redirectRegex]
|
||||
[http.middlewares.Middleware20]
|
||||
[http.middlewares.Middleware20.redirectRegex]
|
||||
regex = "foobar"
|
||||
replacement = "foobar"
|
||||
permanent = true
|
||||
[http.middlewares.Middleware20]
|
||||
[http.middlewares.Middleware20.redirectScheme]
|
||||
[http.middlewares.Middleware21]
|
||||
[http.middlewares.Middleware21.redirectScheme]
|
||||
scheme = "foobar"
|
||||
port = "foobar"
|
||||
permanent = true
|
||||
[http.middlewares.Middleware21]
|
||||
[http.middlewares.Middleware21.replacePath]
|
||||
path = "foobar"
|
||||
[http.middlewares.Middleware22]
|
||||
[http.middlewares.Middleware22.replacePathRegex]
|
||||
[http.middlewares.Middleware22.replacePath]
|
||||
path = "foobar"
|
||||
[http.middlewares.Middleware23]
|
||||
[http.middlewares.Middleware23.replacePathRegex]
|
||||
regex = "foobar"
|
||||
replacement = "foobar"
|
||||
[http.middlewares.Middleware23]
|
||||
[http.middlewares.Middleware23.retry]
|
||||
[http.middlewares.Middleware24]
|
||||
[http.middlewares.Middleware24.retry]
|
||||
attempts = 42
|
||||
initialInterval = "42s"
|
||||
[http.middlewares.Middleware24]
|
||||
[http.middlewares.Middleware24.stripPrefix]
|
||||
[http.middlewares.Middleware25]
|
||||
[http.middlewares.Middleware25.stripPrefix]
|
||||
prefixes = ["foobar", "foobar"]
|
||||
forceSlash = true
|
||||
[http.middlewares.Middleware25]
|
||||
[http.middlewares.Middleware25.stripPrefixRegex]
|
||||
[http.middlewares.Middleware26]
|
||||
[http.middlewares.Middleware26.stripPrefixRegex]
|
||||
regex = ["foobar", "foobar"]
|
||||
[http.serversTransports]
|
||||
[http.serversTransports.ServersTransport0]
|
||||
|
||||
@@ -205,6 +205,15 @@ http:
|
||||
realm: foobar
|
||||
headerField: foobar
|
||||
Middleware09:
|
||||
encodedCharacters:
|
||||
allowEncodedSlash: true
|
||||
allowEncodedBackSlash: true
|
||||
allowEncodedNullCharacter: true
|
||||
allowEncodedSemicolon: true
|
||||
allowEncodedPercent: true
|
||||
allowEncodedQuestionMark: true
|
||||
allowEncodedHash: true
|
||||
Middleware10:
|
||||
errors:
|
||||
status:
|
||||
- foobar
|
||||
@@ -214,7 +223,7 @@ http:
|
||||
name1: 42
|
||||
service: foobar
|
||||
query: foobar
|
||||
Middleware10:
|
||||
Middleware11:
|
||||
forwardAuth:
|
||||
address: foobar
|
||||
tls:
|
||||
@@ -239,12 +248,12 @@ http:
|
||||
maxBodySize: 42
|
||||
preserveLocationHeader: true
|
||||
preserveRequestMethod: true
|
||||
Middleware11:
|
||||
Middleware12:
|
||||
grpcWeb:
|
||||
allowOrigins:
|
||||
- foobar
|
||||
- foobar
|
||||
Middleware12:
|
||||
Middleware13:
|
||||
headers:
|
||||
customRequestHeaders:
|
||||
name0: foobar
|
||||
@@ -299,7 +308,7 @@ http:
|
||||
sslTemporaryRedirect: true
|
||||
sslHost: foobar
|
||||
sslForceHost: true
|
||||
Middleware13:
|
||||
Middleware14:
|
||||
ipAllowList:
|
||||
sourceRange:
|
||||
- foobar
|
||||
@@ -311,7 +320,7 @@ http:
|
||||
- foobar
|
||||
ipv6Subnet: 42
|
||||
rejectStatusCode: 42
|
||||
Middleware14:
|
||||
Middleware15:
|
||||
ipWhiteList:
|
||||
sourceRange:
|
||||
- foobar
|
||||
@@ -322,7 +331,7 @@ http:
|
||||
- foobar
|
||||
- foobar
|
||||
ipv6Subnet: 42
|
||||
Middleware15:
|
||||
Middleware16:
|
||||
inFlightReq:
|
||||
amount: 42
|
||||
sourceCriterion:
|
||||
@@ -334,7 +343,7 @@ http:
|
||||
ipv6Subnet: 42
|
||||
requestHeaderName: foobar
|
||||
requestHost: true
|
||||
Middleware16:
|
||||
Middleware17:
|
||||
passTLSClientCert:
|
||||
pem: true
|
||||
info:
|
||||
@@ -359,7 +368,7 @@ http:
|
||||
commonName: true
|
||||
serialNumber: true
|
||||
domainComponent: true
|
||||
Middleware17:
|
||||
Middleware18:
|
||||
plugin:
|
||||
PluginConf0:
|
||||
name0: foobar
|
||||
@@ -367,7 +376,7 @@ http:
|
||||
PluginConf1:
|
||||
name0: foobar
|
||||
name1: foobar
|
||||
Middleware18:
|
||||
Middleware19:
|
||||
rateLimit:
|
||||
average: 42
|
||||
period: 42s
|
||||
@@ -399,34 +408,34 @@ http:
|
||||
readTimeout: 42s
|
||||
writeTimeout: 42s
|
||||
dialTimeout: 42s
|
||||
Middleware19:
|
||||
Middleware20:
|
||||
redirectRegex:
|
||||
regex: foobar
|
||||
replacement: foobar
|
||||
permanent: true
|
||||
Middleware20:
|
||||
Middleware21:
|
||||
redirectScheme:
|
||||
scheme: foobar
|
||||
port: foobar
|
||||
permanent: true
|
||||
Middleware21:
|
||||
Middleware22:
|
||||
replacePath:
|
||||
path: foobar
|
||||
Middleware22:
|
||||
Middleware23:
|
||||
replacePathRegex:
|
||||
regex: foobar
|
||||
replacement: foobar
|
||||
Middleware23:
|
||||
Middleware24:
|
||||
retry:
|
||||
attempts: 42
|
||||
initialInterval: 42s
|
||||
Middleware24:
|
||||
Middleware25:
|
||||
stripPrefix:
|
||||
prefixes:
|
||||
- foobar
|
||||
- foobar
|
||||
forceSlash: true
|
||||
Middleware25:
|
||||
Middleware26:
|
||||
stripPrefixRegex:
|
||||
regex:
|
||||
- foobar
|
||||
|
||||
@@ -133,3 +133,8 @@ entryPoints:
|
||||
--entryPoints.websecure.http.encodedCharacters.allowEncodedQuestionMark=false
|
||||
--entryPoints.websecure.http.encodedCharacters.allowEncodedHash=false
|
||||
```
|
||||
|
||||
!!! info "Encoded Characters filtering on a per-route basis"
|
||||
|
||||
If you need to configure encoded character filtering on a per-route basis, you can use the `EncodedCharacters` middleware.
|
||||
Refer to the documentation for the [`EncodedCharacter` middleware](../reference/routing-configuration/http/middlewares/encodedcharacters.md) for detailed implementation instructions and configuration options.
|
||||
|
||||
@@ -292,6 +292,7 @@ nav:
|
||||
- 'ContentType': 'reference/routing-configuration/http/middlewares/contenttype.md'
|
||||
- 'DigestAuth': 'reference/routing-configuration/http/middlewares/digestauth.md'
|
||||
- '<span class="nav-link-with-icon">Distributed RateLimit <img src="https://doc.traefik.io/traefik-hub/img/ps-traefik-hub-logo-light.svg" class="menu-icon" alt="Traefik Hub API Gateway"></span>' : 'reference/routing-configuration/http/middlewares/distributed-ratelimit.md'
|
||||
- 'EncodedCharacters': 'reference/routing-configuration/http/middlewares/encodedcharacters.md'
|
||||
- 'Errors': 'reference/routing-configuration/http/middlewares/errorpages.md'
|
||||
- 'ForwardAuth': 'reference/routing-configuration/http/middlewares/forwardauth.md'
|
||||
- 'GrpcWeb': 'reference/routing-configuration/http/middlewares/grpcweb.md'
|
||||
|
||||
@@ -1106,6 +1106,39 @@ spec:
|
||||
containing user credentials.
|
||||
type: string
|
||||
type: object
|
||||
encodedCharacters:
|
||||
description: EncodedCharacters configures which encoded characters
|
||||
are allowed in the request path.
|
||||
properties:
|
||||
allowEncodedBackSlash:
|
||||
description: AllowEncodedBackSlash defines whether requests with
|
||||
encoded back slash characters in the path are allowed.
|
||||
type: boolean
|
||||
allowEncodedHash:
|
||||
description: AllowEncodedHash defines whether requests with encoded
|
||||
hash characters in the path are allowed.
|
||||
type: boolean
|
||||
allowEncodedNullCharacter:
|
||||
description: AllowEncodedNullCharacter defines whether requests
|
||||
with encoded null characters in the path are allowed.
|
||||
type: boolean
|
||||
allowEncodedPercent:
|
||||
description: AllowEncodedPercent defines whether requests with
|
||||
encoded percent characters in the path are allowed.
|
||||
type: boolean
|
||||
allowEncodedQuestionMark:
|
||||
description: AllowEncodedQuestionMark defines whether requests
|
||||
with encoded question mark characters in the path are allowed.
|
||||
type: boolean
|
||||
allowEncodedSemicolon:
|
||||
description: AllowEncodedSemicolon defines whether requests with
|
||||
encoded semicolon characters in the path are allowed.
|
||||
type: boolean
|
||||
allowEncodedSlash:
|
||||
description: AllowEncodedSlash defines whether requests with encoded
|
||||
slash characters in the path are allowed.
|
||||
type: boolean
|
||||
type: object
|
||||
errors:
|
||||
description: |-
|
||||
ErrorPage holds the custom error middleware configuration.
|
||||
|
||||
@@ -27,6 +27,7 @@ type Middleware struct {
|
||||
IPWhiteList *IPWhiteList `json:"ipWhiteList,omitempty" toml:"ipWhiteList,omitempty" yaml:"ipWhiteList,omitempty" export:"true"`
|
||||
IPAllowList *IPAllowList `json:"ipAllowList,omitempty" toml:"ipAllowList,omitempty" yaml:"ipAllowList,omitempty" export:"true"`
|
||||
Headers *Headers `json:"headers,omitempty" toml:"headers,omitempty" yaml:"headers,omitempty" export:"true"`
|
||||
EncodedCharacters *EncodedCharacters `json:"encodedCharacters,omitempty" toml:"encodedCharacters,omitempty" yaml:"encodedCharacters,omitempty" export:"true"`
|
||||
Errors *ErrorPage `json:"errors,omitempty" toml:"errors,omitempty" yaml:"errors,omitempty" export:"true"`
|
||||
RateLimit *RateLimit `json:"rateLimit,omitempty" toml:"rateLimit,omitempty" yaml:"rateLimit,omitempty" export:"true"`
|
||||
RedirectRegex *RedirectRegex `json:"redirectRegex,omitempty" toml:"redirectRegex,omitempty" yaml:"redirectRegex,omitempty" export:"true"`
|
||||
@@ -217,6 +218,26 @@ type DigestAuth struct {
|
||||
|
||||
// +k8s:deepcopy-gen=true
|
||||
|
||||
// EncodedCharacters configures which encoded characters are allowed in the request path.
|
||||
type EncodedCharacters struct {
|
||||
// AllowEncodedSlash defines whether requests with encoded slash characters in the path are allowed.
|
||||
AllowEncodedSlash bool `json:"allowEncodedSlash,omitempty" toml:"allowEncodedSlash,omitempty" yaml:"allowEncodedSlash,omitempty" export:"true"`
|
||||
// AllowEncodedBackSlash defines whether requests with encoded back slash characters in the path are allowed.
|
||||
AllowEncodedBackSlash bool `json:"allowEncodedBackSlash,omitempty" toml:"allowEncodedBackSlash,omitempty" yaml:"allowEncodedBackSlash,omitempty" export:"true"`
|
||||
// AllowEncodedNullCharacter defines whether requests with encoded null characters in the path are allowed.
|
||||
AllowEncodedNullCharacter bool `json:"allowEncodedNullCharacter,omitempty" toml:"allowEncodedNullCharacter,omitempty" yaml:"allowEncodedNullCharacter,omitempty" export:"true"`
|
||||
// AllowEncodedSemicolon defines whether requests with encoded semicolon characters in the path are allowed.
|
||||
AllowEncodedSemicolon bool `json:"allowEncodedSemicolon,omitempty" toml:"allowEncodedSemicolon,omitempty" yaml:"allowEncodedSemicolon,omitempty" export:"true"`
|
||||
// AllowEncodedPercent defines whether requests with encoded percent characters in the path are allowed.
|
||||
AllowEncodedPercent bool `json:"allowEncodedPercent,omitempty" toml:"allowEncodedPercent,omitempty" yaml:"allowEncodedPercent,omitempty" export:"true"`
|
||||
// AllowEncodedQuestionMark defines whether requests with encoded question mark characters in the path are allowed.
|
||||
AllowEncodedQuestionMark bool `json:"allowEncodedQuestionMark,omitempty" toml:"allowEncodedQuestionMark,omitempty" yaml:"allowEncodedQuestionMark,omitempty" export:"true"`
|
||||
// AllowEncodedHash defines whether requests with encoded hash characters in the path are allowed.
|
||||
AllowEncodedHash bool `json:"allowEncodedHash,omitempty" toml:"allowEncodedHash,omitempty" yaml:"allowEncodedHash,omitempty" export:"true"`
|
||||
}
|
||||
|
||||
// +k8s:deepcopy-gen=true
|
||||
|
||||
// ErrorPage holds the custom error middleware configuration.
|
||||
// This middleware returns a custom page in lieu of the default, according to configured ranges of HTTP Status codes.
|
||||
type ErrorPage struct {
|
||||
|
||||
@@ -306,6 +306,22 @@ func (in *DigestAuth) DeepCopy() *DigestAuth {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *EncodedCharacters) DeepCopyInto(out *EncodedCharacters) {
|
||||
*out = *in
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EncodedCharacters.
|
||||
func (in *EncodedCharacters) DeepCopy() *EncodedCharacters {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(EncodedCharacters)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ErrorPage) DeepCopyInto(out *ErrorPage) {
|
||||
*out = *in
|
||||
@@ -905,6 +921,11 @@ func (in *Middleware) DeepCopyInto(out *Middleware) {
|
||||
*out = new(Headers)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.EncodedCharacters != nil {
|
||||
in, out := &in.EncodedCharacters, &out.EncodedCharacters
|
||||
*out = new(EncodedCharacters)
|
||||
**out = **in
|
||||
}
|
||||
if in.Errors != nil {
|
||||
in, out := &in.Errors, &out.Errors
|
||||
*out = new(ErrorPage)
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
package encodedcharacters
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/traefik/traefik/v3/pkg/config/dynamic"
|
||||
"github.com/traefik/traefik/v3/pkg/middlewares"
|
||||
)
|
||||
|
||||
const typeName = "EncodedCharacters"
|
||||
|
||||
type encodedCharacters struct {
|
||||
next http.Handler
|
||||
deniedCharacters map[string]struct{}
|
||||
name string
|
||||
}
|
||||
|
||||
// NewEncodedCharacters creates an Encoded Characters middleware.
|
||||
func NewEncodedCharacters(ctx context.Context, next http.Handler, config dynamic.EncodedCharacters, name string) http.Handler {
|
||||
middlewares.GetLogger(ctx, name, typeName).Debug().Msg("Creating middleware")
|
||||
|
||||
return &encodedCharacters{
|
||||
next: next,
|
||||
deniedCharacters: mapDeniedCharacters(config),
|
||||
name: name,
|
||||
}
|
||||
}
|
||||
|
||||
func (ec *encodedCharacters) GetTracingInformation() (string, string) {
|
||||
return ec.name, typeName
|
||||
}
|
||||
|
||||
func (ec *encodedCharacters) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||
logger := middlewares.GetLogger(req.Context(), ec.name, typeName)
|
||||
|
||||
if len(ec.deniedCharacters) == 0 {
|
||||
ec.next.ServeHTTP(rw, req)
|
||||
return
|
||||
}
|
||||
|
||||
escapedPath := req.URL.EscapedPath()
|
||||
|
||||
for i := 0; i < len(escapedPath); i++ {
|
||||
if escapedPath[i] != '%' {
|
||||
continue
|
||||
}
|
||||
|
||||
// This should never happen as the standard library will reject requests containing invalid percent-encodings.
|
||||
// This discards URLs with a percent character at the end.
|
||||
if i+2 >= len(escapedPath) {
|
||||
rw.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// This rejects a request with a path containing the given encoded characters.
|
||||
if _, exists := ec.deniedCharacters[escapedPath[i:i+3]]; exists {
|
||||
logger.Debug().Msgf("Rejecting request because it contains encoded character %s in the URL path: %s", escapedPath[i:i+3], escapedPath)
|
||||
rw.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
i += 2
|
||||
}
|
||||
|
||||
ec.next.ServeHTTP(rw, req)
|
||||
}
|
||||
|
||||
// mapDeniedCharacters returns a map of unallowed encoded characters.
|
||||
func mapDeniedCharacters(config dynamic.EncodedCharacters) map[string]struct{} {
|
||||
characters := make(map[string]struct{})
|
||||
|
||||
if !config.AllowEncodedSlash {
|
||||
characters["%2F"] = struct{}{}
|
||||
characters["%2f"] = struct{}{}
|
||||
}
|
||||
if !config.AllowEncodedBackSlash {
|
||||
characters["%5C"] = struct{}{}
|
||||
characters["%5c"] = struct{}{}
|
||||
}
|
||||
if !config.AllowEncodedNullCharacter {
|
||||
characters["%00"] = struct{}{}
|
||||
}
|
||||
if !config.AllowEncodedSemicolon {
|
||||
characters["%3B"] = struct{}{}
|
||||
characters["%3b"] = struct{}{}
|
||||
}
|
||||
if !config.AllowEncodedPercent {
|
||||
characters["%25"] = struct{}{}
|
||||
}
|
||||
if !config.AllowEncodedQuestionMark {
|
||||
characters["%3F"] = struct{}{}
|
||||
characters["%3f"] = struct{}{}
|
||||
}
|
||||
if !config.AllowEncodedHash {
|
||||
characters["%23"] = struct{}{}
|
||||
}
|
||||
|
||||
return characters
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
package encodedcharacters
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/traefik/traefik/v3/pkg/config/dynamic"
|
||||
)
|
||||
|
||||
func TestEncodedCharacters(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
config dynamic.EncodedCharacters
|
||||
path string
|
||||
expectedStatusCode int
|
||||
}{
|
||||
{
|
||||
desc: "deny encoded slash",
|
||||
config: dynamic.EncodedCharacters{},
|
||||
path: "/foo%2fbar",
|
||||
expectedStatusCode: http.StatusBadRequest,
|
||||
},
|
||||
{
|
||||
desc: "allow encoded slash",
|
||||
config: dynamic.EncodedCharacters{
|
||||
AllowEncodedSlash: true,
|
||||
},
|
||||
path: "/foo%2fbar",
|
||||
expectedStatusCode: http.StatusOK,
|
||||
},
|
||||
{
|
||||
desc: "deny encoded backslash",
|
||||
config: dynamic.EncodedCharacters{},
|
||||
path: "/foo%5cbar",
|
||||
expectedStatusCode: http.StatusBadRequest,
|
||||
},
|
||||
{
|
||||
desc: "allow encoded backslash",
|
||||
config: dynamic.EncodedCharacters{
|
||||
AllowEncodedBackSlash: true,
|
||||
},
|
||||
path: "/foo%5cbar",
|
||||
expectedStatusCode: http.StatusOK,
|
||||
},
|
||||
{
|
||||
desc: "deny encoded null character",
|
||||
config: dynamic.EncodedCharacters{},
|
||||
path: "/foo%00bar",
|
||||
expectedStatusCode: http.StatusBadRequest,
|
||||
},
|
||||
{
|
||||
desc: "allow encoded null character",
|
||||
config: dynamic.EncodedCharacters{
|
||||
AllowEncodedNullCharacter: true,
|
||||
},
|
||||
path: "/foo%00bar",
|
||||
expectedStatusCode: http.StatusOK,
|
||||
},
|
||||
{
|
||||
desc: "deny encoded semi colon",
|
||||
config: dynamic.EncodedCharacters{},
|
||||
path: "/foo%3bbar",
|
||||
expectedStatusCode: http.StatusBadRequest,
|
||||
},
|
||||
{
|
||||
desc: "allow encoded semi colon",
|
||||
config: dynamic.EncodedCharacters{
|
||||
AllowEncodedSemicolon: true,
|
||||
},
|
||||
path: "/foo%3bbar",
|
||||
expectedStatusCode: http.StatusOK,
|
||||
},
|
||||
{
|
||||
desc: "deny encoded percent",
|
||||
config: dynamic.EncodedCharacters{},
|
||||
path: "/foo%25bar",
|
||||
expectedStatusCode: http.StatusBadRequest,
|
||||
},
|
||||
{
|
||||
desc: "allow encoded percent",
|
||||
config: dynamic.EncodedCharacters{
|
||||
AllowEncodedPercent: true,
|
||||
},
|
||||
path: "/foo%25bar",
|
||||
expectedStatusCode: http.StatusOK,
|
||||
},
|
||||
{
|
||||
desc: "deny encoded question mark",
|
||||
config: dynamic.EncodedCharacters{},
|
||||
path: "/foo%3fbar",
|
||||
expectedStatusCode: http.StatusBadRequest,
|
||||
},
|
||||
{
|
||||
desc: "allow encoded question mark",
|
||||
config: dynamic.EncodedCharacters{
|
||||
AllowEncodedQuestionMark: true,
|
||||
},
|
||||
path: "/foo%3fbar",
|
||||
expectedStatusCode: http.StatusOK,
|
||||
},
|
||||
{
|
||||
desc: "deny encoded hash",
|
||||
config: dynamic.EncodedCharacters{},
|
||||
path: "/foo%23bar",
|
||||
expectedStatusCode: http.StatusBadRequest,
|
||||
},
|
||||
{
|
||||
desc: "allow encoded hash",
|
||||
config: dynamic.EncodedCharacters{
|
||||
AllowEncodedHash: true,
|
||||
},
|
||||
path: "/foo%23bar",
|
||||
expectedStatusCode: http.StatusOK,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
next := http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {})
|
||||
handler := NewEncodedCharacters(t.Context(), next, test.config, "test-encoded-characters")
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, test.path, nil)
|
||||
recorder := httptest.NewRecorder()
|
||||
handler.ServeHTTP(recorder, req)
|
||||
require.Equal(t, test.expectedStatusCode, recorder.Code)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMapDeniedCharacters(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
config dynamic.EncodedCharacters
|
||||
expectedDeniedChar map[string]struct{}
|
||||
}{
|
||||
{
|
||||
desc: "deny all characters",
|
||||
config: dynamic.EncodedCharacters{},
|
||||
expectedDeniedChar: map[string]struct{}{
|
||||
"%2F": {}, "%2f": {}, // slash
|
||||
"%5C": {}, "%5c": {}, // backslash
|
||||
"%00": {}, // null
|
||||
"%3B": {}, "%3b": {}, // semicolon
|
||||
"%25": {}, // percent
|
||||
"%3F": {}, "%3f": {}, // question mark
|
||||
"%23": {}, // hash
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "allow only encoded slash",
|
||||
config: dynamic.EncodedCharacters{
|
||||
AllowEncodedSlash: true,
|
||||
},
|
||||
expectedDeniedChar: map[string]struct{}{
|
||||
"%5C": {}, "%5c": {}, // backslash
|
||||
"%00": {}, // null
|
||||
"%3B": {}, "%3b": {}, // semicolon
|
||||
"%25": {}, // percent
|
||||
"%3F": {}, "%3f": {}, // question mark
|
||||
"%23": {}, // hash
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "allow all characters",
|
||||
config: dynamic.EncodedCharacters{
|
||||
AllowEncodedSlash: true,
|
||||
AllowEncodedBackSlash: true,
|
||||
AllowEncodedNullCharacter: true,
|
||||
AllowEncodedSemicolon: true,
|
||||
AllowEncodedPercent: true,
|
||||
AllowEncodedQuestionMark: true,
|
||||
AllowEncodedHash: true,
|
||||
},
|
||||
expectedDeniedChar: map[string]struct{}{},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
deniedMap := mapDeniedCharacters(test.config)
|
||||
require.Equal(t, test.expectedDeniedChar, deniedMap)
|
||||
require.Len(t, deniedMap, len(test.expectedDeniedChar))
|
||||
})
|
||||
}
|
||||
}
|
||||
+9
@@ -43,6 +43,7 @@ type MiddlewareSpecApplyConfiguration struct {
|
||||
IPWhiteList *dynamic.IPWhiteList `json:"ipWhiteList,omitempty"`
|
||||
IPAllowList *dynamic.IPAllowList `json:"ipAllowList,omitempty"`
|
||||
Headers *dynamic.Headers `json:"headers,omitempty"`
|
||||
EncodedCharacters *dynamic.EncodedCharacters `json:"encodedCharacters,omitempty"`
|
||||
Errors *ErrorPageApplyConfiguration `json:"errors,omitempty"`
|
||||
RateLimit *RateLimitApplyConfiguration `json:"rateLimit,omitempty"`
|
||||
RedirectRegex *dynamic.RedirectRegex `json:"redirectRegex,omitempty"`
|
||||
@@ -139,6 +140,14 @@ func (b *MiddlewareSpecApplyConfiguration) WithHeaders(value dynamic.Headers) *M
|
||||
return b
|
||||
}
|
||||
|
||||
// WithEncodedCharacters sets the EncodedCharacters field in the declarative configuration to the given value
|
||||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the EncodedCharacters field is set to the value of the last call.
|
||||
func (b *MiddlewareSpecApplyConfiguration) WithEncodedCharacters(value dynamic.EncodedCharacters) *MiddlewareSpecApplyConfiguration {
|
||||
b.EncodedCharacters = &value
|
||||
return b
|
||||
}
|
||||
|
||||
// WithErrors sets the Errors field in the declarative configuration to the given value
|
||||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the Errors field is set to the value of the last call.
|
||||
|
||||
@@ -312,6 +312,7 @@ func (p *Provider) loadConfigurationFromCRD(ctx context.Context, client Client)
|
||||
IPWhiteList: middleware.Spec.IPWhiteList,
|
||||
IPAllowList: middleware.Spec.IPAllowList,
|
||||
Headers: middleware.Spec.Headers,
|
||||
EncodedCharacters: middleware.Spec.EncodedCharacters,
|
||||
Errors: errorPage,
|
||||
RateLimit: rateLimit,
|
||||
RedirectRegex: middleware.Spec.RedirectRegex,
|
||||
|
||||
@@ -36,6 +36,7 @@ type MiddlewareSpec struct {
|
||||
IPWhiteList *dynamic.IPWhiteList `json:"ipWhiteList,omitempty"`
|
||||
IPAllowList *dynamic.IPAllowList `json:"ipAllowList,omitempty"`
|
||||
Headers *dynamic.Headers `json:"headers,omitempty"`
|
||||
EncodedCharacters *dynamic.EncodedCharacters `json:"encodedCharacters,omitempty"`
|
||||
Errors *ErrorPage `json:"errors,omitempty"`
|
||||
RateLimit *RateLimit `json:"rateLimit,omitempty"`
|
||||
RedirectRegex *dynamic.RedirectRegex `json:"redirectRegex,omitempty"`
|
||||
|
||||
@@ -858,6 +858,11 @@ func (in *MiddlewareSpec) DeepCopyInto(out *MiddlewareSpec) {
|
||||
*out = new(dynamic.Headers)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.EncodedCharacters != nil {
|
||||
in, out := &in.EncodedCharacters, &out.EncodedCharacters
|
||||
*out = new(dynamic.EncodedCharacters)
|
||||
**out = **in
|
||||
}
|
||||
if in.Errors != nil {
|
||||
in, out := &in.Errors, &out.Errors
|
||||
*out = new(ErrorPage)
|
||||
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
"github.com/traefik/traefik/v3/pkg/middlewares/compress"
|
||||
"github.com/traefik/traefik/v3/pkg/middlewares/contenttype"
|
||||
"github.com/traefik/traefik/v3/pkg/middlewares/customerrors"
|
||||
"github.com/traefik/traefik/v3/pkg/middlewares/encodedcharacters"
|
||||
"github.com/traefik/traefik/v3/pkg/middlewares/gatewayapi/headermodifier"
|
||||
gapiredirect "github.com/traefik/traefik/v3/pkg/middlewares/gatewayapi/redirect"
|
||||
"github.com/traefik/traefik/v3/pkg/middlewares/gatewayapi/urlrewrite"
|
||||
@@ -192,6 +193,16 @@ func (b *Builder) buildConstructor(ctx context.Context, middlewareName string) (
|
||||
}
|
||||
}
|
||||
|
||||
// EncodedCharacters
|
||||
if config.EncodedCharacters != nil {
|
||||
if middleware != nil {
|
||||
return nil, badConf
|
||||
}
|
||||
middleware = func(next http.Handler) (http.Handler, error) {
|
||||
return encodedcharacters.NewEncodedCharacters(ctx, next, *config.EncodedCharacters, middlewareName), nil
|
||||
}
|
||||
}
|
||||
|
||||
// CustomErrors
|
||||
if config.Errors != nil {
|
||||
if middleware != nil {
|
||||
|
||||
Reference in New Issue
Block a user