net-ssh 8.0.0.rc3 → 8.0.0.rc4
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/CHANGES.txt +14 -0
- data/README.md +10 -1
- data/lib/net/ssh/authentication/ed25519.rb +1 -1
- data/lib/net/ssh/authentication/methods/abstract.rb +2 -8
- data/lib/net/ssh/authentication/methods/publickey.rb +3 -1
- data/lib/net/ssh/authentication/security_key.rb +70 -0
- data/lib/net/ssh/authentication/session.rb +29 -17
- data/lib/net/ssh/buffer.rb +4 -0
- data/lib/net/ssh/buffered_io.rb +40 -3
- data/lib/net/ssh/config.rb +10 -1
- data/lib/net/ssh/connection/channel.rb +45 -1
- data/lib/net/ssh/connection/session.rb +51 -4
- data/lib/net/ssh/errors.rb +14 -6
- data/lib/net/ssh/key_factory.rb +7 -1
- data/lib/net/ssh/service/forward.rb +26 -21
- data/lib/net/ssh/transport/algorithms.rb +40 -18
- data/lib/net/ssh/transport/session.rb +4 -0
- data/lib/net/ssh/version.rb +1 -1
- data/lib/net/ssh.rb +7 -5
- data.tar.gz.sig +0 -0
- metadata +2 -1
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 475db99174313b32d90dc650a358dd0cbec01ae59b0daca8f2e02eeb97a8cb9e
|
|
4
|
+
data.tar.gz: 9cdb86ac170fa3b3ca90357a106bc9742210dd1eae61e7525f4b36f71342a590
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6ada5144a2d5b86aae33b317a42a915b9107731b921fd01bfac3678da06499e9e1fd9c5f4bb310787f345ff8279445c82b00fb129c1837341d40fe7f9eea1779
|
|
7
|
+
data.tar.gz: 0cf535957b80c52960df55d93073efff00aa7cf89a9e7323a4dc38e59833ae0bf3a2e419c032c282ffe38bb14234486436c4ff151931dff73d0330229a83508c
|
checksums.yaml.gz.sig
CHANGED
|
Binary file
|
data/CHANGES.txt
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
=== 8.0.0 rc4
|
|
2
|
+
|
|
3
|
+
* Security: RSA signatures using SHA-1 (`ssh-rsa`, `ssh-rsa-cert-v01@openssh.com`) are no longer offered by default for host keys or public key authentication, as in OpenSSH since 8.8; RSA keys keep working via rsa-sha2-256/512. For servers that only support SHA-1 add `HostKeyAlgorithms +ssh-rsa` / `PubkeyAcceptedAlgorithms +ssh-rsa` in ssh_config (or `host_key: '+ssh-rsa'` / `pubkey_algorithms: %w[+ssh-rsa]`), or pass `append_all_supported_algorithms: true` [#1039]
|
|
4
|
+
* Security: ssh_config `PubkeyAcceptedAlgorithms` (and its old name `PubkeyAcceptedKeyTypes`) is now honoured, including `+`, `-` and `^`, and `pubkey_algorithms` now applies to all key types instead of only RSA, adding `rsa-sha2-512`; DSA (`ssh-dss`) keys are no longer offered by default, as in OpenSSH since 7.0 — add `+ssh-dss` or pass `append_all_supported_algorithms: true` to re-enable [#923]
|
|
5
|
+
* Security: ssh_config `Ciphers`, `MACs`, `KexAlgorithms` and `HostKeyAlgorithms` lists starting with `+`, `-` or `^` now append to, remove from or prepend to the default list, as in OpenSSH, instead of silently enabling every supported algorithm including weak ones that are off by default; every listed item is honoured and `-` accepts wildcards. Pass `append_all_supported_algorithms: true` to also get the full supported list [#925]
|
|
6
|
+
* Support agent-backed FIDO/U2F security keys (sk-ecdsa-sha2-nistp256@openssh.com, sk-ssh-ed25519@openssh.com) for publickey auth [#1007]
|
|
7
|
+
* Port forwarding no longer buffers a whole stream in memory when one side reads slowly: like OpenSSH, window is granted only as data is written to the local socket, and the local socket is read only while the remote window has room. An application that writes everything to a forwarded port before reading anything now blocks once the window fills, as it would with `ssh -L` or a direct TCP connection; the old unbounded buffering hid this [#1037, #1003]
|
|
8
|
+
* Data forwarded to a local socket just before the remote end sends EOF or closes the channel is no longer truncated or dropped: it is written out before the socket is shut down or closed, without blocking the event loop until the local application reads [#1037]
|
|
9
|
+
* Multi-factor authentication works when the server's AuthenticationMethods order differs from net-ssh's: after each partial success the allowed methods are tried again, as OpenSSH does [#873, #872]
|
|
10
|
+
* `HostKeyError#host`, `#port` and `#ip` now return the connected host, port and IP (`ip` is nil through a ProxyCommand) instead of always nil [#963]
|
|
11
|
+
* OpenSSH-format private keys with Windows (CRLF) line endings now load [#922, #921]
|
|
12
|
+
* Send CHANNEL_EOF before CHANNEL_CLOSE when closing a session's remaining channels, as OpenSSH does; fixes hangs at close with servers that wait for EOF, such as some SFTP servers [#672]
|
|
13
|
+
* ssh_config: trailing `# comments` are now ignored, as in OpenSSH; a `#` inside quotes or in the middle of a word is kept [#990]
|
|
14
|
+
|
|
1
15
|
=== 8.0.0 rc3
|
|
2
16
|
|
|
3
17
|
* Fix "Calling Net::SSH::Buffer methods on HostKeyEntries PubKey is deprecated" warnings printed on every connection to a host in known_hosts [#1026, #1027]
|
data/README.md
CHANGED
|
@@ -39,13 +39,17 @@ Unsecure algoritms will definitely be removed in Net::SSH 8.*.
|
|
|
39
39
|
|
|
40
40
|
| Name | Support | Details |
|
|
41
41
|
|----------------------|-----------------------|----------|
|
|
42
|
-
|
|
|
42
|
+
| rsa-sha2-512 | OK | |
|
|
43
|
+
| rsa-sha2-256 | OK | |
|
|
44
|
+
| ssh-rsa | Disabled by default in 8.0 | SHA-1 signatures, off by default as in OpenSSH 8.8; enable with `HostKeyAlgorithms +ssh-rsa` or `host_key: '+ssh-rsa'` |
|
|
43
45
|
| ssh-ed25519 | OK | Backed by Ruby OpenSSL raw Ed25519 APIs (`openssl >= 3.2.0`) |
|
|
44
46
|
| ecdsa-sha2-nistp521 | OK | [using weak elliptic curves](https://safecurves.cr.yp.to/) |
|
|
45
47
|
| ecdsa-sha2-nistp384 | OK | [using weak elliptic curves](https://safecurves.cr.yp.to/) |
|
|
46
48
|
| ecdsa-sha2-nistp256 | OK | [using weak elliptic curves](https://safecurves.cr.yp.to/) |
|
|
47
49
|
| ssh-dss | Deprecated in 6.0 | unsecure, will be removed in 8.0 |
|
|
48
50
|
|
|
51
|
+
RSA public key authentication likewise uses rsa-sha2-512 or rsa-sha2-256 by default; ssh-rsa and ssh-dss keys are only offered when enabled, e.g. with `PubkeyAcceptedAlgorithms +ssh-rsa` or `pubkey_algorithms: %w[+ssh-rsa]`.
|
|
52
|
+
|
|
49
53
|
### Key Exchange
|
|
50
54
|
|
|
51
55
|
| Name | Support | Details |
|
|
@@ -194,6 +198,11 @@ Ed25519 public key auth is backed by Ruby OpenSSL. The `bcrypt_pbkdf` gem is onl
|
|
|
194
198
|
gem install bcrypt_pbkdf
|
|
195
199
|
```
|
|
196
200
|
|
|
201
|
+
### FIDO/U2F security keys
|
|
202
|
+
|
|
203
|
+
`sk-ecdsa-sha2-nistp256@openssh.com` and `sk-ssh-ed25519@openssh.com` keys work when held in `ssh-agent`.
|
|
204
|
+
Loading `sk-*` private key files from disk is not supported.
|
|
205
|
+
|
|
197
206
|
## RUBY SUPPORT
|
|
198
207
|
|
|
199
208
|
* See [net-ssh.gemspec](https://github.com/net-ssh/net-ssh/blob/master/net-ssh.gemspec) for current versions ruby requirements
|
|
@@ -74,7 +74,7 @@ module Net
|
|
|
74
74
|
end
|
|
75
75
|
|
|
76
76
|
def self.read(datafull, password)
|
|
77
|
-
datafull = datafull.strip
|
|
77
|
+
datafull = datafull.strip.encode(universal_newline: true)
|
|
78
78
|
raise ArgumentError.new("Expected #{MBEGIN} at start of private key") unless datafull.start_with?(MBEGIN)
|
|
79
79
|
raise ArgumentError.new("Expected #{MEND} at end of private key") unless datafull.end_with?(MEND)
|
|
80
80
|
|
|
@@ -20,9 +20,7 @@ module Net
|
|
|
20
20
|
# this.
|
|
21
21
|
attr_reader :key_manager
|
|
22
22
|
|
|
23
|
-
#
|
|
24
|
-
# extended to other keys, e.g after reading of
|
|
25
|
-
# PubkeyAcceptedAlgorithms option from ssh_config file is implemented.
|
|
23
|
+
# public key algorithms to be used
|
|
26
24
|
attr_reader :pubkey_algorithms
|
|
27
25
|
|
|
28
26
|
# Instantiates a new authentication method.
|
|
@@ -31,11 +29,7 @@ module Net
|
|
|
31
29
|
@key_manager = options[:key_manager]
|
|
32
30
|
@options = options
|
|
33
31
|
@prompt = options[:password_prompt]
|
|
34
|
-
@pubkey_algorithms =
|
|
35
|
-
|| %w[rsa-sha2-256-cert-v01@openssh.com
|
|
36
|
-
ssh-rsa-cert-v01@openssh.com
|
|
37
|
-
rsa-sha2-256
|
|
38
|
-
ssh-rsa]
|
|
32
|
+
@pubkey_algorithms = session.transport.pubkey_algorithms
|
|
39
33
|
self.logger = session.logger
|
|
40
34
|
end
|
|
41
35
|
|
|
@@ -15,6 +15,8 @@ module Net
|
|
|
15
15
|
def authenticate(next_service, username, password = nil)
|
|
16
16
|
return false unless key_manager
|
|
17
17
|
|
|
18
|
+
debug { "pubkey_algorithms: #{pubkey_algorithms.join(', ')}" }
|
|
19
|
+
|
|
18
20
|
key_manager.each_identity do |identity|
|
|
19
21
|
return true if authenticate_with(identity, next_service, username)
|
|
20
22
|
end
|
|
@@ -123,7 +125,7 @@ module Net
|
|
|
123
125
|
end
|
|
124
126
|
end
|
|
125
127
|
end
|
|
126
|
-
elsif authenticate_with_alg(identity, next_service, username, type)
|
|
128
|
+
elsif pubkey_algorithms.include?(type) && authenticate_with_alg(identity, next_service, username, type)
|
|
127
129
|
# success
|
|
128
130
|
return true
|
|
129
131
|
end
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
require 'net/ssh/authentication/pub_key_fingerprint'
|
|
2
|
+
|
|
3
|
+
module Net
|
|
4
|
+
module SSH
|
|
5
|
+
module Authentication
|
|
6
|
+
# FIDO/U2F security keys held by ssh-agent. The agent does the signing (incl. user
|
|
7
|
+
# presence); net-ssh only parses and re-serialises the public key blob (see PROTOCOL.u2f).
|
|
8
|
+
module SecurityKey
|
|
9
|
+
SK_ECDSA_SHA2_NISTP256 = "sk-ecdsa-sha2-nistp256@openssh.com"
|
|
10
|
+
SK_SSH_ED25519 = "sk-ssh-ed25519@openssh.com"
|
|
11
|
+
TYPES = [SK_ECDSA_SHA2_NISTP256, SK_SSH_ED25519].freeze
|
|
12
|
+
|
|
13
|
+
def self.understands?(type)
|
|
14
|
+
TYPES.include?(type)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Public key of an agent-held security key; signing is delegated to the agent.
|
|
18
|
+
class PubKey
|
|
19
|
+
include Net::SSH::Authentication::PubKeyFingerprint
|
|
20
|
+
|
|
21
|
+
attr_reader :ssh_type, :curve, :public_key_data, :application
|
|
22
|
+
|
|
23
|
+
# Wire formats:
|
|
24
|
+
# sk-ecdsa-sha2-nistp256@openssh.com: string curve, string Q, string application
|
|
25
|
+
# sk-ssh-ed25519@openssh.com: string public_key, string application
|
|
26
|
+
def self.read_keyblob(type, buffer)
|
|
27
|
+
case type
|
|
28
|
+
when SK_ECDSA_SHA2_NISTP256
|
|
29
|
+
curve = buffer.read_string
|
|
30
|
+
public_key_data = buffer.read_string
|
|
31
|
+
application = buffer.read_string
|
|
32
|
+
new(type, public_key_data, application, curve: curve)
|
|
33
|
+
when SK_SSH_ED25519
|
|
34
|
+
public_key_data = buffer.read_string
|
|
35
|
+
application = buffer.read_string
|
|
36
|
+
new(type, public_key_data, application)
|
|
37
|
+
else
|
|
38
|
+
raise NotImplementedError, "unsupported security key type `#{type}'"
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def initialize(ssh_type, public_key_data, application, curve: nil)
|
|
43
|
+
@ssh_type = ssh_type
|
|
44
|
+
@public_key_data = public_key_data
|
|
45
|
+
@application = application
|
|
46
|
+
@curve = curve
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def to_blob
|
|
50
|
+
buffer = Net::SSH::Buffer.new
|
|
51
|
+
buffer.write_string(ssh_type)
|
|
52
|
+
buffer.write_string(curve) if curve
|
|
53
|
+
buffer.write_string(public_key_data)
|
|
54
|
+
buffer.write_string(application)
|
|
55
|
+
buffer.to_s
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def ssh_signature_type
|
|
59
|
+
ssh_type
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Only used to de-duplicate identities, so a stable key-specific string suffices.
|
|
63
|
+
def to_pem
|
|
64
|
+
"#{ssh_type} #{[to_blob].pack('m0')}"
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
@@ -69,27 +69,22 @@ module Net
|
|
|
69
69
|
|
|
70
70
|
attempted = []
|
|
71
71
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
pubkey_algorithms: options[:pubkey_algorithms] || nil)
|
|
83
|
-
rescue NameError
|
|
84
|
-
debug {"Mechanism #{name} was requested, but isn't a known type. Ignoring it."}
|
|
85
|
-
next
|
|
72
|
+
# After a partial success (sshd AuthenticationMethods) restart from the most preferred method the server now allows, as OpenSSH does.
|
|
73
|
+
loop do
|
|
74
|
+
@partial_success = false
|
|
75
|
+
|
|
76
|
+
@auth_methods.each do |name|
|
|
77
|
+
next unless @allowed_auth_methods.include?(name)
|
|
78
|
+
|
|
79
|
+
attempted << name
|
|
80
|
+
return true if try_authenticate(name, next_service, username, password, key_manager)
|
|
81
|
+
break if @partial_success
|
|
86
82
|
end
|
|
87
83
|
|
|
88
|
-
|
|
89
|
-
rescue Net::SSH::Authentication::DisallowedMethod
|
|
84
|
+
break unless @partial_success
|
|
90
85
|
end
|
|
91
86
|
|
|
92
|
-
error { "all authorization methods failed (tried #{attempted.join(', ')})" }
|
|
87
|
+
error { "all authorization methods failed (tried #{attempted.uniq.join(', ')})" }
|
|
93
88
|
return false
|
|
94
89
|
ensure
|
|
95
90
|
key_manager.finish if key_manager
|
|
@@ -109,6 +104,7 @@ module Net
|
|
|
109
104
|
|
|
110
105
|
when USERAUTH_FAILURE
|
|
111
106
|
@allowed_auth_methods = packet[:authentications].split(/,/)
|
|
107
|
+
@partial_success ||= packet[:partial_success]
|
|
112
108
|
debug { "allowed methods: #{packet[:authentications]}" }
|
|
113
109
|
return packet
|
|
114
110
|
|
|
@@ -136,6 +132,22 @@ module Net
|
|
|
136
132
|
|
|
137
133
|
private
|
|
138
134
|
|
|
135
|
+
def try_authenticate(method_name, next_service, username, password, key_manager)
|
|
136
|
+
debug { "trying #{method_name}" }
|
|
137
|
+
begin
|
|
138
|
+
auth_class = Methods.const_get(method_name.split(/\W+/).map { |p| p.capitalize }.join)
|
|
139
|
+
method = auth_class.new(self,
|
|
140
|
+
key_manager: key_manager, password_prompt: options[:password_prompt])
|
|
141
|
+
rescue NameError
|
|
142
|
+
debug {"Mechanism #{method_name} was requested, but isn't a known type. Ignoring it."}
|
|
143
|
+
return false
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
method.authenticate(next_service, username, password)
|
|
147
|
+
rescue Net::SSH::Authentication::DisallowedMethod
|
|
148
|
+
false
|
|
149
|
+
end
|
|
150
|
+
|
|
139
151
|
# Returns an array of paths to the key files usually defined
|
|
140
152
|
# by system default.
|
|
141
153
|
def default_keys
|
data/lib/net/ssh/buffer.rb
CHANGED
|
@@ -2,6 +2,7 @@ require 'net/ssh/transport/openssl'
|
|
|
2
2
|
|
|
3
3
|
require 'net/ssh/authentication/certificate'
|
|
4
4
|
require 'net/ssh/authentication/ed25519_loader'
|
|
5
|
+
require 'net/ssh/authentication/security_key'
|
|
5
6
|
|
|
6
7
|
module Net
|
|
7
8
|
module SSH
|
|
@@ -338,6 +339,9 @@ module Net
|
|
|
338
339
|
key = Net::SSH::Authentication::ED25519::PubKey.read_keyblob(self)
|
|
339
340
|
when /^ecdsa\-sha2\-(\w*)$/
|
|
340
341
|
key = OpenSSL::PKey::EC.read_keyblob($1, self)
|
|
342
|
+
when Net::SSH::Authentication::SecurityKey::SK_ECDSA_SHA2_NISTP256,
|
|
343
|
+
Net::SSH::Authentication::SecurityKey::SK_SSH_ED25519
|
|
344
|
+
key = Net::SSH::Authentication::SecurityKey::PubKey.read_keyblob(type, self)
|
|
341
345
|
else
|
|
342
346
|
raise NotImplementedError, "unsupported key type `#{type}'"
|
|
343
347
|
end
|
data/lib/net/ssh/buffered_io.rb
CHANGED
|
@@ -166,6 +166,11 @@ module Net
|
|
|
166
166
|
# http://github.com/net-ssh/net-ssh/tree/portfwfix
|
|
167
167
|
#
|
|
168
168
|
module ForwardedBufferedIo
|
|
169
|
+
def self.extended(object) # :nodoc:
|
|
170
|
+
object.instance_variable_set(:@on_sent, nil)
|
|
171
|
+
object.instance_variable_set(:@shutdown_write_when_drained, false)
|
|
172
|
+
end
|
|
173
|
+
|
|
169
174
|
def fill(n = 8192)
|
|
170
175
|
begin
|
|
171
176
|
super(n)
|
|
@@ -182,11 +187,25 @@ module Net
|
|
|
182
187
|
end
|
|
183
188
|
end
|
|
184
189
|
|
|
190
|
+
# Internal and experimental: registers a block called with the number of
|
|
191
|
+
# bytes each #send_pending wrote to the socket.
|
|
192
|
+
def on_sent(&block) # :nodoc:
|
|
193
|
+
@on_sent = block
|
|
194
|
+
end
|
|
195
|
+
|
|
185
196
|
def send_pending
|
|
186
197
|
begin
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
198
|
+
pending = output.length
|
|
199
|
+
result = super
|
|
200
|
+
@on_sent.call(pending - output.length) if @on_sent && output.length < pending
|
|
201
|
+
shutdown_write_if_drained
|
|
202
|
+
result
|
|
203
|
+
rescue Errno::EPIPE, Errno::ECONNRESET => e
|
|
204
|
+
# Like OpenSSH's chan_write_failed: the peer is gone, so drop what it
|
|
205
|
+
# will never read and close, letting the forwarded channel close too.
|
|
206
|
+
debug { "write failed => discarding #{output.length} bytes and closing:#{e}" }
|
|
207
|
+
output.clear!
|
|
208
|
+
close
|
|
190
209
|
return 0
|
|
191
210
|
rescue IOError => e
|
|
192
211
|
if e.message =~ /closed/ then
|
|
@@ -197,6 +216,24 @@ module Net
|
|
|
197
216
|
end
|
|
198
217
|
end
|
|
199
218
|
end
|
|
219
|
+
|
|
220
|
+
# Internal: shuts down the write side once pending output has been sent,
|
|
221
|
+
# as OpenSSH does on CHANNEL_EOF (chan_rcvd_ieof, chan_obuf_empty).
|
|
222
|
+
def shutdown_write_when_drained # :nodoc:
|
|
223
|
+
@shutdown_write_when_drained = true
|
|
224
|
+
shutdown_write_if_drained
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
private
|
|
228
|
+
|
|
229
|
+
def shutdown_write_if_drained
|
|
230
|
+
return unless @shutdown_write_when_drained && output.empty? && !closed?
|
|
231
|
+
|
|
232
|
+
@shutdown_write_when_drained = false
|
|
233
|
+
shutdown(Socket::SHUT_WR)
|
|
234
|
+
rescue Errno::EPIPE, Errno::ENOTCONN => e
|
|
235
|
+
debug { "shutdown failed => shallowing exception:#{e}" }
|
|
236
|
+
end
|
|
200
237
|
end
|
|
201
238
|
end
|
|
202
239
|
end
|
data/lib/net/ssh/config.rb
CHANGED
|
@@ -31,6 +31,7 @@ module Net
|
|
|
31
31
|
# * PreferredAuthentications => maps to the :auth_methods option
|
|
32
32
|
# * ProxyCommand => maps to the :proxy option
|
|
33
33
|
# * ProxyJump => maps to the :proxy option
|
|
34
|
+
# * PubkeyAcceptedAlgorithms (or PubkeyAcceptedKeyTypes) => maps to :pubkey_algorithms
|
|
34
35
|
# * PubKeyAuthentication => maps to the :auth_methods option
|
|
35
36
|
# * RekeyLimit => :rekey_limit
|
|
36
37
|
# * StrictHostKeyChecking => :verify_host_key
|
|
@@ -95,6 +96,7 @@ module Net
|
|
|
95
96
|
next if value.nil?
|
|
96
97
|
|
|
97
98
|
key.downcase!
|
|
99
|
+
value = uncomment(value)
|
|
98
100
|
value = unquote(value)
|
|
99
101
|
|
|
100
102
|
value = case value.strip
|
|
@@ -230,7 +232,7 @@ module Net
|
|
|
230
232
|
user: :user,
|
|
231
233
|
checkhostip: :check_host_ip
|
|
232
234
|
}.freeze
|
|
233
|
-
def translate_config_key(hash, key, value, settings)
|
|
235
|
+
def translate_config_key(hash, key, value, settings) # rubocop:disable Metrics/AbcSize
|
|
234
236
|
case key
|
|
235
237
|
when :stricthostkeychecking
|
|
236
238
|
hash[:verify_host_key] = translate_verify_host_key(value)
|
|
@@ -276,6 +278,8 @@ module Net
|
|
|
276
278
|
if (proxy = setup_proxy(*value))
|
|
277
279
|
hash[:proxy] = proxy
|
|
278
280
|
end
|
|
281
|
+
when :pubkeyacceptedalgorithms, :pubkeyacceptedkeytypes
|
|
282
|
+
hash[:pubkey_algorithms] = value.split(/,/)
|
|
279
283
|
when :pubkeyauthentication
|
|
280
284
|
if value
|
|
281
285
|
(hash[:auth_methods] << 'publickey').uniq!
|
|
@@ -399,6 +403,11 @@ module Net
|
|
|
399
403
|
!condition_matches.empty? && condition_matches.all?
|
|
400
404
|
end
|
|
401
405
|
|
|
406
|
+
# As in OpenSSH's argv_split, "#" starts a comment only at the start of a token and outside quotes.
|
|
407
|
+
def uncomment(string)
|
|
408
|
+
string[/\A(?:\s*(?!#)(?:\\.|"[^"]*"?|'[^']*'?|[^\s"'])+)*/]
|
|
409
|
+
end
|
|
410
|
+
|
|
402
411
|
def unquote(string)
|
|
403
412
|
string =~ /^"(.*)"$/ ? Regexp.last_match(1) : string
|
|
404
413
|
end
|
|
@@ -118,6 +118,8 @@ module Net
|
|
|
118
118
|
|
|
119
119
|
@local_maximum_packet_size = max_pkt_size
|
|
120
120
|
@local_window_size = @local_maximum_window_size = max_win_size
|
|
121
|
+
@local_consumed = 0
|
|
122
|
+
@grant_window_on_consume = false
|
|
121
123
|
|
|
122
124
|
@on_confirm_open = on_confirm_open
|
|
123
125
|
|
|
@@ -254,6 +256,26 @@ module Net
|
|
|
254
256
|
output.append(data.to_s)
|
|
255
257
|
end
|
|
256
258
|
|
|
259
|
+
# Internal and experimental, used by port forwarding: from now on window
|
|
260
|
+
# is granted only for bytes reported via #consumed, so a slow consumer
|
|
261
|
+
# stalls the remote sender instead of buffering everything it sends.
|
|
262
|
+
# Must be called before any data is received.
|
|
263
|
+
def grant_window_on_consume! # :nodoc:
|
|
264
|
+
raise ArgumentError, "grant_window_on_consume! must be called before any data is received" if local_window_size != local_maximum_window_size
|
|
265
|
+
|
|
266
|
+
@grant_window_on_consume = true
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
# Internal and experimental: reports that +bytes+ of received data have
|
|
270
|
+
# been processed, letting the remote end send that much more.
|
|
271
|
+
def consumed(bytes) # :nodoc:
|
|
272
|
+
raise ArgumentError, "consumed requires grant_window_on_consume!" unless @grant_window_on_consume
|
|
273
|
+
raise ArgumentError, "consumed bytes must not be negative" if bytes.negative?
|
|
274
|
+
|
|
275
|
+
@local_consumed += bytes
|
|
276
|
+
adjust_window_for_consumed
|
|
277
|
+
end
|
|
278
|
+
|
|
257
279
|
# Returns true if the channel exists in the channel list of the session,
|
|
258
280
|
# and false otherwise. This can be used to determine whether a channel has
|
|
259
281
|
# been closed or not.
|
|
@@ -598,7 +620,12 @@ module Net
|
|
|
598
620
|
# type, and data.
|
|
599
621
|
def do_extended_data(type, data)
|
|
600
622
|
update_local_window_size(data.length)
|
|
601
|
-
|
|
623
|
+
if @on_extended_data
|
|
624
|
+
@on_extended_data.call(self, type, data)
|
|
625
|
+
elsif @grant_window_on_consume
|
|
626
|
+
# Nothing will ever consume it, so don't let it shrink the window.
|
|
627
|
+
consumed(data.length)
|
|
628
|
+
end
|
|
602
629
|
end
|
|
603
630
|
|
|
604
631
|
# Invokes the #on_eof callback when the server indicates that no
|
|
@@ -651,6 +678,8 @@ module Net
|
|
|
651
678
|
# server telling it that the window size has grown.
|
|
652
679
|
def update_local_window_size(size)
|
|
653
680
|
@local_window_size -= size
|
|
681
|
+
return adjust_window_for_consumed if @grant_window_on_consume
|
|
682
|
+
|
|
654
683
|
if local_window_size < local_maximum_window_size / 2
|
|
655
684
|
connection.send_message(
|
|
656
685
|
Buffer.from(:byte, CHANNEL_WINDOW_ADJUST, :long, remote_id, :long, LOCAL_WINDOW_SIZE_INCREMENT)
|
|
@@ -663,6 +692,21 @@ module Net
|
|
|
663
692
|
end
|
|
664
693
|
end
|
|
665
694
|
|
|
695
|
+
# Mirrors OpenSSH's channel_check_window: once the window is below half,
|
|
696
|
+
# give back what was consumed, growing the maximum window the same way
|
|
697
|
+
# #update_local_window_size does.
|
|
698
|
+
def adjust_window_for_consumed
|
|
699
|
+
return if @local_consumed.zero? || local_window_size >= local_maximum_window_size / 2
|
|
700
|
+
return if remote_id.nil? || local_closed? || remote_closed?
|
|
701
|
+
|
|
702
|
+
growth = (GOOD_LOCAL_MAXIUMUM_WINDOW_SIZE - local_maximum_window_size).clamp(0, LOCAL_WINDOW_SIZE_INCREMENT)
|
|
703
|
+
adjust = @local_consumed + growth
|
|
704
|
+
connection.send_message(Buffer.from(:byte, CHANNEL_WINDOW_ADJUST, :long, remote_id, :long, adjust))
|
|
705
|
+
@local_window_size += adjust
|
|
706
|
+
@local_maximum_window_size += growth
|
|
707
|
+
@local_consumed = 0
|
|
708
|
+
end
|
|
709
|
+
|
|
666
710
|
# Gets an +Array+ of local environment variables in the remote process'
|
|
667
711
|
# environment.
|
|
668
712
|
# A variable name can either be described by a +Regexp+ or +String+.
|
|
@@ -24,7 +24,7 @@ module Net
|
|
|
24
24
|
# # 'ssh' is an instance of Net::SSH::Connection::Session
|
|
25
25
|
# ssh.exec! "/etc/init.d/some_process start"
|
|
26
26
|
# end
|
|
27
|
-
class Session
|
|
27
|
+
class Session # rubocop:disable Metrics/ClassLength
|
|
28
28
|
include Loggable
|
|
29
29
|
include Constants
|
|
30
30
|
|
|
@@ -74,6 +74,8 @@ module Net
|
|
|
74
74
|
@channel_id_counter = -1
|
|
75
75
|
@channels = Hash.new(NilChannel.new(self))
|
|
76
76
|
@listeners = { transport.socket => nil }
|
|
77
|
+
@read_paused = {}
|
|
78
|
+
@draining = {}
|
|
77
79
|
@pending_requests = []
|
|
78
80
|
@channel_open_handlers = {}
|
|
79
81
|
@on_global_request = {}
|
|
@@ -120,12 +122,19 @@ module Net
|
|
|
120
122
|
# connection.
|
|
121
123
|
def close
|
|
122
124
|
info { "closing remaining channels (#{channels.length} open)" }
|
|
123
|
-
|
|
125
|
+
|
|
126
|
+
channels.each do |id, channel|
|
|
127
|
+
channel.eof!
|
|
128
|
+
channel.close
|
|
129
|
+
end
|
|
130
|
+
|
|
124
131
|
begin
|
|
125
132
|
loop(0.1) { channels.any? }
|
|
126
133
|
rescue Net::SSH::Disconnect
|
|
127
134
|
raise unless channels.empty?
|
|
128
135
|
end
|
|
136
|
+
@draining.each_key(&:close)
|
|
137
|
+
@draining.clear
|
|
129
138
|
transport.close
|
|
130
139
|
end
|
|
131
140
|
|
|
@@ -146,10 +155,13 @@ module Net
|
|
|
146
155
|
# a +true+ value for +include_invisible+, then those will be counted.
|
|
147
156
|
#
|
|
148
157
|
# This can be useful for determining whether the event loop should continue
|
|
149
|
-
# to be run.
|
|
158
|
+
# to be run. A forwarded socket still writing out data that arrived before
|
|
159
|
+
# its channel closed also counts as busy.
|
|
150
160
|
#
|
|
151
161
|
# ssh.loop { ssh.busy? }
|
|
152
162
|
def busy?(include_invisible = false)
|
|
163
|
+
return true if @draining.any?
|
|
164
|
+
|
|
153
165
|
if include_invisible
|
|
154
166
|
channels.any?
|
|
155
167
|
else
|
|
@@ -255,7 +267,7 @@ module Net
|
|
|
255
267
|
def ev_do_calculate_rw_wait(wait)
|
|
256
268
|
r = listeners.keys
|
|
257
269
|
w = r.select { |w2| w2.respond_to?(:pending_write?) && w2.pending_write? }
|
|
258
|
-
[r, w, io_select_wait(wait)]
|
|
270
|
+
[r.reject { |io| @read_paused.key?(io) }, w, io_select_wait(wait)]
|
|
259
271
|
end
|
|
260
272
|
|
|
261
273
|
# This is called internally as part of #process.
|
|
@@ -281,6 +293,7 @@ module Net
|
|
|
281
293
|
|
|
282
294
|
Array(writers).each do |writer|
|
|
283
295
|
writer.send_pending
|
|
296
|
+
close_if_drained(writer) if @draining.key?(writer)
|
|
284
297
|
end
|
|
285
298
|
end
|
|
286
299
|
|
|
@@ -485,9 +498,36 @@ module Net
|
|
|
485
498
|
# Removes the given io object from the listeners collection, so that the
|
|
486
499
|
# event loop will no longer monitor it.
|
|
487
500
|
def stop_listening_to(io)
|
|
501
|
+
@read_paused.delete(io)
|
|
502
|
+
@draining.delete(io)
|
|
488
503
|
listeners.delete(io)
|
|
489
504
|
end
|
|
490
505
|
|
|
506
|
+
# Internal and experimental, used by port forwarding: stops the event
|
|
507
|
+
# loop from reading +io+ while it keeps flushing its pending output.
|
|
508
|
+
def pause_reading(io) # :nodoc:
|
|
509
|
+
raise ArgumentError, "the transport socket can't be paused" if io.equal?(transport.socket)
|
|
510
|
+
|
|
511
|
+
@read_paused[io] = true if listeners.key?(io)
|
|
512
|
+
end
|
|
513
|
+
|
|
514
|
+
def resume_reading(io) # :nodoc:
|
|
515
|
+
@read_paused.delete(io)
|
|
516
|
+
end
|
|
517
|
+
|
|
518
|
+
def reading_paused?(io) # :nodoc:
|
|
519
|
+
@read_paused.key?(io)
|
|
520
|
+
end
|
|
521
|
+
|
|
522
|
+
# Internal and experimental, used by port forwarding: stops reading +io+
|
|
523
|
+
# but keeps flushing its pending output, then closes it and stops
|
|
524
|
+
# listening, as OpenSSH's chan_rcvd_oclose does.
|
|
525
|
+
def close_when_drained(io) # :nodoc:
|
|
526
|
+
pause_reading(io)
|
|
527
|
+
@draining[io] = true
|
|
528
|
+
close_if_drained(io)
|
|
529
|
+
end
|
|
530
|
+
|
|
491
531
|
# Returns a reference to the Net::SSH::Service::Forward service, which can
|
|
492
532
|
# be used for forwarding ports over SSH.
|
|
493
533
|
def forward
|
|
@@ -565,6 +605,13 @@ module Net
|
|
|
565
605
|
end
|
|
566
606
|
end
|
|
567
607
|
|
|
608
|
+
def close_if_drained(io)
|
|
609
|
+
return if io.pending_write? && !io.closed? && !closed? && listeners.key?(io)
|
|
610
|
+
|
|
611
|
+
io.close unless io.closed?
|
|
612
|
+
stop_listening_to(io)
|
|
613
|
+
end
|
|
614
|
+
|
|
568
615
|
def channel_closed(channel)
|
|
569
616
|
channel.remote_closed!
|
|
570
617
|
channel.close
|
data/lib/net/ssh/errors.rb
CHANGED
|
@@ -62,19 +62,21 @@ module Net
|
|
|
62
62
|
@data && @data[:fingerprint]
|
|
63
63
|
end
|
|
64
64
|
|
|
65
|
-
# Returns the host name
|
|
65
|
+
# Returns the host name that was connected to.
|
|
66
66
|
def host
|
|
67
|
-
|
|
67
|
+
session&.host
|
|
68
68
|
end
|
|
69
69
|
|
|
70
|
-
# Returns the port number
|
|
70
|
+
# Returns the port number that was connected to.
|
|
71
71
|
def port
|
|
72
|
-
|
|
72
|
+
session&.port
|
|
73
73
|
end
|
|
74
74
|
|
|
75
|
-
# Returns the IP address of the remote host, as reported by the socket
|
|
75
|
+
# Returns the IP address of the remote host, as reported by the socket,
|
|
76
|
+
# or nil when connected through a ProxyCommand.
|
|
76
77
|
def ip
|
|
77
|
-
|
|
78
|
+
peer_ip = session&.socket&.peer_ip
|
|
79
|
+
peer_ip unless peer_ip == Net::SSH::Transport::PacketStream::PROXY_COMMAND_HOST_IP
|
|
78
80
|
end
|
|
79
81
|
|
|
80
82
|
# Returns the key itself, as reported by the remote host.
|
|
@@ -87,6 +89,12 @@ module Net
|
|
|
87
89
|
def remember_host!
|
|
88
90
|
@callback.call
|
|
89
91
|
end
|
|
92
|
+
|
|
93
|
+
private
|
|
94
|
+
|
|
95
|
+
def session
|
|
96
|
+
@data && @data[:session]
|
|
97
|
+
end
|
|
90
98
|
end
|
|
91
99
|
|
|
92
100
|
# Raised when the cached key for a particular host does not match the
|
data/lib/net/ssh/key_factory.rb
CHANGED
|
@@ -2,6 +2,7 @@ require 'net/ssh/transport/openssl'
|
|
|
2
2
|
require 'net/ssh/prompt'
|
|
3
3
|
|
|
4
4
|
require 'net/ssh/authentication/ed25519_loader'
|
|
5
|
+
require 'net/ssh/authentication/security_key'
|
|
5
6
|
|
|
6
7
|
module Net
|
|
7
8
|
module SSH
|
|
@@ -92,7 +93,7 @@ module Net
|
|
|
92
93
|
blob = nil
|
|
93
94
|
begin
|
|
94
95
|
blob = fields.shift
|
|
95
|
-
end while !blob.nil? &&
|
|
96
|
+
end while !blob.nil? && !public_key_type?(blob)
|
|
96
97
|
blob = fields.shift
|
|
97
98
|
|
|
98
99
|
raise Net::SSH::Exception, "public key at #{filename} is not valid" if blob.nil?
|
|
@@ -104,6 +105,11 @@ module Net
|
|
|
104
105
|
|
|
105
106
|
private
|
|
106
107
|
|
|
108
|
+
def public_key_type?(type)
|
|
109
|
+
/^(ssh-(rsa|dss|ed25519)|ecdsa-sha2-nistp\d+)(-cert-v01@openssh\.com)?$/.match?(type) ||
|
|
110
|
+
Net::SSH::Authentication::SecurityKey.understands?(type)
|
|
111
|
+
end
|
|
112
|
+
|
|
107
113
|
# rubocop:disable Style/Documentation, Lint/DuplicateMethods
|
|
108
114
|
class KeyType
|
|
109
115
|
def self.read(key_data, passphrase)
|
|
@@ -320,6 +320,11 @@ module Net
|
|
|
320
320
|
session.listen_to(client)
|
|
321
321
|
channel[:socket] = client
|
|
322
322
|
|
|
323
|
+
# Grant window only for data the local end has accepted, so a slow
|
|
324
|
+
# reader stalls the remote sender instead of filling our memory.
|
|
325
|
+
channel.grant_window_on_consume!
|
|
326
|
+
client.on_sent { |bytes| channel.consumed(bytes) }
|
|
327
|
+
|
|
323
328
|
channel.on_data do |ch, data|
|
|
324
329
|
debug { "data:#{data.length} on #{type} forwarded channel" }
|
|
325
330
|
ch[:socket].enqueue(data)
|
|
@@ -327,36 +332,36 @@ module Net
|
|
|
327
332
|
|
|
328
333
|
channel.on_eof do |ch|
|
|
329
334
|
debug { "eof #{type} on #{type} forwarded channel" }
|
|
330
|
-
|
|
331
|
-
ch[:socket].send_pending
|
|
332
|
-
ch[:socket].shutdown Socket::SHUT_WR
|
|
333
|
-
rescue IOError => e
|
|
334
|
-
if e.message =~ /closed/ then
|
|
335
|
-
debug { "epipe in on_eof => shallowing exception:#{e}" }
|
|
336
|
-
else
|
|
337
|
-
raise
|
|
338
|
-
end
|
|
339
|
-
rescue Errno::EPIPE => e
|
|
340
|
-
debug { "epipe in on_eof => shallowing exception:#{e}" }
|
|
341
|
-
rescue Errno::ENOTCONN => e
|
|
342
|
-
debug { "enotconn in on_eof => shallowing exception:#{e}" }
|
|
343
|
-
end
|
|
335
|
+
ch[:socket].shutdown_write_when_drained
|
|
344
336
|
end
|
|
345
337
|
|
|
346
338
|
channel.on_close do |ch|
|
|
347
339
|
debug { "closing #{type} forwarded channel" }
|
|
348
|
-
ch[:socket]
|
|
349
|
-
session.stop_listening_to(ch[:socket])
|
|
340
|
+
session.close_when_drained(ch[:socket])
|
|
350
341
|
end
|
|
351
342
|
|
|
352
343
|
channel.on_process do |ch|
|
|
353
|
-
|
|
344
|
+
socket = ch[:socket]
|
|
345
|
+
if socket.closed?
|
|
354
346
|
ch.info { "#{type} forwarded connection closed" }
|
|
347
|
+
session.stop_listening_to(socket)
|
|
355
348
|
ch.close
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
349
|
+
else
|
|
350
|
+
if socket.available > 0
|
|
351
|
+
data = socket.read_available(8192)
|
|
352
|
+
ch.debug { "read #{data.length} bytes from client, sending over #{type} forwarded connection" }
|
|
353
|
+
ch.send_data(data)
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
# Like OpenSSH's channel_pre_open, only read more once the remote
|
|
357
|
+
# window can take what is already queued; writes to the socket keep
|
|
358
|
+
# flowing. ch.output is still unsent here because Channel#process
|
|
359
|
+
# runs this block before enqueue_pending_output.
|
|
360
|
+
if ch.output.length + socket.available < ch.remote_window_size.to_i
|
|
361
|
+
session.resume_reading(socket)
|
|
362
|
+
else
|
|
363
|
+
session.pause_reading(socket)
|
|
364
|
+
end
|
|
360
365
|
end
|
|
361
366
|
end
|
|
362
367
|
end
|
|
@@ -8,6 +8,7 @@ require 'net/ssh/transport/kex'
|
|
|
8
8
|
require 'net/ssh/transport/kex/curve25519_sha256_loader'
|
|
9
9
|
require 'net/ssh/transport/server_version'
|
|
10
10
|
require 'net/ssh/authentication/ed25519_loader'
|
|
11
|
+
require 'net/ssh/authentication/security_key'
|
|
11
12
|
|
|
12
13
|
module Net
|
|
13
14
|
module SSH
|
|
@@ -19,7 +20,7 @@ module Net
|
|
|
19
20
|
#
|
|
20
21
|
# You will never instantiate or reference this directly. It is used
|
|
21
22
|
# internally by the transport layer.
|
|
22
|
-
class Algorithms
|
|
23
|
+
class Algorithms # rubocop:disable Metrics/ClassLength
|
|
23
24
|
include Loggable
|
|
24
25
|
include Constants
|
|
25
26
|
|
|
@@ -31,11 +32,8 @@ module Net
|
|
|
31
32
|
ecdsa-sha2-nistp521
|
|
32
33
|
ecdsa-sha2-nistp384
|
|
33
34
|
ecdsa-sha2-nistp256
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
ssh-rsa
|
|
37
|
-
rsa-sha2-256
|
|
38
|
-
rsa-sha2-512],
|
|
35
|
+
rsa-sha2-512
|
|
36
|
+
rsa-sha2-256],
|
|
39
37
|
|
|
40
38
|
kex: %w[ecdh-sha2-nistp521
|
|
41
39
|
ecdh-sha2-nistp384
|
|
@@ -52,7 +50,16 @@ module Net
|
|
|
52
50
|
|
|
53
51
|
hmac: %w[hmac-sha2-512-etm@openssh.com hmac-sha2-256-etm@openssh.com
|
|
54
52
|
hmac-sha2-512 hmac-sha2-256
|
|
55
|
-
hmac-sha1]
|
|
53
|
+
hmac-sha1],
|
|
54
|
+
|
|
55
|
+
pubkey_algorithms: %w[ssh-ed25519-cert-v01@openssh.com
|
|
56
|
+
ecdsa-sha2-nistp256-cert-v01@openssh.com ecdsa-sha2-nistp384-cert-v01@openssh.com
|
|
57
|
+
ecdsa-sha2-nistp521-cert-v01@openssh.com
|
|
58
|
+
rsa-sha2-512-cert-v01@openssh.com rsa-sha2-256-cert-v01@openssh.com
|
|
59
|
+
ssh-ed25519
|
|
60
|
+
ecdsa-sha2-nistp256 ecdsa-sha2-nistp384 ecdsa-sha2-nistp521] +
|
|
61
|
+
[Authentication::SecurityKey::SK_SSH_ED25519, Authentication::SecurityKey::SK_ECDSA_SHA2_NISTP256] +
|
|
62
|
+
%w[rsa-sha2-512 rsa-sha2-256]
|
|
56
63
|
}.freeze
|
|
57
64
|
|
|
58
65
|
if Net::SSH::Transport::ChaCha20Poly1305CipherLoader::LOADED
|
|
@@ -76,7 +83,8 @@ module Net
|
|
|
76
83
|
|
|
77
84
|
# Define all algorithms, with the deprecated, supported by Net::SSH.
|
|
78
85
|
ALGORITHMS = {
|
|
79
|
-
host_key: DEFAULT_ALGORITHMS[:host_key] +
|
|
86
|
+
host_key: DEFAULT_ALGORITHMS[:host_key] +
|
|
87
|
+
%w[ssh-rsa-cert-v01@openssh.com ssh-rsa-cert-v00@openssh.com ssh-rsa ssh-dss],
|
|
80
88
|
|
|
81
89
|
kex: DEFAULT_ALGORITHMS[:kex] +
|
|
82
90
|
%w[diffie-hellman-group-exchange-sha1
|
|
@@ -98,6 +106,8 @@ module Net
|
|
|
98
106
|
hmac-md5 hmac-md5-96
|
|
99
107
|
none],
|
|
100
108
|
|
|
109
|
+
pubkey_algorithms: DEFAULT_ALGORITHMS[:pubkey_algorithms] + %w[ssh-rsa-cert-v01@openssh.com ssh-rsa ssh-dss],
|
|
110
|
+
|
|
101
111
|
compression: %w[none zlib@openssh.com zlib],
|
|
102
112
|
language: %w[]
|
|
103
113
|
}.freeze
|
|
@@ -269,7 +279,9 @@ module Net
|
|
|
269
279
|
|
|
270
280
|
ALGORITHMS.each do |algorithm, supported|
|
|
271
281
|
algorithms[algorithm] = compose_algorithm_list(
|
|
272
|
-
supported,
|
|
282
|
+
supported,
|
|
283
|
+
DEFAULT_ALGORITHMS[algorithm],
|
|
284
|
+
options[algorithm] || DEFAULT_ALGORITHMS[algorithm],
|
|
273
285
|
options[:append_all_supported_algorithms]
|
|
274
286
|
)
|
|
275
287
|
end
|
|
@@ -287,7 +299,11 @@ module Net
|
|
|
287
299
|
# existing known key for the host has preference.
|
|
288
300
|
|
|
289
301
|
existing_keys = session.host_keys
|
|
290
|
-
host_keys = existing_keys.flat_map { |key| key.respond_to?(:ssh_types) ? key.ssh_types : [key.ssh_type] }
|
|
302
|
+
host_keys = existing_keys.flat_map { |key| key.respond_to?(:ssh_types) ? key.ssh_types : [key.ssh_type] }
|
|
303
|
+
# A known ssh-rsa key also verifies with rsa-sha2-*, and must not re-enable disabled SHA-1 algorithms.
|
|
304
|
+
disabled_sha1 = %w[ssh-rsa ssh-rsa-cert-v01@openssh.com ssh-rsa-cert-v00@openssh.com] - algorithms[:host_key]
|
|
305
|
+
host_keys = host_keys.flat_map { |type| type == 'ssh-rsa' ? %w[rsa-sha2-512 rsa-sha2-256 ssh-rsa] : [type] }
|
|
306
|
+
host_keys = (host_keys - disabled_sha1).uniq
|
|
291
307
|
algorithms[:host_key].each do |name|
|
|
292
308
|
host_keys << name unless host_keys.include?(name)
|
|
293
309
|
end
|
|
@@ -296,21 +312,27 @@ module Net
|
|
|
296
312
|
end
|
|
297
313
|
|
|
298
314
|
# Composes the list of algorithms by taking supported algorithms and matching with supplied options.
|
|
299
|
-
def compose_algorithm_list(supported, option, append_all_supported_algorithms = false)
|
|
315
|
+
def compose_algorithm_list(supported, defaults, option, append_all_supported_algorithms = false)
|
|
300
316
|
return supported.dup unless option
|
|
301
317
|
|
|
302
318
|
list = []
|
|
303
319
|
option = Array(option).compact.uniq
|
|
304
320
|
|
|
305
|
-
if option.first && option.first.start_with?('+', '-')
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
321
|
+
if option.first && option.first.start_with?('+', '-', '^')
|
|
322
|
+
ops = { '^' => [], '+' => [], '-' => [] }
|
|
323
|
+
cur_list = nil
|
|
324
|
+
option.each do |cur_opt|
|
|
325
|
+
if ops.key?(cur_opt[0])
|
|
326
|
+
cur_list = ops[cur_opt[0]]
|
|
327
|
+
cur_opt = cur_opt[1..-1]
|
|
328
|
+
end
|
|
329
|
+
cur_list << cur_opt
|
|
330
|
+
end
|
|
310
331
|
|
|
311
|
-
list
|
|
332
|
+
list = ops['^'] + defaults + ops['+']
|
|
333
|
+
list.concat(supported) if append_all_supported_algorithms
|
|
312
334
|
|
|
313
|
-
|
|
335
|
+
ops['-'].each do |opt|
|
|
314
336
|
if opt.include?('*')
|
|
315
337
|
opt_escaped = Regexp.escape(opt)
|
|
316
338
|
algo_re = /\A#{opt_escaped.gsub('\*', '[A-Za-z\d\-@\.]*')}\z/
|
data/lib/net/ssh/version.rb
CHANGED
data/lib/net/ssh.rb
CHANGED
|
@@ -174,11 +174,13 @@ module Net
|
|
|
174
174
|
# * :properties => a hash of key/value pairs to add to the new connection's
|
|
175
175
|
# properties (see Net::SSH::Connection::Session#properties)
|
|
176
176
|
# * :proxy => a proxy instance (see Proxy) to use when connecting
|
|
177
|
-
# * :pubkey_algorithms => the public key authentication algorithms to
|
|
178
|
-
#
|
|
179
|
-
#
|
|
180
|
-
#
|
|
181
|
-
#
|
|
177
|
+
# * :pubkey_algorithms => the public key authentication algorithms to offer,
|
|
178
|
+
# in order of preference (e.g. %w[ssh-ed25519 rsa-sha2-512 rsa-sha2-256]).
|
|
179
|
+
# Keys whose type isn't listed are skipped. Like PubkeyAcceptedAlgorithms,
|
|
180
|
+
# entries starting with +, - or ^ modify the default list. ssh-rsa (SHA-1),
|
|
181
|
+
# ssh-rsa-cert-v01@openssh.com and ssh-dss are supported but not offered
|
|
182
|
+
# unless listed here (e.g. '+ssh-rsa') or :append_all_supported_algorithms
|
|
183
|
+
# is set.
|
|
182
184
|
# * :rekey_blocks_limit => the max number of blocks to process before rekeying
|
|
183
185
|
# * :rekey_limit => the max number of bytes to process before rekeying
|
|
184
186
|
# * :rekey_packet_limit => the max number of packets to process before rekeying
|
data.tar.gz.sig
CHANGED
|
Binary file
|
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: 8.0.0.
|
|
4
|
+
version: 8.0.0.rc4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jamis Buck
|
|
@@ -224,6 +224,7 @@ files:
|
|
|
224
224
|
- lib/net/ssh/authentication/methods/publickey.rb
|
|
225
225
|
- lib/net/ssh/authentication/pageant.rb
|
|
226
226
|
- lib/net/ssh/authentication/pub_key_fingerprint.rb
|
|
227
|
+
- lib/net/ssh/authentication/security_key.rb
|
|
227
228
|
- lib/net/ssh/authentication/session.rb
|
|
228
229
|
- lib/net/ssh/buffer.rb
|
|
229
230
|
- lib/net/ssh/buffered_io.rb
|
metadata.gz.sig
CHANGED
|
Binary file
|