Merge pull request #97385 from FabianKramm/fix-http2-proxy

kubectl proxy: override request host
This commit is contained in:
Kubernetes Prow Robot 2021-02-25 15:36:15 -08:00 committed by GitHub
commit 49e0c4456d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 47 additions and 0 deletions

View File

@ -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)
}

View File

@ -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)

View File

@ -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 {