chore(coturn): automated TURN shared-secret rotation #46

Closed
sorb wants to merge 76 commits from turn-secret-rotation-20260801-020001 into main
Showing only changes of commit 3e147554eb - Show all commits
@@ -126,7 +126,66 @@ schließt genau diesen Callback explizit aus), daher selbst geschrieben.
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.
**Bekannte Deckungslücke (Stand vor der Client-Erweiterung unten)**: schützt nur
unverschlüsselte Räume/DMs - 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.
## 3. Client-seitiges Scanning für verschlüsselte Räume (Issue #19-Erweiterung, 2026-07-29)
Da Synapse bei E2EE-Räumen strukturell nie den Schlüssel hat, kann nur der **Client**
Klartext scannen - einmal beim Senden (vor der Verschlüsselung), einmal beim Empfangen
(nach der Entschlüsselung). Umgesetzt in `ThreadNet-Web` (Fork von Element Web).
### Architektur
Ein neuer, eigener HTTP-Dienst (`apps/production/clamav-http-scanner.py`, eigenes Image via
`clamav-http-scanner-Dockerfile`) macht denselben ClamAV-Pod für Browser-JS erreichbar
(clamd spricht nur rohes TCP, das kann ein Browser nicht). Erreichbar unter
`https://axion1337.chat/_scan`. Auth über Synapses eigenen
`/_matrix/client/v3/account/whoami`-Endpunkt (kein eigenes Auth-System nötig) - verhindert,
dass der Dienst zu einem offenen "teste dein Malware gegen unseren Virenscanner"-Orakel für
das ganze Internet wird. Fail-open bei Scanner-Fehlern, wie beim Synapse-Modul.
**Zwei Patch-Stellen im `ThreadNet-Web`-Fork** (im Repo
`rohana.axion1337.de/sorb/ThreadNet-Web.git`, nicht in diesem gitops-Repo):
- **Empfang**: `apps/web/src/utils/DecryptFile.ts`, Funktion `decryptFile()` - der einzige
Punkt im ganzen Client, an dem entschlüsselte Klartext-Bytes für *jeden* Anhangstyp
entstehen (Bild/Audio/Video/Datei laufen alle über `MediaEventHelper` hier durch). Scan
direkt nach dem Entschlüsseln, vor der Rückgabe als `Blob`.
- **Versand**: `apps/web/src/ContentMessages.ts`, Funktion `uploadFile()` - die eine
gemeinsame Funktion für alle Anhangs-Uploads (Hauptdatei, generierte Thumbnails,
Sprachnachrichten), unabhängig davon ob der Zielraum verschlüsselt ist. Scan direkt nach
dem Einlesen der Datei, vor Verschlüsselung/Upload.
- Gemeinsame Hilfsdatei: `apps/web/src/utils/ContentScanner.ts` (neue `scanContent()`-
Funktion + `ContentScanRejectedError`), von beiden Stellen genutzt. Fehlertexte über die
bereits bestehenden Error-Rendering-Pfade in `MImageBody.tsx`/`MAudioBody.tsx`/
`VideoBodyViewModel.ts`/`FileBodyViewModel.ts` (gleiches Muster wie die schon vorhandenen
`DecryptError`/`DownloadError`).
**Live getestet** (2026-07-29): EICAR in verschlüsseltem Gruppenraum ("testgruppe") und in
1:1-DMs zwischen zwei echten Accounts - in beiden Fällen zuverlässig **vor dem Upload**
blockiert. Vorher (nur Synapse-Modul) lief das durch.
### ⚠️ Wichtig für zukünftige Desktop-/Electron-Forks
**Dieser Fix gilt nur für den Web-Client (Browser), nicht automatisch für Element Desktop.**
Element Desktop (`apps/desktop` im selben Monorepo) ist ein Electron-Wrapper, der die
Web-App nicht selbst baut, sondern per `scripts/fetch-package.ts` ein fertiges,
**offiziell von `element-hq/element-web` signiertes Release-Tarball** herunterlädt und in
ein `webapp.asar` packt (`pnpm run fetch``pnpm run asar-webapp`). Ohne weitere Änderung
würde ein Desktop-Build also die **unveränderte Upstream-Version** bündeln - unsere Patches
wären nicht drin, obwohl sie im selben Monorepo liegen.
**Für einen zukünftigen Desktop-Fork mit dieser Funktion**: `fetch-package.ts` akzeptiert
bereits eine beliebige URL statt einer Versionsnummer als Override
(`targetVersion.includes("://")`-Zweig, keine Code-Änderung nötig) - man müsste nur unseren
eigenen `apps/web`-Build als `.tar.gz` irgendwo selbst hosten (z.B. als Gitea-Release-Asset)
und `pnpm run fetch https://.../unser-build.tar.gz` statt der Standard-Versionsnummer
aufrufen.
**Element X (Mobile, iOS/Android)** ist davon komplett unberührt - eigene Codebasis auf
Basis von `matrix-rust-sdk`, kein gemeinsamer Code mit `ThreadNet-Web`. Ein Schutz dort
wäre ein separates, eigenständiges Projekt.