ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

SSH密钥过期?拆解四种常见报错与修复方案

2026/9/11 6:03:58 拓冰建站 浏览量
SSH密钥过期?拆解四种常见报错与修复方案 1. SSH密钥真的会“过期”吗先把这个概念拆开如果你把 SSH 密钥理解成门禁卡那公私钥对更像是一把锁和一把钥匙。私钥在你手里公钥这把“锁”放在服务器的authorized_keys里。只要这两样东西不被换掉、不丢失SSH 密钥本身就没有“到期”的概念。有一年我帮公司运维一批服务器某天突然有人喊“密钥过期了”排查了半天结果发现是有人重置了系统之前放进去的authorized_keys全没了。密钥还完好地躺在本地~/.ssh里只是服务器不再认你而已。严格来说真正“会过期”的 SSH 密钥只有两类一类是用 SSH CA 签发的证书证书里带了有效期另一类是在authorized_keys里给某个公钥加了expiry-time2025-01-01之类的限制到点 OpenSSH 就拒绝。这两种都是主动设置的“有效期限”多数个人开发者和中小团队基本不会用。所以我们日常所说的“密钥过期”拆开来看就是三种情况服务器换身份了、服务器不认你的公钥了、本地拿不出私钥了。把这些情况归好类之后再去看报错就清楚多了。下面这四种报错覆盖了日常 99% 的“密钥失效”场景Host key verification failed客户端不认服务器主机的身份常见于服务器重装、迁移后主机密钥变了Permission denied (publickey)服务器不认你提供的公钥常见于authorized_keys被覆盖、用户名不对、公钥和私钥不配对password authentication failed密码认证被拒这不一定是密钥的问题也可能密码被改、账号被锁no mutual signature algorithm新旧端算法不匹配老的 RSA 密钥容易被卡在这一步。我的日常习惯是远程故障排查先问自己一句是known_hosts在报错还是publickey在报错这决定了接下来动哪一块。搞清楚这个概念后面几章按图索骥就行。2. 看懂四种高频报错密钥“失效”到底断在哪一环SSH 连接在身份验证之前还有一个“客户端先验证服务器身份”的步骤。换句话说OpenSSH 客户端连服务器时会做两件事先用known_hosts里的指纹确认服务器主机是靠谱的再用自己的身份公钥和服务器上的authorized_keys比对。所以拿到一坨报错信息直接去搜索往往容易得到又臭又长的教程。不如先自己看一眼报错开头的关键词把问题分成“服务器身份问题”还是“客户端身份问题”再动手。我判断问题最喜欢用的是ssh -vvv userremote有时候会加四个v。输出虽然冗长但关键行非常明确debug1: Found key in /home/xxx/.ssh/known_hosts:12—— 说明服务器指纹这一步已经过了debug1: Offering public key: ...—— 说明客户端正在向服务器提供身份Authentications that can continue: publickey—— 说明服务器当前只接受公钥登录。有人刚接触-vvv时容易迷我的经验是直接 grep 关键词比如ssh -vvv userremote 21 | grep -E Offer|accept|denied|Host key|found这样能快速定位。下面这张对照表是我这几年整理出来的可以直接抄报错关键词通常意味着最先检查的地方Host key verification failedknown_hosts 指纹不匹配服务器是否重装过、IP 是否被复用Permission denied (publickey)身份没有通过授权ssh-add -l有没有加载、authorized_keys是否还在password authentication failed密码被拒绝密码是否改过、账号是否被锁、远端是否只允许密钥no mutual signature algorithm双方没有共同算法私钥是否过旧考虑换成 ed25519我见过不少朋友在Permission denied (publickey)出现后第一反应就是重新生成密钥。这其实有点浪费。重新生成密钥只是换了一把新锁如果问题出在authorized_keys被清空那把旧锁对应的钥匙其实还好好的把旧公钥再放上去就行。重新生成密钥意味着你要重新把公钥分发到所有服务器反而更容易出错。3. known_hosts 换脸服务器重装和 IP 复用导致主机指纹对不上Host key verification failed可能是最像“过期”的一类问题。它的场景一般是这样的你有一台服务器平时连得好好的某天突然系统重装或者从镜像重建IP 没变但 SSH 主机密钥已经换了。这时候本地known_hosts里还存着旧的主机指纹 OpenSSH 为了保证你不会被中间人欺骗宁可拒绝连接也不会给你弹个“指纹变了”的确认框。完整报错一般长这样 WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!很多人在这一步收到警告后会非常紧张担心服务器被入侵。实际上对个人场景来说绝大多数情况都是这台服务器的系统被重装过或者你买了一台新机器用了同一个 IP又或者 Docker 容器重建后宿主机端口映射到了新的容器主机密钥上。只有在你完全确定服务器不做更改、却发现指纹变化时才有必要警觉安全事件。修复方式也简单删除旧指纹让客户端重新认识一次即可ssh-keygen -R 你的服务器IP # 或者指定端口 ssh-keygen -R 你的服务器IP -p 端口号如果你知道这是一台全新的机器也可以直接用ssh-keyscan先把新指纹取回来ssh-keyscan -t ed25519,rsa 你的服务器IP ~/.ssh/known_hosts但这里有个小坑ssh-keyscan默认会一次性取回服务器当前真实的主机密钥如果你连接的是一台已经被劫持的机器那它写入的就是劫持者的指纹。所以更稳妥的流程是先ssh-keyscan拿到指纹再通过你信任的渠道比如云控制台、机房提供的指纹手动比对一下确认无误再写入known_hosts。顺便说一句很多人在华为交换机、H3C 这类网络设备上配置 SSH 后也遇到过类似报错。网络设备的known_hosts处理其实和个人电脑一样只是设备端保存主机密钥的位置和命令不一样。比如有些交换机默认生成 RSA 主机密钥时长度只有 1024 位新版 OpenSSH 客户端会嫌它太弱而拒绝连接表现也接近于no mutual signature algorithm。我记得华为 VRP 平台上可以用ssh key rsa或rsa local-key-pair create重新生成主机密钥位数充足了问题就解决了。4. 服务器不认这把公钥authorized_keys、目录权限和 StrictModes 的那些坑如果说 known_hosts 是“钥匙串认家门”那authorized_keys就是“门锁自己记录谁能进”。这一环出问题表现通常是Permission denied (publickey)无限循环你输入多少次密码都没用或者干脆不给你密码输入的机会。常见原因有三类。第一类是authorized_keys文件里压根没有你的公钥或者被别人覆盖了。这种情况最容易发生在重装系统、迁移用户目录、多人共管服务器时。排查方法很简单登录服务器或者通过其他管理通道看一眼文件内容是否包含你的公钥。比如你想确认本地私钥对应的公钥长什么样可以用ssh-keygen -y -f ~/.ssh/id_ed25519把输出 COPY 下来再去服务器上cat ~/.ssh/authorized_keys对比。两边一致说明公钥没丢不一致把公钥追加进去echo ssh-ed25519 AAAAC3... your_comment ~/.ssh/authorized_keys第二类是文件权限不对。OpenSSH 默认开着StrictModes只要.ssh目录或authorized_keys文件权限过宽比如组用户或其他用户可写sshd 会直接忽略这个文件即使内容完全正确也拒绝登录。标准权限是这样chmod 700 ~/.ssh chmod 600 ~/.ssh/authorized_keys chmod 600 ~/.ssh/id_ed25519需要注意~家目录本身的权限也不能过于开放。某些发行版对家目录要求是 755 没问题但如果你把家目录设成了 777那也一样会被StrictModes拦下来。群晖、威联通这类 NAS 上我见过有人把共享文件夹权限和家目录权限揉在一起导致登录失败的最后的解决办法就是把/root/.ssh或/home/某用户/.ssh权限重新收敛一下。第三类是公钥和私钥不配对。你可能把旧公钥放上了服务器但本地默认加载的是另一把新私钥。最典型的场景是~/.ssh/id_rsa和id_ed25519同时存在系统默认使用id_ed25519而服务器上只放了老的 RSA 公钥。这时候可以用-i参数指定私钥ssh -i ~/.ssh/id_rsa userremote也有人一直遇到“SSH服务器拒绝了密码”的报错。这个报错比较特殊它往往和密钥无关而是服务器那边的sshd_config禁止了密码登录或者允许的认证方式里根本没有密码。常见的排查项是检查PasswordAuthentication、PubkeyAuthentication、PermitRootLogin这些配置。我有个朋友在 Windows 上用 Bitvise SSH Server 搭过一个测试环境密码登录一直失败最后发现问题出在 Bitvise 的虚拟账户权限没给够密码没错但账户被禁用。所以这一类问题要往认证策略和账户状态那边多想想。这里我想专门提一个高级但很有用的配置authorized_keys本身支持过期时间。如果你确实想让某个密钥到期可以在公钥前面加前缀expiry-time2025-06-30T00:00:00 ssh-ed25519 AAAAC3... temp_user到期之后即使公钥还在文件里OpenSSH 也会直接忽略它。这个功能比你自己写脚本去清理文件要可靠得多适合临时授权给外包、短期协作者不用怕忘记删。5. 重启后密钥像消失了一样ssh-agent、passphrase 和 IdentityFile还有一种“过期”是我见得最多的连接时报错但服务器端和密钥文件都没问题问题出在本地ssh-agent根本没加载这把私钥。很多人给私钥设置了 passphrase这是好习惯但也带来麻烦。当你用ssh userhost登录时如果不小心把密码短语输错太多次或者系统重启后 agent 被清空那么下次连接时 OpenSSH 可能只尝试默认的几个密钥文件而不会主动弹出密码短语窗口。表现就是“密钥突然不能用了”。先确认 agent 里有没有东西ssh-add -l如果提示The agent has no identities.说明 agent 是空的把私钥重新加进去ssh-add ~/.ssh/id_ed25519如果你设了 passphrase它会要求你输入一次。之后在这个 agent 存活期间连接远程服务器都不需要再输入 passphrase。但这里有个隐藏的槽点如果远程服务器开启了pam_ssh或者你登录的是图形会话系统可能每次登录都重新启动一个 agent导致之前ssh-add的密钥“消失”。解决这个问题的核心思路是把加载密钥这件事自动化而不是指望 agent 记住。比如在~/.ssh/config里写Host myserver HostName 192.168.1.20 User ubuntu IdentityFile ~/.ssh/id_ed25519 IdentitiesOnly yes AddKeysToAgent yes ServerAliveInterval 60IdentitiesOnly yes很关键。如果本地同时有多把私钥尤其是有 GitLab、GitHub、公司内部服务器等多套密钥时OpenSSH 默认会把它们都试一遍。服务器端看到一堆“不认识的公钥”后可能直接拒绝或者产生很慢的连接。IdentitiesOnly会告诉它只尝试IdentityFile指定的这把钥匙既快又稳。另外日常最容易遇到的“VSCode 连接 SSH 远程服务器失败”很多时候并不是 VSCode 的问题而是本地 agent 里没有正确加载密钥。VSCode Remote-SSH 只负责调用你本机的ssh命令它不管理密钥。我一般先在本机终端里跑一遍ssh userhost确认能连上再回到 VSCode 里重载窗口。如果本机能连但 VSCode 不行再检查 Remote-SSH 使用的配置文件路径通常是~/.ssh/config确认没有在 VSCode 设置里覆盖了默认配置。6. 实际场景修复记录VSCode 远程、GitLab、群晖与批量登录这一章我把几个高频场景的完整修复链路写一遍方便直接对照操作。第一个场景是 GitLab / GitHub 提示权限被拒。很多人用 SSH 方式 clone 仓库突然某天提交时提示gitgitlab.com: Permission denied (publickey).第一件事是用调试模式测试一下ssh -T gitgitlab.com输出里会显示使用了哪把公钥、服务器是否接受。常见的修复是去 GitLab 的 SSH Keys 页面把新公钥加进去把旧的删掉。公钥内容可以从本地获取cat ~/.ssh/id_ed25519.pub如果本地同时有 GitLab 和 GitHub 的密钥记得在~/.ssh/config里分别指定例如Host gitlab.com HostName gitlab.com User git IdentityFile ~/.ssh/id_ed25519_gitlab IdentitiesOnly yes有些仓库也支持设置自己的core.sshCommand比如git config core.sshCommand ssh -i ~/.ssh/id_ed25519_gitlab -o IdentitiesOnlyyes这样即使全局 SSH 配置很乱单独这个仓库也能走到正确的密钥上。第二个场景是群晖上配好 SSH 密钥之后还是免密失败。群晖的 OpenSSH 行为跟普通 Linux 大体一致但有个常见坑很多人通过 Web 界面新建了用户然后用sudo su或admin登录公钥却放到了错误用户的home目录。群晖默认情况下每个用户的 home 目录需要在“控制面板 → 用户账号 → 高级设置”里开启否则你连~/.ssh目录都建不出来。正确操作是开启群晖的 SSH 功能控制面板 → 终端机和 SNMP → 启动 SSH 功能用管理员账户 SSH 登录创建/volume1/homes/用户名/.ssh目录把公钥写入authorized_keys再执行权限修正命令。权限命令在群晖上尤其重要因为它的盘符路径是/volume1/...权限继承关系复杂我一般直接对用户 home 目录做一次收敛chmod 700 /volume1/homes/用户名/.ssh chmod 600 /volume1/homes/用户名/.ssh/authorized_keys chown -R 用户名:users /volume1/homes/用户名/.ssh第三个场景是批量登录。做运维的人经常要同时连几十台机器每次指定不同的密钥和用户很痛苦。我习惯在~/.ssh/config里把主机分组Host batch01 batch02 batch03 User app IdentityFile ~/.ssh/id_ed25519_app IdentitiesOnly yes Host web01 HostName 10.0.0.11 User deploy IdentityFile ~/.ssh/id_ed25519_deploy然后用ssh batch01这样的短别名连接。如果需要批量执行命令配合-F指定配置文件会更灵活。之前有个朋友问“SSH 批量登录到底怎么搞”其实前提就是你得先把每台服务器的公钥都配好之后才能谈脚本化和自动化。密钥分发这一步做不扎实后面所有批量操作都会变成碰运气。第四个场景比较小众但搜索热度很高华为交换机 SSH 配置。网络设备和 Linux 的公钥存放方式不一样华为交换机一般通过ssh user username authentication-type publickey这样的命令绑定用户和公钥公钥格式也需要通过public-key peer或ssh key之类的视图导入。如果你习惯用 Linux 上的公钥文件直接贴过去很有可能会被设备拒绝因为它期望的是经过格式转换的 RFC 形式的公钥。处理方式是把 OpenSSH 公钥转成设备能识别的格式ssh-keygen -e -f ~/.ssh/id_ed25519.pub生成的带---- BEGIN SSH2 PUBLIC KEY ----头的内容再贴到交换机里命中率会高很多。7. 让密钥长久不再“过期”轮换周期、可过期证书、攻击防御最后这部分算是我个人的经验沉淀。我的核心观点是与其等密钥出问题再去排查不如给密钥定一个“生命周期”管理方案。第一点轮换密钥。我现在的习惯是每年给每类用途各生成一把新的 ed25519 密钥尤其是 Git、服务器登录这类长期使用的场景。生成命令很简单ssh-keygen -t ed25519 -a 100 -C work-2025 -f ~/.ssh/id_ed25519_work替换旧密钥时先在服务器上把新公钥加进去测试无误后再删除旧公钥这能避免把服务器锁在门外。轮换的目的不是为了制造工作量而是为了降低私钥泄露后的暴露面。万一某把私钥已经被别人拿到了你定期轮换至少能限制他的利用窗口。第二点给临时密钥设置真实的过期时间。前文提到的expiry-time非常好用。如果我不是很确定一份临时授权什么时候结束我会直接加上expiry-time比如给外包同事一周的访问权限expiry-time2025-06-07T23:59:59 ssh-ed25519 AAAAC3... outsider这个方案比记在日历里靠谱多了到期自动失效不需要人为介入。如果你的环境规模大、机器多更专业的做法是搭一套 SSH CA给客户端密钥签发带有效期的证书这样就不用每台机器都手动改authorized_keys。但中小团队完全可以从expiry-time开始。第三点防御大量 SSH 连接攻击。服务器的公网 IP 裸奔在外时每天都会被扫描。常见现象是日志里出现大量的Failed password或者Connection closed by authenticating user有时候还伴随着SSH大量连接怎么办的焦虑。我的建议分三层立刻把密码登录关掉只保留密钥登录给 sshd 设置登录频率限制比如MaxAuthTries调低部署 fail2ban对连续失败的 IP 做自动封禁。但这里要补一句关掉密码登录之前确认你的密钥能正常免密登录并且至少保留一条带外管理通道比如云厂商的 VNC 或救援模式。我见过有人在只留密钥登录的服务器上丢掉了自己的私钥最后只能通过控制台重置密码进场折腾了一下午。这个教训值得记住。还有一个细节常常被忽略时间同步。如果你把 SSH CA 证书或者expiry-time用起来了客户端和服务器的系统时间不一致也会导致验证失败。尤其在某些虚拟化环境里时钟漂移很严重。检查一下timedatectl或者使用 NTP 服务避免出现“明明没过期系统却说已过期”的诡异情况。最后分享一点个人感悟。我折腾 SSH 也有不少年了经历过把服务器锁在外面、经历过 known_hosts 指纹连续两天都在变、也经历过 agent 重启后密钥“卡”在 VSCode 里。回过头看“SSH 密钥过期”这个说法更像是一个现象描述而不是真正的技术结论。大多数情况下它指向的是 known_hosts、authorized_keys、ssh-agent 这三个环节里某一个连接的断裂。把这几个环节弄明白再配合定期的密钥轮换和临时授权的过期策略你会发现“密钥过期”这个词会慢慢从你的日常里消失。