Handbook sections

Audit logging with auditd

Last reviewed

Without a tamper-resistant audit trail you cannot say who did what. auditd records, inside the kernel, privileged commands, identity and permission changes, time changes and module loads, for accountability and forensics.

Who did what, and when?

Without a tamper-resistant audit trail you cannot answer the first question after an intrusion: who did what, and when. The Linux audit subsystem (auditd) is the kernel-backed witness that records security-relevant events as the kernel sees them, independent of any application's own logging. An attacker who lands a foothold will escalate privileges, add or modify accounts, change permissions, load a kernel module (a rootkit, a packet sniffer), roll the clock to scramble timestamps, then delete the application logs. If those actions leave no kernel-level record, the incident is invisible. auditd is the record that survives a hostile process.

Why harden auditd

Application logs lie by omission: a process can simply not log, or be killed before it does. auditd records syscalls and file accesses inside the kernel, so it captures the privileged action even when the offending program is hostile. A correct ruleset gives you three things:

  • Accountability : every record carries the login UID (auid), which survives su/sudo, so an action is tied to the real human behind it, not just the effective root identity.
  • Forensics : a precise, timestamped sequence of who changed what, the backbone of any incident investigation.
  • Tamper resistance : an immutable ruleset (-e 2) cannot be silently unloaded by an attacker without a reboot, which is itself a loud, auditable event.

A host with auditd installed but no rules loaded is a common false sense of security: the daemon runs, yet records nothing of interest.

The three layers of the Linux audit framework

Layer Role
Kernel audit subsystem generates events at the source (syscalls, file access)
auditd (user-space daemon) receives events and writes /var/log/audit/audit.log
auditctl / ausearch / aureport load rules, search and summarize events

Events are generated in the kernel, so an attacker who modifies a file cannot stop the event from being emitted. This is the source of truth a hardened host needs, and the control required by the CIS Benchmarks and ANSSI BP-028 (recommendation R73, audit-log the system activity). Note that the audit subsystem is not namespaced: it does not work inside a container (LXC, Docker), so auditd is run on a VM or bare metal.

What Pavois audits: the effective, loaded ruleset

Pavois reads the live, loaded ruleset with auditctl -l, the rules the kernel is enforcing right now, rather than parsing /etc/audit/rules.d/*.rules. This is the effective-config principle that sets Pavois apart: a rule file on disk that was never loaded (no augenrules --load, a syntax error, a rejected syscall) makes a file scanner (OVAL/oscap) falsely pass while the kernel audits nothing. auditctl -l reflects reality.

Each rule checks that a rule bearing the expected key (-k) is loaded. Across its auditd rules Pavois covers: time changes (adjtimex/settimeofday/clock_settime), identity and privilege (/etc/passwd, /etc/group, /etc/shadow, sudoers, and setuid/setgid use), permission and MAC-policy changes (chmod/chown/*xattr, SELinux/AppArmor), kernel module loading (init_module/finit_module/delete_module), privileged commands and admin actions, file deletions, the audit log directory itself, and the immutable flag (-e 2) that finalizes the ruleset.

How do you verify and operate auditd?

  • Check the subsystem is live with auditctl -s: enabled 1 confirms the kernel is auditing and auditd runs (non-zero pid); watch lost (events dropped) and backlog. enabled 0 or a failing command (you are likely in a container) means nothing is recorded.
  • List the enforced rules with auditctl -l. The control directives -D, -b, -f do not appear there, by design; a non-zero count confirms the load, and a rejected rule silently lowers it.
  • Persist rules in /etc/audit/rules.d/*.rules, compiled by augenrules --load. Files load in sort order (10- base, 30- rules, 99- the -e 2 lock) and the kernel applies first-match-wins. auditctl -R loads rules in memory only, lost at the next boot: production always goes through rules.d.
  • Double every syscall rule in arch=b32 AND arch=b64: on a 64-bit host, a 32-bit binary can invoke the same syscall via the 32-bit ABI and escape a b64-only rule (the 32/64 bit syscall mismatch warning).
  • Size the backlog (-b, for example 8192) so events are not dropped under load; auditctl -s showing lost > 0 means the kernel queue overflowed.
  • Choose the disk-full action deliberately in auditd.conf: space_left_action and especially admin_space_left_action can be halt or single, so a saturated audit partition can stop the machine. Isolate /var/log/audit on its own partition and set rotation (max_log_file, max_log_file_action = ROTATE). log_format = ENRICHED resolves UIDs and syscalls before writing, which matters for a SIEM.
  • Lock it for production with -e 2 (immutable): any auditctl change is then refused until a reboot. Enable it only once the ruleset is stable.

Pitfalls

  • ausearch -k is unreliable without -if. On Debian and RHEL, ausearch -k <key> often returns <no matches> while the events are in the log; always force the input file: ausearch -if /var/log/audit/audit.log -k <key>. The same applies to aureport.
  • Use auid, not uid, for accountability: auid stays the login session's user even after su/sudo, which is exactly what answers who is behind an action.
  • -e 2 needs a reboot to change. Pavois harden apply writes the missing rules to a managed drop-in under /etc/audit/rules.d/ and loads them, but immutability is fully effective only after a reboot.

Coverage and gaps

Pavois verifies the presence of the keyed rules in the live ruleset (the CIS / ANSSI control set). It does not yet score auditd.conf tuning (admin_space_left_action, rotation, log_format = ENRICHED) nor remote-forwarding integrity via audisp; those are operator decisions, documented in the verify section above rather than failed.

FAQ

Does auditd work in a container? No. The audit subsystem is global to the host and not namespaced; run it on a VM or bare metal.

Why does Pavois read auditctl -l instead of the rule files? Because only the loaded ruleset is what the kernel enforces. A rule file present but never loaded protects nothing, and Pavois audits the effective state.

What does -e 2 change? It makes the ruleset immutable until the next reboot, so an attacker who gains root cannot silently disable auditing without a reboot, itself a visible, auditable event.