The Linux Audit framework records security-relevant events directly from the kernel: who changed a file, which commands ran with root privileges, when a kernel module was loaded. auditd is the user-space daemon that writes those events to disk, and it is the standard answer to questions like "who edited /etc/sudoers last night?". In this tutorial you will install auditd on Ubuntu 24.04, configure log retention, write a persistent rule set for the events that matter on a typical server, and search the results with ausearch and aureport.
Prerequisites
To follow this tutorial, you will need:
- A server running Ubuntu 24.04 LTS on x86_64, for example a CubePath VPS, with a non-root user that has
sudoprivileges. - Some free disk space for the audit logs. The configuration in this guide keeps up to 500 MB.
The syscall rules in this guide use arch=b64 and arch=b32 filters for x86_64. On arm64 servers, drop the b32 lines and note that some legacy syscalls (for example unlink and rename) do not exist there; use only their *at variants.
Step 1 - Installing auditd
Install the daemon and its tools:
sudo apt update
sudo apt install auditd
The package enables and starts the service. Check that it is running:
sudo systemctl status auditd
● auditd.service - Security Auditing Service
Loaded: loaded (/usr/lib/systemd/system/auditd.service; enabled; preset: enabled)
Active: active (running) since Thu 2026-09-25 10:02:11 UTC; 8s ago
Then query the kernel side of the audit system:
sudo auditctl -s
enabled 1
failure 1
pid 2317
rate_limit 0
backlog_limit 8192
lost 0
backlog 0
enabled 1 means the kernel is auditing, and lost 0 means no events have been dropped. You will come back to the lost counter in the troubleshooting section.
Step 2 - Configuring log retention
The daemon settings are in /etc/audit/auditd.conf. The Ubuntu defaults keep only five files of 8 MB each, which a busy server can fill in a day. Open the file:
sudo nano /etc/audit/auditd.conf
Adjust these keys and leave the rest as they are:
max_log_file = 50
num_logs = 10
max_log_file_action = ROTATE
space_left = 75
space_left_action = SYSLOG
admin_space_left = 50
admin_space_left_action = SUSPEND
disk_full_action = SUSPEND
With these values auditd keeps ten files of 50 MB in /var/log/audit/, rotating the oldest out. The three *_action keys decide what happens when the partition runs low (space_left and admin_space_left are in megabytes): first a warning to syslog, then auditd stops writing until space is freed. SUSPEND keeps the server running; HALT is only appropriate on systems where running without audit is not allowed.
Restart the daemon to apply the changes:
sudo systemctl restart auditd
Step 3 - Understanding audit rules
There are three kinds of rules, all written in the syntax that auditctl accepts:
- Control rules configure the audit system itself, for example
-b 8192(kernel buffer size) or-e 2(lock the configuration). - File watches (
-w) record access to a file or directory.-pselects the access type:rread,wwrite,xexecute,aattribute change. - Syscall rules (
-a always,exit) record system calls that match a set of filters (-F), such as the architecture, the user or the path.
Every rule should carry a key (-k name), a label that you later use to search the logs.
You can add a rule at runtime to try it out. It will be lost at the next restart:
sudo auditctl -w /etc/hosts -p wa -k hosts_change
sudo auditctl -l
-w /etc/hosts -p wa -k hosts_change
Remove all runtime rules again before continuing:
sudo auditctl -D
Step 4 - Writing a persistent rule set
On Ubuntu, persistent rules live in /etc/audit/rules.d/. At startup, augenrules concatenates every *.rules file in that directory in alphabetical order into /etc/audit/audit.rules and loads it. The package already installs audit.rules there, which clears existing rules (-D), sets the buffer size and the failure mode.
Create a new file for your rules:
sudo nano /etc/audit/rules.d/50-server.rules
Add the following rules:
## Identity: users, groups and passwords
-w /etc/passwd -p wa -k identity
-w /etc/group -p wa -k identity
-w /etc/shadow -p wa -k identity
-w /etc/gshadow -p wa -k identity
## Privilege configuration
-w /etc/sudoers -p wa -k sudoers
-w /etc/sudoers.d/ -p wa -k sudoers
## SSH server configuration and authorized keys of root
-w /etc/ssh/sshd_config -p wa -k sshd_config
-w /etc/ssh/sshd_config.d/ -p wa -k sshd_config
-w /root/.ssh/ -p wa -k root_ssh
## Scheduled tasks
-w /etc/crontab -p wa -k cron
-w /etc/cron.d/ -p wa -k cron
-w /var/spool/cron/ -p wa -k cron
## Every command run as root by a logged-in user (via sudo, su or a root shell)
-a always,exit -F arch=b64 -S execve -F euid=0 -F auid>=1000 -F auid!=unset -k root_exec
-a always,exit -F arch=b32 -S execve -F euid=0 -F auid>=1000 -F auid!=unset -k root_exec
## Kernel module loading and unloading
-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
## System time changes
-a always,exit -F arch=b64 -S adjtimex,settimeofday,clock_settime -k time_change
-a always,exit -F arch=b32 -S adjtimex,settimeofday,clock_settime -k time_change
-w /etc/localtime -p wa -k time_change
## Files deleted or renamed by real users (not system services)
-a always,exit -F arch=b64 -S unlink,unlinkat,rename,renameat -F auid>=1000 -F auid!=unset -k delete
-a always,exit -F arch=b32 -S unlink,unlinkat,rename,renameat -F auid>=1000 -F auid!=unset -k delete
A few details are worth understanding:
auidis the audit user ID: the UID a person logged in with. It stays the same aftersudoorsu, which is how auditd knows who was really behind an action run as root.auid!=unsetexcludes processes that were never started from a login, such as system daemons.- The
root_execrules record every program executed with root privileges by a person who logged in with a normal account (UID 1000 or higher), including every command typed in asudo -ishell. Commands started by system services are not recorded, which keeps the volume manageable. - Each syscall rule has a
b64and ab32version, because a 64-bit process can also call the 32-bit syscall interface and would otherwise evade the rule.
Load the new rules and list them:
sudo augenrules --load
sudo auditctl -l
-w /etc/passwd -p wa -k identity
-w /etc/group -p wa -k identity
-w /etc/shadow -p wa -k identity
...
-a always,exit -F arch=b64 -S execve -F euid=0 -F auid>=1000 -F auid!=-1 -F key=root_exec
If augenrules prints an error, it names the rule and line that failed. The Ubuntu package also ships ready-made rule sets for compliance frameworks (STIG, PCI DSS, OSPP) in /usr/share/doc/auditd/examples/rules/, which you can copy into /etc/audit/rules.d/ if you need them.
Step 5 - Testing the rules
Trigger a few events. Add a test user, which modifies /etc/passwd, /etc/shadow and /etc/group:
sudo useradd audittest
Search the log by key with ausearch. The -i flag translates numeric UIDs, syscalls and timestamps into readable text:
sudo ausearch -k identity -i --start today
----
type=PROCTITLE msg=audit(09/25/2026 10:31:42.118:412) : proctitle=useradd audittest
type=PATH msg=audit(09/25/2026 10:31:42.118:412) : item=1 name=/etc/passwd inode=3159 nametype=CREATE ...
type=SYSCALL msg=audit(09/25/2026 10:31:42.118:412) : arch=x86_64 syscall=rename success=yes exit=0 ... auid=your_user uid=root gid=root euid=root ... comm=useradd exe=/usr/sbin/useradd key=identity
The SYSCALL record shows the important part: the command (useradd), the effective user (root) and the person who ran it (auid=your_user).
Now test the root_exec rule:
sudo cat /etc/shadow > /dev/null
sudo ausearch -k root_exec -i --start recent
--start recent means the last 10 minutes. You will see EXECVE records with the full command lines (sudo cat /etc/shadow and cat /etc/shadow) and your user as auid, even though the command ran as root.
Remove the test user when you are done:
sudo userdel audittest
Step 6 - Searching and reporting
ausearch filters events and aureport summarizes them. These are the searches you will use most:
sudo ausearch -k sudoers -i
sudo ausearch -ua your_user -i --start yesterday
sudo ausearch -f /etc/ssh/sshd_config -i
sudo ausearch -m USER_LOGIN --success no -i
They find, in order: changes to sudo configuration, everything done by a given login user since yesterday, every event touching a file, and failed logins.
For an overview, start with the summary report:
sudo aureport --summary
Then drill down with the specific reports:
sudo aureport -au --failed -i
sudo aureport -k --summary
sudo aureport -x --summary
The first lists failed authentication attempts, the second counts events per rule key (useful to find a noisy rule), and the third shows which executables generated the most events.
Both tools accept --start and --end with values such as today, yesterday, this-week, recent or an explicit date and time (--start 09/24/2026 18:00:00, in your locale's date format).
Step 7 - Locking the configuration (optional)
An attacker who gains root can simply delete your audit rules. To prevent that, make the rules immutable: once loaded, they cannot be changed until the next reboot. Create a file that sorts last:
sudo nano /etc/audit/rules.d/99-finalize.rules
Add a single line:
-e 2
Load the rules:
sudo augenrules --load
sudo auditctl -s | grep enabled
enabled 2
From now on, any auditctl change fails with Operation not permitted, and changing the rules requires editing the files and rebooting. Enable this only after your rule set is stable.
Step 8 - Forwarding audit events off the server
Local audit logs can be deleted by someone with root access. Sending a copy to another system makes tampering visible. The simplest option is the syslog plugin, which passes every event to rsyslog (and from there to your central log server or SIEM):
sudo nano /etc/audit/plugins.d/syslog.conf
Change active = no to:
active = yes
Restart auditd:
sudo systemctl restart auditd
Events now also appear in the journal and in /var/log/syslog, tagged audisp-syslog. Configure rsyslog to forward them to your log server over TCP or TLS. To send events directly to another auditd server instead, the audispd-plugins package provides the audisp-remote plugin, documented in man audisp-remote.conf.
Troubleshooting
auditctl -s shows a growing lost counter. The kernel buffer overflowed during a burst of events. Raise -b 8192 in /etc/audit/rules.d/audit.rules (for example to -b 32768) and look for a noisy rule with sudo aureport -k --summary.
augenrules --load reports Error sending add rule data request (Invalid argument). A syscall in the rule does not exist on this architecture (common on arm64) or a field is misspelled. Check the line it reports.
Rules disappear after a reboot. They were added with auditctl only. Put them in a file under /etc/audit/rules.d/.
/var/log/audit/ fills the disk. Lower max_log_file or num_logs, and find the rule responsible with aureport -k --summary. Watches on busy directories and syscall rules without auid filters are the usual cause.
auditctl returns Operation not permitted for every change. The configuration is locked with -e 2. Remove 99-finalize.rules, make your changes and reboot.
Conclusion
Your server now records changes to accounts, sudo and SSH configuration, root command execution, kernel modules and time, keeps the logs rotated, and can forward them to a central system. Start small and check the per-key summary regularly to keep the rule set useful rather than noisy. As next steps, forward the events to a central log platform, add watches for your own application configuration files, and review failed authentications with aureport -au --failed as part of your routine.
