net-ssh 2.7.0 → 2.8.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES.txt +17 -0
- data/THANKS.txt +25 -0
- data/lib/net/ssh.rb +6 -4
- data/lib/net/ssh/authentication/agent/socket.rb +3 -3
- data/lib/net/ssh/authentication/key_manager.rb +7 -7
- data/lib/net/ssh/authentication/pageant.rb +35 -51
- data/lib/net/ssh/authentication/session.rb +2 -1
- data/lib/net/ssh/buffer.rb +2 -2
- data/lib/net/ssh/config.rb +33 -8
- data/lib/net/ssh/key_factory.rb +7 -1
- data/lib/net/ssh/proxy/command.rb +8 -1
- data/lib/net/ssh/proxy/http.rb +1 -1
- data/lib/net/ssh/proxy/socks4.rb +1 -1
- data/lib/net/ssh/proxy/socks5.rb +2 -2
- data/lib/net/ssh/service/forward.rb +42 -7
- data/lib/net/ssh/transport/session.rb +7 -1
- data/lib/net/ssh/version.rb +1 -1
- data/net-ssh.gemspec +7 -3
- data/test/authentication/test_agent.rb +6 -6
- data/test/authentication/test_key_manager.rb +7 -3
- data/test/authentication/test_session.rb +2 -3
- data/test/configs/auth_off +4 -0
- data/test/configs/auth_on +4 -0
- data/test/configs/empty +0 -0
- data/test/manual/test_forward.rb +27 -14
- data/test/start/test_connection.rb +53 -0
- data/test/test_config.rb +37 -1
- data/test/test_key_factory.rb +20 -2
- metadata +7 -3
data/CHANGES.txt
CHANGED
@@ -1,5 +1,22 @@
|
|
1
1
|
|
2
2
|
|
3
|
+
=== 2.8.0 / 01 Feb 2014
|
4
|
+
|
5
|
+
* Handle ssh-rsa and ssh-dss certificate files [bobveznat]
|
6
|
+
* Correctly interpret /etc/ssh_config Authentication settings based on openssh /etc/ssh_config system defaults [therealjessesanford, liggitt]
|
7
|
+
* Fixed pageant support for Windows [jarredholman]
|
8
|
+
* Support %r in ProxyCommand configuration in ssh_config files as defined in OpenSSH [yugui]
|
9
|
+
* Don't use ssh-agent if :keys_only is true [SFEley]
|
10
|
+
* Fix the bug in keys with comments [bobtfish]
|
11
|
+
* Add a failing tests for options in pub keys [bobtfish]
|
12
|
+
* Assert that the return value from ssh block is returned [carlhoerberg]
|
13
|
+
* Don't close the connection it's already closed [carlhoerberg]
|
14
|
+
* Ensure the connection closes even on exception [carlhoerberg]
|
15
|
+
* Make the authentication error message more useful [deric]
|
16
|
+
* Fix "ConnectionError" typo in lib/net/ssh/proxy/socks5.rb [mirakui]
|
17
|
+
* Allow KeyManager to recover from incompatible agents [ecki, delano]
|
18
|
+
* Fix for "Authentication Method determination can pick up a class from the root namespace" [dave.sieh]
|
19
|
+
|
3
20
|
=== 2.7.0 / 11 Sep 2013
|
4
21
|
|
5
22
|
* Fix for 'Could not parse PKey: no start line' error on private keys with passphrases (issue #101) [metametaclass]
|
data/THANKS.txt
CHANGED
@@ -19,6 +19,30 @@ Chris Andrews <chris@nodnol.org> and Lee Jensen <lee@outerim.com>
|
|
19
19
|
Hiroshi Nakamura
|
20
20
|
* fixed errors with JRuby tests
|
21
21
|
|
22
|
+
bobveznat
|
23
|
+
therealjessesanford
|
24
|
+
liggitt
|
25
|
+
jarredholman
|
26
|
+
yugui
|
27
|
+
SFEley
|
28
|
+
bobtfish
|
29
|
+
carlhoerberg
|
30
|
+
deric
|
31
|
+
mirakui
|
32
|
+
ecki
|
33
|
+
Dave Sieh
|
34
|
+
metametaclass
|
35
|
+
fnordfish
|
36
|
+
krishicks
|
37
|
+
noric
|
38
|
+
GabKlein
|
39
|
+
Josh Kalderimis
|
40
|
+
voxik
|
41
|
+
Olipro
|
42
|
+
jansegre
|
43
|
+
priteau
|
44
|
+
jordimassaguerpla
|
45
|
+
Kenichi Kamiya
|
22
46
|
Andreas Wolff
|
23
47
|
mhuffnagle
|
24
48
|
ohrite
|
@@ -83,3 +107,4 @@ watsonian
|
|
83
107
|
Grant Hutchins
|
84
108
|
Michael Schubert
|
85
109
|
mtrudel
|
110
|
+
Aurélien Derouineau
|
data/lib/net/ssh.rb
CHANGED
@@ -204,15 +204,17 @@ module Net
|
|
204
204
|
if auth.authenticate("ssh-connection", user, options[:password])
|
205
205
|
connection = Connection::Session.new(transport, options)
|
206
206
|
if block_given?
|
207
|
-
|
208
|
-
|
209
|
-
|
207
|
+
begin
|
208
|
+
yield connection
|
209
|
+
ensure
|
210
|
+
connection.close unless connection.closed?
|
211
|
+
end
|
210
212
|
else
|
211
213
|
return connection
|
212
214
|
end
|
213
215
|
else
|
214
216
|
transport.close
|
215
|
-
raise AuthenticationFailed, user
|
217
|
+
raise AuthenticationFailed, "Authentication failed for user #{user}@#{host}"
|
216
218
|
end
|
217
219
|
end
|
218
220
|
|
@@ -76,9 +76,9 @@ module Net; module SSH; module Authentication
|
|
76
76
|
type, body = send_and_wait(SSH2_AGENT_REQUEST_VERSION, :string, Transport::ServerVersion::PROTO_VERSION)
|
77
77
|
|
78
78
|
if type == SSH2_AGENT_VERSION_RESPONSE
|
79
|
-
raise
|
79
|
+
raise AgentNotAvailable, "SSH2 agents are not yet supported"
|
80
80
|
elsif type != SSH_AGENT_RSA_IDENTITIES_ANSWER1 && type != SSH_AGENT_RSA_IDENTITIES_ANSWER2
|
81
|
-
raise
|
81
|
+
raise AgentNotAvailable, "unknown response from agent: #{type}, #{body.to_s.inspect}"
|
82
82
|
end
|
83
83
|
end
|
84
84
|
|
@@ -126,7 +126,7 @@ module Net; module SSH; module Authentication
|
|
126
126
|
# Returns the agent socket factory to use.
|
127
127
|
def agent_socket_factory
|
128
128
|
if Net::SSH::Authentication::PLATFORM == :win32
|
129
|
-
Pageant::
|
129
|
+
Pageant::Socket
|
130
130
|
else
|
131
131
|
UNIXSocket
|
132
132
|
end
|
@@ -12,7 +12,7 @@ module Net
|
|
12
12
|
|
13
13
|
# This class encapsulates all operations done by clients on a user's
|
14
14
|
# private keys. In practice, the client should never need a reference
|
15
|
-
# to a private key; instead, they grab a list of "identities" (public
|
15
|
+
# to a private key; instead, they grab a list of "identities" (public
|
16
16
|
# keys) that are available from the KeyManager, and then use
|
17
17
|
# the KeyManager to do various private key operations using those
|
18
18
|
# identities.
|
@@ -37,12 +37,13 @@ module Net
|
|
37
37
|
attr_reader :options
|
38
38
|
|
39
39
|
# Create a new KeyManager. By default, the manager will
|
40
|
-
# use the ssh-agent
|
40
|
+
# use the ssh-agent if it is running and the `:keys_only` option
|
41
|
+
# is not true.
|
41
42
|
def initialize(logger, options={})
|
42
43
|
self.logger = logger
|
43
44
|
@key_files = []
|
44
45
|
@key_data = []
|
45
|
-
@use_agent =
|
46
|
+
@use_agent = !options[:keys_only]
|
46
47
|
@known_identities = {}
|
47
48
|
@agent = nil
|
48
49
|
@options = options
|
@@ -91,9 +92,8 @@ module Net
|
|
91
92
|
# ssh-agent. Note that identities from an ssh-agent are always listed
|
92
93
|
# first in the array, with other identities coming after.
|
93
94
|
#
|
94
|
-
# If key manager was created with :keys_only option,
|
95
|
-
# from ssh-agent will be
|
96
|
-
# key_data.
|
95
|
+
# If key manager was created with :keys_only option, no identities
|
96
|
+
# from ssh-agent will be loaded.
|
97
97
|
def each_identity
|
98
98
|
prepared_identities = prepare_identities_from_files + prepare_identities_from_data
|
99
99
|
|
@@ -139,7 +139,7 @@ module Net
|
|
139
139
|
if info[:key].nil? && info[:from] == :file
|
140
140
|
begin
|
141
141
|
info[:key] = KeyFactory.load_private_key(info[:file], options[:passphrase], true)
|
142
|
-
rescue Exception, OpenSSL::OpenSSLError => e
|
142
|
+
rescue Exception, OpenSSL::OpenSSLError => e
|
143
143
|
raise KeyManagerError, "the given identity is known, but the private key could not be loaded: #{e.class} (#{e.message})"
|
144
144
|
end
|
145
145
|
end
|
@@ -110,21 +110,49 @@ module Net; module SSH; module Authentication
|
|
110
110
|
"pageant process not running"
|
111
111
|
end
|
112
112
|
|
113
|
-
@
|
114
|
-
@
|
113
|
+
@input_buffer = Net::SSH::Buffer.new
|
114
|
+
@output_buffer = Net::SSH::Buffer.new
|
115
115
|
end
|
116
116
|
|
117
117
|
# Forwards the data to #send_query, ignoring any arguments after
|
118
|
-
# the first.
|
118
|
+
# the first.
|
119
119
|
def send(data, *args)
|
120
|
-
@
|
121
|
-
|
120
|
+
@input_buffer.append(data)
|
121
|
+
|
122
|
+
ret = data.length
|
123
|
+
|
124
|
+
while true
|
125
|
+
return ret if @input_buffer.length < 4
|
126
|
+
msg_length = @input_buffer.read_long + 4
|
127
|
+
@input_buffer.reset!
|
128
|
+
|
129
|
+
return ret if @input_buffer.length < msg_length
|
130
|
+
msg = @input_buffer.read!(msg_length)
|
131
|
+
@output_buffer.append(send_query(msg))
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
# Reads +n+ bytes from the cached result of the last query. If +n+
|
136
|
+
# is +nil+, returns all remaining data from the last query.
|
137
|
+
def read(n = nil)
|
138
|
+
@output_buffer.read(n)
|
122
139
|
end
|
123
140
|
|
141
|
+
def close
|
142
|
+
end
|
143
|
+
|
144
|
+
def send_query(query)
|
145
|
+
if RUBY_VERSION < "1.9"
|
146
|
+
send_query_18(query)
|
147
|
+
else
|
148
|
+
send_query_19(query)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
124
152
|
# Packages the given query string and sends it to the pageant
|
125
153
|
# process via the Windows messaging subsystem. The result is
|
126
154
|
# cached, to be returned piece-wise when #read is called.
|
127
|
-
def
|
155
|
+
def send_query_18(query)
|
128
156
|
res = nil
|
129
157
|
filemap = 0
|
130
158
|
ptr = nil
|
@@ -165,43 +193,10 @@ module Net; module SSH; module Authentication
|
|
165
193
|
Win.closeHandle(filemap) if filemap != 0
|
166
194
|
end
|
167
195
|
|
168
|
-
# Conceptually close the socket. This doesn't really do anthing
|
169
|
-
# significant, but merely complies with the Socket interface.
|
170
|
-
def close
|
171
|
-
@res = nil
|
172
|
-
@pos = 0
|
173
|
-
end
|
174
|
-
|
175
|
-
# Conceptually asks if the socket is closed. As with #close,
|
176
|
-
# this doesn't really do anything significant, but merely
|
177
|
-
# complies with the Socket interface.
|
178
|
-
def closed?
|
179
|
-
@res.nil? && @pos.zero?
|
180
|
-
end
|
181
|
-
|
182
|
-
# Reads +n+ bytes from the cached result of the last query. If +n+
|
183
|
-
# is +nil+, returns all remaining data from the last query.
|
184
|
-
def read(n = nil)
|
185
|
-
return nil unless @res
|
186
|
-
if n.nil?
|
187
|
-
start, @pos = @pos, @res.size
|
188
|
-
return @res[start..-1]
|
189
|
-
else
|
190
|
-
start, @pos = @pos, @pos + n
|
191
|
-
return @res[start, n]
|
192
|
-
end
|
193
|
-
end
|
194
|
-
|
195
|
-
end
|
196
|
-
|
197
|
-
# Socket changes for Ruby 1.9
|
198
|
-
# Functionality is the same as Ruby 1.8 but it includes the new calls to
|
199
|
-
# the DL module as well as other pointer transformations
|
200
|
-
class Socket19 < Socket
|
201
196
|
# Packages the given query string and sends it to the pageant
|
202
197
|
# process via the Windows messaging subsystem. The result is
|
203
198
|
# cached, to be returned piece-wise when #read is called.
|
204
|
-
def
|
199
|
+
def send_query_19(query)
|
205
200
|
res = nil
|
206
201
|
filemap = 0
|
207
202
|
ptr = nil
|
@@ -244,17 +239,6 @@ module Net; module SSH; module Authentication
|
|
244
239
|
Win.CloseHandle(filemap) if filemap != 0
|
245
240
|
end
|
246
241
|
end
|
247
|
-
|
248
|
-
# Selects which socket to use depending on the ruby version
|
249
|
-
# This is needed due changes in the DL module.
|
250
|
-
def self.socket_factory
|
251
|
-
if RUBY_VERSION < "1.9"
|
252
|
-
Socket
|
253
|
-
else
|
254
|
-
Socket19
|
255
|
-
end
|
256
|
-
end
|
257
|
-
|
258
242
|
end
|
259
243
|
|
260
244
|
end; end; end
|
@@ -2,6 +2,7 @@ require 'net/ssh/loggable'
|
|
2
2
|
require 'net/ssh/transport/constants'
|
3
3
|
require 'net/ssh/authentication/constants'
|
4
4
|
require 'net/ssh/authentication/key_manager'
|
5
|
+
require 'net/ssh/authentication/methods/none'
|
5
6
|
require 'net/ssh/authentication/methods/publickey'
|
6
7
|
require 'net/ssh/authentication/methods/hostbased'
|
7
8
|
require 'net/ssh/authentication/methods/password'
|
@@ -41,7 +42,7 @@ module Net; module SSH; module Authentication
|
|
41
42
|
self.logger = transport.logger
|
42
43
|
@transport = transport
|
43
44
|
|
44
|
-
@auth_methods = options[:auth_methods] ||
|
45
|
+
@auth_methods = options[:auth_methods] || Net::SSH::Config.default_auth_methods
|
45
46
|
@options = options
|
46
47
|
|
47
48
|
@allowed_auth_methods = @auth_methods
|
data/lib/net/ssh/buffer.rb
CHANGED
@@ -243,14 +243,14 @@ module Net; module SSH
|
|
243
243
|
# a key. Only RSA, DSA, and ECDSA keys are supported.
|
244
244
|
def read_keyblob(type)
|
245
245
|
case type
|
246
|
-
when
|
246
|
+
when /^ssh-dss(-cert-v01@openssh\.com)?$/
|
247
247
|
key = OpenSSL::PKey::DSA.new
|
248
248
|
key.p = read_bignum
|
249
249
|
key.q = read_bignum
|
250
250
|
key.g = read_bignum
|
251
251
|
key.pub_key = read_bignum
|
252
252
|
|
253
|
-
when
|
253
|
+
when /^ssh-rsa(-cert-v01@openssh\.com)?$/
|
254
254
|
key = OpenSSL::PKey::RSA.new
|
255
255
|
key.e = read_bignum
|
256
256
|
key.n = read_bignum
|
data/lib/net/ssh/config.rb
CHANGED
@@ -8,6 +8,7 @@ module Net; module SSH
|
|
8
8
|
#
|
9
9
|
# Only a subset of OpenSSH configuration options are understood:
|
10
10
|
#
|
11
|
+
# * ChallengeResponseAuthentication => maps to the :auth_methods option
|
11
12
|
# * Ciphers => maps to the :encryption option
|
12
13
|
# * Compression => :compression
|
13
14
|
# * CompressionLevel => :compression_level
|
@@ -25,6 +26,7 @@ module Net; module SSH
|
|
25
26
|
# * Port => :port
|
26
27
|
# * PreferredAuthentications => maps to the :auth_methods option
|
27
28
|
# * ProxyCommand => maps to the :proxy option
|
29
|
+
# * PubKeyAuthentication => maps to the :auth_methods option
|
28
30
|
# * RekeyLimit => :rekey_limit
|
29
31
|
# * User => :user
|
30
32
|
# * UserKnownHostsFile => :user_known_hosts_file
|
@@ -35,19 +37,30 @@ module Net; module SSH
|
|
35
37
|
class Config
|
36
38
|
class << self
|
37
39
|
@@default_files = %w(~/.ssh/config /etc/ssh_config /etc/ssh/ssh_config)
|
40
|
+
# The following defaults follow the openssh client ssh_config defaults.
|
41
|
+
# http://lwn.net/Articles/544640/
|
42
|
+
# "hostbased" is off and "none" is not supported but we allow it since
|
43
|
+
# it's used by some clients to query the server for allowed auth methods
|
44
|
+
@@default_auth_methods = %w(none publickey password keyboard-interactive)
|
38
45
|
|
39
46
|
# Returns an array of locations of OpenSSH configuration files
|
40
47
|
# to parse by default.
|
41
48
|
def default_files
|
42
49
|
@@default_files
|
43
50
|
end
|
51
|
+
|
52
|
+
def default_auth_methods
|
53
|
+
@@default_auth_methods
|
54
|
+
end
|
44
55
|
|
45
56
|
# Loads the configuration data for the given +host+ from all of the
|
46
57
|
# given +files+ (defaulting to the list of files returned by
|
47
58
|
# #default_files), translates the resulting hash into the options
|
48
59
|
# recognized by Net::SSH, and returns them.
|
49
60
|
def for(host, files=default_files)
|
50
|
-
translate(files.inject({}) { |settings, file|
|
61
|
+
hash = translate(files.inject({}) { |settings, file|
|
62
|
+
load(file, host, settings)
|
63
|
+
})
|
51
64
|
end
|
52
65
|
|
53
66
|
# Load the OpenSSH configuration settings in the given +file+ for the
|
@@ -59,6 +72,8 @@ module Net; module SSH
|
|
59
72
|
def load(path, host, settings={})
|
60
73
|
file = File.expand_path(path)
|
61
74
|
return settings unless File.readable?(file)
|
75
|
+
|
76
|
+
settings[:auth_methods] ||= default_auth_methods.clone
|
62
77
|
|
63
78
|
globals = {}
|
64
79
|
matched_host = nil
|
@@ -119,6 +134,7 @@ module Net; module SSH
|
|
119
134
|
# the returned hash will have Symbols for keys.
|
120
135
|
def translate(settings)
|
121
136
|
settings.inject({}) do |hash, (key, value)|
|
137
|
+
hash[:auth_methods] ||= settings[:auth_methods] || default_auth_methods.clone
|
122
138
|
case key
|
123
139
|
when 'bindaddress' then
|
124
140
|
hash[:bind_address] = value
|
@@ -138,8 +154,9 @@ module Net; module SSH
|
|
138
154
|
hash[:global_known_hosts_file] = value
|
139
155
|
when 'hostbasedauthentication' then
|
140
156
|
if value
|
141
|
-
hash[:auth_methods]
|
142
|
-
|
157
|
+
(hash[:auth_methods] << "hostbased").uniq!
|
158
|
+
else
|
159
|
+
hash[:auth_methods].delete("hostbased")
|
143
160
|
end
|
144
161
|
when 'hostkeyalgorithms' then
|
145
162
|
hash[:host_key] = value.split(/,/)
|
@@ -153,8 +170,15 @@ module Net; module SSH
|
|
153
170
|
hash[:hmac] = value.split(/,/)
|
154
171
|
when 'passwordauthentication'
|
155
172
|
if value
|
156
|
-
hash[:auth_methods]
|
157
|
-
|
173
|
+
(hash[:auth_methods] << 'password').uniq!
|
174
|
+
else
|
175
|
+
hash[:auth_methods].delete('password')
|
176
|
+
end
|
177
|
+
when 'challengeresponseauthentication'
|
178
|
+
if value
|
179
|
+
(hash[:auth_methods] << 'keyboard-interactive').uniq!
|
180
|
+
else
|
181
|
+
hash[:auth_methods].delete('keyboard-interactive')
|
158
182
|
end
|
159
183
|
when 'port'
|
160
184
|
hash[:port] = value
|
@@ -165,10 +189,11 @@ module Net; module SSH
|
|
165
189
|
require 'net/ssh/proxy/command'
|
166
190
|
hash[:proxy] = Net::SSH::Proxy::Command.new(value)
|
167
191
|
end
|
168
|
-
|
192
|
+
when 'pubkeyauthentication'
|
169
193
|
if value
|
170
|
-
hash[:auth_methods]
|
171
|
-
|
194
|
+
(hash[:auth_methods] << 'publickey').uniq!
|
195
|
+
else
|
196
|
+
hash[:auth_methods].delete('publickey')
|
172
197
|
end
|
173
198
|
when 'rekeylimit'
|
174
199
|
hash[:rekey_limit] = interpret_size(value)
|
data/lib/net/ssh/key_factory.rb
CHANGED
@@ -105,7 +105,13 @@ module Net; module SSH
|
|
105
105
|
# the file describes an RSA or DSA key, and will load it
|
106
106
|
# appropriately. The new public key is returned.
|
107
107
|
def load_data_public_key(data, filename="")
|
108
|
-
|
108
|
+
fields = data.split(/ /)
|
109
|
+
|
110
|
+
blob = nil
|
111
|
+
begin
|
112
|
+
blob = fields.shift
|
113
|
+
end while !blob.nil? && !/^(ssh-(rsa|dss)|ecdsa-sha2-nistp\d+)$/.match(blob)
|
114
|
+
blob = fields.shift
|
109
115
|
|
110
116
|
raise Net::SSH::Exception, "public key at #{filename} is not valid" if blob.nil?
|
111
117
|
|
@@ -33,13 +33,20 @@ module Net; module SSH; module Proxy
|
|
33
33
|
|
34
34
|
# Return a new socket connected to the given host and port via the
|
35
35
|
# proxy that was requested when the socket factory was instantiated.
|
36
|
-
def open(host, port)
|
36
|
+
def open(host, port, connection_options = nil)
|
37
37
|
command_line = @command_line_template.gsub(/%(.)/) {
|
38
38
|
case $1
|
39
39
|
when 'h'
|
40
40
|
host
|
41
41
|
when 'p'
|
42
42
|
port.to_s
|
43
|
+
when 'r'
|
44
|
+
remote_user = connection_options && connection_options[:remote_user]
|
45
|
+
if remote_user
|
46
|
+
remote_user
|
47
|
+
else
|
48
|
+
raise ArgumentError, "remote user name not available"
|
49
|
+
end
|
43
50
|
when '%'
|
44
51
|
'%'
|
45
52
|
else
|
data/lib/net/ssh/proxy/http.rb
CHANGED
@@ -48,7 +48,7 @@ module Net; module SSH; module Proxy
|
|
48
48
|
|
49
49
|
# Return a new socket connected to the given host and port via the
|
50
50
|
# proxy that was requested when the socket factory was instantiated.
|
51
|
-
def open(host, port)
|
51
|
+
def open(host, port, connection_options = nil)
|
52
52
|
socket = TCPSocket.new(proxy_host, proxy_port)
|
53
53
|
socket.write "CONNECT #{host}:#{port} HTTP/1.0\r\n"
|
54
54
|
|
data/lib/net/ssh/proxy/socks4.rb
CHANGED
@@ -47,7 +47,7 @@ module Net
|
|
47
47
|
|
48
48
|
# Return a new socket connected to the given host and port via the
|
49
49
|
# proxy that was requested when the socket factory was instantiated.
|
50
|
-
def open(host, port)
|
50
|
+
def open(host, port, connection_options)
|
51
51
|
socket = TCPSocket.new(proxy_host, proxy_port)
|
52
52
|
ip_addr = IPAddr.new(Resolv.getaddress(host))
|
53
53
|
|
data/lib/net/ssh/proxy/socks5.rb
CHANGED
@@ -62,7 +62,7 @@ module Net
|
|
62
62
|
|
63
63
|
# Return a new socket connected to the given host and port via the
|
64
64
|
# proxy that was requested when the socket factory was instantiated.
|
65
|
-
def open(host, port)
|
65
|
+
def open(host, port, connection_options = nil)
|
66
66
|
socket = TCPSocket.new(proxy_host, proxy_port)
|
67
67
|
|
68
68
|
methods = [METHOD_NO_AUTH]
|
@@ -108,7 +108,7 @@ module Net
|
|
108
108
|
ipv6addr hostname = socket.recv(16)
|
109
109
|
else
|
110
110
|
socket.close
|
111
|
-
raise
|
111
|
+
raise ConnectError, "Illegal response type"
|
112
112
|
end
|
113
113
|
portnum = socket.recv(2)
|
114
114
|
|
@@ -47,8 +47,12 @@ module Net; module SSH; module Service
|
|
47
47
|
# If three arguments are given, it is as if the local bind address is
|
48
48
|
# "127.0.0.1", and the rest are applied as above.
|
49
49
|
#
|
50
|
+
# To request an ephemeral port on the remote server, provide 0 (zero) for
|
51
|
+
# the port number. In all cases, this method will return the port that
|
52
|
+
# has been assigned.
|
53
|
+
#
|
50
54
|
# ssh.forward.local(1234, "www.capify.org", 80)
|
51
|
-
# ssh.forward.local("0.0.0.0",
|
55
|
+
# assigned_port = ssh.forward.local("0.0.0.0", 0, "www.capify.org", 80)
|
52
56
|
def local(*args)
|
53
57
|
if args.length < 3 || args.length > 4
|
54
58
|
raise ArgumentError, "expected 3 or 4 parameters, got #{args.length}"
|
@@ -69,6 +73,7 @@ module Net; module SSH; module Service
|
|
69
73
|
end
|
70
74
|
end
|
71
75
|
|
76
|
+
local_port = socket.addr[1] if local_port == 0 # ephemeral port was requested
|
72
77
|
remote_host = args.shift
|
73
78
|
remote_port = args.shift.to_i
|
74
79
|
|
@@ -89,6 +94,8 @@ module Net; module SSH; module Service
|
|
89
94
|
channel[:socket].close
|
90
95
|
end
|
91
96
|
end
|
97
|
+
|
98
|
+
local_port
|
92
99
|
end
|
93
100
|
|
94
101
|
# Terminates an active local forwarded port. If no such forwarded port
|
@@ -120,15 +127,21 @@ module Net; module SSH; module Service
|
|
120
127
|
# forwarded immediately. If the remote server is not able to begin the
|
121
128
|
# listener for this request, an exception will be raised asynchronously.
|
122
129
|
#
|
123
|
-
#
|
124
|
-
#
|
125
|
-
#
|
130
|
+
# To request an ephemeral port on the remote server, provide 0 (zero) for
|
131
|
+
# the port number. The assigned port will show up in the # #active_remotes
|
132
|
+
# list.
|
126
133
|
#
|
127
|
-
#
|
128
|
-
#
|
134
|
+
# If you want to block until the port is active, you could do something
|
135
|
+
# like this:
|
136
|
+
#
|
137
|
+
# old_active_remotes = ssh.forward.active_remotes
|
138
|
+
# ssh.forward.remote(80, "www.google.com", 0, "0.0.0.0")
|
139
|
+
# ssh.loop { !(ssh.forward.active_remotes.length > old_active_remotes.length) }
|
140
|
+
# assigned_port = (ssh.forward.active_remotes - old_active_remotes).first[0]
|
129
141
|
def remote(port, host, remote_port, remote_host="127.0.0.1")
|
130
142
|
session.send_global_request("tcpip-forward", :string, remote_host, :long, remote_port) do |success, response|
|
131
143
|
if success
|
144
|
+
remote_port = response.read_long if remote_port == 0
|
132
145
|
debug { "remote forward from remote #{remote_host}:#{remote_port} to #{host}:#{port} established" }
|
133
146
|
@remote_forwarded_ports[[remote_port, remote_host]] = Remote.new(host, port)
|
134
147
|
else
|
@@ -257,6 +270,24 @@ module Net; module SSH; module Service
|
|
257
270
|
end
|
258
271
|
end
|
259
272
|
|
273
|
+
# not a real socket, so use a simpler behaviour
|
274
|
+
def prepare_simple_client(client, channel, type)
|
275
|
+
channel[:socket] = client
|
276
|
+
|
277
|
+
channel.on_data do |ch, data|
|
278
|
+
ch.debug { "data:#{data.length} on #{type} forwarded channel" }
|
279
|
+
ch[:socket].send(data)
|
280
|
+
end
|
281
|
+
|
282
|
+
channel.on_process do |ch|
|
283
|
+
data = ch[:socket].read(8192)
|
284
|
+
if data
|
285
|
+
ch.debug { "read #{data.length} bytes from client, sending over #{type} forwarded connection" }
|
286
|
+
ch.send_data(data)
|
287
|
+
end
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
260
291
|
# The callback used when a new "forwarded-tcpip" channel is requested
|
261
292
|
# by the server. This will open a new socket to the host/port specified
|
262
293
|
# when the forwarded connection was first requested.
|
@@ -287,7 +318,11 @@ module Net; module SSH; module Service
|
|
287
318
|
|
288
319
|
begin
|
289
320
|
agent = Authentication::Agent.connect(logger)
|
290
|
-
|
321
|
+
if (agent.socket.is_a? ::IO)
|
322
|
+
prepare_client(agent.socket, channel, :agent)
|
323
|
+
else
|
324
|
+
prepare_simple_client(agent.socket, channel, :agent)
|
325
|
+
end
|
291
326
|
rescue Exception => e
|
292
327
|
error { "attempted to connect to agent but failed: #{e.class.name} (#{e.message})" }
|
293
328
|
raise Net::SSH::ChannelOpenFailed.new(2, "could not connect to authentication agent")
|
@@ -64,7 +64,13 @@ module Net; module SSH; module Transport
|
|
64
64
|
|
65
65
|
debug { "establishing connection to #{@host}:#{@port}" }
|
66
66
|
factory = options[:proxy] || TCPSocket
|
67
|
-
@socket = timeout(options[:timeout] || 0) {
|
67
|
+
@socket = timeout(options[:timeout] || 0) {
|
68
|
+
case
|
69
|
+
when options[:proxy] then factory.open(@host, @port, options)
|
70
|
+
when @bind_address.nil? then factory.open(@host, @port)
|
71
|
+
else factory.open(@host, @port, @bind_address)
|
72
|
+
end
|
73
|
+
}
|
68
74
|
@socket.extend(PacketStream)
|
69
75
|
@socket.logger = @logger
|
70
76
|
|
data/lib/net/ssh/version.rb
CHANGED
data/net-ssh.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "net-ssh"
|
8
|
-
s.version = "2.
|
8
|
+
s.version = "2.8.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Jamis Buck", "Delano Mandelbaum"]
|
12
|
-
s.date = "
|
12
|
+
s.date = "2014-02-01"
|
13
13
|
s.description = "Net::SSH: a pure-Ruby implementation of the SSH2 client protocol. It allows you to write programs that invoke and interact with processes on remote servers, via SSH2."
|
14
14
|
s.email = "net-ssh@solutious.com"
|
15
15
|
s.extra_rdoc_files = [
|
@@ -120,6 +120,9 @@ Gem::Specification.new do |s|
|
|
120
120
|
"test/authentication/test_key_manager.rb",
|
121
121
|
"test/authentication/test_session.rb",
|
122
122
|
"test/common.rb",
|
123
|
+
"test/configs/auth_off",
|
124
|
+
"test/configs/auth_on",
|
125
|
+
"test/configs/empty",
|
123
126
|
"test/configs/eqsign",
|
124
127
|
"test/configs/exact_match",
|
125
128
|
"test/configs/host_plus",
|
@@ -133,6 +136,7 @@ Gem::Specification.new do |s|
|
|
133
136
|
"test/connection/test_session.rb",
|
134
137
|
"test/known_hosts/github",
|
135
138
|
"test/manual/test_forward.rb",
|
139
|
+
"test/start/test_connection.rb",
|
136
140
|
"test/start/test_options.rb",
|
137
141
|
"test/start/test_transport.rb",
|
138
142
|
"test/test_all.rb",
|
@@ -171,7 +175,7 @@ Gem::Specification.new do |s|
|
|
171
175
|
s.licenses = ["MIT"]
|
172
176
|
s.require_paths = ["lib"]
|
173
177
|
s.rubyforge_project = "net-ssh"
|
174
|
-
s.rubygems_version = "1.8.
|
178
|
+
s.rubygems_version = "1.8.23"
|
175
179
|
s.summary = "Net::SSH: a pure-Ruby implementation of the SSH2 client protocol."
|
176
180
|
|
177
181
|
if s.respond_to? :specification_version then
|
@@ -43,7 +43,7 @@ module Authentication
|
|
43
43
|
assert_equal Net::SSH::Transport::ServerVersion::PROTO_VERSION, buffer.read_string
|
44
44
|
s.return(SSH2_AGENT_VERSION_RESPONSE)
|
45
45
|
end
|
46
|
-
assert_raises(
|
46
|
+
assert_raises(Net::SSH::Authentication::AgentNotAvailable) { agent.negotiate! }
|
47
47
|
end
|
48
48
|
|
49
49
|
def test_negotiate_should_raise_error_if_response_was_unexpected
|
@@ -51,7 +51,7 @@ module Authentication
|
|
51
51
|
assert_equal SSH2_AGENT_REQUEST_VERSION, type
|
52
52
|
s.return(255)
|
53
53
|
end
|
54
|
-
assert_raises(Net::SSH::Authentication::
|
54
|
+
assert_raises(Net::SSH::Authentication::AgentNotAvailable) { agent.negotiate! }
|
55
55
|
end
|
56
56
|
|
57
57
|
def test_negotiate_should_be_successful_with_expected_response
|
@@ -65,7 +65,7 @@ module Authentication
|
|
65
65
|
def test_identities_should_fail_if_SSH_AGENT_FAILURE_recieved
|
66
66
|
socket.expect do |s, type, buffer|
|
67
67
|
assert_equal SSH2_AGENT_REQUEST_IDENTITIES, type
|
68
|
-
s.return(SSH_AGENT_FAILURE)
|
68
|
+
s.return(SSH_AGENT_FAILURE)
|
69
69
|
end
|
70
70
|
assert_raises(Net::SSH::Authentication::AgentError) { agent.identities }
|
71
71
|
end
|
@@ -73,7 +73,7 @@ module Authentication
|
|
73
73
|
def test_identities_should_fail_if_SSH2_AGENT_FAILURE_recieved
|
74
74
|
socket.expect do |s, type, buffer|
|
75
75
|
assert_equal SSH2_AGENT_REQUEST_IDENTITIES, type
|
76
|
-
s.return(SSH2_AGENT_FAILURE)
|
76
|
+
s.return(SSH2_AGENT_FAILURE)
|
77
77
|
end
|
78
78
|
assert_raises(Net::SSH::Authentication::AgentError) { agent.identities }
|
79
79
|
end
|
@@ -81,7 +81,7 @@ module Authentication
|
|
81
81
|
def test_identities_should_fail_if_SSH_COM_AGENT2_FAILURE_recieved
|
82
82
|
socket.expect do |s, type, buffer|
|
83
83
|
assert_equal SSH2_AGENT_REQUEST_IDENTITIES, type
|
84
|
-
s.return(SSH_COM_AGENT2_FAILURE)
|
84
|
+
s.return(SSH_COM_AGENT2_FAILURE)
|
85
85
|
end
|
86
86
|
assert_raises(Net::SSH::Authentication::AgentError) { agent.identities }
|
87
87
|
end
|
@@ -202,4 +202,4 @@ module Authentication
|
|
202
202
|
|
203
203
|
end
|
204
204
|
|
205
|
-
end
|
205
|
+
end
|
@@ -18,7 +18,7 @@ module Authentication
|
|
18
18
|
manager.add "/second"
|
19
19
|
manager.add "/third"
|
20
20
|
manager.add "/second"
|
21
|
-
|
21
|
+
assert_equal 3, manager.key_files.length
|
22
22
|
final_files = manager.key_files.map {|item| item.split('/').last}
|
23
23
|
assert_equal %w(first second third), final_files
|
24
24
|
end
|
@@ -30,12 +30,16 @@ module Authentication
|
|
30
30
|
assert !manager.use_agent?
|
31
31
|
end
|
32
32
|
|
33
|
+
def test_use_agent_is_false_if_keys_only
|
34
|
+
assert !manager(:keys_only => true).use_agent?
|
35
|
+
end
|
36
|
+
|
33
37
|
def test_each_identity_should_load_from_key_files
|
34
38
|
manager.stubs(:agent).returns(nil)
|
35
39
|
first = File.expand_path("/first")
|
36
40
|
second = File.expand_path("/second")
|
37
41
|
stub_file_private_key first, rsa
|
38
|
-
stub_file_private_key second, dsa
|
42
|
+
stub_file_private_key second, dsa
|
39
43
|
|
40
44
|
identities = []
|
41
45
|
manager.each_identity { |identity| identities << identity }
|
@@ -43,7 +47,7 @@ module Authentication
|
|
43
47
|
assert_equal 2, identities.length
|
44
48
|
assert_equal rsa.to_blob, identities.first.to_blob
|
45
49
|
assert_equal dsa.to_blob, identities.last.to_blob
|
46
|
-
|
50
|
+
|
47
51
|
assert_equal({:from => :file, :file => first, :key => rsa}, manager.known_identities[rsa])
|
48
52
|
assert_equal({:from => :file, :file => second, :key => dsa}, manager.known_identities[dsa])
|
49
53
|
end
|
@@ -8,7 +8,7 @@ module Authentication
|
|
8
8
|
include Net::SSH::Authentication::Constants
|
9
9
|
|
10
10
|
def test_constructor_should_set_defaults
|
11
|
-
assert_equal %w(none publickey
|
11
|
+
assert_equal %w(none publickey password keyboard-interactive), session.auth_methods
|
12
12
|
assert_equal session.auth_methods, session.allowed_auth_methods
|
13
13
|
end
|
14
14
|
|
@@ -20,7 +20,7 @@ module Authentication
|
|
20
20
|
end
|
21
21
|
|
22
22
|
Net::SSH::Authentication::Methods::Publickey.any_instance.expects(:authenticate).with("next service", "username", "password").raises(Net::SSH::Authentication::DisallowedMethod)
|
23
|
-
Net::SSH::Authentication::Methods::
|
23
|
+
Net::SSH::Authentication::Methods::Password.any_instance.expects(:authenticate).with("next service", "username", "password").returns(true)
|
24
24
|
Net::SSH::Authentication::Methods::None.any_instance.expects(:authenticate).with("next service", "username", "password").returns(false)
|
25
25
|
|
26
26
|
assert session.authenticate("next service", "username", "password")
|
@@ -44,7 +44,6 @@ module Authentication
|
|
44
44
|
end
|
45
45
|
|
46
46
|
Net::SSH::Authentication::Methods::Publickey.any_instance.expects(:authenticate).with("next service", "username", "password").returns(false)
|
47
|
-
Net::SSH::Authentication::Methods::Hostbased.any_instance.expects(:authenticate).with("next service", "username", "password").returns(false)
|
48
47
|
Net::SSH::Authentication::Methods::Password.any_instance.expects(:authenticate).with("next service", "username", "password").returns(false)
|
49
48
|
Net::SSH::Authentication::Methods::KeyboardInteractive.any_instance.expects(:authenticate).with("next service", "username", "password").returns(false)
|
50
49
|
Net::SSH::Authentication::Methods::None.any_instance.expects(:authenticate).with("next service", "username", "password").returns(false)
|
data/test/configs/empty
ADDED
File without changes
|
data/test/manual/test_forward.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# $ ruby -Ilib -Itest -rrubygems test/test_forward.rb
|
1
|
+
# $ ruby -Ilib -Itest -rrubygems test/manual/test_forward.rb
|
2
2
|
|
3
3
|
# Tests for the following patch:
|
4
4
|
#
|
@@ -30,14 +30,6 @@ class TestForward < Test::Unit::TestCase
|
|
30
30
|
[localhost ,ENV['USER'], {:keys => "~/.ssh/id_rsa", :verbose => :debug}]
|
31
31
|
end
|
32
32
|
|
33
|
-
def find_free_port
|
34
|
-
server = TCPServer.open(0)
|
35
|
-
server.setsockopt(Socket::SOL_SOCKET,Socket::SO_REUSEADDR,true)
|
36
|
-
port = server.addr[1]
|
37
|
-
server.close
|
38
|
-
port
|
39
|
-
end
|
40
|
-
|
41
33
|
def start_server_sending_lot_of_data(exceptions)
|
42
34
|
server = TCPServer.open(0)
|
43
35
|
Thread.start do
|
@@ -77,12 +69,34 @@ class TestForward < Test::Unit::TestCase
|
|
77
69
|
return server
|
78
70
|
end
|
79
71
|
|
72
|
+
def test_local_ephemeral_port_should_work_correctly
|
73
|
+
session = Net::SSH.start(*ssh_start_params)
|
74
|
+
|
75
|
+
assert_nothing_raised do
|
76
|
+
assigned_port = session.forward.local(0, localhost, 22)
|
77
|
+
assert_not_nil assigned_port
|
78
|
+
assert_operator assigned_port, :>, 0
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def test_remote_ephemeral_port_should_work_correctly
|
83
|
+
session = Net::SSH.start(*ssh_start_params)
|
84
|
+
|
85
|
+
assert_nothing_raised do
|
86
|
+
session.forward.remote(22, localhost, 0, localhost)
|
87
|
+
session.loop { !(session.forward.active_remotes.length > 0) }
|
88
|
+
assigned_port = session.forward.active_remotes.first[0]
|
89
|
+
assert_not_nil assigned_port
|
90
|
+
assert_operator assigned_port, :>, 0
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
80
94
|
def test_loop_should_not_abort_when_local_side_of_forward_is_closed
|
81
95
|
session = Net::SSH.start(*ssh_start_params)
|
82
96
|
server_exc = Queue.new
|
83
97
|
server = start_server_sending_lot_of_data(server_exc)
|
84
98
|
remote_port = server.addr[1]
|
85
|
-
local_port =
|
99
|
+
local_port = 0 # request ephemeral port
|
86
100
|
session.forward.local(local_port, localhost, remote_port)
|
87
101
|
client_done = Queue.new
|
88
102
|
Thread.start do
|
@@ -104,7 +118,7 @@ class TestForward < Test::Unit::TestCase
|
|
104
118
|
server_exc = Queue.new
|
105
119
|
server = start_server_sending_lot_of_data(server_exc)
|
106
120
|
remote_port = server.addr[1]
|
107
|
-
local_port =
|
121
|
+
local_port = 0 # request ephemeral port
|
108
122
|
session.forward.local(local_port, localhost, remote_port)
|
109
123
|
client_done = Queue.new
|
110
124
|
Thread.start do
|
@@ -163,7 +177,7 @@ class TestForward < Test::Unit::TestCase
|
|
163
177
|
session = Net::SSH.start(*ssh_start_params)
|
164
178
|
server = start_server_closing_soon
|
165
179
|
remote_port = server.addr[1]
|
166
|
-
local_port =
|
180
|
+
local_port = 0 # request ephemeral port
|
167
181
|
session.forward.local(local_port, localhost, remote_port)
|
168
182
|
client_done = Queue.new
|
169
183
|
Thread.start do
|
@@ -203,8 +217,7 @@ class TestForward < Test::Unit::TestCase
|
|
203
217
|
client_exception = Queue.new
|
204
218
|
client_data = Queue.new
|
205
219
|
remote_port = server.addr[1]
|
206
|
-
local_port =
|
207
|
-
session.forward.local(local_port, localhost, remote_port)
|
220
|
+
local_port = session.forward.local(0, localhost, remote_port)
|
208
221
|
Thread.start do
|
209
222
|
begin
|
210
223
|
client = TCPSocket.new(localhost, local_port)
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'common'
|
2
|
+
require 'net/ssh'
|
3
|
+
|
4
|
+
module NetSSH
|
5
|
+
class TestConnection < Test::Unit::TestCase
|
6
|
+
attr_reader :connection_session
|
7
|
+
|
8
|
+
def setup
|
9
|
+
authentication_session = mock('authentication_session')
|
10
|
+
authentication_session.stubs(:authenticate).returns(true)
|
11
|
+
Net::SSH::Authentication::Session.stubs(:new).returns(authentication_session)
|
12
|
+
Net::SSH::Transport::Session.stubs(:new).returns(mock('transport_session'))
|
13
|
+
@connection_session = mock('connection_session')
|
14
|
+
Net::SSH::Connection::Session.expects(:new => connection_session)
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_close_connection_on_exception
|
18
|
+
@connection_session.expects(:closed?).returns(false)
|
19
|
+
@connection_session.expects(:close).once
|
20
|
+
|
21
|
+
begin
|
22
|
+
Net::SSH.start('localhost', 'testuser') { raise "error" }
|
23
|
+
rescue RuntimeError
|
24
|
+
# We aren't interested in the exception
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_close_connection_on_exception_only_if_still_open
|
29
|
+
conn_open = states('conn').starts_as(true)
|
30
|
+
@connection_session.expects(:close).then(conn_open.is(false)).once
|
31
|
+
@connection_session.expects(:closed?).when(conn_open.is(false)).returns(true)
|
32
|
+
|
33
|
+
begin
|
34
|
+
Net::SSH.start('localhost', 'testuser') do |ssh|
|
35
|
+
ssh.close
|
36
|
+
raise "error"
|
37
|
+
end
|
38
|
+
rescue RuntimeError
|
39
|
+
# We aren't interested in the exception
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_return_value_is_returned
|
44
|
+
@connection_session.expects(:closed?).returns(false)
|
45
|
+
@connection_session.expects(:close).once
|
46
|
+
|
47
|
+
val = 1
|
48
|
+
retval = Net::SSH.start('localhost', 'testuser') { val }
|
49
|
+
assert_equal(val, retval)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
data/test/test_config.rb
CHANGED
@@ -97,7 +97,7 @@ class TestConfig < Test::Unit::TestCase
|
|
97
97
|
assert_equal 6, net_ssh[:compression_level]
|
98
98
|
assert_equal 100, net_ssh[:timeout]
|
99
99
|
assert_equal true, net_ssh[:forward_agent]
|
100
|
-
assert_equal %w(hostbased password publickey), net_ssh[:auth_methods].sort
|
100
|
+
assert_equal %w(hostbased keyboard-interactive none password publickey), net_ssh[:auth_methods].sort
|
101
101
|
assert_equal %w(d e f), net_ssh[:host_key]
|
102
102
|
assert_equal %w(g h i), net_ssh[:keys]
|
103
103
|
assert_equal %w(j k l), net_ssh[:hmac]
|
@@ -106,6 +106,42 @@ class TestConfig < Test::Unit::TestCase
|
|
106
106
|
assert_equal "127.0.0.1", net_ssh[:bind_address]
|
107
107
|
assert_equal [/^LC_.*$/], net_ssh[:send_env]
|
108
108
|
end
|
109
|
+
|
110
|
+
def test_translate_should_turn_off_authentication_methods
|
111
|
+
open_ssh = {
|
112
|
+
'hostbasedauthentication' => false,
|
113
|
+
'passwordauthentication' => false,
|
114
|
+
'pubkeyauthentication' => false,
|
115
|
+
'challengeresponseauthentication' => false
|
116
|
+
}
|
117
|
+
|
118
|
+
net_ssh = Net::SSH::Config.translate(open_ssh)
|
119
|
+
|
120
|
+
assert_equal %w(none), net_ssh[:auth_methods].sort
|
121
|
+
end
|
122
|
+
|
123
|
+
def test_translate_should_turn_on_authentication_methods
|
124
|
+
open_ssh = {
|
125
|
+
'hostbasedauthentication' => true,
|
126
|
+
'passwordauthentication' => true,
|
127
|
+
'pubkeyauthentication' => true,
|
128
|
+
'challengeresponseauthentication' => true
|
129
|
+
}
|
130
|
+
|
131
|
+
net_ssh = Net::SSH::Config.translate(open_ssh)
|
132
|
+
|
133
|
+
assert_equal %w(hostbased keyboard-interactive none password publickey), net_ssh[:auth_methods].sort
|
134
|
+
end
|
135
|
+
|
136
|
+
def test_for_should_turn_off_authentication_methods
|
137
|
+
config = Net::SSH::Config.for("test.host", [config(:empty), config(:auth_off), config(:auth_on)])
|
138
|
+
assert_equal %w(none), config[:auth_methods].sort
|
139
|
+
end
|
140
|
+
|
141
|
+
def test_for_should_turn_on_authentication_methods
|
142
|
+
config = Net::SSH::Config.for("test.host", [config(:empty), config(:auth_on), config(:auth_off)])
|
143
|
+
assert_equal %w(hostbased keyboard-interactive none password publickey), config[:auth_methods].sort
|
144
|
+
end
|
109
145
|
|
110
146
|
def test_load_with_plus_sign_hosts
|
111
147
|
config = Net::SSH::Config.load(config(:host_plus), "test.host")
|
data/test/test_key_factory.rb
CHANGED
@@ -65,6 +65,20 @@ class TestKeyFactory < Test::Unit::TestCase
|
|
65
65
|
assert_equal rsa_key.to_blob, Net::SSH::KeyFactory.load_public_key(@key_file).to_blob
|
66
66
|
end
|
67
67
|
|
68
|
+
def test_load_public_rsa_key_with_comment_should_return_key
|
69
|
+
File.expects(:read).with(@key_file).returns(public(rsa_key) + " key_comment")
|
70
|
+
assert_equal rsa_key.to_blob, Net::SSH::KeyFactory.load_public_key(@key_file).to_blob
|
71
|
+
end
|
72
|
+
|
73
|
+
def test_load_public_rsa_key_with_options_should_return_key
|
74
|
+
File.expects(:read).with(@key_file).returns(public(rsa_key, 'environment="FOO=bar"'))
|
75
|
+
assert_equal rsa_key.to_blob, Net::SSH::KeyFactory.load_public_key(@key_file).to_blob
|
76
|
+
end
|
77
|
+
|
78
|
+
def test_load_public_rsa_key_with_options_and_comment_should_return_key
|
79
|
+
File.expects(:read).with(@key_file).returns(public(rsa_key, 'environment="FOO=bar"') + " key_comment")
|
80
|
+
assert_equal rsa_key.to_blob, Net::SSH::KeyFactory.load_public_key(@key_file).to_blob
|
81
|
+
end
|
68
82
|
if defined?(OpenSSL::PKey::EC)
|
69
83
|
def test_load_unencrypted_private_ecdsa_sha2_nistp256_key_should_return_key
|
70
84
|
File.expects(:read).with("/key-file").returns(ecdsa_sha2_nistp256_key.to_pem)
|
@@ -165,8 +179,12 @@ WEKt5v3QsUEgVrzkM4K9UbI=
|
|
165
179
|
key.export(OpenSSL::Cipher::Cipher.new("des-ede3-cbc"), password)
|
166
180
|
end
|
167
181
|
|
168
|
-
def public(key)
|
169
|
-
result = "
|
182
|
+
def public(key, args = nil)
|
183
|
+
result = ""
|
184
|
+
if !args.nil?
|
185
|
+
result << "#{args} "
|
186
|
+
end
|
187
|
+
result << "#{key.ssh_type} "
|
170
188
|
result << [Net::SSH::Buffer.from(:key, key).to_s].pack("m*").strip.tr("\n\r\t ", "")
|
171
189
|
result << " joe@host.test"
|
172
190
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: net-ssh
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.8.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date:
|
13
|
+
date: 2014-02-01 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: test-unit
|
@@ -157,6 +157,9 @@ files:
|
|
157
157
|
- test/authentication/test_key_manager.rb
|
158
158
|
- test/authentication/test_session.rb
|
159
159
|
- test/common.rb
|
160
|
+
- test/configs/auth_off
|
161
|
+
- test/configs/auth_on
|
162
|
+
- test/configs/empty
|
160
163
|
- test/configs/eqsign
|
161
164
|
- test/configs/exact_match
|
162
165
|
- test/configs/host_plus
|
@@ -170,6 +173,7 @@ files:
|
|
170
173
|
- test/connection/test_session.rb
|
171
174
|
- test/known_hosts/github
|
172
175
|
- test/manual/test_forward.rb
|
176
|
+
- test/start/test_connection.rb
|
173
177
|
- test/start/test_options.rb
|
174
178
|
- test/start/test_transport.rb
|
175
179
|
- test/test_all.rb
|
@@ -224,7 +228,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
224
228
|
version: '0'
|
225
229
|
requirements: []
|
226
230
|
rubyforge_project: net-ssh
|
227
|
-
rubygems_version: 1.8.
|
231
|
+
rubygems_version: 1.8.23
|
228
232
|
signing_key:
|
229
233
|
specification_version: 3
|
230
234
|
summary: ! 'Net::SSH: a pure-Ruby implementation of the SSH2 client protocol.'
|