From da26a6589a74d7399edac83dd9813766403dea07 Mon Sep 17 00:00:00 2001 From: Andrew Burke <31974658+atburke@users.noreply.github.com> Date: Thu, 12 May 2022 09:39:18 -0700 Subject: [PATCH] Ignore HTTP_PROXY in reverse tunnels, part 2 (#12335) This change disables HTTP_PROXY in a few places that were missed in #11990. --- api/client/client.go | 9 ++++++++- api/client/contextdialer.go | 8 ++++---- integration/proxy_test.go | 29 +++++++++++++++++++++++++++++ lib/auth/authclient/authclient.go | 2 ++ lib/auth/clt.go | 7 ++++++- lib/reversetunnel/resolver.go | 3 ++- lib/service/connect.go | 2 ++ 7 files changed, 53 insertions(+), 7 deletions(-) diff --git a/api/client/client.go b/api/client/client.go index 9ecb8cbcf7a1f..9fc08d896e48d 100644 --- a/api/client/client.go +++ b/api/client/client.go @@ -288,7 +288,12 @@ type ( // authConnect connects to the Teleport Auth Server directly. func authConnect(ctx context.Context, params connectParams) (*Client, error) { - dialer := NewDialer(params.cfg.KeepAlivePeriod, params.cfg.DialTimeout) + var dialer ContextDialer + if params.cfg.IgnoreHTTPProxy { + dialer = NewDirectDialer(params.cfg.KeepAlivePeriod, params.cfg.DialTimeout) + } else { + dialer = NewDialer(params.cfg.KeepAlivePeriod, params.cfg.DialTimeout) + } clt := newClient(params.cfg, dialer, params.tlsConfig) if err := clt.dialGRPC(ctx, params.addr); err != nil { return nil, trace.Wrap(err, "failed to connect to addr %v as an auth server", params.addr) @@ -462,6 +467,8 @@ type Config struct { // ALPNSNIAuthDialClusterName if present the client will include ALPN SNI routing information in TLS Hello message // allowing to dial auth service through Teleport Proxy directly without using SSH Tunnels. ALPNSNIAuthDialClusterName string + // IgnoreHTTPProxy disables support for HTTP proxying when true. + IgnoreHTTPProxy bool } // CheckAndSetDefaults checks and sets default config values. diff --git a/api/client/contextdialer.go b/api/client/contextdialer.go index 52c8efb2b2a23..0b9f1fbfd40d8 100644 --- a/api/client/contextdialer.go +++ b/api/client/contextdialer.go @@ -45,8 +45,8 @@ func (f ContextDialerFunc) DialContext(ctx context.Context, network, addr string return f(ctx, network, addr) } -// newDirectDialer makes a new dialer to connect directly to an Auth server. -func newDirectDialer(keepAlivePeriod, dialTimeout time.Duration) ContextDialer { +// NewDirectDialer makes a new dialer to connect directly to an Auth server, ignoring any HTTP proxies. +func NewDirectDialer(keepAlivePeriod, dialTimeout time.Duration) ContextDialer { return &net.Dialer{ Timeout: dialTimeout, KeepAlive: keepAlivePeriod, @@ -57,7 +57,7 @@ func newDirectDialer(keepAlivePeriod, dialTimeout time.Duration) ContextDialer { // on the environment. func NewDialer(keepAlivePeriod, dialTimeout time.Duration) ContextDialer { return ContextDialerFunc(func(ctx context.Context, network, addr string) (net.Conn, error) { - dialer := newDirectDialer(keepAlivePeriod, dialTimeout) + dialer := NewDirectDialer(keepAlivePeriod, dialTimeout) if proxyAddr := proxy.GetProxyAddress(addr); proxyAddr != nil { return DialProxyWithDialer(ctx, proxyAddr.Host, addr, dialer) } @@ -86,7 +86,7 @@ func NewProxyDialer(ssh ssh.ClientConfig, keepAlivePeriod, dialTimeout time.Dura // newTunnelDialer makes a dialer to connect to an Auth server through the SSH reverse tunnel on the proxy. func newTunnelDialer(ssh ssh.ClientConfig, keepAlivePeriod, dialTimeout time.Duration) ContextDialer { - dialer := newDirectDialer(keepAlivePeriod, dialTimeout) + dialer := NewDirectDialer(keepAlivePeriod, dialTimeout) return ContextDialerFunc(func(ctx context.Context, network, addr string) (conn net.Conn, err error) { conn, err = dialer.DialContext(ctx, network, addr) if err != nil { diff --git a/integration/proxy_test.go b/integration/proxy_test.go index b1793d55730ee..5dd78f503a12b 100644 --- a/integration/proxy_test.go +++ b/integration/proxy_test.go @@ -239,6 +239,35 @@ func TestALPNSNIHTTPSProxy(t *testing.T) { require.Greater(t, ps.Count(), 0, "proxy did not intercept any connection") } +// TestMultiPortNoProxy tests that the reverse tunnel does NOT use http_proxy +// when not in single-port mode. +func TestMultiPortNoProxy(t *testing.T) { + // set the http_proxy environment variable + t.Setenv("http_proxy", "fakeproxy.example.com") + + username := mustGetCurrentUser(t).Username + // httpproxy won't proxy when target address is localhost, so use this instead. + addr, err := getLocalIP() + require.NoError(t, err) + + suite := newProxySuite(t, + withRootClusterConfig(rootClusterStandardConfig(t)), + withLeafClusterConfig(leafClusterStandardConfig(t)), + withRootClusterNodeName(addr), + withLeafClusterNodeName(addr), + withRootClusterPorts(standardPortSetup()), + withLeafClusterPorts(standardPortSetup()), + withRootAndLeafClusterRoles(createTestRole(username)), + withStandardRoleMapping(), + ) + + // Wait for both cluster to see each other via reverse tunnels. + require.Eventually(t, waitForClusters(suite.root.Tunnel, 1), 10*time.Second, 1*time.Second, + "Two clusters do not see each other: tunnels are not working.") + require.Eventually(t, waitForClusters(suite.leaf.Tunnel, 1), 10*time.Second, 1*time.Second, + "Two clusters do not see each other: tunnels are not working.") +} + // TestAlpnSniProxyKube tests Kubernetes access with custom Kube API mock where traffic is forwarded via //SNI ALPN proxy service to Kubernetes service based on TLS SNI value. func TestALPNSNIProxyKube(t *testing.T) { diff --git a/lib/auth/authclient/authclient.go b/lib/auth/authclient/authclient.go index b09fadb107163..8186967e1b3db 100644 --- a/lib/auth/authclient/authclient.go +++ b/lib/auth/authclient/authclient.go @@ -55,6 +55,8 @@ func Connect(ctx context.Context, cfg *Config) (auth.ClientI, error) { Credentials: []apiclient.Credentials{ apiclient.LoadTLS(cfg.TLS), }, + // Deliberately ignore HTTP proxies for backwards compatibility. + IgnoreHTTPProxy: true, }) if err != nil { return nil, trace.Wrap(err, "failed direct dial to auth server: %v", err) diff --git a/lib/auth/clt.go b/lib/auth/clt.go index 019432e93b733..5b324af7f323f 100644 --- a/lib/auth/clt.go +++ b/lib/auth/clt.go @@ -129,7 +129,12 @@ func NewHTTPClient(cfg client.Config, tls *tls.Config, params ...roundtrip.Clien if len(cfg.Addrs) == 0 { return nil, trace.BadParameter("no addresses to dial") } - contextDialer := client.NewDialer(cfg.KeepAlivePeriod, cfg.DialTimeout) + var contextDialer client.ContextDialer + if cfg.IgnoreHTTPProxy { + contextDialer = client.NewDirectDialer(cfg.KeepAlivePeriod, cfg.DialTimeout) + } else { + contextDialer = client.NewDialer(cfg.KeepAlivePeriod, cfg.DialTimeout) + } dialer = client.ContextDialerFunc(func(ctx context.Context, network, _ string) (conn net.Conn, err error) { for _, addr := range cfg.Addrs { conn, err = contextDialer.DialContext(ctx, network, addr) diff --git a/lib/reversetunnel/resolver.go b/lib/reversetunnel/resolver.go index 528063a0acbc3..55fc139c4deb9 100644 --- a/lib/reversetunnel/resolver.go +++ b/lib/reversetunnel/resolver.go @@ -58,8 +58,9 @@ func WebClientResolver(ctx context.Context, addrs []utils.NetAddr, insecureTLS b for _, addr := range addrs { // In insecure mode, any certificate is accepted. In secure mode the hosts // CAs are used to validate the certificate on the proxy. + // Ignore HTTP proxy for backwards compatibility. tunnelAddr, err := webclient.GetTunnelAddr( - &webclient.Config{Context: ctx, ProxyAddr: addr.String(), Insecure: insecureTLS}) + &webclient.Config{Context: ctx, ProxyAddr: addr.String(), Insecure: insecureTLS, IgnoreHTTPProxy: true}) if err != nil { errs = append(errs, err) diff --git a/lib/service/connect.go b/lib/service/connect.go index 04599c1a31468..23719641963f5 100644 --- a/lib/service/connect.go +++ b/lib/service/connect.go @@ -964,6 +964,8 @@ func (process *TeleportProcess) newClientDirect(authServers []utils.NetAddr, tls apiclient.LoadTLS(tlsConfig), }, DialOpts: dialOpts, + // Deliberately ignore HTTP proxies for backwards compatibility. + IgnoreHTTPProxy: true, }, cltParams...) if err != nil { return nil, trace.Wrap(err)