Filter ForwardAuth request headers

This commit is contained in:
Nikita Konev 2020-10-07 17:36:04 +03:00 committed by GitHub
parent f2e53a3569
commit 326be29568
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 171 additions and 3 deletions

View file

@ -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.

View file

@ -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"

View file

@ -139,6 +139,7 @@
address = "foobar"
trustForwardHeader = true
authResponseHeaders = ["foobar", "foobar"]
authRequestHeaders = ["foobar", "foobar"]
[http.middlewares.Middleware09.forwardAuth.tls]
ca = "foobar"
caOptional = true

View file

@ -158,6 +158,9 @@ http:
authResponseHeaders:
- foobar
- foobar
authRequestHeaders:
- foobar
- foobar
Middleware10:
headers:
customRequestHeaders:

View file

@ -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",

View file

@ -288,6 +288,7 @@
address = "foobar"
trustForwardHeader = true
authResponseHeaders = ["foobar", "foobar"]
authRequestHeaders = ["foobar", "foobar"]
[http.middlewares.Middleware15.forwardAuth.tls]
ca = "foobar"
caOptional = true

View file

@ -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

View file

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

View file

@ -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",

View file

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

View file

@ -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))

View file

@ -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 {

View file

@ -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"`
}

View file

@ -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)

View file

@ -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": {

View file

@ -305,6 +305,20 @@
</div>
</div>
</q-card-section>
<!-- EXTRA FIELDS FROM MIDDLEWARES - [forwardAuth] - authRequestHeaders -->
<q-card-section v-if="middleware.forwardAuth">
<div class="row items-start no-wrap">
<div class="col">
<div class="text-subtitle2">Auth Request Headers</div>
<q-chip
v-for="(reqHeader, key) in exData(middleware).authRequestHeaders" :key="key"
dense
class="app-chip app-chip-green">
{{ reqHeader }}
</q-chip>
</div>
</div>
</q-card-section>
<!-- EXTRA FIELDS FROM MIDDLEWARES - [headers] - customRequestHeaders -->
<q-card-section v-if="middleware.headers">