net-ssh 2.9.4 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/.travis.yml +12 -4
- data/CHANGES.txt +43 -5
- data/README.rdoc +8 -50
- data/Rakefile +13 -5
- data/lib/net/ssh/authentication/key_manager.rb +28 -19
- data/lib/net/ssh/authentication/methods/keyboard_interactive.rb +11 -4
- data/lib/net/ssh/authentication/methods/password.rb +3 -1
- data/lib/net/ssh/authentication/pageant.rb +42 -22
- data/lib/net/ssh/connection/channel.rb +7 -26
- data/lib/net/ssh/connection/session.rb +7 -14
- data/lib/net/ssh/known_hosts.rb +20 -1
- data/lib/net/ssh/proxy/command.rb +14 -3
- data/lib/net/ssh/proxy/http.rb +3 -2
- data/lib/net/ssh/proxy/socks4.rb +2 -1
- data/lib/net/ssh/proxy/socks5.rb +3 -2
- data/lib/net/ssh/ruby_compat.rb +0 -5
- data/lib/net/ssh/transport/algorithms.rb +3 -4
- data/lib/net/ssh/transport/cipher_factory.rb +0 -1
- data/lib/net/ssh/transport/server_version.rb +9 -3
- data/lib/net/ssh/transport/session.rb +11 -9
- data/lib/net/ssh/version.rb +4 -4
- data/lib/net/ssh.rb +17 -2
- data/net-ssh-public_cert.pem +15 -15
- data/net-ssh.gemspec +11 -4
- data/setup.rb +1 -1
- data/test/README.txt +6 -13
- data/test/authentication/methods/test_keyboard_interactive.rb +21 -0
- data/test/authentication/test_key_manager.rb +14 -1
- data/test/configs/proxy_remote_user +2 -0
- data/test/connection/test_channel.rb +0 -3
- data/test/connection/test_session.rb +15 -21
- data/test/integration/README.txt +19 -0
- data/test/integration/Vagrantfile +12 -0
- data/test/integration/common.rb +57 -0
- data/test/integration/playbook.yml +48 -0
- data/test/integration/test_id_rsa_keys.rb +84 -0
- data/test/known_hosts/github_hash +1 -0
- data/test/start/test_options.rb +15 -1
- data/test/test_all.rb +1 -0
- data/test/test_config.rb +6 -0
- data/test/test_known_hosts.rb +11 -3
- data/test/transport/kex/test_diffie_hellman_group1_sha1.rb +5 -1
- data/test/transport/kex/test_diffie_hellman_group_exchange_sha1.rb +5 -1
- data/test/transport/kex/test_diffie_hellman_group_exchange_sha256.rb +2 -17
- data/test/transport/test_algorithms.rb +21 -17
- data/test/transport/test_packet_stream.rb +1 -0
- data/test/transport/test_session.rb +1 -1
- data.tar.gz.sig +0 -0
- metadata +25 -19
- metadata.gz.sig +0 -0
- data/Rudyfile +0 -96
data/lib/net/ssh/known_hosts.rb
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
require 'strscan'
|
|
2
|
+
require 'openssl'
|
|
3
|
+
require 'base64'
|
|
2
4
|
require 'net/ssh/buffer'
|
|
3
5
|
|
|
4
6
|
module Net; module SSH
|
|
@@ -111,7 +113,9 @@ module Net; module SSH
|
|
|
111
113
|
next if scanner.match?(/$|#/)
|
|
112
114
|
|
|
113
115
|
hostlist = scanner.scan(/\S+/).split(/,/)
|
|
114
|
-
|
|
116
|
+
found = entries.all? { |entry| hostlist.include?(entry) } ||
|
|
117
|
+
known_host_hash?(hostlist, entries, scanner)
|
|
118
|
+
next unless found
|
|
115
119
|
|
|
116
120
|
scanner.skip(/\s*/)
|
|
117
121
|
type = scanner.scan(/\S+/)
|
|
@@ -127,6 +131,21 @@ module Net; module SSH
|
|
|
127
131
|
keys
|
|
128
132
|
end
|
|
129
133
|
|
|
134
|
+
# Indicates whether one of the entries matches an hostname that has been
|
|
135
|
+
# stored as a HMAC-SHA1 hash in the known hosts.
|
|
136
|
+
def known_host_hash?(hostlist, entries, scanner)
|
|
137
|
+
if hostlist.size == 1 && hostlist.first =~ /\A\|1(\|.+){2}\z/
|
|
138
|
+
chunks = hostlist.first.split(/\|/)
|
|
139
|
+
salt = Base64.decode64(chunks[2])
|
|
140
|
+
digest = OpenSSL::Digest.new('sha1')
|
|
141
|
+
entries.each do |entry|
|
|
142
|
+
hmac = OpenSSL::HMAC.digest(digest, salt, entry)
|
|
143
|
+
return true if Base64.encode64(hmac).chomp == chunks[3]
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
false
|
|
147
|
+
end
|
|
148
|
+
|
|
130
149
|
# Tries to append an entry to the current source file for the given host
|
|
131
150
|
# and key. If it is unable to (because the file is not writable, for
|
|
132
151
|
# instance), an exception will be raised.
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
require 'socket'
|
|
2
|
+
require 'rubygems'
|
|
2
3
|
require 'net/ssh/proxy/errors'
|
|
3
4
|
require 'net/ssh/ruby_compat'
|
|
4
5
|
|
|
@@ -66,12 +67,22 @@ module Net; module SSH; module Proxy
|
|
|
66
67
|
raise ConnectError, "#{e}: #{command_line}"
|
|
67
68
|
end
|
|
68
69
|
@command_line = command_line
|
|
69
|
-
|
|
70
|
-
|
|
70
|
+
if Gem.win_platform?
|
|
71
|
+
# read_nonblock and write_nonblock are not available on Windows
|
|
72
|
+
# pipe. Use sysread and syswrite as a replacement works.
|
|
73
|
+
def io.send(data, flag)
|
|
74
|
+
syswrite(data)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def io.recv(size)
|
|
78
|
+
sysread(size)
|
|
79
|
+
end
|
|
80
|
+
else
|
|
81
|
+
def io.send(data, flag)
|
|
71
82
|
write_nonblock(data)
|
|
72
83
|
end
|
|
73
84
|
|
|
74
|
-
def recv(size)
|
|
85
|
+
def io.recv(size)
|
|
75
86
|
read_nonblock(size)
|
|
76
87
|
end
|
|
77
88
|
end
|
data/lib/net/ssh/proxy/http.rb
CHANGED
|
@@ -48,8 +48,9 @@ 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, connection_options
|
|
52
|
-
socket =
|
|
51
|
+
def open(host, port, connection_options)
|
|
52
|
+
socket = Socket.tcp(proxy_host, proxy_port, nil, nil,
|
|
53
|
+
connect_timeout: connection_options[:timeout])
|
|
53
54
|
socket.write "CONNECT #{host}:#{port} HTTP/1.0\r\n"
|
|
54
55
|
|
|
55
56
|
if options[:user]
|
data/lib/net/ssh/proxy/socks4.rb
CHANGED
|
@@ -48,7 +48,8 @@ module Net
|
|
|
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
50
|
def open(host, port, connection_options)
|
|
51
|
-
socket =
|
|
51
|
+
socket = Socket.tcp(proxy_host, proxy_port, nil, nil,
|
|
52
|
+
connect_timeout: connection_options[:timeout])
|
|
52
53
|
ip_addr = IPAddr.new(Resolv.getaddress(host))
|
|
53
54
|
|
|
54
55
|
packet = [VERSION, CONNECT, port.to_i, ip_addr.to_i, options[:user]].pack("CCnNZ*")
|
data/lib/net/ssh/proxy/socks5.rb
CHANGED
|
@@ -62,8 +62,9 @@ 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, connection_options
|
|
66
|
-
socket =
|
|
65
|
+
def open(host, port, connection_options)
|
|
66
|
+
socket = Socket.tcp(proxy_host, proxy_port, nil, nil,
|
|
67
|
+
connect_timeout: connection_options[:timeout])
|
|
67
68
|
|
|
68
69
|
methods = [METHOD_NO_AUTH]
|
|
69
70
|
methods << METHOD_PASSWD if options[:user]
|
data/lib/net/ssh/ruby_compat.rb
CHANGED
|
@@ -224,10 +224,9 @@ module Net; module SSH; module Transport
|
|
|
224
224
|
end
|
|
225
225
|
lwarn { "unsupported #{algorithm} algorithm: `#{unsupported}'" } unless unsupported.empty?
|
|
226
226
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
list.each { |name| algorithms[algorithm] << name unless algorithms[algorithm].include?(name) }
|
|
227
|
+
if options[:append_all_supported_algorithms]
|
|
228
|
+
list.each { |name| algorithms[algorithm] << name unless algorithms[algorithm].include?(name) }
|
|
229
|
+
end
|
|
231
230
|
end
|
|
232
231
|
end
|
|
233
232
|
|
|
@@ -76,7 +76,6 @@ module Net; module SSH; module Transport
|
|
|
76
76
|
cipher.padding = 0
|
|
77
77
|
|
|
78
78
|
cipher.extend(Net::SSH::Transport::CTR) if (name =~ /-ctr(@openssh.org)?$/)
|
|
79
|
-
|
|
80
79
|
cipher.iv = Net::SSH::Transport::KeyExpander.expand_key(cipher.iv_len, options[:iv], options) if ossl_name != "rc4"
|
|
81
80
|
|
|
82
81
|
key_len = KEY_LEN_OVERRIDE[name] || cipher.key_len
|
|
@@ -25,11 +25,11 @@ module Net; module SSH; module Transport
|
|
|
25
25
|
|
|
26
26
|
# Instantiates a new ServerVersion and immediately (and synchronously)
|
|
27
27
|
# negotiates the SSH protocol in effect, using the given socket.
|
|
28
|
-
def initialize(socket, logger)
|
|
28
|
+
def initialize(socket, logger, timeout = nil)
|
|
29
29
|
@header = ""
|
|
30
30
|
@version = nil
|
|
31
31
|
@logger = logger
|
|
32
|
-
negotiate!(socket)
|
|
32
|
+
negotiate!(socket, timeout)
|
|
33
33
|
end
|
|
34
34
|
|
|
35
35
|
private
|
|
@@ -37,9 +37,12 @@ module Net; module SSH; module Transport
|
|
|
37
37
|
# Negotiates the SSH protocol to use, via the given socket. If the server
|
|
38
38
|
# reports an incompatible SSH version (e.g., SSH1), this will raise an
|
|
39
39
|
# exception.
|
|
40
|
-
def negotiate!(socket)
|
|
40
|
+
def negotiate!(socket, timeout)
|
|
41
41
|
info { "negotiating protocol version" }
|
|
42
42
|
|
|
43
|
+
if timeout && !IO.select([socket], nil, nil, timeout)
|
|
44
|
+
raise Net::SSH::ConnectionTimeout, "timeout during server version negotiating"
|
|
45
|
+
end
|
|
43
46
|
loop do
|
|
44
47
|
@version = ""
|
|
45
48
|
loop do
|
|
@@ -63,6 +66,9 @@ module Net; module SSH; module Transport
|
|
|
63
66
|
raise Net::SSH::Exception, "incompatible SSH version `#{@version}'"
|
|
64
67
|
end
|
|
65
68
|
|
|
69
|
+
if timeout && !IO.select(nil, [socket], nil, timeout)
|
|
70
|
+
raise Net::SSH::ConnectionTimeout, "timeout during client version negotiating"
|
|
71
|
+
end
|
|
66
72
|
debug { "local is `#{PROTO_VERSION}'" }
|
|
67
73
|
socket.write "#{PROTO_VERSION}\r\n"
|
|
68
74
|
socket.flush
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
require 'socket'
|
|
2
|
-
require 'timeout'
|
|
3
2
|
|
|
4
3
|
require 'net/ssh/errors'
|
|
5
4
|
require 'net/ssh/loggable'
|
|
@@ -63,14 +62,15 @@ module Net; module SSH; module Transport
|
|
|
63
62
|
@options = options
|
|
64
63
|
|
|
65
64
|
debug { "establishing connection to #{@host}:#{@port}" }
|
|
66
|
-
|
|
67
|
-
@socket =
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
65
|
+
|
|
66
|
+
@socket =
|
|
67
|
+
if (factory = options[:proxy])
|
|
68
|
+
factory.open(@host, @port, options)
|
|
69
|
+
else
|
|
70
|
+
Socket.tcp(@host, @port, @bind_address, nil,
|
|
71
|
+
connect_timeout: options[:timeout])
|
|
72
72
|
end
|
|
73
|
-
|
|
73
|
+
|
|
74
74
|
@socket.extend(PacketStream)
|
|
75
75
|
@socket.logger = @logger
|
|
76
76
|
|
|
@@ -81,10 +81,12 @@ module Net; module SSH; module Transport
|
|
|
81
81
|
@host_key_verifier = select_host_key_verifier(options[:paranoid])
|
|
82
82
|
|
|
83
83
|
|
|
84
|
-
@server_version =
|
|
84
|
+
@server_version = ServerVersion.new(socket, logger, options[:timeout])
|
|
85
85
|
|
|
86
86
|
@algorithms = Algorithms.new(self, options)
|
|
87
87
|
wait { algorithms.initialized? }
|
|
88
|
+
rescue Errno::ETIMEDOUT
|
|
89
|
+
raise Net::SSH::ConnectionTimeout
|
|
88
90
|
end
|
|
89
91
|
|
|
90
92
|
# Returns the host (and possibly IP address) in a format compatible with
|
data/lib/net/ssh/version.rb
CHANGED
|
@@ -45,15 +45,15 @@ module Net; module SSH
|
|
|
45
45
|
end
|
|
46
46
|
|
|
47
47
|
# The major component of this version of the Net::SSH library
|
|
48
|
-
MAJOR =
|
|
48
|
+
MAJOR = 3
|
|
49
49
|
|
|
50
50
|
# The minor component of this version of the Net::SSH library
|
|
51
|
-
MINOR =
|
|
51
|
+
MINOR = 0
|
|
52
52
|
|
|
53
53
|
# The tiny component of this version of the Net::SSH library
|
|
54
|
-
TINY =
|
|
54
|
+
TINY = 1
|
|
55
55
|
|
|
56
|
-
# The prerelease component of this version of the Net::SSH library
|
|
56
|
+
# The prerelease component of this version of the Net::SSH library
|
|
57
57
|
# nil allowed
|
|
58
58
|
PRE = nil
|
|
59
59
|
|
data/lib/net/ssh.rb
CHANGED
|
@@ -62,13 +62,14 @@ module Net
|
|
|
62
62
|
# Net::SSH.start for a description of each option.
|
|
63
63
|
VALID_OPTIONS = [
|
|
64
64
|
:auth_methods, :bind_address, :compression, :compression_level, :config,
|
|
65
|
-
:encryption, :forward_agent, :hmac, :host_key,
|
|
65
|
+
:encryption, :forward_agent, :hmac, :host_key, :remote_user,
|
|
66
66
|
:keepalive, :keepalive_interval, :keepalive_maxcount, :kex, :keys, :key_data,
|
|
67
67
|
:languages, :logger, :paranoid, :password, :port, :proxy,
|
|
68
68
|
:rekey_blocks_limit,:rekey_limit, :rekey_packet_limit, :timeout, :verbose,
|
|
69
69
|
:global_known_hosts_file, :user_known_hosts_file, :host_key_alias,
|
|
70
70
|
:host_name, :user, :properties, :passphrase, :keys_only, :max_pkt_size,
|
|
71
|
-
:max_win_size, :send_env, :use_agent, :number_of_password_prompts
|
|
71
|
+
:max_win_size, :send_env, :use_agent, :number_of_password_prompts,
|
|
72
|
+
:append_supported_algorithms, :non_interactive
|
|
72
73
|
]
|
|
73
74
|
|
|
74
75
|
# The standard means of starting a new SSH connection. When used with a
|
|
@@ -168,15 +169,25 @@ module Net
|
|
|
168
169
|
# * :user => the user name to log in as; this overrides the +user+
|
|
169
170
|
# parameter, and is primarily only useful when provided via an SSH
|
|
170
171
|
# configuration file.
|
|
172
|
+
# * :remote_user => used for substitution into the '%r' part of a ProxyCommand
|
|
171
173
|
# * :user_known_hosts_file => the location of the user known hosts file.
|
|
172
174
|
# Set to an array to specify multiple user known hosts files.
|
|
173
175
|
# Defaults to %w(~/.ssh/known_hosts ~/.ssh/known_hosts2).
|
|
174
176
|
# * :use_agent => Set false to disable the use of ssh-agent. Defaults to
|
|
175
177
|
# true
|
|
178
|
+
# * :non_interactive => set to true if your app is non interactive and prefers
|
|
179
|
+
# authentication failure vs password prompt
|
|
176
180
|
# * :verbose => how verbose to be (Logger verbosity constants, Logger::DEBUG
|
|
177
181
|
# is very verbose, Logger::FATAL is all but silent). Logger::FATAL is the
|
|
178
182
|
# default. The symbols :debug, :info, :warn, :error, and :fatal are also
|
|
179
183
|
# supported and are translated to the corresponding Logger constant.
|
|
184
|
+
# * :append_all_supported_algorithms => set to +true+ to append all supported
|
|
185
|
+
# algorithms by net-ssh. Was the default behaviour until 2.10
|
|
186
|
+
# * :number_of_password_prompts => Number of prompts for the password
|
|
187
|
+
# authentication method defaults to 3 set to 0 to disable prompt for
|
|
188
|
+
# password auth method
|
|
189
|
+
# * :non_interactive => non interactive applications should set it to true
|
|
190
|
+
# to prefer failing a password/etc auth methods vs asking for password
|
|
180
191
|
def self.start(host, user, options={}, &block)
|
|
181
192
|
invalid_options = options.keys - VALID_OPTIONS
|
|
182
193
|
if invalid_options.any?
|
|
@@ -192,6 +203,10 @@ module Net
|
|
|
192
203
|
options[:logger].level = Logger::FATAL
|
|
193
204
|
end
|
|
194
205
|
|
|
206
|
+
if options[:non_interactive]
|
|
207
|
+
options[:number_of_password_prompts] = 0
|
|
208
|
+
end
|
|
209
|
+
|
|
195
210
|
if options[:verbose]
|
|
196
211
|
options[:logger].level = case options[:verbose]
|
|
197
212
|
when Fixnum then options[:verbose]
|
data/net-ssh-public_cert.pem
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
-----BEGIN CERTIFICATE-----
|
|
2
2
|
MIIDODCCAiCgAwIBAgIBADANBgkqhkiG9w0BAQUFADBCMRAwDgYDVQQDDAduZXQt
|
|
3
3
|
c3NoMRkwFwYKCZImiZPyLGQBGRYJc29sdXRpb3VzMRMwEQYKCZImiZPyLGQBGRYD
|
|
4
|
-
|
|
4
|
+
Y29tMB4XDTE0MTIwMjE3MzkyMFoXDTE1MTIwMjE3MzkyMFowQjEQMA4GA1UEAwwH
|
|
5
5
|
bmV0LXNzaDEZMBcGCgmSJomT8ixkARkWCXNvbHV0aW91czETMBEGCgmSJomT8ixk
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
6
|
+
ARkWA2NvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJ0qnw4JV5JN
|
|
7
|
+
MWelqu7pnW2z6GZJ7+zLFYJQNETJyF0U5zo7aCRK08OeUxnpu/TCCXK8iQVkNLfz
|
|
8
|
+
9pVIhF+X8pMEIruAkYGwBt1aWfuSNeyodyMk0vpZdxBHbOTJ4qBRUc6qOtNOeOzv
|
|
9
|
+
8ObYUX52P/EMMaeXTRU+e7MGkB9pb6FvPPNx5akxwIaoRvtcMsc/hJnQuP5r96w6
|
|
10
|
+
t06MgKbXhWAX6gev0RVlrQqzxXst6iuvsrgZGjFqzob5wbTiX9M0+bFAB0EI7tJC
|
|
11
|
+
sv5keEbtNRaU7p3ZbMm4wTHHJLOtD+BpUCSzwv4ToNj9mZtJBMYw2Eeo7z1DklEG
|
|
12
|
+
mr95zbe+zNMCAwEAAaM5MDcwCQYDVR0TBAIwADAdBgNVHQ4EFgQU1bTfpzmitXwv
|
|
13
|
+
LmTXi0IO5vd8NGYwCwYDVR0PBAQDAgSwMA0GCSqGSIb3DQEBBQUAA4IBAQA0Aps8
|
|
14
|
+
UPINGa8XUUtrZtzrgX0/iyXNkKY1ld85g1N3WKEAVLfQI7TlGr0Qv2Ekx6RqlxbR
|
|
15
|
+
Vyq08pytSnghW2otR3bIGMGQzqxAeRLb25cjEwH7YIJ32n7ZC1fpMnBZOBDmueWA
|
|
16
|
+
B9EonmoO3ne7AJSgIvBbZzBPhzM4HrQGRW8LsPFsuj+dcJI43HOQwkmv2TRz0+t6
|
|
17
|
+
mGZldmqLcK0abv4JepLfB9XTue3kuyA29NGBibqyvRwlKckLpvKfHZX6Jxad8xxm
|
|
18
|
+
MbvRpzgROzyfw1qYi4dnIyMwTtXFFcZ0a2jpxHPkcTYFK6TzvFgDLAP0Y/u9jqUQ
|
|
19
|
+
eZ7/3CdSi/isZHEw
|
|
20
20
|
-----END CERTIFICATE-----
|
data/net-ssh.gemspec
CHANGED
|
@@ -2,17 +2,17 @@
|
|
|
2
2
|
# DO NOT EDIT THIS FILE DIRECTLY
|
|
3
3
|
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
|
4
4
|
# -*- encoding: utf-8 -*-
|
|
5
|
-
# stub: net-ssh
|
|
5
|
+
# stub: net-ssh 3.0.1 ruby lib
|
|
6
6
|
|
|
7
7
|
Gem::Specification.new do |s|
|
|
8
8
|
s.name = "net-ssh"
|
|
9
|
-
s.version = "
|
|
9
|
+
s.version = "3.0.1"
|
|
10
10
|
|
|
11
11
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
|
12
12
|
s.require_paths = ["lib"]
|
|
13
13
|
s.authors = ["Jamis Buck", "Delano Mandelbaum", "Mikl\u{f3}s Fazekas"]
|
|
14
14
|
s.cert_chain = ["net-ssh-public_cert.pem"]
|
|
15
|
-
s.date = "
|
|
15
|
+
s.date = "2015-09-25"
|
|
16
16
|
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."
|
|
17
17
|
s.email = "net-ssh@solutious.com"
|
|
18
18
|
s.extra_rdoc_files = [
|
|
@@ -26,7 +26,6 @@ Gem::Specification.new do |s|
|
|
|
26
26
|
"Manifest",
|
|
27
27
|
"README.rdoc",
|
|
28
28
|
"Rakefile",
|
|
29
|
-
"Rudyfile",
|
|
30
29
|
"THANKS.txt",
|
|
31
30
|
"lib/net/ssh.rb",
|
|
32
31
|
"lib/net/ssh/authentication/agent.rb",
|
|
@@ -135,12 +134,19 @@ Gem::Specification.new do |s|
|
|
|
135
134
|
"test/configs/negative_match",
|
|
136
135
|
"test/configs/nohost",
|
|
137
136
|
"test/configs/numeric_host",
|
|
137
|
+
"test/configs/proxy_remote_user",
|
|
138
138
|
"test/configs/send_env",
|
|
139
139
|
"test/configs/substitutes",
|
|
140
140
|
"test/configs/wild_cards",
|
|
141
141
|
"test/connection/test_channel.rb",
|
|
142
142
|
"test/connection/test_session.rb",
|
|
143
|
+
"test/integration/README.txt",
|
|
144
|
+
"test/integration/Vagrantfile",
|
|
145
|
+
"test/integration/common.rb",
|
|
146
|
+
"test/integration/playbook.yml",
|
|
147
|
+
"test/integration/test_id_rsa_keys.rb",
|
|
143
148
|
"test/known_hosts/github",
|
|
149
|
+
"test/known_hosts/github_hash",
|
|
144
150
|
"test/manual/test_forward.rb",
|
|
145
151
|
"test/manual/test_pageant.rb",
|
|
146
152
|
"test/start/test_connection.rb",
|
|
@@ -180,6 +186,7 @@ Gem::Specification.new do |s|
|
|
|
180
186
|
]
|
|
181
187
|
s.homepage = "https://github.com/net-ssh/net-ssh"
|
|
182
188
|
s.licenses = ["MIT"]
|
|
189
|
+
s.required_ruby_version = Gem::Requirement.new(">= 2.0")
|
|
183
190
|
s.rubyforge_project = "net-ssh"
|
|
184
191
|
s.rubygems_version = "2.4.6"
|
|
185
192
|
s.signing_key = "/mnt/gem/net-ssh-private_key.pem"
|
data/setup.rb
CHANGED
|
@@ -1460,7 +1460,7 @@ class Installer
|
|
|
1460
1460
|
begin
|
|
1461
1461
|
require 'test/unit'
|
|
1462
1462
|
rescue LoadError
|
|
1463
|
-
setup_rb_error 'test/unit cannot loaded. You need Ruby 1.
|
|
1463
|
+
setup_rb_error 'test/unit cannot loaded. You need Ruby 1.9 or later to invoke this task.'
|
|
1464
1464
|
end
|
|
1465
1465
|
runner = Test::Unit::AutoRunner.new(true)
|
|
1466
1466
|
runner.to_run << TESTDIR
|
data/test/README.txt
CHANGED
|
@@ -1,28 +1,21 @@
|
|
|
1
|
-
2011-01-19
|
|
2
|
-
|
|
3
1
|
RUNNING TESTS
|
|
4
2
|
|
|
5
3
|
Run the test suite from the net-ssh directory with the following command:
|
|
6
4
|
|
|
7
|
-
ruby -Ilib -Itest
|
|
5
|
+
ruby -Ilib -Itest test/test_all.rb
|
|
8
6
|
|
|
9
7
|
Run a single test file like this:
|
|
10
8
|
|
|
11
|
-
ruby -Ilib -Itest
|
|
12
|
-
|
|
9
|
+
ruby -Ilib -Itest test/transport/test_server_version.rb
|
|
13
10
|
|
|
14
11
|
EXPECTED RESULTS
|
|
15
12
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
* Ruby 1.8: all tests pass (up until version 2.5)
|
|
19
|
-
|
|
20
|
-
* JRuby 1.7: 98% test pass (510 tests, 1914 assertions, 2 failures, 9 errors)
|
|
21
|
-
|
|
22
|
-
* JRuby 1.6: 98% test pass (510 tests, 1914 assertions, 4 failures, 5 errors)
|
|
13
|
+
https://travis-ci.org/net-ssh/net-ssh/
|
|
23
14
|
|
|
24
|
-
|
|
15
|
+
INTEGRATION TESTS
|
|
25
16
|
|
|
17
|
+
brew install ansible ; ansible-galaxy install rvm_io.rvm1-ruby ; vagrant up ; vagrant ssh
|
|
18
|
+
cd /net-ssh ; rake integration-test
|
|
26
19
|
|
|
27
20
|
PORT FORWARDING TESTS
|
|
28
21
|
|
|
@@ -71,6 +71,27 @@ module Authentication; module Methods
|
|
|
71
71
|
assert subject.authenticate("ssh-connection", "jamis", "the-password")
|
|
72
72
|
end
|
|
73
73
|
|
|
74
|
+
def test_authenticate_should_not_prompt_for_input_when_in_non_interactive_mode
|
|
75
|
+
|
|
76
|
+
def transport.options
|
|
77
|
+
{non_interactive: true}
|
|
78
|
+
end
|
|
79
|
+
transport.expect do |t,packet|
|
|
80
|
+
assert_equal USERAUTH_REQUEST, packet.type
|
|
81
|
+
t.return(USERAUTH_INFO_REQUEST, :string, "", :string, "", :string, "", :long, 2, :string, "Name:", :bool, true, :string, "Password:", :bool, false)
|
|
82
|
+
t.expect do |t2,packet2|
|
|
83
|
+
assert_equal USERAUTH_INFO_RESPONSE, packet2.type
|
|
84
|
+
assert_equal 2, packet2.read_long
|
|
85
|
+
assert_equal "", packet2.read_string
|
|
86
|
+
assert_equal "", packet2.read_string
|
|
87
|
+
t2.return(USERAUTH_SUCCESS)
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
assert subject.authenticate("ssh-connection", "jamis", nil)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
|
|
74
95
|
def test_authenticate_should_prompt_for_input_when_password_is_not_given
|
|
75
96
|
subject.expects(:prompt).with("Name:", true).returns("name")
|
|
76
97
|
subject.expects(:prompt).with("Password:", false).returns("password")
|
|
@@ -56,6 +56,15 @@ module Authentication
|
|
|
56
56
|
assert_equal({:from => :file, :file => second, :key => dsa}, manager.known_identities[dsa])
|
|
57
57
|
end
|
|
58
58
|
|
|
59
|
+
def test_each_identity_should_not_prompt_for_passphrase_in_non_interactive_mode
|
|
60
|
+
manager(:non_interactive => true).stubs(:agent).returns(nil)
|
|
61
|
+
first = File.expand_path("/first")
|
|
62
|
+
stub_file_private_key first, rsa, :passphrase => :should_not_be_asked
|
|
63
|
+
identities = []
|
|
64
|
+
manager.each_identity { |identity| identities << identity }
|
|
65
|
+
assert_equal(identities, [])
|
|
66
|
+
end
|
|
67
|
+
|
|
59
68
|
def test_identities_should_load_from_agent
|
|
60
69
|
manager.stubs(:agent).returns(agent)
|
|
61
70
|
|
|
@@ -156,7 +165,9 @@ module Authentication
|
|
|
156
165
|
|
|
157
166
|
def stub_file_private_key(name, key, options = {})
|
|
158
167
|
manager.add(name)
|
|
168
|
+
File.stubs(:file?).with(name).returns(true)
|
|
159
169
|
File.stubs(:readable?).with(name).returns(true)
|
|
170
|
+
File.stubs(:file?).with(name + ".pub").returns(true)
|
|
160
171
|
File.stubs(:readable?).with(name + ".pub").returns(false)
|
|
161
172
|
|
|
162
173
|
case options.fetch(:passphrase, :indifferently)
|
|
@@ -179,7 +190,9 @@ module Authentication
|
|
|
179
190
|
|
|
180
191
|
def stub_file_public_key(name, key)
|
|
181
192
|
manager.add(name)
|
|
182
|
-
File.stubs(:
|
|
193
|
+
File.stubs(:file?).with(name).returns(true)
|
|
194
|
+
File.stubs(:readable?).with(name).returns(true)
|
|
195
|
+
File.stubs(:file?).with(name + ".pub").returns(true)
|
|
183
196
|
File.stubs(:readable?).with(name + ".pub").returns(true)
|
|
184
197
|
|
|
185
198
|
Net::SSH::KeyFactory.expects(:load_public_key).with(name + ".pub").returns(key).at_least_once
|
|
@@ -74,11 +74,8 @@ module Connection
|
|
|
74
74
|
assert !channel.closing?
|
|
75
75
|
|
|
76
76
|
connection.expect { |t,packet| assert_equal CHANNEL_CLOSE, packet.type }
|
|
77
|
-
connection.expects(:cleanup_channel).with(channel)
|
|
78
77
|
channel.close
|
|
79
78
|
|
|
80
|
-
channel.process
|
|
81
|
-
|
|
82
79
|
assert channel.closing?
|
|
83
80
|
end
|
|
84
81
|
|
|
@@ -117,14 +117,14 @@ module Connection
|
|
|
117
117
|
end
|
|
118
118
|
|
|
119
119
|
def test_process_should_exit_after_processing_if_block_is_true_then_false
|
|
120
|
-
session.channels[0] = stub("channel", :
|
|
120
|
+
session.channels[0] = stub("channel", :closing? => false)
|
|
121
121
|
session.channels[0].expects(:process)
|
|
122
122
|
IO.expects(:select).never
|
|
123
123
|
process_times(2)
|
|
124
124
|
end
|
|
125
125
|
|
|
126
126
|
def test_process_should_not_process_channels_that_are_closing
|
|
127
|
-
session.channels[0] = stub("channel", :
|
|
127
|
+
session.channels[0] = stub("channel", :closing? => true)
|
|
128
128
|
session.channels[0].expects(:process).never
|
|
129
129
|
IO.expects(:select).never
|
|
130
130
|
process_times(2)
|
|
@@ -299,17 +299,8 @@ module Connection
|
|
|
299
299
|
end
|
|
300
300
|
|
|
301
301
|
def test_channel_close_packet_should_be_routed_to_corresponding_channel_and_channel_should_be_closed_and_removed
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
# CHANNEL_CLOSE to server and are waiting for server's response.
|
|
305
|
-
expects(:local_closed?).returns(true)
|
|
306
|
-
expects(:do_close)
|
|
307
|
-
expects(:close).with()
|
|
308
|
-
expects(:remote_closed!).with()
|
|
309
|
-
expects(:remote_closed?).with().returns(true)
|
|
310
|
-
expects(:local_id).returns(14)
|
|
311
|
-
end
|
|
312
|
-
|
|
302
|
+
channel_at(14).expects(:do_close).with()
|
|
303
|
+
session.channels[14].expects(:close).with()
|
|
313
304
|
transport.return(CHANNEL_CLOSE, :long, 14)
|
|
314
305
|
process_times(2)
|
|
315
306
|
assert session.channels.empty?
|
|
@@ -386,13 +377,13 @@ module Connection
|
|
|
386
377
|
options = { :keepalive => true, :keepalive_interval => 300, :keepalive_maxcount => 3 }
|
|
387
378
|
expected_packet = P(:byte, Net::SSH::Packet::GLOBAL_REQUEST, :string, "keepalive@openssh.com", :bool, true)
|
|
388
379
|
[1,2,3].each do |i|
|
|
389
|
-
Time.stubs(:now).returns(i*300)
|
|
380
|
+
Time.stubs(:now).returns(Time.at(i*300))
|
|
390
381
|
IO.stubs(:select).with([socket],[],nil,timeout).returns(nil)
|
|
391
|
-
transport.expects(:enqueue_message).with{ |msg| msg.content == expected_packet.content
|
|
382
|
+
transport.expects(:enqueue_message).with{ |msg| msg.content == expected_packet.content }
|
|
392
383
|
session(options).process
|
|
393
384
|
end
|
|
394
385
|
|
|
395
|
-
Time.stubs(:now).returns(4*300)
|
|
386
|
+
Time.stubs(:now).returns(Time.at(4*300))
|
|
396
387
|
IO.stubs(:select).with([socket],[],nil,timeout).returns(nil)
|
|
397
388
|
transport.expects(:enqueue_message).with{ |msg| msg.content == expected_packet.content }
|
|
398
389
|
assert_raises(Net::SSH::Timeout) { session(options).process }
|
|
@@ -482,6 +473,11 @@ module Connection
|
|
|
482
473
|
assert_equal "some data", session.exec!("ls")
|
|
483
474
|
end
|
|
484
475
|
|
|
476
|
+
def test_exec_bang_without_block_should_return_empty_string_for_empty_command_output
|
|
477
|
+
prep_exec('ls', :stdout, '')
|
|
478
|
+
assert_equal "", session.exec!('ls')
|
|
479
|
+
end
|
|
480
|
+
|
|
485
481
|
private
|
|
486
482
|
|
|
487
483
|
def prep_exec(command, *data)
|
|
@@ -497,10 +493,8 @@ module Connection
|
|
|
497
493
|
|
|
498
494
|
t2.return(CHANNEL_SUCCESS, :long, p[:remote_id])
|
|
499
495
|
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
datum = data[index+1]
|
|
503
|
-
|
|
496
|
+
data.each_slice(2) do |type, datum|
|
|
497
|
+
next if datum.empty?
|
|
504
498
|
if type == :stdout
|
|
505
499
|
t2.return(CHANNEL_DATA, :long, p[:remote_id], :string, datum)
|
|
506
500
|
else
|
|
@@ -532,7 +526,7 @@ module Connection
|
|
|
532
526
|
end
|
|
533
527
|
|
|
534
528
|
def channel_at(local_id)
|
|
535
|
-
session.channels[local_id] = stub("channel", :process => true, :
|
|
529
|
+
session.channels[local_id] = stub("channel", :process => true, :closing? => false)
|
|
536
530
|
end
|
|
537
531
|
|
|
538
532
|
def transport(options={})
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Integration tests with vagrant
|
|
2
|
+
|
|
3
|
+
Requirements:
|
|
4
|
+
|
|
5
|
+
* Vagrant (https://www.vagrantup.com/)
|
|
6
|
+
* Ansible (http://docs.ansible.com/intro_installation.html)
|
|
7
|
+
|
|
8
|
+
Setup:
|
|
9
|
+
|
|
10
|
+
ansible-galaxy install rvm_io.rvm1-ruby
|
|
11
|
+
vagrant up
|
|
12
|
+
vagrant ssh
|
|
13
|
+
cd /net-ssh
|
|
14
|
+
rake integration-test
|
|
15
|
+
|
|
16
|
+
# TODO
|
|
17
|
+
|
|
18
|
+
* get it running on ci (probalby needs docker)
|
|
19
|
+
* could not get gem install jeweler to work
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
VAGRANTFILE_API_VERSION = "2"
|
|
2
|
+
|
|
3
|
+
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
|
|
4
|
+
config.vm.box = "ubuntu/trusty64"
|
|
5
|
+
config.vm.provision "ansible" do |ansible|
|
|
6
|
+
ansible.playbook = "./playbook.yml"
|
|
7
|
+
ansible.sudo = true
|
|
8
|
+
ansible.verbose ='vvvv'
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
config.vm.synced_folder "../..", "/net-ssh"
|
|
12
|
+
end
|