- setup.sh erkennt Engine (Docker, Podman, podman-docker-Wrapper) und Compose-Werkzeug (docker compose, docker-compose, podman-compose, podman compose) - Rootless Podman wird mit Hinweis abgelehnt (Ulimits, Port 443, Zertifikatsrechte) - Als root: vm.max_map_count, firewalld-Port und podman-restart.service setzen - Neue Befehle: up, down, ps, logs, info; WAZUH_SKIP_SYSCTL und CONTAINER_ENGINE - Images voll qualifiziert (docker.io/..., localhost/...) wegen Podman-Short-Name-Policy - Bind-Mounts mit :z fuer SELinux (RHEL) - README: Abschnitt Red Hat / Podman, Befehle ueber setup.sh Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
193 lines
10 KiB
Markdown
193 lines
10 KiB
Markdown
# Wazuh in Docker – Daten per SSH abholen
|
||
|
||
Wazuh 4.14 (Manager, Indexer, Dashboard) als Single-Node in Docker. Die überwachten Hosts brauchen
|
||
**keinen Agent und keine eingehende Verbindung zum Server**. Alles läuft über SSH, das der Server
|
||
zu den Hosts aufbaut.
|
||
|
||
```
|
||
┌──────────────────────── Docker ───────────────────────────┐
|
||
Host web01 ◄──ssh──┐ │ sshpull.collector ──► /remote-logs/<host>/*.log ──┐ │
|
||
Host db01 ◄──ssh──┼─┤ (journalctl -f / tail -F, Reconnect + Resume) │ │
|
||
Host ... ◄──ssh──┤ │ ▼ │
|
||
│ │ wazuh.manager ◄── <localfile> (Decoder/Regeln) │
|
||
└─┤ wazuh-agentlessd: FIM + Befehls-Diffs per ssh │
|
||
│ │ filebeat │
|
||
│ ▼ │
|
||
│ wazuh.indexer ◄── wazuh.dashboard :443 │
|
||
└───────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
## Was pro Host abgeholt wird
|
||
|
||
| Baustein | Wie | Ergebnis in Wazuh |
|
||
|---|---|---|
|
||
| `logs: journal` | `journalctl -o json -f` über eine dauerhafte SSH-Verbindung. Nach einem Abbruch geht es per Journal-Cursor lückenlos weiter. | Alle Standard-Decoder/-Regeln (sshd, sudo, PAM, Kernel, …), z. B. Brute-Force-Erkennung |
|
||
| `logs: file` | `tail -F` auf eine Datei. Nach einem Abbruch geht es über Inode und Byte-Offset weiter. | Wie oben, `format` wählbar (`syslog`, `apache`, `json`, `audit`, …) |
|
||
| `integrity` | Agentless: Größe, Rechte, Besitzer und SHA256 aller Dateien unter den Pfaden, periodisch. Der Vergleich läuft gegen eine eigene Baseline pro Host. | Alarme bei neuen, geänderten oder gelöschten Dateien (siehe Regeln unten) |
|
||
| `commands` | Agentless: Befehle ausführen und die Ausgabe mit dem letzten Lauf vergleichen. | Alarm „agentless: Change detected“ mit Diff (z. B. neuer offener Port, neuer Benutzer) |
|
||
| `agent_tunnel` | Optional: Reverse-Tunnel `ssh -R 1514/1515`. Ein echter Wazuh-Agent auf dem Host verbindet sich mit `127.0.0.1`. | Voller Agent-Umfang (SCA, Schwachstellen, Inventar, Echtzeit-FIM) |
|
||
|
||
Die Agentless-Skripte (`manager/agentless/sshpull_*`) sind eigene Versionen ohne `expect`. Sie
|
||
nutzen Schlüssel-Login und optional `sudo -n`. Bei den mitgelieferten Wazuh-Skripten wirkt die
|
||
`use_sudo`-Option nicht.
|
||
|
||
Der Integritätscheck nutzt bewusst **nicht** das FIM-Protokoll von Wazuh. Damit landen alle
|
||
Agentless-Hosts in der FIM-Datenbank des Managers, nur nach Pfad geordnet. `/etc/passwd` von
|
||
Host A, Host B und vom Manager würde sich gegenseitig überschreiben, und der erste Lauf erzeugt
|
||
pro Datei einen „File added“-Alarm. Hier gilt stattdessen: Der erste Lauf legt nur die Baseline
|
||
an (ein Alarm), danach werden nur echte Änderungen gemeldet.
|
||
|
||
### Eigene Regeln (`manager/rules/sshpull_rules.xml`)
|
||
|
||
| ID | Level | Bedeutung |
|
||
|---|---|---|
|
||
| 100501 | 3 | FIM-Baseline für einen Host angelegt |
|
||
| 100502 | 5 | Datei hinzugefügt |
|
||
| 100503 | 7 | Datei geändert (Inhalt, Größe, Rechte oder Besitzer) |
|
||
| 100504 | 7 | Datei gelöscht |
|
||
| 100505 | 10 | Mehr als 200 Änderungen in einem Lauf: nur eine Zusammenfassung (Update oder Kompromittierung?) |
|
||
| 100506 | 10 | Sicherheitskritische Datei (passwd, shadow, sudoers, sshd_config, pam.d, cron, authorized_keys, ld.so.preload) |
|
||
| 100507 | 10 | Neue oder geänderte Datei mit SUID/SGID-Bit |
|
||
| 100511 | 7 | SSH-Abholung gestört (Log-Stream, Tunnel oder Agentless-Check). Wird nur beim Zustandswechsel gemeldet. |
|
||
| 100512 | 3 | SSH-Abholung wieder verbunden |
|
||
|
||
Befehls-Diffs kommen über die Wazuh-Standardregel 555 („Integrity checksum for agentless
|
||
device changed“), mit dem Diff im Alarmtext.
|
||
|
||
## Installation
|
||
|
||
Voraussetzungen: Docker **oder** Podman, ca. 8 GB RAM frei, ca. 10 GB Platz für Images und Daten.
|
||
|
||
`setup.sh` erkennt die Umgebung selbst (`./setup.sh info` zeigt, was erkannt wurde):
|
||
|
||
| Erkannt | Verwendet |
|
||
|---|---|
|
||
| Docker | `docker compose` oder `docker-compose` |
|
||
| Podman (z. B. RHEL, auch mit podman-docker-Wrapper) | `podman-compose` oder `podman compose` |
|
||
| SELinux aktiv | Alle Bind-Mounts haben `:z`. Ohne SELinux wirkt das nicht. |
|
||
|
||
Als root (`sudo ./setup.sh ...`) passt das Skript das System zusätzlich an:
|
||
|
||
- `vm.max_map_count=262144` (dauerhaft in `/etc/sysctl.d/99-wazuh.conf`)
|
||
- firewalld: Port für das Dashboard freigeben
|
||
- Podman: `podman-restart.service` aktivieren, damit der Stack nach einem Reboot startet
|
||
|
||
Ohne root gibt es dafür nur Hinweise. Mit `WAZUH_SKIP_SYSCTL=1` bleibt der Kernel-Parameter
|
||
unangetastet. Mit `CONTAINER_ENGINE=docker|podman` lässt sich die Erkennung übersteuern.
|
||
|
||
```bash
|
||
./setup.sh # Passwörter (.env), TLS-Zertifikate, SSH-Key ssh/id_ed25519, Images bauen
|
||
nano hosts.yml # Hosts eintragen (siehe hosts.yml.example)
|
||
```
|
||
|
||
### Red Hat / Podman
|
||
|
||
```bash
|
||
sudo dnf install -y podman openssh-clients
|
||
sudo dnf install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-9.noarch.rpm # RHEL 9
|
||
sudo dnf install -y podman-compose
|
||
sudo ./setup.sh # Podman bitte als root, alle weiteren Befehle ebenfalls mit sudo
|
||
```
|
||
|
||
Podman muss als root laufen. Rootless scheitert an den Ulimits der Wazuh-Container (memlock,
|
||
nofile), an Port 443 und an den Zertifikatsrechten. `setup.sh` bricht in diesem Fall mit einem
|
||
Hinweis ab.
|
||
|
||
### Zielhosts vorbereiten
|
||
|
||
Auf jedem Host wird ein Benutzer `wazuh-pull` angelegt. Er bekommt die Gruppen
|
||
`systemd-journal` und `adm` (Leserechte auf Logs) und den öffentlichen Schlüssel mit
|
||
`restrict` (kein PTY, keine Weiterleitungen):
|
||
|
||
```bash
|
||
./setup.sh host-script | ssh admin@web01 sudo sh
|
||
# Optionen: --sudo NOPASSWD-sudo (nur nötig für root-only Dateien, z.B. /etc/shadow im FIM)
|
||
# --tunnel erlaubt Reverse-Forwarding auf 127.0.0.1:1514/1515 (für agent_tunnel)
|
||
```
|
||
|
||
Das Skript erst ansehen, dann ausführen: `./setup.sh host-script`
|
||
|
||
### Host-Keys, Test, Start
|
||
|
||
```bash
|
||
./setup.sh keyscan # Host-Keys -> ssh/known_hosts, Fingerprints mit den Hosts vergleichen!
|
||
./setup.sh test # prüft Login, Gruppen, Journal-/Datei-Leserechte
|
||
./setup.sh up # starten (down, ps, logs [dienst] analog)
|
||
./setup.sh passwords # Login fürs Dashboard: https://<server>/ (admin / ...)
|
||
```
|
||
|
||
Der erste Start dauert 1–3 Minuten, bis der Indexer bereit ist.
|
||
|
||
### Hosts ändern
|
||
|
||
`hosts.yml` bearbeiten, dann `./setup.sh apply`. Damit werden Manager und Collector neu
|
||
erzeugt. Der Manager schreibt seine Agentless- und Localfile-Konfiguration beim Start aus
|
||
`hosts.yml` neu (`manager/generate.py`).
|
||
|
||
## Wo finde ich die Daten im Dashboard?
|
||
|
||
Per SSH geholte Logs und Agentless-Ergebnisse laufen **über den Manager**. Im Dashboard
|
||
erscheinen sie deshalb beim Agent `000` (`wazuh.manager`). Unterscheiden lassen sich die Hosts
|
||
so:
|
||
|
||
- Logs: `location: /remote-logs/web01/journal.log` oder `predecoder.hostname: web01`
|
||
- FIM: `data.sshpull_fim.host: web01`, Befehls-Diffs: `location` enthält `wazuh-pull@web01`
|
||
- Verbindungsstatus: `rule.groups: sshpull_status`
|
||
|
||
Beispiel-Filter in *Threat Hunting*: `rule.groups: sshpull` oder `location:*web01*`
|
||
|
||
Hinweis: Rohlogs ohne Regeltreffer werden (wie bei Wazuh üblich) nicht indexiert, nur Alerts.
|
||
Alles archivieren: in `config/wazuh_cluster/wazuh_manager.conf` `<logall_json>yes</logall_json>`
|
||
setzen und in Filebeat das Archiv aktivieren (siehe Wazuh-Doku „archives“).
|
||
|
||
## Optional: voller Agent über SSH-Tunnel
|
||
|
||
Wenn auf einem Host ein Wazuh-Agent installiert werden darf, er aber den Server nicht erreicht:
|
||
|
||
1. In `hosts.yml` beim Host `agent_tunnel: true` setzen. Host mit `--tunnel` vorbereiten.
|
||
2. Agent auf dem Host installieren (Paket von packages.wazuh.com, gleiche Version wie der
|
||
Manager) mit `WAZUH_MANAGER=127.0.0.1`, bzw. in `ossec.conf` `<address>127.0.0.1</address>`.
|
||
3. `./setup.sh apply`. Der Collector hält den Tunnel offen, der Agent registriert sich über
|
||
`127.0.0.1:1515` und erscheint als eigener Agent im Dashboard.
|
||
|
||
Ports 1514 und 1515 sind nicht nach außen veröffentlicht. Nur der Collector erreicht sie.
|
||
|
||
## Dateien
|
||
|
||
| Pfad | Zweck |
|
||
|---|---|
|
||
| `hosts.yml` | Die einzige Stelle für Hosts und Optionen |
|
||
| `docker-compose.yml` | Stack. Nach außen geht nur das Dashboard (`DASHBOARD_PORT`) |
|
||
| `.env` | Version, Zeitzone, Passwörter (von `setup.sh` erzeugt, nicht committen) |
|
||
| `ssh/` | `id_ed25519` (privater Schlüssel, nicht committen), `known_hosts` |
|
||
| `common/sshpull_hosts.py` | Laden und Prüfen von `hosts.yml`, SSH-Config erzeugen (Manager und Collector) |
|
||
| `collector/` | Log-Streaming-Dienst |
|
||
| `manager/` | Manager-Image: openssh-client, Agentless-Skripte, Config-Generator |
|
||
| `config/` | Wazuh-Konfiguration (Basis: wazuh/wazuh-docker single-node) |
|
||
|
||
## Fehlersuche
|
||
|
||
```bash
|
||
./setup.sh logs sshpull.collector # Verbindungen/Reconnects pro Host
|
||
./setup.sh logs wazuh.manager | grep -i agentless
|
||
```
|
||
|
||
Für `exec` die passende Engine nehmen (`docker` oder `sudo podman`). Die Containernamen zeigt
|
||
`./setup.sh ps`, z. B.:
|
||
`sudo podman exec wazuh_wazuh.manager_1 ls -la /remote-logs/web01/`
|
||
|
||
- `journal=WARNUNG:nur-eigene-Logs…` bei `./setup.sh test`: Der Benutzer ist nicht in der
|
||
Gruppe `systemd-journal`. Danach neu einloggen bzw. den Collector neu starten.
|
||
- Podman: Hängt `./setup.sh up` bei „podman wait“, konnte ein Container nicht angelegt werden.
|
||
Die Ursache zeigt `sudo podman-compose --verbose up -d 2>&1 | grep -i error`.
|
||
- `Host key verification failed`: `./setup.sh keyscan` ausführen, oder in `hosts.yml`
|
||
`ssh.host_key_checking: accept-new` setzen.
|
||
- Agentless-Checks laufen das erste Mal kurz nach dem Start, danach alle `interval` Sekunden.
|
||
Der erste Lauf legt nur die Baseline an, Alarme gibt es erst bei Änderungen. Die Checks
|
||
laufen nacheinander. Bei vielen Hosts mit großen Pfaden (`/usr`) das `interval` großzügig
|
||
wählen.
|
||
- Baseline eines Hosts neu anlegen (z. B. nach einem gewollten Update):
|
||
`<docker|podman> exec wazuh_wazuh.manager_1 rm /var/ossec/agentless/.sshpull/web01.json`
|
||
- Ohne `sudo` sind root-only Dateien (z. B. `/etc/shadow`, Host-Keys) nur über Größe, Rechte
|
||
und Besitzer überwacht, nicht über den Inhalt.
|