📑 Daftar Isi
- 1. Install the Audit Package
- 2. Enable and Verify the Service
- 3. Understand the Two Rule Types
- 4. Write File Integrity Rules
- 5. Load Rules and Lock Them Down
- 6. Test It: Touch a File, Then Read the Log
- 7. Set Up Log Rotation
- 8. Read Logs with aureport
- 9. Performance: Don't Watch Everything
- Common Troubleshooting
Skip the fluff. You run production servers, and on those servers are files that must never change without you knowing. /etc/passwd. /etc/ssh/sshd_config. Cron jobs. Application configs. The question is simple: how do you actually find out if someone touched them? Short answer: auditd.
And no, this isn’t some heavyweight Linux Security Module like SELinux or AppArmor. auditd is the kernel audit daemon, and its entire job is to record. Who accessed which file, through which program, at what time, from which login session. Think of it as a security camera, but for the command line.
The core problem is that Linux doesn’t record file access by default. The kernel is capable of it, but the audit subsystem ships disabled. So when an attacker (or an admin having a bad day) modifies a critical file, the system just sits there. No trace, no timestamp, no log. You notice a week later when something feels off, and by then reconstructing what happened and when is nearly impossible. And it’s not just a security thing. If you operate under compliance frameworks like PCI-DSS or ISO 27001, file integrity monitoring isn’t optional, it’s a hard requirement. When an external auditor asks you to prove /etc/shadow was never modified without authorization and you can’t, that’s a very long, very uncomfortable conversation.
Symptoms are often subtle, too. A service starts acting weird with a config you don’t recognize. A new SSH user appears that you never created. Load spikes at odd hours. With auditd running from day one, you just query the log: who touched this file last? When? From where? It’s all right there. It saves you hours of guessing.
Okay, enough theory. Let’s get our hands dirty. Everything below comes from setups I’ve actually deployed on client servers, from Ubuntu 22.04 to AlmaLinux 9. Every command is safe to copy-paste, and if all you want is a solid baseline finished by today, just follow the order top to bottom. Don’t skip ahead, especially the immutable part.
1. Install the Audit Package
On Debian/Ubuntu, install two packages at once: auditd and audispd-plugins (the second one is for notifications later, if you end up needing them).
sudo apt update
sudo apt install auditd audispd-plugins
For the RHEL family (Rocky, AlmaLinux, CentOS):
sudo dnf install audit audispd-plugins
2. Enable and Verify the Service
sudo systemctl enable auditd
sudo systemctl start auditd
sudo systemctl status auditd
If it says active (running), good. But here’s the trap: a running daemon doesn’t mean active rules. Plenty of people stop right here thinking they’re protected. auditd with no rules is a security camera that’s powered on but has no lens.
3. Understand the Two Rule Types
| Rule Type | Example | Best For |
|---|---|---|
| File watch (-w) | auditctl -w /etc/passwd -p wa -k passwd_changes | Watching specific files whose contents must not change |
| Syscall filter (-a) | auditctl -a always,exit -F arch=b64 -S openat -F dir=/etc -k etc_open | Low-level access patterns, good for directories |
For file integrity, the first type (file watch) is your workhorse. It’s lightweight, and it maps exactly to what you want: ‘tell me when this file gets written or appended.’ The letters after -p are permissions: r=read, w=write, a=append, x=execute. wa means write + append. Add r if you also want to know about reads, but expect a much bigger log volume, so use it only for the most sensitive files.
4. Write File Integrity Rules
Ground rule: write your rules into /etc/audit/rules.d/audit.rules so they survive reboots. If you add rules with auditctl on the fly, they’re gone the moment the box restarts. This is the list I typically deploy:
-w /etc/passwd -p wa -k passwd_changes
-w /etc/shadow -p wa -k shadow_changes
-w /etc/group -p wa -k group_changes
-w /etc/sudoers -p wa -k sudoers_changes
-w /etc/ssh/sshd_config -p wa -k sshd_config_changes
-w /etc/crontab -p wa -k cron_changes
-w /etc/cron.d/ -p wa -k cron_changes
-w /etc/hosts -p wa -k hosts_changes
-w /etc/ld.so.preload -p wa -k preload_changes
-w /root/.bashrc -p wa -k root_shell_changes
-w /root/.ssh/authorized_keys -p wa -k root_ssh_keys
Take a close look at /etc/ld.so.preload in that list. It’s a rootkit favorite. If an attacker writes a shared object path in there, every process on the system loads their payload silently. A tiny file, huge damage. Watch it.
Also note the trailing slash on /etc/cron.d/. Directory watches are recursive, so everything inside gets covered. The same trick applies when you want to watch an application directory later.
5. Load Rules and Lock Them Down
sudo augenrules --load
sudo auditctl -l
augenrules collects every .rules file in /etc/audit/rules.d, merges them, and loads them into the kernel. Then auditctl -l shows you what’s actually active. If a rule had a typo, it won’t show up in the output. Always verify.
Finally, add one line at the very bottom of the rules file: -e 2. That’s immutable mode. Once loaded, rules can’t be modified, appended, or deleted until reboot. It stops an attacker (or a panicking admin) from killing your audit trail mid-incident. But know the trade-off: to change rules later, you need a reboot. So finalize your rule list first, then add -e 2 as the last thing.
6. Test It: Touch a File, Then Read the Log
Make a harmless change to a watched file. Append a line to /etc/hosts, then remove it. Wait a second, then query:
sudo ausearch -k hosts_changes -i
Here’s what you’ll see (identifiers sanitized):
----
time->Mon Jul 20 03:12:44 2026
type=PATH msg=audit(1752963164.412:1234): item=1 name="/etc/hosts" inode=12345 dev=08:01 mode=file,640 ouid=root ogid=root rdev=00:00 nametype=NORMAL
type=PATH msg=audit(1752963164.412:1234): item=0 name="/etc" inode=12344 dev=08:01 mode=dir,755 ouid=root ogid=root rdev=00:00 nametype=PARENT
type=CWD msg=audit(1752963164.412:1234): cwd="/root"
type=SYSCALL msg=audit(1752963164.412:1234): arch=c000003e syscall=257 success=yes exit=12 a0=ffffff9c a1=55d41e2f5f20 a2=1 a3=1 items=2 ppid=1721 pid=2073 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts0 ses=2 comm="tee" exe="/usr/bin/tee" subj==unconfined key="hosts_changes"
type=PROCTITLE msg=audit(1752963164.412:1234): proctitle=7465653A202F6574632F686F737473
What matters in this output:
- time: 03:12:44, when the change happened. If it’s 3 AM and you weren’t doing anything, that’s red flag number one.
- comm and exe: tee (or vim, nano, whatever), the program that touched the file.
- auid=1000: the original login user. This is different from uid. Auid means ‘original user id’ and represents who actually logged in from a terminal. So even if an attacker su’s to root from a regular user, the auid still gives them away.
- ppid and pid: the process tree. Useful for tracing which process called what.
- key: hosts_changes, the rule group you created earlier. This makes filtering trivial.
Notice the proctitle field too. It’s a hex dump of the command line that ran. Decode it and the proctitle above becomes tee: /etc/hosts. So you know the exact command that was executed.

7. Set Up Log Rotation
Audit logs grow fast when you have many rules. Configure rotation in /etc/audit/auditd.conf so you never fill the disk:
max_log_file = 8
num_logs = 5
max_log_file_action = ROTATE
space_left = 512
space_left_action = SYSLOG
admin_space_left = 256
action_mail_acct = root
Quick rundown: max_log_file 8 means 8 MB per file. Once it hits 8 MB, the old file rotates (max_log_file_action = ROTATE), and num_logs 5 keeps five generations (audit.log, audit.log.1, etc.). space_left 512 means when free disk hits 512 MB, log a warning to syslog. At 256 MB (admin_space_left), auditd stops writing entirely to avoid a completely full disk.
After editing the config, reload the daemon: sudo systemctl restart auditd. On some older RHEL versions, systemctl restart can act weird with auditd. If you hit that, use sudo service auditd restart. Either way, confirm the service is active again afterward.
8. Read Logs with aureport
ausearch finds specific events; aureport gives you statistical summaries:
sudo aureport --summary
sudo aureport -f --success
sudo aureport -au
- –summary: a rundown of all event types, most frequent first.
- -f –success: successful file access, useful for auditing who’s opening sensitive files.
- -au: authentication, showing login success and failure patterns.
For daily monitoring, I usually run these manually during an incident, or ship the summary to email/Telegram via a small script. That’s a topic for another article though.
9. Performance: Don’t Watch Everything
This is what makes people give up halfway: the server feels heavy, so auditd takes the blame. Usually it’s not auditd, it’s the rules. The classic mistakes:
- Watching /var recursively, so every app read of a cache file gets logged and the log fills in hours.
- Watching /usr/bin with -p x, logging every single program execution. On a busy server, that’s a recipe for a full disk.
- Never checking the backlog. If auditd gets flooded, events it can’t process get dropped (lost). And you won’t notice unless you check auditctl -s.
Check audit health anytime with: sudo auditctl -s
Watch backlog and lost in the output. If backlog keeps creeping toward backlog_limit and lost starts climbing, your rules are too broad. Trim them. On a healthy system, lost should be 0.
Common Troubleshooting
| Symptom | Likely Cause | Fix |
|---|---|---|
| ausearch empty even though rules exist | Rules loaded but no event happened yet | Test by writing a file, then re-check; confirm the daemon is running |
| auditd active but no rules at all | Rules were added via auditctl only / rules file is empty | Write them in /etc/audit/rules.d, then run augenrules –load |
| Lost events keep climbing | Buffer overflow / too many rules | Trim watches, or raise the buffer (e.g. -b 8192) |
| /var/log/audit disk full | Rotation not configured | Set max_log_file_action = ROTATE and num_logs |
| systemctl restart auditd fails | Older RHEL versions | Use service auditd restart |
| auid = 4294967295 | SSH key login / no loginuid | Normal for certain sessions; comm and exe are still recorded |
| Can’t change rules anymore | Immutable mode (-e 2) is active | Reboot, or schedule a maintenance window |
If you want to go deeper, here are a few articles that connect well: how to read journalctl logs to cross-reference audit trails with system logs, troubleshooting high load on servers when load spikes for no obvious reason, and VPS monitoring basics to build out a complete monitoring pattern. For the deeper command reference, the official auditd man page has everything.
Q: Can auditd detect rootkits?
Not exactly. auditd is a recorder, not a scanner. It won’t tell you ‘this is a rootkit.’ But almost every rootkit modifies system files (ld.so.preload, crontab, binaries), and those modifications are guaranteed to be logged. So it doesn’t catch the malware itself, it catches the footprints. Pair auditd with AIDE (file checksums) and an EDR/antivirus and you have a healthy stack.
Q: What’s the difference between auditd and AIDE?
AIDE hashes your files and compares against a baseline, usually on a nightly schedule. auditd works in real time. Think of it this way: AIDE tells you ‘this file changed since last night,’ auditd tells you ‘this file changed at this exact time by this exact process.’ They complement each other; they’re not replacements.
Q: I set -e 2 immutable. How do I change rules if I really need to?
Without a reboot, your only way out is booting into single-user mode or passing init=/bin/bash through the bootloader. That’s exactly why you always put -e 2 last, after your rules are final. Don’t experiment with it on a live production box.
Q: How much performance impact does auditd actually have?
With sensible rules (watching specific sensitive files, not giant directories), the impact is negligible, well under 1% CPU. What kills performance is rules that log every exec or every read. Start small, watch auditctl -s, and grow the rule set gradually.
Before you close this page, make sure all five are done: install and enable auditd, write your integrity rules into /etc/audit/rules.d/audit.rules, load with augenrules and verify with auditctl -l, configure rotation in auditd.conf, and run one test change then confirm it shows up in ausearch. All five green? Case closed. Done.