129 lines
6.4 KiB
Markdown
129 lines
6.4 KiB
Markdown
# Wazuh SIEM — fleet integration
|
|
|
|
The fleet's auditd stream is analysed by **Wazuh running on its own node:
|
|
`wazuh1.bitasylum.org`** (deployed + managed independently — keeping the
|
|
memory-hungry indexer off the serving host, which runs SPDK = the fleet's root
|
|
disk / a SPOF; see TODO #6/#7). This directory is the **fleet ↔ Wazuh
|
|
integration**, not a deployment of Wazuh.
|
|
|
|
**Ingestion is agentless** (no agent in the diskless image — that would carry
|
|
per-host registration state, breaking invariant #1). The host side is unchanged;
|
|
the serving-host collector relays a copy to the SIEM node:
|
|
|
|
```
|
|
host: auditd → audisp-syslog (LOCAL6) → rsyslog omfwd ─┐
|
|
▼ TCP/514
|
|
serving host: audit-collector (rsyslog) ──relay(omfwd)──► wazuh1.bitasylum.org
|
|
│ still writes durable per-host files (manager :514 syslog
|
|
└ disk-assisted queue (store-and-forward) → indexer → dashboard)
|
|
```
|
|
|
|
The collector keeps its durable file copy + disk queue and **relays a copy**, so a
|
|
SIEM outage never drops audit records or blocks the file writes.
|
|
|
|
## To turn it on (two ends)
|
|
|
|
**1. On the SIEM node (`wazuh1`):** add the syslog reception block — insert
|
|
[config/fleet-remote-syslog.xml](config/fleet-remote-syslog.xml) into the
|
|
manager's `ossec.conf`, set `<allowed-ips>` to the **serving host's IP**, and make
|
|
sure the manager's TCP syslog port is reachable (the upstream single-node compose +
|
|
[docker-compose.override.yml](docker-compose.override.yml) publish `514/tcp`).
|
|
Restart the manager.
|
|
|
|
**2. On the serving host:** the relay is already wired in
|
|
[../audit-collector/rsyslog.conf](../audit-collector/rsyslog.conf), targeting
|
|
`wazuh1.bitasylum.org:514`. Apply it by rebuilding the collector image (the conf
|
|
is COPY'd in at build):
|
|
|
|
```bash
|
|
cd serving && docker compose build audit && docker compose up -d audit
|
|
# the serving host must RESOLVE + ROUTE to wazuh1.bitasylum.org
|
|
```
|
|
|
|
## Decode the fleet's auditd (custom decoder + rules)
|
|
|
|
The fleet ships auditd **agentlessly** via audisp-syslog, and STIG sets
|
|
`name_format = hostname` (keep it — compliance), which prepends `node=<host>` to
|
|
each record. Wazuh's built-in auditd decoder expects the line to start with
|
|
`type=`, so it decodes **nothing** (`"decoder":{}`) — `rule.groups: audit` stays
|
|
empty. Install the fleet decoder + rules **on the master** so the records decode + alert.
|
|
|
|
**Declarative (recommended) — bind-mount via compose**, using the image's
|
|
`/wazuh-config-mount` mechanism (entrypoint copies the tree into `/var/ossec` at
|
|
startup; correct ownership, survives `--force-recreate`, no `docker cp`). Add to
|
|
the `wazuh.master` service (see [docker-compose.override.yml](docker-compose.override.yml)) —
|
|
put the files next to your compose / `wazuh_manager.conf`:
|
|
|
|
```yaml
|
|
volumes:
|
|
- ./config/local_decoder.xml:/wazuh-config-mount/etc/decoders/local_decoder.xml
|
|
- ./config/local_rules.xml:/wazuh-config-mount/etc/rules/local_rules.xml
|
|
```
|
|
```bash
|
|
docker compose up -d --force-recreate wazuh.master
|
|
docker exec <master> grep -iE 'error|rules|decoder' /var/ossec/logs/ossec.log | tail # no errors
|
|
# verify decoding interactively — paste a full_log line (the `Jun 27 ... node=...`):
|
|
docker exec -it <master> /var/ossec/bin/wazuh-logtest
|
|
```
|
|
In a cluster, mount on the **master only** — Wazuh syncs `etc/decoders` + `etc/rules`
|
|
to the workers. A config change needs a recreate/restart (the copy is at startup).
|
|
|
|
> Quick one-off test without editing compose (lost on recreate):
|
|
> `docker cp config/local_decoder.xml <master>:/var/ossec/etc/decoders/ && docker cp config/local_rules.xml <master>:/var/ossec/etc/rules/ && docker exec <master> /var/ossec/bin/wazuh-control restart`
|
|
`wazuh-logtest` should show `decoder 'fleet-auditd'` and `audit.type` extracted,
|
|
firing rule `100100` (+ a higher rule for auth/SELinux types). Then in the
|
|
dashboard: **`rule.groups: fleet_audit`** populates. (The base rule is **level 0**
|
|
— captured but not alerted — so the high-volume SYSCALL/PATH stream doesn't flood
|
|
alerts; only security-relevant types alert. Tune `config/local_rules.xml`.)
|
|
|
|
**Full audit trail (not just alerts):** to make *every* record searchable
|
|
(forensics / ISM retention), enable archive indexing — `<logall_json>yes</logall_json>`
|
|
on the manager **and** `archives.enabled: true` in the manager's
|
|
`/etc/filebeat/filebeat.yml`, then add the `wazuh-archives-*` index pattern in the
|
|
dashboard. High-volume — slim the auditd rules on the host or filter before
|
|
indexing.
|
|
|
|
## Verify
|
|
|
|
```bash
|
|
# on the SIEM node, after a diskless host has booted + generated auditd events:
|
|
docker compose logs wazuh.manager | grep -i syslog # listener up, source accepted
|
|
# Wazuh dashboard → Security events: auditd records from the boot-LAN hosts appear,
|
|
# decoded (type=SYSCALL/USER_LOGIN/...) and rule-tagged.
|
|
```
|
|
|
|
If nothing arrives: the manager logs rejected sources — confirm `<allowed-ips>`
|
|
matches the serving host's IP **as wazuh1 sees it** (may be its management IP, not
|
|
`SERVER_IP`), then tighten to a /32.
|
|
|
|
## ⚠ Protect the hop — it's now on the wire
|
|
|
|
Co-located it was loopback; serving-host → `wazuh1` now crosses the network, and
|
|
audit records are sensitive (ISM: protect logs in transit). The relay ships
|
|
**plaintext 514** for first bring-up — only acceptable on a **trusted private
|
|
path**. Before relying on it, switch to **syslog-over-TLS** (`gtls`, port 6514,
|
|
the manager's cert + the fleet CA) on both the relay (`omfwd` with the TLS driver)
|
|
and the manager `<remote>` block. Same CA work as the secure-path / audit-offload
|
|
TLS TODO.
|
|
|
|
## Production checklist
|
|
|
|
- **TLS the relay → wazuh1 hop** (above).
|
|
- **Rotate all Wazuh credentials** on the node (indexer admin, API, dashboard).
|
|
- **Restrict the dashboard/API** (`:443`/`:55000`) to admin hosts / VPN — never the
|
|
boot LAN.
|
|
- **`allowed-ips` → /32** of the serving host.
|
|
- **Retention/archival**: indexer ISM for the ISM-mandated retention + cold archive.
|
|
- **SSP**: update the "Detect" section + ISM event-logging controls once
|
|
bench-verified, and record the retention design.
|
|
|
|
## Files here
|
|
|
|
| File | Where it's used |
|
|
|---|---|
|
|
| `config/fleet-remote-syslog.xml` | the `<remote>` block to add to the **manager** on wazuh1 |
|
|
| `docker-compose.override.yml` | reference overlay if wazuh1 uses the upstream single-node compose |
|
|
| `.env.example` | reference env for deploying the wazuh1 node |
|
|
|
|
(The actual fleet-side relay lives in `../audit-collector/rsyslog.conf`.)
|