Installing and Configuring Auditd on Ubuntu Server: Tracing User Actions Down to the Kernel Level

Ubuntu tutorial - IT technology blog
Ubuntu tutorial - IT technology blog

A Monday Morning Nightmare: Nginx Crashes and the Trail Goes Cold

At 9:00 AM on a Monday morning, the system suddenly started throwing nonstop 502 Bad Gateway errors. Customer complaints began pouring in. Checking /etc/nginx/nginx.conf, I discovered that someone had modified an upstream block at 11:15 PM the night before, causing Nginx syntax checks to fail on reload. Digging into /var/log/auth.log revealed that three DevOps engineers had SSHed into the server around that exact time.

When I inspected each user’s ~/.bash_history, the investigation hit a complete dead end. The command history was blank. One person had used a tmux session and disconnected abruptly due to network drops, another shared a deploy account, or the edit command had been executed behind the scenes via a bash script. No one took ownership. Who opened the file? Which process overwrote the data? Who was the real UID behind the action? All traces had vanished into thin air.

Why Ubuntu’s Default Logging Is Easy to Bypass

Many sysadmins rely heavily on syslog, journalctl, or bash history when investigating incidents. Unfortunately, these mechanisms only operate on the surface and are remarkably easy to circumvent:

  • Bash history lives in User Space: A user can easily type set +o history, prefix commands with a space, or simply delete ~/.bash_history, erasing their operational footprint without a trace.
  • Syslog passively waits for applications to report: Nginx or SSH only log predefined application events (like logins or reloads). They are completely blind when someone uses vim, nano, or sed to peek at a private key inside /etc/ssl/private/.
  • The trail goes cold after privilege escalation: When someone executes sudo su to switch to root, their original identity is easily lost. Without a mechanism tracking the Audit User ID (auid) tied to the initial login session, you have no reliable way to identify who is operating behind that root account.

Makeshift Workarounds and Their Real-World Limits

Before turning to Auditd, our team tried several common workarounds, but all came with serious drawbacks:

1. Locking Files with chattr +i

Running chattr +i /etc/nginx/nginx.conf prevents edits and deletions effectively—even root is blocked. However, this approach is only practical for purely static files. Whenever a CI/CD pipeline needs to update the configuration, the deployment fails. Furthermore, it provides zero visibility into who attempted to tamper with the file.

2. File Integrity Monitoring with AIDE or Tripwire

These tools periodically scan file hashes via cron jobs (e.g., every 30 minutes). Their fatal flaw is latency. They only notify you after the file has already been altered, lack real-time alerting, and cannot pinpoint the perpetrator’s PID or UID.

3. Monitoring with inotifywait Scripts

While custom scripts catch file events quickly, they consume excessive CPU and RAM when directories contain many nested files. Under heavy disk I/O bursts, the inotify event queue overflows, causing the script to crash immediately.

The Enterprise Standard: Capturing Kernel-Level Events with Auditd

Instead of playing catch-up at the application layer, the Linux Audit Daemon (Auditd) intercepts events directly inside the Linux Kernel. Regardless of whether a user runs sophisticated scripts or deletes logs in user space, every system call—such as openat, execve, or unlink—is captured intact by the kernel.

Below is a configuration battle-tested on an Ubuntu 22.04 LTS cluster handling approximately 3,500 requests per second. It ensures all sensitive activities are tracked while keeping Auditd’s CPU usage below 1.5%.

Step 1: Install Auditd

Ubuntu Server typically does not have Auditd enabled out of the box. Install the required packages using the following commands:

sudo apt update
sudo apt install -y auditd audispd-plugins
sudo systemctl enable --now auditd
sudo systemctl status auditd

Step 2: Configure Audit Rules Following Security Best Practices

Auditd loads rules from /etc/audit/rules.d/. Never edit the temporary /etc/audit/audit.rules file directly, as it gets overwritten on every reboot. Instead, create a dedicated rule file:

sudo nano /etc/audit/rules.d/custom-security.rules

Paste in the following production-ready rules:

## 1. Delete all existing rules from kernel memory
-D

## 2. Increase buffer size to 8192 (prevents log drops during I/O spikes)
-b 8192

## When buffer is full: 1 = send syslog warning, 2 = kernel panic (use 1 for safety)
-f 1

## 3. Monitor changes to user accounts and sudo privileges
-w /etc/passwd -p wa -k identity_changes
-w /etc/shadow -p wa -k identity_changes
-w /etc/group -p wa -k identity_changes
-w /etc/sudoers -p wa -k sudoers_tamper
-w /etc/sudoers.d/ -p wa -k sudoers_tamper

## 4. Monitor Nginx configuration and SSL certificate changes
-w /etc/nginx/ -p wa -k nginx_config_changes
-w /etc/ssl/ -p rwa -k ssl_key_access

## 5. Capture system calls attempting to alter system time
-a always,exit -F arch=b64 -S adjtimex,settimeofday,clock_settime -k time_modification

## 6. Monitor service management and firewall modifications
-w /bin/systemctl -p x -k service_mgmt
-w /sbin/iptables -p x -k iptables_mgmt
-w /sbin/ufw -p x -k firewall_mgmt

## 7. Lock configuration (prevents root from disabling auditd; only removable via server reboot)
-e 2

Key parameters to remember:

  • -w <path>: File or directory path to monitor (Watch).
  • -p rwa: Permissions/actions to capture: r (read), w (write), a (attribute change like chmod/chown), x (execute).
  • -k <key_name>: Filter key tag to easily search and parse logs later.
  • -e 2: Sets rules into immutable mode. Even the root user cannot modify or delete rules via auditctl until the server is rebooted.

Once saved, load the rules into the kernel using:

sudo augenrules --load
sudo auditctl -l

Step 3: Track Down the Real Identity Using ausearch

All audit events are logged to /var/log/audit/audit.log. Avoid using grep or cat directly, as raw audit logs are filled with encoded hex strings that are hard to decipher. The standard tool for this is ausearch.

Now, let’s search for who tampered with Nginx using the key nginx_config_changes:

sudo ausearch -k nginx_config_changes -i

Adding the -i (interpret) flag is crucial so the utility automatically translates UIDs, syscall numbers, and Unix timestamps into human-readable usernames and dates. The output will look like this:

type=SYSCALL msg=audit(23/10/2026 23:15:30.124:412) : arch=x86_64 syscall=openat success=yes exit=3 a0=0xffffff9c a1=0x55d78a2f4160 a2=O_WRONLY|O_CREAT|O_TRUNC items=2 ppid=1420 pid=2105 auid=hungnv uid=root euid=root comm=nano exe=/bin/nano key="nginx_config_changes"
type=PATH msg=audit(23/10/2026 23:15:30.124:412) : item=0 name="/etc/nginx/nginx.conf" inode=131075 dev=08:01 mode=file,644 ouid=root ogid=root

Pay close attention to the two most critical fields:

  • uid=root: The command was executed with root privileges via the /bin/nano editor.
  • auid=hungnv: This is the Audit UID. It accurately records that user hungnv initially authenticated via SSH before elevating to root with sudo su.

The culprit is unmasked—backed by solid, irrefutable evidence.

Step 4: Generate Quick Summary Reports with aureport

Every morning before starting work, you can run these three commands to get a complete security overview of the server over the last 24 hours:

## Check failed SSH login attempts (brute-force indicator)
sudo aureport -l --failed

## Summary of most accessed sensitive files
sudo aureport -f -i --summary

## Summary of root-level command executions
sudo aureport -x --summary

Step 5: Configure Log Rotation to Prevent Disk Overflow

On high-traffic servers, audit.log can grow by several gigabytes daily. To prevent disk exhaustion outages, open the configuration file /etc/audit/auditd.conf:

sudo nano /etc/audit/auditd.conf

Adjust the storage limits to safe thresholds:

# Limit each log file to a maximum of 50MB
max_log_file = 50

# Keep up to 10 rotated logs (caps total size at ~500MB)
num_logs = 10

# Automatically rotate logs when the size limit is reached
max_log_file_action = ROTATE

# Send warning to syslog when disk space drops below 100MB
space_left = 100
space_left_action = SYSLOG

# Suspend audit logging when free disk space falls below 50MB (avoids filling the root disk)
admin_space_left = 50
admin_space_left_action = SUSPEND

Save the file and apply the new configuration with:

sudo service auditd restart

Two Critical Caveats Before Going to Production

Auditd is extremely powerful, but misconfiguring it can backfire:

  • Never enable read watches (-p r) on high-churn directories: Placing read audits on /var/log, /tmp, or application cache directories causes the kernel to generate thousands of log events per second. This spikes disk IOPS and causes immediate server degradation.
  • Think twice before enabling -e 2: Once rules are locked into immutable mode, you cannot modify or delete any rule using auditctl. Applying any changes will strictly require a full server reboot. Thoroughly test your configuration in a Staging environment for at least 2 to 3 days before enabling this on Production.
Share: