auditd Basics
Every other tool in this module so far either grants access (sudo, PAM) or blocks it (firewalls, SELinux/AppArmor, fail2ban). auditd does neither — it watches, at the kernel level, and records exactly who did what, to which file or syscall, whether or not the action was actually permitted. When least privilege fails, when a sudo rule turns out to have been too broad, or when an incident response team needs to know precisely what a compromised account touched, auditd's log is often the only record detailed enough to answer the question.
What You Will Learn
By the end of this article you will be able to:
- explain what the Linux Audit Framework records, and how it differs from ordinary syslog/journald logging;
- write a persistent audit rule watching a specific file or syscall;
- search recorded events with
ausearchand summarize them withaureport; - explain why an audit rule alone doesn't stop anything, and how that's different from SELinux/AppArmor;
- avoid the most common way auditd rules quietly stop taking effect.
What auditd Records That syslog Doesn't
Ordinary application logging — journalctl, /var/log/auth.log — records what an application chose to log, in whatever format that application's authors decided on. The Linux Audit Framework operates one level below that: it hooks directly into the kernel's syscall table, and can record events like "this specific file was opened for writing, by this UID, at this exact time, using this exact syscall" regardless of whether the application involved logs anything about it at all.
● auditd.service - Security Auditing Service
Loaded: loaded (/lib/systemd/system/auditd.service; enabled)
Active: active (running)
This is precisely why SELinux denials are recorded as AVC (Access Vector Cache) events in the audit log rather than in an application's own log file — the denial happens inside the kernel's security module, before the application ever gets a chance to log anything about the attempt at all. SELinux and AppArmor already used ausearch -m AVC for exactly this reason.
Writing a Rule
Rules can be added temporarily with auditctl, or persistently in /etc/audit/rules.d/audit.rules (recommended for anything meant to survive a reboot):
-w /etc/passwd -p wa -k passwd_changes
-w /etc/shadow -p wa -k shadow_changes
-w /etc/sudoers -p wa -k sudoers_changes
-w <path>— watch this specific file or directory;-p wa— which types of access trigger a record:w(write),a(attribute change, such aschmod/chown),r(read),x(execute);-k <key>— a searchable label attached to every event this rule generates, making later searches (ausearch -k passwd_changes) far easier than grepping raw audit output.
augenrules --load compiles every file under /etc/audit/rules.d/ into the running kernel ruleset — editing the rules file alone has no effect until this step (or a reboot) applies it.
Beyond watching a specific path, a rule can watch a syscall directly, which catches an action regardless of which specific file it targets:
This records every program execution (execve) by a real, logged-in user (auid>=1000 excludes system accounts; auid!=4294967295 excludes processes with no login session at all, such as most system services) — effectively a system-wide record of every command a human account actually ran, independent of whether that command happened to touch a watched file.
auid versus uid
auid (audit UID, sometimes called "login UID") is set once at login and, critically, does not change when a user runs sudo or su — this is precisely what makes it possible to trace an action back to the original account that logged in, even after several privilege escalations. uid is the current effective user ID, which does change with sudo/su. A rule filtering on auid therefore survives privilege escalation in a way a uid filter wouldn't.
Searching Recorded Events: ausearch
type=PATH msg=audit(1753776042.118:381): item=0 name="/etc/sudoers" inode=131094 dev=fd:00 mode=0100440 ouid=0 ogid=0 rdev=00:00
type=SYSCALL msg=audit(1753776042.118:381): arch=c000003e syscall=2 success=yes exit=3 a0=... auid=1000 uid=0 gid=0 euid=0
The auid=1000 field here, alongside uid=0, tells the real story even though the syscall itself ran with root privileges (uid=0, euid=0): whoever logged in as UID 1000 is the one who ultimately triggered this write to /etc/sudoers, most likely via sudo visudo.
-ts today restricts the search to today's events; -i (interpret) translates raw numeric UIDs and syscall numbers into readable names where possible:
type=SYSCALL msg=audit(07/29/2026 09:41:02.118:381) : arch=x86_64 syscall=open success=yes exit=3 a0=... auid=deploy uid=root gid=root euid=root
Summarizing: aureport
For a higher-level view than individual event records, aureport aggregates:
Authentication Summary Report
================================
# total success failed
================================
142 138 4
Executable Summary Report
================================
# total file
================================
84 /usr/bin/sudo
19 /usr/bin/su
aureport is the tool to reach for first when the question is "what's the overall pattern" rather than "show me every matching event" — ausearch for the latter, aureport for the former.
auditd Records; It Does Not Block
This is the single most important distinction between auditd and SELinux/AppArmor, and worth stating precisely: an audit rule watching /etc/shadow for writes generates a record every time it's written, but it does nothing to prevent the write itself. Blocking unauthorized access is DAC's and MAC's job (Least Privilege, SELinux and AppArmor); auditd's job is exclusively visibility — proving, after the fact, exactly what happened and who did it.
Interview angle: why record something you can't prevent
An interviewer might ask why a security team would bother recording an action instead of just blocking it outright. The answer: many legitimate actions need to remain possible — an administrator does need to be able to edit /etc/sudoers — and the value of an audit trail is in accountability and incident response, not prevention. When something does go wrong, "prevent everything" isn't achievable (a legitimate admin account can always be used maliciously by whoever controls it), but "know exactly what happened, in what order, by which account" is achievable, and that's what makes recovery and root-cause analysis possible at all.
A Rule That Isn't Actually Persistent
Rules added directly with auditctl -w ... apply immediately but vanish on reboot — a very easy trap, since the command appears to work and the rule is genuinely active right up until the next restart:
This looks identical to a persistent rule in every way that matters right now — but only a rule written into a file under /etc/audit/rules.d/ and loaded with augenrules --load survives a reboot. auditctl is the right tool for quick, temporary testing; /etc/audit/rules.d/ is the right place for anything meant to remain in effect.
Practical Scenario: Who Modified /etc/sudoers
Problem. A sudoers rule that shouldn't exist was discovered during a routine review (the same scenario worked through in sudo Security). Auditd was already configured with a watch on /etc/sudoers, and the question now is who actually added it, and when.
Search the audit log for every recorded change:
type=SYSCALL msg=audit(03/14/2026 11:02:17.442:2201) : ... auid=admin_jane uid=root exe=/usr/sbin/visudo
Conclusion. auid=admin_jane identifies the account that logged in and triggered the change — regardless of the fact that the syscall itself ran as uid=root through visudo — giving a specific person and timestamp to follow up with, rather than an anonymous "root did it" that every sudo-elevated action would otherwise look identical to.
Common Mistakes
- Adding a rule with
auditctland assuming it's permanent. As covered above, only rules loaded from/etc/audit/rules.d/viaaugenrules --loadsurvive a reboot. - Filtering on
uidinstead ofauidwhen trying to trace an action back to a person.uidchanges with everysudo/su;auiddoesn't, which is exactly what makes it useful for accountability. - Assuming an audit rule prevents anything. auditd only records; blocking a write requires DAC or MAC (file permissions, SELinux/AppArmor), a distinct and complementary control.
- Watching too much, too broadly. A syscall-level rule with no filtering (
-S execvewith noauidrestriction, for instance) generates enormous log volume on a busy server, making the genuinely important events harder to find rather than easier — scope rules to what actually needs accountability.
Exercises
- Add a persistent rule watching
/etc/passwdfor write and attribute-change events, load it withaugenrules --load, make a trivial change (adding a comment with a text editor, for example), and confirm the event withausearch. - Explain, using a concrete example, why a rule filtering on
auidis more useful for accountability than one filtering onuid. - Run
aureport -au --summaryon a system with some authentication history and interpret the ratio of successful to failed attempts. - Explain why auditd recording a security-relevant event and SELinux blocking it are not redundant with each other, even when both are triggered by the same underlying action.
Summary
auditd hooks into the kernel to record security-relevant events — file access, syscalls, authentication — independent of what any individual application chooses to log, and its auid field survives privilege escalation in a way ordinary uid tracking doesn't. ausearch finds specific recorded events and aureport summarizes patterns across many of them, but auditd itself never blocks anything — it is a visibility tool, not an access-control one, and rules only persist across a reboot when written to /etc/audit/rules.d/ rather than added ad hoc with auditctl. The next article, Permission Audit, turns the kind of periodic review this module keeps returning to — who has what access, and does it still match what's actually needed — into a repeatable process.