docs(adr): ADR-0021 - federation closed, and why the tighter option was wrong

sorb chose C, fully disabling federation at the edge. Building it showed that would
have killed group calls: lk-jwt-service verifies OpenID tokens through
/_matrix/federation/v1/openid/userinfo and reaches it over the public name, so a path
block on /_matrix/federation is the mrtc outage again with a different cause. C was
dropped and B implemented.

The ADR records the measurement the decision rests on - zero destinations, zero remote
users, zero rooms with outside participation in four months - and the trap, so nobody
completes C later as a quick follow-up. Doing that safely means binding the auth
service to Synapse in-cluster first, which is its own undertaking with a call
acceptance.

Also recorded because it nearly slipped through: Flux applied the ConfigMap while
Synapse kept running its old config from 2026-08-01. The config is rendered at pod
start, so the change was inert until a restart - the same class as #0044. Verified
afterwards inside the running process rather than in the ConfigMap, with the client
API and the OpenID endpoint still answering.
This commit is contained in:
Thore Cimbal
2026-08-19 12:00:00 +00:00
parent 7a14adb015
commit 15134d1846
3 changed files with 133 additions and 1 deletions
+81
View File
@@ -0,0 +1,81 @@
---
type: adr
id: "0021"
status: accepted
date: 2026-08-19
supersedes: null
superseded_by: null
related:
- "docs/issues/0060-gitops-17-federation-allowlist-or-closed-federation-dec.md"
- "docs/issues/0088-gitops-56-networkpolicy-ausgehender-verkehr-ist-unbesch.md"
---
# ADR-0021: Föderation wird geschlossen — leere Whitelist statt offener Tür
## Kontext
Die Frage stand seit dem 2026-07-28 offen, weil niemand wusste, was eine Schließung
kosten würde. Am 2026-08-19 wurde sie gemessen statt geschätzt.
**Föderation war offen und öffentlich erreichbar.** In `synapse-values.yaml` stand zu
Föderation nichts, es galten die Synapse-Vorgaben; `/_matrix/federation/v1/version` und
`/_matrix/key/v2/server` antworteten von außen mit HTTP 200. Dass Port 8448 zu ist,
änderte daran nichts: Die Delegation in `/.well-known/matrix/server` führt die
Föderation über `matrix.axion1337.chat:443`, denselben Port wie den Client-Verkehr.
**Benutzt wurde sie nie.** Über die gesamte Betriebszeit seit dem 2026-04-21, aus der
Synapse-Datenbank: **0** Einträge in `destinations`, **0** je erfolgreich kontaktierte
Server, **0** fremde Nutzer in den 31 eigenen Räumen, **0** Räume mit fremder
Beteiligung. Nicht „wenig", sondern null.
Damit ist der Handel nicht „Reichweite gegen Sicherheit", sondern **eine ungenutzte
Fähigkeit gegen die größte fremdzugewandte Angriffsfläche, die Synapse hat** — und
historisch die, in der seine CVEs sitzen.
## Optionen
**A: offen lassen.** Kein Aufwand; dauerhaft Angriffsfläche für null nachgewiesenen
Bedarf.
**B: `federation_domain_whitelist: []`.** Föderation nur mit ausdrücklich genannten
Servern, leer also mit keinem. Eine Zeile, versioniert, in Minuten umkehrbar.
Die Endpunkte antworten weiterhin — der Server wird unbeteiligt, nicht unsichtbar.
**C: Föderations-Endpunkte am Rand sperren.** Zunächst als „kleinste Fläche" gewählt,
dann **verworfen** — beim Umsetzen zeigte sich, dass es die Gruppen-Calls zerstört
hätte (siehe unten).
## Entscheidung
**Option B.** `federation_domain_whitelist: []` in
`gitops:apps/production/custom-configs/synapse-values.yaml`.
## Warum C verworfen wurde — der Teil, der nicht offensichtlich ist
Der MatrixRTC-Authorisation-Service (`lk-jwt-service`) prüft OpenID-Tokens über
**`/_matrix/federation/v1/openid/userinfo`** — einen Föderations-Pfad. Und er ruft ihn
über den **öffentlichen** Namen auf: Das Deployment trägt keine `hostAliases` und
`dnsPolicy: ClusterFirst`, `matrix.axion1337.chat` löst also auf die öffentliche IP auf
und der Verkehr läuft über Traefik.
Wer `/_matrix/federation/` am Ingress sperrt, kappt damit die Token-Prüfung für
Gruppen-Calls — **derselbe Ausfall wie beim gelöschten `mrtc`-Record, nur mit anderer
Ursache.** Ein Pfad-Block sieht dabei korrekter aus als die Whitelist, weshalb dieser
Zusammenhang im Konfigurations-Kommentar festgehalten ist und nicht nur hier.
Synapse bedient diesen Endpunkt ohne X-Matrix-Signatur (`REQUIRE_AUTH = False`); die
Whitelist greift dort also nicht und darf es auch nicht.
## Konsequenzen
- Fremde Server können weder beitreten noch Ereignisse einliefern. Bestehende Räume und
Nutzer sind nicht betroffen — es gab keine fremde Beteiligung, die enden könnte.
- **Die Fähigkeit ist eine Zeile entfernt**, nicht verloren: Ein Domain-Eintrag öffnet
gezielt für einen Partner. Das war der Grund, B gegenüber einem harten Rückbau
vorzuziehen.
- Die Endpunkte antworten weiterhin von außen. Wer das ändern will, muss **zuerst** den
Auth-Service clusterintern an Synapse binden — sonst siehe oben. Das ist ein eigenes
Vorhaben mit Call-Abnahme, kein Nebenschritt.
- Eine Port-Sperre bei Hetzner kann diese Trennung **nicht** leisten: Föderation und
Client-Verkehr teilen sich 443. Die Konfiguration ist die einzige Stelle, an der sie
überhaupt trennbar sind.
@@ -79,3 +79,53 @@ nur, ob das Raumverzeichnis über Föderation sichtbar ist, und ist unabhängig
Grundsatzfrage.
**Entscheidung sorb steht aus.**
## Entscheidung und Umsetzung 2026-08-19
**sorb: erst C, dann C verworfen — B umgesetzt.** Beim Bauen von C zeigte sich, dass
ein Pfad-Block die Gruppen-Calls zerstört hätte: `lk-jwt-service` prüft OpenID-Tokens
über `/_matrix/federation/v1/openid/userinfo` und ruft ihn über den **öffentlichen**
Namen auf (keine `hostAliases`, `dnsPolicy: ClusterFirst`), also über Traefik. Ein
Block auf `/_matrix/federation/` hätte denselben Ausfall erzeugt wie der gelöschte
`mrtc`-Record. Festgehalten als [ADR-0021](../adr/0021-foederation-geschlossen.md),
inklusive der Begründung, warum C nicht nachträglich „noch schnell" nachgeholt werden
sollte.
`federation_domain_whitelist: []` steht in
`gitops:apps/production/custom-configs/synapse-values.yaml` (Commit `3935f35`).
**Zwei Fallen beim Umsetzen, beide vor dem Ausrollen bemerkt:**
1. Der erste Einschub **zerschnitt den `auto_join`-Block**`auto_join_rooms_for_guests`
landete unter `federation`. Nach dem Zusammenführen der Fragmente funktional
identisch, zu lesen falsch; korrigiert, der Diff ist jetzt 20 Zeilen Zugewinn und
nichts Verschobenes.
2. **Flux hat angewandt, Synapse lief weiter mit der alten Konfiguration.** Die
ConfigMap trug die Zeile, der laufende Pod nicht — er stammte vom 2026-08-01. Die
Konfiguration wird beim Pod-Start gerendert; ohne Neustart ist die Änderung
wirkungslos. Dieselbe Klasse wie #0044. Neustart per
`rollout restart statefulset/matrix-stack-synapse-main` angestoßen.
**Zur Hetzner-Port-Sperre:** Sie kann diese Trennung nicht leisten. 8448 ist bereits zu,
und die Delegation führt die Föderation über **443** — denselben Port wie alle Clients.
Die Synapse-Konfiguration ist die einzige Stelle, an der Föderation und Client-Verkehr
überhaupt trennbar sind.
### Abnahme nach dem Neustart (2026-08-19, Synapse-Start 11:13 UTC)
| Prüfung | Ergebnis |
|---|---|
| `federation_domain_whitelist: []` **im laufenden Prozess** (nicht nur in der ConfigMap) | ✅ vorhanden |
| `/_matrix/federation/v1/openid/userinfo` — die Element-Call-Abhängigkeit | ✅ HTTP 401 (bedient, verlangt Token) |
| `/_matrix/client/versions` — Client-Verkehr | ✅ HTTP 200 |
| `/_matrix/federation/v1/version` | HTTP 200 — **erwartet** |
Der letzte Punkt ist kein Mangel, sondern die bewusste Grenze von Option B: Die
Endpunkte antworten weiterhin, der Server ist **unbeteiligt, nicht unsichtbar**. Was
sich geändert hat, ist nicht die Sichtbarkeit, sondern dass Synapse mit keinem fremden
Server mehr Ereignisse austauscht.
**Noch offen: die Abnahme im echten Gruppen-Call.** `curl` belegt, dass der
OpenID-Endpunkt antwortet — nicht, dass die vollständige Token-Prüfung durchläuft. Für
Änderungen im Call-Pfad gilt hier die Regel aus #0054: Abnahme im echten Call ist
Rollout-Voraussetzung, nicht Nacharbeit.