auditd can tell you who did what to which file, and it’s way more granular than just ls -l.

Let’s see auditd in action, tracking a specific file. Imagine you want to know every time someone reads, writes, or changes permissions on /etc/passwd.

First, you need to configure auditd to watch that file. This is done by adding rules to /etc/audit/rules.d/audit.rules. Let’s say you create a file named 00-file-access.rules in that directory with the following content:

## Track access to /etc/passwd
-w /etc/passwd -p rwx -k passwd_access

Here’s what that means:

  • -w /etc/passwd: This tells auditd to watch the file /etc/passwd. You can also watch directories.
  • -p rwx: This specifies the permissions to monitor: r for read, w for write, x for execute. You can also add a for attribute changes (like chmod).
  • -k passwd_access: This is a key string. It’s an arbitrary tag you assign to this rule, making it easier to filter audit logs later.

After saving this file, you need to tell the auditd service to reload its rules. The command for this is:

sudo augenrules --load

This command reads all the .rules files in /etc/audit/rules.d/ and compiles them into a single set of rules that auditd will use. If you’re not using augenrules (some older systems might not), you’d typically restart the service:

sudo systemctl restart auditd

Now, let’s simulate some activity.

First, a read:

sudo cat /etc/passwd > /dev/null

Next, a write (though cat to /dev/null doesn’t actually change the file, auditd will still log the attempt to open for writing):

sudo echo "test" >> /etc/passwd

This will likely fail due to permissions, but the attempt is logged.

Finally, a permission change:

sudo chmod 600 /etc/passwd

Now, let’s check the audit logs. The primary tool for this is ausearch. To find all events related to our passwd_access key, you’d run:

sudo ausearch -k passwd_access

You’ll see output similar to this (timestamps and PIDs will vary):

----
time->Wed Jan 24 10:30:00 2024
type=SYSCALL msg=audit(1706097000.123:456): arch=c000003e syscall=2 success=yes exit=3 a0=0 a1=7ffc5a7b1000 a2=0 a3=0 items=1 ppid=1234 pid=5678 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts0 ses=1 comm="cat" exe="/usr/bin/cat" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key="passwd_access"
type=V3_SYSCALL msg=audit(1706097000.123:456): arch=c000003e syscall=2 success=yes exit=3 a0=0 a1=7ffc5a7b1000 a2=0 a3=0 items=1 ppid=1234 pid=5678 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts0 ses=1 comm="cat" exe="/usr/bin/cat" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key="passwd_access"
type=CWD msg=audit(1706097000.123:456): cwd="/home/user"
type=PATH msg=audit(1706097000.123:456): item=0 name="/etc/passwd" inode=123456 dev=08:01 mode=0100644 ouid=0 ogid=0 rdev=00:00 obj=unconfined_u:object_r:etc_t:s0 nametype=NORMAL cap_fp=0000000000000000 cap_fi=0000000000000000 cap_fe=0000000000000000 cap_fver=0
----
time->Wed Jan 24 10:31:00 2024
type=SYSCALL msg=audit(1706097060.456:789): arch=c000003e syscall=2 success=yes exit=4 a0=0 a1=7ffc5a7b1000 a2=0 a3=0 items=1 ppid=1234 pid=5679 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts0 ses=1 comm="echo" exe="/usr/bin/echo" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key="passwd_access"
type=V3_SYSCALL msg=audit(1706097060.456:789): arch=c000003e syscall=2 success=yes exit=4 a0=0 a1=7ffc5a7b1000 a2=0 a3=0 items=1 ppid=1234 pid=5679 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts0 ses=1 comm="echo" exe="/usr/bin/echo" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key="passwd_access"
type=CWD msg=audit(1706097060.456:789): cwd="/home/user"
type=PATH msg=audit(1706097060.456:789): item=0 name="/etc/passwd" inode=123456 dev=08:01 mode=0100644 ouid=0 ogid=0 rdev=00:00 obj=unconfined_u:object_r:etc_t:s0 nametype=NORMAL cap_fp=0000000000000000 cap_fi=0000000000000000 cap_fe=0000000000000000 cap_fver=0
----
time->Wed Jan 24 10:32:00 2024
type=SYSCALL msg=audit(1706097120.789:101): arch=c000003e syscall=90 success=yes exit=0 a0=0x5 a1=0x100000 a2=0x100000 a3=0x0 items=1 ppid=1234 pid=5680 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts0 ses=1 comm="chmod" exe="/usr/bin/chmod" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key="passwd_access"
type=V3_SYSCALL msg=audit(1706097120.789:101): arch=c000003e syscall=90 success=yes exit=0 a0=0x5 a1=0x100000 a2=0x100000 a3=0x0 items=1 ppid=1234 pid=5680 auid=1000 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts0 ses=1 comm="chmod" exe="/usr/bin/chmod" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key="passwd_access"
type=CWD msg=audit(1706097120.789:101): cwd="/home/user"
type=PATH msg=audit(1706097120.789:101): item=0 name="/etc/passwd" inode=123456 dev=08:01 mode=0100644 ouid=0 ogid=0 rdev=00:00 obj=unconfined_u:object_r:etc_t:s0 nametype=NORMAL cap_fp=0000000000000000 cap_fi=0000000000000000 cap_fe=0000000000000000 cap_fver=0

Each block represents an audit event. You can see type=SYSCALL which is the core system call that triggered the event. The exe field shows the executable (cat, echo, chmod), and key="passwd_access" confirms it’s one of our monitored events. The auid (audit user ID) shows the user who initiated the action.

To get more human-readable output, ausearch can be combined with aureport:

sudo ausearch -k passwd_access | aureport -f -i

This will give you a summary of file access events, including the executable, user, and the file accessed.

The actual system call number syscall=2 for cat and echo corresponds to open or openat. syscall=90 for chmod corresponds to fchmodat. The type=PATH message details the specific file (/etc/passwd) and its attributes.

auditd’s power lies in its ability to hook into the kernel’s security auditing subsystem. When you set a watch rule, you’re essentially telling the kernel: "Every time this file is accessed in any way (read, write, execute, attribute change), generate an audit record." These records are then collected by the auditd daemon and written to /var/log/audit/audit.log. The ausearch command is the frontend to query this log data efficiently, using the kernel’s audit event structures.

One of the most powerful, yet often overlooked, aspects of auditd is its ability to monitor system calls themselves. Beyond just file access, you can create rules to track specific system calls performed by specific users or processes. For example, to track all attempts by user alice to execute the reboot system call (syscall number 169):

-a always,exit -F arch=b64 -S reboot -F auid=1001 -k reboot_attempt
-a always,exit -F arch=b32 -S reboot -F auid=1001 -k reboot_attempt

(Note: auid=1001 is an example, you’d find the actual UID for alice). This allows for very fine-grained security monitoring, catching potentially malicious activities before they escalate.

If you ever want to see all the audit rules currently loaded by the kernel, you can use sudo auditctl -l.

Want structured learning?

Take the full Linux & Systems Programming course →