From d3a17ce3f4dd8a47e6ec96d2d8d89223acf4457b Mon Sep 17 00:00:00 2001 From: Andrew Harding Date: Mon, 10 Sep 2018 17:05:57 -0600 Subject: [PATCH 1/2] support connecting to http proxies via https Signed-off-by: Andrew Harding --- pkg/common/grpcutil/dialer.go | 2 +- pkg/common/grpcutil/proxy.go | 50 +++++++++++++++++++++++------------ 2 files changed, 34 insertions(+), 18 deletions(-) diff --git a/pkg/common/grpcutil/dialer.go b/pkg/common/grpcutil/dialer.go index 2642b85519..491077b798 100644 --- a/pkg/common/grpcutil/dialer.go +++ b/pkg/common/grpcutil/dialer.go @@ -54,7 +54,7 @@ func (d *grpcDialer) Dial(ctx context.Context, addr string) (*grpc.ClientConn, e ctx, cancel := context.WithTimeout(context.Background(), timeout) defer cancel() - conn, err := proxyDial(ctx, addr) + conn, err := proxyDial(ctx, d.log, addr) if err != nil { d.log.Print(err) return nil, err diff --git a/pkg/common/grpcutil/proxy.go b/pkg/common/grpcutil/proxy.go index e90ae19304..b39a03ac10 100644 --- a/pkg/common/grpcutil/proxy.go +++ b/pkg/common/grpcutil/proxy.go @@ -24,6 +24,7 @@ package grpcutil import ( "bufio" + "crypto/tls" "fmt" "io" "net" @@ -31,13 +32,14 @@ import ( "net/http/httputil" "net/url" + "github.com/sirupsen/logrus" "golang.org/x/net/context" "google.golang.org/grpc" ) const grpcUA = "grpc-go/" + grpc.Version -func mapAddress(ctx context.Context, address string) (string, error) { +func getProxyURL(ctx context.Context, address string) (*url.URL, error) { req := &http.Request{ URL: &url.URL{ Scheme: "https", @@ -46,12 +48,17 @@ func mapAddress(ctx context.Context, address string) (string, error) { } url, err := http.ProxyFromEnvironment(req) if err != nil { - return "", err + return nil, err } if url == nil { - return "", nil + return nil, nil + } + switch url.Scheme { + case "http", "https": + default: + return nil, fmt.Errorf("unsupported proxy scheme: %q", url.Scheme) } - return url.Host, nil + return url, nil } // To read a response from a net.Conn, http.ReadResponse() takes a bufio.Reader. @@ -106,26 +113,35 @@ func doHTTPConnectHandshake(ctx context.Context, conn net.Conn, addr string) (_ // proxyDial dials to a proxy and does an HTTP CONNECT handshake if proxying is // enabled. Otherwise, it just does a regular TCP dial. It is based on the // newProxyDialer wrapper implementation from the gRPC codebase. -func proxyDial(ctx context.Context, addr string) (conn net.Conn, err error) { - var skipHandshake bool - newAddr, err := mapAddress(ctx, addr) +func proxyDial(ctx context.Context, log logrus.StdLogger, addr string) (conn net.Conn, err error) { + log.Printf("checking for proxy URL for %s", addr) + proxyURL, err := getProxyURL(ctx, addr) if err != nil { + log.Printf("failed to obtain proxy url for address %s: %v", addr, err) return nil, err } - if newAddr == "" { - skipHandshake = true - newAddr = addr + + if proxyURL == nil { + // no proxy; dial the address directly + return new(net.Dialer).DialContext(ctx, "tcp", addr) } - conn, err = new(net.Dialer).DialContext(ctx, "tcp", newAddr) + // Dial the proxy + log.Printf("proxying via %s to reach %s", proxyURL.String(), addr) + conn, err = new(net.Dialer).DialContext(ctx, "tcp", proxyURL.Host) if err != nil { return nil, err } - if !skipHandshake { - conn, err = doHTTPConnectHandshake(ctx, conn, addr) - if err != nil { - return nil, err - } + + // if the proxy is over HTTPS, wrap the connection in a TLS connection + // before doing the HTTP CONNECT handshake. + if proxyURL.Scheme == "https" { + log.Printf("configuring TLS to connect to server %q", proxyURL.Hostname()) + conn = tls.Client(conn, &tls.Config{ + ServerName: proxyURL.Hostname(), + }) } - return conn, nil + + // Do the HTTP-CONNECT handshake. If unsuccessful, conn is closed. + return doHTTPConnectHandshake(ctx, conn, addr) } From 2a66ddb3d3c81e99e35a590b3cb39df5d0450f09 Mon Sep 17 00:00:00 2001 From: Andrew Harding Date: Mon, 10 Sep 2018 17:26:08 -0600 Subject: [PATCH 2/2] consolidate proxied connection logging Signed-off-by: Andrew Harding --- pkg/common/grpcutil/proxy.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/pkg/common/grpcutil/proxy.go b/pkg/common/grpcutil/proxy.go index b39a03ac10..5ff5510586 100644 --- a/pkg/common/grpcutil/proxy.go +++ b/pkg/common/grpcutil/proxy.go @@ -114,10 +114,9 @@ func doHTTPConnectHandshake(ctx context.Context, conn net.Conn, addr string) (_ // enabled. Otherwise, it just does a regular TCP dial. It is based on the // newProxyDialer wrapper implementation from the gRPC codebase. func proxyDial(ctx context.Context, log logrus.StdLogger, addr string) (conn net.Conn, err error) { - log.Printf("checking for proxy URL for %s", addr) proxyURL, err := getProxyURL(ctx, addr) if err != nil { - log.Printf("failed to obtain proxy url for address %s: %v", addr, err) + log.Printf("Failed to obtain proxy url for address %s: %v", addr, err) return nil, err } @@ -127,7 +126,7 @@ func proxyDial(ctx context.Context, log logrus.StdLogger, addr string) (conn net } // Dial the proxy - log.Printf("proxying via %s to reach %s", proxyURL.String(), addr) + log.Printf("Proxying via %s to reach %s", proxyURL.String(), addr) conn, err = new(net.Dialer).DialContext(ctx, "tcp", proxyURL.Host) if err != nil { return nil, err @@ -136,7 +135,6 @@ func proxyDial(ctx context.Context, log logrus.StdLogger, addr string) (conn net // if the proxy is over HTTPS, wrap the connection in a TLS connection // before doing the HTTP CONNECT handshake. if proxyURL.Scheme == "https" { - log.Printf("configuring TLS to connect to server %q", proxyURL.Hostname()) conn = tls.Client(conn, &tls.Config{ ServerName: proxyURL.Hostname(), })