auditd rules that matter
Identity files, privileged execs, module loads.
A lock stops most break-ins. A camera and a till receipt tell you what happened on the one night someone got through. Hardening a Linux box is the lock work: fewer open ports, tighter permissions, less that can go wrong. Auditing is the camera and the receipt. It does not stop an attacker. It writes down what they did, in an order you can replay later. auditd (the Linux audit daemon, a background program that writes down security events) is that camera, and the rules you give it decide where the camera points.
Here is why the recording holds up. The audit machinery does not watch your applications from the outside. It sits underneath them, down in the kernel (the core of the operating system that talks to the hardware). When a program opens /etc/shadow or loads a kernel module, that action has to pass through the kernel, and the kernel writes down what happened and hands it to auditd before the program's own logging ever runs. A web server that has been taken over can lie in its own log file. It cannot un-ask the kernel for the file it just opened. That is the whole job of writing good rules: capture the handful of events an attacker cannot avoid triggering, and none of the millions they never touch.
Where auditd Sits
The recording path is short. Your rules live in text files under /etc/audit/rules.d/. At boot, and whenever you reload, those files are compiled into a set of live rules held inside the kernel. From then on, every system call that matches a rule produces an audit record, which the kernel hands to the auditd process over a netlink socket (a private channel the kernel uses to talk to programs). auditd writes each record to /var/log/audit/audit.log in a fixed field format, one record per line. A single event often spans several of those lines, all stamped with the same ID. Think of auditd as a court stenographer. It does not judge and it does not summarize. It types down exactly what happened, with a timestamp and who did it, so the transcript still means something when you read it back weeks later.
Two Shapes Of Rule
Almost every useful rule is one of two shapes. A watch is a camera pointed at one file or directory. You write -w and the path, then -p and the kinds of access you care about, then -k and a label so you can find the events later. The four access letters are r (read), w (write), x (execute), and a (attribute change, meaning permissions or ownership). So -w /etc/shadow -p wa says: tell me about any write to the shadow password file, or any change to its permissions.
The other shape is a syscall rule, which taps one specific action no matter where it happens. It reads -a always,exit, then -F arch=b64 to say which build of the call you mean, then -S and the call's name, then -k and a label. -a always,exit means record this event as the call returns, so you also capture whether it succeeded. A syscall (system call, the request a program makes to the kernel to do real work like open a file or load code) is the lowest honest layer you can watch. Here is a starting ruleset that earns the disk it takes.
## Wipe any rules already loaded, so this file is the whole truth-D## Kernel event buffer, and what to do if it ever fills-b 8192-f 1## Identity: accounts, passwords, and who may become root-w /etc/passwd -p wa -k identity-w /etc/shadow -p wa -k identity-w /etc/gshadow -p wa -k identity-w /etc/group -p wa -k identity-w /etc/sudoers -p wa -k identity-w /etc/sudoers.d/ -p wa -k identity## Privilege escalation: record every run of these binaries-w /usr/bin/sudo -p x -k priv-esc-w /bin/su -p x -k priv-esc## Kernel module loads and unloads, on both the 64-bit and 32-bit paths-a always,exit -F arch=b64 -S init_module,finit_module,delete_module -k modules-a always,exit -F arch=b32 -S init_module,finit_module,delete_module -k modules## Lock the configuration: no rule changes until the next reboot-e 2
This file is a trimmed version of the audit rules the CIS (Center for Internet Security) benchmarks ship, and starting from theirs beats a blank file because they already encode the events worth watching. Walk it top to bottom. The identity block watches the files that decide who exists and who has power. /etc/passwd lists accounts, /etc/shadow holds the password hashes, /etc/group and /etc/gshadow define groups, and /etc/sudoers plus anything dropped into /etc/sudoers.d/ decide who may run commands as root. An attacker with a foothold wants a way back in. The quiet moves are: add a new account, add an existing account to a powerful group, or drop a one-line file into /etc/sudoers.d/ that grants themselves password-free root. Every one of those is a write to a file on this list, and every one lands in your log with the identity label.
The privilege block records each time someone runs sudo or su (switch user), the two normal doors from an ordinary account up to root. On its own that is a list of who reached for power. The field that makes it useful is auid, the login user ID, also called the loginuid. When you log in, the kernel stamps your session with your original user ID and does not change it when you run sudo and become root. So even after alice becomes root and edits a file as root, the record still carries auid=alice. uid tells you who someone is right now. auid tells you who they were when they walked in the door. For a defender chasing an incident, auid is the name that matters.
The last block watches kernel module loads. A kernel module is code that runs inside the kernel itself, below every user account and below most security tools. That is exactly where a rootkit (malware that hides itself and hands an attacker lasting control) wants to live, because from there it can hide files and processes from the very commands you would use to find it. init_module and finit_module load a module, delete_module unloads one. Notice the two nearly identical lines, one saying arch=b64 and one saying arch=b32. More on why in a moment.
Loading And Reading The Trail
Editing the rules file changes nothing by itself. Two commands turn text into live monitoring. auditctl talks to the kernel directly and takes effect instantly, but its changes vanish on reboot, like a sticky note on the monitor. augenrules reads every file in rules.d, stitches them into one ordered set, and loads that, which is the version written into the rulebook. Use augenrules for anything you want to survive a restart.
Two things confirm the change took. enabled 2 means auditing is on and the configuration is locked, which is the -e 2 line taking hold. And the rules read back match what you wrote, with the file watches keyed by -k and the syscall rules showing the key as -F key=modules, which is how the kernel prints them. One quiet detail: /etc/sudoers.d/ comes back without its trailing slash, because auditctl normalizes the path. The rule still works.
Now read what the camera caught. Raw records are dense and full of numbers. ausearch pulls records by the label you tagged, and its -i flag interprets the numbers into names: user IDs become usernames, syscall numbers become call names, timestamps become readable dates. aureport rolls records up into a summary.
Read the SYSCALL line. alice, sitting in /home/alice, opened /etc/shadow for writing with vim, and it succeeded. uid=root, because she had used sudo. auid=alice, because the login stamp followed her through. Without a single extra tool you now know who touched the password file, as which identity, when, and whether it worked. The authentication report underneath is your quick scan for the ugly pattern: line 1 is a failed SSH (Secure Shell, an encrypted remote login) attempt from an outside address at three in the morning, exactly the kind of line worth a second look.
Cutting The Noise
A camera that films everything films nothing you can use. Backups read /etc/passwd. Package updates rewrite files under /etc. Record all of it and the one write that matters drowns. Three moves keep the signal high. First, filter by auid so you watch humans, not daemons: adding -F auid>=1000 -F auid!=unset to a syscall rule limits it to real login accounts and skips system services. Second, drop known-noisy record types with an exclude rule instead of deleting the whole watch. Third, when you are hunting for probing, match failed access with -F exit=-EACCES or -F exit=-EPERM, because a burst of permission-denied errors on sensitive files is often someone quietly testing the locks.
Protecting The Trail
A recording is only evidence if the suspect cannot edit it. Two habits keep the log honest. The -e 2 line at the very bottom of the ruleset locks the configuration: once loaded, no one can add, remove, or disable a rule until the machine reboots, and a reboot of a production box is loud and visible. It goes last because nothing after it can load. The second habit is to ship audit records off the host as they are written, to a central collector the attacker does not control, since the local log on a machine they own is the first thing they reach for. The detection course covers that pipeline. One more file matters here: /etc/audit/auditd.conf decides what auditd does as the disk fills, through settings like max_log_file, num_logs, and disk_full_action.
There is a failure mode that bites from the other side. Set the ruleset too broad and the disk fills with events nobody reads, burying the one that mattered. Worse, if disk_full_action is set to halt, or the ruleset uses -f 2 (panic the kernel when the audit backlog overflows), a full disk or a flood of events can freeze the whole host on purpose, and your monitoring becomes an outage. Watch the high-signal targets, tune out the known noise, ship the rest off-box, and leave the failure mode at -f 1 (write the problem to the kernel log) unless you have a specific reason and a tested plan for -f 2.
sudo vim /etc/shadow, and saves a change. In the audit record, uid=root. Which field still identifies alice, and why?-F arch=b64 and one with -F arch=b32. Why both?-f 2 in the ruleset and disk_full_action = halt in auditd.conf, on the logic that stricter is always safer. What is the real-world consequence?When you next change a rule, prove it before you trust it. Load with augenrules --load, confirm enabled and the rule list with auditctl -s and auditctl -l, then trigger the event on purpose (touch a watched file, or run sudo) and pull it straight back with ausearch -k. A rule you have watched fire once is a rule you can rely on at three in the morning.
Try this
Work through “Protecting The Trail” yourself on a sandbox you can throw away, following the commands above in order. Then break one step deliberately and re-run, so you have seen the failure before it finds you.
Takeaway
The trap worth remembering here: one rule, two doors: watch both ABIs. Check that on your own systems before you need to, because it is cheaper to find on a quiet afternoon than during an incident.