# 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//*.log ──┐ │ Host db01 ◄──ssh──┼─┤ (journalctl -f / tail -F, Reconnect + Resume) │ │ Host ... ◄──ssh──┤ │ ▼ │ │ │ wazuh.manager ◄── (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 und docker-compose, ca. 8 GB RAM frei, ca. 10 GB Platz für Images und Daten. Für den Indexer: ```bash echo 'vm.max_map_count=262144' | sudo tee /etc/sysctl.d/99-wazuh.conf && sudo sysctl --system ``` ```bash ./setup.sh # Passwörter (.env), TLS-Zertifikate, SSH-Key ssh/id_ed25519, Images bauen nano hosts.yml # Hosts eintragen (siehe hosts.yml.example) ``` ### 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 docker-compose up -d ./setup.sh passwords # Login fürs Dashboard: https:/// (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` `yes` 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` `
127.0.0.1
`. 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 docker-compose logs -f sshpull.collector # Verbindungen/Reconnects pro Host docker-compose exec wazuh.manager grep -i agentless /var/ossec/logs/ossec.log docker-compose exec wazuh.manager ls -la /remote-logs/web01/ # kommen Logs an? ``` - `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. - `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-compose exec wazuh.manager 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.