How do I add my own public key to Vagrant VM?
Asked Answered
R

13

109

I got a problem with adding an ssh key to a Vagrant VM. Basically the setup that I have here works fine. Once the VMs are created, I can access them via vagrant ssh, the user "vagrant" exists and there's an ssh key for this user in the authorized_keys file.

What I'd like to do now is: to be able to connect to those VMs via ssh or use scp. So I would only need to add my public key from id_rsa.pub to the authorized_keys - just like I'd do with ssh-copy-id.

Is there a way to tell Vagrant during the setup that my public key should be included? If not (which is likely, according to my google results), is there a way to easily append my public key during the vagrant setup?

Royster answered 6/5, 2015 at 11:31 Comment(0)
V
70

Copying the desired public key would fall squarely into the provisioning phase. The exact answer depends on what provisioning you fancy to use (shell, Chef, Puppet etc). The most trivial would be a file provisioner for the key, something along this:

config.vm.provision "file", source: "~/.ssh/id_rsa.pub", destination: "~/.ssh/me.pub"

Well, actually you need to append to authorized_keys. Use the the shell provisioner, like so:

Vagrant.configure(2) do |config|
  # ... other config
  config.vm.provision "shell", inline: <<-SHELL
    cat /home/vagrant/.ssh/me.pub >> /home/vagrant/.ssh/authorized_keys
  SHELL
  # ... other config
end

You can also use a true provisioner, like Puppet. For example see Managing SSH Authorized Keys with Puppet.

Volumetric answered 6/5, 2015 at 11:37 Comment(11)
Thanks for your answer - that was the nudge that I needed :) I thought maybe Vagrant would provide something, but with the provisioning it's possible. Maybe a bit "ugly", but it works like a charm. Basically I'm just copying the file as you suggested and then use the shell provisioner to append the key. virtualhost.vm.provision "shell", inline: "cat ~vagrant/.ssh/me.pub >> ~vagrant/.ssh/authorized_keys"Royster
There are unicode characters hidden in @tehK's comment above (before the is) that may ruin your afternoon - here's a fixed copy/pastable version virtualhost.vm.provision "shell", inline: "cat ~vagrant/.ssh/me.pub >> ~vagrant/.ssh/authorized_keys"Mayfield
This solution worked beautifully! Thank you so much :)Motorbus
Sorry , I'm late to the game. Whats config.vm.provision?Brice
@Brice a line in the vagrant file.Volumetric
Don't hardcode ~/.ssh/id_rsa.pub. Get the keys from ssh-add -L instead.Motoneuron
@Motoneuron and how would you do that?Optometry
@Optometry What do you mean? ssh-add -L lists keys currently loaded by the agent.Motoneuron
syntax error, unexpected end, expecting end-of-input 🤷Luau
@Luau the 'end' keyword was misleading. Check the edited answer.Erythema
Side note, vagrant halt and up did not do the trick for an existing machine, I destroyed it. Not sure if "reload" would've done the job.Aquatint
N
94

You can use Ruby's core File module, like so:

  config.vm.provision "shell" do |s|
    ssh_pub_key = File.readlines("#{Dir.home}/.ssh/id_rsa.pub").first.strip
    s.inline = <<-SHELL
      echo #{ssh_pub_key} >> /home/vagrant/.ssh/authorized_keys
      echo #{ssh_pub_key} >> /root/.ssh/authorized_keys
    SHELL
  end

This working example appends ~/.ssh/id_rsa.pub to the ~/.ssh/authorized_keys of both the vagrant and root user, which will allow you to use your existing SSH key.

Napery answered 1/7, 2015 at 6:22 Comment(5)
Good and it works, but it will add a line at every provision which might be executed several times.Federica
I'm voting to reject a major addition to this answer - because that addition should be added as it's own answer. @user76329 (if you ever come back to read this) should add it as a separate answer instead.Geisler
@Federica If you want the shell provisoner to run only once, here is one solution: blog.ouseful.info/2015/07/27/… Basically you create a file-flag to mark that provisoning already happend.Silvey
I needed to add equivalent of: mkdir ~/.ssh && touch authorized_keysKodiak
@Federica For idempotence you are better served with Ansible, shell can only do so much. docs.ansible.com/ansible/latest/modules/…Minima
V
70

Copying the desired public key would fall squarely into the provisioning phase. The exact answer depends on what provisioning you fancy to use (shell, Chef, Puppet etc). The most trivial would be a file provisioner for the key, something along this:

config.vm.provision "file", source: "~/.ssh/id_rsa.pub", destination: "~/.ssh/me.pub"

Well, actually you need to append to authorized_keys. Use the the shell provisioner, like so:

Vagrant.configure(2) do |config|
  # ... other config
  config.vm.provision "shell", inline: <<-SHELL
    cat /home/vagrant/.ssh/me.pub >> /home/vagrant/.ssh/authorized_keys
  SHELL
  # ... other config
end

You can also use a true provisioner, like Puppet. For example see Managing SSH Authorized Keys with Puppet.

Volumetric answered 6/5, 2015 at 11:37 Comment(11)
Thanks for your answer - that was the nudge that I needed :) I thought maybe Vagrant would provide something, but with the provisioning it's possible. Maybe a bit "ugly", but it works like a charm. Basically I'm just copying the file as you suggested and then use the shell provisioner to append the key. virtualhost.vm.provision "shell", inline: "cat ~vagrant/.ssh/me.pub >> ~vagrant/.ssh/authorized_keys"Royster
There are unicode characters hidden in @tehK's comment above (before the is) that may ruin your afternoon - here's a fixed copy/pastable version virtualhost.vm.provision "shell", inline: "cat ~vagrant/.ssh/me.pub >> ~vagrant/.ssh/authorized_keys"Mayfield
This solution worked beautifully! Thank you so much :)Motorbus
Sorry , I'm late to the game. Whats config.vm.provision?Brice
@Brice a line in the vagrant file.Volumetric
Don't hardcode ~/.ssh/id_rsa.pub. Get the keys from ssh-add -L instead.Motoneuron
@Motoneuron and how would you do that?Optometry
@Optometry What do you mean? ssh-add -L lists keys currently loaded by the agent.Motoneuron
syntax error, unexpected end, expecting end-of-input 🤷Luau
@Luau the 'end' keyword was misleading. Check the edited answer.Erythema
Side note, vagrant halt and up did not do the trick for an existing machine, I destroyed it. Not sure if "reload" would've done the job.Aquatint
J
48

There's a more "elegant" way of accomplishing what you want to do. You can find the existing private key and use it instead of going through the trouble of adding your public key.

Proceed like this to see the path to existing private key (look below for IdentityFile):

run

 vagrant ssh-config 

result:

$ vagrant ssh-config
Host magento2.vagrant150
  HostName 127.0.0.1
  User vagrant
  Port 3150
  UserKnownHostsFile /dev/null
  StrictHostKeyChecking no
  PasswordAuthentication no
  IdentityFile "/Users/madismanni/m2/vagrant-magento/.vagrant/machines/magento2.vagrant150/virtualbox/private_key"
  IdentitiesOnly yes
  LogLevel FATAL

Then you can use the private key like this, note also the switch for switching off password authentication

ssh -i /Users/madismanni/m2/vagrant-magento/.vagrant/machines/magento2.vagrant150/virtualbox/private_key -o PasswordAuthentication=no [email protected] -p 3150
Judy answered 23/4, 2016 at 14:50 Comment(2)
Got: ssh_exhange_identification: Connection closed by remote host. Now I can't even get authentication error on vagrant up. It says Guest-Specific operations were attempted on a machine that is not ready for guest communication. This should not happen and a but should be reported.Brice
Very slick indeed. I will do this going forward. Some might consider also adding the generated ssh config to their default ~/.ssh/config using an Include statement, as to avoid the -i and -F switches every time you run ssh. To be explicit, vagrant ssh-config > ~/.ssh/vagrant-ssh ; echo Include vagrant-ssh > ~/.ssh/config ; now you can ssh yourvm without further ado.Burrow
F
21

This excellent answer was added by user76329 in a rejected Suggested Edit

Expanding on Meow's example, we can copy the local pub/private ssh keys, set permissions, and make the inline script idempotent (runs once and will only repeat if the test condition fails, thus needing provisioning):

config.vm.provision "shell" do |s|
  ssh_prv_key = ""
  ssh_pub_key = ""
  if File.file?("#{Dir.home}/.ssh/id_rsa")
    ssh_prv_key = File.read("#{Dir.home}/.ssh/id_rsa")
    ssh_pub_key = File.readlines("#{Dir.home}/.ssh/id_rsa.pub").first.strip
  else
    puts "No SSH key found. You will need to remedy this before pushing to the repository."
  end
  s.inline = <<-SHELL
    if grep -sq "#{ssh_pub_key}" /home/vagrant/.ssh/authorized_keys; then
      echo "SSH keys already provisioned."
      exit 0;
    fi
    echo "SSH key provisioning."
    mkdir -p /home/vagrant/.ssh/
    touch /home/vagrant/.ssh/authorized_keys
    echo #{ssh_pub_key} >> /home/vagrant/.ssh/authorized_keys
    echo #{ssh_pub_key} > /home/vagrant/.ssh/id_rsa.pub
    chmod 644 /home/vagrant/.ssh/id_rsa.pub
    echo "#{ssh_prv_key}" > /home/vagrant/.ssh/id_rsa
    chmod 600 /home/vagrant/.ssh/id_rsa
    chown -R vagrant:vagrant /home/vagrant
    exit 0
  SHELL
end
Fran answered 11/2, 2018 at 22:6 Comment(1)
For those provisioning to OpenBSD, you will need grep -xsq for the grep command to work properly.Kilovoltampere
F
14

A shorter and more correct code should be:

ssh_pub_key = File.readlines("#{Dir.home}/.ssh/id_rsa.pub").first.strip
config.vm.provision 'shell', inline: 'mkdir -p /root/.ssh'
config.vm.provision 'shell', inline: "echo #{ssh_pub_key} >> /root/.ssh/authorized_keys"
config.vm.provision 'shell', inline: "echo #{ssh_pub_key} >> /home/vagrant/.ssh/authorized_keys", privileged: false

Otherwise user's .ssh/authorized_keys will belong to root user.

Still it will add a line at every provision run, but Vagrant is used for testing and a VM usually have short life, so not a big problem.

Federica answered 26/4, 2016 at 13:9 Comment(3)
I had to add config.vm.provision 'shell', inline: "mkdir -p /root/.ssh" after the first line since folder did not existBerberine
@Berberine I use ssh-copy-id, so it is always created for me but it is more correct to create it for others. I will edit, thanks.Federica
Thank you for the comment about why you used the privileged=false, I had been wondering why my changes were being seemingly clobbered, I had been editing root's auth keys file, and not vagrants as I was only using ~/.ssh/authorized_keys. Thank you for reminding me not to be dumb. ;)Heaver
B
10

I end up using code like:

config.ssh.forward_agent    = true
config.ssh.insert_key       = false
config.ssh.private_key_path =  ["~/.vagrant.d/insecure_private_key","~/.ssh/id_rsa"]
config.vm.provision :shell, privileged: false do |s|
  ssh_pub_key = File.readlines("#{Dir.home}/.ssh/id_rsa.pub").first.strip
  s.inline = <<-SHELL
     echo #{ssh_pub_key} >> /home/$USER/.ssh/authorized_keys
     sudo bash -c "echo #{ssh_pub_key} >> /root/.ssh/authorized_keys"
  SHELL
end

Note that we should not hard code path to /home/vagrant/.ssh/authorized_keys since some vagrant boxes not using the vagrant username.

Bandstand answered 22/1, 2017 at 5:49 Comment(0)
P
3

None of the older posts worked for me although some came close. I had to make rsa keys with keygen in the terminal and go with custom keys. In other words defeated from using Vagrant's keys.

I'm on Mac OS Mojave as of the date of this post. I've setup two Vagrant boxes in one Vagrantfile. I'm showing all of the first box so newbies can see the context. I put the .ssh folder in the same folder as the Vagrant file, otherwise use user9091383 setup.

Credit for this solution goes to this coder.

Vagrant.configure("2") do |config|
  config.vm.define "pfbox", primary: true do |pfbox|
        pfbox.vm.box = "ubuntu/xenial64"
        pfbox.vm.network "forwarded_port", host: 8084, guest: 80
        pfbox.vm.network "forwarded_port", host: 8080, guest: 8080
        pfbox.vm.network "forwarded_port", host: 8079, guest: 8079
        pfbox.vm.network "forwarded_port", host: 3000, guest: 3000
        pfbox.vm.provision :shell, path: ".provision/bootstrap.sh"
        pfbox.vm.synced_folder "ubuntu", "/home/vagrant"
        pfbox.vm.provision "file", source: "~/.gitconfig", destination: "~/.gitconfig"
        pfbox.vm.network "private_network", type: "dhcp"
        pfbox.vm.network "public_network"
        pfbox.ssh.insert_key = false
        ssh_key_path = ".ssh/"  # This may not be necessary.  I may remove.
        pfbox.vm.provision "shell", inline: "mkdir -p /home/vagrant/.ssh"
        pfbox.ssh.private_key_path = ["~/.vagrant.d/insecure_private_key", ".ssh/id_rsa"]
        pfbox.vm.provision "file", source: ".ssh/id_rsa.pub", destination: ".ssh/authorized_keys"
        pfbox.vm.box_check_update = "true"
        pfbox.vm.hostname = "pfbox"
        # VirtualBox
          config.vm.provider "virtualbox" do |vb|
            # vb.gui = true
            vb.name = "pfbox" # friendly name for Oracle VM VirtualBox Manager
            vb.memory = 2048 # memory in megabytes 2.0 GB
            vb.cpus = 1 # cpu cores, can't be more than the host actually has.
          end
  end
  config.vm.define "dbbox" do |dbbox|
        ...
Photima answered 24/1, 2019 at 18:50 Comment(0)
S
2

This is an excellent thread that helped me solve a similar situation as the original poster describes.

While I ultimately used the settings/logic presented in smartwjw’s answer, I ran into a hitch since I use the VAGRANT_HOME environment variable to save the core vagrant.d directory stuff on an external hard drive on one of my development systems.

So here is the adjusted code I am using in my Vagrantfile to accommodate for a VAGRANT_HOME environment variable being set; the “magic” happens in this line vagrant_home_path = ENV["VAGRANT_HOME"] ||= "~/.vagrant.d":

config.ssh.insert_key = false
config.ssh.forward_agent = true
vagrant_home_path = ENV["VAGRANT_HOME"] ||= "~/.vagrant.d"
config.ssh.private_key_path = ["#{vagrant_home_path}/insecure_private_key", "~/.ssh/id_rsa"]
config.vm.provision :shell, privileged: false do |shell_action|
  ssh_public_key = File.readlines("#{Dir.home}/.ssh/id_rsa.pub").first.strip
  shell_action.inline = <<-SHELL
    echo #{ssh_public_key} >> /home/$USER/.ssh/authorized_keys
  SHELL
end
Spurt answered 10/7, 2017 at 18:0 Comment(0)
W
1

For the inline shell provisioners - it is common for a public key to contains spaces, comments, etc. So make sure to put (escaped) quotes around the var that expands to the public key:

config.vm.provision 'shell', inline: "echo \"#{ssh_pub_key}\" >> /home/vagrant/.ssh/authorized_keys", privileged: false
Wakefield answered 24/12, 2017 at 21:21 Comment(0)
A
1

It's rather an old Question but maybe this would help someone nowadays, hopefully.

What works like a charm for me is:

Vagrant.configure("2") do |config|

  config.vm.box = "debian/bullseye64"
  config.vm.define "debian-1"
  config.vm.hostname = "debian-1"
  # config.vm.network "private_network", ip: "192.168.56.2" # this enables Internal network mode for VirtualBox
  config.vm.network "private_network", type: "dhcp" # this enables Host-only network mode for VirtualBox
  config.vm.network "forwarded_port", guest: 8081, host: 8081 # with this you can hit http://mypc:8081 to load the web service configured in the vm..
  config.ssh.host = "mypc" # use the base host's hostname.
  config.ssh.insert_key = true # do not use the global public image key.
  config.ssh.forward_agent = true # have already the agent keys preconfigured for ease.

  config.vm.provision "ansible" do |ansible|
    ansible.playbook = "../../../ansible/playbooks/configurations.yaml"
    ansible.inventory_path = "../../../ansible/inventory/hosts.ini"
    ansible.extra_vars = {
      nodes: "#{config.vm.hostname}",
      username: "vagrant"
    }
    ansible.ask_vault_pass = true
  end
end

Then my Ansible provisioner playbook/role configurations.yaml contains this:

- name: Create .ssh folder if not exists
  file:
    state: directory
    path: "{{ ansible_env.HOME }}/.ssh"

- name: Add authorised key (for remote connection)
  authorized_key:
    state: present
    user: "{{ username }}"
    key: "{{ lookup('file', 'eos_id_rsa.pub') }}"

- name: Add public SSH key in ~/.ssh
  copy:
    src: eos_id_rsa.pub
    dest: "{{ ansible_env.HOME }}/.ssh"
    owner: "{{ username }}"
    group: "{{ username }}"

- name: Add private SSH key in ~/.ssh
  copy:
    src: eos_id_rsa
    dest: "{{ ansible_env.HOME }}/.ssh"
    owner: "{{ username }}"
    group: "{{ username }}"
    mode: 0600
Abra answered 30/12, 2022 at 19:6 Comment(0)
V
0

A pretty complete example, hope this helps someone who visits next. Moved all the concrete values to external config files. IP assignment is just for trying out.

# -*- mode: ruby -*-
# vi: set ft=ruby :

require 'yaml'
vmconfig = YAML.load_file('vmconfig.yml')

=begin
Script to created VMs with public IPs, VM creation governed by the provided
config file.
All Vagrant configuration is done below. The "2" in Vagrant.configure
configures the configuration version (we support older styles for
backwards compatibility). Please don't change it unless you know what
you're doing
Default user `vagrant` is created and ssh key is overridden. make sure to have
the files `vagrant_rsa` (private key) and `vagrant_rsa.pub` (public key) in the
path `./.ssh/`
Same files need to be available for all the users you want to create in each of
these VMs
=end

uid_start = vmconfig['uid_start']
ip_start = vmconfig['ip_start']
vagrant_private_key = Dir.pwd + '/.ssh/vagrant_rsa'
guest_sshkeys = '/' + Dir.pwd.split('/')[-1] + '/.ssh/'
Vagrant.configure('2') do |config|
  vmconfig['machines'].each do |machine|
    config.vm.define "#{machine}" do |node|
      ip_start += 1
      node.vm.box = vmconfig['vm_box_name']
      node.vm.box_version = vmconfig['vm_box_version']
      node.vm.box_check_update = false
      node.vm.boot_timeout = vmconfig['vm_boot_timeout']
      node.vm.hostname = "#{machine}"
      node.vm.network "public_network", bridge: "#{vmconfig['bridge_name']}", auto_config: false
      node.vm.provision "shell", run: "always", inline: "ifconfig #{vmconfig['ethernet_device']} #{vmconfig['public_ip_part']}#{ip_start} netmask #{vmconfig['subnet_mask']} up"
      node.ssh.insert_key = false
      node.ssh.private_key_path = ['~/.vagrant.d/insecure_private_key', "#{vagrant_private_key}"]
      node.vm.provision "file", source: "#{vagrant_private_key}.pub", destination: "~/.ssh/authorized_keys"
      node.vm.provision "shell", inline: <<-EOC
        sudo sed -i -e "\\#PasswordAuthentication yes# s#PasswordAuthentication yes#PasswordAuthentication no#g" /etc/ssh/sshd_config
        sudo systemctl restart sshd.service
      EOC
      vmconfig['users'].each do |user|
        uid_start += 1
        node.vm.provision "shell", run: "once", privileged: true, inline: <<-CREATEUSER
          sudo useradd -m -s /bin/bash -U #{user} -u #{uid_start}
          sudo mkdir /home/#{user}/.ssh
          sudo cp #{guest_sshkeys}#{user}_rsa.pub /home/#{user}/.ssh/authorized_keys
          sudo chown -R #{user}:#{user} /home/#{user}
          sudo su
          echo "%#{user} ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/#{user}
          exit
        CREATEUSER
      end
    end
  end
Viscoid answered 22/6, 2020 at 19:44 Comment(0)
H
-1

Madis Maenni answer is closest to best solution:

just do:

vagrant ssh-config >> ~/.ssh/config
chmod 600 ~/.ssh/config

then you can just ssh via hostname.

To get list of hostnames configured in ~/.ssh/config

grep -E '^Host ' ~/.ssh/config

My example:

$ grep -E '^Host' ~/.ssh/config
Host web
Host db
$ ssh web
[vagrant@web ~]$
Hashum answered 19/2, 2019 at 22:18 Comment(0)
R
-2

Generate a rsa key pair for vagrant authentication ssh-keygen -f ~/.ssh/vagrant

You might also want to add the vagrant identity files to your ~/.ssh/config

IdentityFile ~/.ssh/vagrant
IdentityFile ~/.vagrant.d/insecure_private_key

For some reason we can't just specify the key we want to insert so we take a few extra steps to generate a key ourselves. This way we get security and knowledge of exactly which key we need (+ all vagrant boxes will get the same key)

Can't ssh to vagrant VMs using the insecure private key (vagrant 1.7.2) How do I add my own public key to Vagrant VM?

config.ssh.insert_key = false
config.ssh.private_key_path = ['~/.ssh/vagrant', '~/.vagrant.d/insecure_private_key']
config.vm.provision "file", source: "~/.ssh/vagrant.pub", destination: "/home/vagrant/.ssh/vagrant.pub"
config.vm.provision "shell", inline: <<-SHELL
cat /home/vagrant/.ssh/vagrant.pub >> /home/vagrant/.ssh/authorized_keys
mkdir -p /root/.ssh
cat /home/vagrant/.ssh/authorized_keys >> /root/.ssh/authorized_keys

SHELL

Rundlet answered 12/12, 2017 at 23:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.