net-ssh 2.6.8 → 2.7.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,4 +1,13 @@
1
1
 
2
+
3
+ === 2.7.0 / 11 Sep 2013
4
+
5
+ * Fix for 'Could not parse PKey: no start line' error on private keys with passphrases (issue #101) [metametaclass]
6
+ * Automatically forward environment variables defined in OpenSSH config files [fnordfish]
7
+ * Guard against socket.gets being nil in Net::SSH::Proxy::HTTP [krishicks]
8
+ * Implemented experimental keepalive feature [noric]
9
+
10
+
2
11
  === 2.6.8 / 6 Jul 2013
3
12
 
4
13
  * Added support for host wildcard substitution [GabKlein]
@@ -62,12 +62,13 @@ module Net
62
62
  # Net::SSH.start for a description of each option.
63
63
  VALID_OPTIONS = [
64
64
  :auth_methods, :bind_address, :compression, :compression_level, :config,
65
- :encryption, :forward_agent, :hmac, :host_key, :kex, :keys, :key_data,
65
+ :encryption, :forward_agent, :hmac, :host_key,
66
+ :keepalive, :keepalive_interval, :kex, :keys, :key_data,
66
67
  :languages, :logger, :paranoid, :password, :port, :proxy,
67
68
  :rekey_blocks_limit,:rekey_limit, :rekey_packet_limit, :timeout, :verbose,
68
69
  :global_known_hosts_file, :user_known_hosts_file, :host_key_alias,
69
70
  :host_name, :user, :properties, :passphrase, :keys_only, :max_pkt_size,
70
- :max_win_size
71
+ :max_win_size, :send_env
71
72
  ]
72
73
 
73
74
  # The standard means of starting a new SSH connection. When used with a
@@ -124,6 +125,11 @@ module Net
124
125
  # specified in an SSH configuration file. It lets you specify an
125
126
  # "alias", similarly to adding an entry in /etc/hosts but without needing
126
127
  # to modify /etc/hosts.
128
+ # :keepalive => set to +true+ to send a keepalive packet to the SSH server
129
+ # when there's no traffic between the SSH server and Net::SSH client for
130
+ # the keepalive_interval seconds. Defaults to +false+.
131
+ # :keepalive_interval => the interval seconds for keepalive.
132
+ # Defaults to +300+ seconds.
127
133
  # * :kex => the key exchange algorithm (or algorithms) to use
128
134
  # * :keys => an array of file names of private keys to use for publickey
129
135
  # and hostbased authentication
@@ -151,6 +157,8 @@ module Net
151
157
  # * :rekey_blocks_limit => the max number of blocks to process before rekeying
152
158
  # * :rekey_limit => the max number of bytes to process before rekeying
153
159
  # * :rekey_packet_limit => the max number of packets to process before rekeying
160
+ # * :send_env => an array of local environment variable names to export to the
161
+ # remote environment. Names may be given as String or Regexp.
154
162
  # * :timeout => how long to wait for the initial connection to be made
155
163
  # * :user => the user name to log in as; this overrides the +user+
156
164
  # parameter, and is primarily only useful when provided via an SSH
@@ -229,6 +229,9 @@ module Net
229
229
  else
230
230
  identity
231
231
  end
232
+ rescue ArgumentError => e
233
+ process_identity_loading_error(identity, e)
234
+ nil
232
235
  rescue Exception => e
233
236
  process_identity_loading_error(identity, e)
234
237
  nil
@@ -176,6 +176,9 @@ module Net; module SSH
176
176
  hash[:user] = value
177
177
  when 'userknownhostsfile'
178
178
  hash[:user_known_hosts_file] = value
179
+ when 'sendenv'
180
+ multi_send_env = value.to_s.split(/\s+/)
181
+ hash[:send_env] = multi_send_env.map { |e| Regexp.new pattern2regex(e).source, false }
179
182
  end
180
183
  hash
181
184
  end
@@ -510,6 +510,7 @@ module Net; module SSH; module Connection
510
510
  @remote_window_size = @remote_maximum_window_size = max_window
511
511
  @remote_maximum_packet_size = max_packet
512
512
  connection.forward.agent(self) if connection.options[:forward_agent] && type == "session"
513
+ forward_local_env(connection.options[:send_env]) if connection.options[:send_env]
513
514
  @on_confirm_open.call(self) if @on_confirm_open
514
515
  end
515
516
 
@@ -625,6 +626,25 @@ module Net; module SSH; module Connection
625
626
  @local_maximum_window_size += 0x20000
626
627
  end
627
628
  end
629
+
630
+ # Gets an +Array+ of local environment variables in the remote process'
631
+ # environment.
632
+ # A variable name can either be described by a +Regexp+ or +String+.
633
+ #
634
+ # channel.forward_local_env [/^GIT_.*$/, "LANG"]
635
+ def forward_local_env(env_variable_patterns)
636
+ Array(env_variable_patterns).each do |env_variable_pattern|
637
+ matched_variables = ENV.find_all do |env_name, _|
638
+ case env_variable_pattern
639
+ when Regexp then env_name =~ env_variable_pattern
640
+ when String then env_name == env_variable_pattern
641
+ end
642
+ end
643
+ matched_variables.each do |env_name, env_value|
644
+ self.env(env_name, env_value)
645
+ end
646
+ end
647
+ end
628
648
  end
629
649
 
630
650
  end; end; end
@@ -25,6 +25,9 @@ module Net; module SSH; module Connection
25
25
  class Session
26
26
  include Constants, Loggable
27
27
 
28
+ # Default IO.select timeout threshold
29
+ DEFAULT_IO_SELECT_TIMEOUT = 300
30
+
28
31
  # The underlying transport layer abstraction (see Net::SSH::Transport::Session).
29
32
  attr_reader :transport
30
33
 
@@ -75,6 +78,8 @@ module Net; module SSH; module Connection
75
78
 
76
79
  @max_pkt_size = (options.has_key?(:max_pkt_size) ? options[:max_pkt_size] : 0x8000)
77
80
  @max_win_size = (options.has_key?(:max_win_size) ? options[:max_win_size] : 0x20000)
81
+
82
+ @last_keepalive_sent_at = nil
78
83
  end
79
84
 
80
85
  # Retrieves a custom property from this instance. This can be used to
@@ -201,7 +206,7 @@ module Net; module SSH; module Connection
201
206
 
202
207
  r = listeners.keys
203
208
  w = r.select { |w2| w2.respond_to?(:pending_write?) && w2.pending_write? }
204
- readers, writers, = Net::SSH::Compat.io_select(r, w, nil, wait)
209
+ readers, writers, = Net::SSH::Compat.io_select(r, w, nil, io_select_wait(wait))
205
210
 
206
211
  postprocess(readers, writers)
207
212
  end
@@ -239,6 +244,7 @@ module Net; module SSH; module Connection
239
244
  writer.send_pending
240
245
  end
241
246
 
247
+ send_keepalive_as_needed(readers, writers)
242
248
  transport.rekey_as_needed
243
249
 
244
250
  return true
@@ -590,6 +596,31 @@ module Net; module SSH; module Connection
590
596
  channels[packet[:local_id]].do_failure
591
597
  end
592
598
 
599
+ def io_select_wait(wait)
600
+ return wait if wait
601
+ return wait unless options[:keepalive]
602
+ keepalive_interval
603
+ end
604
+
605
+ def keepalive_interval
606
+ options[:keepalive_interval] || DEFAULT_IO_SELECT_TIMEOUT
607
+ end
608
+
609
+ def should_send_keepalive?
610
+ return false unless options[:keepalive]
611
+ return true unless @last_keepalive_sent_at
612
+ Time.now - @last_keepalive_sent_at >= keepalive_interval
613
+ end
614
+
615
+ def send_keepalive_as_needed(readers, writers)
616
+ return unless readers.nil? && writers.nil?
617
+ return unless should_send_keepalive?
618
+ info { "sending keepalive" }
619
+ msg = Net::SSH::Buffer.from(:byte, Packet::IGNORE, :string, "keepalive")
620
+ send_message(msg)
621
+ @last_keepalive_sent_at = Time.now
622
+ end
623
+
593
624
  MAP = Constants.constants.inject({}) do |memo, name|
594
625
  value = const_get(name)
595
626
  next unless Integer === value
@@ -73,7 +73,7 @@ module Net; module SSH; module Proxy
73
73
  version, code, reason = socket.gets.chomp.split(/ /, 3)
74
74
  headers = {}
75
75
 
76
- while (line = socket.gets.chomp) != ""
76
+ while (line = socket.gets) && (line.chomp! != "")
77
77
  name, value = line.split(/:/, 2)
78
78
  headers[name.strip] = value.strip
79
79
  end
@@ -48,10 +48,10 @@ module Net; module SSH
48
48
  MAJOR = 2
49
49
 
50
50
  # The minor component of this version of the Net::SSH library
51
- MINOR = 6
51
+ MINOR = 7
52
52
 
53
53
  # The tiny component of this version of the Net::SSH library
54
- TINY = 8
54
+ TINY = 0
55
55
 
56
56
  # The current version of the Net::SSH library as a Version instance
57
57
  CURRENT = new(MAJOR, MINOR, TINY)
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "net-ssh"
8
- s.version = "2.6.8"
8
+ s.version = "2.7.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Jamis Buck", "Delano Mandelbaum"]
12
- s.date = "2013-07-06"
12
+ s.date = "2013-09-11"
13
13
  s.description = "Net::SSH: a pure-Ruby implementation of the SSH2 client protocol. It allows you to write programs that invoke and interact with processes on remote servers, via SSH2."
14
14
  s.email = "net-ssh@solutious.com"
15
15
  s.extra_rdoc_files = [
@@ -126,12 +126,14 @@ Gem::Specification.new do |s|
126
126
  "test/configs/multihost",
127
127
  "test/configs/nohost",
128
128
  "test/configs/numeric_host",
129
+ "test/configs/send_env",
129
130
  "test/configs/substitutes",
130
131
  "test/configs/wild_cards",
131
132
  "test/connection/test_channel.rb",
132
133
  "test/connection/test_session.rb",
133
134
  "test/known_hosts/github",
134
135
  "test/manual/test_forward.rb",
136
+ "test/start/test_options.rb",
135
137
  "test/start/test_transport.rb",
136
138
  "test/test_all.rb",
137
139
  "test/test_buffer.rb",
@@ -0,0 +1,2 @@
1
+ Host 1234
2
+ SendEnv GIT_* LANG LC_*
@@ -363,6 +363,44 @@ module Connection
363
363
  session.process
364
364
  end
365
365
 
366
+ def test_process_should_call_enqueue_message_if_io_select_timed_out
367
+ timeout = Net::SSH::Connection::Session::DEFAULT_IO_SELECT_TIMEOUT
368
+ options = { :keepalive => true }
369
+ expected_packet = P(:byte, Net::SSH::Packet::IGNORE, :string, "keepalive")
370
+ IO.stubs(:select).with([socket],[],nil,timeout).returns(nil)
371
+ transport.expects(:enqueue_message).with{ |msg| msg.content == expected_packet.content }
372
+ session(options).process
373
+ end
374
+
375
+ def test_process_should_not_call_enqueue_message_unless_io_select_timed_out
376
+ timeout = Net::SSH::Connection::Session::DEFAULT_IO_SELECT_TIMEOUT
377
+ options = { :keepalive => true }
378
+ IO.stubs(:select).with([socket],[],nil,timeout).returns([[],[],[]])
379
+ transport.expects(:enqueue_message).never
380
+ session(options).process
381
+ end
382
+
383
+ def test_process_should_not_call_enqueue_message_unless_keepalive_interval_not_go_on
384
+ timeout = 10
385
+ options = { :keepalive => true, :keepalive_interval => timeout }
386
+ Time.stubs(:now).returns(Time.at(0), Time.at(9), Time.at(timeout))
387
+ IO.stubs(:select).with([socket],[],nil,timeout).returns(nil)
388
+ transport.expects(:enqueue_message).times(2)
389
+ 3.times { session(options).process }
390
+ end
391
+
392
+ def test_process_should_call_io_select_with_nil_as_last_arg_if_keepalive_disabled
393
+ IO.expects(:select).with([socket],[],nil,nil).returns([[],[],[]])
394
+ session.process
395
+ end
396
+
397
+ def test_process_should_call_io_select_with_interval_as_last_arg_if_keepalive_interval_passed
398
+ timeout = 10
399
+ options = { :keepalive => true, :keepalive_interval => timeout }
400
+ IO.expects(:select).with([socket],[],nil,timeout).returns([[],[],[]])
401
+ session(options).process
402
+ end
403
+
366
404
  def test_loop_should_call_process_until_process_returns_false
367
405
  IO.stubs(:select).with([socket],[],nil,nil).returns([[],[],[]])
368
406
  session.expects(:process).with(nil).times(4).returns(true,true,true,false).yields
@@ -0,0 +1,36 @@
1
+ require 'common'
2
+ require 'net/ssh'
3
+
4
+ module NetSSH
5
+ class TestStartOptions < Test::Unit::TestCase
6
+ def setup
7
+ authentication_session = mock('authentication_session')
8
+ authentication_session.stubs(:authenticate).returns(true)
9
+ Net::SSH::Authentication::Session.stubs(:new).returns(authentication_session)
10
+ Net::SSH::Transport::Session.stubs(:new).returns(mock('transport_session'))
11
+ Net::SSH::Connection::Session.stubs(:new).returns(mock('connection_session'))
12
+ end
13
+
14
+ def test_start_should_accept_keepalive_option
15
+ assert_nothing_raised do
16
+ options = { :keepalive => true }
17
+ Net::SSH.start('localhost', 'testuser', options)
18
+ end
19
+ end
20
+
21
+ def test_start_should_accept_keepalive_interval_option
22
+ assert_nothing_raised do
23
+ options = { :keepalive_interval => 10 }
24
+ Net::SSH.start('localhost', 'testuser', options)
25
+ end
26
+ end
27
+
28
+ def test_start_should_accept_send_env_option
29
+ assert_nothing_raised do
30
+ options = { :send_env => [ /^LC_.*$/, "LANG" ] }
31
+ Net::SSH.start('localhost', 'testuser', options)
32
+ end
33
+ end
34
+ end
35
+ end
36
+
@@ -86,7 +86,8 @@ class TestConfig < Test::Unit::TestCase
86
86
  'passwordauthentication' => true,
87
87
  'port' => 1234,
88
88
  'pubkeyauthentication' => true,
89
- 'rekeylimit' => 1024
89
+ 'rekeylimit' => 1024,
90
+ 'sendenv' => "LC_*"
90
91
  }
91
92
 
92
93
  net_ssh = Net::SSH::Config.translate(open_ssh)
@@ -103,6 +104,7 @@ class TestConfig < Test::Unit::TestCase
103
104
  assert_equal 1234, net_ssh[:port]
104
105
  assert_equal 1024, net_ssh[:rekey_limit]
105
106
  assert_equal "127.0.0.1", net_ssh[:bind_address]
107
+ assert_equal [/^LC_.*$/], net_ssh[:send_env]
106
108
  end
107
109
 
108
110
  def test_load_with_plus_sign_hosts
@@ -134,7 +136,13 @@ class TestConfig < Test::Unit::TestCase
134
136
  net_ssh = Net::SSH::Config.translate(config)
135
137
  assert_equal 'prefix.1234.sufix', net_ssh[:host_name]
136
138
  end
137
-
139
+
140
+ def test_load_with_send_env
141
+ config = Net::SSH::Config.load(config(:send_env), "1234")
142
+ net_ssh = Net::SSH::Config.translate(config)
143
+ assert_equal [/^GIT_.*$/, /^LANG$/, /^LC_.*$/], net_ssh[:send_env]
144
+ end
145
+
138
146
  private
139
147
 
140
148
  def config(name)
@@ -1,5 +1,6 @@
1
1
  require 'common'
2
2
  require 'net/ssh/transport/kex/diffie_hellman_group_exchange_sha1'
3
+ require 'transport/kex/test_diffie_hellman_group_exchange_sha1'
3
4
 
4
5
  module Transport; module Kex
5
6
 
@@ -3,6 +3,7 @@ require 'openssl'
3
3
  unless defined?(OpenSSL::PKey::EC)
4
4
  puts "Skipping tests for ecdh-sha2-nistp384 key exchange"
5
5
  else
6
+ require 'transport/kex/test_ecdh_sha2_nistp256'
6
7
  module Transport; module Kex
7
8
  class TestEcdhSHA2NistP384 < TestEcdhSHA2NistP256
8
9
 
@@ -3,6 +3,7 @@ require 'openssl'
3
3
  unless defined?(OpenSSL::PKey::EC)
4
4
  puts "Skipping tests for ecdh-sha2-nistp521 key exchange"
5
5
  else
6
+ require 'transport/kex/test_ecdh_sha2_nistp256'
6
7
  module Transport; module Kex
7
8
  class TestEcdhSHA2NistP521 < TestEcdhSHA2NistP256
8
9
 
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: 2.6.8
4
+ version: 2.7.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2013-07-06 00:00:00.000000000 Z
13
+ date: 2013-09-11 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: test-unit
@@ -163,12 +163,14 @@ files:
163
163
  - test/configs/multihost
164
164
  - test/configs/nohost
165
165
  - test/configs/numeric_host
166
+ - test/configs/send_env
166
167
  - test/configs/substitutes
167
168
  - test/configs/wild_cards
168
169
  - test/connection/test_channel.rb
169
170
  - test/connection/test_session.rb
170
171
  - test/known_hosts/github
171
172
  - test/manual/test_forward.rb
173
+ - test/start/test_options.rb
172
174
  - test/start/test_transport.rb
173
175
  - test/test_all.rb
174
176
  - test/test_buffer.rb