云服务器 SSH 密钥治理指南:从暴露面盘点到轮换、禁用与回滚

枫少@KillBoy
枫少@KillBoy
枫少@KillBoy
管理员
303
文章
0
粉丝
安全运维1 86字数 3296阅读10分59秒阅读模式
AI智能摘要
SSH 密钥常被当作一次性配置,却可能在人员流动中悄悄累积成巨大的暴露面:离职员工的旧密钥仍保留 root 权限,私钥被拷到多台设备,甚至误提交进 Git,而团队却说不清究竟谁还能登录。文章从生命周期管理切入,讲清如何盘点服务器上的授权公钥、识别每把密钥的主人并标记高风险路径,再通过分级收敛、轮换验证与可回滚的软删除流程,避免误删唯一可用凭据——你的密钥清单,真的对得上号吗?
— AI 生成的文章内容摘要

SSH 密钥是云服务器最常用的登录凭据之一,但也是最容易被忽视的安全薄弱点。很多团队在初始化服务器时生成一对密钥,然后就再也不去管它:公钥散落在多台机器的 authorized_keys 文件里,私钥可能被拷贝到多个开发者的笔记本上,甚至有人把私钥提交到 Git 仓库而不自知。更常见的情况是,某个员工离职后,他的密钥仍然保留在服务器上,拥有完整的 root 权限。当密钥数量增多、人员变动频繁时,你很难说清楚"现在到底有哪些人能登录这台服务器"——这正是密钥治理要解决的核心问题。

把 SSH 密钥当作一次性配置而不是持续管理的资产,是大多数安全事件的根源。密钥一旦泄露或被遗留,攻击者不需要破解密码,只需要找到一把能用的私钥就能直接进入系统。本文不讨论如何修改 SSH 配置文件来加固单台服务器,而是从密钥生命周期管理的角度,带你走一遍从暴露面盘点、分级收敛、轮换验证到回滚的完整流程,重点解决两个实际问题:轮换时如何避免误删唯一可用的凭据,以及如何确认旧密钥已经彻底失效。

运维人员在工作台前检查服务器密钥配置

先搞清楚暴露面:你的密钥散落在哪里

治理的第一步不是改配置,而是摸清现状。你需要回答三个问题:哪些服务器在用密钥登录?每台服务器上授权了哪些公钥?这些公钥对应的私钥掌握在谁手里?

盘点服务器上的授权公钥

登录每一台 linux 服务器,检查所有用户目录下的 authorized_keys 文件。不要只盯着 root,普通用户的密钥同样可能拥有 sudo 权限。用一条命令遍历所有用户:

for user in $(awk -F: '$3 >= 1000 {print $1}' /etc/passwd); do
  echo "=== $user ==="
  cat /home/$user/.ssh/authorized_keys 2>/dev/null
done

同时检查 root 的密钥文件:/root/.ssh/authorized_keys。对于使用云厂商密钥对功能的场景(比如腾讯云的 SSH 密钥绑定),公钥会写入实例的系统配置中,你需要登录实例后查看实际生效的密钥列表,而不能只看云控制台的绑定关系——控制台显示的状态和实例内部实际配置有时并不一致。

识别每一把公钥的主人

拿到公钥列表后,最麻烦的工作是给它们"对上号"。公钥文件的最后一段通常是注释字段,很多人会顺手写上自己的邮箱或主机名,这能帮你快速识别。对于没有注释的密钥,可以比对本地开发者电脑上的 ~/.ssh/*.pub 文件,或者检查团队成员是否在 GitHub/GitLab 上公开过相同的公钥。

建议建立一张密钥登记表,记录以下字段:公钥指纹(用 ssh-keygen -lf 获取)、所属人员或用途、授权的主机列表、创建时间、上次使用时间、风险等级。这张表是后续所有治理动作的依据。

找出高风险登录路径

盘点时重点关注三类风险:

  • 无注释或无法识别归属的密钥:这类密钥可能是测试遗留、自动化脚本生成,也可能是攻击者植入的后门。
  • 长期未使用的密钥:通过 lastlog 或 journalctl 检查某个密钥对应的登录记录,超过 90 天没有使用的密钥应该被标记为待清理。
  • 共享密钥:如果多个人共用同一把私钥(比如团队共用一个部署密钥),一旦其中一人离职或设备丢失,你无法单独撤销某个人的访问权限。

分级与收敛:建立最小权限边界

盘点的结果往往不太好看:一台生产服务器上可能有十几把公钥,其中一半是没人认识的。这时候不要急着全部删除,先分级,再收敛。

按角色划分密钥类型

把密钥分成三类,分别管理:

  • 个人密钥:每个开发者、运维人员使用自己的独立密钥,私钥只存在于个人设备上。这类密钥用于日常登录和操作。
  • 服务账号密钥:用于 CI/CD 流水线、备份任务、监控采集等自动化场景。服务账号应该使用独立的系统用户,权限限定在特定目录和命令,不能直接给 root 权限。
  • 应急密钥:由安全负责人或运维主管保管,存放在加密的离线介质中,平时不使用,只在紧急情况下(比如其他密钥全部失效)启用。

收敛授权范围

对于识别出的个人密钥,确认使用者的实际需求。一个只负责维护 Web 服务的开发人员,不需要在所有数据库服务器上有登录权限。在 authorized_keys 中可以通过 command= 前缀限制密钥只能执行特定命令,通过 from= 限制来源 IP:

command="/usr/local/bin/backup-only",from="10.0.0.0/8" ssh-ed25519 AAAAC3... user@example.com

这种限制在 OpenSSH 的 authorized_keys 文件中直接生效,不需要修改服务器全局配置。对于不需要 sudo 的普通用户,确认其账号不在 sudoers 或 wheel 组中。

清理无法归属的密钥

对于无法确认归属的密钥,先不要直接删除。把它们的指纹记录下来,然后从 authorized_keys 中移除,观察一段时间(比如 7 到 14 天)是否有登录失败或业务异常。如果确实没有影响,再彻底删除。这个"软删除"过程能避免误删仍然在用的凭据。

轮换:换掉旧钥匙,别把自己锁在门外

密钥轮换的核心目标是:在旧密钥失效之前,确保新密钥已经验证可用。很多人犯的错误是直接删除旧密钥——如果新密钥因为权限问题或配置错误无法登录,你就被锁在服务器外面了。

生成新密钥并部署

为每个用户生成新的密钥对,推荐使用 Ed25519 算法(比 RSA 更安全且性能更好):

ssh-keygen -t ed25519 -a 100 -f ~/.ssh/id_ed25519 -C "user@company-2025"

然后把新公钥追加到目标服务器的 authorized_keys 文件中。注意是追加,不是覆盖:

cat ~/.ssh/id_ed25519.pub | ssh user@server "cat >> ~/.ssh/authorized_keys"

追加完成后,立即用新密钥测试登录。确认能正常登录后,再考虑移除旧密钥。

验证新密钥可用后再清理旧密钥

这是整个轮换流程中最关键的一步。很多事故的发生是因为运维人员在轮换时只更新了控制台的密钥绑定,却没有验证实例内部的实际配置;或者把新旧公钥搞混,删掉了仍然在用的那一把。

验证新密钥的步骤:

  1. 用新私钥登录:ssh -i ~/.ssh/id_ed25519 user@server
  2. 确认登录后执行 whoami 和 sudo -l,验证权限符合预期。
  3. 保持当前登录会话不退出,另开一个终端窗口,用新密钥再登录一次,确认多会话正常。

只有在两个独立会话都能用新密钥登录后,才可以从 authorized_keys 中移除旧公钥。移除后,不要立即关闭当前会话,先确认新会话不受影响,再退出。

处理服务账号的轮换

服务账号的密钥轮换需要更谨慎。CI/CD 系统、备份脚本、监控代理都在用这些密钥,如果轮换时没有同步更新所有使用方,会导致自动化任务大面积失败。

建议的做法是:先在目标服务器上添加新公钥,然后逐个更新使用方配置(比如 Jenkins 的凭据、Ansible 的 vault),每更新一个就验证对应的自动化任务是否正常。全部确认后,再移除旧公钥。如果服务账号数量多,可以分批进行,每批间隔一个业务周期。

紧急禁用:当密钥泄露时,先止血再收尾

假设你在 GitHub 上发现了公司服务器的私钥被公开,或者某个员工的笔记本电脑被盗,需要立即撤销该密钥的访问权限。这时候不需要走完整的轮换流程,先止血。

立即移除授权公钥

最快的处置方式是从所有相关服务器的 authorized_keys 文件中移除对应公钥。如果有配置管理工具(Ansible、SaltStack、Puppet),可以批量执行。手动操作时,用一条命令在多个服务器上执行:

ssh server1 "sed -i '/AAAAC3NzaC1lZDI1NTE5AAAAI.../d' ~/.ssh/authorized_keys"

注意 sed 的匹配模式要精确到公钥的唯一标识部分,避免误删其他密钥。

检查是否有其他后门

移除公钥后,攻击者可能已经通过该密钥植入了其他后门。建议检查:

  • /root/.ssh/authorized_keys 和 /home/*/.ssh/authorized_keys 中是否出现了新的未知公钥
  • ~/.ssh/config 和 /etc/ssh/sshd_config 是否被修改
  • 近期是否有异常登录记录:journalctl -u sshd --since "7 days ago" | grep "Accepted"

确认密钥已失效

移除公钥后,攻击者持有的私钥理论上无法再登录。但你要验证这一点:用该私钥尝试登录,应该被拒绝。如果服务器配置了 AuthorizedKeysCommand 从外部获取公钥(比如从 LDAP 或云厂商拉取),还需要同步更新外部源中的公钥数据。

验证与回滚:每次变更都要能退回去

密钥治理的每一次变更(添加、删除、轮换)都应该有对应的验证和回滚方案,否则一次误操作可能让整个团队失去服务器访问能力。

变更前的备份

在修改任何 authorized_keys 文件之前,先备份:

cp ~/.ssh/authorized_keys ~/.ssh/authorized_keys.bak.$(date +%Y%m%d)

如果使用配置管理工具,确保变更前有版本快照,并且知道如何回滚到上一个版本。

分阶段上线

不要把变更一次性推送到所有服务器。先在测试环境或非核心服务器上验证,确认无误后再推广到预发布和生产环境。对于生产环境,可以按服务器分组逐步推进,每组之间观察一段时间。

保留应急访问通道

在任何轮换或清理操作之前,请确保你至少有一条不依赖被变更密钥的应急访问通道:

  • 云厂商控制台的 VNC/WebShell 登录(不受 SSH 密钥影响)
  • 保留一把应急密钥,存放在安全的地方(比如密码管理器的保险库中),平时不用

如果轮换过程中出现意外(比如新密钥无法登录、服务器配置错误),立即通过应急通道登录,恢复备份的 authorized_keys 文件。

回滚的具体操作

假设你删除了旧密钥后发现新密钥无法登录,回滚步骤是:

  1. 通过云控制台的 VNC 或应急密钥登录服务器。
  2. 从备份文件恢复 authorized_keys:cp ~/.ssh/authorized_keys.bak.20250101 ~/.ssh/authorized_keys
  3. 检查文件权限:chmod 600 ~/.ssh/authorized_keys,确保属主正确。
  4. 用旧密钥测试登录,确认恢复成功。

建立持续治理机制

密钥治理不是一次性项目,而是一个持续的过程。建议建立以下机制:

  • 定期盘点:每季度检查一次所有服务器的 authorized_keys,更新密钥登记表。
  • 离职流程:员工离职时,当天移除其所有密钥,并检查其名下的服务账号密钥是否也需要清理。
  • 日志监控:开启 SSH 登录日志的集中采集,对使用未知密钥的登录行为设置告警。可以定期比对 auth.log 中的登录指纹和登记表,发现未登记的密钥立即调查。
  • 密钥有效期:为密钥设置合理的轮换周期,比如每 6 到 12 个月强制轮换一次。对于高权限密钥(root、sudo 用户),轮换周期应更短。

SSH 密钥治理的本质,是把"谁能登录我的服务器"这个问题从模糊的"大概就那几个人"变成清晰的、可审计的清单。通过盘点、分级、收敛、轮换和验证这套流程,你不仅能降低密钥泄露的风险,还能在每一次人员变动或安全事件中快速定位和处置问题。密钥数量越少、归属越清晰,你的服务器暴露面就越小,应急响应也就越快。

 
枫少@KillBoy
    • 橡皮鸭子
      橡皮鸭子 2

      离职当天清密钥这流程太重要了

    匿名

    发表评论

    匿名网友

    拖动滑块以完成验证