mirror of
https://github.com/traefik/traefik
synced 2026-02-03 06:30:31 +00:00
Add authSignInURL in forward auth middleware
This commit is contained in:
@@ -1434,6 +1434,10 @@ spec:
|
||||
AuthResponseHeadersRegex defines the regex to match headers to copy from the authentication server response and set on forwarded request, after stripping all headers that match the regex.
|
||||
More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/middlewares/forwardauth/#authresponseheadersregex
|
||||
type: string
|
||||
authSigninURL:
|
||||
description: AuthSigninURL specifies the URL to redirect to when
|
||||
the authentication server returns 401 Unauthorized.
|
||||
type: string
|
||||
forwardBody:
|
||||
description: ForwardBody defines whether to send the request body
|
||||
to the authentication server.
|
||||
|
||||
@@ -589,6 +589,10 @@ spec:
|
||||
AuthResponseHeadersRegex defines the regex to match headers to copy from the authentication server response and set on forwarded request, after stripping all headers that match the regex.
|
||||
More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/middlewares/forwardauth/#authresponseheadersregex
|
||||
type: string
|
||||
authSigninURL:
|
||||
description: AuthSigninURL specifies the URL to redirect to when
|
||||
the authentication server returns 401 Unauthorized.
|
||||
type: string
|
||||
forwardBody:
|
||||
description: ForwardBody defines whether to send the request body
|
||||
to the authentication server.
|
||||
|
||||
@@ -53,25 +53,26 @@ spec:
|
||||
|
||||
## Configuration Options
|
||||
|
||||
| Field | Description | Default | Required |
|
||||
|:-----------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:--------|:---------|
|
||||
| <a id="opt-address" href="#opt-address" title="#opt-address">`address`</a> | Authentication server address. | "" | Yes |
|
||||
| <a id="opt-trustForwardHeader" href="#opt-trustForwardHeader" title="#opt-trustForwardHeader">`trustForwardHeader`</a> | Trust all `X-Forwarded-*` headers. | false | No |
|
||||
| <a id="opt-authResponseHeaders" href="#opt-authResponseHeaders" title="#opt-authResponseHeaders">`authResponseHeaders`</a> | List of headers to copy from the authentication server response and set on forwarded request, replacing any existing conflicting headers. | [] | No |
|
||||
| <a id="opt-authResponseHeadersRegex" href="#opt-authResponseHeadersRegex" title="#opt-authResponseHeadersRegex">`authResponseHeadersRegex`</a> | Regex to match by the headers to copy from the authentication server response and set on forwarded request, after stripping all headers that match the regex.<br /> More information [here](#authresponseheadersregex). | "" | No |
|
||||
| <a id="opt-authRequestHeaders" href="#opt-authRequestHeaders" title="#opt-authRequestHeaders">`authRequestHeaders`</a> | List of the headers to copy from the request to the authentication server. <br /> It allows filtering headers that should not be passed to the authentication server. <br /> If not set or empty, then all request headers are passed. | [] | No |
|
||||
| <a id="opt-addAuthCookiesToResponse" href="#opt-addAuthCookiesToResponse" title="#opt-addAuthCookiesToResponse">`addAuthCookiesToResponse`</a> | List of cookies to copy from the authentication server to the response, replacing any existing conflicting cookie from the forwarded response.<br /> Please note that all backend cookies matching the configured list will not be added to the response. | [] | No |
|
||||
| <a id="opt-forwardBody" href="#opt-forwardBody" title="#opt-forwardBody">`forwardBody`</a> | Sets the `forwardBody` option to `true` to send the Body. As body is read inside Traefik before forwarding, this breaks streaming. | false | No |
|
||||
| <a id="opt-maxBodySize" href="#opt-maxBodySize" title="#opt-maxBodySize">`maxBodySize`</a> | Set the `maxBodySize` to limit the body size in bytes. If body is bigger than this, it returns a 401 (unauthorized). If left unset, the request body size is unrestricted which can have performance or security implications. < br/>More information [here](#maxbodysize).| -1 | No |
|
||||
| <a id="opt-headerField" href="#opt-headerField" title="#opt-headerField">`headerField`</a> | Defines a header field to store the authenticated user. | "" | No |
|
||||
| <a id="opt-preserveLocationHeader" href="#opt-preserveLocationHeader" title="#opt-preserveLocationHeader">`preserveLocationHeader`</a> | Defines whether to forward the Location header to the client as is or prefix it with the domain name of the authentication server. | false | No |
|
||||
| <a id="opt-preserveRequestMethod" href="#opt-preserveRequestMethod" title="#opt-preserveRequestMethod">`preserveRequestMethod`</a> | Defines whether to preserve the original request method while forwarding the request to the authentication server. | false | No |
|
||||
| <a id="opt-tls-ca" href="#opt-tls-ca" title="#opt-tls-ca">`tls.ca`</a> | Sets the path to the certificate authority used for the secured connection to the authentication server, it defaults to the system bundle. | "" | No |
|
||||
| <a id="opt-tls-cert" href="#opt-tls-cert" title="#opt-tls-cert">`tls.cert`</a> | Sets the path to the public certificate used for the secure connection to the authentication server. When using this option, setting the key option is required. | "" | No |
|
||||
| <a id="opt-tls-key" href="#opt-tls-key" title="#opt-tls-key">`tls.key`</a> | Sets the path to the private key used for the secure connection to the authentication server. When using this option, setting the `cert` option is required. | "" | No |
|
||||
| <a id="opt-tls-caSecret" href="#opt-tls-caSecret" title="#opt-tls-caSecret">`tls.caSecret`</a> | Defines the secret that contains the certificate authority used for the secured connection to the authentication server, it defaults to the system bundle. **This option is only available for the Kubernetes CRD**. | | No |
|
||||
| <a id="opt-tls-certSecret" href="#opt-tls-certSecret" title="#opt-tls-certSecret">`tls.certSecret`</a> | Defines the secret that contains both the private and public certificates used for the secure connection to the authentication server. **This option is only available for the Kubernetes CRD**. | | No |
|
||||
| <a id="opt-tls-insecureSkipVerify" href="#opt-tls-insecureSkipVerify" title="#opt-tls-insecureSkipVerify">`tls.insecureSkipVerify`</a> | During TLS connections, if this option is set to `true`, the authentication server will accept any certificate presented by the server regardless of the host names it covers. | false | No |
|
||||
| Field | Description | Default | Required |
|
||||
|:-----------------------------------------------------------------------------------------------------------------------------------------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:--------|:---------|
|
||||
| <a id="opt-address" href="#opt-address" title="#opt-address">`address`</a> | Authentication server address. | "" | Yes |
|
||||
| <a id="opt-trustForwardHeader" href="#opt-trustForwardHeader" title="#opt-trustForwardHeader">`trustForwardHeader`</a> | Trust all `X-Forwarded-*` headers. | false | No |
|
||||
| <a id="opt-authResponseHeaders" href="#opt-authResponseHeaders" title="#opt-authResponseHeaders">`authResponseHeaders`</a> | List of headers to copy from the authentication server response and set on forwarded request, replacing any existing conflicting headers. | [] | No |
|
||||
| <a id="opt-authResponseHeadersRegex" href="#opt-authResponseHeadersRegex" title="#opt-authResponseHeadersRegex">`authResponseHeadersRegex`</a> | Regex to match by the headers to copy from the authentication server response and set on forwarded request, after stripping all headers that match the regex.<br /> More information [here](#authresponseheadersregex). | "" | No |
|
||||
| <a id="opt-authRequestHeaders" href="#opt-authRequestHeaders" title="#opt-authRequestHeaders">`authRequestHeaders`</a> | List of the headers to copy from the request to the authentication server. <br /> It allows filtering headers that should not be passed to the authentication server. <br /> If not set or empty, then all request headers are passed. | [] | No |
|
||||
| <a id="opt-addAuthCookiesToResponse" href="#opt-addAuthCookiesToResponse" title="#opt-addAuthCookiesToResponse">`addAuthCookiesToResponse`</a> | List of cookies to copy from the authentication server to the response, replacing any existing conflicting cookie from the forwarded response.<br /> Please note that all backend cookies matching the configured list will not be added to the response. | [] | No |
|
||||
| <a id="opt-forwardBody" href="#opt-forwardBody" title="#opt-forwardBody">`forwardBody`</a> | Sets the `forwardBody` option to `true` to send the Body. As body is read inside Traefik before forwarding, this breaks streaming. | false | No |
|
||||
| <a id="opt-maxBodySize" href="#opt-maxBodySize" title="#opt-maxBodySize">`maxBodySize`</a> | Set the `maxBodySize` to limit the body size in bytes. If body is bigger than this, it returns a 401 (unauthorized). If left unset, the request body size is unrestricted which can have performance or security implications. < br/>More information [here](#maxbodysize). | -1 | No |
|
||||
| <a id="opt-headerField" href="#opt-headerField" title="#opt-headerField">`headerField`</a> | Defines a header field to store the authenticated user. | "" | No |
|
||||
| <a id="opt-preserveLocationHeader" href="#opt-preserveLocationHeader" title="#opt-preserveLocationHeader">`preserveLocationHeader`</a> | Defines whether to forward the Location header to the client as is or prefix it with the domain name of the authentication server. | false | No |
|
||||
| <a id="opt-preserveRequestMethod" href="#opt-preserveRequestMethod" title="#opt-preserveRequestMethod">`preserveRequestMethod`</a> | Defines whether to preserve the original request method while forwarding the request to the authentication server. | false | No |
|
||||
| <a id="opt-authSigninURL" href="#opt-authSigninURL" title="#opt-authSigninURL">`authSigninURL`</a> | Specifies the URL to redirect to when the authentication server returns 401 Unauthorized. | "" | No |
|
||||
| <a id="opt-tls-ca" href="#opt-tls-ca" title="#opt-tls-ca">`tls.ca`</a> | Sets the path to the certificate authority used for the secured connection to the authentication server, it defaults to the system bundle. | "" | No |
|
||||
| <a id="opt-tls-cert" href="#opt-tls-cert" title="#opt-tls-cert">`tls.cert`</a> | Sets the path to the public certificate used for the secure connection to the authentication server. When using this option, setting the key option is required. | "" | No |
|
||||
| <a id="opt-tls-key" href="#opt-tls-key" title="#opt-tls-key">`tls.key`</a> | Sets the path to the private key used for the secure connection to the authentication server. When using this option, setting the `cert` option is required. | "" | No |
|
||||
| <a id="opt-tls-caSecret" href="#opt-tls-caSecret" title="#opt-tls-caSecret">`tls.caSecret`</a> | Defines the secret that contains the certificate authority used for the secured connection to the authentication server, it defaults to the system bundle. **This option is only available for the Kubernetes CRD**. | | No |
|
||||
| <a id="opt-tls-certSecret" href="#opt-tls-certSecret" title="#opt-tls-certSecret">`tls.certSecret`</a> | Defines the secret that contains both the private and public certificates used for the secure connection to the authentication server. **This option is only available for the Kubernetes CRD**. | | No |
|
||||
| <a id="opt-tls-insecureSkipVerify" href="#opt-tls-insecureSkipVerify" title="#opt-tls-insecureSkipVerify">`tls.insecureSkipVerify`</a> | During TLS connections, if this option is set to `true`, the authentication server will accept any certificate presented by the server regardless of the host names it covers. | false | No |
|
||||
|
||||
### authResponseHeadersRegex
|
||||
|
||||
|
||||
@@ -220,6 +220,7 @@
|
||||
maxBodySize = 42
|
||||
preserveLocationHeader = true
|
||||
preserveRequestMethod = true
|
||||
authSigninURL = "foobar"
|
||||
[http.middlewares.Middleware11.forwardAuth.tls]
|
||||
ca = "foobar"
|
||||
cert = "foobar"
|
||||
|
||||
@@ -248,6 +248,7 @@ http:
|
||||
maxBodySize: 42
|
||||
preserveLocationHeader: true
|
||||
preserveRequestMethod: true
|
||||
authSigninURL: foobar
|
||||
Middleware12:
|
||||
grpcWeb:
|
||||
allowOrigins:
|
||||
|
||||
@@ -1435,6 +1435,10 @@ spec:
|
||||
AuthResponseHeadersRegex defines the regex to match headers to copy from the authentication server response and set on forwarded request, after stripping all headers that match the regex.
|
||||
More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/middlewares/forwardauth/#authresponseheadersregex
|
||||
type: string
|
||||
authSigninURL:
|
||||
description: AuthSigninURL specifies the URL to redirect to when
|
||||
the authentication server returns 401 Unauthorized.
|
||||
type: string
|
||||
forwardBody:
|
||||
description: ForwardBody defines whether to send the request body
|
||||
to the authentication server.
|
||||
|
||||
@@ -292,6 +292,8 @@ type ForwardAuth struct {
|
||||
PreserveLocationHeader bool `json:"preserveLocationHeader,omitempty" toml:"preserveLocationHeader,omitempty" yaml:"preserveLocationHeader,omitempty" export:"true"`
|
||||
// PreserveRequestMethod defines whether to preserve the original request method while forwarding the request to the authentication server.
|
||||
PreserveRequestMethod bool `json:"preserveRequestMethod,omitempty" toml:"preserveRequestMethod,omitempty" yaml:"preserveRequestMethod,omitempty" export:"true"`
|
||||
// AuthSigninURL specifies the URL to redirect to when the authentication server returns 401 Unauthorized.
|
||||
AuthSigninURL string `json:"authSigninURL,omitempty" toml:"authSigninURL,omitempty" yaml:"authSigninURL,omitempty" export:"true"`
|
||||
}
|
||||
|
||||
func (f *ForwardAuth) SetDefaults() {
|
||||
|
||||
@@ -59,6 +59,7 @@ type forwardAuth struct {
|
||||
maxBodySize int64
|
||||
preserveLocationHeader bool
|
||||
preserveRequestMethod bool
|
||||
authSigninURL string
|
||||
}
|
||||
|
||||
// NewForward creates a forward auth middleware.
|
||||
@@ -84,6 +85,7 @@ func NewForward(ctx context.Context, next http.Handler, config dynamic.ForwardAu
|
||||
maxBodySize: dynamic.ForwardAuthDefaultMaxBodySize,
|
||||
preserveLocationHeader: config.PreserveLocationHeader,
|
||||
preserveRequestMethod: config.PreserveRequestMethod,
|
||||
authSigninURL: config.AuthSigninURL,
|
||||
}
|
||||
|
||||
if config.MaxBodySize != nil {
|
||||
@@ -232,6 +234,15 @@ func (fa *forwardAuth) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
// If auth server returns 401 and AuthSigninURL is configured, redirect to signin URL.
|
||||
if fa.authSigninURL != "" && forwardResponse.StatusCode == http.StatusUnauthorized {
|
||||
logger.Debug().Msgf("Redirecting to signin URL: %s", fa.authSigninURL)
|
||||
|
||||
tracer.CaptureResponse(forwardSpan, forwardResponse.Header, http.StatusFound, trace.SpanKindClient)
|
||||
http.Redirect(rw, req, fa.authSigninURL, http.StatusFound)
|
||||
return
|
||||
}
|
||||
|
||||
// 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 {
|
||||
|
||||
@@ -872,6 +872,91 @@ func TestForwardAuthPreserveRequestMethod(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestForwardAuthAuthSigninURL(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
authSigninURL string
|
||||
authServerStatus int
|
||||
expectedStatus int
|
||||
expectedLocation string
|
||||
nextShouldBeCalled bool
|
||||
}{
|
||||
{
|
||||
desc: "redirects to signin URL on 401",
|
||||
authSigninURL: "https://auth.example.com/login",
|
||||
authServerStatus: http.StatusUnauthorized,
|
||||
expectedStatus: http.StatusFound,
|
||||
expectedLocation: "https://auth.example.com/login",
|
||||
nextShouldBeCalled: false,
|
||||
},
|
||||
{
|
||||
desc: "no redirect on 401 without signin URL",
|
||||
authServerStatus: http.StatusUnauthorized,
|
||||
expectedStatus: http.StatusUnauthorized,
|
||||
nextShouldBeCalled: false,
|
||||
},
|
||||
{
|
||||
desc: "no redirect on other error statuses with signin URL",
|
||||
authSigninURL: "https://auth.example.com/login",
|
||||
authServerStatus: http.StatusForbidden,
|
||||
expectedStatus: http.StatusForbidden,
|
||||
nextShouldBeCalled: false,
|
||||
},
|
||||
{
|
||||
desc: "no redirect on OK status with signin URL",
|
||||
authSigninURL: "https://auth.example.com/login",
|
||||
authServerStatus: http.StatusOK,
|
||||
expectedStatus: http.StatusOK,
|
||||
nextShouldBeCalled: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
authServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
http.Error(w, http.StatusText(test.authServerStatus), test.authServerStatus)
|
||||
}))
|
||||
t.Cleanup(authServer.Close)
|
||||
|
||||
nextCalled := false
|
||||
next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
nextCalled = true
|
||||
})
|
||||
|
||||
auth := dynamic.ForwardAuth{
|
||||
Address: authServer.URL,
|
||||
AuthSigninURL: test.authSigninURL,
|
||||
}
|
||||
middleware, err := NewForward(t.Context(), next, auth, "authTest")
|
||||
require.NoError(t, err)
|
||||
|
||||
ts := httptest.NewServer(middleware)
|
||||
t.Cleanup(ts.Close)
|
||||
|
||||
client := &http.Client{
|
||||
CheckRedirect: func(r *http.Request, via []*http.Request) error {
|
||||
return http.ErrUseLastResponse
|
||||
},
|
||||
}
|
||||
|
||||
req := testhelpers.MustNewRequest(http.MethodGet, ts.URL, nil)
|
||||
res, err := client.Do(req)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, test.expectedStatus, res.StatusCode)
|
||||
assert.Equal(t, test.nextShouldBeCalled, nextCalled)
|
||||
|
||||
if test.expectedLocation != "" {
|
||||
location, err := res.Location()
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, test.expectedLocation, location.String())
|
||||
} else {
|
||||
assert.Empty(t, res.Header.Get("Location"))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type mockTracer struct {
|
||||
embedded.Tracer
|
||||
|
||||
|
||||
+9
@@ -41,6 +41,7 @@ type ForwardAuthApplyConfiguration struct {
|
||||
MaxBodySize *int64 `json:"maxBodySize,omitempty"`
|
||||
PreserveLocationHeader *bool `json:"preserveLocationHeader,omitempty"`
|
||||
PreserveRequestMethod *bool `json:"preserveRequestMethod,omitempty"`
|
||||
AuthSigninURL *string `json:"authSigninURL,omitempty"`
|
||||
}
|
||||
|
||||
// ForwardAuthApplyConfiguration constructs a declarative configuration of the ForwardAuth type for use with
|
||||
@@ -150,3 +151,11 @@ func (b *ForwardAuthApplyConfiguration) WithPreserveRequestMethod(value bool) *F
|
||||
b.PreserveRequestMethod = &value
|
||||
return b
|
||||
}
|
||||
|
||||
// WithAuthSigninURL sets the AuthSigninURL field in the declarative configuration to the given value
|
||||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the AuthSigninURL field is set to the value of the last call.
|
||||
func (b *ForwardAuthApplyConfiguration) WithAuthSigninURL(value string) *ForwardAuthApplyConfiguration {
|
||||
b.AuthSigninURL = &value
|
||||
return b
|
||||
}
|
||||
|
||||
@@ -1011,6 +1011,7 @@ func createForwardAuthMiddleware(k8sClient Client, namespace string, auth *traef
|
||||
ForwardBody: auth.ForwardBody,
|
||||
PreserveLocationHeader: auth.PreserveLocationHeader,
|
||||
PreserveRequestMethod: auth.PreserveRequestMethod,
|
||||
AuthSigninURL: auth.AuthSigninURL,
|
||||
}
|
||||
forwardAuth.SetDefaults()
|
||||
|
||||
|
||||
@@ -185,6 +185,8 @@ type ForwardAuth struct {
|
||||
PreserveLocationHeader bool `json:"preserveLocationHeader,omitempty"`
|
||||
// PreserveRequestMethod defines whether to preserve the original request method while forwarding the request to the authentication server.
|
||||
PreserveRequestMethod bool `json:"preserveRequestMethod,omitempty"`
|
||||
// AuthSigninURL specifies the URL to redirect to when the authentication server returns 401 Unauthorized.
|
||||
AuthSigninURL string `json:"authSigninURL,omitempty"`
|
||||
}
|
||||
|
||||
// +k8s:deepcopy-gen=true
|
||||
|
||||
Reference in New Issue
Block a user