From 0695125331bbdb96a4d3e04c8e0a6100e49d8b51 Mon Sep 17 00:00:00 2001 From: Gavin Frazar Date: Mon, 30 Jan 2023 15:29:47 -0800 Subject: [PATCH] fix tsh db connect with hardware backed key (#20806) * Check key policy in tsh db connect flow * Load key policy from identity file --- lib/client/api_test.go | 2 +- tool/tsh/db.go | 19 ++++++++++++++----- tool/tsh/proxy.go | 8 ++++++-- tool/tsh/tsh.go | 3 +++ tool/tsh/tsh_test.go | 2 ++ 5 files changed, 26 insertions(+), 8 deletions(-) diff --git a/lib/client/api_test.go b/lib/client/api_test.go index 6cc8ac784d12f..aa567ac4fe4cf 100644 --- a/lib/client/api_test.go +++ b/lib/client/api_test.go @@ -188,7 +188,7 @@ func TestNew(t *testing.T) { Host: "localhost", HostLogin: "vincent", HostPort: 22, - KeysDir: "/tmp", + KeysDir: t.TempDir(), Username: "localuser", SiteName: "site", Tracer: tracing.NoopProvider().Tracer("test"), diff --git a/tool/tsh/db.go b/tool/tsh/db.go index 09e996851d080..a8af9d823c0dc 100644 --- a/tool/tsh/db.go +++ b/tool/tsh/db.go @@ -36,6 +36,7 @@ import ( "github.com/gravitational/teleport" "github.com/gravitational/teleport/api/client/proto" "github.com/gravitational/teleport/api/types" + "github.com/gravitational/teleport/api/utils/keys" "github.com/gravitational/teleport/lib/client" dbprofile "github.com/gravitational/teleport/lib/client/db" "github.com/gravitational/teleport/lib/client/db/dbcmd" @@ -461,7 +462,7 @@ func onDatabaseConfig(cf *CLIConf) error { // "tsh db config" prints out instructions for native clients to connect to // the remote proxy directly. Return errors here when direct connection // does NOT work (e.g. when ALPN local proxy is required). - if isLocalProxyAlwaysRequired(database.Protocol) { + if isLocalProxyAlwaysRequired(tc, database.Protocol) { return trace.BadParameter(formatDbCmdUnsupportedDBProtocol(cf, database)) } // MySQL requires ALPN local proxy in single port mode. @@ -550,11 +551,15 @@ func maybeStartLocalProxy(ctx context.Context, cf *CLIConf, tc *client.TeleportC // Some protocols (Snowflake) only work in the local tunnel mode. // ElasticSearch can work without the --tunnel flag, but not via `tsh db connect`. localProxyTunnel := cf.LocalProxyTunnel - if requiresLocalProxyTunnel(db.Protocol) || db.Protocol == defaults.ProtocolElasticsearch { + if requiresLocalProxyTunnel(tc, db.Protocol) || db.Protocol == defaults.ProtocolElasticsearch { localProxyTunnel = true } - log.Debugf("Starting local proxy") + if localProxyTunnel { + log.Debug("Starting local proxy tunnel") + } else { + log.Debug("Starting local proxy") + } listener, err := net.Listen("tcp", "localhost:0") if err != nil { @@ -1051,12 +1056,16 @@ func formatDatabaseConfigCommand(clusterFlag string, db tlsca.RouteToDatabase) s // shouldUseLocalProxyForDatabase returns true if the ALPN local proxy should // be used for connecting to the provided database. func shouldUseLocalProxyForDatabase(tc *client.TeleportClient, db *tlsca.RouteToDatabase) bool { - return tc.TLSRoutingEnabled || isLocalProxyAlwaysRequired(db.Protocol) + return tc.TLSRoutingEnabled || isLocalProxyAlwaysRequired(tc, db.Protocol) } // isLocalProxyAlwaysRequired returns true for protocols that always requires // an ALPN local proxy. -func isLocalProxyAlwaysRequired(protocol string) bool { +func isLocalProxyAlwaysRequired(tc *client.TeleportClient, protocol string) bool { + switch tc.PrivateKeyPolicy { + case keys.PrivateKeyPolicyHardwareKey, keys.PrivateKeyPolicyHardwareKeyTouch: + return true + } switch protocol { case defaults.ProtocolSQLServer, defaults.ProtocolSnowflake, diff --git a/tool/tsh/proxy.go b/tool/tsh/proxy.go index 68c6b3eccb542..7f6ee9651fde8 100644 --- a/tool/tsh/proxy.go +++ b/tool/tsh/proxy.go @@ -381,7 +381,7 @@ func onProxyCommandDB(cf *CLIConf) error { } // Some protocols require the --tunnel flag, e.g. Snowflake. - if !cf.LocalProxyTunnel && requiresLocalProxyTunnel(routeToDatabase.Protocol) { + if !cf.LocalProxyTunnel && requiresLocalProxyTunnel(client, routeToDatabase.Protocol) { return trace.BadParameter(formatDbCmdUnsupportedWithCondition(cf, routeToDatabase, "without the --tunnel flag")) } @@ -831,7 +831,11 @@ func envVarCommand(format, key, value string) (string, error) { } // requiresLocalProxyTunnel returns whether the given protocol requires a local proxy with the --tunnel flag. -func requiresLocalProxyTunnel(protocol string) bool { +func requiresLocalProxyTunnel(tc *libclient.TeleportClient, protocol string) bool { + switch tc.PrivateKeyPolicy { + case keys.PrivateKeyPolicyHardwareKey, keys.PrivateKeyPolicyHardwareKeyTouch: + return true + } switch protocol { case defaults.ProtocolSnowflake: return true diff --git a/tool/tsh/tsh.go b/tool/tsh/tsh.go index 6c2f4cfce97cc..66823ae026d81 100644 --- a/tool/tsh/tsh.go +++ b/tool/tsh/tsh.go @@ -3116,6 +3116,9 @@ func makeClientForProxy(cf *CLIConf, proxy string, useProfileLogin bool) (*clien if err != nil { return nil, trace.Wrap(err) } + if c.PrivateKeyPolicy == "" { + c.PrivateKeyPolicy = keys.GetPrivateKeyPolicy(key.PrivateKey) + } rootCluster, err := key.RootClusterName() if err != nil { diff --git a/tool/tsh/tsh_test.go b/tool/tsh/tsh_test.go index 36bd9d8e4fac5..962f42915a864 100644 --- a/tool/tsh/tsh_test.go +++ b/tool/tsh/tsh_test.go @@ -851,6 +851,8 @@ func TestMakeClient(t *testing.T) { agentKeys, err := tc.LocalAgent().ExtendedAgent.List() require.NoError(t, err) require.Greater(t, len(agentKeys), 0) + require.Equal(t, keys.PrivateKeyPolicyNone, tc.PrivateKeyPolicy, + "private key policy should be configured from the identity file") } // accessApprover allows watching and updating access requests