diff --git a/lib/vagrant/machine.rb b/lib/vagrant/machine.rb index 3d46edde9..1f2329d91 100644 --- a/lib/vagrant/machine.rb +++ b/lib/vagrant/machine.rb @@ -300,6 +300,12 @@ module Vagrant end end + # If we have a private key in our data dir, then use that + data_private_key = @data_dir.join("private_key") + if data_private_key.file? + info[:private_key_path] = [data_private_key.to_s] + end + # Setup the keys info[:private_key_path] ||= [] if !info[:private_key_path].is_a?(Array) diff --git a/plugins/communicators/ssh/communicator.rb b/plugins/communicators/ssh/communicator.rb index eac6fe618..59cc65787 100644 --- a/plugins/communicators/ssh/communicator.rb +++ b/plugins/communicators/ssh/communicator.rb @@ -1,6 +1,7 @@ require 'logger' require 'pathname' require 'stringio' +require 'thread' require 'timeout' require 'log4r' @@ -27,25 +28,57 @@ module VagrantPlugins end def initialize(machine) + @lock = Mutex.new @machine = machine @logger = Log4r::Logger.new("vagrant::communication::ssh") @connection = nil + @inserted_key = false end def ready? @logger.debug("Checking whether SSH is ready...") # Attempt to connect. This will raise an exception if it fails. - connect + begin + connect + @logger.info("SSH is ready!") + rescue Vagrant::Errors::VagrantError => e + # We catch a `VagrantError` which would signal that something went + # wrong expectedly in the `connect`, which means we didn't connect. + @logger.info("SSH not up: #{e.inspect}") + return false + end + + # If we're already attempting to switch out the SSH key, then + # just return that we're ready (for Machine#guest). + @lock.synchronize do + return true if @inserted_key + @inserted_key = true + end + + # If we used a password, then insert the insecure key + ssh_info = @machine.ssh_info + if ssh_info[:password] && ssh_info[:private_key_path].empty? + @logger.info("Inserting insecure key to avoid password") + @machine.guest.capability( + :insert_public_key, + Vagrant.source_root.join("keys", "vagrant.pub").read) + + # Write out the private key in the data dir so that the + # machine automatically picks it up. + @machine.data_dir.join("private_key").open("w+") do |f| + f.write(Vagrant.source_root.join("keys", "vagrant").read) + end + + @logger.info("Disconecting SSH so we can reconnect with new SSH key") + @connection.close + @connection = nil + + return ready? + end # If we reached this point then we successfully connected - @logger.info("SSH is ready!") true - rescue Vagrant::Errors::VagrantError => e - # We catch a `VagrantError` which would signal that something went - # wrong expectedly in the `connect`, which means we didn't connect. - @logger.info("SSH not up: #{e.inspect}") - return false end def execute(command, opts=nil, &block) diff --git a/plugins/guests/linux/cap/insert_public_key.rb b/plugins/guests/linux/cap/insert_public_key.rb new file mode 100644 index 000000000..69a526541 --- /dev/null +++ b/plugins/guests/linux/cap/insert_public_key.rb @@ -0,0 +1,17 @@ +module VagrantPlugins + module GuestLinux + module Cap + class InsertPublicKey + def self.insert_public_key(machine, contents) + machine.communicate.tap do |comm| + comm.execute("echo #{contents} > /tmp/key.pub") + comm.execute("mkdir -p ~/.ssh") + comm.execute("chmod 0700 ~/.ssh") + comm.execute("cat /tmp/key.pub >> ~/.ssh/authorized_keys") + comm.execute("chmod 0600 ~/.ssh/authorized_keys") + end + end + end + end + end +end diff --git a/plugins/guests/linux/plugin.rb b/plugins/guests/linux/plugin.rb index c3529faad..c47a9d674 100644 --- a/plugins/guests/linux/plugin.rb +++ b/plugins/guests/linux/plugin.rb @@ -16,6 +16,11 @@ module VagrantPlugins Cap::Halt end + guest_capability("linux", "insert_public_key") do + require_relative "cap/insert_public_key" + Cap::InsertPublicKey + end + guest_capability("linux", "shell_expand_guest_path") do require_relative "cap/shell_expand_guest_path" Cap::ShellExpandGuestPath diff --git a/plugins/kernel_v2/config/ssh_connect.rb b/plugins/kernel_v2/config/ssh_connect.rb index f3925f09c..7b2ad68e5 100644 --- a/plugins/kernel_v2/config/ssh_connect.rb +++ b/plugins/kernel_v2/config/ssh_connect.rb @@ -6,6 +6,7 @@ module VagrantPlugins attr_accessor :private_key_path attr_accessor :username attr_accessor :password + attr_accessor :insert_key def initialize @host = UNSET_VALUE @@ -13,6 +14,7 @@ module VagrantPlugins @private_key_path = UNSET_VALUE @username = UNSET_VALUE @password = UNSET_VALUE + @insert_key = UNSET_VALUE end def finalize! @@ -21,6 +23,7 @@ module VagrantPlugins @private_key_path = nil if @private_key_path == UNSET_VALUE @username = nil if @username == UNSET_VALUE @password = nil if @password == UNSET_VALUE + @insert_key = true if @insert_key == UNSET_VALUE if @private_key_path && !@private_key_path.is_a?(Array) @private_key_path = [@private_key_path] diff --git a/test/unit/vagrant/machine_test.rb b/test/unit/vagrant/machine_test.rb index 29758246f..9cb6fe6b8 100644 --- a/test/unit/vagrant/machine_test.rb +++ b/test/unit/vagrant/machine_test.rb @@ -382,6 +382,20 @@ describe Vagrant::Machine do expect(instance.ssh_info[:private_key_path]).to be_empty expect(instance.ssh_info[:password]).to eql("") end + + it "should return the private key in the data dir above all else" do + provider_ssh_info[:private_key_path] = nil + instance.config.ssh.private_key_path = nil + instance.config.ssh.password = "" + + instance.data_dir.join("private_key").open("w+") do |f| + f.write("hey") + end + + expect(instance.ssh_info[:private_key_path]).to eql( + [instance.data_dir.join("private_key").to_s]) + expect(instance.ssh_info[:password]).to eql("") + end end end