Rework access control origin configuration
This commit is contained in:
parent
fb51ebcba6
commit
082fb166a2
14 changed files with 203 additions and 122 deletions
|
@ -197,7 +197,7 @@ This functionality allows for more advanced security features to quickly be set.
|
||||||
```yaml tab="Docker"
|
```yaml tab="Docker"
|
||||||
labels:
|
labels:
|
||||||
- "traefik.http.middlewares.testheader.headers.accesscontrolallowmethods=GET,OPTIONS,PUT"
|
- "traefik.http.middlewares.testheader.headers.accesscontrolallowmethods=GET,OPTIONS,PUT"
|
||||||
- "traefik.http.middlewares.testheader.headers.accesscontrolalloworigin=origin-list-or-null"
|
- "traefik.http.middlewares.testheader.headers.accesscontrolalloworiginlist=https://foo.bar.org,https://example.org"
|
||||||
- "traefik.http.middlewares.testheader.headers.accesscontrolmaxage=100"
|
- "traefik.http.middlewares.testheader.headers.accesscontrolmaxage=100"
|
||||||
- "traefik.http.middlewares.testheader.headers.addvaryheader=true"
|
- "traefik.http.middlewares.testheader.headers.addvaryheader=true"
|
||||||
```
|
```
|
||||||
|
@ -213,14 +213,16 @@ spec:
|
||||||
- "GET"
|
- "GET"
|
||||||
- "OPTIONS"
|
- "OPTIONS"
|
||||||
- "PUT"
|
- "PUT"
|
||||||
accessControlAllowOrigin: "origin-list-or-null"
|
accessControlAllowOriginList:
|
||||||
|
- "https://foo.bar.org"
|
||||||
|
- "https://example.org"
|
||||||
accessControlMaxAge: 100
|
accessControlMaxAge: 100
|
||||||
addVaryHeader: "true"
|
addVaryHeader: "true"
|
||||||
```
|
```
|
||||||
|
|
||||||
```yaml tab="Consul Catalog"
|
```yaml tab="Consul Catalog"
|
||||||
- "traefik.http.middlewares.testheader.headers.accesscontrolallowmethods=GET,OPTIONS,PUT"
|
- "traefik.http.middlewares.testheader.headers.accesscontrolallowmethods=GET,OPTIONS,PUT"
|
||||||
- "traefik.http.middlewares.testheader.headers.accesscontrolalloworigin=origin-list-or-null"
|
- "traefik.http.middlewares.testheader.headers.accesscontrolalloworiginlist=https://foo.bar.org,https://example.org"
|
||||||
- "traefik.http.middlewares.testheader.headers.accesscontrolmaxage=100"
|
- "traefik.http.middlewares.testheader.headers.accesscontrolmaxage=100"
|
||||||
- "traefik.http.middlewares.testheader.headers.addvaryheader=true"
|
- "traefik.http.middlewares.testheader.headers.addvaryheader=true"
|
||||||
```
|
```
|
||||||
|
@ -228,7 +230,7 @@ spec:
|
||||||
```json tab="Marathon"
|
```json tab="Marathon"
|
||||||
"labels": {
|
"labels": {
|
||||||
"traefik.http.middlewares.testheader.headers.accesscontrolallowmethods": "GET,OPTIONS,PUT",
|
"traefik.http.middlewares.testheader.headers.accesscontrolallowmethods": "GET,OPTIONS,PUT",
|
||||||
"traefik.http.middlewares.testheader.headers.accesscontrolalloworigin": "origin-list-or-null",
|
"traefik.http.middlewares.testheader.headers.accesscontrolalloworiginlist": "https://foo.bar.org,https://example.org",
|
||||||
"traefik.http.middlewares.testheader.headers.accesscontrolmaxage": "100",
|
"traefik.http.middlewares.testheader.headers.accesscontrolmaxage": "100",
|
||||||
"traefik.http.middlewares.testheader.headers.addvaryheader": "true"
|
"traefik.http.middlewares.testheader.headers.addvaryheader": "true"
|
||||||
}
|
}
|
||||||
|
@ -237,7 +239,7 @@ spec:
|
||||||
```yaml tab="Rancher"
|
```yaml tab="Rancher"
|
||||||
labels:
|
labels:
|
||||||
- "traefik.http.middlewares.testheader.headers.accesscontrolallowmethods=GET,OPTIONS,PUT"
|
- "traefik.http.middlewares.testheader.headers.accesscontrolallowmethods=GET,OPTIONS,PUT"
|
||||||
- "traefik.http.middlewares.testheader.headers.accesscontrolalloworigin=origin-list-or-null"
|
- "traefik.http.middlewares.testheader.headers.accesscontrolalloworiginlist=https://foo.bar.org,https://example.org"
|
||||||
- "traefik.http.middlewares.testheader.headers.accesscontrolmaxage=100"
|
- "traefik.http.middlewares.testheader.headers.accesscontrolmaxage=100"
|
||||||
- "traefik.http.middlewares.testheader.headers.addvaryheader=true"
|
- "traefik.http.middlewares.testheader.headers.addvaryheader=true"
|
||||||
```
|
```
|
||||||
|
@ -246,7 +248,7 @@ labels:
|
||||||
[http.middlewares]
|
[http.middlewares]
|
||||||
[http.middlewares.testHeader.headers]
|
[http.middlewares.testHeader.headers]
|
||||||
accessControlAllowMethods= ["GET", "OPTIONS", "PUT"]
|
accessControlAllowMethods= ["GET", "OPTIONS", "PUT"]
|
||||||
accessControlAllowOrigin = "origin-list-or-null"
|
accessControlAllowOriginList = ["https://foo.bar.org","https://example.org"]
|
||||||
accessControlMaxAge = 100
|
accessControlMaxAge = 100
|
||||||
addVaryHeader = true
|
addVaryHeader = true
|
||||||
```
|
```
|
||||||
|
@ -260,7 +262,9 @@ http:
|
||||||
- GET
|
- GET
|
||||||
- OPTIONS
|
- OPTIONS
|
||||||
- PUT
|
- PUT
|
||||||
accessControlAllowOrigin: "origin-list-or-null"
|
accessControlAllowOriginList:
|
||||||
|
- https://foo.bar.org
|
||||||
|
- https://example.org
|
||||||
accessControlMaxAge: 100
|
accessControlMaxAge: 100
|
||||||
addVaryHeader: true
|
addVaryHeader: true
|
||||||
```
|
```
|
||||||
|
@ -295,14 +299,22 @@ The `accessControlAllowHeaders` indicates which header field names can be used a
|
||||||
|
|
||||||
The `accessControlAllowMethods` indicates which methods can be used during requests.
|
The `accessControlAllowMethods` indicates which methods can be used during requests.
|
||||||
|
|
||||||
### `accessControlAllowOrigin`
|
### `accessControlAllowOriginList`
|
||||||
|
|
||||||
The `accessControlAllowOrigin` indicates whether a resource can be shared by returning different values.
|
The `accessControlAllowOriginList` indicates whether a resource can be shared by returning different values.
|
||||||
The three options for this value are:
|
|
||||||
|
|
||||||
- `origin-list-or-null`
|
A wildcard origin `*` can also be configured, and will match all requests.
|
||||||
- `*`
|
If this value is set by a backend server, it will be overwritten by Traefik
|
||||||
- `null`
|
|
||||||
|
This value can contains a list of allowed origins.
|
||||||
|
|
||||||
|
More information including how to use the settings can be found on:
|
||||||
|
|
||||||
|
- [Mozilla.org](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin)
|
||||||
|
- [w3](https://www.w3.org/TR/cors/#access-control-allow-origin-response-header)
|
||||||
|
- [IETF](https://tools.ietf.org/html/rfc6454#section-7.1)
|
||||||
|
|
||||||
|
Traefik no longer supports the null value, as it is [no longer recommended as a return value](https://w3c.github.io/webappsec-cors-for-developers/#avoid-returning-access-control-allow-origin-null).
|
||||||
|
|
||||||
### `accessControlExposeHeaders`
|
### `accessControlExposeHeaders`
|
||||||
|
|
||||||
|
@ -314,7 +326,7 @@ The `accessControlMaxAge` indicates how long a preflight request can be cached.
|
||||||
|
|
||||||
### `addVaryHeader`
|
### `addVaryHeader`
|
||||||
|
|
||||||
The `addVaryHeader` is used in conjunction with `accessControlAllowOrigin` to determine whether the vary header should be added or modified to demonstrate that server responses can differ beased on the value of the origin header.
|
The `addVaryHeader` is used in conjunction with `accessControlAllowOriginList` to determine whether the vary header should be added or modified to demonstrate that server responses can differ based on the value of the origin header.
|
||||||
|
|
||||||
### `allowedHosts`
|
### `allowedHosts`
|
||||||
|
|
||||||
|
|
|
@ -100,3 +100,11 @@ rules:
|
||||||
```
|
```
|
||||||
|
|
||||||
After having both resources applied, Traefik will work properly.
|
After having both resources applied, Traefik will work properly.
|
||||||
|
|
||||||
|
## v2.1 to v2.2
|
||||||
|
|
||||||
|
### Headers middleware: accessControlAllowOrigin
|
||||||
|
|
||||||
|
`accessControlAllowOrigin` is deprecated.
|
||||||
|
This field will be removed in future 2.x releases.
|
||||||
|
Please configure your allowed origins in `accessControlAllowOriginList` instead.
|
||||||
|
|
|
@ -34,6 +34,7 @@
|
||||||
- "traefik.http.middlewares.middleware10.headers.accesscontrolallowheaders=foobar, foobar"
|
- "traefik.http.middlewares.middleware10.headers.accesscontrolallowheaders=foobar, foobar"
|
||||||
- "traefik.http.middlewares.middleware10.headers.accesscontrolallowmethods=foobar, foobar"
|
- "traefik.http.middlewares.middleware10.headers.accesscontrolallowmethods=foobar, foobar"
|
||||||
- "traefik.http.middlewares.middleware10.headers.accesscontrolalloworigin=foobar"
|
- "traefik.http.middlewares.middleware10.headers.accesscontrolalloworigin=foobar"
|
||||||
|
- "traefik.http.middlewares.middleware10.headers.accesscontrolalloworiginlist=foobar, foobar"
|
||||||
- "traefik.http.middlewares.middleware10.headers.accesscontrolexposeheaders=foobar, foobar"
|
- "traefik.http.middlewares.middleware10.headers.accesscontrolexposeheaders=foobar, foobar"
|
||||||
- "traefik.http.middlewares.middleware10.headers.accesscontrolmaxage=42"
|
- "traefik.http.middlewares.middleware10.headers.accesscontrolmaxage=42"
|
||||||
- "traefik.http.middlewares.middleware10.headers.addvaryheader=true"
|
- "traefik.http.middlewares.middleware10.headers.addvaryheader=true"
|
||||||
|
@ -134,6 +135,7 @@
|
||||||
- "traefik.http.routers.router1.tls.domains[1].main=foobar"
|
- "traefik.http.routers.router1.tls.domains[1].main=foobar"
|
||||||
- "traefik.http.routers.router1.tls.domains[1].sans=foobar, foobar"
|
- "traefik.http.routers.router1.tls.domains[1].sans=foobar, foobar"
|
||||||
- "traefik.http.routers.router1.tls.options=foobar"
|
- "traefik.http.routers.router1.tls.options=foobar"
|
||||||
|
- "traefik.http.services.service01.loadbalancer.healthcheck.followredirects=true"
|
||||||
- "traefik.http.services.service01.loadbalancer.healthcheck.headers.name0=foobar"
|
- "traefik.http.services.service01.loadbalancer.healthcheck.headers.name0=foobar"
|
||||||
- "traefik.http.services.service01.loadbalancer.healthcheck.headers.name1=foobar"
|
- "traefik.http.services.service01.loadbalancer.healthcheck.headers.name1=foobar"
|
||||||
- "traefik.http.services.service01.loadbalancer.healthcheck.hostname=foobar"
|
- "traefik.http.services.service01.loadbalancer.healthcheck.hostname=foobar"
|
||||||
|
|
|
@ -147,6 +147,7 @@
|
||||||
accessControlAllowHeaders = ["foobar", "foobar"]
|
accessControlAllowHeaders = ["foobar", "foobar"]
|
||||||
accessControlAllowMethods = ["foobar", "foobar"]
|
accessControlAllowMethods = ["foobar", "foobar"]
|
||||||
accessControlAllowOrigin = "foobar"
|
accessControlAllowOrigin = "foobar"
|
||||||
|
accessControlAllowOriginList = ["foobar", "foobar"]
|
||||||
accessControlExposeHeaders = ["foobar", "foobar"]
|
accessControlExposeHeaders = ["foobar", "foobar"]
|
||||||
accessControlMaxAge = 42
|
accessControlMaxAge = 42
|
||||||
addVaryHeader = true
|
addVaryHeader = true
|
||||||
|
|
|
@ -170,6 +170,9 @@ http:
|
||||||
- foobar
|
- foobar
|
||||||
- foobar
|
- foobar
|
||||||
accessControlAllowOrigin: foobar
|
accessControlAllowOrigin: foobar
|
||||||
|
accessControlAllowOriginList:
|
||||||
|
- foobar
|
||||||
|
- foobar
|
||||||
accessControlExposeHeaders:
|
accessControlExposeHeaders:
|
||||||
- foobar
|
- foobar
|
||||||
- foobar
|
- foobar
|
||||||
|
|
|
@ -41,6 +41,8 @@
|
||||||
| `traefik/http/middlewares/Middleware10/headers/accessControlAllowMethods/0` | `foobar` |
|
| `traefik/http/middlewares/Middleware10/headers/accessControlAllowMethods/0` | `foobar` |
|
||||||
| `traefik/http/middlewares/Middleware10/headers/accessControlAllowMethods/1` | `foobar` |
|
| `traefik/http/middlewares/Middleware10/headers/accessControlAllowMethods/1` | `foobar` |
|
||||||
| `traefik/http/middlewares/Middleware10/headers/accessControlAllowOrigin` | `foobar` |
|
| `traefik/http/middlewares/Middleware10/headers/accessControlAllowOrigin` | `foobar` |
|
||||||
|
| `traefik/http/middlewares/Middleware10/headers/accessControlAllowOriginList/0` | `foobar` |
|
||||||
|
| `traefik/http/middlewares/Middleware10/headers/accessControlAllowOriginList/1` | `foobar` |
|
||||||
| `traefik/http/middlewares/Middleware10/headers/accessControlExposeHeaders/0` | `foobar` |
|
| `traefik/http/middlewares/Middleware10/headers/accessControlExposeHeaders/0` | `foobar` |
|
||||||
| `traefik/http/middlewares/Middleware10/headers/accessControlExposeHeaders/1` | `foobar` |
|
| `traefik/http/middlewares/Middleware10/headers/accessControlExposeHeaders/1` | `foobar` |
|
||||||
| `traefik/http/middlewares/Middleware10/headers/accessControlMaxAge` | `42` |
|
| `traefik/http/middlewares/Middleware10/headers/accessControlMaxAge` | `42` |
|
||||||
|
|
|
@ -34,6 +34,7 @@
|
||||||
"traefik.http.middlewares.middleware10.headers.accesscontrolallowheaders": "foobar, foobar",
|
"traefik.http.middlewares.middleware10.headers.accesscontrolallowheaders": "foobar, foobar",
|
||||||
"traefik.http.middlewares.middleware10.headers.accesscontrolallowmethods": "foobar, foobar",
|
"traefik.http.middlewares.middleware10.headers.accesscontrolallowmethods": "foobar, foobar",
|
||||||
"traefik.http.middlewares.middleware10.headers.accesscontrolalloworigin": "foobar",
|
"traefik.http.middlewares.middleware10.headers.accesscontrolalloworigin": "foobar",
|
||||||
|
"traefik.http.middlewares.middleware10.headers.accesscontrolalloworiginlist": "foobar, foobar",
|
||||||
"traefik.http.middlewares.middleware10.headers.accesscontrolexposeheaders": "foobar, foobar",
|
"traefik.http.middlewares.middleware10.headers.accesscontrolexposeheaders": "foobar, foobar",
|
||||||
"traefik.http.middlewares.middleware10.headers.accesscontrolmaxage": "42",
|
"traefik.http.middlewares.middleware10.headers.accesscontrolmaxage": "42",
|
||||||
"traefik.http.middlewares.middleware10.headers.addvaryheader": "true",
|
"traefik.http.middlewares.middleware10.headers.addvaryheader": "true",
|
||||||
|
@ -132,6 +133,7 @@
|
||||||
"traefik.http.routers.router1.tls.domains[1].main": "foobar",
|
"traefik.http.routers.router1.tls.domains[1].main": "foobar",
|
||||||
"traefik.http.routers.router1.tls.domains[1].sans": "foobar, foobar",
|
"traefik.http.routers.router1.tls.domains[1].sans": "foobar, foobar",
|
||||||
"traefik.http.routers.router1.tls.options": "foobar",
|
"traefik.http.routers.router1.tls.options": "foobar",
|
||||||
|
"traefik.http.services.service01.loadbalancer.healthcheck.followredirects": "true",
|
||||||
"traefik.http.services.service01.loadbalancer.healthcheck.headers.name0": "foobar",
|
"traefik.http.services.service01.loadbalancer.healthcheck.headers.name0": "foobar",
|
||||||
"traefik.http.services.service01.loadbalancer.healthcheck.headers.name1": "foobar",
|
"traefik.http.services.service01.loadbalancer.healthcheck.headers.name1": "foobar",
|
||||||
"traefik.http.services.service01.loadbalancer.healthcheck.hostname": "foobar",
|
"traefik.http.services.service01.loadbalancer.healthcheck.hostname": "foobar",
|
||||||
|
|
|
@ -28,7 +28,7 @@
|
||||||
[http.middlewares]
|
[http.middlewares]
|
||||||
[http.middlewares.cors.headers]
|
[http.middlewares.cors.headers]
|
||||||
accessControlAllowMethods= ["GET", "OPTIONS", "PUT"]
|
accessControlAllowMethods= ["GET", "OPTIONS", "PUT"]
|
||||||
accessControlAllowOrigin = "origin-list-or-null"
|
accessControlAllowOriginList = ["https://foo.bar.org"]
|
||||||
accessControlMaxAge = 100
|
accessControlMaxAge = 100
|
||||||
addVaryHeader = true
|
addVaryHeader = true
|
||||||
|
|
||||||
|
|
|
@ -367,6 +367,7 @@
|
||||||
accessControlAllowHeaders = ["foobar", "foobar"]
|
accessControlAllowHeaders = ["foobar", "foobar"]
|
||||||
accessControlAllowMethods = ["foobar", "foobar"]
|
accessControlAllowMethods = ["foobar", "foobar"]
|
||||||
accessControlAllowOrigin = "foobar"
|
accessControlAllowOrigin = "foobar"
|
||||||
|
accessControlAllowOriginList = ["foobar", "foobar"]
|
||||||
accessControlExposeHeaders = ["foobar", "foobar"]
|
accessControlExposeHeaders = ["foobar", "foobar"]
|
||||||
accessControlMaxAge = 42
|
accessControlMaxAge = 42
|
||||||
addVaryHeader = true
|
addVaryHeader = true
|
||||||
|
|
|
@ -158,7 +158,9 @@ type Headers struct {
|
||||||
// AccessControlAllowMethods must be used in response to a preflight request with Access-Control-Request-Method set.
|
// AccessControlAllowMethods must be used in response to a preflight request with Access-Control-Request-Method set.
|
||||||
AccessControlAllowMethods []string `json:"accessControlAllowMethods,omitempty" toml:"accessControlAllowMethods,omitempty" yaml:"accessControlAllowMethods,omitempty"`
|
AccessControlAllowMethods []string `json:"accessControlAllowMethods,omitempty" toml:"accessControlAllowMethods,omitempty" yaml:"accessControlAllowMethods,omitempty"`
|
||||||
// AccessControlAllowOrigin Can be "origin-list-or-null" or "*". From (https://www.w3.org/TR/cors/#access-control-allow-origin-response-header)
|
// AccessControlAllowOrigin Can be "origin-list-or-null" or "*". From (https://www.w3.org/TR/cors/#access-control-allow-origin-response-header)
|
||||||
AccessControlAllowOrigin string `json:"accessControlAllowOrigin,omitempty" toml:"accessControlAllowOrigin,omitempty" yaml:"accessControlAllowOrigin,omitempty"`
|
AccessControlAllowOrigin string `json:"accessControlAllowOrigin,omitempty" toml:"accessControlAllowOrigin,omitempty" yaml:"accessControlAllowOrigin,omitempty"` // Deprecated
|
||||||
|
// AccessControlAllowOriginList is a list of allowable origins. Can also be a wildcard origin "*".
|
||||||
|
AccessControlAllowOriginList []string `json:"accessControlAllowOriginList,omitempty" toml:"accessControlAllowOriginList,omitempty" yaml:"accessControlAllowOriginList,omitempty"`
|
||||||
// AccessControlExposeHeaders sets valid headers for the response.
|
// AccessControlExposeHeaders sets valid headers for the response.
|
||||||
AccessControlExposeHeaders []string `json:"accessControlExposeHeaders,omitempty" toml:"accessControlExposeHeaders,omitempty" yaml:"accessControlExposeHeaders,omitempty"`
|
AccessControlExposeHeaders []string `json:"accessControlExposeHeaders,omitempty" toml:"accessControlExposeHeaders,omitempty" yaml:"accessControlExposeHeaders,omitempty"`
|
||||||
// AccessControlMaxAge sets the time that a preflight request may be cached.
|
// AccessControlMaxAge sets the time that a preflight request may be cached.
|
||||||
|
@ -200,7 +202,7 @@ func (h *Headers) HasCorsHeadersDefined() bool {
|
||||||
return h != nil && (h.AccessControlAllowCredentials ||
|
return h != nil && (h.AccessControlAllowCredentials ||
|
||||||
len(h.AccessControlAllowHeaders) != 0 ||
|
len(h.AccessControlAllowHeaders) != 0 ||
|
||||||
len(h.AccessControlAllowMethods) != 0 ||
|
len(h.AccessControlAllowMethods) != 0 ||
|
||||||
h.AccessControlAllowOrigin != "" ||
|
len(h.AccessControlAllowOriginList) != 0 ||
|
||||||
len(h.AccessControlExposeHeaders) != 0 ||
|
len(h.AccessControlExposeHeaders) != 0 ||
|
||||||
h.AccessControlMaxAge != 0 ||
|
h.AccessControlMaxAge != 0 ||
|
||||||
h.AddVaryHeader)
|
h.AddVaryHeader)
|
||||||
|
|
|
@ -47,6 +47,7 @@ func TestDecodeConfiguration(t *testing.T) {
|
||||||
"traefik.http.middlewares.Middleware8.headers.accesscontrolallowheaders": "X-foobar, X-fiibar",
|
"traefik.http.middlewares.Middleware8.headers.accesscontrolallowheaders": "X-foobar, X-fiibar",
|
||||||
"traefik.http.middlewares.Middleware8.headers.accesscontrolallowmethods": "GET, PUT",
|
"traefik.http.middlewares.Middleware8.headers.accesscontrolallowmethods": "GET, PUT",
|
||||||
"traefik.http.middlewares.Middleware8.headers.accesscontrolalloworigin": "foobar",
|
"traefik.http.middlewares.Middleware8.headers.accesscontrolalloworigin": "foobar",
|
||||||
|
"traefik.http.middlewares.Middleware8.headers.accesscontrolalloworiginList": "foobar, fiibar",
|
||||||
"traefik.http.middlewares.Middleware8.headers.accesscontrolexposeheaders": "X-foobar, X-fiibar",
|
"traefik.http.middlewares.Middleware8.headers.accesscontrolexposeheaders": "X-foobar, X-fiibar",
|
||||||
"traefik.http.middlewares.Middleware8.headers.accesscontrolmaxage": "200",
|
"traefik.http.middlewares.Middleware8.headers.accesscontrolmaxage": "200",
|
||||||
"traefik.http.middlewares.Middleware8.headers.addvaryheader": "true",
|
"traefik.http.middlewares.Middleware8.headers.addvaryheader": "true",
|
||||||
|
@ -516,6 +517,10 @@ func TestDecodeConfiguration(t *testing.T) {
|
||||||
"PUT",
|
"PUT",
|
||||||
},
|
},
|
||||||
AccessControlAllowOrigin: "foobar",
|
AccessControlAllowOrigin: "foobar",
|
||||||
|
AccessControlAllowOriginList: []string{
|
||||||
|
"foobar",
|
||||||
|
"fiibar",
|
||||||
|
},
|
||||||
AccessControlExposeHeaders: []string{
|
AccessControlExposeHeaders: []string{
|
||||||
"X-foobar",
|
"X-foobar",
|
||||||
"X-fiibar",
|
"X-fiibar",
|
||||||
|
@ -964,6 +969,10 @@ func TestEncodeConfiguration(t *testing.T) {
|
||||||
"PUT",
|
"PUT",
|
||||||
},
|
},
|
||||||
AccessControlAllowOrigin: "foobar",
|
AccessControlAllowOrigin: "foobar",
|
||||||
|
AccessControlAllowOriginList: []string{
|
||||||
|
"foobar",
|
||||||
|
"fiibar",
|
||||||
|
},
|
||||||
AccessControlExposeHeaders: []string{
|
AccessControlExposeHeaders: []string{
|
||||||
"X-foobar",
|
"X-foobar",
|
||||||
"X-fiibar",
|
"X-fiibar",
|
||||||
|
@ -1118,6 +1127,7 @@ func TestEncodeConfiguration(t *testing.T) {
|
||||||
"traefik.HTTP.Middlewares.Middleware8.Headers.AccessControlAllowHeaders": "X-foobar, X-fiibar",
|
"traefik.HTTP.Middlewares.Middleware8.Headers.AccessControlAllowHeaders": "X-foobar, X-fiibar",
|
||||||
"traefik.HTTP.Middlewares.Middleware8.Headers.AccessControlAllowMethods": "GET, PUT",
|
"traefik.HTTP.Middlewares.Middleware8.Headers.AccessControlAllowMethods": "GET, PUT",
|
||||||
"traefik.HTTP.Middlewares.Middleware8.Headers.AccessControlAllowOrigin": "foobar",
|
"traefik.HTTP.Middlewares.Middleware8.Headers.AccessControlAllowOrigin": "foobar",
|
||||||
|
"traefik.HTTP.Middlewares.Middleware8.Headers.AccessControlAllowOriginList": "foobar, fiibar",
|
||||||
"traefik.HTTP.Middlewares.Middleware8.Headers.AccessControlExposeHeaders": "X-foobar, X-fiibar",
|
"traefik.HTTP.Middlewares.Middleware8.Headers.AccessControlExposeHeaders": "X-foobar, X-fiibar",
|
||||||
"traefik.HTTP.Middlewares.Middleware8.Headers.AccessControlMaxAge": "200",
|
"traefik.HTTP.Middlewares.Middleware8.Headers.AccessControlMaxAge": "200",
|
||||||
"traefik.HTTP.Middlewares.Middleware8.Headers.AddVaryHeader": "true",
|
"traefik.HTTP.Middlewares.Middleware8.Headers.AddVaryHeader": "true",
|
||||||
|
|
|
@ -20,20 +20,31 @@ const (
|
||||||
typeName = "Headers"
|
typeName = "Headers"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func handleDeprecation(ctx context.Context, cfg *dynamic.Headers) {
|
||||||
|
if cfg.AccessControlAllowOrigin != "" {
|
||||||
|
log.FromContext(ctx).Warn("accessControlAllowOrigin is deprecated, please use accessControlAllowOriginList instead.")
|
||||||
|
cfg.AccessControlAllowOriginList = append(cfg.AccessControlAllowOriginList, cfg.AccessControlAllowOrigin)
|
||||||
|
cfg.AccessControlAllowOrigin = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type headers struct {
|
type headers struct {
|
||||||
name string
|
name string
|
||||||
handler http.Handler
|
handler http.Handler
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a Headers middleware.
|
// New creates a Headers middleware.
|
||||||
func New(ctx context.Context, next http.Handler, config dynamic.Headers, name string) (http.Handler, error) {
|
func New(ctx context.Context, next http.Handler, cfg dynamic.Headers, name string) (http.Handler, error) {
|
||||||
// HeaderMiddleware -> SecureMiddleWare -> next
|
// HeaderMiddleware -> SecureMiddleWare -> next
|
||||||
logger := log.FromContext(middlewares.GetLoggerCtx(ctx, name, typeName))
|
mCtx := middlewares.GetLoggerCtx(ctx, name, typeName)
|
||||||
|
logger := log.FromContext(mCtx)
|
||||||
logger.Debug("Creating middleware")
|
logger.Debug("Creating middleware")
|
||||||
|
|
||||||
hasSecureHeaders := config.HasSecureHeadersDefined()
|
handleDeprecation(mCtx, &cfg)
|
||||||
hasCustomHeaders := config.HasCustomHeadersDefined()
|
|
||||||
hasCorsHeaders := config.HasCorsHeadersDefined()
|
hasSecureHeaders := cfg.HasSecureHeadersDefined()
|
||||||
|
hasCustomHeaders := cfg.HasCustomHeadersDefined()
|
||||||
|
hasCorsHeaders := cfg.HasCorsHeadersDefined()
|
||||||
|
|
||||||
if !hasSecureHeaders && !hasCustomHeaders && !hasCorsHeaders {
|
if !hasSecureHeaders && !hasCustomHeaders && !hasCorsHeaders {
|
||||||
return nil, errors.New("headers configuration not valid")
|
return nil, errors.New("headers configuration not valid")
|
||||||
|
@ -43,14 +54,14 @@ func New(ctx context.Context, next http.Handler, config dynamic.Headers, name st
|
||||||
nextHandler := next
|
nextHandler := next
|
||||||
|
|
||||||
if hasSecureHeaders {
|
if hasSecureHeaders {
|
||||||
logger.Debug("Setting up secureHeaders from %v", config)
|
logger.Debug("Setting up secureHeaders from %v", cfg)
|
||||||
handler = newSecure(next, config)
|
handler = newSecure(next, cfg)
|
||||||
nextHandler = handler
|
nextHandler = handler
|
||||||
}
|
}
|
||||||
|
|
||||||
if hasCustomHeaders || hasCorsHeaders {
|
if hasCustomHeaders || hasCorsHeaders {
|
||||||
logger.Debug("Setting up customHeaders/Cors from %v", config)
|
logger.Debug("Setting up customHeaders/Cors from %v", cfg)
|
||||||
handler = NewHeader(nextHandler, config)
|
handler = NewHeader(nextHandler, cfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &headers{
|
return &headers{
|
||||||
|
@ -73,29 +84,29 @@ type secureHeader struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// newSecure constructs a new secure instance with supplied options.
|
// newSecure constructs a new secure instance with supplied options.
|
||||||
func newSecure(next http.Handler, headers dynamic.Headers) *secureHeader {
|
func newSecure(next http.Handler, cfg dynamic.Headers) *secureHeader {
|
||||||
opt := secure.Options{
|
opt := secure.Options{
|
||||||
BrowserXssFilter: headers.BrowserXSSFilter,
|
BrowserXssFilter: cfg.BrowserXSSFilter,
|
||||||
ContentTypeNosniff: headers.ContentTypeNosniff,
|
ContentTypeNosniff: cfg.ContentTypeNosniff,
|
||||||
ForceSTSHeader: headers.ForceSTSHeader,
|
ForceSTSHeader: cfg.ForceSTSHeader,
|
||||||
FrameDeny: headers.FrameDeny,
|
FrameDeny: cfg.FrameDeny,
|
||||||
IsDevelopment: headers.IsDevelopment,
|
IsDevelopment: cfg.IsDevelopment,
|
||||||
SSLRedirect: headers.SSLRedirect,
|
SSLRedirect: cfg.SSLRedirect,
|
||||||
SSLForceHost: headers.SSLForceHost,
|
SSLForceHost: cfg.SSLForceHost,
|
||||||
SSLTemporaryRedirect: headers.SSLTemporaryRedirect,
|
SSLTemporaryRedirect: cfg.SSLTemporaryRedirect,
|
||||||
STSIncludeSubdomains: headers.STSIncludeSubdomains,
|
STSIncludeSubdomains: cfg.STSIncludeSubdomains,
|
||||||
STSPreload: headers.STSPreload,
|
STSPreload: cfg.STSPreload,
|
||||||
ContentSecurityPolicy: headers.ContentSecurityPolicy,
|
ContentSecurityPolicy: cfg.ContentSecurityPolicy,
|
||||||
CustomBrowserXssValue: headers.CustomBrowserXSSValue,
|
CustomBrowserXssValue: cfg.CustomBrowserXSSValue,
|
||||||
CustomFrameOptionsValue: headers.CustomFrameOptionsValue,
|
CustomFrameOptionsValue: cfg.CustomFrameOptionsValue,
|
||||||
PublicKey: headers.PublicKey,
|
PublicKey: cfg.PublicKey,
|
||||||
ReferrerPolicy: headers.ReferrerPolicy,
|
ReferrerPolicy: cfg.ReferrerPolicy,
|
||||||
SSLHost: headers.SSLHost,
|
SSLHost: cfg.SSLHost,
|
||||||
AllowedHosts: headers.AllowedHosts,
|
AllowedHosts: cfg.AllowedHosts,
|
||||||
HostsProxyHeaders: headers.HostsProxyHeaders,
|
HostsProxyHeaders: cfg.HostsProxyHeaders,
|
||||||
SSLProxyHeaders: headers.SSLProxyHeaders,
|
SSLProxyHeaders: cfg.SSLProxyHeaders,
|
||||||
STSSeconds: headers.STSSeconds,
|
STSSeconds: cfg.STSSeconds,
|
||||||
FeaturePolicy: headers.FeaturePolicy,
|
FeaturePolicy: cfg.FeaturePolicy,
|
||||||
}
|
}
|
||||||
|
|
||||||
return &secureHeader{
|
return &secureHeader{
|
||||||
|
@ -119,13 +130,16 @@ type Header struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewHeader constructs a new header instance from supplied frontend header struct.
|
// NewHeader constructs a new header instance from supplied frontend header struct.
|
||||||
func NewHeader(next http.Handler, headers dynamic.Headers) *Header {
|
func NewHeader(next http.Handler, cfg dynamic.Headers) *Header {
|
||||||
hasCustomHeaders := headers.HasCustomHeadersDefined()
|
hasCustomHeaders := cfg.HasCustomHeadersDefined()
|
||||||
hasCorsHeaders := headers.HasCorsHeadersDefined()
|
hasCorsHeaders := cfg.HasCorsHeadersDefined()
|
||||||
|
|
||||||
|
ctx := log.With(context.Background(), log.Str(log.MiddlewareType, typeName))
|
||||||
|
handleDeprecation(ctx, &cfg)
|
||||||
|
|
||||||
return &Header{
|
return &Header{
|
||||||
next: next,
|
next: next,
|
||||||
headers: &headers,
|
headers: &cfg,
|
||||||
hasCustomHeaders: hasCustomHeaders,
|
hasCustomHeaders: hasCustomHeaders,
|
||||||
hasCorsHeaders: hasCorsHeaders,
|
hasCorsHeaders: hasCorsHeaders,
|
||||||
}
|
}
|
||||||
|
@ -159,29 +173,6 @@ func (s *Header) modifyCustomRequestHeaders(req *http.Request) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// preRequestModifyCorsResponseHeaders sets during request processing time,
|
|
||||||
// all the CORS response headers that we already know that are supposed to be set,
|
|
||||||
// and which do not depend on a later state of the response.
|
|
||||||
// One notable example of a header that can only be modified later on is "Vary",
|
|
||||||
// And this is set in the post-response response modifier method
|
|
||||||
func (s *Header) preRequestModifyCorsResponseHeaders(rw http.ResponseWriter, req *http.Request) {
|
|
||||||
originHeader := req.Header.Get("Origin")
|
|
||||||
allowOrigin := s.getAllowOrigin(originHeader)
|
|
||||||
|
|
||||||
if allowOrigin != "" {
|
|
||||||
rw.Header().Set("Access-Control-Allow-Origin", allowOrigin)
|
|
||||||
}
|
|
||||||
|
|
||||||
if s.headers.AccessControlAllowCredentials {
|
|
||||||
rw.Header().Set("Access-Control-Allow-Credentials", "true")
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(s.headers.AccessControlExposeHeaders) > 0 {
|
|
||||||
exposeHeaders := strings.Join(s.headers.AccessControlExposeHeaders, ",")
|
|
||||||
rw.Header().Set("Access-Control-Expose-Headers", exposeHeaders)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// PostRequestModifyResponseHeaders set or delete response headers.
|
// PostRequestModifyResponseHeaders set or delete response headers.
|
||||||
// This method is called AFTER the response is generated from the backend
|
// This method is called AFTER the response is generated from the backend
|
||||||
// and can merge/override headers from the backend response.
|
// and can merge/override headers from the backend response.
|
||||||
|
@ -194,6 +185,25 @@ func (s *Header) PostRequestModifyResponseHeaders(res *http.Response) error {
|
||||||
res.Header.Set(header, value)
|
res.Header.Set(header, value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if res != nil && res.Request != nil {
|
||||||
|
originHeader := res.Request.Header.Get("Origin")
|
||||||
|
allowed, match := s.isOriginAllowed(originHeader)
|
||||||
|
|
||||||
|
if allowed {
|
||||||
|
res.Header.Set("Access-Control-Allow-Origin", match)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.headers.AccessControlAllowCredentials {
|
||||||
|
res.Header.Set("Access-Control-Allow-Credentials", "true")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(s.headers.AccessControlExposeHeaders) > 0 {
|
||||||
|
exposeHeaders := strings.Join(s.headers.AccessControlExposeHeaders, ",")
|
||||||
|
res.Header.Set("Access-Control-Expose-Headers", exposeHeaders)
|
||||||
|
}
|
||||||
|
|
||||||
if !s.headers.AddVaryHeader {
|
if !s.headers.AddVaryHeader {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -241,30 +251,24 @@ func (s *Header) processCorsHeaders(rw http.ResponseWriter, req *http.Request) b
|
||||||
rw.Header().Set("Access-Control-Allow-Methods", allowMethods)
|
rw.Header().Set("Access-Control-Allow-Methods", allowMethods)
|
||||||
}
|
}
|
||||||
|
|
||||||
allowOrigin := s.getAllowOrigin(originHeader)
|
allowed, match := s.isOriginAllowed(originHeader)
|
||||||
|
if allowed {
|
||||||
if allowOrigin != "" {
|
rw.Header().Set("Access-Control-Allow-Origin", match)
|
||||||
rw.Header().Set("Access-Control-Allow-Origin", allowOrigin)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rw.Header().Set("Access-Control-Max-Age", strconv.Itoa(int(s.headers.AccessControlMaxAge)))
|
rw.Header().Set("Access-Control-Max-Age", strconv.Itoa(int(s.headers.AccessControlMaxAge)))
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
s.preRequestModifyCorsResponseHeaders(rw, req)
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Header) getAllowOrigin(header string) string {
|
func (s *Header) isOriginAllowed(origin string) (bool, string) {
|
||||||
switch s.headers.AccessControlAllowOrigin {
|
for _, item := range s.headers.AccessControlAllowOriginList {
|
||||||
case "origin-list-or-null":
|
if item == "*" || item == origin {
|
||||||
if len(header) == 0 {
|
return true, item
|
||||||
return "null"
|
|
||||||
}
|
}
|
||||||
return header
|
|
||||||
case "*":
|
|
||||||
return "*"
|
|
||||||
default:
|
|
||||||
return ""
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false, ""
|
||||||
}
|
}
|
||||||
|
|
|
@ -203,7 +203,7 @@ func TestCORSPreflights(t *testing.T) {
|
||||||
desc: "Test Simple Preflight",
|
desc: "Test Simple Preflight",
|
||||||
header: NewHeader(emptyHandler, dynamic.Headers{
|
header: NewHeader(emptyHandler, dynamic.Headers{
|
||||||
AccessControlAllowMethods: []string{"GET", "OPTIONS", "PUT"},
|
AccessControlAllowMethods: []string{"GET", "OPTIONS", "PUT"},
|
||||||
AccessControlAllowOrigin: "origin-list-or-null",
|
AccessControlAllowOriginList: []string{"https://foo.bar.org"},
|
||||||
AccessControlMaxAge: 600,
|
AccessControlMaxAge: 600,
|
||||||
}),
|
}),
|
||||||
requestHeaders: map[string][]string{
|
requestHeaders: map[string][]string{
|
||||||
|
@ -221,7 +221,7 @@ func TestCORSPreflights(t *testing.T) {
|
||||||
desc: "Wildcard origin Preflight",
|
desc: "Wildcard origin Preflight",
|
||||||
header: NewHeader(emptyHandler, dynamic.Headers{
|
header: NewHeader(emptyHandler, dynamic.Headers{
|
||||||
AccessControlAllowMethods: []string{"GET", "OPTIONS", "PUT"},
|
AccessControlAllowMethods: []string{"GET", "OPTIONS", "PUT"},
|
||||||
AccessControlAllowOrigin: "*",
|
AccessControlAllowOriginList: []string{"*"},
|
||||||
AccessControlMaxAge: 600,
|
AccessControlMaxAge: 600,
|
||||||
}),
|
}),
|
||||||
requestHeaders: map[string][]string{
|
requestHeaders: map[string][]string{
|
||||||
|
@ -239,7 +239,7 @@ func TestCORSPreflights(t *testing.T) {
|
||||||
desc: "Allow Credentials Preflight",
|
desc: "Allow Credentials Preflight",
|
||||||
header: NewHeader(emptyHandler, dynamic.Headers{
|
header: NewHeader(emptyHandler, dynamic.Headers{
|
||||||
AccessControlAllowMethods: []string{"GET", "OPTIONS", "PUT"},
|
AccessControlAllowMethods: []string{"GET", "OPTIONS", "PUT"},
|
||||||
AccessControlAllowOrigin: "*",
|
AccessControlAllowOriginList: []string{"*"},
|
||||||
AccessControlAllowCredentials: true,
|
AccessControlAllowCredentials: true,
|
||||||
AccessControlMaxAge: 600,
|
AccessControlMaxAge: 600,
|
||||||
}),
|
}),
|
||||||
|
@ -259,7 +259,7 @@ func TestCORSPreflights(t *testing.T) {
|
||||||
desc: "Allow Headers Preflight",
|
desc: "Allow Headers Preflight",
|
||||||
header: NewHeader(emptyHandler, dynamic.Headers{
|
header: NewHeader(emptyHandler, dynamic.Headers{
|
||||||
AccessControlAllowMethods: []string{"GET", "OPTIONS", "PUT"},
|
AccessControlAllowMethods: []string{"GET", "OPTIONS", "PUT"},
|
||||||
AccessControlAllowOrigin: "*",
|
AccessControlAllowOriginList: []string{"*"},
|
||||||
AccessControlAllowHeaders: []string{"origin", "X-Forwarded-For"},
|
AccessControlAllowHeaders: []string{"origin", "X-Forwarded-For"},
|
||||||
AccessControlMaxAge: 600,
|
AccessControlMaxAge: 600,
|
||||||
}),
|
}),
|
||||||
|
@ -279,7 +279,7 @@ func TestCORSPreflights(t *testing.T) {
|
||||||
desc: "No Request Headers Preflight",
|
desc: "No Request Headers Preflight",
|
||||||
header: NewHeader(emptyHandler, dynamic.Headers{
|
header: NewHeader(emptyHandler, dynamic.Headers{
|
||||||
AccessControlAllowMethods: []string{"GET", "OPTIONS", "PUT"},
|
AccessControlAllowMethods: []string{"GET", "OPTIONS", "PUT"},
|
||||||
AccessControlAllowOrigin: "*",
|
AccessControlAllowOriginList: []string{"*"},
|
||||||
AccessControlAllowHeaders: []string{"origin", "X-Forwarded-For"},
|
AccessControlAllowHeaders: []string{"origin", "X-Forwarded-For"},
|
||||||
AccessControlMaxAge: 600,
|
AccessControlMaxAge: 600,
|
||||||
}),
|
}),
|
||||||
|
@ -352,6 +352,12 @@ func TestCORSResponses(t *testing.T) {
|
||||||
emptyHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
|
emptyHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
|
||||||
nonEmptyHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Vary", "Testing") })
|
nonEmptyHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Vary", "Testing") })
|
||||||
existingOriginHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Vary", "Origin") })
|
existingOriginHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Vary", "Origin") })
|
||||||
|
existingAccessControlAllowOriginHandlerSet := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Access-Control-Allow-Origin", "http://foo.bar.org")
|
||||||
|
})
|
||||||
|
existingAccessControlAllowOriginHandlerAdd := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Add("Access-Control-Allow-Origin", "http://foo.bar.org")
|
||||||
|
})
|
||||||
|
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
desc string
|
desc string
|
||||||
|
@ -362,7 +368,7 @@ func TestCORSResponses(t *testing.T) {
|
||||||
{
|
{
|
||||||
desc: "Test Simple Request",
|
desc: "Test Simple Request",
|
||||||
header: NewHeader(emptyHandler, dynamic.Headers{
|
header: NewHeader(emptyHandler, dynamic.Headers{
|
||||||
AccessControlAllowOrigin: "origin-list-or-null",
|
AccessControlAllowOriginList: []string{"https://foo.bar.org"},
|
||||||
}),
|
}),
|
||||||
requestHeaders: map[string][]string{
|
requestHeaders: map[string][]string{
|
||||||
"Origin": {"https://foo.bar.org"},
|
"Origin": {"https://foo.bar.org"},
|
||||||
|
@ -374,7 +380,7 @@ func TestCORSResponses(t *testing.T) {
|
||||||
{
|
{
|
||||||
desc: "Wildcard origin Request",
|
desc: "Wildcard origin Request",
|
||||||
header: NewHeader(emptyHandler, dynamic.Headers{
|
header: NewHeader(emptyHandler, dynamic.Headers{
|
||||||
AccessControlAllowOrigin: "*",
|
AccessControlAllowOriginList: []string{"*"},
|
||||||
}),
|
}),
|
||||||
requestHeaders: map[string][]string{
|
requestHeaders: map[string][]string{
|
||||||
"Origin": {"https://foo.bar.org"},
|
"Origin": {"https://foo.bar.org"},
|
||||||
|
@ -386,12 +392,10 @@ func TestCORSResponses(t *testing.T) {
|
||||||
{
|
{
|
||||||
desc: "Empty origin Request",
|
desc: "Empty origin Request",
|
||||||
header: NewHeader(emptyHandler, dynamic.Headers{
|
header: NewHeader(emptyHandler, dynamic.Headers{
|
||||||
AccessControlAllowOrigin: "origin-list-or-null",
|
AccessControlAllowOriginList: []string{"https://foo.bar.org"},
|
||||||
}),
|
}),
|
||||||
requestHeaders: map[string][]string{},
|
requestHeaders: map[string][]string{},
|
||||||
expected: map[string][]string{
|
expected: map[string][]string{},
|
||||||
"Access-Control-Allow-Origin": {"null"},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
desc: "Not Defined origin Request",
|
desc: "Not Defined origin Request",
|
||||||
|
@ -402,7 +406,7 @@ func TestCORSResponses(t *testing.T) {
|
||||||
{
|
{
|
||||||
desc: "Allow Credentials Request",
|
desc: "Allow Credentials Request",
|
||||||
header: NewHeader(emptyHandler, dynamic.Headers{
|
header: NewHeader(emptyHandler, dynamic.Headers{
|
||||||
AccessControlAllowOrigin: "*",
|
AccessControlAllowOriginList: []string{"*"},
|
||||||
AccessControlAllowCredentials: true,
|
AccessControlAllowCredentials: true,
|
||||||
}),
|
}),
|
||||||
requestHeaders: map[string][]string{
|
requestHeaders: map[string][]string{
|
||||||
|
@ -416,7 +420,7 @@ func TestCORSResponses(t *testing.T) {
|
||||||
{
|
{
|
||||||
desc: "Expose Headers Request",
|
desc: "Expose Headers Request",
|
||||||
header: NewHeader(emptyHandler, dynamic.Headers{
|
header: NewHeader(emptyHandler, dynamic.Headers{
|
||||||
AccessControlAllowOrigin: "*",
|
AccessControlAllowOriginList: []string{"*"},
|
||||||
AccessControlExposeHeaders: []string{"origin", "X-Forwarded-For"},
|
AccessControlExposeHeaders: []string{"origin", "X-Forwarded-For"},
|
||||||
}),
|
}),
|
||||||
requestHeaders: map[string][]string{
|
requestHeaders: map[string][]string{
|
||||||
|
@ -430,7 +434,7 @@ func TestCORSResponses(t *testing.T) {
|
||||||
{
|
{
|
||||||
desc: "Test Simple Request with Vary Headers",
|
desc: "Test Simple Request with Vary Headers",
|
||||||
header: NewHeader(emptyHandler, dynamic.Headers{
|
header: NewHeader(emptyHandler, dynamic.Headers{
|
||||||
AccessControlAllowOrigin: "origin-list-or-null",
|
AccessControlAllowOriginList: []string{"https://foo.bar.org"},
|
||||||
AddVaryHeader: true,
|
AddVaryHeader: true,
|
||||||
}),
|
}),
|
||||||
requestHeaders: map[string][]string{
|
requestHeaders: map[string][]string{
|
||||||
|
@ -444,7 +448,7 @@ func TestCORSResponses(t *testing.T) {
|
||||||
{
|
{
|
||||||
desc: "Test Simple Request with Vary Headers and non-empty response",
|
desc: "Test Simple Request with Vary Headers and non-empty response",
|
||||||
header: NewHeader(nonEmptyHandler, dynamic.Headers{
|
header: NewHeader(nonEmptyHandler, dynamic.Headers{
|
||||||
AccessControlAllowOrigin: "origin-list-or-null",
|
AccessControlAllowOriginList: []string{"https://foo.bar.org"},
|
||||||
AddVaryHeader: true,
|
AddVaryHeader: true,
|
||||||
}),
|
}),
|
||||||
requestHeaders: map[string][]string{
|
requestHeaders: map[string][]string{
|
||||||
|
@ -458,7 +462,7 @@ func TestCORSResponses(t *testing.T) {
|
||||||
{
|
{
|
||||||
desc: "Test Simple Request with Vary Headers and existing vary:origin response",
|
desc: "Test Simple Request with Vary Headers and existing vary:origin response",
|
||||||
header: NewHeader(existingOriginHandler, dynamic.Headers{
|
header: NewHeader(existingOriginHandler, dynamic.Headers{
|
||||||
AccessControlAllowOrigin: "origin-list-or-null",
|
AccessControlAllowOriginList: []string{"https://foo.bar.org"},
|
||||||
AddVaryHeader: true,
|
AddVaryHeader: true,
|
||||||
}),
|
}),
|
||||||
requestHeaders: map[string][]string{
|
requestHeaders: map[string][]string{
|
||||||
|
@ -470,6 +474,29 @@ func TestCORSResponses(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
desc: "Test Simple Request with non-empty response: set ACAO",
|
||||||
|
header: NewHeader(existingAccessControlAllowOriginHandlerSet, dynamic.Headers{
|
||||||
|
AccessControlAllowOriginList: []string{"*"},
|
||||||
|
}),
|
||||||
|
requestHeaders: map[string][]string{
|
||||||
|
"Origin": {"https://foo.bar.org"},
|
||||||
|
},
|
||||||
|
expected: map[string][]string{
|
||||||
|
"Access-Control-Allow-Origin": {"*"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Test Simple Request with non-empty response: add ACAO",
|
||||||
|
header: NewHeader(existingAccessControlAllowOriginHandlerAdd, dynamic.Headers{
|
||||||
|
AccessControlAllowOriginList: []string{"*"},
|
||||||
|
}),
|
||||||
|
requestHeaders: map[string][]string{
|
||||||
|
"Origin": {"https://foo.bar.org"},
|
||||||
|
},
|
||||||
|
expected: map[string][]string{
|
||||||
|
"Access-Control-Allow-Origin": {"*"},
|
||||||
|
},
|
||||||
|
}, {
|
||||||
desc: "Test Simple CustomRequestHeaders Not Hijacked by CORS",
|
desc: "Test Simple CustomRequestHeaders Not Hijacked by CORS",
|
||||||
header: NewHeader(emptyHandler, dynamic.Headers{
|
header: NewHeader(emptyHandler, dynamic.Headers{
|
||||||
CustomRequestHeaders: map[string]string{"foo": "bar"},
|
CustomRequestHeaders: map[string]string{"foo": "bar"},
|
||||||
|
@ -487,10 +514,11 @@ func TestCORSResponses(t *testing.T) {
|
||||||
t.Run(test.desc, func(t *testing.T) {
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
req := testhelpers.MustNewRequest(http.MethodGet, "/foo", nil)
|
req := testhelpers.MustNewRequest(http.MethodGet, "/foo", nil)
|
||||||
req.Header = test.requestHeaders
|
req.Header = test.requestHeaders
|
||||||
|
|
||||||
rw := httptest.NewRecorder()
|
rw := httptest.NewRecorder()
|
||||||
test.header.ServeHTTP(rw, req)
|
test.header.ServeHTTP(rw, req)
|
||||||
err := test.header.PostRequestModifyResponseHeaders(rw.Result())
|
res := rw.Result()
|
||||||
|
res.Request = req
|
||||||
|
err := test.header.PostRequestModifyResponseHeaders(res)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, test.expected, rw.Result().Header)
|
assert.Equal(t, test.expected, rw.Result().Header)
|
||||||
})
|
})
|
||||||
|
|
|
@ -94,6 +94,8 @@ func Test_buildConfiguration(t *testing.T) {
|
||||||
"traefik/http/middlewares/Middleware09/headers/accessControlAllowHeaders/0": "foobar",
|
"traefik/http/middlewares/Middleware09/headers/accessControlAllowHeaders/0": "foobar",
|
||||||
"traefik/http/middlewares/Middleware09/headers/accessControlAllowHeaders/1": "foobar",
|
"traefik/http/middlewares/Middleware09/headers/accessControlAllowHeaders/1": "foobar",
|
||||||
"traefik/http/middlewares/Middleware09/headers/accessControlAllowOrigin": "foobar",
|
"traefik/http/middlewares/Middleware09/headers/accessControlAllowOrigin": "foobar",
|
||||||
|
"traefik/http/middlewares/Middleware09/headers/accessControlAllowOriginList/0": "foobar",
|
||||||
|
"traefik/http/middlewares/Middleware09/headers/accessControlAllowOriginList/1": "foobar",
|
||||||
"traefik/http/middlewares/Middleware09/headers/contentTypeNosniff": "true",
|
"traefik/http/middlewares/Middleware09/headers/contentTypeNosniff": "true",
|
||||||
"traefik/http/middlewares/Middleware09/headers/accessControlAllowCredentials": "true",
|
"traefik/http/middlewares/Middleware09/headers/accessControlAllowCredentials": "true",
|
||||||
"traefik/http/middlewares/Middleware09/headers/featurePolicy": "foobar",
|
"traefik/http/middlewares/Middleware09/headers/featurePolicy": "foobar",
|
||||||
|
@ -543,6 +545,10 @@ func Test_buildConfiguration(t *testing.T) {
|
||||||
"foobar",
|
"foobar",
|
||||||
},
|
},
|
||||||
AccessControlAllowOrigin: "foobar",
|
AccessControlAllowOrigin: "foobar",
|
||||||
|
AccessControlAllowOriginList: []string{
|
||||||
|
"foobar",
|
||||||
|
"foobar",
|
||||||
|
},
|
||||||
AccessControlExposeHeaders: []string{
|
AccessControlExposeHeaders: []string{
|
||||||
"foobar",
|
"foobar",
|
||||||
"foobar",
|
"foobar",
|
||||||
|
|
Loading…
Reference in a new issue