diff options
author | Miklós Fazekas <mfazekas@szemafor.com> | 2020-01-19 11:41:07 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-01-19 11:41:07 +0100 |
commit | 4f21491f2709082fce25fd91f41d785ec45a5f17 (patch) | |
tree | 1e5f05a2ca7b04252c943742837b5fc3d7f524e9 | |
parent | 5c31c9dc5f65b151a9195d1d7c307e2f6d155da1 (diff) | |
parent | f99ba4c04b16f7078f56e709cd67f31ed8a4a325 (diff) | |
download | net-ssh-4f21491f2709082fce25fd91f41d785ec45a5f17.tar.gz |
Merge pull request #722 from anderscarling/certkeys
Support :certkeys and CertificateFile configuration option
-rw-r--r-- | lib/net/ssh.rb | 4 | ||||
-rw-r--r-- | lib/net/ssh/authentication/key_manager.rb | 29 | ||||
-rw-r--r-- | lib/net/ssh/authentication/session.rb | 7 | ||||
-rw-r--r-- | lib/net/ssh/config.rb | 48 | ||||
-rw-r--r-- | test/authentication/test_key_manager.rb | 203 | ||||
-rw-r--r-- | test/configs/exact_match | 4 | ||||
-rw-r--r-- | test/integration/test_cert_user_auth.rb | 58 | ||||
-rw-r--r-- | test/test_config.rb | 3 |
8 files changed, 279 insertions, 77 deletions
diff --git a/lib/net/ssh.rb b/lib/net/ssh.rb index a0734c6..542c757 100644 --- a/lib/net/ssh.rb +++ b/lib/net/ssh.rb @@ -66,7 +66,7 @@ module Net auth_methods bind_address compression compression_level config encryption forward_agent hmac host_key remote_user keepalive keepalive_interval keepalive_maxcount kex keys key_data - languages logger paranoid password port proxy + keycerts languages logger paranoid password port proxy 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 @@ -144,6 +144,8 @@ module Net # * :kex => the key exchange algorithm (or algorithms) to use # * :keys => an array of file names of private keys to use for publickey # and hostbased authentication + # * :keycerts => an array of file names of key certificates to use + # with publickey authentication # * :key_data => an array of strings, with each element of the array being # a raw private key in PEM format. # * :keys_only => set to +true+ to use only private keys from +keys+ and diff --git a/lib/net/ssh/authentication/key_manager.rb b/lib/net/ssh/authentication/key_manager.rb index 52192f2..242d5d5 100644 --- a/lib/net/ssh/authentication/key_manager.rb +++ b/lib/net/ssh/authentication/key_manager.rb @@ -30,6 +30,9 @@ module Net # The list of user key data that will be examined attr_reader :key_data + # The list of user key certificate files that will be examined + attr_reader :keycert_files + # The map of loaded identities attr_reader :known_identities @@ -43,6 +46,7 @@ module Net self.logger = logger @key_files = [] @key_data = [] + @keycert_files = [] @use_agent = options[:use_agent] != false @known_identities = {} @agent = nil @@ -66,6 +70,12 @@ module Net self end + # Add the given keycert_file to the list of keycert files that will be used. + def add_keycert(keycert_file) + keycert_files.push(File.expand_path(keycert_file)).uniq! + self + end + # Add the given key_file to the list of keys that will be used. def add_key_data(key_data_) key_data.push(key_data_).uniq! @@ -108,7 +118,7 @@ module Net user_identities.delete(corresponding_user_identity) if corresponding_user_identity if !options[:keys_only] || corresponding_user_identity - known_identities[key] = { from: :agent } + known_identities[key] = { from: :agent, identity: key } yield key end end @@ -122,6 +132,21 @@ module Net yield key end + known_identity_blobs = known_identities.keys.map(&:to_blob) + keycert_files.each do |keycert_file| + keycert = KeyFactory.load_public_key(keycert_file) + next if known_identity_blobs.include?(keycert.to_blob) + + (_, corresponding_identity) = known_identities.detect { |public_key, _| + public_key.to_pem == keycert.to_pem + } + + if corresponding_identity + known_identities[keycert] = corresponding_identity + yield keycert + end + end + self end @@ -152,7 +177,7 @@ module Net if info[:from] == :agent raise KeyManagerError, "the agent is no longer available" unless agent - return agent.sign(identity, data.to_s) + return agent.sign(info[:identity], data.to_s) end raise KeyManagerError, "[BUG] can't determine identity origin (#{info.inspect})" diff --git a/lib/net/ssh/authentication/session.rb b/lib/net/ssh/authentication/session.rb index 0baacc3..dfc5c06 100644 --- a/lib/net/ssh/authentication/session.rb +++ b/lib/net/ssh/authentication/session.rb @@ -63,6 +63,7 @@ module Net key_manager = KeyManager.new(logger, options) keys.each { |key| key_manager.add(key) } unless keys.empty? + keycerts.each { |keycert| key_manager.add_keycert(keycert) } unless keycerts.empty? key_data.each { |key2| key_manager.add_key_data(key2) } unless key_data.empty? default_keys.each { |key| key_manager.add(key) } unless options.key?(:keys) || options.key?(:key_data) @@ -146,6 +147,12 @@ module Net Array(options[:keys]) end + # Returns an array of paths to the keycert files that should be used when + # attempting any key-based authentication mechanism. + def keycerts + Array(options[:keycerts]) + end + # Returns an array of the key data that should be used when # attempting any key-based authentication mechanism. def key_data diff --git a/lib/net/ssh/config.rb b/lib/net/ssh/config.rb index d998a58..5c81e61 100644 --- a/lib/net/ssh/config.rb +++ b/lib/net/ssh/config.rb @@ -11,6 +11,7 @@ module Net # # * ChallengeResponseAuthentication => maps to the :auth_methods option challenge-response (then coleasced into keyboard-interactive) # * KbdInteractiveAuthentication => maps to the :auth_methods keyboard-interactive + # * CertificateFile => maps to the :keycerts option # * Ciphers => maps to the :encryption option # * Compression => :compression # * CompressionLevel => :compression_level @@ -129,7 +130,7 @@ module Net block_seen = true elsif !block_seen case key - when 'identityfile' + when 'identityfile', 'certificatefile' (globals[key] ||= []) << value when 'include' included_file_paths(base_dir, value).each do |file_path| @@ -140,7 +141,7 @@ module Net end elsif block_matched case key - when 'identityfile' + when 'identityfile', 'certificatefile' (settings[key] ||= []) << value when 'include' included_file_paths(base_dir, value).each do |file_path| @@ -161,7 +162,7 @@ module Net globals.merge(settings) do |key, oldval, newval| case key - when 'identityfile' + when 'identityfile', 'certificatefile' oldval + newval else newval @@ -196,25 +197,26 @@ module Net private + TRANSLATE_CONFIG_KEY_RENAME_MAP = { + bindaddress: :bind_address, + compression: :compression, + compressionlevel: :compression_level, + certificatefile: :keycerts, + connecttimeout: :timeout, + forwardagent: :forward_agent, + identitiesonly: :keys_only, + identityagent: :identity_agent, + globalknownhostsfile: :global_known_hosts_file, + hostkeyalias: :host_key_alias, + identityfile: :keys, + fingerprinthash: :fingerprint_hash, + port: :port, + stricthostkeychecking: :strict_host_key_checking, + user: :user, + userknownhostsfile: :user_known_hosts_file, + checkhostip: :check_host_ip + }.freeze def translate_config_key(hash, key, value, settings) - rename = { - bindaddress: :bind_address, - compression: :compression, - compressionlevel: :compression_level, - connecttimeout: :timeout, - forwardagent: :forward_agent, - identitiesonly: :keys_only, - identityagent: :identity_agent, - globalknownhostsfile: :global_known_hosts_file, - hostkeyalias: :host_key_alias, - identityfile: :keys, - fingerprinthash: :fingerprint_hash, - port: :port, - stricthostkeychecking: :strict_host_key_checking, - user: :user, - userknownhostsfile: :user_known_hosts_file, - checkhostip: :check_host_ip - } case key when :ciphers hash[:encryption] = value.split(/,/) @@ -276,8 +278,8 @@ module Net hash[:send_env] = multi_send_env.map { |e| Regexp.new pattern2regex(e).source, false } when :numberofpasswordprompts hash[:number_of_password_prompts] = value.to_i - when *rename.keys - hash[rename[key]] = value + when *TRANSLATE_CONFIG_KEY_RENAME_MAP.keys + hash[TRANSLATE_CONFIG_KEY_RENAME_MAP[key]] = value end end diff --git a/test/authentication/test_key_manager.rb b/test/authentication/test_key_manager.rb index f9299a9..c40779f 100644 --- a/test/authentication/test_key_manager.rb +++ b/test/authentication/test_key_manager.rb @@ -9,6 +9,10 @@ module Authentication assert manager.known_identities.empty? end + def test_keycert_files_are_empty_by_default + assert manager.keycert_files.empty? + end + def test_assume_agent_is_available_by_default assert manager.use_agent? end @@ -23,6 +27,16 @@ module Authentication assert_equal %w[first second third], final_files end + def test_add_ensures_keycert_list_is_unique + manager.add_keycert "/first" + manager.add_keycert "/second" + manager.add_keycert "/third" + manager.add_keycert "/second" + assert_equal 3, manager.keycert_files.length + final_files = manager.keycert_files.map {|item| item.split('/').last} + assert_equal %w[first second third], final_files + end + def test_use_agent_should_be_set_to_false_if_agent_could_not_be_found Net::SSH::Authentication::Agent.expects(:connect).raises(Net::SSH::Authentication::AgentNotAvailable) assert manager.use_agent? @@ -42,8 +56,8 @@ module Authentication manager.stubs(:agent).returns(nil) first = File.expand_path("/first") second = File.expand_path("/second") - stub_file_private_key first, rsa - stub_file_private_key second, dsa + stub_file_private_key first, rsa, rsa_pk + stub_file_private_key second, dsa, dsa_pk identities = [] manager.each_identity { |identity| identities << identity } @@ -52,27 +66,57 @@ module Authentication assert_equal rsa.to_blob, identities.first.to_blob assert_equal dsa.to_blob, identities.last.to_blob - assert_equal({ from: :file, file: first, key: rsa }, manager.known_identities[rsa]) - assert_equal({ from: :file, file: second, key: dsa }, manager.known_identities[dsa]) + assert_equal({ from: :file, file: first, key: rsa }, manager.known_identities[rsa_pk]) + assert_equal({ from: :file, file: second, key: dsa }, manager.known_identities[dsa_pk]) end - def test_each_identity_should_load_form_cert_file + def test_each_identity_should_load_from_implicit_cert_file manager.stubs(:agent).returns(nil) first = File.expand_path("/first") - stub_file_cert first, rsa + stub_implicit_file_cert first, rsa, rsa_cert identities = [] manager.each_identity { |identity| identities << identity } assert_equal 1, identities.length + assert_equal rsa_cert.to_blob, identities.first.to_blob + assert_equal({ from: :file, file: first }, manager.known_identities[rsa_cert]) + end + + def test_each_identity_should_load_from_explicit_cert_file_given_matching_key_is_loaded + manager.stubs(:agent).returns(nil) + stub_explicit_file_cert File.expand_path("/rsa-cert"), rsa_cert + first = File.expand_path("/first") + stub_file_private_key first, rsa, rsa_pk + + identities = [] + manager.each_identity { |identity| identities << identity } + + assert_equal 2, identities.length assert_equal rsa.to_blob, identities.first.to_blob - assert_equal({ from: :file, file: first }, manager.known_identities[rsa]) + assert_equal rsa_cert.to_blob, identities.last.to_blob + assert_equal({ from: :file, file: first, key: rsa }, manager.known_identities[rsa_pk]) + assert_equal({ from: :file, file: first, key: rsa }, manager.known_identities[rsa_cert]) + end + + def test_each_identity_should_ignore_explicit_cert_file_unless_matching_key_is_avaiable + manager.stubs(:agent).returns(nil) + stub_explicit_file_cert File.expand_path("/rsa-cert"), rsa_cert + first = File.expand_path("/first") + stub_file_private_key first, dsa, dsa_pk + + identities = [] + manager.each_identity { |identity| identities << identity } + + assert_equal 1, identities.length + assert_equal dsa.to_blob, identities.first.to_blob + assert_equal({ from: :file, file: first, key: dsa }, manager.known_identities[dsa_pk]) end def test_each_identity_should_not_prompt_for_passphrase_in_non_interactive_mode manager(non_interactive: true).stubs(:agent).returns(nil) first = File.expand_path("/first") - stub_file_private_key first, rsa, passphrase: :should_not_be_asked + stub_file_private_key first, rsa, rsa_pk, passphrase: :should_not_be_asked identities = [] manager.each_identity { |identity| identities << identity } assert_equal(identities, []) @@ -85,11 +129,28 @@ module Authentication manager.each_identity { |identity| identities << identity } assert_equal 2, identities.length - assert_equal rsa.to_blob, identities.first.to_blob - assert_equal dsa.to_blob, identities.last.to_blob + assert_equal rsa_pk.to_blob, identities.first.to_blob + assert_equal dsa_pk.to_blob, identities.last.to_blob + + assert_equal({ from: :agent, identity: rsa_pk }, manager.known_identities[rsa_pk]) + assert_equal({ from: :agent, identity: dsa_pk }, manager.known_identities[dsa_pk]) + end + + def test_each_identity_should_match_explicit_keycert_with_agent_provided_identity + manager.stubs(:agent).returns(agent) + stub_explicit_file_cert File.expand_path("/cert"), rsa_cert - assert_equal({ from: :agent }, manager.known_identities[rsa]) - assert_equal({ from: :agent }, manager.known_identities[dsa]) + identities = [] + manager.each_identity { |identity| identities << identity } + + assert_equal 3, identities.length + assert_equal rsa_pk.to_blob, identities[0].to_blob + assert_equal dsa_pk.to_blob, identities[1].to_blob + assert_equal rsa_cert.to_blob, identities[2].to_blob + + assert_equal({ from: :agent, identity: rsa_pk }, manager.known_identities[rsa_pk]) + assert_equal({ from: :agent, identity: dsa_pk }, manager.known_identities[dsa_pk]) + assert_equal({ from: :agent, identity: rsa_pk }, manager.known_identities[rsa_cert]) end def test_identities_with_ecdsa_should_load_from_agent @@ -99,32 +160,32 @@ module Authentication manager.each_identity { |identity| identities << identity } assert_equal 5, identities.length - assert_equal rsa.to_blob, identities[0].to_blob - assert_equal dsa.to_blob, identities[1].to_blob - assert_equal ecdsa_sha2_nistp256.to_blob, identities[2].to_blob - assert_equal ecdsa_sha2_nistp384.to_blob, identities[3].to_blob - assert_equal ecdsa_sha2_nistp521.to_blob, identities[4].to_blob - - assert_equal({ from: :agent }, manager.known_identities[rsa]) - assert_equal({ from: :agent }, manager.known_identities[dsa]) - assert_equal({ from: :agent }, manager.known_identities[ecdsa_sha2_nistp256]) - assert_equal({ from: :agent }, manager.known_identities[ecdsa_sha2_nistp384]) - assert_equal({ from: :agent }, manager.known_identities[ecdsa_sha2_nistp521]) + assert_equal rsa_pk.to_blob, identities[0].to_blob + assert_equal dsa_pk.to_blob, identities[1].to_blob + assert_equal ecdsa_sha2_nistp256_pk.to_blob, identities[2].to_blob + assert_equal ecdsa_sha2_nistp384_pk.to_blob, identities[3].to_blob + assert_equal ecdsa_sha2_nistp521_pk.to_blob, identities[4].to_blob + + assert_equal({ from: :agent, identity: rsa_pk }, manager.known_identities[rsa_pk]) + assert_equal({ from: :agent, identity: dsa_pk }, manager.known_identities[dsa_pk]) + assert_equal({ from: :agent, identity: ecdsa_sha2_nistp256_pk }, manager.known_identities[ecdsa_sha2_nistp256_pk]) + assert_equal({ from: :agent, identity: ecdsa_sha2_nistp384_pk }, manager.known_identities[ecdsa_sha2_nistp384_pk]) + assert_equal({ from: :agent, identity: ecdsa_sha2_nistp521_pk }, manager.known_identities[ecdsa_sha2_nistp521_pk]) end def test_only_identities_with_key_files_should_load_from_agent_of_keys_only_set manager(keys_only: true).stubs(:agent).returns(agent) first = File.expand_path("/first") - stub_file_private_key first, rsa + stub_file_private_key first, rsa, rsa_pk identities = [] manager.each_identity { |identity| identities << identity } assert_equal 1, identities.length - assert_equal rsa.to_blob, identities.first.to_blob + assert_equal rsa_pk.to_blob, identities.first.to_blob - assert_equal({ from: :agent }, manager.known_identities[rsa]) + assert_equal({ from: :agent, identity: rsa_pk }, manager.known_identities[rsa_pk]) assert manager.use_agent? end @@ -132,9 +193,9 @@ module Authentication manager.stubs(:agent).returns(agent) first = File.expand_path("/first") - stub_file_public_key first, rsa + stub_file_private_key first, rsa, rsa_pk second = File.expand_path("/second") - stub_file_private_key second, dsa, passphrase: :should_not_be_asked + stub_file_private_key second, dsa, dsa_pk, passphrase: :should_not_be_asked identities = [] manager.each_identity do |identity| @@ -143,23 +204,31 @@ module Authentication end assert_equal 1, identities.length - assert_equal rsa.to_blob, identities.first.to_blob + assert_equal rsa_pk.to_blob, identities.first.to_blob end def test_sign_with_agent_originated_key_should_request_signature_from_agent manager.stubs(:agent).returns(agent) manager.each_identity { |identity| } # preload the known_identities - agent.expects(:sign).with(rsa, "hello, world").returns("abcxyz123") - assert_equal "abcxyz123", manager.sign(rsa, "hello, world") + agent.expects(:sign).with(rsa_pk, "hello, world").returns("abcxyz123") + assert_equal "abcxyz123", manager.sign(rsa_pk, "hello, world") + end + + def test_sign_with_agent_originated_key_should_be_signable_through_explicitly_loaded_cert + stub_explicit_file_cert File.expand_path("/cert"), rsa_cert + manager.stubs(:agent).returns(agent) + manager.each_identity { |identity| } # preload the known_identities + agent.expects(:sign).with(rsa_pk, "hello, world").returns("abcxyz123") + assert_equal "abcxyz123", manager.sign(rsa_cert, "hello, world") end def test_sign_with_file_originated_key_should_load_private_key_and_sign_with_it manager.stubs(:agent).returns(nil) first = File.expand_path("/first") - stub_file_private_key first, rsa(512) + stub_file_private_key first, rsa(512), rsa_pk rsa.expects(:ssh_do_sign).with("hello, world").returns("abcxyz123") manager.each_identity { |identity| } # preload the known_identities - assert_equal "\0\0\0\assh-rsa\0\0\0\011abcxyz123", manager.sign(rsa, "hello, world") + assert_equal "\0\0\0\assh-rsa\0\0\0\011abcxyz123", manager.sign(rsa_pk, "hello, world") end def test_sign_with_file_originated_key_should_raise_key_manager_error_if_unloadable @@ -180,7 +249,7 @@ module Authentication private - def stub_file_private_key(name, key, options = {}) + def stub_file_private_key(name, key, public_key, options = {}) manager.add(name) File.stubs(:file?).with(name).returns(true) File.stubs(:readable?).with(name).returns(true) @@ -199,9 +268,9 @@ module Authentication Net::SSH::KeyFactory.expects(:load_private_key).with(name, nil, any_of(true, false), prompt).returns(key).at_least_once end - # do not override OpenSSL::PKey::EC#public_key - # (it will be called in transport/openssl.rb.) - key.stubs(:public_key).returns(key) unless key.public_key.is_a?(OpenSSL::PKey::EC::Point) + # We need to stub #public_key as we rely on object identity to + # access #known_identities by private_key + key.stubs(:public_key).returns(public_key) end def stub_file_public_key(name, key) @@ -215,7 +284,7 @@ module Authentication Net::SSH::KeyFactory.expects(:load_public_key).with(name + ".pub").returns(key).at_least_once end - def stub_file_cert(name, key) + def stub_implicit_file_cert(name, key, cert) manager.add(name) File.stubs(:file?).with(name).returns(true) File.stubs(:readable?).with(name).returns(true) @@ -224,7 +293,33 @@ module Authentication File.stubs(:file?).with(name + "-cert.pub").returns(true) File.stubs(:readable?).with(name + "-cert.pub").returns(true) - Net::SSH::KeyFactory.expects(:load_public_key).with(name + "-cert.pub").returns(key).at_least_once + Net::SSH::KeyFactory.expects(:load_public_key).with(name + "-cert.pub").returns(cert).at_least_once + end + + def stub_explicit_file_cert(name, cert) + manager.add_keycert(name) + File.stubs(:file?).with(name).returns(true) + File.stubs(:readable?).with(name).returns(true) + + Net::SSH::KeyFactory.expects(:load_public_key).with(name).returns(cert).at_least_once + end + + def rsa_cert + @cert ||= begin + cert = Net::SSH::Authentication::Certificate.new + cert.type = :user + cert.key = rsa_pk + cert.serial = 1 + cert.key_id = "test key" + cert.valid_principals = %w[test user] + cert.valid_before = Time.now - 86400 + cert.valid_after = Time.now + 86400 + cert.critical_options = {} + cert.extensions = {} + cert.reserved = '' + cert.sign!(OpenSSL::PKey::DSA.new(512)) + cert + end end def rsa(size=512) @@ -247,15 +342,35 @@ module Authentication @ecdsa_sha2_nistp521 ||= OpenSSL::PKey::EC.new('secp521r1').generate_key end + def rsa_pk + @rsa_pk ||= rsa.public_key + end + + def dsa_pk + @dsa_pk ||= dsa.public_key + end + + def ecdsa_sha2_nistp256_pk + @ecdsa_sha2_nistp256_pk ||= ecdsa_sha2_nistp256.public_key + end + + def ecdsa_sha2_nistp384_pk + @ecdsa_sha2_nistp384_pk ||= ecdsa_sha2_nistp521.public_key + end + + def ecdsa_sha2_nistp521_pk + @ecdsa_sha2_nistp521_pk ||= ecdsa_sha2_nistp521.public_key + end + def agent - @agent ||= stub("agent", identities: [rsa, dsa]) + @agent ||= stub("agent", identities: [rsa_pk, dsa_pk]) end def agent_with_ecdsa_keys - @agent ||= stub("agent", identities: [rsa, dsa, - ecdsa_sha2_nistp256, - ecdsa_sha2_nistp384, - ecdsa_sha2_nistp521]) + @agent ||= stub("agent", identities: [rsa_pk, dsa_pk, + ecdsa_sha2_nistp256_pk, + ecdsa_sha2_nistp384_pk, + ecdsa_sha2_nistp521_pk]) end def prompt diff --git a/test/configs/exact_match b/test/configs/exact_match index 908d631..911f750 100644 --- a/test/configs/exact_match +++ b/test/configs/exact_match @@ -5,4 +5,6 @@ Host other.host Host test.host Compression yes ForwardAgent yes - Port 1234
\ No newline at end of file + Port 1234 + CertificateFile ~/.ssh/id_rsa-my-cert.pub + CertificateFile ~/.ssh/cert.pub diff --git a/test/integration/test_cert_user_auth.rb b/test/integration/test_cert_user_auth.rb index 963665f..b975d0a 100644 --- a/test/integration/test_cert_user_auth.rb +++ b/test/integration/test_cert_user_auth.rb @@ -9,22 +9,68 @@ unless ENV['NET_SSH_NO_ED25519'] class TestCertUserAuth < NetSSHTest include IntegrationTestHelpers - - def test_ed25519_with_cert + + def test_ed25519_with_implicit_cert Dir.mktmpdir do |dir| sh "rm -rf #{dir}/id_rsa_ed25519 #{dir}/id_rsa_ed25519.pub" sh "ssh-keygen -q -f #{dir}/id_rsa_ed25519 -t ed25519 -N ''" sign_user_key('net_ssh_1',"#{dir}/id_rsa_ed25519.pub") - - # sshopts = '-vvvv -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no' - # sh "ssh -i #{dir}/id_rsa_ed25519 #{sshopts} net_ssh_1@localhost echo 'hello'" - + ret = Net::SSH.start("localhost", "net_ssh_1", keys: "#{dir}/id_rsa_ed25519") do |ssh| ssh.exec! 'echo "hello from:$USER"' end assert_equal "hello from:net_ssh_1\n", ret end end + + def test_ed25519_with_explicit_cert + Dir.mktmpdir do |dir| + sh "rm -rf #{dir}/id_rsa_ed25519 #{dir}/id_rsa_ed25519.pub" + sh "ssh-keygen -q -f #{dir}/id_rsa_ed25519 -t ed25519 -N ''" + sign_user_key('net_ssh_1',"#{dir}/id_rsa_ed25519.pub") + sh "mv #{dir}/id_rsa_ed25519-cert.pub #{dir}/cert" + + ret = Net::SSH.start("localhost", "net_ssh_1", keys: "#{dir}/id_rsa_ed25519", keycerts: "#{dir}/cert") do |ssh| + ssh.exec! 'echo "hello from:$USER"' + end + assert_equal "hello from:net_ssh_1\n", ret + end + end + + def test_ed25519_with_cert_in_agent + Dir.mktmpdir do |dir| + with_agent do + sh "rm -rf #{dir}/id_rsa_ed25519 #{dir}/id_rsa_ed25519.pub" + sh "ssh-keygen -q -f #{dir}/id_rsa_ed25519 -t ed25519 -N 'pwd'" + sign_user_key('net_ssh_1',"#{dir}/id_rsa_ed25519.pub") + ssh_add("#{dir}/id_rsa_ed25519", "pwd") + sh "rm -rf #{dir}/id_rsa_ed25519 #{dir}/id_rsa_ed25519.pub #{dir}/id_rsa_ed25519-cert.pub" + + ret = Net::SSH.start("localhost", "net_ssh_1") do |ssh| + ssh.exec! 'echo "hello from:$USER"' + end + assert_equal "hello from:net_ssh_1\n", ret + end + end + end + + def test_ed25519_with_key_in_agent_and_explicit_cert + Dir.mktmpdir do |dir| + with_agent do + sh "rm -rf #{dir}/id_rsa_ed25519 #{dir}/id_rsa_ed25519.pub" + sh "ssh-keygen -q -f #{dir}/id_rsa_ed25519 -t ed25519 -N ''" + # add key before signing cert + ssh_add("#{dir}/id_rsa_ed25519", "pwd") + sign_user_key('net_ssh_1',"#{dir}/id_rsa_ed25519.pub") + sh "rm -rf #{dir}/id_rsa_ed25519 #{dir}/id_rsa_ed25519.pub" + + ret = Net::SSH.start("localhost", "net_ssh_1", keycerts: "#{dir}/id_rsa_ed25519-cert.pub") do |ssh| + ssh.exec! 'echo "hello from:$USER"' + end + assert_equal "hello from:net_ssh_1\n", ret + end + end + end end end diff --git a/test/test_config.rb b/test/test_config.rb index 67b7c86..eab6d7f 100644 --- a/test/test_config.rb +++ b/test/test_config.rb @@ -89,6 +89,7 @@ class TestConfig < NetSSHTest assert config[:compression] assert config[:forward_agent] assert_equal %w(~/.ssh/id_dsa), config[:keys] + assert_equal %w(~/.ssh/id_rsa-my-cert.pub ~/.ssh/cert.pub), config[:keycerts] assert !config.key?(:rekey_limit) end @@ -131,6 +132,7 @@ class TestConfig < NetSSHTest 'hostkeyalgorithms' => "d,e,f", 'identityfile' => %w(g h i), 'macs' => "j,k,l", + 'certificatefile' => %w(m n o), 'passwordauthentication' => true, 'port' => 1234, 'pubkeyauthentication' => true, @@ -155,6 +157,7 @@ class TestConfig < NetSSHTest assert_equal %w(d e f), net_ssh[:host_key] assert_equal %w(g h i), net_ssh[:keys] assert_equal %w(j k l), net_ssh[:hmac] + assert_equal %w(m n o), net_ssh[:keycerts] assert_equal 1234, net_ssh[:port] assert_equal 1024, net_ssh[:rekey_limit] assert_equal "127.0.0.1", net_ssh[:bind_address] |