diff --git a/lib/vagrant/util/ssh.rb b/lib/vagrant/util/ssh.rb index 0a4704e54..4378b5fe9 100644 --- a/lib/vagrant/util/ssh.rb +++ b/lib/vagrant/util/ssh.rb @@ -146,6 +146,13 @@ module Vagrant "-o", "UserKnownHostsFile=/dev/null"] end + if !ssh_info[:disable_deprecated_algorithms] + command_options += [ + "-o", "PubkeyAcceptedKeyTypes=+ssh-rsa", + "-o", "HostKeyAlgorithms=+ssh-rsa", + ] + end + # If we're not in plain mode and :private_key_path is set attach the private key path(s). if !plain_mode && options[:private_key_path] options[:private_key_path].each do |path| diff --git a/plugins/commands/ssh_config/command.rb b/plugins/commands/ssh_config/command.rb index e0bf79ff2..4e350ecf1 100644 --- a/plugins/commands/ssh_config/command.rb +++ b/plugins/commands/ssh_config/command.rb @@ -55,7 +55,8 @@ module VagrantPlugins proxy_command: ssh_info[:proxy_command], ssh_command: ssh_info[:ssh_command], forward_env: ssh_info[:forward_env], - config: ssh_info[:config] + config: ssh_info[:config], + disable_deprecated_algorithms: ssh_info[:disable_deprecated_algorithms] } # Render the template and output directly to STDOUT diff --git a/plugins/kernel_v2/config/ssh_connect.rb b/plugins/kernel_v2/config/ssh_connect.rb index 21116d26e..2e7e2ed65 100644 --- a/plugins/kernel_v2/config/ssh_connect.rb +++ b/plugins/kernel_v2/config/ssh_connect.rb @@ -18,6 +18,7 @@ module VagrantPlugins attr_accessor :dsa_authentication attr_accessor :extra_args attr_accessor :remote_user + attr_accessor :disable_deprecated_algorithms def initialize @host = UNSET_VALUE @@ -35,6 +36,7 @@ module VagrantPlugins @dsa_authentication = UNSET_VALUE @extra_args = UNSET_VALUE @remote_user = UNSET_VALUE + @disable_deprecated_algorithms = UNSET_VALUE end def finalize! @@ -51,6 +53,7 @@ module VagrantPlugins @dsa_authentication = true if @dsa_authentication == UNSET_VALUE @extra_args = nil if @extra_args == UNSET_VALUE @config = nil if @config == UNSET_VALUE + @disable_deprecated_algorithms = false if @disable_deprecated_algorithms == UNSET_VALUE @connect_timeout = DEFAULT_SSH_CONNECT_TIMEOUT if @connect_timeout == UNSET_VALUE if @private_key_path && !@private_key_path.is_a?(Array) diff --git a/templates/commands/ssh_config/config.erb b/templates/commands/ssh_config/config.erb index b0cdbeedc..af89e43ed 100644 --- a/templates/commands/ssh_config/config.erb +++ b/templates/commands/ssh_config/config.erb @@ -36,3 +36,7 @@ Host <%= host_key %> <% if proxy_command -%> ProxyCommand <%= proxy_command %> <% end -%> +<% if !disable_deprecated_algorithms -%> + PubkeyAcceptedKeyTypes +ssh-rsa + HostKeyAlgorithms +ssh-rsa +<% end -%> diff --git a/test/unit/plugins/commands/ssh_config/command_test.rb b/test/unit/plugins/commands/ssh_config/command_test.rb index 84f593e54..a22e5bd55 100644 --- a/test/unit/plugins/commands/ssh_config/command_test.rb +++ b/test/unit/plugins/commands/ssh_config/command_test.rb @@ -56,6 +56,8 @@ Host #{machine.name} IdentityFile /home/vagrant/.private/keys.key IdentitiesOnly yes LogLevel FATAL + PubkeyAcceptedKeyTypes +ssh-rsa + HostKeyAlgorithms +ssh-rsa SSHCONFIG end @@ -178,5 +180,31 @@ Host #{machine.name} expect(output).to include("Include /custom/ssh/config") end + + it "includes enabling ssh-rsa key types and host algorithms" do + output = "" + allow(subject).to receive(:safe_puts) do |data| + output += data if data + end + + subject.execute + + expect(output).to include("PubkeyAcceptedKeyTypes +ssh-rsa") + expect(output).to include("HostKeyAlgorithms +ssh-rsa") + end + + it "does not enable ssh-rsa key types and host algorithms when disabled" do + allow(machine).to receive(:ssh_info) { ssh_info.merge(disable_deprecated_algorithms: true) } + output = "" + allow(subject).to receive(:safe_puts) do |data| + output += data if data + end + + subject.execute + + expect(output).not_to include("PubkeyAcceptedKeyTypes +ssh-rsa") + expect(output).not_to include("HostKeyAlgorithms +ssh-rsa") + end + end end diff --git a/test/unit/vagrant/util/ssh_test.rb b/test/unit/vagrant/util/ssh_test.rb index f1998994e..db73a25d4 100644 --- a/test/unit/vagrant/util/ssh_test.rb +++ b/test/unit/vagrant/util/ssh_test.rb @@ -87,7 +87,55 @@ describe Vagrant::Util::SSH do expect(described_class.exec(ssh_info)).to eq(nil) expect(Vagrant::Util::SafeExec).to have_received(:exec) - .with(ssh_path, "vagrant@localhost", "-p", "2222", "-o", "LogLevel=FATAL","-o", "Compression=yes", "-o", "DSAAuthentication=yes", "-o", "StrictHostKeyChecking=no", "-o", "UserKnownHostsFile=/dev/null", "-i", ssh_info[:private_key_path][0]) + .with( + ssh_path, "vagrant@localhost", "-p", "2222", "-o", "LogLevel=FATAL", + "-o", "Compression=yes", + "-o", "DSAAuthentication=yes", + "-o", "StrictHostKeyChecking=no", + "-o", "UserKnownHostsFile=/dev/null", + "-o", "PubkeyAcceptedKeyTypes=+ssh-rsa", + "-o", "HostKeyAlgorithms=+ssh-rsa", + "-i", ssh_info[:private_key_path][0], + ) + end + + context "when deprecated algorithms are disabled" do + let(:ssh_info) {{ + host: "localhost", + port: 2222, + username: "vagrant", + disable_deprecated_algorithms: true, + }} + + it "does not include options to enable ssh-rsa key types and host key algorithms" do + expect(Vagrant::Util::SafeExec).to receive(:exec) do |*args| + args.each do |arg| + expect(arg).not_to eq("PubkeyAcceptedKeyTypes=+ssh-rsa") + expect(arg).not_to eq("HostKeyAlgorithms=+ssh-rsa") + end + end + described_class.exec(ssh_info) + end + end + + context "when deprecated algorithms are not disabled" do + let(:ssh_info) {{ + host: "localhost", + port: 2222, + username: "vagrant", + }} + + it "includes options to enable ssh-rsa key types and host key algorithms" do + expect(Vagrant::Util::SafeExec).to receive(:exec) do |*args| + expect( + args.any? { |arg| arg == "PubkeyAcceptedKeyTypes=+ssh-rsa" } + ).to be_truthy + expect( + args.any? { |arg| arg == "HostKeyAlgorithms=+ssh-rsa" } + ).to be_truthy + end + described_class.exec(ssh_info) + end end context "when using '%' in a private_key_path" do @@ -106,7 +154,16 @@ describe Vagrant::Util::SSH do described_class.exec(ssh_info) expect(Vagrant::Util::SafeExec).to have_received(:exec) - .with(ssh_path, "vagrant@localhost", "-p", "2222", "-o", "LogLevel=FATAL","-o", "Compression=yes", "-o", "DSAAuthentication=yes", "-o", "StrictHostKeyChecking=no", "-o", "UserKnownHostsFile=/dev/null", "-o", "IdentityFile=\"/tmp/percent%%folder\"") + .with( + ssh_path, "vagrant@localhost", "-p", "2222", "-o", "LogLevel=FATAL", + "-o", "Compression=yes", + "-o", "DSAAuthentication=yes", + "-o", "StrictHostKeyChecking=no", + "-o", "UserKnownHostsFile=/dev/null", + "-o", "PubkeyAcceptedKeyTypes=+ssh-rsa", + "-o", "HostKeyAlgorithms=+ssh-rsa", + "-o", "IdentityFile=\"/tmp/percent%%folder\"", + ) end end @@ -125,7 +182,14 @@ describe Vagrant::Util::SSH do expect(described_class.exec(ssh_info)).to eq(nil) expect(Vagrant::Util::SafeExec).to have_received(:exec) - .with(ssh_path, "vagrant@localhost", "-p", "2222", "-o", "LogLevel=FATAL", "-o", "StrictHostKeyChecking=no", "-o", "UserKnownHostsFile=/dev/null", "-i", ssh_info[:private_key_path][0]) + .with( + ssh_path, "vagrant@localhost", "-p", "2222", "-o", "LogLevel=FATAL", + "-o", "StrictHostKeyChecking=no", + "-o", "UserKnownHostsFile=/dev/null", + "-o", "PubkeyAcceptedKeyTypes=+ssh-rsa", + "-o", "HostKeyAlgorithms=+ssh-rsa", + "-i", ssh_info[:private_key_path][0], + ) end end @@ -143,7 +207,12 @@ describe Vagrant::Util::SSH do expect(described_class.exec(ssh_info)).to eq(nil) expect(Vagrant::Util::SafeExec).to have_received(:exec) - .with(ssh_path, "vagrant@localhost", "-p", "2222", "-o", "LogLevel=FATAL", "-i", ssh_info[:private_key_path][0]) + .with( + ssh_path, "vagrant@localhost", "-p", "2222", "-o", "LogLevel=FATAL", + "-o", "PubkeyAcceptedKeyTypes=+ssh-rsa", + "-o", "HostKeyAlgorithms=+ssh-rsa", + "-i", ssh_info[:private_key_path][0], + ) end end @@ -162,7 +231,13 @@ describe Vagrant::Util::SSH do expect(described_class.exec(ssh_info, {plain_mode: true})).to eq(nil) expect(Vagrant::Util::SafeExec).to have_received(:exec) - .with(ssh_path, "localhost", "-p", "2222", "-o", "LogLevel=FATAL", "-o", "StrictHostKeyChecking=no", "-o", "UserKnownHostsFile=/dev/null") + .with( + ssh_path, "localhost", "-p", "2222", "-o", "LogLevel=FATAL", + "-o", "StrictHostKeyChecking=no", + "-o", "UserKnownHostsFile=/dev/null", + "-o", "PubkeyAcceptedKeyTypes=+ssh-rsa", + "-o", "HostKeyAlgorithms=+ssh-rsa", + ) end end @@ -180,7 +255,16 @@ describe Vagrant::Util::SSH do expect(described_class.exec(ssh_info)).to eq(nil) expect(Vagrant::Util::SafeExec).to have_received(:exec) - .with(ssh_path, "vagrant@localhost", "-p", "2222", "-o", "LogLevel=FATAL", "-o", "StrictHostKeyChecking=no", "-o", "UserKnownHostsFile=/dev/null", "-i", ssh_info[:private_key_path][0],"-o", "ForwardX11=yes", "-o", "ForwardX11Trusted=yes") + .with( + ssh_path, "vagrant@localhost", "-p", "2222", "-o", "LogLevel=FATAL", + "-o", "StrictHostKeyChecking=no", + "-o", "UserKnownHostsFile=/dev/null", + "-o", "PubkeyAcceptedKeyTypes=+ssh-rsa", + "-o", "HostKeyAlgorithms=+ssh-rsa", + "-i", ssh_info[:private_key_path][0], + "-o", "ForwardX11=yes", + "-o", "ForwardX11Trusted=yes", + ) end end @@ -198,7 +282,15 @@ describe Vagrant::Util::SSH do expect(described_class.exec(ssh_info)).to eq(nil) expect(Vagrant::Util::SafeExec).to have_received(:exec) - .with(ssh_path, "vagrant@localhost", "-p", "2222", "-o", "LogLevel=FATAL", "-o", "StrictHostKeyChecking=no", "-o", "UserKnownHostsFile=/dev/null", "-i", ssh_info[:private_key_path][0],"-o", "ForwardAgent=yes") + .with( + ssh_path, "vagrant@localhost", "-p", "2222", "-o", "LogLevel=FATAL", + "-o", "StrictHostKeyChecking=no", + "-o", "UserKnownHostsFile=/dev/null", + "-o", "PubkeyAcceptedKeyTypes=+ssh-rsa", + "-o", "HostKeyAlgorithms=+ssh-rsa", + "-i", ssh_info[:private_key_path][0], + "-o", "ForwardAgent=yes", + ) end end @@ -216,7 +308,15 @@ describe Vagrant::Util::SSH do expect(described_class.exec(ssh_info)).to eq(nil) expect(Vagrant::Util::SafeExec).to have_received(:exec) - .with(ssh_path, "vagrant@localhost", "-p", "2222", "-o", "LogLevel=FATAL", "-o", "StrictHostKeyChecking=no", "-o", "UserKnownHostsFile=/dev/null", "-i", ssh_info[:private_key_path][0], "-L", "8008:localhost:80") + .with( + ssh_path, "vagrant@localhost", "-p", "2222", "-o", "LogLevel=FATAL", + "-o", "StrictHostKeyChecking=no", + "-o", "UserKnownHostsFile=/dev/null", + "-o", "PubkeyAcceptedKeyTypes=+ssh-rsa", + "-o", "HostKeyAlgorithms=+ssh-rsa", + "-i", ssh_info[:private_key_path][0], + "-L", "8008:localhost:80", + ) end end @@ -234,7 +334,15 @@ describe Vagrant::Util::SSH do expect(described_class.exec(ssh_info)).to eq(nil) expect(Vagrant::Util::SafeExec).to have_received(:exec) - .with(ssh_path, "vagrant@localhost", "-p", "2222", "-o", "LogLevel=FATAL", "-o", "StrictHostKeyChecking=no", "-o", "UserKnownHostsFile=/dev/null", "-i", ssh_info[:private_key_path][0], "-6") + .with( + ssh_path, "vagrant@localhost", "-p", "2222", "-o", "LogLevel=FATAL", + "-o", "StrictHostKeyChecking=no", + "-o", "UserKnownHostsFile=/dev/null", + "-o", "PubkeyAcceptedKeyTypes=+ssh-rsa", + "-o", "HostKeyAlgorithms=+ssh-rsa", + "-i", ssh_info[:private_key_path][0], + "-6", + ) end end diff --git a/website/content/docs/vagrantfile/ssh_settings.mdx b/website/content/docs/vagrantfile/ssh_settings.mdx index 8aa431283..5ec2bff7b 100644 --- a/website/content/docs/vagrantfile/ssh_settings.mdx +++ b/website/content/docs/vagrantfile/ssh_settings.mdx @@ -27,6 +27,10 @@ defaults are typically fine, but you can fine tune whatever you would like. - `config.ssh.config` (string) - Path to a custom ssh_config file to use for configuring the SSH connections. +- `config.ssh.disable_deprecated_algorithms` (boolean) - If `true`, this setting will + not configure the SSH client to allow connecting to a host using ssh-rsa key types + and host key algorithms. Defaults to false. + - `config.ssh.dsa_authentication` (boolean) - If `false`, this setting will not include `DSAAuthentication` when ssh'ing into a machine. If this is not set, it will default to `true` and `DSAAuthentication=yes` will be used with ssh.