Hands-on Linux Auditing with auditd
Learn to configure auditd rules, watch critical files and syscalls, and extract actionable security intelligence with ausearch and aureport on modern Linux.
Before you start
- ▸Root or sudo access on the target system
- ▸Basic familiarity with systemd service management
- ▸Understanding of Linux file permissions and UIDs
- ▸A 64-bit Linux kernel (rules shown target x86-64)
The Linux Audit Framework, centred on auditd, gives you a tamper-evident, kernel-level record of who did what and when — before any userspace logging daemon even gets involved. It is the backbone of CIS, STIG, and PCI-DSS compliance on Linux, and once you understand how rules, events, and reports fit together it becomes an indispensable forensic tool. This guide covers practical usage: writing rules, watching files and syscalls, querying the log, and generating summary reports.
Install and Start auditd
Most enterprise distros ship auditd in the base install. If it is missing, install it:
# Debian / Ubuntu
sudo apt install auditd audispd-plugins
# Fedora / RHEL / Rocky
sudo dnf install audit audit-libs
# Arch
sudo pacman -S audit
Enable and start the service with systemd:
sudo systemctl enable --now auditd
Verify it is running and confirm the kernel module is active:
sudo systemctl status auditd
auditctl -s
auditctl -s prints the audit status block. Look for enabled 1 and pid pointing to the auditd process. Output will vary but a healthy system shows enabled 1.
Understanding the Rule Pipeline
Rules flow from three places, in priority order:
- /etc/audit/audit.rules — compiled by
augenrulesor loaded directly; do not edit by hand. - /etc/audit/rules.d/*.rules — drop-in files merged at startup. This is where you write persistent rules.
- auditctl -a/-w — runtime-only rules, lost on reboot. Useful for testing.
After editing any file under rules.d/, regenerate and reload:
sudo augenrules --load
Writing File-Watch Rules
File watches use the -w flag and are the easiest starting point. The -p flag sets the permission filter: read, write, execute, attribute-change. The -k flag attaches a searchable key.
Watch critical system files
Create /etc/audit/rules.d/40-watches.rules:
sudo tee /etc/audit/rules.d/40-watches.rules <<'EOF'
# Passwd and shadow changes
-w /etc/passwd -p wa -k identity
-w /etc/shadow -p wa -k identity
-w /etc/group -p wa -k identity
-w /etc/gshadow -p wa -k identity
# sudoers modifications
-w /etc/sudoers -p wa -k sudoers
-w /etc/sudoers.d/ -p wa -k sudoers
# SSH server config
-w /etc/ssh/sshd_config -p wa -k sshd_config
# Audit config self-protection
-w /etc/audit/ -p wa -k audit_rules
-w /etc/audit/rules.d/ -p wa -k audit_rules
EOF
sudo augenrules --load
auditctl -l # confirm rules are loaded
Writing Syscall Rules
Syscall rules are more powerful and more expensive. Use them for detecting privilege escalation, file deletion, and network socket creation by specific users or binaries.
Detect privilege escalation attempts
Watch for successful setuid/setgid calls by non-root users:
sudo tee /etc/audit/rules.d/50-privilege-escalation.rules <<'EOF'
# execve by any process that gained elevated privs
-a always,exit -F arch=b64 -S execve -C uid!=euid -F euid=0 -k setuid_exec
-a always,exit -F arch=b32 -S execve -C uid!=euid -F euid=0 -k setuid_exec
EOF
Detect file deletion
sudo tee /etc/audit/rules.d/55-delete.rules <<'EOF'
-a always,exit -F arch=b64 -S unlinkat,rename,renameat -F auid>=1000 -F auid!=4294967295 -k file_deletion
-a always,exit -F arch=b32 -S unlinkat,rename,renameat -F auid>=1000 -F auid!=4294967295 -k file_deletion
EOF
The magic value 4294967295 (-1 as unsigned 32-bit) means the kernel could not determine an audit UID — it filters out kernel threads and processes that ran before auditd started.
Watch outbound socket connections
sudo tee /etc/audit/rules.d/60-network.rules <<'EOF'
-a always,exit -F arch=b64 -S connect -F auid>=1000 -F auid!=4294967295 -k user_connect
EOF
Lock the ruleset
Place this as the last line in a file like 99-finalize.rules. It makes the ruleset immutable until reboot — even root cannot change it.
echo '-e 2' | sudo tee /etc/audit/rules.d/99-finalize.rules
Warning: With -e 2 active, augenrules --load will fail until reboot. Only add this line when your ruleset is stable.
sudo augenrules --load
Querying Events with ausearch
ausearch reads /var/log/audit/audit.log (and rotated logs) and outputs structured event records. Use -i to interpret UIDs and syscall names into human-readable strings.
Search by key
sudo ausearch -k identity -i
Search by user
sudo ausearch -ua 1001 -i # by audit UID
sudo ausearch -ui bob -i # by username (resolves via NSS)
Filter by time window
sudo ausearch -k sudoers -ts today -i
sudo ausearch -k file_deletion -ts '2024-05-01 08:00:00' -te '2024-05-01 18:00:00' -i
Filter by return code (failed events)
sudo ausearch -k user_connect -sv no -i # only failed connects
Pipe to aureport for quick summaries
sudo ausearch -k setuid_exec -i | aureport -f -i --summary
Generating Reports with aureport
aureport produces tabular summaries directly from the audit log. No pipe required for the common reports.
Overall system summary
sudo aureport --summary
Authentication events
sudo aureport -au -i # all auth attempts
sudo aureport -au --failed -i # only failures — spot brute-force fast
Executable and syscall reports
sudo aureport -x --summary -i # top executed binaries
sudo aureport -s --summary -i # top syscalls recorded
Key-tagged rule hits
sudo aureport -k --summary -i
Anomaly events
sudo aureport --anomaly -i
Verifying the Setup
Trigger a known event and confirm it is recorded end-to-end:
# Touch a watched file as a normal user
sudo -u nobody touch /etc/passwd 2>/dev/null || true
# Now look for it
sudo ausearch -k identity -ts recent -i
You should see an SYSCALL record with comm="touch", the UID of the calling process, and the identity key. If the record is absent, check auditctl -l to confirm the watch is loaded and journalctl -u auditd for errors.
Troubleshooting
- No events recorded after rule load: Run
auditctl -l— if the list is empty,augenrules --loadmay have encountered a syntax error. Checkjournalctl -u auditd -n 50. - High CPU / disk I/O: Syscall rules on common calls (
read,write) without tight filters will flood the log. Always scope with-F auid>=1000or a specific-F exe=path. - Arch mismatch — rules silently ignored: On 64-bit systems always add both
arch=b64andarch=b32rule pairs; 32-bit binaries running under a 64-bit kernel use different syscall numbers. - augenrules exits non-zero after adding -e 2: Expected. The kernel rejected the reload because the ruleset is locked. Rules from before the lock are still active. Reboot to apply changes to a locked ruleset.
- ausearch returns nothing for a key: Confirm the key spelling with
auditctl -l | grep -k; keys are case-sensitive. - Fedora/RHEL SELinux AVC messages in audit.log: These are normal. Use
ausearch -m AVC -ito separate them from your custom-keyed rules.
Frequently asked questions
- What is the difference between auditctl -w and a syscall rule?
- A -w file-watch rule is syntactic sugar that internally translates to inode-based path watches using the 'path' filter. Syscall rules (-a always,exit -S) give you finer control: you can filter by architecture, UID, EUID, executable path, or return value, and capture events that have no direct file path association.
- Why do I need both arch=b64 and arch=b32 syscall rules?
- A 64-bit kernel can run 32-bit ELF binaries. These use a different syscall table (int 0x80 ABI vs. syscall), so a rule specifying only arch=b64 is invisible to 32-bit processes. Omitting the b32 variant is a common audit gap.
- Will enabling heavy syscall rules noticeably slow down my system?
- Broadly scoped rules on high-frequency syscalls like read or write will generate enormous log volume and measurable overhead. Always narrow rules with filters such as -F auid>=1000 or -F exe=/path/to/binary. File watches on infrequently written config files have negligible performance impact.
- How do I rotate or archive audit logs without losing events?
- auditd handles log rotation internally via the max_log_file_action and num_logs settings in /etc/audit/auditd.conf. Set max_log_file_action=ROTATE and num_logs to how many compressed logs to retain. For long-term archival, configure the audisp-remote plugin or pipe logs to a SIEM.
- Can auditd rules survive a reboot?
- Yes, but only rules written to /etc/audit/rules.d/ and compiled with augenrules. Rules added with auditctl at the command line are runtime-only and are lost when the service restarts or the system reboots.
Related guides
Manage Secrets with Ansible Vault
Encrypt Ansible secrets with AES-256 using ansible-vault: encrypt files and inline vars, automate with password files, and isolate group-level secrets with vault IDs.
AppArmor Explained
Learn how AppArmor profiles work, how to switch between enforce and complain mode, create new profiles, and diagnose access denials on Ubuntu, Debian, and Arch.
Apply CIS Benchmarks with OpenSCAP
Use OpenSCAP and scap-security-guide to evaluate, report on, and remediate Linux systems against CIS Benchmarks — covering install, eval, and automation.
How to Audit a Linux System with auditd
Set up auditd on Linux to track file access, syscalls, and privilege use. Covers persistent rules, file watches, ausearch, and aureport across major distros.