41 pages ยท 8 sections
Ctrl K
GitHub Portfolio

Wazuh EDR/XDR

Wazuh is an open-source security platform that provides unified XDR and SIEM protection for endpoints and cloud workloads. It delivers threat detection, integrity monitoring, incident response, and compliance monitoring across on-premises, cloud, and container environments.

Wazuh Architecture

Wazuh 4.x uses a distributed architecture with four main components:

ComponentFunctionDeployment
Wazuh ServerAnalyzes agent data, decodes events, matches against rules, triggers alertsLinux server or container
Wazuh IndexerFull-text search and analytics engine (OpenSearch fork)Cluster for HA
Wazuh DashboardWeb-based security analytics and visualization (OpenSearch Dashboards)Web server
Wazuh AgentEndpoint sensor collecting logs, file integrity, configuration dataLinux, Windows, macOS
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                    Wazuh Server                           โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”‚
โ”‚  โ”‚  Analysisd   โ”‚  โ”‚  Remoted     โ”‚  โ”‚  Execd       โ”‚   โ”‚
โ”‚  โ”‚  (Decoders)  โ”‚  โ”‚  (Agent comm)โ”‚  โ”‚  (Active resp)โ”‚  โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ”‚
โ”‚         โ”‚                 โ”‚                  โ”‚            โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”‚
โ”‚  โ”‚                  Rules Engine                     โ”‚    โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                          โ”‚
            โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
            โ–ผ             โ–ผ             โ–ผ
      โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
      โ”‚  Agent 1 โ”‚ โ”‚  Agent 2 โ”‚ โ”‚  Agent N โ”‚
      โ”‚ (Linux)  โ”‚ โ”‚(Windows) โ”‚ โ”‚ (macOS)  โ”‚
      โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
            โ”‚             โ”‚             โ”‚
            โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                          โ–ผ
               โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
               โ”‚  Wazuh Indexer   โ”‚
               โ”‚  (OpenSearch)    โ”‚
               โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                        โ–ผ
               โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
               โ”‚ Wazuh Dashboard  โ”‚
               โ”‚  (Web UI/Kibana) โ”‚
               โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Deployment Options

Docker Compose Deployment (All-in-One)

# docker-compose.wazuh.yml
version: "3.8"

services:
  wazuh-manager:
    image: wazuh/wazuh-manager:4.7.2
    container_name: wazuh-manager
    hostname: wazuh-manager
    restart: unless-stopped
    ports:
      - "1514:1514"     # Agent syslog
      - "1515:1515"     # Agent enrollment
      - "514:514/udp"   # Syslog UDP
      - "55000:55000"   # Wazuh API
    environment:
      - INDEXER_URL=https://wazuh-indexer:9200
      - INDEXER_USERNAME=admin
      - INDEXER_PASSWORD=${INDEXER_PASSWORD}
      - FILEBEAT_SSL_VERIFICATION_MODE=full
      - SSL_CERTIFICATE_AUTHORITIES=/etc/ssl/root-ca.pem
      - SSL_CERTIFICATE=/etc/ssl/filebeat.pem
      - SSL_KEY=/etc/ssl/filebeat.key
      - API_USERNAME=wazuh-wui
      - API_PASSWORD=${API_PASSWORD}
    volumes:
      - wazuh_api_config:/var/ossec/api/configuration
      - wazuh_etc:/var/ossec/etc
      - wazuh_logs:/var/ossec/logs
      - wazuh_queue:/var/ossec/queue
      - wazuh_var_multigroups:/var/ossec/var/multigroups
      - wazuh_integrations:/var/ossec/integrations
      - wazuh_active_response:/var/ossec/active-response/bin
      - wazuh_agentless:/var/ossec/agentless
      - wazuh_wodles:/var/ossec/wodles
      - wazuh_filebeat:/etc/filebeat
      - ./certs/root-ca.pem:/etc/ssl/root-ca.pem:ro
      - ./certs/filebeat.pem:/etc/ssl/filebeat.pem:ro
      - ./certs/filebeat.key:/etc/ssl/filebeat.key:ro
    networks:
      - wazuh

  wazuh-indexer:
    image: wazuh/wazuh-indexer:4.7.2
    container_name: wazuh-indexer
    hostname: wazuh-indexer
    restart: unless-stopped
    ports:
      - "9200:9200"
    environment:
      - OPENSEARCH_JAVA_OPTS="-Xms2g -Xmx2g"
      - bootstrap.memory_lock=true
      - NODE_NAME=wazuh-indexer
      - CLUSTER_NAME=wazuh-cluster
      - CLUSTER_INITIAL_MASTER_NODES=wazuh-indexer
      - DISCOVERY_SEED_HOSTS=wazuh-indexer
      - plugins.security.ssl.http.enabled=true
      - plugins.security.ssl.http.pemcert_filepath=/usr/share/wazuh-indexer/certs/indexer.pem
      - plugins.security.ssl.http.pemkey_filepath=/usr/share/wazuh-indexer/certs/indexer.key
      - plugins.security.ssl.http.pemtrustedcas_filepath=/usr/share/wazuh-indexer/certs/root-ca.pem
      - plugins.security.ssl.transport.enabled=true
      - plugins.security.ssl.transport.pemcert_filepath=/usr/share/wazuh-indexer/certs/indexer.pem
      - plugins.security.ssl.transport.pemkey_filepath=/usr/share/wazuh-indexer/certs/indexer.key
      - plugins.security.ssl.transport.pemtrustedcas_filepath=/usr/share/wazuh-indexer/certs/root-ca.pem
      - plugins.security.allow_default_init_securityindex=true
      - plugins.security.authcz.admin_dn="CN=admin,OU=Wazuh,O=Wazuh,L=California,C=US"
    ulimits:
      memlock:
        soft: -1
        hard: -1
      nofile:
        soft: 65536
        hard: 65536
    volumes:
      - wazuh_indexer_data:/var/lib/wazuh-indexer
      - ./certs/root-ca.pem:/usr/share/wazuh-indexer/certs/root-ca.pem:ro
      - ./certs/indexer.pem:/usr/share/wazuh-indexer/certs/indexer.pem:ro
      - ./certs/indexer.key:/usr/share/wazuh-indexer/certs/indexer.key:ro
      - ./certs/admin.pem:/usr/share/wazuh-indexer/certs/admin.pem:ro
      - ./certs/admin.key:/usr/share/wazuh-indexer/certs/admin.key:ro
    networks:
      - wazuh

  wazuh-dashboard:
    image: wazuh/wazuh-dashboard:4.7.2
    container_name: wazuh-dashboard
    hostname: wazuh-dashboard
    restart: unless-stopped
    ports:
      - "443:5601"
    environment:
      - INDEXER_USERNAME=admin
      - INDEXER_PASSWORD=${INDEXER_PASSWORD}
      - WAZUH_API_URL=https://wazuh-manager
      - API_USERNAME=wazuh-wui
      - API_PASSWORD=${API_PASSWORD}
    volumes:
      - ./certs/root-ca.pem:/usr/share/wazuh-dashboard/certs/root-ca.pem:ro
      - ./certs/dashboard.pem:/usr/share/wazuh-dashboard/certs/dashboard.pem:ro
      - ./certs/dashboard.key:/usr/share/wazuh-dashboard/certs/dashboard.key:ro
    depends_on:
      - wazuh-indexer
      - wazuh-manager
    networks:
      - wazuh

volumes:
  wazuh_api_config:
  wazuh_etc:
  wazuh_logs:
  wazuh_queue:
  wazuh_var_multigroups:
  wazuh_integrations:
  wazuh_active_response:
  wazuh_agentless:
  wazuh_wodles:
  wazuh_filebeat:
  wazuh_indexer_data:

networks:
  wazuh:
    driver: bridge

Certificate Generation

#!/bin/bash
# generate-wazuh-certs.sh โ€” Generate certificates for Wazuh deployment

cd /opt/wazuh/certs || exit 1

# Root CA
openssl genrsa -out root-ca.key 2048
openssl req -x509 -new -nodes -key root-ca.key -sha256 -days 3650 \
  -subj "/C=US/ST=California/L=San Francisco/O=Company/CN=Wazuh Root CA" \
  -out root-ca.pem

# Function to generate a certificate
generate_cert() {
    local name=$1
    local subject=$2
    
    openssl genrsa -out "${name}.key" 2048
    openssl req -new -key "${name}.key" \
      -subj "$subject" \
      -out "${name}.csr"
    openssl x509 -req -in "${name}.csr" \
      -CA root-ca.pem -CAkey root-ca.key \
      -CAcreateserial -out "${name}.pem" \
      -days 365 -sha256
}

generate_cert "indexer" "/C=US/ST=California/L=San Francisco/O=Company/OU=Wazuh/CN=wazuh-indexer"
generate_cert "dashboard" "/C=US/ST=California/L=San Francisco/O=Company/OU=Wazuh/CN=wazuh-dashboard"
generate_cert "filebeat" "/C=US/ST=California/L=San Francisco/O=Company/OU=Wazuh/CN=wazuh-manager"
generate_cert "admin" "/C=US/ST=California/L=San Francisco/O=Company/OU=Wazuh/CN=admin"

# Set permissions
chmod 644 *.pem
chmod 600 *.key
rm -f *.csr

echo "Certificates generated in $(pwd)"

Agent Installation

Linux Agent

# RHEL/CentOS/Amazon Linux 2
curl -o wazuh-agent-4.7.2-1.x86_64.rpm \
  https://packages.wazuh.com/4.x/yum/wazuh-agent-4.7.2-1.x86_64.rpm
sudo WAZUH_MANAGER='wazuh-manager.internal' WAZUH_AGENT_NAME='$(hostname)' \
  rpm -ihv wazuh-agent-4.7.2-1.x86_64.rpm

sudo systemctl daemon-reload
sudo systemctl enable wazuh-agent
sudo systemctl start wazuh-agent

# Ubuntu/Debian
curl -s https://packages.wazuh.com/key/GPG-KEY-WAZUH | sudo gpg --no-default-keyring --keyring gnupg-ring:/usr/share/keyrings/wazuh.gpg --import
sudo 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" | \
  sudo tee /etc/apt/sources.list.d/wazuh.list
sudo apt update
sudo WAZUH_MANAGER='wazuh-manager.internal' WAZUH_AGENT_NAME='$(hostname)' \
  apt install wazuh-agent

# Custom ossec.conf for centralized configuration
sudo tee /var/ossec/etc/ossec.conf > /dev/null <<'EOF'
<ossec_config>
  <client>
    <server>
      <address>wazuh-manager.internal</address>
      <port>1514</port>
      <protocol>tcp</protocol>
    </server>
    <config-profile>ubuntu, ubuntu22, webserver</config-profile>
    <notify_time>10</notify_time>
    <time-reconnect>60</time-reconnect>
    <auto_restart>yes</auto_restart>
    <crypto_method>aes</crypto_method>
  </client>

  <localfile>
    <log_format>syslog</log_format>
    <location>/var/log/syslog</location>
  </localfile>

  <localfile>
    <log_format>syslog</log_format>
    <location>/var/log/auth.log</location>
  </localfile>

  <localfile>
    <log_format>syslog</log_format>
    <location>/var/log/kern.log</location>
  </localfile>

  <localfile>
    <log_format>json</log_format>
    <location>/var/log/app/application.json</location>
  </localfile>

  <!-- File Integrity Monitoring -->
  <syscheck>
    <directories check_all="yes" report_changes="yes" realtime="yes">
      /etc,/usr/bin,/usr/sbin
    </directories>
    <directories check_all="yes" report_changes="yes" restrict="etc">
      /etc/shadow,/etc/passwd,/etc/group
    </directories>
    <directories check_all="yes" report_changes="yes">
      /var/www,/opt/app
    </directories>
    <ignore>/etc/mtab</ignore>
    <ignore>/etc/prelink.cache</ignore>
    <ignore>/etc/resolv.conf</ignore>
    <ignore>/etc/hdparam</ignore>
    <ignore>/etc/udev/devices</ignore>
    <ignore>/etc/network/run</ignore>
    <frequency>43200</frequency>
  </syscheck>

  <!-- Vulnerability Detection -->
  <wodle name="syscollector">
    <disabled>no</disabled>
    <interval>1h</interval>
    <scan_on_start>yes</scan_on_start>
    <hardware>yes</hardware>
    <os>yes</os>
    <network>yes</network>
    <packages>yes</packages>
    <ports all="no">yes</ports>
    <processes>yes</processes>
  </wodle>

  <!-- Rootcheck -->
  <rootcheck>
    <disabled>no</disabled>
    <check_files>yes</check_files>
    <check_trojans>yes</check_trojans>
    <check_dev>yes</check_dev>
    <check_sys>yes</check_sys>
    <check_pids>yes</check_pids>
    <check_ports>yes</check_ports>
    <check_if>yes</check_if>
    <frequency>43200</frequency>
  </rootcheck>
</ossec_config>
EOF

sudo systemctl restart wazuh-agent

Windows Agent

# PowerShell installation script
$WazuhManager = "wazuh-manager.internal"
$AgentName = $env:COMPUTERNAME
$DownloadUrl = "https://packages.wazuh.com/4.x/windows/wazuh-agent-4.7.2-1.msi"

# Download
Invoke-WebRequest -Uri $DownloadUrl -OutFile "$env:TEMP\wazuh-agent.msi"

# Install
Start-Process msiexec.exe -ArgumentList "/i","$env:TEMP\wazuh-agent.msi", "/qn", "WAZUH_MANAGER=$WazuhManager", "WAZUH_AGENT_NAME=$AgentName" -Wait

# Configure
$Config = @"
<ossec_config>
  <client>
    <server>
      <address>$WazuhManager</address>
      <port>1514</port>
      <protocol>tcp</protocol>
    </server>
    <crypto_method>aes</crypto_method>
  </client>

  <localfile>
    <location>Application</location>
    <log_format>eventchannel</log_format>
  </localfile>

  <localfile>
    <location>Security</location>
    <log_format>eventchannel</log_format>
    <query>Event/System[EventID != 5145 and EventID != 5156]</query>
  </localfile>

  <localfile>
    <location>System</location>
    <log_format>eventchannel</log_format>
  </localfile>

  <localfile>
    <location>Microsoft-Windows-Sysmon/Operational</location>
    <log_format>eventchannel</log_format>
  </localfile>

  <syscheck>
    <directories check_all="yes" report_changes="yes" realtime="yes">
      C:\Windows\System32\drivers\etc
    </directories>
    <directories check_all="yes" report_changes="yes">
      C:\inetpub\wwwroot
    </directories>
    <windows_registry arch="both">
      HKEY_LOCAL_MACHINE\Software\Classes\batfile
    </windows_registry>
    <windows_registry arch="both">
      HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Run
    </windows_registry>
    <frequency>7200</frequency>
  </syscheck>

  <wodle name="syscollector">
    <disabled>no</disabled>
    <interval>1h</interval>
    <os>yes</os>
    <packages>yes</packages>
    <hotfixes>yes</hotfixes>
  </wodle>
</ossec_config>
"@

Set-Content -Path "C:\Program Files (x86)\ossec-agent\ossec.conf" -Value $Config

# Start service
Start-Service -Name "Wazuh"
Set-Service -Name "Wazuh" -StartupType Automatic

# Verify
Get-Service -Name "Wazuh"
& "C:\Program Files (x86)\ossec-agent\agent-auth.exe" -m $WazuhManager

macOS Agent

# Download and install
curl -o wazuh-agent.pkg \
  https://packages.wazuh.com/4.x/macos/wazuh-agent-4.7.2-1.intel64.pkg
sudo installer -pkg wazuh-agent.pkg -target /

# Configure
sudo tee /Library/Ossec/etc/ossec.conf > /dev/null <<'EOF'
<ossec_config>
  <client>
    <server>
      <address>wazuh-manager.internal</address>
      <port>1514</port>
      <protocol>tcp</protocol>
    </server>
  </client>
  <localfile>
    <log_format>syslog</log_format>
    <location>/var/log/system.log</location>
  </localfile>
  <syscheck>
    <directories check_all="yes" realtime="yes">/etc,/usr/bin</directories>
  </syscheck>
</ossec_config>
EOF

# Start
sudo /Library/Ossec/bin/wazuh-control start

Rules and Decoders Customization

Custom Detection Rules

<!-- /var/ossec/etc/rules/local_rules.xml -->
<group name="custom,application,">

  <!-- Detect failed authentication bursts -->
  <rule id="100001" level="10" frequency="8" timeframe="120">
    <if_matched_sid>100002</if_matched_sid>
    <description>Multiple failed login attempts for user: $(dstuser)</description>
    <mitre>
      <id>T1110</id>
      <id>T1110.001</id>
    </mitre>
    <group>authentication_failed,pci_dss_10.2.4,pci_dss_10.2.5,gpg13_7.1,gdpr_IV_35.7.d,gdpr_IV_32.2,hipaa_164.312.b,nist_800_53_AU.14,nist_800_53_AC.7,</group>
  </rule>

  <rule id="100002" level="5">
    <decoded_as>json</decoded_as>
    <field name="event_type">authentication_failed</field>
    <description>Application authentication failed</description>
    <group>authentication_failed,</group>
  </rule>

  <!-- Detect privilege escalation via sudo -->
  <rule id="100010" level="10">
    <if_sid>5402</if_sid>
    <match>root</match>
    <description>Sudo escalation to root detected for user: $(srcuser)</description>
    <mitre>
      <id>T1548</id>
      <id>T1548.003</id>
    </mitre>
    <group>pci_dss_10.2.5,pci_dss_10.2.2,gpg13_7.6,gpg13_7.8,gpg13_7.13,gdpr_IV_32.2,hipaa_164.312.b,nist_800_53_AU.6,nist_800_53_AC.6,</group>
  </rule>

  <!-- Detect suspicious file modifications in web root -->
  <rule id="100020" level="12">
    <if_group> syscheck </if_group>
    <field name="file" type="pcre2">^/var/www/html/.*\.(php|jsp|asp|aspx|sh)$</field>
    <description>Suspicious file modification in web root: $(file)</description>
    <mitre>
      <id>T1505</id>
      <id>T1505.003</id>
    </mitre>
    <group>pci_dss_11.5,gpg13_4.11,gdpr_IV_35.7.d,hipaa_164.312.c.1,hipaa_164.312.c.2,nist_800_53_SI.7,trojan_activity,</group>
  </rule>

  <!-- Detect outbound connections to known C2 IPs -->
  <rule id="100030" level="14">
    <if_group> firewall </if_group>
    <list field="dstip" lookup="address_match_key">etc/lists/c2-blacklist</list>
    <description>Outbound connection to known C2 server: $(dstip)</description>
    <mitre>
      <id>T1071</id>
      <id>T1071.001</id>
    </mitre>
    <group>pci_dss_1.3.1,gpg13_4.12,gdpr_IV_35.7.d,hipaa_164.312.a.1,nist_800_53_AC.4,nist_800_53_SC.7,nist_800_53_SI.4,</group>
  </rule>

  <!-- API rate limiting violation -->
  <rule id="100040" level="8" frequency="20" timeframe="60">
    <if_matched_sid>100041</if_matched_sid>
    <description>API rate limit exceeded by IP: $(srcip) โ€” Possible DoS or scraping</description>
    <mitre>
      <id>T1498</id>
      <id>T1498.001</id>
    </mitre>
    <group>pci_dss_11.4,gpg13_4.2,gdpr_IV_35.7.d,nist_800_53_SI.4,</group>
  </rule>

  <rule id="100041" level="3">
    <decoded_as>json</decoded_as>
    <field name="event_type">rate_limit_exceeded</field>
    <description>API rate limit exceeded</description>
  </rule>

</group>

Custom Decoders

<!-- /var/ossec/etc/decoders/local_decoders.xml -->
<decoder name="custom-application">
  <program_name>^api-gateway$</program_name>
</decoder>

<decoder name="custom-app-json">
  <parent>custom-application</parent>
  <plugin_decoder offset="after_parent">JSON_Decoder</plugin_decoder>
</decoder>

<!-- Decoder for application structured logs -->
<decoder name="application-structured">
  <program_name>^app-logger$</program_name>
  <plugin_decoder>JSON_Decoder</plugin_decoder>
</decoder>

<!-- Extract fields from API gateway logs -->
<decoder name="api-gateway-access">
  <type>syslog</type>
  <program_name>^nginx</program_name>
  <regex type="pcre2">^(\S+) \S+ \S+ \[(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2})\] "(\w+) (\S+) HTTP/\d\.\d" (\d{3}) (\d+) "(\S+)" "(.+?)"$</regex>
  <order>srcip, timestamp, method, url, status, bytes, referrer, user_agent</order>
</decoder>

Active Response Configuration

<!-- /var/ossec/etc/ossec.conf โ€” Active Response section -->
<ossec_config>
  <command>
    <name>firewall-drop</name>
    <executable>firewall-drop</executable>
    <timeout_allowed>yes</timeout_allowed>
  </command>

  <command>
    <name>host-deny</name>
    <executable>host-deny</executable>
    <timeout_allowed>yes</timeout_allowed>
  </command>

  <command>
    <name>custom-block-ip</name>
    <executable>custom-block-ip.sh</executable>
    <timeout_allowed>yes</timeout_allowed>
  </command>

  <!-- Block IPs with repeated failed SSH attempts -->
  <active-response>
    <command>firewall-drop</command>
    <location>server</location>
    <level>10</level>
    <timeout>3600</timeout>  <!-- 1 hour block -->
    <repeated_offenders>1h,4h,24h,168h</repeated_offenders>
  </active-response>

  <!-- Block C2 connections immediately -->
  <active-response>
    <command>custom-block-ip</command>
    <location>all</location>
    <rules_id>100030</rules_id>
    <timeout>0</timeout>  <!-- Permanent -->
  </active-response>

  <!-- Block API abusers -->
  <active-response>
    <command>custom-block-ip</command>
    <location>all</location>
    <rules_id>100040</rules_id>
    <timeout>7200</timeout>  <!-- 2 hour block -->
    <repeated_offenders>2h,8h,24h,72h</repeated_offenders>
  </active-response>
</ossec_config>
#!/bin/bash
# /var/ossec/active-response/bin/custom-block-ip.sh
# Custom active response script for IP blocking

ACTION=$1
USER=$2
IP=$3
ALERT_ID=$4
RULE_ID=$5

# AWS Security Group integration
AWS_REGION="us-east-1"
SECURITY_GROUP="sg-wazuh-blocklist"

log_message() {
    echo "$(date '+%Y-%m-%d %H:%M:%S') $1" >> /var/ossec/logs/active-responses.log
}

case "$ACTION" in
    add)
        log_message "Blocking IP $IP (Alert: $ALERT_ID, Rule: $RULE_ID)"
        # Add to AWS Security Group
        aws ec2 authorize-security-group-ingress \
            --region "$AWS_REGION" \
            --group-id "$SECURITY_GROUP" \
            --protocol tcp \
            --port 0-65535 \
            --cidr "$IP/32" \
            --description "Wazuh block: $(date +%s) alert=$ALERT_ID" 2>/dev/null || true
        # Also add local iptables rule
        iptables -I INPUT -s "$IP" -j DROP
        ;;
    delete)
        log_message "Unblocking IP $IP"
        # Remove from AWS Security Group
        aws ec2 revoke-security-group-ingress \
            --region "$AWS_REGION" \
            --group-id "$SECURITY_GROUP" \
            --protocol tcp \
            --port 0-65535 \
            --cidr "$IP/32" 2>/dev/null || true
        # Remove local iptables rule
        iptables -D INPUT -s "$IP" -j DROP
        ;;
esac

exit 0

Integrations

VirusTotal Integration

<!-- /var/ossec/etc/ossec.conf -->
<ossec_config>
  <integration>
    <name>virustotal</name>
    <api_key>${VIRUSTOTAL_API_KEY}</api_key>
    <group>syscheck,pci_dss_11.4</group>
    <alert_format>json</alert_format>
    <rule_id>100020</rule_id>
  </integration>
</ossec_config>

Slack and PagerDuty Alerts

<!-- Slack integration for security team channel -->
<integration>
  <name>slack</name>
  <hook_url>https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX</hook_url>
  <alert_format>json</alert_format>
  <level>10</level>
  <group>pci_dss,gdpr,nist_800_53</group>
</integration>

<!-- PagerDuty for critical alerts only -->
<integration>
  <name>custom</name>
  <hook_url>https://events.pagerduty.com/integration/YOUR_INTEGRATION_KEY/enqueue</hook_url>
  <alert_format>json</alert_format>
  <level>14</level>
  <group>attack,c2</group>
</integration>

MITRE ATT&CK Mapping

Wazuh Rule IDDetectionMITRE TechniqueTactic
100001Brute force authenticationT1110 โ€” Brute ForceCredential Access
100010Privilege escalation (sudo)T1548 โ€” Abuse Elevation ControlPrivilege Escalation
100020Web shell uploadT1505.003 โ€” Web ShellPersistence
100030C2 communicationT1071 โ€” Application Layer ProtocolCommand and Control
100040API abuse / DoST1498 โ€” Network Denial of ServiceImpact
5402 (built-in)Sudo usageT1548.003 โ€” SudoPrivilege Escalation
5710 (built-in)SSH brute forceT1110.001 โ€” Password GuessingCredential Access
5501 (built-in)Login failedT1110 โ€” Brute ForceCredential Access
554 (built-in)Web attack (SQLi)T1190 โ€” Exploit Public-Facing AppInitial Access
31103 (built-in)Repeated attacksT1595.002 โ€” Vulnerability ScanningReconnaissance

Cloud Security Monitoring

AWS CloudTrail Integration

#!/bin/bash
# wazuh-aws-cloudtrail.sh โ€” CloudTrail integration setup

BUCKET="company-cloudtrail-logs"
QUEUE_NAME="wazuh-cloudtrail-queue"

echo "[1/3] Creating SQS queue..."
aws sqs create-queue --queue-name "$QUEUE_NAME"
QUEUE_URL=$(aws sqs get-queue-url --queue-name "$QUEUE_NAME" --query 'QueueUrl' --output text)
QUEUE_ARN=$(aws sqs get-queue-attributes --queue-url "$QUEUE_URL" --attribute-names QueueArn --query 'Attributes.QueueArn' --output text)

echo "[2/3] Configuring S3 bucket notifications..."
aws s3api put-bucket-notification-configuration \
    --bucket "$BUCKET" \
    --notification-configuration '{
        "QueueConfigurations": [{
            "Id": "wazuh-cloudtrail",
            "QueueArn": "'"$QUEUE_ARN"'",
            "Events": ["s3:ObjectCreated:*"],
            "Filter": {"KeyFilterRules": [{"Name": "prefix", "Value": "AWSLogs/"}]}
        }]
    }'

echo "[3/3] Configuring Wazuh S3 module..."
cat >> /var/ossec/etc/ossec.conf <<EOF
<wodle name="aws-s3">
  <disabled>no</disabled>
  <interval>10m</interval>
  <run_on_start>yes</run_on_start>
  <skip_on_error>yes</skip_on_error>
  <bucket type="cloudtrail">
    <name>$BUCKET</name>
    <aws_profile>default</aws_profile>
  </bucket>
</wodle>
EOF

sudo systemctl restart wazuh-manager
echo "CloudTrail integration configured"

File Integrity Monitoring (FIM)

Wazuh's FIM capability monitors critical files and directories for unauthorized changes, generating alerts with before/after comparisons.

<!-- FIM configuration in ossec.conf -->
<syscheck>
  <!-- System files -->
  <directories check_all="yes" report_changes="yes" realtime="yes">
    /etc/passwd,/etc/shadow,/etc/group,/etc/gshadow
  </directories>
  
  <directories check_all="yes" report_changes="yes" realtime="yes">
    /etc/ssh,/etc/pam.d,/etc/sudoers,/etc/sudoers.d
  </directories>
  
  <directories check_all="yes" report_changes="yes" realtime="yes">
    /bin,/sbin,/usr/bin,/usr/sbin
  </directories>
  
  <!-- Application files -->
  <directories check_all="yes" report_changes="yes" realtime="yes">
    /var/www,/opt/app,/etc/nginx
  </directories>
  
  <!-- Scheduled tasks -->
  <directories check_all="yes">
    /etc/cron.d,/etc/cron.daily,/etc/cron.hourly,/etc/cron.weekly,/etc/cron.monthly
  </directories>
  <directories check_all="yes">/var/spool/cron/crontabs</directories>
  
  <!-- Kernel modules -->
  <directories check_all="yes">/etc/modprobe.d,/etc/modules-load.d</directories>
  
  <!-- Ignore commonly changing files -->
  <ignore>/etc/resolv.conf</ignore>
  <ignore>/etc/hdparam</ignore>
  <ignore>/etc/network/run</ignore>
  <ignore>/etc/mtab</ignore>
  <ignore>/etc/prelink.cache</ignore>
  <ignore>/etc/resolvconf</ignore>
  <ignore>/var/log</ignore>
  <ignore>/var/spool/postfix/private</ignore>
  
  <!-- Scan frequency -->
  <frequency>43200</frequency>  <!-- Every 12 hours -->
  
  <!-- Real-time for critical files is enabled via realtime="yes" above -->
  
  <!-- Who-data audit (Linux) -->
  <whodata>
    <startup_healthcheck>yes</startup_healthcheck>
  </whodata>
</syscheck>

Vulnerability Detection

<!-- Vulnerability detection in ossec.conf (manager-side) -->
<wodle name="vulnerability-detector">
  <disabled>no</disabled>
  <interval>5m</interval>
  <min_full_scan_interval>6h</min_full_scan_interval>
  <run_on_start>yes</run_on_start>
  
  <!-- Ubuntu feeds -->
  <provider name="canonical">
    <enabled>yes</enabled>
    <os>focal</os>
    <os>jammy</os>
    <update_interval>1h</update_interval>
  </provider>
  
  <!-- RHEL feeds -->
  <provider name="redhat">
    <enabled>yes</enabled>
    <os>8</os>
    <os>9</os>
    <update_interval>1h</update_interval>
  </provider>
  
  <!-- Amazon Linux -->
  <provider name="alas">
    <enabled>yes</enabled>
    <os>amazon-linux-2</os>
    <os>amazon-linux-2022</os>
    <update_interval>1h</update_interval>
  </provider>
  
  <!-- NVD feed -->
  <provider name="nvd">
    <enabled>yes</enabled>
    <update_interval>1h</update_interval>
  </provider>
  
  <!-- Alert severity threshold -->
  <!-- Vulnerabilities with CVSS >= 7.0 (HIGH) will generate alerts -->
  <!--<severity>Medium</severity>-->
</wodle>

Custom Rules for Compliance

<!-- PCI-DSS compliance rules -->
<group name="pci_dss,">
  
  <!-- PCI 10.2.4 โ€” Invalid logical access attempts -->
  <rule id="200001" level="8">
    <if_sid>5710</if_sid>
    <description>PCI DSS 10.2.4: Invalid SSH login attempt to $(dstuser)</description>
    <group>pci_dss_10.2.4,invalid_login,authentication_failed,</group>
  </rule>
  
  <!-- PCI 10.2.5 โ€” Use of identification and authentication mechanisms -->
  <rule id="200002" level="3">
    <if_sid>5501</if_sid>
    <description>PCI DSS 10.2.5: Successful SSH login by $(dstuser)</description>
    <group>pci_dss_10.2.5,authentication_success,</group>
  </rule>
  
  <!-- PCI 11.5 โ€” Unauthorized changes to critical files -->
  <rule id="200003" level="11">
    <if_sid>550</if_sid>
    <field name="file" type="pcre2">/etc/(passwd|shadow|group|ssh|nginx)</field>
    <description>PCI DSS 11.5: Critical file modified: $(file)</description>
    <group>pci_dss_11.5,syscheck,files_changed,</group>
  </rule>
  
  <!-- PCI 11.4 โ€” Intrusion detection alert -->
  <rule id="200004" level="12">
    <if_group>attacks</if_group>
    <description>PCI DSS 11.4: Intrusion detection alert triggered</description>
    <group>pci_dss_11.4,attacks,</group>
  </rule>

</group>

<!-- GDPR compliance rules -->
<group name="gdpr,">
  
  <!-- GDPR Article 5 โ€” Integrity and confidentiality -->
  <rule id="200010" level="13">
    <if_group>malware</if_group>
    <description>GDPR Art.5: Potential data integrity compromise โ€” malware detected</description>
    <group>gdpr_IV_32.2,gdpr_IV_35.7.d,malware,</group>
  </rule>
  
  <!-- GDPR Article 32 โ€” Security of processing -->
  <rule id="200011" level="10">
    <if_group>rootcheck</if_group>
    <description>GDPR Art.32: System anomaly detected โ€” potential security issue</description>
    <group>gdpr_IV_32.2,rootcheck,</group>
  </rule>
  
  <!-- GDPR Article 33 โ€” Data breach notification -->
  <rule id="200012" level="14">
    <if_matched_sid>200003</if_matched_sid>
    <field name="file">/etc/shadow</field>
    <description>GDPR Art.33: Potential data breach โ€” authentication database modified</description>
    <group>gdpr_IV_33,gdpr_IV_34.1,pci_dss_10.2.5,pci_dss_11.4,</group>
  </rule>

</group>

Wazuh API for Automation

#!/usr/bin/env python3
"""
wazuh_api_client.py โ€” Python client for Wazuh API automation.
Automates alert retrieval, agent management, and configuration.
"""

import requests
import urllib3
import json
from datetime import datetime, timedelta
from typing import List, Dict, Optional

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

class WazuhAPI:
    """Wazuh API client for security automation."""
    
    def __init__(self, host: str, username: str, password: str, verify_ssl: bool = False):
        self.base_url = f"https://{host}:55000"
        self.verify = verify_ssl
        self.token = self._authenticate(username, password)
    
    def _authenticate(self, username: str, password: str) -> str:
        """Obtain JWT token."""
        resp = requests.get(
            f"{self.base_url}/security/user/authenticate",
            auth=(username, password),
            verify=self.verify
        )
        resp.raise_for_status()
        return resp.json()['data']['token']
    
    def _headers(self) -> dict:
        return {"Authorization": f"Bearer {self.token}"}
    
    def get_alerts(self, severity: Optional[str] = None, 
                   hours: int = 24, limit: int = 100) -> List[Dict]:
        """Retrieve security alerts from the SIEM."""
        query = {
            "q": f"timestamp>{self._ago(hours)}",
            "sort": "-timestamp",
            "limit": limit
        }
        if severity:
            query["q"] += f";rule.level>={self._severity_level(severity)}"
        
        resp = requests.get(
            f"{self.base_url}/security/events",
            headers=self._headers(),
            params=query,
            verify=self.verify
        )
        resp.raise_for_status()
        return resp.json()['data']['affected_items']
    
    def get_agents(self, status: Optional[str] = None) -> List[Dict]:
        """List all agents with optional status filter."""
        params = {"limit": 1000}
        if status:
            params["status"] = status
        
        resp = requests.get(
            f"{self.base_url}/agents",
            headers=self._headers(),
            params=params,
            verify=self.verify
        )
        resp.raise_for_status()
        return resp.json()['data']['affected_items']
    
    def get_agent_vulnerabilities(self, agent_id: str) -> List[Dict]:
        """Get vulnerability scan results for a specific agent."""
        resp = requests.get(
            f"{self.base_url}/vulnerability/{agent_id}",
            headers=self._headers(),
            verify=self.verify
        )
        resp.raise_for_status()
        return resp.json()['data']['affected_items']
    
    def block_ip(self, agent_id: str, ip: str, timeout: int = 3600) -> Dict:
        """Trigger active response to block an IP on an agent."""
        resp = requests.put(
            f"{self.base_url}/active-response",
            headers=self._headers(),
            json={
                "command": "firewall-drop",
                "arguments": [ip, "add", str(timeout)],
                "alert": {"data": {"srcip": ip}},
                "custom": True
            },
            params={"agents_list": agent_id},
            verify=self.verify
        )
        resp.raise_for_status()
        return resp.json()
    
    def get_fim_events(self, agent_id: str, hours: int = 24) -> List[Dict]:
        """Get file integrity monitoring events for an agent."""
        resp = requests.get(
            f"{self.base_url}/syscheck/{agent_id}",
            headers=self._headers(),
            params={
                "q": f"date>{self._ago(hours)}",
                "sort": "-date",
                "limit": 500
            },
            verify=self.verify
        )
        resp.raise_for_status()
        return resp.json()['data']['affected_items']
    
    @staticmethod
    def _ago(hours: int) -> str:
        ago = datetime.utcnow() - timedelta(hours=hours)
        return ago.strftime("%Y-%m-%dT%H:%M:%SZ")
    
    @staticmethod
    def _severity_level(severity: str) -> int:
        levels = {"low": 3, "medium": 7, "high": 10, "critical": 13}
        return levels.get(severity.lower(), 7)


# Example: Daily security report
if __name__ == "__main__":
    import os
    
    wazuh = WazuhAPI(
        host=os.environ["WAZUH_HOST"],
        username=os.environ["WAZUH_USER"],
        password=os.environ["WAZUH_PASS"]
    )
    
    # Get critical alerts from last 24 hours
    critical_alerts = wazuh.get_alerts(severity="critical", hours=24, limit=50)
    print(f"Critical alerts (24h): {len(critical_alerts)}")
    
    # Check agent health
    agents = wazuh.get_agents()
    offline = [a for a in agents if a['status'] != 'active']
    print(f"Total agents: {len(agents)}, Offline: {len(offline)}")
    
    # Get vulnerability summary
    total_vulns = 0
    for agent in agents[:5]:
        vulns = wazuh.get_agent_vulnerabilities(agent['id'])
        critical_vulns = [v for v in vulns if v.get('severity') == 'Critical']
        total_vulns += len(vulns)
        if critical_vulns:
            print(f"Agent {agent['name']}: {len(critical_vulns)} critical vulns")
    
    print(f"Total vulnerabilities: {total_vulns}")

Wazuh Agent Deployment at Scale

For large-scale deployments, use configuration management (Ansible/Puppet/Chef) to distribute agents. Deploy agents in groups with centralized agent.conf pushed from the manager for unified policy management. Use the Wazuh API to automate agent registration and group assignment.

Related Topics