From 5fbbb7ee0066c02a0aca130d3609de18e04371cf Mon Sep 17 00:00:00 2001 From: Andrew Lytvynov Date: Thu, 14 May 2020 18:03:19 -0700 Subject: [PATCH] Add options to skip connecting to ssh-agent from tsh In particular, gpg-agent doesn't support SSH certificates, so writing to it is potentially disruptive for the user: https://dev.gnupg.org/T1756 User has two options: - pass `--no-use-local-ssh-agent` for individual calls to tsh - set `TELEPORT_USE_LOCAL_SSH_AGENT=false` to make it permanent Fixes #3169 --- lib/client/api.go | 13 +++++++++---- lib/client/keyagent.go | 9 +++++++-- lib/client/keyagent_test.go | 10 +++++----- tool/tsh/tsh.go | 36 ++++++++++++++++++++++-------------- 4 files changed, 43 insertions(+), 25 deletions(-) diff --git a/lib/client/api.go b/lib/client/api.go index 8f86a3598cf54..039fa3f7625b6 100644 --- a/lib/client/api.go +++ b/lib/client/api.go @@ -256,6 +256,10 @@ type Config struct { // Browser can be used to pass the name of a browser to override the system default // (not currently implemented), or set to 'none' to suppress browser opening entirely. Browser string + + // UseLocalSSHAgent will write user certificates to the local ssh-agent (or + // similar) socket at $SSH_AUTH_SOCK. + UseLocalSSHAgent bool } // CachePolicy defines cache policy for local clients @@ -269,9 +273,10 @@ type CachePolicy struct { // MakeDefaultConfig returns default client config func MakeDefaultConfig() *Config { return &Config{ - Stdout: os.Stdout, - Stderr: os.Stderr, - Stdin: os.Stdin, + Stdout: os.Stdout, + Stderr: os.Stderr, + Stdin: os.Stdin, + UseLocalSSHAgent: true, } } @@ -834,7 +839,7 @@ func NewClient(c *Config) (tc *TeleportClient, err error) { } else { // initialize the local agent (auth agent which uses local SSH keys signed by the CA): webProxyHost, _ := tc.WebProxyHostPort() - tc.localAgent, err = NewLocalAgent(c.KeysDir, webProxyHost, c.Username) + tc.localAgent, err = NewLocalAgent(c.KeysDir, webProxyHost, c.Username, c.UseLocalSSHAgent) if err != nil { return nil, trace.Wrap(err) } diff --git a/lib/client/keyagent.go b/lib/client/keyagent.go index b00bffb8460b1..cfb329a60cdad 100644 --- a/lib/client/keyagent.go +++ b/lib/client/keyagent.go @@ -68,7 +68,7 @@ type LocalKeyAgent struct { // NewLocalAgent reads all Teleport certificates from disk (using FSLocalKeyStore), // creates a LocalKeyAgent, loads all certificates into it, and returns the agent. -func NewLocalAgent(keyDir string, proxyHost string, username string) (a *LocalKeyAgent, err error) { +func NewLocalAgent(keyDir, proxyHost, username string, useLocalSSHAgent bool) (a *LocalKeyAgent, err error) { keystore, err := NewFSLocalKeyStore(keyDir) if err != nil { return nil, trace.Wrap(err) @@ -80,12 +80,17 @@ func NewLocalAgent(keyDir string, proxyHost string, username string) (a *LocalKe }), Agent: agent.NewKeyring(), keyStore: keystore, - sshAgent: connectToSSHAgent(), noHosts: make(map[string]bool), username: username, proxyHost: proxyHost, } + if useLocalSSHAgent { + a.sshAgent = connectToSSHAgent() + } else { + log.Debug("Skipping connection to the local ssh-agent.") + } + // unload all teleport keys from the agent first to ensure // we don't leave stale keys in the agent err = a.UnloadKeys() diff --git a/lib/client/keyagent_test.go b/lib/client/keyagent_test.go index e643dd6ee1c83..1c38e5b0f5779 100644 --- a/lib/client/keyagent_test.go +++ b/lib/client/keyagent_test.go @@ -100,7 +100,7 @@ func (s *KeyAgentTestSuite) SetUpTest(c *check.C) { // a teleport key with the teleport username. func (s *KeyAgentTestSuite) TestAddKey(c *check.C) { // make a new local agent - lka, err := NewLocalAgent(s.keyDir, s.hostname, s.username) + lka, err := NewLocalAgent(s.keyDir, s.hostname, s.username, true) c.Assert(err, check.IsNil) // add the key to the local agent, this should write the key @@ -159,7 +159,7 @@ func (s *KeyAgentTestSuite) TestLoadKey(c *check.C) { userdata := []byte("hello, world") // make a new local agent - lka, err := NewLocalAgent(s.keyDir, s.hostname, s.username) + lka, err := NewLocalAgent(s.keyDir, s.hostname, s.username, true) c.Assert(err, check.IsNil) // unload any keys that might be in the agent for this user @@ -217,7 +217,7 @@ func (s *KeyAgentTestSuite) TestLoadKey(c *check.C) { func (s *KeyAgentTestSuite) TestHostCertVerification(c *check.C) { // Make a new local agent. - lka, err := NewLocalAgent(s.keyDir, s.hostname, s.username) + lka, err := NewLocalAgent(s.keyDir, s.hostname, s.username, true) c.Assert(err, check.IsNil) // By default user has not refused any hosts. @@ -297,7 +297,7 @@ func (s *KeyAgentTestSuite) TestHostCertVerification(c *check.C) { func (s *KeyAgentTestSuite) TestHostKeyVerification(c *check.C) { // make a new local agent - lka, err := NewLocalAgent(s.keyDir, s.hostname, s.username) + lka, err := NewLocalAgent(s.keyDir, s.hostname, s.username, true) c.Assert(err, check.IsNil) // by default user has not refused any hosts: @@ -351,7 +351,7 @@ func (s *KeyAgentTestSuite) TestHostKeyVerification(c *check.C) { func (s *KeyAgentTestSuite) TestDefaultHostPromptFunc(c *check.C) { keygen := testauthority.New() - a, err := NewLocalAgent(s.keyDir, s.hostname, s.username) + a, err := NewLocalAgent(s.keyDir, s.hostname, s.username, true) c.Assert(err, check.IsNil) _, keyBytes, err := keygen.GenerateKeyPair("") diff --git a/tool/tsh/tsh.go b/tool/tsh/tsh.go index 9bc7a2e75d7cd..6a4eba5c05edd 100644 --- a/tool/tsh/tsh.go +++ b/tool/tsh/tsh.go @@ -77,8 +77,6 @@ type CLIConf struct { NodeLogin string // InsecureSkipVerify bypasses verification of HTTPS certificate when talking to web proxy InsecureSkipVerify bool - // IsUnderTest is set to true for unit testing - IsUnderTest bool // Remote SSH session to join SessionID string // Src:dest parameter for SCP @@ -107,10 +105,6 @@ type CLIConf struct { Namespace string // NoCache is used to turn off client cache for nodes discovery NoCache bool - // LoadSystemAgentOnly when set to true will cause tsh agent to load keys into the system agent and - // then exit. This is useful when calling tsh agent from a script (for example ~/.bash_profile) - // to load keys into your system agent. - LoadSystemAgentOnly bool // BenchThreads is amount of concurrent threads to run BenchThreads int // BenchDuration is a duration for the benchmark @@ -165,6 +159,10 @@ type CLIConf struct { // Browser can be used to pass the name of a browser to override the system default // (not currently implemented), or set to 'none' to suppress browser opening entirely. Browser string + + // UseLocalSSHAgent set to false will prevent this client from attempting to + // connect to the local ssh-agent (or similar) socket at $SSH_AUTH_SOCK. + UseLocalSSHAgent bool } func main() { @@ -181,21 +179,21 @@ func main() { default: cmdLine = cmdLineOrig } - Run(cmdLine, false) + Run(cmdLine) } const ( - clusterEnvVar = "TELEPORT_SITE" - clusterHelp = "Specify the cluster to connect" - bindAddrEnvVar = "TELEPORT_LOGIN_BIND_ADDR" - authEnvVar = "TELEPORT_AUTH" - browserHelp = "Set to 'none' to suppress browser opening on login" + clusterEnvVar = "TELEPORT_SITE" + clusterHelp = "Specify the cluster to connect" + bindAddrEnvVar = "TELEPORT_LOGIN_BIND_ADDR" + authEnvVar = "TELEPORT_AUTH" + browserHelp = "Set to 'none' to suppress browser opening on login" + useLocalSSHAgentEnvVar = "TELEPORT_USE_LOCAL_SSH_AGENT" ) // Run executes TSH client. same as main() but easier to test -func Run(args []string, underTest bool) { +func Run(args []string) { var cf CLIConf - cf.IsUnderTest = underTest utils.InitLogger(utils.LoggingForCLI, logrus.WarnLevel) // configure CLI argument parser: @@ -220,6 +218,10 @@ func Run(args []string, underTest bool) { app.Flag("gops-addr", "Specify gops addr to listen on").Hidden().StringVar(&cf.GopsAddr) app.Flag("skip-version-check", "Skip version checking between server and client.").BoolVar(&cf.SkipVersionCheck) app.Flag("debug", "Verbose logging to stdout").Short('d').BoolVar(&cf.Debug) + app.Flag("use-local-ssh-agent", "Load generated SSH certificates into the local ssh-agent (specified via $SSH_AUTH_SOCK). You can also set TELEPORT_USE_LOCAL_SSH_AGENT environment variable. Default is true."). + Envar(useLocalSSHAgentEnvVar). + Default("true"). + BoolVar(&cf.UseLocalSSHAgent) app.HelpFlag.Short('h') ver := app.Command("version", "Print the version") // ssh @@ -1086,6 +1088,12 @@ func makeClient(cf *CLIConf, useProfileLogin bool) (*client.TeleportClient, erro // (not currently implemented) or set to 'none' to suppress browser opening entirely. c.Browser = cf.Browser + // Do not write SSH certs into the local ssh-agent if user requested it. + // + // This is specifically for gpg-agent, which doesn't support SSH + // certificates (https://dev.gnupg.org/T1756) + c.UseLocalSSHAgent = cf.UseLocalSSHAgent + tc, err := client.NewClient(c) if err != nil { return nil, trace.Wrap(err)