CentOS 服务器上部署了 auditd 之后,很多人以为只要服务跑起来就万事大吉,实际上真正的坑在于两个地方:一是规则写得粗糙,导致磁盘被无效日志撑爆;二是日志轮转配置不当,要么丢失关键审计记录,要么在轮转瞬间产生性能尖峰。解决这两个问题,不需要花哨的工具,只需要把 auditd 规则和 logrotate 的配合逻辑吃透。

auditd 规则的核心思路:只记录你真正会去看的

默认的 auditd 规则往往会记录大量系统调用,几分钟就能产生几百兆日志。在生产环境里,这种“全量采集”的思路完全不可取。正确的做法是从业务风险出发,反向推导需要监控的节点。比如,你关心的是敏感文件被意外修改、提权行为、异常的用户登录,那就只针对这些场景写规则。

先看一个典型的文件监控规则。假设要监控 /etc/shadow 的写入和属性变更,可以在 /etc/audit/rules.d/ 下创建一个自定义规则文件,比如 audit.rules:

-w /etc/shadow -p wa -k shadow_changes
-w /etc/sudoers -p wa -k sudoers_changes
-w /etc/passwd -p wa -k passwd_changes

这里的 -w 表示监控文件,-p wa 表示只关注写入和属性变更,-k 是给事件打上标签,方便后续用 ausearch 按标签检索。很多人会习惯性地加 -p r,把读操作也监控进去,这在绝大多数场景下完全没有必要,只会徒增日志量。除非你需要追踪谁读取了某个高度敏感的文件,否则不要监控读操作。

系统调用层面的规则要更加克制。比如要监控提权行为,可以监控 execve 系统调用,但必须加上严格的过滤条件,否则瞬间就会产生海量日志。下面这条规则只记录 root 用户执行的操作,并且排除了常见的系统守护进程,避免噪音:

-a always,exit -F arch=b64 -S execve -F euid=0 -F uid!=0 -k root_exec
-a always,exit -F arch=b32 -S execve -F euid=0 -F uid!=0 -k root_exec

这里的关键在于 -F euid=0 -F uid!=0,它的意思是有效用户 ID 为 root,但实际用户 ID 不是 root,也就是普通用户通过 sudo 或 su 提权后执行命令的场景。arch=b64 和 arch=b32 分别对应 64 位和 32 位的系统调用,两者都要写上,否则在混合环境下会漏掉记录。这种精确过滤的规则,一天产生的日志量可能只有几十兆,远低于不加过滤的几百兆甚至几个 G。

还有一个容易被忽视的点:auditd 的规则是有顺序的,规则文件按字母顺序加载。如果你有多个规则文件,建议用数字前缀命名,比如 10-base.rules、20-file.rules、30-syscall.rules,这样可以清晰地控制加载顺序,也方便后续维护。

日志轮转的致命陷阱:auditd 有自己的轮转逻辑

这是绝大多数运维人员踩过的坑。CentOS 上 auditd 的日志默认路径是 /var/log/audit/audit.log,很多人会习惯性地在 /etc/logrotate.d/ 下配一个轮转规则,然后发现 auditd 根本不按预期轮转,甚至出现日志丢失或服务僵死的情况。原因在于 auditd 内部有一套自己的轮转机制,由 /etc/audit/auditd.conf 中的参数控制,外部 logrotate 如果强行介入,会和内部轮转产生冲突。

auditd.conf 中控制轮转的核心参数有几个:num_logs 指定保留的日志文件数量,max_log_file 指定单个日志文件的最大大小(单位是 MB),max_log_file_action 指定当日志达到上限时的动作,一般是 ROTATE。当 auditd 自己执行轮转时,它会自动把当前 audit.log 重命名为 audit.log.1,然后创建新的 audit.log 继续写入,整个过程不会丢失日志,也不需要重启服务。

问题出在很多人同时启用了 logrotate 对 /var/log/audit/ 的处理。logrotate 的典型操作是 mv 旧日志、创建新文件、发送信号让进程重新打开文件句柄。但 auditd 并不依赖文件句柄,它通过内核 netlink 接口直接写入文件,logrotate 的 postrotate 脚本如果发送 SIGHUP 或者直接重启 auditd,反而会导致短暂的审计真空期,这在高安全要求的环境里是不可接受的。

正确的做法是:完全由 auditd 内部轮转接管日志管理,不要为 /var/log/audit/ 配置 logrotate。如果你需要更精细的轮转策略,比如按天轮转而不是按大小,可以在 auditd.conf 中调整 max_log_file 和 num_logs 的组合,配合 cron 任务手动触发轮转。例如,每天凌晨执行 service auditd rotate,这个命令会通知 auditd 立即执行一次轮转,不会中断服务,也不会丢失日志。

0 2 * * * /sbin/service auditd rotate

这样既实现了按天轮转,又避免了 logrotate 的干扰。轮转后的旧日志文件可以用 logrotate 单独处理压缩和删除,但前提是只处理 audit.log.* 这些已经轮转出来的文件,不要触碰正在写入的 audit.log。可以在 logrotate 中这样配置:

/var/log/audit/audit.log.* {
    daily
    missingok
    rotate 30
    compress
    delaycompress
    notifempty
    sharedscripts
    postrotate
        /bin/kill -HUP `cat /var/run/syslogd.pid 2> /dev/null` 2> /dev/null || true
    endscript
}

这里的关键是路径写的是 audit.log.* 而不是 audit.log,这样 logrotate 只会处理已经轮转出来的历史文件,不会干扰 auditd 当前正在写入的日志。postrotate 里的 kill HUP 是发给 syslogd 的,跟 auditd 无关,不会造成审计中断。

磁盘空间保护:max_log_file_action 的进阶用法

auditd.conf 中的 max_log_file_action 除了 ROTATE,还支持 SUSPEND 和 KEEP_LOGS 等选项。在磁盘空间紧张的场景下,SUSPEND 是一个值得考虑的选项。它的作用是当日志文件达到上限时,暂停审计记录,直到管理员手动处理。这听起来很危险,因为暂停期间审计是空白的,但在某些合规场景下,宁可暂停审计也不能让磁盘写满导致系统不可用。ROTATE 虽然能自动轮转,但如果 num_logs 设置过大,或者日志产生速度远超预期,磁盘仍然可能被写满。

更稳妥的做法是结合 IGNORE 和 SYSLOG 两个动作。把 max_log_file_action 设为 IGNORE,意味着 auditd 不会主动限制日志大小,但同时把日志转发到 syslog,由 syslog 的轮转机制统一管理。配置方法是在 auditd.conf 中设置 dispatcher = /sbin/audispd,然后配置 audispd 插件将日志写入 syslog。这样可以利用 rsyslog 强大的过滤和转发能力,把审计日志集中存储到远程日志服务器,本地只保留少量缓冲,彻底解决磁盘压力问题。

如果必须保留本地日志,建议在 auditd.conf 中设置 space_left_action 和 admin_space_left_action 为 email 或 exec,当磁盘空间低于阈值时自动发送告警或执行清理脚本。space_left 的单位是 MB,一般建议设置为磁盘总容量的 10% 左右,比如 200GB 的磁盘就设 space_left = 20480。admin_space_left 是更紧急的阈值,可以设为 5%,触发后执行更激进的清理动作。

性能优化:减少 auditd 对系统的影响

auditd 在内核层面挂钩了系统调用,不可避免地会带来性能开销。在高并发的服务器上,不合理的规则可能导致 CPU 使用率明显上升。优化的核心原则是:减少规则数量,缩小监控范围,使用 -F 过滤条件尽可能早地排除无关事件。

一个容易被忽视的优化点是 freq 参数。在 audit 规则中,freq 用来限制事件的记录频率,比如 freq=100 表示每 100 次匹配的事件只记录一次。这在监控高频系统调用时非常有用,比如监控某个目录下的文件创建,如果业务每秒创建上千个临时文件,不加 freq 限制的话日志会瞬间爆炸,CPU 也会被 auditd 拖垮。

-a always,exit -F arch=b64 -S openat -F dir=/tmp -F success=1 -F freq=100 -k tmp_file_monitor

这条规则监控 /tmp 目录下成功的文件打开操作,但每 100 次才记录一次,既保留了审计线索,又大幅降低了性能影响。freq 的值需要根据业务实际情况调整,太小起不到优化作用,太大会丢失关键信息,一般从 50 到 500 之间取值比较合理。

另外,auditd 的日志写入是同步的,如果磁盘 I/O 成为瓶颈,可以考虑在 auditd.conf 中设置 write_logs = no,然后通过 audispd 将日志异步转发到 syslog 或其他存储后端。异步写入能显著降低 auditd 对业务线程的阻塞,但代价是极端情况下可能丢失少量日志,需要在性能和完整性之间做权衡。

把 auditd 规则和日志轮转配合好,本质上是在审计完整性和系统资源之间找到平衡点。规则写得越精准,日志量越小,轮转压力越低,性能影响也越可控。反之,规则粗糙、轮转混乱,不仅起不到安全审计的作用,反而可能成为系统稳定性的隐患。CentOS 环境下 auditd 的默认配置只是一个起点,真正落地时必须根据业务特点做深度定制。