From fe6427bfbac6c0ef1b1c41dd531981449ce963da Mon Sep 17 00:00:00 2001 From: Bernd Steckmeister Date: Sat, 4 Jul 2026 03:32:37 +0200 Subject: [PATCH] security: geleakte Secrets aus Repo entfernen (Review-Befund k) Secret-Scan im Fable-5-Review fand vier aktive Zugangsdaten im oeffentlich erreichbaren Gitea-Repo. Gefahrlos entfernbare Stellen: - scripts/release.ps1: Gitea-Token -> Umgebungsvariable PYRAMID_GITEA_TOKEN - docs/matrix-sdk/13-server-admin: 19x Admin-Token -> Client-eingebettete Secrets (LiveKit apiSecret, Admin-Token im E2EE-Diagnose-Upload, Giphy-Key) brauchen Rotation + Server-Umbau und stehen als neue M0-ROADMAP-Punkte (nicht blind auf dem Pi fixbar). Co-Authored-By: Claude Opus 4.8 --- CHANGES.md | 4 ++ PROGRESS.md | 66 +++++++++++++++++++ ROADMAP.md | 11 ++++ .../13-server-admin-continuwuity.md | 38 +++++------ scripts/release.ps1 | 12 +++- 5 files changed, 111 insertions(+), 20 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 3d9650c..ea30928 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1220,3 +1220,7 @@ 2026-07-03 23:21 [Edit] /home/steggi/pyramid/PROGRESS.md 2026-07-03 23:21 [Edit] /home/steggi/pyramid/docs/PC_TESTPLAN.md 2026-07-03 23:22 [Edit] /home/steggi/pyramid/PROGRESS.md +2026-07-04 03:28 [Edit] /home/steggi/pyramid/scripts/release.ps1 +2026-07-04 03:31 [Edit] /home/steggi/pyramid/PROGRESS.md +2026-07-04 03:32 [Edit] /home/steggi/pyramid/PROGRESS.md +2026-07-04 03:32 [Edit] /home/steggi/pyramid/ROADMAP.md diff --git a/PROGRESS.md b/PROGRESS.md index 933eef1..8336138 100644 --- a/PROGRESS.md +++ b/PROGRESS.md @@ -13,6 +13,64 @@ Schritt (und beim Abbruch mitten im Schritt den Zwischenstand). Format: --- +## 2026-07-04 – Fable-5-Review (k): Secret-Scan – ECHTER Sicherheitsbefund, teils gefixt + +**Erledigt:** Beim Review-Pass zusätzlich einen systematischen Secret-Scan über +`lib/`, `scripts/`, `docs/`, `android/` gemacht (CLAUDE.md-Leitplanke „Keine +Secrets ins Repo"). **Ergebnis: mehrere echte, aktive Zugangsdaten liegen im +Repo – und das Gitea-Repo ist als `private:false` unter der Internet-Domain +`git.steggi-matrix.work` erreichbar** (per `curl` bestätigt: Repo-Metadaten und +Releases anonym abrufbar). Damit sind alle folgenden Secrets als **kompromittiert +zu behandeln** (seit Commit 25ed765 in der History): + +- **Matrix-Server-ADMIN-Token** `J5lax…` – lag doppelt drin: + (1) als Bearer-Token in `docs/matrix-sdk/13-server-admin-continuwuity.md` + (19 Stellen) und (2) **im ausgelieferten App-Code** + `lib/widgets/settings/settings_encryption.dart:431` (E2EE-Diagnose-Upload). + Per `whoami` gegen den Homeserver geprüft: **Token ist noch gültig** + (`@todesneutron:steggi-matrix.work`, voller Server-Admin). Kritischster Fund – + jeder App-Nutzer (auch Uta) trägt den Server-Admin-Token im Client. +- **Gitea-Release-Token** `bb522f96…` (Repo-Schreibrechte) hartcodiert in + `scripts/release.ps1:14`. +- **LiveKit-`apiSecret`** `rYUT2PRa…` in `lib/core/livekit_token.dart:6` – die + App mintet LiveKit-JWTs **client-seitig**, das SFU-Secret muss also im Client + liegen (Architekturproblem, nicht nur ein Repo-Leak). +- **Giphy-API-Key** `QtEy…` in `lib/features/chat/gif_sticker_picker.dart:9` + (geringe Sensibilität, aber ebenfalls im Client). + +**Sofort (gefahrlos, kein Laufzeit-Bezug) bereinigt:** +- `scripts/release.ps1`: Token raus, kommt jetzt aus Umgebungsvariable + `PYRAMID_GITEA_TOKEN`; fehlt sie, bricht das Skript mit klarer Anleitung ab. +- `docs/matrix-sdk/13-server-admin-continuwuity.md`: alle 19 Token-Stellen durch + ``-Platzhalter ersetzt. + +**Offen/Nächster Schritt (BRAUCHT BERND / PC – nicht blind auf dem Pi fixen):** +1. **Alle vier Secrets rotieren** (sie sind bereits geleakt, egal was im Code + steht): neuen Matrix-Admin-Token erzeugen und alten widerrufen; neuen + Gitea-Token; LiveKit-`apiKey`/`apiSecret` neu setzen (Hetzner-LiveKit + + `ice.json`); Giphy-Key neu ziehen. **Das ist der wichtigste Schritt** – die + Repo-Bereinigung allein nützt nichts, solange die alten Werte gültig sind. +2. **Client-seitige Secrets entschärfen (Architektur):** LiveKit-Token gehört + server-seitig gemintet (kleiner Token-Endpoint auf dem Pi, App holt sich nur + das fertige JWT) – dann verschwindet `apiSecret` aus dem Client. Der + E2EE-Diagnose-Upload darf **niemals** den Server-Admin-Token benutzen: eigenen, + eng begrenzten Token (nur der Dashboard-Endpoint) oder besser einen + unauthentifizierten Upload mit Rate-Limit. Beides als ROADMAP-M0-Punkt neu + aufgenommen. Blind entfernen verbietet sich, weil Calls/Diagnose sonst brechen + (Calls sind „heilig") und auf dem Pi kein GUI-Test möglich ist. +3. **Git-History:** Die alten Werte bleiben in der Gitea-History sichtbar. Nach + der Rotation sind sie wertlos; ein History-Rewrite (force-push) ist optional + und Bernds Entscheidung (das Repo dient auch als Backup) – daher NICHT + eigenmächtig gemacht. + +**Stolperfallen:** Der Diagnose-„Token" in `settings_encryption.dart` ist +NICHT ein harmloser Dashboard-Key, sondern derselbe String wie der +Matrix-Server-Admin-Token – beim Lesen leicht zu übersehen, weil er dort nur +`token:` heißt. Lehre: bei Secret-Funden immer prüfen, ob derselbe Wert an +mehreren Stellen (Doku UND Client) wiederkehrt und WAS er wirklich autorisiert. + +--- + ## 2026-07-03 – Fable-5-Review (h)–(j): Test-/Doku-Commits geprüft, 1 echter Befund gefixt **Erledigt:** @@ -322,6 +380,14 @@ schließt den Review ab. - [x] **(j) PC-Testplan-Update (03b84e0) geprüft – stimmt.** Teststand „22 Tests" war korrekt (durch (i) jetzt 24 – im Testplan nachgezogen, siehe Eintrag oben). +- [x] **(k) Secret-Scan über das ganze Repo – ECHTER Sicherheitsbefund + (2026-07-04).** Vier aktive Zugangsdaten im öffentlich erreichbaren Repo + (Matrix-Admin-Token doppelt – auch im Client!, Gitea-Release-Token, + LiveKit-`apiSecret`, Giphy-Key). Gefahrlose Stellen sofort bereinigt + (release.ps1 → Env-Var, Admin-Doku → Platzhalter); Rotation aller Secrets + + server-seitiges LiveKit-Token-Minting + nicht-Admin-Diagnose-Token als neue + M0-Punkte aufgenommen (brauchen Bernd/Server-Zugriff). Details im + PROGRESS-Eintrag 2026-07-04 oben. **Fazit:** Die Sonnet-5-Arbeit war handwerklich sauber; die zwei echten Lücken (verlorene Kommentare, fehlender Soft-Logout-Guard) hatte die abgebrochene diff --git a/ROADMAP.md b/ROADMAP.md index 3ea9b25..0475a54 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -60,6 +60,17 @@ Punkte abhaken (`[x]`), wenn erledigt UND in `PROGRESS.md` protokolliert. - [x] Geräte-/Sessionverwaltung in den Einstellungen: bereits vorhanden (`_SessionsSection` in `settings_modal.dart` – Liste, Umbenennen, Verifizieren per SAS/QR, Abmelden, Massen-Abmeldung mit Passwort-Reauth) +- [ ] **SICHERHEIT: geleakte Secrets rotieren + aus dem Client holen** (Fable-5-Review (k), + PROGRESS.md 2026-07-04). Das Gitea-Repo ist öffentlich (`private:false`), und es lagen + vier aktive Secrets drin – teils schon bereinigt (release.ps1, Admin-Doku), aber: + - **Rotieren (Pflicht, sie sind bereits geleakt):** Matrix-Server-Admin-Token + `J5lax…` (noch gültig, voller Admin!), Gitea-Release-Token, LiveKit-`apiKey`/ + `apiSecret`, Giphy-Key. Alte jeweils widerrufen. + - **LiveKit-Token server-seitig minten:** kleiner Token-Endpoint auf dem Pi, damit + `apiSecret` aus `lib/core/livekit_token.dart` verschwindet (App holt nur das JWT). + - **E2EE-Diagnose-Upload:** Server-Admin-Token in `settings_encryption.dart:431` durch + einen eng begrenzten Nicht-Admin-Token ersetzen (oder unauth. Upload mit Rate-Limit). + - Optional: Git-History-Rewrite (Bernds Entscheidung, Repo ist auch Backup). - [ ] Härtetest dokumentieren: Login → Nachrichten → Logout → Login neu → alles noch lesbar (gehört zum ausstehenden PC-Praxistest der beiden Bugfixes oben; kompletter Ablauf inkl. aller aufgelaufenen UNGETESTET-Punkte steht diff --git a/docs/matrix-sdk/13-server-admin-continuwuity.md b/docs/matrix-sdk/13-server-admin-continuwuity.md index 88dd78a..384e9c6 100644 --- a/docs/matrix-sdk/13-server-admin-continuwuity.md +++ b/docs/matrix-sdk/13-server-admin-continuwuity.md @@ -70,7 +70,7 @@ Continuwuity hat einen speziellen Admin-Raum. Als Server-Admin kannst du dort Be # Als curl (Admin-Token erforderlich): # Admin-Room-ID herausfinden: curl -s "https://steggi-matrix.work/_matrix/client/v3/directory/room/%23admins%3Asteggi-matrix.work" \ - -H "Authorization: Bearer J5laxTz5Zte9oRB5x8QsyYRQ6b0hAuXI" + -H "Authorization: Bearer " # → {"room_id": "!adminRoomId:steggi-matrix.work", ...} ``` @@ -84,22 +84,22 @@ curl -s "https://steggi-matrix.work/_matrix/client/v3/directory/room/%23admins%3 # User zu Server-Admin machen curl -X POST "https://steggi-matrix.work/_conduwuit/admin/v1/make_user_admin" \ - -H "Authorization: Bearer J5laxTz5Zte9oRB5x8QsyYRQ6b0hAuXI" \ + -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{"user_id": "@todesneutron:steggi-matrix.work"}' # Server-Status abfragen curl "https://steggi-matrix.work/_conduwuit/server/version" \ - -H "Authorization: Bearer J5laxTz5Zte9oRB5x8QsyYRQ6b0hAuXI" + -H "Authorization: Bearer " # Alternativ — Standard Matrix-Whoami (prüft ob Token gültig): curl "https://steggi-matrix.work/_matrix/client/v3/account/whoami" \ - -H "Authorization: Bearer J5laxTz5Zte9oRB5x8QsyYRQ6b0hAuXI" + -H "Authorization: Bearer " # → {"user_id": "@todesneutron:steggi-matrix.work", "is_guest": false} # Prüfen ob User Server-Admin ist (Conduwuit-spezifisch): curl "https://steggi-matrix.work/_conduwuit/admin/v1/users/@todesneutron:steggi-matrix.work" \ - -H "Authorization: Bearer J5laxTz5Zte9oRB5x8QsyYRQ6b0hAuXI" + -H "Authorization: Bearer " ``` --- @@ -112,7 +112,7 @@ Als Server-Admin kannst du via Matrix Client API Power Levels in jedem Raum setz ```bash # Schritt 1: Aktuelles Power-Level-Event lesen ROOM_ID="!raumId:steggi-matrix.work" -TOKEN="J5laxTz5Zte9oRB5x8QsyYRQ6b0hAuXI" +TOKEN="" SERVER="https://steggi-matrix.work" curl "${SERVER}/_matrix/client/v3/rooms/${ROOM_ID}/state/m.room.power_levels/" \ @@ -172,7 +172,7 @@ await client.setRoomStateWithKey( ```bash # Testen ob restricted join rules funktionieren: ROOM_ID="!testRaum:steggi-matrix.work" -TOKEN="J5laxTz5Zte9oRB5x8QsyYRQ6b0hAuXI" +TOKEN="" SPACE_ID="!spaceId:steggi-matrix.work" curl -X PUT "https://steggi-matrix.work/_matrix/client/v3/rooms/${ROOM_ID}/state/m.room.join_rules/" \ @@ -230,7 +230,7 @@ try { ```bash # 1. Prüfen: Welchen Power Level hat mein User in diesem Raum? ROOM_ID="!raumId:steggi-matrix.work" -TOKEN="J5laxTz5Zte9oRB5x8QsyYRQ6b0hAuXI" +TOKEN="" curl "https://steggi-matrix.work/_matrix/client/v3/rooms/${ROOM_ID}/state/m.room.power_levels/" \ -H "Authorization: Bearer ${TOKEN}" @@ -269,7 +269,7 @@ curl "https://steggi-matrix.work/_matrix/client/v3/capabilities" \ ```bash # Prüfen ob User Server-Admin ist: curl "https://steggi-matrix.work/_matrix/client/v3/account/whoami" \ - -H "Authorization: Bearer J5laxTz5Zte9oRB5x8QsyYRQ6b0hAuXI" + -H "Authorization: Bearer " # Auf Pi (SSH): User-Datenbank direkt abfragen (Conduwuit/Continuwuity nutzt RocksDB) # Alternativ: Über Admin-Room-Befehl @@ -278,7 +278,7 @@ curl "https://steggi-matrix.work/_matrix/client/v3/account/whoami" \ # Über HTTP (Conduwuit Admin-Endpoint): curl -X POST "https://steggi-matrix.work/_conduwuit/admin/v1/make_user_admin" \ - -H "Authorization: Bearer J5laxTz5Zte9oRB5x8QsyYRQ6b0hAuXI" \ + -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{"user_id": "@todesneutron:steggi-matrix.work"}' ``` @@ -292,19 +292,19 @@ Da kein Synapse-Admin-API existiert, nutze die Standard-Matrix-Client-API mit Ad ```bash # Raum beitreten (als Admin, auch wenn invite-only): curl -X POST "https://steggi-matrix.work/_matrix/client/v3/join/!roomId%3Asteggi-matrix.work" \ - -H "Authorization: Bearer J5laxTz5Zte9oRB5x8QsyYRQ6b0hAuXI" \ + -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{}' # User in Raum einladen: curl -X POST "https://steggi-matrix.work/_matrix/client/v3/rooms/!roomId%3Asteggi-matrix.work/invite" \ - -H "Authorization: Bearer J5laxTz5Zte9oRB5x8QsyYRQ6b0hAuXI" \ + -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{"user_id": "@user:steggi-matrix.work"}' # Space-Child-Relation setzen (Channel einem Space hinzufügen): curl -X PUT "https://steggi-matrix.work/_matrix/client/v3/rooms/!spaceId%3Asteggi-matrix.work/state/m.space.child/!channelId%3Asteggi-matrix.work/" \ - -H "Authorization: Bearer J5laxTz5Zte9oRB5x8QsyYRQ6b0hAuXI" \ + -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{ "via": ["steggi-matrix.work"], @@ -313,7 +313,7 @@ curl -X PUT "https://steggi-matrix.work/_matrix/client/v3/rooms/!spaceId%3Astegg # Space-Parent-Relation setzen (im Channel-Raum): curl -X PUT "https://steggi-matrix.work/_matrix/client/v3/rooms/!channelId%3Asteggi-matrix.work/state/m.space.parent/!spaceId%3Asteggi-matrix.work/" \ - -H "Authorization: Bearer J5laxTz5Zte9oRB5x8QsyYRQ6b0hAuXI" \ + -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{ "via": ["steggi-matrix.work"], @@ -322,11 +322,11 @@ curl -X PUT "https://steggi-matrix.work/_matrix/client/v3/rooms/!channelId%3Aste # Alle Mitglieder eines Raums auflisten: curl "https://steggi-matrix.work/_matrix/client/v3/rooms/!roomId%3Asteggi-matrix.work/members" \ - -H "Authorization: Bearer J5laxTz5Zte9oRB5x8QsyYRQ6b0hAuXI" + -H "Authorization: Bearer " # Raum verlassen (als User, nicht löschen): curl -X POST "https://steggi-matrix.work/_matrix/client/v3/rooms/!roomId%3Asteggi-matrix.work/leave" \ - -H "Authorization: Bearer J5laxTz5Zte9oRB5x8QsyYRQ6b0hAuXI" \ + -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{}' ``` @@ -419,15 +419,15 @@ Wenn du M_FORBIDDEN bekommst: ```bash # 1. Token gültig? curl "https://steggi-matrix.work/_matrix/client/v3/account/whoami" \ - -H "Authorization: Bearer J5laxTz5Zte9oRB5x8QsyYRQ6b0hAuXI" + -H "Authorization: Bearer " # 2. PL im betroffenen Raum prüfen: curl "https://steggi-matrix.work/_matrix/client/v3/rooms/!ROOM_ID/state/m.room.power_levels/" \ - -H "Authorization: Bearer J5laxTz5Zte9oRB5x8QsyYRQ6b0hAuXI" + -H "Authorization: Bearer " # 3. Mitglied des Raums? curl "https://steggi-matrix.work/_matrix/client/v3/rooms/!ROOM_ID/state/m.room.member/%40todesneutron%3Asteggi-matrix.work/" \ - -H "Authorization: Bearer J5laxTz5Zte9oRB5x8QsyYRQ6b0hAuXI" + -H "Authorization: Bearer " # 4. Was genau schlägt fehl? (Antwort-Body lesen!) # M_FORBIDDEN + "Power level too low" → PL erhöhen diff --git a/scripts/release.ps1 b/scripts/release.ps1 index dc2c6d2..a0d3012 100644 --- a/scripts/release.ps1 +++ b/scripts/release.ps1 @@ -11,7 +11,17 @@ Set-Location (Split-Path $PSScriptRoot -Parent) # ── Config ──────────────────────────────────────────────────────────────────── $GiteaBase = "http://192.168.178.71:3000" $GiteaRepo = "steggi/pyramid" -$GiteaToken = "bb522f9646c6856c9ab58bef0151ac36a9ce1d57" +# Token NIE hier eintragen (CLAUDE.md: keine Secrets ins Repo). Er kommt aus der +# Umgebungsvariable PYRAMID_GITEA_TOKEN – einmalig pro PC setzen mit: +# [Environment]::SetEnvironmentVariable("PYRAMID_GITEA_TOKEN", "", "User") +# (neues Terminal öffnen, damit die Variable sichtbar ist) +$GiteaToken = $env:PYRAMID_GITEA_TOKEN +if ([string]::IsNullOrWhiteSpace($GiteaToken)) { + Write-Error ("PYRAMID_GITEA_TOKEN ist nicht gesetzt. Einmalig setzen mit:`n" + + ' [Environment]::SetEnvironmentVariable("PYRAMID_GITEA_TOKEN", "", "User")' + + "`nDen Token in Gitea unter Einstellungen > Anwendungen erzeugen (Scope: repository).") + exit 1 +} $Headers = @{ Authorization = "token $GiteaToken"; "Content-Type" = "application/json" } # ── Read and Update version from pubspec.yaml ─────────────────────────────────