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 }} + +
+
+