
Best-practice Linux Auditd rule set with 14,956 MITRE ATT&CK-mapped rules, Ansible deployment role, and lint/test tooling for security monitoring and compliance auditing.
This repository provides comprehensive best practices for Linux Audit Daemon (Auditd) configuration and deployment, including an extensive set of security-focused audit rules, an Ansible role for automated deployment and the tools that test the rules on real kernels.
The changes to the rule set, the role and the tests are listed in CHANGELOG.md.
Auditd is a powerful Linux auditing system that provides comprehensive system monitoring and logging capabilities. It is designed to track security-relevant events and is essential for:
Our comprehensive audit rules (/ndaal/audit_best_practices.rules or /dataset/audit_best_practices.rules) are designed to meet various security standards and best practices, including:
| File | Content |
|---|---|
dataset/audit_best_practices.rules | The main rule set: 14,956 active rules with 903 keys, written as arch=b32/arch=b64 pairs. This is the file the Ansible role downloads. |
dataset/audit_best_practices_high_volume.rules | Companion file with the unfiltered collection: every execve of a non-system user, kill/tkill/tgkill and outbound connect. Every rule in it is commented out, so the file is off by default. Its header explains how to enable one block at a time. |
ndaal/ | Byte-identical copies of both files. |
*.rules.sha-256 | SHA-256 sidecars in sha256sum format. The Ansible role checks every download against them. After editing a rule file, run tools/update_rules_checksums.sh to regenerate them. |
tools/key-decisions.tsv | One decision per key collision pair, applied by tools/resolve_key_collisions.py. |
A rule that a measurement proved wrong is commented out with a dated note that gives the reason. Nothing is deleted, so the file keeps the history of each decision.
A key that starts with a technique ID follows MITRE ATT&CK Enterprise 19.2. ATT&CK v19 revoked six IDs that the file used. On 2026-09-27 their keys were renamed to the successors that MITRE names for them. Change every SIEM query and alert that uses an old key:
| Old key | New key |
|---|---|
T1562.001_Impair_Defenses_Disable_or_Modify_Tools | T1685_Disable_or_Modify_Tools |
T1562.004_Impair_Defenses_Disable_or_Modify_System_Firewall | T1686_Disable_or_Modify_System_Firewall |
T1070.002_Indicator_Removal_Clear_Linux_or_Mac_System_Logs | T1685.006_Disable_or_Modify_Tools_Clear_Linux_or_Mac_System_Logs |
T1107_File_Deletion | T1070.004_Indicator_Removal_File_Deletion |
T1169_Sudo | T1548.003_Abuse_Elevation_Control_Mechanism_Sudo_and_Sudo_Caching |
T1079_Multilayer_Encryption | T1573_Encrypted_Channel |
The watch on /var/log/audit/ carries T1685.004_Disable_or_Modify_Tools_Disable_or_Modify_Linux_Audit_System_Log, the v19 sub-technique for the audit log itself, instead of T1685.006. Dated notes written before the rename, and the commented-out rules they explain, keep the old names.
Also on 2026-09-27, keys that named the wrong technique were corrected. Change the SIEM queries and alerts for these rules as well:
| Rule | Old key | New key |
|---|---|---|
Writes and attribute changes on /etc/passwd (a new pair; reads keep T1087) | T1087_Account_Discovery | T1098_Account_Manipulation |
Writes and attribute changes on /etc/shadow | T1087_Account_Discovery | T1098_Account_Manipulation |
A person reading /etc/shadow | T1087_Account_Discovery | T1003.008_OS_Credential_Dumping_etc_passwd_and_etc_shadow |
/etc/ssh/sshd_config | T1021_Remote_Services | T1021.004_Remote_Services_SSH |
/root/.ssh/authorized_keys | T1021_Remote_Services | T1098.004_Account_Manipulation_SSH_Authorized_Keys |
/etc/systemd/system/ | T1053.006_Scheduled_Task_Systemd_Timers | T1543.002_Create_or_Modify_System_Process_Systemd_Service |
date | T1083_File_and_Directory_Discovery | T1124_System_Time_Discovery |
| The Python interpreters (pip, pipx, conda and npm keep T1072) | T1072_Software_Deployment_Tools | T1059.006_Command_and_Scripting_Interpreter_Python |
| mysql and psql run by a person | T1213_002_database_access | T1213.006_Data_from_Information_Repositories_Databases |
Writes and attribute changes on /etc/group | T1087_Account_Discovery | T1098_Account_Manipulation |
Writes and attribute changes on /etc/gshadow (a new pair; reads keep T1087) | T1087_Account_Discovery | T1098_Account_Manipulation |
/usr/lib/systemd/system/ (/run/systemd/transient/ keeps T1053.006) | T1053.006_Scheduled_Task_Systemd_Timers | T1543.002_Create_or_Modify_System_Process_Systemd_Service |
/home/vagrant/.ssh/authorized_keys | T1021_Remote_Services | T1098.004_Account_Manipulation_SSH_Authorized_Keys |
Writes into /var/log/tomcat10/, 64-bit (a typo) | tomcattomcat | tomcat |
Writes into /etc/mandiant/, 32-bit (a typo) | mmandiant_config | mandiant_config |
unix_chkpwd, the password check of sudo and of screen locks, reads /etc/shadow with the person's audit ID, so each password check now arrives as T1003.008. Filter exe=/usr/sbin/unix_chkpwd in the SIEM rule for credential dumping. The other 34 rules with a key in the T1234_567 spelling never labelled a record, because an earlier rule matches the same events. They are commented out with a note that names that rule.
32-bit calls of kexec_load, capset, perf_event_open and semtimedop_time64 now carry KEXEC, capability_change_ebpf, perf_event_ebpf and T1559_Inter-Process_Communication instead of 32bit_abi. The elasticsearch-data watch on the whole data directory is off on both ABIs: its warning makes it opt-in. On 64-bit hosts the elasticsearch-nodes and elasticsearch-data-deletion keys, which it took until then, appear again.
The kernel attaches the key of the earliest-loaded rule that matches an event. This holds for syscall rules and path watches alike (kernel/auditfilter.c, kernel/auditsc.c). A broad rule placed early therefore takes the key from every specific rule after it. Until 2026-09-20 the unfiltered procmon execve rule stood at line 1,575, and 368 keys never appeared on a record.
The catch-all rules now close the file, in this order:
user_exec: every execve of a login session (auid>=500, auid!=-1) that no watch and no elevation rule has claimedprocmon: every remaining execve (daemons, cron, boot)setres*/setfs*, stime and 32-bit umount rulesnetwork_changes (every socket and setsockopt) and elasticsearch-bind-success (every successful bind)32bit_abi: -S all for arch=b32, always the last -a rule of the fileAdd every new rule above this block. tools/auditd-rules-lint.sh fails a file in which a catch-all moves up: it reports key shadowing and a -S all rule that is not last.