Linux 基础体系 · 第 75/85 篇。示例面向现代主流 Linux 发行版;发行版差异、权限和生产风险会明确说明。

Linux auditd 审计:规则、事件、性能、检索和合规留存

auditd 是 Linux Audit 子系统的用户态守护进程。它接收内核产生的审计记录,将记录写入本地日志,或转发给其他插件。它解决的是“内核观察到了哪些安全相关行为,以及这些行为由谁、以什么身份、从哪里发起”的问题,而不是普通应用日志的统一收集器。

Linux 审计通常由以下组件组成:

  • 内核 Audit 子系统:在系统调用、身份变化、权限检查、SELinux 决策等路径上生成审计记录。
  • auditd:从内核审计队列读取记录,并写入 /var/log/audit/audit.log 等文件。
  • auditctl:查看和临时修改内核中的规则。
  • augenrules:将 /etc/audit/rules.d/*.rules 合并后加载。
  • ausearch:按时间、用户、事件类型、规则 key 等条件检索。
  • aureport:生成登录、认证、系统调用、失败事件等汇总报告。
  • audisp 插件:将审计记录发送到其他处理程序,例如远程审计接收端。

不同发行版的包名和服务管理方式可能不同。RHEL、Rocky Linux、AlmaLinux、Fedora 通常使用 auditauditd 包;Debian、Ubuntu 常见包名为 auditd。配置路径也可能由发行版打包策略决定,但 /etc/audit/auditd.conf/etc/audit/rules.d/ 是现代主流发行版中最常见的布局。

一、审计数据流:从系统调用到日志文件

一个简化的数据流如下:

flowchart LR
    P[进程] --> K[内核安全/系统调用路径]
    K --> M[规则匹配]
    M -->|匹配| Q[内核审计队列]
    M -->|不匹配| X[不生成该规则事件]
    Q --> A[auditd]
    A --> L[/var/log/audit/audit.log]
    A --> D[auディisp 插件]
    L --> S[ausearch/aureport]
    D --> R[远程接收或 SIEM]

实际路径中的关键状态如下:

  1. 进程执行系统调用,例如 openat()execve()setuid()
  2. 内核根据已加载的审计规则判断是否需要记录。
  3. 如果需要记录,内核创建一个或多个审计记录,并放入审计队列。
  4. auditd 从队列读取记录,按照 auditd.conf 中的策略写入文件。
  5. ausearchaureport 读取文件并按事件进行解析。
  6. 如果配置了 audisp 插件,记录还可能被转发到远端系统。

因此,auditd 停止运行并不等于内核完全停止产生审计事件。内核可能继续向队列写入,直到队列满;队列满后的行为由 failurebacklog_limit 和速率限制等配置共同决定。反过来,auditd 正常运行也不意味着所有安全行为都被审计:没有匹配规则的行为不会自动产生完整事件。

审计链路可以抽象为:

进程行为规则匹配内核队列auditd本地或远程存储\text{进程行为} \rightarrow \text{规则匹配} \rightarrow \text{内核队列} \rightarrow \text{auditd} \rightarrow \text{本地或远程存储}

其中任一环节失败,都可能造成审计缺口。特别是“规则没有匹配”和“事件产生后丢失”是两种不同问题,排查时不能混为一谈。

Linux 内核是否启用了 Audit 支持,可先检查:

grep CONFIG_AUDIT /boot/config-$(uname -r)

常见结果类似:

CONFIG_AUDIT=y
CONFIG_AUDITSYSCALL=y

如果内核没有相关能力,安装 auditd 也不能补充内核事件。现代发行版通常默认启用,但定制内核、容器宿主机和极简系统不能假设这一点。

二、审计规则:审计什么、在什么条件下审计

1. 一条规则本质上是一个匹配谓词

一条系统调用审计规则可以理解为一个谓词:

R(E)=actionlistarchsyscallfiltersR(E)= \text{action} \land \text{list} \land \text{arch} \land \text{syscall} \land \text{filters}

其中:

  • action 表示匹配后执行什么动作,常见为 alwaysnever
  • list 表示规则挂在哪类内核审计列表,常见为 exit
  • arch 表示系统调用 ABI 架构,例如 b64b32
  • syscall 表示系统调用名称,例如 execveopenat
  • filters 表示用户、路径、退出码、成功状态等附加条件。

只有当这些条件同时满足时,规则才会生成事件。例如:

-a always,exit -F arch=b64 -S execve,execveat \
   -F auid>=1000 -F auid!=4294967295 -k user-command

它的含义不是“记录所有命令”,而是:

  • 只匹配 64 位系统调用;
  • 只关注 execveexecveat
  • 只记录审计登录身份 auid 大于等于 1000 的进程;
  • 排除尚未建立登录身份的进程;
  • 给生成的事件附加 user-command 这个 key。

这里的 auid 与当前有效用户 uid 不同。uid 可能因为 sudosu 或 setuid 程序发生变化;auid 通常表示最初建立该会话的审计身份,用于回答“哪个登录用户最终发起了这个行为”。这也是审计中区分“当前权限身份”和“责任主体”的基础。

2. 规则来源与生命周期

临时查看当前内核规则:

sudo auditctl -l

查看审计状态:

sudo auditctl -s

典型字段包括:

enabled 1
failure 1
pid 1234
rate_limit 0
backlog_limit 8192
lost 0
backlog 0

这些字段分别表示:

  • enabled:审计是否启用;某些系统还会显示锁定状态。
  • failure:规则或日志链路失败时的处理模式。
  • pid:当前 auditd 进程号。
  • rate_limit:每秒允许的事件数量上限,0 通常表示不限速。
  • backlog_limit:内核审计队列容量。
  • lost:内核已经丢弃的事件数量。
  • backlog:当前排队等待用户态读取的事件数量。

生产系统不应只执行 auditctl -l 就认为规则持久化了。直接用 auditctl 加载的规则通常只影响当前运行状态,重启后可能消失。持久化配置通常放在:

/etc/audit/rules.d/10-base.rules
/etc/audit/rules.d/50-custom.rules

然后由 augenrules 合并和加载:

sudo augenrules --check
sudo augenrules --load
sudo auditctl -l

不同发行版对服务重载的封装不同。修改规则后,应以本机发行版的 auditd 文档为准,不要在生产环境中盲目执行 systemctl restart auditd;某些系统会限制直接重启,重启还可能制造审计空窗。

如果规则文件最后包含:

-e 2

则表示将审计配置锁定为不可变状态。锁定后,通常只能通过重启加载新规则。这是防止运行中的特权进程修改审计策略的机制,但也会增加变更成本,因此必须确保规则经过验证,并把该行放在规则文件最后。

3. 常见规则类型

监视文件或目录

-w /etc/passwd -p wa -k identity
-w /etc/group -p wa -k identity
-w /etc/shadow -p wa -k identity
-w /etc/sudoers -p wa -k privilege-policy
-w /etc/sudoers.d/ -p wa -k privilege-policy
-w /etc/ssh/sshd_config -p wa -k ssh-config

-p 权限掩码常见含义为:

  • r:读取
  • w:写入
  • x:执行
  • a:属性变化,例如权限、属主、时间戳变化

例如 -p wa 表示关注内容写入和元数据变化。

-w 是传统 watch 语法,兼容性较好,但在现代内核和 audit 工具中,系统调用规则配合 -F path=-F dir= 往往更容易表达复杂过滤条件。目录监视还要考虑递归范围、挂载边界和发行版实现差异,不能把一个大型目录树直接当成低成本规则。

监视身份与权限变化

-a always,exit -F arch=b64 \
  -S setuid,setgid,setreuid,setregid,setresuid,setresgid \
  -k identity-change

-a always,exit -F arch=b64 \
  -S capset \
  -k capability-change

这些规则关注进程身份或 Linux capabilities 的变化。它们能够辅助发现提权行为,但不能代替 SELinux/AppArmor 的访问控制,也不能仅凭一次 setuid 事件判断“提权成功”。必须继续查看对应的 exit 值、进程、父进程和后续行为。

监视模块加载

-a always,exit -F arch=b64 \
  -S init_module,finit_module,delete_module \
  -F auid>=1000 -F auid!=4294967295 \
  -k kernel-module

该规则只覆盖列出的系统调用和 64 位 ABI。系统中如果允许 32 位程序运行,还需要评估是否添加对应的 arch=b32 规则;不能简单复制所有规则,否则可能因为系统调用名称在不同 ABI 下不存在而导致加载失败或结果不符合预期。

监视命令执行

-a always,exit -F arch=b64 \
  -S execve,execveat \
  -F auid>=1000 -F auid!=4294967295 \
  -k user-command

该规则常用于追踪交互式用户执行的程序,但它的成本可能很高。execve 触发频繁时,包管理器、构建系统、脚本解释器和服务启动过程会产生大量事件。它还不能记录 shell 中“没有启动新程序”的内建命令,例如:

cd /tmp
export A=1
umask 027

因此,execve 审计记录的是进程执行,不是完整的 shell 命令历史。PROCTITLEEXECVE 记录也可能受参数长度、编码和内核实现影响,不能等价替代终端录制或应用级审计。

4. 架构条件不能省略

在 x86-64 主机上,常见系统调用架构是 b64。如果规则不指定架构,或者只覆盖一个 ABI,可能遗漏 32 位兼容程序。

可以查看机器架构:

uname -m

也可以观察已有事件中的:

arch=c000003e

arch 字段通常以十六进制表示;c000003e 常见于 x86-64。这里的日志字段不是配置文件中的 b64 字符串,两者是同一类架构信息的不同表达。

规则验证必须检查加载结果,而不是只看配置文件:

sudo augenrules --load
sudo auditctl -l
sudo auditctl -s

如果加载失败,应查看 auditd 或内核日志。常见原因包括:

  • 系统调用名称在当前架构中不存在;
  • 规则字段拼写错误;
  • 试图修改已经由 -e 2 锁定的配置;
  • 规则文件合并顺序导致行为与预期不同;
  • 发行版使用了不同版本的 auditctl 语法。

三、事件模型:一行日志不等于一个事件

1. 事件与记录的区别

Linux Audit 中,一个事件通常由同一时间戳和序列号关联的多条记录组成。例如:

type=SYSCALL msg=audit(1710000000.123:456): ...
type=EXECVE  msg=audit(1710000000.123:456): ...
type=PROCTITLE msg=audit(1710000000.123:456): ...
type=CWD     msg=audit(1710000000.123:456): ...

其中:

audit(时间戳:序列号)

括号中的 456 是该审计事件的序列号。多个记录共享同一个序列号,表示它们属于同一个事件上下文。

SYSCALL 常包含:

  • syscall:系统调用编号;
  • success:系统调用是否成功;
  • exit:返回值或错误码;
  • a0a3:系统调用原始参数;
  • items:关联路径项数量;
  • auid:审计登录身份;
  • uideuidsuidfsuid:不同用户身份;
  • pidppid:进程和父进程;
  • comm:进程短名称;
  • exe:可执行文件路径;
  • subj:SELinux 等安全上下文;
  • key:命中的规则 key。

PATH 记录说明系统调用涉及的文件路径,CWD 记录当时的工作目录,EXECVE 记录执行参数,PROCTITLE 记录进程标题。并不是每种系统调用都生成这些附属记录,具体字段取决于事件类型和内核路径。

例如,文件路径可能同时出现在 PATH 中,而实际成功与否应优先查看 SYSCALL 的:

success=yes
exit=0

如果看到:

success=no
exit=-13

通常表示系统调用失败,-13 对应 EACCES。失败事件仍然有审计价值,因为它能显示探测、越权尝试或错误配置;不能只收集成功事件。

2. 一次文件操作的推导示例

假设用户执行:

sudo sh -c 'echo test >> /etc/example.conf'

可能发生如下过程:

  1. shell 或重定向逻辑调用 openat()
  2. 内核根据 /etc/example.conf 的规则进行匹配。
  3. 如果文件以追加写方式打开,规则 -p w 可能命中。
  4. 内核记录执行该系统调用的进程身份、路径和返回值。
  5. auditd 将相关 SYSCALLPATH 等记录写入日志。
  6. ausearch -k some-key 根据规则 key 找到整个事件。

但不能从这条事件直接推出“文件最终内容一定是 test”。系统调用可能成功打开文件后,后续写入失败;也可能另一个进程马上覆盖内容。审计事件描述的是被观测到的内核行为,不自动提供业务语义上的最终状态。

3. 身份字段的实际含义

一个事件可能出现:

auid=1001 uid=0 euid=0

合理解释是:用户 1001 登录后,通过 sudoroot 有效身份执行了操作。若只查看 uid=0,只能知道当时进程拥有 root 身份,无法知道最初责任用户。

但是 auid 并非任何场景都可靠:

  • 系统启动阶段的服务可能没有登录审计身份;
  • 由内核、守护进程或容器运行时创建的进程可能出现未设置值;
  • 容器内外的 UID、PID 和审计上下文不能直接等同;
  • 身份切换后,uidauid 的变化规律取决于 PAM、登录方式和程序行为。

因此,事件分析通常要同时看 auiduideuidsespidppidexeaddr 和时间窗口。

四、登录、SSH 与审计关联

SSH 登录通常涉及多个日志来源:

  • sshd 的认证和会话日志;
  • PAM 生成的审计事件;
  • 内核记录的进程、文件和身份变化;
  • systemd journal 或 rsyslog 转发后的文本日志。

常见审计事件类型包括:

  • USER_AUTH:认证行为;
  • USER_LOGIN:登录结果;
  • USER_STARTUSER_END:用户会话开始和结束;
  • CRED_ACQCRED_REFR:凭据获取或刷新;
  • USER_CMD:某些用户态组件记录的命令行为。

具体事件是否产生,取决于 OpenSSH、PAM 模块、发行版配置和审计规则,不能保证每台机器都有完全相同的事件集合。OpenSSH 的认证、会话和日志行为应结合本机 sshd_config 以及 OpenSSH 手册确认,不能仅凭 auditd 日志推断所有认证细节。

查询最近的登录相关事件:

sudo ausearch -m USER_LOGIN,USER_AUTH,USER_START \
  --start recent --interpret

查询 SSH 服务日志:

sudo journalctl -u sshd --since "1 hour ago"

Debian/Ubuntu 上服务名通常为 ssh

sudo journalctl -u ssh --since "1 hour ago"

关联分析时,可按以下路径核对:

  1. 在 SSH 日志中找到时间、远端地址、用户名和认证结果。
  2. 在审计事件中检查 addrauidsesuid
  3. 在同一时间窗口查找 execve、权限变化和敏感文件访问。
  4. 使用 pidppidexe 判断后续行为是否由 sshd、shell 或 sudo 派生。
  5. 结合 NTP/chrony 状态确认不同主机的时间可比较。

这类关联不是“按用户名 grep 一下”。同一个用户名可以有多个并发会话,pid 会变化,auid 可能相同,时间戳还可能因时钟偏差造成误判。

五、检索与报告:按事件解析,而不是直接 grep

1. ausearch 的基本用法

查询某个规则 key:

sudo ausearch -k ssh-config --interpret

查询某个时间范围:

sudo ausearch --start today --end now --interpret

查询某个用户的事件:

sudo ausearch -ua 1001 --start recent --interpret

查询失败的系统调用:

sudo ausearch --success no --start recent --interpret

查询具体事件类型:

sudo ausearch -m SYSCALL,PATH --start recent --interpret

--interpret 会把 UID、系统调用号、网络地址等字段转换为更容易阅读的形式。机器处理时应慎用解释后的文本,优先保留原始字段:

sudo ausearch -k user-command --raw --start today

直接使用:

grep "key=user-command" /var/log/audit/audit.log

有几个问题:

  • 一个事件由多条记录组成,grep 可能只拿到其中一行;
  • 日志可能已经轮转;
  • 字段可能十六进制编码;
  • 时间、UID 和事件类型的解析容易出错;
  • 多行记录之间需要依据 audit(timestamp:serial) 重新分组。

所以,grep 适合快速定位,不适合作为审计事件解析器。

2. aureport 适合汇总,不适合替代原始证据

常见报告:

sudo aureport --auth
sudo aureport --login
sudo aureport --failed
sudo aureport --syscall

例如:

sudo aureport --failed --summary

它能回答“失败认证有多少”“哪些系统调用失败较多”等汇总问题,但汇总会丢失很多上下文。调查单个事件时,应回到 ausearch 的原始记录,保留完整事件组和日志文件来源。

3. 完整检索算例

假设目标是调查“用户 alice 在今天是否修改了 SSH 配置,并随后执行了什么程序”。

第一步,找到配置修改事件:

sudo ausearch -k ssh-config --start today --end now --interpret

从结果中记录:

  • audit 序列号;
  • auid
  • uideuid
  • pidppid
  • exe
  • successexit
  • PATH 中的实际路径;
  • sesaddr

第二步,按 auid 和会话继续查找命令执行:

sudo ausearch -ua 1001 --start today --end now -k user-command --interpret

第三步,核对修改是否成功:

type=SYSCALL ... success=yes exit=0 auid=1001 uid=0 euid=0 ...
type=PATH ... name="/etc/ssh/sshd_config" nametype=NORMAL ...

如果只看到:

success=no exit=-13

则说明该次系统调用被拒绝,不能写成“配置已被修改”。

第四步,检查 SSH 服务是否重新加载:

sudo journalctl -u sshd --since today

配置文件的写入、sshd -t 校验和服务重载是三个不同事实。审计记录只能证明被审计的系统调用;要判断服务是否采用新配置,还必须检查服务日志和当前服务状态。

六、性能:审计开销来自哪里

审计性能不能用一个固定百分比概括。开销取决于:

  • 系统调用频率;
  • 规则数量;
  • 每条规则中的字段过滤;
  • 是否进行路径解析;
  • 是否记录 execve 参数;
  • 事件生成速率;
  • auditd 写盘速度;
  • 磁盘轮转和远程转发;
  • 突发流量与内核队列大小。

可以把单个事件从内核到磁盘的平均处理时间粗略表示为:

Tevent=Tmatch+Tformat+Tqueue+TwriteT_{\text{event}} = T_{\text{match}} + T_{\text{format}} + T_{\text{queue}} + T_{\text{write}}

其中:

  • TmatchT_{\text{match}}:规则匹配和路径判断时间;
  • TformatT_{\text{format}}:生成 SYSCALLPATH 等记录的时间;
  • TqueueT_{\text{queue}}:等待用户态读取的时间;
  • TwriteT_{\text{write}}:写入本地文件或插件的时间。

这不是内核的性能保证,只是定位瓶颈的模型。系统调用越频繁,单位时间的事件数越多:

λaudit=iλipi\lambda_{\text{audit}} = \sum_i \lambda_i \cdot p_i

其中 λi\lambda_i 是第 ii 类行为的发生速率,pip_i 是该行为命中审计规则的比例。当事件到达速率长期高于 auditd 的处理速率时,队列会增长;若持续增长到 backlog_limit,就会触发丢失或失败策略。

查看当前压力:

sudo auditctl -s

重点观察:

  • backlog 是否长期接近 backlog_limit
  • lost 是否增长;
  • 日志写盘是否出现延迟;
  • auditd 是否报告磁盘或插件错误。

1. 高成本规则的典型来源

以下规则可能产生很大流量:

-a always,exit -F arch=b64 -S all -k everything

或对整个根文件系统进行广泛路径监视:

-w / -p wa -k all-files

它们的问题不是“语法错误”,而是匹配范围过大。构建机、数据库服务器、容器宿主机和高并发 Web 服务器可能迅速产生海量事件,导致:

  • CPU 消耗上升;
  • 日志磁盘写满;
  • 关键事件被普通噪声淹没;
  • 内核队列堆积;
  • 在严格失败模式下阻塞或停止业务系统调用。

更合理的做法是围绕风险边界选择规则,例如身份数据库、SSH 配置、sudo 策略、内核模块、时间配置、关键服务二进制和特定管理操作,而不是默认记录所有系统调用。

2. 队列与失败策略

auditd.conf 中常见配置包括:

log_file = /var/log/audit/audit.log
log_format = RAW
flush = incremental_async
freq = 50
num_logs = 10
max_log_file = 100
max_log_file_action = ROTATE
space_left = 100
space_left_action = SYSLOG
admin_space_left = 50
admin_space_left_action = SUSPEND
disk_full_action = SUSPEND
disk_error_action = SUSPEND

这些参数的确切默认值和可用动作依发行版版本而异,修改前应查看:

man auditd.conf

flush=incremental_async 一般降低同步写盘开销,但在机器突然掉电时可能损失尚未落盘的数据。更强的刷新策略通常提高持久性成本;这是可靠性与性能的取舍,不存在对所有场景都正确的值。

内核失败策略常见为:

  • 0:静默处理;
  • 1:打印内核消息;
  • 2:触发 panic。

生产环境使用 failure=2 需要非常谨慎。它提高了“审计不可用时不继续运行”的强度,却可能把磁盘满、审计守护进程故障或规则错误转化为主机不可用。合规要求若没有明确规定,不能只因“更严格”就直接启用。

可以在低风险环境模拟压力并观察:

for i in $(seq 1 1000); do true; done
sudo auditctl -s

这个例子只有在已启用 execve 等规则时才会显著产生事件,而且 true 可能由 shell 内建命令或外部程序实现,结果随 shell 和发行版变化。测试结果不能直接当作生产吞吐基准。

七、日志轮转、磁盘治理与合规留存

1. 轮转不是留存策略

max_log_file 控制单个审计日志文件大小,num_logs 控制轮转文件数量;但这些参数只描述本地文件生命周期。例如:

max_log_file = 100
num_logs = 30
max_log_file_action = ROTATE

如果单位是 MiB,这个配置最多只保留约 3 GiB 的轮转文件,实际还受已有文件、文件系统、压缩策略和发行版行为影响。它不能证明“保留 180 天”,因为每天事件量可能不同。

基于容量的留存时间可以估算为:

DCusablerdailyD \approx \frac{C_{\text{usable}}}{r_{\text{daily}}}

其中:

  • CusableC_{\text{usable}} 是用于审计日志的可用空间;
  • rdailyr_{\text{daily}} 是每天产生的日志量;
  • DD 是估计留存天数。

如果每天流量有突发,应该使用高分位而不是平均值。例如平均每天 2 GiB、峰值每天 10 GiB 的主机,用平均值计算出来的留存期在事故期间可能迅速失真。

2. 磁盘满的故障路径

审计日志写满磁盘时,可能经历:

  1. space_left 触发提醒动作;
  2. 低于 admin_space_left 后触发管理员级动作;
  3. 文件系统无空间后触发 disk_full_action
  4. 如果失败策略严格,部分系统调用可能失败,甚至主机停机;
  5. 如果策略宽松,业务继续运行,但审计证据出现缺口。

因此,必须为 /var/log/audit 单独规划容量或文件系统,并监控:

df -h /var/log/audit
df -ih /var/log/audit
sudo auditctl -s
sudo journalctl -u auditd --since "1 hour ago"

同时要注意 inode 耗尽、远程挂载不可用、权限被错误修改和日志轮转失败等非容量问题。

3. 合规留存需要额外的存储与完整性控制

auditd 的本地日志默认是普通文件。它不会自动提供不可抵赖性、WORM 保留、跨主机备份或完整的篡改证明。要满足具体合规要求,通常还需要:

  • 将事件实时或准实时发送到独立日志接收端;
  • 接收端使用访问隔离、对象锁定或 WORM 存储;
  • 限制审计管理员对历史日志的删除和修改权限;
  • 对归档文件计算哈希并保存哈希清单;
  • 使用可信时间同步;
  • 记录轮转、上传、失败和恢复状态;
  • 定期进行恢复演练和随机抽样校验;
  • 明确保留期限、法定保全和删除规则。

远程转发不能自动等价于加密传输或不可篡改。使用 audisp-remote 时,应确认本机版本支持的传输方式、目标端认证和网络保护措施;必要时通过受控网络、VPN 或符合组织要求的加密通道保护链路。

审计日志本身含有用户名、路径、命令参数、网络地址和安全上下文,属于敏感数据。集中留存时还要控制谁能够检索,避免“为了合规保留日志”却扩大了凭据、令牌或业务参数的暴露面。

八、与 journald、rsyslog 和 SELinux 的边界

1. auditd 与 journald

journald 主要管理 systemd 服务日志、内核日志和结构化字段;auditd 主要消费 Linux Audit 事件。二者可能在同一主机共存,但不是同一个数据源。

例如:

journalctl -k

适合查看内核日志,而:

ausearch -m SYSCALL --start recent

用于查询审计系统调用事件。

某些发行版会把审计相关消息同时呈现在 journal 中,但不能因为 journalctl 能看到某条消息,就认为它保留了完整的 Audit 事件组。合规取证时应保留原始审计日志或经过验证的结构化归档。

2. rsyslog 与 auditd

rsyslog 擅长接收和转发 syslog 消息;auditd 负责 Audit 记录。把 /var/log/audit/audit.log 当作普通文本文件交给 rsyslog 读取,可能破坏事件分组、重复转发或造成文件竞争。应优先使用 auditd/audisp 提供的插件链路,再在接收端统一进入日志平台。

3. SELinux/AppArmor 与 auditd

SELinux 和 AppArmor 是访问控制机制;auditd 是记录和检索机制。以 SELinux 为例,一次访问可能:

  • 被 SELinux 拒绝并产生 AVC 审计记录;
  • 被传统 Unix 权限拒绝并产生系统调用失败记录;
  • 权限允许但没有匹配 Audit 规则,因此没有对应文件审计事件。

因此:

  • auditd 不能替代 SELinux/AppArmor;
  • SELinux 允许不代表业务允许;
  • 没有 Audit 事件不代表行为没有发生;
  • AVC 日志也不能代替对关键文件和身份变化的审计。

排查权限问题时,通常要并行查看:

sudo ausearch -m AVC --start recent --interpret
sudo ausearch --success no --start recent --interpret
sudo journalctl -k --since "10 minutes ago"

具体命令是否可用取决于系统是否启用了 SELinux;AppArmor 系统应使用对应的内核日志和工具。

九、生产变更流程与故障恢复

审计规则属于安全控制面,修改时应区分“验证规则”和“启用规则”。

推荐在测试主机执行:

sudo cp -a /etc/audit/rules.d /etc/audit/rules.d.backup.$(date +%F-%H%M%S)
sudo augenrules --check
sudo augenrules --load
sudo auditctl -l
sudo auditctl -s

然后用实际动作验证:

sudo touch /etc/example.conf
sudo chmod 640 /etc/example.conf
sudo ausearch -k example-key --start recent --interpret

如果规则是:

-w /etc/example.conf -p wa -k example-key

预期应看到与 touchchmod 相关的事件,但具体记录数量、PATH 类型和系统调用名称会随内核与工具版本变化。验证重点是:

  • 事件是否产生;
  • 事件中的路径是否正确;
  • 成功和失败状态是否符合实际;
  • auiduidexe 是否可解释;
  • lost 是否保持不变;
  • 日志轮转和远程发送是否正常。

如果新规则导致事件量暴涨,恢复步骤应包括:

  1. 停止继续扩大规则范围;
  2. 从备份恢复规则文件;
  3. 若没有 -e 2,重新加载经过验证的旧规则;
  4. 若已经锁定,按变更设计重启到已知规则版本;
  5. 检查 auditctl -slostbacklog 和失败状态;
  6. 标记受影响的审计时间窗口,不要把不完整日志当成完整证据。

lost=0 只说明内核报告的丢失计数当前为零,不能证明过去没有发生规则遗漏,也不能证明远程接收端已经成功持久化。完整性判断必须结合规则版本、主机状态、轮转记录、远程接收确认和存储校验。

十、常见误解与边界

误解一:安装 auditd 后自动审计所有行为

错误。没有匹配规则的普通文件读取、进程执行或权限变化,不会自动形成你所需要的完整事件。发行版可能预置一部分规则,但其范围和目的不同,必须用 auditctl -l 确认。

误解二:审计记录了“用户输入的每条命令”

错误。execve 记录程序执行;shell 内建命令、别名展开、函数逻辑和部分解释器内部行为不会因此完整出现。命令参数还可能被截断、编码或由解释器统一呈现。

误解三:uid=0 就表示 root 直接登录

错误。sudo、setuid 程序和服务都可能产生 uid=0。应结合 auidsesexeppid、SSH/PAM 日志判断责任链。

误解四:没有事件就代表没有访问

错误。可能是规则未覆盖、使用了未匹配的 ABI、事件在其他主机或容器边界产生,也可能在队列、磁盘或远程传输阶段丢失。

误解五:把规则写得越宽越安全

错误。过宽规则会增加性能压力、日志噪声和故障概率,反而降低调查质量。规则应从威胁模型和合规控制项推导:先确定需要证明的事实,再选择能最小成本证明该事实的系统调用、路径和身份过滤条件。

审计的核心价值不是“日志越多越好”,而是让关键安全事实可验证:

  • 谁建立了会话;
  • 谁以什么权限执行了什么程序;
  • 哪个敏感对象被访问或修改;
  • 操作成功还是失败;
  • 事件是否完整到达了受控存储;
  • 在规定期限内能否检索并证明日志未被悄然删除或修改。

当规则、事件关联、性能保护、检索方法和独立留存同时成立时,auditd 才能从“主机上有一个日志文件”变成可用于安全响应和合规取证的审计控制。


系列导航与关联阅读

官方资料

本文依据 Linux 内核、systemd 与主流发行版官方文档重新梳理;正文与实验由 WR BLOG 编写。