Middlewares: add forwardAuth.authResponseHeadersRegex
This commit is contained in:
parent
b5198e63c4
commit
49cdb67ddc
|
@ -164,7 +164,7 @@ http:
|
||||||
|
|
||||||
### `authResponseHeaders`
|
### `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"
|
```yaml tab="Docker"
|
||||||
labels:
|
labels:
|
||||||
|
@ -217,6 +217,59 @@ http:
|
||||||
- "X-Secret"
|
- "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`
|
### `authRequestHeaders`
|
||||||
|
|
||||||
The `authRequestHeaders` option is the list of the headers to copy from the request to the authentication server.
|
The `authRequestHeaders` option is the list of the headers to copy from the request to the authentication server.
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
- "traefik.http.middlewares.middleware08.errors.status=foobar, foobar"
|
- "traefik.http.middlewares.middleware08.errors.status=foobar, foobar"
|
||||||
- "traefik.http.middlewares.middleware09.forwardauth.address=foobar"
|
- "traefik.http.middlewares.middleware09.forwardauth.address=foobar"
|
||||||
- "traefik.http.middlewares.middleware09.forwardauth.authresponseheaders=foobar, 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.authrequestheaders=foobar, foobar"
|
||||||
- "traefik.http.middlewares.middleware09.forwardauth.tls.ca=foobar"
|
- "traefik.http.middlewares.middleware09.forwardauth.tls.ca=foobar"
|
||||||
- "traefik.http.middlewares.middleware09.forwardauth.tls.caoptional=true"
|
- "traefik.http.middlewares.middleware09.forwardauth.tls.caoptional=true"
|
||||||
|
|
|
@ -139,6 +139,7 @@
|
||||||
address = "foobar"
|
address = "foobar"
|
||||||
trustForwardHeader = true
|
trustForwardHeader = true
|
||||||
authResponseHeaders = ["foobar", "foobar"]
|
authResponseHeaders = ["foobar", "foobar"]
|
||||||
|
authResponseHeadersRegex = "foobar"
|
||||||
authRequestHeaders = ["foobar", "foobar"]
|
authRequestHeaders = ["foobar", "foobar"]
|
||||||
[http.middlewares.Middleware09.forwardAuth.tls]
|
[http.middlewares.Middleware09.forwardAuth.tls]
|
||||||
ca = "foobar"
|
ca = "foobar"
|
||||||
|
|
|
@ -158,6 +158,7 @@ http:
|
||||||
authResponseHeaders:
|
authResponseHeaders:
|
||||||
- foobar
|
- foobar
|
||||||
- foobar
|
- foobar
|
||||||
|
authResponseHeadersRegex: foobar
|
||||||
authRequestHeaders:
|
authRequestHeaders:
|
||||||
- foobar
|
- foobar
|
||||||
- foobar
|
- foobar
|
||||||
|
|
|
@ -31,6 +31,7 @@
|
||||||
| `traefik/http/middlewares/Middleware09/forwardAuth/authRequestHeaders/1` | `foobar` |
|
| `traefik/http/middlewares/Middleware09/forwardAuth/authRequestHeaders/1` | `foobar` |
|
||||||
| `traefik/http/middlewares/Middleware09/forwardAuth/authResponseHeaders/0` | `foobar` |
|
| `traefik/http/middlewares/Middleware09/forwardAuth/authResponseHeaders/0` | `foobar` |
|
||||||
| `traefik/http/middlewares/Middleware09/forwardAuth/authResponseHeaders/1` | `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/ca` | `foobar` |
|
||||||
| `traefik/http/middlewares/Middleware09/forwardAuth/tls/caOptional` | `true` |
|
| `traefik/http/middlewares/Middleware09/forwardAuth/tls/caOptional` | `true` |
|
||||||
| `traefik/http/middlewares/Middleware09/forwardAuth/tls/cert` | `foobar` |
|
| `traefik/http/middlewares/Middleware09/forwardAuth/tls/cert` | `foobar` |
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
"traefik.http.middlewares.middleware08.errors.status": "foobar, foobar",
|
"traefik.http.middlewares.middleware08.errors.status": "foobar, foobar",
|
||||||
"traefik.http.middlewares.middleware09.forwardauth.address": "foobar",
|
"traefik.http.middlewares.middleware09.forwardauth.address": "foobar",
|
||||||
"traefik.http.middlewares.middleware09.forwardauth.authresponseheaders": "foobar, 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.authrequestheaders": "foobar, foobar",
|
||||||
"traefik.http.middlewares.middleware09.forwardauth.tls.ca": "foobar",
|
"traefik.http.middlewares.middleware09.forwardauth.tls.ca": "foobar",
|
||||||
"traefik.http.middlewares.middleware09.forwardauth.tls.caoptional": "true",
|
"traefik.http.middlewares.middleware09.forwardauth.tls.caoptional": "true",
|
||||||
|
|
|
@ -288,6 +288,7 @@
|
||||||
address = "foobar"
|
address = "foobar"
|
||||||
trustForwardHeader = true
|
trustForwardHeader = true
|
||||||
authResponseHeaders = ["foobar", "foobar"]
|
authResponseHeaders = ["foobar", "foobar"]
|
||||||
|
authResponseHeadersRegex = "foobar"
|
||||||
authRequestHeaders = ["foobar", "foobar"]
|
authRequestHeaders = ["foobar", "foobar"]
|
||||||
[http.middlewares.Middleware15.forwardAuth.tls]
|
[http.middlewares.Middleware15.forwardAuth.tls]
|
||||||
ca = "foobar"
|
ca = "foobar"
|
||||||
|
|
|
@ -140,11 +140,12 @@ type ErrorPage struct {
|
||||||
|
|
||||||
// ForwardAuth holds the http forward authentication configuration.
|
// ForwardAuth holds the http forward authentication configuration.
|
||||||
type ForwardAuth struct {
|
type ForwardAuth struct {
|
||||||
Address string `json:"address,omitempty" toml:"address,omitempty" yaml:"address,omitempty"`
|
Address string `json:"address,omitempty" toml:"address,omitempty" yaml:"address,omitempty"`
|
||||||
TLS *ClientTLS `json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,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"`
|
TrustForwardHeader bool `json:"trustForwardHeader,omitempty" toml:"trustForwardHeader,omitempty" yaml:"trustForwardHeader,omitempty" export:"true"`
|
||||||
AuthResponseHeaders []string `json:"authResponseHeaders,omitempty" toml:"authResponseHeaders,omitempty" yaml:"authResponseHeaders,omitempty"`
|
AuthResponseHeaders []string `json:"authResponseHeaders,omitempty" toml:"authResponseHeaders,omitempty" yaml:"authResponseHeaders,omitempty"`
|
||||||
AuthRequestHeaders []string `json:"authRequestHeaders,omitempty" toml:"authRequestHeaders,omitempty" yaml:"authRequestHeaders,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
|
// +k8s:deepcopy-gen=true
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -25,13 +26,14 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
type forwardAuth struct {
|
type forwardAuth struct {
|
||||||
address string
|
address string
|
||||||
authResponseHeaders []string
|
authResponseHeaders []string
|
||||||
next http.Handler
|
authResponseHeadersRegex *regexp.Regexp
|
||||||
name string
|
next http.Handler
|
||||||
client http.Client
|
name string
|
||||||
trustForwardHeader bool
|
client http.Client
|
||||||
authRequestHeaders []string
|
trustForwardHeader bool
|
||||||
|
authRequestHeaders []string
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewForward creates a forward auth middleware.
|
// 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
|
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
|
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()
|
req.RequestURI = req.URL.RequestURI()
|
||||||
fa.next.ServeHTTP(rw, req)
|
fa.next.ServeHTTP(rw, req)
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,6 +57,7 @@ func TestForwardAuthSuccess(t *testing.T) {
|
||||||
w.Header().Set("X-Auth-Secret", "secret")
|
w.Header().Set("X-Auth-Secret", "secret")
|
||||||
w.Header().Add("X-Auth-Group", "group1")
|
w.Header().Add("X-Auth-Group", "group1")
|
||||||
w.Header().Add("X-Auth-Group", "group2")
|
w.Header().Add("X-Auth-Group", "group2")
|
||||||
|
w.Header().Add("Foo-Bar", "auth-value")
|
||||||
fmt.Fprintln(w, "Success")
|
fmt.Fprintln(w, "Success")
|
||||||
}))
|
}))
|
||||||
t.Cleanup(server.Close)
|
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.Equal(t, "user@example.com", r.Header.Get("X-Auth-User"))
|
||||||
assert.Empty(t, r.Header.Get("X-Auth-Secret"))
|
assert.Empty(t, r.Header.Get("X-Auth-Secret"))
|
||||||
assert.Equal(t, []string{"group1", "group2"}, r.Header["X-Auth-Group"])
|
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")
|
fmt.Fprintln(w, "traefik")
|
||||||
})
|
})
|
||||||
|
|
||||||
auth := dynamic.ForwardAuth{
|
auth := dynamic.ForwardAuth{
|
||||||
Address: server.URL,
|
Address: server.URL,
|
||||||
AuthResponseHeaders: []string{"X-Auth-User", "X-Auth-Group"},
|
AuthResponseHeaders: []string{"X-Auth-User", "X-Auth-Group"},
|
||||||
|
AuthResponseHeadersRegex: "^Foo-",
|
||||||
}
|
}
|
||||||
middleware, err := NewForward(context.Background(), next, auth, "authTest")
|
middleware, err := NewForward(context.Background(), next, auth, "authTest")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -80,6 +84,8 @@ func TestForwardAuthSuccess(t *testing.T) {
|
||||||
|
|
||||||
req := testhelpers.MustNewRequest(http.MethodGet, ts.URL, nil)
|
req := testhelpers.MustNewRequest(http.MethodGet, ts.URL, nil)
|
||||||
req.Header.Set("X-Auth-Group", "admin_group")
|
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)
|
res, err := http.DefaultClient.Do(req)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, http.StatusOK, res.StatusCode)
|
assert.Equal(t, http.StatusOK, res.StatusCode)
|
||||||
|
|
|
@ -373,10 +373,11 @@ func createForwardAuthMiddleware(k8sClient Client, namespace string, auth *v1alp
|
||||||
}
|
}
|
||||||
|
|
||||||
forwardAuth := &dynamic.ForwardAuth{
|
forwardAuth := &dynamic.ForwardAuth{
|
||||||
Address: auth.Address,
|
Address: auth.Address,
|
||||||
TrustForwardHeader: auth.TrustForwardHeader,
|
TrustForwardHeader: auth.TrustForwardHeader,
|
||||||
AuthResponseHeaders: auth.AuthResponseHeaders,
|
AuthResponseHeaders: auth.AuthResponseHeaders,
|
||||||
AuthRequestHeaders: auth.AuthRequestHeaders,
|
AuthResponseHeadersRegex: auth.AuthResponseHeadersRegex,
|
||||||
|
AuthRequestHeaders: auth.AuthRequestHeaders,
|
||||||
}
|
}
|
||||||
|
|
||||||
if auth.TLS == nil {
|
if auth.TLS == nil {
|
||||||
|
|
|
@ -85,11 +85,12 @@ type DigestAuth struct {
|
||||||
|
|
||||||
// ForwardAuth holds the http forward authentication configuration.
|
// ForwardAuth holds the http forward authentication configuration.
|
||||||
type ForwardAuth struct {
|
type ForwardAuth struct {
|
||||||
Address string `json:"address,omitempty"`
|
Address string `json:"address,omitempty"`
|
||||||
TrustForwardHeader bool `json:"trustForwardHeader,omitempty"`
|
TrustForwardHeader bool `json:"trustForwardHeader,omitempty"`
|
||||||
AuthResponseHeaders []string `json:"authResponseHeaders,omitempty"`
|
AuthResponseHeaders []string `json:"authResponseHeaders,omitempty"`
|
||||||
AuthRequestHeaders []string `json:"authRequestHeaders,omitempty"`
|
AuthResponseHeadersRegex string `json:"authResponseHeadersRegex,omitempty"`
|
||||||
TLS *ClientTLS `json:"tls,omitempty"`
|
AuthRequestHeaders []string `json:"authRequestHeaders,omitempty"`
|
||||||
|
TLS *ClientTLS `json:"tls,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ClientTLS holds TLS specific configurations as client.
|
// ClientTLS holds TLS specific configurations as client.
|
||||||
|
|
Loading…
Reference in a new issue