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) 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 { 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 { 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 { return func(c *genericapiserver.Config) http.Handler {
@ -259,7 +258,26 @@ func newImpersonationReverseProxyFunc(restConfig *rest.Config) (func(*genericapi
return 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 { if err != nil {
plog.WarningErr("rejecting request as we cannot act as the current user", err, plog.WarningErr("rejecting request as we cannot act as the current user", err,
"url", r.URL.String(), "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} gv := schema.GroupVersion{Group: requestInfo.APIGroup, Version: requestInfo.APIVersion}
responsewriters.ErrorNegotiated(err, s, gv, w, r) 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 ( import (
"context" "context"
"math/rand"
"net" "net"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
@ -359,7 +360,7 @@ func TestImpersonatorHTTPHandler(t *testing.T) {
ExecProvider: &api.ExecConfig{}, ExecProvider: &api.ExecConfig{},
AuthProvider: &api.AuthProviderConfig{}, 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", name: "fail to get transport from config",
@ -369,7 +370,7 @@ func TestImpersonatorHTTPHandler(t *testing.T) {
Transport: http.DefaultTransport, Transport: http.DefaultTransport,
TLSClientConfig: rest.TLSClientConfig{Insecure: true}, 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", name: "Impersonate-User header already in request",
@ -513,6 +514,10 @@ func TestImpersonatorHTTPHandler(t *testing.T) {
metav1.AddToGroupVersion(scheme, metav1.Unversioned) metav1.AddToGroupVersion(scheme, metav1.Unversioned)
codecs := serializer.NewCodecFactory(scheme) codecs := serializer.NewCodecFactory(scheme)
serverConfig := genericapiserver.NewRecommendedConfig(codecs) 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() w := httptest.NewRecorder()
requestBeforeServe := tt.request.Clone(tt.request.Context()) requestBeforeServe := tt.request.Clone(tt.request.Context())