diff options
author | Miklós Fazekas <mfazekas@szemafor.com> | 2020-03-16 18:33:55 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-03-16 18:33:55 +0100 |
commit | 7d241bb8d93e745d0ace459ef8854a7db3316ada (patch) | |
tree | 14ce5abdde9f7541fa09e1a14dd4cb505824d23f | |
parent | fc91300d7ee6299ba2fe4039cc8085aec9edb7f3 (diff) | |
parent | aea5223eea51c2b60e6373dc8175d49f7520f4e6 (diff) | |
download | net-ssh-7d241bb8d93e745d0ace459ef8854a7db3316ada.tar.gz |
Merge pull request #745 from uzxmx/master
Add set_env option
-rw-r--r-- | .rubocop_todo.yml | 3 | ||||
-rw-r--r-- | lib/net/ssh.rb | 5 | ||||
-rw-r--r-- | lib/net/ssh/config.rb | 2 | ||||
-rw-r--r-- | lib/net/ssh/connection/channel.rb | 12 | ||||
-rw-r--r-- | test/configs/set_env | 2 | ||||
-rw-r--r-- | test/integration/common.rb | 1 | ||||
-rw-r--r-- | test/integration/playbook.yml | 6 | ||||
-rw-r--r-- | test/integration/test_channel.rb | 40 | ||||
-rw-r--r-- | test/integration/test_forward.rb | 12 | ||||
-rw-r--r-- | test/start/test_options.rb | 7 | ||||
-rw-r--r-- | test/test_config.rb | 8 |
11 files changed, 76 insertions, 22 deletions
diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index bc4efd8..a1b7067 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -274,6 +274,8 @@ Metrics/ClassLength: # Offense count: 38 Metrics/CyclomaticComplexity: Max: 27 + Exclude: + - 'lib/net/ssh/config.rb' # Offense count: 211 # Configuration parameters: CountComments, ExcludedMethods. @@ -299,6 +301,7 @@ Naming/AccessorMethodName: Exclude: - 'lib/net/ssh/authentication/methods/password.rb' - 'lib/net/ssh/authentication/pageant.rb' + - 'lib/net/ssh/connection/channel.rb' - 'lib/net/ssh/connection/session.rb' - 'lib/net/ssh/transport/kex/abstract5656.rb' - 'lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb' diff --git a/lib/net/ssh.rb b/lib/net/ssh.rb index 542c757..9df01f0 100644 --- a/lib/net/ssh.rb +++ b/lib/net/ssh.rb @@ -4,6 +4,7 @@ ENV['HOME'] ||= ENV['HOMEPATH'] ? "#{ENV['HOMEDRIVE']}#{ENV['HOMEPATH']}" : Dir. require 'logger' require 'etc' +require 'shellwords' require 'net/ssh/config' require 'net/ssh/errors' @@ -70,7 +71,7 @@ module Net rekey_blocks_limit rekey_limit rekey_packet_limit timeout verbose known_hosts global_known_hosts_file user_known_hosts_file host_key_alias host_name user properties passphrase keys_only max_pkt_size - max_win_size send_env use_agent number_of_password_prompts + max_win_size send_env set_env use_agent number_of_password_prompts append_all_supported_algorithms non_interactive password_prompt agent_socket_factory minimum_dh_bits verify_host_key fingerprint_hash check_host_ip @@ -175,6 +176,8 @@ module Net # * :rekey_packet_limit => the max number of packets to process before rekeying # * :send_env => an array of local environment variable names to export to the # remote environment. Names may be given as String or Regexp. + # * :set_env => a hash of environment variable names and values to set to the + # remote environment. Override the ones if specified in +send_env+. # * :timeout => how long to wait for the initial connection to be made # * :user => the user name to log in as; this overrides the +user+ # parameter, and is primarily only useful when provided via an SSH diff --git a/lib/net/ssh/config.rb b/lib/net/ssh/config.rb index 5c81e61..a40262e 100644 --- a/lib/net/ssh/config.rb +++ b/lib/net/ssh/config.rb @@ -276,6 +276,8 @@ module Net when :sendenv multi_send_env = value.to_s.split(/\s+/) hash[:send_env] = multi_send_env.map { |e| Regexp.new pattern2regex(e).source, false } + when :setenv + hash[:set_env] = Shellwords.split(value.to_s).map { |e| e.split '=', 2 }.to_h when :numberofpasswordprompts hash[:number_of_password_prompts] = value.to_i when *TRANSLATE_CONFIG_KEY_RENAME_MAP.keys diff --git a/lib/net/ssh/connection/channel.rb b/lib/net/ssh/connection/channel.rb index a47d77d..7bfee72 100644 --- a/lib/net/ssh/connection/channel.rb +++ b/lib/net/ssh/connection/channel.rb @@ -2,8 +2,8 @@ require 'net/ssh/loggable' require 'net/ssh/connection/constants' require 'net/ssh/connection/term' -module Net - module SSH +module Net + module SSH module Connection # The channel abstraction. Multiple "channels" can be multiplexed onto a @@ -530,6 +530,7 @@ module Net @remote_maximum_packet_size = max_packet connection.forward.agent(self) if connection.options[:forward_agent] && type == "session" forward_local_env(connection.options[:send_env]) if connection.options[:send_env] + set_remote_env(connection.options[:set_env]) if connection.options[:set_env] @on_confirm_open.call(self) if @on_confirm_open end @@ -677,6 +678,13 @@ module Net end end end + + # Set a +Hash+ of environment variables in the remote process' environment. + # + # channel.set_remote_env foo: 'bar', baz: 'whale' + def set_remote_env(env) + env.each { |key, value| self.env(key, value) } + end end end diff --git a/test/configs/set_env b/test/configs/set_env new file mode 100644 index 0000000..35df6c0 --- /dev/null +++ b/test/configs/set_env @@ -0,0 +1,2 @@ +Host 1234 + SetEnv foo="bar" baz=whale cat="black hole" diff --git a/test/integration/common.rb b/test/integration/common.rb index ef99b04..6295ada 100644 --- a/test/integration/common.rb +++ b/test/integration/common.rb @@ -94,6 +94,7 @@ module IntegrationTestHelpers # @yield [pid, port] def start_sshd_7_or_later(port = '2200', config: nil) + pid = nil if config with_lines_as_tempfile(config) do |path| pid = spawn('sudo', '/opt/net-ssh-openssh/sbin/sshd', '-D', '-f', path, '-p', port) diff --git a/test/integration/playbook.yml b/test/integration/playbook.yml index e2268c6..dd9d078 100644 --- a/test/integration/playbook.yml +++ b/test/integration/playbook.yml @@ -41,14 +41,14 @@ - lineinfile: dest=/etc/sudoers.d/net_ssh_1 mode=0440 state=present create=yes line='net_ssh_2 ALL=(ALL) NOPASSWD:ALL' regexp=net_ssh_2 - unarchive: - src: https://cdn.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-7.4p1.tar.gz + src: https://cdn.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-7.9p1.tar.gz dest: /tmp remote_src: True validate_certs: False - - name: building and installing openssh 7.4 (used in forward test) + - name: building and installing openssh 7.9 (used in forward test) command: sh -c "./configure --prefix=/opt/net-ssh-openssh && make && sudo make install" args: - chdir: /tmp/openssh-7.4p1/ + chdir: /tmp/openssh-7.9p1/ - name: drop installed openssh etc/ in favor of symlink file: state: absent diff --git a/test/integration/test_channel.rb b/test/integration/test_channel.rb index 2b424b4..d07e0a1 100644 --- a/test/integration/test_channel.rb +++ b/test/integration/test_channel.rb @@ -51,11 +51,11 @@ class TestChannel < NetSSHTest proxy = Net::SSH::Proxy::Command.new("/bin/nc localhost 22") res = nil Net::SSH.start(*ssh_start_params(proxy: proxy)) do |ssh| - chanell_success_handler = lambda do + channel_success_handler = lambda do sleep(0.1) system("killall /bin/nc") end - channel = ssh_exec(ssh, "echo Begin ; sleep 100 ; echo End", chanell_success_handler) do |ch, _type, data| + channel = ssh_exec(ssh, "echo Begin ; sleep 100 ; echo End", channel_success_handler) do |ch, _type, data| ch[:result] ||= "" ch[:result] << data end @@ -72,11 +72,11 @@ class TestChannel < NetSSHTest proxy = Net::SSH::Proxy::Command.new("/bin/nc localhost 22") res = nil Net::SSH.start(*ssh_start_params(proxy: proxy)) do |ssh| - chanell_success_handler = lambda do + channel_success_handler = lambda do sleep(0.1) system("killall /bin/nc") end - channel = ssh_exec(ssh, "echo Hello!", chanell_success_handler) do |ch, _type, data| + channel = ssh_exec(ssh, "echo Hello!", channel_success_handler) do |ch, _type, data| ch[:result] ||= "" ch[:result] << data end @@ -104,4 +104,36 @@ class TestChannel < NetSSHTest end end end + + def test_channel_should_set_environment_variables_on_remote + setup_ssh_env do + start_sshd_7_or_later(config: 'AcceptEnv *') do |_pid, port| + Timeout.timeout(4) do + begin + # We have our own sshd, give it a chance to come up before + # listening. + proxy = Net::SSH::Proxy::Command.new("/bin/nc localhost #{port}") + res = nil + Net::SSH.start(*ssh_start_params(port: port, proxy: proxy, set_env: { foo: 'bar', baz: 'whale will' })) do |ssh| + channel_success_handler = lambda do + sleep(0.1) + system("killall /bin/nc") + end + channel = ssh_exec(ssh, "echo $foo; echo $baz", channel_success_handler) do |ch, _type, data| + ch[:result] ||= "" + ch[:result] << data + end + channel.wait + res = channel[:result] + assert_equal(res, "bar\nwhale will\n") + end + assert_equal(res, "bar\nwhale will\n") + rescue SocketError, Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Net::SSH::Proxy::ConnectError + sleep 0.25 + retry + end + end + end + end + end end diff --git a/test/integration/test_forward.rb b/test/integration/test_forward.rb index 9cd8e64..3ff76cd 100644 --- a/test/integration/test_forward.rb +++ b/test/integration/test_forward.rb @@ -24,18 +24,6 @@ require 'tempfile' class ForwardTestBase < NetSSHTest include IntegrationTestHelpers - # @yield [pid, port] - def start_sshd_7_or_later(port = '2200') - pid = spawn('sudo', '/opt/net-ssh-openssh/sbin/sshd', '-D', '-p', port) - yield pid, port - ensure - # Our pid is sudo, -9 (KILL) on sudo will not clean up its children - # properly, so we just have to hope that -15 (TERM) will manage to bring - # down sshd. - system('sudo', 'kill', '-15', pid.to_s) - Process.wait(pid) - end - def localhost 'localhost' end diff --git a/test/start/test_options.rb b/test/start/test_options.rb index 26c2a14..43a88a7 100644 --- a/test/start/test_options.rb +++ b/test/start/test_options.rb @@ -32,6 +32,13 @@ module NetSSH end end + def test_start_should_accept_set_env_option + assert_nothing_raised do + options = { set_env: { foo: 'bar', baz: 'whale will' } } + Net::SSH.start('localhost', 'testuser', options) + end + end + def test_start_should_accept_number_of_password_prompts_option assert_nothing_raised do options = { number_of_password_prompts: 2 } diff --git a/test/test_config.rb b/test/test_config.rb index eab6d7f..f71a952 100644 --- a/test/test_config.rb +++ b/test/test_config.rb @@ -138,6 +138,7 @@ class TestConfig < NetSSHTest 'pubkeyauthentication' => true, 'rekeylimit' => 1024, 'sendenv' => "LC_*", + 'setenv' => 'foo="bar" baz=whale cat="black hole"', 'numberofpasswordprompts' => '123', 'serveraliveinterval' => '2', 'serveralivecountmax' => '4', @@ -162,6 +163,7 @@ class TestConfig < NetSSHTest assert_equal 1024, net_ssh[:rekey_limit] assert_equal "127.0.0.1", net_ssh[:bind_address] assert_equal [/^LC_.*$/], net_ssh[:send_env] + assert_equal Hash['foo' => 'bar', 'baz' => 'whale', 'cat' => 'black hole'], net_ssh[:set_env] assert_equal 123, net_ssh[:number_of_password_prompts] assert_equal 4, net_ssh[:keepalive_maxcount] assert_equal 2, net_ssh[:keepalive_interval] @@ -285,6 +287,12 @@ class TestConfig < NetSSHTest assert_equal [/^GIT_.*$/, /^LANG$/, /^LC_.*$/], net_ssh[:send_env] end + def test_load_with_set_env + config = Net::SSH::Config.load(config(:set_env), '1234') + net_ssh = Net::SSH::Config.translate(config) + assert_equal Hash['foo' => 'bar', 'baz' => 'whale', 'cat' => 'black hole'], net_ssh[:set_env] + end + def test_load_with_remote_user config = Net::SSH::Config.load(config(:proxy_remote_user), "behind-proxy") net_ssh = Net::SSH::Config.translate(config) |