Normal logs tell you that a service restarted or someone logged in. auditd, the Linux audit framework, records who changed /etc/passwd, which user ran commands as root, and what touched your web config, at the kernel level. That makes the trail hard to bypass. It’s the standard tool for compliance (PCI DSS, ISO 27001) and very useful for investigating incidents.
Install and start
sudo apt install auditd audispd-plugins # Debian/Ubuntu
sudo dnf install audit # RHEL family (usually preinstalled)
sudo systemctl enable --now auditd
sudo auditctl -s # status: enabled 1
Logs go to /var/log/audit/audit.log.
Rules: what to audit
Rules come in two main types:
- File watches (
-w) log access to specific files or directories. - Syscall rules (
-a) log specific system calls, filtered by user, architecture and so on.
Put persistent rules in /etc/audit/rules.d/*.rules:
sudo tee /etc/audit/rules.d/50-server.rules > /dev/null <<'EOF'
## Identity and authentication
-w /etc/passwd -p wa -k identity
-w /etc/group -p wa -k identity
-w /etc/shadow -p wa -k identity
-w /etc/sudoers -p wa -k sudoers
-w /etc/sudoers.d/ -p wa -k sudoers
## SSH configuration and keys
-w /etc/ssh/sshd_config -p wa -k sshd
-w /etc/ssh/sshd_config.d/ -p wa -k sshd
-w /root/.ssh/ -p wa -k rootkeys
## Scheduled tasks (common persistence)
-w /etc/crontab -p wa -k cron
-w /etc/cron.d/ -p wa -k cron
-w /var/spool/cron/ -p wa -k cron
-w /etc/systemd/system/ -p wa -k systemd
## Web server config
-w /etc/nginx/ -p wa -k nginx
## Commands executed as root by a logged-in user
-a always,exit -F arch=b64 -S execve -F euid=0 -F auid>=1000 -F auid!=unset -k root-cmd
EOF
sudo augenrules --load
sudo auditctl -l # list active rules
-p wa— log writes and attribute changes (rfor reads,xfor execution).-k— a key for searching later.auidis the audit UID, the user who originally logged in. It’s preserved throughsudoandsu, so you see “alice ran this as root”.
Start small. Auditing every execve system-wide produces huge logs on busy servers. The rule above limits it to root commands from human logins.
Search: ausearch
sudo ausearch -k identity -i # changes to user/group files, human-readable
sudo ausearch -k sshd -ts today -i
sudo ausearch -k root-cmd -ts recent -i # root commands in the last 10 minutes
sudo ausearch -f /etc/nginx/nginx.conf -i # anything touching this file
sudo ausearch -ua alice -ts yesterday -i # everything by alice (auid)
-i translates numeric UIDs, syscalls and so on into names. Example output:
type=SYSCALL msg=audit(10/14/2026 09:12:44.311:2210) : … auid=alice uid=root … comm=vim exe=/usr/bin/vim key=sshd
type=PATH … name=/etc/ssh/sshd_config … nametype=NORMAL
The user alice, logged in via sudo, edited sshd_config with vim at 09:12:44.
Reports: aureport
sudo aureport --summary
sudo aureport -au # authentication attempts
sudo aureport -l # logins
sudo aureport -k # events by key
sudo aureport -x --summary # executables
sudo aureport --failed # failed events
Log size and rotation
auditd rotates its own logs. Don’t add them to logrotate. Settings are in /etc/audit/auditd.conf:
max_log_file = 50 # MB per file
num_logs = 10
max_log_file_action = ROTATE
space_left_action = SYSLOG
disk_full_action = SUSPEND
On small VPS disks, keep max_log_file × num_logs reasonable and monitor disk space.
Make logs tamper-resistant
An attacker with root can delete local logs. Forward audit events off the server with the audisp syslog plugin (/etc/audit/plugins.d/syslog.conf, active = yes) plus rsyslog forwarding. See rsyslog.
For the strictest setups, add -e 2 as the last rule to lock the audit configuration until reboot. Changing the rules then requires a reboot, which is itself an auditable event.
Performance
Audit rules cost a little CPU per matching event. File watches on config directories are negligible. Broad syscall rules (every open, every execve by every user) can be noticeable on busy servers. Measure with auditctl -s (lost events, backlog) and keep the rules focused.
Quick reference
| Task | Command |
|---|---|
| Status | auditctl -s |
| List rules | auditctl -l |
| Load rules from rules.d | augenrules --load |
| Watch a file | -w /path -p wa -k key |
| Search by key | ausearch -k key -i |
| Search by user | ausearch -ua USER -i |
| Summary | aureport --summary |
Related: AIDE file integrity, Lynis and history.