groot c49bfa1c34
Some checks failed
release-tag / release-image (push) Failing after 5m54s
Vollständiges Redesign
2026-07-23 14:45:35 +02:00
2026-04-23 21:56:44 +02:00
2026-07-23 14:45:35 +02:00
2026-07-23 14:45:35 +02:00
2026-07-23 14:45:35 +02:00
2026-07-23 14:45:35 +02:00
2026-05-02 21:37:21 +02:00
2026-07-23 14:45:35 +02:00
2026-07-23 14:45:35 +02:00
2026-07-23 14:45:35 +02:00
2026-07-23 14:45:35 +02:00
2026-07-23 14:45:35 +02:00
2026-07-23 14:45:35 +02:00
2026-07-23 14:45:35 +02:00
2026-07-23 14:45:35 +02:00
2026-07-23 14:45:35 +02:00
2026-07-23 14:45:35 +02:00
2026-07-23 14:45:35 +02:00
2026-07-23 14:45:35 +02:00
2026-07-23 14:45:35 +02:00

Greenfield SIEM

Kompletter Neuaufbau des bisherigen Projekts. Vom Altprojekt bleibt absichtlich nur der HTTP-Ingress-Vertrag erhalten.

Was dieses Projekt löst

Der Eventpfad ist append-only, asynchron und auf große Datenmengen ausgelegt:

Windows Agents
    │ POST /ingest
    ▼
Ingress API ──► PostgreSQL (nur Agent-Authentifizierung / Control Plane)
    │
    ▼
Redpanda (persistente Queue, 24h Recovery-Puffer)
    │
    ▼
Processor
    ├──► ClickHouse (normalisierte, durchsuchbare Events, 90 Tage)
    └──► gzip Spool ──► Garage/S3 (Raw-Batches, 30 Tage)
             │
             └── rclone übernimmt Upload + Retention

ClickHouse ──► Detector ──► PostgreSQL (Detections / Status)
ClickHouse + PostgreSQL ──► API/UI

Keine Event-Zeilen in PostgreSQL. Keine MariaDB. Keine synchronen Detection-Abfragen im Ingress. Keine Raw-XML-Duplikate in ClickHouse.

One-click Deployment

Voraussetzung: Docker Engine / Docker Desktop mit Docker Compose Plugin.

Linux / macOS / WSL

chmod +x deploy.sh
./deploy.sh

Windows PowerShell

./deploy.ps1

Das Deployment erzeugt beim ersten Start kryptographisch zufällige Secrets in .env, erzeugt die Garage-Konfiguration, baut die SIEM-Binaries und startet alle Dienste.

Danach:

  • UI: http://SERVER:8080/ui
  • Ingress: http://SERVER:8090/ingest
  • Redpanda Console: lokal http://127.0.0.1:8081
  • Prometheus: lokal http://127.0.0.1:9090

Beim ersten Start werden UI_USERNAME=admin und ein zufälliges UI_PASSWORD erzeugt. Browser fragen diese Zugangsdaten beim Öffnen von /ui ab.

UI-Benutzer/Passwort und Enrollment-Key werden am Ende des Deployments ausgegeben und stehen außerdem in der geschützten .env. Die UI/API ist standardmäßig per HTTP Basic Auth geschützt; für Zugriff über ein fremdes Netz gehört zusätzlich TLS davor.

Bestehende Collector können den bisherigen Enrollment-Key weiterverwenden, ohne eine .env von Hand anzulegen. Linux/macOS/WSL: ENROLLMENT_KEY="ALTER_KEY" ./deploy.sh. PowerShell: $env:ENROLLMENT_KEY="ALTER_KEY"; ./deploy.ps1. Alle anderen Secrets werden trotzdem zufällig erzeugt.

Bestehender Ingress bleibt kompatibel

POST /ingest
X-API-Key: <agent-api-key>
X-Enrollment-Key: <nur bei Erstanmeldung erforderlich>
Content-Type: application/json

Beispiel:

[
  {
    "host": "CLIENT-01",
    "channel": "Security",
    "id": 4740,
    "source": "windows-agent",
    "ts": "2026-07-23T12:00:00Z",
    "meta": {
      "provider": "Microsoft-Windows-Security-Auditing",
      "target_user": "alice",
      "workstation": "CLIENT-42",
      "src_ip": "10.10.20.15"
    }
  }
]

Alternativ darf msg weiterhin das bisherige Windows-XML enthalten. Der Processor extrahiert daraus die relevanten Felder. Das XML wird nicht als ClickHouse-Spalte gespeichert, sondern nur im komprimierten Raw-Archiv.

Erfolgreiche Aufnahme:

{"accepted":1}

HTTP 202 bedeutet: Der Batch wurde authentifiziert und dauerhaft in die Queue geschrieben. ClickHouse muss dafür nicht synchron antworten.

Speicherdesign

ClickHouse siem.events

Eine kanonische Tabelle enthält u. a.:

  • Event- und Ingest-Zeit
  • Agent / Host / Channel / Provider / Event-Code
  • normalisierte category, action, outcome, severity
  • User / Subject / Target User
  • Source-/Destination-IP und Ports
  • Workstation
  • Logon-Type
  • Prozess / Parent-Prozess / Commandline
  • seltene XML-Felder in attributes Map(String,String)
  • Raw-Object-Key + Index im archivierten Originalbatch
  • Queue-Partition / Offset / stabile Event-UID
  • Ingest-Verzögerung zur Erkennung von Agent-/Queue-Zeitproblemen
  • häufige Authentifizierungsdetails (Domain, Status/Substatus, FailureReason, Auth-Package)

Häufig genutzte Felder werden nicht zusätzlich in attributes dupliziert. Die Map enthält nur unbekannte Restfelder, maximal 32 Einträge pro Event mit begrenzter Länge. Das vollständige Original bleibt im Raw-Archiv.

Die Tabelle verwendet ReplacingMergeTree, Monats-Partitionen und einen zeitbasierten Sortierschlüssel. Die Standard-TTL beträgt 90 Tage.

Deduplication

Die Pipeline ist absichtlich at-least-once. Ein Crash nach ClickHouse-Insert aber vor dem Redpanda-Commit kann einen Batch erneut liefern.

Daher erhält schon der Ingress-Batch eine stabile ID aus SHA-256(agent_id + Batch-Inhalt). Die Event-ID lautet:

<batch_uid>:<event-index-im-batch>

Damit werden sowohl ein Redpanda-Retry als auch ein identischer HTTP-Retry des Collectors als dasselbe Event erkannt. Queue-Partition und Offset werden zusätzlich nur zur Provenienz gespeichert.

  • Eventtabelle: ReplacingMergeTree
  • Detection-Zähler: uniqExact(event_uid)
  • Dashboard-Rollups: uniqExactState(event_uid) / uniqExactMerge

Wiederholte Verarbeitung verfälscht damit keine Zähler.

Dashboard-Rollup

siem.events_5m wird über eine Materialized View fortlaufend gepflegt. Die Startseite zählt nicht Millionen Einzelereignisse, sondern arbeitet auf dem 5-Minuten-Rollup.

Standard-Retention:

Datenklasse Retention
Vollständig durchsuchbare normalisierte Events 90 Tage
5-Minuten-Rollups 730 Tage
Raw-Batches in Garage/S3 30 Tage
Redpanda Recovery-Queue 24 Stunden
Detections / Agent-Metadaten dauerhaft, bis organisatorisch bereinigt
Prometheus Betriebsmetriken 30 Tage

Anpassbar vor dem ersten Start in .env:

EVENT_RETENTION_DAYS=90
ROLLUP_RETENTION_DAYS=730
RAW_RETENTION=720h
KAFKA_RETENTION_MS=86400000

Detections in Version 1

Der Detector läuft unabhängig vom UI alle 30 Sekunden mit einem begrenzten Lookback und schreibt nur Findings in PostgreSQL.

Enthalten:

  • Audit Log gelöscht (1102)
  • Dienst installiert (7045)
  • Account Lockout (4740)
  • Failed Logon Burst (4625)
  • Password Spray (4625 gegen viele Benutzer)
  • privilegierte Gruppenmitgliedschaft geändert (4728 / 4732 / 4756)

Es gibt bewusst keine pauschale new_event_id-Detection mehr. Event 5857 und vergleichbare Betriebsereignisse sind normale Events, keine Incidents.

UI

/ui enthält:

  • Eventzahl 24h aus Rollup
  • aktive Hosts 24h
  • offene High-/Critical-Detections
  • Eventsuche nach Zeitraum, Host, User, IP, Event-ID und Channel
  • Detection-Liste mit Statusänderung
  • Agent-Liste / Last Seen

Die initiale Eventsuche ist auf 24 Stunden und 500 Ergebnisse begrenzt. Der ClickHouse-Sortierschlüssel beginnt mit Tenant und Event-Zeit; alle UI-Suchen besitzen zusätzlich ein hart begrenztes Zeitfenster und Ergebnislimit. Das verhindert wiederkehrende Vollscans über die gesamte Historie.

Raw-Event wiederherstellen

Die Eventliste enthält raw_object_key. Das Original ist ein gzip-komprimierter Ingress-Batch.

Einfacher über das mitgelieferte Hilfsskript:

./raw-event.sh '2026/07/23/12/<object>.json.gz' <raw_index>

Ohne raw_index wird der gesamte dekomprimierte Ingress-Batch ausgegeben. raw_index bezeichnet das Event innerhalb dieses JSON-Batches.

Stress-Test

Der Stress-Agent ist im selben Image enthalten und benötigt eine explizite Bestätigung:

docker compose run --rm ingress stress-agent \
  --url http://ingress:8080/ingest \
  --api-key stress-agent-key \
  --enrollment-key "$(grep '^ENROLLMENT_KEY=' .env | cut -d= -f2-)" \
  --scenario mixed \
  --rate 5000 \
  --batch 250 \
  --duration 5m \
  --confirm-load-test

Szenarien: mixed, failed-logon, lockout, catalog.

Nur in einer kontrollierten Umgebung beziehungsweise gegen das eigene SIEM verwenden.

Betriebschecks

Der schnellste Gesamtcheck:

./doctor.sh

Er prüft Containerstatus, HTTP-Readiness, Redpanda/Consumer-Lag, ClickHouse-Zeilen und Speicher, aktuelle Ingest-Verzögerung, Control-Plane-Zähler, Raw-Spool und aktuelle Fehler.

Zusätzlich:

docker compose ps
docker compose logs -f ingress processor detector api

Ingress:

curl http://127.0.0.1:8090/healthz
curl http://127.0.0.1:8090/readyz

ClickHouse Zeilenzahl / Speicher:

docker compose exec clickhouse clickhouse-client \
  --user siem --password "$(grep '^CLICKHOUSE_PASSWORD=' .env | cut -d= -f2-)" \
  --query "SELECT formatReadableQuantity(sum(rows)), formatReadableSize(sum(bytes_on_disk)) FROM system.parts WHERE database='siem' AND table='events' AND active"

Queue:

docker compose exec redpanda rpk group describe siem-processor-v1

Hardware für das Single-Node-Deployment

Für einen Test reichen typischerweise 4 CPU-Kerne, 8 GB RAM und SSD-Speicher. Für eure bisher beobachtete Produktivrate würde ich mindestens 8 Kerne, 16 GB RAM und schnelle lokale NVMe einplanen. Die benötigte SSD-Größe hängt stark von Eventgröße, Commandlines und Raw-XML ab; ./doctor.sh zeigt die reale Kompression und Wachstumsrate, sodass die Retention nach einigen Tagen anhand echter Werte dimensioniert werden kann.

Kapazität

Die bisher beobachteten ca. 19,2 Mio. Events in 48 Stunden entsprechen nur rund 111 Events/s. Das ist für ClickHouse/Redpanda eine kleine Last. Der neue Pfad ist bewusst für deutlich höhere Raten gebaut; die reale Grenze hängt vor allem von CPU, SSD-Durchsatz, Eventgröße, Anzahl der Hosts und Query-Last ab.

Für hohe Last zuerst skalieren:

  1. KAFKA_PARTITIONS erhöhen und mehrere Processor-Instanzen starten.
  2. ClickHouse auf schnelle lokale NVMe legen.
  3. Redpanda und ClickHouse nicht auf dieselbe langsame Netzwerkplatte legen.
  4. Raw-Retention reduzieren, wenn XML sehr groß ist.
  5. Erst ab echtem Bedarf ClickHouse / Redpanda / Garage als Mehrknoten-Cluster betreiben.

Single-node vs. HA

compose.yml ist ein One-click Single-node Deployment. Es eliminiert die bisherigen Architekturprobleme, ist aber kein hochverfügbarer Cluster: fällt der eine Docker-Host aus, sind alle Komponenten betroffen.

Für echte HA sind mindestens sinnvoll:

  • 3 Redpanda Broker, Replication Factor 3
  • repliziertes ClickHouse (oder ClickHouse Cloud)
  • 3 Garage Nodes / anderes redundantes S3
  • PostgreSQL mit Backup/Replica
  • Reverse Proxy/TLS und zentraler Identity Provider vor UI/API

Die Anwendungskomponenten selbst sind stateless und können danach horizontal vervielfacht werden.

Sicherheit

  • Datenbank-, Redpanda-, Garage- und Prometheus-Adminports binden standardmäßig nur an 127.0.0.1.
  • Nur Ingress und UI binden standardmäßig an 0.0.0.0.
  • UI/API verlangen ein beim Deployment zufällig erzeugtes Basic-Auth-Passwort.
  • Für produktiven externen Zugriff gehört trotzdem TLS/Reverse-Proxy vor Ingress und UI, da Basic Auth allein keine Transportverschlüsselung bietet.
  • .env enthält Secrets und darf nicht ins Repository.
  • Der Ingress speichert ausschließlich SHA-256-Hashes der Agent-API-Keys in PostgreSQL.
  • Raw-Logs können personenbezogene Daten enthalten. Retention und Zugriffsrechte entsprechend festlegen.

Projektstruktur

cmd/siem                 ein Binary, mehrere Rollen
internal/ingress         kompatible HTTP-Aufnahme
internal/queue           Redpanda/Kafka Producer + Consumer
internal/processor       Normalisierung + ClickHouse + Raw-Spool
internal/normalize       Windows XML / Meta → kanonisches Event
internal/clickhouse      ClickHouse HTTP-Client
internal/postgres        Agenten + Detections
internal/detector        kontinuierliche Regeln
internal/api             Investigation API + UI
internal/stress          begrenzter Lastgenerator
doctor.sh                 Pipeline-/Kapazitätsdiagnose
raw-event.sh              Raw-Archiv abrufen
deploy/clickhouse        analytisches Schema
deploy/postgres          Control-Plane-Schema
deploy/garage            S3-Archivkonfiguration
deploy/prometheus        Betriebsmetriken

Reset

Nur wenn wirklich alle Daten weg sollen:

./reset-DANGEROUS.sh

Das Skript verlangt zusätzlich die Eingabe DELETE und entfernt anschließend die Docker-Volumes.

Description
No description provided
Readme 2 MiB
Languages
Go 72.2%
Shell 12.9%
HTML 8.7%
PowerShell 5.7%
Dockerfile 0.5%