Merge pull request #13219 from chrisroberts/ssh-keys

Add keypair support for ed25519
This commit is contained in:
Chris Roberts 2023-06-27 10:33:11 -07:00 committed by GitHub
commit 827140cd90
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 434 additions and 70 deletions

View File

@ -1,9 +1,22 @@
# Insecure Keypair
# Insecure Keypairs
These keys are the "insecure" public/private keypair we offer to
[base box creators](https://www.vagrantup.com/docs/boxes/base.html) for use in their base boxes so that
vagrant installations can automatically SSH into the boxes.
# Vagrant Keypairs
There are currently two "insecure" public/private keypairs for
Vagrant. One keypair was generated using the older RSA algorithm
and the other keypair was generated using the more recent ED25519
algorithm.
The `vagrant.pub` file includes the public key for both keypairs. It
is important for box creators to include both keypairs as versions of
Vagrant prior to 2.3.8 will only use the RSA private key.
# Custom Keys
If you're working with a team or company or with a custom box and
you want more secure SSH, you should create your own keypair
and configure the private key in the Vagrantfile with

7
keys/vagrant.key.ed25519 Normal file
View File

@ -0,0 +1,7 @@
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
QyNTUxOQAAACDdWHcQaTZc8Q6nycsP0CqMNRfsLxvYVxqKosrHyTp+WAAAAJj2TBMT9kwT
EwAAAAtzc2gtZWQyNTUxOQAAACDdWHcQaTZc8Q6nycsP0CqMNRfsLxvYVxqKosrHyTp+WA
AAAEAveRHRHSCjIxbNKHDRzezD0U3R3UEEmS7R33fzvPQAD91YdxBpNlzxDqfJyw/QKow1
F+wvG9hXGoqiysfJOn5YAAAAEHNwb3hAdmFncmFudC1kZXYBAgMEBQ==
-----END OPENSSH PRIVATE KEY-----

27
keys/vagrant.key.rsa Normal file
View File

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEA6NF8iallvQVp22WDkTkyrtvp9eWW6A8YVr+kz4TjGYe7gHzI
w+niNltGEFHzD8+v1I2YJ6oXevct1YeS0o9HZyN1Q9qgCgzUFtdOKLv6IedplqoP
kcmF0aYet2PkEDo3MlTBckFXPITAMzF8dJSIFo9D8HfdOV0IAdx4O7PtixWKn5y2
hMNG0zQPyUecp4pzC6kivAIhyfHilFR61RGL+GPXQ2MWZWFYbAGjyiYJnAmCP3NO
Td0jMZEnDkbUvxhMmBYSdETk1rRgm+R4LOzFUGaHqHDLKLX+FIPKcF96hrucXzcW
yLbIbEgE98OHlnVYCzRdK8jlqm8tehUc9c9WhQIBIwKCAQEA4iqWPJXtzZA68mKd
ELs4jJsdyky+ewdZeNds5tjcnHU5zUYE25K+ffJED9qUWICcLZDc81TGWjHyAqD1
Bw7XpgUwFgeUJwUlzQurAv+/ySnxiwuaGJfhFM1CaQHzfXphgVml+fZUvnJUTvzf
TK2Lg6EdbUE9TarUlBf/xPfuEhMSlIE5keb/Zz3/LUlRg8yDqz5w+QWVJ4utnKnK
iqwZN0mwpwU7YSyJhlT4YV1F3n4YjLswM5wJs2oqm0jssQu/BT0tyEXNDYBLEF4A
sClaWuSJ2kjq7KhrrYXzagqhnSei9ODYFShJu8UWVec3Ihb5ZXlzO6vdNQ1J9Xsf
4m+2ywKBgQD6qFxx/Rv9CNN96l/4rb14HKirC2o/orApiHmHDsURs5rUKDx0f9iP
cXN7S1uePXuJRK/5hsubaOCx3Owd2u9gD6Oq0CsMkE4CUSiJcYrMANtx54cGH7Rk
EjFZxK8xAv1ldELEyxrFqkbE4BKd8QOt414qjvTGyAK+OLD3M2QdCQKBgQDtx8pN
CAxR7yhHbIWT1AH66+XWN8bXq7l3RO/ukeaci98JfkbkxURZhtxV/HHuvUhnPLdX
3TwygPBYZFNo4pzVEhzWoTtnEtrFueKxyc3+LjZpuo+mBlQ6ORtfgkr9gBVphXZG
YEzkCD3lVdl8L4cw9BVpKrJCs1c5taGjDgdInQKBgHm/fVvv96bJxc9x1tffXAcj
3OVdUN0UgXNCSaf/3A/phbeBQe9xS+3mpc4r6qvx+iy69mNBeNZ0xOitIjpjBo2+
dBEjSBwLk5q5tJqHmy/jKMJL4n9ROlx93XS+njxgibTvU6Fp9w+NOFD/HvxB3Tcz
6+jJF85D5BNAG3DBMKBjAoGBAOAxZvgsKN+JuENXsST7F89Tck2iTcQIT8g5rwWC
P9Vt74yboe2kDT531w8+egz7nAmRBKNM751U/95P9t88EDacDI/Z2OwnuFQHCPDF
llYOUI+SpLJ6/vURRbHSnnn8a/XG+nzedGH5JGqEJNQsz+xT2axM0/W/CRknmGaJ
kda/AoGANWrLCz708y7VYgAtW2Uf1DPOIYMdvo6fxIB5i9ZfISgcJ/bbCUkFrhoH
+vq/5CIWxCPp0f85R4qxxQ5ihxJ0YDQT9Jpx4TMss4PSavPaBH3RXow5Ohe+bYoQ
NE5OgEXk2wVfZczCZpigBKbKZHNYcelXtTt/nP3rsCuGcM4h53s=
-----END RSA PRIVATE KEY-----

View File

@ -1 +1,2 @@
ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA6NF8iallvQVp22WDkTkyrtvp9eWW6A8YVr+kz4TjGYe7gHzIw+niNltGEFHzD8+v1I2YJ6oXevct1YeS0o9HZyN1Q9qgCgzUFtdOKLv6IedplqoPkcmF0aYet2PkEDo3MlTBckFXPITAMzF8dJSIFo9D8HfdOV0IAdx4O7PtixWKn5y2hMNG0zQPyUecp4pzC6kivAIhyfHilFR61RGL+GPXQ2MWZWFYbAGjyiYJnAmCP3NOTd0jMZEnDkbUvxhMmBYSdETk1rRgm+R4LOzFUGaHqHDLKLX+FIPKcF96hrucXzcWyLbIbEgE98OHlnVYCzRdK8jlqm8tehUc9c9WhQ== vagrant insecure public key
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIN1YdxBpNlzxDqfJyw/QKow1F+wvG9hXGoqiysfJOn5Y vagrant insecure public key

1
keys/vagrant.pub.ed25519 Normal file
View File

@ -0,0 +1 @@
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIN1YdxBpNlzxDqfJyw/QKow1F+wvG9hXGoqiysfJOn5Y vagrant insecure public key

1
keys/vagrant.pub.rsa Normal file
View File

@ -0,0 +1 @@
ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA6NF8iallvQVp22WDkTkyrtvp9eWW6A8YVr+kz4TjGYe7gHzIw+niNltGEFHzD8+v1I2YJ6oXevct1YeS0o9HZyN1Q9qgCgzUFtdOKLv6IedplqoPkcmF0aYet2PkEDo3MlTBckFXPITAMzF8dJSIFo9D8HfdOV0IAdx4O7PtixWKn5y2hMNG0zQPyUecp4pzC6kivAIhyfHilFR61RGL+GPXQ2MWZWFYbAGjyiYJnAmCP3NOTd0jMZEnDkbUvxhMmBYSdETk1rRgm+R4LOzFUGaHqHDLKLX+FIPKcF96hrucXzcWyLbIbEgE98OHlnVYCzRdK8jlqm8tehUc9c9WhQ== vagrant insecure public key

View File

@ -68,8 +68,11 @@ module Vagrant
# The path where the plugins are stored (gems)
attr_reader :gems_path
# The path to the default private key
attr_reader :default_private_key_path
# The path to the default private keys directory
attr_reader :default_private_keys_directory
# The paths for each of the default private keys
attr_reader :default_private_key_paths
# Initializes a new environment with the given options. The options
# is a hash where the main available key is `cwd`, which defines where
@ -174,7 +177,12 @@ module Vagrant
# Setup the default private key
@default_private_key_path = @home_path.join("insecure_private_key")
copy_insecure_private_key
@default_private_keys_directory = @home_path.join("insecure_private_keys")
if !@default_private_keys_directory.directory?
@default_private_keys_directory.mkdir
end
@default_private_key_paths = []
copy_insecure_private_keys
# Initialize localized plugins
plugins = Vagrant::Plugin::Manager.instance.localize!(self)
@ -196,6 +204,13 @@ module Vagrant
hook(:environment_load, runner: Action::PrimaryRunner.new(env: self))
end
# The path to the default private key
# NOTE: deprecated, used default_private_keys_directory instead
def default_private_key_path
# TODO(spox): Add deprecation warning
@default_private_key_path
end
# Return a human-friendly string for pretty printed or inspected
# instances.
#
@ -1053,14 +1068,18 @@ module Vagrant
end
end
# This method copies the private key into the home directory if it
# doesn't already exist.
# This method copies the private keys into the home directory if they
# do not already exist. The `default_private_key_path` references the
# original rsa based private key and is retained for compatibility. The
# `default_private_keys_directory` contains the list of valid private
# keys supported by Vagrant.
#
# This must be done because `ssh` requires that the key is chmod
# NOTE: The keys are copied because `ssh` requires that the key is chmod
# 0600, but if Vagrant is installed as a separate user, then the
# effective uid won't be able to read the key. So the key is copied
# to the home directory and chmod 0600.
def copy_insecure_private_key
def copy_insecure_private_keys
# First setup the deprecated single key path
if !@default_private_key_path.exist?
@logger.info("Copying private key to home directory")
@ -1084,6 +1103,29 @@ module Vagrant
@default_private_key_path.chmod(0600)
end
end
# Now setup the key directory
Dir.glob(File.expand_path("keys/vagrant.key.*", Vagrant.source_root)).each do |source|
destination = default_private_keys_directory.join(File.basename(source))
default_private_key_paths << destination
next if File.exist?(destination)
begin
FileUtils.cp(source, destination)
rescue Errno::EACCES
raise Errors::CopyPrivateKeyFailed,
source: source,
destination: destination
end
end
if !Util::Platform.windows?
default_private_key_paths.each do |key_path|
if Util::FileMode.from_octal(key_path.stat.mode) != "600"
@logger.info("Changing permissions on private key (#{key_path}) to 0600")
key_path.chmod(0600)
end
end
end
end
# Finds the Vagrantfile in the given directory.

View File

@ -502,7 +502,7 @@ module Vagrant
if @config.ssh.private_key_path
info[:private_key_path] = @config.ssh.private_key_path
else
info[:private_key_path] = @env.default_private_key_path
info[:private_key_path] = @env.default_private_key_paths
end
end

View File

@ -1,55 +1,154 @@
require "base64"
require "openssl"
require "ed25519"
require "securerandom"
require "vagrant/util/retryable"
module Vagrant
module Util
class Keypair
extend Retryable
class Ed25519
# Magic string header
AUTH_MAGIC = "openssh-key-v1".freeze
# Key type identifier
KEY_TYPE = "ssh-ed25519".freeze
# Header of private key file content
PRIVATE_KEY_START = "-----BEGIN OPENSSH PRIVATE KEY-----\n".freeze
# Footer of private key file content
PRIVATE_KEY_END = "-----END OPENSSH PRIVATE KEY-----".freeze
# Creates an SSH keypair and returns it.
#
# @param [String] password Password for the key, or nil for no password.
# @return [Array<String, String, String>] PEM-encoded public and private key,
# respectively. The final element is the OpenSSH encoded public
# key.
def self.create(password=nil)
# This sometimes fails with RSAError. It is inconsistent and strangely
# sleeps seem to fix it. We just retry this a few times. See GH-5056
rsa_key = nil
retryable(on: OpenSSL::PKey::RSAError, sleep: 2, tries: 5) do
rsa_key = OpenSSL::PKey::RSA.new(2048)
# Encodes given string
#
# @param [String] s String to encode
# @return [String]
def self.string(s)
[s.length].pack("N") + s
end
public_key = rsa_key.public_key
private_key = rsa_key.to_pem
if password
cipher = OpenSSL::Cipher.new('des3')
private_key = rsa_key.to_pem(cipher, password)
# Encodes given string with padding to block size
#
# @param [String] s String to encode
# @param [Integer] blocksize Defined block size
# @return [String]
def self.padded_string(s, blocksize)
pad = blocksize - (s.length % blocksize)
string(s + Array(1..pad).pack("c*"))
end
# Generate the binary necessary for the OpenSSH public key.
binary = [7].pack("N")
binary += "ssh-rsa"
["e", "n"].each do |m|
val = public_key.send(m)
data = val.to_s(2)
first_byte = data[0,1].unpack("c").first
if val < 0
data[0] = [0x80 & first_byte].pack("c")
elsif first_byte < 0
data = 0.chr + data
# Creates an ed25519 SSH key pair
# @return [Array<String, String, String>] Public key, openssh private key, openssh public key with comment
# @note Password support was not included as it's not actively used anywhere. If it ends up being
# something that's needed, it can be revisited
def self.create(password=nil)
if password
raise NotImplementedError,
"Ed25519 key pair generation does not support passwords"
end
binary += [data.length].pack("N") + data
# Generate the key
base_key = ::Ed25519::SigningKey.generate
# Define the comment used for the key
comment = "vagrant"
# Grab the raw public key
public_key = base_key.verify_key.to_bytes
# Encode the public key for use building the openssh private key
encoded_public_key = string(KEY_TYPE) + string(public_key)
# Format the public key into the openssh public key format for writing
openssh_public_key = "#{KEY_TYPE} #{Base64.encode64(encoded_public_key).gsub("\n", "")} #{comment}"
# Agent encoded private key is used when building the openssh private key
# (https://datatracker.ietf.org/doc/html/draft-miller-ssh-agent#section-4.2.3)
# (https://dnaeon.github.io/openssh-private-key-binary-format/)
agent_private_key = [
([SecureRandom.random_number((2**32)-1)] * 2).pack("NN"), # checkint, random uint32 value, twice (used for encryption verification)
encoded_public_key, # includes the key type and public key
string(base_key.seed + public_key), # private key with public key concatenated
string(comment), # comment for the key
].join
# Build openssh private key data (https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.key)
private_key = [
AUTH_MAGIC + "\0", # Magic string
string("none"), # cipher name, no encryption, so none
string("none"), # kdf name, no encryption, so none
string(""), # kdf options/data, no encryption, so empty string
[1].pack("N"), # Number of keys (just one)
string(encoded_public_key), # The public key
padded_string(agent_private_key, 8) # Private key encoded with agent rules, padded for 8 byte block size
].join
# Create the openssh private key content
openssh_private_key = [
PRIVATE_KEY_START,
Base64.encode64(private_key),
PRIVATE_KEY_END,
].join
return [public_key, openssh_private_key, openssh_public_key]
end
end
class Rsa
extend Retryable
# Creates an SSH keypair and returns it.
#
# @param [String] password Password for the key, or nil for no password.
# @return [Array<String, String, String>] PEM-encoded public and private key,
# respectively. The final element is the OpenSSH encoded public
# key.
def self.create(password=nil)
# This sometimes fails with RSAError. It is inconsistent and strangely
# sleeps seem to fix it. We just retry this a few times. See GH-5056
rsa_key = nil
retryable(on: OpenSSL::PKey::RSAError, sleep: 2, tries: 5) do
rsa_key = OpenSSL::PKey::RSA.new(2048)
end
public_key = rsa_key.public_key
private_key = rsa_key.to_pem
if password
cipher = OpenSSL::Cipher.new('des3')
private_key = rsa_key.to_pem(cipher, password)
end
# Generate the binary necessary for the OpenSSH public key.
binary = [7].pack("N")
binary += "ssh-rsa"
["e", "n"].each do |m|
val = public_key.send(m)
data = val.to_s(2)
first_byte = data[0,1].unpack("c").first
if val < 0
data[0] = [0x80 & first_byte].pack("c")
elsif first_byte < 0
data = 0.chr + data
end
binary += [data.length].pack("N") + data
end
openssh_key = "ssh-rsa #{Base64.encode64(binary).gsub("\n", "")} vagrant"
public_key = public_key.to_pem
return [public_key, private_key, openssh_key]
end
end
# Supported key types.
VALID_TYPES = {ed25519: Ed25519, rsa: Rsa}.freeze
# Ordered mapping of openssh key type name to lookup name
PREFER_KEY_TYPES = {"ssh-ed25519".freeze => :ed25519, "ssh-rsa".freeze => :rsa}.freeze
def self.create(password=nil, type: :rsa)
if !VALID_TYPES.key?(type)
raise ArgumentError,
"Invalid key type requested (supported types: #{VALID_TYPES.keys.map(&:inspect)})"
end
openssh_key = "ssh-rsa #{Base64.encode64(binary).gsub("\n", "")} vagrant"
public_key = public_key.to_pem
return [public_key, private_key, openssh_key]
VALID_TYPES[type].create(password)
end
end
end

View File

@ -185,7 +185,29 @@ module VagrantPlugins
@machine.guest.capability?(:remove_public_key)
raise Vagrant::Errors::SSHInsertKeyUnsupported if !cap
_pub, priv, openssh = Vagrant::Util::Keypair.create
# Check for supported key type
key_type = catch(:key_type) do
begin
Vagrant::Util::Keypair::PREFER_KEY_TYPES.each do |type_name, type|
throw :key_type, type if supports_key_type?(type_name)
end
nil
rescue => err
@logger.warn("Failed to check key types server supports: #{err}")
nil
end
end
@logger.debug("Detected key type for new private key: #{key_type}")
# If no key type was discovered, default to rsa
if key_type.nil?
@logger.debug("Failed to detect supported key type, defaulting to rsa")
key_type = :rsa
end
@logger.info("Creating new ssh keypair (type: #{key_type.inspect})")
_pub, priv, openssh = Vagrant::Util::Keypair.create(type: key_type)
@logger.info("Inserting key to avoid password: #{openssh}")
@machine.ui.detail("\n"+I18n.t("vagrant.inserting_random_key"))
@ -748,8 +770,9 @@ module VagrantPlugins
def insecure_key?(path)
return false if !path
return false if !File.file?(path)
source_path = Vagrant.source_root.join("keys", "vagrant")
return File.read(path).chomp == source_path.read.chomp
Dir.glob(Vagrant.source_root.join("keys", "vagrant.key.*")).any? do |source_path|
File.read(path).chomp == File.read(source_path).chomp
end
end
def create_remote_directory(dir)
@ -759,6 +782,35 @@ module VagrantPlugins
def machine_config_ssh
@machine.config.ssh
end
protected
# Check if server supports given key type
#
# @param [String, Symbol] type Key type
# @return [Boolean]
# @note This does not use a stable API and may be subject
# to unexpected breakage on net-ssh updates
def supports_key_type?(type)
if @connection.nil?
raise Vagrant::Errors::SSHNotReady
end
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")
return false
end
if !server_data.is_a?(Hash)
@logger.warn("Server data is not expected type (expecting Hash, got #{server_data.class})")
return false
end
@logger.debug("server data used for host key support check: #{server_data.inspect}")
server_data[:host_key].include?(type.to_s)
end
end
end
end

View File

@ -242,6 +242,10 @@ describe VagrantPlugins::CommunicatorSSH::Communicator do
let(:openssh){ :openssh }
let(:private_key_file){ double("private_key_file") }
let(:path_joiner){ double("path_joiner") }
let(:algorithms) { double(:algorithms) }
let(:transport) { double(:transport, algorithms: algorithms) }
let(:valid_key_types) { [] }
let(:server_data) { { host_key: valid_key_types} }
before do
allow(Vagrant::Util::Keypair).to receive(:create).
@ -255,6 +259,8 @@ describe VagrantPlugins::CommunicatorSSH::Communicator do
allow(path_joiner).to receive(:join).and_return(private_key_file)
allow(guest).to receive(:capability).with(:insert_public_key)
allow(guest).to receive(:capability).with(:remove_public_key)
allow(connection).to receive(:transport).and_return(transport)
allow(algorithms).to receive(:instance_variable_get).with(:@server_data).and_return(server_data)
end
after{ communicator.ready? }
@ -280,6 +286,53 @@ describe VagrantPlugins::CommunicatorSSH::Communicator do
it "should remove the default public key" do
expect(guest).to receive(:capability).with(:remove_public_key, any_args)
end
context "with server algorithm support data" do
context "when no key type matches are found" do
it "should default to rsa type" do
expect(Vagrant::Util::Keypair).to receive(:create).
with(type: :rsa).and_call_original
end
end
context "when rsa is the only match" do
let(:valid_key_types) { ["ssh-edsca", "ssh-rsa"] }
it "should use rsa type" do
expect(Vagrant::Util::Keypair).to receive(:create).
with(type: :rsa).and_call_original
end
end
context "when ed25519 and rsa are both available" do
let(:valid_key_types) { ["ssh-ed25519", "ssh-rsa"] }
it "should use ed25519 type" do
expect(Vagrant::Util::Keypair).to receive(:create).
with(type: :ed25519).and_call_original
end
end
context "when ed25519 is the only match" do
let(:valid_key_types) { ["ssh-edsca", "ssh-ed25519"] }
it "should use ed25519 type" do
expect(Vagrant::Util::Keypair).to receive(:create).
with(type: :ed25519).and_call_original
end
end
end
context "when an error is encountered getting server data" do
before do
expect(connection).to receive(:transport).and_raise(StandardError)
end
it "should default to rsa key" do
expect(Vagrant::Util::Keypair).to receive(:create).
with(type: :rsa).and_call_original
end
end
end
end
end
@ -929,6 +982,45 @@ describe VagrantPlugins::CommunicatorSSH::Communicator do
end
end
describe ".insecure_key?" do
let(:key_data) { "" }
let(:key_file) {
if !@key_file
f = Tempfile.new
f.write(key_data)
f.close
@key_file = f.path
end
@key_file
}
after { File.delete(key_file) }
context "when using rsa private key" do
let(:key_data) { File.read(Vagrant.source_root.join("keys", "vagrant.key.rsa")) }
it "should match as insecure key" do
expect(communicator.send(:insecure_key?, key_file)).to be_truthy
end
end
context "when using ed25519 private key" do
let(:key_data) { File.read(Vagrant.source_root.join("keys", "vagrant.key.ed25519")) }
it "should match as insecure key" do
expect(communicator.send(:insecure_key?, key_file)).to be_truthy
end
end
context "when using unknown private key" do
let(:key_data) { "invalid data" }
it "should not match as insecure key" do
expect(communicator.send(:insecure_key?, key_file)).to be_falsey
end
end
end
describe ".generate_environment_export" do
it "should generate bourne shell compatible export" do
expect(communicator.send(:generate_environment_export, "TEST", "value")).to eq("export TEST=\"value\"\n")

View File

@ -729,7 +729,9 @@ describe Vagrant::Machine do
provider_ssh_info[:private_key_path] = nil
instance.config.ssh.private_key_path = nil
expect(ssh_klass).to receive(:check_key_permissions).once.with(Pathname.new(instance.env.default_private_key_path.to_s))
instance.env.default_private_key_paths.each do |key_path|
expect(ssh_klass).to receive(:check_key_permissions).once.with(Pathname.new(key_path.to_s))
end
instance.ssh_info
end
@ -773,7 +775,7 @@ describe Vagrant::Machine do
instance.config.ssh.private_key_path = nil
expect(instance.ssh_info[:private_key_path]).to eq(
[instance.env.default_private_key_path.to_s]
instance.env.default_private_key_paths
)
end
@ -783,7 +785,7 @@ describe Vagrant::Machine do
instance.config.ssh.keys_only = false
expect(instance.ssh_info[:private_key_path]).to eq(
[instance.env.default_private_key_path.to_s]
instance.env.default_private_key_paths
)
end

View File

@ -1,34 +1,60 @@
require "openssl"
require "ed25519"
require "net/ssh"
require File.expand_path("../../../base", __FILE__)
require "vagrant/util/keypair"
describe Vagrant::Util::Keypair do
describe ".create" do
it "generates a usable keypair with no password" do
# I don't know how to validate the final return value yet...
pubkey, privkey, _ = described_class.create
describe Vagrant::Util::Keypair::Rsa do
describe ".create" do
it "generates a usable keypair with no password" do
# I don't know how to validate the final return value yet...
pubkey, privkey, _ = described_class.create
pubkey = OpenSSL::PKey::RSA.new(pubkey)
privkey = OpenSSL::PKey::RSA.new(privkey)
pubkey = OpenSSL::PKey::RSA.new(pubkey)
privkey = OpenSSL::PKey::RSA.new(privkey)
encrypted = pubkey.public_encrypt("foo")
decrypted = privkey.private_decrypt(encrypted)
encrypted = pubkey.public_encrypt("foo")
decrypted = privkey.private_decrypt(encrypted)
expect(decrypted).to eq("foo")
expect(decrypted).to eq("foo")
end
it "generates a keypair that requires a password" do
pubkey, privkey, _ = described_class.create("password")
pubkey = OpenSSL::PKey::RSA.new(pubkey)
privkey = OpenSSL::PKey::RSA.new(privkey, "password")
encrypted = pubkey.public_encrypt("foo")
decrypted = privkey.private_decrypt(encrypted)
expect(decrypted).to eq("foo")
end
end
end
it "generates a keypair that requires a password" do
pubkey, privkey, _ = described_class.create("password")
describe Vagrant::Util::Keypair::Ed25519 do
describe ".create" do
it "generates a usable keypair with no password" do
pubkey, ossh_privkey, _ = described_class.create
pubkey = OpenSSL::PKey::RSA.new(pubkey)
privkey = OpenSSL::PKey::RSA.new(privkey, "password")
encrypted = pubkey.public_encrypt("foo")
decrypted = privkey.private_decrypt(encrypted)
privkey = Net::SSH::Authentication::ED25519::PrivKey.read(ossh_privkey, "").sign_key
pubkey = Ed25519::VerifyKey.new(pubkey)
expect(decrypted).to eq("foo")
message = "vagrant test"
signature = privkey.sign(message)
expect(pubkey.verify(signature, message)).to be_truthy
end
it "does not generate a keypair that requires a password" do
expect {
described_class.create("my password")
}.to raise_error(NotImplementedError)
end
end
end
end

View File

@ -115,15 +115,16 @@ users, passwords, private keys, etc.).
By default, Vagrant expects a "vagrant" user to SSH into the machine as.
This user should be setup with the
[insecure keypair](https://github.com/hashicorp/vagrant/tree/main/keys)
[insecure keypairs](https://github.com/hashicorp/vagrant/tree/main/keys)
that Vagrant uses as a default to attempt to SSH. It should belong to a
group named "vagrant". Also, even though Vagrant uses key-based
authentication by default, it is a general convention to set the
password for the "vagrant" user to "vagrant". This lets people login as
that user manually if they need to.
To configure SSH access with the insecure keypair, place the public
key into the `~/.ssh/authorized_keys` file for the "vagrant" user. Note
To configure SSH access with the insecure keypair, place the [public
keys](https://github.com/hashicorp/vagrant/tree/main/keys/vagrant.pub)
into the `~/.ssh/authorized_keys` file for the "vagrant" user. Note
that OpenSSH is very picky about file permissions. Therefore, make sure
that `~/.ssh` has `0700` permissions and the authorized keys file has
`0600` permissions.