记一次堡垒机上线后运维账号收口不彻底导致生产 MySQL 被误操作的排查实录

堡垒机上线后生产 MySQL 仍可被误操作,根因是运维账号收口不彻底、跳板机白名单遗漏、DB 账号直连未切。

问题背景

公司安全基建升级,计划上线堡垒机(JumpServer)实现「所有运维操作必须走堡垒机、所有服务器 SSH 必须走跳板、DB 账号密码统一托管」三收口。堡垒机上线后,运维团队把「服务器 SSH 登录」和「Web 管理界面」都切到了堡垒机,表面看收口完成。

但在一次生产变更中,DBA 误把「生产库 ALTER TABLE」脚本跑到了「核心业务库」,造成业务中断 40 分钟。事后复盘发现:虽然堡垒机已上线,但「DB 直连账号」和「跳板机白名单」仍存在历史遗留的直连通道,导致「堡垒机只是多了一层、不是唯一通道」。安全团队要求彻查「账号收口不彻底」的全链路。

故障现象

  • 2026-08-30 02:17,DBA 小李在本地用 mysql -h core-db.prod.internal -u app_rw -p 直连生产核心库,执行了 ALTER TABLE order_items ADD COLUMN ext_json JSON
  • 02:19,核心业务接口开始报错「Unknown column ’ext_json’」,订单创建/支付全部失败。
  • 02:21,监控告警「核心库 QPS 骤降 90%」,值班 SRE 发现「堡垒机会话日志里没有这条 ALTER 操作」。
  • 02:25,DBA 意识到跑错库,紧急回滚,但 ext_json 列已写入,需做 pt-online-schema-change 反向变更。
  • 02:40,业务恢复,累计影响订单 1200+ 笔、支付失败 800+ 笔。

关键信号:堡垒机上线后,DB 直连仍可绕过堡垒机,且堡垒机会话审计完全缺失这条高危操作。

排查过程

第一步:确认堡垒机是否真正「收口」

堡垒机(JumpServer)已上线 3 周,SSH 登录必须走堡垒机、Web 管理必须走堡垒机。检查堡垒机「会话录像」和「命令审计」,08-30 02:17 没有任何 mysql 相关会话。

1
2
3
4
5
# 在堡垒机管理后台搜索
user: app_rw
asset: core-db.prod.internal
time: 2026-08-30 02:00-03:00
# 结果:无记录

说明 DBA 不是通过堡垒机连的 DB。

第二步:检查跳板机白名单

跳板机(Bastion Host)是堡垒机的前置层,所有服务器 SSH 必须先登录跳板机,再从跳板机 ssh 到目标服务器。

1
2
3
4
# 检查跳板机 /etc/ssh/sshd_config 的 AllowUsers / Match User
Match User app_rw
    AllowTcpForwarding yes
    PermitOpen any

PermitOpen any 允许 app_rw 从跳板机任意端口转发到任意目标,这意味着 app_rw 可以从本地 ssh -L 3306:core-db.prod.internal:3306 jumpbox 再用本地 3306 端口直连 DB,完全绕过堡垒机。

第三步:检查 DB 账号直连白名单

检查 MySQL mysql.user 表和 information_schema.processlist

1
2
3
SELECT user, host, plugin, authentication_string
FROM mysql.user
WHERE user IN ('app_rw', 'app_ro', 'backup');

结果发现:

1
2
3
4
user     host                    plugin
app_rw   10.8.0.%                mysql_native_password
app_rw   172.16.%.%              mysql_native_password
app_rw   bastion-01.internal     caching_sha2_password

10.8.0.%172.16.%.% 是办公网和开发网段,意味着「只要知道密码,任何开发机/办公 PC 都能直连生产 DB」,完全绕过堡垒机。

第四步:检查历史「直连脚本」与「跳板机配置漂移」

检查 DBA 本地 ~/.my.cnf 和团队 Wiki,发现:

  • 旧脚本 prod-db-connect.sh 仍使用直连方式(mysql -h core-db.prod.internal
  • 跳板机白名单在 2026-07-15 曾因「紧急修复某业务连通性」临时放开 PermitOpen any,事后未收口
  • 堡垒机上线 checklist 里「DB 账号收口」被标记「已完成」,但实际只做了「堡垒机 Web 管理界面托管密码」,没有做「DB 驱动层强制走堡垒机代理」

第五步:确认误操作路径

DBA 小李的本地操作:

1
2
3
# 本地执行
mysql -h core-db.prod.internal -u app_rw -p
# 输入密码后直连成功,执行 ALTER

这条连接没有经过堡垒机,也没有经过跳板机审计,完全「裸奔」。

解决方案

1. 立即止血:禁用直连账号、收紧跳板机白名单

1
2
3
4
# MySQL 紧急禁用直连
RENAME USER 'app_rw'@'10.8.0.%' TO 'app_rw_legacy'@'10.8.0.%';
RENAME USER 'app_rw'@'172.16.%.%' TO 'app_rw_legacy'@'172.16.%.%';
FLUSH PRIVILEGES;

跳板机配置收口:

1
2
3
4
5
# /etc/ssh/sshd_config
Match User app_rw
    AllowTcpForwarding no
    PermitOpen bastion-01.internal:3306
    # 只允许转发到堡垒机代理端口

2. DB 账号全面收口到堡垒机

  • 所有生产 DB 账号(app_rwapp_robackup)只保留 bastion-01.internal 一个 host 白名单
  • 密码统一托管到堡垒机「数据库资产」,运维人员必须通过堡垒机 Web 界面或堡垒机代理端口连接
  • 堡垒机开启「数据库会话录像」和「SQL 审计」

3. 跳板机白名单收口 + 堡垒机前置

  • 跳板机 PermitOpen 只允许转发到堡垒机 IP
  • 所有服务器 SSH 登录必须先登录堡垒机,再从堡垒机 ssh 到目标(跳板机仅作为堡垒机后端,不再暴露给用户)
  • 堡垒机「资产」里所有服务器的「连接方式」统一改为「SSH → 堡垒机」

4. 建立「账号收口」验收门禁

  • 新服务器上线 checklist 必须包含「堡垒机资产录入 + DB 账号收口验证」
  • 每月做一次「直连白名单扫描」(脚本扫描 mysql.user 的非堡垒机 host)
  • 堡垒机「会话缺失告警」:如果服务器上有登录但堡垒机无记录,立即告警

根因分析

根本原因:堡垒机上线 checklist 只做了「形式收口」(Web 管理界面托管密码),没有做「链路收口」(DB 驱动层、跳板机白名单、服务器 SSH 登录全部强制走堡垒机)。

直接原因

  • 跳板机 PermitOpen any 未收口,允许本地端口转发直连 DB
  • MySQL user.host 白名单仍保留办公网/开发网段
  • 堡垒机 checklist 把「DB 账号收口」标记「已完成」,但实际只托管了密码,没有切断直连

管理原因:安全团队和运维团队对「收口」的理解不一致——安全团队认为「必须走堡垒机」,运维团队认为「堡垒机上线了、密码托管了」就是收口,缺少「可验证的收口证据」。

预防措施

  1. 堡垒机 checklist 必须包含「链路验证」:不仅要托管密码,还要验证「直连是否失败」「堡垒机会话是否记录」
  2. 每月做一次「直连白名单扫描」:脚本扫描 mysql.userredis.confmongo.conf 等所有可能直连的配置,输出「非堡垒机 host」清单,纳入安全巡检
  3. 跳板机白名单收口 + 堡垒机前置:跳板机只允许转发到堡垒机,堡垒机作为唯一入口
  4. 建立「会话缺失告警」:服务器 /var/log/auth.log 有登录但堡垒机无记录,立即告警
  5. DBA 工具链升级:所有 DB 连接必须走堡垒机代理(mysql -h bastion-proxy.internal -P 33061),本地 ~/.my.cnf 全部删除或指向代理

总结

堡垒机上线只是「安全收口」的起点,不是终点。真正的收口需要「链路验证 + 白名单收口 + 会话审计 + 门禁验收」四层保障。一次 MySQL 误操作暴露了「形式收口 vs 实质收口」的差距,也推动团队建立了「每月直连白名单扫描 + 会话缺失告警 + 堡垒机 checklist 链路验证」三道防线。安全基建不是「装个设备就完事」,而是「持续验证、持续收口、持续审计」。

使用 Hugo 构建
主题 StackJimmy 设计