Wazuh SIEM Integration#

InfraGuard ships a built-in plugin that forwards events to Wazuh’s indexer (OpenSearch-compatible) in bulk. Events are batched in memory and flushed periodically, keeping network overhead low while still delivering near-real-time visibility into your redirector traffic.

Architecture#

The Wazuh plugin extends BatchForwardingPlugin, which handles the buffering and flush logic. The plugin itself handles Wazuh-specific authentication and payload formatting.

Filter Pipeline --> Event Dispatcher --> Wazuh Plugin
                                             |
                                        buffer (deque)
                                             |
                                     flush at batch_size or interval
                                             |
                                    POST /_bulk (NDJSON)
                                             |
                                      Wazuh-Indexer (OpenSearch)

Configuration#

plugins:
  wazuh:
    enabled: true
    wazuh_api_url: "https://wazuh-manager:55000"
    indexer_url: "https://wazuh-indexer:9200"
    username: "wazuh-wui"
    password: "${WAZUH_PASSWORD}"
    index_name: "infraguard-events"
    batch_size: 50              # flush after 50 events (default)
    flush_interval_seconds: 10  # or flush every 10 seconds (default)
    verify_ssl: true

Authentication#

The plugin authenticates against the Wazuh API using JWT:

  1. POSTs credentials to /security/user/authenticate
  2. Receives a JWT token
  3. Caches the token and refreshes it every 800 seconds (tokens expire at ~900s)

The JWT is used for the Wazuh manager API. The indexer uses basic auth with the same credentials.

Event Format#

Events are sent as NDJSON (newline-delimited JSON) to the /_bulk endpoint:

{"index": {"_index": "infraguard-events"}}
{"timestamp": "2025-03-15T14:30:00Z", "domain": "cdn.example.com", "client_ip": "203.0.113.42", "method": "GET", "uri": "/api/updates", "filter_result": "block", "filter_reason": "ja3_blocked", "user_agent": "python-requests/2.28.0", "protocol": "https"}

Each flush sends one HTTP request with all buffered events in a single _bulk payload.

Batch Buffering#

The BatchForwardingPlugin base class provides:

  • In-memory deque: Events are appended to a deque as they arrive
  • Size-triggered flush: When the deque reaches batch_size, a flush fires immediately
  • Time-triggered flush: A background asyncio task flushes every flush_interval_seconds regardless of buffer size
  • Backpressure: If the indexer is unreachable, events stay in the buffer. The deque is unbounded, so a prolonged outage will eventually consume memory. Monitor the circuit breaker state and indexer health.

Querying Events#

Once events land in Wazuh-Indexer, they’re searchable through the Wazuh dashboard (Kibana fork) or the OpenSearch API:

# Find all blocked requests from a specific IP
curl -k -u admin:admin "https://wazuh-indexer:9200/infraguard-events/_search" \
  -H "Content-Type: application/json" \
  -d '{"query": {"bool": {"must": [{"match": {"client_ip": "203.0.113.42"}}, {"match": {"filter_result": "block"}}]}}}'