Intrusion detection on Linux: auditd, AIDE, CrowdSec and Wazuh
Patching closes holes; intrusion detection tells you when someone got through anyway. On a Linux server the useful free tools fall into three groups: the kernel audit system (auditd) records who changed what, file integrity checkers such as AIDE spot altered files, and log-based tools such as CrowdSec, fail2ban and Wazuh react to attack patterns.
What each tool does
| Tool | What it watches | What you get |
|---|---|---|
| auditd (Linux Audit) | System calls in the kernel: file writes, program starts, permission changes | A detailed record of who did what, searchable with ausearch and aureport. Records only; does not block. |
| AIDE | Checksums and attributes of files, compared with a saved baseline | A daily report of added, removed and changed files. See File integrity monitoring. |
| CrowdSec | Logs (SSH, web servers, many others) matched against attack scenarios | Decisions to block an IP, enforced by a separate "bouncer" (firewall, web server). Optional shared community blocklist. |
| fail2ban | Log lines matched by regular expressions per service ("jails") | Temporary firewall bans after repeated failures. |
| Wazuh | Logs, file integrity, rootkit checks, configuration assessment, via an agent on each host | Central server, alerts and a web dashboard for many servers. |
| OSSEC | Logs, file integrity, malware and rootkit checks | The original open-source host IDS, maintained by Atomicorp (release 4.4.1, October 2026); no bundled dashboard. |
Two names you will meet in older guides: Tripwire open source is still packaged in Debian 13, but its last upstream tag (2.4.3.7) dates from March 2018; AIDE is the actively maintained choice. Lynis is an auditing tool that checks your configuration and suggests hardening; it is useful, but it does not watch for intrusions between runs.
auditd: record sensitive changes
The Linux audit system is built into the kernel. The auditd daemon writes its records to /var/log/audit/audit.log. RHEL installs it by default. On Debian and Ubuntu:
sudo apt install auditd
sudo auditctl -s # status: "enabled 1" means auditing is on
Put permanent rules in a file in /etc/audit/rules.d/ (the name must end in .rules). The augenrules tool merges these files into /etc/audit/audit.rules and loads them. Older guides use -w /etc/passwd -p wa; the auditctl manual now calls that form deprecated for performance reasons and recommends the syscall form with -F path= (one file) or -F dir= (a directory tree), shown here.
Rules for accounts, sudo and SSH
Create /etc/audit/rules.d/50-local.rules:
## accounts and passwords
-a always,exit -F arch=b64 -F path=/etc/passwd -F perm=wa -F key=identity
-a always,exit -F arch=b32 -F path=/etc/passwd -F perm=wa -F key=identity
-a always,exit -F arch=b64 -F path=/etc/shadow -F perm=wa -F key=identity
-a always,exit -F arch=b32 -F path=/etc/shadow -F perm=wa -F key=identity
-a always,exit -F arch=b64 -F path=/etc/group -F perm=wa -F key=identity
-a always,exit -F arch=b32 -F path=/etc/group -F perm=wa -F key=identity
## sudo rights
-a always,exit -F arch=b64 -F path=/etc/sudoers -F perm=wa -F key=sudoers
-a always,exit -F arch=b64 -F dir=/etc/sudoers.d/ -F perm=wa -F key=sudoers
## SSH server config and root's keys
-a always,exit -F arch=b64 -F path=/etc/ssh/sshd_config -F perm=wa -F key=sshd
-a always,exit -F arch=b64 -F dir=/etc/ssh/sshd_config.d/ -F perm=wa -F key=sshd
-a always,exit -F arch=b64 -F dir=/root/.ssh/ -F perm=wa -F key=ssh_keys
perm=wa means "write or attribute change", so reads are not logged. The key is a label you search by later. On 64-bit x86 servers, the arch=b32 lines catch 32-bit programs too; copy that pattern for the other rules if you want the same coverage (the upstream example rules do). Add a dir= line for each .ssh directory of a real login user, and only for directories that exist.
Programs started by the web server user
A web server account should almost never start a shell, curl or wget. When it does, it is often a web shell or an exploited plugin. Log every program it runs:
# find the user's numeric ID: www-data is 33 on Debian/Ubuntu, apache is 48 on RHEL
id -u www-data
## add to 50-local.rules (replace 33 with your ID)
-a always,exit -F arch=b64 -S execve -F euid=33 -F key=webexec
-a always,exit -F arch=b32 -S execve -F euid=33 -F key=webexec
If PHP-FPM or another application server runs under its own user, add rules for that user too. Expect some noise at first if your application legitimately runs tools such as image converters; note what is normal.
Load and lock the rules
sudo augenrules --check # do the merged rules differ from what is loaded?
sudo augenrules --load
sudo auditctl -l # list active rules
Optionally, add a file named 99-finalize.rules containing -e 2. This locks the audit configuration: nobody, not even root, can change or delete rules until the next reboot. It makes it harder for an intruder to switch auditing off quietly, but every rule change then needs a reboot.
The upstream project ships ready-made rule sets (for example for STIG and PCI DSS) in /usr/share/doc/auditd/examples/audit-rules/ on Debian. Copy only the files you need; loading all of them together is not intended.
Reading audit logs: ausearch and aureport
# who touched account files today, with user names instead of numbers
sudo ausearch -k identity -ts today -i
# programs run by the web user in the last 10 minutes
sudo ausearch -k webexec -ts recent -i
# overview of everything recorded
sudo aureport --summary
# events per rule key, failed logins this week, account changes
sudo aureport -k --summary
sudo aureport -au --failed -ts this-week -i
sudo aureport -m -i
Each record shows the login user (auid), which stays the same after sudo or su. That is how you tell which person changed /etc/sudoers even if they did it as root. Remember that an attacker with root can delete audit.log: forward it off the server as described in Logging and monitoring.
CrowdSec and fail2ban: react to attacks
Both read your logs and block addresses that behave like attackers, for example dozens of failed SSH logins or scans for /wp-login.php.
- fail2ban is in every major distribution. Put settings in
/etc/fail2ban/jail.localorjail.d/*.local, never injail.conf. On journal-only systems such as Debian 12 and 13, usebackend = systemd. Check bans withsudo fail2ban-client status sshd; lift one withsudo fail2ban-client set sshd unbanip 203.0.113.5. - CrowdSec separates detection from blocking. The Security Engine parses logs and applies scenarios; a remediation component such as
crowdsec-firewall-bouncer-nftablesor-iptablesenforces the decisions. CrowdSec recommends its own package repository (see its install guide). Useful commands:sudo cscli metricsandsudo cscli decisions list. Sharing your detections is how you receive the community blocklist; read what is shared before enrolling.
Neither tool detects a successful intrusion. They reduce noise and slow down guessing. Brute-force attacks covers an active attack.
Wazuh: many servers, one dashboard
Wazuh installs a small agent on each server (its documentation gives about 35 MB of RAM on average). Agents send log events, file integrity results, rootkit checks and configuration assessments to a central Wazuh server, which stores them in the Wazuh indexer and shows them in a web dashboard. The central part is heavier: the quickstart asks for 4 vCPU, 8 GiB RAM and 50 GB of disk for up to 25 agents (90 days of alerts), and lists RHEL 7 to 10, Ubuntu up to 24.04, Amazon Linux and CentOS Stream 10 for it. The agent itself supports more systems.
Wazuh suits a team watching ten or more servers. For one or two servers, auditd plus AIDE plus central logging gives most of the value with far less to maintain.
Choosing and resource use
| Situation | Suggested set |
|---|---|
| One or a few servers | auditd with the rules above, AIDE daily, logs forwarded off-host, fail2ban or CrowdSec for SSH and web logins |
| A small fleet (10+) | The same on each host, plus a central log server or Wazuh for alerts in one place |
| Compliance requirements | auditd with the matching upstream rule set, file integrity monitoring, central retention; check what your auditor expects |
- auditd costs little with focused rules like these. Broad rules (every
execveby every user, or all of/etc) can slow busy servers and fill the disk. The upstream default configuration rotatesaudit.logat 8 MB and keeps 5 files; checkmax_log_fileandnum_logsin/etc/audit/auditd.conf. - AIDE uses CPU and disk I/O while it scans, usually once a day. Schedule it outside peak hours.
- fail2ban and CrowdSec use modest memory and CPU proportional to log volume.
- Wazuh agents are light; the central server needs the resources above.
Whichever you choose, test it: add a line to a watched file, run a command as the web user, and confirm the event reaches you.
Sources
- auditctl(8) manual page
- augenrules(8) manual page
- ausearch(8) manual page
- aureport(8) manual page
- auditd.conf(5) manual page
- 30-stig.rules example audit rules, linux-audit project
- README-rules, linux-audit project
- auditd package file list (trixie), Debian
- jail.conf(5), fail2ban manual page, Debian
- fail2ban-client(1) manual page, Debian
- Introduction, CrowdSec documentation
- Linux installation, CrowdSec documentation
- Firewall remediation component, CrowdSec documentation
- Quickstart, Wazuh documentation
- Wazuh agent installation, Wazuh documentation
- OSSEC
- Open Source Tripwire tags, GitHub
- tripwire packages, Debian
- Lynis, CISOfy