- Deployment guides for TURN, Authentik, Monitoring, Element, Policies - Task tracking (TASKS.md) - Element desktop setup scripts for all platforms - Installation guide Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
498 lines
32 KiB
Markdown
498 lines
32 KiB
Markdown
# Invite-basierte Selbstregistrierung mit MAS in ESS Community unter Ansible und FluxCD
|
||
|
||
## Zusammenfassung
|
||
|
||
Für ein entity["company","Element","matrix software company"]-basiertes ESS-Community-Deployment mit aktivem Matrix Authentication Service ist eine invite-basierte Selbstregistrierung **ohne Authentik** sauber umsetzbar. Der belastbare Weg ist: **MAS-Registrierung aktivieren, Registrierungstoken erzwingen, Tokens über die MAS Admin API erzeugen, und Standard-Onboarding über Synapse `auto_join_rooms` abbilden**. In MAS-/MSC3861-Setups sind die alten Synapse-Mechanismen für Registration Tokens bzw. Shared-Secret-Registration nicht mehr der richtige Pfad: Die Synapse-Registration-Token-API ist in diesem Modus deaktiviert, ebenso die Shared-Secret-Registration-API; die offizielle Automationsoberfläche für externe Tools ist stattdessen die MAS Admin API. citeturn1view2turn20view1turn26search11turn26search5turn35view0
|
||
|
||
Für die Zuweisung zu Räumen und Spaces gilt: **MAS-Claims sind für Identitätsattribute gedacht, nicht für Raum-/Space-Mitgliedschaften**. Der robuste Standard ist daher eine Zweiteilung: **allgemeine Onboarding-Räume und Spaces über Synapse `auto_join_rooms`**, und **feingranulare, zielgruppenspezifische Mitgliedschaften über einen kleinen Post-Registration-Provisioner oder ein Synapse-Modul** über `on_user_registration` bzw. `on_user_login`. Gruppen/Communities sind heute kein sinnvolles Ziel mehr, weil Synapse diese Funktion deprecated und anschließend entfernt hat; für aktuelle Deployments solltest du konsequent mit **Spaces + Rooms** arbeiten. citeturn16view0turn16view1turn16view2turn34view0turn35view0turn17search0turn17search2
|
||
|
||
Für GitOps empfehle ich eine klare Trennung zwischen **statischer Konfiguration** und **operativem Invite-Zustand**. Statisch in Git/SOPS/Flux gehören: MAS-Account-Settings, SMTP-Daten, der MAS-Admin-OAuth-Client und optionale Onboarding-Policies. **Kurzlebige Einmal-Tokens** sollten dagegen in der Regel **nicht** als dauerhafter Desired State in Git geführt werden, sondern bei Bedarf über die MAS Admin API erzeugt, per E-Mail verschickt und nur optional als Audit-Record abgelegt werden. Das passt deutlich besser zu FluxCD als das “GitOpsen” flüchtiger Einladungen. Die optionale LiveKit-/Element-Call-Schicht ist für den Registrierungsfluss architektonisch unabhängig. citeturn20view1turn23view0turn23view3turn25search0turn25search13
|
||
|
||
## Zielbild und Architektur
|
||
|
||
Die offizielle ESS-Dokumentation unterstützt zusätzliche Konfiguration für Synapse, MAS, Element Web und MatrixRTC/LiveKit jeweils über `additional`-Blöcke. MAS läuft dabei neben Synapse und spricht mit Synapse über den dedizierten `matrix_authentication_service`-Mechanismus; Synapse 1.136+ führt diese Integration als stabile Konfiguration. Für dein Ziel bedeutet das: ESS/Helm liefert die Plattform, MAS erzwingt die Token-basierte Registrierung, Synapse übernimmt Mitgliedschaften, und Element Web kann optional eine eigene Landing-/Invite-Seite anzeigen. citeturn1view2turn1view5turn31view1turn35view0turn24search0
|
||
|
||
```mermaid
|
||
flowchart TB
|
||
subgraph GitOps["GitOps-Steuerung"]
|
||
A[Ansible]
|
||
F[FluxCD]
|
||
G[Git Repository]
|
||
S[SOPS Secrets]
|
||
A --> G
|
||
S --> G
|
||
F --> G
|
||
end
|
||
|
||
subgraph K8s["Kubernetes / ESS Helm"]
|
||
HR[HelmRelease matrix-stack]
|
||
EW[Element Web]
|
||
MAS[Matrix Authentication Service]
|
||
SYN[Synapse]
|
||
PG[(PostgreSQL)]
|
||
SMTP[SMTP]
|
||
MOD[Optional: Post-Registration Provisioner / Synapse-Modul]
|
||
LK[Optional: LiveKit / Element Call]
|
||
end
|
||
|
||
G --> HR
|
||
HR --> EW
|
||
HR --> MAS
|
||
HR --> SYN
|
||
HR --> PG
|
||
HR --> LK
|
||
|
||
MAS --> PG
|
||
SYN --> PG
|
||
MAS --> SMTP
|
||
MAS <--> SYN
|
||
EW --> MAS
|
||
EW --> SYN
|
||
|
||
A -->|MAS Admin API| MAS
|
||
A -->|E-Mail Einladung| User[Neuer Benutzer]
|
||
User -->|Invite-Link + Registration Token| EW
|
||
User -->|Registrierung| MAS
|
||
MAS -->|User-Provisionierung| SYN
|
||
SYN -->|auto_join_rooms| User
|
||
MOD -->|join/invite per Synapse Admin API| SYN
|
||
```
|
||
|
||
Die praktikabelste Zielarchitektur ist deshalb: **Invite-Mail → MAS Token-Registierung → Synapse-Provisionierung → automatisches Standard-Onboarding via `auto_join_rooms` → optionaler Fein-Provisioner für zusätzliche Spaces/Räume**. Für Spaces ist wichtig: Synapse behandelt Spaces “unter der Haube” als Räume, daher können Space-Aliasse in `auto_join_rooms` verwendet werden. **Automatisch erzeugen** kann Synapse über `autocreate_auto_join_rooms` jedoch **keine Spaces**; Spaces müssen also vorher existieren. citeturn16view0turn16view1turn16view5
|
||
|
||
Die Optionen lassen sich so einordnen:
|
||
|
||
| Option | Bewertung | Stärken | Schwächen | Geeignet für |
|
||
|---|---|---|---|---|
|
||
| MAS Registration Tokens | **Empfohlen** | Nativ im MAS-Stack, sauber mit ESS/MAS, externe Tooling-Anbindung über Admin API | Keine offizielle, dokumentierte “Einladungs-E-Mail-Engine” für Invite-Links; Token-Versand musst du selbst automatisieren | ESS Community mit FluxCD/Ansible citeturn20view1turn12view0turn37view1turn1view2 |
|
||
| Authentik Invitations | Möglich, aber hier unnötig | Fertiges Invite-/Enrollment-Modell, URL-basierte Invitations | Zusätzlicher IdP-Layer, mehr bewegliche Teile, für dein Ziel kein Muss | Wenn ohnehin zentraler OIDC-IdP Standard ist citeturn36search0turn36search1 |
|
||
| Manuelle Admin-Erstellung | Solide als Fallback | Maximal kontrolliert, kein Self-Service nötig | Kein Selbstregistrierungsfluss, hoher Betriebsaufwand | Break-glass, Admin-/Testkonten citeturn27view1turn26search5 |
|
||
|
||
Wichtig für die Zielmodellierung: **Groups/Communities** sind im aktuellen Synapse-Kontext kein tragfähiges Onboarding-Ziel mehr. Die offizielle Synapse-Kommunikation beschreibt Groups/Communities als deprecated und ab Synapse 1.61 entfernt; Spaces sind der vorgesehene Ersatz. Wenn du also “Spaces, Groups, Rooms” als Zielbild denkst, sollte die reale Implementierung **Spaces + Rooms** heißen. citeturn17search0turn17search2
|
||
|
||
## MAS-Konfiguration und GitOps-Manifeste
|
||
|
||
ESS stellt laut offizieller Doku genau den von dir benötigten Mechanismus bereit: Zusätzliche MAS-Konfiguration wird unter `matrixAuthenticationService.additional` in YAML-Fragmente gelegt und in die finale MAS-Konfiguration gemerged. Dasselbe gilt für Synapse unter `synapse.additional` und für Element Web unter `elementWeb.additional`. citeturn1view2turn31view1
|
||
|
||
Der kleinste sinnvolle MAS-Block für invite-basierte Self-Registration sieht so aus. Die Felder `password_registration_enabled`, `registration_token_required` und `password_registration_email_required` sind offiziell dokumentiert; ESS dokumentiert zusätzlich explizit, dass `password_registration_email_required: false` **nur** in Kombination mit Restriktionen wie `registration_token_required: true` auf öffentlich föderierenden Deployments verantwortbar ist, weil die Instanz sonst missbraucht werden kann. citeturn2view4turn20view0turn1view2
|
||
|
||
```yaml
|
||
apiVersion: v1
|
||
kind: Secret
|
||
metadata:
|
||
name: ess-mas-values-secret
|
||
namespace: matrix
|
||
stringData:
|
||
values.yaml: |
|
||
matrixAuthenticationService:
|
||
additional:
|
||
10-account.yaml:
|
||
config: |
|
||
passwords:
|
||
enabled: true
|
||
minimum_complexity: 3
|
||
|
||
account:
|
||
password_registration_enabled: true
|
||
registration_token_required: true
|
||
password_registration_email_required: false
|
||
password_change_allowed: true
|
||
password_recovery_enabled: false
|
||
login_with_email_allowed: false
|
||
|
||
# Optional, falls du Recoveries / Verifizierung brauchst
|
||
email:
|
||
from: '"Matrix Auth" <no-reply@__DOMAIN__>'
|
||
reply_to: '"Support" <support@__DOMAIN__>'
|
||
transport: smtp
|
||
mode: starttls
|
||
hostname: __SMTP_HOST__
|
||
port: 587
|
||
username: __SMTP_USERNAME__
|
||
password: __SMTP_PASSWORD__
|
||
|
||
# Für halböffentliche Setups empfehlenswert
|
||
captcha:
|
||
service: cloudflare_turnstile
|
||
site_key: __TURNSTILE_SITE_KEY__
|
||
secret_key: __TURNSTILE_SECRET_KEY__
|
||
|
||
policy:
|
||
data:
|
||
registration:
|
||
banned_usernames:
|
||
literals: ["admin", "root", "support"]
|
||
emails:
|
||
allowed_addresses:
|
||
suffixes: ["@example.org"]
|
||
requester:
|
||
banned_ips:
|
||
- 10.0.0.0/8
|
||
- 192.168.0.0/16
|
||
|
||
rate_limiting:
|
||
registration:
|
||
burst: 2
|
||
per_second: 0.0003
|
||
login:
|
||
per_ip:
|
||
burst: 5
|
||
per_second: 0.02
|
||
per_account:
|
||
burst: 30
|
||
per_second: 0.1
|
||
```
|
||
|
||
Damit externe Tools Tokens anlegen können, muss die **MAS Admin API** aktiviert werden. Die offizielle MAS-Doku sagt ausdrücklich, dass `adminapi` nicht standardmäßig exponiert ist und in einen Listener aufgenommen werden muss; Zugriffe werden mit dem Scope `urn:mas:admin` geschützt. Für automatisierte Tools ist der dokumentierte Weg ein **OAuth-Client mit `client_credentials`**, dessen `client_id` in `policy.data.admin_clients` steht. citeturn20view1turn23view0turn23view3
|
||
|
||
```yaml
|
||
apiVersion: v1
|
||
kind: Secret
|
||
metadata:
|
||
name: ess-mas-admin-client
|
||
namespace: matrix
|
||
stringData:
|
||
admin-client.yaml: |
|
||
http:
|
||
listeners:
|
||
- name: web
|
||
resources:
|
||
- name: discovery
|
||
- name: human
|
||
- name: oauth
|
||
- name: compat
|
||
- name: graphql
|
||
playground: false
|
||
- name: assets
|
||
path: ./share/assets/
|
||
- name: adminapi
|
||
binds:
|
||
- address: "[::]:8080"
|
||
|
||
clients:
|
||
- client_id: 01JV0000000000000000000001
|
||
client_auth_method: client_secret_basic
|
||
client_secret: "__MAS_ADMIN_CLIENT_SECRET__"
|
||
|
||
policy:
|
||
data:
|
||
admin_clients:
|
||
- 01JV0000000000000000000001
|
||
```
|
||
|
||
Dieses Secret bindest du wiederum als weiteren `additional`-Eintrag ein. So vermeidest du Dateipfad-Annahmen im Container und hältst den Client-Secret-Wert SOPS-fähig. citeturn31view1turn20view1turn23view0
|
||
|
||
```yaml
|
||
apiVersion: v1
|
||
kind: Secret
|
||
metadata:
|
||
name: ess-mas-values-secret
|
||
namespace: matrix
|
||
stringData:
|
||
values.yaml: |
|
||
matrixAuthenticationService:
|
||
additional:
|
||
20-admin-client.yaml:
|
||
configSecret: ess-mas-admin-client
|
||
configSecretKey: admin-client.yaml
|
||
```
|
||
|
||
Für FluxCD genügt ein klassisches `valuesFrom`-Muster. Das bildet dein bestehendes Repo-Schema mit ConfigMaps/Secrets gut ab und bleibt mit SOPS kompatibel. citeturn31view1turn1view2
|
||
|
||
```yaml
|
||
apiVersion: helm.toolkit.fluxcd.io/v2
|
||
kind: HelmRelease
|
||
metadata:
|
||
name: matrix-stack
|
||
namespace: matrix
|
||
spec:
|
||
interval: 1h
|
||
chart:
|
||
spec:
|
||
chart: matrix-stack
|
||
sourceRef:
|
||
kind: HelmRepository
|
||
name: element-ess-oci
|
||
namespace: flux-system
|
||
valuesFrom:
|
||
- kind: ConfigMap
|
||
name: ess-synapse-custom
|
||
valuesKey: values.yaml
|
||
- kind: ConfigMap
|
||
name: ess-element-custom
|
||
valuesKey: values.yaml
|
||
- kind: Secret
|
||
name: ess-mas-values-secret
|
||
valuesKey: values.yaml
|
||
values:
|
||
serverName: "__SERVER_NAME__"
|
||
postgres:
|
||
enabled: true
|
||
synapse:
|
||
enabled: true
|
||
ingress:
|
||
host: "__SYNAPSE_HOST__"
|
||
matrixAuthenticationService:
|
||
enabled: true
|
||
ingress:
|
||
host: "__MAS_HOST__"
|
||
elementWeb:
|
||
enabled: true
|
||
ingress:
|
||
host: "__ELEMENT_HOST__"
|
||
matrixRTC:
|
||
enabled: true
|
||
ingress:
|
||
host: "__MRTC_HOST__"
|
||
```
|
||
|
||
Für das automatische Standard-Onboarding nimmst du Synapse `auto_join_rooms`. Die offizielle Synapse-Doku bestätigt: neue Benutzer werden automatisch zu den gelisteten Räumen hinzugefügt; **Space-Aliasse sind erlaubt**, weil Spaces Räume sind; **Spaces werden aber nicht automatisch erzeugt**, und für private/invite-only Auto-Join-Setups muss `auto_join_mxid_localpart` gesetzt sein, wobei dieser Benutzer in den betreffenden Räumen Mitglied sein und Einladungsrechte haben muss. citeturn16view0turn16view1turn16view2
|
||
|
||
```yaml
|
||
apiVersion: v1
|
||
kind: ConfigMap
|
||
metadata:
|
||
name: ess-synapse-custom
|
||
namespace: matrix
|
||
data:
|
||
values.yaml: |
|
||
synapse:
|
||
additional:
|
||
20-onboarding.yaml:
|
||
config: |
|
||
auto_join_rooms:
|
||
- "#welcome:__SERVER_NAME__"
|
||
- "#announcements:__SERVER_NAME__"
|
||
- "#community-space:__SERVER_NAME__"
|
||
|
||
autocreate_auto_join_rooms: false
|
||
auto_join_mxid_localpart: system
|
||
auto_join_rooms_for_guests: false
|
||
```
|
||
|
||
Für die User Experience kannst du Element Web mit einer eigenen Welcome-/Invite-Seite versehen. Offiziell unterstützt Element Web dafür `embedded_pages.welcome_url`, `login_for_welcome`, Branding-Parameter und – für native OIDC-Setups – OIDC-Client-Optionen. Für deinen Fall ist das vor allem nützlich, **weil die offizielle MAS-Dokumentation keinen stabil dokumentierten URL-Parameter zum Prefill eines Registration Tokens beschreibt**; die saubere Lösung ist daher oft eine eigene Landing-Page, die den Token erklärt oder temporär speichert und dann zum Registrierungsfluss führt. Diese Aussage ist eine Architektur-Inferenz aus den offiziellen Element- und MAS-Dokumenten. citeturn24search0turn20view1turn22view0
|
||
|
||
```yaml
|
||
apiVersion: v1
|
||
kind: ConfigMap
|
||
metadata:
|
||
name: ess-element-custom
|
||
namespace: matrix
|
||
data:
|
||
values.yaml: |
|
||
elementWeb:
|
||
additional:
|
||
config.json: |
|
||
{
|
||
"embedded_pages": {
|
||
"welcome_url": "https://__ELEMENT_HOST__/invite/index.html",
|
||
"login_for_welcome": false
|
||
},
|
||
"branding": {
|
||
"auth_header_logo_url": "https://__ELEMENT_HOST__/assets/logo.svg"
|
||
}
|
||
}
|
||
```
|
||
|
||
Ein wichtiger Betriebs-Hinweis: Es gibt aktuell ein **offenes GitHub-Issue** im ESS-Helm-Repository, das meldet, dass `matrixAuthenticationService.additional.*` in bestimmten Konstellationen ignoriert werden könne. Das ist kein offizieller Release-Hinweis, aber als Failure-Mode ernst zu nehmen. Deshalb solltest du nach jeder Änderung das **gerenderte MAS-ConfigFile im Pod** prüfen, bevor du mit echten Einladungen arbeitest. citeturn38view0turn31view1
|
||
|
||
## Einladungsworkflow und Provisionierung
|
||
|
||
Der saubere GitOps-Ablauf ist in der Praxis fünfstufig. Zuerst legst du **statische Konfiguration** in Git ab: MAS-Settings, SMTP, Admin-Client, Synapse Auto-Join. Danach reconciled Flux diese Ressourcen in den Cluster. Anschließend erzeugt Ansible oder ein Shell-Skript **on demand** ein kurzlebiges Registration Token über die MAS Admin API. Dieses Token wird per E-Mail verschickt – entweder als Link zu deiner eigenen Invite-Landing-Page oder als Nachricht mit Token + Registrierungsanweisung. Beim ersten erfolgreichen Abschluss der Registrierung provisioniert MAS den Account in Synapse; Synapse zieht dann `auto_join_rooms`. Wenn du danach noch gezielt Spaces/Räume abhängig von Abteilung, Rolle oder Domäne vergeben willst, übernimmt das ein Provisioner oder ein Synapse-Modul. citeturn20view1turn12view0turn16view0turn34view0
|
||
|
||
Für die **MAS Admin API** ist der dokumentierte Automationspfad `client_credentials`. Die MAS-Doku nennt dafür explizit statische Clients + `policy.data.admin_clients`. Das Access-Token kannst du discovery-basiert holen, damit du keine Token-Endpoint-Pfade hart codierst. citeturn20view1turn22view0turn23view0
|
||
|
||
```bash
|
||
#!/usr/bin/env bash
|
||
set -euo pipefail
|
||
|
||
MAS_ISSUER="https://account.__DOMAIN__/"
|
||
CLIENT_ID="01JV0000000000000000000001"
|
||
CLIENT_SECRET="${MAS_ADMIN_CLIENT_SECRET}"
|
||
|
||
TOKEN_ENDPOINT="$(
|
||
curl -fsSL "${MAS_ISSUER}.well-known/openid-configuration" \
|
||
| jq -r '.token_endpoint'
|
||
)"
|
||
|
||
ACCESS_TOKEN="$(
|
||
curl -fsSL -u "${CLIENT_ID}:${CLIENT_SECRET}" \
|
||
-H 'Content-Type: application/x-www-form-urlencoded' \
|
||
-d 'grant_type=client_credentials' \
|
||
-d 'scope=urn:mas:admin' \
|
||
"${TOKEN_ENDPOINT}" \
|
||
| jq -r '.access_token'
|
||
)"
|
||
|
||
curl -fsSL -X POST "https://account.__DOMAIN__/api/admin/v1/user-registration-tokens" \
|
||
-H "Authorization: Bearer ${ACCESS_TOKEN}" \
|
||
-H 'Content-Type: application/json' \
|
||
-d '{
|
||
"usage_limit": 1,
|
||
"expires_at": "2026-05-01T12:00:00Z"
|
||
}' | jq
|
||
```
|
||
|
||
Die MAS-OpenAPI-Spezifikation dokumentiert für Registration Tokens genau die Felder, die du dafür brauchst: `token` optional, `usage_limit`, `expires_at`, dazu die Verwaltungsendpunkte zum **Listen**, **Updaten**, **Revoken** und **Unrevoken** unter `/api/admin/v1/user-registration-tokens`. “Valid” bedeutet laut Spec: nicht abgelaufen, nicht widerrufen und Usage-Limit nicht ausgeschöpft. Für Invite-Flows solltest du in der Regel **`usage_limit: 1` und kurze Expiry-Zeiten** verwenden. citeturn12view0turn13view0turn37view0turn37view1turn37view2
|
||
|
||
Beispiel für Rotation bzw. Widerruf eines ungenutzten Tokens:
|
||
|
||
```bash
|
||
TOKEN_ID="01JVABCDEF..."
|
||
curl -fsSL -X POST "https://account.__DOMAIN__/api/admin/v1/user-registration-tokens/${TOKEN_ID}/revoke" \
|
||
-H "Authorization: Bearer ${ACCESS_TOKEN}"
|
||
```
|
||
|
||
Wenn du Token-Limits oder die Ablaufzeit nachträglich anpassen willst, ist laut MAS-Admin-Spec `PUT /api/admin/v1/user-registration-tokens/{id}` der vorgesehene Weg. citeturn13view0turn11view0
|
||
|
||
In Ansible lässt sich derselbe Flow sehr gut abbilden. Hier ein minimales Beispiel mit Discovery, Token-Erzeugung und Mailversand. Der SMTP-Provider ist bewusst als Platzhalter gelassen, weil du ihn nicht spezifiziert hast. Die Speicherung des erzeugten Registration-Tokens in Git ist **optional**; ich würde standardmäßig nur **Token-ID, Empfänger, Ablaufzeit und Versandzeitpunkt** als Audit speichern, nicht das Secret selbst. citeturn20view1turn12view0turn3view1turn2view5
|
||
|
||
```yaml
|
||
- name: Discover MAS token endpoint
|
||
ansible.builtin.uri:
|
||
url: "https://account.__DOMAIN__/.well-known/openid-configuration"
|
||
return_content: true
|
||
register: oidc_discovery
|
||
|
||
- name: Get MAS admin access token
|
||
ansible.builtin.uri:
|
||
url: "{{ oidc_discovery.json.token_endpoint }}"
|
||
method: POST
|
||
user: "{{ mas_admin_client_id }}"
|
||
password: "{{ mas_admin_client_secret }}"
|
||
force_basic_auth: true
|
||
body_format: form-urlencoded
|
||
body:
|
||
grant_type: client_credentials
|
||
scope: urn:mas:admin
|
||
return_content: true
|
||
register: mas_oauth
|
||
|
||
- name: Create single-use registration token
|
||
ansible.builtin.uri:
|
||
url: "https://account.__DOMAIN__/api/admin/v1/user-registration-tokens"
|
||
method: POST
|
||
headers:
|
||
Authorization: "Bearer {{ mas_oauth.json.access_token }}"
|
||
body_format: json
|
||
body:
|
||
usage_limit: 1
|
||
expires_at: "{{ invite_expires_at }}"
|
||
return_content: true
|
||
register: mas_invite
|
||
|
||
- name: Build invite URL
|
||
ansible.builtin.set_fact:
|
||
invite_url: "https://__ELEMENT_HOST__/invite/?token={{ mas_invite.json.data.attributes.token | urlencode }}"
|
||
|
||
- name: Send invitation email
|
||
community.general.mail:
|
||
host: "__SMTP_HOST__"
|
||
port: 587
|
||
secure: starttls
|
||
username: "__SMTP_USER__"
|
||
password: "__SMTP_PASSWORD__"
|
||
to: "{{ invitee_email }}"
|
||
from: "no-reply@__DOMAIN__"
|
||
subject: "Deine Matrix-Einladung"
|
||
body: |
|
||
Hallo,
|
||
|
||
hier ist deine Einladung für Matrix.
|
||
Einladung: {{ invite_url }}
|
||
|
||
Das Token ist einmalig nutzbar und gültig bis {{ invite_expires_at }}.
|
||
```
|
||
|
||
Für die **automatische Zuweisung** zu Spaces/Räumen nach erster Anmeldung gibt es drei robuste Muster:
|
||
|
||
Erstens der Standardpfad über `auto_join_rooms`: ideal für **alle** neuen Benutzer, z. B. `#welcome`, `#announcements` und ein Haupt-Space. Das ist die einfachste und offizielle Lösung. Zweitens ein **Post-Registration-Provisioner**, der den Benutzer zusätzlich in weitere Räume einlädt oder joined. Dafür ist der offizielle Synapse-Admin-Endpunkt `POST /_synapse/admin/v1/join/<room_id_or_alias>` gedacht. Drittens ein **Synapse-Modul**, das `on_user_registration` oder `on_user_login` implementiert. Die Primärdoku beschreibt genau diese Callback-Punkte; seit Synapse 1.135 müssen Module allerdings worker-safe sein, weil `on_user_registration` auch auf Workern laufen kann. citeturn16view0turn27view0turn34view0turn35view0
|
||
|
||
Ein Provisioner gegen die Synapse Admin API kann so aussehen. Dabei setze ich voraus, dass du bereits einen **user-bound** Synapse-Admin-Token hast; dafür verweist die offizielle MAS-Doku auf den Device-Code-Flow mit Synapse-Admin-Scopes. Dasselbe Dokument zeigt auch, wie man kombinierte MAS-/Synapse-Admin-Tokens interaktiv anfordern kann. citeturn27view0turn32search1turn32search0
|
||
|
||
```bash
|
||
#!/usr/bin/env bash
|
||
set -euo pipefail
|
||
|
||
SYNAPSE_BASE="https://matrix.__DOMAIN__"
|
||
SYNAPSE_ADMIN_TOKEN="${SYNAPSE_ADMIN_TOKEN}"
|
||
USER_ID="$1"
|
||
|
||
join_room() {
|
||
local alias="$1"
|
||
curl -fsSL -X POST \
|
||
-H "Authorization: Bearer ${SYNAPSE_ADMIN_TOKEN}" \
|
||
-H 'Content-Type: application/json' \
|
||
"${SYNAPSE_BASE}/_synapse/admin/v1/join/${alias}" \
|
||
-d "{\"user_id\":\"${USER_ID}\"}" >/dev/null
|
||
}
|
||
|
||
join_room "#community-space:__SERVER_NAME__"
|
||
join_room "#announcements:__SERVER_NAME__"
|
||
join_room "#team-general:__SERVER_NAME__"
|
||
```
|
||
|
||
Wichtig ist hier die Abgrenzung zu “Claim Mappings”: Die offizielle MAS-OIDC-Doku beschreibt `claims_imports` für **`localpart`**, **`displayname`**, **`email`** und **`account_name`**. Daraus folgt: Claim-Mappings sind nützlich, wenn du später doch einen Upstream-OIDC-Provider ergänzen willst, aber sie lösen **nicht** die Mitgliedschaft in Spaces/Räumen. Für deinen Authentik-freien Invite-Flow ist das korrekte Modell also **Token + Synapse-Onboarding**, nicht “Claims → Rooms”. citeturn21search1turn9search4
|
||
|
||
## Sicherheit und Betriebsmodell
|
||
|
||
Die sicherste Konfiguration für dein Szenario ist nicht “offene Registrierung mit etwas Deko”, sondern **echte Invite-only-Registrierung**. Das bedeutet konkret: `password_registration_enabled: true`, `registration_token_required: true` und nur dann `password_registration_email_required: false`, wenn du bewusst auf Token-Schutz, kurze Laufzeiten, Single-Use und gute Missbrauchsbarrieren setzt. Genau davor warnt die ESS-Doku ausdrücklich: Ein öffentlich föderierender Server ohne Mailpflicht **und** ohne starke Restriktionen wird missbraucht. citeturn2view4turn20view0turn1view2
|
||
|
||
Ich würde die Sicherheitsprofile so priorisieren:
|
||
|
||
| Profil | Empfohlene Settings | Wann sinnvoll | Kommentar |
|
||
|---|---|---|---|
|
||
| Striktes Invite-only | `password_registration_enabled: true`, `registration_token_required: true`, `password_registration_email_required: false`, `usage_limit: 1`, kurze Expiry | Kleine Community, kuratierte Benutzerbasis | Beste Mischung aus UX und Missbrauchsschutz citeturn2view4turn12view0turn37view2 |
|
||
| Invite-only mit Recovery | Wie oben, aber `password_recovery_enabled: true` und SMTP voll konfiguriert | Wenn Nutzer Self-Service-Passwort-Reset brauchen | Erhöht Komfort, verlangt sauberen Mailbetrieb citeturn2view4turn2view5 |
|
||
| Halböffentlich | Tokenpflicht + CAPTCHA + E-Mail-Allowlist + restriktivere Rate-Limits | Wenn Einladungen wiederverwendbar oder breiter verteilt werden | Mehr Schutz gegen Bot-/Spam-Missbrauch citeturn3view2turn20view0turn3view1 |
|
||
|
||
Zu den wichtigsten Härtungsmaßnahmen gehören: **Single-Use (`usage_limit: 1`)**, **kurze Gültigkeit**, **Rate Limits für Registrierungen**, **optional CAPTCHA**, **optional Domain-Allowlist für E-Mails**, **Banned IP/User-Agent-Regeln** und ein **interner oder streng geschützter MAS-Admin-API-Zugang**. MAS dokumentiert all diese Stellhebel explizit: `policy.data.registration.*`, `emails.allowed_addresses`, `requester.banned_ips`, CAPTCHA-Provider und `rate_limiting.registration`. citeturn20view0turn3view1turn3view2turn2view5
|
||
|
||
Für die Raum-/Space-Seite gilt zusätzlich: Halte sensible Onboarding-Räume **invite-only** und vermische “öffentlich sichtbare Discovery” nicht mit einem privaten Invite-Modell. Die Matrix-/Synapse-Dokumentation empfiehlt bei privaten Setups ausdrücklich, die Sichtbarkeit öffentlicher Räume restriktiv zu halten; Synapse hat die Standardregeln für Room-Directory-Publikation in neueren Versionen auch bewusst verschärft. citeturn17search6turn35view0
|
||
|
||
Ein subtiler, aber wichtiger Punkt ist die Trennung von **MAS Admin** und **Synapse Admin**. Offiziell ist `urn:mas:admin` für automatisierte `client_credentials`-Tools dokumentiert. Für `urn:synapse:admin:*` beschreibt die Scope-Referenz dagegen ein benutzergebundenes Modell; außerdem braucht Synapse-Admin zusätzlich die Matrix-API-Scopes. Daraus folgt als belastbare Betriebsregel: **Reine Maschinenautomation sollte primär MAS Admin API nutzen; Synapse-Admin-Aktionen entweder über Auto-Join, über ein Synapse-Modul oder über einen echten Service-User mit benutzergebundenem Admin-Token**. citeturn20view1turn32search0turn32search1
|
||
|
||
## Tests, Rollout und Recovery
|
||
|
||
Der Rollout sollte nicht mit echten Einladungen beginnen, sondern mit einem **vollständigen Dry-Run auf Staging oder einem Testkonto**. Zuerst reconcile-st du Flux, dann prüfst du das HelmRelease und die gerenderte MAS-Konfiguration. Danach testest du die MAS Admin API, erzeugst ein kurzes Einmal-Token, registrierst einen neuen Testnutzer, überprüfst `times_used`/`valid`, kontrollierst die Raum-/Space-Mitgliedschaften und widerrufst übrige Tokens. Damit deckst du sowohl die ESS-/MAS-Seite als auch die Synapse-Onboarding-Seite einmal komplett ab. citeturn20view1turn12view0turn16view0turn27view0
|
||
|
||
Die fachliche Checkliste dafür ist kurz, aber strikt:
|
||
|
||
- **Konfiguration vorhanden:** `matrixAuthenticationService.additional` und `synapse.additional` sind im gerenderten Config-Output sichtbar. Wegen des offenen ESS-Issues zu `additional.*` solltest du das aktiv prüfen. citeturn31view1turn38view0
|
||
- **MAS Admin API erreichbar:** `/api/spec.json` bzw. das Anlegen eines Tokens funktioniert mit `urn:mas:admin`. citeturn20view1turn10view0
|
||
- **Token-Lebenszyklus korrekt:** neues Token erscheint als `valid`, nach Nutzung steigt `times_used`, bei `usage_limit: 1` ist es anschließend nicht mehr gültig. citeturn12view0turn37view2
|
||
- **Onboarding korrekt:** Testnutzer landet im Haupt-Space, in Welcome/Announcements und – falls aktiviert – in weiteren Zielräumen. citeturn16view0turn27view0
|
||
- **Missbrauchsschutz greift:** Ablaufzeit, CAPTCHA, Rate-Limits und Policy-Regeln sind aktiv. citeturn3view1turn3view2turn20view0
|
||
|
||
Die wichtigsten Failure Modes und ihre Recovery-Schritte sind diese:
|
||
|
||
| Symptom | Wahrscheinliche Ursache | Recovery |
|
||
|---|---|---|
|
||
| Registrierung fordert trotzdem E-Mail-Verifikation | `password_registration_email_required` ist noch `true` oder falsches MAS-Configfile aktiv | Gerendertes MAS-Configfile prüfen, Werte korrigieren, neu deployen citeturn2view4turn1view2turn38view0 |
|
||
| `403` bei Token-Erzeugung | OAuth-Client nicht in `policy.data.admin_clients` oder falscher Scope | Client-/Policy-Fragment korrigieren, neuen Access Token holen citeturn20view1turn23view3 |
|
||
| `404` auf `/api/admin/v1/...` | `adminapi` nicht in `http.listeners.resources` aktiviert | Listener-Block korrigieren und Redeploy citeturn20view1 |
|
||
| User landet nicht im Space | Space existiert nicht, Space wurde nicht separat angelegt, oder `auto_join_mxid_localpart`-User kann nicht einladen | Space vorab anlegen, `system`-User joinen lassen, Rechte prüfen; Spaces werden nicht automatisch erstellt citeturn16view0turn16view1turn16view2 |
|
||
| Feingranulare Raumzuweisung klappt nicht | Du versuchst Membership über Claim-Mapping zu lösen | Membership über Auto-Join, Provisioner oder Synapse-Modul abbilden citeturn21search1turn34view0 |
|
||
| “Groups” lassen sich nicht mehr sinnvoll zuweisen | Groups/Communities sind im aktuellen Synapse-Modell entfernt | Auf Spaces + Rooms umstellen citeturn17search0turn17search2 |
|
||
|
||
Wenn ein Invite-Token versehentlich verschickt wurde, ist der saubere Recovery-Schritt **nicht** das HelmRelease zu verändern, sondern das Token per MAS Admin API zu **revoken** und bei Bedarf ein neues zu erzeugen. Wenn ein Benutzer zwar registriert wurde, aber Raumzuweisungen fehlen, ist die schnellste operative Korrektur ein nachgelagerter Join über den Synapse-Admin-Endpunkt. Wenn ein Synapse-Modul dafür verantwortlich ist, beachte die Worker-Eigenschaft von `on_user_registration`. citeturn13view0turn27view0turn35view0
|
||
|
||
## Quellenbasis und offene Punkte
|
||
|
||
Die belastbarsten Primärquellen für dieses Design sind die offiziellen Dokumentationen zu ESS, MAS, Synapse, Element Web und LiveKit. Für den Vergleichspfad “Authentik Invitations” ist die offizielle Authentik-Doku hinzugezogen worden.
|
||
|
||
Die Priorisierung war dabei:
|
||
|
||
- ESS Advanced-Doku zur Einbindung zusätzlicher Konfigurationsdateien für MAS, Synapse, Element Web und MatrixRTC. citeturn1view2
|
||
- ESS Chart-Values zur Bestätigung, dass `matrixAuthenticationService.additional`/`synapse.additional`/`elementWeb.additional` offizielle Wertepfade sind. citeturn31view1turn31view4
|
||
- MAS-Konfigurationsreferenz für `account.*`, `captcha`, `policy`, `rate_limiting`, `email`, `clients`, `admin_clients` und HTTP-Listener. citeturn2view4turn20view0turn22view0turn23view0
|
||
- MAS Admin API und OpenAPI-Schema für Token-Erzeugung, Update, Revoke, Validitätsbegriff und Automationsmodell. citeturn20view1turn12view0turn13view0turn37view1turn37view2
|
||
- MAS Scope- und Access-Token-Doku für den Unterschied zwischen `urn:mas:admin` und benutzergebundenem Synapse-Admin-Zugriff. citeturn32search0turn32search1
|
||
- Synapse-Konfigurationsmanual für `auto_join_rooms`, Space-Aliasse, `autocreate_auto_join_rooms` und `auto_join_mxid_localpart`. citeturn16view0turn16view1turn16view2
|
||
- Synapse-Admin- und Modul-Doku für Raumzuweisung per API und Callback-Punkte `on_user_registration` / `on_user_login`. citeturn27view0turn34view0turn35view0
|
||
- Element-Web-Konfiguration für Welcome-/Branding-/OIDC-nahe UX-Optionen. citeturn24search0
|
||
- LiveKit-/Element-Call-Doku als optionale, vom Registrierungsfluss getrennte RTC-Schicht. citeturn25search0turn25search13
|
||
- Authentik-Invitationsdoku nur für den Vergleich innerhalb der Optionen-Tabelle. citeturn36search0turn36search1
|
||
|
||
Offen beziehungsweise bewusst konservativ behandelt bleiben drei Punkte. Erstens beschreibt die offizielle MAS-Dokumentation nach dem hier ausgewerteten Stand **keinen stabil dokumentierten Invite-Link-Parameter**, mit dem ein Registration Token sicher per URL in die MAS-UI vorbefüllt wird; deshalb empfehle ich eine eigene Landing-Page oder Token-Übermittlung im Mailtext. Zweitens ist **client_credentials** sauber für die **MAS Admin API** dokumentiert, nicht aber als Standardmuster für **Synapse-Admin-Automation**; für Synapse-Onboarding ist deshalb `auto_join_rooms` oder ein user-/modulbasierter Pfad robuster. Drittens gibt es aktuell ein offenes ESS-Helm-Issue zu `matrixAuthenticationService.additional.*`; deshalb solltest du das gerenderte MAS-Configfile im Deployment immer verifizieren, bevor du produktive Einladungen versendest. citeturn20view1turn32search0turn38view0
|
||
|