1
0
Fork 0

Add option to preserve request method in forwardAuth

This commit is contained in:
Shivam Saxena 2025-01-23 18:58:04 +05:30 committed by GitHub
parent 2b6a04bc1d
commit 2afa03b55c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 139 additions and 5 deletions

View file

@ -260,6 +260,8 @@ type ForwardAuth struct {
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 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() {

View file

@ -53,6 +53,7 @@ func TestDecodeConfiguration(t *testing.T) {
"traefik.http.middlewares.Middleware7.forwardauth.trustforwardheader": "true",
"traefik.http.middlewares.Middleware7.forwardauth.forwardbody": "true",
"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.allowedhosts": "foobar, fiibar",
"traefik.http.middlewares.Middleware8.headers.accesscontrolallowheaders": "X-foobar, X-fiibar",
@ -578,8 +579,9 @@ func TestDecodeConfiguration(t *testing.T) {
"foobar",
"fiibar",
},
ForwardBody: true,
MaxBodySize: pointer(int64(42)),
ForwardBody: true,
MaxBodySize: pointer(int64(42)),
PreserveRequestMethod: true,
},
},
"Middleware8": {
@ -1126,8 +1128,9 @@ func TestEncodeConfiguration(t *testing.T) {
"foobar",
"fiibar",
},
ForwardBody: true,
MaxBodySize: pointer(int64(42)),
ForwardBody: true,
MaxBodySize: pointer(int64(42)),
PreserveRequestMethod: true,
},
},
"Middleware8": {
@ -1342,6 +1345,7 @@ func TestEncodeConfiguration(t *testing.T) {
"traefik.HTTP.Middlewares.Middleware7.ForwardAuth.TLS.Key": "foobar",
"traefik.HTTP.Middlewares.Middleware7.ForwardAuth.TrustForwardHeader": "true",
"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.AccessControlAllowHeaders": "X-foobar, X-fiibar",
"traefik.HTTP.Middlewares.Middleware8.Headers.AccessControlAllowMethods": "GET, PUT",

View file

@ -57,6 +57,7 @@ type forwardAuth struct {
forwardBody bool
maxBodySize int64
preserveLocationHeader bool
preserveRequestMethod bool
}
// NewForward creates a forward auth middleware.
@ -81,6 +82,7 @@ func NewForward(ctx context.Context, next http.Handler, config dynamic.ForwardAu
forwardBody: config.ForwardBody,
maxBodySize: dynamic.ForwardAuthDefaultMaxBodySize,
preserveLocationHeader: config.PreserveLocationHeader,
preserveRequestMethod: config.PreserveRequestMethod,
}
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) {
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 {
logger.Debug().Err(err).Msgf("Error calling %s", fa.address)
observability.SetStatusErrorf(req.Context(), "Error calling %s. Cause %s", fa.address, err)

View file

@ -739,6 +739,63 @@ func TestForwardAuthPreserveLocationHeader(t *testing.T) {
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 {
embedded.Tracer

View file

@ -791,6 +791,7 @@ func createForwardAuthMiddleware(k8sClient Client, namespace string, auth *traef
AddAuthCookiesToResponse: auth.AddAuthCookiesToResponse,
ForwardBody: auth.ForwardBody,
PreserveLocationHeader: auth.PreserveLocationHeader,
PreserveRequestMethod: auth.PreserveRequestMethod,
}
forwardAuth.SetDefaults()

View file

@ -167,6 +167,8 @@ type ForwardAuth struct {
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 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.

View file

@ -91,6 +91,7 @@ func Test_buildConfiguration(t *testing.T) {
"traefik/http/middlewares/Middleware08/forwardAuth/forwardBody": "true",
"traefik/http/middlewares/Middleware08/forwardAuth/maxBodySize": "42",
"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/port": "foobar",
"traefik/http/middlewares/Middleware15/redirectScheme/permanent": "true",
@ -446,6 +447,7 @@ func Test_buildConfiguration(t *testing.T) {
ForwardBody: true,
MaxBodySize: pointer(int64(42)),
PreserveLocationHeader: true,
PreserveRequestMethod: true,
},
},
"Middleware06": {