SSH新IP指纹写入known_hosts:原理、命令与自动化实践 📅 发布时间:2026/9/8 8:28:06 👁 浏览次数: 有人第一次连 VPS 或公司内网新机器时被那行“The authenticity of host ... cant be established”搞得很慌也有人反过来天天自动部署脚本里被这行交互确认卡住烦到想拍桌子。其实这背后就是 SSH 的 host key 校验机制在起作用而我们要做的就是把这个“大姑娘上轿头一回”的确认动作提前做好也就是把新IP的SSH指纹写进 known_hosts。这篇就围绕“新IP的SSH指纹如何正确加入known_hosts”这个操作把原理、手动姿势、批量玩法、常见坑一次讲明白。适合刚接触 Linux 服务器的新手也适合被各种 CI/CD、自动化运维脚本困扰的同行。内容全部基于我自己的踩坑记录和实操经验不是那种网上抄来抄去的命令堆砌。1. 先搞清楚 known_hosts 到底管什么1.1 为什么每次连新IP都要问一次SSH 协议本身是不带身份认证的客户端和服务器建立连接时服务器会把自己的 host key主机公钥发给客户端。这个公钥的指纹如果不在你本地的 known_hosts 文件里SSH 客户端就无法确认“对面到底是不是真的那台服务器”所以它宁愿停下来问你一句指纹对不对要不要存下来这个设计其实跟浏览器访问 HTTPS 网站时校验证书一样都是为了防中间人攻击。假如没有这一步你连的服务器可能根本不是你以为的那台而是被人劫持后伪装出来的你的密码、会话内容都会被偷走。所以 known_hosts 这个文件就是客户端本地的“信任白名单”。很多新手第一次看到这条提示时都是直接敲 yes这没问题但如果是在自动化场景下连 yes 都没法敲那就得换一种方式提前写入。这也是“新IP的SSH指纹添加到known_hosts文件”这个需求最常见的出现场景。1.2 known_hosts 文件的存储位置和格式known_hosts 分两个层级存在全局文件/etc/ssh/ssh_known_hosts对所有用户生效普通用户一般没有写权限。用户文件~/.ssh/known_hosts只对当前用户生效这是绝大多数情况下我们操作的文件。文件内容的格式一般是192.168.1.10 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI... [192.168.1.10]:2222 ssh-rsa AAAAB3NzaC1yc2EAAA... github.com,140.82.112.3 ssh-rsa AAAAB3NzaC1yc2EAAA...每一行由“主机名或IP或 [IP]:端口”、“密钥类型”、“公钥内容Base64编码”三部分组成用空格分开。如果 SSH 客户端开了 HashKnownHosts 选项默认macOS上开着主机名部分会变成一串哈希值比如|1|xxxx|yyyy普通人肉眼看不出这行对应哪台机器但 SSH 客户端自己能算出来。另外需要注意该文件的权限必须是 600owner 可读写其他人啥都不能干否则 SSH 客户端会直接拒绝读取甚至报出 “Bad owner or permissions” 错误。这个坑在后面排查部分我会再提。提示不要随便手动编辑 known_hosts 去改内容格式错了很容易导致 SSH 连接直接失败。正确做法是优先用ssh-keyscan生成或者用ssh-keygen -R删除旧记录后重新连接生成。2. 手动添加新IP指纹的几种可靠姿势2.1 用 ssh-keyscan 一键抓取并追加ssh-keyscan是 OpenSSH 自带的小工具作用是主动连接某台服务器的 SSH 端口抓取它返回的 host key然后输出成 known_hosts 兼容的格式。它不会真正建立 SSH 会话也不会要求输入密码所以非常适合用在脚本里。单台机器手动添加的姿势ssh-keyscan -t ed25519,rsa,ecdsa 192.168.1.10 2/dev/null ~/.ssh/known_hosts-t指定抓取哪些密钥类型建议写全ed25519,rsa,ecdsa防止你服务器上只启用了某一种类型而漏掉。2/dev/null是为了屏蔽掉 ssh-keyscan 输出到 stderr 的各种错误和提示信息只把真正的密钥行追加进去。执行完之后可以用下面的命令检查是否写入成功ssh-keygen -F 192.168.1.10如果成功会打印出对应主机的公钥信息如果没输出来说明没写进去。如果目标 SSH 端口不是默认的 22需要加-p参数ssh-keyscan -p 2222 -t ed25519 192.168.1.11 ~/.ssh/known_hosts这时的 known_hosts 里记录的主机名会带有端口号格式类似[192.168.1.11]:2222。这个细节容易忽略后面排查会展开讲。2.2 交互式确认方式的底层逻辑和注意事项如果你不想用 ssh-keyscan也想在第一次连接时人工确认指纹那就老老实实看提示信息。SSH 客户端会显示三种 key 指纹的哈希MD5/SHA256你得自己判断这个值是否正确。判断依据通常是云厂商控制台一般会提供 host key 指纹阿里云、腾讯云、AWS 都有这个字段。公司内部资产管理系统里如果登记了初始指纹可以比对。如果什么渠道都没有那就只能按“首次连接信任”的宽松策略来了。首次连接时的提示长这样The authenticity of host 192.168.1.10 (192.168.1.10) cant be established. ED25519 key fingerprint is SHA256:xxxxxxxxxxxxxxxxxxxxxxx. This key is not known by any other names. Are you sure you want to continue connecting (yes/no/[fingerprint])?注意这个[fingerprint]选项意思是你可以直接输入一个指纹值让客户端校验而不只是无脑敲 yes。这在安全要求高、你又已经提前知道指纹的场景下非常有用。输入 yes 后记录会被写进~/.ssh/known_hosts。这个过程里 SSH 客户端会检查目标主机名/IP是否已经存在于文件中如果存在但内容对不上就会进入另一条“指纹冲突”的错误流程而不是白白问你一句。3. 批量场景下的指纹管理和自动化3.1 内网上百台机器如何一次搞定真实工作中运维手里一堆机器、开发要在十台跳板机上做免密登录、CI 环境要连几十个部署节点这种“新IP加指纹”的需求如果靠一台台手动 yes效率太低而且不可复现。批量抓取可以这样写for ip in $(cat server.list); do ssh-keyscan -t ed25519,rsa $ip 2/dev/null ~/.ssh/known_hosts done但直接这样跑会有个问题如果server.list里某个 IP 连不上ssh-keyscan 可能超时很久默认没有超时限制会比较慢而且多次运行脚本会导致 known_hosts 文件里出现大量重复记录。所以更稳妥的做法是cat server.list | xargs -P 10 -I {} ssh-keyscan -t ed25519,rsa {} 2/dev/null ~/.ssh/known_hosts sort -u ~/.ssh/known_hosts -o ~/.ssh/known_hostsxargs -P 10表示并行10个进程去抓取速度能快很多。sort -u用来去重避免同一条记录被多次写入。如果你管理的是几百上千台机器或者想做到更规范的“集中式管理”可以用 Ansible 分发 known_hosts 文件思路是先在一台管理机上生成好全量 known_hosts然后通过 Ansible 推送到所有目标机器的/etc/ssh/ssh_known_hosts实现全局信任同一批主机。这比每台机器各维护一份高效得多也方便后续加新机器时统一更新。3.2 StrictHostKeyChecking 和 UserKnownHostsFile 的正确用法自动化场景里除了 ssh-keyscanOpenSSH 还提供了几个影响 host key 校验的配置项这里重点说StrictHostKeyChecking和UserKnownHostsFile。StrictHostKeyChecking有三个取值yes最严格如果主机不在 known_hosts 里直接拒绝连接。no最宽松自动接受新主机的 key 并写进 known_hosts也不检查指纹是否变化。安全风险极大千万不要在生产环境用。accept-new介于两者之间自动接受新主机的 key 并写入 known_hosts但一旦 known_hosts 里已经有该主机的记录且指纹对不上连接还是会失败。对自动化任务来说accept-new是一个比较实用的折中方案。第一次连接时不再交互确认之后的连接保持校验ssh -o StrictHostKeyCheckingaccept-new user192.168.1.10在 CI/CD 流水线里更规范的做法是使用独立的 known_hosts 文件不污染运维人员本机的 known_hostsssh -o StrictHostKeyCheckingaccept-new -o UserKnownHostsFile/tmp/known_hosts user192.168.1.10UserKnownHostsFile指定一个临时文件路径这个文件不存在也会自动创建相当于把信任列表隔离在流水线项目内部。这个思路和 Docker 容器里跑 SSH 的场景也很契合——容器每次重建后是全新的文件系统把 known_hosts 挂载成 Volume 或提前生成到镜像里能避免每次构建都重新询问指纹。提醒StrictHostKeyCheckingno看起来很省事但它同时会禁用对 host key 变化时的告警等于把 SSH 的防中间人机制关了。在同一局域网、公网环境里这等于裸奔。就算是内网测试我也建议用accept-new而不是no。4. 实战中踩过的坑和排查经验4.1 端口变化导致 keyscan 抓不到指纹有一回一个测试环境把 SSH 端口从 22 改成了 2299我没注意直接跑ssh-keyscan 192.168.1.20结果什么都抓不到。看 stderr 才发现它是去连的 22 端口而那个端口已经没有任何服务了。ssh-keyscan 默认连 22 端口。遇到非标端口必须显式指定ssh-keyscan -p 2299 192.168.1.20而且写入 known_hosts 后后续连接也必须用一样的端口写法比如ssh -p 2299 user192.168.1.20。这时候文件里的记录会是[192.168.1.20]:2299如果你用默认端口去连SSH 客户端会去找192.168.1.20这条找不到又把它当成新IP提示确认。看起来是“明明加过了为什么还要问”实际是端口号导致 known_hosts 里的 key 匹配不上。4.2 服务器重装系统导致的 host key verification failed这个坑很多人都碰到过。比如一台机器重装系统后host key 全部重新生成原来 known_hosts 里存的指纹对不上了连接时报错 WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY! Host key verification failed.正确做法是先把旧的记录删掉ssh-keygen -R 192.168.1.20如果你还用了非标端口删除时也要带上端口说明ssh-keygen -R [192.168.1.20]:2299然后再重新连接生成新记录或者用 ssh-keyscan 加回去。这里要特别提醒如果这台机器的域名和IP都指向同一个主机known_hosts 里可能会有多条记录改哪个删哪个要看清别一股脑全删了再把合法的也丢了。可以用ssh-keygen -F 192.168.1.20和ssh-keygen -F host.example.com分别查看。最坏情况下可以直接备份后清空 known_hosts重新把所有常用主机连一遍但这会干扰指纹校验的连续性对安全敏感的环境不建议这么做。4.3 known_hosts 文件权限错误导致的连接失败一个很隐蔽的问题是 known_hosts 文件权限被改错了。比如你用chmod 777 ~/.ssh/known_hosts或者用 root 编辑过后再赋权给普通用户SSH 客户端可能会拒绝加载这个文件或者报这样的错误Bad owner or permissions on /home/user/.ssh/known_hosts修复方法chmod 600 ~/.ssh/known_hosts同时~/.ssh目录本身权限也建议保持 700不然部分系统也会警告。这类问题在工作中出现频率不低尤其是多用户共用同一台跳板机有人图方便用 root 把文件拷来拷去结果权限就乱了。4.4 多条相同主机记录导致连接时指纹冲突有时候因为人为编辑或者脚本重复执行known_hosts 里同一台主机会有好几条不同类型的 key 记录。比如同时有ssh-rsa和ssh-ed25519两条服务器端如果同时启用了两种 key那没问题但如果服务器端实际只返回其中一种而你 known_hosts 里恰好另一种类型不匹配比如服务器改了 host key 后旧记录没删干净就会莫名其妙地报错。排查手段就是ssh-keygen -F IP看当前匹配的记录再配合grep IP ~/.ssh/known_hosts看全部相关行有重复就用ssh-keygen -R IP清掉再重新添加。5. 几个少见但实用的进阶玩法5.1 同时记录域名和IP减少不必要的确认连接使用域名时SSH 客户端默认会用你输入的域名去匹配 known_hosts。比如你ssh userweb01.example.com它会找web01.example.com的记录而不会自动拿 DNS 解析出来的 IP 去匹配。实际情况是很多机器既有域名又有固定IP如果你一会儿用域名连一会儿用IP连第一次都会触发确认。解决办法是在抓取时同时写入域名和IP两种别名ssh-keyscan -t ed25519 web01.example.com 192.168.1.30 ~/.ssh/known_hostsssh-keyscan 支持一次传入多个主机名/IP输出时会分别为每个名字生成一条记录。这样你在任何一端发起连接都能命中已有记录省去重复确认。5.2 用 HashKnownHosts 隐藏主机名兼顾安全和隐私如果你细心一点会发现有些系统比如 macOS 默认就是开启的known_hosts 里的主机名不是明文而是一串哈希值。这是HashKnownHosts选项的作用开启后即使 known_hosts 文件泄露攻击者也没法一眼看出你连过哪些机器。如果你希望自己生成的 known_hosts 也采用哈希形式可以修改~/.ssh/config中的全局配置Host * HashKnownHosts yes然后重新生成或连接后续写入的记录就会以哈希形式存储。注意如果你需要手动ssh-keygen -R删除某台主机即使文件里是哈希值-R IP也能正常工作因为它内部会做同样的哈希计算来匹配。5.3 服务器 host key 轮换后如何平滑过渡生产环境出于合规要求可能需要定期轮换服务器的 host key尤其是已经发生过 key 泄露的场景。轮换后如果直接重启 sshd所有客户端的 known_hosts 都会因为不匹配而报错导致线上业务连接中断。平滑的做法是预先在服务器端生成好新的 host key通过安全的带外渠道把新指纹通知到各客户端客户端提前把旧记录删除、新记录预写入 known_hosts再约定时间统一重启 sshd。整个过程可以配合 Ansible、SaltStack 之类的配置管理工具批量下发避免业务窗口内出现大面积连接失败。这个操作在网络上其实不太常见因为多数团队是一台台敲命令处理的但一旦机器多了不搞平滑轮换光是几十台机器的告警就能让人焦头烂额。6. 与SSH其他易混淆概念的边界6.1 known_hosts 和 authorized_keys 别搞混刚开始用 SSH 的人很容易把这两个文件搞混known_hosts是用来验证“我连的服务器是不是真的服务器”它存在客户端解决的是“客户端信任服务器”的问题。authorized_keys是用来做免密登录的它存在服务器的~/.ssh/下解决的是“服务器信任客户端”的问题。简单类比known_hosts 是你手机里存的“房东钥匙指纹”确认开门的人是不是房东authorized_keys 是房东门禁系统里登记的“你的指纹”用来识别你能不能进这扇门。方向不一样千万别把这两个文件路径混淆否则要么连不上要么免密登录配置半天不生效。6.2 StrictHostKeyChecking 和 PasswordAuthentication 是两码事有时候改了 StrictHostKeyChecking 还是连不上因为服务器端禁用了密码登录或者密钥认证没配置好。这两个配置不在同一个层次StrictHostKeyChecking 只管主机指纹校验PasswordAuthentication 管的是登录认证方式。就算你把 host key 校验全部跳过没有正确配置登录凭据SSH 连接照样失败。排查问题时先把这两件事分开先确认“能不能连上”网络、端口、host key 是否匹配再确认“登录是否成功”用户名、密码/密钥。别在 known_hosts 上纠结半天结果根本是密钥认证失败。6.3 known_hosts 记录和 sshd 配置的 HostKey 指令服务器端/etc/ssh/sshd_config里有HostKey指令指定了 sshd 启动时加载哪些主机密钥文件常见的是ssh_host_rsa_key、ssh_host_ecdsa_key、ssh_host_ed25519_key。当你发现 ssh-keyscan 抓取到的 key 类型和 known_hosts 里的记录不一致时可以顺便检查一下服务器端 HostKey 配置确认 sshd 到底在提供哪些类型的 host key。还有一个很少人注意的情况服务器新增或移除了某个 HostKey 文件后重启 sshd 会改变它对外提供的密钥类型组合客户端 known_hosts 里如果只存了某个类型而新指纹缺失也会导致认证报错。所以服务端在调整 host key 类型后客户端一定要同步更新 known_hosts。7. 我的经验总结和小建议从第一次在实验室里无脑敲 yes到现在管着几十台服务器的 SSH 信任链我最大的感受是known_hosts 的管理看起来是小得不能再小的事但一旦出问题要么连接失败得莫名其妙要么被中间人攻击的风险闷在鼓里。平时多做一点规划能让后面省掉很多麻烦。我的个人习惯可以给大家参考所有服务器的 key 抓取都用脚本批量完成写入后立即ssh-keygen -F验证自动化任务统一用StrictHostKeyCheckingaccept-new绝不妥协成no每季度巡检一次 known_hosts把已经不用的机器记录清掉把变更过 IP 的记录更新到位手动清理旧 key 时永远先用-F查一遍再动手避免误删。最后再分享一个小技巧如果你不确定新抓的指纹对不对可以把 keyscan 结果放到一个临时文件里用ssh-keygen -lf查看可读指纹再跟你掌握的实际指纹做比对。确认无误后再追加进正式的 known_hosts这样既省掉了交互确认的麻烦又不会盲目信任一个“陌生人”。ssh-keyscan -t ed25519 192.168.1.40 /tmp/new_hostkey.pub ssh-keygen -lf /tmp/new_hostkey.pub # 比对指纹无误后 cat /tmp/new_hostkey.pub ~/.ssh/known_hosts这套流程我用了很久稳妥且高效遇到问题也知道从哪下手排查希望能给你们省点时间。