Skip to content

Commit

Permalink
Merge pull request #25 from stripe/pspieker-add-connect-proxy-auth
Browse files Browse the repository at this point in the history
Add Proxy-Authorization header support for URL encoded credentials on HTTPS Requests
  • Loading branch information
cds2-stripe authored Jul 10, 2024
2 parents 560c3ba + 2f08560 commit e7f34c1
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 8 deletions.
30 changes: 23 additions & 7 deletions https.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"bytes"
"context"
"crypto/tls"
"encoding/base64"
"errors"
"fmt"
"io"
Expand Down Expand Up @@ -119,21 +120,21 @@ func (proxy *ProxyHttpServer) handleHttps(w http.ResponseWriter, r *http.Request
host += ":80"
}

var httpsProxyURL string = proxy.HttpsProxyAddr
var httpsProxyString string = proxy.HttpsProxyAddr
if r.Header.Get(PerRequestHTTPSProxyHeaderKey) != "" {
httpsProxyURL = r.Header.Get(PerRequestHTTPSProxyHeaderKey)
httpsProxyString = r.Header.Get(PerRequestHTTPSProxyHeaderKey)
}

httpsProxy, err := httpsProxyAddr(r.URL, httpsProxyURL)
httpsProxyString, err := httpsProxyAddr(r.URL, httpsProxyString)
if err != nil {
ctx.Warnf("Error configuring HTTPS proxy err=%q url=%q", err, r.URL.String())
}

var targetSiteCon net.Conn
if httpsProxy == "" {
if httpsProxyString == "" {
targetSiteCon, err = proxy.connectDialContext(ctx, "tcp", host)
} else {
targetSiteCon, err = proxy.connectDialProxyWithContext(ctx, httpsProxy, host)
targetSiteCon, err = proxy.connectDialProxyWithContext(ctx, httpsProxyString, host)
}
if err != nil {
httpError(proxyClient, ctx, err)
Expand Down Expand Up @@ -543,11 +544,21 @@ func (proxy *ProxyHttpServer) connectDialProxyWithContext(ctx *ProxyCtx, proxyHo
c = tls.Client(c, proxy.Tr.TLSClientConfig)
}

connectRequestHeaders := make(http.Header)

// Add authentication header if needed to the CONNECT request to the proxy
user := proxyURL.User
if user != nil {
if auth := user.String(); auth != "" {
connectRequestHeaders.Add("Proxy-Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(auth)))
}
}

connectReq := &http.Request{
Method: "CONNECT",
URL: &url.URL{Opaque: host},
Host: host,
Header: make(http.Header),
Header: connectRequestHeaders,
}
connectReq.Write(c)
// Read response.
Expand Down Expand Up @@ -605,5 +616,10 @@ func httpsProxyAddr(reqURL *url.URL, httpsProxy string) (string, error) {
service = proxyURL.Scheme
}

return fmt.Sprintf("%s://%s:%s", proxyURL.Scheme, proxyURL.Hostname(), service), nil
hostname := proxyURL.Hostname()
if proxyURL.User != nil && proxyURL.User.String() != "" {
hostname = proxyURL.User.String() + "@" + hostname
}

return fmt.Sprintf("%s://%s:%s", proxyURL.Scheme, hostname, service), nil
}
7 changes: 6 additions & 1 deletion proxy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -750,7 +750,12 @@ func TestOverrideHttpsProxyAddrsFromEnvWithRequest(t *testing.T) {

fakeExternalProxy := goproxy.NewProxyHttpServer()
fakeExternalProxyTestStruct := httptest.NewServer(fakeExternalProxy)
fakeExternalProxy.OnRequest().HandleConnect(goproxy.AlwaysMitm)
var AlwaysMitmAndPassthrough goproxy.FuncHttpsHandler = func(host string, ctx *goproxy.ProxyCtx) (*goproxy.ConnectAction, string) {
// TODO: make this test use the X-Https-Upstream-Proxy header and parse it out here to make sure it's set and passed through
// to the authorization header
return goproxy.MitmConnect, host
}
fakeExternalProxy.OnRequest().HandleConnect(AlwaysMitmAndPassthrough)
tagExternalProxyPassthrough := func(resp *http.Response, ctx *goproxy.ProxyCtx) *http.Response {
b, err := ioutil.ReadAll(resp.Body)
panicOnErr(err, "readAll resp")
Expand Down

0 comments on commit e7f34c1

Please sign in to comment.