SSH升级警告别忽略:从故障排查到平滑升级完整指南
我记得很清楚那是一个周五下午我例行给一台跑了三年的 Ubuntu 16.04 做安全更新。apt upgrade跑完OpenSSH 从 7.2 跳到了 8.9。当时屏幕上滚过几条 warning我没细看觉得能连上就行。结果周一早上群里炸了——三个同事的 git 推送全部失败一个测试环境的自动化脚本卡在 ssh 登录还有一台老设备的 web 管理后台直接登不进去。排查了整整一天最后定位到的问题就是升级时屏幕上那一串被忽略的 ssh 升级警告。所以今天这篇我想把ssh 升级警告这件事讲透。它看起来只是一堆黄色文字实际上是新版 OpenSSH 在提醒你算法要换了、配置项要改写了、密钥要轮换了。如果你不提前把警告背后的逻辑搞清楚升级之后的故障会让你排查到怀疑人生。这篇文章适合所有需要自己管服务器的人——运维、后端开发、DevOps甚至是只在实验室里跑几台 Linux 的科研党。我会把常见警告的含义、升级前的准备、升级后最容易踩的坑以及一套可以照抄的平滑升级流程全部写出来。1. 先搞清楚升级时那些警告到底在说什么1.1 警告不是错误却是故障的预告很多人看到升级成功就以为万事大吉忽略了安装过程中 sshd 打印出来的 warning。我的经验是OpenSSH 是一个极少随便发警告的软件它一旦 warning往往意味着某个功能正在被弃用或者某种连接方式即将失效。升级过程中常见的警告大致分这么几类配置项弃用警告典型如Deprecated option UseDNS。意思是你在sshd_config里写的UseDNS这个参数新版 OpenSSH 已经不建议使用了。它暂时还能生效但未来某个大版本会直接删掉。算法签名警告比如连接时报ssh-rsa signature algorithm is not in the accepted list。这表示当前使用的 RSA 签名算法基于 SHA-1已经不在新版 OpenSSH 的默认接受列表里了。host key 变化警告升级后第一次连接时客户端提示REMOTE HOST IDENTIFICATION HAS CHANGED。这个尤其吓人因为它看起来像中间人攻击但很多时候只是升级过程中 host key 被重新生成。文件权限和格式警告比如Permissions 0644 for /etc/ssh/ssh_host_rsa_key are too open。新版 OpenSSH 对私钥权限检查更严格老系统留下的宽松权限会被点名。你把这些警告类比成汽车的仪表盘就明白了黄色故障灯不一定立刻让车熄火但它提示你某个系统正在异常。忽略它迟早会在高速上把你扔下来。1.2 为什么新版 OpenSSH 越来越爱唠叨这不是 OpenSSH 变得矫情而是安全形势逼的。我简单梳理一下时间线SHA-1 哈希算法早就被学术界证明存在碰撞攻击虽然实际利用成本高但作为签名算法的根基已经不牢靠。OpenSSH 从 8.8 版本开始默认禁用了ssh-rsa注意这里的ssh-rsa特指用 RSA 私钥配合 SHA-1 做签名而不是 RSA 密钥本身。1024 位 RSA 密钥已经可以在合理成本内被破解OpenSSH 逐步提高对密钥长度的最低要求现在普遍建议至少 2048 位更推荐直接上 ed25519。旧的密钥交换算法比如diffie-hellman-group14-sha1因为密钥强度不足也在逐步被移出默认支持列表。这里有一个很关键的点很多人搞混了升级服务端影响的是所有连接这台机器的客户端升级客户端影响的是你连的所有服务器。所以你要是只在服务器上执行了apt upgrade而同事的电脑还跑着老版本 OpenSSH、老版本 PuTTY、老版本 Git for Windows那升级之后出现连不上、认证失败、推送挂掉就是必然的。理解了这个你就能明白升级时的每一条 warning 都不是废话。它本质上是在告诉你你当前的环境里有一些组件还在依赖旧算法而这些旧算法马上就要被放弃了。2. 升级前先做三件事备份、审计、冻结基线2.1 备份配置不是复制文件那么简单我见过太多人升级前不备份 SSH 配置结果升级完发现sshd_config被默认配置覆盖PermitRootLogin从yes变成了prohibit-password然后远程 root 密码登录全部失效自己也被锁在门外。备份这一步不能省但也不能只是把那几个文件复制走。我建议直接打包整个/etc/ssh和当前用户的~/.ssh# 备份系统级 SSH 配置和 host key sudo tar -czf /root/backup/ssh_backup_$(date %F).tar.gz /etc/ssh # 备份当前用户密钥和 known_hosts tar -czf ~/ssh_config_backup_$(date %F).tar.gz ~/.ssh备份完后必须记录升级前的版本号ssh -V sshd -V # 有的系统上 sshd -V 会直接打印版本有的需要用 ssh -V 看我习惯把版本号和关键配置单独写到一个文本文件里升级完再和现状对比。这个文件不占地方但在出问题的时候能帮你快速建立升级前是这样、升级后变成那样的对照关系。2.2 用 sshd -t 摸清配置文件的暗雷升级前跑一遍配置检查能把很多 warning 提前暴露出来sudo sshd -t这个命令不会真的重启 sshd只做配置解析。如果配置有语法错误它会直接报错如果有弃用参数它会打印 warning。比如我当年那台服务器跑完之后立刻看到/etc/ssh/sshd_config line 12: Deprecated option UseDNS /etc/ssh/sshd_config line 18: Deprecated option Protocol这就是典型的还能用但迟早要改的配置项。UseDNS的作用是在登录时对客户端 IP 做反向 DNS 解析大多数内网场景根本不需要Protocol在 OpenSSH 7.6 以后就只有 2 了写不写都一样。这些参数不会让 sshd 拒绝启动但它们就像房子里的老电线暂时不出事出事就是大事。处理方式不是盲目删除。如果你确认自己根本用不到这些功能那就删掉如果某个参数还在被业务依赖比如老客户端需要Ciphers aes128-cbc你需要先评估客户端是否还能升级再做取舍。反正原则是升级前先跑sshd -t把 warning 全部列出来逐条决定是改掉还是保留。2.3 升级前把密钥和 known_hosts 策略想清楚升级过程中最容易翻车的是密钥体系所以我建议升级前先把家底摸清楚。先看当前服务器上有哪些 host key以及它们的类型和长度ls -l /etc/ssh/ssh_host_* sudo ssh-keygen -l -f /etc/ssh/ssh_host_rsa_key.pub sudo ssh-keygen -l -f /etc/ssh/ssh_host_ed25519_key.pub如果输出里只有 RSA 2048甚至还有 RSA 1024那我强烈建议你在升级前先把 ed25519 的 host key 生成好sudo ssh-keygen -t ed25519 -f /etc/ssh/ssh_host_ed25519_key -N 为什么要提前生成因为新版 OpenSSH 会更倾向于使用 ed25519 做 host key 协商你提前生成好升级后 sshd 重启时就能直接使用新算法客户端连接时不容易出现算法不匹配。再看用户的authorized_keyscat ~/.ssh/authorized_keys把里面的公钥按谁在用、什么设备、什么应用列个清单。这一步不是为了检查而是为了建立一个升级后验证基线升级完成后逐个用清单里的密钥登录一遍能登上去就说明没问题登不上去就说明算法或权限出问题了。3. 升级后最扎心的三连坑连不上、认证失败、git 推送挂掉3.1 ssh 服务器拒绝了密码的完整排查链路先还原一下我平时被问得最多的场景升级完 OpenSSH 后用户用密码登录服务器端日志显示Failed password但用户确定密码没输错。更奇怪的是换一个账户密码登录竟然成功了于是大家开始怀疑是账户被锁定。先别急着怀疑密码。我的排查链路是这样的第一步确认是不是算法代沟。用调试模式连接一次ssh -vvv userserver注意看输出里的协商过程。如果你看到类似no matching key exchange method found、ssh-rsa signature algorithm is not in the accepted list这就不是密码的问题是客户端拿出的算法服务端不认。第二步看服务端日志sudo journalctl -u ssh -f # 或者 sudo tail -f /var/log/auth.log如果日志里有Connection closed by authenticating user或userauth_pubkey: signature algorithm ssh-rsa not in accepted list同样指向算法代沟。第三步验证密码认证本身是否正常。如果确认客户端支持password认证方式但你怀疑是 sshd 配置把密码登录关掉了可以临时强制指定认证方式ssh -o PreferredAuthenticationspassword -o PubkeyAuthenticationno userserver如果这样能登进去而用默认配置登不进去那就说明sshd_config里的PubkeyAuthentication yes和PasswordAuthentication yes在升级时被改动了。这时候拿出你升级前的备份diff一下diff /root/backup/ssh_backup_*/etc/ssh/sshd_config /etc/ssh/sshd_config第四步检查PermitRootLogin。很多老系统当年为了省事直接PermitRootLogin yes新版默认策略收紧后这个值如果不显式配置会被改成prohibit-passwordroot 密码登录直接失效。我把这个排查链路整理成表格遇到问题直接对照现象日志/提示关键字根因处理方式密码正确但拒绝登录Failed passwordno matching key exchange算法代沟不是密码错升级客户端或临时放开旧算法root 密码登录失败无明确报错密码阶段直接断开PermitRootLogin被收紧显式配置PermitRootLogin yes不建议或改用密钥登录所有账户密码都无法登录Permission denied (publickey,password)sshd_config被覆盖用备份 diff 恢复关键配置连接后立刻关闭Connection closed by 127.0.0.1 port端口映射/代理链路问题检查代理端日志再查 sshd 日志这里还要专门提一个热词里反复出现的场景ssh -p 12062 userhost connection closed by 127.0.0.1。这种报错是典型的连接到了某个代理端口但代理后面的 sshd 拒绝或直接关闭了连接。排查顺序是先确认代理比如内网穿透工具、跳板机是否在线再确认目标 sshd 是否在监听最后看目标 sshd 日志里有没有来自代理 IP 的连接记录。升级 OpenSSH 后如果遇到这种问题多半是代理和目标服务器之间的算法协商失败而不是代理本身坏了。3.2 host key 变化引发的连锁反应升级 OpenSSH 本身一般不改变 host key但如果你在升级过程中重装过系统、重建过容器或者用了某些自动化脚本重建了/etc/sshhost key 就会变。一旦 host key 变了客户端第一次连接时就会看到WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!很多人一看这个警告就慌以为被中间人攻击了。我的处理流程是这样的先核对新 host key 指纹。在服务器本机执行sudo ssh-keygen -l -f /etc/ssh/ssh_host_ed25519_key.pub sudo ssh-keygen -l -f /etc/ssh/ssh_host_rsa_key.pub然后删除known_hosts里的旧条目ssh-keygen -f ~/.ssh/known_hosts -R 服务器IP或主机名重新连接时客户端会询问是否信任新指纹。这时候你拿着刚才在服务器上看到的指纹对比一下一致就输入yes。永远不要为了省事直接清空整个known_hosts因为那会让你失去对指纹变化记录的敏感性下次真的遇到中间人攻击你也看不出来。我记得有一次同事图省事直接把~/.ssh/known_hosts删了结果两周后一台服务器被劫持他居然没发现因为 known_hosts 里没有任何旧记录可对比。这个习惯非常危险。3.3 旧客户端与新版服务端的算法代沟这是升级后最普遍、也最容易被误判的一类问题。OpenSSH 8.8 之后RSA 签名算法被默认禁用这是很多老客户端一夜之间连不上的根源。典型场景包括Git 推送失败git push报gitgithub.com: Permission denied (publickey)。很多人第一反应是密钥丢了实际可能是本机 OpenSSH 升级后不再用ssh-rsa签名去认证而远程仓库平台还只接受 RSA 签名GitHub 早支持 ed25519但这个报错在自建 GitLab 上很常见。老设备管理不上手头有一台跑着 CentOS 6 的设备自带 OpenSSH 5.3算法列表非常老。服务端升级到 9.x 后连接直接报no matching key exchange method found。GUI 工具连不上MobaXterm、Termux、老版本 PuTTY底层 ssh 实现如果没更新同样会被新服务端拒之门外。遇到这种情况我的建议是分层处理短期应急在sshd_config里临时放行旧算法。比如KexAlgorithms diffie-hellman-group14-sha1 HostKeyAlgorithms ssh-rsa PubkeyAcceptedAlgorithms ssh-rsa注意这个方案有真实风险。SHA-1 已经不被信任ssh-rsa签名算法一旦放开等于把服务器的认证强度拉低到了十年前的水平。只适合在迁移过渡期临时用而且必须在同一台机器上记录清楚到期移除。长期方案升级客户端 更换密钥类型。把用户密钥统一换成 ed25519ssh-keygen -t ed25519 -a 100 -f ~/.ssh/id_ed25519 -N 然后把公钥分发到目标机器上。客户端用 ed25519 密钥后签名用的算法是ssh-ed25519完全不受ssh-rsa弃用影响。这里还要单独说下 vscode 和 git 这类工具。VSCode Remote-SSH 用的是系统自带的 ssh 客户端所以它报错时你排查的方向应该是系统 ssh 本身。而 Git for Windows 内置了自己的 ssh更新 Git for Windows 往往比调服务端配置更直接。4. 实操一次完整的 SSH 平滑升级流程4.1 分阶段升级策略为什么不是一口气全升我见过不少团队拿了新版本就apt upgrade一把梭所有服务器同时升。SSH 是服务器的入口把入口搞坏了等于把自己锁在门外所以绝对不能一把梭。我的做法是分三批测试机先在一台专门跑测试的机器上升级模拟生产环境连接场景重点验证密码登录、密钥登录、git 操作、rsync 传输这些常用链路。非核心业务机升级后即使出了问题影响面可控。核心生产机确认前面两批都没问题后利用业务低峰期升级并保留回滚窗口备份都还在随时可以降级。如果你管理的机器数量大推荐用配置管理工具批量执行。但有个细节必须注意批量执行前先批量验证配置命令里可以先跑ansible all -m shell -a sshd -t --become全部返回OK之后再进入重启 sshd 的环节。任何一台机器的sshd -t报错都不要强行继续。4.2 升级过程中的关键命令与配置迁移不同发行版升级命令不一样我列个表发行版升级命令服务名Debian/Ubuntusudo apt update sudo apt install openssh-server openssh-clientssh或sshdRHEL/CentOS 7sudo yum update openssh*sshdRHEL/CentOS 8/Rockysudo dnf update openssh*sshd升级完成后先不要急着重启 sshd。你现在的会话如果断了而 sshd 又因为配置问题起不来那就只能去机房或者用带外管理卡了。正确顺序是先开一个新的终端窗口用ssh连接同一个服务器保持一个备用会话在线更稳的做法是开 tmux 或 screen。确认配置没问题sudo sshd -t重启服务sudo systemctl restart sshd在新窗口里验证ssh -V和ssh localhost确认能正常登录。重启 sshd 这个命令我特别提醒一下老教程上经常写service ssh restart但新版系统上服务名统一用systemctl管理。你先确定服务名systemctl status ssh # 或 systemctl status sshd确定是哪个就用对应的。别在升级后盲打systemctl restart ssh如果服务名叫sshd这条命令会让你误以为重启成功了实际上根本没执行。升级后还要检查用户密钥权限。新版 OpenSSH 对authorized_keys权限检查很严格chmod 700 ~/.ssh chmod 600 ~/.ssh/authorized_keys这是一个我在生产环境里反复踩过的点升级前权限是 644 也能用升级后直接不给登录日志里一句Authentication refused: bad ownership or modes半年前的老坑了。至于 Windows 上怎么开启和升级 SSH现在新版 Windows 10/11 自带 OpenSSH Client用 PowerShell 查看版本ssh -V如果没有开启可以到设置 - 可选功能里添加 OpenSSH 客户端。如果你想在 Windows 上连 Linux 服务器把自带的 OpenSSH 客户端升到新版能规避掉大量算法代沟问题。Windows 上的 OpenSSH 服务端一般不推荐开除非你有特殊需求。4.3 客户端侧同步升级与配置对齐服务端升级完成只是完成了一半。我见过太多团队只升级服务器客户端全是老版本最后连接问题频发还以为是服务端配置写错了。客户端侧要做的三件事第一升级本机 ssh。Linux 用户直接apt update apt upgrade openssh-client。Windows 用户检查自带版本Git for Windows 用户更新 Git 本身。macOS 用户一般跟随系统更新但如果你用 Homebrew 装过openssh记得brew upgrade openssh。第二更新 known_hosts 和指纹。如果服务端 host key 策略变了客户端可能提示指纹不匹配按前面 3.2 节的方法处理。第三配置对齐。如果你有统一管理客户端的方案比如公司内部有标准镜像建议把以下配置下沉到所有开发人员的~/.ssh/config里Host * HostKeyAlgorithms ssh-ed25519 PubkeyAcceptedAlgorithms ssh-ed25519 PreferredAuthentications publickey,password这样至少保证大家优先用 ed25519 协商不会因为 RSA 签名问题连不上。再补一个 VSCode Remote-SSH 场景。升级服务器后VSCode 首次连接时会在目标机器上重新部署vscode-server这个过程依赖 scp。如果你看到类似正在使用 scp 将 vs code 服务器复制到主机然后卡住或者报错多半是目标机器上残留了旧版本的~/.vscode-server。处理方法是删掉它重来rm -rf ~/.vscode-server重新连一次VSCode 会重新拉取匹配当前版本的 server。这个坑在我升级过几次 OpenSSH 后反复出现十有八九是 vscode-server 残留而不是 SSH 本身的问题。5. 升级后的安全加固与长期运维建议5.1 算法白名单与最小化暴露面升级完成后我建议你趁热打铁把 sshd 配置整理成一个安全基线。下面这个配置是我在多数生产环境里采用的每一项都有明确目的# 只允许 ed25519 host keyRSA 作为后备 HostKey /etc/ssh/ssh_host_ed25519_key HostKey /etc/ssh/ssh_host_rsa_key # 禁止 root 密码登录只允许密钥 PermitRootLogin prohibit-password # 收紧密钥交换算法去掉老旧的 sha1 族 KexAlgorithms curve25519-sha256,curve25519-sha256libssh.org,diffie-hellman-group16-sha512 HostKeyAlgorithms ssh-ed25519,rsa-sha2-512,rsa-sha2-256 Ciphers chacha20-poly1305openssh.com,aes256-gcmopenssh.com,aes128-gcmopenssh.com MACs umac-128-etmopenssh.com,hmac-sha2-512-etmopenssh.com,hmac-sha2-256-etmopenssh.com这里解释一下为什么不是越少越好算法列表太极端会导致一些正常客户端连不上。比如你直接删掉 RSA host key某些老设备就只能干瞪眼。所以我保留了rsa-sha2-512/256它们是 RSA 密钥配合 SHA-2 签名安全性够用兼容性也还行。另外升级完可以顺手轮换 host key。注意这里不是指频繁换常规场景下 host key 可以几年不换但如果你之前一直只有 RSA host key建议现在就把 ed25519 host key 补上让新客户端优先走 ed25519。如果你要强制客户端都改用 ed25519 用户密钥可以写清楚PubkeyAcceptedAlgorithms ssh-ed25519,rsa-sha2-512,rsa-sha2-256这样ssh-rsa签名会被直接拒绝配合你已经换好的 ed25519 用户密钥整个认证链路就是现代安全标准了。这个配置变更一定要提前通知所有需要连这台机器的人不然又是一波连不上的投诉。5.2 把升级警告变成常态化巡检项最后一个建议非常实际把 SSH 的升级警告处理方法写进你的运维手册里变成常态化巡检项而不是每次升级都临时抱佛脚。我自己的做法是每次升级前把sshd -t的输出截图或保存到变更记录里。升级后用ssh -G检查客户端默认算法用sshd -T查看服务端实际生效配置两个对照着看。每季度跑一次密钥审计列出哪些服务器还在用 RSA 1024哪些用户密钥快到期了。对配置变更做台账/etc/ssh/sshd_config每次改动都记录日期、原因、责任人。这一步看起来很琐碎但真到出问题时台账就是你的案发现场记录。我当时排查那台 Ubuntu 服务器花了几个小时才想起来去看升级前的配置备份。后来我把这个流程固化下来再遇到类似问题十分钟内就能定位。另外有一点想强调SSH 升级警告不是一个孤立事件。你这次升级服务端下次可能就要升级客户端你这次处理了算法代沟下次可能还会遇到密钥轮换问题。把每次遇到的新警告记下来哪怕只是在个人笔记里记一行累积起来就是你自己的SSH 避坑手册。说起来我以前也嫌sshd -t麻烦也嫌 warning 吵。直到那次因为忽略 warning 折腾了一整天我才明白 OpenSSH 的警告有多值钱。现在我的习惯是收到升级通知先跑一遍sshd -t把 warning 一条条记录升级完再 diff 一遍配置。这套动作只要几十秒钟但帮我省掉的加班时间真的数不清了。下次轮到你升级的时候记得别跳过这几步。