Add option to preserve request method in forwardAuth
This commit is contained in:
parent
2b6a04bc1d
commit
2afa03b55c
15 changed files with 139 additions and 5 deletions
|
@ -746,5 +746,45 @@ http:
|
||||||
preserveLocationHeader = true
|
preserveLocationHeader = true
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### `preserveRequestMethod`
|
||||||
|
|
||||||
|
_Optional, Default=false_
|
||||||
|
|
||||||
|
`preserveRequestMethod` defines whether to preserve the original request method while forwarding the request to the authentication server. By default, when this option is set to `false`, incoming requests are always forwarded as `GET` requests to the authentication server.
|
||||||
|
|
||||||
|
```yaml tab="Docker & Swarm"
|
||||||
|
labels:
|
||||||
|
- "traefik.http.middlewares.test-auth.forwardauth.preserveRequestMethod=true"
|
||||||
|
```
|
||||||
|
|
||||||
|
```yaml tab="Kubernetes"
|
||||||
|
apiVersion: traefik.io/v1alpha1
|
||||||
|
kind: Middleware
|
||||||
|
metadata:
|
||||||
|
name: test-auth
|
||||||
|
spec:
|
||||||
|
forwardAuth:
|
||||||
|
# ...
|
||||||
|
preserveRequestMethod: true
|
||||||
|
```
|
||||||
|
|
||||||
|
```json tab="Consul Catalog"
|
||||||
|
- "traefik.http.middlewares.test-auth.forwardauth.preserveRequestMethod=true"
|
||||||
|
```
|
||||||
|
|
||||||
|
```yaml tab="File (YAML)"
|
||||||
|
http:
|
||||||
|
middlewares:
|
||||||
|
test-auth:
|
||||||
|
forwardAuth:
|
||||||
|
# ...
|
||||||
|
preserveRequestMethod: true
|
||||||
|
```
|
||||||
|
|
||||||
|
```toml tab="File (TOML)"
|
||||||
|
[http.middlewares.test-auth.forwardAuth]
|
||||||
|
# ...
|
||||||
|
preserveRequestMethod = true
|
||||||
|
```
|
||||||
|
|
||||||
{!traefik-for-business-applications.md!}
|
{!traefik-for-business-applications.md!}
|
||||||
|
|
|
@ -42,6 +42,7 @@
|
||||||
- "traefik.http.middlewares.middleware10.forwardauth.headerfield=foobar"
|
- "traefik.http.middlewares.middleware10.forwardauth.headerfield=foobar"
|
||||||
- "traefik.http.middlewares.middleware10.forwardauth.maxbodysize=42"
|
- "traefik.http.middlewares.middleware10.forwardauth.maxbodysize=42"
|
||||||
- "traefik.http.middlewares.middleware10.forwardauth.preservelocationheader=true"
|
- "traefik.http.middlewares.middleware10.forwardauth.preservelocationheader=true"
|
||||||
|
- "traefik.http.middlewares.middleware10.forwardauth.preserverequestmethod=true"
|
||||||
- "traefik.http.middlewares.middleware10.forwardauth.tls.ca=foobar"
|
- "traefik.http.middlewares.middleware10.forwardauth.tls.ca=foobar"
|
||||||
- "traefik.http.middlewares.middleware10.forwardauth.tls.caoptional=true"
|
- "traefik.http.middlewares.middleware10.forwardauth.tls.caoptional=true"
|
||||||
- "traefik.http.middlewares.middleware10.forwardauth.tls.cert=foobar"
|
- "traefik.http.middlewares.middleware10.forwardauth.tls.cert=foobar"
|
||||||
|
|
|
@ -185,6 +185,7 @@
|
||||||
forwardBody = true
|
forwardBody = true
|
||||||
maxBodySize = 42
|
maxBodySize = 42
|
||||||
preserveLocationHeader = true
|
preserveLocationHeader = true
|
||||||
|
preserveRequestMethod = true
|
||||||
[http.middlewares.Middleware10.forwardAuth.tls]
|
[http.middlewares.Middleware10.forwardAuth.tls]
|
||||||
ca = "foobar"
|
ca = "foobar"
|
||||||
cert = "foobar"
|
cert = "foobar"
|
||||||
|
|
|
@ -212,6 +212,7 @@ http:
|
||||||
forwardBody: true
|
forwardBody: true
|
||||||
maxBodySize: 42
|
maxBodySize: 42
|
||||||
preserveLocationHeader: true
|
preserveLocationHeader: true
|
||||||
|
preserveRequestMethod: true
|
||||||
Middleware11:
|
Middleware11:
|
||||||
grpcWeb:
|
grpcWeb:
|
||||||
allowOrigins:
|
allowOrigins:
|
||||||
|
|
|
@ -1248,6 +1248,11 @@ spec:
|
||||||
the Location header to the client as is or prefix it with the
|
the Location header to the client as is or prefix it with the
|
||||||
domain name of the authentication server.
|
domain name of the authentication server.
|
||||||
type: boolean
|
type: boolean
|
||||||
|
preserveRequestMethod:
|
||||||
|
description: PreserveRequestMethod defines whether to preserve
|
||||||
|
the original request method while forwarding the request to
|
||||||
|
the authentication server.
|
||||||
|
type: boolean
|
||||||
tls:
|
tls:
|
||||||
description: TLS defines the configuration used to secure the
|
description: TLS defines the configuration used to secure the
|
||||||
connection to the authentication server.
|
connection to the authentication server.
|
||||||
|
|
|
@ -52,6 +52,7 @@ THIS FILE MUST NOT BE EDITED BY HAND
|
||||||
| `traefik/http/middlewares/Middleware10/forwardAuth/headerField` | `foobar` |
|
| `traefik/http/middlewares/Middleware10/forwardAuth/headerField` | `foobar` |
|
||||||
| `traefik/http/middlewares/Middleware10/forwardAuth/maxBodySize` | `42` |
|
| `traefik/http/middlewares/Middleware10/forwardAuth/maxBodySize` | `42` |
|
||||||
| `traefik/http/middlewares/Middleware10/forwardAuth/preserveLocationHeader` | `true` |
|
| `traefik/http/middlewares/Middleware10/forwardAuth/preserveLocationHeader` | `true` |
|
||||||
|
| `traefik/http/middlewares/Middleware10/forwardAuth/preserveRequestMethod` | `true` |
|
||||||
| `traefik/http/middlewares/Middleware10/forwardAuth/tls/ca` | `foobar` |
|
| `traefik/http/middlewares/Middleware10/forwardAuth/tls/ca` | `foobar` |
|
||||||
| `traefik/http/middlewares/Middleware10/forwardAuth/tls/caOptional` | `true` |
|
| `traefik/http/middlewares/Middleware10/forwardAuth/tls/caOptional` | `true` |
|
||||||
| `traefik/http/middlewares/Middleware10/forwardAuth/tls/cert` | `foobar` |
|
| `traefik/http/middlewares/Middleware10/forwardAuth/tls/cert` | `foobar` |
|
||||||
|
|
|
@ -506,6 +506,11 @@ spec:
|
||||||
the Location header to the client as is or prefix it with the
|
the Location header to the client as is or prefix it with the
|
||||||
domain name of the authentication server.
|
domain name of the authentication server.
|
||||||
type: boolean
|
type: boolean
|
||||||
|
preserveRequestMethod:
|
||||||
|
description: PreserveRequestMethod defines whether to preserve
|
||||||
|
the original request method while forwarding the request to
|
||||||
|
the authentication server.
|
||||||
|
type: boolean
|
||||||
tls:
|
tls:
|
||||||
description: TLS defines the configuration used to secure the
|
description: TLS defines the configuration used to secure the
|
||||||
connection to the authentication server.
|
connection to the authentication server.
|
||||||
|
|
|
@ -1248,6 +1248,11 @@ spec:
|
||||||
the Location header to the client as is or prefix it with the
|
the Location header to the client as is or prefix it with the
|
||||||
domain name of the authentication server.
|
domain name of the authentication server.
|
||||||
type: boolean
|
type: boolean
|
||||||
|
preserveRequestMethod:
|
||||||
|
description: PreserveRequestMethod defines whether to preserve
|
||||||
|
the original request method while forwarding the request to
|
||||||
|
the authentication server.
|
||||||
|
type: boolean
|
||||||
tls:
|
tls:
|
||||||
description: TLS defines the configuration used to secure the
|
description: TLS defines the configuration used to secure the
|
||||||
connection to the authentication server.
|
connection to the authentication server.
|
||||||
|
|
|
@ -260,6 +260,8 @@ type ForwardAuth struct {
|
||||||
MaxBodySize *int64 `json:"maxBodySize,omitempty" toml:"maxBodySize,omitempty" yaml:"maxBodySize,omitempty" export:"true"`
|
MaxBodySize *int64 `json:"maxBodySize,omitempty" toml:"maxBodySize,omitempty" yaml:"maxBodySize,omitempty" export:"true"`
|
||||||
// PreserveLocationHeader defines whether to forward the Location header to the client as is or prefix it with the domain name of the authentication server.
|
// PreserveLocationHeader defines whether to forward the Location header to the client as is or prefix it with the domain name of the authentication server.
|
||||||
PreserveLocationHeader bool `json:"preserveLocationHeader,omitempty" toml:"preserveLocationHeader,omitempty" yaml:"preserveLocationHeader,omitempty" export:"true"`
|
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"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *ForwardAuth) SetDefaults() {
|
func (f *ForwardAuth) SetDefaults() {
|
||||||
|
|
|
@ -53,6 +53,7 @@ func TestDecodeConfiguration(t *testing.T) {
|
||||||
"traefik.http.middlewares.Middleware7.forwardauth.trustforwardheader": "true",
|
"traefik.http.middlewares.Middleware7.forwardauth.trustforwardheader": "true",
|
||||||
"traefik.http.middlewares.Middleware7.forwardauth.forwardbody": "true",
|
"traefik.http.middlewares.Middleware7.forwardauth.forwardbody": "true",
|
||||||
"traefik.http.middlewares.Middleware7.forwardauth.maxbodysize": "42",
|
"traefik.http.middlewares.Middleware7.forwardauth.maxbodysize": "42",
|
||||||
|
"traefik.http.middlewares.Middleware7.forwardauth.preserveRequestMethod": "true",
|
||||||
"traefik.http.middlewares.Middleware8.headers.accesscontrolallowcredentials": "true",
|
"traefik.http.middlewares.Middleware8.headers.accesscontrolallowcredentials": "true",
|
||||||
"traefik.http.middlewares.Middleware8.headers.allowedhosts": "foobar, fiibar",
|
"traefik.http.middlewares.Middleware8.headers.allowedhosts": "foobar, fiibar",
|
||||||
"traefik.http.middlewares.Middleware8.headers.accesscontrolallowheaders": "X-foobar, X-fiibar",
|
"traefik.http.middlewares.Middleware8.headers.accesscontrolallowheaders": "X-foobar, X-fiibar",
|
||||||
|
@ -578,8 +579,9 @@ func TestDecodeConfiguration(t *testing.T) {
|
||||||
"foobar",
|
"foobar",
|
||||||
"fiibar",
|
"fiibar",
|
||||||
},
|
},
|
||||||
ForwardBody: true,
|
ForwardBody: true,
|
||||||
MaxBodySize: pointer(int64(42)),
|
MaxBodySize: pointer(int64(42)),
|
||||||
|
PreserveRequestMethod: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"Middleware8": {
|
"Middleware8": {
|
||||||
|
@ -1126,8 +1128,9 @@ func TestEncodeConfiguration(t *testing.T) {
|
||||||
"foobar",
|
"foobar",
|
||||||
"fiibar",
|
"fiibar",
|
||||||
},
|
},
|
||||||
ForwardBody: true,
|
ForwardBody: true,
|
||||||
MaxBodySize: pointer(int64(42)),
|
MaxBodySize: pointer(int64(42)),
|
||||||
|
PreserveRequestMethod: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"Middleware8": {
|
"Middleware8": {
|
||||||
|
@ -1342,6 +1345,7 @@ func TestEncodeConfiguration(t *testing.T) {
|
||||||
"traefik.HTTP.Middlewares.Middleware7.ForwardAuth.TLS.Key": "foobar",
|
"traefik.HTTP.Middlewares.Middleware7.ForwardAuth.TLS.Key": "foobar",
|
||||||
"traefik.HTTP.Middlewares.Middleware7.ForwardAuth.TrustForwardHeader": "true",
|
"traefik.HTTP.Middlewares.Middleware7.ForwardAuth.TrustForwardHeader": "true",
|
||||||
"traefik.HTTP.Middlewares.Middleware7.ForwardAuth.PreserveLocationHeader": "false",
|
"traefik.HTTP.Middlewares.Middleware7.ForwardAuth.PreserveLocationHeader": "false",
|
||||||
|
"traefik.HTTP.Middlewares.Middleware7.ForwardAuth.PreserveRequestMethod": "true",
|
||||||
"traefik.HTTP.Middlewares.Middleware8.Headers.AccessControlAllowCredentials": "true",
|
"traefik.HTTP.Middlewares.Middleware8.Headers.AccessControlAllowCredentials": "true",
|
||||||
"traefik.HTTP.Middlewares.Middleware8.Headers.AccessControlAllowHeaders": "X-foobar, X-fiibar",
|
"traefik.HTTP.Middlewares.Middleware8.Headers.AccessControlAllowHeaders": "X-foobar, X-fiibar",
|
||||||
"traefik.HTTP.Middlewares.Middleware8.Headers.AccessControlAllowMethods": "GET, PUT",
|
"traefik.HTTP.Middlewares.Middleware8.Headers.AccessControlAllowMethods": "GET, PUT",
|
||||||
|
|
|
@ -57,6 +57,7 @@ type forwardAuth struct {
|
||||||
forwardBody bool
|
forwardBody bool
|
||||||
maxBodySize int64
|
maxBodySize int64
|
||||||
preserveLocationHeader bool
|
preserveLocationHeader bool
|
||||||
|
preserveRequestMethod bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewForward creates a forward auth middleware.
|
// NewForward creates a forward auth middleware.
|
||||||
|
@ -81,6 +82,7 @@ func NewForward(ctx context.Context, next http.Handler, config dynamic.ForwardAu
|
||||||
forwardBody: config.ForwardBody,
|
forwardBody: config.ForwardBody,
|
||||||
maxBodySize: dynamic.ForwardAuthDefaultMaxBodySize,
|
maxBodySize: dynamic.ForwardAuthDefaultMaxBodySize,
|
||||||
preserveLocationHeader: config.PreserveLocationHeader,
|
preserveLocationHeader: config.PreserveLocationHeader,
|
||||||
|
preserveRequestMethod: config.PreserveRequestMethod,
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.MaxBodySize != nil {
|
if config.MaxBodySize != nil {
|
||||||
|
@ -135,7 +137,12 @@ func (fa *forwardAuth) GetTracingInformation() (string, string, trace.SpanKind)
|
||||||
func (fa *forwardAuth) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
func (fa *forwardAuth) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||||
logger := middlewares.GetLogger(req.Context(), fa.name, typeNameForward)
|
logger := middlewares.GetLogger(req.Context(), fa.name, typeNameForward)
|
||||||
|
|
||||||
forwardReq, err := http.NewRequestWithContext(req.Context(), http.MethodGet, fa.address, nil)
|
forwardReqMethod := http.MethodGet
|
||||||
|
if fa.preserveRequestMethod {
|
||||||
|
forwardReqMethod = req.Method
|
||||||
|
}
|
||||||
|
|
||||||
|
forwardReq, err := http.NewRequestWithContext(req.Context(), forwardReqMethod, fa.address, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Debug().Err(err).Msgf("Error calling %s", fa.address)
|
logger.Debug().Err(err).Msgf("Error calling %s", fa.address)
|
||||||
observability.SetStatusErrorf(req.Context(), "Error calling %s. Cause %s", fa.address, err)
|
observability.SetStatusErrorf(req.Context(), "Error calling %s. Cause %s", fa.address, err)
|
||||||
|
|
|
@ -739,6 +739,63 @@ func TestForwardAuthPreserveLocationHeader(t *testing.T) {
|
||||||
assert.Equal(t, relativeURL, res.Header.Get("Location"))
|
assert.Equal(t, relativeURL, res.Header.Get("Location"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestForwardAuthPreserveRequestMethod(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
preserveRequestMethod bool
|
||||||
|
originalReqMethod string
|
||||||
|
expectedReqMethodInAuthServer string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "preserve request method",
|
||||||
|
preserveRequestMethod: true,
|
||||||
|
originalReqMethod: http.MethodPost,
|
||||||
|
expectedReqMethodInAuthServer: http.MethodPost,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "do not preserve request method",
|
||||||
|
preserveRequestMethod: false,
|
||||||
|
originalReqMethod: http.MethodPost,
|
||||||
|
expectedReqMethodInAuthServer: http.MethodGet,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, test := range testCases {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
reqReachesAuthServer := false
|
||||||
|
authServer := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||||
|
reqReachesAuthServer = true
|
||||||
|
assert.Equal(t, test.expectedReqMethodInAuthServer, req.Method)
|
||||||
|
}))
|
||||||
|
t.Cleanup(authServer.Close)
|
||||||
|
|
||||||
|
reqReachesNextServer := false
|
||||||
|
next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
reqReachesNextServer = true
|
||||||
|
assert.Equal(t, test.originalReqMethod, r.Method)
|
||||||
|
})
|
||||||
|
|
||||||
|
auth := dynamic.ForwardAuth{
|
||||||
|
Address: authServer.URL,
|
||||||
|
PreserveRequestMethod: test.preserveRequestMethod,
|
||||||
|
}
|
||||||
|
|
||||||
|
middleware, err := NewForward(context.Background(), next, auth, "authTest")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
ts := httptest.NewServer(middleware)
|
||||||
|
t.Cleanup(ts.Close)
|
||||||
|
|
||||||
|
req := testhelpers.MustNewRequest(test.originalReqMethod, ts.URL, nil)
|
||||||
|
res, err := http.DefaultClient.Do(req)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, http.StatusOK, res.StatusCode)
|
||||||
|
assert.True(t, reqReachesAuthServer)
|
||||||
|
assert.True(t, reqReachesNextServer)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type mockTracer struct {
|
type mockTracer struct {
|
||||||
embedded.Tracer
|
embedded.Tracer
|
||||||
|
|
||||||
|
|
|
@ -791,6 +791,7 @@ func createForwardAuthMiddleware(k8sClient Client, namespace string, auth *traef
|
||||||
AddAuthCookiesToResponse: auth.AddAuthCookiesToResponse,
|
AddAuthCookiesToResponse: auth.AddAuthCookiesToResponse,
|
||||||
ForwardBody: auth.ForwardBody,
|
ForwardBody: auth.ForwardBody,
|
||||||
PreserveLocationHeader: auth.PreserveLocationHeader,
|
PreserveLocationHeader: auth.PreserveLocationHeader,
|
||||||
|
PreserveRequestMethod: auth.PreserveRequestMethod,
|
||||||
}
|
}
|
||||||
forwardAuth.SetDefaults()
|
forwardAuth.SetDefaults()
|
||||||
|
|
||||||
|
|
|
@ -167,6 +167,8 @@ type ForwardAuth struct {
|
||||||
MaxBodySize *int64 `json:"maxBodySize,omitempty"`
|
MaxBodySize *int64 `json:"maxBodySize,omitempty"`
|
||||||
// PreserveLocationHeader defines whether to forward the Location header to the client as is or prefix it with the domain name of the authentication server.
|
// PreserveLocationHeader defines whether to forward the Location header to the client as is or prefix it with the domain name of the authentication server.
|
||||||
PreserveLocationHeader bool `json:"preserveLocationHeader,omitempty"`
|
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"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ClientTLS holds the client TLS configuration.
|
// ClientTLS holds the client TLS configuration.
|
||||||
|
|
|
@ -91,6 +91,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||||
"traefik/http/middlewares/Middleware08/forwardAuth/forwardBody": "true",
|
"traefik/http/middlewares/Middleware08/forwardAuth/forwardBody": "true",
|
||||||
"traefik/http/middlewares/Middleware08/forwardAuth/maxBodySize": "42",
|
"traefik/http/middlewares/Middleware08/forwardAuth/maxBodySize": "42",
|
||||||
"traefik/http/middlewares/Middleware08/forwardAuth/preserveLocationHeader": "true",
|
"traefik/http/middlewares/Middleware08/forwardAuth/preserveLocationHeader": "true",
|
||||||
|
"traefik/http/middlewares/Middleware08/forwardAuth/preserveRequestMethod": "true",
|
||||||
"traefik/http/middlewares/Middleware15/redirectScheme/scheme": "foobar",
|
"traefik/http/middlewares/Middleware15/redirectScheme/scheme": "foobar",
|
||||||
"traefik/http/middlewares/Middleware15/redirectScheme/port": "foobar",
|
"traefik/http/middlewares/Middleware15/redirectScheme/port": "foobar",
|
||||||
"traefik/http/middlewares/Middleware15/redirectScheme/permanent": "true",
|
"traefik/http/middlewares/Middleware15/redirectScheme/permanent": "true",
|
||||||
|
@ -446,6 +447,7 @@ func Test_buildConfiguration(t *testing.T) {
|
||||||
ForwardBody: true,
|
ForwardBody: true,
|
||||||
MaxBodySize: pointer(int64(42)),
|
MaxBodySize: pointer(int64(42)),
|
||||||
PreserveLocationHeader: true,
|
PreserveLocationHeader: true,
|
||||||
|
PreserveRequestMethod: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"Middleware06": {
|
"Middleware06": {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue