ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

MongoDB审计日志配置指南:从合规要求到实战落地

MongoDB审计日志配置指南:从合规要求到实战落地 作为一个在数据库运维和系统安全这条路上摸爬滚打了不少年的人我越来越发现一个道理数据库的安全配置往往是“多做一步”和“少做一步”的差别。就拿 MongoDB 来说很多人把它当成一个“能存数据就行”的 NoSQL 数据库装完、配好副本集、写完索引就觉得万事大吉。直到等保测评、内部审计或者客户安全审查找上门才发现自己连“谁在什么时候删了集合”都查不出来那一刻真的会冒冷汗。所以今天这篇我想认真聊聊MongoDB 审计日志配置。这不仅仅是给你贴一段配置命令而是从“为什么要做”到“具体怎么做”再到“做完之后怎么用”把审计日志这一整套东西掰开揉碎讲清楚。无论你是刚接触 MongoDB 的运维新手还是被合规检查搞得焦头烂额的老手这篇文章应该都能给你一些参考。1. 审计日志到底是什么以及它为什么总被合规性挂在嘴边1.1 一套可追溯的“数据库监控录像”审计日志Audit Log在数据库领域里的地位可以理解成一套“监控录像系统”。监控录像记录的是谁在什么时间出现在哪里、做了什么动作而 MongoDB 审计日志记录的则是哪个用户、从哪个 IP、在什么时间、执行了什么数据库操作、操作的结果是成功还是失败。这套“录像”不是你想开就开、想关就关的摆设它通常是一个数据库系统在面临安全合规审查时最直接、最关键的技术证据。为什么这么说因为无论是国内等保测评里的“安全审计”条款还是金融、医疗等行业对数据库访问的“可追溯性”要求核心逻辑都是一致的每一个对关键数据的敏感操作都必须留下痕迹并且这个痕迹不能被普通用户随意篡改或删除。我在实际项目里遇到过一种非常典型的场景业务方反馈某张核心表的数据被批量修改了但所有人都说“不是我干的”开发环境、测试环境、生产环境的账号混在一起谁也说不清。这时候如果没有审计日志这个锅就只能大家一起背拿不出证据。而如果配置了审计日志拉出来一查哪个账号、哪条 IP、执行了什么 update 语句、影响了多少行清清楚楚问题定位就是分分钟的事。1.2 合规性要求里的“常驻嘉宾”等保、SOX 与 GDPR聊到合规性就不得不提几个最常见的“要求来源”。国内的网络安全等级保护等保 2.0在三级及以上信息系统中对数据库审计提出了明确要求核心就是“应启用安全审计功能审计覆盖到每个用户对重要的用户行为和重要安全事件进行审计”。什么意思就是说你不能只记“谁登录了”还得记“登录之后做了什么”、记“删了什么表”、记“改了什么权限”并且日志要留存足够长时间一般是 6 个月以上具体看测评要求。海外市场这块更严格。比如美国的 SOX萨班斯-奥克斯利法案要求上市公司必须保留影响财务数据的操作记录GDPR欧盟通用数据保护条例则要求对涉及个人隐私数据的访问有完整的审计链条。如果你的业务要出海或者正在做海外 SaaS那审计日志不是“可选项”而是“准入门槛”。1.3 审计日志不是“一开了之”的免责金牌这里我想泼一盆冷水很多团队以为装个企业版、打开审计开关就算合规了。但实际上合规审计员去看你的审计系统时会重点检查三个层面是否覆盖了所有关键操作比如只开了 DDL结构变更审计却漏掉了 DML数据增删改那数据被改了照样查不出来。日志是否真实有效日志有没有被篡改、被普通管理员随意清空日志是否可读可用存了几百 GB 的日志文件却没人看得懂、没有归档策略等于白存。所以配置审计日志只是“起步动作”后面的日志管理、权限隔离、定期检查才是真正体现安全水位的地方。这条主线我们接下来会贯穿全文。2. 配置审计日志前的“必修课”版本、权限与存储规划2.1 先泼冷水社区版不支持别在版本上踩坑这是我在各种技术群里看到问得最多的问题“为什么我按文档配置了 auditLog启动时却报参数不识别”答案非常简单MongoDB 的审计日志功能是 Enterprise Server企业版专属功能。社区版Community Server从架构层面就没编译进这个模块哪怕你用--auditDestination参数硬启动服务也会直接报错退出。所以第一步请先确认你的 MongoDB 版本。如果你用的是社区版有两条路可以走升级到企业版这是最省心、最正统的方案不仅解决审计日志还能一并使用 LDAP 认证、字段级加密、静态加密等安全特性。继续用社区版走代码层面的“伪审计”比如通过oplog结合定时任务或者用change streams监听敏感集合的变化把变化记录到另一个库。这种方案能解决一部分追溯需求但性能开销大、逻辑复杂而且无法记录“查询”这类不产生数据变更的操作只能算临时替代品。如果你只是在本地测试、想先体验一下审计功能可以注册 MongoDB Atlas 的免费 M10 集群或者是官方评估版镜像跑一个容器。但生产环境我还是建议直接上企业版省得后续被合规卡脖子。2.2 谁有资格开审计权限模型有讲究审计日志的配置和查看本质上是高权限操作。在 MongoDB 里和审计相关的角色主要有两个root角色超级管理员可以操作任何系统配置。__system角色内部系统角色通常只有root用户在授权后才能拥有。在开启审计功能之前你至少需要一个拥有root权限的管理员账号来执行相关命令。这里有个安全习惯我特别想强调请不要在业务代码里用 root 账号连接数据库。业务账号应该只赋最小必要权限比如readWrite 指定库而审计管理、权限分配这类操作应该由 DBA 通过独立的堡垒机 独立账号完成。为什么强调这点因为审计系统最忌讳“自己审自己”如果业务账号本身就具备清除审计日志的权限那这份日志的可信度就打折扣了。正确做法是把“业务写入”和“审计管理”两条权限线彻底分开。我用一个简表来展示常见的权限划分思路模块建议角色权限范围备注业务读写appUser指定库的 readWrite不授予 anyAdmin 权限数据结构管理dbAdmin指定库的 schema 变更由研发/DBA 按需分配审计运维root或其他自定义角色查看审计日志、管理审计配置仅限运维堡垒机双人复核监控巡检monitorclusterMonitor 等只读权限配合 Prometheus 等监控系统使用2.3 日志存哪syslog、console 与 file 三选一MongoDB 企业版审计日志支持三种输出目标--auditDestination我分别说下它们的适配场景syslog输出到系统日志服务。好处是日志可以直接走集中式日志采集比如 rsyslog → Kafka → ES 的链路坏处是设备性能差、格式转换有损耗如果系统日志空间管理不当还可能丢掉审计内容。console输出到标准输出。适合容器化部署K8s 里 pod 的标准输出会被自动收集到日志平台但本地调试时比较难精确提取独立审计文件。file输出到磁盘文件。这是最传统也最可控的方案你可以把审计日志写到独立的文件系统上通过 logrotate 或脚本做轮转、归档、压缩。也是大多数传统运维团队最容易上手的方式。个人建议如果公司有成熟的日志平台ELK、Loki等优先用 syslog 或者 file 日志采集 agent 的组合如果只是单机环境直接用 file 输出简单可靠。磁盘空间这块也要提前规划。审计日志的增长速度有多夸张我用一个真实的场景给你估算一个日均请求量在千万级左右的业务库开启全量审计后单日审计日志轻松超过 20GB。这不是危言耸听审计日志的全量记录是极其吃 IO 和磁盘的。所以后面会专门讲如何通过auditFilter做过滤以及如何规划日志轮转策略。3. 核心配置实操从 YAML 文件到运行时动态调整3.1 安装与基础启动先让服务跑起来为确保我们下面的配置可复现我先假设你已经在 Linux 服务器上装好了 MongoDB Enterprise 6.x本次实践基于 6.0.3 版本验证。如果你是老版本4.x 或 5.x配置语法基本一致但建议升级后再按本文配置。在 MongoDB 中配置审计日志可以在启动时通过 mongod 参数指定也可以在运行时动态调整。生产环境我建议两者结合启动参数保证服务一启动就有审计能力运行命令便于灰度调整过滤范围两不耽误。我们先用最简单的 YAML 配置文件方式启动# /etc/mongod.conf storage: dbPath: /var/lib/mongo systemLog: destination: file logAppend: true path: /var/log/mongodb/mongod.log net: port: 27017 bindIp: 0.0.0.0 processManagement: fork: true pidFilePath: /var/run/mongod.pid # 开启审计日志 auditLog: destination: file format: JSON path: /var/log/mongodb/auditLog.json filter: { atype: { $in: [authenticate, createCollection, dropCollection, createUser, dropUser] } }这里有几个字段需要解释一下destination: file指定审计日志输出到文件。format: JSON审计日志的存储格式支持JSON和BSON。JSON 可读性好、适合日志采集BSON 体积更小、写性能更好但需要用bsondump工具查看。对于绝大多数场景选 JSON 就够了。filter这个很关键它是一个 JSON 表达式相当于对记录的事件做了一道“白名单”过滤。比如我上面写的这个 filter就只记录了登录认证authenticate、集合创建/删除、用户创建/删除这几类操作。配置完成后启动 MongoDBsudo systemctl start mongod然后检查审计日志是否已经在写入tail -f /var/log/mongodb/auditLog.json正常情况下当你执行任意一条数据库操作比如登录、查询时这个文件就会追加对应的审计条目。3.2 手把手配置一个“生产级”的审计过滤器上面的基础配置只是让你“有审计日志”而已。到了生产环境如果只是把 filter 省略或写一个空条件那就意味着 MongoDB 默认记录所有数据库操作这会带来两个直接后果一是数据量大到不可用二是写入性能急剧下降。所以设计一个优秀的auditFilter是生产级审计的核心难点。我来说说我的实践思路以及附件里的关键代码。先看一段我目前在生产环境使用的过滤器// 审计过滤器记录敏感操作 所有权限变更 认证失败事件 { atype: { $in: [ authenticate, // 认证成功/失败 createUser, // 创建用户 dropUser, // 删除用户 grantRolesToUser, // 给用户授予角色 revokeRolesFromUser, // 撤销用户角色 createCollection, // 创建集合 dropCollection, // 删除集合 createIndex, // 创建索引 dropIndex, // 删除索引 killCursors, // 关闭游标用于安全排查 shutdown // 关库操作 ] }, // 参数兜底如果操作是更新文档同时记录一下集合名 $or: [ { param.command: { $in: [update, delete, insert] } }, { param.command: findAndModify } ] }你可能会问既然上面已经用atype白名单筛掉了一堆事件为什么还要再加一个$or去匹配具体的param.command这是我在实际使用中踩过坑后的一个经验MongoDB 审计日志对于 DML 操作很多情况下不会在atype层面区分“更新”和“删除”而是把它们统一归类为write或command。如果只设置atype白名单容易漏掉一些敏感命令。更稳健的思路是先用atype做粗粒度筛选记录关键系统事件再用param.command做细粒度补充记录业务侧的关键增删改。这里我也整理了一个表格把常见审计事件类型和推荐配置的关系列明白事件类型atype 关键字是否默认开启推荐策略用户登录/登出authenticate否建议开启用户管理createUser/dropUser/updateUser否核心需求配合权限审计开启角色管理grantRolesToUser/revokeRolesFromUser否核心需求必开结构变更createCollection/dropCollection/createIndex/dropIndex否建议开启捕获非法删改字段数据写操作insert/update/delete/command否按业务敏感度选择数据读操作query否通常关闭量大且价值低系统控制shutdown/replSetReconfig否建议开启审计配置变更setAuditConfig否建议开启防止关闭审计后无从追溯3.3 运行时动态调整审计规则边跑边调不停机很多人在第一次配置完审计日志后会遇到一个尴尬上线后审计日志量暴涨磁盘每天都在报警但你又不想重启数据库。这时候就该用 MongoDB 官方提供的运行时审计配置能力了。在企业版中你可以通过db.setAuditConfig()方法在不用重启进程的情况下动态修改审计参数。比如我想临时把“数据读操作query”也纳入审计可以这样操作db.adminCommand({ setAuditConfig: 1, filter: { atype: { $in: [authenticate, query] } } })如果过了三个月发现读操作日志量太大再把query去掉即可db.adminCommand({ setAuditConfig: 1, filter: { atype: { $in: [authenticate, createUser, dropUser] } } })这里要特别强调一个经验教训setAuditConfig执行后虽然能动态影响后续日志记录但它只会影响“之后”产生的新事件不会追溯“之前”已经写入的日志文件。另外setAuditConfig本身也需要被审计否则可能出现“管理员偷偷关掉审计再操作、再开审计”的作弊链条。好在 MongoDB 默认就会将setAuditConfig记录为atype: setAuditConfig你只要在 filter 里加上这个类型就行。养成习惯别有侥幸心理。3.4 三个动作验证配置是否真的生效配置完毕之后不能只是看一眼文件在增长就觉得大功告成。我通常会用三个动作做一次快速验证确保审计系统真正可用验证一认证审计用正确的账号、密码登录一次再用错误的密码登录一次然后分别检查审计日志里是否出现对应的authenticate记录并且能从result字段中区分成功与失败。mongo -u appUser -p wrongPassword --authenticationDatabase admin然后去auditLog.json里找到类似这样的记录{ atype: authenticate, result: 5, param: { user: appUser, db: admin } }result值为 0 表示成功非 0 表示失败如 5 表示认证失败这个字段是排查暴力破解的重要指标。验证二DDL 审计在测试库里创建一个临时集合再删除它确认日志中出现了createCollection和dropCollection记录并且能匹配到执行人的用户名和客户端 IP。db.test_audit.insertOne({a: 1}) db.test_audit.drop()然后提取关键信息{ atype: dropCollection, param: { ns: test_db.test_audit }, users: [{ user: appUser, db: admin }] }验证三权限变更审计创建一个临时测试账号并授予它角色再删除确认createUser、grantRolesToUser、dropUser是否都正常留痕。db.adminCommand({ createUser: temp_user, pwd: temp_pass, roles: [readWrite] }) db.adminCommand({ dropUser: temp_user })这三个验证顺序走一遍基本能验证审计日志的功能完整性和配置准确性。注意验证过程尽量放在测试环境生产环境的验证动作要提前申请窗口避免影响业务。4. 审计日志字段拆解从一条 JSON 看到整个安全事件链4.1 一条完整审计记录有哪几层读懂“人地时何事果”我们拿到一条审计日志时如果只是一味地把它当成字符串那就浪费了它真正的价值。MongoDB 审计日志的 JSON 结构非常有规律任何一条完整记录基本都包含以下字段我挑几条高频的字段说明atype审计事件类型。比如authenticate、createCollection、dropUser等。ts事件发生的时间戳。格式是嵌入式文档包含日期和纳秒值{$date: 2024-12-06T09:21:56.12308:00}处理时注意时区转换。local和remote本端地址和对端 IP。remote字段对定位“哪个客户端在操作”至关重要。remote里的ip可能是移动办公 IP、跳板机 IP甚至是内网 IP结合用户登录记录就能画出一条访问链路。users执行该操作的用户列表格式是数组每项包含user和db两个属性。注意如果操作是object例如系统内部操作该字段可能为空。param事件的具体参数。这一块不同事件类型差异极大比如createCollection事件里会有ns库表名update事件里会有query和update语句可以精确看到改了什么字段。result操作结果。0 表示成功非 0 表示失败。失败原因会体现在错误码里比如 13 表示权限不足18 表示认证失败26 表示命名空间不存在。我把一个典型的createUser审计记录拿来做示范{ atype: createUser, ts: { $date: 2024-12-06T09:25:31.12308:00 }, local: { ip: 192.168.10.20, port: 27017 }, remote: { ip: 10.0.0.88, port: 52301 }, users: [{ user: root, db: admin }], param: { user: temp_user, db: admin, roles: [{ role: readWrite, db: test_db }] }, result: 0 }这条日志能告诉我们的信息非常丰富是管理员root在某台内网 IP10.0.0.88上操作。操作目标是新建一个temp_user账号并且授予了它在test_db里的readWrite权限。操作时间是 2024年12月6日上午9点25分31秒。操作结果是成功result: 0。如果有一天安全组发现这个账号有异常顺着这条记录就能追问谁创建的什么目的角色是否合理这就是审计链路闭环的意义。4.2 为什么“认证失败”事件在合规审计中很重要很多团队的审计 filter 只关心 DDL 和 DML却漏掉了authenticate事件。在我看来这是审计配置里最大的遗憾之一。认证失败事件authenticate且result ! 0是探测数据库口令攻击最直接的信号。如果某段时间内某个 IP 对 MongoDB 的认证失败次数异常升高基本可以断言对方在尝试暴力破解或者撞库。这时候如果你因为“日志太多”而把 authenticate 过滤掉了就等于主动放弃了发现安全攻击的机会窗口。我建议在审计过滤器里至少保留authenticate事件同时对认证失败做一次告警联动。告警的实现方式也很简单写一个脚本间隔 5 分钟扫描审计日志统计失败次数超过阈值就触发告警。后面第七节我会给一个 Python 脚本示例直接抄作业即可。4.3 字段映射如何把审计日志变成合规报表合规审查员手里的“审计报表”通常不会直接看原始 JSON而是看你提供的结构化报告。所以在配置审计日志时你就要有意识地帮后面的人做“翻译”。常见的字段映射关系如下审计原始字段合规报表字段说明ts操作时间精确到秒级别且必须包含时区remote.ip来源 IP若通过堡垒机则记录真实源 IP 需自定义users.user/users.db操作账号/来源库用户账号与认证库atype操作类型对应“增删改查、权限变更、系统变更”等分类param.ns涉及库表注意区分db.collection的格式result操作结果成功/失败需与审计标准对应param.command具体命令大字段需要从嵌套 JSON 中提取我个人习惯在审计日志落盘后通过 Fluentd 或 Logstash 把 JSON 转成一行一个 JSON 对象再清洗成宽表放 ES 里做分析。这样出报表的时候SQL 都不用写Kibana 上拖拖拽拽就出来了。后面第六节会展开讲这套链路。5. 日志轮转、存储与性能踩坑实录5.1 疯狂成长的日志文件轮转是刚需不是可选项我之前聊到过全量审计日志一天 20GB 并不是梦。如果磁盘只有 100GB不到一周就会报警。而且 MongoDB 本身对审计日志的轮转支持非常“原始”它会把事件追加到当前文件直到你主动执行db.adminCommand({ rotateAuditLog: 1 })或者重启 mongod。正因如此生产环境一定要做操作系统的日志轮转和MongoDB 应用的日志轮转两层配合操作系统层配置 logrotate每天或按文件大小切割。MongoDB 层借助SIGUSR2信号或者rotateAuditLog命令让 mongod 重新打开一个日志文件。一个常见的 logrotate 配置模板是这样# /etc/logrotate.d/mongodb-audit /var/log/mongodb/auditLog.json { daily rotate 14 compress delaycompress missingok notifempty copytruncate su mongod mongod create 0640 mongod mongod postrotate /usr/bin/mongosh -u dbaUser -p xxx --authenticationDatabase admin --eval db.adminCommand({ rotateAuditLog: 1 }) endscript }这里有个小细节copytruncate参数非常关键因为 mongod 持有文件句柄简单的 rename 会导致 mongod 还在往旧文件已经被改名里写日志新文件永远为空。用了copytruncate后logrotate 会先复制原文件为快照然后清空原文件这样 mongod 不用重启也能继续写到同一个路径下虽然极端情况下会丢少量字节但审计场景下可以接受。5.2 审计日志对性能的影响真的有传说中那么夸张吗先说结论审计日志确实有性能开销但合理配置后影响可控。我做过一组压测对比完全关闭审计TPS 约 3.5 万。开启全量审计filter为空TPS 直接掉到约 1.2 万下降幅度超过 65%IO 和 CPU 飙升。开启精选审计只记录authenticate DDL 权限变更TPS 约 3.2 万下降了不到 10%基本可接受。为什么会有这么大的差异因为 MongoDB 审计日志底层是同步写盘的除非你使用异步系统调用每产生一条记录都要经历一次序列化、写入缓冲、落盘的过程。如果 filter 为空意味着每一次查询、每一次写操作都要记录这个量级接近业务操作量的数倍IO 压力自然巨大。所以合理做法是业务核心库只审计“变更类”操作不审计“查询类”操作。查询操作信息密度低且并发量大往往最容易拖垮性能。尽量选用BSON格式而不是JSON。BSON 在序列化和磁盘占用上都比 JSON 更省对性能更友好。但要配bsondump做查看和转储。将审计日志目录放在独立磁盘最好是 SSD避免和业务数据盘争抢 IO。5.3 磁盘写满导致的“宕机”隐患一定要做容量规划这是很多新手忽略的一个坑mongod 在审计日志无法写入时会拒绝继续服务这与普通日志不同普通日志写不进去通常只是丢日志。想象一下这个场景磁盘快满了审计日志写不进去MongoDB 为了防止“该审计的没审计”直接拒绝对外提供服务导致业务全挂。听起来很滑稽但它真实发生在不少生产事故里。解决办法其实也很简单审计日志所在文件系统要设置独立的容量监控告警线建议设在 70%。日志轮转周期不要单独依赖 logrotatemongod 层也要定期巡检文件大小。考虑把审计日志输出到集中式日志平台本地只留小时级的滚动文件防止本地占满。如果你用容器化部署K8s建议直接把审计日志输出到console让 Pod 的 stdout 被日志采集器收走本地不落盘从根本上规避磁盘写满问题。不过要注意K8s 的默认 stdout 日志轮转策略也是需要额外配置的不能掉以轻心。6. 常见问题与排查技巧实录6.1 认证成功却查不到 createIndex 记录先查 filter 语法有次同事跑来问我他们已经在审计日志里看到了业务账号登录记录但怎么都查不到索引创建的记录。我让他把当前的 filter 拉出来看发现他写的是{ atype: createIndex }而 MongoDB 的审计文档里创建索引的类型其实是createIndexes注意最后多了个 s。他写成了单数自然匹配不上。这类坑非常典型排查方向也很简单先确认事件类型名称再检查语法。另外需要注意filter中字符串用双引号还是单引号都不影响最终解析但 YAML 文件里嵌套过滤器时外层必须用单引号包起来以避免 YAML 渲染问题。6.2 日志文件巨大grep 半天才能定位一条记录很多团队熬到审计日志超过 10GB 后才开始后悔没在配审计时就把分析平台准备好。文件一大了grep、cat根本没法用。这时候有两个实用技巧用jq做流式过滤。比如我只想找认证失败的事件cat auditLog.json | jq -c select(.atypeauthenticate and .result ! 0)按天归档 索引。我习惯每天晚上把前一天的审计日志用gzip压缩然后按日期命名归档例如auditLog-20241206.json.gz。查询某天的历史时直接zcat即可速度比大文件 grep 快得多。再往后建议把日志传输到集中式平台如 ES、ClickHouse 或云上日志服务配合可视化查询效率和体验都是单机文件比不了的。6.3 用户通过堡垒机连库审计里的 IP 全是跳板机怎么解决这是很多大公司的通病应用绕不过堡垒机导致审计日志里的remote.ip永远都是堡垒机地址真正“人”的 IP 被吞了。审计追溯时只能看到跳板机连接而定位不到具体责任人。解决方案有几条路按推荐程度排列通过应用层传递用户信息。MongoDB 支持在连接字符串里设置appName把它设置为业务员工的工号或账号再结合审计日志里的param.appName字段就能定位到具体代码/用户。堡垒机侧同步日志。堡垒机系统一般都有登录审计可以和 MongoDB 审计日志按时间点做“关联碰撞”。虽然行得通但是操作繁琐追溯链路长。使用 MongoDB Enterprise 的 LDAP 集成。如果企业用 LDAP/AD 做统一认证MongoDB 审计日志里可以直接记录到 LDAP 用户名而不是简单地显示账号名这样配合堡垒机“谁申请了这个账号”一起看基本能闭环。6.4 日志格式选 BSON 后怎么快速查看内容如果你选了BSON格式为了压测数据量那么直接查看时需要用到 MongoDB 自带的bsondump工具。基本用法bsondump /var/log/mongodb/auditLog.bson | head -n 20bsondump会把二进制 BSON 转成一行一行的 JSON 输出方便你继续管道处理。要注意的是bsondump所在的工具包在 MongoDB Enterprise 安装目录的bin下如果PATH里没配好就直接用绝对路径执行。7. 一个可直接抄作业的审计日志分析脚本7.1 五分钟实现“失败认证实时告警”前面反复提到认证失败是安全攻击的重要信号。这里我直接给一个短小精悍的 Python 脚本它可以每 5 分钟扫描一次审计 JSON 文件统计最近 5 分钟内的认证失败次数超过阈值就打印告警你也可以改成飞书/钉钉/邮件通知。我实际项目里就是这么改的非常简单。#!/usr/bin/env python3 # -*- coding: utf-8 -*- MongoDB 审计日志认证失败检测脚本 每隔 300 秒执行一次统计最近 5 分钟失败认证数量。 依赖MongoDB Enterprise 审计日志为 JSON 格式 import json import time import subprocess from datetime import datetime, timedelta AUDIT_LOG_PATH /var/log/mongodb/auditLog.json THRESHOLD 10 # 5 分钟内失败超过 10 次则告警 WINDOW_SECONDS 300 def parse_log_line(line: str): try: return json.loads(line) except Exception: return None def main(): # 读取文件末尾 N 行。生产环境建议结合 jq、tailf 等方式持续消费 tail subprocess.check_output([tail, -n, 100000, AUDIT_LOG_PATH]).decode(utf-8) lines tail.splitlines() now datetime.now() since now - timedelta(secondsWINDOW_SECONDS) fail_count 0 suspicious_ip {} for line in lines: obj parse_log_line(line) if not obj: continue # 只关心认证事件 if obj.get(atype) ! authenticate: continue # 解析时间戳 ts_str obj[ts][$date] try: # 格式形如: 2024-12-06T09:21:56.12308:00 event_time datetime.fromisoformat(ts_str) except Exception: continue if event_time since: continue if obj.get(result, 0) ! 0: fail_count 1 remote obj.get(remote, {}).get(ip, unknown) suspicious_ip[remote] suspicious_ip.get(remote, 0) 1 if fail_count THRESHOLD: print(f[ALERT] 最近 {WINDOW_SECONDS // 60} 分钟认证失败次数: {fail_count}) for ip, cnt in suspicious_ip.items(): print(f 来源 IP: {ip}失败次数: {cnt}) # 在这里接入你的告警渠道如飞书机器人、Zabbix 等 else: print([OK] 认证失败次数正常。) if __name__ __main__: main()这个脚本的核心思路是不读整个大文件而是用tail只取末尾附近的行避免每次加载几十 GB 的文件。同时用时间窗口过滤确保只看最近 5 分钟的数据。你可以用 crontab 调度它*/5 * * * * /usr/local/bin/audit_auth_monitor.py如果觉得 10 次 / 5分钟太敏感可以根据业务规模调高阈值。但无论如何这个检测能力一定要有因为它大概率是你发现数据库爆破攻击的第一道防线。7.2 用同一份日志定位“谁动了我的数据”除了告警审计日志最常见的用法就是事后追溯。假设业务方反馈test_db.user集合里有一批数据被删了时间大概在昨天凌晨你要想快速定位可以把筛选条件写得具体一些cat auditLog.json | jq -c select( .param.ns test_db.user and (.atype delete or .atype dropCollection or .atype drop) )通过这个查询你能看到所有匹配的删除/清理操作及其关联的用户和 IP再结合时间范围内的网络登录记录基本就能锁定责任人。要注意删除数据对应的atype在不同版本里写法可能不同常见的是delete或更新的名称建议根据实际日志先小范围 grep 看看有哪些类型再扩展查询。8. 日志与合规报表“最后一公里”从原始 JSON 到可读的审计报告8.1 一份合规审计员愿意看的报告长什么样做合规整改时安全团队最怕“交付一个日志文件夹让审计员自己翻”。一份过关的审计报告至少要具备以下模块审计范围说明哪些集群、哪些库、哪些操作被覆盖。统计摘要通过表格展示一周内认证失败数、权限变更数、DDL 操作数、数据变更操作数等关键指标。可疑行为分析用高亮或单独章节说明超过阈值的异常行为如同一 IP 多次失败。原始记录索引所有日志有统一 ID 或文件路径以便审计员随机抽验回溯。这个环节不用开发一套完整平台绝大部分中小型团队用现成工具就能搞定。我自己的方案是Fluentd 采集审计 JSON → Kafka/Redis 缓冲 → Python 脚本做清洗和统计 → 输出到 ES/Kibana 做可视化。如果不想上 ES直接用 ClickHouse 存宽表也是不错的选择查询效率极高。8.2 一个用 Python 生成周报的示例片段这里给一段生成“审计周报”的简化代码核心是按 atype 和 result 做聚合统计然后导出到 Markdown 表格#!/usr/bin/env python3 从 MongoDB 审计日志JSON生成简单周报统计 import json import glob from collections import Counter, defaultdict files glob.glob(/var/log/mongodb/auditLog*.json) stats Counter() failed_ips defaultdict(int) for f in files: with open(f, r, encodingutf-8) as fp: for line in fp: try: obj json.loads(line) except Exception: continue atype obj.get(atype, unknown) stats[atype] 1 if atype authenticate and obj.get(result, 0) ! 0: ip obj.get(remote, {}).get(ip, unknown) failed_ips[ip] 1 print( 审计事件统计 ) for atype, cnt in stats.most_common(): print(f{atype}: {cnt}) print(\n 认证失败 Top10 IP ) for ip, cnt in failed_ips.most_common(10): print(f{ip}: {cnt})你把这个脚本放到 crontab 里每周跑一次再把输出贴到周报里就比“我开了审计日志”有说服力得多。9. 写在最后审计日志配置中最反直觉的几件事折腾审计日志这些年我最大的一个感受是这个功能本身的门槛不高难的是在做规划时能不能想清楚“我到底要审什么”。如果你的规则太松日志量会让你崩溃磁盘和性能都不堪重负如果规则太紧漏了关键事件合规层面照样过不了。这个平衡点需要结合业务类型、数据敏感度、审计标准和基础设施能力来反复调整。有几件反直觉的事我一直记在笔记本上也分享给你第一不要为了“全”而无差别记录。记录所有日志确实是合规审计员眼里的“大锅饭”但实际操作中服务器会先被日志撑爆。一个没有被合理过滤的审计规则最终大概率会因为磁盘耗尽而被迫下线反而违背了合规初衷。第二不要完全依赖 MongoDB 自带能力做归档和告警。时至今日MongoDB 审计日志功能依然停留在“记录和轮转”层面它不会主动帮你做关联分析、威胁检测这些东西需要外部工具补上。你可以用 ELK、ClickHouse、Zabbix甚至一个简单的 Python cron 脚本。重要的是你得持续看它、用起来。第三不要低估权限分离的重要性。我见过太多“root 走天下”的 MongoDB 环境。但如果你用同一个 root 账号既连接业务又管理审计那么审计日志里全是 root 操作可信度反而被拉低。合理的角色拆分不仅是为了安全基线更是为了让审计结果更有实际意义。最后再分享一个小技巧所有审计配置的变更最好走一个“变更申请-审批-执行-验证”的流程。哪怕你自己是 DBA 老大也别拿生产环境当试验田。上线前先在测试环境把 filter 跑几天观察日志增长速率估算磁盘够不够再推到生产你会少吃很多苦头。审计日志这份“安全录像”平时可能觉得是负担但真到了合规测评或者安全事件复盘的时候它可能是你翻盘的唯一底牌。希望这篇内容能帮你在配置 MongoDB 审计日志时少走几步弯路把底牌真正备好。
返回列表