Skip to content

Commit

Permalink
Fix JumpHost TLSRouting flow when root cluster is offline (#13791)
Browse files Browse the repository at this point in the history
  • Loading branch information
smallinsky authored Jun 28, 2022
1 parent 2f1511b commit 20b63e0
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 23 deletions.
8 changes: 4 additions & 4 deletions integration/proxy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,15 @@ import (
"time"

"github.com/google/uuid"
"github.com/gravitational/teleport/api/breaker"
"github.com/gravitational/teleport/integration/helpers"
"github.com/gravitational/trace"
"github.com/stretchr/testify/require"
"go.mongodb.org/mongo-driver/bson"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

"github.com/gravitational/teleport/api/breaker"
"github.com/gravitational/teleport/api/client"
"github.com/gravitational/teleport/api/types"
"github.com/gravitational/teleport/integration/helpers"
"github.com/gravitational/teleport/lib"
"github.com/gravitational/teleport/lib/auth/testauthority"
"github.com/gravitational/teleport/lib/defaults"
Expand Down Expand Up @@ -601,7 +601,7 @@ func TestALPNProxyRootLeafAuthDial(t *testing.T) {
require.NoError(t, err)

// Dial root auth service.
rootAuthClient, err := proxyClient.ConnectToAuthServiceThroughALPNSNIProxy(ctx, "root.example.com")
rootAuthClient, err := proxyClient.ConnectToAuthServiceThroughALPNSNIProxy(ctx, "root.example.com", "")
require.NoError(t, err)
pr, err := rootAuthClient.Ping(ctx)
require.NoError(t, err)
Expand All @@ -610,7 +610,7 @@ func TestALPNProxyRootLeafAuthDial(t *testing.T) {
require.NoError(t, err)

// Dial leaf auth service.
leafAuthClient, err := proxyClient.ConnectToAuthServiceThroughALPNSNIProxy(ctx, "leaf.example.com")
leafAuthClient, err := proxyClient.ConnectToAuthServiceThroughALPNSNIProxy(ctx, "leaf.example.com", "")
require.NoError(t, err)
pr, err = leafAuthClient.Ping(ctx)
require.NoError(t, err)
Expand Down
54 changes: 47 additions & 7 deletions lib/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import (
"github.com/gravitational/teleport/api/breaker"
"github.com/gravitational/teleport/api/client"
"github.com/gravitational/teleport/api/client/proto"
"github.com/gravitational/teleport/api/client/webclient"
apidefaults "github.com/gravitational/teleport/api/defaults"
"github.com/gravitational/teleport/api/observability/tracing"
tracessh "github.com/gravitational/teleport/api/observability/tracing/ssh"
Expand Down Expand Up @@ -424,7 +425,19 @@ func (proxy *ProxyClient) IssueUserCertsWithMFA(ctx context.Context, params Reis
}
if params.RouteToCluster != rootClusterName {
clt.Close()
clt, err = proxy.ConnectToCluster(ctx, rootClusterName)
rootClusterProxy := proxy
if jumpHost := proxy.teleportClient.JumpHosts; jumpHost != nil {
// In case of MFA connect to root teleport proxy instead of JumpHost to request
// MFA certificates.
proxy.teleportClient.JumpHosts = nil
rootClusterProxy, err = proxy.teleportClient.ConnectToProxy(ctx)
proxy.teleportClient.JumpHosts = jumpHost
if err != nil {
return nil, trace.Wrap(err)
}
defer rootClusterProxy.Close()
}
clt, err = rootClusterProxy.ConnectToCluster(ctx, rootClusterName)
if err != nil {
return nil, trace.Wrap(err)
}
Expand Down Expand Up @@ -1081,15 +1094,20 @@ func (proxy *ProxyClient) loadTLS(clusterName string) (*tls.Config, error) {
// ConnectToAuthServiceThroughALPNSNIProxy uses ALPN proxy service to connect to remote/local auth
// service and returns auth client. For routing purposes, TLS ServerName is set to destination auth service
// cluster name with ALPN values set to teleport-auth protocol.
func (proxy *ProxyClient) ConnectToAuthServiceThroughALPNSNIProxy(ctx context.Context, clusterName string) (auth.ClientI, error) {
func (proxy *ProxyClient) ConnectToAuthServiceThroughALPNSNIProxy(ctx context.Context, clusterName, proxyAddr string) (auth.ClientI, error) {
tlsConfig, err := proxy.loadTLS(clusterName)
if err != nil {
return nil, trace.Wrap(err)
}

if proxyAddr == "" {
proxyAddr = proxy.teleportClient.WebProxyAddr
}

tlsConfig.InsecureSkipVerify = proxy.teleportClient.InsecureSkipVerify
clt, err := auth.NewClient(client.Config{
Context: ctx,
Addrs: []string{proxy.teleportClient.WebProxyAddr},
Addrs: []string{proxyAddr},
Credentials: []client.Credentials{
client.LoadTLS(tlsConfig),
},
Expand All @@ -1102,6 +1120,28 @@ func (proxy *ProxyClient) ConnectToAuthServiceThroughALPNSNIProxy(ctx context.Co
return clt, nil
}

func (proxy *ProxyClient) shouldDialWithTLSRouting(ctx context.Context) (string, bool) {
if len(proxy.teleportClient.JumpHosts) > 0 {
// Check if the provided JumpHost address is a Teleport Proxy.
// This is needed to distinguish if the JumpHost address from Teleport Proxy Web address
// or Teleport Proxy SSH address.
jumpHostAddr := proxy.teleportClient.JumpHosts[0].Addr.String()
resp, err := webclient.Find(
&webclient.Config{
Context: ctx,
ProxyAddr: jumpHostAddr,
Insecure: proxy.teleportClient.InsecureSkipVerify,
},
)
if err != nil {
// HTTP ping call failed. The JumpHost address is not a Teleport proxy address
return "", false
}
return jumpHostAddr, resp.Proxy.TLSRoutingEnabled
}
return proxy.teleportClient.WebProxyAddr, proxy.teleportClient.TLSRoutingEnabled
}

// ConnectToCluster connects to the auth server of the given cluster via proxy.
// It returns connected and authenticated auth server client
func (proxy *ProxyClient) ConnectToCluster(ctx context.Context, clusterName string) (auth.ClientI, error) {
Expand All @@ -1115,10 +1155,10 @@ func (proxy *ProxyClient) ConnectToCluster(ctx context.Context, clusterName stri
)
defer span.End()

// If proxy supports multiplex listener mode dial root/leaf cluster auth service via ALPN Proxy
// directly without using SSH tunnels.
if proxy.teleportClient.TLSRoutingEnabled {
clt, err := proxy.ConnectToAuthServiceThroughALPNSNIProxy(ctx, clusterName)
if proxyAddr, ok := proxy.shouldDialWithTLSRouting(ctx); ok {
// If proxy supports multiplex listener mode dial root/leaf cluster auth service via ALPN Proxy
// directly without using SSH tunnels.
clt, err := proxy.ConnectToAuthServiceThroughALPNSNIProxy(ctx, clusterName, proxyAddr)
if err != nil {
return nil, trace.Wrap(err)
}
Expand Down
45 changes: 33 additions & 12 deletions tool/tsh/proxy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,11 @@ import (
"testing"
"time"

"github.com/gravitational/teleport"
"github.com/gravitational/trace"
"github.com/stretchr/testify/require"
"golang.org/x/crypto/ssh/agent"

"github.com/gravitational/teleport"
apidefaults "github.com/gravitational/teleport/api/defaults"
"github.com/gravitational/teleport/api/types"
apievents "github.com/gravitational/teleport/api/types/events"
Expand Down Expand Up @@ -210,18 +210,39 @@ func testJumpHostSSHAccess(t *testing.T, s *suite) {
})
require.NoError(t, err)

// Connect to leaf node though jump host set to proxy web port where TLS Routing is enabled.
err = Run(context.Background(), []string{
"ssh",
"--insecure",
"-J", s.leaf.Config.Proxy.WebAddr.Addr,
s.leaf.Config.Hostname,
"echo", "hello",
}, func(cf *CLIConf) error {
cf.mockSSOLogin = mockSSOLogin(t, s.root.GetAuthServer(), s.user)
return nil
t.Run("root cluster online", func(t *testing.T) {
// Connect to leaf node though jump host set to proxy web port where TLS Routing is enabled.
err = Run(context.Background(), []string{
"ssh",
"--insecure",
"-J", s.leaf.Config.Proxy.WebAddr.Addr,
s.leaf.Config.Hostname,
"echo", "hello",
}, func(cf *CLIConf) error {
cf.mockSSOLogin = mockSSOLogin(t, s.root.GetAuthServer(), s.user)
return nil
})
require.NoError(t, err)
})

t.Run("root cluster offline", func(t *testing.T) {
// Terminate root cluster.
err = s.root.Close()
require.NoError(t, err)

// Check JumpHost flow when root cluster is offline.
err = Run(context.Background(), []string{
"ssh",
"--insecure",
"-J", s.leaf.Config.Proxy.WebAddr.Addr,
s.leaf.Config.Hostname,
"echo", "hello",
}, func(cf *CLIConf) error {
cf.mockSSOLogin = mockSSOLogin(t, s.root.GetAuthServer(), s.user)
return nil
})
require.NoError(t, err)
})
require.NoError(t, err)
}

// TestProxySSHDial verifies "tsh proxy ssh" command.
Expand Down

0 comments on commit 20b63e0

Please sign in to comment.