Installing and Configuring Linux Auditd on CentOS Stream 9: Comprehensive Security Event Monitoring

CentOS tutorial - IT technology blog
CentOS tutorial - IT technology blog

A Real-World Scenario: A 2 AM Outage and the Challenge of Tracing Covert Changes

At 2:15 AM, PagerDuty started alerting non-stop. The Nginx reverse proxy cluster was completely down, and over 15,000 incoming HTTPS requests per minute were failing with 502 Bad Gateway errors. After SSHing into the production CentOS Stream 9 server, I discovered that the certificate file /etc/pki/tls/certs/api.crt had its permissions changed to 000. Running history showed a completely empty terminal—the culprit had either prepended a space to their commands or executed unset HISTFILE before exiting the shell.

Checking journalctl -u nginx and /var/log/messages yielded only a curt message: cannot load certificate: Permission denied. There was absolutely no trace of who had run the chmod command on the certificate file in the middle of the night.

That is the price of neglecting the Linux Audit Framework when provisioning new servers. Syslog and journald only passively record what applications report. In contrast, auditd hooks directly into the Linux kernel via the Audit Subsystem. Whenever a process makes a system call (reading, overwriting, deleting files, or changing ownership), the kernel immediately logs it. Most importantly, the auid (Audit UID) field allows the kernel to trace precisely which user account initiated the command, even if they ran sudo su - to switch to root.

Installing Auditd on CentOS Stream 9

On a CentOS Stream 9 Minimal installation, the audit package is usually present, but utility tools may be missing. Install the complete package suite with:

sudo dnf install -y audit audit-libs

Enable and start the daemon:

sudo systemctl enable auditd
sudo systemctl start auditd

Important note: Systemd on RHEL 9 and CentOS Stream 9 blocks the systemctl restart auditd command to prevent interruptions to the kernel’s security logging process. To restart the service, you must use:

sudo service auditd restart

Check the status of the audit subsystem in the kernel:

sudo auditctl -s

If the output returns enabled 1 and a valid pid, the kernel is ready to capture audit events.

Detailed Configuration: Battle-Tested Rules for Production Servers

The Auditd architecture consists of two main parts: /etc/audit/auditd.conf (manages log files, storage limits, and rotation behavior) and the /etc/audit/rules.d/ directory (contains monitoring rules).

1. Preventing Disk Overflow in auditd.conf

On high-traffic server clusters, audit logs can consume tens of gigabytes per day. If the /var/log partition fills up, services on the server risk crashing or freezing. Open the configuration file:

sudo vi /etc/audit/auditd.conf

Configure safe storage thresholds:

# Maximum size for each log file (MB)
max_log_file = 50

# Keep a maximum of 10 rotated log files (500MB total max)
num_logs = 10
max_log_file_action = ROTATE

# Warn via syslog when remaining disk space falls below 100MB
space_left = 100
sace_left_action = SYSLOG

# Suspend audit logging when remaining disk space falls below 50MB to protect the server
admin_space_left = 50
admin_space_left_action = SUSPEND
disk_full_action = SUSPEND

This configuration caps total audit log usage at 500MB while triggering early syslog warnings before disk space is exhausted.

2. Creating a Persistent Monitoring Ruleset

Avoid defining rules directly using the auditctl command, as they will be lost after a reboot. Instead, declare them persistently in /etc/audit/rules.d/audit.rules:

sudo vi /etc/audit/rules.d/audit.rules

Paste the following standardized ruleset:

## Flush all existing rules to avoid conflicts
-D

## Increase buffer to 8192 messages (prevents dropped logs during I/O spikes)
-b 8192

## 1. Monitor user accounts and system permissions
-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

## 2. Monitor Nginx configs and SSL certs
-w /etc/nginx/ -p wa -k nginx_config_tamper
-w /etc/pki/tls/ -p wa -k ssl_certs_tamper

## 3. Capture file permission and ownership syscalls (chmod, chown)
-a always,exit -F arch=b64 -S chmod,fchmod,fchmodat -F auid>=1000 -F auid!=unset -k file_permission_change
-a always,exit -F arch=b64 -S chown,fchown,lchown,fchownat -F auid>=1000 -F auid!=unset -k file_owner_change

## 4. Capture file and directory deletion syscalls
-a always,exit -F arch=b64 -S unlink,unlinkat,rmdir -F auid>=1000 -F auid!=unset -k file_deletion

## 5. Make the configuration immutable (uncomment only when the system is stable)
#-e 2

Key parameters to remember:

  • -w <path>: Defines a watchpoint on a specific file or directory.
  • -p wa: Filters two permission operations: write (write/modify) and attribute (changes to permissions, ownership, or timestamps).
  • -k <key>: Attaches a custom key/tag to quickly filter events during queries.
  • -F auid>=1000 -F auid!=unset: Filters for real logged-in users (UID 1000 and above), ignoring system daemons (UID < 1000) to prevent log noise.
  • -e 2: Locks the audit configuration in the kernel. Once this flag is set, rules cannot be altered until the server is rebooted.

Load the new rules into the kernel:

sudo augenrules --load

Verify the currently active rules:

sudo auditctl -l

Investigative Hands-on: Tracing Evidence with ausearch and aureport

Avoid using cat or grep directly on /var/log/audit/audit.log. Raw audit logs contain hex-encoded strings that are difficult to parse manually. Instead, leverage the power of ausearch and aureport.

1. Simulating an Unauthorized Action

Log in with a regular user account (e.g., hungnd with UID 1001) and use sudo to modify the Nginx configuration permissions:

sudo chmod 000 /etc/nginx/nginx.conf

2. Tracing the Incident with ausearch

Search for the event using the file_permission_change key, adding the -i (interpret) flag to decode hex values into human-readable text:

sudo ausearch -k file_permission_change -i --recent

The resulting log output reveals the complete context:

type=SYSCALL msg=audit(18/10/2026 02:15:30.120:412) : arch=x86_64 syscall=fchmodat success=yes exit=0 a0=0xffffff9c a1=0x154b200 a2=000 items=1 ppid=2450 pid=2810 auid=hungnd uid=root gid=root euid=root ses=3 comm="chmod" exe="/usr/bin/chmod" key="file_permission_change"
type=PATH msg=audit(18/10/2026 02:15:30.120:412) : item=0 name="/etc/nginx/nginx.conf" inode=135894 dev=fd:00 mode=0100644 ouid=0 ogid=0

What do these key fields tell us?

  • comm & exe: The command executed was /usr/bin/chmod.
  • name: The affected target file was /etc/nginx/nginx.conf.
  • uid=root: The command ran with root privileges.
  • auid=hungnd: The originating identity. Even though the user escalated privileges using sudo, the audit subsystem pinpointed that the user who initiated the session was originally hungnd.

3. Quick Summaries with aureport

For periodic security audits, aureport produces concise summary reports in seconds.

Generate summary statistics for successful and failed logins:

sudo aureport -l --summary

List operations denied by the kernel (potential signs of permission probing or privilege escalation attempts):

sudo aureport --failed

View a list of files modified within the last 24 hours:

sudo aureport -f -ts yesterday

Auditd cannot prevent malicious actions while they are occurring. However, it delivers what matters most during an incident: incontrovertible forensic evidence to establish accountability and permanently remediate operational vulnerabilities.

Share: