堡垒机与SSH集中认证:小团队落地五步

服务器从一台变成三台之后,SSH 管理就开始失控:密钥散落在几个人的笔记本上、离职的人还记得密码、审计日志里只有 IP 分不清是谁、某个跳板机的 root 密码被贴在了聊天记录里。这些问题不是「安全意识」问题,而是缺一套集中认证和审计的结构。本文写的是小团队能落地的方案:从密钥集中下发,到跳板机强制转发,再到会话录制与命令审计,每一步都给可执行的配置。

一、先明确目标:不是「更安全的密码」,而是「可归因」

很多团队加固 SSH 的第一反应是把密码换成密钥,但密钥分散在多台机器上,效果只是「更难管理」。集中认证要解决的是三件事:

  • 身份唯一:一个自然人对应一个可追踪的身份,不共享账号;
  • 路径唯一:所有运维访问必须经过同一台入口,入口之外直接拒绝;
  • 记录完整:每次登录、每条高权限命令都能对应到人。

做到这三点,即使某一台内网机器被攻破,也拿不到横向移动的便利,事后也能回答「是谁在什么时候改了 nginx 配置」。

二、第一步:内网机器只允许从入口登录

最容易被忽略也最有效的一步,是把业务机的 SSH 收窄到「只接受来自跳板机的连接」,而不是所有人可连。

# /etc/ssh/sshd_config (业务机)
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
AllowUsers deploy ops
Match Address 10.10.0.0/24
    AllowUsers *

顺序上要注意:Match 块必须放在文件末尾,块内的指令会覆盖全局设置。更稳妥的做法是用防火墙收口,因为 sshd 配置写错会把自己锁在门外:

# 只允许跳板机访问 22 端口
iptables -A INPUT -p tcp --dport 22 -s 10.10.0.5 -j ACCEPT
iptables -A INPUT -p tcp --dport 22 -j DROP

改防火墙前务必先开一个已登录的会话留着,验证新连接能通再退出。这是所有运维里最容易造成「自己把自己关在门外」的操作。

三、第二步:密钥集中管理,而不是人手一份拷来拷去

小团队不必上完整的堡垒机产品,可以先用「一台跳板 + 一份带注释的 authorized_keys」过渡:

# /home/ops/.ssh/authorized_keys
restrict,command="/usr/local/bin/audit-log.sh ops-zhang" ssh-ed25519 AAAAC3...zhang@laptop
restrict,command="/usr/local/bin/audit-log.sh ops-li"   ssh-ed25519 AAAAC3...li@workstation

restrict 是 OpenSSH 7.2 之后引入的选项,一次性关掉端口转发、agent 转发、pty、X11 等所有扩展功能,再按需用 permitopen 或 pty 放开。它比逐条写 no-* 更不容易漏项。

如果团队超过五个人,或者有人用多台设备,用 SSH 证书(CA)更省事:跳板机上放一份 CA 密钥,为每个人签发一张有效期 30 天的证书,业务机只信任这个 CA。这样加人、减人都不需要逐台改 authorized_keys:

# 在 CA 主机上签发
ssh-keygen -s /etc/ssh/ca_user -I "zhang@2026Q4" -n deploy,ops -V +30d id_zhang.pub
# 业务机 sshd_config
TrustedUserCAKeys /etc/ssh/ca_user.pub

证书方式的关键收益是「过期即失效」。离职或设备丢失时,不用去翻哪台机器上还有他的公钥,等证书过期即可,需要立刻生效则用 RevokedKeys 撤销。

四、第三步:跳板机强制转发,禁止直连

只有「业务机只信跳板」还不够,因为跳板机上的人可以自由登录业务机。更彻底的做法是在跳板机上把登录用户限制为只能转发,不能拿 shell:

# /etc/ssh/sshd_config (跳板机)
Match User ops
    PermitTTY no
    X11Forwarding no
    AllowAgentForwarding no
    AllowTcpForwarding yes
    ForceCommand /bin/echo "jump host: use -J or ProxyJump"

PermitTTY no 是关键:没有伪终端,用户就无法在跳板机上执行交互命令,但 ssh -J 的 TCP 转发依然可用。这样跳板机就退化成一个纯粹的网络通路,被攻破也拿不到 shell。

客户端侧统一配置,避免每个人自己拼命令:

# ~/.ssh/config
Host web-*
    ProxyJump ops@10.10.0.5
    User deploy
    IdentityFile ~/.ssh/id_ed25519
    ServerAliveInterval 30

五、第四步:会话录制与命令审计

审计的目标不是「监视每个人」,而是出事时能复盘。三条成本递增的路线:

  • 只记命令:配置 SyslogFacility AUTH + auditd 的 execve 规则,记录每个进程的完整参数。开销极低,但对交互式编辑器里的操作无效(在 vim 里改了什么看不到)。
  • 记录完整终端输出:用 script 命令或 tlog 包装登录 shell,把所有输出落成文件。可读性最好,能还原「屏幕上到底显示了什么」。
  • 强制录制:sshd 的 ForceCommand 指向一个记录器,用户无法绕过。

最小可用的命令审计规则(auditd):

# /etc/audit/rules.d/ops.rules
-a always,exit -F arch=b64 -S execve -F auid>=1000 -F auid!=4294967295 -k ops_cmd
-w /etc/nginx/ -p wa -k nginx_conf
-w /etc/ssh/sshd_config -p wa -k ssh_conf

查询时用 ausearch -k ops_cmd -ts recent 拉出最近的命令。注意 auid!=4294967295 这一条,它排除了无法归属到具体用户的进程(比如某些系统服务),否则日志会被大量噪声淹没。

审计日志本身要防篡改:写入远端 syslog(rsyslog 的 @@ 转发),或者定期 auditctl -l 对比规则是否被改动。日志只留本机的审计,等于把证据交给被审计的人保管。

六、第五步:给运维操作加一道「变更留痕」

集中认证解决了「谁登录了」,但不能回答「改了什么、为什么改」。小团队的最低要求是把配置变更纳入版本管理:

# /etc/nginx 目录直接做成 git 仓库
cd /etc && git init nginx && cd nginx
printf '*.log\ntmp/\n' > .gitignore
git add -A && git commit -m "baseline: $(date +%F)"

之后每次改动前 git commit -m "原因",出问题可以 git diff 看改了什么、git checkout 秒级回滚。成本几乎为零,但比任何审计工具都实用 —— 因为复盘时最想知道的「改之前是什么样」,它直接给出答案。

七、落地顺序与验收

不要一次全改。推荐顺序:先做防火墙收口(业务机只允许跳板访问),再做密钥集中与证书(消除共享账号),然后跳板机禁 shell,最后上审计与录制。每一步都可独立回滚,且每一步都能立刻降低风险。

验收用四条命令就够了:

  • 从办公网直连业务机,应超时(ssh -o ConnectTimeout=5 web-01);
  • 经跳板机登录,应成功,且 who 里显示的用户名对应真实姓名;
  • 在跳板机上执行 ls,应被拒绝(PermitTTY no 生效);
  • ausearch -k ops_cmd -ts today | tail 能看到刚才执行的命令。

八、常见踩坑与排障顺序

这套结构上线后,最容易出问题的不是配置本身,而是「改了一半」造成的半通不通状态。整理六个实际踩过的坑:

  • 把 Match 块写在全局指令之前:sshd 对 Match 的处理是「从该行开始到下一个 Match 或文件末尾」,写在前面会让后面的全局项覆盖它,表现是「配置里明明有,就是不生效」。用 sshd -T -C user=ops,addr=10.10.0.9 输出「该用户在该来源下实际生效的配置」,比读文件可靠得多。
  • 改了 sshd_config 没重启,误以为生效:systemctl reload sshd 会重新读取配置但保留现有连接;新配置写错时 reload 会失败并保留旧配置,而 restart 可能直接让服务起不来。永远先 sshd -t 测语法,再 reload。
  • 防火墙规则写成「加了 DROP 但没加 ACCEPT 顺序」:iptables 是按顺序匹配的,DROP 写在 ACCEPT 前面会把自己也拦掉。用 iptables -L INPUT -n --line-numbers 确认顺序,并优先用 -I 插入到顶部。
  • 证书有效期设太长:一年期的证书等于把「离职即失效」的收益还回去了。30 天是常见折中,短到需要定期续签但不至于天天打扰。
  • agent 转发开着:AllowAgentForwarding yes 时,跳板机上的 root 可以借用你的 agent 登录任何你被授权的机器。前面配置里的 no 不是可选项。
  • auditd 规则没持久化:auditctl -a 加的规则重启后消失,必须写进 /etc/audit/rules.d/ 并用 augenrules --load 加载,重启后 auditctl -l 复核。

排障顺序建议固定为「客户端能否连通 → 跳板机是否允许转发 → 业务机是否接受该来源 → 用户是否被授权 → 命令审计是否记录」。上一层不通就不要查下一层,这样五分钟内能定位到具体哪一环。

九、权限分层:把「能登录」和「能改配置」分开

集中认证之后,下一个问题往往是「所有人都能 sudo」。小团队不必上完整的 RBAC,用 sudoers 分三档就够:

# /etc/sudoers.d/ops
Cmnd_Alias READONLY = /bin/systemctl status *, /usr/bin/journalctl, /usr/bin/tail
Cmnd_Alias RELOAD   = /bin/systemctl reload nginx, /usr/bin/nginx -t
Cmnd_Alias FULL     = ALL
ops-zhang ALL=(root) NOPASSWD: READONLY, RELOAD
ops-li    ALL=(root) NOPASSWD: READONLY
%wheel    ALL=(root) FULL

注意 systemctl 的路径要写成绝对路径且不要用通配符放开 /bin/systemctl *,否则等于给了 root(可以 systemctl 启动任意单元)。改完用 sudo -l -U ops-li 复核实际权限,这一步能查出 90% 的配置错误。

更细一层可以用 sudo 的 NOEXEC 或 sudoedit 限制「只能编辑指定文件」,但对小团队来说,把「只读」「能 reload」「能全权」分清楚,已经能挡住绝大多数误操作。

十、小结

集中认证与审计的价值不在「更复杂的登录流程」,而在把「谁、从哪、做了什么」变成可查询的事实。对小团队来说,一台跳板机 + SSH 证书 + 防火墙收口 + auditd 命令审计 + 配置 git 化,总工作量不超过半天,却能把最容易出事的运维环节从「靠自觉」变成「有结构」。下一步想再提升,才需要考虑专门的堡垒机产品 —— 但在那之前,这五步已经覆盖了绝大多数真实风险。

Last modification:October 31st, 2026 at 07:23 pm

Leave a Comment