diff --git a/staging/src/k8s.io/apimachinery/pkg/util/proxy/upgradeaware.go b/staging/src/k8s.io/apimachinery/pkg/util/proxy/upgradeaware.go index 43f528d9cd6..8ef16eeb63f 100644 --- a/staging/src/k8s.io/apimachinery/pkg/util/proxy/upgradeaware.go +++ b/staging/src/k8s.io/apimachinery/pkg/util/proxy/upgradeaware.go @@ -74,6 +74,12 @@ type UpgradeAwareHandler struct { RequireSameHostRedirects bool // UseRequestLocation will use the incoming request URL when talking to the backend server. UseRequestLocation bool + // UseLocationHost overrides the HTTP host header in requests to the backend server to use the Host from Location. + // This will override the req.Host field of a request, while UseRequestLocation will override the req.URL field + // of a request. The req.URL.Host specifies the server to connect to, while the req.Host field + // specifies the Host header value to send in the HTTP request. If this is false, the incoming req.Host header will + // just be forwarded to the backend server. + UseLocationHost bool // FlushInterval controls how often the standard HTTP proxy will flush content from the upstream. FlushInterval time.Duration // MaxBytesPerSec controls the maximum rate for an upstream connection. No rate is imposed if the value is zero. @@ -227,6 +233,11 @@ func (h *UpgradeAwareHandler) ServeHTTP(w http.ResponseWriter, req *http.Request if !h.UseRequestLocation { newReq.URL = &loc } + if h.UseLocationHost { + // exchanging req.Host with the backend location is necessary for backends that act on the HTTP host header (e.g. API gateways), + // because req.Host has preference over req.URL.Host in filling this header field + newReq.Host = h.Location.Host + } proxy := httputil.NewSingleHostReverseProxy(&url.URL{Scheme: h.Location.Scheme, Host: h.Location.Host}) proxy.Transport = h.Transport @@ -282,6 +293,9 @@ func (h *UpgradeAwareHandler) tryUpgrade(w http.ResponseWriter, req *http.Reques backendConn, rawResponse, err = utilnet.ConnectWithRedirects(req.Method, &location, clone.Header, req.Body, utilnet.DialerFunc(h.DialForUpgrade), h.RequireSameHostRedirects) } else { klog.V(6).Infof("Connecting to backend proxy (direct dial) %s\n Headers: %v", &location, clone.Header) + if h.UseLocationHost { + clone.Host = h.Location.Host + } clone.URL = &location backendConn, err = h.DialForUpgrade(clone) } diff --git a/staging/src/k8s.io/apimachinery/pkg/util/proxy/upgradeaware_test.go b/staging/src/k8s.io/apimachinery/pkg/util/proxy/upgradeaware_test.go index 3f2d41f9460..02fc02ec73e 100644 --- a/staging/src/k8s.io/apimachinery/pkg/util/proxy/upgradeaware_test.go +++ b/staging/src/k8s.io/apimachinery/pkg/util/proxy/upgradeaware_test.go @@ -85,6 +85,7 @@ func (fakeConn) SetWriteDeadline(t time.Time) error { return nil } type SimpleBackendHandler struct { requestURL url.URL + requestHost string requestHeader http.Header requestBody []byte requestMethod string @@ -95,6 +96,7 @@ type SimpleBackendHandler struct { func (s *SimpleBackendHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { s.requestURL = *req.URL + s.requestHost = req.Host s.requestHeader = req.Header s.requestMethod = req.Method var err error @@ -162,6 +164,7 @@ func TestServeHTTP(t *testing.T) { notExpectedRespHeader []string upgradeRequired bool expectError func(err error) bool + useLocationHost bool }{ { name: "root path, simple get", @@ -222,6 +225,27 @@ func TestServeHTTP(t *testing.T) { "Access-Control-Allow-Methods", }, }, + { + name: "use location host", + method: "GET", + requestPath: "/some/path", + expectedPath: "/some/path", + useLocationHost: true, + }, + { + name: "use location host - invalid upgrade", + method: "GET", + upgradeRequired: true, + requestHeader: map[string]string{ + httpstream.HeaderConnection: httpstream.HeaderUpgrade, + }, + expectError: func(err error) bool { + return err != nil && strings.Contains(err.Error(), "invalid upgrade response: status code 200") + }, + requestPath: "/some/path", + expectedPath: "/some/path", + useLocationHost: true, + }, } for i, test := range tests { @@ -244,6 +268,7 @@ func TestServeHTTP(t *testing.T) { backendURL, _ := url.Parse(backendServer.URL) backendURL.Path = test.requestPath proxyHandler := NewUpgradeAwareHandler(backendURL, nil, false, test.upgradeRequired, responder) + proxyHandler.UseLocationHost = test.useLocationHost proxyServer := httptest.NewServer(proxyHandler) defer proxyServer.Close() proxyURL, _ := url.Parse(proxyServer.URL) @@ -274,6 +299,13 @@ func TestServeHTTP(t *testing.T) { t.Errorf("Error from proxy request: %v", err) } + // Host + if test.useLocationHost && backendHandler.requestHost != backendURL.Host { + t.Errorf("Unexpected request host: %s", backendHandler.requestHost) + } else if !test.useLocationHost && backendHandler.requestHost == backendURL.Host { + t.Errorf("Unexpected request host: %s", backendHandler.requestHost) + } + if test.expectError != nil { if !responder.called { t.Errorf("%d: responder was not invoked", i) diff --git a/staging/src/k8s.io/kubectl/pkg/proxy/proxy_server.go b/staging/src/k8s.io/kubectl/pkg/proxy/proxy_server.go index 2b21bb4c9dd..a7712fe2c91 100644 --- a/staging/src/k8s.io/kubectl/pkg/proxy/proxy_server.go +++ b/staging/src/k8s.io/kubectl/pkg/proxy/proxy_server.go @@ -211,6 +211,7 @@ func NewProxyHandler(apiProxyPrefix string, filter *FilterServer, cfg *rest.Conf proxy := proxy.NewUpgradeAwareHandler(target, transport, false, false, responder) proxy.UpgradeTransport = upgradeTransport proxy.UseRequestLocation = true + proxy.UseLocationHost = true proxyServer := http.Handler(proxy) if filter != nil {