--- title: Moderation & Content-Scanning description: Draupnir-Moderationsbot + ClamAV published: true date: 2026-08-13T18:04:36.999Z tags: editor: markdown dateCreated: 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`): ```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 ``` 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 ` | Raum unter Schutz stellen (Voraussetzung für Bans!) | | `list create ` | Neue Policy-Liste (automatisch beobachtet + geschützt) | | `ban ` | 2. Argument = Policy-Liste, NICHT der Ziel-Raum | | `kick ` | 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](https://git.lab/axion1337.chat/axion1337.chat-gitops/-/issues/44). Element X (Mobile) komplett unberührt, eigene Codebasis. Folgeidee (LOW): [Issue #43](https://git.lab/axion1337.chat/axion1337.chat-gitops/-/issues/43) - Grafana-Dashboard über die bestehenden Loki-Logs für Erkennungen/Scanner-Ausfälle. Details: [Element-Customization](/betrieb/element-customization) und `docs/deployment-guides/06-moderation-content-scanning.md`.