我可以自动向 known_hosts 添加新主机吗?

gar*_*les 298 linux ssh known-hosts

这是我的情况:我正在设置一个测试工具,它将从中央客户端启动多个虚拟机实例,然后通过ssh. 虚拟机将具有以前未使用的主机名和 IP 地址,因此它们不会出现在~/.ssh/known_hosts中央客户端的文件中。

我遇到的问题是ssh针对新虚拟实例运行的第一个命令总是会出现一个交互式提示:

The authenticity of host '[hostname] ([IP address])' can't be established.
RSA key fingerprint is [key fingerprint].
Are you sure you want to continue connecting (yes/no)?
Run Code Online (Sandbox Code Playgroud)

有没有一种方法可以绕过这个问题并使客户端机器已经知道新主机,也许是通过使用已经嵌入到虚拟机映像中的公钥?如果可以的话,我真的很想避免使用 Expect 或其他任何东西来回答交互式提示。

yar*_*ena 259

IMO,执行此操作的最佳方法如下:

ssh-keygen -R [hostname]
ssh-keygen -R [ip_address]
ssh-keygen -R [hostname],[ip_address]
ssh-keyscan -H [hostname],[ip_address] >> ~/.ssh/known_hosts
ssh-keyscan -H [ip_address] >> ~/.ssh/known_hosts
ssh-keyscan -H [hostname] >> ~/.ssh/known_hosts
Run Code Online (Sandbox Code Playgroud)

这将确保没有重复的条目,您的主机名和 IP 地址都被覆盖,并且还将散列输出,这是一项额外的安全措施。

  • 你能确定回复 ssh-keyscan 请求的机器真的是你想要与之交谈的机器吗?如果不是,你已经向一个处于中间攻击的人敞开了心扉。 (9认同)
  • 这可能是个坏主意。通过更新这些密钥,您将面临中间人攻击。为避免重复条目,请改为检查 `ssh-keygen -F [address]` 的返回状态。https://medium.com/@wblankenship/bash-automatically-populating-a-known-hosts-file-15ea28d06c02 (7认同)
  • 为什么需要所有 3 个 ssh-keyscan?您不能只使用第一个,因为它适用于主机名和 ip 吗? (4认同)
  • @JasperWallace 是的,为此您至少需要指纹甚至更好的公钥,在这种情况下,您可以将其直接添加到known_hosts,从而使这个问题变得毫无意义。如果您只有指纹,则必须编写一个额外的步骤,用您的指纹验证下载的公钥...... (3认同)
  • 这不是这样做的方法。中间件。 (2认同)

Ign*_*ams 175

StrictHostKeyChecking选项设置为no,在配置文件中或通过-o

ssh -o StrictHostKeyChecking=no username@hostname.com

  • 这让您对中间人攻击持开放态度,这可能不是一个好主意。 (86认同)
  • @Mnebuerquo:如果您担心安全性,那么您对这个问题根本没有任何关系。您将拥有正确的主机密钥,这些密钥是从您要连接的系统的控制台收集的,您将在第一次连接时手动验证它。你当然不会“自动”做任何事情。 (18认同)
  • @JasperWallace,虽然这通常是很好的建议,但特定用例(部署测试虚拟机并向它们发送命令)应该足够安全。 (13认同)
  • 拒绝投票,因为这不能回答问题,并且会导致严重的安全漏洞。 (13认同)
  • 这给出了一个“警告:将“主机名,1.2.3.4”(RSA)永久添加到已知主机列表中。 StrictHostKeyChecking=no -o LogLevel=ERROR -o UserKnownHostsFile=/dev/null username@hostname.com` (10认同)
  • 如果确实需要,请使用`StrictHostKeyChecking=accept-new` 代替`=no`([参见我的回答](https://serverfault.com/a/977776/157527))。否则@JasperWallace [是对的](https://serverfault.com/questions/132970/can-i-automatically-add-a-new-host-to-known-hosts/977776#comment622492_132973):你总是受制于对与服务器的每个连接进行中间人攻击。 (4认同)
  • @IgnacioVazquez-Abrams,只需添加有关安全性的免责声明即可。考虑到对安全的影响,这个答案是完全可以接受的。许多环境(例如公司防火墙后面)使安全问题不那么严重。 (2认同)
  • 停用安全功能来抑制“恼人”的警告是一种非常糟糕的做法! (2认同)
  • 虽然这个答案不安全,但它明确回答了这个问题(甚至包括“绕过”这个词),所以在我看来这是最正确的答案。如果这是在安全的测试网络中完成的,这似乎是问题的目标,这将不是问题。我对其进行了编辑,以使风险更加清晰。希望一切顺利。 (2认同)

fiv*_*vef 119

对于懒人:

ssh-keyscan -H <host> >> ~/.ssh/known_hosts
Run Code Online (Sandbox Code Playgroud)

-H 散列主机名/IP 地址

  • 容易受到 MITM 攻击。你不是在检查钥匙指纹。 (9认同)
  • @Mnebuerquo 你说要做什么,但不说怎么做,这会很有帮助。 (9认同)
  • @jameshfisher 是的,它容易受到 MITM 攻击,但是当您手动执行此操作时,您是否曾将向您显示的 RSA 指纹与服务器中的实际指纹进行比较?不?所以这个答案是为你做的方式。如果是,则不应使用此答案并手动执行或实施其他安全措施... (9认同)
  • @Mnebuerquo 如果您还让我们知道处理此问题的更好方法,当我们需要使用无人参与的批处理脚本克隆存储库并且我们想绕过此提示时,我会非常高兴。如果您认为这不是正确的解决方案,请阐明一个真正的解决方案! (3认同)
  • BradChesney79 的答案在任何意义上都不是更好的方法。从字面上看,他在那里所做的就是使用 nmap 获取 SSH 主机密钥指纹,然后将其与 ssh-keyscan 所说的指纹进行比较。在这两种情况下,指纹都来自*同一个地方*。它与任何其他自动化解决方案一样容易受到 MITM 的攻击。验证 SSH 公钥的*唯一*安全有效的方法是通过一些受信任的带外通道。(或者设置某种密钥签名基础设施。) (3认同)
  • “ssh-keyscan -H &lt;host&gt; &gt;&gt; ~/.ssh/known_hosts”产生的条目更像是 ssh 对用户交互所做的。(-H 散列远程主机的名称。) (2认同)
  • 这不是这样做的方法。中间人。 (2认同)

小智 43

如前所述,使用按键扫描将是正确且不显眼的方法。

ssh-keyscan -t rsa,dsa HOST 2>&1 | sort -u - ~/.ssh/known_hosts > ~/.ssh/tmp_hosts
mv ~/.ssh/tmp_hosts ~/.ssh/known_hosts
Run Code Online (Sandbox Code Playgroud)

仅当尚未添加主机时,上述内容才可以添加主机。它也不是并发安全的;您不能在同一台机器上同时多次执行该代码段,因为 tmp_hosts 文件可能会被破坏,最终导致 known_hosts 文件变得臃肿...

  • 这不是这样做的方法。中间人。 (2认同)
  • 如果您非常关心 MITM,那么部署 DNSSEC 和 SSHFP 记录或使用其他一些安全方法来分发密钥,那么这种拼凑解决方案将无关紧要。 (2认同)

Ale*_*lex 19

您可以使用ssh-keyscan命令来获取公钥并将其附加到您的known_hosts文件中。

  • 确保检查指纹以确保它是正确的密钥。否则,您将面临 MITM 攻击。 (5认同)
  • @Mnebuerquo 在一般情况下公平点,但是如果有人已经知道正确的密钥是什么,为什么还要尝试以编程方式收集密钥? (5认同)
  • 这不是这样做的方法。中间人。 (2认同)

Dom*_*nik 13

检查每个新服务器/主机的指纹。这是验证服务器身份的唯一方法。如果没有它,您的 SSH 连接可能会受到中间人攻击

Do not use the old value StrictHostKeyChecking=no which never checks the authenticity of the server at all. Though the meaning of StrictHostKeyChecking=no is planned to be flipped later.

Second option, but less secure, is to use StrictHostKeyChecking=accept-new, which was introduced in version 7.6 (2017-10-03) of OpenSSH:

The first "accept-new" will automatically accept hitherto-unseen keys but will refuse connections for changed or invalid hostkeys.


Zar*_*art 10

这是将ssh-keyscan合并到您的游戏中的方法:

---
# ansible playbook that adds ssh fingerprints to known_hosts
- hosts: all
  connection: local
  gather_facts: no
  tasks:
  - command: /usr/bin/ssh-keyscan -T 10 {{ ansible_host }}
    register: keyscan
  - lineinfile: name=~/.ssh/known_hosts create=yes line={{ item }}
    with_items: '{{ keyscan.results | map(attribute='stdout_lines') | list }}'
Run Code Online (Sandbox Code Playgroud)

  • @jameshfisher 如果您还让我们知道处理此问题的更好方法,我会非常高兴,当我们需要使用无人参与的批处理脚本克隆存储库并且我们想绕过此提示时。如果您认为这不是正确的解决方案,请阐明一个真正的解决方案!如果您认为这不是正确的做法,请告诉我们“如何”去做! (7认同)
  • 这不是这样做的方法。中间件。 (2认同)

Chr*_*ris 8

这将是一个完整的解决方案,仅第一次接受主机密钥

#!/usr/bin/env ansible-playbook
---
- name: accept ssh fingerprint automatically for the first time
  hosts: all
  connection: local
  gather_facts: False

  tasks:
    - name: "check if known_hosts contains server's fingerprint"
      command: ssh-keygen -F {{ inventory_hostname }}
      register: keygen
      failed_when: keygen.stderr != ''
      changed_when: False

    - name: fetch remote ssh key
      command: ssh-keyscan -T5 {{ inventory_hostname }}
      register: keyscan
      failed_when: keyscan.rc != 0 or keyscan.stdout == ''
      changed_when: False
      when: keygen.rc == 1

    - name: add ssh-key to local known_hosts
      lineinfile:
        name: ~/.ssh/known_hosts
        create: yes
        line: "{{ item }}"
      when: keygen.rc == 1
      with_items: '{{ keyscan.stdout_lines|default([]) }}'
Run Code Online (Sandbox Code Playgroud)

  • @jameshfisher 感谢您回复“MITM”此线程上的每个答案。你有实际的解决方案吗?如果没有,请在下次按 CTRL-V 之前考虑一下您的评论如何浪费宝贵的屏幕空间 (5认同)
  • 这不是这样做的方法。中间件。 (3认同)
  • @Dr-Bracket 最上面的评论说得最好,“确保您免受 MITM 攻击的唯一有效方法是通过一些带外可信通道验证主机的公钥”。在 OP 的示例中,他们“从中央客户端启动多个虚拟机实例”,这必须提供这样的可信通道。 (2认同)

cjs*_*cjs 8

要正确执行此操作,您真正想做的是在创建 VM 时收集它们的主机公钥,并将它们按known_hosts格式放入文件中。然后-o GlobalKnownHostsFile=...,您可以使用, 指向该文件,以确保您连接到您认为应该连接的主机。你如何做到这一点取决于你如何设置虚拟机,但是,如果可能的话,从虚拟文件系统中读取它,或者甚至让主机/etc/ssh/ssh_host_rsa_key.pub在配置期间打印内容可能会成功。

也就是说,这可能不值得,这取决于您在什么样的环境中工作以及您预期的对手是谁。如上述其他几个答案中所述,执行简单的“首次连接时存储”(通过扫描或仅在第一次“真实”连接期间)可能会容易得多,并且仍能提供一些安全性。但是,如果您这样做,我强烈建议您将用户已知的主机文件 ( -o UserKnownHostsFile=...) 更改为特定于此特定测试安装的文件;这将避免使用测试信息污染您的个人已知主机文件,并在您删除 VM 时轻松清理现在无用的公钥。


Fel*_*bar 7

我做了一个单行脚本,有点长,但对于具有多个 IP 的主机执行此任务很有用,使用digbash

(host=github.com; ssh-keyscan -H $host; for ip in $(dig @8.8.8.8 github.com +short); do ssh-keyscan -H $host,$ip; ssh-keyscan -H $ip; done) 2> /dev/null >> .ssh/known_hosts
Run Code Online (Sandbox Code Playgroud)


Ven*_*ngs 6

我有一个类似的问题,发现提供的一些答案只是让我部分地获得了自动化解决方案。以下是我最终使用的内容,希望对您有所帮助:

ssh -o "StrictHostKeyChecking no" -o PasswordAuthentication=no 10.x.x.x
Run Code Online (Sandbox Code Playgroud)

它将密钥添加到known_hosts并且不提示输入密码。

  • 没有人检查指纹。 (8认同)
  • 容易受到 MITM 攻击。你不是在检查指纹。 (4认同)

Jac*_*ans 6

你如何建造这些机器?你能运行一个 dns 更新脚本吗?你可以加入一个IPA域吗?

FreeIPA 会自动执行此操作,但本质上您只需要区域上的SSHFP dns 记录和DNSSEC(freeipa 提供可配置选项(默认禁用 dnssec))。

您可以通过运行从您的主机获取现有的 SSHFP 记录。

ssh-keygen -r jersey.jacobdevans.com

jersey.jacobdevans.com IN SSHFP 1 1 4d8589de6b1a48e148d8fc9fbb967f1b29f53ebc jersey.jacobdevans.com IN SSHFP 1 2 6503272a11ba6d7fec2518c02dfed88f3d455ac7786ee5dbd72df63307209d55 jersey.jacobdevans.com IN SSHFP 3 1 5a7a1e8ab8f25b86b63c377b303659289b895736> jersey.jacobdevans.com IN SSHFP 3 2 1f50f790117dfedd329dbcf622a7d47551e12ff5913902c66a7da28e47de4f4b

然后一旦发布,您将添加VerifyHostKeyDNS yes到您的 ssh_config 或 ~/.ssh/config

如果/当 google 决定开启 DNSSEC,您可以在没有主机密钥提示的情况下通过 ssh 进入。

ssh jersey.jacobdevans.com

但是我的域还没有签名,所以现在你会看到......

debug1:服务器主机密钥:ecdsa-sha2-nistp256 SHA256:H1D3kBF9/t0ynbz2IqfUdVHhL/WROQLgan2ijkfeT0s

debug1:在 DNS 中发现 4 个不安全的指纹

debug1:匹配主机密钥指纹

在 DNS 中发现无法确定主机 'jersey.jacobdevans.com (2605:6400:10:434::10)' 的真实性。ECDSA 密钥指纹为 SHA256:H1D3kBF9/t0ynbz2IqfUdVHhL/WROQLGan2ijkfeT0s。在 DNS 中找到匹配的主机密钥指纹。您确定要继续连接吗(是/否)?不


小智 6

以下避免在 ~/.ssh/known_hosts 中出现重复条目​​:

if ! grep "$(ssh-keyscan github.com 2>/dev/null)" ~/.ssh/known_hosts > /dev/null; then
    ssh-keyscan github.com >> ~/.ssh/known_hosts
fi
Run Code Online (Sandbox Code Playgroud)