我们运维组有台跳板机,管着公司三十多台业务服务器。上个月某天早上,值班同事在群里发了一句「怎么今天 SSH 这么慢」,我登上机器看了一眼,当时没当回事后来才发现,那台跳板机的 /root/.ssh/authorized_keys 文件已经堆到了 200 多行,全是这些年离职同事、同事自己换的笔记本、临时给外部运维开的公钥 nobody 都没删。日志里每来一次 SSH 连接,sshd 都要把这 200 多行公钥从头到尾读一遍、做一遍椭圆曲线验签,再跟客户端拿来的那把对上号。前后不过 3 秒,但对天天在跳板机上跑发布脚本的人来说,这 3 秒乘以几十台机器的并发,体感就是「整条链路都卡」。
如果你也在管理跳板机、堡垒机或者需要给几十台机器统一分发公钥,可以先看看这几家的机房方案和技术说明:vmrack(企业级自动化运维与全球主机) cubecloud(资深运维值守的全球云服务器)
说实话,这件事一开始并不复杂,清理一下 authorized_keys 就好了。但我在清理的过程中意识到,authorized_keys 这个文件本身就是个「只增不减」的设计它没有任何过期机制,也没有撤销机制,你今天删掉一把旧公钥,三个月后可能又有人把同样的旧笔记本密钥贴回来。于是我想干脆换个思路:不再往 authorized_keys 里堆公钥,而是改用 OpenSSH 的证书机制,让 sshd 直接信任一个 CA,由 CA 按需签发短期证书。
为什么 authorized_keys 堆到 200 行就明显变慢
要理解这件事,得先说清楚 sshd 认证公钥时到底干了什么。你用 ssh -v 连一次,日志里通常会看到 Offering public key: /root/.ssh/id_rsa 和 Server accepts key 这两行,中间发生的事大致是这样的:
第一,sshd 打开 /etc/ssh/sshd_config 里 AuthorizedKeysFile 指定的文件(默认是 .ssh/authorized_keys),按行读。第二,每读一行就用 ssh-keygen -l -f 类似的逻辑解析出公钥指纹,然后拿这个指纹去和客户端提供的公钥做比对。第三,只要有一行匹配上,认证就通过。整个过程里,200 行就意味着要读 200 行、解析 200 次指纹,ECDSA/ED25519 的验签本身不慢,慢的是文件 I/O 加上每行都要在内存里做一次 base64 解码和二进制结构解析。
更麻烦的是第二层影响。sshd 默认 fork 出子进程来处理每个连接,如果开了 MaxStartups 之外的高并发,又遇上慢速网络客户端带着一堆 ServerAliveInterval 保活,200 行的解析延迟会被放大到每一次认证尝试上。我们在 /var/log/secure 里统计过,改之前单次连接的认证阶段平均耗时 312 毫秒,清理到 40 行之后降到 47 毫秒,差了 6 倍多。而这还只是一台跳板机如果是 CI 流水线里几十台机器同时连,那就是几十个 3 秒叠一起。

第一阶段:先把 authorized_keys 清干净
不管你最后用不用证书,这一步都得做,因为绝大多数人卡住的原因就是历史遗留的公钥堆积。清理前先做两件事:备份,加临时密钥保命。
第一件事是备份。你在终端里敲 cp ~/.ssh/authorized_keys{,.bak.$(date +%F)},别问我为什么要留这个备份我见过太多人清理完发现自己把自己关在门外的。
第二件事是留后路。在你确认新密钥能登录之前,一定要在另一个终端里保持一个已登录的会话不要退出。很多教程会让你 authorized_keys 改完直接 rm 掉旧的,如果这时新密钥有问题,你的连接会立刻断,而云主机的救援模式又不是每家都给你那么方便。
清理的思路是「按用途分文件」,而不是「按人分文件」。OpenSSH 支持在 sshd_config 里写多行 AuthorizedKeysFile:
AuthorizedKeysFile .ssh/authorized_keys .ssh/keys/team .ssh/keys/temp
这样团队长期持有的公钥放 keys/team,临时给外部同事开的、约定一两周后失效的公钥放 keys/temp,你自己根目录下的 authorized_keys 留给最高权限的那把。分完之后,keys/temp 目录挂个定时任务,超过 30 天的自动删,根本不用人肉记着清理。
不过说实话,分文件只能解决「乱」,解决不了「慢」和「撤不掉」。文件里只要还有 200 行,sshd 该读还是得读;临时公钥放进去的时候你也没法真的作废它,除非你重新改文件并 reload sshd而 reload 期间正在进行的连接是会受影响的。

第二阶段:改用 CA 签发的短期证书
OpenSSH 从 5.6 开始就支持证书登录,思路其实特别直接:
我们在内网建一台「CA 机器」,用 ssh-keygen 生成一对 CA 私钥和公钥。CA 公钥放到所有服务器的 /etc/ssh/sshd_config 里,配成 TrustedUserCAKeys /etc/ssh/ca.pub,同时开 AuthorizedKeysFile .ssh/authorized_keys(保留,作为应急通道)。这样 sshd 只需要信任一把 CA 公钥,就等于信任了「这把 CA 能签出来的所有证书」。
这里最关键的一点,是 CA 私钥只放在 CA 机器上,运维人员平时拿不到。要登录某台服务器,走一个签发流程:
ssh-keygen -s /path/to/ca_key -I deploy-2026-11-05 -n deploy -V +1h:20261105xxxxxx -h -z 1 user@target-server
几个参数值得解释。-I 是证书里的身份标识,会被记进日志和 lastlog,做审计的时候全靠它,所以别写成 a、test 这种鬼名字。-n 是 principal,也就是这份证书允许登录哪个系统用户。-V 后面跟有效期区间,+1h 表示从现在起一小时后过期
证书过期即失效,不需要去任何地方撤销,也不需要 reload sshd。这才是我真正想要的特性:安全边界由时间本身决定,而不是由某个人记得去改文件决定。
签发出来的证书是个 -cert.pub 文件,客户端用 ssh -i xxx-cert.pub user@target 就能连上。注意客户端要同时拿到普通私钥和证书两个文件,缺一不可,证书本身不含私钥。

第三阶段:审计与撤销,这两件事别省
短期证书最大的好处是天然可审计。sshd 日志里会明确写出认证方式,比如:
Accepted publickey for deploy from 10.20.30.41 port 51234 ssh2: RSA-CERT: SHA256:xxxxxx ID deploy-2026-11-05
你一眼就能看出这份证书是谁签的、给谁用、什么时候签的。相比之下 authorized_keys 里一行公钥,你根本不知道它是哪把私钥、哪台笔记本、什么时候贴上去的。
至于撤销,OpenSSH 有 sshd-revoke 这个小工具,它在 CA 机器上维护一个 /etc/ssh/revoked_keys 撤销列表,配合 RevokedKeys /etc/ssh/revoked_keys 配置生效。但说实话,绝大多数场景下你不需要它一小时后证书自己就过期了。撤销列表主要用在「紧急刹车」上:万一某个用户的私钥泄露了,你想立刻掐断他手上所有还没过期的证书。
需要提醒的是,CA 私钥的权限一定要严格。常见的做法是给 CA 机器加 passphrase,签发脚本通过 SSH_ASKPASS 或者 ssh-agent 之类的机制取用;再配一条 authorized_keys 的 command= 限制,让跳板机上那个普通账号只能执行签发脚本,别的什么都干不了。CA 私钥泄露了,等于所有服务器的门都开了。

到底该不该上证书:几个现实的取舍
说完好处,也得说说代价,不然就成了纯推销。
如果你的服务器只有两三台、密钥就三五把,那 authorized_keys 根本不成问题,折腾证书纯属给自己增加复杂度。我这次动手,是因为我们面对的是三十多台机器加上一堆临时运维需求,清理的边际收益明显低于上证书的收益。
如果一定要用证书,有几个坑要提前想清楚:一是所有客户端都得改流程,运维同事的 ~/.ssh/config 里得加 IdentityFile 指向证书文件,CI 流水线里的部署脚本也要改;二是 CA 机器本身成了新的单点,它挂了就没人能签发新证书了,虽然已经签发的短期证书还能继续用,但建议你至少准备两台 CA 机器,或者干脆做主备切换;三是证书的签发流程必须跟工单系统打通,不然就变成了谁想签就签,审计的意义也就没了。
我们最后的选择是折中的:日常运维走 CA 签发的一小时期证书,紧急情况下保留 root 的 authorized_keys 通道,物理访问机房作为最后的兜底。上线三周之后,那台跳板机的 authorized_keys 只剩 4 行,SSH 认证平均耗时稳定在 40 毫秒以内,而每一条登录日志都带着明确的签发标识。那种「半夜查日志还得猜这把钥匙是谁的」的感觉,总算没了。
如果你手上的 SSH 密钥也开始堆成小山了,建议先别急着上证书,先统计一下 wc -l ~/.ssh/authorized_keys。超过 50 行就值得动手清理,超过 100 行,证书方案基本是时候该提上日程了。


喜欢
顶
难过
囧
围观
无聊





