internal/concierge/impersonator: use http/2.0 as much as we can

Signed-off-by: Monis Khan <mok@vmware.com>
This commit is contained in:
Andrew Keesler 2021-03-18 15:32:33 -04:00
parent bd8c243636
commit c22ac17dfe
No known key found for this signature in database
GPG Key ID: 27CE0444346F9413
2 changed files with 41 additions and 8 deletions

View File

@ -217,15 +217,14 @@ func newImpersonationReverseProxyFunc(restConfig *rest.Config) (func(*genericapi
return nil, fmt.Errorf("could not parse host URL from in-cluster config: %w", err)
}
kubeTransportConfig, err := restConfig.TransportConfig()
http1RoundTripper, err := getTransportForProtocol(restConfig, "http/1.1")
if err != nil {
return nil, fmt.Errorf("could not get in-cluster transport config: %w", err)
return nil, fmt.Errorf("could not get http/1.1 round tripper: %w", err)
}
kubeTransportConfig.TLS.NextProtos = []string{"http/1.1"} // TODO huh?
kubeRoundTripper, err := transport.New(kubeTransportConfig)
http2RoundTripper, err := getTransportForProtocol(restConfig, "h2")
if err != nil {
return nil, fmt.Errorf("could not get in-cluster transport: %w", err)
return nil, fmt.Errorf("could not get http/2.0 round tripper: %w", err)
}
return func(c *genericapiserver.Config) http.Handler {
@ -259,7 +258,26 @@ func newImpersonationReverseProxyFunc(restConfig *rest.Config) (func(*genericapi
return
}
rt, err := getTransportForUser(userInfo, kubeRoundTripper)
reqInfo, ok := request.RequestInfoFrom(r.Context())
if !ok {
plog.Warning("aggregated API server logic did not set request info but it is always supposed to do so",
"url", r.URL.String(),
"method", r.Method,
)
newInternalErrResponse(w, r, c.Serializer, "invalid request info")
return
}
// when we are running regular requests (e.g., CRUD) we should always be able to use HTTP/2.0
// since KAS always supports that and it goes through proxies just fine. for long running
// requests (e.g., proxy, watch), we know they use http/1.1 with an upgrade to
// websockets/SPDY (this upgrade is NEVER to HTTP/2.0 as the KAS does not support that).
baseRT := http2RoundTripper
if c.LongRunningFunc(r, reqInfo) {
baseRT = http1RoundTripper
}
rt, err := getTransportForUser(userInfo, baseRT)
if err != nil {
plog.WarningErr("rejecting request as we cannot act as the current user", err,
"url", r.URL.String(),
@ -335,3 +353,13 @@ func newStatusErrResponse(w http.ResponseWriter, r *http.Request, s runtime.Nego
gv := schema.GroupVersion{Group: requestInfo.APIGroup, Version: requestInfo.APIVersion}
responsewriters.ErrorNegotiated(err, s, gv, w, r)
}
func getTransportForProtocol(restConfig *rest.Config, protocol string) (http.RoundTripper, error) {
transportConfig, err := restConfig.TransportConfig()
if err != nil {
return nil, fmt.Errorf("could not get in-cluster transport config: %w", err)
}
transportConfig.TLS.NextProtos = []string{protocol}
return transport.New(transportConfig)
}

View File

@ -5,6 +5,7 @@ package impersonator
import (
"context"
"math/rand"
"net"
"net/http"
"net/http/httptest"
@ -359,7 +360,7 @@ func TestImpersonatorHTTPHandler(t *testing.T) {
ExecProvider: &api.ExecConfig{},
AuthProvider: &api.AuthProviderConfig{},
},
wantCreationErr: "could not get in-cluster transport config: execProvider and authProvider cannot be used in combination",
wantCreationErr: "could not get http/1.1 round tripper: could not get in-cluster transport config: execProvider and authProvider cannot be used in combination",
},
{
name: "fail to get transport from config",
@ -369,7 +370,7 @@ func TestImpersonatorHTTPHandler(t *testing.T) {
Transport: http.DefaultTransport,
TLSClientConfig: rest.TLSClientConfig{Insecure: true},
},
wantCreationErr: "could not get in-cluster transport: using a custom transport with TLS certificate options or the insecure flag is not allowed",
wantCreationErr: "could not get http/1.1 round tripper: using a custom transport with TLS certificate options or the insecure flag is not allowed",
},
{
name: "Impersonate-User header already in request",
@ -513,6 +514,10 @@ func TestImpersonatorHTTPHandler(t *testing.T) {
metav1.AddToGroupVersion(scheme, metav1.Unversioned)
codecs := serializer.NewCodecFactory(scheme)
serverConfig := genericapiserver.NewRecommendedConfig(codecs)
serverConfig.Config.LongRunningFunc = func(_ *http.Request, _ *request.RequestInfo) bool {
// take the HTTP/2.0 vs HTTP/1.1 branch randomly to make sure we exercise both branches
return rand.Int()%2 == 0 //nolint:gosec // we don't care whether this is cryptographically secure or not
}
w := httptest.NewRecorder()
requestBeforeServe := tt.request.Clone(tt.request.Context())