Clean connection headers for forward auth request only

Co-authored-by: Kevin Pollet <pollet.kevin@gmail.com>
This commit is contained in:
Romain 2024-09-27 11:18:05 +02:00 committed by GitHub
parent b00f640d72
commit a42d396ed2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 30 additions and 30 deletions

View file

@ -637,3 +637,15 @@ Increasing the `readTimeout` value could be the solution notably if you are deal
- TCP: `Error while handling TCP connection: readfrom tcp X.X.X.X:X->X.X.X.X:X: read tcp X.X.X.X:X->X.X.X.X:X: i/o timeout` - TCP: `Error while handling TCP connection: readfrom tcp X.X.X.X:X->X.X.X.X:X: read tcp X.X.X.X:X->X.X.X.X:X: i/o timeout`
- HTTP: `'499 Client Closed Request' caused by: context canceled` - HTTP: `'499 Client Closed Request' caused by: context canceled`
- HTTP: `ReverseProxy read error during body copy: read tcp X.X.X.X:X->X.X.X.X:X: use of closed network connection` - HTTP: `ReverseProxy read error during body copy: read tcp X.X.X.X:X->X.X.X.X:X: use of closed network connection`
## v2.11.3
### Connection headers
In `v2.11.3`, the handling of the request Connection headers directives has changed to prevent any abuse.
Before, Traefik removed any header listed in the Connection header just before forwarding the request to the backends.
Now, Traefik removes the headers listed in the Connection header as soon as the request is handled.
As a consequence, middlewares do not have access to those Connection headers,
and a new option has been introduced to specify which ones could go through the middleware chain before being removed: `<entrypoint>.forwardedHeaders.connection`.
Please check out the [entrypoint forwarded headers connection option configuration](../routing/entrypoints.md#forwarded-headers) documentation.

View file

@ -13,16 +13,21 @@ const (
upgradeHeader = "Upgrade" upgradeHeader = "Upgrade"
) )
// Remover removes hop-by-hop headers listed in the "Connection" header. // RemoveConnectionHeaders removes hop-by-hop headers listed in the "Connection" header.
// See RFC 7230, section 6.1. // See RFC 7230, section 6.1.
func Remover(next http.Handler) http.HandlerFunc { func RemoveConnectionHeaders(req *http.Request) {
return func(rw http.ResponseWriter, req *http.Request) {
var reqUpType string var reqUpType string
if httpguts.HeaderValuesContainsToken(req.Header[connectionHeader], upgradeHeader) { if httpguts.HeaderValuesContainsToken(req.Header[connectionHeader], upgradeHeader) {
reqUpType = req.Header.Get(upgradeHeader) reqUpType = req.Header.Get(upgradeHeader)
} }
removeConnectionHeaders(req.Header) for _, f := range req.Header[connectionHeader] {
for _, sf := range strings.Split(f, ",") {
if sf = textproto.TrimString(sf); sf != "" {
req.Header.Del(sf)
}
}
}
if reqUpType != "" { if reqUpType != "" {
req.Header.Set(connectionHeader, upgradeHeader) req.Header.Set(connectionHeader, upgradeHeader)
@ -30,17 +35,4 @@ func Remover(next http.Handler) http.HandlerFunc {
} else { } else {
req.Header.Del(connectionHeader) req.Header.Del(connectionHeader)
} }
next.ServeHTTP(rw, req)
}
}
func removeConnectionHeaders(h http.Header) {
for _, f := range h[connectionHeader] {
for _, sf := range strings.Split(f, ",") {
if sf = textproto.TrimString(sf); sf != "" {
h.Del(sf)
}
}
}
} }

View file

@ -50,19 +50,13 @@ func TestRemover(t *testing.T) {
t.Run(test.desc, func(t *testing.T) { t.Run(test.desc, func(t *testing.T) {
t.Parallel() t.Parallel()
next := http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {})
h := Remover(next)
req := httptest.NewRequest(http.MethodGet, "https://localhost", nil) req := httptest.NewRequest(http.MethodGet, "https://localhost", nil)
for k, v := range test.reqHeaders { for k, v := range test.reqHeaders {
req.Header.Set(k, v) req.Header.Set(k, v)
} }
rw := httptest.NewRecorder() RemoveConnectionHeaders(req)
h.ServeHTTP(rw, req)
assert.Equal(t, test.expected, req.Header) assert.Equal(t, test.expected, req.Header)
}) })

View file

@ -89,7 +89,7 @@ func NewForward(ctx context.Context, next http.Handler, config dynamic.ForwardAu
fa.authResponseHeadersRegex = re fa.authResponseHeadersRegex = re
} }
return Remover(fa), nil return fa, nil
} }
func (fa *forwardAuth) GetTracingInformation() (string, ext.SpanKindEnum) { func (fa *forwardAuth) GetTracingInformation() (string, ext.SpanKindEnum) {
@ -195,6 +195,8 @@ func (fa *forwardAuth) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
func writeHeader(req, forwardReq *http.Request, trustForwardHeader bool, allowedHeaders []string) { func writeHeader(req, forwardReq *http.Request, trustForwardHeader bool, allowedHeaders []string) {
utils.CopyHeaders(forwardReq.Header, req.Header) utils.CopyHeaders(forwardReq.Header, req.Header)
RemoveConnectionHeaders(forwardReq)
utils.RemoveHeaders(forwardReq.Header, hopHeaders...) utils.RemoveHeaders(forwardReq.Header, hopHeaders...)
forwardReq.Header = filterForwardRequestHeaders(forwardReq.Header, allowedHeaders) forwardReq.Header = filterForwardRequestHeaders(forwardReq.Header, allowedHeaders)