Skip to content

Commit

Permalink
try to use ssh agent if no password or key files have been specified
Browse files Browse the repository at this point in the history
Signed-off-by: Alex Pop <apop@chef.io>
  • Loading branch information
alexpop committed Nov 23, 2016
1 parent 0404986 commit 7c990bc
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 10 deletions.
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ end

group :tools do
gem 'pry', '~> 0.10'
gem 'rb-readline'
gem 'license_finder'
gem 'github_changelog_generator', '~> 1'
end
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,13 @@ train = Train.create('ssh',
host: '1.2.3.4', port: 22, user: 'root', key_files: '/vagrant')
```

If you don't specify the `key_files` and `password` options, SSH agent authentication will be attempted. For example:

```ruby
require 'train'
train = Train.create('ssh', host: '1.2.3.4', port: 22, user: 'root')
```

**WinRM**

```ruby
Expand Down
30 changes: 23 additions & 7 deletions lib/train/transports/ssh.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class SSHPTYFailed < Train::TransportError; end
# files.
#
# @author Fletcher Nichol <fnichol@nichol.ca>
class SSH < Train.plugin(1)
class SSH < Train.plugin(1) # rubocop:disable Metrics/ClassLength
name 'ssh'

autoload :Connection, 'train/transports/ssh_connection'
Expand Down Expand Up @@ -82,12 +82,6 @@ def validate_options(options)
super(options)

key_files = Array(options[:key_files])
if key_files.empty? and options[:password].nil?
fail Train::ClientError,
'You must configure at least one authentication method for SSH:'\
' Password or key.'
end

options[:auth_methods] ||= ['none']

unless key_files.empty?
Expand All @@ -100,6 +94,17 @@ def validate_options(options)
options[:auth_methods].push('password', 'keyboard-interactive')
end

if options[:auth_methods] == ['none']
if ssh_known_identities.empty?
fail Train::ClientError,
'You must configure at least one authentication method for SSH:'\
' Agent, Key or Password.'
else
logger.debug('[SSH] Using Agent keys as no password or key file have been specified')
options[:auth_methods].push('publickey')
end
end

if options[:pty]
logger.warn('[SSH] PTY requested: stderr will be merged into stdout')
end
Expand All @@ -108,6 +113,17 @@ def validate_options(options)
self
end

# Creates an SSH Authentication KeyManager instance and saves it for
# potential future reuse.
#
# @return [Hash] hash of SSH Known Identities
# @api private
def ssh_known_identities
# Force KeyManager to load the key(s)
@manager ||= Net::SSH::Authentication::KeyManager.new(nil).each_identity {}
@manager.known_identities
end

# Builds the hash of options needed by the Connection object on
# construction.
#
Expand Down
26 changes: 23 additions & 3 deletions test/unit/transports/ssh_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ def detect_family
password: rand.to_s,
key_files: rand.to_s,
}}
let(:cls_agent) { cls.new({ host: rand.to_s }) }

describe 'default options' do
let(:ssh) { cls.new({ host: 'dummy' }) }
Expand Down Expand Up @@ -84,6 +85,26 @@ def detect_family
"root@#{conf[:host]}",
])
end

it 'sets the right auth_methods when password is specified' do
conf[:key_files] = nil
cls.new(conf).connection.method(:options).call[:auth_methods].must_equal ["none", "password", "keyboard-interactive"]
end

it 'sets the right auth_methods when keys are specified' do
conf[:password] = nil
cls.new(conf).connection.method(:options).call[:auth_methods].must_equal ["none", "publickey"]
end

it 'sets the right auth_methods for agent auth' do
cls_agent.stubs(:ssh_known_identities).returns({:some => 'rsa_key'})
cls_agent.connection.method(:options).call[:auth_methods].must_equal ['none', 'publickey']
end

it 'works with ssh agent auth' do
cls_agent.stubs(:ssh_known_identities).returns({:some => 'rsa_key'})
cls_agent.connection
end
end

describe 'converting connection to string for logging' do
Expand Down Expand Up @@ -111,9 +132,8 @@ def detect_family
end

it 'does not like key and password == nil' do
conf.delete(:password)
conf.delete(:key_files)
proc { cls.new(conf).connection }.must_raise Train::ClientError
cls_agent.stubs(:ssh_known_identities).returns({})
proc { cls_agent.connection }.must_raise Train::ClientError
end

it 'wont connect if it is not possible' do
Expand Down

0 comments on commit 7c990bc

Please sign in to comment.