net-ssh 5.0.2 → 7.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/.dockerignore +6 -0
- data/.github/config/rubocop_linter_action.yml +4 -0
- data/.github/workflows/ci-with-docker.yml +44 -0
- data/.github/workflows/ci.yml +87 -0
- data/.github/workflows/rubocop.yml +13 -0
- data/.gitignore +3 -0
- data/.rubocop.yml +19 -2
- data/.rubocop_todo.yml +623 -511
- data/CHANGES.txt +76 -0
- data/Dockerfile +27 -0
- data/Dockerfile.openssl3 +17 -0
- data/Gemfile +2 -0
- data/Gemfile.noed25519 +2 -0
- data/Manifest +0 -1
- data/README.md +293 -0
- data/Rakefile +6 -2
- data/appveyor.yml +4 -2
- data/docker-compose.yml +23 -0
- data/lib/net/ssh/authentication/agent.rb +36 -14
- data/lib/net/ssh/authentication/certificate.rb +19 -7
- data/lib/net/ssh/authentication/constants.rb +0 -1
- data/lib/net/ssh/authentication/ed25519.rb +83 -50
- data/lib/net/ssh/authentication/ed25519_loader.rb +5 -8
- data/lib/net/ssh/authentication/key_manager.rb +74 -33
- data/lib/net/ssh/authentication/methods/abstract.rb +12 -3
- data/lib/net/ssh/authentication/methods/hostbased.rb +3 -5
- data/lib/net/ssh/authentication/methods/keyboard_interactive.rb +5 -3
- data/lib/net/ssh/authentication/methods/none.rb +6 -9
- data/lib/net/ssh/authentication/methods/password.rb +2 -3
- data/lib/net/ssh/authentication/methods/publickey.rb +58 -16
- data/lib/net/ssh/authentication/pageant.rb +97 -97
- data/lib/net/ssh/authentication/pub_key_fingerprint.rb +2 -3
- data/lib/net/ssh/authentication/session.rb +27 -23
- data/lib/net/ssh/buffer.rb +91 -40
- data/lib/net/ssh/buffered_io.rb +24 -26
- data/lib/net/ssh/config.rb +99 -53
- data/lib/net/ssh/connection/channel.rb +101 -87
- data/lib/net/ssh/connection/constants.rb +0 -4
- data/lib/net/ssh/connection/event_loop.rb +30 -25
- data/lib/net/ssh/connection/keepalive.rb +12 -12
- data/lib/net/ssh/connection/session.rb +115 -111
- data/lib/net/ssh/connection/term.rb +56 -58
- data/lib/net/ssh/errors.rb +12 -12
- data/lib/net/ssh/key_factory.rb +108 -22
- data/lib/net/ssh/known_hosts.rb +120 -36
- data/lib/net/ssh/loggable.rb +10 -11
- data/lib/net/ssh/packet.rb +1 -1
- data/lib/net/ssh/prompt.rb +9 -11
- data/lib/net/ssh/proxy/command.rb +1 -2
- data/lib/net/ssh/proxy/errors.rb +2 -4
- data/lib/net/ssh/proxy/http.rb +18 -20
- data/lib/net/ssh/proxy/https.rb +8 -10
- data/lib/net/ssh/proxy/jump.rb +8 -10
- data/lib/net/ssh/proxy/socks4.rb +2 -4
- data/lib/net/ssh/proxy/socks5.rb +3 -6
- data/lib/net/ssh/service/forward.rb +9 -8
- data/lib/net/ssh/test/channel.rb +24 -26
- data/lib/net/ssh/test/extensions.rb +37 -35
- data/lib/net/ssh/test/kex.rb +6 -8
- data/lib/net/ssh/test/local_packet.rb +0 -2
- data/lib/net/ssh/test/packet.rb +3 -3
- data/lib/net/ssh/test/remote_packet.rb +6 -8
- data/lib/net/ssh/test/script.rb +25 -27
- data/lib/net/ssh/test/socket.rb +12 -15
- data/lib/net/ssh/test.rb +12 -12
- data/lib/net/ssh/transport/algorithms.rb +177 -118
- data/lib/net/ssh/transport/cipher_factory.rb +34 -50
- data/lib/net/ssh/transport/constants.rb +13 -9
- data/lib/net/ssh/transport/ctr.rb +8 -14
- data/lib/net/ssh/transport/hmac/abstract.rb +20 -5
- data/lib/net/ssh/transport/hmac/md5.rb +0 -2
- data/lib/net/ssh/transport/hmac/md5_96.rb +0 -2
- data/lib/net/ssh/transport/hmac/none.rb +0 -2
- data/lib/net/ssh/transport/hmac/ripemd160.rb +0 -2
- data/lib/net/ssh/transport/hmac/sha1.rb +0 -2
- data/lib/net/ssh/transport/hmac/sha1_96.rb +0 -2
- data/lib/net/ssh/transport/hmac/sha2_256.rb +7 -11
- data/lib/net/ssh/transport/hmac/sha2_256_96.rb +4 -8
- data/lib/net/ssh/transport/hmac/sha2_256_etm.rb +12 -0
- data/lib/net/ssh/transport/hmac/sha2_512.rb +6 -9
- data/lib/net/ssh/transport/hmac/sha2_512_96.rb +4 -8
- data/lib/net/ssh/transport/hmac/sha2_512_etm.rb +12 -0
- data/lib/net/ssh/transport/hmac.rb +13 -11
- data/lib/net/ssh/transport/identity_cipher.rb +11 -13
- data/lib/net/ssh/transport/kex/abstract.rb +130 -0
- data/lib/net/ssh/transport/kex/abstract5656.rb +72 -0
- data/lib/net/ssh/transport/kex/curve25519_sha256.rb +39 -0
- data/lib/net/ssh/transport/kex/curve25519_sha256_loader.rb +30 -0
- data/lib/net/ssh/transport/kex/diffie_hellman_group14_sha1.rb +5 -19
- data/lib/net/ssh/transport/kex/diffie_hellman_group14_sha256.rb +11 -0
- data/lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb +30 -139
- data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha1.rb +1 -8
- data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha256.rb +5 -9
- data/lib/net/ssh/transport/kex/ecdh_sha2_nistp256.rb +20 -81
- data/lib/net/ssh/transport/kex/ecdh_sha2_nistp384.rb +5 -4
- data/lib/net/ssh/transport/kex/ecdh_sha2_nistp521.rb +5 -4
- data/lib/net/ssh/transport/kex.rb +15 -10
- data/lib/net/ssh/transport/key_expander.rb +7 -8
- data/lib/net/ssh/transport/openssl.rb +149 -111
- data/lib/net/ssh/transport/packet_stream.rb +53 -22
- data/lib/net/ssh/transport/server_version.rb +17 -16
- data/lib/net/ssh/transport/session.rb +35 -11
- data/lib/net/ssh/transport/state.rb +44 -44
- data/lib/net/ssh/verifiers/accept_new.rb +7 -2
- data/lib/net/ssh/verifiers/accept_new_or_local_tunnel.rb +1 -2
- data/lib/net/ssh/verifiers/always.rb +10 -4
- data/lib/net/ssh/verifiers/never.rb +4 -2
- data/lib/net/ssh/version.rb +2 -2
- data/lib/net/ssh.rb +17 -9
- data/net-ssh-public_cert.pem +18 -19
- data/net-ssh.gemspec +9 -7
- data/support/ssh_tunnel_bug.rb +3 -3
- data.tar.gz.sig +0 -0
- metadata +65 -41
- metadata.gz.sig +0 -0
- data/.travis.yml +0 -52
- data/Gemfile.noed25519.lock +0 -41
- data/README.rdoc +0 -169
- data/lib/net/ssh/ruby_compat.rb +0 -13
- data/support/arcfour_check.rb +0 -20
data/lib/net/ssh/known_hosts.rb
CHANGED
|
@@ -1,10 +1,69 @@
|
|
|
1
1
|
require 'strscan'
|
|
2
2
|
require 'openssl'
|
|
3
3
|
require 'base64'
|
|
4
|
+
require 'delegate'
|
|
4
5
|
require 'net/ssh/buffer'
|
|
6
|
+
require 'net/ssh/authentication/ed25519_loader'
|
|
5
7
|
|
|
6
8
|
module Net
|
|
7
9
|
module SSH
|
|
10
|
+
module HostKeyEntries
|
|
11
|
+
# regular public key entry
|
|
12
|
+
class PubKey < Delegator
|
|
13
|
+
def initialize(key, comment: nil) # rubocop:disable Lint/MissingSuper
|
|
14
|
+
@key = key
|
|
15
|
+
@comment = comment
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def ssh_type
|
|
19
|
+
@key.ssh_type
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def ssh_types
|
|
23
|
+
[ssh_type]
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def to_blob
|
|
27
|
+
@key.to_blob
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def __getobj__
|
|
31
|
+
Kernel.warn("Calling Net::SSH::Buffer methods on HostKeyEntries PubKey is deprecated")
|
|
32
|
+
@key
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def matches_key?(server_key)
|
|
36
|
+
@key.ssh_type == server_key.ssh_type && @key.to_blob == server_key.to_blob
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# @cert-authority entry
|
|
41
|
+
class CertAuthority
|
|
42
|
+
def ssh_types
|
|
43
|
+
%w[
|
|
44
|
+
ecdsa-sha2-nistp256-cert-v01@openssh.com
|
|
45
|
+
ecdsa-sha2-nistp384-cert-v01@openssh.com
|
|
46
|
+
ecdsa-sha2-nistp521-cert-v01@openssh.com
|
|
47
|
+
ssh-ed25519-cert-v01@openssh.com
|
|
48
|
+
ssh-rsa-cert-v01@openssh.com
|
|
49
|
+
ssh-rsa-cert-v00@openssh.com
|
|
50
|
+
]
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def initialize(key, comment: nil)
|
|
54
|
+
@key = key
|
|
55
|
+
@comment = comment
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def matches_key?(server_key)
|
|
59
|
+
if ssh_types.include?(server_key.ssh_type)
|
|
60
|
+
server_key.signature_valid? && (server_key.signature_key.to_blob == @key.to_blob)
|
|
61
|
+
else
|
|
62
|
+
false
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
8
67
|
|
|
9
68
|
# Represents the result of a search in known hosts
|
|
10
69
|
# see search_for
|
|
@@ -40,26 +99,24 @@ module Net
|
|
|
40
99
|
# This is used internally by Net::SSH, and will never need to be used directly
|
|
41
100
|
# by consumers of the library.
|
|
42
101
|
class KnownHosts
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
SUPPORTED_TYPE = %w[ssh-rsa ssh-dss]
|
|
50
|
-
end
|
|
102
|
+
SUPPORTED_TYPE = %w[ssh-rsa ssh-dss
|
|
103
|
+
ecdsa-sha2-nistp256
|
|
104
|
+
ecdsa-sha2-nistp384
|
|
105
|
+
ecdsa-sha2-nistp521]
|
|
106
|
+
|
|
107
|
+
SUPPORTED_TYPE.push('ssh-ed25519') if Net::SSH::Authentication::ED25519Loader::LOADED
|
|
51
108
|
|
|
52
|
-
class <<self
|
|
109
|
+
class << self
|
|
53
110
|
# Searches all known host files (see KnownHosts.hostfiles) for all keys
|
|
54
111
|
# of the given host. Returns an enumerable of keys found.
|
|
55
|
-
def search_for(host, options={})
|
|
56
|
-
HostKeys.new(search_in(hostfiles(options), host), host, self, options)
|
|
112
|
+
def search_for(host, options = {})
|
|
113
|
+
HostKeys.new(search_in(hostfiles(options), host, options), host, self, options)
|
|
57
114
|
end
|
|
58
115
|
|
|
59
116
|
# Search for all known keys for the given host, in every file given in
|
|
60
117
|
# the +files+ array. Returns the list of keys.
|
|
61
|
-
def search_in(files, host)
|
|
62
|
-
files.flat_map { |file| KnownHosts.new(file).keys_for(host) }
|
|
118
|
+
def search_in(files, host, options = {})
|
|
119
|
+
files.flat_map { |file| KnownHosts.new(file).keys_for(host, options) }
|
|
63
120
|
end
|
|
64
121
|
|
|
65
122
|
# Looks in the given +options+ hash for the :user_known_hosts_file and
|
|
@@ -71,12 +128,14 @@ module Net
|
|
|
71
128
|
#
|
|
72
129
|
# If you only want the user known host files, you can pass :user as
|
|
73
130
|
# the second option.
|
|
74
|
-
def hostfiles(options, which
|
|
131
|
+
def hostfiles(options, which = :all)
|
|
75
132
|
files = []
|
|
76
133
|
|
|
77
134
|
files += Array(options[:user_known_hosts_file] || %w[~/.ssh/known_hosts ~/.ssh/known_hosts2]) if which == :all || which == :user
|
|
78
135
|
|
|
79
|
-
|
|
136
|
+
if which == :all || which == :global
|
|
137
|
+
files += Array(options[:global_known_hosts_file] || %w[/etc/ssh/ssh_known_hosts /etc/ssh/ssh_known_hosts2])
|
|
138
|
+
end
|
|
80
139
|
|
|
81
140
|
return files
|
|
82
141
|
end
|
|
@@ -84,14 +143,12 @@ module Net
|
|
|
84
143
|
# Looks in all user known host files (see KnownHosts.hostfiles) and tries to
|
|
85
144
|
# add an entry for the given host and key to the first file it is able
|
|
86
145
|
# to.
|
|
87
|
-
def add(host, key, options={})
|
|
146
|
+
def add(host, key, options = {})
|
|
88
147
|
hostfiles(options, :user).each do |file|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
# try the next hostfile
|
|
94
|
-
end
|
|
148
|
+
KnownHosts.new(file).add(host, key)
|
|
149
|
+
return
|
|
150
|
+
rescue SystemCallError
|
|
151
|
+
# try the next hostfile
|
|
95
152
|
end
|
|
96
153
|
end
|
|
97
154
|
end
|
|
@@ -119,39 +176,66 @@ module Net
|
|
|
119
176
|
# "[net.ssh.test]:5555"
|
|
120
177
|
# "[1,2,3,4]:5555"
|
|
121
178
|
# "[net.ssh.test]:5555,[1.2.3.4]:5555
|
|
122
|
-
def keys_for(host)
|
|
179
|
+
def keys_for(host, options = {})
|
|
123
180
|
keys = []
|
|
124
181
|
return keys unless File.readable?(source)
|
|
125
182
|
|
|
126
183
|
entries = host.split(/,/)
|
|
184
|
+
host_name = entries[0]
|
|
185
|
+
host_ip = entries[1]
|
|
127
186
|
|
|
128
187
|
File.open(source) do |file|
|
|
129
|
-
scanner = StringScanner.new("")
|
|
130
188
|
file.each_line do |line|
|
|
131
|
-
|
|
189
|
+
if line.start_with?('@')
|
|
190
|
+
marker, hosts, type, key_content, comment = line.split(' ')
|
|
191
|
+
else
|
|
192
|
+
marker = nil
|
|
193
|
+
hosts, type, key_content, comment = line.split(' ')
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
# Skip empty line or one that is commented
|
|
197
|
+
next if hosts.nil? || hosts.start_with?('#')
|
|
132
198
|
|
|
133
|
-
|
|
134
|
-
next if scanner.match?(/$|#/)
|
|
199
|
+
hostlist = hosts.split(',')
|
|
135
200
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
201
|
+
next unless SUPPORTED_TYPE.include?(type)
|
|
202
|
+
|
|
203
|
+
found = hostlist.any? { |pattern| match(host_name, pattern) } || known_host_hash?(hostlist, entries)
|
|
139
204
|
next unless found
|
|
140
205
|
|
|
141
|
-
|
|
142
|
-
|
|
206
|
+
found = hostlist.include?(host_ip) if options[:check_host_ip] && entries.size > 1 && hostlist.size > 1
|
|
207
|
+
next unless found
|
|
143
208
|
|
|
144
|
-
|
|
209
|
+
blob = key_content.unpack("m*").first
|
|
210
|
+
raw_key = Net::SSH::Buffer.new(blob).read_key
|
|
145
211
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
212
|
+
keys <<
|
|
213
|
+
if marker == "@cert-authority"
|
|
214
|
+
HostKeyEntries::CertAuthority.new(raw_key, comment: comment)
|
|
215
|
+
else
|
|
216
|
+
HostKeyEntries::PubKey.new(raw_key, comment: comment)
|
|
217
|
+
end
|
|
149
218
|
end
|
|
150
219
|
end
|
|
151
220
|
|
|
152
221
|
keys
|
|
153
222
|
end
|
|
154
223
|
|
|
224
|
+
def match(host, pattern)
|
|
225
|
+
if pattern.include?('*') || pattern.include?('?')
|
|
226
|
+
# see man 8 sshd for pattern details
|
|
227
|
+
pattern_regexp = pattern.split('*', -1).map do |x|
|
|
228
|
+
x.split('?', -1).map do |y|
|
|
229
|
+
Regexp.escape(y)
|
|
230
|
+
end.join('.')
|
|
231
|
+
end.join('.*')
|
|
232
|
+
|
|
233
|
+
host =~ Regexp.new("\\A#{pattern_regexp}\\z")
|
|
234
|
+
else
|
|
235
|
+
host == pattern
|
|
236
|
+
end
|
|
237
|
+
end
|
|
238
|
+
|
|
155
239
|
# Indicates whether one of the entries matches an hostname that has been
|
|
156
240
|
# stored as a HMAC-SHA1 hash in the known hosts.
|
|
157
241
|
def known_host_hash?(hostlist, entries)
|
data/lib/net/ssh/loggable.rb
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
module Net
|
|
1
|
+
module Net
|
|
2
2
|
module SSH
|
|
3
|
-
|
|
4
3
|
# A simple module to make logging easier to deal with. It assumes that the
|
|
5
4
|
# logger instance (if not nil) quacks like a Logger object (in Ruby's
|
|
6
5
|
# standard library). Although used primarily internally by Net::SSH, it
|
|
@@ -19,45 +18,45 @@ module Net
|
|
|
19
18
|
# The logger instance that will be used to log messages. If nil, nothing
|
|
20
19
|
# will be logged.
|
|
21
20
|
attr_accessor :logger
|
|
22
|
-
|
|
21
|
+
|
|
23
22
|
# Displays the result of yielding if the log level is Logger::DEBUG or
|
|
24
23
|
# greater.
|
|
25
24
|
def debug
|
|
26
25
|
logger.add(Logger::DEBUG, nil, facility) { yield } if logger && logger.debug?
|
|
27
26
|
end
|
|
28
|
-
|
|
27
|
+
|
|
29
28
|
# Displays the result of yielding if the log level is Logger::INFO or
|
|
30
29
|
# greater.
|
|
31
30
|
def info
|
|
32
31
|
logger.add(Logger::INFO, nil, facility) { yield } if logger && logger.info?
|
|
33
32
|
end
|
|
34
|
-
|
|
33
|
+
|
|
35
34
|
# Displays the result of yielding if the log level is Logger::WARN or
|
|
36
35
|
# greater. (Called lwarn to avoid shadowing with Kernel#warn.)
|
|
37
36
|
def lwarn
|
|
38
37
|
logger.add(Logger::WARN, nil, facility) { yield } if logger && logger.warn?
|
|
39
38
|
end
|
|
40
|
-
|
|
39
|
+
|
|
41
40
|
# Displays the result of yielding if the log level is Logger:ERROR or
|
|
42
41
|
# greater.
|
|
43
42
|
def error
|
|
44
43
|
logger.add(Logger::ERROR, nil, facility) { yield } if logger && logger.error?
|
|
45
44
|
end
|
|
46
|
-
|
|
45
|
+
|
|
47
46
|
# Displays the result of yielding if the log level is Logger::FATAL or
|
|
48
47
|
# greater.
|
|
49
48
|
def fatal
|
|
50
49
|
logger.add(Logger::FATAL, nil, facility) { yield } if logger && logger.fatal?
|
|
51
50
|
end
|
|
52
|
-
|
|
51
|
+
|
|
53
52
|
private
|
|
54
|
-
|
|
53
|
+
|
|
55
54
|
# Sets the "facility" value, used for reporting where a log message
|
|
56
55
|
# originates. It defaults to the name of class with the object_id
|
|
57
56
|
# appended.
|
|
58
57
|
def facility
|
|
59
|
-
@facility ||= self.class.
|
|
58
|
+
@facility ||= self.class.to_s.gsub(/::/, ".").gsub(/([a-z])([A-Z])/, "\\1_\\2").downcase + "[%x]" % object_id
|
|
60
59
|
end
|
|
61
60
|
end
|
|
62
61
|
end
|
|
63
|
-
end
|
|
62
|
+
end
|
data/lib/net/ssh/packet.rb
CHANGED
|
@@ -5,7 +5,6 @@ require 'net/ssh/connection/constants'
|
|
|
5
5
|
|
|
6
6
|
module Net
|
|
7
7
|
module SSH
|
|
8
|
-
|
|
9
8
|
# A specialization of Buffer that knows the format of certain common
|
|
10
9
|
# packet types. It auto-parses those packet types, and allows them to
|
|
11
10
|
# be accessed via the #[] accessor.
|
|
@@ -85,6 +84,7 @@ module Net
|
|
|
85
84
|
def [](name)
|
|
86
85
|
name = name.to_sym
|
|
87
86
|
raise ArgumentError, "no such element #{name}" unless @named_elements.key?(name)
|
|
87
|
+
|
|
88
88
|
@named_elements[name]
|
|
89
89
|
end
|
|
90
90
|
|
data/lib/net/ssh/prompt.rb
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
require 'io/console'
|
|
2
2
|
|
|
3
|
-
module Net
|
|
3
|
+
module Net
|
|
4
4
|
module SSH
|
|
5
|
-
|
|
6
5
|
# Default prompt implementation, called for asking password from user.
|
|
7
6
|
# It will never be instantiated directly, but will instead be created for
|
|
8
7
|
# you automatically.
|
|
@@ -13,8 +12,8 @@ module Net
|
|
|
13
12
|
#
|
|
14
13
|
# prompter = options[:password_prompt].start({type:'password'})
|
|
15
14
|
# while !ok && max_retries < 3
|
|
16
|
-
# user = prompter.ask("user: ",
|
|
17
|
-
# password = prompter.ask("password: ",
|
|
15
|
+
# user = prompter.ask("user: ", true)
|
|
16
|
+
# password = prompter.ask("password: ", false)
|
|
18
17
|
# ok = send(user, password)
|
|
19
18
|
# prompter.sucess if ok
|
|
20
19
|
# end
|
|
@@ -24,9 +23,9 @@ module Net
|
|
|
24
23
|
def self.default(options = {})
|
|
25
24
|
@default ||= new(options)
|
|
26
25
|
end
|
|
27
|
-
|
|
26
|
+
|
|
28
27
|
def initialize(options = {}); end
|
|
29
|
-
|
|
28
|
+
|
|
30
29
|
# default prompt object implementation. More sophisticated implemenetations
|
|
31
30
|
# might implement caching.
|
|
32
31
|
class Prompter
|
|
@@ -36,22 +35,22 @@ module Net
|
|
|
36
35
|
$stdout.puts(info[:instruction]) unless info[:instruction].empty?
|
|
37
36
|
end
|
|
38
37
|
end
|
|
39
|
-
|
|
38
|
+
|
|
40
39
|
# ask input from user, a prompter might ask for multiple inputs
|
|
41
40
|
# (like user and password) in a single session.
|
|
42
|
-
def ask(prompt, echo=true)
|
|
41
|
+
def ask(prompt, echo = true)
|
|
43
42
|
$stdout.print(prompt)
|
|
44
43
|
$stdout.flush
|
|
45
44
|
ret = $stdin.noecho(&:gets).chomp
|
|
46
45
|
$stdout.print("\n")
|
|
47
46
|
ret
|
|
48
47
|
end
|
|
49
|
-
|
|
48
|
+
|
|
50
49
|
# success method will be called when the password was accepted
|
|
51
50
|
# It's a good time to save password asked to a cache.
|
|
52
51
|
def success; end
|
|
53
52
|
end
|
|
54
|
-
|
|
53
|
+
|
|
55
54
|
# start password session. Multiple questions might be asked multiple times
|
|
56
55
|
# on the returned object. Info hash tries to uniquely identify the password
|
|
57
56
|
# session, so caching implementations can save passwords properly.
|
|
@@ -59,6 +58,5 @@ module Net
|
|
|
59
58
|
Prompter.new(info)
|
|
60
59
|
end
|
|
61
60
|
end
|
|
62
|
-
|
|
63
61
|
end
|
|
64
62
|
end
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
require 'socket'
|
|
2
2
|
require 'rubygems'
|
|
3
3
|
require 'net/ssh/proxy/errors'
|
|
4
|
-
require 'net/ssh/ruby_compat'
|
|
5
4
|
|
|
6
5
|
module Net
|
|
7
6
|
module SSH
|
|
8
7
|
module Proxy
|
|
9
|
-
|
|
10
8
|
# An implementation of a command proxy. To use it, instantiate it,
|
|
11
9
|
# then pass the instantiated object via the :proxy key to
|
|
12
10
|
# Net::SSH.start:
|
|
@@ -106,6 +104,7 @@ module Net
|
|
|
106
104
|
if IO.select([self], nil, [self], timeout_in_seconds) == nil
|
|
107
105
|
raise "Unexpected spurious read wakeup"
|
|
108
106
|
end
|
|
107
|
+
|
|
109
108
|
retry
|
|
110
109
|
end
|
|
111
110
|
result
|
data/lib/net/ssh/proxy/errors.rb
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
require 'net/ssh/errors'
|
|
2
2
|
|
|
3
|
-
module Net
|
|
4
|
-
module SSH
|
|
3
|
+
module Net
|
|
4
|
+
module SSH
|
|
5
5
|
module Proxy
|
|
6
|
-
|
|
7
6
|
# A general exception class for all Proxy errors.
|
|
8
7
|
class Error < Net::SSH::Exception; end
|
|
9
8
|
|
|
@@ -12,7 +11,6 @@ module Net
|
|
|
12
11
|
|
|
13
12
|
# Used when the server doesn't recognize the user's credentials.
|
|
14
13
|
class UnauthorizedError < Error; end
|
|
15
|
-
|
|
16
14
|
end
|
|
17
15
|
end
|
|
18
16
|
end
|
data/lib/net/ssh/proxy/http.rb
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
require 'socket'
|
|
2
2
|
require 'net/ssh/proxy/errors'
|
|
3
3
|
|
|
4
|
-
module Net
|
|
5
|
-
module SSH
|
|
4
|
+
module Net
|
|
5
|
+
module SSH
|
|
6
6
|
module Proxy
|
|
7
|
-
|
|
8
7
|
# An implementation of an HTTP proxy. To use it, instantiate it, then
|
|
9
8
|
# pass the instantiated object via the :proxy key to Net::SSH.start:
|
|
10
9
|
#
|
|
@@ -26,14 +25,14 @@ module Net
|
|
|
26
25
|
class HTTP
|
|
27
26
|
# The hostname or IP address of the HTTP proxy.
|
|
28
27
|
attr_reader :proxy_host
|
|
29
|
-
|
|
28
|
+
|
|
30
29
|
# The port number of the proxy.
|
|
31
30
|
attr_reader :proxy_port
|
|
32
|
-
|
|
31
|
+
|
|
33
32
|
# The map of additional options that were given to the object at
|
|
34
33
|
# initialization.
|
|
35
34
|
attr_reader :options
|
|
36
|
-
|
|
35
|
+
|
|
37
36
|
# Create a new socket factory that tunnels via the given host and
|
|
38
37
|
# port. The +options+ parameter is a hash of additional settings that
|
|
39
38
|
# can be used to tweak this proxy connection. Specifically, the following
|
|
@@ -41,52 +40,52 @@ module Net
|
|
|
41
40
|
#
|
|
42
41
|
# * :user => the user name to use when authenticating to the proxy
|
|
43
42
|
# * :password => the password to use when authenticating
|
|
44
|
-
def initialize(proxy_host, proxy_port=80, options={})
|
|
43
|
+
def initialize(proxy_host, proxy_port = 80, options = {})
|
|
45
44
|
@proxy_host = proxy_host
|
|
46
45
|
@proxy_port = proxy_port
|
|
47
46
|
@options = options
|
|
48
47
|
end
|
|
49
|
-
|
|
48
|
+
|
|
50
49
|
# Return a new socket connected to the given host and port via the
|
|
51
50
|
# proxy that was requested when the socket factory was instantiated.
|
|
52
51
|
def open(host, port, connection_options)
|
|
53
52
|
socket = establish_connection(connection_options[:timeout])
|
|
54
53
|
socket.write "CONNECT #{host}:#{port} HTTP/1.1\r\n"
|
|
55
54
|
socket.write "Host: #{host}:#{port}\r\n"
|
|
56
|
-
|
|
55
|
+
|
|
57
56
|
if options[:user]
|
|
58
57
|
credentials = ["#{options[:user]}:#{options[:password]}"].pack("m*").gsub(/\s/, "")
|
|
59
58
|
socket.write "Proxy-Authorization: Basic #{credentials}\r\n"
|
|
60
59
|
end
|
|
61
|
-
|
|
60
|
+
|
|
62
61
|
socket.write "\r\n"
|
|
63
|
-
|
|
62
|
+
|
|
64
63
|
resp = parse_response(socket)
|
|
65
|
-
|
|
64
|
+
|
|
66
65
|
return socket if resp[:code] == 200
|
|
67
|
-
|
|
66
|
+
|
|
68
67
|
socket.close
|
|
69
68
|
raise ConnectError, resp.inspect
|
|
70
69
|
end
|
|
71
|
-
|
|
70
|
+
|
|
72
71
|
protected
|
|
73
|
-
|
|
72
|
+
|
|
74
73
|
def establish_connection(connect_timeout)
|
|
75
74
|
Socket.tcp(proxy_host, proxy_port, nil, nil,
|
|
76
75
|
connect_timeout: connect_timeout)
|
|
77
76
|
end
|
|
78
|
-
|
|
77
|
+
|
|
79
78
|
def parse_response(socket)
|
|
80
79
|
version, code, reason = socket.gets.chomp.split(/ /, 3)
|
|
81
80
|
headers = {}
|
|
82
|
-
|
|
81
|
+
|
|
83
82
|
while (line = socket.gets) && (line.chomp! != "")
|
|
84
83
|
name, value = line.split(/:/, 2)
|
|
85
84
|
headers[name.strip] = value.strip
|
|
86
85
|
end
|
|
87
|
-
|
|
86
|
+
|
|
88
87
|
body = socket.read(headers["Content-Length"].to_i) if headers["Content-Length"]
|
|
89
|
-
|
|
88
|
+
|
|
90
89
|
return { version: version,
|
|
91
90
|
code: code.to_i,
|
|
92
91
|
reason: reason,
|
|
@@ -94,7 +93,6 @@ module Net
|
|
|
94
93
|
body: body }
|
|
95
94
|
end
|
|
96
95
|
end
|
|
97
|
-
|
|
98
96
|
end
|
|
99
97
|
end
|
|
100
98
|
end
|
data/lib/net/ssh/proxy/https.rb
CHANGED
|
@@ -3,10 +3,9 @@ require 'openssl'
|
|
|
3
3
|
require 'net/ssh/proxy/errors'
|
|
4
4
|
require 'net/ssh/proxy/http'
|
|
5
5
|
|
|
6
|
-
module Net
|
|
7
|
-
module SSH
|
|
6
|
+
module Net
|
|
7
|
+
module SSH
|
|
8
8
|
module Proxy
|
|
9
|
-
|
|
10
9
|
# A specialization of the HTTP proxy which encrypts the whole connection
|
|
11
10
|
# using OpenSSL. This has the advantage that proxy authentication
|
|
12
11
|
# information is not sent in plaintext.
|
|
@@ -17,27 +16,27 @@ module Net
|
|
|
17
16
|
# taken by Net::SSH::Proxy::HTTP it supports:
|
|
18
17
|
#
|
|
19
18
|
# * :ssl_context => the SSL configuration to use for the connection
|
|
20
|
-
def initialize(proxy_host, proxy_port=80, options={})
|
|
19
|
+
def initialize(proxy_host, proxy_port = 80, options = {})
|
|
21
20
|
@ssl_context = options.delete(:ssl_context) ||
|
|
22
21
|
OpenSSL::SSL::SSLContext.new
|
|
23
22
|
super(proxy_host, proxy_port, options)
|
|
24
23
|
end
|
|
25
|
-
|
|
24
|
+
|
|
26
25
|
protected
|
|
27
|
-
|
|
26
|
+
|
|
28
27
|
# Shim to make OpenSSL::SSL::SSLSocket behave like a regular TCPSocket
|
|
29
28
|
# for all intents and purposes of Net::SSH::BufferedIo
|
|
30
29
|
module SSLSocketCompatibility
|
|
31
|
-
def self.extended(object)
|
|
30
|
+
def self.extended(object) # :nodoc:
|
|
32
31
|
object.define_singleton_method(:recv, object.method(:sysread))
|
|
33
32
|
object.sync_close = true
|
|
34
33
|
end
|
|
35
|
-
|
|
34
|
+
|
|
36
35
|
def send(data, _opts)
|
|
37
36
|
syswrite(data)
|
|
38
37
|
end
|
|
39
38
|
end
|
|
40
|
-
|
|
39
|
+
|
|
41
40
|
def establish_connection(connect_timeout)
|
|
42
41
|
plain_socket = super(connect_timeout)
|
|
43
42
|
OpenSSL::SSL::SSLSocket.new(plain_socket, @ssl_context).tap do |socket|
|
|
@@ -46,7 +45,6 @@ module Net
|
|
|
46
45
|
end
|
|
47
46
|
end
|
|
48
47
|
end
|
|
49
|
-
|
|
50
48
|
end
|
|
51
49
|
end
|
|
52
50
|
end
|
data/lib/net/ssh/proxy/jump.rb
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
require 'uri'
|
|
2
2
|
require 'net/ssh/proxy/command'
|
|
3
3
|
|
|
4
|
-
module Net
|
|
5
|
-
module SSH
|
|
4
|
+
module Net
|
|
5
|
+
module SSH
|
|
6
6
|
module Proxy
|
|
7
|
-
|
|
8
7
|
# An implementation of a jump proxy. To use it, instantiate it,
|
|
9
8
|
# then pass the instantiated object via the :proxy key to
|
|
10
9
|
# Net::SSH.start:
|
|
@@ -18,39 +17,38 @@ module Net
|
|
|
18
17
|
class Jump < Command
|
|
19
18
|
# The jump proxies
|
|
20
19
|
attr_reader :jump_proxies
|
|
21
|
-
|
|
20
|
+
|
|
22
21
|
# Create a new socket factory that tunnels via multiple jump proxes as
|
|
23
22
|
# [user@]host[:port].
|
|
24
23
|
def initialize(jump_proxies)
|
|
25
24
|
@jump_proxies = jump_proxies
|
|
26
25
|
end
|
|
27
|
-
|
|
26
|
+
|
|
28
27
|
# Return a new socket connected to the given host and port via the jump
|
|
29
28
|
# proxy that was requested when the socket factory was instantiated.
|
|
30
29
|
def open(host, port, connection_options = nil)
|
|
31
30
|
build_proxy_command_equivalent(connection_options)
|
|
32
31
|
super
|
|
33
32
|
end
|
|
34
|
-
|
|
33
|
+
|
|
35
34
|
# We cannot build the ProxyCommand template until we know if the :config
|
|
36
35
|
# option was specified during `Net::SSH.start`.
|
|
37
36
|
def build_proxy_command_equivalent(connection_options = nil)
|
|
38
37
|
first_jump, extra_jumps = jump_proxies.split(",", 2)
|
|
39
38
|
config = connection_options && connection_options[:config]
|
|
40
39
|
uri = URI.parse("ssh://#{first_jump}")
|
|
41
|
-
|
|
42
|
-
template = "ssh"
|
|
40
|
+
|
|
41
|
+
template = "ssh".dup
|
|
43
42
|
template << " -l #{uri.user}" if uri.user
|
|
44
43
|
template << " -p #{uri.port}" if uri.port
|
|
45
44
|
template << " -J #{extra_jumps}" if extra_jumps
|
|
46
45
|
template << " -F #{config}" if config != true && config
|
|
47
46
|
template << " -W %h:%p "
|
|
48
47
|
template << uri.host
|
|
49
|
-
|
|
48
|
+
|
|
50
49
|
@command_line_template = template
|
|
51
50
|
end
|
|
52
51
|
end
|
|
53
|
-
|
|
54
52
|
end
|
|
55
53
|
end
|
|
56
54
|
end
|
data/lib/net/ssh/proxy/socks4.rb
CHANGED
|
@@ -6,7 +6,6 @@ require 'net/ssh/proxy/errors'
|
|
|
6
6
|
module Net
|
|
7
7
|
module SSH
|
|
8
8
|
module Proxy
|
|
9
|
-
|
|
10
9
|
# An implementation of a SOCKS4 proxy. To use it, instantiate it, then
|
|
11
10
|
# pass the instantiated object via the :proxy key to Net::SSH.start:
|
|
12
11
|
#
|
|
@@ -38,7 +37,7 @@ module Net
|
|
|
38
37
|
# Create a new proxy connection to the given proxy host and port.
|
|
39
38
|
# Optionally, a :user key may be given to identify the username
|
|
40
39
|
# with which to authenticate.
|
|
41
|
-
def initialize(proxy_host, proxy_port=1080, options={})
|
|
40
|
+
def initialize(proxy_host, proxy_port = 1080, options = {})
|
|
42
41
|
@proxy_host = proxy_host
|
|
43
42
|
@proxy_port = proxy_port
|
|
44
43
|
@options = options
|
|
@@ -50,7 +49,7 @@ module Net
|
|
|
50
49
|
socket = Socket.tcp(proxy_host, proxy_port, nil, nil,
|
|
51
50
|
connect_timeout: connection_options[:timeout])
|
|
52
51
|
ip_addr = IPAddr.new(Resolv.getaddress(host))
|
|
53
|
-
|
|
52
|
+
|
|
54
53
|
packet = [VERSION, CONNECT, port.to_i, ip_addr.to_i, options[:user]].pack("CCnNZ*")
|
|
55
54
|
socket.send packet, 0
|
|
56
55
|
|
|
@@ -63,7 +62,6 @@ module Net
|
|
|
63
62
|
return socket
|
|
64
63
|
end
|
|
65
64
|
end
|
|
66
|
-
|
|
67
65
|
end
|
|
68
66
|
end
|
|
69
67
|
end
|