From 326be29568426e4ac8e2bf65fdae3906e8dd58fd Mon Sep 17 00:00:00 2001
From: Nikita Konev <3160384+nkonev@users.noreply.github.com>
Date: Wed, 7 Oct 2020 17:36:04 +0300
Subject: [PATCH] Filter ForwardAuth request headers
---
docs/content/middlewares/forwardauth.md | 57 +++++++++++++++++++
.../dynamic-configuration/docker-labels.yml | 1 +
.../reference/dynamic-configuration/file.toml | 1 +
.../reference/dynamic-configuration/file.yaml | 3 +
.../marathon-labels.json | 1 +
pkg/config/dynamic/fixtures/sample.toml | 1 +
pkg/config/dynamic/middlewares.go | 1 +
pkg/config/dynamic/zz_generated.deepcopy.go | 5 ++
pkg/config/label/label_test.go | 10 ++++
pkg/middlewares/auth/forward.go | 24 +++++++-
pkg/middlewares/auth/forward_test.go | 43 +++++++++++++-
pkg/provider/kubernetes/crd/kubernetes.go | 1 +
.../crd/traefik/v1alpha1/middleware.go | 1 +
.../traefik/v1alpha1/zz_generated.deepcopy.go | 5 ++
pkg/provider/kv/kv_test.go | 6 ++
.../components/_commons/PanelMiddlewares.vue | 14 +++++
16 files changed, 171 insertions(+), 3 deletions(-)
diff --git a/docs/content/middlewares/forwardauth.md b/docs/content/middlewares/forwardauth.md
index c98d9b9aa..5580e0e5f 100644
--- a/docs/content/middlewares/forwardauth.md
+++ b/docs/content/middlewares/forwardauth.md
@@ -217,6 +217,63 @@ http:
- "X-Secret"
```
+### `authRequestHeaders`
+
+The `authRequestHeaders` option is the list of the headers to copy from the request to the authentication server.
+It allows to prevent passing headers that have not to be passed to the authentication server.
+If not set or empty then all request headers will be passed.
+
+```yaml tab="Docker"
+labels:
+ - "traefik.http.middlewares.test-auth.forwardauth.authRequestHeaders=Accept,X-CustomHeader"
+```
+
+```yaml tab="Kubernetes"
+apiVersion: traefik.containo.us/v1alpha1
+kind: Middleware
+metadata:
+ name: test-auth
+spec:
+ forwardAuth:
+ address: https://example.com/auth
+ authRequestHeaders:
+ - "Accept"
+ - "X-CustomHeader"
+```
+
+```yaml tab="Consul Catalog"
+- "traefik.http.middlewares.test-auth.forwardauth.authRequestHeaders=Accept,X-CustomHeader"
+```
+
+```json tab="Marathon"
+"labels": {
+ "traefik.http.middlewares.test-auth.forwardauth.authRequestHeaders": "Accept,X-CustomHeader"
+}
+```
+
+```yaml tab="Rancher"
+labels:
+ - "traefik.http.middlewares.test-auth.forwardauth.authRequestHeaders=Accept,X-CustomHeader"
+```
+
+```toml tab="File (TOML)"
+[http.middlewares]
+ [http.middlewares.test-auth.forwardAuth]
+ address = "https://example.com/auth"
+ authRequestHeaders = "Accept,X-CustomHeader"
+```
+
+```yaml tab="File (YAML)"
+http:
+ middlewares:
+ test-auth:
+ forwardAuth:
+ address: "https://example.com/auth"
+ authRequestHeaders:
+ - "Accept"
+ - "X-CustomHeader"
+```
+
### `tls`
The `tls` option is the TLS configuration from Traefik to the authentication server.
diff --git a/docs/content/reference/dynamic-configuration/docker-labels.yml b/docs/content/reference/dynamic-configuration/docker-labels.yml
index 05d33bd98..fd4ef1113 100644
--- a/docs/content/reference/dynamic-configuration/docker-labels.yml
+++ b/docs/content/reference/dynamic-configuration/docker-labels.yml
@@ -24,6 +24,7 @@
- "traefik.http.middlewares.middleware08.errors.status=foobar, foobar"
- "traefik.http.middlewares.middleware09.forwardauth.address=foobar"
- "traefik.http.middlewares.middleware09.forwardauth.authresponseheaders=foobar, foobar"
+- "traefik.http.middlewares.middleware09.forwardauth.authrequestheaders=foobar, foobar"
- "traefik.http.middlewares.middleware09.forwardauth.tls.ca=foobar"
- "traefik.http.middlewares.middleware09.forwardauth.tls.caoptional=true"
- "traefik.http.middlewares.middleware09.forwardauth.tls.cert=foobar"
diff --git a/docs/content/reference/dynamic-configuration/file.toml b/docs/content/reference/dynamic-configuration/file.toml
index 061af180e..67ebefdf7 100644
--- a/docs/content/reference/dynamic-configuration/file.toml
+++ b/docs/content/reference/dynamic-configuration/file.toml
@@ -139,6 +139,7 @@
address = "foobar"
trustForwardHeader = true
authResponseHeaders = ["foobar", "foobar"]
+ authRequestHeaders = ["foobar", "foobar"]
[http.middlewares.Middleware09.forwardAuth.tls]
ca = "foobar"
caOptional = true
diff --git a/docs/content/reference/dynamic-configuration/file.yaml b/docs/content/reference/dynamic-configuration/file.yaml
index 9a9076ee6..5f8f36f51 100644
--- a/docs/content/reference/dynamic-configuration/file.yaml
+++ b/docs/content/reference/dynamic-configuration/file.yaml
@@ -158,6 +158,9 @@ http:
authResponseHeaders:
- foobar
- foobar
+ authRequestHeaders:
+ - foobar
+ - foobar
Middleware10:
headers:
customRequestHeaders:
diff --git a/docs/content/reference/dynamic-configuration/marathon-labels.json b/docs/content/reference/dynamic-configuration/marathon-labels.json
index 3e9552dc0..a67628229 100644
--- a/docs/content/reference/dynamic-configuration/marathon-labels.json
+++ b/docs/content/reference/dynamic-configuration/marathon-labels.json
@@ -24,6 +24,7 @@
"traefik.http.middlewares.middleware08.errors.status": "foobar, foobar",
"traefik.http.middlewares.middleware09.forwardauth.address": "foobar",
"traefik.http.middlewares.middleware09.forwardauth.authresponseheaders": "foobar, foobar",
+"traefik.http.middlewares.middleware09.forwardauth.authrequestheaders": "foobar, foobar",
"traefik.http.middlewares.middleware09.forwardauth.tls.ca": "foobar",
"traefik.http.middlewares.middleware09.forwardauth.tls.caoptional": "true",
"traefik.http.middlewares.middleware09.forwardauth.tls.cert": "foobar",
diff --git a/pkg/config/dynamic/fixtures/sample.toml b/pkg/config/dynamic/fixtures/sample.toml
index e9503550f..ca69b7925 100644
--- a/pkg/config/dynamic/fixtures/sample.toml
+++ b/pkg/config/dynamic/fixtures/sample.toml
@@ -288,6 +288,7 @@
address = "foobar"
trustForwardHeader = true
authResponseHeaders = ["foobar", "foobar"]
+ authRequestHeaders = ["foobar", "foobar"]
[http.middlewares.Middleware15.forwardAuth.tls]
ca = "foobar"
caOptional = true
diff --git a/pkg/config/dynamic/middlewares.go b/pkg/config/dynamic/middlewares.go
index e4b507355..57fee2f42 100644
--- a/pkg/config/dynamic/middlewares.go
+++ b/pkg/config/dynamic/middlewares.go
@@ -144,6 +144,7 @@ type ForwardAuth struct {
TLS *ClientTLS `json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty"`
TrustForwardHeader bool `json:"trustForwardHeader,omitempty" toml:"trustForwardHeader,omitempty" yaml:"trustForwardHeader,omitempty" export:"true"`
AuthResponseHeaders []string `json:"authResponseHeaders,omitempty" toml:"authResponseHeaders,omitempty" yaml:"authResponseHeaders,omitempty"`
+ AuthRequestHeaders []string `json:"authRequestHeaders,omitempty" toml:"authRequestHeaders,omitempty" yaml:"authRequestHeaders,omitempty"`
}
// +k8s:deepcopy-gen=true
diff --git a/pkg/config/dynamic/zz_generated.deepcopy.go b/pkg/config/dynamic/zz_generated.deepcopy.go
index 54c40e6ea..9733bed13 100644
--- a/pkg/config/dynamic/zz_generated.deepcopy.go
+++ b/pkg/config/dynamic/zz_generated.deepcopy.go
@@ -344,6 +344,11 @@ func (in *ForwardAuth) DeepCopyInto(out *ForwardAuth) {
*out = make([]string, len(*in))
copy(*out, *in)
}
+ if in.AuthRequestHeaders != nil {
+ in, out := &in.AuthRequestHeaders, &out.AuthRequestHeaders
+ *out = make([]string, len(*in))
+ copy(*out, *in)
+ }
return
}
diff --git a/pkg/config/label/label_test.go b/pkg/config/label/label_test.go
index e78ccd420..54f9c5358 100644
--- a/pkg/config/label/label_test.go
+++ b/pkg/config/label/label_test.go
@@ -36,6 +36,7 @@ func TestDecodeConfiguration(t *testing.T) {
"traefik.http.middlewares.Middleware6.errors.status": "foobar, fiibar",
"traefik.http.middlewares.Middleware7.forwardauth.address": "foobar",
"traefik.http.middlewares.Middleware7.forwardauth.authresponseheaders": "foobar, fiibar",
+ "traefik.http.middlewares.Middleware7.forwardauth.authrequestheaders": "foobar, fiibar",
"traefik.http.middlewares.Middleware7.forwardauth.tls.ca": "foobar",
"traefik.http.middlewares.Middleware7.forwardauth.tls.caoptional": "true",
"traefik.http.middlewares.Middleware7.forwardauth.tls.cert": "foobar",
@@ -496,6 +497,10 @@ func TestDecodeConfiguration(t *testing.T) {
"foobar",
"fiibar",
},
+ AuthRequestHeaders: []string{
+ "foobar",
+ "fiibar",
+ },
},
},
"Middleware8": {
@@ -964,6 +969,10 @@ func TestEncodeConfiguration(t *testing.T) {
"foobar",
"fiibar",
},
+ AuthRequestHeaders: []string{
+ "foobar",
+ "fiibar",
+ },
},
},
"Middleware8": {
@@ -1134,6 +1143,7 @@ func TestEncodeConfiguration(t *testing.T) {
"traefik.HTTP.Middlewares.Middleware6.Errors.Status": "foobar, fiibar",
"traefik.HTTP.Middlewares.Middleware7.ForwardAuth.Address": "foobar",
"traefik.HTTP.Middlewares.Middleware7.ForwardAuth.AuthResponseHeaders": "foobar, fiibar",
+ "traefik.HTTP.Middlewares.Middleware7.ForwardAuth.AuthRequestHeaders": "foobar, fiibar",
"traefik.HTTP.Middlewares.Middleware7.ForwardAuth.TLS.CA": "foobar",
"traefik.HTTP.Middlewares.Middleware7.ForwardAuth.TLS.CAOptional": "true",
"traefik.HTTP.Middlewares.Middleware7.ForwardAuth.TLS.Cert": "foobar",
diff --git a/pkg/middlewares/auth/forward.go b/pkg/middlewares/auth/forward.go
index e3a0bd067..38ba9ed0a 100644
--- a/pkg/middlewares/auth/forward.go
+++ b/pkg/middlewares/auth/forward.go
@@ -31,6 +31,7 @@ type forwardAuth struct {
name string
client http.Client
trustForwardHeader bool
+ authRequestHeaders []string
}
// NewForward creates a forward auth middleware.
@@ -43,6 +44,7 @@ func NewForward(ctx context.Context, next http.Handler, config dynamic.ForwardAu
next: next,
name: name,
trustForwardHeader: config.TrustForwardHeader,
+ authRequestHeaders: config.AuthRequestHeaders,
}
// Ensure our request client does not follow redirects
@@ -89,7 +91,7 @@ func (fa *forwardAuth) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
// forwardReq.
tracing.InjectRequestHeaders(req)
- writeHeader(req, forwardReq, fa.trustForwardHeader)
+ writeHeader(req, forwardReq, fa.trustForwardHeader, fa.authRequestHeaders)
forwardResponse, forwardErr := fa.client.Do(forwardReq)
if forwardErr != nil {
@@ -158,10 +160,12 @@ func (fa *forwardAuth) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
fa.next.ServeHTTP(rw, req)
}
-func writeHeader(req, forwardReq *http.Request, trustForwardHeader bool) {
+func writeHeader(req, forwardReq *http.Request, trustForwardHeader bool, allowedHeaders []string) {
utils.CopyHeaders(forwardReq.Header, req.Header)
utils.RemoveHeaders(forwardReq.Header, forward.HopHeaders...)
+ forwardReq.Header = filterForwardRequestHeaders(forwardReq.Header, allowedHeaders)
+
if clientIP, _, err := net.SplitHostPort(req.RemoteAddr); err == nil {
if trustForwardHeader {
if prior, ok := req.Header[forward.XForwardedFor]; ok {
@@ -215,3 +219,19 @@ func writeHeader(req, forwardReq *http.Request, trustForwardHeader bool) {
forwardReq.Header.Del(xForwardedURI)
}
}
+
+func filterForwardRequestHeaders(forwardRequestHeaders http.Header, allowedHeaders []string) http.Header {
+ if len(allowedHeaders) == 0 {
+ return forwardRequestHeaders
+ }
+
+ filteredHeaders := http.Header{}
+ for _, headerName := range allowedHeaders {
+ values := forwardRequestHeaders.Values(headerName)
+ if len(values) > 0 {
+ filteredHeaders[http.CanonicalHeaderKey(headerName)] = append([]string(nil), values...)
+ }
+ }
+
+ return filteredHeaders
+}
diff --git a/pkg/middlewares/auth/forward_test.go b/pkg/middlewares/auth/forward_test.go
index 400e21cee..0ce8bbf9e 100644
--- a/pkg/middlewares/auth/forward_test.go
+++ b/pkg/middlewares/auth/forward_test.go
@@ -242,6 +242,7 @@ func Test_writeHeader(t *testing.T) {
testCases := []struct {
name string
headers map[string]string
+ authRequestHeaders []string
trustForwardHeader bool
emptyHost bool
expectedHeaders map[string]string
@@ -368,6 +369,45 @@ func Test_writeHeader(t *testing.T) {
},
checkForUnexpectedHeaders: true,
},
+ {
+ name: "filter forward request headers",
+ headers: map[string]string{
+ "X-CustomHeader": "CustomHeader",
+ "Content-Type": "multipart/form-data; boundary=---123456",
+ },
+ authRequestHeaders: []string{
+ "X-CustomHeader",
+ },
+ trustForwardHeader: false,
+ expectedHeaders: map[string]string{
+ "x-customHeader": "CustomHeader",
+ "X-Forwarded-Proto": "http",
+ "X-Forwarded-Host": "foo.bar",
+ "X-Forwarded-Uri": "/path?q=1",
+ "X-Forwarded-Method": "GET",
+ },
+ checkForUnexpectedHeaders: true,
+ },
+ {
+ name: "filter forward request headers doesn't add new headers",
+ headers: map[string]string{
+ "X-CustomHeader": "CustomHeader",
+ "Content-Type": "multipart/form-data; boundary=---123456",
+ },
+ authRequestHeaders: []string{
+ "X-CustomHeader",
+ "X-Non-Exists-Header",
+ },
+ trustForwardHeader: false,
+ expectedHeaders: map[string]string{
+ "X-CustomHeader": "CustomHeader",
+ "X-Forwarded-Proto": "http",
+ "X-Forwarded-Host": "foo.bar",
+ "X-Forwarded-Uri": "/path?q=1",
+ "X-Forwarded-Method": "GET",
+ },
+ checkForUnexpectedHeaders: true,
+ },
}
for _, test := range testCases {
@@ -383,9 +423,10 @@ func Test_writeHeader(t *testing.T) {
forwardReq := testhelpers.MustNewRequest(http.MethodGet, "http://foo.bar/path?q=1", nil)
- writeHeader(req, forwardReq, test.trustForwardHeader)
+ writeHeader(req, forwardReq, test.trustForwardHeader, test.authRequestHeaders)
actualHeaders := forwardReq.Header
+
expectedHeaders := test.expectedHeaders
for key, value := range expectedHeaders {
assert.Equal(t, value, actualHeaders.Get(key))
diff --git a/pkg/provider/kubernetes/crd/kubernetes.go b/pkg/provider/kubernetes/crd/kubernetes.go
index 4cbe00c06..b30524ab7 100644
--- a/pkg/provider/kubernetes/crd/kubernetes.go
+++ b/pkg/provider/kubernetes/crd/kubernetes.go
@@ -376,6 +376,7 @@ func createForwardAuthMiddleware(k8sClient Client, namespace string, auth *v1alp
Address: auth.Address,
TrustForwardHeader: auth.TrustForwardHeader,
AuthResponseHeaders: auth.AuthResponseHeaders,
+ AuthRequestHeaders: auth.AuthRequestHeaders,
}
if auth.TLS == nil {
diff --git a/pkg/provider/kubernetes/crd/traefik/v1alpha1/middleware.go b/pkg/provider/kubernetes/crd/traefik/v1alpha1/middleware.go
index 9dfd1e57b..450f5bad9 100644
--- a/pkg/provider/kubernetes/crd/traefik/v1alpha1/middleware.go
+++ b/pkg/provider/kubernetes/crd/traefik/v1alpha1/middleware.go
@@ -88,6 +88,7 @@ type ForwardAuth struct {
Address string `json:"address,omitempty"`
TrustForwardHeader bool `json:"trustForwardHeader,omitempty"`
AuthResponseHeaders []string `json:"authResponseHeaders,omitempty"`
+ AuthRequestHeaders []string `json:"authRequestHeaders,omitempty"`
TLS *ClientTLS `json:"tls,omitempty"`
}
diff --git a/pkg/provider/kubernetes/crd/traefik/v1alpha1/zz_generated.deepcopy.go b/pkg/provider/kubernetes/crd/traefik/v1alpha1/zz_generated.deepcopy.go
index 51b080561..d5d6e6ad9 100644
--- a/pkg/provider/kubernetes/crd/traefik/v1alpha1/zz_generated.deepcopy.go
+++ b/pkg/provider/kubernetes/crd/traefik/v1alpha1/zz_generated.deepcopy.go
@@ -171,6 +171,11 @@ func (in *ForwardAuth) DeepCopyInto(out *ForwardAuth) {
*out = make([]string, len(*in))
copy(*out, *in)
}
+ if in.AuthRequestHeaders != nil {
+ in, out := &in.AuthRequestHeaders, &out.AuthRequestHeaders
+ *out = make([]string, len(*in))
+ copy(*out, *in)
+ }
if in.TLS != nil {
in, out := &in.TLS, &out.TLS
*out = new(ClientTLS)
diff --git a/pkg/provider/kv/kv_test.go b/pkg/provider/kv/kv_test.go
index d2dcda5f9..4d3c5f16f 100644
--- a/pkg/provider/kv/kv_test.go
+++ b/pkg/provider/kv/kv_test.go
@@ -71,6 +71,8 @@ func Test_buildConfiguration(t *testing.T) {
"traefik/http/services/Service03/weighted/services/1/weight": "42",
"traefik/http/middlewares/Middleware08/forwardAuth/authResponseHeaders/0": "foobar",
"traefik/http/middlewares/Middleware08/forwardAuth/authResponseHeaders/1": "foobar",
+ "traefik/http/middlewares/Middleware08/forwardAuth/authRequestHeaders/0": "foobar",
+ "traefik/http/middlewares/Middleware08/forwardAuth/authRequestHeaders/1": "foobar",
"traefik/http/middlewares/Middleware08/forwardAuth/tls/key": "foobar",
"traefik/http/middlewares/Middleware08/forwardAuth/tls/insecureSkipVerify": "true",
"traefik/http/middlewares/Middleware08/forwardAuth/tls/ca": "foobar",
@@ -407,6 +409,10 @@ func Test_buildConfiguration(t *testing.T) {
"foobar",
"foobar",
},
+ AuthRequestHeaders: []string{
+ "foobar",
+ "foobar",
+ },
},
},
"Middleware06": {
diff --git a/webui/src/components/_commons/PanelMiddlewares.vue b/webui/src/components/_commons/PanelMiddlewares.vue
index daab41a76..89ee4dd74 100644
--- a/webui/src/components/_commons/PanelMiddlewares.vue
+++ b/webui/src/components/_commons/PanelMiddlewares.vue
@@ -305,6 +305,20 @@
+
+
+
+
+
Auth Request Headers
+
+ {{ reqHeader }}
+
+
+
+