Introduction: Why a Single Security Tool Will Never Be Enough
Here's a reality check that every Linux administrator eventually confronts: no single intrusion detection tool covers all the attack vectors that modern adversaries exploit. You can deploy the most sophisticated host-based IDS on the market, and it still won't catch a lateral movement attempt sneaking through your network at the packet level. Run the most powerful network IDS available, and it'll remain completely blind to an attacker who's already gained local access and is quietly modifying system binaries.
This isn't theoretical, either.
In 2025, CISA added seven Linux kernel vulnerabilities to its Known Exploited Vulnerabilities catalog — all actively weaponized against production infrastructure. Ransomware groups like Qilin, Kraken, and RansomHub are increasingly going after Linux systems, and their attack chains span multiple layers: initial network access, privilege escalation through kernel exploits, persistence via modified configuration files, and data exfiltration through covert network channels.
The answer is defense in depth — specifically, building a multi-layer intrusion detection architecture that monitors your Linux systems at every critical level: file integrity, system calls, host-level events, and network traffic. In this guide, we'll build exactly that stack using four complementary open-source tools: AIDE for file integrity monitoring, auditd for system call auditing, Wazuh for centralized host-based detection and SIEM capabilities, and Suricata for network-level intrusion detection.
By the end, you'll have a practical, production-ready detection pipeline that leaves attackers with nowhere to hide. So, let's dive in.
Understanding the Detection Layers
Before we start configuring anything, let's take a step back and understand what each layer actually detects — and more importantly, where the gaps are that the other layers fill.
Layer 1: File Integrity Monitoring (AIDE)
AIDE (Advanced Intrusion Detection Environment) creates a cryptographic snapshot of your filesystem — file sizes, permissions, ownership, timestamps, and hashes — and alerts you when anything changes unexpectedly. This layer catches post-compromise activities like modified binaries, injected backdoors, altered configuration files, and tampered log files. Think of it as your "ground truth" layer: if an attacker changes something on disk, AIDE will notice.
Layer 2: System Call Auditing (auditd)
The Linux Audit Framework (auditd) operates at the kernel level, monitoring system calls in real time. Unlike AIDE, which detects changes after the fact, auditd can tell you who did what, when, and how. It monitors process execution, file access, privilege changes, network socket operations, and module loading. This is your forensics and compliance layer — and honestly, it's one of the most underrated tools in the Linux security toolbox.
Layer 3: Host-Based Detection and SIEM (Wazuh)
Wazuh ties everything together. It collects and correlates logs from AIDE, auditd, system logs, and application logs, then applies detection rules mapped to the MITRE ATT&CK framework. Wazuh also provides its own file integrity monitoring, vulnerability detection, Security Configuration Assessment (SCA), and active response capabilities. Starting with version 4.12.0 (May 2025), Wazuh added eBPF support for its FIM module, enabling more efficient monitoring on modern kernels. Version 4.14.0 (October 2025) introduced normalized inventory of Linux systemd units, giving visibility into service states across endpoints.
Layer 4: Network Intrusion Detection (Suricata)
Suricata monitors network traffic at the packet level using signature-based and protocol-based detection. It catches exploit attempts, command-and-control communications, data exfiltration, and lateral movement — all the things that host-based tools miss because they happen on the wire, not on the filesystem or in system calls.
Layer 1: Setting Up AIDE for File Integrity Monitoring
Installation
AIDE is available in the default repositories of all major distributions. Getting it installed is straightforward:
# Debian/Ubuntu
sudo apt install aide aide-common
# RHEL/CentOS/Rocky/Alma
sudo dnf install aide
# SUSE/openSUSE
sudo zypper install aide
Configuring AIDE Rules
The default AIDE configuration monitors too many files with insufficient granularity. For a production intrusion detection setup, you want to focus on the directories and files that attackers actually target. I've spent more time than I'd like to admit fine-tuning these rules, and the configuration below reflects what works well in practice. Edit the main configuration file:
# /etc/aide/aide.conf (Debian/Ubuntu)
# /etc/aide.conf (RHEL-based)
# Define custom rule groups
CRITICAL = p+i+l+n+u+g+s+b+m+c+sha512+xattrs
NORMAL = p+i+l+n+u+g+s+m+c+sha256
LOGS = p+i+l+n+u+g+S
PERMS = p+i+u+g+acl+selinux+xattrs
# Monitor critical system binaries with strongest checking
/bin CRITICAL
/sbin CRITICAL
/usr/bin CRITICAL
/usr/sbin CRITICAL
/usr/lib CRITICAL
/usr/lib64 CRITICAL
# Monitor configuration files
/etc NORMAL
# Monitor kernel modules
/lib/modules CRITICAL
# Monitor boot files
/boot CRITICAL
# Monitor cron directories
/etc/cron.d CRITICAL
/etc/cron.daily CRITICAL
/etc/cron.hourly CRITICAL
/etc/cron.weekly CRITICAL
/etc/cron.monthly CRITICAL
/var/spool/cron CRITICAL
# SSH configuration and authorized keys
/etc/ssh CRITICAL
/root/.ssh CRITICAL
# Monitor systemd units for persistence
/etc/systemd/system CRITICAL
/usr/lib/systemd/system CRITICAL
# PAM configuration
/etc/pam.d CRITICAL
/etc/security CRITICAL
# Logs — only check permissions and existence, not content
/var/log LOGS
# Exclude volatile files
!/var/log/.*\.gz$
!/var/lib/apt
!/var/cache
!/tmp
!/run
!/proc
!/sys
!/dev
Initializing the Database
Now generate the initial baseline database. This creates the cryptographic snapshot against which all future checks are compared:
# Initialize the AIDE database
sudo aide --init
# On Debian/Ubuntu, use aideinit wrapper
sudo aideinit
# Move the new database into place
sudo cp /var/lib/aide/aide.db.new /var/lib/aide/aide.db
Critical security step: Copy the database to read-only media or a remote location. If an attacker gains root access, they can regenerate the AIDE database to hide their modifications. This is the kind of detail that's easy to overlook but can completely undermine your file integrity monitoring:
# Copy database to a remote secure location
sudo scp /var/lib/aide/aide.db secure-backup@backup-server:/aide-databases/$(hostname)-aide.db
# Or mount a read-only filesystem for the database
# Add to /etc/fstab:
# /dev/sdb1 /var/lib/aide/readonly ext4 ro,noexec,nosuid 0 0
Automating AIDE Checks
Create a systemd timer for regular integrity checks. I prefer this approach over cron — you get better logging and more granular control:
# /etc/systemd/system/aide-check.service
[Unit]
Description=AIDE File Integrity Check
After=local-fs.target
[Service]
Type=oneshot
ExecStart=/usr/bin/aide --check
StandardOutput=journal
StandardError=journal
Nice=19
IOSchedulingClass=idle
# /etc/systemd/system/aide-check.timer
[Unit]
Description=Run AIDE integrity check daily
[Timer]
OnCalendar=*-*-* 03:00:00
RandomizedDelaySec=1800
Persistent=true
[Install]
WantedBy=timers.target
sudo systemctl enable --now aide-check.timer
Layer 2: Configuring Auditd for System Call Monitoring
Installation and Core Configuration
The Linux Audit Framework is included in all enterprise distributions, but you may need to install the userspace tools:
# Debian/Ubuntu
sudo apt install auditd audispd-plugins
# RHEL/CentOS/Rocky
sudo dnf install audit audit-libs
# Verify the service is running
sudo systemctl enable --now auditd
Configure the core audit daemon settings for production use. Pay particular attention to the space management settings — running out of disk space for audit logs can either cause the system to halt or (worse) silently drop events:
# /etc/audit/auditd.conf
log_file = /var/log/audit/audit.log
log_format = ENRICHED
log_group = adm
priority_boost = 4
flush = INCREMENTAL_ASYNC
freq = 50
num_logs = 10
max_log_file = 50
max_log_file_action = ROTATE
space_left = 150
space_left_action = SYSLOG
admin_space_left = 50
admin_space_left_action = SUSPEND
disk_full_action = SUSPEND
disk_error_action = SUSPEND
tcp_listen_queue = 5
tcp_max_per_addr = 1
tcp_client_max_idle = 0
distribute_network = no
Writing Detection-Focused Audit Rules
Generic audit rules generate too much noise. The following ruleset is designed around common MITRE ATT&CK techniques targeting Linux systems — it's opinionated, but I've found these rules strike a good balance between detection coverage and alert volume. Create or edit the rules file:
# /etc/audit/rules.d/intrusion-detection.rules
# Self Auditing - ensure the audit configuration is immutable
# Place this at the end to prevent modification
# -e 2
# ===== Persistence Detection (MITRE T1053, T1543, T1546) =====
# Monitor crontab modifications
-w /etc/crontab -p wa -k persistence_cron
-w /etc/cron.d/ -p wa -k persistence_cron
-w /var/spool/cron/ -p wa -k persistence_cron
# Monitor systemd service creation (persistence via services)
-w /etc/systemd/system/ -p wa -k persistence_systemd
-w /usr/lib/systemd/system/ -p wa -k persistence_systemd
-w /etc/init.d/ -p wa -k persistence_init
# SSH authorized_keys modifications
-w /root/.ssh/authorized_keys -p wa -k persistence_ssh
-w /home/ -p wa -k persistence_ssh
# Shell profile modifications for persistence
-w /etc/profile -p wa -k persistence_shell
-w /etc/profile.d/ -p wa -k persistence_shell
-w /etc/bashrc -p wa -k persistence_shell
-w /etc/bash.bashrc -p wa -k persistence_shell
# ===== Privilege Escalation Detection (MITRE T1548) =====
# Monitor SUID/SGID bit changes
-a always,exit -F arch=b64 -S chmod,fchmod,fchmodat -F auid>=1000 -F auid!=4294967295 -k priv_escalation_setuid
# Monitor setuid/setgid calls
-a always,exit -F arch=b64 -S setuid,setreuid,setresuid,setfsuid -F auid>=1000 -F auid!=4294967295 -k priv_escalation_setuid
-a always,exit -F arch=b64 -S setgid,setregid,setresgid,setfsgid -F auid>=1000 -F auid!=4294967295 -k priv_escalation_setgid
# Sudo and su usage
-w /usr/bin/sudo -p x -k priv_escalation_sudo
-w /usr/bin/su -p x -k priv_escalation_su
-w /etc/sudoers -p wa -k priv_escalation_sudoers
-w /etc/sudoers.d/ -p wa -k priv_escalation_sudoers
# ===== Credential Access (MITRE T1003, T1552) =====
# Monitor password and shadow file access
-w /etc/passwd -p wa -k credential_access
-w /etc/shadow -p ra -k credential_access
-w /etc/gshadow -p wa -k credential_access
-w /etc/security/opasswd -p wa -k credential_access
# PAM configuration changes
-w /etc/pam.d/ -p wa -k credential_pam
# ===== Defense Evasion (MITRE T1070, T1562) =====
# Log deletion and tampering
-w /var/log/ -p wa -k defense_evasion_logs
-w /var/log/audit/ -p wa -k defense_evasion_audit
# Firewall rule modifications
-w /etc/iptables/ -p wa -k defense_evasion_firewall
-w /etc/nftables.conf -p wa -k defense_evasion_firewall
-a always,exit -F arch=b64 -S setsockopt -F a0=0x6 -F a2=0x17 -k defense_evasion_firewall
# Killing security processes
-a always,exit -F arch=b64 -S kill -F a1=9 -k defense_evasion_kill
# ===== Execution Detection (MITRE T1059) =====
# Unusual process execution from /tmp, /dev/shm, /var/tmp
-a always,exit -F arch=b64 -S execve -F dir=/tmp -k exec_from_tmp
-a always,exit -F arch=b64 -S execve -F dir=/dev/shm -k exec_from_shm
-a always,exit -F arch=b64 -S execve -F dir=/var/tmp -k exec_from_vartmp
# Kernel module loading (MITRE T1547.006)
-a always,exit -F arch=b64 -S init_module,finit_module -k kernel_module_load
-a always,exit -F arch=b64 -S delete_module -k kernel_module_unload
-w /usr/sbin/insmod -p x -k kernel_module_tools
-w /usr/sbin/modprobe -p x -k kernel_module_tools
-w /usr/sbin/rmmod -p x -k kernel_module_tools
# ===== Network Activity (MITRE T1071) =====
# Outbound connection attempts by non-standard users
-a always,exit -F arch=b64 -S connect -F auid>=1000 -F auid!=4294967295 -k network_connect
# Make audit configuration immutable (uncomment in production)
# -e 2
Load the rules and verify everything looks good:
# Load rules
sudo augenrules --load
# Verify loaded rules
sudo auditctl -l
# Check for errors
sudo auditctl -s
Querying Audit Logs
The ausearch and aureport tools are essential for investigating detections. Here are some queries you'll find yourself running often:
# Search for persistence attempts in the last hour
sudo ausearch -k persistence_cron -ts recent
# Search for privilege escalation events today
sudo ausearch -k priv_escalation_sudo -ts today
# Generate a summary report of all events
sudo aureport --summary
# Generate an executable report (what was run)
sudo aureport -x --summary
# Show failed authentication attempts
sudo aureport --auth --failed
# Search for specific events by MITRE key
sudo ausearch -k defense_evasion_logs -i | head -50
Layer 3: Deploying Wazuh for Centralized Detection
Architecture Overview
Wazuh operates as a three-component system. The Wazuh server receives and analyzes data from agents, the Wazuh indexer (based on OpenSearch) stores and indexes alerts, and the Wazuh dashboard provides visualization and management. For environments with fewer than 50 agents, a single-server deployment works well. Larger deployments will benefit from a distributed architecture with clustered indexer nodes.
Server Installation
Wazuh provides an automated installation script that handles the heavy lifting. For a single-server deployment:
# Download and run the Wazuh installation assistant
curl -sO https://packages.wazuh.com/4.14/wazuh-install.sh
sudo bash ./wazuh-install.sh -a
# The installer will:
# 1. Install the Wazuh indexer
# 2. Install the Wazuh server
# 3. Install the Wazuh dashboard
# 4. Generate SSL certificates
# 5. Start all services
# After installation, retrieve the admin credentials
sudo tar -O -xvf wazuh-install-files.tar wazuh-install-files/wazuh-passwords.txt
Access the Wazuh dashboard at https://your-server-ip:443 using the extracted credentials.
Agent Deployment on Monitored Hosts
Deploy Wazuh agents on every Linux system you want to monitor. The process is pretty standard across distros:
# Add the Wazuh repository
curl -s https://packages.wazuh.com/key/GPG-KEY-WAZUH | gpg --no-default-keyring \
--keyring gnupg-ring:/usr/share/keyrings/wazuh.gpg --import && chmod 644 \
/usr/share/keyrings/wazuh.gpg
echo "deb [signed-by=/usr/share/keyrings/wazuh.gpg] https://packages.wazuh.com/4.x/apt/ \
stable main" | tee /etc/apt/sources.list.d/wazuh.list
# Install the agent
sudo apt update && sudo apt install wazuh-agent
# Configure the agent to point to your Wazuh server
sudo sed -i 's/MANAGER_IP/your-wazuh-server-ip/' /var/ossec/etc/ossec.conf
# Enable and start the agent
sudo systemctl daemon-reload
sudo systemctl enable --now wazuh-agent
Configuring Wazuh for Multi-Layer Integration
Here's where the real power of Wazuh comes through — when you configure it to ingest data from the other detection layers. This is the part that transforms four separate tools into a cohesive detection system. Edit the agent configuration at /var/ossec/etc/ossec.conf on each monitored host:
<!-- /var/ossec/etc/ossec.conf on the agent -->
<ossec_config>
<!-- Ingest auditd logs for correlation -->
<localfile>
<log_format>audit</log_format>
<location>/var/log/audit/audit.log</location>
</localfile>
<!-- Ingest AIDE reports -->
<localfile>
<log_format>full_command</log_format>
<command>aide --check 2>&1 | grep -E "^(changed|added|removed):"</command>
<frequency>86400</frequency>
<alias>aide-check</alias>
</localfile>
<!-- Enable Wazuh native FIM with eBPF (4.12.0+) -->
<syscheck>
<disabled>no</disabled>
<frequency>43200</frequency>
<scan_on_start>yes</scan_on_start>
<!-- Enable real-time monitoring with eBPF -->
<directories check_all="yes" realtime="yes">/etc,/usr/bin,/usr/sbin</directories>
<directories check_all="yes" realtime="yes">/bin,/sbin</directories>
<directories check_all="yes">/boot</directories>
<!-- Who-data: track who modified files -->
<directories check_all="yes" whodata="yes">/etc/ssh</directories>
<directories check_all="yes" whodata="yes">/etc/pam.d</directories>
<directories check_all="yes" whodata="yes">/etc/sudoers.d</directories>
<!-- Ignore volatile files -->
<ignore>/etc/mtab</ignore>
<ignore>/etc/resolv.conf</ignore>
<ignore type="sregex">\.swp$</ignore>
</syscheck>
<!-- Security Configuration Assessment -->
<sca>
<enabled>yes</enabled>
<scan_on_start>yes</scan_on_start>
<interval>12h</interval>
</sca>
<!-- Vulnerability detection -->
<vulnerability-detection>
<enabled>yes</enabled>
</vulnerability-detection>
</ossec_config>
Custom Detection Rules for Linux Attacks
Wazuh ships with thousands of rules, but you'll want custom rules tailored to your specific environment. These are some of the ones I'd consider essential for any Linux deployment. Add them on the Wazuh server at /var/ossec/etc/rules/local_rules.xml:
<group name="linux,attack,custom">
<!-- Detect reverse shell attempts -->
<rule id="100100" level="14">
<if_sid>80700</if_sid>
<match type="pcre2">bash\s+-i\s+>&\s+/dev/tcp|nc\s+-e\s+/bin|python.*socket.*connect|perl.*socket.*INET</match>
<description>Possible reverse shell attempt detected.</description>
<mitre>
<id>T1059</id>
</mitre>
</rule>
<!-- Detect cryptominer indicators -->
<rule id="100101" level="12">
<if_sid>80700</if_sid>
<match type="pcre2">stratum\+tcp://|xmrig|minerd|cpuminer|cryptonight</match>
<description>Possible cryptocurrency mining activity detected.</description>
<mitre>
<id>T1496</id>
</mitre>
</rule>
<!-- Detect unusual crontab modifications from auditd -->
<rule id="100102" level="10">
<if_sid>80700</if_sid>
<field name="audit.key">persistence_cron</field>
<description>Crontab modification detected — possible persistence mechanism (ATT&CK T1053).</description>
<mitre>
<id>T1053.003</id>
</mitre>
</rule>
<!-- Detect execution from world-writable directories -->
<rule id="100103" level="12">
<if_sid>80700</if_sid>
<field name="audit.key">exec_from_tmp|exec_from_shm</field>
<description>Binary execution from /tmp or /dev/shm detected — common attacker staging area.</description>
<mitre>
<id>T1059</id>
</mitre>
</rule>
<!-- Multiple failed SSH logins followed by success -->
<rule id="100104" level="13" frequency="5" timeframe="120">
<if_matched_sid>5710</if_matched_sid>
<same_source_ip />
<description>Possible brute-force SSH attack — multiple failed attempts from same source.</description>
<mitre>
<id>T1110</id>
</mitre>
</rule>
</group>
Active Response Configuration
Wazuh can automatically respond to detected threats, which is incredibly useful once you've tuned your rules properly. Configure active response on the Wazuh server to block brute-force attackers:
<!-- /var/ossec/etc/ossec.conf on the Wazuh server -->
<ossec_config>
<active-response>
<command>firewall-drop</command>
<location>local</location>
<rules_id>100104</rules_id>
<timeout>3600</timeout>
</active-response>
<active-response>
<command>host-deny</command>
<location>local</location>
<rules_id>100100</rules_id>
<timeout>0</timeout>
</active-response>
</ossec_config>
Layer 4: Deploying Suricata for Network Intrusion Detection
Installation and Initial Setup
Suricata provides deep packet inspection and protocol analysis. Install it on a system positioned to see relevant network traffic — typically a gateway, a mirror port, or a dedicated sensor:
# Debian/Ubuntu (from PPA for latest version)
sudo add-apt-repository ppa:oisf/suricata-stable
sudo apt update && sudo apt install suricata suricata-update
# RHEL/CentOS/Rocky
sudo dnf install epel-release
sudo dnf install suricata
# Verify installation
suricata --build-info | head -20
Core Configuration
Edit the Suricata configuration file at /etc/suricata/suricata.yaml. The two things you absolutely need to get right are your network definitions and the capture interface. Get these wrong and you'll either miss traffic or drown in false positives:
# /etc/suricata/suricata.yaml (key sections)
vars:
address-groups:
HOME_NET: "[192.168.0.0/16,10.0.0.0/8,172.16.0.0/12]"
EXTERNAL_NET: "!$HOME_NET"
HTTP_SERVERS: "$HOME_NET"
DNS_SERVERS: "$HOME_NET"
SSH_SERVERS: "$HOME_NET"
port-groups:
HTTP_PORTS: "80"
SHELLCODE_PORTS: "!80"
SSH_PORTS: "22"
# Capture settings
af-packet:
- interface: eth0
cluster-id: 99
cluster-type: cluster_flow
defrag: yes
use-mmap: yes
tpacket-v3: yes
# Enable EVE JSON logging for Wazuh integration
outputs:
- eve-log:
enabled: yes
filetype: regular
filename: eve.json
types:
- alert:
payload: yes
payload-printable: yes
packet: yes
metadata: yes
- dns:
enabled: yes
- tls:
enabled: yes
extended: yes
- files:
enabled: yes
force-magic: yes
force-hash: [md5, sha256]
- ssh:
enabled: yes
- flow:
enabled: yes
# Enable protocol detection
app-layer:
protocols:
tls:
enabled: yes
detection-ports:
dp: 443
ssh:
enabled: yes
dns:
enabled: yes
http:
enabled: yes
Rule Management
Suricata uses rules to detect threats. The Emerging Threats (ET) Open ruleset provides comprehensive coverage and is a solid starting point:
# Update rule sources
sudo suricata-update update-sources
# Enable the ET Open ruleset
sudo suricata-update enable-source et/open
# Update rules
sudo suricata-update
# Add custom rules for your environment
# /etc/suricata/rules/local.rules
# Detect outbound connections to known C2 ports
alert tcp $HOME_NET any -> $EXTERNAL_NET [4444,5555,6666,1337,31337] (
msg:"CUSTOM Potential C2 connection on suspicious port";
flow:established,to_server;
classtype:trojan-activity;
sid:9000001; rev:1;
)
# Detect SSH tunneling (large volume of SSH traffic)
alert tcp $HOME_NET any -> $EXTERNAL_NET 22 (
msg:"CUSTOM Possible SSH tunnel - high data volume";
flow:established;
dsize:>1000;
threshold:type both, track by_src, count 100, seconds 60;
classtype:policy-violation;
sid:9000002; rev:1;
)
# Detect potential data exfiltration via DNS
alert dns $HOME_NET any -> any any (
msg:"CUSTOM Suspicious DNS query - possible data exfiltration";
dns.query; content:"."; pcre:"/^[a-zA-Z0-9]{30,}\./";
classtype:bad-unknown;
sid:9000003; rev:1;
)
Start Suricata and verify it's running correctly:
# Test configuration
sudo suricata -T -c /etc/suricata/suricata.yaml
# Start Suricata
sudo systemctl enable --now suricata
# Verify it's capturing traffic
sudo tail -f /var/log/suricata/eve.json | jq 'select(.event_type=="alert")'
Integrating Suricata with Wazuh
Now for the piece that brings the network layer into your centralized view. Feed Suricata's alerts into Wazuh by adding this to the agent configuration on the Suricata host:
<!-- /var/ossec/etc/ossec.conf on the Suricata host -->
<localfile>
<log_format>json</log_format>
<location>/var/log/suricata/eve.json</location>
</localfile>
Wazuh includes built-in decoders and rules for Suricata EVE JSON logs, so alerts will automatically appear in the Wazuh dashboard with full context — source/destination IPs, protocol details, and matched rule information. No extra configuration needed on the server side.
Tying It All Together: The Correlation Pipeline
With all four layers deployed, let's trace how a real attack would flow through the stack. This is where the multi-layer approach really proves its worth. Consider this scenario: an attacker exploits a web application vulnerability, gains a shell, escalates privileges, installs a backdoor, and begins exfiltrating data.
Attack Phase 1: Initial Access
Suricata detects the exploit attempt via its ET Open rules, triggering an alert for the malicious HTTP payload. The alert flows into Wazuh through the EVE JSON integration.
Attack Phase 2: Command Execution
Auditd captures the execve system calls as the attacker runs commands from /tmp or /dev/shm. The exec_from_tmp and exec_from_shm audit keys fire, and Wazuh rule 100103 triggers a high-severity alert.
Attack Phase 3: Privilege Escalation
Auditd logs the setuid and setresuid calls under the priv_escalation_setuid key. If the attacker modifies /etc/sudoers, both auditd and Wazuh's native FIM detect the change simultaneously.
Attack Phase 4: Persistence
The attacker adds a crontab entry or creates a systemd service. AIDE detects the new files on its next scan. Auditd records the file creation in real time under the persistence_cron or persistence_systemd key. Wazuh correlates both signals and fires rule 100102.
Attack Phase 5: Data Exfiltration
Suricata detects the outbound connection to a suspicious port (rule 9000001) or the DNS tunneling pattern (rule 9000003). Auditd logs the network connect system call under the network_connect key. Wazuh correlates the network alert with the host-level events, providing a complete timeline of the attack from initial compromise to exfiltration.
Operational Best Practices
Tuning for Signal Over Noise
Let me be honest: the most common failure mode for IDS deployments is alert fatigue. Your first week will likely produce thousands of false positives. Don't let that discourage you — it's completely normal. Here's how to manage them:
- Start in monitor-only mode. Don't enable active response or blocking until you've tuned your rules for at least two weeks. Seriously, resist the temptation.
- Whitelist known-good activity. When your configuration management tool (Ansible, Puppet, Salt) modifies files, those changes will trigger AIDE and auditd. Create targeted exclusions for expected change windows.
- Use Wazuh's rule refinement. If a rule generates false positives, don't disable it entirely. Instead, create a child rule with additional conditions that lower its level or exclude specific patterns.
- Monitor alert volume trends. A sudden spike in alerts often indicates either a real incident or a configuration change that needs a tuning response. Either way, investigate.
Database and Log Retention
Plan your storage carefully. A Wazuh deployment with 50 agents generates approximately 5-10 GB of indexed data per day (sometimes more, depending on how chatty your auditd rules are). Configure index rotation and retention policies:
# Wazuh indexer — set index retention via ISM policy
# Navigate to Wazuh Dashboard > Indexer Management > Index Management
# Recommended retention:
# - Security alerts: 90 days hot, 365 days warm
# - Audit logs: 365 days (or per compliance requirements)
# - FIM data: 180 days
# - Suricata flow logs: 30 days
Securing the Detection Infrastructure
Your IDS infrastructure is itself a high-value target — and this is something that's easy to forget. If an attacker compromises your Wazuh server, they can suppress alerts and operate undetected. Apply these hardening measures:
- Isolate the Wazuh server on a dedicated management network. It should not be accessible from the same networks it monitors.
- Enable TLS for all agent-server communication. Wazuh does this by default since version 4.0, but verify your certificates are valid and rotation is configured.
- Forward critical alerts to an out-of-band channel. Configure Wazuh to send high-severity alerts (level 12+) via email, Slack, or a separate syslog server that the attacker is unlikely to have access to.
- Monitor the monitors. Use auditd to watch the Wazuh agent process and configuration files. If the agent stops or its config changes, you need to know immediately.
- Protect the AIDE database on read-only media or a remote system, as discussed earlier.
Compliance Mapping
One nice bonus of this architecture: it maps directly to major compliance frameworks. If you're dealing with auditors (and who isn't these days), having this ready saves a lot of headaches:
- PCI DSS 4.0 — Requirement 10 (Log and Monitor All Access) and Requirement 11 (Test Security Regularly). AIDE satisfies file integrity monitoring requirements (11.5), auditd and Wazuh cover audit trail requirements (10.2), and Suricata addresses IDS/IPS requirements (11.4).
- CIS Benchmarks — Wazuh's SCA module directly evaluates CIS benchmark compliance for your Linux distributions, generating pass/fail reports automatically.
- NIST 800-53 — Controls AU-2 (Audit Events), AU-6 (Audit Review), SI-4 (Information System Monitoring), and SI-7 (Software Integrity) are all covered by this architecture.
- HIPAA — The audit controls (§164.312(b)) and integrity controls (§164.312(c)(1)) requirements are satisfied by the auditd and AIDE layers respectively.
Monitoring and Threat Hunting with the Stack
Once your stack is deployed and tuned, don't just wait for alerts — use it proactively. Here are some practical queries and techniques for hunting common Linux threats:
Hunting for Unauthorized SSH Keys
# Using Wazuh API to query FIM changes on authorized_keys files
curl -k -X GET "https://wazuh-server:55000/syscheck/agent-id?pretty=true&search=authorized_keys" \
-H "Authorization: Bearer $TOKEN"
# Using ausearch for the same on a local system
sudo ausearch -k persistence_ssh -ts this-week -i
Hunting for Hidden Processes
# Compare /proc entries against ps output to find hidden processes
diff <(ls /proc | grep -E "^[0-9]+$" | sort -n) \
<(ps -eo pid --no-headers | tr -d " " | sort -n)
# Query auditd for processes that exited immediately (common for droppers)
sudo ausearch -m EXECVE -ts today -i | grep -E "argc=[12] " | head -20
Hunting for DNS-Based Exfiltration
# Query Suricata DNS logs for unusually long queries
cat /var/log/suricata/eve.json | \
jq 'select(.event_type=="dns") | select(.dns.query[0].rrname | length > 50) |
{timestamp: .timestamp, query: .dns.query[0].rrname, src: .src_ip}'
Conclusion: Detection Is a Process, Not a Product
Building a multi-layer intrusion detection system on Linux isn't a one-time project — it's an ongoing operational practice. The deployment covered in this guide gives you visibility across four critical detection layers: filesystem changes (AIDE), system call behavior (auditd), host-level event correlation (Wazuh), and network traffic analysis (Suricata). Together, they create overlapping fields of detection that make it extremely difficult for an attacker to operate without triggering at least one alert.
But the technology is only half the story.
The other half is operational discipline. You need to tune your rules regularly, investigate alerts promptly, update your threat intelligence feeds, and continuously expand your detection coverage as new attack techniques emerge. The MITRE ATT&CK framework — recently updated to v18 in October 2025 with the new Detection Strategies and Analytics model — provides an excellent roadmap for identifying gaps in your detection coverage.
My recommendation? Start by deploying one layer at a time. Get AIDE running first — it's the simplest and provides immediate value. Add auditd rules next, focusing on the MITRE ATT&CK techniques most relevant to your environment. Deploy Wazuh as your central correlation engine, and finally add Suricata for network visibility. Within a few weeks, you'll have a detection capability that rivals commercial EDR solutions — without the licensing costs and with full transparency into how every detection works.
The attackers are getting better every quarter. Your detection needs to keep pace.