docs: add moderation/content-scanning deployment guide (Issue #18)

This commit is contained in:
Thore Cimbal
2026-07-29 12:00:00 +00:00
parent bb51047e1d
commit e659242990
2 changed files with 113 additions and 0 deletions
@@ -0,0 +1,109 @@
# Moderation Bot & Content Scanning
**Status**: ✅ Draupnir deployed (2026-07-29, Closes Issue #18) | ⏳ Content Scanner geplant, noch nicht umgesetzt (Issue #19)
**Konfiguration**: `apps/production/draupnir*.yaml`
## 1. Draupnir (Moderationsbot)
Community-Nachfolger von Mjolnir. Läuft als eigener Bot-Account (`@draupnir:axion1337.chat`),
verwaltet Ban-Listen ("Policy Rooms") und setzt sie in geschützten Räumen durch.
### Warum Draupnir statt Mjolnir?
Mjolnir gilt als Vorgänger-Projekt und wird von der Community nicht mehr aktiv weiterentwickelt;
Draupnir ist der aktive Fork mit denselben Kernfunktionen plus Erweiterungen (u.a. native
Rust-Crypto-Unterstützung, siehe unten).
### Bot-Account & Zugriff (Bootstrap)
Da Authentifizierung über MAS läuft (kein klassisches `registration_shared_secret`), wird der
Bot-Account über MAS' eigenes CLI-Tool angelegt:
```bash
kubectl exec -it -n matrix deploy/matrix-stack-matrix-authentication-service -- \
mas-cli manage register-user draupnir --yes
kubectl exec -it -n matrix deploy/matrix-stack-matrix-authentication-service -- \
mas-cli manage issue-compatibility-token draupnir
```
Der ausgegebene Token wird per `sops apps/production/draupnir-secret.yaml` manuell eingetragen
(kein automatisierter Schritt - der Token darf nirgends unverschlüsselt landen).
### Wichtige Stolpersteine (live gefunden, nicht aus der Doku ableitbar)
- **Version**: `gnuxie/draupnir:v2.9.0` crasht beim ersten Start mit `initialManager`
("Can't join remote room because no servers..."). Das automatische Anlegen des
Management-Rooms über `initialManager` funktioniert erst **ab v3.1.0**. Aktuell deployt:
`v3.1.0`.
- **CLI-Argument statt Env-Var**: v3.x hat die automatische Config-Erkennung über
`NODE_CONFIG_DIR` entfernt - der Container braucht jetzt explizit
`args: ["bot", "--draupnir-config", "/data/config/default.yaml"]`, sonst
`TypeError: No configuration path has been found for Draupnir.` (per Extraktion von
`dist/config.js` aus dem Image bestätigt, nicht dokumentiert gefunden).
- **NetworkPolicy**: Der Bot muss Synapse direkt anrufen können. Da `matrix-stack-synapse`
intern über haproxy geroutet wird und `allow-ingress-haproxy` standardmäßig nur Traefik
(`kube-system`) erlaubt, braucht Draupnir eine eigene `podSelector`-Ausnahme in
`networkpolicy.yaml` - sonst schlägt jede Anfrage an den Homeserver silent fehl.
### Verschlüsselter Management-Room
Standardmäßig unverschlüsselt (Draupnirs zugrundeliegende Bot-Library aktiviert Crypto nicht
automatisch). Für einen verschlüsselten Management-Room:
1. `experimentalRustCrypto: true` in der Config ergänzen (via `sops`) - vom Hersteller selbst
als "not considered production safe" gekennzeichnet, in unserem Test aber ohne Fehler
gelaufen (Pod stabil, kein Crash, `End-to-end encryption enabled` in den Logs).
2. Verschlüsselung ist eine Raum-Eigenschaft, die beim Erstellen gesetzt wird - das Flag allein
verschlüsselt einen bereits bestehenden Management-Room **nicht** rückwirkend. Dafür in
Element: Raumeinstellungen → Sicherheit & Datenschutz → Verschlüsselung aktivieren.
### Profilbild setzen
Erfordert eine `mxc://`-URL (Bild muss zuerst hochgeladen werden, z.B. per Chat an den Bot
senden, dann in Element per "View Source" die `mxc://`-URL kopieren):
```
!draupnir avatar mxc://<server>/<media-id>
```
### Befehle (Kurzreferenz)
Alle Befehle im (verschlüsselten) Management-Room, Präfix `!draupnir`:
| Befehl | Zweck |
|--------|-------|
| `status` | Bot-Status, beobachtete Listen, geschützte Räume |
| `rooms add <room>` | Raum unter Draupnirs Schutz stellen (Voraussetzung für Bans!) |
| `list create <shortcode> <alias>` | Neue Policy-Liste anlegen (wird automatisch beobachtet + geschützt) |
| `watch <shortcode>` | Zusätzliche Policy-Liste beobachten |
| `ban <user> <liste> <grund>` | **Wichtig**: 2. Argument ist die Policy-Liste, NICHT der Ziel-Raum! Der Ban gilt automatisch in allen Räumen, die diese Liste beobachten und geschützt sind |
| `kick <user> <room> <grund>` | Direkter, sofortiger Kick aus einem konkreten Raum (ohne Listen-Umweg) |
| `rules` | Zeigt die Regeln einer Policy-Liste an |
| `unban <user> <liste>` | Regel wieder entfernen |
**Live getestet** (2026-07-29): Testraum geschützt, Policy-Liste angelegt, Testnutzer über
`ban`+Liste erfolgreich aus dem geschützten Raum entfernt. Kernmechanismus bestätigt
funktionsfähig.
## 2. Content Scanner (Issue #19, LOW - noch nicht umgesetzt)
**Wichtige Einschränkung, vorab geklärt**: `matrix-content-scanner-python` ist ein Proxy, den
der **Client** explizit statt der normalen Media-Endpunkte aufrufen muss - Synapse selbst
leitet nichts automatisch dorthin um. Diese client-seitige Unterstützung existiert nur noch in
den veralteten, nicht mehr gepflegten Android/iOS-SDKs. Weder aktuelles Element Web
(matrix-js-sdk, was hier läuft) noch Element X unterstützen das. Zusätzlich hat Synapse bei
verschlüsselten Anhängen ohnehin nie den Schlüssel - nur ein kooperierender Client kann ihn dem
Scanner geben.
**Konsequenz**: Falls umgesetzt, schützt der Scanner **keinen echten Nutzer-Traffic
automatisch** - nur manuelle/skriptgesteuerte Admin-Prüfungen sind möglich (z.B.
`kubectl port-forward` + `curl` nach manuellem Upload). Eine echte, transparente Absicherung
bräuchte entweder einen Client-Fork oder ein eigenes Synapse-`check_media_file_for_spam`-Modul
(deutlich größerer, separater Aufwand).
Geplante Komponenten bei Umsetzung: ClamAV (`clamav/clamav:1.5.3`, eigene PVC für die
Signatur-DB) + `vectorim/matrix-content-scanner` (Config-Keys `scan.script`/
`scan.temp_directory`/`crypto.request_secret_path` - aus dem echten `config.sample.yaml`
bestätigt). Kein Ingress, keine NetworkPolicy-Ausnahme nötig, da nichts im Cluster oder von
außen automatisch darauf zugreift.