在Ubuntu系统中,使用ssh-copy-id命令分发SSH公钥是最快捷的免密登录配置方式,而known_hosts文件则负责记录远程主机的指纹信息,防止中间人攻击。实际操作中,很多人只会用ssh-copy-id把公钥丢到远程服务器,却忽略了known_hosts的指纹校验机制,导致安全隐患。下面直接给出完整的操作流程、原理解释和安全加固建议。

一、SSH密钥对的基本原理

SSH免密登录的核心是密钥对机制。你在本地机器上生成一对密钥:私钥(id_rsa或id_ed25519)留在自己手里,公钥(id_rsa.pub或id_ed25519.pub)放到远程服务器的~/.ssh/authorized_keys文件中。当你连接远程主机时,本地SSH客户端用私钥签名挑战信息,远程服务器用公钥验证签名,验证通过就允许登录。整个过程不需要输入密码。

二、生成SSH密钥对

在Ubuntu本地终端执行以下命令生成密钥对,推荐使用Ed25519算法,比RSA更安全、更高效:

ssh-keygen -t ed25519 -C "your_email@example.com"

如果你的系统不支持Ed25519,可以退而求其次用RSA 4096位:

ssh-keygen -t rsa -b 4096 -C "your_email@example.com"

执行后会提示输入保存路径(默认~/.ssh/id_ed25519)和密码短语(passphrase)。密码短语是可选的,但强烈建议设置,相当于给私钥加了一层锁。生成完毕后,~/.ssh目录下会出现私钥文件和对应的.pub公钥文件。

三、ssh-copy-id分发公钥的完整操作

最简单的用法,把本地公钥推送到远程服务器:

ssh-copy-id user@remote_host

系统会提示你输入远程用户的密码,输入后ssh-copy-id会自动完成以下操作:登录远程主机、检查~/.ssh目录是否存在、创建authorized_keys文件、追加公钥内容、设置正确的文件权限(600)。

如果你的SSH端口不是默认的22,比如改成了2222:

ssh-copy-id -p 2222 user@remote_host

如果你想指定使用哪个公钥文件(默认是id_rsa.pub,但如果你生成的是ed25519):

ssh-copy-id -i ~/.ssh/id_ed25519.pub user@remote_host

还有一种情况,远程服务器禁用了密码登录,只能用密钥登录,这时候你需要先通过其他方式(比如控制台)把公钥手动写入authorized_keys,或者用ssh-copy-id配合已有的有效密钥:

ssh-copy-id -i ~/.ssh/id_ed25519.pub -o "IdentityFile=~/.ssh/id_ed25519" user@remote_host

四、known_hosts指纹校验机制详解

known_hosts文件位于本地~/.ssh/known_hosts,它记录了你曾经连接过的所有远程主机的公钥指纹。每次SSH连接时,客户端会把远程主机出示的公钥指纹与known_hosts中已有的记录对比。如果匹配,正常连接;如果不匹配,会弹出警告,提示可能存在中间人攻击。

查看known_hosts内容:

cat ~/.ssh/known_hosts

输出类似这样:

192.168.1.100 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGrw+TBxIgMB...

每行包含主机名/IP、密钥类型、Base64编码的指纹。当你第一次连接某台主机时,系统会问你是否信任该指纹,输入yes后指纹就被写入known_hosts。

五、指纹校验失败的常见场景与处理

最常见的指纹变化原因是远程主机重装了系统或者SSH服务重新生成了主机密钥。这时候你需要删除旧的记录:

ssh-keygen -R remote_host

或者直接删除known_hosts中对应的行:

sed -i '/remote_host/d' ~/.ssh/known_hosts

删除后下次连接会重新提示确认指纹。如果你在批量管理服务器时经常遇到这个问题,可以在~/.ssh/config中设置:

Host *
    StrictHostKeyChecking accept-new

这个配置的意思是:对于已知主机严格校验,对于新主机自动接受。注意这会降低安全性,生产环境不建议使用。更安全的做法是使用StrictHostKeyChecking=ask(默认值)或者配合证书认证。

六、known_hosts的安全加固策略

第一,定期检查known_hosts文件,确认没有被恶意篡改。可以用sha256校验:

sha256sum ~/.ssh/known_hosts

第二,对于IP经常变化的服务器,建议使用主机名而非IP作为known_hosts的key,这样即使IP变了,只要主机名对应的DNS解析正确,指纹仍然有效。在~/.ssh/config中配置:

Host myserver
    HostName 192.168.1.100
    User ubuntu
    IdentityFile ~/.ssh/id_ed25519

第三,启用known_hosts的哈希存储,避免明文暴露主机信息:

HashKnownHosts yes

把这行加到~/.ssh/config中,known_hosts中的主机名会被哈希处理,别人无法直接从文件中看出你连接过哪些服务器。

七、ssh-copy-id和known_hosts配合使用的最佳实践

在自动化运维场景中,比如用Ansible批量分发密钥,ssh-copy-id配合StrictHostKeyChecking=no可以跳过指纹确认,但这在安全审计中是不合格的。正确做法是:先通过安全渠道(比如带外管理)获取远程主机的指纹,手动写入known_hosts,然后再执行ssh-copy-id。

具体步骤:先获取远程主机指纹:

ssh-keyscan -H remote_host >> ~/.ssh/known_hosts

然后再分发密钥:

ssh-copy-id user@remote_host

这样既自动化又安全。ssh-keyscan会扫描远程主机的公钥并追加到known_hosts,但要注意ssh-keyscan本身也可能被中间人欺骗,所以在高安全环境中应该通过可信渠道验证指纹。

八、常见错误排查

如果ssh-copy-id报错"Permission denied",检查几点:远程用户的~/.ssh目录权限必须是700,authorized_keys文件权限必须是600,家目录权限不能是777(即不能对其他用户可写)。在远程服务器上执行:

chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
chmod 755 ~

如果报错"ssh-copy-id: command not found",说明远程主机没有安装openssh-client包,需要先在远程执行:

sudo apt update && sudo apt install openssh-client -y

九、总结与安全建议

ssh-copy-id是Ubuntu上最方便的密钥分发工具,known_hosts是防范中间人攻击的第一道防线。两者配合使用才能构建完整的SSH安全体系。核心建议:使用Ed25519密钥算法、设置私钥密码短语、开启known_hosts哈希、严格校验主机指纹、定期审计授权密钥列表。对于企业级环境,还应该考虑使用SSH证书认证替代传统的authorized_keys机制,实现集中化的密钥生命周期管理。

把这些操作形成标准化流程,写进运维文档,团队协作时才不会出现安全漏洞。SSH安全不是一次性配置,而是持续维护的过程。