如何将我的公钥添加到Vagrant虚拟机?

108
我在将SSH密钥添加到Vagrant虚拟机时遇到了问题。基本上,我这里的设置工作正常。一旦创建了虚拟机,我可以通过“vagrant ssh”访问它们,用户“vagrant”存在,并且该用户在authorized_keys文件中有一个ssh密钥。
现在我想做的是:能够通过ssh连接或使用scp连接到这些VM。因此,我只需要像使用ssh-copy-id一样将我的公共密钥从id_rsa.pub添加到authorized_keys中。
是否有方法在Vagrant安装过程中告诉Vagrant应包含我的公钥?如果没有(根据我的谷歌搜索结果很可能没有),是否有一种在Vagrant安装过程中轻松追加我的公钥的方法?
13个回答

94

您可以使用Ruby的核心File模块,如下所示:

  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

这个工作示例将~/.ssh/id_rsa.pub添加到vagrant和root用户的~/.ssh/authorized_keys中,这样您就可以使用现有的SSH密钥。


2
好的,它能够正常工作,但是它会在每次执行多次的情况下添加一行。 - sekrett
3
我投票反对对这个回答的重大补充——因为应该将该补充作为独立的回答添加。如果@user76329(如果您回来阅读这篇文章)希望添加该补充,请将其作为单独的答案添加。 - HPierce
1
如果您希望shell provisoner仅运行一次,这里有一个解决方案:https://blog.ouseful.info/2015/07/27/running-a-shell-script-once-only-in-vagrant/ 基本上,您需要创建一个文件标志来标记已经发生的provisoning。 - rszalski
我需要添加相当于以下命令的内容:mkdir ~/.ssh && touch authorized_keys - Douglas Denhartog
@sekrett 对于幂等性,你最好使用Ansible,shell只能做到这么多。https://docs.ansible.com/ansible/latest/modules/authorized_key_module.html - MGP

70

复制所需的公钥将完全落入provisioning阶段。确切的答案取决于您喜欢使用哪种provisioning方式(shell、Chef、Puppet等)。最简单的方法是使用file provisioner来为该密钥提供支持,大致如下:

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

实际上,您需要将内容追加到authorized_keys文件中。可以使用shell provisioner进行操作,如下所示:

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

你也可以使用像Puppet这样的真正的供应商。例如请参阅使用Puppet管理SSH授权密钥


10
谢谢你的回答-那正是我需要的推动力 :) 我以为Vagrant可能会提供某些东西,但通过配置它也可以做到。或许有点“丑陋”,但它非常有效。基本上,我只是像你建议的那样复制文件,然后使用shell provisioner来追加密钥。virtualhost.vm.provision "shell", inline: "cat ~vagrant/.ssh/me.pub >> ~vagrant/.ssh/authorized_keys" - tehK
6
@tehK在上面的评论(在is之前)中隐藏了Unicode字符,这可能会破坏您的下午 - 这是一个修复后可以复制粘贴的版本:virtualhost.vm.provision "shell",inline:"cat ~vagrant/.ssh/me.pub >> ~vagrant/.ssh/authorized_keys"。请注意,该版本已经修正并可以安全使用。 - Aidan Kane
抱歉,我来晚了。config.vm.provision是什么? - user2568374
6
不要将 ~/.ssh/id_rsa.pub 写死在代码里。应该从 ssh-add -L 命令中获取密钥。 - Timur
2
@Timur,你会怎么做呢? - kraxor
显示剩余6条评论

47

有一种更“优雅”的方法可以实现你想要做的事情。你可以找到现有的私钥,而不是费力地添加你的公钥。

按照以下步骤查看现有私钥的路径(请参阅下面的IdentityFile):

运行

vagrant ssh-config

结果如下:

$ 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

然后你可以像这样使用私钥,注意也要关闭密码身份验证:

ssh -i /Users/madismanni/m2/vagrant-magento/.vagrant/machines/magento2.vagrant150/virtualbox/private_key -o PasswordAuthentication=no vagrant@127.0.0.1 -p 3150

收到:ssh_exchange_identification:远程主机关闭连接。现在我甚至无法在vagrant up中获得身份验证错误。它说尝试在未准备好进行客户通信的机器上进行特定于Guest的操作。这不应该发生,应该报告错误。 - user2568374
非常流畅。我将来会这样做。有些人可能还会考虑使用Include语句将生成的ssh配置添加到默认的~/.ssh/config中,以避免每次运行ssh时使用-i和-F开关。明确地说,vagrant ssh-config > ~/.ssh/vagrant-ssh ; echo Include vagrant-ssh > ~/.ssh/config ; 现在您可以轻松地ssh yourvm了。 - Jepper

20

这个优秀的回答是由user76329添加的,它是一个被拒绝的建议性编辑

扩展Meow的示例,我们可以复制本地pub / priv ssh密钥,设置权限,并使内联脚本具有幂等性(仅运行一次,并且只有在测试条件失败时才会重复,因此需要进行配置):

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

1
对于那些打算使用OpenBSD的人来说,你需要使用grep -xsq命令来正确运行grep命令。 - cstroe

13

一个更短并且更正确的代码应该是:

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
否则用户的.ssh/authorized_keys将属于root用户。
尽管每次提供运行时都会添加一行,但Vagrant用于测试,虚拟机通常寿命不长,因此不是大问题。

2
由于文件夹不存在,我不得不在第一行后添加config.vm.provision 'shell',inline: "mkdir -p /root/.ssh" - geekQ
@geekQ 我使用 ssh-copy-id,所以它总是为我创建的,但为其他人创建更加正确。我会进行编辑,谢谢。 - sekrett

10

我最终使用了以下代码:

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

请注意,我们不应该硬编码路径为/home/vagrant/.ssh/authorized_keys,因为有些vagrant boxes不使用vagrant用户名。


非常好的答案。对我帮助很大。但是我有一些设置可以调整vagrant.d目录的存储位置,所以我调整了您的配置以处理这种情况。详情请参见此处 - Giacomo1968

3

尽管某些旧帖子接近有效,但它们对我来说都无法正常工作。我必须在终端中使用 keygen 制作 rsa 密钥并使用自定义密钥。换句话说,不能使用Vagrant的密钥。

截至本帖日期,我使用的是Mac OS Mojave。我在一个Vagrantfile中设置了两个 Vagrant boxes。我展示了第一个 box 的所有内容,以便新手可以看到上下文。我将 .ssh 文件夹放在与 Vagrantfile相同的文件夹中,否则请使用 user9091383 的设置。

这个解决方案的来源来自于 这位开发者。

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|
        ...

2
这是一篇非常好的帖子,帮助我解决了与原帖作者描述类似的情况。虽然最终我使用了 smartwjw 的答案中提出的设置/逻辑,但我遇到了一个问题,因为我在我的一个开发系统上使用 VAGRANT_HOME 环境变量将核心 vagrant.d 目录保存在外部硬盘上。所以这里是我在 Vagrantfile 中使用的调整代码来适应已设置的 VAGRANT_HOME 环境变量;“魔法”发生在这一行 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

1
对于行内 shell provisioners 来说,公钥通常包含空格、注释等。因此,请确保在扩展为公钥的变量周围加上(转义)引号:
config.vm.provision 'shell', inline: "echo \"#{ssh_pub_key}\" >> /home/vagrant/.ssh/authorized_keys", privileged: false

0

这是一个比较旧的问题,但或许现在仍有人需要帮助。

对我来说非常有效的方法是:

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

然后我的Ansible provisioner playbook/role configurations.yaml 包含如下内容:

- 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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接