diff --git a/docs/content/middlewares/http/forwardauth.md b/docs/content/middlewares/http/forwardauth.md index 97e6a8fbe..c8167b964 100644 --- a/docs/content/middlewares/http/forwardauth.md +++ b/docs/content/middlewares/http/forwardauth.md @@ -571,3 +571,44 @@ http: [http.middlewares.test-auth.forwardAuth.tls] insecureSkipVerify: true ``` + +### `headerField` + +_Optional_ + +You can define a header field to store the authenticated user using the `headerField`option. + +```yaml tab="Docker & Swarm" +labels: + - "traefik.http.middlewares.test-auth.forwardauth.headerField=X-WebAuth-User" +``` + +```yaml tab="Kubernetes" +apiVersion: traefik.io/v1alpha1 +kind: Middleware +metadata: + name: test-auth +spec: + forwardAuth: + # ... + headerField: X-WebAuth-User +``` + +```json tab="Consul Catalog" +- "traefik.http.middlewares.test-auth.forwardauth.headerField=X-WebAuth-User" +``` + +```yaml tab="File (YAML)" +http: + middlewares: + test-auth: + forwardAuth: + # ... + headerField: "X-WebAuth-User" +``` + +```toml tab="File (TOML)" +[http.middlewares.test-auth.forwardAuth] + # ... + headerField = "X-WebAuth-User" +``` \ No newline at end of file diff --git a/docs/content/reference/dynamic-configuration/docker-labels.yml b/docs/content/reference/dynamic-configuration/docker-labels.yml index a3e65baa2..4abb97126 100644 --- a/docs/content/reference/dynamic-configuration/docker-labels.yml +++ b/docs/content/reference/dynamic-configuration/docker-labels.yml @@ -37,6 +37,7 @@ - "traefik.http.middlewares.middleware10.forwardauth.authrequestheaders=foobar, foobar" - "traefik.http.middlewares.middleware10.forwardauth.authresponseheaders=foobar, foobar" - "traefik.http.middlewares.middleware10.forwardauth.authresponseheadersregex=foobar" +- "traefik.http.middlewares.middleware10.forwardauth.headerfield=foobar" - "traefik.http.middlewares.middleware10.forwardauth.tls.ca=foobar" - "traefik.http.middlewares.middleware10.forwardauth.tls.caoptional=true" - "traefik.http.middlewares.middleware10.forwardauth.tls.cert=foobar" diff --git a/docs/content/reference/dynamic-configuration/file.toml b/docs/content/reference/dynamic-configuration/file.toml index cd0ffde60..a45ceee14 100644 --- a/docs/content/reference/dynamic-configuration/file.toml +++ b/docs/content/reference/dynamic-configuration/file.toml @@ -167,6 +167,7 @@ authResponseHeadersRegex = "foobar" authRequestHeaders = ["foobar", "foobar"] addAuthCookiesToResponse = ["foobar", "foobar"] + headerField = "foobar" [http.middlewares.Middleware10.forwardAuth.tls] ca = "foobar" cert = "foobar" diff --git a/docs/content/reference/dynamic-configuration/file.yaml b/docs/content/reference/dynamic-configuration/file.yaml index d0de416ee..df0fef94c 100644 --- a/docs/content/reference/dynamic-configuration/file.yaml +++ b/docs/content/reference/dynamic-configuration/file.yaml @@ -192,6 +192,7 @@ http: addAuthCookiesToResponse: - foobar - foobar + headerField: foobar Middleware11: grpcWeb: allowOrigins: diff --git a/docs/content/reference/dynamic-configuration/kv-ref.md b/docs/content/reference/dynamic-configuration/kv-ref.md index 10b9a8d00..ac6a42f3d 100644 --- a/docs/content/reference/dynamic-configuration/kv-ref.md +++ b/docs/content/reference/dynamic-configuration/kv-ref.md @@ -46,6 +46,7 @@ THIS FILE MUST NOT BE EDITED BY HAND | `traefik/http/middlewares/Middleware10/forwardAuth/authResponseHeaders/0` | `foobar` | | `traefik/http/middlewares/Middleware10/forwardAuth/authResponseHeaders/1` | `foobar` | | `traefik/http/middlewares/Middleware10/forwardAuth/authResponseHeadersRegex` | `foobar` | +| `traefik/http/middlewares/Middleware10/forwardAuth/headerField` | `foobar` | | `traefik/http/middlewares/Middleware10/forwardAuth/tls/ca` | `foobar` | | `traefik/http/middlewares/Middleware10/forwardAuth/tls/caOptional` | `true` | | `traefik/http/middlewares/Middleware10/forwardAuth/tls/cert` | `foobar` | diff --git a/pkg/config/dynamic/middlewares.go b/pkg/config/dynamic/middlewares.go index 633229f3e..51ff1be8c 100644 --- a/pkg/config/dynamic/middlewares.go +++ b/pkg/config/dynamic/middlewares.go @@ -241,6 +241,9 @@ type ForwardAuth struct { AuthRequestHeaders []string `json:"authRequestHeaders,omitempty" toml:"authRequestHeaders,omitempty" yaml:"authRequestHeaders,omitempty" export:"true"` // AddAuthCookiesToResponse defines the list of cookies to copy from the authentication server response to the response. AddAuthCookiesToResponse []string `json:"addAuthCookiesToResponse,omitempty" toml:"addAuthCookiesToResponse,omitempty" yaml:"addAuthCookiesToResponse,omitempty" export:"true"` + // HeaderField defines a header field to store the authenticated user. + // More info: https://doc.traefik.io/traefik/v3.0/middlewares/http/forwardauth/#headerfield + HeaderField string `json:"headerField,omitempty" toml:"headerField,omitempty" yaml:"headerField,omitempty" export:"true"` } // +k8s:deepcopy-gen=true diff --git a/pkg/middlewares/auth/forward.go b/pkg/middlewares/auth/forward.go index 26053a50e..68c382e22 100644 --- a/pkg/middlewares/auth/forward.go +++ b/pkg/middlewares/auth/forward.go @@ -13,6 +13,7 @@ import ( "github.com/traefik/traefik/v3/pkg/config/dynamic" "github.com/traefik/traefik/v3/pkg/middlewares" + "github.com/traefik/traefik/v3/pkg/middlewares/accesslog" "github.com/traefik/traefik/v3/pkg/middlewares/connectionheader" "github.com/traefik/traefik/v3/pkg/middlewares/observability" "github.com/traefik/traefik/v3/pkg/tracing" @@ -51,6 +52,7 @@ type forwardAuth struct { trustForwardHeader bool authRequestHeaders []string addAuthCookiesToResponse map[string]struct{} + headerField string } // NewForward creates a forward auth middleware. @@ -71,6 +73,7 @@ func NewForward(ctx context.Context, next http.Handler, config dynamic.ForwardAu trustForwardHeader: config.TrustForwardHeader, authRequestHeaders: config.AuthRequestHeaders, addAuthCookiesToResponse: addAuthCookiesToResponse, + headerField: config.HeaderField, } // Ensure our request client does not follow redirects @@ -174,6 +177,15 @@ func (fa *forwardAuth) ServeHTTP(rw http.ResponseWriter, req *http.Request) { forwardSpan.End() } + if fa.headerField != "" { + if elems := forwardResponse.Header[http.CanonicalHeaderKey(fa.headerField)]; len(elems) > 0 { + logData := accesslog.GetLogData(req) + if logData != nil { + logData.Core[accesslog.ClientUsername] = elems[0] + } + } + } + // Pass the forward response's body and selected headers if it // didn't return a response within the range of [200, 300). if forwardResponse.StatusCode < http.StatusOK || forwardResponse.StatusCode >= http.StatusMultipleChoices {