net-ssh 5.0.0.beta1 → 5.0.0.beta2
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.tar.gz.sig +0 -0
- data/.rubocop_todo.yml +98 -258
- data/CHANGES.txt +8 -0
- data/Gemfile +1 -3
- data/Rakefile +37 -39
- data/lib/net/ssh.rb +26 -25
- data/lib/net/ssh/authentication/agent.rb +228 -225
- data/lib/net/ssh/authentication/certificate.rb +166 -164
- data/lib/net/ssh/authentication/constants.rb +17 -14
- data/lib/net/ssh/authentication/ed25519.rb +107 -104
- data/lib/net/ssh/authentication/ed25519_loader.rb +32 -28
- data/lib/net/ssh/authentication/key_manager.rb +5 -3
- data/lib/net/ssh/authentication/methods/abstract.rb +53 -47
- data/lib/net/ssh/authentication/methods/hostbased.rb +32 -33
- data/lib/net/ssh/authentication/methods/keyboard_interactive.rb +2 -4
- data/lib/net/ssh/authentication/methods/none.rb +10 -10
- data/lib/net/ssh/authentication/methods/password.rb +13 -13
- data/lib/net/ssh/authentication/methods/publickey.rb +54 -55
- data/lib/net/ssh/authentication/pageant.rb +468 -465
- data/lib/net/ssh/authentication/pub_key_fingerprint.rb +44 -0
- data/lib/net/ssh/authentication/session.rb +127 -123
- data/lib/net/ssh/buffer.rb +305 -303
- data/lib/net/ssh/buffered_io.rb +163 -162
- data/lib/net/ssh/config.rb +230 -227
- data/lib/net/ssh/connection/channel.rb +659 -654
- data/lib/net/ssh/connection/constants.rb +30 -26
- data/lib/net/ssh/connection/event_loop.rb +108 -104
- data/lib/net/ssh/connection/keepalive.rb +54 -50
- data/lib/net/ssh/connection/session.rb +677 -678
- data/lib/net/ssh/connection/term.rb +180 -176
- data/lib/net/ssh/errors.rb +101 -99
- data/lib/net/ssh/key_factory.rb +108 -108
- data/lib/net/ssh/known_hosts.rb +148 -154
- data/lib/net/ssh/loggable.rb +56 -54
- data/lib/net/ssh/packet.rb +82 -78
- data/lib/net/ssh/prompt.rb +55 -53
- data/lib/net/ssh/proxy/command.rb +103 -102
- data/lib/net/ssh/proxy/errors.rb +12 -8
- data/lib/net/ssh/proxy/http.rb +92 -91
- data/lib/net/ssh/proxy/https.rb +42 -39
- data/lib/net/ssh/proxy/jump.rb +50 -47
- data/lib/net/ssh/proxy/socks4.rb +0 -2
- data/lib/net/ssh/proxy/socks5.rb +11 -11
- data/lib/net/ssh/ruby_compat.rb +1 -0
- data/lib/net/ssh/service/forward.rb +364 -362
- data/lib/net/ssh/test.rb +85 -83
- data/lib/net/ssh/test/channel.rb +146 -142
- data/lib/net/ssh/test/extensions.rb +148 -146
- data/lib/net/ssh/test/kex.rb +35 -31
- data/lib/net/ssh/test/local_packet.rb +48 -44
- data/lib/net/ssh/test/packet.rb +87 -84
- data/lib/net/ssh/test/remote_packet.rb +35 -31
- data/lib/net/ssh/test/script.rb +173 -171
- data/lib/net/ssh/test/socket.rb +59 -55
- data/lib/net/ssh/transport/algorithms.rb +413 -412
- data/lib/net/ssh/transport/cipher_factory.rb +108 -105
- data/lib/net/ssh/transport/constants.rb +35 -31
- data/lib/net/ssh/transport/ctr.rb +1 -1
- data/lib/net/ssh/transport/hmac.rb +1 -1
- data/lib/net/ssh/transport/hmac/abstract.rb +67 -64
- data/lib/net/ssh/transport/hmac/sha2_256_96.rb +1 -1
- data/lib/net/ssh/transport/hmac/sha2_512_96.rb +1 -1
- data/lib/net/ssh/transport/identity_cipher.rb +55 -51
- data/lib/net/ssh/transport/kex.rb +2 -4
- data/lib/net/ssh/transport/kex/diffie_hellman_group14_sha1.rb +47 -40
- data/lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb +201 -197
- data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha1.rb +53 -56
- data/lib/net/ssh/transport/kex/ecdh_sha2_nistp256.rb +94 -87
- data/lib/net/ssh/transport/kex/ecdh_sha2_nistp384.rb +17 -10
- data/lib/net/ssh/transport/kex/ecdh_sha2_nistp521.rb +17 -10
- data/lib/net/ssh/transport/key_expander.rb +29 -25
- data/lib/net/ssh/transport/openssl.rb +17 -30
- data/lib/net/ssh/transport/packet_stream.rb +193 -192
- data/lib/net/ssh/transport/server_version.rb +64 -66
- data/lib/net/ssh/transport/session.rb +286 -284
- data/lib/net/ssh/transport/state.rb +198 -196
- data/lib/net/ssh/verifiers/lenient.rb +29 -25
- data/lib/net/ssh/verifiers/null.rb +13 -9
- data/lib/net/ssh/verifiers/secure.rb +45 -45
- data/lib/net/ssh/verifiers/strict.rb +20 -16
- data/lib/net/ssh/version.rb +55 -53
- data/net-ssh.gemspec +4 -4
- data/support/ssh_tunnel_bug.rb +2 -2
- metadata +25 -24
- metadata.gz.sig +0 -0
data/lib/net/ssh/proxy/errors.rb
CHANGED
@@ -1,14 +1,18 @@
|
|
1
1
|
require 'net/ssh/errors'
|
2
2
|
|
3
|
-
module Net
|
3
|
+
module Net
|
4
|
+
module SSH
|
5
|
+
module Proxy
|
4
6
|
|
5
|
-
|
6
|
-
|
7
|
+
# A general exception class for all Proxy errors.
|
8
|
+
class Error < Net::SSH::Exception; end
|
7
9
|
|
8
|
-
|
9
|
-
|
10
|
+
# Used for reporting proxy connection errors.
|
11
|
+
class ConnectError < Error; end
|
10
12
|
|
11
|
-
|
12
|
-
|
13
|
+
# Used when the server doesn't recognize the user's credentials.
|
14
|
+
class UnauthorizedError < Error; end
|
13
15
|
|
14
|
-
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/net/ssh/proxy/http.rb
CHANGED
@@ -1,98 +1,99 @@
|
|
1
1
|
require 'socket'
|
2
2
|
require 'net/ssh/proxy/errors'
|
3
3
|
|
4
|
-
module Net
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
# Return a new socket connected to the given host and port via the
|
50
|
-
# proxy that was requested when the socket factory was instantiated.
|
51
|
-
def open(host, port, connection_options)
|
52
|
-
socket = establish_connection(connection_options[:timeout])
|
53
|
-
socket.write "CONNECT #{host}:#{port} HTTP/1.0\r\n"
|
54
|
-
|
55
|
-
if options[:user]
|
56
|
-
credentials = ["#{options[:user]}:#{options[:password]}"].pack("m*").gsub(/\s/, "")
|
57
|
-
socket.write "Proxy-Authorization: Basic #{credentials}\r\n"
|
58
|
-
end
|
59
|
-
|
60
|
-
socket.write "\r\n"
|
61
|
-
|
62
|
-
resp = parse_response(socket)
|
63
|
-
|
64
|
-
return socket if resp[:code] == 200
|
65
|
-
|
66
|
-
socket.close
|
67
|
-
raise ConnectError, resp.inspect
|
68
|
-
end
|
69
|
-
|
70
|
-
protected
|
71
|
-
|
72
|
-
def establish_connection(connect_timeout)
|
73
|
-
Socket.tcp(proxy_host, proxy_port, nil, nil,
|
74
|
-
connect_timeout: connect_timeout)
|
75
|
-
end
|
76
|
-
|
77
|
-
def parse_response(socket)
|
78
|
-
version, code, reason = socket.gets.chomp.split(/ /, 3)
|
79
|
-
headers = {}
|
80
|
-
|
81
|
-
while (line = socket.gets) && (line.chomp! != "")
|
82
|
-
name, value = line.split(/:/, 2)
|
83
|
-
headers[name.strip] = value.strip
|
4
|
+
module Net
|
5
|
+
module SSH
|
6
|
+
module Proxy
|
7
|
+
|
8
|
+
# An implementation of an HTTP proxy. To use it, instantiate it, then
|
9
|
+
# pass the instantiated object via the :proxy key to Net::SSH.start:
|
10
|
+
#
|
11
|
+
# require 'net/ssh/proxy/http'
|
12
|
+
#
|
13
|
+
# proxy = Net::SSH::Proxy::HTTP.new('proxy_host', proxy_port)
|
14
|
+
# Net::SSH.start('host', 'user', :proxy => proxy) do |ssh|
|
15
|
+
# ...
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# If the proxy requires authentication, you can pass :user and :password
|
19
|
+
# to the proxy's constructor:
|
20
|
+
#
|
21
|
+
# proxy = Net::SSH::Proxy::HTTP.new('proxy_host', proxy_port,
|
22
|
+
# :user => "user", :password => "password")
|
23
|
+
#
|
24
|
+
# Note that HTTP digest authentication is not supported; Basic only at
|
25
|
+
# this point.
|
26
|
+
class HTTP
|
27
|
+
# The hostname or IP address of the HTTP proxy.
|
28
|
+
attr_reader :proxy_host
|
29
|
+
|
30
|
+
# The port number of the proxy.
|
31
|
+
attr_reader :proxy_port
|
32
|
+
|
33
|
+
# The map of additional options that were given to the object at
|
34
|
+
# initialization.
|
35
|
+
attr_reader :options
|
36
|
+
|
37
|
+
# Create a new socket factory that tunnels via the given host and
|
38
|
+
# port. The +options+ parameter is a hash of additional settings that
|
39
|
+
# can be used to tweak this proxy connection. Specifically, the following
|
40
|
+
# options are supported:
|
41
|
+
#
|
42
|
+
# * :user => the user name to use when authenticating to the proxy
|
43
|
+
# * :password => the password to use when authenticating
|
44
|
+
def initialize(proxy_host, proxy_port=80, options={})
|
45
|
+
@proxy_host = proxy_host
|
46
|
+
@proxy_port = proxy_port
|
47
|
+
@options = options
|
84
48
|
end
|
85
|
-
|
86
|
-
|
87
|
-
|
49
|
+
|
50
|
+
# Return a new socket connected to the given host and port via the
|
51
|
+
# proxy that was requested when the socket factory was instantiated.
|
52
|
+
def open(host, port, connection_options)
|
53
|
+
socket = establish_connection(connection_options[:timeout])
|
54
|
+
socket.write "CONNECT #{host}:#{port} HTTP/1.0\r\n"
|
55
|
+
|
56
|
+
if options[:user]
|
57
|
+
credentials = ["#{options[:user]}:#{options[:password]}"].pack("m*").gsub(/\s/, "")
|
58
|
+
socket.write "Proxy-Authorization: Basic #{credentials}\r\n"
|
59
|
+
end
|
60
|
+
|
61
|
+
socket.write "\r\n"
|
62
|
+
|
63
|
+
resp = parse_response(socket)
|
64
|
+
|
65
|
+
return socket if resp[:code] == 200
|
66
|
+
|
67
|
+
socket.close
|
68
|
+
raise ConnectError, resp.inspect
|
69
|
+
end
|
70
|
+
|
71
|
+
protected
|
72
|
+
|
73
|
+
def establish_connection(connect_timeout)
|
74
|
+
Socket.tcp(proxy_host, proxy_port, nil, nil,
|
75
|
+
connect_timeout: connect_timeout)
|
76
|
+
end
|
77
|
+
|
78
|
+
def parse_response(socket)
|
79
|
+
version, code, reason = socket.gets.chomp.split(/ /, 3)
|
80
|
+
headers = {}
|
81
|
+
|
82
|
+
while (line = socket.gets) && (line.chomp! != "")
|
83
|
+
name, value = line.split(/:/, 2)
|
84
|
+
headers[name.strip] = value.strip
|
85
|
+
end
|
86
|
+
|
87
|
+
body = socket.read(headers["Content-Length"].to_i) if headers["Content-Length"]
|
88
|
+
|
89
|
+
return { version: version,
|
90
|
+
code: code.to_i,
|
91
|
+
reason: reason,
|
92
|
+
headers: headers,
|
93
|
+
body: body }
|
88
94
|
end
|
89
|
-
|
90
|
-
return { version: version,
|
91
|
-
code: code.to_i,
|
92
|
-
reason: reason,
|
93
|
-
headers: headers,
|
94
|
-
body: body }
|
95
95
|
end
|
96
|
-
end
|
97
96
|
|
98
|
-
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
data/lib/net/ssh/proxy/https.rb
CHANGED
@@ -3,47 +3,50 @@ require 'openssl'
|
|
3
3
|
require 'net/ssh/proxy/errors'
|
4
4
|
require 'net/ssh/proxy/http'
|
5
5
|
|
6
|
-
module Net
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
protected
|
26
|
-
|
27
|
-
# Shim to make OpenSSL::SSL::SSLSocket behave like a regular TCPSocket
|
28
|
-
# for all intents and purposes of Net::SSH::BufferedIo
|
29
|
-
module SSLSocketCompatibility
|
30
|
-
def self.extended(object) #:nodoc:
|
31
|
-
object.define_singleton_method(:recv, object.method(:sysread))
|
32
|
-
object.sync_close = true
|
6
|
+
module Net
|
7
|
+
module SSH
|
8
|
+
module Proxy
|
9
|
+
|
10
|
+
# A specialization of the HTTP proxy which encrypts the whole connection
|
11
|
+
# using OpenSSL. This has the advantage that proxy authentication
|
12
|
+
# information is not sent in plaintext.
|
13
|
+
class HTTPS < HTTP
|
14
|
+
# Create a new socket factory that tunnels via the given host and
|
15
|
+
# port. The +options+ parameter is a hash of additional settings that
|
16
|
+
# can be used to tweak this proxy connection. In addition to the options
|
17
|
+
# taken by Net::SSH::Proxy::HTTP it supports:
|
18
|
+
#
|
19
|
+
# * :ssl_context => the SSL configuration to use for the connection
|
20
|
+
def initialize(proxy_host, proxy_port=80, options={})
|
21
|
+
@ssl_context = options.delete(:ssl_context) ||
|
22
|
+
OpenSSL::SSL::SSLContext.new
|
23
|
+
super(proxy_host, proxy_port, options)
|
33
24
|
end
|
34
|
-
|
35
|
-
|
36
|
-
|
25
|
+
|
26
|
+
protected
|
27
|
+
|
28
|
+
# Shim to make OpenSSL::SSL::SSLSocket behave like a regular TCPSocket
|
29
|
+
# for all intents and purposes of Net::SSH::BufferedIo
|
30
|
+
module SSLSocketCompatibility
|
31
|
+
def self.extended(object) #:nodoc:
|
32
|
+
object.define_singleton_method(:recv, object.method(:sysread))
|
33
|
+
object.sync_close = true
|
34
|
+
end
|
35
|
+
|
36
|
+
def send(data, _opts)
|
37
|
+
syswrite(data)
|
38
|
+
end
|
37
39
|
end
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
40
|
+
|
41
|
+
def establish_connection(connect_timeout)
|
42
|
+
plain_socket = super(connect_timeout)
|
43
|
+
OpenSSL::SSL::SSLSocket.new(plain_socket, @ssl_context).tap do |socket|
|
44
|
+
socket.extend(SSLSocketCompatibility)
|
45
|
+
socket.connect
|
46
|
+
end
|
45
47
|
end
|
46
48
|
end
|
47
|
-
end
|
48
49
|
|
49
|
-
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
data/lib/net/ssh/proxy/jump.rb
CHANGED
@@ -1,53 +1,56 @@
|
|
1
1
|
require 'uri'
|
2
2
|
require 'net/ssh/proxy/command'
|
3
3
|
|
4
|
-
module Net
|
4
|
+
module Net
|
5
|
+
module SSH
|
6
|
+
module Proxy
|
7
|
+
|
8
|
+
# An implementation of a jump proxy. To use it, instantiate it,
|
9
|
+
# then pass the instantiated object via the :proxy key to
|
10
|
+
# Net::SSH.start:
|
11
|
+
#
|
12
|
+
# require 'net/ssh/proxy/jump'
|
13
|
+
#
|
14
|
+
# proxy = Net::SSH::Proxy::Jump.new('user@proxy')
|
15
|
+
# Net::SSH.start('host', 'user', :proxy => proxy) do |ssh|
|
16
|
+
# ...
|
17
|
+
# end
|
18
|
+
class Jump < Command
|
19
|
+
# The jump proxies
|
20
|
+
attr_reader :jump_proxies
|
21
|
+
|
22
|
+
# Create a new socket factory that tunnels via multiple jump proxes as
|
23
|
+
# [user@]host[:port].
|
24
|
+
def initialize(jump_proxies)
|
25
|
+
@jump_proxies = jump_proxies
|
26
|
+
end
|
27
|
+
|
28
|
+
# Return a new socket connected to the given host and port via the jump
|
29
|
+
# proxy that was requested when the socket factory was instantiated.
|
30
|
+
def open(host, port, connection_options = nil)
|
31
|
+
build_proxy_command_equivalent(connection_options)
|
32
|
+
super
|
33
|
+
end
|
34
|
+
|
35
|
+
# We cannot build the ProxyCommand template until we know if the :config
|
36
|
+
# option was specified during `Net::SSH.start`.
|
37
|
+
def build_proxy_command_equivalent(connection_options = nil)
|
38
|
+
first_jump, extra_jumps = jump_proxies.split(",", 2)
|
39
|
+
config = connection_options && connection_options[:config]
|
40
|
+
uri = URI.parse("ssh://#{first_jump}")
|
41
|
+
|
42
|
+
template = "ssh"
|
43
|
+
template << " -l #{uri.user}" if uri.user
|
44
|
+
template << " -p #{uri.port}" if uri.port
|
45
|
+
template << " -J #{extra_jumps}" if extra_jumps
|
46
|
+
template << " -F #{config}" if config != true && config
|
47
|
+
template << " -W %h:%p "
|
48
|
+
template << uri.host
|
49
|
+
|
50
|
+
@command_line_template = template
|
51
|
+
end
|
52
|
+
end
|
5
53
|
|
6
|
-
# An implementation of a jump proxy. To use it, instantiate it,
|
7
|
-
# then pass the instantiated object via the :proxy key to
|
8
|
-
# Net::SSH.start:
|
9
|
-
#
|
10
|
-
# require 'net/ssh/proxy/jump'
|
11
|
-
#
|
12
|
-
# proxy = Net::SSH::Proxy::Jump.new('user@proxy')
|
13
|
-
# Net::SSH.start('host', 'user', :proxy => proxy) do |ssh|
|
14
|
-
# ...
|
15
|
-
# end
|
16
|
-
class Jump < Command
|
17
|
-
|
18
|
-
# The jump proxies
|
19
|
-
attr_reader :jump_proxies
|
20
|
-
|
21
|
-
# Create a new socket factory that tunnels via multiple jump proxes as
|
22
|
-
# [user@]host[:port].
|
23
|
-
def initialize(jump_proxies)
|
24
|
-
@jump_proxies = jump_proxies
|
25
|
-
end
|
26
|
-
|
27
|
-
# Return a new socket connected to the given host and port via the jump
|
28
|
-
# proxy that was requested when the socket factory was instantiated.
|
29
|
-
def open(host, port, connection_options = nil)
|
30
|
-
build_proxy_command_equivalent(connection_options)
|
31
|
-
super
|
32
|
-
end
|
33
|
-
|
34
|
-
# We cannot build the ProxyCommand template until we know if the :config
|
35
|
-
# option was specified during `Net::SSH.start`.
|
36
|
-
def build_proxy_command_equivalent(connection_options = nil)
|
37
|
-
first_jump, extra_jumps = jump_proxies.split(",", 2)
|
38
|
-
config = connection_options && connection_options[:config]
|
39
|
-
uri = URI.parse("ssh://#{first_jump}")
|
40
|
-
|
41
|
-
template = "ssh"
|
42
|
-
template << " -l #{uri.user}" if uri.user
|
43
|
-
template << " -p #{uri.port}" if uri.port
|
44
|
-
template << " -J #{extra_jumps}" if extra_jumps
|
45
|
-
template << " -F #{config}" if config != true && config
|
46
|
-
template << " -W %h:%p "
|
47
|
-
template << uri.host
|
48
|
-
|
49
|
-
@command_line_template = template
|
50
54
|
end
|
51
55
|
end
|
52
|
-
|
53
|
-
end; end; end
|
56
|
+
end
|
data/lib/net/ssh/proxy/socks4.rb
CHANGED
data/lib/net/ssh/proxy/socks5.rb
CHANGED
@@ -101,7 +101,7 @@ module Net
|
|
101
101
|
address_type = socket.recv(1).getbyte(0)
|
102
102
|
case address_type
|
103
103
|
when 1
|
104
|
-
socket.recv(4)
|
104
|
+
socket.recv(4) # get four bytes for IPv4 address
|
105
105
|
when 3
|
106
106
|
len = socket.recv(1).getbyte(0)
|
107
107
|
hostname = socket.recv(len)
|
@@ -123,19 +123,19 @@ module Net
|
|
123
123
|
|
124
124
|
private
|
125
125
|
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
126
|
+
# Simple username/password negotiation with the SOCKS5 server.
|
127
|
+
def negotiate_password(socket)
|
128
|
+
packet = [0x01, options[:user].length, options[:user],
|
129
|
+
options[:password].length, options[:password]].pack("CCA*CA*")
|
130
|
+
socket.send packet, 0
|
131
131
|
|
132
|
-
|
132
|
+
version, status = socket.recv(2).unpack("CC")
|
133
133
|
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
end
|
134
|
+
if status != SUCCESS
|
135
|
+
socket.close
|
136
|
+
raise UnauthorizedError, "could not authorize user"
|
138
137
|
end
|
138
|
+
end
|
139
139
|
end
|
140
140
|
|
141
141
|
end
|