docs: document real ClamAV/Synapse module setup and test results (Issue #19)

This commit is contained in:
Thore Cimbal
2026-07-29 12:00:00 +00:00
parent 7ac584d27b
commit 4ca87a68c7
3 changed files with 47 additions and 23 deletions
@@ -1,7 +1,7 @@
# 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`
**Status**: ✅ Draupnir deployed (2026-07-29, Closes Issue #18) | Content Scanner deployed + live getestet (2026-07-29, Closes Issue #19)
**Konfiguration**: `apps/production/draupnir*.yaml`, `apps/production/clamav*.yaml`, `apps/production/clamav_spam_checker.py`
## 1. Draupnir (Moderationsbot)
@@ -86,24 +86,47 @@ Alle Befehle im (verschlüsselten) Management-Room, Präfix `!draupnir`:
`ban`+Liste erfolgreich aus dem geschützten Raum entfernt. Kernmechanismus bestätigt
funktionsfähig.
## 2. Content Scanner (Issue #19, LOW - noch nicht umgesetzt)
## 2. Content Scanner (Issue #19)
**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.
**Verworfener erster Ansatz**: `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
veralteten, nicht mehr gepflegten Android/iOS-SDKs; weder aktuelles Element Web noch Element X
unterstützen das (geprüft: kein `content_scanner`-Hook im offenen `element-x-android`-Repo).
Element selbst hat echtes serverseitiges Scanning - aber nur in der kommerziellen
**Element Pro** + **ESS Pro**-Kombination, nicht in unserer offenen ESS-Community-Installation.
**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).
**Tatsächlich umgesetzt**: ein eigenes, kleines Synapse-Modul (`clamav_spam_checker.py`),
das Synapses echten, dokumentierten Hook `check_media_file_for_spam` nutzt - läuft
**serverseitig**, transparent für jeden Client, ganz ohne Mitwirkung des Clients. Kein
fertiges Modul dafür existiert (auch das verbreitete `synapse-http-antispam`-Brückenmodul
schließt genau diesen Callback explizit aus), daher selbst geschrieben.
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.
**Architektur**:
- ClamAV (`clamav/clamav:1.5.3`) läuft als eigener Pod, PVC für die Signatur-Datenbank.
- Das Modul (`apps/production/clamav_spam_checker.py`) wird per ConfigMap gemounted und über
`PYTHONPATH` importierbar gemacht (`synapse.extraVolumes`/`extraVolumeMounts`/`extraEnv` -
kein Custom-Synapse-Image nötig).
- Spricht ClamAVs natives INSTREAM-Protokoll direkt über **Twisted**-Netzwerk-Primitives
(`HostnameEndpoint`/`connectProtocol`), nicht über `asyncio` - Synapse läuft auf Twisteds
Reactor, nicht auf einer laufenden asyncio-Event-Loop. Ein erster Versuch mit
`asyncio.open_connection`/`wait_for` schlug live mit `RuntimeError: no running event loop`
fehl und fiel dadurch (durch das eigene Fail-Open-Verhalten) unbemerkt auf "durchlassen"
zurück - die EICAR-Testdatei wurde beim ersten Versuch nicht erkannt. Nach Umstellung auf
Twisted-Primitives funktioniert es sauber.
- **Fail-open** bei Scanner-Fehlern (Verbindungsfehler/Timeout → Datei wird durchgelassen,
laut geloggt) - ein ClamAV-Ausfall soll nicht alle Uploads auf dem Homeserver blockieren.
**Live getestet und bestätigt** (2026-07-29):
- Normale Datei in unverschlüsseltem Raum → läuft durch (kein Regressionsschaden).
- EICAR-Testdatei in unverschlüsseltem Raum → zuverlässig blockiert
(`ClamAV rejected an upload: Eicar-Test-Signature`, Client bekommt `400 Bad content` -
Synapse gibt bewusst keine Begründung an den Client zurück, nur in den Server-Logs sichtbar).
- EICAR-Testdatei in verschlüsseltem Raum/DM → **läuft durch** - erwartete, strukturelle
Grenze: Synapse hat bei E2EE nie den Entschlüsselungsschlüssel, sieht nur Ciphertext. Nur
ein kooperierender Client könnte das lösen (siehe oben, existiert nicht offen verfügbar).
**Bekannte Deckungslücke**: schützt nur unverschlüsselte Räume/DMs (viele öffentliche/
föderierte Räume) - keine Warnung/Kennzeichnung für Nutzer in verschlüsselten Räumen, dass
dort kein Scanning stattfindet. Folgeidee (Issue #43, LOW): Grafana-Dashboard über die
bestehenden Loki-Logs, um Erkennungen/Scanner-Ausfälle sichtbar zu machen.