Clean connection headers for forward auth request only
Co-authored-by: Kevin Pollet <pollet.kevin@gmail.com>
This commit is contained in:
parent
b00f640d72
commit
a42d396ed2
4 changed files with 30 additions and 30 deletions
|
@ -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.
|
||||||
|
|
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
})
|
})
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in a new issue