Merge pull request #13334 from chrisroberts/ssh-supported-key-type-inspect
Inspect guest for supported key types
This commit is contained in:
commit
66c39b70f5
@ -838,26 +838,59 @@ module VagrantPlugins
|
|||||||
end
|
end
|
||||||
|
|
||||||
def supported_key_types
|
def supported_key_types
|
||||||
|
return @supported_key_types if @supported_key_types
|
||||||
|
|
||||||
if @connection.nil?
|
if @connection.nil?
|
||||||
raise Vagrant::Errors::SSHNotReady
|
raise Vagrant::Errors::SSHNotReady
|
||||||
end
|
end
|
||||||
|
|
||||||
server_data = @connection.
|
list = ""
|
||||||
transport&.
|
result = sudo("sshd -T | grep key", {error_check: false}) do |type, data|
|
||||||
algorithms&.
|
list << data
|
||||||
instance_variable_get(:@server_data)
|
|
||||||
if server_data.nil?
|
|
||||||
@logger.warn("No server data available for key type support check")
|
|
||||||
raise ServerDataError, "no data available"
|
|
||||||
end
|
|
||||||
if !server_data.is_a?(Hash)
|
|
||||||
@logger.warn("Server data is not expected type (expecting Hash, got #{server_data.class})")
|
|
||||||
raise ServerDataError, "unexpected type encountered (expecting Hash, got #{server_data.class})"
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@logger.debug("server supported key type list: #{server_data[:host_key]}")
|
# If the command failed, attempt to extract some supported
|
||||||
|
# key information from within net-ssh
|
||||||
|
if result != 0
|
||||||
|
server_data = @connection.
|
||||||
|
transport&.
|
||||||
|
algorithms&.
|
||||||
|
instance_variable_get(:@server_data)
|
||||||
|
if server_data.nil?
|
||||||
|
@logger.warn("No server data available for key type support check")
|
||||||
|
raise ServerDataError, "no data available"
|
||||||
|
end
|
||||||
|
if !server_data.is_a?(Hash)
|
||||||
|
@logger.warn("Server data is not expected type (expecting Hash, got #{server_data.class})")
|
||||||
|
raise ServerDataError, "unexpected type encountered (expecting Hash, got #{server_data.class})"
|
||||||
|
end
|
||||||
|
|
||||||
server_data[:host_key]
|
@logger.debug("server supported key type list (extracted from connection server info using host key): #{server_data[:host_key]}")
|
||||||
|
return @supported_key_types = server_data[:host_key]
|
||||||
|
end
|
||||||
|
|
||||||
|
# Convert the options into a Hash for easy access
|
||||||
|
opts = Hash[*list.split("\n").map{|line| line.split(" ", 2)}.flatten]
|
||||||
|
|
||||||
|
# Define the option names to check for in preferred order
|
||||||
|
# NOTE: pubkeyacceptedkeytypes has been renamed to pubkeyacceptedalgorithms
|
||||||
|
# ref: https://github.com/openssh/openssh-portable/commit/ee9c0da8035b3168e8e57c1dedc2d1b0daf00eec
|
||||||
|
["pubkeyacceptedalgorithms", "pubkeyacceptedkeytypes", "hostkeyalgorithms"].each do |opt_name|
|
||||||
|
next if !opts.key?(opt_name)
|
||||||
|
|
||||||
|
@supported_key_types = opts[opt_name].split(",")
|
||||||
|
@logger.debug("server supported key type list (using #{opt_name}): #{@supported_key_types}")
|
||||||
|
|
||||||
|
return @supported_key_types
|
||||||
|
end
|
||||||
|
|
||||||
|
# Still here means unable to determine key types
|
||||||
|
# so log what information was returned and toss
|
||||||
|
# and error
|
||||||
|
@logger.warn("failed to determine supported key types from remote inspection")
|
||||||
|
@logger.debug("data returned for supported key types remote inspection: #{list.inspect}")
|
||||||
|
|
||||||
|
raise ServerDataError, "no data available"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -69,7 +69,8 @@ describe VagrantPlugins::CommunicatorSSH::Communicator do
|
|||||||
let(:command_stderr_data) { '' }
|
let(:command_stderr_data) { '' }
|
||||||
# Mock for net-ssh scp
|
# Mock for net-ssh scp
|
||||||
let(:scp) { double("scp") }
|
let(:scp) { double("scp") }
|
||||||
|
# Value returned from remote ssh supported key check
|
||||||
|
let(:sudo_supported_key_list) { "pubkeyacceptedalgorithms ssh-rsa" }
|
||||||
|
|
||||||
# Setup for commands using the net-ssh connection. This can be reused where needed
|
# Setup for commands using the net-ssh connection. This can be reused where needed
|
||||||
# by providing to `before`
|
# by providing to `before`
|
||||||
@ -93,13 +94,16 @@ describe VagrantPlugins::CommunicatorSSH::Communicator do
|
|||||||
and_yield(nil, exit_data)
|
and_yield(nil, exit_data)
|
||||||
# Return mocked net-ssh connection during setup
|
# Return mocked net-ssh connection during setup
|
||||||
allow(communicator).to receive(:retryable).and_return(connection)
|
allow(communicator).to receive(:retryable).and_return(connection)
|
||||||
|
# Stub in a response for supported key types check
|
||||||
|
allow(communicator).to receive(:sudo).with("sshd -T | grep key", any_args).
|
||||||
|
and_yield(:stdout, sudo_supported_key_list).and_return(0)
|
||||||
end
|
end
|
||||||
|
|
||||||
before do
|
before do
|
||||||
allow(host).to receive(:capability?).and_return(false)
|
allow(host).to receive(:capability?).and_return(false)
|
||||||
end
|
end
|
||||||
|
|
||||||
describe ".wait_for_ready" do
|
describe "#wait_for_ready" do
|
||||||
before(&connection_setup)
|
before(&connection_setup)
|
||||||
context "with no static config (default scenario)" do
|
context "with no static config (default scenario)" do
|
||||||
context "when ssh_info requires a multiple tries before it is ready" do
|
context "when ssh_info requires a multiple tries before it is ready" do
|
||||||
@ -162,7 +166,7 @@ describe VagrantPlugins::CommunicatorSSH::Communicator do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "reset!" do
|
describe "#reset!" do
|
||||||
let(:connection) { double("connection") }
|
let(:connection) { double("connection") }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
@ -182,7 +186,7 @@ describe VagrantPlugins::CommunicatorSSH::Communicator do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe ".ready?" do
|
describe "#ready?" do
|
||||||
before(&connection_setup)
|
before(&connection_setup)
|
||||||
it "returns true if shell test is successful" do
|
it "returns true if shell test is successful" do
|
||||||
expect(communicator.ready?).to be(true)
|
expect(communicator.ready?).to be(true)
|
||||||
@ -248,8 +252,6 @@ describe VagrantPlugins::CommunicatorSSH::Communicator do
|
|||||||
let(:path_joiner){ double("path_joiner") }
|
let(:path_joiner){ double("path_joiner") }
|
||||||
let(:algorithms) { double(:algorithms) }
|
let(:algorithms) { double(:algorithms) }
|
||||||
let(:transport) { double(:transport, algorithms: algorithms) }
|
let(:transport) { double(:transport, algorithms: algorithms) }
|
||||||
let(:valid_key_types) { [] }
|
|
||||||
let(:server_data) { { host_key: valid_key_types} }
|
|
||||||
|
|
||||||
before do
|
before do
|
||||||
allow(Vagrant::Util::Keypair).to receive(:create).
|
allow(Vagrant::Util::Keypair).to receive(:create).
|
||||||
@ -264,7 +266,6 @@ describe VagrantPlugins::CommunicatorSSH::Communicator do
|
|||||||
allow(guest).to receive(:capability).with(:insert_public_key)
|
allow(guest).to receive(:capability).with(:insert_public_key)
|
||||||
allow(guest).to receive(:capability).with(:remove_public_key)
|
allow(guest).to receive(:capability).with(:remove_public_key)
|
||||||
allow(connection).to receive(:transport).and_return(transport)
|
allow(connection).to receive(:transport).and_return(transport)
|
||||||
allow(algorithms).to receive(:instance_variable_get).with(:@server_data).and_return(server_data)
|
|
||||||
allow(communicator).to receive(:supported_key_types).and_raise(described_class.const_get(:ServerDataError))
|
allow(communicator).to receive(:supported_key_types).and_raise(described_class.const_get(:ServerDataError))
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -297,7 +298,7 @@ describe VagrantPlugins::CommunicatorSSH::Communicator do
|
|||||||
|
|
||||||
context "with server algorithm support data" do
|
context "with server algorithm support data" do
|
||||||
before do
|
before do
|
||||||
allow(communicator).to receive(:supported_key_types).and_call_original
|
allow(communicator).to receive(:supported_key_types).and_return(valid_key_types)
|
||||||
end
|
end
|
||||||
|
|
||||||
context "when rsa is the only match" do
|
context "when rsa is the only match" do
|
||||||
@ -371,8 +372,7 @@ describe VagrantPlugins::CommunicatorSSH::Communicator do
|
|||||||
|
|
||||||
context "when an error is encountered getting server data" do
|
context "when an error is encountered getting server data" do
|
||||||
before do
|
before do
|
||||||
expect(communicator).to receive(:supported_key_types).and_call_original
|
expect(communicator).to receive(:supported_key_types).and_raise(StandardError)
|
||||||
expect(connection).to receive(:transport).and_raise(StandardError)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should default to rsa key" do
|
it "should default to rsa key" do
|
||||||
@ -385,7 +385,7 @@ describe VagrantPlugins::CommunicatorSSH::Communicator do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe ".execute" do
|
describe "#execute" do
|
||||||
before(&connection_setup)
|
before(&connection_setup)
|
||||||
it "runs valid command and returns successful status code" do
|
it "runs valid command and returns successful status code" do
|
||||||
expect(command_channel).to receive(:send_data).with(/ls \/\n/)
|
expect(command_channel).to receive(:send_data).with(/ls \/\n/)
|
||||||
@ -579,7 +579,7 @@ describe VagrantPlugins::CommunicatorSSH::Communicator do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe ".test" do
|
describe "#test" do
|
||||||
before(&connection_setup)
|
before(&connection_setup)
|
||||||
context "with exit code as zero" do
|
context "with exit code as zero" do
|
||||||
it "returns true" do
|
it "returns true" do
|
||||||
@ -598,7 +598,7 @@ describe VagrantPlugins::CommunicatorSSH::Communicator do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe ".upload" do
|
describe "#upload" do
|
||||||
before do
|
before do
|
||||||
expect(communicator).to receive(:scp_connect).and_yield(scp)
|
expect(communicator).to receive(:scp_connect).and_yield(scp)
|
||||||
allow(communicator).to receive(:create_remote_directory)
|
allow(communicator).to receive(:create_remote_directory)
|
||||||
@ -704,7 +704,7 @@ describe VagrantPlugins::CommunicatorSSH::Communicator do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe ".download" do
|
describe "#download" do
|
||||||
before do
|
before do
|
||||||
expect(communicator).to receive(:scp_connect).and_yield(scp)
|
expect(communicator).to receive(:scp_connect).and_yield(scp)
|
||||||
end
|
end
|
||||||
@ -715,7 +715,7 @@ describe VagrantPlugins::CommunicatorSSH::Communicator do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe ".connect" do
|
describe "#connect" do
|
||||||
|
|
||||||
it "cannot be called directly" do
|
it "cannot be called directly" do
|
||||||
expect{ communicator.connect }.to raise_error(NoMethodError)
|
expect{ communicator.connect }.to raise_error(NoMethodError)
|
||||||
@ -1030,7 +1030,7 @@ describe VagrantPlugins::CommunicatorSSH::Communicator do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe ".insecure_key?" do
|
describe "#insecure_key?" do
|
||||||
let(:key_data) { "" }
|
let(:key_data) { "" }
|
||||||
let(:key_file) {
|
let(:key_file) {
|
||||||
if !@key_file
|
if !@key_file
|
||||||
@ -1069,7 +1069,7 @@ describe VagrantPlugins::CommunicatorSSH::Communicator do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe ".generate_environment_export" do
|
describe "#generate_environment_export" do
|
||||||
it "should generate bourne shell compatible export" do
|
it "should generate bourne shell compatible export" do
|
||||||
expect(communicator.send(:generate_environment_export, "TEST", "value")).to eq("export TEST=\"value\"\n")
|
expect(communicator.send(:generate_environment_export, "TEST", "value")).to eq("export TEST=\"value\"\n")
|
||||||
end
|
end
|
||||||
@ -1082,4 +1082,129 @@ describe VagrantPlugins::CommunicatorSSH::Communicator do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "#supported_key_types" do
|
||||||
|
let(:sudo_result) { 0 }
|
||||||
|
let(:sudo_data) { "" }
|
||||||
|
let(:server_data_error) { VagrantPlugins::CommunicatorSSH::Communicator::ServerDataError }
|
||||||
|
let(:transport) { double("transport", algorithms: algorithms) }
|
||||||
|
let(:algorithms) { double("algorithms") }
|
||||||
|
|
||||||
|
before do
|
||||||
|
allow(communicator).to receive(:ready?).and_return(true)
|
||||||
|
expect(communicator).to receive(:sudo).
|
||||||
|
with("sshd -T | grep key", any_args).
|
||||||
|
and_yield(:stdout, sudo_data).
|
||||||
|
and_return(sudo_result)
|
||||||
|
# The @connection value is checked to determine if supported key types
|
||||||
|
# can be checked. To facilitate this, set it to a non-nil value
|
||||||
|
communicator.instance_variable_set(:@connection, connection)
|
||||||
|
allow(connection).to receive(:transport).and_return(transport)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should raise an error when no data is returned" do
|
||||||
|
expect { communicator.send(:supported_key_types) }.to raise_error(server_data_error)
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when sudo command is unsuccessful" do
|
||||||
|
let(:sudo_result) { 1 }
|
||||||
|
|
||||||
|
it "should inspect the net-ssh connection" do
|
||||||
|
expect(algorithms).to receive(:instance_variable_get).
|
||||||
|
with(:@server_data).and_return({})
|
||||||
|
communicator.send(:supported_key_types)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when data includes pubkeyacceptedalgorithms" do
|
||||||
|
let(:sudo_data) do
|
||||||
|
"pubkeyauthentication yes
|
||||||
|
gssapikeyexchange no
|
||||||
|
gssapistorecredentialsonrekey no
|
||||||
|
trustedusercakeys none
|
||||||
|
revokedkeys none
|
||||||
|
authorizedkeyscommand none
|
||||||
|
authorizedkeyscommanduser none
|
||||||
|
hostkeyagent none
|
||||||
|
hostbasedacceptedkeytypes ecdsa-sha2-nistp521,ssh-ed25519,rsa-sha2-512,rsa-sha2-256,ssh-rsa
|
||||||
|
hostkeyalgorithms ssh-ed25519,rsa-sha2-512,rsa-sha2-256,ssh-rsa
|
||||||
|
pubkeyacceptedalgorithms rsa-sha2-512,rsa-sha2-256,ssh-rsa
|
||||||
|
authorizedkeysfile .ssh/authorized_keys
|
||||||
|
hostkey /etc/ssh/ssh_host_rsa_key
|
||||||
|
rekeylimit 0 0"
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should return expected values" do
|
||||||
|
expect(communicator.send(:supported_key_types)).to eq(["rsa-sha2-512", "rsa-sha2-256", "ssh-rsa"])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when data includes pubkeyacceptedkeytypes" do
|
||||||
|
let(:sudo_data) do
|
||||||
|
"pubkeyauthentication yes
|
||||||
|
gssapikeyexchange no
|
||||||
|
gssapistorecredentialsonrekey no
|
||||||
|
trustedusercakeys none
|
||||||
|
revokedkeys none
|
||||||
|
authorizedkeyscommand none
|
||||||
|
authorizedkeyscommanduser none
|
||||||
|
hostkeyagent none
|
||||||
|
hostbasedacceptedkeytypes ecdsa-sha2-nistp521,ssh-ed25519,rsa-sha2-512,rsa-sha2-256,ssh-rsa
|
||||||
|
hostkeyalgorithms ssh-ed25519,rsa-sha2-512,rsa-sha2-256,ssh-rsa
|
||||||
|
pubkeyacceptedkeytypes rsa-sha2-512,rsa-sha2-256,ssh-rsa
|
||||||
|
authorizedkeysfile .ssh/authorized_keys
|
||||||
|
hostkey /etc/ssh/ssh_host_rsa_key
|
||||||
|
rekeylimit 0 0"
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should return expected values" do
|
||||||
|
expect(communicator.send(:supported_key_types)).
|
||||||
|
to eq(["rsa-sha2-512", "rsa-sha2-256", "ssh-rsa"])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when data does not include pubkeyacceptedalgorithms or pubkeyacceptedkeytypes" do
|
||||||
|
let(:sudo_data) do
|
||||||
|
"pubkeyauthentication yes
|
||||||
|
gssapikeyexchange no
|
||||||
|
gssapistorecredentialsonrekey no
|
||||||
|
trustedusercakeys none
|
||||||
|
revokedkeys none
|
||||||
|
authorizedkeyscommand none
|
||||||
|
authorizedkeyscommanduser none
|
||||||
|
hostkeyagent none
|
||||||
|
hostbasedacceptedkeytypes ecdsa-sha2-nistp521,ssh-ed25519,rsa-sha2-512,rsa-sha2-256,ssh-rsa
|
||||||
|
hostkeyalgorithms ssh-ed25519,rsa-sha2-512,rsa-sha2-256,ssh-rsa
|
||||||
|
authorizedkeysfile .ssh/authorized_keys
|
||||||
|
hostkey /etc/ssh/ssh_host_rsa_key
|
||||||
|
rekeylimit 0 0"
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should use hostkeyalgorithms" do
|
||||||
|
expect(communicator.send(:supported_key_types)).
|
||||||
|
to eq(["ssh-ed25519", "rsa-sha2-512", "rsa-sha2-256", "ssh-rsa"])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when data does not include defined config options" do
|
||||||
|
let(:sudo_data) do
|
||||||
|
"pubkeyauthentication yes
|
||||||
|
gssapikeyexchange no
|
||||||
|
gssapistorecredentialsonrekey no
|
||||||
|
trustedusercakeys none
|
||||||
|
revokedkeys none
|
||||||
|
authorizedkeyscommand none
|
||||||
|
authorizedkeyscommanduser none
|
||||||
|
hostkeyagent none
|
||||||
|
authorizedkeysfile .ssh/authorized_keys
|
||||||
|
hostkey /etc/ssh/ssh_host_rsa_key
|
||||||
|
rekeylimit 0 0"
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should raise error" do
|
||||||
|
expect { communicator.send(:supported_key_types) }.
|
||||||
|
to raise_error(server_data_error)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user