Files
ThreadNetWiki/betrieb/moderation-content-scanning.md
T

5.6 KiB

title, description, published, date, tags, editor, dateCreated
title description published date tags editor dateCreated
Moderation & Content-Scanning Draupnir-Moderationsbot + ClamAV true 2026-08-13T18:04:36.999Z markdown 2026-08-13T18:04:36.999Z

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)

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.

Bot-Account & Zugriff (Bootstrap)

Authentifizierung läuft über MAS (kein klassisches registration_shared_secret):

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

Token wird manuell per sops apps/production/draupnir-secret.yaml eingetragen.

Stolpersteine (live gefunden)

  • gnuxie/draupnir:v2.9.0 crasht mit initialManager ("Can't join remote room..."). Die automatische Management-Room-Erstellung funktioniert erst ab v3.1.0 - deployt.
  • v3.x braucht ein explizites CLI-Argument statt NODE_CONFIG_DIR-Autodiscovery: args: ["bot", "--draupnir-config", "/data/config/default.yaml"].
  • NetworkPolicy: matrix-stack-synapse routet über haproxy, dessen Ingress-Policy standardmäßig nur Traefik erlaubt - eigene podSelector-Ausnahme für Draupnir nötig.

Verschlüsselter Management-Room

Standardmäßig unverschlüsselt. Mit experimentalRustCrypto: true (Hersteller-Warnung: "not considered production safe", in unserem Test aber fehlerfrei) + manuellem Aktivieren der Raumverschlüsselung in Element (wirkt nicht rückwirkend auf bereits erstellte Räume).

Befehle (Kurzreferenz)

Befehl Zweck
status Bot-Status
rooms add <room> Raum unter Schutz stellen (Voraussetzung für Bans!)
list create <shortcode> <alias> Neue Policy-Liste (automatisch beobachtet + geschützt)
ban <user> <liste> <grund> 2. Argument = Policy-Liste, NICHT der Ziel-Raum
kick <user> <room> <grund> Direkter Kick ohne Listen-Umweg
rules Regeln einer Policy-Liste anzeigen

Live getestet (2026-07-29): Testraum geschützt, Policy-Liste angelegt, Testnutzer über ban+Liste erfolgreich entfernt. Kernmechanismus bestätigt funktionsfähig.

2. Content Scanner (Issue #19)

Verworfen: matrix-content-scanner-python ist ein Proxy, den der Client explizit statt der normalen Media-Endpunkte aufrufen muss - weder aktuelles Element Web noch Element X unterstützen das (geprüft: kein Hook im offenen element-x-android-Repo). Element hat echtes serverseitiges Scanning nur in der kommerziellen Element Pro + ESS Pro-Kombination.

Umgesetzt: eigenes, kleines Synapse-Modul (clamav_spam_checker.py) nutzt Synapses echten check_media_file_for_spam-Hook - serverseitig, transparent für jeden Client, kein fertiges Modul dafür existiert (synapse-http-antispam schließt genau diesen Callback aus). ClamAV als eigener Pod, Modul per ConfigMap + PYTHONPATH gemounted (kein Custom-Image). Spricht ClamAVs INSTREAM-Protokoll über Twisted-Primitives, nicht asyncio - Synapse läuft auf Twisteds Reactor, ein erster asyncio-Versuch schlug live mit RuntimeError: no running event loop fehl (Fail-Open verschleierte das zunächst - EICAR kam durch). Fail-open bei Scanner-Fehlern bleibt so: ClamAV-Ausfall blockiert nicht alle Uploads.

Live getestet (2026-07-29): normale Datei unverschlüsselt → durchgelassen; EICAR unverschlüsselt → zuverlässig blockiert.

3. Client-seitiges Scanning für verschlüsselte Räume (Erweiterung, 2026-07-29)

Synapse hat bei E2EE nie den Schlüssel - nur der Client kann Klartext scannen. Neuer HTTP-Dienst clamav-http-scanner.py macht ClamAV für Browser-JS erreichbar (https://axion1337.chat/_scan, Auth via Synapses eigenem /whoami, Fail-open). Zwei Patch-Stellen im ThreadNet-Web-Fork:

  • Empfang: apps/web/src/utils/DecryptFile.ts (decryptFile()) - einziger Punkt für alle Anhangstypen.
  • Versand: apps/web/src/ContentMessages.ts (uploadFile()) - eine Funktion für alle Uploads (Datei, Thumbnails, Sprachnachrichten).

Live getestet: EICAR in verschlüsseltem Gruppenraum + 1:1-DMs vor Upload blockiert. Zusätzlich Empfangsseite unabhängig vom Absender bestätigt: EICAR über einen echten, ungepatchten Client (app.element.io) in denselben Raum geschickt - beim Download im gepatchten Client greift der Scanner trotzdem.

Wichtig, korrigiert: gilt bestätigt für den Web-Client. Für Electron/Desktop unklar bzw. nicht automatisch - apps/desktop hat zwar einen webapp-artifact-Mechanismus, der den eigenen Fork-Build übernehmen würde, aber keinen laufenden CI-Runner, und der Build-Job checkt noch das Upstream-Repo aus. Die existierende Desktop-Build (mit der Discord-Style-Raumliste) kam aus einem manuellen, nicht wiederholten Build. Neues Issue: #44. Element X (Mobile) komplett unberührt, eigene Codebasis.

Folgeidee (LOW): Issue #43

  • Grafana-Dashboard über die bestehenden Loki-Logs für Erkennungen/Scanner-Ausfälle.

Details: Element-Customization und docs/deployment-guides/06-moderation-content-scanning.md.