Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(v8) Fix tunnel address for TLS routing if public tunnel address is present #8995

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 39 additions & 8 deletions api/client/webclient/webclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -328,12 +328,16 @@ type GithubSettings struct {
}

// The tunnel addr is retrieved in the following preference order:
// 1. Reverse Tunnel Public Address.
// 2. If proxy support ALPN listener where all services are exposed on single port return proxy address.
// 1. If proxy support ALPN listener where all services are exposed on single port return ProxyPublicAddr/ProxyAddr.
// 2. Reverse Tunnel Public Address.
// 3. SSH Proxy Public Address Host + Tunnel Port.
// 4. HTTP Proxy Public Address Host + Tunnel Port.
// 5. Proxy Address Host + Tunnel Port.
func tunnelAddr(proxyAddr string, settings ProxySettings) (string, error) {
if settings.TLSRoutingEnabled {
return tunnelAddrForTLSRouting(proxyAddr, settings)
}

// If a tunnel public address is set, nothing else has to be done, return it.
sshSettings := settings.SSH
if sshSettings.TunnelPublicAddr != "" {
Expand All @@ -348,12 +352,6 @@ func tunnelAddr(proxyAddr string, settings ProxySettings) (string, error) {
}
}

if settings.TLSRoutingEnabled && proxyAddr != "" {
if port, err := extractPort(proxyAddr); err == nil {
tunnelPort = port
}
}

// If a tunnel public address has not been set, but a related HTTP or SSH
// public address has been set, extract the hostname but use the port from
// the tunnel listen address.
Expand All @@ -376,6 +374,39 @@ func tunnelAddr(proxyAddr string, settings ProxySettings) (string, error) {
return net.JoinHostPort(host, tunnelPort), nil
}

// tunnelAddrForTLSRouting returns reverse tunnel proxy address for proxy supporting TLS Routing.
func tunnelAddrForTLSRouting(proxyAddr string, settings ProxySettings) (string, error) {
if settings.SSH.PublicAddr != "" {
// Check if PublicAddr contains a port number.
if _, err := extractPort(settings.SSH.PublicAddr); err == nil {
return extractHostPort(settings.SSH.PublicAddr)
}
// Get port number from proxyAddr or use default one.
port := strconv.Itoa(defaults.ProxyWebListenPort)
if webPort, err := extractPort(proxyAddr); err == nil {
port = webPort
}

if host, err := extractHost(settings.SSH.PublicAddr); err == nil {
return net.JoinHostPort(host, port), nil
}
}

// Got proxyAddr with a port number for instance: proxy.example.com:3080
if _, err := extractPort(proxyAddr); err == nil {
return proxyAddr, nil
}
host, err := extractHost(proxyAddr)
if err != nil {
return "", trace.Wrap(err, "failed to parse the given proxy address")
}

// Got proxy address without a port like: proxy.example.com
// If proxyAddr doesn't contain any port it means that HTTPS port should be used because during Find call
// The destination URL is constructed by the fmt.Sprintf("https://%s/webapi/find", proxyAddr) function.
return net.JoinHostPort(host, strconv.Itoa(defaults.StandardHTTPSPort)), nil
}

// extractHostPort takes addresses like "tcp://host:port/path" and returns "host:port".
func extractHostPort(addr string) (string, error) {
if addr == "" {
Expand Down
37 changes: 36 additions & 1 deletion api/client/webclient/webclient_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,17 +177,52 @@ func TestTunnelAddr(t *testing.T) {
settings: ProxySettings{SSH: SSHProxySettings{}},
expectedTunnelAddr: "proxy.example.com:3024",
}))
t.Run("should use PublicAddr and WebAddrPort if TLSRoutingEnabled was enabled", testTunnelAddr(testCase{
t.Run("should use PublicAddr with ProxyWebPort if TLSRoutingEnabled was enabled", testTunnelAddr(testCase{
proxyAddr: "proxy.example.com:443",
settings: ProxySettings{
SSH: SSHProxySettings{
PublicAddr: "public.example.com",
TunnelListenAddr: "[::]:5024",
TunnelPublicAddr: "tpa.example.com:3032",
},
TLSRoutingEnabled: true,
},
expectedTunnelAddr: "public.example.com:443",
}))
t.Run("should use PublicAddr with custom port if TLSRoutingEnabled was enabled", testTunnelAddr(testCase{
proxyAddr: "proxy.example.com:443",
settings: ProxySettings{
SSH: SSHProxySettings{
PublicAddr: "public.example.com:443",
TunnelListenAddr: "[::]:5024",
TunnelPublicAddr: "tpa.example.com:3032",
},
TLSRoutingEnabled: true,
},
expectedTunnelAddr: "public.example.com:443",
}))
t.Run("should use proxyAddr with custom ProxyWebPort if TLSRoutingEnabled was enabled", testTunnelAddr(testCase{
proxyAddr: "proxy.example.com:443",
settings: ProxySettings{
SSH: SSHProxySettings{
TunnelListenAddr: "[::]:5024",
TunnelPublicAddr: "tpa.example.com:3032",
},
TLSRoutingEnabled: true,
},
expectedTunnelAddr: "proxy.example.com:443",
}))
t.Run("should use proxyAddr with default https port if TLSRoutingEnabled was enabled", testTunnelAddr(testCase{
proxyAddr: "proxy.example.com",
settings: ProxySettings{
SSH: SSHProxySettings{
TunnelListenAddr: "[::]:5024",
TunnelPublicAddr: "tpa.example.com:3032",
},
TLSRoutingEnabled: true,
},
expectedTunnelAddr: "proxy.example.com:443",
}))
}

func TestExtract(t *testing.T) {
Expand Down
6 changes: 6 additions & 0 deletions api/defaults/defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,12 @@ const (
// connections from SSH nodes who wish to use "reverse tunnell" (when they
// run behind an environment/firewall which only allows outgoing connections)
SSHProxyTunnelListenPort = 3024

// ProxyWebListenPort is the default Teleport Proxy WebPort address.
ProxyWebListenPort = 3080

// StandardHTTPSPort is the default port used for the https URI scheme.
StandardHTTPSPort = 443
)

const (
Expand Down