Skip to content

Add HTTP proxy support for tunnel connections#1514

Open
shayonj wants to merge 1 commit intocloudflare:masterfrom
shayonj:s/proxy-fix
Open

Add HTTP proxy support for tunnel connections#1514
shayonj wants to merge 1 commit intocloudflare:masterfrom
shayonj:s/proxy-fix

Conversation

@shayonj
Copy link

@shayonj shayonj commented Aug 2, 2025

This PR adds support for HTTP and SOCKS proxy configurations to cloudflared tunnel connections via standard environment variables (HTTP_PROXY, HTTPS_PROXY, ALL_PROXY). This enables cloudflared to work in enterprise environments that require all outbound traffic to route through corporate proxy infrastructure.

Changes include:

  • Added proxyAwareDialer struct that implements both HTTP CONNECT and SOCKS proxy protocols
  • Modified rawTCPService and tcpOverWSService to use proxy.Dialer interface instead of net.Dialer
  • Added proxy detection using proxy.FromEnvironmentUsing() for SOCKS proxies and http.ProxyFromEnvironment() for HTTP proxies
  • There is now a createProxyDialer to make delegation easy
  • DNS dialer specifically uses direct connection to avoid circular dependencies when resolving proxy hostnames
  • Added tests

Proxy precedence order:

  • SOCKS proxy detection via ALL_PROXY environment variable
  • HTTP proxy detection via HTTP_PROXY/HTTPS_PROXY environment variables (supports both upper and lower case)
  1. Direct connection fallback when no proxy is configured

Authentication support:

  • Basic authentication via URL format (http://user:pass@proxy:8080)
  • Proxy-Authorization headers for HTTP CONNECT method
  • SOCKS4/SOCKS5 proxy support via golang.org/x/net/proxy

Usage Example:

export HTTP_PROXY="http://user:pass@proxy.corp.com:8080"
export HTTPS_PROXY="http://user:pass@proxy.corp.com:8080"

# Or SOCKS proxy
export ALL_PROXY="socks5://proxy.corp.com:1080"

./cloudflared tunnel run --credentials-file tunnel.json my-tunnel

Example:
image

#1076

@shayonj shayonj force-pushed the s/proxy-fix branch 2 times, most recently from 7995418 to 664ed3f Compare August 2, 2025 13:33
@yash-srivastava
Copy link

Hi, will this work for quick tunnel setup as well? ./cloudflared tunnel --loglevel debug --url http://localhost:8080. Because when I ran this I was not able to see the log lines which you configured in code.

Copy link
Contributor

@GoncaloGarcia GoncaloGarcia left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey! Thank you for the PR.

As discussed offline it seems like your interest is mostly the HTTP proxy? If so, we don't need to introduce support for SOCKS proxy in this PR.

Additionally, I wasn't able to verify the behavior by setting up a local instance of mitmproxy and setting the flags as your example suggests. Could you provide a script that sets up a basic example with a client + cloudflared running a tunnel + mitmproxy logging the requests?

return nil, fmt.Errorf("proxy connection failed: %w", err)
}

connectReq := fmt.Sprintf("CONNECT %s HTTP/1.1\r\nHost: %s\r\n\r\n", addr, addr)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you use the request struct instead and req.Write instead?

// Build CONNECT request using http.Request
    req := &http.Request{
        Method:     "CONNECT",
        URL:        &url.URL{Host: addr},
        Host:       addr,
        Proto:      "HTTP/1.1",
        ProtoMajor: 1,
        ProtoMinor: 1,
        Header:     make(http.Header),
    }

Also, is there a reason why you're hardcoding HTTP 1.1?

Comment on lines +142 to +150
httpProxy := getEnvProxy("HTTP_PROXY", "http_proxy")
httpsProxy := getEnvProxy("HTTPS_PROXY", "https_proxy")

if httpProxy == "" && httpsProxy == "" {
if logger != nil {
logger.Debug().Msg("proxy: no proxy configured, using direct connection")
}
return baseDialer
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you use golang.org/x/net/http/httpproxy instead of checking for these variables manually?

require.Error(t, err)
}

func TestProxyAwareDialer(t *testing.T) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add some tests that use the authentication variables in the proxy URL and another for NO_PROXY?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants