net-ssh 7.3.6 → 8.1.2

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.
Files changed (80) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/CHANGES.txt +106 -5
  4. data/README.md +71 -57
  5. data/lib/net/ssh/authentication/agent.rb +35 -11
  6. data/lib/net/ssh/authentication/certificate.rb +2 -2
  7. data/lib/net/ssh/authentication/ed25519.rb +137 -44
  8. data/lib/net/ssh/authentication/ed25519_loader.rb +14 -9
  9. data/lib/net/ssh/authentication/key_manager.rb +22 -11
  10. data/lib/net/ssh/authentication/methods/abstract.rb +6 -8
  11. data/lib/net/ssh/authentication/methods/hostbased.rb +3 -3
  12. data/lib/net/ssh/authentication/methods/publickey.rb +52 -38
  13. data/lib/net/ssh/authentication/security_key.rb +71 -0
  14. data/lib/net/ssh/authentication/session.rb +30 -17
  15. data/lib/net/ssh/buffer.rb +8 -3
  16. data/lib/net/ssh/buffered_io.rb +40 -3
  17. data/lib/net/ssh/config.rb +123 -50
  18. data/lib/net/ssh/connection/channel.rb +49 -2
  19. data/lib/net/ssh/connection/session.rb +151 -13
  20. data/lib/net/ssh/errors.rb +34 -6
  21. data/lib/net/ssh/key_factory.rb +7 -1
  22. data/lib/net/ssh/known_hosts.rb +103 -38
  23. data/lib/net/ssh/multiplex/client.rb +162 -0
  24. data/lib/net/ssh/multiplex/control_path.rb +120 -0
  25. data/lib/net/ssh/multiplex/proxy_transport.rb +218 -0
  26. data/lib/net/ssh/multiplex.rb +127 -0
  27. data/lib/net/ssh/prompt.rb +43 -5
  28. data/lib/net/ssh/proxy/https.rb +28 -4
  29. data/lib/net/ssh/proxy/jump.rb +61 -8
  30. data/lib/net/ssh/service/forward.rb +35 -24
  31. data/lib/net/ssh/test.rb +2 -1
  32. data/lib/net/ssh/transport/algorithms.rb +92 -34
  33. data/lib/net/ssh/transport/chacha20_poly1305_cipher.rb +89 -16
  34. data/lib/net/ssh/transport/chacha20_poly1305_cipher_loader.rb +20 -1
  35. data/lib/net/ssh/transport/cipher_factory.rb +23 -1
  36. data/lib/net/ssh/transport/constants.rb +1 -0
  37. data/lib/net/ssh/transport/gcm_cipher.rb +27 -6
  38. data/lib/net/ssh/transport/hmac/sha1_etm.rb +12 -0
  39. data/lib/net/ssh/transport/hmac.rb +2 -0
  40. data/lib/net/ssh/transport/kex/curve25519_sha256.rb +41 -9
  41. data/lib/net/ssh/transport/kex/curve25519_sha256_loader.rb +14 -6
  42. data/lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb +8 -0
  43. data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha1.rb +45 -15
  44. data/lib/net/ssh/transport/openssl.rb +2 -1
  45. data/lib/net/ssh/transport/packet_stream.rb +12 -0
  46. data/lib/net/ssh/transport/session.rb +61 -45
  47. data/lib/net/ssh/verifiers/accept_new_or_local_tunnel.rb +3 -1
  48. data/lib/net/ssh/verifiers/always.rb +16 -4
  49. data/lib/net/ssh/verifiers/never.rb +2 -1
  50. data/lib/net/ssh/version.rb +3 -3
  51. data/lib/net/ssh.rb +164 -63
  52. data.tar.gz.sig +0 -0
  53. metadata +33 -61
  54. metadata.gz.sig +0 -0
  55. data/.dockerignore +0 -9
  56. data/.github/FUNDING.yml +0 -1
  57. data/.github/config/rubocop_linter_action.yml +0 -4
  58. data/.github/workflows/ci-with-docker.yml +0 -58
  59. data/.github/workflows/ci.yml +0 -99
  60. data/.github/workflows/rubocop.yml +0 -16
  61. data/.gitignore +0 -15
  62. data/.rubocop.yml +0 -22
  63. data/.rubocop_todo.yml +0 -1083
  64. data/DEVELOPMENT.md +0 -23
  65. data/Dockerfile +0 -32
  66. data/Dockerfile.openssl3 +0 -17
  67. data/Dockerfile.zlib_ng +0 -29
  68. data/Gemfile +0 -13
  69. data/Gemfile.noed25519 +0 -12
  70. data/Gemfile.norbnacl +0 -12
  71. data/ISSUE_TEMPLATE.md +0 -30
  72. data/Manifest +0 -132
  73. data/Rakefile +0 -196
  74. data/SECURITY.md +0 -4
  75. data/THANKS.txt +0 -110
  76. data/appveyor.yml +0 -58
  77. data/docker-compose.yml +0 -23
  78. data/net-ssh-public_cert.pem +0 -21
  79. data/net-ssh.gemspec +0 -48
  80. data/support/ssh_tunnel_bug.rb +0 -65
@@ -56,6 +56,11 @@ module Net
56
56
  # The hash of options that were given to the object at initialization.
57
57
  attr_reader :options
58
58
 
59
+ # The signature algorithms the server accepts for publickey
60
+ # authentication, from its RFC 8308 server-sig-algs extension, or nil if
61
+ # it did not send one.
62
+ attr_reader :server_sig_algs
63
+
59
64
  # Instantiates a new transport layer abstraction. This will block until
60
65
  # the initial key exchange completes, leaving you with a ready-to-use
61
66
  # transport session.
@@ -66,6 +71,7 @@ module Net
66
71
  @port = options[:port] || DEFAULT_PORT
67
72
  @bind_address = options[:bind_address] || nil
68
73
  @options = options
74
+ @host_key_verifier = select_host_key_verifier(options[:verify_host_key])
69
75
 
70
76
  @socket =
71
77
  if (factory = options[:proxy])
@@ -79,21 +85,24 @@ module Net
79
85
 
80
86
  @socket.extend(PacketStream)
81
87
  @socket.logger = @logger
88
+ set_no_delay unless options[:no_delay] == false
82
89
 
83
90
  debug { "connection established" }
84
91
 
85
92
  @queue = []
86
93
  @queued_bytes = 0
87
94
 
88
- @host_key_verifier = select_host_key_verifier(options[:verify_host_key])
89
-
90
95
  @server_version = ServerVersion.new(socket, logger, options[:timeout])
91
96
 
92
97
  @algorithms = Algorithms.new(self, options)
93
98
  @algorithms.start
94
99
  wait { algorithms.initialized? }
95
100
  rescue Errno::ETIMEDOUT
101
+ @socket&.close
96
102
  raise Net::SSH::ConnectionTimeout
103
+ rescue StandardError
104
+ @socket&.close
105
+ raise
97
106
  end
98
107
 
99
108
  def host_keys
@@ -103,6 +112,10 @@ module Net
103
112
  end
104
113
  end
105
114
 
115
+ def pubkey_algorithms
116
+ algorithms[:pubkey_algorithms]
117
+ end
118
+
106
119
  # Returns the host (and possibly IP address) in a format compatible with
107
120
  # SSH known-host files.
108
121
  def host_as_string
@@ -211,12 +224,16 @@ module Net
211
224
  when DEBUG
212
225
  send(packet[:always_display] ? :fatal : :debug) { packet[:message] }
213
226
 
227
+ when EXT_INFO
228
+ accept_ext_info(packet)
229
+
214
230
  when KEXINIT
215
231
  algorithms.accept_kexinit(packet)
216
232
 
217
233
  else
218
234
  return packet if algorithms.allow?(packet)
219
- raise Net::SSH::Exception, "unexpected packet type #{packet.type} during initial key exchange" unless algorithms.initialized?
235
+ # Algorithms#rekey! clears initialized?, so the session id is what marks the first key exchange as done.
236
+ raise Net::SSH::Exception, "unexpected packet type #{packet.type} during initial key exchange" unless algorithms.session_id
220
237
 
221
238
  push(packet)
222
239
  end
@@ -287,29 +304,45 @@ module Net
287
304
 
288
305
  private
289
306
 
307
+ # Parses an RFC 8308 EXT_INFO packet, which servers send after the first
308
+ # NEWKEYS and possibly again just before USERAUTH_SUCCESS.
309
+ def accept_ext_info(packet)
310
+ count = packet.read_long or raise Net::SSH::Exception, "malformed EXT_INFO packet from server"
311
+ count.times do
312
+ name = packet.read_string
313
+ value = packet.read_string
314
+ raise Net::SSH::Exception, "malformed EXT_INFO packet from server" if name.nil? || value.nil?
315
+
316
+ debug { "EXT_INFO #{name}: #{value}" }
317
+ @server_sig_algs = value.split(",") if name == "server-sig-algs"
318
+ end
319
+ end
320
+
290
321
  def shift_queue
291
322
  packet = @queue.shift
292
323
  @queued_bytes -= packet.length
293
324
  packet
294
325
  end
295
326
 
296
- # Compatibility verifier which allows users to keep using
297
- # custom verifier code without adding new :verify_signature
298
- # method.
299
- class CompatibleVerifier
300
- def initialize(verifier)
301
- @verifier = verifier
302
- end
327
+ # Turns Nagle's algorithm off on the socket, so that a packet written
328
+ # while the previous one is still unacknowledged goes out right away
329
+ # instead of waiting for the server's ACK. A ProxyCommand hands the
330
+ # transport a pipe rather than a socket, and the command on its other
331
+ # end owns the TCP connection, so there is nothing to set in that case.
332
+ def set_no_delay
333
+ return unless socket.respond_to?(:setsockopt)
303
334
 
304
- def verify(arguments)
305
- @verifier.verify(arguments)
306
- end
307
-
308
- def verify_signature(&block)
309
- yield
310
- end
335
+ debug { "setting TCP_NODELAY" }
336
+ socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
311
337
  end
312
338
 
339
+ REMOVED_VERIFY_HOST_KEY_VALUES = {
340
+ false => :never,
341
+ true => :accept_new_or_local_tunnel,
342
+ very: :accept_new,
343
+ secure: :always
344
+ }.freeze
345
+
313
346
  # Instantiates a new host-key verification class, based on the value of
314
347
  # the parameter.
315
348
  #
@@ -323,46 +356,29 @@ module Net
323
356
  #
324
357
  # If the argument happens to respond to :verify and :verify_signature,
325
358
  # it is returned directly. Otherwise, an exception is raised.
326
- #
327
- # Values false, true, and :very were deprecated in
328
- # [#595](https://github.com/net-ssh/net-ssh/pull/595)
329
359
  def select_host_key_verifier(verifier)
330
360
  case verifier
331
- when false
332
- Kernel.warn('verify_host_key: false is deprecated, use :never')
333
- Net::SSH::Verifiers::Never.new
334
361
  when :never then
335
362
  Net::SSH::Verifiers::Never.new
336
- when true
337
- Kernel.warn('verify_host_key: true is deprecated, use :accept_new_or_local_tunnel')
338
- Net::SSH::Verifiers::AcceptNewOrLocalTunnel.new
339
363
  when :accept_new_or_local_tunnel, nil then
340
364
  Net::SSH::Verifiers::AcceptNewOrLocalTunnel.new
341
- when :very
342
- Kernel.warn('verify_host_key: :very is deprecated, use :accept_new')
343
- Net::SSH::Verifiers::AcceptNew.new
344
365
  when :accept_new then
345
366
  Net::SSH::Verifiers::AcceptNew.new
346
- when :secure then
347
- Kernel.warn('verify_host_key: :secure is deprecated, use :always')
348
- Net::SSH::Verifiers::Always.new
349
367
  when :always then
350
368
  Net::SSH::Verifiers::Always.new
369
+ when *REMOVED_VERIFY_HOST_KEY_VALUES.keys
370
+ raise ArgumentError,
371
+ "verify_host_key: #{verifier.inspect} has been removed, use #{REMOVED_VERIFY_HOST_KEY_VALUES[verifier].inspect}"
351
372
  else
352
- if verifier.respond_to?(:verify)
353
- if verifier.respond_to?(:verify_signature)
354
- verifier
355
- else
356
- Kernel.warn("Warning: verifier without :verify_signature is deprecated")
357
- CompatibleVerifier.new(verifier)
358
- end
359
- else
360
- raise(
361
- ArgumentError,
362
- "Invalid argument to :verify_host_key (or deprecated " \
363
- ":paranoid): #{verifier.inspect}"
364
- )
373
+ raise ArgumentError, "Invalid argument to :verify_host_key: #{verifier.inspect}" unless verifier.respond_to?(:verify)
374
+
375
+ unless verifier.respond_to?(:verify_signature)
376
+ raise ArgumentError,
377
+ "verify_host_key verifier #{verifier.inspect} must implement verify_signature(&block): " \
378
+ "yield to the block, which checks the server's host key signature, and return its result"
365
379
  end
380
+
381
+ verifier
366
382
  end
367
383
  end
368
384
  end
@@ -7,11 +7,13 @@ module Net
7
7
  # verify a connection if the server is the localhost and the port is a
8
8
  # nonstandard port number. Those two conditions will typically mean the
9
9
  # connection is being tunnelled through a forwarded port, so the known-hosts
10
- # file will not be helpful (in general).
10
+ # file will not be helpful (in general). A key marked @revoked is refused
11
+ # either way.
11
12
  class AcceptNewOrLocalTunnel < AcceptNew
12
13
  # Tries to determine if the connection is being tunnelled, and if so,
13
14
  # returns true. Otherwise, performs the standard strict verification.
14
15
  def verify(arguments)
16
+ refuse_revoked!(arguments)
15
17
  return true if tunnelled?(arguments)
16
18
 
17
19
  super
@@ -10,10 +10,13 @@ module Net
10
10
  # (HostKeyUnknown). This is in contrast to the "Strict" class, which will
11
11
  # silently add the key to your known_hosts file. If the server does appear at
12
12
  # least once, but the key given does not match any known for the server, an
13
- # exception will be raised (HostKeyMismatch).
13
+ # exception will be raised (HostKeyMismatch). A key marked @revoked in a
14
+ # host file is always refused (HostKeyRevoked).
14
15
  # Otherwise, this returns true.
15
16
  class Always
16
17
  def verify(arguments)
18
+ refuse_revoked!(arguments)
19
+
17
20
  host_keys = arguments[:session].host_keys
18
21
 
19
22
  # We've never seen this host before, so raise an exception.
@@ -47,13 +50,22 @@ module Net
47
50
 
48
51
  private
49
52
 
53
+ # Custom :known_hosts objects may return a host_keys collection with no
54
+ # revocation support.
55
+ def refuse_revoked!(arguments)
56
+ host_keys = arguments[:session].host_keys
57
+ return unless host_keys.respond_to?(:revoked?) && host_keys.revoked?(arguments[:key])
58
+
59
+ exception = HostKeyRevoked.new("fingerprint #{arguments[:fingerprint]} " +
60
+ "is revoked for #{host_keys.host.inspect}")
61
+ exception.data = arguments
62
+ raise exception
63
+ end
64
+
50
65
  # Enforces the @cert-authority certificate constraints for entries that
51
66
  # implement them (Net::SSH::HostKeyEntries::CertAuthority). Non-certificate
52
67
  # entries do not respond to these predicates and are left untouched.
53
68
  def verify_certificate!(entry, host_keys, arguments)
54
- # Plain keys carry no certificate constraints, and respond_to? on this delegator prints a deprecation warning (#1026).
55
- return if entry.is_a?(Net::SSH::HostKeyEntries::PubKey)
56
-
57
69
  cert = arguments[:key]
58
70
 
59
71
  if entry.respond_to?(:matches_validity?) && !entry.matches_validity?(cert)
@@ -3,7 +3,8 @@ module Net
3
3
  module Verifiers
4
4
  # This host key verifier simply allows every key it sees, without
5
5
  # any verification. This is simple, but very insecure because it
6
- # exposes you to MiTM attacks.
6
+ # exposes you to MiTM attacks. It does not read the known hosts files,
7
+ # so it accepts keys marked @revoked as well.
7
8
  class Never
8
9
  # Returns true.
9
10
  def verify(arguments)
@@ -46,13 +46,13 @@ module Net
46
46
  end
47
47
 
48
48
  # The major component of this version of the Net::SSH library
49
- MAJOR = 7
49
+ MAJOR = 8
50
50
 
51
51
  # The minor component of this version of the Net::SSH library
52
- MINOR = 3
52
+ MINOR = 1
53
53
 
54
54
  # The tiny component of this version of the Net::SSH library
55
- TINY = 6
55
+ TINY = 2
56
56
 
57
57
  # The prerelease component of this version of the Net::SSH library
58
58
  # nil allowed
data/lib/net/ssh.rb CHANGED
@@ -8,6 +8,7 @@ require 'net/ssh/loggable'
8
8
  require 'net/ssh/transport/session'
9
9
  require 'net/ssh/authentication/session'
10
10
  require 'net/ssh/connection/session'
11
+ require 'net/ssh/multiplex'
11
12
  require 'net/ssh/prompt'
12
13
 
13
14
  module Net
@@ -62,14 +63,15 @@ module Net
62
63
  auth_methods bind_address compression compression_level config
63
64
  encryption forward_agent hmac host_key identity_agent remote_user
64
65
  keepalive keepalive_interval keepalive_maxcount kex keys key_data
65
- keycerts keycert_data languages logger paranoid password port proxy
66
+ keycerts keycert_data languages logger password port proxy
66
67
  rekey_blocks_limit rekey_limit rekey_packet_limit timeout verbose
67
68
  known_hosts global_known_hosts_file user_known_hosts_file host_key_alias
68
69
  host_name user properties passphrase keys_only max_pkt_size
69
70
  max_win_size send_env set_env use_agent number_of_password_prompts
70
71
  append_all_supported_algorithms non_interactive password_prompt
71
- agent_socket_factory minimum_dh_bits verify_host_key
72
- fingerprint_hash check_host_ip pubkey_algorithms
72
+ prompt_without_tty agent_socket_factory minimum_dh_bits verify_host_key
73
+ fingerprint_hash check_host_ip pubkey_algorithms no_delay
74
+ control_master control_path preopen_channels
73
75
  ]
74
76
 
75
77
  # The standard means of starting a new SSH connection. When used with a
@@ -98,7 +100,10 @@ module Net
98
100
  # ssh.loop
99
101
  # end
100
102
  #
101
- # This method accepts the following options (all are optional):
103
+ # This method accepts the following options (all are optional). Omit an
104
+ # option to use its default or ssh_config value; passing +nil+ (or an array
105
+ # containing +nil+) raises ArgumentError, except for :password,
106
+ # :passphrase, :logger and :password_prompt, where +nil+ means the default.
102
107
  #
103
108
  # * :auth_methods => an array of authentication methods to try
104
109
  # * :bind_address => the IP address on the connecting machine to use in
@@ -109,13 +114,48 @@ module Net
109
114
  # * :compression => the compression algorithm to use, or +true+ to use
110
115
  # whatever is supported.
111
116
  # * :compression_level => the compression level to use when sending data
117
+ # * :control_master => set to +:auto+ to run the session through an
118
+ # already running OpenSSH ControlMaster for the same host, port and user
119
+ # (started e.g. with <tt>ssh -M</tt> or <tt>ControlMaster auto</tt>),
120
+ # using its proxy mode (OpenSSH 7.4 or later). This skips the TCP
121
+ # connection, key exchange and authentication. If there is no usable
122
+ # master (no ControlPath, no socket, refused, timed out, owned by another
123
+ # user) Net::SSH logs why and connects as usual; it never starts a master
124
+ # itself. The master's own settings then apply instead of the options
125
+ # for setting up a connection (:keys, :key_data, :password, :passphrase,
126
+ # :auth_methods, :verify_host_key, :known_hosts, :host_key, :kex,
127
+ # :encryption, :hmac, :compression, :rekey_*, :proxy, :keepalive and the
128
+ # like); explicitly passed ones are named at info level. Over the
129
+ # master, remote port forwarding (Service::Forward#remote and
130
+ # #cancel_remote) raises Net::SSH::NotSupportedOverControlMaster, as the
131
+ # master consumes the global request reply, so success, failure and the
132
+ # port allocated for port 0 can't be observed; other global requests
133
+ # raise it too. Agent and X11 forwarding requests raise it
134
+ # too, and :forward_agent is ignored (logged at info level), because the
135
+ # master serves them from its own agent or display only if it has
136
+ # ForwardAgent or ForwardX11 enabled itself, and rejects them otherwise.
137
+ # Keepalive and rekeying are left to the master. ssh_config's
138
+ # ControlMaster is ignored, only this option enables it. An invalid
139
+ # ControlPath raises ArgumentError. A missing or stale socket is logged
140
+ # at info level, a socket that can't be used (another owner, refused,
141
+ # wrong protocol, timed out) at warn level. Has no effect on Windows.
142
+ # Defaults to +false+.
143
+ # * :control_path => the ControlMaster socket to use with
144
+ # :control_master, with the same ~, ${ENV} and % tokens as ssh_config's
145
+ # ControlPath (%C, %d, %h, %i, %j, %k, %L, %l, %n, %p, %r, %u, %%).
146
+ # The master at that path decides which host and user the session is
147
+ # connected to, so a fixed path without %h, %p, %r or %C sends every
148
+ # host through the same master and the explicitly passed host key and
149
+ # authentication options are ignored. Defaults to ControlPath from
150
+ # ssh_config; +"none"+ disables the master.
112
151
  # * :config => set to +true+ to load the default OpenSSH config files
113
152
  # (~/.ssh/config, /etc/ssh_config), or to +false+ to not load them, or to
114
153
  # a file-name (or array of file-names) to load those specific configuration
115
154
  # files. Defaults to +true+.
116
155
  # * :encryption => the encryption cipher (or ciphers) to use
117
156
  # * :forward_agent => set to true if you want the SSH agent connection to
118
- # be forwarded
157
+ # be forwarded. Ignored for a session through a ControlMaster (see
158
+ # :control_master).
119
159
  # * :known_hosts => a custom object holding known hosts records.
120
160
  # It must implement #search_for and `add` in a similiar manner as KnownHosts.
121
161
  # * :global_known_hosts_file => the location of the global known hosts
@@ -137,7 +177,11 @@ module Net
137
177
  # Defaults to +300+ seconds.
138
178
  # * :keepalive_maxcount => the maximun number of keepalive packet miss allowed.
139
179
  # Defaults to 3
140
- # * :kex => the key exchange algorithm (or algorithms) to use
180
+ # * :kex => the key exchange algorithm (or algorithms) to use. Like
181
+ # KexAlgorithms, entries starting with +, - or ^ modify the default list.
182
+ # diffie-hellman-group14-sha1 is supported but not offered unless listed
183
+ # here (e.g. '+diffie-hellman-group14-sha1') or
184
+ # :append_all_supported_algorithms is set.
141
185
  # * :keys => an array of file names of private keys to use for publickey
142
186
  # and hostbased authentication
143
187
  # * :keycerts => an array of file names of key certificates to use
@@ -155,24 +199,51 @@ module Net
155
199
  # packet. Default is 0x8000 (32768 bytes). Increase to 0x10000 (65536 bytes)
156
200
  # for better performance if your SSH server supports it (most do).
157
201
  # * :max_win_size => maximum size we tell the other side that is supported for
158
- # the window.
202
+ # the window, i.e. how much the server may send on a channel before net-ssh
203
+ # reads it. Default is 0x200000 (2 MB), as in OpenSSH. Smaller values limit
204
+ # how much data is in flight; some servers (e.g. Mikrotik) truncate output
205
+ # when the window is small.
206
+ # * :minimum_dh_bits => the smallest group, in bits, accepted from the server
207
+ # in diffie-hellman-group-exchange key exchange. Defaults to 2048, as in
208
+ # OpenSSH; lower it (e.g. to 1024) only to reach legacy servers that offer
209
+ # nothing larger.
210
+ # * :no_delay => turns Nagle's algorithm off on the connection's socket
211
+ # (TCP_NODELAY), so a packet written while the previous one is still
212
+ # unacknowledged is sent right away instead of waiting for the server's
213
+ # ACK. OpenSSH does the same for every connection. Set to +false+ to keep
214
+ # Nagle's algorithm on. Has no effect when the socket comes from a
215
+ # ProxyCommand, whose command owns the TCP connection. Defaults to +true+.
159
216
  # * :non_interactive => set to true if your app is non interactive and prefers
160
217
  # authentication failure vs password prompt. Non-interactive applications
161
218
  # should set it to true to prefer failing a password/etc auth methods vs.
162
- # asking for password.
163
- # * :paranoid => deprecated alias for :verify_host_key
219
+ # asking for password. Like OpenSSH, Net::SSH never prompts when $stdin is
220
+ # not a TTY (e.g. in a background job), so it defaults to +true+ then,
221
+ # unless :password_prompt or :prompt_without_tty is given or an
222
+ # SSH_ASKPASS program can be used (see Net::SSH::Prompt.askpass).
164
223
  # * :passphrase => the passphrase to use when loading a private key (default
165
224
  # is +nil+, for no passphrase)
166
225
  # * :password => the password to use to login
167
226
  # * :port => the port to use when connecting to the remote host
227
+ # * :preopen_channels => set to +true+ to keep one spare "session" channel
228
+ # open, so that Net::SSH::Connection::Session#open_channel can hand it
229
+ # out at once and a command that waits for the previous one to finish
230
+ # costs one round trip instead of two. The spare counts against the
231
+ # server's MaxSessions; if the server refuses it, pre-opening stops for
232
+ # the session. Defaults to +false+.
233
+ # * :prompt_without_tty => set to +true+ to prompt for passwords, key
234
+ # passphrases and keyboard-interactive responses on $stdout/$stdin even
235
+ # when $stdin is not a TTY, e.g. to read a password piped into the
236
+ # process. Defaults to +false+.
168
237
  # * :properties => a hash of key/value pairs to add to the new connection's
169
238
  # properties (see Net::SSH::Connection::Session#properties)
170
239
  # * :proxy => a proxy instance (see Proxy) to use when connecting
171
- # * :pubkey_algorithms => the public key authentication algorithms to use for
172
- # this connection. Valid values are 'rsa-sha2-256-cert-v01@openssh.com',
173
- # 'ssh-rsa-cert-v01@openssh.com', 'rsa-sha2-256', 'ssh-rsa'. Currently, this
174
- # option is only used for RSA public key authentication and ignored for other
175
- # types.
240
+ # * :pubkey_algorithms => the public key authentication algorithms to offer,
241
+ # in order of preference (e.g. %w[ssh-ed25519 rsa-sha2-512 rsa-sha2-256]).
242
+ # Keys whose type isn't listed are skipped. Like PubkeyAcceptedAlgorithms,
243
+ # entries starting with +, - or ^ modify the default list. ssh-rsa (SHA-1),
244
+ # ssh-rsa-cert-v01@openssh.com and ssh-dss are supported but not offered
245
+ # unless listed here (e.g. '+ssh-rsa') or :append_all_supported_algorithms
246
+ # is set.
176
247
  # * :rekey_blocks_limit => the max number of blocks to process before rekeying
177
248
  # * :rekey_limit => the max number of bytes to process before rekeying
178
249
  # * :rekey_packet_limit => the max number of packets to process before rekeying
@@ -184,7 +255,8 @@ module Net
184
255
  # * :user => the user name to log in as; this overrides the +user+
185
256
  # parameter, and is primarily only useful when provided via an SSH
186
257
  # configuration file.
187
- # * :remote_user => used for substitution into the '%r' part of a ProxyCommand
258
+ # * :remote_user => used for substitution into the '%r' part of a ProxyCommand;
259
+ # defaults to the user name the connection logs in as.
188
260
  # * :user_known_hosts_file => the location of the user known hosts file.
189
261
  # Set to an array to specify multiple user known hosts files.
190
262
  # Defaults to %w(~/.ssh/known_hosts ~/.ssh/known_hosts2).
@@ -196,7 +268,9 @@ module Net
196
268
  # default. The symbols :debug, :info, :warn, :error, and :fatal are also
197
269
  # supported and are translated to the corresponding Logger constant.
198
270
  # * :append_all_supported_algorithms => set to +true+ to append all supported
199
- # algorithms by net-ssh. Was the default behaviour until 2.10
271
+ # algorithms by net-ssh, except the +none+ cipher and MAC, which must be
272
+ # named explicitly (e.g. encryption: '+none'). Was the default behaviour
273
+ # until 2.10
200
274
  # * :number_of_password_prompts => Number of prompts for the password
201
275
  # authentication method defaults to 3 set to 0 to disable prompt for
202
276
  # password auth method
@@ -211,24 +285,35 @@ module Net
211
285
  # * :accept_new_or_local_tunnel (insecure) ::Net::SSH::Verifiers::AcceptNewOrLocalTunnel
212
286
  # * :accept_new (insecure) ::Net::SSH::Verifiers::AcceptNew
213
287
  # * :always (secure) ::Net::SSH::Verifiers::Always
214
- # You can also provide an own Object which responds to +verify+. The argument
215
- # given to +verify+ is a hash consisting of the +:key+, the +:key_blob+,
216
- # the +:fingerprint+ and the +:session+. Returning true accepts the host key,
217
- # returning false declines it and closes the connection.
288
+ # You can also provide an own Object which responds to +verify+ and
289
+ # +verify_signature+. The argument given to +verify+ is a hash consisting
290
+ # of the +:key+, the +:key_blob+, the +:fingerprint+ and the +:session+.
291
+ # Returning true accepts the host key, returning false declines it and
292
+ # closes the connection. +verify_signature+ is given a block that checks
293
+ # the server's host key signature and must return the block's result.
218
294
  # * :fingerprint_hash => 'MD5' or 'SHA256', defaults to 'SHA256'
219
295
  # If +user+ parameter is nil it defaults to USER from ssh_config, or
220
296
  # local username
221
297
  def self.start(host, user = nil, options = {}, &block)
298
+ if options.key?(:paranoid)
299
+ raise ArgumentError, ":paranoid has been removed, use :verify_host_key (same values)"
300
+ end
301
+
222
302
  invalid_options = options.keys - VALID_OPTIONS
223
303
  if invalid_options.any?
224
304
  raise ArgumentError, "invalid option(s): #{invalid_options.join(', ')}"
225
305
  end
226
306
 
307
+ reject_nil_options(options)
308
+ Multiplex.validate_options!(options)
309
+ explicit_options = options.keys
310
+ host_arg = host
311
+
312
+ options = options.dup
227
313
  assign_defaults(options)
228
- _sanitize_options(options)
229
314
 
230
315
  options[:user] = user if user
231
- options = configuration_for(host, options.fetch(:config, true)).merge(options)
316
+ options = configuration_for(host, options.fetch(:config, true), user: options[:user]).merge(options)
232
317
  host = options.fetch(:host_name, host)
233
318
 
234
319
  options[:check_host_ip] = true unless options.key?(:check_host_ip)
@@ -237,8 +322,6 @@ module Net
237
322
  options[:number_of_password_prompts] = 0
238
323
  end
239
324
 
240
- _support_deprecated_option_paranoid(options)
241
-
242
325
  if options[:verbose]
243
326
  options[:logger].level = case options[:verbose]
244
327
  when Integer then options[:verbose]
@@ -251,26 +334,35 @@ module Net
251
334
  end
252
335
  end
253
336
 
254
- transport = Transport::Session.new(host, options)
255
- auth = Authentication::Session.new(transport, options)
256
-
257
337
  user = options.fetch(:user, user) || Etc.getpwuid.name
258
- if auth.authenticate("ssh-connection", user, options[:password])
259
- connection = Connection::Session.new(transport, options)
260
- if block_given?
261
- begin
262
- yield connection
263
- ensure
264
- connection.close unless connection.closed?
265
- end
266
- else
267
- return connection
338
+ options[:remote_user] ||= user
339
+
340
+ connection = Multiplex.connect(host_arg, host, user, options, explicit: explicit_options) if options[:control_master]
341
+ connection ||= connect_directly(host, user, options)
342
+
343
+ if block_given?
344
+ begin
345
+ yield connection
346
+ ensure
347
+ connection.close unless connection.closed?
268
348
  end
269
349
  else
350
+ connection
351
+ end
352
+ end
353
+
354
+ def self.connect_directly(host, user, options)
355
+ transport = Transport::Session.new(host, options)
356
+ auth = Authentication::Session.new(transport, options)
357
+
358
+ unless auth.authenticate("ssh-connection", user, options[:password])
270
359
  transport.close
271
360
  raise AuthenticationFailed, "Authentication failed for user #{user}@#{host}"
272
361
  end
362
+
363
+ Connection::Session.new(transport, options)
273
364
  end
365
+ private_class_method :connect_directly
274
366
 
275
367
  # Returns a hash of the configuration options for the given host, as read
276
368
  # from the SSH configuration file(s). If +use_ssh_config+ is true (the
@@ -280,15 +372,17 @@ module Net
280
372
  # be a file name (or array of file names) of SSH configuration file(s)
281
373
  # to read.
282
374
  #
375
+ # +user+ is the explicitly requested remote user, matched by <tt>Match User</tt>.
376
+ #
283
377
  # See Net::SSH::Config for the full description of all supported options.
284
- def self.configuration_for(host, use_ssh_config)
378
+ def self.configuration_for(host, use_ssh_config, user: nil)
285
379
  files = case use_ssh_config
286
380
  when true then Net::SSH::Config.expandable_default_files
287
381
  when false, nil then return {}
288
382
  else Array(use_ssh_config)
289
383
  end
290
384
 
291
- Net::SSH::Config.for(host, files)
385
+ Net::SSH::Config.for(host, files, user: user)
292
386
  end
293
387
 
294
388
  def self.assign_defaults(options)
@@ -297,6 +391,9 @@ module Net
297
391
  options[:logger].level = Logger::FATAL
298
392
  end
299
393
 
394
+ unless options[:password_prompt] || options[:prompt_without_tty] || Prompt.can_ask?
395
+ options[:non_interactive] = true
396
+ end
300
397
  options[:password_prompt] ||= Prompt.default(options)
301
398
 
302
399
  %i[password passphrase].each do |key|
@@ -304,31 +401,35 @@ module Net
304
401
  end
305
402
  end
306
403
 
307
- def self._sanitize_options(options)
308
- invalid_option_values = [nil, [nil]]
309
- unless (options.values & invalid_option_values).empty?
310
- nil_options = options.select { |_k, v| invalid_option_values.include?(v) }.map(&:first)
311
- Kernel.warn "#{caller_locations(2, 1)[0]}: Passing nil, or [nil] to Net::SSH.start is deprecated for keys: #{nil_options.join(', ')}"
312
- end
313
- end
314
- private_class_method :_sanitize_options
404
+ NIL_ALLOWED_OPTIONS = %i[password passphrase logger password_prompt].freeze
315
405
 
316
- def self._support_deprecated_option_paranoid(options)
317
- if options.key?(:paranoid)
318
- Kernel.warn(
319
- ":paranoid is deprecated, please use :verify_host_key. Supported " \
320
- "values are exactly the same, only the name of the option has changed."
321
- )
322
- if options.key?(:verify_host_key)
323
- Kernel.warn(
324
- "Both :paranoid and :verify_host_key were specified. " \
325
- ":verify_host_key takes precedence, :paranoid will be ignored."
326
- )
327
- else
328
- options[:verify_host_key] = options.delete(:paranoid)
329
- end
330
- end
406
+ NIL_OPTION_HINTS = {
407
+ config: "config: false to skip ssh_config files",
408
+ proxy: "proxy: false to connect without a proxy",
409
+ keys: "keys: [] to use no key files",
410
+ use_agent: "use_agent: false to not use ssh-agent",
411
+ forward_agent: "forward_agent: false to not forward ssh-agent",
412
+ keepalive: "keepalive: false to send no keepalives",
413
+ compression: "compression: false to disable compression",
414
+ control_master: "control_master: false to not use a ControlMaster",
415
+ control_path: "control_path: 'none' to not use a ControlMaster socket",
416
+ send_env: "send_env: [] to send no environment variables",
417
+ set_env: "set_env: {} to set no environment variables"
418
+ }.freeze
419
+ private_constant :NIL_ALLOWED_OPTIONS, :NIL_OPTION_HINTS
420
+
421
+ def self.reject_nil_options(options)
422
+ nil_keys = options.select do |key, value|
423
+ !NIL_ALLOWED_OPTIONS.include?(key) && (value.nil? || (value.is_a?(Array) && value.include?(nil)))
424
+ end.keys
425
+ return if nil_keys.empty?
426
+
427
+ message = "nil is not allowed for #{nil_keys.map(&:inspect).join(', ')}; " \
428
+ "omit #{nil_keys.one? ? 'it' : 'them'} to use the default or ssh_config value"
429
+ hints = NIL_OPTION_HINTS.values_at(*nil_keys).compact
430
+ message += ", or pass #{hints.join(', or ')}" if hints.any?
431
+ raise ArgumentError, message
331
432
  end
332
- private_class_method :_support_deprecated_option_paranoid
433
+ private_class_method :reject_nil_options
333
434
  end
334
435
  end
data.tar.gz.sig CHANGED
Binary file