net-ssh 8.0.0.beta3 → 8.0.0.rc1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ee72c6673cb55fe69cdb90bc49d0b7266b29cb80f7a14794f873e048075cde2b
4
- data.tar.gz: b9416f51d2b80d5d0bbbcac1b33a4272d5b5add59d81421ca8eaa84db2fd0823
3
+ metadata.gz: aad28c615382be2934f9a43923ede5e5eed71383f3af54a0440ebf94ad40dc1a
4
+ data.tar.gz: 938c219b1073d44e27de4b5d4c95c01df2903d52f6b737454fb504d878ee6592
5
5
  SHA512:
6
- metadata.gz: 4f1d529b4af06145b892c6ece94d2fcc6a9d2b65b93db633189b71988b8bc03aa64feffdfce3509ce64809378ff542b04f01381419d5a9e11246c7303d258583
7
- data.tar.gz: 2170524d2a24e1ec8d0c07c3237d140ad00c412c59875f6f178b6f4a2c47ce9d16f8418dee56e0425d8ec8a214f50e0cc96e434d1b6e37998125e1cda9ebb41e
6
+ metadata.gz: a9663038da70081bafeaa5a9bfabc0241f7c249386fc6f475f09611d697c6db00161624777ec92abe0ae8f301f4ba20b2ba3ea869766ebccd6c11648acf5834b
7
+ data.tar.gz: d8eb01f0e3aad7af012363f211873842c9b80e382d304fb0c3faef045d3eea4ec389ec6aa54be49c600a70bacb566c23b18f0aa0d2d6decf5631169bec01aa59
checksums.yaml.gz.sig CHANGED
Binary file
@@ -7,7 +7,7 @@ jobs:
7
7
  runs-on: ubuntu-22.04
8
8
  strategy:
9
9
  matrix:
10
- ruby-version: [2.7.8, 3.0.6, 3.1.3, 3.2.1, 3.3.0, 3.4]
10
+ ruby-version: [2.7.8, 3.0.6, 3.1.3, 3.2.1, 3.3, 3.4, '4.0']
11
11
  steps:
12
12
  - uses: actions/checkout@v3
13
13
 
data/CHANGES.txt CHANGED
@@ -1,7 +1,25 @@
1
+ === 8.0.0 rc1
2
+
3
+ * BREAKING: TCP_NODELAY is now set on the connection socket by default, as OpenSSH does; pass `no_delay: false` to keep Nagle's algorithm on [#1017]
4
+ * Ruby 4.0 support: add `logger` as a runtime dependency; Windows Pageant users on Ruby 4.0 need `fiddle` in their Gemfile [#984, #1012]
5
+ * Close the socket when connection setup fails, e.g. on a server version timeout [#1013]
6
+ * Ignore SSH_MSG_EXT_INFO from servers that send it unrequested [#1020]
7
+ * Fix `undefined method 'to_pem'` for ECDSA key_data when an agent is running [#982]
8
+ * Agent forwarding connects to the agent set by `identity_agent`/IdentityAgent [#974]
9
+ * Keyboard-interactive prompts show input when the server asks for it to be echoed [#1014]
10
+ * Raise Net::SSH::Exception on a truncated KEXINIT [#1016]
11
+ * Remote forwarding no longer prints diagnostics to stdout [#953]
12
+
1
13
  === 8.0.0 beta3
2
14
 
3
15
  * Verify @cert-authority host certificate constraints — principals (shell-glob, matching OpenSSH), validity window, host type, and critical options — instead of trusting any CA-signed certificate for any host [#1009]
4
16
 
17
+ === 8.0.0 beta1
18
+
19
+ * BREAKING: require Ruby 2.7 and the openssl gem >= 3.2 [#1001]
20
+ * Use OpenSSL for Ed25519, X25519 and ChaCha20-Poly1305; the ed25519, x25519 and rbnacl gems are no longer needed [#1001]
21
+ * Support AEAD-encrypted OpenSSH private keys [#1001]
22
+
5
23
  === 7.3.3
6
24
 
7
25
  * Support .pub file as IdentityFile to filter/prioritize agent keys (1Password SSH agent use case) [#942]
data/Dockerfile CHANGED
@@ -3,6 +3,9 @@ FROM ruby:${RUBY_VERSION}
3
3
 
4
4
  ARG BUNDLERV=
5
5
 
6
+ # Ruby 2.7 and 3.0 images are built on Debian bullseye, which only archive.debian.org still serves.
7
+ RUN if grep -q bullseye /etc/os-release; then sed -i 's|deb.debian.org|archive.debian.org|' /etc/apt/sources.list; fi
8
+
6
9
  RUN apt update && apt install -y openssh-server sudo netcat-openbsd \
7
10
  && useradd --create-home --shell '/bin/bash' --comment 'NetSSH' 'net_ssh_1' \
8
11
  && useradd --create-home --shell '/bin/bash' --comment 'NetSSH' 'net_ssh_2' \
data/Dockerfile.zlib_ng CHANGED
@@ -11,7 +11,7 @@ ENV INSTALL_PATH="/netssh"
11
11
  # Ruby/OpenSSL initialises — setting it in the Rakefile is too late.
12
12
  ENV OPENSSL_CONF="/netssh/test/openssl3.conf"
13
13
 
14
- RUN dnf install -y openssl openssl-devel ruby ruby-devel git make gcc zlib-ng crypto-policies-scripts && \
14
+ RUN dnf install -y openssl openssl-devel ruby ruby-devel git make gcc redhat-rpm-config zlib-ng crypto-policies-scripts && \
15
15
  update-crypto-policies --set LEGACY
16
16
 
17
17
  WORKDIR $INSTALL_PATH
data/Rakefile CHANGED
@@ -9,7 +9,11 @@ require "rake"
9
9
  require "rake/clean"
10
10
  require "bundler/gem_tasks"
11
11
 
12
- require "rdoc/task"
12
+ begin
13
+ require "rdoc/task"
14
+ rescue LoadError
15
+ # rdoc is no longer a default gem as of Ruby 4.0; the rest of the Rakefile works without it.
16
+ end
13
17
 
14
18
  desc "When releasing make sure NET_SSH_BUILDGEM_SIGNED is set"
15
19
  task :check_NET_SSH_BUILDGEM_SIGNED do
@@ -27,17 +31,19 @@ require_relative "lib/net/ssh/version"
27
31
  version = Net::SSH::Version::CURRENT
28
32
 
29
33
  extra_files = %w[LICENSE.txt THANKS.txt CHANGES.txt]
30
- RDoc::Task.new do |rdoc|
31
- rdoc.rdoc_dir = "rdoc"
32
- rdoc.title = "#{name} #{version}"
33
- rdoc.generator = 'hanna' # gem install hanna-nouveau
34
- rdoc.main = 'README.md'
35
- rdoc.rdoc_files.include("README*")
36
- rdoc.rdoc_files.include("bin/*.rb")
37
- rdoc.rdoc_files.include("lib/**/*.rb")
38
- extra_files.each { |file|
39
- rdoc.rdoc_files.include(file) if File.exist?(file)
40
- }
34
+ if defined?(RDoc::Task)
35
+ RDoc::Task.new do |rdoc|
36
+ rdoc.rdoc_dir = "rdoc"
37
+ rdoc.title = "#{name} #{version}"
38
+ rdoc.generator = 'hanna' # gem install hanna-nouveau
39
+ rdoc.main = 'README.md'
40
+ rdoc.rdoc_files.include("README*")
41
+ rdoc.rdoc_files.include("bin/*.rb")
42
+ rdoc.rdoc_files.include("lib/**/*.rb")
43
+ extra_files.each { |file|
44
+ rdoc.rdoc_files.include(file) if File.exist?(file)
45
+ }
46
+ end
41
47
  end
42
48
 
43
49
  namespace :cert do
@@ -127,9 +127,10 @@ module Net
127
127
  if agent
128
128
  agent.identities.each do |key|
129
129
  corresponding_user_identity = user_identities.detect { |identity|
130
- identity[:public_key] && identity[:public_key].to_pem == key.to_pem
130
+ identity[:public_key] && identity[:public_key].to_blob == key.to_blob
131
131
  }
132
- user_identities.delete(corresponding_user_identity) if corresponding_user_identity
132
+ # By identity: EC::Point#== raises TypeError when compared with other key types.
133
+ user_identities.reject! { |identity| identity.equal?(corresponding_user_identity) }
133
134
 
134
135
  if !options[:keys_only] || corresponding_user_identity
135
136
  known_identities[key] = { from: :agent, identity: key }
@@ -41,9 +41,9 @@ module Net
41
41
  def ask(prompt, echo = true)
42
42
  $stdout.print(prompt)
43
43
  $stdout.flush
44
- ret = $stdin.noecho(&:gets).chomp
45
- $stdout.print("\n")
46
- ret
44
+ ret = echo ? $stdin.gets : $stdin.noecho(&:gets)
45
+ $stdout.print("\n") unless echo
46
+ ret.chomp
47
47
  end
48
48
 
49
49
  # success method will be called when the password was accepted
@@ -388,13 +388,13 @@ module Net
388
388
  originator_address = packet.read_string
389
389
  originator_port = packet.read_long
390
390
 
391
- puts "REMOTE 0: #{connected_port} #{connected_address} #{originator_address} #{originator_port}"
391
+ debug { "REMOTE 0: #{connected_port} #{connected_address} #{originator_address} #{originator_port}" }
392
392
  remote = @remote_forwarded_ports[[connected_port, connected_address]]
393
393
  if remote.nil?
394
394
  raise Net::SSH::ChannelOpenFailed.new(1, "unknown request from remote forwarded connection on #{connected_address}:#{connected_port}")
395
395
  end
396
396
 
397
- puts "REMOTE: #{remote.host} #{remote.port}"
397
+ debug { "REMOTE: #{remote.host} #{remote.port}" }
398
398
  client = TCPSocket.new(remote.host, remote.port)
399
399
  info { "connected #{connected_address}:#{connected_port} originator #{originator_address}:#{originator_port}" }
400
400
 
@@ -409,7 +409,7 @@ module Net
409
409
  channel[:invisible] = true
410
410
 
411
411
  begin
412
- agent = Authentication::Agent.connect(logger, session.options[:agent_socket_factory])
412
+ agent = Authentication::Agent.connect(logger, session.options[:agent_socket_factory], session.options[:identity_agent])
413
413
  if (agent.socket.is_a? ::IO)
414
414
  prepare_client(agent.socket, channel, :agent)
415
415
  else
@@ -347,16 +347,16 @@ module Net
347
347
 
348
348
  packet.read(16) # skip the cookie value
349
349
 
350
- data[:kex] = packet.read_string.split(/,/)
351
- data[:host_key] = packet.read_string.split(/,/)
352
- data[:encryption_client] = packet.read_string.split(/,/)
353
- data[:encryption_server] = packet.read_string.split(/,/)
354
- data[:hmac_client] = packet.read_string.split(/,/)
355
- data[:hmac_server] = packet.read_string.split(/,/)
356
- data[:compression_client] = packet.read_string.split(/,/)
357
- data[:compression_server] = packet.read_string.split(/,/)
358
- data[:language_client] = packet.read_string.split(/,/)
359
- data[:language_server] = packet.read_string.split(/,/)
350
+ data[:kex] = read_name_list(packet)
351
+ data[:host_key] = read_name_list(packet)
352
+ data[:encryption_client] = read_name_list(packet)
353
+ data[:encryption_server] = read_name_list(packet)
354
+ data[:hmac_client] = read_name_list(packet)
355
+ data[:hmac_server] = read_name_list(packet)
356
+ data[:compression_client] = read_name_list(packet)
357
+ data[:compression_server] = read_name_list(packet)
358
+ data[:language_client] = read_name_list(packet)
359
+ data[:language_server] = read_name_list(packet)
360
360
 
361
361
  # TODO: if first_kex_packet_follows, we need to try to skip the
362
362
  # actual kexinit stuff and try to guess what the server is doing...
@@ -366,6 +366,13 @@ module Net
366
366
  return data
367
367
  end
368
368
 
369
+ # Reads one comma-separated algorithm name-list from a KEXINIT packet.
370
+ def read_name_list(packet)
371
+ list = packet.read_string or raise Net::SSH::Exception, "malformed KEXINIT packet from server"
372
+
373
+ list.split(/,/)
374
+ end
375
+
369
376
  # Given the #algorithms map of preferred algorithm types, this constructs
370
377
  # a KEXINIT packet to send to the server. It does not actually send it,
371
378
  # it simply builds the packet and returns it.
@@ -12,6 +12,7 @@ module Net
12
12
  DEBUG = 4
13
13
  SERVICE_REQUEST = 5
14
14
  SERVICE_ACCEPT = 6
15
+ EXT_INFO = 7
15
16
 
16
17
  #--
17
18
  # Algorithm negotiation messages
@@ -62,6 +62,7 @@ module Net
62
62
  @port = options[:port] || DEFAULT_PORT
63
63
  @bind_address = options[:bind_address] || nil
64
64
  @options = options
65
+ @host_key_verifier = select_host_key_verifier(options[:verify_host_key])
65
66
 
66
67
  @socket =
67
68
  if (factory = options[:proxy])
@@ -75,20 +76,23 @@ module Net
75
76
 
76
77
  @socket.extend(PacketStream)
77
78
  @socket.logger = @logger
79
+ set_no_delay unless options[:no_delay] == false
78
80
 
79
81
  debug { "connection established" }
80
82
 
81
83
  @queue = []
82
84
 
83
- @host_key_verifier = select_host_key_verifier(options[:verify_host_key])
84
-
85
85
  @server_version = ServerVersion.new(socket, logger, options[:timeout])
86
86
 
87
87
  @algorithms = Algorithms.new(self, options)
88
88
  @algorithms.start
89
89
  wait { algorithms.initialized? }
90
90
  rescue Errno::ETIMEDOUT
91
+ @socket&.close
91
92
  raise Net::SSH::ConnectionTimeout
93
+ rescue StandardError
94
+ @socket&.close
95
+ raise
92
96
  end
93
97
 
94
98
  def host_keys
@@ -206,6 +210,10 @@ module Net
206
210
  when DEBUG
207
211
  send(packet[:always_display] ? :fatal : :debug) { packet[:message] }
208
212
 
213
+ # We don't advertise ext-info-c, but some servers send EXT_INFO anyway (#955).
214
+ when EXT_INFO
215
+ debug { "ignoring EXT_INFO packet" }
216
+
209
217
  when KEXINIT
210
218
  algorithms.accept_kexinit(packet)
211
219
 
@@ -276,6 +284,18 @@ module Net
276
284
 
277
285
  private
278
286
 
287
+ # Turns Nagle's algorithm off on the socket, so that a packet written
288
+ # while the previous one is still unacknowledged goes out right away
289
+ # instead of waiting for the server's ACK. A ProxyCommand hands the
290
+ # transport a pipe rather than a socket, and the command on its other
291
+ # end owns the TCP connection, so there is nothing to set in that case.
292
+ def set_no_delay
293
+ return unless socket.respond_to?(:setsockopt)
294
+
295
+ debug { "setting TCP_NODELAY" }
296
+ socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
297
+ end
298
+
279
299
  # Compatibility verifier which allows users to keep using
280
300
  # custom verifier code without adding new :verify_signature
281
301
  # method.
@@ -56,7 +56,7 @@ module Net
56
56
 
57
57
  # The prerelease component of this version of the Net::SSH library
58
58
  # nil allowed
59
- PRE = "beta3"
59
+ PRE = "rc1"
60
60
 
61
61
  # The current version of the Net::SSH library as a Version instance
62
62
  CURRENT = new(*[MAJOR, MINOR, TINY, PRE].compact)
data/lib/net/ssh.rb CHANGED
@@ -69,7 +69,7 @@ module Net
69
69
  max_win_size send_env set_env use_agent number_of_password_prompts
70
70
  append_all_supported_algorithms non_interactive password_prompt
71
71
  agent_socket_factory minimum_dh_bits verify_host_key
72
- fingerprint_hash check_host_ip pubkey_algorithms
72
+ fingerprint_hash check_host_ip pubkey_algorithms no_delay
73
73
  ]
74
74
 
75
75
  # The standard means of starting a new SSH connection. When used with a
@@ -156,6 +156,12 @@ module Net
156
156
  # for better performance if your SSH server supports it (most do).
157
157
  # * :max_win_size => maximum size we tell the other side that is supported for
158
158
  # the window.
159
+ # * :no_delay => turns Nagle's algorithm off on the connection's socket
160
+ # (TCP_NODELAY), so a packet written while the previous one is still
161
+ # unacknowledged is sent right away instead of waiting for the server's
162
+ # ACK. OpenSSH does the same for every connection. Set to +false+ to keep
163
+ # Nagle's algorithm on. Has no effect when the socket comes from a
164
+ # ProxyCommand, whose command owns the TCP connection. Defaults to +true+.
159
165
  # * :non_interactive => set to true if your app is non interactive and prefers
160
166
  # authentication failure vs password prompt. Non-interactive applications
161
167
  # should set it to true to prefer failing a password/etc auth methods vs.
data/net-ssh.gemspec CHANGED
@@ -30,6 +30,7 @@ Gem::Specification.new do |spec|
30
30
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
31
31
  spec.require_paths = ["lib"]
32
32
 
33
+ spec.add_dependency("logger")
33
34
  spec.add_dependency("openssl", ">= 3.2.0")
34
35
 
35
36
  spec.add_development_dependency("bcrypt_pbkdf", "~> 1.0") unless RUBY_PLATFORM == "java"
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.beta3
4
+ version: 8.0.0.rc1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jamis Buck
@@ -32,8 +32,22 @@ cert_chain:
32
32
  ufHf4AX2UIkJbh7zCPkiNCqIr7MSWLNFG/9lOlHYsEJM8XujT1ofPobYx6YSFx/C
33
33
  7HBrI7UX7awt6UvBZebhcHzyMHxg/B5PVQllPA==
34
34
  -----END CERTIFICATE-----
35
- date: 2026-08-09 00:00:00.000000000 Z
35
+ date: 2026-10-04 00:00:00.000000000 Z
36
36
  dependencies:
37
+ - !ruby/object:Gem::Dependency
38
+ name: logger
39
+ requirement: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :runtime
45
+ prerelease: false
46
+ version_requirements: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ version: '0'
37
51
  - !ruby/object:Gem::Dependency
38
52
  name: openssl
39
53
  requirement: !ruby/object:Gem::Requirement
metadata.gz.sig CHANGED
Binary file