109 lines
5.6 KiB
Markdown
109 lines
5.6 KiB
Markdown
---
|
|
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 <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](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`.
|