linuxlab.io
Tutorials▾
  • Linux & networking
    File system, processes, TCP/IP, BGP and OSPF
    →
  • Terraform & IaC
    HCL, state, plan/apply on a LocalStack sandbox
    →
  • Git & GitHub
    Object model, plumbing, branching, GitHub Actions
    →
All tutorials →
PricingAboutSign inCreate account
/
  • Introduction
  • Lessons
  • How it works
  • Simulator
  • Knowledge base
  • Interview prep
Index
Categories
All entries
Footer
linuxlab-TutorialsPricingAboutPrivacy & cookies
Copyright © 2026 LinuxLab. All rights reserved.
home/linux/kb/Security/auditd

kb/security ── Security ── advanced

auditd: syscall and file audit

auditd writes kernel events to /var/log/audit/audit.log: file watches (-w), syscall rules (-a), execs. Use ausearch to search, aureport for reports. This is the basis of compliance (PCI-DSS, HIPAA, FZ-152).

view as markdownaka: audit, auditctl, ausearch, aureport, linux-audit, audit-framework

Why auditd

When you need to answer "who, when, what was read or changed" with forensic precision, the Linux audit subsystem (kauditd in the kernel plus auditd in userspace) is the tool. Scenarios:

  • Compliance (PCI-DSS, HIPAA, GDPR, FZ-152): mandatory logging of access to sensitive files
  • Forensics after a breach: exactly what the attacker changed
  • Insider threat: which admin read the HR database
  • Debugging "the file suddenly disappeared": which process deleted it
  • SELinux AVC denials: tied to audit.log

This is not for general logging. It is for a precise, targeted watch. Without rules, auditd by default records only login events.

Architecture

┌──────────────┐
│   userspace  │
│  ┌────────┐  │
│  │auditd  │  │ ←─── netlink ───┐
│  └────────┘  │                  │
│       │      │                  │
└───────┼──────┘                  │
        │ /var/log/audit/audit.log │
                                   │
┌───────▼──────────────────────────┴────┐
│             kernel                     │
│  ┌──────────┐    ┌──────────────┐    │
│  │  kauditd │◄───│ audit_filter │◄── syscall enter/exit
│  └──────────┘    └──────────────┘    │
│                                       │
└───────────────────────────────────────┘
  • The kernel checks the audit rules on every syscall. On a match, the event goes into the queue for kauditd.
  • kauditd (a kernel thread) sends it over netlink to userspace.
  • auditd (PID-1-launched, the only reader of the netlink socket) formats and writes to /var/log/audit/audit.log.
  • If auditd crashes, the failure mode applies: panic (kernel panic), printk (to dmesg), or silent (no message).

Installation

bash
apt install auditd                     # Debian/Ubuntu
dnf install audit                       # RHEL
systemctl enable --now auditd

Config is /etc/audit/auditd.conf:

ini
log_file = /var/log/audit/audit.log
log_format = ENRICHED                   # resolves UID names, ports, etc
max_log_file = 100                      # MB
num_logs = 10                           # rotation
space_left = 1000                       # MB free
space_left_action = email
admin_space_left_action = halt          # halt the server if there is no space at all
disp_qos = lossy                        # on overload, drop (vs lossless = block the syscall)

In production, use space_left_action = email or syslog. Never halt unless you know why.

auditctl, runtime rules

There are two kinds of rules:

File watches -w

bash
auditctl -w /etc/passwd -p wa -k passwd-changes
auditctl -w /etc/shadow -p rwa -k shadow-access
auditctl -w /etc/sudoers -p wa -k sudoers-changes
auditctl -w /etc/ssh/sshd_config -p wa -k sshd-config-changes
auditctl -w /var/log/audit/ -p wa -k audit-log-access

Permissions:

  • r, read
  • w, write
  • x, execute
  • a, attribute change (chmod, chown, setxattr)

-k KEY is a label for searching with ausearch.

Syscall rules -a

bash
# Any exec
auditctl -a always,exit -F arch=b64 -S execve -k exec-events
# Any chmod/chown
auditctl -a always,exit -F arch=b64 -S chmod -S fchmod -S fchmodat -k perm-changes
# File deletions by ordinary users
auditctl -a always,exit -F arch=b64 -S unlink -S unlinkat -F auid>=1000 -F auid!=4294967295 -k user-deletes
  • always,exit logs on return from the syscall (so the exit code is available)
  • arch=b64 is for 64-bit syscalls (for 32-bit ones, use b32)
  • -S is a specific syscall
  • -F is a filter on a field (auid, uid, pid, and so on)
  • auid is the audit UID (the original one, not replaced by sudo)

Persistent rules

Runtime rules are lost on reboot. Persistent rules go in /etc/audit/rules.d/*.rules:

# /etc/audit/rules.d/audit.rules
-D                                       # clear everything
-b 8192                                  # buffer size
-f 1                                     # failure mode: 1=printk
# File watches
-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 identity
-w /etc/sudoers.d/ -p wa -k identity
-w /etc/ssh/sshd_config -p wa -k sshd
# Critical syscalls
-a always,exit -F arch=b64 -S execve -F auid>=1000 -F auid!=-1 -k user-exec
-a always,exit -F arch=b64 -S adjtimex -S settimeofday -k time-change
# Network config
-w /etc/sysconfig/network-scripts/ -p wa -k network-config
-w /etc/hosts -p wa -k network-config
# Make immutable: once the rules are loaded, no one can change them
-e 2
bash
augenrules --load                        # load all *.rules
auditctl -l                              # currently active

-e 2 (immutable) is the last line. After it, only a reboot can change the rules. This protects against tampering.

ausearch, searching events

bash
# Everything by key
ausearch -k passwd-changes
# A specific user
ausearch -ua alice
ausearch -ui 1001                        # by UID
# Over a time range
ausearch -ts today                        # today
ausearch -ts yesterday -te now
ausearch -ts 06/01/2026 12:00:00
# By event type
ausearch -m USER_LOGIN                    # USER_LOGIN, USER_AUTH, EXECVE, CWD, PATH, AVC...
ausearch -m AVC                           # SELinux denials
# By file
ausearch -f /etc/passwd
# PID
ausearch -p 12345
# Summary
ausearch -k passwd-changes --interpret    # human-readable UID, syscall name

Pipe into aureport for aggregation:

bash
ausearch -m USER_LOGIN -ts today | aureport --login -i

aureport, reports

bash
aureport                                  # summary
aureport --login                          # successful and failed logins
aureport --auth                           # auth events
aureport --executable                     # top executables
aureport --file --summary -i              # top accessed files
aureport --user                           # activity by UID
aureport --pid                            # activity by PID
aureport --auth --failed --interpret      # all failed auth

Example: insider-threat detection

Suppose you want to log who reads /etc/shadow:

bash
auditctl -w /etc/shadow -p r -k shadow-read

An hour later:

bash
ausearch -k shadow-read -i | grep -A2 SYSCALL | grep -oP 'auid=\d+ uid=\d+'

If someone opened shadow with no clear reason, there is a record of it.

Performance

Audit adds overhead on every matched syscall. Rules must be precise:

RuleOverhead
-w /etc/passwd -p waminimal (write/attr happen rarely)
-a always,exit -S execve -F auid>=1000low (execs are rare)
-a always,exit -S read (no filters)crushing (read happens extremely often)
-a always,exit -S openat -F dir=/varmedium (depends on load)

Do not write open rules without -F filters. Test in a dev environment.

SELinux AVC and audit

When [[selinux-apparmor|SELinux]] blocks something, the event is written to audit.log as an AVC denial:

bash
ausearch -m AVC -ts recent

This shows exactly what triggered. Then use audit2allow:

bash
ausearch -m AVC -ts recent | audit2allow -M my-fix
semodule -i my-fix.pp                     # load the policy module

More in selinux-policy.

When things go wrong

  • auditctl -l is empty: the rules are not loaded. Run augenrules --load or systemctl restart auditd.
  • auditctl -e 2 does not work: the kernel config CONFIG_AUDITSYSCALL=y is missing. Check cat /proc/sys/kernel/audit_enabled.
  • audit.log balloons in a day: a rule is too open. aureport --summary shows the top events; refactor the rules.
  • auditctl: failed to set rule: the rules are immutable (-e 2 was already set). Only a reboot allows a change.
  • ausearch: no matches: events are not being written at all. Check that auditd is running and that /var/log/audit/audit.log is growing. If the file is missing, the file mode is 600, the dir mode is 700, owner root.
  • Performance dropped after auditd: a global read rule. Remove it.
  • No ENRICHED: an old version. Without log_format = ENRICHED, UIDs and numbers are not resolved. Update the audit package.

Alternatives

  • eBPF + bpftrace: for debugging without rules, not for compliance
  • fapolicyd / fanotify: file-access policy, complements auditd
  • Falco / sysdig: userspace tracing for containers
  • OSSEC / Wazuh: file integrity monitoring plus alerting on top of auditd

§ команды

bash
auditctl -l

All audit rules active at the moment

bash
auditctl -w /etc/sudoers -p wa -k sudoers-changes

A watch on changes to sudoers, the classic compliance rule

bash
ausearch -k sudoers-changes -ts today -i

Find all events for the key from today, interpreted

bash
aureport --executable -i | head

Top executables on the system, who ran what

bash
ausearch -m AVC -ts recent | head

All recent SELinux denials, the input for audit2allow

bash
augenrules --load

Reload the rules from /etc/audit/rules.d/*.rules, the persistent ones

bash
ausearch -ua alice -ts yesterday -te now -i | grep EXECVE

All execs by user alice over the last day, an audit trail

§ см. также

  • selinux-policySELinux policy: types, domains, audit2allowSELinux: every process has a domain, every object has a type. The policy defines which domains may do what to which types. audit2allow generates rules from AVC denials, semanage tunes, and .pp files are policy modules.
  • selinux-apparmorSELinux and AppArmor: Mandatory Access ControlSELinux and AppArmor are MAC: a control layer on top of normal permissions. They stop a process from doing anything outside its profile or type.
  • pamPAM: Pluggable Authentication ModulesPAM is the authentication framework in Linux. Programs (sudo, login, sshd) do not check passwords themselves. They call PAM, which decides whether to let you in through a stack of modules in `/etc/pam.d/<service>`.
  • ssh-hardeningSSH hardening: locking down the serverSSH hardening: keys only (PasswordAuthentication no), disable root login, AllowUsers/AllowGroups, MaxAuthTries, a fail2ban jail on sshd. Optionally a custom port plus Match blocks for guests.
  • secrets-managementSecrets management: Vault, k8s Secrets, sealed-secretsKeep secrets out of git and out of env vars in code. Options: HashiCorp Vault (general purpose, dynamic creds), k8s Secrets (base64, needs encryption- at-rest), sealed-secrets (commit-friendly), external-secrets (sync from a cloud vault).
  • loki-grafana-loggingLoki: label-based logs, LogQL, Promtail/Vector pipelineLoki is log aggregation with a label-based index, not full-text like Elastic. Cheap on S3 storage. Promtail/Vector are the agents. LogQL resembles PromQL: filter, parse, aggregate. Cardinality is the enemy.
Footer
linuxlab-
Copyright © 2026 LinuxLab. All rights reserved.
Tutorials
Pricing
About
Privacy & cookies