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

Fix ping endpoint when proxy has multiple public addrs #7368

Merged
merged 3 commits into from
Jun 23, 2021
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
33 changes: 17 additions & 16 deletions lib/service/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -2579,22 +2579,23 @@ func (process *TeleportProcess) initProxyEndpoint(conn *Connector) error {

webHandler, err = web.NewHandler(
web.Config{
Proxy: tsrv,
AuthServers: cfg.AuthServers[0],
DomainName: cfg.Hostname,
ProxyClient: conn.Client,
ProxySSHAddr: proxySSHAddr,
ProxyWebAddr: cfg.Proxy.WebAddr,
ProxySettings: proxySettings,
CipherSuites: cfg.CipherSuites,
FIPS: cfg.FIPS,
AccessPoint: accessPoint,
Emitter: streamEmitter,
PluginRegistry: process.PluginRegistry,
HostUUID: process.Config.HostUUID,
Context: process.ExitContext(),
StaticFS: fs,
ClusterFeatures: process.getClusterFeatures(),
Proxy: tsrv,
AuthServers: cfg.AuthServers[0],
DomainName: cfg.Hostname,
ProxyClient: conn.Client,
ProxySSHAddr: proxySSHAddr,
ProxyWebAddr: cfg.Proxy.WebAddr,
ProxyPublicAddrs: cfg.Proxy.PublicAddrs,
ProxySettings: proxySettings,
CipherSuites: cfg.CipherSuites,
FIPS: cfg.FIPS,
AccessPoint: accessPoint,
Emitter: streamEmitter,
PluginRegistry: process.PluginRegistry,
HostUUID: process.Config.HostUUID,
Context: process.ExitContext(),
StaticFS: fs,
ClusterFeatures: process.getClusterFeatures(),
})
if err != nil {
return trace.Wrap(err)
Expand Down
8 changes: 3 additions & 5 deletions lib/web/apiserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,8 @@ type Config struct {
ProxySSHAddr utils.NetAddr
// ProxyWebAddr points to the web (HTTPS) address of the proxy
ProxyWebAddr utils.NetAddr
// ProxyPublicAddr contains web proxy public addresses.
ProxyPublicAddrs []utils.NetAddr

// CipherSuites is the list of cipher suites Teleport suppports.
CipherSuites []uint16
Expand Down Expand Up @@ -174,9 +176,6 @@ type RewritingHandler struct {

// appHandler is a http.Handler to forward requests to applications.
appHandler *app.Handler

// publicAddr is the public address the proxy is running at.
publicAddr string
}

// Check if this request should be forwarded to an application handler to
Expand All @@ -190,7 +189,7 @@ func (h *RewritingHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
h.appHandler.ServeHTTP(w, r)
return
}
if redir, ok := app.HasName(r, h.publicAddr); ok {
r0mant marked this conversation as resolved.
Show resolved Hide resolved
if redir, ok := app.HasName(r, h.handler.cfg.ProxyPublicAddrs); ok {
http.Redirect(w, r, redir, http.StatusFound)
return
}
Expand Down Expand Up @@ -478,7 +477,6 @@ func NewHandler(cfg Config, opts ...HandlerOption) (*RewritingHandler, error) {
),
handler: h,
appHandler: appHandler,
publicAddr: cfg.ProxySettings.SSH.PublicAddr,
}, nil
}

Expand Down
37 changes: 27 additions & 10 deletions lib/web/apiserver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2683,16 +2683,17 @@ func createProxy(t *testing.T, proxyID string, node *regular.Server, authServer
fs, err := NewDebugFileSystem("../../webassets/teleport")
require.NoError(t, err)
handler, err := NewHandler(Config{
Proxy: revTunServer,
AuthServers: utils.FromAddr(authServer.Addr()),
DomainName: authServer.ClusterName(),
ProxyClient: client,
CipherSuites: utils.DefaultCipherSuites(),
AccessPoint: client,
Context: context.Background(),
HostUUID: proxyID,
Emitter: client,
StaticFS: fs,
Proxy: revTunServer,
AuthServers: utils.FromAddr(authServer.Addr()),
DomainName: authServer.ClusterName(),
ProxyClient: client,
ProxyPublicAddrs: utils.MustParseAddrList("proxy-1.example.com", "proxy-2.example.com"),
CipherSuites: utils.DefaultCipherSuites(),
AccessPoint: client,
Context: context.Background(),
HostUUID: proxyID,
Emitter: client,
StaticFS: fs,
}, SetSessionStreamPollPeriod(200*time.Millisecond), SetClock(clock))
require.NoError(t, err)

Expand Down Expand Up @@ -2944,3 +2945,19 @@ func validateTerminalStream(t *testing.T, conn *websocket.Conn) {
err = waitForOutput(stream, "foo")
require.NoError(t, err)
}

// TestProxyMultiAddr makes sure ping endpoint can be called over any of
// the proxy's configured public addresses.
func TestProxyMultiAddr(t *testing.T) {
env := newWebPack(t, 1)
proxy := env.proxies[0]
req, err := http.NewRequest(http.MethodGet, proxy.newClient(t).Endpoint("webapi", "ping"), nil)
require.NoError(t, err)
// Make sure ping endpoint can be reached over all proxy public addrs.
for _, proxyAddr := range proxy.handler.handler.cfg.ProxyPublicAddrs {
req.Host = proxyAddr.Host()
resp, err := client.NewInsecureWebClient().Do(req)
require.NoError(t, err)
require.NoError(t, resp.Body.Close())
}
}
37 changes: 18 additions & 19 deletions lib/web/app/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -254,36 +254,35 @@ func HasClientCert(r *http.Request) bool {
// HasName checks if the client is attempting to connect to a
// host that is different than the public address of the proxy. If it is, it
// redirects back to the application launcher in the Web UI.
func HasName(r *http.Request, proxyPublicAddr string) (string, bool) {
func HasName(r *http.Request, proxyPublicAddrs []utils.NetAddr) (string, bool) {
raddr, err := utils.ParseAddr(r.Host)
if err != nil {
return "", false
}
paddr, err := utils.ParseAddr(proxyPublicAddr)
if err != nil {
return "", false
}

// The following requests can not be for an application:
//
// * The request is for localhost or loopback.
// * The request is for an IP address.
// * The request is for the public address of the proxy.
if utils.IsLocalhost(raddr.Host()) {
return "", false
}
if net.ParseIP(raddr.Host()) != nil {
return "", false
for _, paddr := range proxyPublicAddrs {
// The following requests can not be for an application:
//
// * The request is for localhost or loopback.
// * The request is for an IP address.
// * The request is for the public address of the proxy.
if utils.IsLocalhost(raddr.Host()) {
return "", false
}
if net.ParseIP(raddr.Host()) != nil {
return "", false
}
if raddr.Host() == paddr.Host() {
return "", false
}
}
if raddr.Host() == paddr.Host() {
if len(proxyPublicAddrs) == 0 {
return "", false
}

// At this point, it is assumed the caller is requesting an application and
// not the proxy, redirect the caller to the application launcher.
u := url.URL{
Scheme: "https",
Host: proxyPublicAddr,
Host: proxyPublicAddrs[0].String(),
Path: fmt.Sprintf("/web/launch/%v", raddr.Host()),
}
return u.String(), true
Expand Down
24 changes: 20 additions & 4 deletions lib/web/apps.go
Original file line number Diff line number Diff line change
Expand Up @@ -292,15 +292,31 @@ func (h *Handler) resolveDirect(ctx context.Context, proxy reversetunnel.Tunnel,
// resolveFQDN makes a best effort attempt to resolve FQDN to an application
// running within a root or leaf cluster.
func (h *Handler) resolveFQDN(ctx context.Context, clt app.Getter, proxy reversetunnel.Tunnel, fqdn string) (*types.App, types.Server, string, error) {
return app.ResolveFQDN(ctx, clt, proxy, []string{h.proxyDNSName()}, fqdn)
return app.ResolveFQDN(ctx, clt, proxy, h.proxyDNSNames(), fqdn)
}

// proxyDNSName is a DNS name the HTTP proxy is available at, where
// the local cluster name is used as a best-effort fallback.
func (h *Handler) proxyDNSName() string {
dnsName, err := utils.DNSName(h.cfg.ProxySettings.SSH.PublicAddr)
if err != nil {
dnsNames := h.proxyDNSNames()
if len(dnsNames) == 0 {
return h.auth.clusterName
}
return dnsName
return dnsNames[0]
}

// proxyDNSNames returns DNS names the HTTP proxy is available at, the local
// cluster name is used as a best-effort fallback.
func (h *Handler) proxyDNSNames() (dnsNames []string) {
for _, addr := range h.cfg.ProxyPublicAddrs {
dnsName, err := utils.DNSName(addr.String())
if err != nil {
continue
}
dnsNames = append(dnsNames, dnsName)
}
if len(dnsNames) == 0 {
return []string{h.auth.clusterName}
}
return dnsNames
}