Middlewares: add forwardAuth.authResponseHeadersRegex

This commit is contained in:
iamolegga 2020-10-29 17:10:04 +03:00 committed by GitHub
parent b5198e63c4
commit 49cdb67ddc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 116 additions and 24 deletions

View file

@ -164,7 +164,7 @@ http:
### `authResponseHeaders`
The `authResponseHeaders` option is the list of the headers to copy from the authentication server to the request.
The `authResponseHeaders` option is the list of the headers to copy from the authentication server to the request. All incoming request's headers in this list are deleted from the request before any copy happens.
```yaml tab="Docker"
labels:
@ -217,6 +217,59 @@ http:
- "X-Secret"
```
### `authResponseHeadersRegex`
The `authResponseHeadersRegex` option is the regex to match the headers that should be copied from the authentication server to the request. All incoming request's headers matching this regex are deleted from the request before any copy happens.
It allows partial matching of the regular expression against the header's key.
You should use start of string (`^`) and end of string (`$`) anchors to ensure a full match against the header's key.
```yaml tab="Docker"
labels:
- "traefik.http.middlewares.test-auth.forwardauth.authResponseHeadersRegex=^X-"
```
```yaml tab="Kubernetes"
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
name: test-auth
spec:
forwardAuth:
address: https://example.com/auth
authResponseHeadersRegex: ^X-
```
```yaml tab="Consul Catalog"
- "traefik.http.middlewares.test-auth.forwardauth.authResponseHeadersRegex=^X-"
```
```json tab="Marathon"
"labels": {
"traefik.http.middlewares.test-auth.forwardauth.authResponseHeadersRegex": "^X-"
}
```
```yaml tab="Rancher"
labels:
- "traefik.http.middlewares.test-auth.forwardauth.authResponseHeadersRegex=^X-"
```
```toml tab="File (TOML)"
[http.middlewares]
[http.middlewares.test-auth.forwardAuth]
address = "https://example.com/auth"
authResponseHeadersRegex = "^X-"
```
```yaml tab="File (YAML)"
http:
middlewares:
test-auth:
forwardAuth:
address: "https://example.com/auth"
authResponseHeadersRegex: "^X-"
```
### `authRequestHeaders`
The `authRequestHeaders` option is the list of the headers to copy from the request 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.authresponseheadersregex=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"

View file

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

View file

@ -158,6 +158,7 @@ http:
authResponseHeaders:
- foobar
- foobar
authResponseHeadersRegex: foobar
authRequestHeaders:
- foobar
- foobar

View file

@ -31,6 +31,7 @@
| `traefik/http/middlewares/Middleware09/forwardAuth/authRequestHeaders/1` | `foobar` |
| `traefik/http/middlewares/Middleware09/forwardAuth/authResponseHeaders/0` | `foobar` |
| `traefik/http/middlewares/Middleware09/forwardAuth/authResponseHeaders/1` | `foobar` |
| `traefik/http/middlewares/Middleware09/forwardAuth/authResponseHeadersRegex` | `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

@ -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.authresponseheadersregex": "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",

View file

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

View file

@ -140,11 +140,12 @@ type ErrorPage struct {
// ForwardAuth holds the http forward authentication configuration.
type ForwardAuth struct {
Address string `json:"address,omitempty" toml:"address,omitempty" yaml:"address,omitempty"`
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"`
Address string `json:"address,omitempty" toml:"address,omitempty" yaml:"address,omitempty"`
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"`
AuthResponseHeadersRegex string `json:"authResponseHeadersRegex,omitempty" toml:"authResponseHeadersRegex,omitempty" yaml:"authResponseHeadersRegex,omitempty"`
AuthRequestHeaders []string `json:"authRequestHeaders,omitempty" toml:"authRequestHeaders,omitempty" yaml:"authRequestHeaders,omitempty"`
}
// +k8s:deepcopy-gen=true

View file

@ -6,6 +6,7 @@ import (
"io/ioutil"
"net"
"net/http"
"regexp"
"strings"
"time"
@ -25,13 +26,14 @@ const (
)
type forwardAuth struct {
address string
authResponseHeaders []string
next http.Handler
name string
client http.Client
trustForwardHeader bool
authRequestHeaders []string
address string
authResponseHeaders []string
authResponseHeadersRegex *regexp.Regexp
next http.Handler
name string
client http.Client
trustForwardHeader bool
authRequestHeaders []string
}
// NewForward creates a forward auth middleware.
@ -66,6 +68,14 @@ func NewForward(ctx context.Context, next http.Handler, config dynamic.ForwardAu
fa.client.Transport = tr
}
if config.AuthResponseHeadersRegex != "" {
re, err := regexp.Compile(config.AuthResponseHeadersRegex)
if err != nil {
return nil, fmt.Errorf("error compiling regular expression %s: %w", config.AuthResponseHeadersRegex, err)
}
fa.authResponseHeadersRegex = re
}
return fa, nil
}
@ -156,6 +166,20 @@ func (fa *forwardAuth) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
}
}
if fa.authResponseHeadersRegex != nil {
for headerKey := range req.Header {
if fa.authResponseHeadersRegex.MatchString(headerKey) {
req.Header.Del(headerKey)
}
}
for headerKey, headerValues := range forwardResponse.Header {
if fa.authResponseHeadersRegex.MatchString(headerKey) {
req.Header[headerKey] = append([]string(nil), headerValues...)
}
}
}
req.RequestURI = req.URL.RequestURI()
fa.next.ServeHTTP(rw, req)
}

View file

@ -57,6 +57,7 @@ func TestForwardAuthSuccess(t *testing.T) {
w.Header().Set("X-Auth-Secret", "secret")
w.Header().Add("X-Auth-Group", "group1")
w.Header().Add("X-Auth-Group", "group2")
w.Header().Add("Foo-Bar", "auth-value")
fmt.Fprintln(w, "Success")
}))
t.Cleanup(server.Close)
@ -65,12 +66,15 @@ func TestForwardAuthSuccess(t *testing.T) {
assert.Equal(t, "user@example.com", r.Header.Get("X-Auth-User"))
assert.Empty(t, r.Header.Get("X-Auth-Secret"))
assert.Equal(t, []string{"group1", "group2"}, r.Header["X-Auth-Group"])
assert.Equal(t, "auth-value", r.Header.Get("Foo-Bar"))
assert.Empty(t, r.Header.Get("Foo-Baz"))
fmt.Fprintln(w, "traefik")
})
auth := dynamic.ForwardAuth{
Address: server.URL,
AuthResponseHeaders: []string{"X-Auth-User", "X-Auth-Group"},
Address: server.URL,
AuthResponseHeaders: []string{"X-Auth-User", "X-Auth-Group"},
AuthResponseHeadersRegex: "^Foo-",
}
middleware, err := NewForward(context.Background(), next, auth, "authTest")
require.NoError(t, err)
@ -80,6 +84,8 @@ func TestForwardAuthSuccess(t *testing.T) {
req := testhelpers.MustNewRequest(http.MethodGet, ts.URL, nil)
req.Header.Set("X-Auth-Group", "admin_group")
req.Header.Set("Foo-Bar", "client-value")
req.Header.Set("Foo-Baz", "client-value")
res, err := http.DefaultClient.Do(req)
require.NoError(t, err)
assert.Equal(t, http.StatusOK, res.StatusCode)

View file

@ -373,10 +373,11 @@ func createForwardAuthMiddleware(k8sClient Client, namespace string, auth *v1alp
}
forwardAuth := &dynamic.ForwardAuth{
Address: auth.Address,
TrustForwardHeader: auth.TrustForwardHeader,
AuthResponseHeaders: auth.AuthResponseHeaders,
AuthRequestHeaders: auth.AuthRequestHeaders,
Address: auth.Address,
TrustForwardHeader: auth.TrustForwardHeader,
AuthResponseHeaders: auth.AuthResponseHeaders,
AuthResponseHeadersRegex: auth.AuthResponseHeadersRegex,
AuthRequestHeaders: auth.AuthRequestHeaders,
}
if auth.TLS == nil {

View file

@ -85,11 +85,12 @@ type DigestAuth struct {
// ForwardAuth holds the http forward authentication configuration.
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"`
Address string `json:"address,omitempty"`
TrustForwardHeader bool `json:"trustForwardHeader,omitempty"`
AuthResponseHeaders []string `json:"authResponseHeaders,omitempty"`
AuthResponseHeadersRegex string `json:"authResponseHeadersRegex,omitempty"`
AuthRequestHeaders []string `json:"authRequestHeaders,omitempty"`
TLS *ClientTLS `json:"tls,omitempty"`
}
// ClientTLS holds TLS specific configurations as client.