Skip to content

SSHKit opens two connections instead of one when using SSHKit::Backend::Netssh::KnownHosts #369

@mattbrictson

Description

@mattbrictson

This bug was first reported here, with the symptom of Capistrano prompting for the SSH password twice: capistrano/capistrano#1774

This got me thinking that perhaps the SSHKit connection pool is actually creating two connections instead of one. And in fact, this is indeed the case.

I added a puts statement to connection_pool.rb like this:

@@ -56,6 +56,7 @@ class SSHKit::Backend::ConnectionPool
   def with(connection_factory, *args)
     cache = find_cache(args)
     conn = cache.pop || begin
+      puts "*** Opening new SSH connection with args: #{args.to_s}"
       connection_factory.call(*args)
     end
     yield(conn)

And then ran cap production deploy:check. Sure enough, two connections were opened.

The interesting thing is that args.to_s generates two different results. Since args.to_s is used to generate a key for the connection pool, this is why the pool thinks it needs to create two separate connections.

to_s is changing because SSHKit::Backend::Netssh::KnownHosts has mutable state and is changing state in between the two SSH executions. You can see that in the second case, my ~/.ssh/known_hosts has now been loaded and cached by KnownHosts, which changes its to_s output.

*** Opening new SSH connection with args: ["xx.xx.xx.xx", "deployer", {:user=>"deployer", :forward_agent=>true, :known_hosts=>#<SSHKit::Backend::Netssh::KnownHosts:0x007f9b78b0abb0 @_mutex=#<Thread::Mutex:0x007f9b78b0ab10>, @files={}>, :compression=>true, :keepalive=>true}]
.
.
.
*** Opening new SSH connection with args: ["xx.xx.xx.xx", "deployer", {:user=>"deployer", :forward_agent=>true, :known_hosts=>#<SSHKit::Backend::Netssh::KnownHosts:0x007f9b78b0abb0 @_mutex=#<Thread::Mutex:0x007f9b78b0ab10>, @files={"~/.ssh/known_hosts"=>#<SSHKit::Backend::Netssh::KnownHostsKeys:0x007f9b7a979b28 @_mutex=#<Thread::Mutex:0x007f9b7a979a88>, @path="/Users/mbrictson/.ssh/known_hosts", @hosts_keys={REDACTED...

I think we need to either:

  • modify KnownHosts#to_s to be a stable value; or
  • change how the pool key is calculated so that it is not sensitive to KnownHosts#to_s

/cc @byroot

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions