3. MongoDB审计日志配置与采集

MongoDB 的审计能力由企业版(Enterprise)内置的审计功能提供,社区版不包含该模块。审计日志的目标是回答“谁在什么时间、从哪个来源、对哪个数据库和集合、执行了什么操作、结果如何”。要把它真正用于安全审计与合规治理,不能只停留在“打开开关”,而要把启用、格式解析、轮转和采集管道作为一个完整链路来设计。

一、审计日志的启用与关键参数

审计通过 auditLog 配置段控制,核心参数包括 destination、format、path、filter 和 runtimeConfiguration。其中 destination 可取 file、syslog(仅 Linux)或 console;format 可取 JSON 或 BSON。生产环境通常选择 file + JSON,便于后续用采集器直接解析。

# mongod.conf 片段(示例)
auditLog:
  destination: file
  format: JSON
  path: /var/log/mongodb/audit.json
  filter: '{ atype: { $in: ["authCheck","authenticate","createUser","dropUser","createRole","dropRole","grantRolesToUser","revokeRolesFromUser","dropCollection","dropDatabase","createIndex","dropIndex"] } }'
  runtimeConfiguration: true

这里有两个容易踩坑的点。第一,filter 使用 MongoDB 查询语法,但作用对象是审计事件文档,字段名是 atype(操作类型)、users、roles、param.ns(命名空间)等,而不是普通集合字段。第二,runtimeConfiguration: true 允许通过 db.adminCommand({setParameter:1, auditLogFilter: ...}) 在不重启实例的情况下调整过滤条件,但该命令本身也会产生审计事件,需要纳入监控。

若 filter 写得过宽,日志量会迅速膨胀;写得过窄,则可能漏掉关键操作。建议先以“认证 + 权限检查 + DDL + 用户/角色变更”为基线,再按合规要求补充 find、insert、update、delete 等数据面操作。

二、审计日志格式解析

JSON 格式的审计日志是“一行一条 JSON 文档”(JSON Lines),每条记录包含 atype、ts、local、remote、users、roles、param、result 等字段。下面是一条典型的认证事件示例:

{
  "atype": "authenticate",
  "ts": { "$date": "2024-06-11T08:12:33.421+08:00" },
  "local": { "ip": "10.20.30.11", "port": 27017 },
  "remote": { "ip": "10.20.30.45", "port": 51234 },
  "users": [ { "user": "app_reader", "db": "admin" } ],
  "roles": [ { "role": "readAnyDatabase", "db": "admin" } ],
  "param": { "user": "app_reader", "db": "admin", "mechanism": "SCRAM-SHA-256" },
  "result": 0
}

解析时重点关注:result 为 0 表示成功,非 0 表示失败(如 18 为 AuthenticationFailed);remote.ip 是客户端来源,用于识别异常登录;param.ns 是操作的目标命名空间;atype 决定事件语义。对于 authCheck 事件,还会出现 param.command 和 param.args,可据此还原具体语句类型。

三、日志轮转

审计文件不会自动按大小切分,需要外部工具配合。MongoDB 提供了 logRotate 命令,但它默认只轮转 mongod 日志,审计日志需要显式指定:

db.adminCommand({ logRotate: 1, auditLog: true })

更常见的做法是用 logrotate 管理,配合 postrotate 调用上述命令,或直接使用 copytruncate 模式。推荐配置如下:

/var/log/mongodb/audit.json {
  daily
  rotate 30
  size 512M
  missingok
  notifempty
  compress
  delaycompress
  copytruncate
}

轮转策略要与采集管道对齐:如果采集器按文件尾部读取(tail),copytruncate 可能导致读取位置错乱,此时应优先使用 create + postrotate 方式,让采集器通过 inode 变化感知新文件。轮转后要验证新文件权限仍为 mongodb:mongodb 0600,避免审计数据被非授权读取。

四、采集工具与管道设计

审计日志的采集通常采用“本地采集器 + 消息队列 + 存储/分析”的三段式管道。常见组合是 Filebeat 或 Fluent Bit 作为采集端,Kafka 作为缓冲,Elasticsearch 或 ClickHouse 作为存储与检索层。下图展示了这一数据流的关键环节与字段处理位置:

MongoDB 实例 auditLog: file/JSON /var/log/mongodb/ audit.json 采集器 Filebeat / Fluent Bit • 多行 JSON 合并 • 字段提取 atype/result • 添加 host/env 标签 • 记录读取偏移量 消息缓冲 Kafka / Redis 削峰 · 解耦 · 重放 topic: mongo-audit 存储与检索 Elasticsearch ClickHouse 告警与合规 异常登录 · 越权 DDL 变更 · 留存 logrotate daily / 512M / 30份 审计数据流:源文件 → 采集解析 → 缓冲 → 存储检索 / 告警合规

采集端的关键配置在于“多行合并”和“字段解析”。由于审计日志是单行 JSON,通常不需要多行合并,但若使用 format: BSON 则需先转码。Filebeat 的 filestream 输入配合 ndjson 解析器即可直接展开字段:

filebeat.inputs:
- type: filestream
  id: mongo-audit
  paths:
    - /var/log/mongodb/audit.json
  parsers:
    - ndjson:
        target: ""
        add_error_key: true
  fields:
    service: mongodb
    log_type: audit
  fields_under_root: true

processors:
  - drop_event.when.equals:
      result: 0
  - add_fields:
      target: ""
      fields:
        env: prod

上面的 drop_event 示例只保留失败事件,适合“异常优先”的告警场景;但合规留存通常要求全量保留,此时应去掉该处理器,改为在存储层按 result 建立索引或分区。管道设计还要考虑背压:当 Kafka 不可用时,采集器应停止读取而非丢弃数据,可通过 queue.mem.events 和 max_procs 控制内存占用,并设置磁盘队列作为兜底。

审计日志本身包含敏感信息(用户名、数据库名、部分命令参数),采集管道必须启用 TLS 传输,存储层要配置基于角色的访问控制,并设置与合规要求匹配的保留周期(如等保要求不少于 6 个月)。

五、验证与常见问题

启用后应做三项验证:一是执行一次成功登录和一次失败登录,确认 authenticate 事件中 result 分别为 0 和非 0;二是执行一次 dropCollection,确认 atype 与 param.ns 正确;三是手动触发一次轮转,确认采集器能继续读取新文件且无重复。常见问题包括:filter 语法错误导致实例启动失败、审计文件权限过宽、轮转后采集器仍持有旧文件句柄、以及 runtimeConfiguration 被误关闭导致动态调整失效。排查时优先查看 mongod 日志中的 audit 相关报错,再核对采集器偏移量文件与文件 inode 是否一致。