From cb73e5824a5acc3b24b4d686c08f351cecc9c9ab Mon Sep 17 00:00:00 2001 From: Marek Smolinski Date: Mon, 15 Nov 2021 11:19:18 +0100 Subject: [PATCH] Fix tunnel address for TLS routing if public tunnel address is present --- api/client/webclient/webclient.go | 47 +++++++++++++++++++++----- api/client/webclient/webclient_test.go | 37 +++++++++++++++++++- api/defaults/defaults.go | 6 ++++ 3 files changed, 81 insertions(+), 9 deletions(-) diff --git a/api/client/webclient/webclient.go b/api/client/webclient/webclient.go index ae10a6deeeba0..806fd66b275ab 100644 --- a/api/client/webclient/webclient.go +++ b/api/client/webclient/webclient.go @@ -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 != "" { @@ -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. @@ -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 == "" { diff --git a/api/client/webclient/webclient_test.go b/api/client/webclient/webclient_test.go index fdd9c7fe85246..aa9931d4227df 100644 --- a/api/client/webclient/webclient_test.go +++ b/api/client/webclient/webclient_test.go @@ -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) { diff --git a/api/defaults/defaults.go b/api/defaults/defaults.go index cd882b7706eef..796df97b67cb3 100644 --- a/api/defaults/defaults.go +++ b/api/defaults/defaults.go @@ -102,6 +102,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 (