diff --git a/components/ingress/pkg/proxy/header.go b/components/ingress/pkg/proxy/header.go index 9105683cd..e768e7448 100644 --- a/components/ingress/pkg/proxy/header.go +++ b/components/ingress/pkg/proxy/header.go @@ -29,9 +29,22 @@ var ( AccessControlAllowOrigin = http.CanonicalHeaderKey("Access-Control-Allow-Origin") ReverseProxyServerPowerBy = http.CanonicalHeaderKey("Reverse-Proxy-Server-PowerBy") - SecWebSocketProtocol = http.CanonicalHeaderKey("Sec-WebSocket-Protocol") - Cookie = http.CanonicalHeaderKey("Cookie") - SetCookie = http.CanonicalHeaderKey("Set-Cookie") - Host = http.CanonicalHeaderKey("Host") - Origin = http.CanonicalHeaderKey("Origin") + SecWebSocketProtocol = http.CanonicalHeaderKey("Sec-WebSocket-Protocol") + SecWebSocketKey = http.CanonicalHeaderKey("Sec-WebSocket-Key") + SecWebSocketVersion = http.CanonicalHeaderKey("Sec-WebSocket-Version") + SecWebSocketExtensions = http.CanonicalHeaderKey("Sec-WebSocket-Extensions") + Cookie = http.CanonicalHeaderKey("Cookie") + SetCookie = http.CanonicalHeaderKey("Set-Cookie") + Host = http.CanonicalHeaderKey("Host") + Origin = http.CanonicalHeaderKey("Origin") + + // Hop-by-hop headers per RFC 7230 §6.1 — must not be forwarded by proxies. + HopByHopConnection = http.CanonicalHeaderKey("Connection") + HopByHopKeepAlive = http.CanonicalHeaderKey("Keep-Alive") + HopByHopProxyAuth = http.CanonicalHeaderKey("Proxy-Authenticate") + HopByHopProxyAuthz = http.CanonicalHeaderKey("Proxy-Authorization") + HopByHopTE = http.CanonicalHeaderKey("TE") + HopByHopTrailer = http.CanonicalHeaderKey("Trailer") + HopByHopTransferEncoding = http.CanonicalHeaderKey("Transfer-Encoding") + HopByHopUpgrade = http.CanonicalHeaderKey("Upgrade") ) diff --git a/components/ingress/pkg/proxy/websocket.go b/components/ingress/pkg/proxy/websocket.go index 7d14d7ad8..eb881d25f 100644 --- a/components/ingress/pkg/proxy/websocket.go +++ b/components/ingress/pkg/proxy/websocket.go @@ -100,17 +100,31 @@ func (w *WebSocketProxy) ServeHTTP(rw http.ResponseWriter, r *http.Request) { dialer = defaultWebSocketDialer } - // Pass headers from the incoming request to the dialer to forward them to - // the final destinations. - requestHeader := http.Header{} - if origin := r.Header.Get(Origin); origin != "" { - requestHeader.Add(Origin, origin) - } - for _, prot := range r.Header[SecWebSocketProtocol] { - requestHeader.Add(SecWebSocketProtocol, prot) + // Forward all incoming headers to the backend except hop-by-hop headers + // (RFC 7230 §6.1) and WebSocket handshake headers managed by the dialer. + // Per RFC 7230, also strip any header named by Connection tokens. + connTokens := map[string]bool{} + for _, v := range r.Header[HopByHopConnection] { + for _, token := range strings.Split(v, ",") { + if h := http.CanonicalHeaderKey(strings.TrimSpace(token)); h != "" { + connTokens[h] = true + } + } } - for _, cokiee := range r.Header[Cookie] { - requestHeader.Add(Cookie, cokiee) + requestHeader := http.Header{} + for key, values := range r.Header { + switch key { + case HopByHopConnection, HopByHopKeepAlive, HopByHopProxyAuth, HopByHopProxyAuthz, + HopByHopTE, HopByHopTrailer, HopByHopTransferEncoding, HopByHopUpgrade, + SecWebSocketKey, SecWebSocketVersion, SecWebSocketExtensions: + continue + } + if connTokens[key] { + continue + } + for _, v := range values { + requestHeader.Add(key, v) + } } if r.Host != "" { requestHeader.Set(Host, r.Host)