From 8e76a96668567dc92f2dcf682b7ad774f6305898 Mon Sep 17 00:00:00 2001 From: Dennis Paradzinski Date: Mon, 11 May 2026 15:19:00 +0200 Subject: [PATCH] feat: switch Prosody to JWT auth (HS256, Embertime as issuer) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replaces the anonymous-OK setup with JWT-gated joins. Only participants that Embertime invited get a valid token — random URL-guessers can no longer enter rooms even if they discover the slug. - ConfigMap: ENABLE_AUTH=1 + AUTH_TYPE=jwt + JWT_APP_ID=embertime - 20-secrets.yaml.example: JWT_APP_SECRET placeholder with docs - CLAUDE.md: documents the new auth model + rotation flow Pipeline rolls config out; the secret itself stays out-of-band — admin copies it from Embertime UI (Settings → Meeting-Server) or queries the embertime DB directly. Co-Authored-By: Claude Opus 4.7 (1M context) --- CLAUDE.md | 13 ++++++++++++- infra/k3s/10-config.yaml | 15 +++++++++++---- infra/k3s/20-secrets.yaml.example | 7 +++++++ 3 files changed, 30 insertions(+), 5 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 6c1bbc7..714808c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -39,9 +39,20 @@ Inter-Component-Auth über shared XMPP-Component-Secrets (im Kubernetes-Secret, Danach: Pipeline rollt aus, in ~3 Min steht `https://meet.it.financeflow.de`. +## Auth-Modell (JWT, Stand 2026-05-11) + +Prosody ist auf **`AUTH_TYPE=jwt`** (HS256) konfiguriert — Joins ohne signiertes Token werden abgelehnt. Embertime ist der einzige Token-Issuer: + +- HMAC-Secret lebt in `app_settings.meeting_jwt_secret` (Embertime-DB) +- Wird auch im k8s-Secret `jitsi-secrets` als `JWT_APP_SECRET` benötigt — beide müssen identisch sein +- Embertime-Endpoint `GET /meetings/:id/join` mintet pro Teilnehmer + Meeting ein zeitlich begrenztes Token (gültig bis Meeting-Ende + 2h) +- Token-Payload enthält `room` (= room_slug), `context.user` (Display-Name + email + moderator-Flag) + +Bei Secret-Rotation: neuer Wert über Embertime-Admin-UI sichtbar, dann `kubectl -n jitsi patch secret jitsi-secrets ...` + `kubectl -n jitsi rollout restart deployment` (sonst gilt noch der alte JWT_APP_SECRET in den laufenden Pods). + ## Was NICHT machen -- Keine Authentifizierung aktivieren — der Witz ist, dass der Service ohne Login funktioniert. `ENABLE_AUTH=0` ist Pflicht. +- `ENABLE_AUTH=0` zurücksetzen — würde das ganze Auth-Modell unwirksam machen. Wenn du eine offene Instanz brauchst, lieber zweite Jitsi-Instanz auf anderem Subdomain. - Keinen Jibri (Recording) ohne expliziten Auftrag — braucht extra Pod mit Chrome + viel mehr Ressourcen - Kein Helm — bleibt beim raw-Manifest-Pattern wie Embertime - Nicht `replicas > 1` für prosody/jicofo — beide sind stateful Single-Instance diff --git a/infra/k3s/10-config.yaml b/infra/k3s/10-config.yaml index 06259e8..395960c 100644 --- a/infra/k3s/10-config.yaml +++ b/infra/k3s/10-config.yaml @@ -22,11 +22,18 @@ data: XMPP_SERVER: "prosody.jitsi.svc.cluster.local" XMPP_BOSH_URL_BASE: "http://prosody.jitsi.svc.cluster.local:5280" - # === Auth disabled — anyone with the URL can start a room. That's the - # whole point of self-hosting (avoids the meet.jit.si Google/GitHub - # moderator gate). === - ENABLE_AUTH: "0" + # === JWT-based auth — Prosody validates HS256 tokens signed by + # Embertime. Only invited participants get a valid token (with + # context.user populated, optional moderator flag). The shared HMAC + # secret lives in the Kubernetes Secret as JWT_APP_SECRET. Guests are + # disabled — a missing/invalid token means no entry. === + ENABLE_AUTH: "1" + AUTH_TYPE: "jwt" ENABLE_GUESTS: "0" + JWT_APP_ID: "embertime" + JWT_ACCEPTED_ISSUERS: "embertime" + JWT_ACCEPTED_AUDIENCES: "embertime" + TOKEN_AUTH_URL: "" # === TLS handled by ingress cert-manager, not by the jitsi/web container === ENABLE_LETSENCRYPT: "0" diff --git a/infra/k3s/20-secrets.yaml.example b/infra/k3s/20-secrets.yaml.example index c6963ec..5c26342 100644 --- a/infra/k3s/20-secrets.yaml.example +++ b/infra/k3s/20-secrets.yaml.example @@ -13,8 +13,15 @@ metadata: namespace: jitsi type: Opaque stringData: + # Inter-component passwords — generate fresh via generate-secrets.sh JICOFO_COMPONENT_SECRET: "REPLACE_WITH_32_RANDOM_CHARS" JICOFO_AUTH_USER: "focus" JICOFO_AUTH_PASSWORD: "REPLACE_WITH_32_RANDOM_CHARS" JVB_AUTH_USER: "jvb" JVB_AUTH_PASSWORD: "REPLACE_WITH_32_RANDOM_CHARS" + # HMAC key shared with Embertime — must match app_settings.meeting_jwt_secret + # in Embertime's DB. Get it from the Embertime UI (Settings → Meeting-Server + # → "Secret rotieren") OR query the DB directly with: + # kubectl -n embertime exec -it deploy/embertime-postgres -- \ + # psql -U embertime -t -c "select meeting_jwt_secret from app_settings" + JWT_APP_SECRET: "REPLACE_WITH_VALUE_FROM_EMBERTIME"