From b5e5df63bc7d18b3b8fd621c2c9d24856221b58e Mon Sep 17 00:00:00 2001 From: Vasili Revelas Date: Wed, 2 Nov 2022 21:45:29 +0200 Subject: [PATCH] Fix SPDY proxy authentication with special chars The username and password sent in the Proxy-Authorization header are not supposed to be percent escaped prior to being base64 encoded. Kubernetes-commit: bbb5513b3b4c956c486685886634c71ce7c31b9f --- pkg/util/httpstream/spdy/roundtripper.go | 7 +++-- pkg/util/httpstream/spdy/roundtripper_test.go | 30 ++++++++++++------- 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/pkg/util/httpstream/spdy/roundtripper.go b/pkg/util/httpstream/spdy/roundtripper.go index 858eafb86..27c3d2d56 100644 --- a/pkg/util/httpstream/spdy/roundtripper.go +++ b/pkg/util/httpstream/spdy/roundtripper.go @@ -297,9 +297,10 @@ func (s *SpdyRoundTripper) proxyAuth(proxyURL *url.URL) string { if proxyURL == nil || proxyURL.User == nil { return "" } - credentials := proxyURL.User.String() - encodedAuth := base64.StdEncoding.EncodeToString([]byte(credentials)) - return fmt.Sprintf("Basic %s", encodedAuth) + username := proxyURL.User.Username() + password, _ := proxyURL.User.Password() + auth := username + ":" + password + return "Basic " + base64.StdEncoding.EncodeToString([]byte(auth)) } // RoundTrip executes the Request and upgrades it. After a successful upgrade, diff --git a/pkg/util/httpstream/spdy/roundtripper_test.go b/pkg/util/httpstream/spdy/roundtripper_test.go index 10b329594..a0b7b169d 100644 --- a/pkg/util/httpstream/spdy/roundtripper_test.go +++ b/pkg/util/httpstream/spdy/roundtripper_test.go @@ -20,7 +20,6 @@ import ( "context" "crypto/tls" "crypto/x509" - "encoding/base64" "io" "net" "net/http" @@ -291,6 +290,16 @@ func TestRoundTripAndNewConnection(t *testing.T) { serverStatusCode: http.StatusSwitchingProtocols, shouldError: false, }, + "proxied valid https, proxy auth with chars that percent escape -> valid https": { + serverFunc: httpsServerValidHostname(t), + proxyServerFunc: httpsServerValidHostname(t), + proxyAuth: url.UserPassword("proxy user", "proxypasswd%"), + clientTLS: &tls.Config{RootCAs: localhostPool}, + serverConnectionHeader: "Upgrade", + serverUpgradeHeader: "SPDY/3.1", + serverStatusCode: http.StatusSwitchingProtocols, + shouldError: false, + }, } for k, testCase := range testCases { @@ -400,18 +409,19 @@ func TestRoundTripAndNewConnection(t *testing.T) { } } - var expectedProxyAuth string if testCase.proxyAuth != nil { - encodedCredentials := base64.StdEncoding.EncodeToString([]byte(testCase.proxyAuth.String())) - expectedProxyAuth = "Basic " + encodedCredentials - } - if len(expectedProxyAuth) == 0 && proxyCalledWithAuth { + expectedUsername := testCase.proxyAuth.Username() + expectedPassword, _ := testCase.proxyAuth.Password() + username, password, ok := (&http.Request{Header: http.Header{"Authorization": []string{proxyCalledWithAuthHeader}}}).BasicAuth() + if !ok { + t.Fatalf("invalid proxy auth header %s", proxyCalledWithAuthHeader) + } + if username != expectedUsername || password != expectedPassword { + t.Fatalf("expected proxy auth \"%s:%s\", got \"%s:%s\"", expectedUsername, expectedPassword, username, password) + } + } else if proxyCalledWithAuth { t.Fatalf("proxy authorization unexpected, got %q", proxyCalledWithAuthHeader) } - if proxyCalledWithAuthHeader != expectedProxyAuth { - t.Fatalf("expected to see a call to the proxy with credentials %q, got %q", testCase.proxyAuth, proxyCalledWithAuthHeader) - } - }) } }