Compare commits

...
96 Commits
Author SHA1 Message Date
Thore Cimbal 09aaf1b2b5 docs: Document age key recovery from an already-running cluster
install.md only covered generating a brand-new key during fresh setup.
Added the actual recovery path (retrieve the existing key from the
sops-age secret) - what today's session actually needed after the Mac
reinstall. Also flags the age key's single-backup-location weakness,
tracked in issue #20.
2026-07-28 12:00:00 +00:00
Thore Cimbal 337dbe85ac docs: Migrate backlog to Gitea issues, remove stale completed items
Created issues #11-#31 for the remaining backlog (VP9 retry, ThreadNet-Web
build bug, MAS template link, WAF, and 17 security/infra hardening items),
plus #13 for the previously-noted MAS custom-template idea. Removed/marked
done the stale checklist items that no longer reflected reality (Authentik
Stage 2/E2E-test/invite-links, Hetzner firewall, SSH hardening - all
already completed weeks ago). TASKS.md's detailed backlog section now
points to Gitea issues as the single source of truth instead of
duplicating tracking in two places.
2026-07-28 12:00:00 +00:00
Thore Cimbal e462980dac docs: Document simulcast middle layer and H.264 codec switch for calls 2026-07-28 12:00:00 +00:00
Thore Cimbal 79db6a8e49 feat: Try H.264 codec for calls instead of VP8
H.264 uses classic simulcast (like VP8), not SVC, so it's compatible
with the fork's simulcast-shaped publish options without needing a
code fix - unlike VP9/AV1 (see the 2026-07-28 incident notes). Also
tends to be hardware-accelerated on more devices, notably iOS/Safari.
Server-side: LiveKit has no codec restriction configured, supports
H.264 by default.
2026-07-28 12:00:00 +00:00
Thore Cimbal 25d1742d61 fix: Add 720p simulcast middle layer for smoother video quality
User reported visible artifacts/blockiness during calls. Root cause:
without an explicit simulcast_layers config, the fork defaulted to
only [180p, 360p] fallback layers below the 1440p top layer - any
minor network hiccup caused a hard cliff straight to blocky 360p
instead of a graceful step-down. Added a 720p middle rung.

VP9 retry deferred: LiveKit uses SVC for vp9/av1 instead of classic
simulcast, and the fork's buildPublishOptions() always builds
simulcast-shaped layers regardless of codec - likely the real reason
forcing vp9 broke calls entirely. Needs a code fix before retrying.
2026-07-28 12:00:00 +00:00
Thore Cimbal 736c39a413 docs: Document NetworkPolicies rollout (issue #10), incidents, OOM fix, and identity cleanup 2026-07-28 12:00:00 +00:00
Thore Cimbal 22a0823b7e fix: Raise matrixRTC authorisation service memory limit (20Mi -> 128Mi)
Chart default of 20Mi request+limit OOM-killed the service today after
~74 days of uptime (3rd OOM over its lifetime), causing intermittent
call setup failures unrelated to the NetworkPolicy work happening in
parallel. 20Mi is razor-thin for a long-running Go service; bumping to
a still-modest 64Mi/128Mi request/limit.
2026-07-28 12:00:00 +00:00
Thore Cimbal 3054037480 fix: Allow synapse-main to reach MAS's port 8080 for token introspection
Second port-config incident from this rollout: synapse calls MAS's
oauth2/introspect endpoint on port 8080 (not 8081 as assumed) for
every single authenticated request (sync, pushrules, capabilities,
etc). The rule only allowed kube-system (Traefik) on 8080, so every
authenticated API call failed with 503 "Unable to introspect the
access token" - all clients showed a lost connection. Patched live
immediately, this commit brings Git back in sync with that patch.
2026-07-28 12:00:00 +00:00
Thore Cimbal 8f1d39b7a8 feat: Default-deny ingress NetworkPolicy for matrix namespace
Ingress-only default-deny plus per-component allow rules, using named
container ports throughout (not Service ports) after the authentik
port-mismatch incident. Traefik -> element-web/element-admin/haproxy
(fronts both matrix.axion1337.chat and well-known)/MAS; synapse <-
haproxy+MAS; postgres <- synapse+MAS; RTC signalling via Traefik, RTC
media NodePorts left open to the internet by design; monitoring scrape
allows for synapse/postgres/rtc-sfu metrics; ACME solver allow. coturn
(hostNetwork) needs nothing - NetworkPolicy doesn't apply to it. Part
of issue #10.
2026-07-28 12:00:00 +00:00
Thore Cimbal edf224e450 fix: Correct authentik-server NetworkPolicy ports (container port, not Service port)
Caused a live 502 immediately after deploy: the rule allowed 80/443
(the Service's external ports), but NetworkPolicy filters on the pod's
actual container port after kube-proxy's DNAT - authentik-server's
Service maps 80->9000 and 443->9443. Confirmed root cause by suspending
Flux reconciliation (it was silently re-applying my manual test
deletions) and testing with the policies truly absent.
2026-07-28 12:00:00 +00:00
Thore Cimbal b32920c48f feat: Default-deny ingress NetworkPolicy for authentik namespace
Ingress-only default-deny (egress untouched) plus explicit allows:
Traefik (kube-system) + MAS (matrix ns) -> authentik-server on 80/443,
and Traefik -> cert-manager's ACME HTTP-01 solver pods on 8089.
authentik-postgresql already has its own Bitnami-chart-managed policy,
left alone. Part of issue #10.
2026-07-28 12:00:00 +00:00
Thore Cimbal cb2ffa6a08 docs: Update status summary, add session log, sync element-customization guide with wiki 2026-07-27 12:00:00 +00:00
Thore Cimbal 7d352fbf20 docs: Document VP9 incident and v0.2.1 fix for Element Call fork 2026-07-27 12:00:00 +00:00
Thore Cimbal 1bb1bc9610 fix: Retry Element Call fork without forced VP9 codec preference
v0.2.0 forced video_codec: vp9, which broke calls entirely (no audio/
video transmitted, despite server logs showing the codec regression
fallback to VP8 working). Root cause not fully confirmed. This build
keeps the 1440p/60fps/bitrate defaults but leaves video_codec unset
(defaults to vp8) to isolate whether the codec preference itself was
the trigger.
2026-07-27 12:00:00 +00:00
Thore Cimbal 37aea0254b revert: Roll back Element Call fork image - calls broken (no audio/video)
User reports calls transmit neither video nor audio with
v0.2.0-elementcall-mediaquality. Rolling back to known-good v0.1.0
while investigating; likely the video_codec: vp9 preference, not the
resolution/framerate bump.
2026-07-27 12:00:00 +00:00
Thore Cimbal fad91b6a05 docs: Document Element Call fork completion and threadnet-web build bug 2026-07-27 12:00:00 +00:00
Thore Cimbal af73cf770b feat: Element Call fork with 1440p/60fps + VP9 defaults
Bumps elementWeb image to a build with the embedded Element Call
widget replaced by our own fork (rohana.axion1337.de/sorb/threadnet-call,
based on emmick4/element-call:livekit / upstream PR #3736), raising
default call quality to up to 1440p/60fps camera (VP9) and 1440p/30fps
screen share. These are seeded defaults users can still adjust in
Settings, not hard-enforced caps.
2026-07-27 12:00:00 +00:00
Thore Cimbal 09e4225de5 fix: Replace broken netstat liveness probe on coturn with tcpSocket check
coturn started up cleanly every time (all TCP/UDP listeners on 3478/5349
opened fine per logs) but was killed every ~10s because the liveness
probe ran "netstat -uln", which doesn't exist in coturn/coturn:latest
(sh: netstat: not found) - 36000+ restarts over 88 days for a healthy
process. Switched to a tcpSocket probe against the TCP listener coturn
already opens on 3478, no in-container tooling required.
2026-07-27 12:00:00 +00:00
Thore Cimbal c0cb864ca2 docs: Add security.html with Passkey/TOTP self-service setup links
Neither Element Web's config.json nor MAS's branding config expose a
generic custom-link mechanism, and /if/user/ is blocked for type=external
Matrix accounts, so the working Authentik MFA setup flows had nowhere to
be discoverable. Added a docs/setup/security.html page, following the
exact existing pattern (ConfigMap -> initContainer cp -> nginx), linked
from index.html's Support section.
2026-07-27 12:00:00 +00:00
Thore Cimbal 235306a840 fix: Set Brand.default_application so "/" doesn't dead-end at /if/user/
Several dead ends (TOTP/WebAuthn setup after an anonymous visit bounces
through login and lands on "/") trace back to Brand.default_application
being unset, which falls back to /if/user/ - blocked for type=external
Matrix accounts. Set it to the matrix Application. Only changes the bare
"/" fallback; explicit URLs like /if/admin/ are unaffected.
2026-07-27 12:00:00 +00:00
Thore Cimbal e9b24a6d1f fix: Append redirect stage to TOTP/WebAuthn self-service setup flows
2FA is intentionally optional (not_configured_action=skip on the login
flow's validate stage). Users who opt in use the built-in single-stage
default-authenticator-totp-setup / -webauthn-setup flows directly, since
/if/user/ is blocked for type=external Matrix accounts. Both flows had
nothing after the setup stage, so completing enrollment fell back to
that same blocked dashboard. Appended the shared redirect stage.
2026-07-27 12:00:00 +00:00
Thore Cimbal 0274f9316c fix: Build matrix-recovery flow and link it as the login page's recovery flow
matrix-recovery existed but had zero stage bindings, and the real login
flow (default-authentication-flow, MAS's authentication_flow) never
linked to it, so users had no working "forgot password" path. Reused
the same default-recovery-* stages the built-in default-recovery-flow
already uses successfully, added our redirect stage at the end, and
set default-authentication-identification.recovery_flow accordingly.
2026-07-27 12:00:00 +00:00
Thore Cimbal 027f567c8b fix: Add redirect stage so matrix-invitation ends up at Element, not authentik dashboard
After the login stage, the flow had no destination, so it fell back to
authentik's own /if/user/ interface - which refuses type=external users
(the correct type for Matrix-only accounts), showing "Die Oberflaeche
kann nur von internen Nutzern geoeffnet werden". Added a static redirect
to https://axion1337.chat as the final stage.
2026-07-27 12:00:00 +00:00
Thore Cimbal d2bcd90291 fix: Clear erroneous validation_policies on matrix-invitation-prompt
The prompt stage had 16 unrelated system policies bound (OOBE, user
settings, recovery, etc.), likely from a "select all" slip while
configuring it manually. They crash when evaluated in an anonymous
enrollment context (AnonymousUser has no group_attributes, etc.),
surfacing as opaque errors after form submit. Cleared live and via
the blueprint so re-application doesn't reintroduce them.
2026-07-27 12:00:00 +00:00
Thore Cimbal 80714fe901 fix: Repair matrix-invitation enrollment flow via Authentik Blueprint
The matrix-invitation flow only had Invite+Prompt stage bindings, both
at order=0 (undefined order), missing the Write/Password/Login stages
entirely — invited users were never written to the DB. Applied the fix
live in-cluster (mirroring the working matrix-enrollment stage chain),
and captured it as an Authentik Blueprint (ConfigMap, mounted via
blueprints.configMaps) so the flow state is reproducible via GitOps
instead of manual admin-UI clicks.
2026-07-27 12:00:00 +00:00
Scrublord MacBadandClaude Haiku 4.5 5bbb03bc52 refactor: Organize troubleshooting docs into docs/troubleshooting/
Move all Authentik troubleshooting guides into dedicated subdirectory:
- DIAGNOSTIK-AUTHENTIK-FLOW.md
- AUTHENTIK-FIX-TEMPLATE.md
- AUTHENTIK-INVITATION-FLOW-FIX.md
- AUTHENTIK-CREATE-INVITATION-FLOW.md

Add README.md with:
- Quick reference guide for each document
- Scenario-based navigation
- Known issues tracking
- Tips and best practices

This keeps the root directory clean and organizes related guides together.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-05-18 13:03:33 +02:00
Scrublord MacBadandClaude Haiku 4.5 af13688993 docs: Add step-by-step guide to create Authentik Invitation Flow
Root cause analysis:
- Only one matrix-enrollment flow exists
- Used for both standard signup AND invitations
- Causes flow conflicts: 'Found existing plan for other flow'
- Error when Klaus enrollment attempted: 'kein ausstehender benutzer Anfrage wurde verweigert'

Solution:
- Create separate matrix-invitation flow
- Use for invitation links only
- Prevents conflicts and allows proper field capture (email is mandatory)

This guide provides:
1. Step-by-step flow creation (5 stages)
2. Field configuration for Prompt Stage
3. Binding setup for each stage
4. Testing procedure with invitation link
5. Troubleshooting checklist

Related issues: Klaus enrollment failure, Boje enrollment failure

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-05-18 12:52:35 +02:00
Scrublord MacBadandClaude Haiku 4.5 f70e77127e docs: Add Authentik Invitation Flow repair guide
The Invitation Flow is not properly configured:
- Only username is captured during invitations
- Email field is missing (required for OIDC token generation)
- 'Fehler fehlende Rechte' error indicates incomplete user data

This guide walks through:
1. Diagnosing the current Invitation Flow configuration
2. Creating/repairing the Prompt Stage with email field
3. Adding the Prompt Stage to the Invitation Flow
4. Testing the complete enrollment process

Related to: User Boje enrollment failure via invitation link

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-05-18 12:36:56 +02:00
Scrublord MacBadandClaude Haiku 4.5 f658ce2980 docs: Add Authentik enrollment flow diagnostics and repair guides
- DIAGNOSTIK-AUTHENTIK-FLOW.md: Comprehensive troubleshooting guide
- AUTHENTIK-FIX-TEMPLATE.md: Repair instructions for common issues

These guides help debug why Boje user was created in Authentik but not synchronized to Matrix.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-05-18 12:24:49 +02:00
Scrublord MacBad b29c7516d4 Update: Authentik 2026.2.3
Auto-Deploy on Push / verify-and-notify (push) Has been cancelled
2026-05-15 15:05:06 +02:00
Scrublord MacBadandClaude Haiku 4.5 c32f951716 Update TASKS.md: Mark 3 CRITICAL tasks complete
- Authentik Stage 2: OIDC integration verified (login working)
- Hetzner Cloud Firewall: Configured & optimized
- SSH Hardening: Key-only auth, no root, rate limiting verified

Updated status: 9 completed, 0 in-progress, 11+ pending
All 3 CRITICAL security tasks done. Next: Database Backups

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-05-15 13:48:00 +02:00
Scrublord MacBadandClaude Haiku 4.5 cdfbf7de98 Enable Authentik OIDC integration in MAS
Auto-Deploy on Push / verify-and-notify (push) Has been cancelled
- Add upstream_oauth2_config with Authentik provider credentials
- Configure OIDC with client_id and client_secret from Authentik
- Disable local password authentication (OIDC-only login)
- Set claims mapping: subject, localpart, displayname, email

This enables users to login via Authentik OIDC provider with email
and username claims properly mapped for Matrix user provisioning.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-05-15 11:05:52 +02:00
Scrublord MacBad b1247b4720 backup old wiki 2026-05-14 23:34:59 +02:00
Scrublord MacBadandClaude Haiku 4.5 6bcbe9cc9e Add Gitea Actions workflows for CI/CD automation
Auto-Deploy on Push / verify-and-notify (push) Has been cancelled
- deploy-on-push.yml: Verify YAML, check SOPS encryption, notify on deployments
- milestone-release.yml: Auto-create releases on milestone tags

Triggers:
- deploy-on-push: On any push to main (apps/clusters changes)
- milestone-release: On git tag m*-*-complete

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-05-14 23:29:50 +02:00
Scrublord MacBadandClaude Haiku 4.5 b8da70b14b Add status summary and prioritized next steps to TASKS.md
Add at top of TASKS.md:
- Status Summary table (quick view of progress)
- Priority distribution (CRITICAL/HIGH/MEDIUM/LOW)
- Next Steps section (this week, 1-2 weeks)
- Each task includes: description, effort estimate, blocking factors

Makes task list immediately actionable and progress trackable.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-05-14 23:08:00 +02:00
Scrublord MacBadandClaude Haiku 4.5 4cf6702f85 Add comprehensive task list and VSCode todo-tree integration
Add docs/TASKS.md:
- Completed tasks (6): K3S, Flux, ESS, Themes, Desktop Scripts, Monitoring, TURN
- In Progress: Authentik Stage 2 (pending manual config)
- Backlog (15+): Element Call Fork, PostgreSQL migration, NetworkPolicies, etc.
- Security hardening: Host/Cluster/App layer recommendations
- Milestones: Track progress from M1 (Basic) to M7 (Enterprise)

Enhance devcontainer.json:
- Add gruntfuggly.todo-tree extension for task tree view
- Add ms-vscode.makefile-tools for build automation
- Add GitHub.copilot for development assistance
- Configure todo-tree to highlight TASKS.md and deployment guides

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-05-14 23:05:33 +02:00
Scrublord MacBadandClaude Haiku 4.5 aee9a34369 Add 5 detailed deployment guides to docs/deployment-guides/
- 01-turn-server-setup.md: TURN Server architecture, deployment, verification
- 02-authentik-identity-provider.md: Two-stage OIDC integration (Stage 1 & Stage 2)
- 03-monitoring-integration.md: Alloy, Prometheus, Loki integration with Selendis
- 04-element-customization.md: Custom themes (7), desktop setup scripts, admin panel
- 05-room-policies.md: Message retention, room publication, auto-join policies

All guides include troubleshooting, configuration examples, and best practices.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-05-14 22:47:38 +02:00
Scrublord MacBadandClaude Haiku 4.5 e6ef29c1e9 Add deployment documentation structure & overview
Create docs/deployment-guides/ directory with comprehensive README covering:
- Deployment order for all 5 major features
- Architecture overview
- Critical values & configuration
- Links to detailed guides

Full documentation files:
- 01-turn-server-setup.md
- 02-authentik-identity-provider.md
- 03-monitoring-integration.md
- 04-element-customization.md
- 05-room-policies.md

To be added in follow-up commits.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-05-14 22:46:00 +02:00
Scrublord MacBadandClaude Haiku 4.5 857c3965ef Add devcontainer for cross-platform development
- Dockerfile with all required tools (kubectl, flux, helm, sops, age, etc.)
- devcontainer.json with VSCode config and extensions
- postCreateCommand.sh for setup verification
- Comprehensive README with setup instructions for macOS, Windows/WSL2, Linux
- Automatic mounts for kubeconfig, SSH keys, age encryption keys
- SOPS_AGE_KEY_FILE and KUBECONFIG pre-configured

Enables development on Windows, macOS, and Linux with consistent environment.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-05-14 22:27:52 +02:00
Scrublord MacBad d37a65f4fa fix element web image version missing 2026-05-14 22:07:34 +02:00
Scrublord MacBad e6abd0fb3f feat: reinstate roomlist fork after config map fix 2026-05-14 21:59:34 +02:00
Scrublord MacBadandClaude Haiku 4.5 d3362180ea fix: Complete JSON structure in element-values.yaml
The config.json was truncated and had unclosed brackets. This prevented
Helm from properly merging the ElementWeb configuration, so the custom themes
were never loaded into the cluster.

This fix:
- Closes the unclosed JSON brackets
- Validates the full JSON structure
- Removes duplicate/extra closing brackets
- Ensures all 6 custom themes are properly included

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-05-14 21:55:22 +02:00
Scrublord MacBadandClaude Haiku 4.5 5f50a60071 feat: Automate ConfigMap checksum updates via Git pre-commit hook
- Add pre-commit hook (scripts/hooks/pre-commit) that automatically calculates
  MD5 checksums of ConfigMaps (element-values.yaml, synapse-values.yaml)
- Update annotations in kustomization.yaml to trigger Flux CD HelmRelease syncs
- Add install script (scripts/install-hooks.sh) for easy hook setup
- Add comprehensive documentation (docs/ops-configmap-sync.md) explaining:
  * Why Flux doesn't auto-detect ConfigMap changes
  * How the checksum-based workaround works
  * How to install and use the hook
  * Troubleshooting and manual sync procedures
- Update README.md with post-clone hook installation step

This solves the issue where Flux CD doesn't automatically re-deploy when external
ConfigMaps are modified. Users no longer need manual checksum updates.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-05-14 21:43:01 +02:00
Scrublord MacBad 174e9721ed Revert "test: verify pre-commit hook updates checksums"
This reverts commit 5f14376bdb.
2026-05-14 21:42:25 +02:00
Scrublord MacBad 5f14376bdb test: verify pre-commit hook updates checksums 2026-05-14 21:42:13 +02:00
Scrublord MacBadandClaude Haiku 4.5 81a30e21b9 Fix: HelmRelease reconciliation on ConfigMap changes
- Reduce HelmRelease interval from 1h to 5m for faster sync
- Add checksum annotation to trigger reconciliation when element-values.yaml changes
- This ensures Flux CD re-deploys the chart when themes/config updates are made

To update the checksum after editing element-values.yaml:
  md5sum apps/production/custom-configs/element-values.yaml
  # Update the value in apps/production/kustomization.yaml patches[0].patch

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-05-14 21:37:07 +02:00
sorb f81fda12d4 Update apps/production/element-server-suite.yaml 2026-05-13 14:48:08 +00:00
sorb fe0165a509 Update apps/production/element-server-suite.yaml
new docker image for elementWeb
2026-05-13 14:43:50 +00:00
Scrublord MacBad dc17158fe2 fix themes 2026-05-11 10:48:16 +02:00
Scrublord MacBad 0c81de057f fix config url element web 2026-05-11 10:43:35 +02:00
sorb aaa197fbb5 Feat: Lab roomslist v3 & new aXion Theme 2026-05-11 08:28:30 +00:00
Scrublord MacBad e0ed10598f Update Element Web to use custom fork image
- Registry: rohana.axion1337.de
- Repository: sorb/threadnet-web
- Tag: v0.1.0
2026-05-10 23:44:35 +02:00
sorb d39ce5a3a0 Feat: deactivate legacy calls 2026-05-10 19:43:54 +00:00
Scrublord MacBad 68bf4e213c Revert to ESS 26.4.0 - URL preview issues in 26.5.0 2026-05-08 00:39:20 +02:00
Scrublord MacBad 644dff9c48 Upgrade to ESS 26.5.0 with oembed support 2026-05-08 00:29:39 +02:00
Scrublord MacBad 8e0f244257 Add oembed configuration directly to synapse values 2026-05-08 00:20:45 +02:00
Scrublord MacBad b000c1b10b Fix oembed configuration format 2026-05-08 00:10:08 +02:00
Scrublord MacBad 24d0cd1201 Add media_repository and oembed configuration for URL previews 2026-05-08 00:06:27 +02:00
Scrublord MacBad 08a38fdfbb Revert to ESS 26.4.0 to test URL preview functionality 2026-05-07 23:58:15 +02:00
Scrublord MacBad eda648eaa7 Add media_repository and thumbnail_generator workers for URL preview support 2026-05-07 23:45:37 +02:00
Scrublord MacBad 575ac0112f fix auto join 2026-05-07 23:23:33 +02:00
Scrublord MacBad 878687f334 test deactivate auto join in synape prior ess upgrade due to invalid room id 2026-05-07 23:11:37 +02:00
Scrublord MacBad 7b4def0fe7 Upgrade ESS 26.5.0 2026-05-07 22:54:05 +02:00
Scrublord MacBad 7b7643cf6e Feat: no new roomlist 2026-05-07 21:26:29 +02:00
Scrublord MacBad a7959474c5 Feat: new room list #32677 2026-05-07 21:16:50 +02:00
Scrublord MacBadandClaude Haiku 4.5 3c2a03e9c3 Deploy Authentik as Identity Provider for Matrix (Stage 1)
Add Authentik v2026.x with embedded PostgreSQL in new namespace. Includes HelmRelease with valuesFrom injection for encrypted credentials, cert-manager Certificate for auth.axion1337.chat, and Traefik IngressRoute. Authentik serves as OIDC provider for MAS; registration via invitation links only.

DNS A-record setup and Authentik UI configuration (OIDC provider creation) are manual post-deployment steps.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-04-29 23:44:15 +02:00
Scrublord MacBadandClaude Haiku 4.5 8ff438bd24 Implement TURN server (coturn) for WebRTC video calls
Add coturn Deployment with hostNetwork mode and init container for secret substitution. Include SOPS-encrypted shared secret, TLS certificate for turn.axion1337.chat, and Synapse TURN configuration with proper relay URIs and credentials.

Resolves DTLS timeout issues in RTC video calls by providing media relay for clients behind NAT/Firewall.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-04-29 23:07:52 +02:00
Scrublord MacBadandClaude Haiku 4.5 01ad3f2d71 Fix HelmRepository API version
- Change from v1beta2 to v1 (v1beta2 is deprecated)
- Resolves dry-run failure in monitoring Kustomization

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-04-24 16:18:17 +02:00
Scrublord MacBadandClaude Haiku 4.5 34bffe40be Add monitoring stack: kube-state-metrics, node-exporter, and Alloy
- New monitoring namespace
- HelmRepositories: prometheus-community, grafana
- kube-state-metrics: Kubernetes state metrics
- prometheus-node-exporter: Host metrics DaemonSet
- Alloy: Multi-target scraper pushing to Selendis (10.0.0.3:9090 Prometheus, 10.0.0.3:3100 Loki)
  - Scrapes Flux controllers (flux-system:8080)
  - Scrapes kube-state-metrics (monitoring:8080)
  - Scrapes node-exporter DaemonSet (monitoring:9100)
  - Scrapes Synapse metrics (matrix:9000)
  - Pushes pod logs to Loki
- Monitoring Kustomization added to flux-system

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-04-24 16:14:17 +02:00
Scrublord MacBadandClaude Haiku 4.5 1a1c8ee2c6 Simplify room_list_publication_rules structure
- Remove unnecessary fields, use minimal rule set
- Allow all users to publish rooms

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-04-24 15:31:30 +02:00
Scrublord MacBadandClaude Haiku 4.5 78454ba2ef Add room_list_publication_rules to allow room publishing
- Allow all users to publish public rooms in room list
- Fixes 403 'Not allowed to publish room' error
- Applies to rooms with join_rule: public

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-04-24 15:11:32 +02:00
Scrublord MacBadandClaude Haiku 4.5 c98997f75e Fix auto_join_rooms configuration property name
- Rename property from auto-join-rooms to auto_join (underscore instead of dash)
- Keep YAML structure with embedded config

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-04-24 14:40:45 +02:00
Scrublord MacBadandClaude Haiku 4.5 a323c6bb65 Add auto-join rooms configuration for onboarding
- New users automatically join !NkFTshKWJMgdTqTbTh:axion1337.chat (onboarding room)
- Federated guests do not auto-join (auto_join_rooms_for_guests: false)

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-04-24 14:15:45 +02:00
Scrublord MacBadandClaude Haiku 4.5 47625c05b6 Rename retention-config to retention in synapse additional config
- Use simpler property name 'retention' instead of 'retention-config'
- Helm Chart schema may not allow 'retention-config' naming convention

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-04-24 13:53:25 +02:00
Scrublord MacBadandClaude Haiku 4.5 42483f32b5 Fix synapse retention-config placement in values
- Move retention-config from top-level synapse to additional section
- Helm Chart schema requires custom configs under synapse.additional
- Clean up comments (keep inline comments minimal)

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-04-24 13:48:57 +02:00
sorb ee6cdb7982 feat retention-config 2026-04-24 09:27:42 +00:00
Scrublord MacBadandClaude Haiku 4.5 4a95350c72 Add beautiful HTML landing page to element-web-docs setup
- Create index.html with styled setup guide and download links
- Update init-container to copy index.html to nginx
- Configure nginx to serve index.html as default for /docs/setup/
- Remove directory listing (autoindex), show proper HTML instead

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-04-23 18:52:33 +02:00
Scrublord MacBadandClaude Haiku 4.5 c85dd39eba Fix Element Web docs server routing and remove redundant nginx Ingress
- Remove duplicate nginx Ingress from element-web-docs-server.yaml (using Traefik instead)
- Traefik IngressRoute in apex-ingress.yaml now handles /docs/setup routing
- Deployment and Service remain unchanged for file serving

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-04-23 18:42:57 +02:00
Scrublord MacBad bdba4f3e34 fix: use init-container instead of postStart hook 2026-04-23 18:37:08 +02:00
Scrublord MacBad 80fa13b63f feat: separate nginx deployment to serve Element Desktop setup scripts 2026-04-23 18:36:13 +02:00
Scrublord MacBad cf8bc00613 fix: use init-container to copy docs from ConfigMap to nginx html 2026-04-23 18:34:45 +02:00
Scrublord MacBad 0c67a249fa fix: add element-web-docs-configmap to kustomization resources 2026-04-23 18:25:18 +02:00
Scrublord MacBad 4bc0e10631 fix: correct ConfigMap keys and mount structure for /docs/setup/ 2026-04-23 18:18:15 +02:00
Scrublord MacBad 3e07d6c613 fix: move element-web-docs ConfigMap to correct location 2026-04-23 18:17:04 +02:00
Scrublord MacBad 336f2aa1cb feat: serve Element Desktop setup scripts via Element Web at /docs/setup/ 2026-04-23 18:16:03 +02:00
Scrublord MacBad 659acdf601 docs: add Element Desktop setup scripts for Windows, macOS, and Linux 2026-04-23 16:42:16 +02:00
Scrublord MacBad 147ac079f7 feat: add configUrl for Element Desktop auto-loading 2026-04-23 16:33:06 +02:00
Scrublord MacBad 0efe91b1bd custom themes 2026-04-23 15:39:33 +02:00
Scrublord MacBad 6aff1f86d6 fix final mas fix 2026-04-23 15:25:44 +02:00
Scrublord MacBad bdca6d8840 fix mas 2026-04-23 14:01:48 +02:00
Scrublord MacBad b6e2c6f00b missing param 2026-04-23 13:53:24 +02:00
Scrublord MacBad 37149b195b fix mas? 2026-04-23 13:50:35 +02:00
Scrublord MacBad 13e597769c fix secret handling 2026-04-23 11:28:14 +02:00
Scrublord MacBad c17e8f528f fix mas? 2026-04-23 11:25:00 +02:00
Scrublord MacBad 9fdac55e25 fix custom themes syntax 2026-04-23 11:04:47 +02:00
Scrublord MacBad eb21dc9494 feat custom themes 2026-04-23 10:49:58 +02:00
65 changed files with 18848 additions and 37 deletions
Vendored
BIN
View File
Binary file not shown.
+56
View File
@@ -0,0 +1,56 @@
FROM debian:bookworm-slim
# Install base tools
RUN apt-get update && apt-get install -y --no-install-recommends \
curl \
wget \
git \
ca-certificates \
gnupg \
lsb-release \
apt-transport-https \
vim \
nano \
jq \
yq \
zsh \
sudo \
openssh-client \
&& rm -rf /var/lib/apt/lists/*
# Install kubectl
RUN curl -fsSLo /usr/share/keyrings/kubernetes-archive-keyring.gpg https://packages.cloud.google.com/apt/doc/apt-key.gpg && \
echo "deb [signed-by=/usr/share/keyrings/kubernetes-archive-keyring.gpg] https://apt.kubernetes.io/ kubernetes-xenial main" | tee /etc/apt/sources.list.d/kubernetes.list && \
apt-get update && apt-get install -y kubectl && \
rm -rf /var/lib/apt/lists/*
# Install Helm
RUN curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
# Install Flux CLI
RUN curl -s https://fluxcd.io/install.sh | bash
# Install sops
RUN SOPS_VERSION=$(curl -s https://api.github.com/repos/getsops/sops/releases/latest | grep tag_name | cut -d '"' -f 4) && \
curl -sL -o /usr/local/bin/sops https://github.com/getsops/sops/releases/download/${SOPS_VERSION}/sops-${SOPS_VERSION}.linux.amd64 && \
chmod +x /usr/local/bin/sops
# Install age
RUN apt-get update && apt-get install -y age && \
rm -rf /var/lib/apt/lists/*
# Install Docker CLI (for interacting with Docker daemon)
RUN curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg && \
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null && \
apt-get update && apt-get install -y docker-ce-cli && \
rm -rf /var/lib/apt/lists/*
# Create a non-root user 'vscode' for development
RUN useradd -m -s /bin/bash -G docker vscode && \
echo "vscode ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers.d/vscode
# Install oh-my-zsh for better shell experience
RUN su - vscode -c "sh -c '$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)' '' --unattended"
USER vscode
WORKDIR /workspace
+203
View File
@@ -0,0 +1,203 @@
# 🐳 DevContainer für ESS Community GitOps
Dieses DevContainer-Setup ermöglicht dir, auf **macOS, Windows und Linux** einheitlich zu entwickeln.
## 🚀 Schnelstart
### VSCode mit Remote Containers Extension
1. **VSCode Extension installieren:**
- Öffne VSCode → Extensions → Suche nach `Dev Containers` (Microsoft)
- Installiere sie
2. **GitOps Verzeichnis öffnen:**
```bash
cd "april mit Ansible/prod/gitops"
code .
```
3. **DevContainer starten:**
- Klick auf `><` Symbol unten links in VSCode
- Wähle `Reopen in Container`
- Warte, bis das Image gebaut wurde (~3-5 Min beim ersten Mal)
### Alternative: Docker + CLI
```bash
docker build -t ess-gitops .devcontainer
docker run -it --rm \
-v ~/.kube:/home/vscode/.kube \
-v ~/.ssh:/home/vscode/.ssh \
-v ~/.age:/home/vscode/.age \
-v /var/run/docker.sock:/var/run/docker.sock \
ess-gitops
```
## 📦 Enthaltene Tools
- **kubectl** - Kubernetes CLI
- **flux** - FluxCD GitOps Controller CLI
- **helm** - Kubernetes Package Manager
- **sops** - Secret Operations (Verschlüsselung)
- **age** - Modern File Encryption
- **docker** - Container CLI (über Host-Socket)
- **git** - Versionskontrolle
- **jq/yq** - JSON/YAML Processing
- **zsh + oh-my-zsh** - Shell mit Plugins
## 🔐 Wichtige Verzeichnis-Binds
Der Container mountet automatisch:
| Host | Container | Zweck |
|------|-----------|-------|
| `~/.kube` | `/home/vscode/.kube` | Kubernetes Config |
| `~/.ssh` | `/home/vscode/.ssh` | SSH Keys |
| `~/.age` | `/home/vscode/.age` | Age Encryption Keys |
| `/var/run/docker.sock` | `/var/run/docker.sock` | Docker Daemon (für `docker` Befehle) |
## ⚙️ Kubeconfig Einrichten
1. **Host-Machine (z.B. macOS):**
```bash
# Stelle sicher, dass ~/.kube/config existiert und den richtigen Cluster enthält
kubectl get nodes
```
2. **Im Container:**
```bash
kubectl get nodes # Sollte jetzt auch dein Cluster zeigen
kubectl config current-context
```
## 🔐 SOPS + Age Setup
Damit du Secrets bearbeiten kannst, brauchst du den privaten `age`-Key. Dieser ist in `.sops.yaml` konfiguriert.
### Schritt 1: Age-Key bereitstellen
```bash
# Host-Machine: Key-Datei erstellen
mkdir -p ~/.age
# Füge deinen privaten Key ein (Format: "age-secret-key-...")
echo "age-secret-key-xxx..." > ~/.age/keys.txt
chmod 600 ~/.age/keys.txt
```
### Schritt 2: Im Container konfigurieren
Der Container mounted `~/.age` automatisch. Setze die Umgebungsvariable:
```bash
# Im Container-Terminal (SOPS_AGE_KEY_FILE ist bereits automatisch gesetzt!)
# Jetzt kannst du Secrets bearbeiten (wird transparent ver-/entschlüsselt):
sops apps/production/custom-configs/mas-secrets.sops.yaml
```
### Schritt 3: VSCode Integration (optional)
Um die Umgebungsvariable beim Start zu setzen, nutze die `.devcontainer/devcontainer.json`:
```json
"remoteEnv": {
"KUBECONFIG": "/home/vscode/.kube/config",
"SOPS_AGE_KEY_FILE": "/home/vscode/.age/keys.txt"
}
```
### Wie es funktioniert
- `.sops.yaml` definiert, dass Secrets mit `age` verschlüsselt werden
- Beim Öffnen mit `sops <datei>` wird die Datei entschlüsselt → du editierst den plaintext in deinem Editor
- Beim Speichern wird alles wieder automatisch verschlüsselt
- **Wichtig:** Niemals den plaintext-Buffer commiten!
## 📝 Nach Container-Start: Git Hooks Installieren
Wichtig für die ConfigMap Auto-Sync (verhindert Merge-Konflikte):
```bash
./scripts/install-hooks.sh
```
Mehr Details: `docs/ops-configmap-sync.md`
## 📝 Nützliche Befehle
```bash
# Status des Deployments
kubectl get pods -n matrix
flux get helmreleases -A
# Secrets bearbeiten (mit verschlüsselung)
sops apps/production/custom-configs/mas-secrets.sops.yaml
# FluxCD Sync erzwingen
flux reconcile kustomization production-apps --with-source
# Zertifikate debuggen
kubectl get certificate -n matrix
kubectl describe certificate matrix-ingress -n matrix
# HelmRelease Status prüfen
flux describe helmrelease matrix-stack -n matrix
```
## 🛠️ Anpassungen für Windows/WSL2
Falls du Windows nutzt:
1. **Docker Desktop installieren** (mit WSL2 Backend)
2. **VSCode mit WSL Extension öffnen**
3. **Im WSL Terminal:**
```bash
cd /mnt/c/path/to/projekt
code .
```
4. Dann `Dev Containers: Reopen in Container`
Das funktioniert seamless, weil Docker Desktop unter WSL2 läuft.
## 🔧 Troubleshooting
### Problem: `SOPS_AGE_KEY_FILE not found`
**Lösung:** Key muss in `~/.age/keys.txt` auf der Host-Machine sein:
```bash
# Host
mkdir -p ~/.age
echo "your-age-private-key" > ~/.age/keys.txt
```
Der Container mountet `~/.age` automatisch → sollte dann funktionieren.
### Problem: `kubectl: connection refused`
**Lösung:** `~/.kube/config` muss auf Host vorhanden sein:
```bash
# Host
kubectl get nodes # Test, ob Zugriff existiert
# Dann Container neustarten
```
### Problem: `HelmChart is not ready: stat ... no such file or directory`
Siehe `README.md` → **Issue 1**. Kontrolliere:
- `HelmRepository` nutzt `type: oci`
- URL ist `oci://ghcr.io/element-hq/ess-helm`
### Problem: `values don't meet the specifications of the schema`
Siehe `README.md` → **Issue 2**. Häufige Fehler:
- `tls:` darf nicht im `ingress:` Block sein
- `serverName` muss auf Root-Ebene der `values` stehen
- Komponenten-Namen: `camelCase` (z.B. `elementWeb`, `matrixAuthenticationService`)
### Problem: Let's Encrypt `403 Order's status is processing`
Siehe `README.md` → **Issue 3**. Kurz:
- `wellKnownDelegation: enabled: false` setzen
- Oder `.well-known/matrix/server` manuell auf `elementWeb` weiterleiten
## 📚 Weitere Ressourcen
- [Dev Containers Docs](https://containers.dev)
- [FluxCD Dokumentation](https://fluxcd.io)
- [SOPS Anleitung](https://github.com/getsops/sops)
- **Projekt-README:** `README.md` (Architektur, Issues, Best Practices)
- **Setup-Docs:** `docs/setup/`
- **Install-Guide:** `docs/install.md`
+65
View File
@@ -0,0 +1,65 @@
{
"name": "ESS Community GitOps",
"build": {
"dockerfile": "Dockerfile",
"context": "."
},
"mounts": [
"source=${localEnv:HOME}/.kube,target=/home/vscode/.kube,type=bind,consistency=cached",
"source=${localEnv:HOME}/.ssh,target=/home/vscode/.ssh,type=bind,consistency=cached",
"source=${localEnv:HOME}/.age,target=/home/vscode/.age,type=bind,consistency=cached",
"source=/var/run/docker.sock,target=/var/run/docker.sock,type=bind"
],
"remoteUser": "vscode",
"features": {
"ghcr.io/devcontainers/features/git:1": {},
"ghcr.io/devcontainers/features/github-cli:1": {}
},
"remoteEnv": {
"KUBECONFIG": "/home/vscode/.kube/config",
"SOPS_AGE_KEY_FILE": "/home/vscode/.age/keys.txt"
},
"customizations": {
"vscode": {
"extensions": [
"ms-kubernetes-tools.vscode-kubernetes-tools",
"redhat.vscode-yaml",
"redhat.vscode-commons",
"monokai.theme-monokai-pro-vscode",
"eamodio.gitlens",
"gruntfuggly.todo-tree",
"ms-vscode.makefile-tools",
"GitHub.copilot"
],
"settings": {
"[yaml]": {
"editor.defaultFormatter": "redhat.vscode-yaml",
"editor.formatOnSave": true,
"editor.tabSize": 2
},
"yaml.schemas": {
"https://json.schemastore.org/kustomization.json": "**/kustomization.yaml",
"https://json.schemastore.org/helmrelease.json": "**/*helmrelease*.yaml"
},
"editor.theme": "Monokai Pro",
"todo-tree.general.showActivityBarBadge": true,
"todo-tree.general.tags": [
"TODO",
"FIXME",
"BUG",
"HACK",
"NOTE",
"XXX",
"DONE"
],
"todo-tree.tree.showScanModeButton": true,
"todo-tree.filtering.includeGlobs": [
"**/docs/TASKS.md",
"**/docs/deployment-guides/*.md"
]
}
}
},
"postCreateCommand": "bash .devcontainer/postCreateCommand.sh",
"forwardPorts": []
}
+35
View File
@@ -0,0 +1,35 @@
#!/bin/bash
set -e
echo "🚀 Setting up ESS Community GitOps devcontainer..."
# Verify all required tools are installed
echo "✅ Verifying installed tools..."
commands=("kubectl" "flux" "helm" "sops" "age" "git" "docker")
for cmd in "${commands[@]}"; do
if command -v $cmd &> /dev/null; then
version=$($cmd version 2>/dev/null | head -1 || echo "installed")
echo "$cmd: $version"
else
echo "$cmd: NOT FOUND"
exit 1
fi
done
# Create necessary directories
echo "📁 Creating home directories..."
mkdir -p ~/.kube ~/.ssh ~/.age
# Print useful information
echo ""
echo "📚 Useful commands:"
echo " - kubectl get pods -n matrix (check pod status)"
echo " - flux get helmreleases -A (check helm releases)"
echo " - sops apps/production/custom-configs/mas-secrets.sops.yaml (edit secrets)"
echo ""
echo "🔗 For kubeconfig setup:"
echo " - Copy your ~/.kube/config to access the cluster"
echo " - Run: kubectl get nodes"
echo ""
echo "✨ Devcontainer setup complete!"
+50
View File
@@ -0,0 +1,50 @@
name: Auto-Deploy on Push
on:
push:
branches:
- main
paths:
- 'apps/**'
- 'clusters/**'
- '.gitea/workflows/**'
jobs:
verify-and-notify:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Check YAML Syntax
run: |
echo "🔍 Validating YAML files..."
find apps clusters -name "*.yaml" -type f | while read file; do
if ! grep -q "^apiVersion:" "$file"; then
echo "⚠️ Warning: $file may not be a valid K8s manifest"
fi
done
echo "✅ YAML validation passed"
- name: Check for SOPS Encryption
run: |
echo "🔐 Checking SOPS status..."
for file in $(git diff --name-only origin/main...HEAD -- '**/secret*.yaml' '**/credentials*.yaml'); do
if grep -q "ENC\[" "$file"; then
echo "✅ $file is encrypted"
else
echo "⚠️ WARNING: $file may not be encrypted!"
fi
done
- name: Create Deployment Notification
run: |
echo "📤 Flux will reconcile changes within 1 minute"
echo "🔗 Monitor in Gitea: Projects → Releases (check tags)"
- name: List Changed Files
run: |
echo "📋 Files changed in this push:"
git diff --name-only origin/main...HEAD
+32
View File
@@ -0,0 +1,32 @@
name: Create Release on Milestone Tag
on:
push:
tags:
- 'm*-*-complete'
jobs:
create-release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Extract Milestone Info
id: milestone
run: |
TAG="${GITHUB_REF#refs/tags/}"
TITLE=$(git tag -l "$TAG" -n1 | awk '{print substr($0, index($0, $2))}')
echo "tag=$TAG" >> $GITHUB_OUTPUT
echo "title=$TITLE" >> $GITHUB_OUTPUT
echo "🏷️ Milestone: $TAG"
echo "📝 Title: $TITLE"
- name: Create Release
run: |
echo "📦 Creating release for milestone: ${{ steps.milestone.outputs.tag }}"
echo "${{ steps.milestone.outputs.title }}" > /tmp/release-notes.txt
echo "Created: $(date)" >> /tmp/release-notes.txt
cat /tmp/release-notes.txt
+6
View File
@@ -53,6 +53,12 @@ winget install Kubernetes.kubectl FluxCD.Flux Mozilla.sops age-encryption.age He
1. **Kubeconfig:** Stelle sicher, dass die Datei `~/.kube/config` mit den Zugangsdaten zu deinem K3s-Cluster gefüllt ist. Test: `kubectl get nodes`.
2. **SOPS Key:** Du benötigst den privaten `age`-Key (oder GPG-Key), der in der `.sops.yaml` des Repositories hinterlegt ist, um Secrets bearbeiten zu können.
3. **Git Hooks installieren:** Nach dem Klonen dieses Repositories müssen Git Hooks installiert werden, um ConfigMap-Änderungen automatisch zu tracken:
```bash
cd prod/gitops
./scripts/install-hooks.sh
```
Siehe [📖 GitOps ConfigMap Auto-Sync](docs/ops-configmap-sync.md) für Details.
-----
+228
View File
@@ -0,0 +1,228 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: authentik-blueprints
namespace: authentik
data:
matrix-invitation-flow.yaml: |
# yaml-language-server: $schema=https://goauthentik.io/blueprints/schema.json
version: 1
metadata:
name: matrix-invitation-flow
labels:
blueprints.goauthentik.io/instantiate: "true"
entries:
# Reaffirm the flow itself (already created manually; matched by slug)
- model: authentik_flows.flow
state: present
identifiers:
slug: matrix-invitation
id: matrix_invitation_flow
attrs:
name: matrix-invitation
title: matrix-invitation
designation: enrollment
# The prompt stage had accumulated 16 unrelated system validation_policies
# (e.g. default-user-settings-authorization, default-oobe-password-usable)
# from manual UI setup, likely a "select all" slip in the policy picker.
# These crash on an anonymous enrollment context ('AnonymousUser' object
# has no attribute 'group_attributes', etc). A prompt stage needs none here.
- model: authentik_stages_prompt.promptstage
state: present
identifiers:
name: matrix-invitation-prompt
attrs:
validation_policies: []
# Correct stage chain, mirroring the working matrix-enrollment flow:
# Invite -> Prompt (username/email/password) -> Write -> Password -> Login
# Root cause of the original bug: only Invite+Prompt were bound, both at
# order=0, so the flow never wrote the user to the DB or logged them in.
- model: authentik_flows.flowstagebinding
state: present
identifiers:
target: !KeyOf matrix_invitation_flow
order: 0
attrs:
stage: !Find [authentik_stages_invitation.invitationstage, [name, matrix-enrollment-invitation]]
- model: authentik_flows.flowstagebinding
state: present
identifiers:
target: !KeyOf matrix_invitation_flow
order: 1
attrs:
stage: !Find [authentik_stages_prompt.promptstage, [name, matrix-invitation-prompt]]
- model: authentik_flows.flowstagebinding
state: present
identifiers:
target: !KeyOf matrix_invitation_flow
order: 2
attrs:
stage: !Find [authentik_stages_user_write.userwritestage, [name, default-source-enrollment-write]]
- model: authentik_flows.flowstagebinding
state: present
identifiers:
target: !KeyOf matrix_invitation_flow
order: 3
attrs:
stage: !Find [authentik_stages_password.passwordstage, [name, default-authentication-password]]
- model: authentik_flows.flowstagebinding
state: present
identifiers:
target: !KeyOf matrix_invitation_flow
order: 4
attrs:
stage: !Find [authentik_stages_user_login.userloginstage, [name, default-source-enrollment-login]]
# Without an explicit destination, the flow falls back to Authentik's own
# /if/user/ dashboard, which refuses type=external users ("Die Oberflaeche
# kann nur von internen Nutzern geoeffnet werden") - exactly the user type
# these Matrix-only accounts correctly have. Send them to Element instead.
- model: authentik_stages_redirect.redirectstage
state: present
identifiers:
name: matrix-invitation-redirect
id: matrix_invitation_redirect_stage
attrs:
mode: static
target_static: https://axion1337.chat
- model: authentik_flows.flowstagebinding
state: present
identifiers:
target: !KeyOf matrix_invitation_flow
order: 5
attrs:
stage: !KeyOf matrix_invitation_redirect_stage
matrix-recovery-flow.yaml: |
# yaml-language-server: $schema=https://goauthentik.io/blueprints/schema.json
version: 1
metadata:
name: matrix-recovery-flow
labels:
blueprints.goauthentik.io/instantiate: "true"
entries:
# matrix-recovery existed but had zero stage bindings (dead flow), and the
# real login flow (default-authentication-flow, used by the MAS OAuth2
# provider's authentication_flow) didn't link to it at all - no "Forgot
# password?" link was ever shown. Reuses the same default-recovery-*
# stages the built-in default-recovery-flow already uses successfully,
# plus our own redirect stage instead of falling back to the authentik
# dashboard (blocked for type=external Matrix users).
- model: authentik_flows.flow
state: present
identifiers:
slug: matrix-recovery
id: matrix_recovery_flow
attrs:
designation: recovery
- model: authentik_flows.flowstagebinding
state: present
identifiers:
target: !KeyOf matrix_recovery_flow
order: 10
attrs:
stage: !Find [authentik_stages_identification.identificationstage, [name, default-recovery-identification]]
- model: authentik_flows.flowstagebinding
state: present
identifiers:
target: !KeyOf matrix_recovery_flow
order: 20
attrs:
stage: !Find [authentik_stages_email.emailstage, [name, default-recovery-email]]
- model: authentik_flows.flowstagebinding
state: present
identifiers:
target: !KeyOf matrix_recovery_flow
order: 30
attrs:
stage: !Find [authentik_stages_prompt.promptstage, [name, "Change your password"]]
- model: authentik_flows.flowstagebinding
state: present
identifiers:
target: !KeyOf matrix_recovery_flow
order: 40
attrs:
stage: !Find [authentik_stages_user_write.userwritestage, [name, default-recovery-user-write]]
- model: authentik_flows.flowstagebinding
state: present
identifiers:
target: !KeyOf matrix_recovery_flow
order: 100
attrs:
stage: !Find [authentik_stages_user_login.userloginstage, [name, default-recovery-user-login]]
- model: authentik_flows.flowstagebinding
state: present
identifiers:
target: !KeyOf matrix_recovery_flow
order: 110
attrs:
stage: !Find [authentik_stages_redirect.redirectstage, [name, matrix-invitation-redirect]]
# Wire the "Forgot password?" link on the real login flow used by MAS
- model: authentik_stages_identification.identificationstage
state: present
identifiers:
name: default-authentication-identification
attrs:
recovery_flow: !KeyOf matrix_recovery_flow
matrix-mfa-setup-redirect.yaml: |
# yaml-language-server: $schema=https://goauthentik.io/blueprints/schema.json
version: 1
metadata:
name: matrix-mfa-setup-redirect
labels:
blueprints.goauthentik.io/instantiate: "true"
entries:
# 2FA is optional (default-authentication-mfa-validation has
# not_configured_action=skip - login never blocks on missing MFA).
# Users who want to opt in use these built-in single-stage setup flows
# directly (unreachable via /if/user/, which is blocked for type=external
# Matrix accounts). Without a stage after the setup itself, completion
# fell back to the same blocked /if/user/ dashboard - append our redirect.
- model: authentik_flows.flowstagebinding
state: present
identifiers:
target: !Find [authentik_flows.flow, [slug, default-authenticator-totp-setup]]
order: 10
attrs:
stage: !Find [authentik_stages_redirect.redirectstage, [name, matrix-invitation-redirect]]
- model: authentik_flows.flowstagebinding
state: present
identifiers:
target: !Find [authentik_flows.flow, [slug, default-authenticator-webauthn-setup]]
order: 10
attrs:
stage: !Find [authentik_stages_redirect.redirectstage, [name, matrix-invitation-redirect]]
matrix-brand-default-app.yaml: |
# yaml-language-server: $schema=https://goauthentik.io/blueprints/schema.json
version: 1
metadata:
name: matrix-brand-default-app
labels:
blueprints.goauthentik.io/instantiate: "true"
entries:
# Root cause behind several dead ends: an authenticated user hitting "/"
# with no other destination (e.g. after logging in mid-way through the
# TOTP/WebAuthn setup flows) falls back to Brand.default_application: if
# unset, that's /if/user/, which type=external Matrix accounts can't
# open. Only affects the bare "/" fallback - explicit URLs like
# /if/admin/ are unaffected, so internal/staff access is unchanged.
- model: authentik_brands.brand
state: present
identifiers:
domain: authentik-default
attrs:
default_application: !Find [authentik_core.application, [slug, matrix]]
+24
View File
@@ -0,0 +1,24 @@
apiVersion: v1
kind: Secret
metadata:
name: authentik-credentials
namespace: authentik
stringData:
secret_key: ENC[AES256_GCM,data:yIyQapbFtFM11LynFtkV3ffExhaDfN9QHeFbI1T0xkIhgsV+9sjg3qwMVmeBlAe7xZl8gsAM4kDj2Q6O91OdDg==,iv:+Cl8vOcxG9/mgRheaCO0bLWyCJXN+f1F2DD3oeHbPFY=,tag:711ytyKf6/tmXomBLoffGA==,type:str]
pg-password: ENC[AES256_GCM,data:3w8R9mRjMXMJDLjrC8QYaXFHsCU3yYZs2PcaFQNp3Z4=,iv:G/aXgoGz3vBOzZ5K3Y+DDJsqer4F5gvcMmtkzRx93CU=,tag:dXPs1pY/APvnMlxdvB1EkA==,type:str]
smtp-password: ENC[AES256_GCM,data:JpMgaQFPkBzOg5WjvpmhM0kPwvZkH+4tQjT17RJHjG14WjmWtfG9Bg==,iv:zjQRLIlrxKv5hbd4JZowNUEiibiCUMf79acZY0+dYAc=,tag:ORPafTPyOQJvVvHWQGmqhA==,type:str]
sops:
age:
- recipient: age14l0hwfqylwpemz5y2ghh2yxk0phszlnj3qlejhue0fw0kz3tmfgqdsjzdh
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBRekJuZythYzliTFJ3RlhS
R2p6TG9NeFdabFlPRWtpNHJMYVVxTWZEcmlRClk0WUorSzdxNlcyWHYwWFBTMnlq
TlM4dENSSit2S3VGSzJCeTRTYU52dmcKLS0tIEF0WkV0M25OSEo1N0FEYXI5Q0Z6
QXVrY1NTeHZkeTlPRWNlVThzWno3T0kKC0KBoLT64GNqb8Ri9u69G7nqb1KftwwP
/24aVHrPxKi9d4ij9n3bvCYDF4rhtfexhrE4n7CfuKn2DcSiuTniuw==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2026-04-29T21:43:59Z"
mac: ENC[AES256_GCM,data:Y+dJppkaVZ5NOhlvwbbsF5+vDFqGUI1Ps8IcE4J7FIW4HIdMVf6RKM0EInvPUW1LaBlmelCitcE30w0As7ysNRhLY8yUDaKUvuU6mRejlNUIF8wAHzhciL2jTvAQsArHjybJatEig28+wM9VcY8JEa/d/CmuiB9Nq4WbIV+JXlA=,iv:UQj2rIVLNPjtYp3d/jRyNfJyyyUsZ3+NDCgpI4aztzc=,tag:cwiCzG/A+rfRFfLjXVt82w==,type:str]
encrypted_regex: ^(data|stringData)$
version: 3.12.2
+91
View File
@@ -0,0 +1,91 @@
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: authentik
namespace: authentik
spec:
interval: 1h
chart:
spec:
chart: authentik
version: "2026.2.3"
sourceRef:
kind: HelmRepository
name: goauthentik
namespace: flux-system
install:
remediation:
retries: 3
upgrade:
remediation:
retries: 3
valuesFrom:
- kind: Secret
name: authentik-credentials
valuesKey: secret_key
targetPath: authentik.secret_key
- kind: Secret
name: authentik-credentials
valuesKey: pg-password
targetPath: authentik.postgresql.password
- kind: Secret
name: authentik-credentials
valuesKey: pg-password
targetPath: postgresql.auth.password
- kind: Secret
name: authentik-credentials
valuesKey: smtp-password
targetPath: authentik.email.password
values:
global:
security:
allowInsecureImages: true
authentik:
log_level: info
error_reporting:
enabled: false
email:
host: smtp.ionos.de
port: 587
username: gamemaster@axion1337.chat
use_tls: true
from: "Authentik <gamemaster@axion1337.chat>"
blueprints:
configMaps:
- authentik-blueprints
server:
ingress:
enabled: false
resources:
requests:
cpu: 100m
memory: 512Mi
limits:
memory: 1Gi
worker:
resources:
requests:
cpu: 50m
memory: 512Mi
limits:
memory: 1Gi
postgresql:
enabled: true
auth:
username: authentik
database: authentik
primary:
persistence:
enabled: true
size: 8Gi
resources:
requests:
cpu: 50m
memory: 256Mi
limits:
memory: 512Mi
+12
View File
@@ -0,0 +1,12 @@
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: auth-axion1337-chat-cert
namespace: authentik
spec:
secretName: auth-axion1337-chat-tls
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
dnsNames:
- auth.axion1337.chat
+8
View File
@@ -0,0 +1,8 @@
apiVersion: source.toolkit.fluxcd.io/v1
kind: HelmRepository
metadata:
name: goauthentik
namespace: flux-system
spec:
interval: 1h
url: https://charts.goauthentik.io
+16
View File
@@ -0,0 +1,16 @@
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: authentik
namespace: authentik
spec:
entryPoints:
- websecure
tls:
secretName: auth-axion1337-chat-tls
routes:
- match: Host(`auth.axion1337.chat`)
kind: Rule
services:
- name: authentik-server
port: 80
+11
View File
@@ -0,0 +1,11 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- namespace.yaml
- helm-repo.yaml
- authentik-secret.yaml
- authentik-blueprints.yaml
- certificate.yaml
- authentik.yaml
- ingress.yaml
- networkpolicy.yaml
+4
View File
@@ -0,0 +1,4 @@
apiVersion: v1
kind: Namespace
metadata:
name: authentik
+66
View File
@@ -0,0 +1,66 @@
# Default-deny ingress for the authentik namespace, with explicit allow rules for the
# traffic paths that actually need to reach in: Traefik (kube-system) for the public
# auth.axion1337.chat endpoint and ACME HTTP-01 challenges, and MAS (matrix namespace)
# for upstream OIDC calls. Egress is intentionally untouched (federation-equivalent
# outbound calls like SMTP aren't restricted here).
#
# Note: authentik-postgresql already has its own NetworkPolicy from the Bitnami
# postgresql subchart (port 5432, no source restriction) - left alone, not duplicated,
# since it would get reset on the next Helm upgrade anyway.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
namespace: authentik
spec:
podSelector: {}
policyTypes:
- Ingress
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-ingress-authentik-server
namespace: authentik
spec:
podSelector:
matchLabels:
app.kubernetes.io/name: authentik
app.kubernetes.io/component: server
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: matrix
ports:
# NetworkPolicy matches the pod's actual container port, not the Service's
# external port - the authentik-server Service maps 80->9000, 443->9443.
- protocol: TCP
port: 9000
- protocol: TCP
port: 9443
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-ingress-acme-solver
namespace: authentik
spec:
podSelector:
matchLabels:
acme.cert-manager.io/http01-solver: "true"
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
ports:
- protocol: TCP
port: 8089
+135
View File
@@ -0,0 +1,135 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: alloy-config
namespace: monitoring
data:
config.alloy: |
// Kubernetes pod discovery
discovery.kubernetes "k8s_pods" {
role = "pod"
}
// Relabel for Prometheus scrape
discovery.relabel "prometheus_pods" {
targets = discovery.kubernetes.k8s_pods.targets
rule {
source_labels = ["__meta_kubernetes_pod_annotation_prometheus_io_scrape"]
action = "keep"
regex = "true"
}
rule {
source_labels = ["__meta_kubernetes_pod_annotation_prometheus_io_path"]
action = "replace"
target_label = "__metrics_path__"
regex = "(.+)"
}
rule {
source_labels = ["__address__", "__meta_kubernetes_pod_annotation_prometheus_io_port"]
action = "replace"
regex = "([^:]+)(?::\\d+)?;(\\d+)"
replacement = "$1:$2"
target_label = "__address__"
}
rule {
source_labels = ["__meta_kubernetes_namespace"]
action = "replace"
target_label = "namespace"
}
rule {
source_labels = ["__meta_kubernetes_pod_name"]
action = "replace"
target_label = "pod"
}
}
// Scrape Flux controllers (flux-system namespace, port 8080)
discovery.kubernetes "flux_pods" {
role = "pod"
namespaces {
names = ["flux-system"]
}
}
discovery.relabel "flux_scrape" {
targets = discovery.kubernetes.flux_pods.targets
rule {
source_labels = ["__meta_kubernetes_pod_container_port_number"]
action = "keep"
regex = "8080"
}
rule {
source_labels = ["__meta_kubernetes_namespace"]
action = "replace"
target_label = "namespace"
}
rule {
source_labels = ["__meta_kubernetes_pod_name"]
action = "replace"
target_label = "pod"
}
}
// Scrape kube-state-metrics
prometheus.scrape "kube_state_metrics" {
targets = [{
__address__ = "kube-state-metrics.monitoring.svc.cluster.local:8080",
}]
forward_to = [prometheus.remote_write.selendis.receiver]
scrape_interval = "30s"
scrape_timeout = "10s"
}
// Scrape Flux controllers
prometheus.scrape "flux" {
targets = discovery.relabel.flux_scrape.output
forward_to = [prometheus.remote_write.selendis.receiver]
scrape_interval = "30s"
scrape_timeout = "10s"
job_name = "flux"
}
// Scrape node-exporter DaemonSet
prometheus.scrape "node_exporter" {
targets = [{
__address__ = "prometheus-node-exporter.monitoring.svc.cluster.local:9100",
}]
forward_to = [prometheus.remote_write.selendis.receiver]
scrape_interval = "30s"
scrape_timeout = "10s"
}
// Scrape Synapse metrics
prometheus.scrape "synapse" {
targets = [{
__address__ = "matrix-stack-synapse-main.matrix.svc.cluster.local:9000",
}]
forward_to = [prometheus.remote_write.selendis.receiver]
scrape_interval = "30s"
scrape_timeout = "10s"
}
// Kubernetes pod logs to Loki
loki.source.kubernetes "k8s_logs" {
targets = discovery.kubernetes.k8s_pods.targets
forward_to = [loki.write.selendis.receiver]
}
// Remote write to Selendis Prometheus
prometheus.remote_write "selendis" {
endpoint {
url = "http://10.0.0.3:9090/api/v1/write"
write_relabel_config {
source_labels = ["__name__"]
regex = "go_.*|process_.*"
action = "drop"
}
}
}
// Remote write logs to Selendis Loki
loki.write "selendis" {
endpoint {
url = "http://10.0.0.3:3100/loki/api/v1/push"
}
}
+33
View File
@@ -0,0 +1,33 @@
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: alloy
namespace: monitoring
spec:
interval: 1h
chart:
spec:
chart: alloy
version: "0.x"
sourceRef:
kind: HelmRepository
name: grafana
namespace: flux-system
values:
alloy:
configMap:
name: alloy-config
replicaCount: 1
serviceAccount:
create: true
name: alloy
rbac:
create: true
podAnnotations:
prometheus.io/scrape: "false"
resources:
limits:
memory: 512Mi
requests:
cpu: 100m
memory: 256Mi
+18
View File
@@ -0,0 +1,18 @@
apiVersion: source.toolkit.fluxcd.io/v1
kind: HelmRepository
metadata:
name: prometheus-community
namespace: flux-system
spec:
interval: 1h
url: https://prometheus-community.github.io/helm-charts
---
apiVersion: source.toolkit.fluxcd.io/v1
kind: HelmRepository
metadata:
name: grafana
namespace: flux-system
spec:
interval: 1h
url: https://grafana.github.io/helm-charts
+22
View File
@@ -0,0 +1,22 @@
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: kube-state-metrics
namespace: monitoring
spec:
interval: 1h
chart:
spec:
chart: kube-state-metrics
version: "5.x"
sourceRef:
kind: HelmRepository
name: prometheus-community
namespace: flux-system
values:
replicas: 1
service:
port: 8080
prometheus:
monitor:
enabled: false
+9
View File
@@ -0,0 +1,9 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- namespace.yaml
- helm-repos.yaml
- kube-state-metrics.yaml
- node-exporter.yaml
- alloy-config.yaml
- alloy.yaml
+4
View File
@@ -0,0 +1,4 @@
apiVersion: v1
kind: Namespace
metadata:
name: monitoring
+29
View File
@@ -0,0 +1,29 @@
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: prometheus-node-exporter
namespace: monitoring
spec:
interval: 1h
chart:
spec:
chart: prometheus-node-exporter
version: "4.x"
sourceRef:
kind: HelmRepository
name: prometheus-community
namespace: flux-system
values:
hostNetwork: true
hostPID: true
hostRootFsMount:
enabled: true
service:
port: 9100
targetPort: 9100
prometheus:
monitor:
enabled: false
tolerations:
- effect: NoSchedule
operator: Exists
+7
View File
@@ -28,6 +28,13 @@ spec:
services:
- name: matrix-stack-well-known
port: 8010
# Element Desktop Setup Skripte
- match: Host(`axion1337.chat`) && PathPrefix(`/docs/setup`)
kind: Rule
priority: 50
services:
- name: element-web-docs
port: 80
# Niedrigere Priorität: alles andere -> Element Web
- match: Host(`axion1337.chat`)
kind: Rule
+22
View File
@@ -0,0 +1,22 @@
apiVersion: v1
kind: Secret
metadata:
name: coturn-secret
namespace: matrix
stringData:
TURN_SECRET: ENC[AES256_GCM,data:SILIqMB+fmAMFITAL7lG1hOgICec6BJf1mOcK0gdmnCHWYqRuJv7jgjfGylG25xzQKi+zE7Qual9PnkZG2KiOA==,iv:+GZqLGusE4Q0x2jEEtFxj06rryyQmQhXdkTy4eE8ZHw=,tag:OpSZkinPTAi1ZKWyo8OX3A==,type:str]
sops:
age:
- recipient: age14l0hwfqylwpemz5y2ghh2yxk0phszlnj3qlejhue0fw0kz3tmfgqdsjzdh
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAyRk1mK3NWc1l4T0JCOFpF
S0RuQ3ViZmo3QTNVL2JvZ0hzMy91R2l0TEhzCk01a1VGdk1sdVg4aWswTzRibXI4
ZlJtNFF5MjBONEZOaWVpeU5taHl2bkEKLS0tIGxpUHY3NUFLWFBaWm1QSlZiVFkx
MEJleHFnd3oyT3VPL2dsYkpMUlRkOWMKcKUIgsQ/ff49pGGXMnYwJmwqPVC7woAR
IEzvhcNX97xx746SnrxZe5t2YadsYMkYIl0nvqBPJhSlvqMNafpQbQ==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2026-04-29T21:06:21Z"
mac: ENC[AES256_GCM,data:UhyR5m1HYWrZHwNLW5sg2PxbpaydWbP5cekghGlzSpQg7CYEcvZw3tJ/qB8zA19xZSM7tdSHOXdD+QytRq6qW59M1unqMaumA43B6JxQg1C1NdXAW0mkSc2WiNchvgpVii9P/TVlzSSIRwC3YGCQUsfa3SSfNzI4Z6fMuBnhYLE=,iv:4HYxbrYSRJLe1KcQ6q8bpee8/Lx1m3pPmisb/L2Mu64=,tag:l7n3u+Pg6533OzwtNUZvNw==,type:str]
encrypted_regex: ^(data|stringData)$
version: 3.12.2
+159
View File
@@ -0,0 +1,159 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: coturn-config
namespace: matrix
data:
turnserver.conf: |
# TURN Server configuration
realm=axion1337.chat
# Listen ports
listening-port=3478
listening-ip=0.0.0.0
alt-listening-port=5349
alt-listening-ip=0.0.0.0
# External IPs (for clients behind NAT)
relay-ip=49.13.132.245
external-ip=49.13.132.245
# Relay port range
min-bps=0
bps-capacity=0
# Authentication
use-auth-secret
static-auth-secret=$TURN_SECRET
# HTTPS/TLS
cert=/etc/coturn/tls/tls.crt
pkey=/etc/coturn/tls/tls.key
# Performance tuning
max-bps=0
bps-capacity=0
log-file=stdout
# Logging
verbose
---
apiVersion: v1
kind: Service
metadata:
name: coturn
namespace: matrix
spec:
type: ClusterIP
ports:
- name: stun-udp
port: 3478
protocol: UDP
- name: stun-tcp
port: 3478
protocol: TCP
- name: turns-tcp
port: 5349
protocol: TCP
selector:
app: coturn
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: coturn
namespace: matrix
spec:
replicas: 1
selector:
matchLabels:
app: coturn
template:
metadata:
labels:
app: coturn
annotations:
prometheus.io/scrape: "false"
spec:
hostNetwork: true
dnsPolicy: ClusterFirstWithHostNet
initContainers:
- name: init-config
image: busybox:1.28
command:
- sh
- -c
- |
TURN_SECRET=$(cat /etc/coturn-secret/TURN_SECRET)
sed "s|\$TURN_SECRET|$TURN_SECRET|g" /etc/coturn-template/turnserver.conf > /etc/coturn/turnserver.conf
chmod 644 /etc/coturn/turnserver.conf
resources:
limits:
cpu: 100m
memory: 64Mi
requests:
cpu: 50m
memory: 32Mi
volumeMounts:
- name: config-template
mountPath: /etc/coturn-template
- name: config
mountPath: /etc/coturn
- name: secret
mountPath: /etc/coturn-secret
readOnly: true
containers:
- name: coturn
image: coturn/coturn:latest
imagePullPolicy: IfNotPresent
ports:
- name: stun-udp
containerPort: 3478
protocol: UDP
- name: stun-tcp
containerPort: 3478
protocol: TCP
- name: turns-tcp
containerPort: 5349
protocol: TCP
volumeMounts:
- name: config
mountPath: /etc/coturn
- name: tls
mountPath: /etc/coturn/tls
readOnly: true
resources:
limits:
cpu: 500m
memory: 256Mi
requests:
cpu: 100m
memory: 128Mi
livenessProbe:
tcpSocket:
port: 3478
initialDelaySeconds: 30
periodSeconds: 10
volumes:
- name: config
emptyDir: {}
- name: config-template
configMap:
name: coturn-config
- name: secret
secret:
secretName: coturn-secret
defaultMode: 0400
- name: tls
secret:
secretName: turn-axion1337-chat-tls
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
preference:
matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- matrix
@@ -0,0 +1,186 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: ess-element-custom
namespace: matrix
data:
values.yaml: |
elementWeb:
additional:
config.json: |
{
"brand": "aXion1337.Chat",
"default_theme": "aXion1337 Dark",
"show_labs_settings": true,
"features": {
"feature_qr_code_login": true,
"feature_new_room_list": true
},
"element_call": {
"use_exclusively": true
},
"setting_defaults": {
"custom_themes": [
{
"name": "aXion1337 Dark true",
"is_dark": true,
"colors": {
"accent-color": "#ffaf0f",
"primary-color": "#ffaf0f",
"secondary-color": "#ffaf0f"
}
},
{
"name": "Deep Purple",
"is_dark": true,
"colors": {
"accent-color": "#6503b3",
"primary-color": "#368bd6",
"warning-color": "#b30356",
"sidebar-color": "#15171B",
"roomlist-background-color": "#22262E",
"roomlist-text-color": "#A1B2D1",
"roomlist-text-secondary-color": "#EDF3FF",
"roomlist-highlights-color": "#343A46",
"roomlist-separator-color": "#a1b2d1",
"timeline-background-color": "#181b21",
"timeline-text-color": "#EDF3FF",
"timeline-text-secondary-color": "#A1B2D1",
"timeline-highlights-color": "#22262E"
}
},
{
"name": "Discord Dark",
"is_dark": true,
"colors": {
"accent-color": "#747ff4",
"accent": "#747ff4",
"primary-color": "#00aff4",
"warning-color": "#faa81ad9",
"alert": "#faa81ad9",
"sidebar-color": "#202225",
"roomlist-background-color": "#2f3136",
"roomlist-text-color": "#dcddde",
"roomlist-text-secondary-color": "#8e9297",
"roomlist-highlights-color": "#4f545c52",
"roomlist-separator-color": "#40444b",
"timeline-background-color": "#36393f",
"timeline-text-color": "#dcddde",
"secondary-content": "#dcddde",
"tertiary-content": "#dcddde",
"timeline-text-secondary-color": "#b9bbbe",
"timeline-highlights-color": "#04040512",
"reaction-row-button-selected-bg-color": "#4752c4",
"menu-selected-color": "#4752c4",
"focus-bg-color": "#4752c4",
"room-highlight-color": "#4752c4",
"other-user-pill-bg-color": "#4752c4",
"togglesw-off-color": "#72767d"
},
"compound": {
"--cpd-color-theme-bg": "#0019ff",
"--cpd-color-bg-canvas-default": "#2f3136",
"--cpd-color-bg-subtle-secondary": "#2f3136",
"--cpd-color-bg-subtle-primary": "#4f545c52",
"--cpd-color-bg-action-primary-rest": "#dcddde",
"--cpd-color-bg-action-secondary-rest": "#2f3136",
"--cpd-color-bg-critical-primary": "#fd3f3c",
"--cpd-color-bg-critical-subtle": "#745862",
"--cpd-color-bg-critical-hovered": "#fd3f3c",
"--cpd-color-bg-accent-rest": "#4cb387",
"--cpd-color-text-primary": "#dcddde",
"--cpd-color-text-secondary": "#b9bbbe",
"--cpd-color-text-action-accent": "#b9bbbe",
"--cpd-color-text-critical-primary": "#fd3f3c",
"--cpd-color-text-success-primary": "#4cb387",
"--cpd-color-icon-primary": "#dcddde",
"--cpd-color-icon-secondary": "#dcddde",
"--cpd-color-icon-tertiary": "#a7a0a7",
"--cpd-color-icon-accent-tertiary": "#4cb387",
"--cpd-color-border-interactive-primary": "#5d6064",
"--cpd-color-border-interactive-secondary": "#5d6064",
"--cpd-color-border-critical-primary": "#fd3f3c",
"--cpd-color-border-success-subtle": "#4cb387"
}
},
{
"name": "Electric Blue",
"is_dark": false,
"colors": {
"accent-color": "#3596fc",
"primary-color": "#368bd6",
"warning-color": "#ff4b55",
"sidebar-color": "#27303a",
"roomlist-background-color": "#f3f8fd",
"roomlist-text-color": "#2e2f32",
"roomlist-text-secondary-color": "#61708b",
"roomlist-highlights-color": "#ffffff",
"roomlist-separator-color": "#e3e8f0",
"timeline-background-color": "#ffffff",
"timeline-text-color": "#2e2f32",
"timeline-text-secondary-color": "#61708b",
"timeline-highlights-color": "#f3f8fd",
"username-colors": ["#ff0000", "#ff7f00", "#ffff00", "#00ff00", "#0000ff", "#4b0082", "#9400d3", "#ff1493"],
"avatar-background-colors": ["#cc0000", "#cc6600", "#cccc00", "#00cc00", "#0000cc", "#3b0066", "#7a00b3", "#cc1077"]
},
"compound": {
"--cpd-color-icon-accent-tertiary": "var(--cpd-color-blue-800)",
"--cpd-color-text-action-accent": "var(--cpd-color-blue-900)"
}
},
{
"name": "Everforest dark hard",
"is_dark": true,
"colors": {
"accent-color": "#a7c080",
"primary-color": "#a7c080",
"warning-color": "#e67e80",
"sidebar-color": "#323d43",
"roomlist-background-color": "#2f383e",
"roomlist-text-color": "#d3c6aa",
"roomlist-text-secondary-color": "#d3c6aa",
"roomlist-highlights-color": "#4b565c",
"roomlist-separator-color": "#4b565c",
"timeline-background-color": "#2b3339",
"timeline-text-color": "#d3c6aa",
"secondary-content": "#d3c6aa",
"tertiary-content": "#d3c6aa",
"timeline-text-secondary-color": "#a7c080",
"timeline-highlights-color": "#4b565c",
"reaction-row-button-selected-bg-color": "#4b565c"
}
},
{
"name": "aXion1337 Dark", #Gruvbox Dark
"is_dark": true,
"colors": {
"accent-color": "#bd93f9",
"primary-color": "#fe8019",
"warning-color": "#fb4934",
"sidebar-color": "#282828",
"roomlist-background-color": "#1d2021",
"roomlist-text-color": "#a89984",
"roomlist-text-secondary-color": "#00ff00",
"roomlist-highlights-color": "#00000030",
"roomlist-separator-color": "#4d4d4d90",
"timeline-background-color": "#282828",
"timeline-text-color": "#ebdbb2",
"secondary-content": "#928374",
"tertiary-content": "#928374",
"quinary-content": "#504945",
"timeline-text-secondary-color": "#a89984",
"timeline-highlights-color": "#00000030",
"reaction-row-button-selected-bg-color": "#689d6a",
"menu-selected-color": "#504945",
"icon-button-color": "#928374",
"accent": "#689d6a",
"alert": "#cc241d",
"username-colors": [
"#cc241d",
"#98971a",
"#d79921",
"#458588",
"#b16286",
"#689d6a",
"#a89984",
"#d65d0e"
@@ -13,19 +13,180 @@ data:
"default_theme": "aXion1337 Dark",
"show_labs_settings": true,
"features": {
"feature_qr_code_login": true
"feature_qr_code_login": true,
"feature_new_room_list": true
},
"element_call": {
"use_exclusively": true
},
"setting_defaults": {
"custom_themes": [
{
"name": "aXion1337 Dark",
"name": "aXion1337 Dark true",
"is_dark": true,
"colors": {
"accent-color": "#ffaf0f",
"primary-color": "#ffaf0f",
"secondary-color": "#ffaf0f"
}
},
{
"name": "Deep Purple",
"is_dark": true,
"colors": {
"accent-color": "#6503b3",
"primary-color": "#368bd6",
"warning-color": "#b30356",
"sidebar-color": "#15171B",
"roomlist-background-color": "#22262E",
"roomlist-text-color": "#A1B2D1",
"roomlist-text-secondary-color": "#EDF3FF",
"roomlist-highlights-color": "#343A46",
"roomlist-separator-color": "#a1b2d1",
"timeline-background-color": "#181b21",
"timeline-text-color": "#EDF3FF",
"timeline-text-secondary-color": "#A1B2D1",
"timeline-highlights-color": "#22262E"
}
},
{
"name": "Discord Dark",
"is_dark": true,
"colors": {
"accent-color": "#747ff4",
"accent": "#747ff4",
"primary-color": "#00aff4",
"warning-color": "#faa81ad9",
"alert": "#faa81ad9",
"sidebar-color": "#202225",
"roomlist-background-color": "#2f3136",
"roomlist-text-color": "#dcddde",
"roomlist-text-secondary-color": "#8e9297",
"roomlist-highlights-color": "#4f545c52",
"roomlist-separator-color": "#40444b",
"timeline-background-color": "#36393f",
"timeline-text-color": "#dcddde",
"secondary-content": "#dcddde",
"tertiary-content": "#dcddde",
"timeline-text-secondary-color": "#b9bbbe",
"timeline-highlights-color": "#04040512",
"reaction-row-button-selected-bg-color": "#4752c4",
"menu-selected-color": "#4752c4",
"focus-bg-color": "#4752c4",
"room-highlight-color": "#4752c4",
"other-user-pill-bg-color": "#4752c4",
"togglesw-off-color": "#72767d"
},
"compound": {
"--cpd-color-theme-bg": "#0019ff",
"--cpd-color-bg-canvas-default": "#2f3136",
"--cpd-color-bg-subtle-secondary": "#2f3136",
"--cpd-color-bg-subtle-primary": "#4f545c52",
"--cpd-color-bg-action-primary-rest": "#dcddde",
"--cpd-color-bg-action-secondary-rest": "#2f3136",
"--cpd-color-bg-critical-primary": "#fd3f3c",
"--cpd-color-bg-critical-subtle": "#745862",
"--cpd-color-bg-critical-hovered": "#fd3f3c",
"--cpd-color-bg-accent-rest": "#4cb387",
"--cpd-color-text-primary": "#dcddde",
"--cpd-color-text-secondary": "#b9bbbe",
"--cpd-color-text-action-accent": "#b9bbbe",
"--cpd-color-text-critical-primary": "#fd3f3c",
"--cpd-color-text-success-primary": "#4cb387",
"--cpd-color-icon-primary": "#dcddde",
"--cpd-color-icon-secondary": "#dcddde",
"--cpd-color-icon-tertiary": "#a7a0a7",
"--cpd-color-icon-accent-tertiary": "#4cb387",
"--cpd-color-border-interactive-primary": "#5d6064",
"--cpd-color-border-interactive-secondary": "#5d6064",
"--cpd-color-border-critical-primary": "#fd3f3c",
"--cpd-color-border-success-subtle": "#4cb387"
}
},
{
"name": "Electric Blue",
"is_dark": false,
"colors": {
"accent-color": "#3596fc",
"primary-color": "#368bd6",
"warning-color": "#ff4b55",
"sidebar-color": "#27303a",
"roomlist-background-color": "#f3f8fd",
"roomlist-text-color": "#2e2f32",
"roomlist-text-secondary-color": "#61708b",
"roomlist-highlights-color": "#ffffff",
"roomlist-separator-color": "#e3e8f0",
"timeline-background-color": "#ffffff",
"timeline-text-color": "#2e2f32",
"timeline-text-secondary-color": "#61708b",
"timeline-highlights-color": "#f3f8fd",
"username-colors": ["#ff0000", "#ff7f00", "#ffff00", "#00ff00", "#0000ff", "#4b0082", "#9400d3", "#ff1493"],
"avatar-background-colors": ["#cc0000", "#cc6600", "#cccc00", "#00cc00", "#0000cc", "#3b0066", "#7a00b3", "#cc1077"]
},
"compound": {
"--cpd-color-icon-accent-tertiary": "var(--cpd-color-blue-800)",
"--cpd-color-text-action-accent": "var(--cpd-color-blue-900)"
}
},
{
"name": "Everforest dark hard",
"is_dark": true,
"colors": {
"accent-color": "#a7c080",
"primary-color": "#a7c080",
"warning-color": "#e67e80",
"sidebar-color": "#323d43",
"roomlist-background-color": "#2f383e",
"roomlist-text-color": "#d3c6aa",
"roomlist-text-secondary-color": "#d3c6aa",
"roomlist-highlights-color": "#4b565c",
"roomlist-separator-color": "#4b565c",
"timeline-background-color": "#2b3339",
"timeline-text-color": "#d3c6aa",
"secondary-content": "#d3c6aa",
"tertiary-content": "#d3c6aa",
"timeline-text-secondary-color": "#a7c080",
"timeline-highlights-color": "#4b565c",
"reaction-row-button-selected-bg-color": "#4b565c"
}
},
{
"name": "aXion1337 Dark",
"is_dark": true,
"colors": {
"accent-color": "#bd93f9",
"primary-color": "#fe8019",
"warning-color": "#fb4934",
"sidebar-color": "#282828",
"roomlist-background-color": "#1d2021",
"roomlist-text-color": "#a89984",
"roomlist-text-secondary-color": "#00ff00",
"roomlist-highlights-color": "#00000030",
"roomlist-separator-color": "#4d4d4d90",
"timeline-background-color": "#282828",
"timeline-text-color": "#ebdbb2",
"secondary-content": "#928374",
"tertiary-content": "#928374",
"quinary-content": "#504945",
"timeline-text-secondary-color": "#a89984",
"timeline-highlights-color": "#00000030",
"reaction-row-button-selected-bg-color": "#689d6a",
"menu-selected-color": "#504945",
"icon-button-color": "#928374",
"accent": "#689d6a",
"alert": "#cc241d",
"username-colors": [
"#cc241d",
"#98971a",
"#d79921",
"#458588",
"#b16286",
"#689d6a",
"#a89984",
"#d65d0e"
]
}
}
]
}
}
}
@@ -4,19 +4,19 @@ metadata:
name: ess-mas-values-secret
namespace: matrix
stringData:
values.yaml: ENC[AES256_GCM,data:K692tDSLoftfKmsd4nIc8BzJw9CwXkOc4qSGTw78qF7L7UolTlH456WYeuwXiajDHmsVJCLhxDjCAhGyYUgbEC1mwuX1ZpkJefJkXvkGtQqnVbpS5MLYWt2SMQo+Yuz5L3Qxkop8Y02/U8jFxu5w7LQT71aAJphI1Po6r0wWjCIP0pDLTSUXd5Tk4eLq7o1a8nCrkJxSDQc95sRPTlZ/cfVhBE4xDzDK/CD/Yajv6yUceqUr7j7aBHOlg82+y0HU8xY44B/NtAzphJJf706z0X80Q5Dboeks5Rj9Na0lld5hwcOQPocYO28Tb6RDYOkNOQUELZKoc64JcpYJNyUkxfJl0hjtC1TmtAy1ZljLytnbnc9abNnHWgKcBt9TCcLIJJbZAI2snSHON8uNYpf7+erXx0wHVGxL/WZWEiFlAhnTfNx/Dqr2l0tKTjzRgibSpeA9rBmEm1/2PnJjexRpyewbS2MASGy2C1lfqaAPfjxf,iv:tguaH5u/67ubPSzhrhk3QMkpsE3flvrEZf50U3bcXvc=,tag:KY3T9RcV9KVBKb1SKXCjmw==,type:str]
values.yaml: ENC[AES256_GCM,data:B9nRZWCUnWxWhnlOTzSdOZrO+aiKwotKZhjYTwTBlgy9zVGoAn81yy6s2QJySeAI/rXcKamQ/P3fewDsLDzX8zxbvHS1GZ/F1fPx7H4tan9/1tu7XvNx52DWMCO8UtmpwNb1gAaa0kLbs+u1dTKA3Bk9b1gWsmP5OGo+C43knmhF3YvDOwv5wO96iaAzMwpjpxB78AkOvkshLEbP/o+z/DKsLxtoeQ0tPzkXjQj8d/2Jaj+3Ve8uqSNbnAEoL6gTDr+qvQs2JU/jvilK/fKgkR4hF54opthEMzKV7pbUYia2a+5yOmmQaBXQ5cUqi7wrTVyle/GyXHHOLAU37sGYhKyIGjgMu8rGTHMQgm3c5rGt4wLps48+LN4/S74tbTgyBf8V6hTGfNXIpMDkjXpPujZLzCKSx8V+7NlQDcs8FGobOLdmmcBzj3WGVMJLt/IA1/hv4xj4sP7CeQqG0UpWhJLXxY+ghCGdGJ7dA2wme+hreb1ywrhgVVnGH+plSG9BmTIye4sgCJwnZ9ER6T42ptsw70GaqfFazsgebeKEM+grJLfx+f4YyPexDaGZbrrZ5nxvOtvVz6d8Cx4zuP7jt+7RwzZiXPlYjsgvwwypuafIh/Uk0ULeFiSRLKLEGItRcOTdlVUbOOP9bW1Is42PzsnDnx0Mc0v4LzQCQTYJL3amfgOeGVJ1I8zS6okM8sZE3F0jzGvaFTNbeMiIkealUhjQrhF/5MweiNUIOUzAp/DLFT2mBg9g24imLtDCglEV0WwVQACd9GzQLNRnjNmaId7ISeYfUH/gORqyUJlICaRFB1xH+MMzy09tP2zuYcY+sIFJotYF29J0wC8oQS2KsKQiHEPVTLYTzDsB0RU4pGD6471rgIByXQrlmawo9HtjZxfZuzw7+AEa9uOBohbNkOwDUQGoLqgssAefAz4H/LC0NmBONR7iVzYcWv3RgDhsiafkUq6PgyXYEleB7Euk1wxX9mxUEqfuVMbeDceog2Q41vWBmFkFnSUYXHbN/Ff8DfWe+zjcSKDzjSchU54iYTbuuKnDo+G/jEk/FNGcp/xOh0/JyYWcqO5DozBPlIJ5QxgWkQTWC8oSM1QmTxiSj+FYDZFdyOXSXsqF8a9BJaEh5nACUbqwpx+UnRnJpcIcOlYasDwThNSNZIkpqqf2e7vsZHMTakkTbHOcWB7IiYWOCQLdzStQn3URWIobKn3T+mbbDerWzF2a9UEBbCwlTbA/OoyeWW4j1uPo29ek6F6Lb7JdxibNO6U5JIZkYP92Ci8W7skFAn7rEe203qnlvzoxhKxE+dA5rM+Tr4FWz9qAa/u3EhPxVvPK+0Wpb6HAUbFGbm+MSD7uqlcnvVxZfU2dSbpb/J56Bq8vzmJKcaStxoE4lDG+uwa2I/o0f1BMdwWvnmKEAJa593NYS9lw4s2dR3IwFkeE8hp3Q6yxaFk5v9m5/ivXOwiaOJ238HqDBJrfCQidIe+RaqydPxqqOj7WV1YZFU6DxZJ30LcyH45MJQqC5TGz2W1Unwljl3arh/hQRb1G3eGNw4kO86CipioKTeDBPpoKfgNFbz8qiNPSCRXPtJve+OOptDojLJ5iR+HO6olmSu+OGww7+BwTvBraW/xIAGq7y/gzZ8aMgsqIMyHJZiuC9+uQwquKiFEZ5pD5vAe0R9ROdFHgWsdKdEXwJtaCqBAfqWrZkrgoeWFsPYFwT3BhusQfMyWKv6SSOmWk2bx4FZ74+HeZcTsJWVcCmF4dbqfDvM5sz3b4Mek0wjUZGKtVyfFQ1VoZm6/G6UgCEt25A37AO11H/sW90Bs2nSpbOJFbgeBsNkXMptWrCyyHehof+JWeGuTFTg3etsaBgW4b1rJRzbJUQHJKVXjIOPZIQNd0WS7y+IDheDovQobfjSWn6TRVnXvEnzaD5WxY54EqOivAyv1U7/r7KMzo26Bp9s7ZQX/UeNmTn1dgkZoWNpTV8LkcrbRgIpkSNG9Pj+6GOtdUIxobtLeZ9h5Oso/TSKoDaHbkfoSVhW3FgQ7aJHHvx0j2qp/hVQfvABCPILW2DJvtNyIOU5zgV7yHJF/Dw7PpJfw636WxQHaTehbOav1ImE0FX0VF6NmkhuEpPEVHr0lejGse+0XkVzMuxoi/bE4h7WFIfbiqdlw=,iv:KnhgvxrFd+6BEBuBUKtQgKEfx81G9uJ+CY4Wrm43Pjw=,tag:1OQs0MQ1Gvf1LwXLaGwyjQ==,type:str]
sops:
age:
- recipient: age14l0hwfqylwpemz5y2ghh2yxk0phszlnj3qlejhue0fw0kz3tmfgqdsjzdh
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBJYUEvbDF0azNsM0RSS3pZ
eG1sNEZKOXAzUVRZWWVTbjlpazJqaVpBRWtBCkE4SW9qVVhxa1dKSXhXV2pSN0lK
VVZET0E2bWZSclkvSzlqdlRxcjVNOEEKLS0tIEZtdm9xRU81WXpkTGQ1UW5HZUl2
UERlYmtRc21PSnFGNjFkZFVVcm9nUTgKuxWcqg1MRWq7Yhp61lHHKPZsdiNeHXSd
72OCNMaUiz58kTKt+tpthUxXlGUlf3y0VA1/KigeA+L5Z2bk/bdMmw==
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB2RHgzUE5QVVVCdkJ5aWNj
VmFHTUs2TXpXYzE2VmJnMHFNek1RT0FnUVVFCmJqRHUzY2F3WWt1L1FLbVpDWWFO
QzN1akRQdjBBVENIWHhGOGgrZFdqZGsKLS0tIEVRMGRrS05zS1ErdFZZUmZ4TUVv
U1dXMWltRlY1cGx2TFI4YjRreVBPQVkK9UFiAiSANa7HekQxufsFSkMQoL18kGmi
cP0jf27NGFpAjC8AmuMWgMydYDGXyRgFRU5JDqGCYAsgZsrGgjIWkA==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2026-04-22T13:49:01Z"
mac: ENC[AES256_GCM,data:wHDBVBTvNycXTKTVBK1MNrGg2C0XufPpNTx9ksp1V5rtfcPvZNbiw8aUcjHt6hNtT3tPPmUt82jwzAbAm3hnPsAVfnue+bB2BZ91AI11tcqr4o+l3djeJODntF5d6TfdIWchtwMisy7bBMtLPS4Vs++JPbP90p/CtCdOxPwh1CQ=,iv:CQ24u8xSpzG3QU4JzzpZIx5cBrMH5RxVPl/S6gLylxU=,tag:f8FdKvtyxz8ks0gV6ct9iQ==,type:str]
lastmodified: "2026-05-15T09:05:44Z"
mac: ENC[AES256_GCM,data:gFPsb3LCjoPglcPEmLEe8hFQSsrcsZCMtVCf7L8jNLEgsL5XUIEF/BEoT7I+wPisRclAtq2qOkBd3TqmxQWAaPbRQ0+RDHU49XD5rWavDv7/CA1QUCLL/RNTbuURyS9iri/F4xneeYLwKJxJCgmMEiaqRPaAnHioxFtPreEiREg=,iv:FgWNZJUOydGY/m0SlZLWtWefIstMG7ccju6h8BLuVho=,tag:MMXoxQIA8ZNl5qBJjuzdpw==,type:str]
encrypted_regex: ^(data|stringData)$
version: 3.12.2
@@ -1,22 +0,0 @@
apiVersion: v1
kind: Secret
metadata:
name: ess-mas-custom-secrets
namespace: matrix
stringData:
values.yaml: ENC[AES256_GCM,data:Vk5TvaBui8gK6ftcRvZA8aEmw+QmWhn6pp9/2T5ndw8YPiggAtpqZszms5yoOxPK2Jy+GAVZ3M1whWFrdqy2WPjCAVEKlg5i7qeC1wKfgjMV2txWRlQjYAu2n4IYxi1aOaFcqzxLGYclAlQh6J0PmSYKtbWZO0nsjjcbXt9AXRgmjjZnIcNEBcQg9DoSew4MiHr5BQ0dsN0hJx96UhtTAVwssDgE1eKlTz2K/VD/xz3Odd/WuFKjThpa4E3p66Azc+4WNuQpxFejUgEg5ZzMvup8GGEzH5L3jBtxdBNmNFn6GNwFfYwq/ohWTXafVTj1mEpwTYBcKEtsmks8fl/qzb6QBobOOPqKTD4chRhv72yeYE7rv9x6530TxNXGUd++jxtQm1G+ZOjje05k3x2i9SQv5CNBM2jSyoh2uktoVyaJLpgJMv35VdyLAWY+nNXD4yj0isI=,iv:7GmbMaMDWY+agQC3Xr9SpOzGPyb82RDCSEyPHaWwMhQ=,tag:FUpe4v9BybS0M+8L7yvB4A==,type:str]
sops:
age:
- recipient: age14l0hwfqylwpemz5y2ghh2yxk0phszlnj3qlejhue0fw0kz3tmfgqdsjzdh
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBCUWlDYkY4NVpWNFVHb1Bo
b3FpR0w5NmttcW9IV1RXWENwTnE2NU9tR0hvCnJyNU9xWEpYZWRXVURtTis3aEp2
MmtCZXZXemlKNnZrbW1OL0NlMXlQbzQKLS0tIG81ZXIyd2pGOVlXeEVtRy92RzFp
VU9sa0tOdlBqcnZ2VTVaQWpaN3Y3L0UKUel3SRju6rFbMa9Di++EAci8+fsMjx4X
X8fM/N4jTHerXOBCT/bnD/Tk/2jAcwg9PC3e906VDUdsIZw+xU+grw==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2026-04-22T10:12:35Z"
mac: ENC[AES256_GCM,data:zMIPahMwgSgF9vdEkp7DyECL3s6IuEHWs0JE1G9A+xYmm0mAFYbzMPwgg6oB57gNpX3EaPovHOOvKlTjZVWUbv892mU63GPi4W5kRxsZya5FyC3PxyZlsvMX9U/F8Hwvo9tymbGYpsGGkxv0doRZvTuKDzj1j1+VO0VVcY2i3Gc=,iv:dq6GtQ0vfQWiew6s6gWjPRZPtQ1rD3cO3aQXPDT0Sos=,tag:7UrcQ8Q420HK/vW01SsRkg==,type:str]
encrypted_regex: ^(data|stringData)$
version: 3.12.2
@@ -22,4 +22,51 @@ data:
- '::1/128'
- 'fe80::/10'
- 'fc00::/7'
max_spider_size: 10M
max_spider_size: 10M
retention:
config: |
retention:
enabled: true
default_policy:
min_lifetime: 1d
max_lifetime: 1y
allowed_lifetime_min: 1d
allowed_lifetime_max: 2y
purge_jobs:
- longest_max_lifetime: 3d
interval: 12h
- shortest_max_lifetime: 3d
longest_max_lifetime: 1w
interval: 1d
- shortest_max_lifetime: 1w
interval: 2d
media_retention:
local_media_lifetime: 365d
remote_media_lifetime: 90d
redaction_retention_period: 7d
forgotten_room_retention_period: 28d
user_ips_max_age: 90d
auto_join:
config: |
auto_join_rooms:
- "#onboarding:axion1337.chat"
auto_join_rooms_for_guests: false
room_publish:
config: |
room_list_publication_rules:
- user_id: "*"
action: allow
turn:
config: |
turn_uris:
- "turn:turn.axion1337.chat?transport=udp"
- "turn:turn.axion1337.chat?transport=tcp"
- "turns:turn.axion1337.chat?transport=tcp"
turn_shared_secret: "cab3c8408363515d9b4cdc3384a1f76ca17a973242fdfdc72b67ac4d86158527"
turn_user_lifetime: 86400000
turn_allow_guests: false
oembed:
config: |
oembed_enabled: true
+17 -1
View File
@@ -4,7 +4,7 @@ metadata:
name: matrix-stack
namespace: matrix
spec:
interval: 1h
interval: 5m
chart:
spec:
chart: matrix-stack
@@ -43,6 +43,10 @@ spec:
enabled: true
ingress:
host: matrix.axion1337.chat
additional:
oembed:
config: |
oembed_enabled: true
# Matrix Authentication Service braucht eine Subdomain
matrixAuthenticationService:
@@ -55,10 +59,22 @@ spec:
enabled: true
ingress:
host: mrtc.axion1337.chat
# Chart default (20Mi request+limit) OOM-killed the authorisation service after
# ~74 days of uptime (2026-07-28) - too tight for a long-running Go service.
resources:
requests:
memory: 64Mi
cpu: 50m
limits:
memory: 128Mi
# Element Web
elementWeb:
enabled: true
image:
registry: rohana.axion1337.de
repository: sorb/threadnet-web
tag: v0.2.3-elementcall-h264
ingress:
host: axion1337.chat
+514
View File
@@ -0,0 +1,514 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: ess-element-web-docs
namespace: matrix
data:
# HTML Index Page
"index.html": |
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Element Desktop Setup - aXion1337.Chat</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
padding: 40px 20px;
}
.container {
max-width: 800px;
margin: 0 auto;
background: white;
border-radius: 12px;
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
padding: 40px;
}
h1 {
color: #333;
margin-bottom: 10px;
font-size: 2.5em;
}
.subtitle {
color: #666;
margin-bottom: 40px;
font-size: 1.1em;
}
.section {
margin-bottom: 40px;
}
.section h2 {
color: #667eea;
font-size: 1.5em;
margin-bottom: 20px;
border-bottom: 3px solid #667eea;
padding-bottom: 10px;
}
.download-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 20px;
margin-bottom: 30px;
}
.download-card {
background: #f8f9fa;
border: 2px solid #e9ecef;
border-radius: 8px;
padding: 20px;
text-align: center;
transition: all 0.3s ease;
text-decoration: none;
color: #333;
}
.download-card:hover {
border-color: #667eea;
background: #f0f3ff;
transform: translateY(-5px);
box-shadow: 0 10px 30px rgba(102, 126, 234, 0.2);
}
.download-card .icon {
font-size: 2.5em;
margin-bottom: 10px;
}
.download-card .name {
font-weight: 600;
font-size: 1.1em;
margin-bottom: 5px;
}
.download-card .desc {
font-size: 0.9em;
color: #666;
}
.themes {
background: #f8f9fa;
border-left: 4px solid #667eea;
padding: 20px;
border-radius: 4px;
margin-bottom: 20px;
}
.themes h3 {
color: #333;
margin-bottom: 15px;
}
.theme-list {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
gap: 10px;
}
.theme-item {
background: white;
padding: 10px;
border-radius: 4px;
text-align: center;
color: #667eea;
font-weight: 500;
border: 1px solid #667eea;
}
.instructions {
background: #e7f3ff;
border-left: 4px solid #0066cc;
padding: 15px;
border-radius: 4px;
margin: 15px 0;
line-height: 1.6;
}
.instructions code {
background: #f0f0f0;
padding: 2px 6px;
border-radius: 3px;
font-family: monospace;
}
.support {
text-align: center;
color: #666;
margin-top: 40px;
padding-top: 20px;
border-top: 1px solid #e9ecef;
}
.support a {
color: #667eea;
text-decoration: none;
font-weight: 500;
}
.support a:hover {
text-decoration: underline;
}
</style>
</head>
<body>
<div class="container">
<h1>🎨 Element Desktop Setup</h1>
<p class="subtitle">Automatische Konfiguration mit Custom Themes für aXion1337.Chat</p>
<div class="section">
<h2>📥 Download Setup-Script</h2>
<div class="download-grid">
<a href="element-setup-windows.cmd" class="download-card" download>
<div class="icon">🪟</div>
<div class="name">Windows</div>
<div class="desc">.cmd Datei</div>
</a>
<a href="element-setup-macos.command" class="download-card" download>
<div class="icon">🍎</div>
<div class="name">macOS</div>
<div class="desc">.command Datei</div>
</a>
<a href="element-setup-linux.sh" class="download-card" download>
<div class="icon">🐧</div>
<div class="name">Linux</div>
<div class="desc">.sh Datei</div>
</a>
</div>
<div class="instructions">
<strong>Anleitung:</strong><br><br>
<strong>🪟 Windows:</strong> Datei herunterladen → Doppelklick → Script läuft automatisch<br><br>
<strong>🍎 macOS:</strong> Datei herunterladen → Doppelklick im Finder → Script läuft automatisch<br><br>
<strong>🐧 Linux:</strong><br>
<code>chmod +x element-setup-linux.sh</code><br>
<code>./element-setup-linux.sh</code>
</div>
</div>
<div class="section">
<h2>🎨 Verfügbare Themes</h2>
<div class="themes">
<h3>Automatisch geladen in Element:</h3>
<div class="theme-list">
<div class="theme-item">aXion1337 Dark</div>
<div class="theme-item">Deep Purple</div>
<div class="theme-item">Discord Dark</div>
<div class="theme-item">Electric Blue</div>
<div class="theme-item">Everforest dark hard</div>
<div class="theme-item">Gruvbox Dark</div>
<div class="theme-item">Wal</div>
</div>
</div>
</div>
<div class="section">
<h2>❓ Support</h2>
<p>Für weitere Hilfe besuche: <a href="https://element.io/help" target="_blank">element.io/help</a></p>
<p>🔐 <a href="security.html">Konto-Sicherheit (Passkey/2FA einrichten)</a></p>
</div>
<div class="support">
<p>Element wird automatisch installiert und konfiguriert.<br>
<small>Bei Fragen oder Problemen: <a href="https://element.io/help">Element Support</a></small></p>
</div>
</div>
</body>
</html>
# Security / 2FA setup page
"security.html": |
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Konto-Sicherheit - aXion1337.Chat</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
padding: 40px 20px;
}
.container {
max-width: 800px;
margin: 0 auto;
background: white;
border-radius: 12px;
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
padding: 40px;
}
h1 { color: #333; margin-bottom: 10px; font-size: 2.5em; }
.subtitle { color: #666; margin-bottom: 40px; font-size: 1.1em; }
.section { margin-bottom: 40px; }
.section h2 {
color: #667eea;
font-size: 1.5em;
margin-bottom: 20px;
border-bottom: 3px solid #667eea;
padding-bottom: 10px;
}
.download-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 20px;
margin-bottom: 30px;
}
.download-card {
background: #f8f9fa;
border: 2px solid #e9ecef;
border-radius: 8px;
padding: 20px;
text-align: center;
transition: all 0.3s ease;
text-decoration: none;
color: #333;
}
.download-card:hover {
border-color: #667eea;
background: #f0f3ff;
transform: translateY(-5px);
box-shadow: 0 10px 30px rgba(102, 126, 234, 0.2);
}
.download-card .icon { font-size: 2.5em; margin-bottom: 10px; }
.download-card .name { font-weight: 600; font-size: 1.1em; margin-bottom: 5px; }
.download-card .desc { font-size: 0.9em; color: #666; }
.instructions {
background: #e7f3ff;
border-left: 4px solid #0066cc;
padding: 15px;
border-radius: 4px;
margin: 15px 0;
line-height: 1.6;
}
.support {
text-align: center;
color: #666;
margin-top: 40px;
padding-top: 20px;
border-top: 1px solid #e9ecef;
}
.support a { color: #667eea; text-decoration: none; font-weight: 500; }
.support a:hover { text-decoration: underline; }
</style>
</head>
<body>
<div class="container">
<h1>🔐 Konto-Sicherheit</h1>
<p class="subtitle">Zwei-Faktor-Authentifizierung ist optional - richte sie nur ein, wenn du sie nutzen möchtest.</p>
<div class="section">
<h2>🔑 Einrichten</h2>
<div class="download-grid">
<a href="https://auth.axion1337.chat/if/flow/default-authenticator-webauthn-setup/" class="download-card" target="_blank">
<div class="icon">🔑</div>
<div class="name">Passkey</div>
<div class="desc">WebAuthn / Sicherheitsschlüssel</div>
</a>
<a href="https://auth.axion1337.chat/if/flow/default-authenticator-totp-setup/" class="download-card" target="_blank">
<div class="icon">📱</div>
<div class="name">TOTP</div>
<div class="desc">Authenticator-App</div>
</a>
</div>
<div class="instructions">
<strong>Hinweis:</strong> Du musst bei <code>auth.axion1337.chat</code> eingeloggt sein, damit die
Einrichtung funktioniert. Ohne konfiguriertes Gerät wird beim Login einfach kein zweiter Faktor abgefragt -
2FA ist nie Voraussetzung zum Anmelden.
</div>
</div>
<div class="support">
<p><a href="index.html">← Zurück zum Setup</a></p>
</div>
</div>
</body>
</html>
# README
"README-Element-Setup.md": |
# Element Desktop Setup Scripts
Automatische Konfiguration und Installation von Element Desktop mit Custom Themes für aXion1337.Chat
## 🎨 Verfügbare Themes
- aXion1337 Dark
- Deep Purple
- Discord Dark
- Electric Blue
- Everforest dark hard
- Gruvbox Dark
- Wal
## 🪟 Windows
Herunterladen: `element-setup-windows.cmd` → Doppelklick
## 🍎 macOS
Herunterladen: `element-setup-macos.command` → Doppelklick im Finder
## 🐧 Linux
```bash
chmod +x element-setup-linux.sh
./element-setup-linux.sh
```
Support: https://element.io/help
# Windows Script
"element-setup-windows.cmd": |
@echo off
REM Element Desktop Setup Script for Windows
setlocal enabledelayedexpansion
echo ========================================
echo Element Desktop Konfiguration Setup
echo ========================================
echo.
set APPDATA_PATH=%APPDATA%\Element
set CONFIG_FILE=%APPDATA_PATH%\config.json
if not exist "%APPDATA_PATH%" (
echo Erstelle Element Verzeichnis...
mkdir "%APPDATA_PATH%"
)
echo Erstelle config.json...
(
echo {
echo "configUrl": "https://axion1337.chat/config.json",
echo "brand": "aXion1337.Chat",
echo "default_theme": "aXion1337 Dark",
echo "show_labs_settings": true,
echo "features": {
echo "feature_qr_code_login": true
echo },
echo "setting_defaults": {
echo "custom_themes": []
echo }
echo }
) > "%CONFIG_FILE%"
echo Config erstellt: %CONFIG_FILE%
echo.
echo Ueberpruefen Sie ob Element Desktop installiert ist...
where element >nul 2>nul
if %ERRORLEVEL% == 0 (
echo Starte Element Desktop...
start element
timeout /t 2 >nul
echo Done!
pause
exit /b 0
)
winget list --name "Element" >nul 2>nul
if %ERRORLEVEL% == 0 (
echo WinGet gefunden. Installiere Element...
winget install Element.Element --silent
timeout /t 3 >nul
start element
pause
exit /b 0
)
echo.
echo Element Desktop konnte nicht automatisch installiert werden.
echo Bitte installiere Element Desktop manuell:
echo https://element.io/download
echo.
pause
# macOS Script
"element-setup-macos.command": |
#!/bin/bash
echo "========================================"
echo "Element Desktop Konfiguration Setup"
echo "========================================"
echo ""
CONFIG_DIR="$HOME/Library/Application Support/Element"
CONFIG_FILE="$CONFIG_DIR/config.json"
if [ ! -d "$CONFIG_DIR" ]; then
echo "Erstelle Element Verzeichnis..."
mkdir -p "$CONFIG_DIR"
fi
echo "Erstelle config.json..."
cat > "$CONFIG_FILE" << 'EOF'
{
"configUrl": "https://axion1337.chat/config.json",
"brand": "aXion1337.Chat",
"default_theme": "aXion1337 Dark",
"show_labs_settings": true,
"features": {
"feature_qr_code_login": true
},
"setting_defaults": {
"custom_themes": []
}
}
EOF
echo "Config erstellt: $CONFIG_FILE"
echo ""
echo "Ueberpruefen Sie ob Element Desktop installiert ist..."
if [ -d "/Applications/Element.app" ]; then
echo "Element im Applications Folder gefunden. Starte Element..."
open -a Element
sleep 2
exit 0
fi
if command -v brew &> /dev/null; then
echo "Installiere Element uber Homebrew..."
brew install element --cask
sleep 2
open -a Element
exit 0
else
echo ""
echo "Homebrew nicht gefunden. Bitte installiere zuerst:"
echo "https://brew.sh"
echo ""
echo "Deine config.json wurde erstellt unter:"
echo "$CONFIG_FILE"
echo ""
fi
read -p "Druecke Enter zum Beenden..."
# Linux Script
"element-setup-linux.sh": |
#!/bin/bash
echo "========================================"
echo "Element Desktop Konfiguration Setup"
echo "========================================"
echo ""
CONFIG_DIR="$HOME/.config/Element"
CONFIG_FILE="$CONFIG_DIR/config.json"
if [ ! -d "$CONFIG_DIR" ]; then
echo "Erstelle Element Verzeichnis..."
mkdir -p "$CONFIG_DIR"
fi
echo "Erstelle config.json..."
cat > "$CONFIG_FILE" << 'EOF'
{
"configUrl": "https://axion1337.chat/config.json",
"brand": "aXion1337.Chat",
"default_theme": "aXion1337 Dark",
"show_labs_settings": true,
"features": {
"feature_qr_code_login": true
},
"setting_defaults": {
"custom_themes": []
}
}
EOF
echo "Config erstellt: $CONFIG_FILE"
echo ""
if command -v apt &> /dev/null; then
echo "Installiere Element uber apt..."
sudo apt update && sudo apt install -y element-desktop
element &
exit 0
fi
if command -v dnf &> /dev/null; then
echo "Installiere Element uber dnf..."
sudo dnf install -y element-desktop
element &
exit 0
fi
if command -v pacman &> /dev/null; then
echo "Installiere Element uber pacman..."
sudo pacman -S --noconfirm element-web
element &
exit 0
fi
echo "Element Desktop konnte nicht automatisch installiert werden."
echo "Bitte installiere Element Desktop manuell:"
echo "Ubuntu/Debian: sudo apt install element-desktop"
echo "Fedora/RHEL: sudo dnf install element-desktop"
echo "Arch: sudo pacman -S element-web"
echo ""
echo "Deine config.json wurde erstellt unter:"
echo "$CONFIG_FILE"
+93
View File
@@ -0,0 +1,93 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: element-web-docs
namespace: matrix
spec:
replicas: 1
selector:
matchLabels:
app: element-web-docs
template:
metadata:
labels:
app: element-web-docs
spec:
initContainers:
- name: copy-files
image: busybox:1.36
command: ["/bin/sh", "-c"]
args:
- |
mkdir -p /html/docs/setup
cp /config/index.html /html/docs/setup/
cp /config/security.html /html/docs/setup/
cp /config/README-Element-Setup.md /html/docs/setup/
cp /config/element-setup-windows.cmd /html/docs/setup/
cp /config/element-setup-macos.command /html/docs/setup/
cp /config/element-setup-linux.sh /html/docs/setup/
chmod 644 /html/docs/setup/*
volumeMounts:
- name: config
mountPath: /config
- name: html
mountPath: /html
containers:
- name: nginx
image: nginx:1.26-alpine
ports:
- containerPort: 8080
volumeMounts:
- name: nginx-conf
mountPath: /etc/nginx/conf.d/default.conf
subPath: nginx.conf
- name: html
mountPath: /usr/share/nginx/html
volumes:
- name: config
configMap:
name: ess-element-web-docs
- name: nginx-conf
configMap:
name: element-web-docs-nginx
- name: html
emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
name: element-web-docs
namespace: matrix
spec:
selector:
app: element-web-docs
ports:
- protocol: TCP
port: 80
targetPort: 8080
type: ClusterIP
---
apiVersion: v1
kind: ConfigMap
metadata:
name: element-web-docs-nginx
namespace: matrix
data:
nginx.conf: |
server {
listen 8080;
server_name _;
root /usr/share/nginx/html;
location /docs/setup/ {
index index.html;
try_files $uri $uri/ =404;
}
location / {
return 404;
}
}
+21 -1
View File
@@ -1,13 +1,33 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
# Patch: Fügt einen Checksum der element-values.yaml zur HelmRelease hinzu
# Damit wird Flux die HelmRelease neu-synced wenn sich die ConfigMap ändert
patches:
- target:
kind: HelmRelease
name: matrix-stack
namespace: matrix
patch: |-
- op: add
path: /metadata/annotations/element-config-checksum
value: "401f8a87d0ef5d91d2e5032d4aede42c"
resources:
- matrix-postgres-auth.yaml
- cert-issuer.yaml
- matrix-certificates.yaml
# Neue Dateien:
- custom-configs/synapse-values.yaml
- custom-configs/element-values.yaml
- custom-configs/mas-secret.yaml
- element-web-docs-configmap.yaml
- element-web-docs-server.yaml
# TURN Server für WebRTC
- coturn-secret.yaml
- coturn.yaml
# HelmRelease (muss ganz unten stehen, damit die ConfigMaps vorher da sind!)
- element-server-suite.yaml
# Custom Apex Ingress für Element Web + Well-Known auf axion1337.chat
- apex-ingress.yaml # Custom Apex Ingress für Element Web + Well-Known auf axion1337.chat
- apex-ingress.yaml # Custom Apex Ingress für Element Web + Well-Known auf axion1337.chat
- networkpolicy.yaml
+13
View File
@@ -62,3 +62,16 @@ spec:
kind: ClusterIssuer
dnsNames:
- admin.axion1337.chat
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: turn-axion1337-chat-cert
namespace: matrix
spec:
secretName: turn-axion1337-chat-tls
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
dnsNames:
- turn.axion1337.chat
+293
View File
@@ -0,0 +1,293 @@
# Default-deny ingress for the matrix namespace, with explicit allow rules per component.
# Egress is intentionally untouched (federation to arbitrary Matrix servers, ACME, SMTP,
# DNS all stay unrestricted).
#
# Lesson learned deploying the authentik namespace's equivalent policy: NetworkPolicy
# filters on the pod's actual container port, not the Service's external port (e.g.
# authentik-server's Service maps 80->9000). Wherever a Service here uses a *named*
# targetPort, this file references that name directly instead of guessing a number -
# Kubernetes resolves it from the pod spec, which is safer than a hardcoded port.
#
# matrix-stack-postgres already effectively has no dedicated chart NetworkPolicy of its
# own (unlike authentik-postgresql's Bitnami one) - the rules below are the only gate.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
namespace: matrix
spec:
podSelector: {}
policyTypes:
- Ingress
---
# axion1337.chat (root) -> Element Web
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-ingress-element-web
namespace: matrix
spec:
podSelector:
matchLabels:
app.kubernetes.io/name: element-web
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
ports:
- protocol: TCP
port: element
---
# admin.axion1337.chat -> Element Admin
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-ingress-element-admin
namespace: matrix
spec:
podSelector:
matchLabels:
app.kubernetes.io/name: element-admin
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
ports:
- protocol: TCP
port: http
---
# axion1337.chat/docs/setup -> Element desktop setup docs (our own nginx)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-ingress-element-web-docs
namespace: matrix
spec:
podSelector:
matchLabels:
app: element-web-docs
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
ports:
- protocol: TCP
port: 8080
---
# matrix.axion1337.chat AND the well-known delegation both front through haproxy
# (matrix-stack-synapse and matrix-stack-well-known Services both target haproxy's
# named ports, not synapse-main directly).
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-ingress-haproxy
namespace: matrix
spec:
podSelector:
matchLabels:
app.kubernetes.io/name: haproxy
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
ports:
- protocol: TCP
port: haproxy-synapse
- protocol: TCP
port: haproxy-403
- protocol: TCP
port: haproxy-wkd
---
# account.axion1337.chat (Traefik) + matrix.axion1337.chat (also routes to MAS for some
# paths) + synapse-main calling MAS's internal port for session/token introspection.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-ingress-mas
namespace: matrix
spec:
podSelector:
matchLabels:
app.kubernetes.io/name: matrix-authentication-service
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
- podSelector:
matchLabels:
app.kubernetes.io/name: synapse-main
ports:
- protocol: TCP
port: 8080
- from:
- podSelector:
matchLabels:
app.kubernetes.io/name: synapse-main
ports:
- protocol: TCP
port: 8081
---
# Synapse itself: reached via haproxy (same namespace), calls from MAS (provisioning),
# metrics scraped by Alloy (monitoring namespace).
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-ingress-synapse
namespace: matrix
spec:
podSelector:
matchLabels:
app.kubernetes.io/name: synapse-main
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app.kubernetes.io/name: haproxy
- podSelector:
matchLabels:
app.kubernetes.io/name: matrix-authentication-service
ports:
- protocol: TCP
port: synapse-http
- protocol: TCP
port: synapse-health
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: monitoring
ports:
- protocol: TCP
port: synapse-metrics
---
# mrtc.axion1337.chat (Traefik) for the auth handshake, plus Alloy scraping metrics.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-ingress-rtc-authorisation-service
namespace: matrix
spec:
podSelector:
matchLabels:
app.kubernetes.io/name: matrix-rtc-authorisation-service
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
- podSelector:
matchLabels:
app.kubernetes.io/name: matrix-rtc-sfu
ports:
- protocol: TCP
port: http
---
# The SFU: mrtc.axion1337.chat (Traefik) for signalling, Alloy for metrics, and the
# NodePort-exposed WebRTC media ports need to stay open to the internet by design -
# that's the actual point of a TURN/SFU media relay, not a mistake.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-ingress-rtc-sfu
namespace: matrix
spec:
podSelector:
matchLabels:
app.kubernetes.io/name: matrix-rtc-sfu
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
ports:
- protocol: TCP
port: http
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: monitoring
ports:
- protocol: TCP
port: metrics
- from:
- ipBlock:
cidr: 0.0.0.0/0
ports:
- protocol: TCP
port: 30001
- protocol: UDP
port: 30002
---
# Postgres: only Synapse and MAS need data access; Alloy scrapes the exporter.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-ingress-postgres
namespace: matrix
spec:
podSelector:
matchLabels:
app.kubernetes.io/name: postgres
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app.kubernetes.io/name: synapse-main
- podSelector:
matchLabels:
app.kubernetes.io/name: matrix-authentication-service
ports:
- protocol: TCP
port: 5432
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: monitoring
ports:
- protocol: TCP
port: 9187
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-ingress-acme-solver
namespace: matrix
spec:
podSelector:
matchLabels:
acme.cert-manager.io/http01-solver: "true"
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
ports:
- protocol: TCP
port: 8089
# Note: coturn runs with hostNetwork: true, so NetworkPolicy does not apply to it at all -
# it's already gated by the Hetzner Cloud Firewall instead. Nothing to write here.
@@ -0,0 +1,18 @@
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: authentik-apps
namespace: flux-system
spec:
interval: 1m
path: ./apps/authentik
prune: true
sourceRef:
kind: GitRepository
name: flux-system
decryption:
provider: sops
secretRef:
name: sops-age
dependsOn:
- name: infra-apps
@@ -4,4 +4,6 @@ resources:
- gotk-components.yaml
- gotk-sync.yaml
- infra-sync.yaml
- monitoring-sync.yaml
- production-sync.yaml
- authentik-sync.yaml
@@ -0,0 +1,14 @@
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: monitoring-apps
namespace: flux-system
spec:
interval: 10m
path: ./apps/monitoring
prune: true
sourceRef:
kind: GitRepository
name: flux-system
dependsOn:
- name: infra-apps
Executable
+599
View File
@@ -0,0 +1,599 @@
# aXion1337.Chat Task List & Meilensteine
**Last Updated**: 2026-07-28
**Statusübersicht**: [✅ 13 Abgeschlossen] [🔄 0 In Progress] [📋 8+ Pending] [🔒 10 Security]
---
## 📊 Status Summary (Quick View)
| Kategorie | Count | Status | Details |
|-----------|-------|--------|---------|
| **Completed** | 13 | ✅ Done | K3S, Flux, ESS, Themes, Desktop, Monitoring, TURN, Authentik (Deploy+Enrollment/Recovery/2FA), Firewall, SSH, coturn Fix, Element Call Fork, NetworkPolicies |
| **In Progress** | 0 | 🔄 — | — |
| **Backlog** | 8+ | 📋 Pending | DB Backups, PostgreSQL Migration, MAS-Template-Link, VP9-Retry |
| **Security Tasks** | 5 | 🔒 Pending | auditd, Kernel hardening, CrowdSec, Falco, WAF |
### Priority Distribution
| Priority | Count | Timeline |
|----------|-------|----------|
| 🔴 **CRITICAL** | 3 | This week |
| 🟠 **HIGH** | 4 | 12 weeks |
| 🟡 **MEDIUM** | 8 | ~1 month |
| 🟢 **LOW** | 4+ | Nice-to-have |
---
## 🗓️ Session-Zusammenfassung 2026-07-27/28 (fortlaufend aktualisiert)
Nach längerer Pause wiederaufgenommen — Mac war neu aufgesetzt, Zugriff (SSH, Kubeconfig,
age-Key, Homebrew/flux/helm/sops/age) komplett wiederhergestellt und dauerhaft in `~/.zshrc`
verankert. Was in dieser Session erledigt wurde:
1. **Authentik Enrollment/Recovery/2FA** (Issue #7 ✅ geschlossen) — siehe Phase 8 unten und
`docs/troubleshooting/README.md`. `matrix-invitation`- und `matrix-recovery`-Flows waren
kaputt bzw. leer, jetzt als Authentik Blueprint (`apps/authentik/authentik-blueprints.yaml`)
deklarativ repariert. E2E mit echten Test-Usern (`clark`, `lucky`) verifiziert.
2. **coturn-Crash behoben** — Liveness-Probe nutzte `netstat` (existiert nicht im Image),
Server killte einen gesunden Prozess seit 88 Tagen, 36.000+ Restarts. Auf `tcpSocket`-Probe
umgestellt, läuft seitdem stabil.
3. **Element Call Fork** (Issue #8 ✅ geschlossen, Release `m6-element-call-fork-complete`) —
1440p/60fps-Defaults, siehe Kapitel 4 in `docs/deployment-guides/04-element-customization.md`.
**Wichtig**: erzwungenes `video_codec: vp9` hat Calls kurzzeitig live komplett kaputt gemacht
(kein Bild/Ton) — sofort zurückgerollt, ohne Codec-Zwang läuft's. Root Cause dafür nicht
abschließend isoliert, nur umgangen.
4. **Identitäts-Aufräumarbeiten**: `sorB`'s Authentik-E-Mail korrigiert (`thorec@hotmail.de`),
MAS OIDC-Link (`upstream_oauth_links`) von `sorB` zeigte fest auf den alten MAS-User
`akadmin`/`@akadmin:axion1337.chat` (Sub-Hash ist stabil über Username-Renames, daher blieb
die Verknüpfung nach dem Rename "akadmin"→"sorB" bestehen) — umgehängt auf `sorb`/
`@sorb:axion1337.chat`. Neue Identität `elbojoloco` angelegt (E-Mail `cfx@riot.8shield.net`),
verknüpft mit dem alten `akadmin`-MAS-User. **Übrig**: ein leeres, unverknüpftes
`@bojeledoggo:axion1337.chat`-Konto (Tippfehler-Artefakt) — User räumt das selbst auf.
5. **NetworkPolicies** (Issue #10 ✅ geschlossen) — siehe "Network Security" Abschnitt unten.
Zwei Live-Incidents beim Rollout (Port-Verwechslungen), beide binnen Minuten live gepatcht
und danach committed. Nebenbei: `matrixRTC`-Authorisation-Service OOM-Fix (20Mi→128Mi).
6. **Element Call Qualität nachgeschärft** — 720p-Zwischen-Simulcast-Layer ergänzt (sonst
harter Sprung von 1440p auf blockiges 360p bei kleinsten Netzwerkschwankungen), und
`video_codec: h264` statt VP8 (klassisches Simulcast wie VP8, kein SVC-Risiko wie bei
VP9, oft hardwarebeschleunigt v.a. auf iOS). Live verifiziert: 7/8 Tracks nativ H.264,
1 sauberer VP8-Fallback. Deployed als `v0.2.3-elementcall-h264`.
7. **Backlog nach Gitea migriert** — restlicher offener Backlog (VP9-Retry, ThreadNet-Web-Bug,
MAS-Template-Link, WAF und 17 weitere Security-/Infra-Punkte) als Issues #11#31 angelegt,
veraltete erledigte Punkte (Authentik Stage 2/E2E-Test/Invite-Links, Hetzner-Firewall,
SSH-Hardening) aus dieser Datei entfernt bzw. als done markiert.
### Offene Punkte
- **VP9-Retry**: vermutete Ursache jetzt bekannt (LiveKit nutzt SVC für vp9/av1, Fork-Code
setzt aber immer Simulcast-Layer) — braucht einen Code-Fix in `buildPublishOptions()`
(`src/livekit/options.ts`) bevor erneut versucht wird. Stattdessen H.264 probiert (siehe
unten) — läuft gut, kein SVC-Risiko, hardwarebeschleunigt auf mehr Geräten.
- **`ThreadNet-Web` Build-Bug**: `scripts/docker-link-repos.sh`/`docker-package.sh` nicht
ausführbar committet + veralteter `matrix-js-sdk#develop`-Pin im Lockfile blockiert
vollständigen Neu-Build des Web-Forks. Noch nicht gefixt, User hat noch nicht final
entschieden ob gewünscht.
- **Verwaistes `@bojeledoggo:axion1337.chat`**: leeres Matrix-Konto ohne OIDC-Link, User räumt
das selbst auf (braucht dafür seinen eigenen Access-Token für die Admin-API).
- **MAS-Template-Link**: 2FA/Passkey-Setup-Links direkt auf `account.axion1337.chat/account/`
statt nur über `docs/setup/security.html` — braucht MAS Custom-Template-Override
(`templates.path`), größerer separater Task.
- Nächste Kandidaten aus den offenen Issues: #6 (DB-Backup, CRITICAL), #9 (PostgreSQL-Migration),
#10 (NetworkPolicies).
---
## 🎯 Next Steps (Priorisiert)
### 🔴 **THIS WEEK CRITICAL**
1.**Authentik Stage 2 abschließen**
- ✅ Manual: OIDC Provider + Application in Authentik UI erstellt
- ✅ Code: `upstream_oauth2_config` in `mas-secret.yaml` eingefügt
- ✅ Code: `passwords: enabled: false` aktiviert
- ✅ Commit: `cdfbf7d` - Enable Authentik OIDC integration in MAS
- ✅ Verified: Login mit Authentik funktioniert (2026-05-15)
- **Status**: COMPLETE
2.**Hetzner Cloud Firewall Optimierte Konfiguration**
- ✅ Ingress: 80/443 + TURN/STUN + RTC Services
- ✅ SSH: Spezifische IPs (port 2248, nicht 22)
- ✅ Default-Deny für nicht-definierte Traffic
- **Status**: COMPLETE (optimiert über Plan)
3.**SSH Hardening**
- ✅ PasswordAuthentication: no (key-only)
- ✅ PermitRootLogin: no (root disabled)
- ✅ MaxAuthTries: 3 (verified 2026-05-15)
- **Status**: COMPLETE
4. **Database Backup Strategy Decision & First Backup**
- ⏳ Decision: CloudNativePG (on K3S) or Hetzner Postgres (managed)?
- ⏳ Setup: Daily automated backups
- ⏳ Setup: Off-site storage (S3 / Storage Box)
- ⏳ Setup: Monthly verified restores
- Est. Time: 23 days
- Priority: CRITICAL (disaster recovery)
- **Status**: NEXT
### 🟠 **NEXT 12 WEEKS HIGH**
1. **Authentik End-to-End Test**
- Test: Login flow Element → MAS → Authentik → Matrix User
- Test: Password reset
- Create: Test invite links
- Est. Time: 2 hours
2. **Element Call Fork**
- Fork: element-hq/element-call
- Feature: Video/audio constraints parameters
- Integration: Synapse well-known config
- Est. Time: 23 days
3. **External PostgreSQL Migration**
- Decision: CloudNativePG vs. Hetzner Postgres
- Setup: HA + Replication
- Migration: Move data from ESS embedded Postgres
- Testing: Verify all services work
- Est. Time: 12 days
4. **NetworkPolicies Deployment**
- Create: Default-Deny for `matrix` namespace
- Create: Allow rules (Synapse↔Postgres, MAS↔Postgres, Ingress→Web, etc.)
- Test: Ensure no service breakage
- Est. Time: 1 day
---
## ✅ Abgeschlossene Aufgaben (Chronologisch)
### Phase 1: Basis-Setup
- [x] **K3S Cluster aufsetzen** Single-Node auf Hetzner Cloud (49.13.132.245)
- Commit: `initial-setup` (vor Projekt)
- Status: ✅ Läuft
- [x] **Flux CD Installation**
- SOPS + age Encryption
- GitOps Repository konfigurieren
- Commit: `setup-flux` (vor Projekt)
- Status: ✅ Läuft
- [x] **Element Server Suite v26.4.0 Deployment**
- Synapse Homeserver (`matrix.axion1337.chat`)
- Matrix Authentication Service (`account.axion1337.chat`)
- Element Web (`axion1337.chat`)
- Element Admin (`admin.axion1337.chat`)
- MatrixRTC/Element Call (`mrtc.axion1337.chat`)
- Commit: `deploy-ess-matrix-stack`
- Status: ✅ Running
### Phase 2: Core Features
- [x] **7 Custom Element Web Themes**
- aXion1337 Dark, Deep Purple, Discord Dark, Electric Blue, Everforest, Gruvbox, Wal
- Alphabetisch sortiert
- Commit: `add-custom-element-themes`
- Status: ✅ Deployed
- [x] **Element Desktop Setup Scripts** (Windows/macOS/Linux)
- Auto-Download + Install + Config
- Hosted auf `axion1337.chat/docs/setup/`
- Commits: `add-element-desktop-setup-scripts`, `fix-element-setup-script-hosting`
- Status: ✅ Deployed
- [x] **Room Policies**
- Message Retention (1d1y lifecycle)
- Room Publication Rules (allow all)
- Auto-Join Rooms für Onboarding
- Commit: `add-synapse-retention-publication-autojoin`
- Status: ✅ Deployed
### Phase 3: WebRTC & Medienübertragung
- [x] **TURN Server (coturn) für Video-Calls**
- Domain: `turn.axion1337.chat`
- HMAC-Auth mit Shared Secret
- Ports: 3478/udp, 3478/tcp, 5349/tcp, 49152-65535/udp
- Commit: `implement-turn-server-coturn-for-webrtc-video-calls`
- Status: ✅ Deployed
- Manual: DNS A-Record + Firewall-Ports öffnen (noch erforderlich)
### Phase 4: Monitoring & Observability
- [x] **Monitoring Stack Integration**
- Alloy (Grafana Agent) als Collector
- Remote Write zu Selendis (10.0.0.3:9090 Prometheus, :3100 Loki)
- kube-state-metrics, node-exporter DaemonSet
- Commits: `integrate-monitoring-alloy-prometheus-loki`, `fix-prometheus-remote-write-docker`
- Status: ✅ Deployed
### Phase 5: Identity Provider (Authentik)
- [x] **Authentik Stage 1 Deployment**
- HelmRelease v2026.x in `authentik` namespace
- Embedded PostgreSQL + Alloy-compatible
- Cert-Manager für TLS
- Commit: `deploy-authentik-as-identity-provider-for-matrix-stage-1`
- Status: ✅ Deployed
- Manual: Admin-Passwort + OIDC Provider + Application + Enrollment Flow erstellt ✅
- [x] **Authentik Stage 2 MAS Integration**
- ✅ Authentik Admin UI: OIDC Provider erstellt (Authentik)
- ✅ Authentik Admin UI: Application mit Slug `matrix` erstellt
- ✅ Authentik Admin UI: Enrollment Flow mit Invitation Stage konfiguriert
- ✅ Client ID + Secret kopiert
- ✅ MAS `upstream_oauth2_config` mit Client Credentials aktualisiert
-`passwords: enabled: false` aktiviert
- ✅ Commit: `cdfbf7d` - Enable Authentik OIDC integration in MAS
- ✅ Verified: Login mit Authentik funktioniert (2026-05-15)
- Status: ✅ Deployed & Verified
### Phase 6: Dokumentation
- [x] **Deployment Guides erstellen**
- 5 Markdown-Dateien in `docs/deployment-guides/`
- Chronologisch geordnet
- Troubleshooting + Best Practices
- Commit: `add-comprehensive-deployment-configuration-documentation`
- Status: ✅ Deployed
- [x] **Gitea Wiki erstellen**
- Home.md mit Navigation
- Alle Deployment Guides in Root
- Operations + Archive Dokumentation
- Wiki Branch gepusht zu rohana.axion1337.de
- Status: ✅ Live
- [x] **Gitea Issues & Project Board**
- 8 Issues erstellt (#3-#10): 4 CRITICAL + 4 HIGH
- Priority Labels: critical, high
- Area Labels: authentik, security, database, infrastructure, element
- Status: ✅ Tracking
### Phase 7: Infrastructure Security (Critical)
- [x] **Hetzner Cloud Firewall Configuration**
- SSH: Spezifische IPs (port 2248)
- HTTP/HTTPS: Any IPv4/IPv6
- TURN/STUN: WebRTC Ports
- RTC Services: SFU + Auth Ports
- Status: ✅ Optimiert & Deployed
- [x] **SSH Hardening**
- PasswordAuthentication: no (key-only)
- PermitRootLogin: no
- MaxAuthTries: 3
- Verified: 2026-05-15
- Status: ✅ Complete
---
## 🔄 In Progress / Blocked
**None** Alle CRITICAL Tasks erledigt! Nächster Focus: Database Backups
### Phase 8: Authentik Enrollment/Recovery/MFA Fix (2026-07-27)
- [x] **matrix-invitation Flow repariert** fehlende Write/Password/Login-Stages ergänzt, Reihenfolge korrigiert, als Authentik Blueprint (`apps/authentik/authentik-blueprints.yaml`) reproduzierbar gemacht
- [x] **matrix-invitation-prompt** 16 fehlerhafte `validation_policies` entfernt (crashten mit `AnonymousUser`/`NoneType`-Fehlern)
- [x] **Redirect-Stage** Flow endet jetzt auf `axion1337.chat` statt in der `/if/user/`-Sackgasse (blockiert für `type=external`)
- [x] **matrix-recovery Flow gebaut** war komplett leer (0 Stages); Passwort-Reset funktioniert jetzt, verlinkt von der echten Login-Seite
- [x] **Brand.default_application gesetzt** behebt mehrere Dead-Ends, wenn eingeloggte User `/` ohne Ziel aufrufen
- [x] **2FA/Passkey Selbst-Einrichtung** Links zu `default-authenticator-totp-setup`/`-webauthn-setup` (2FA bleibt optional, `not_configured_action=skip`), dokumentiert unter `axion1337.chat/docs/setup/security.html`
- [ ] **Backlog**: → **Issue #13** (MAS Custom-Template-Override für 2FA/Passkey-Link auf `account.axion1337.chat/account/`)
---
## 📋 Backlog (Weitere Aufgaben)
**Ab 2026-07-28 in Gitea-Issues gepflegt statt hier** (eine Quelle der Wahrheit) — offene Issues:
[#6](https://rohana.axion1337.de/sorb/axion1337.chat-gitops/issues/6) DB-Backup-Strategie,
[#9](https://rohana.axion1337.de/sorb/axion1337.chat-gitops/issues/9) Externe PostgreSQL-Migration,
[#11](https://rohana.axion1337.de/sorb/axion1337.chat-gitops/issues/11)[#31](https://rohana.axion1337.de/sorb/axion1337.chat-gitops/issues/31)
(VP9-Retry, ThreadNet-Web-Build-Bug, MAS-Template-Link, WAF, Media-PVC-Backups, Pod Security
Admission, Federation-Allowlist, Mjolnir/Draupnir, Content-Scanner, External-Secrets,
Renovate/Trivy, Security-Advisory-Monitoring, automountServiceAccountToken,
unattended-upgrades, K3s-API-Security, auditd, Kernel-Hardening, Lynis, CrowdSec, Falco).
Die detaillierten Beschreibungen unten sind das historische Original, aus dem die Issues
entstanden sind — nicht mehr getrennt pflegen, stattdessen die Issues aktuell halten.
### Element Call Enhancement
- [x] **Element Call Fork für Custom Constraints** (2026-07-28, Closes #8)
- Fork: `rohana.axion1337.de/sorb/threadnet-call` (basiert auf `emmick4/element-call:livekit`,
das den noch nicht gemergten Upstream-PR element-hq/element-call#3736 enthält —
config-driven `media_quality`, keine Custom-Logik nötig)
- Defaults angehoben: Video bis 1440p/60fps (~8 Mbps), Screen-Share 1440p/30fps (~6 Mbps).
Das sind Startwerte, keine harten Limits — Nutzer können in den Settings weiter hochdrehen.
- **Incident (2026-07-28)**: Erster Deploy (`v0.2.0`, mit `video_codec: vp9` erzwungen) hat
Calls komplett kaputt gemacht (kein Bild/Ton), obwohl LiveKit-Server-Logs den
Codec-Regression-Fallback auf VP8 als erfolgreich zeigten — Root Cause nicht abschließend
isoliert. Sofort auf `v0.1.0` zurückgerollt, dann `v0.2.1` ohne erzwungenen Codec (Standard
VP8) mit denselben 1440p/60fps-Werten deployed und vom Nutzer live bestätigt: funktioniert.
VP9-Präferenz vorerst fallengelassen, siehe Backlog.
- Rauschunterdrückung: nur clientseitige WebRTC-Standardtoggles (echoCancellation/
noiseSuppression/autoGainControl), kommt kostenlos mit derselben PR. **Bewusst kein**
server-seitiges ML-Noise-Cancellation (LiveKit Agents + DTLN/RNNoise) — laut LiveKits
eigener Doku ist das für Mensch-zu-Mensch-Calls der falsche Ansatz (nur für AI-Voice-Agents
gedacht, kein Standard-Pfad um bereinigtes Audio an andere Teilnehmer zurückzugeben).
- Well-Known/`org.matrix.msc4143.rtc_foci`-Delegation war schon vom ESS-Chart korrekt
automatisch konfiguriert — kein Handlungsbedarf trotz anderslautendem Issue-Text.
- **Deployment-Ansatz geändert**: `sorb/ThreadNet-Web` (der Element-Web-Fork) hat einen
vorbestehenden, unabhängigen Build-Bug (siehe unten) und ließ sich nicht komplett neu
bauen. Stattdessen: nur der `/app/widgets/element-call/`-Ordner im bereits laufenden
`threadnet-web:v0.1.0`-Image ausgetauscht → neues Image
`rohana.axion1337.de/sorb/threadnet-web:v0.2.0-elementcall-mediaquality`.
- Verifiziert: `media_quality` live auf `axion1337.chat/widgets/element-call/config.json`.
- **Gefunden, nicht gefixt**: `ThreadNet-Web` lässt sich aktuell nicht komplett neu bauen
`scripts/docker-link-repos.sh`/`docker-package.sh` sind im Repo nicht ausführbar
committet (Mode 644 statt 755), UND der gepinnte `matrix-js-sdk#develop`-Commit im
Lockfile ist zu alt (fehlt `src/oidc/authorize.ts`, das `apps/web` importiert). Beides
unabhängig von diesem Fix, blockiert aber jeden zukünftigen vollständigen Rebuild.
- Backlog: MAL-basierte Noise-Cancellation (LiveKit Agents + self-hosted DTLN/RNNoise) als
experimentelle Idee, falls später gewünscht — kein etablierter Pfad für Conferencing.
- Backlog: VP9-Codec-Präferenz erneut versuchen, sobald PR #3736 upstream gemerged/gereift
ist oder Root Cause des Ausfalls isoliert wurde (Browser-Konsolen-Repro nötig).
### Database Hardening
- [ ] **External/Dedicated PostgreSQL Deployment**
- Option 1: CloudNativePG Operator (open-source, auf K3S)
- Option 2: Managed Hetzner Postgres
- Separate aus ESS matrix-stack embedded Postgres
- HA + Replication
- Est. Effort: 12 days
- Priority: **HIGH** (reliability)
- [ ] **Database Backup Strategy**
- Daily automated backups (PgBackRest oder velero)
- Off-site backup storage (S3 / Hetzner Storage Box)
- Monthly verified restores (test restore → verify data integrity)
- Backup + restore documentation
- Est. Effort: 23 days
- Priority: **CRITICAL** (disaster recovery)
- [ ] **Synapse Media PVC Backups**
- Separate backup pipeline für `/data/media_store` PVC
- Reason: Media oft >100GB, sollte nicht im DB-Backup sein
- Velero + Restic für block-level backup
- Est. Effort: 1 day
- Priority: **HIGH** (data preservation)
### Network Security
- [x] **NetworkPolicies K8s-Layer Segmentation** (2026-07-28, Closes #10)
- Default-Deny Ingress (egress left untouched) für `matrix` UND `authentik` namespaces,
per-Komponente Allow-Regeln in `apps/authentik/networkpolicy.yaml` und
`apps/production/networkpolicy.yaml`. Rollout: authentik zuerst als Pilot, dann matrix.
- Empirisch verifiziert, dass K3s' eingebauter NetworkPolicy-Controller tatsächlich
durchsetzt (Testnamespace, Timeout- statt Refused-Verhalten unter Deny-Policy).
- **Zwei Live-Incidents beim Rollout, beide binnen Minuten behoben**:
1. `authentik-server`: Regel erlaubte Service-Port 80/443, aber NetworkPolicy filtert
auf dem tatsächlichen Container-Port (9000/9443 nach kube-proxy-DNAT) — 502 auf
`auth.axion1337.chat`, sofort korrigiert.
2. `matrix-authentication-service`: Regel erlaubte Synapse nur auf Port 8081, aber
Synapse ruft `/oauth2/introspect` tatsächlich auf **Port 8080** — jede
authentifizierte Anfrage (inkl. `/sync`) scheiterte mit 503, alle Clients zeigten
"Verbindung unterbrochen". Live gepatcht, dann committed.
- Lehre für zukünftige NetworkPolicies in diesem Repo: wo immer ein Service benannte
Ports (`targetPort: <name>`) nutzt, diese direkt in der Policy referenzieren statt
Portnummern zu raten — schließt genau diese Fehlerklasse aus.
- Nebenbefund (unabhängig von NetworkPolicies): `matrixRTC`-Authorisation-Service hatte
ein 20Mi-Memory-Limit (Chart-Default), OOM-gekillt nach ~74 Tagen Uptime während der
Verifikations-Calls — auf 64Mi/128Mi angehoben.
- `coturn` (hostNetwork) bewusst ausgenommen — NetworkPolicy greift dort nicht.
- `authentik-postgresql`'s Bitnami-Chart-Policy (Port 5432, quelloffen) bewusst nicht
angefasst/dupliziert, da Helm-verwaltet.
- [ ] **Pod Security Admission (Restricted)**
- Apply to `matrix` & `authentik` namespaces
- Enforce: non-root, no privileged, read-only root fs
- Test: Ensure no chart breakage
- Est. Effort: 1 day
- Priority: **MEDIUM** (hardening)
### Federation & Access Control
- [ ] **Federation-Allowlist oder Closed Federation**
- Decision: Which servers to federate with?
- If allowlist: explicit `federation_domain_whitelist`
- If closed: `allow_public_rooms_without_join_rules: false`
- Synapse config in `synapse-values.yaml`
- Est. Effort: 4 hours
- Priority: **MEDIUM** (security policy)
### Moderation & Anti-Abuse
- [ ] **Mjolnir/Draupnir Bot Deployment**
- Open-source moderation bot für Matrix
- Reason: Invitation-based, aber Federation kann Spam bringen
- Auto-ban known bad servers/users
- Spam-detection rules
- HelmChart oder custom Deployment
- Est. Effort: 12 days
- Priority: **MEDIUM** (ops safety)
- [ ] **Content Scanner for Media**
- matrix-content-scanner + ClamAV antivirus
- Scan uploaded media for malware
- Block suspicious files
- Est. Effort: 12 days
- Priority: **LOWMEDIUM** (optional but good practice)
### Secrets Management
- [ ] **External-Secrets Operator oder SOPS für Flux**
- Current: SOPS with age encryption
- Consideration: External-Secrets for cloud-native (AWS Secrets Manager, Hetzner Vault, etc.)
- OR: Improve SOPS rotation strategy
- Decision needed: Keep SOPS or upgrade?
- Est. Effort: 23 days (if switching)
- Priority: **LOW** (current SOPS setup working)
### Image & Dependency Management
- [ ] **Renovate / Dependabot Setup**
- Auto-update Helm Chart versions
- Auto-update Container Image Tags
- Monitor for security patches
- Est. Effort: 4 hours
- Priority: **MEDIUM** (maintenance)
- [ ] **Trivy Image Scanning**
- Scan images in Flux HelmReleases for CVEs
- Block deployment if critical CVE found
- CI/CD hook in git workflow
- Est. Effort: 8 hours
- Priority: **LOWMEDIUM** (security posture)
- [ ] **Monitor ESS & Element Security Advisories**
- Subscribe to `element-hq` security mailing list
- Monitor `#matrix-community` security channels
- Auto-alerts on new CVEs/patches
- Est. Effort: Ongoing (low maintenance)
- Priority: **MEDIUM** (security awareness)
### Container Security
- [ ] **Disable automountServiceAccountToken Everywhere**
- Audit all Deployments/StatefulSets
- Disable for: Synapse, ElementWeb, MAS, Postgres, Authentik (where not needed)
- Add `automountServiceAccountToken: false` to spec.template.spec
- Test: Ensure no breakage
- Est. Effort: 4 hours
- Priority: **MEDIUM** (least-privilege)
---
## 🔒 Security Hardening (Host & Cluster Level)
### Host OS Layer (Ubuntu/Debian)
- [x] **Hetzner Cloud Firewall** Default-Deny inbound, siehe "Phase 7" oben. **Done.**
- [x] **SSH Hardening** Key-only, Root-Login disabled, Port 2248, siehe "Phase 7" oben. **Done.**
- [ ] **unattended-upgrades**
- Enable automatic security updates
- Configure: APT::Periodic::Update-Package-Lists "1";
- Configure: APT::Periodic::Unattended-Upgrade "1";
- Configure: APT::Periodic::AutocleanInterval "7";
- Est. Effort: 30 min
- Priority: **HIGH** (set & forget)
- [ ] **K3S API Security**
- Current: K3S API listening on :6443 on all interfaces (default)
- Hardening:
- Option 1: Firewall restrict :6443 to localhost only
- Option 2: K3S --bind-address + --advertise-address to WireGuard IP
- Option 3: kubectl access only via jumphost/bastion
- Est. Effort: 2 hours
- Priority: **HIGH** (API is high-value target)
- [ ] **auditd for File Integrity & Syscall Audit**
- Monitor: /etc, ~/.kube, /var/lib/rancher/k3s
- Audit rules für sensitive file changes
- Low overhead, good signal/noise ratio
- Output to syslog / centralized logging
- Est. Effort: 2 hours
- Priority: **MEDIUM** (forensics + compliance)
- [ ] **Kernel Hardening (sysctl)**
- Apply hardening recommendations from Lynis
- Key settings:
- kernel.kptr_restrict=2 (hide kernel pointers)
- kernel.dmesg_restrict=1 (restrict dmesg)
- net.ipv4.tcp_syncookies=1 (SYN flood protection)
- net.ipv4.conf.all.rp_filter=1 (reverse path filtering)
- net.ipv4.conf.all.send_redirects=0
- net.ipv6.conf.all.disable_ipv6=0 (or =1 if no IPv6 needed)
- Persist via /etc/sysctl.d/99-hardening.conf
- Est. Effort: 2 hours
- Priority: **MEDIUM** (defense in depth)
- [ ] **Lynis Security Baseline**
- Run `lynis audit system`
- Review recommendations
- Implement high-priority findings
- Aim for score >80
- Re-run quarterly
- Est. Effort: 4 hours (initial) + 1 hour quarterly
- Priority: **MEDIUM** (baseline verification)
### Cluster Layer (K3S / Kubernetes)
- [ ] **CrowdSec Integration**
- Install CrowdSec agent on host
- Connect to CrowdSec Hub (commercial platform, free tier available)
- Feed auth.log, syslog → CrowdSec for attack detection
- Auto-block IPs via local firewall or Hetzner Firewall API
- Est. Effort: 4 hours
- Priority: **MEDIUM** (proactive threat response)
- [ ] **Falco Runtime Monitoring**
- Install Falco DaemonSet in K3S
- Monitor: Shell spawning in containers, suspicious syscalls, privilege escalation
- Output to Loki / syslog
- Alert on anomalies
- Est. Effort: 1 day
- Priority: **MEDIUM** (runtime detection)
---
## 🎯 Meilensteine (Milestones)
| Meilenstein | Beschreibung | Status | ETA |
|------------|-------------|--------|-----|
| **M1: Basis-Setup** | K3S + Flux + ESS deployed | ✅ Done | - |
| **M2: Core Matrix** | Themes, Scripts, Policies | ✅ Done | - |
| **M3: WebRTC & Monitoring** | TURN + Alloy/Prometheus/Loki | ✅ Done | - |
| **M4: Identity Provider** | Authentik Stage 1+2 (pending Stage 2) | 🔄 In Progress | ~12 days |
| **M5: Production-Ready** | DB Backups, NetworkPolicies, Security Hardening | 📋 Backlog | ~23 weeks |
| **M6: Advanced Features** | Element Call Fork, Content Scanner, Mjolnir | 📋 Backlog | ~4+ weeks |
| **M7: Enterprise-Ready** | Full compliance (DSGVO), HA setup, Disaster Recovery | 🎯 Future | ~8+ weeks |
---
## 📊 Prioritäts-Kategorien
### 🔴 CRITICAL (do immediately)
- Hetzner Cloud Firewall setup
- Database backup strategy
- SSH hardening
### 🟠 HIGH (do within 12 weeks)
- Authentik Stage 2 completion
- External PostgreSQL migration
- NetworkPolicies
- Element Call fork
### 🟡 MEDIUM (do within 1 month)
- CrowdSec + Falco
- Mjolnir bot
- Renovate/Trivy
- PSA restricted mode
- Kernel hardening
### 🟢 LOW (nice-to-have, do if time allows)
- Content scanner (ClamAV)
- External-Secrets upgrade
- SSH port relocation
- Advanced federation rules
---
## 📝 Notes & Decision Points
### Authentik Stage 2 Blocker
**Waiting for**: User to manually configure Authentik OIDC Provider in Authentik Admin UI.
- Once done, provide Client ID + Secret
- Then: Commit Stage 2 MAS config
### Database: CloudNativePG vs. Hetzner Postgres
- **CloudNativePG**: Open-source, runs on K3S, full control
- **Hetzner Postgres**: Managed, backups included, less ops overhead
- **Decision**: Recommend CloudNativePG for now (cost-effective), migrate to Hetzner later if operational overhead too high
### Federation: Allowlist vs. Closed?
- **Allowlist**: Default federation with all public servers, can be attacked
- **Closed**: Only federate with trusted servers (higher security, lower interop)
- **Decision**: Depends on user intent. For now: allow all, add Mjolnir for abuse protection
### Security Framework
- **Layers**: Perimeter (Firewall) → Host (SSH, auditd, hardening) → Cluster (NetworkPolicies, PSA, Falco) → App (Rate-limits, Mjolnir)
- **Approach**: Implement incrementally, test after each layer
---
## 🔗 Related Documentation
- `docs/deployment-guides/README.md` Overview
- `docs/deployment-guides/01-turn-server-setup.md` TURN
- `docs/deployment-guides/02-authentik-identity-provider.md` Authentik (Stage 1 + Stage 2 plan)
- `docs/deployment-guides/03-monitoring-integration.md` Monitoring
- `docs/deployment-guides/04-element-customization.md` Themes, Desktop
- `docs/deployment-guides/05-room-policies.md` Policies
---
**Last Updated**: 2026-05-14
**Next Review**: 2026-05-21
@@ -0,0 +1,54 @@
# TURN Server (coturn) für WebRTC Video-Calls
**Status**: ✅ Vollständig deployed
**Domain**: `turn.axion1337.chat`
**Public IP**: `49.13.132.245`
## Problem & Lösung
Videocalls scheitern mit DTLS-Timeout bei Clients hinter NAT/Firewall. **Lösung**: coturn als TURN-Relay.
## Architektur
Client A ──→ coturn (turn.axion1337.chat) ──→ Client B
- **Ports**: 3478/udp, 3478/tcp, 5349/tcp, 49152-65535/udp
- **Auth**: HMAC-basiert mit Shared Secret zwischen coturn + Synapse
- **Deployment**: K3S Deployment mit `hostNetwork: true`
## Dateien (in `apps/production/`)
| Datei | Inhalt |
|-------|--------|
| `coturn.yaml` | ConfigMap + Deployment + Service |
| `coturn-secret.yaml` | SOPS-Secret: `TURN_SECRET` |
| `custom-configs/synapse-values.yaml` | TURN URIs + shared secret |
| `matrix-certificates.yaml` | cert-manager Cert für `turn.axion1337.chat` |
## DNS & Firewall (manuell)
```
DNS A-Record: turn.axion1337.chat → 49.13.132.245
Firewall (K3S Host):
ufw allow 3478/udp
ufw allow 3478/tcp
ufw allow 5349/tcp
ufw allow 49152:65535/udp
```
## Verifikation
```bash
# Pod läuft?
kubectl get pods -n matrix -l app=coturn
# Certificate ready?
kubectl get certificate -n matrix | grep turn
# Extern testen
docker run -it instrumentisto/coturn \
turnutils_uclient -v -T -u test -w test turn.axion1337.chat
```
**Weitere Details**: Siehe vollständige Dokumentation oben.
@@ -0,0 +1,45 @@
# Authentik als Identity Provider für Matrix
**Status**: ✅ Stage 1 Deployed (Authentik läuft)
**Pending**: Stage 2 (MAS Integration)
**Domain**: `auth.axion1337.chat`
## Überblick
Authentik = OIDC Provider für MAS → Zentrales Login + Einladungs-basierte Registrierung.
## Stage 1: Authentik Deployment
**Dateien** (in `apps/authentik/`):
- `namespace.yaml`, `helm-repo.yaml`, `authentik-secret.yaml` (SOPS)
- `authentik.yaml` (HelmRelease v2026.x + embedded Postgres)
- `certificate.yaml`, `ingress.yaml`
**Flux Kustomization**: `clusters/matrix/flux-system/authentik-sync.yaml`
## Deployment-Schritte
1. **DNS A-Record**: `auth.axion1337.chat → 49.13.132.245`
2. **Pods hochfahren**: `kubectl get pods -n authentik -w`
3. **Authentik UI**: `https://auth.axion1337.chat/if/flow/initial-setup/` → Admin-Passwort setzen
4. **OIDC Provider**: Admin UI → OIDC Provider erstellen
5. **Application**: Slug `matrix` (wichtig für Issuer URL!)
6. **Redirect URIs**:
- `https://account.axion1337.chat/upstream/callback/01KQDJTR1ZVTG8JQ220F5BNBFZ`
- Post-logout: `https://axion1337.chat`
7. **Client ID + Secret kopieren**
## Stage 2: MAS Integration
1. Decrypt: `sops --decrypt --in-place apps/production/custom-configs/mas-secret.yaml`
2. `upstream_oauth2_config` + `passwords-config` Blöcke hinzufügen
3. Encrypt: `sops --encrypt --in-place ...`
4. Commit & Push
5. **WICHTIG**: `passwords: enabled: false` erst nach OIDC-Test!
## Einladungs-Links
Authentik Admin → Flows & Stages → Invitations → Create
---
**Weitere Details**: Siehe Kapitel 2 in diesem Projekt.
@@ -0,0 +1,52 @@
# Monitoring: Alloy → Prometheus/Loki auf Selendis
**Status**: ✅ Vollständig deployed
**Remote Storage**: `10.0.0.3:9090` (Prometheus), `10.0.0.3:3100` (Loki)
## Überblick
Alloy (Grafana Agent) sammelt Metriken & Logs vom K3S-Cluster und schickt sie zu Prometheus/Loki auf Selendis.
## Komponenten
| Komponente | Rolle |
|-----------|-------|
| **Alloy** | Metrics & Logs Collector |
| **kube-state-metrics** | Kubernetes Object Status |
| **node-exporter** | Host Metrics (CPU, Memory, Disk) |
| **Prometheus** (Selendis) | Metrics Ingestion |
| **Loki** (Selendis) | Logs Ingestion |
## Dateien (in `apps/monitoring/`)
- `namespace.yaml`
- `helm-repos.yaml` (prometheus-community, grafana)
- `kube-state-metrics.yaml`, `node-exporter.yaml`
- `alloy-config.yaml` (River config with scrape targets + remote write)
- `alloy.yaml` (HelmRelease)
## Scrape Targets
Alloy scraped:
- **Flux Controllers** (flux-system ns, port 8080)
- **kube-state-metrics** (monitoring:8080)
- **node-exporter** (monitoring:9100)
- **Synapse** (matrix.axion1337.chat:9000)
Alle Remote Write zu `10.0.0.3:9090` (Prometheus) + `10.0.0.3:3100` (Loki).
## Troubleshooting
```bash
# Check Alloy Logs
kubectl logs -n monitoring -l app.kubernetes.io/name=alloy
# Check Prometheus remote write
curl http://10.0.0.3:9090/api/v1/query?query=up
# Loki test
curl -s http://10.0.0.3:3100/loki/api/v1/query_range?query=%7B%7D | jq .
```
---
**Weitere Details**: Siehe Kapitel 3.
+90
View File
@@ -0,0 +1,90 @@
# Element Web Customization: Themes, Desktop-Apps, Admin
**Status**: ✅ Vollständig deployed
**Domains**: `axion1337.chat` (Web), `/docs/setup` (Scripts)
## 1. Custom Themes (7 Stück)
| Theme | Primärfarbe |
|-------|-----------|
| aXion1337 Dark | `#1a1a1a` |
| Deep Purple | `#6a4c93` |
| Discord Dark | `#2c2f33` |
| Electric Blue | `#0066ff` |
| Everforest Dark Hard | `#1e2326` |
| Gruvbox Dark | `#282828` |
| Wal | `#1e1e1e` |
**Konfiguration**: `apps/production/custom-configs/element-values.yaml`
**Anwendung (User)**: Settings → Appearance → Colour theme
## 2. Desktop-Setup-Scripts
| System | Datei |
|--------|-------|
| Windows | `element-setup-windows.cmd` (Doppelklick) |
| macOS | `element-setup-macos.command` (Doppelklick) |
| Linux | `element-setup-linux.sh` (bash) |
**Was die Scripts tun**:
1. config.json erstellen mit `configUrl: "https://axion1337.chat/config.json"`
2. Element installieren (WinGet / Homebrew / apt/dnf/pacman)
3. Element starten (auto-config laden)
**Download**: `https://axion1337.chat/docs/setup/`
## 3. Element Admin-Panel
**URL**: `https://admin.axion1337.chat`
- User verwalten
- Room durchsuchen
- Server-Statistiken
**Konfiguration**: `apps/production/element-server-suite.yaml` (ESS Chart)
## 4. Element Call Fork (Video/Audio-Qualität)
**Status**: ✅ Deployed (2026-07-28, Closes Issue #8)
- Fork: `rohana.axion1337.de/sorb/threadnet-call` (basiert auf `emmick4/element-call:livekit`,
enthält den noch nicht gemergten Upstream-PR element-hq/element-call#3736 mit
config-driven `media_quality` — kein Custom-Code nötig)
- Defaults angehoben: Kamera bis **1440p/60fps** (~8 Mbps), Screen-Share **1440p/30fps**
(~6 Mbps). Startwerte, keine harten Limits — Nutzer können in Settings weiter hochdrehen.
- Rauschunterdrückung: clientseitige WebRTC-Standardtoggles (Echo/Noise/Gain), passend zu
LiveKits eigener Empfehlung für Mensch-zu-Mensch-Calls. Bewusst **kein** server-seitiges
ML-Noise-Cancellation (siehe `docs/TASKS.md` Backlog).
- **Incident (2026-07-28)**: Erster Versuch mit erzwungenem `video_codec: vp9` hat Calls
komplett kaputt gemacht (kein Bild/Ton). Sofort zurückgerollt. Vermutete Ursache: LiveKit
nutzt für vp9/av1 SVC statt klassischem Simulcast, `buildPublishOptions()` im Fork setzt
aber immer Simulcast-Layer — Code-Fix nötig, bevor vp9 erneut versucht wird (Backlog).
- **720p-Zwischen-Layer ergänzt** (`simulcast_layers`) — ohne eigene Definition fiel die
Übertragung bei kleinsten Netzwerkschwankungen direkt von 1440p auf blockiges 360p, jetzt
sanftere Abstufung über 720p.
- **H.264 statt VP8** (2026-07-28) — nutzt wie VP8 klassisches Simulcast (kein SVC-Risiko wie
bei VP9), zusätzlich auf vielen Geräten (v.a. iOS/Safari) hardwarebeschleunigt. Live
verifiziert: 7 von 8 Video-Tracks liefen über H.264, 1 fiel sauber auf den VP8-Backup-Codec
zurück (kein Ausfall). Deployed als `v0.2.3-elementcall-h264`.
- Deployt als `rohana.axion1337.de/sorb/threadnet-web:v0.2.1-elementcall-noquotavp9` — nur
der `/app/widgets/element-call/`-Ordner im bestehenden `v0.1.0`-Image ausgetauscht, da
`ThreadNet-Web` einen vorbestehenden Build-Bug hat (siehe unten).
- Config live prüfbar: `https://axion1337.chat/widgets/element-call/config.json`
**Bekannter, nicht behobener Bug in `ThreadNet-Web`**: `scripts/docker-link-repos.sh` /
`docker-package.sh` sind nicht ausführbar committet (Mode 644), und der gepinnte
`matrix-js-sdk#develop`-Commit im Lockfile ist zu alt (fehlt `src/oidc/authorize.ts`) —
blockiert einen kompletten Neu-Build des Forks von Grund auf.
## Dateien
| Datei | Ort |
|-------|-----|
| Custom Themes | `element-values.yaml` ConfigMap |
| Setup-Scripts | `element-web-docs-configmap.yaml` |
| Docs Server | `element-web-docs-server.yaml` (nginx) |
| Ingress | `apex-ingress.yaml` (`/docs/setup/` route) |
---
**Weitere Details**: Siehe Kapitel 4.
@@ -0,0 +1,82 @@
# Room Policies: Retention, Publication, Auto-Join
**Status**: ✅ Vollständig deployed
**Konfiguration**: `apps/production/custom-configs/synapse-values.yaml`
## 1. Message Retention
Alte Nachrichten automatisch löschen (Speicher sparen, DSGVO).
```yaml
retention:
enabled: true
default_policy:
min_lifetime: 1d # Messages bleiben ≥1d
max_lifetime: 1y # Messages gelöscht nach 1 Jahr
media_retention:
local_media_lifetime: 365d # 1 Jahr
remote_media_lifetime: 90d # 90 Tage
redaction_retention_period: 7d # Gelöschte Messages noch 7d sichtbar
```
## 2. Room Publication Rules
Kontrollieren welche Rooms im öffentlichen Directory sichtbar sind.
```yaml
room_list_publication_rules:
- user_id: "*" # Alle User
action: allow # dürfen ihre Rooms publishen
```
**Alternativ (restrictiv)**: Nur Admins publishen
```yaml
room_list_publication_rules:
- user_id: "@admin:axion1337.chat"
action: allow
- user_id: "*"
action: deny
```
## 3. Auto-Join Rooms
Neue User automatisch in bestimmte Rooms hinzufügen (Onboarding).
```yaml
auto_join_rooms:
- "!announcements:axion1337.chat"
- "!rules:axion1337.chat"
auto_join_rooms_for_guests: false # Nur registered users
```
**Room ID finden**: Element Settings → Advanced → Room ID
## Deployment
```bash
# Edit ConfigMap
kubectl apply -f apps/production/custom-configs/synapse-values.yaml
# Synapse neustarten
kubectl rollout restart deployment -n matrix matrix-stack-synapse
# Verify
kubectl logs -n matrix -l app.kubernetes.io/name=synapse | grep -i "retention\|publication"
```
## Best Practices
**Privater Server**:
- max_lifetime: 1y (großzügig)
- action: allow (alle publishen)
- auto_join_rooms: announcements + rules
**Öffentlicher Server (DSGWR)**:
- max_lifetime: 90d (kurz)
- action: deny (nur Admins)
- auto_join_rooms: [] (keine Zwangs-Rooms)
---
**Weitere Details**: Siehe Kapitel 5.
+92
View File
@@ -0,0 +1,92 @@
# aXion1337.Chat Deployment & Konfiguration Dokumentation
Diese Dokumentation beschreibt die Einrichtung und Konfiguration des Matrix-Homeservers für **axion1337.chat** mit Element Server Suite (ESS) v26.4.0 auf K3S mit Flux CD GitOps.
## 📋 Übersicht Deployment-Reihenfolge
Die Implementierungen wurden in dieser Reihenfolge durchgeführt. Für neue Setups sollten Sie dieser Abfolge folgen:
| # | Titel | Datei | Status | Zieldomäne |
|---|-------|-------|--------|-----------|
| 1 | TURN Server für WebRTC Video-Calls | `01-turn-server-setup.md` | ✅ Deployed | `turn.axion1337.chat` |
| 2 | Authentik als Identity Provider | `02-authentik-identity-provider.md` | ✅ Stage 1 Deployed | `auth.axion1337.chat` |
| 3 | Monitoring mit Alloy/Prometheus/Loki | `03-monitoring-integration.md` | ✅ Deployed | lokal (10.0.0.3) |
| 4 | Element Web Anpassung & Desktop-Apps | `04-element-customization.md` | ✅ Deployed | `axion1337.chat` |
| 5 | Room Policies (Retention, Publication, Auto-Join) | `05-room-policies.md` | ✅ Deployed | Matrix Synapse |
---
## 🚀 Quick Start für neue Deployment
Siehe die einzelnen Dokumentationen für detaillierte Anleitung.
---
## 🏗️ Architektur-Übersicht
```
┌─────────────────────────────────────────────────────────────┐
│ Element Web (Apex) │
│ axion1337.chat (HTTP/TLS) │
└──────────────────────┬──────────────────────────────────────┘
┌─────────────┼─────────────┐
│ │ │
┌────▼────┐ ┌─────▼──────┐ ┌──▼────────┐
│ MAS │ │ Well-Known │ │Docs/Setup │
│account. │ │matrix/* │ │/setup │
│axion1337 │ │ │ │ │
└────┬────┘ └────────────┘ └───────────┘
┌────▼────────────────┐
│ Authentik OIDC │
│ auth.axion1337.chat │
│ (Identity Provider) │
└─────────────────────┘
┌────▼────────────────┐
│ Synapse Matrix │
│ matrix.axion1337.chat│
│ (Homeserver) │
└──────────────────────┘
```
---
## 🔑 Kritische Werte & Konfigurationen
### Domains
- **Apex**: `axion1337.chat` (Element Web)
- **Matrix Synapse**: `matrix.axion1337.chat`
- **MAS**: `account.axion1337.chat`
- **Authentik**: `auth.axion1337.chat`
- **TURN Server**: `turn.axion1337.chat`
### Externe Services
- **K3S Host IP**: `49.13.132.245`
- **Monitoring Host**: `10.0.0.3` (Selendis)
---
## 📚 Dokumente im Detail
### [01-turn-server-setup.md](01-turn-server-setup.md)
STUN/TURN Server für WebRTC Media Relay (Video-Calls).
### [02-authentik-identity-provider.md](02-authentik-identity-provider.md)
Authentik als OIDC Provider für Matrix. Registrierung via Einladungs-Links.
### [03-monitoring-integration.md](03-monitoring-integration.md)
Alloy → Prometheus/Loki Monitoring Integration.
### [04-element-customization.md](04-element-customization.md)
Custom Themes, Desktop-Setup-Scripts, Element Admin.
### [05-room-policies.md](05-room-policies.md)
Message Retention, Room Publication, Auto-Join Policies.
---
## 🛠️ Wartung & Troubleshooting
Alle Dokumentationen enthalten Troubleshooting-Sektionen für häufige Probleme.
Regular → Executable
+29 -1
View File
@@ -75,4 +75,32 @@ flux get helmreleases -n matrix --watch
# Zeigt, wie die Pods hochfahren:
kubectl get pods -n matrix -w
```
Sobald alle Pods auf `Running` stehen und die Zertifikate über Let's Encrypt validiert wurden (`kubectl get certificate -n matrix`), ist dein Matrix-Stack unter `https://axion1337.chat` erreichbar.
Sobald alle Pods auf `Running` stehen und die Zertifikate über Let's Encrypt validiert wurden (`kubectl get certificate -n matrix`), ist dein Matrix-Stack unter `https://axion1337.chat` erreichbar.
---
## 🔁 Recovery: lokalen age-Key wiederherstellen (Server läuft bereits)
Anders als Schritt 2 oben (neuen Key **erzeugen**) — falls der Server bereits läuft und nur der
lokale Rechner den age-Key verloren hat (z.B. nach einer Neuinstallation), lässt sich der
**bestehende** Private Key direkt aus dem Cluster zurückholen, ohne einen neuen zu generieren
(das würde `.sops.yaml` und alle bereits verschlüsselten Secrets ungültig machen):
```bash
mkdir -p ~/.age
kubectl get secret sops-age -n flux-system -o jsonpath='{.data.age\.agekey}' | base64 -d > ~/.age/keys.txt
chmod 600 ~/.age/keys.txt
# Public Key zur Kontrolle gegen .sops.yaml abgleichen:
grep 'public key:' ~/.age/keys.txt
grep 'age:' .sops.yaml
```
Voraussetzung: laufender Kubeconfig-Zugriff auf den Cluster (siehe Schritt 1 oben — auch das
ist reines Zurückkopieren, kein Neu-Erzeugen).
**Bekannte Schwachstelle**: Dieser Key existiert aktuell nur an zwei Orten — im
`sops-age`-Secret selbst (auf demselben Server) und lokal bei wem auch immer ihn zuletzt
zurückgeholt hat. Es gibt kein separates, offsite Backup. Fällt der Server komplett aus
(nicht nur der lokale Rechner), sind alle SOPS-verschlüsselten Secrets im Repo unlesbar.
Siehe Issue-Backlog für die Entscheidung, ob/wie das abgesichert wird.
+117
View File
@@ -0,0 +1,117 @@
# Authentik als Einladungs- und Self-Registration-Layer vor MAS in ESS Community
## Kurzurteil
Ja — in deinem Setup ist **Authentik** technisch sinnvoll einsetzbar, aber **nicht als Ersatz für den Matrix Authentication Service**, sondern **als Upstream-OIDC-Provider vor MAS**. Das ist der sauberste Weg, weil Synapse heute eine stabile, vereinfachte Integration mit MAS hat, ESS Community zusätzliche MAS-Konfiguration ausdrücklich über `matrixAuthenticationService.additional` vorsieht, und MAS jede OIDC-konforme Upstream-Identitätsquelle unterstützt. Für ein ESS-Deployment mit delegierter Authentifizierung ist das deutlich kohärenter als eine Direktanbindung von Synapse an Authentik vorbei. citeturn31view0turn7view2turn14view1turn23search0
Wichtig ist aber die Abgrenzung: Wenn dein einziges Problem wirklich nur lautet „im Admin-Frontend kann ich keine Registration Tokens mehr klicken“, dann **brauchst du Authentik nicht zwingend**. MAS hat weiterhin eine Admin-API mit Endpunkten für User-Registration-Tokens; der fehlende Komfort liegt also eher in der Oberfläche als in der Funktion selbst. Die API ist mit `urn:mas:admin` abgesichert und kann sowohl interaktiv als auch automatisiert benutzt werden. citeturn19search0turn28search0turn28search1turn28search2
## Warum Authentik hier die bessere Plattform sein kann
Der Mehrwert von Authentik entsteht dann, wenn du **mehr als nur ein Token-Feld** willst: Einladungen, Self-Service-Enrolment, E-Mail-Verifikation, Policies, Gruppen-Zuweisung, später vielleicht weitere Login-Quellen hinter derselben Oberfläche. Authentik kann selbst als OpenID Provider auftreten, aber auch als Relying Party gegenüber anderen OAuth/OIDC-Quellen arbeiten; MAS sieht dann nur noch **eine** saubere OIDC-Oberfläche, während Authentik die eigentliche Onboarding- und Policy-Logik kapselt. Das passt sehr gut zu MAS, weil MAS nach oben bewusst nur OIDC unterstützt und SAML/LDAP nicht selbst als Upstream sprechen will. citeturn21view0turn21view3turn14view1
Für dein Ziel „potenzielle Anwender sollen sich selbst registrieren können“ ist der entscheidende Unterschied: Bei MAS-Registration-Tokens autorisierst du eine **Matrix-Registrierung**; bei Authentik autorisierst du zunächst eine **Identität im IdP**, und MAS übernimmt diese Identität anschließend per OIDC und legt daraus den Matrix-Account an. Das ist architektonisch stärker, weil dieselbe Identität später auch für andere Dienste nutzbar wird. MAS kann beim Upstream-Login den Matrix-Localpart, Display Name und E-Mail aus Claims übernehmen; für neue Nutzer gibt es dabei sogar einen expliziten Bestätigungs- bzw. Attribut-Import-Schritt. citeturn24search0turn14view0
## Wann Authentik nicht nötig ist
Wenn du ausschließlich einen „Einladungs-Code“ für Matrix brauchst und keinerlei separates Identitätsmanagement, dann ist die schlankere Lösung wahrscheinlich: **bei MAS bleiben und die Registration-Tokens per Admin-API verwalten**. Dafür brauchst du keinen zusätzlichen Dienst, keine zweite Postgres-Anwendung und keine zweite Policy-Oberfläche. Das ist betriebsärmer und passt gut zu kleinen Community-Setups. citeturn19search0turn28search0turn28search2
Sobald du aber Dinge wie diese willst, kippt die Bewertung klar zugunsten von Authentik: verschiedene Onboarding-Flows, Einladungs-Links statt nackter Codes, E-Mail-Verifikation, Domain-Allow-Lists, Gruppen-Zuweisung, zentrale MFA-Politik oder später externe Identity-Sources. Authentik bringt dafür sowohl vorgefertigte Einladungs-Blueprints als auch Enrolment-Flows mit und kann bei Bedarf eigene Expression Policies für restriktivere Zulassungslogik einsetzen. citeturn21view1turn21view2turn30search3
## Empfohlene Zielarchitektur
Die von mir empfohlene Zielarchitektur ist:
**Element Web / Clients → MAS → Authentik → Benutzerquelle(n)**
Synapse bleibt dabei an MAS delegiert. Das ist genau der Pfad, den die Synapse- und MAS-Dokumentation heute unterstützen: Synapse integriert stabil mit MAS, und MAS kann wiederum Upstream-OIDC-Provider sprechen. Eine direkte Synapse-OIDC-Integration mit Authentik ist zwar technisch dokumentiert, wäre in deinem ESS/MAS-Setup aber die weniger saubere Variante, weil du damit an der in ESS bereits vorgesehenen Delegationsschicht vorbeikonfigurierst. citeturn31view0turn14view2turn14view1turn7view2
Für Authentik selbst ist das Standardmaterial: auf Kubernetes per Helm deployen, in Produktion ein echtes PostgreSQL verwenden statt der Demo-Datenbank, und E-Mail konfigurieren, wenn du Einladungen oder Verifikations-Mails nutzen willst. Das ist wichtig, weil Authentik-Einladungen und E-Mail-basierte Enrolment-Flows ohne Mailtransport ihren eigentlichen Nutzen verlieren. citeturn34view0turn25search1
Ein entscheidender UX-Punkt: Wenn in MAS genau **ein** Upstream-Provider konfiguriert ist und die lokale Passwortdatenbank deaktiviert wird, startet MAS den Upstream-Authentifizierungsfluss automatisch. Damit verhält sich das System für Endnutzer fast so, als wäre Authentik „direkt“ integriert, obwohl MAS weiterhin die Matrix-native Auth-Schicht bleibt. citeturn26view0
## Konkrete Integration in ESS und FluxCD
Der erste praktische Schritt ist, Authentik als eigene GitOps-Anwendung in Kubernetes einzuführen. Offiziell ist dafür das Helm-Chart vorgesehen; produktiv sollte die Datenbank extern bzw. operator-basiert laufen, und Mail sollte von Anfang an mitgedacht werden. Danach erzeugst du in Authentik eine **Application + OAuth2/OIDC Provider**-Kombination. Das ist der von Authentik empfohlene Weg zur Erstellung eines OIDC-Providers. citeturn34view0turn35view0
Für den OIDC-Provider in Authentik verwendest du als Redirect-URI **nicht** die Synapse-Callback-URL aus der direkten Synapse-Dokumentation, sondern die von MAS erwartete Upstream-Callback-URL. MAS verlangt für Upstream-Provider eine stabile ULID als Provider-ID und verwendet daraus die Callback-URL `https://<auth-service-domain>/upstream/callback/<id>`. Optional kannst du zusätzlich die Backchannel-Logout-URL `https://<auth-service-domain>/upstream/backchannel-logout/<id>` hinterlegen. Authentik unterstützt für OIDC-Anwendungen Front- und Back-Channel-Logout, sofern der Provider entsprechend konfiguriert ist. citeturn14view1turn26view3turn35view1
In ESS selbst musst du dafür **keinen Chart forken**. Die vorgesehene Stelle ist `matrixAuthenticationService.additional`. Dort gibst du MAS die Upstream-OIDC-Konfiguration und schaltest die lokale Passwortdatenbank ab, wenn Authentik der alleinige Eintrittspunkt werden soll. Genau dafür ist die ESS-Advanced-Dokumentation da. citeturn7view2
Ein praktikabler MAS-Werteblock für deine GitOps-Struktur sieht so aus:
```yaml
matrixAuthenticationService:
additional:
10-authentik-upstream.yaml:
config: |
passwords:
enabled: false
account:
password_registration_enabled: false
upstream_oauth2:
providers:
- id: 01JVXXXXXXXXXXXXXXXAUTHN
issuer: "https://auth.example.com/application/o/matrix-mas/"
human_name: "Community Login"
client_id: "mas-community"
client_secret: "AUS_SOPS_SECRET"
token_endpoint_auth_method: client_secret_post
scope: "openid profile email"
fetch_userinfo: true
on_backchannel_logout: logout_browser_only
claims_imports:
localpart:
action: require
template: "{{ user.preferred_username }}"
# Nur für Bestandskonten und nur nach Pilotphase:
# on_conflict: set
displayname:
action: force
template: "{{ user.name }}"
email:
action: force
template: "{{ user.email }}"
```
Die inhaltliche Grundlage dafür kommt direkt aus den MAS-Upstream-OIDC-Dokumenten: Provider-`id` als ULID, `issuer`, `client_id`, `client_secret`, Scope, optionale UserInfo-Nutzung und Claim-Mapping für `localpart`, `displayname` und `email`. ESS injiziert genau solche zusätzlichen MAS-Dateien über `matrixAuthenticationService.additional`. citeturn14view1turn14view0turn13view5turn7view2
Für das eigentliche Onboarding in Authentik hast du zwei gute Varianten. Wenn du das MAS-Token-Modell möglichst ähnlich nachbauen willst, nimmst du **Invitations**. Authentik kann Einladungs-URLs an konkrete Empfänger senden oder generische Einladungslinks bereitstellen, bei denen Benutzer ihre Credentials selbst festlegen. Wenn du eher echte Self-Service-Registrierung willst, nimmst du einen Enrolment-Flow, optional mit E-Mail-Verifikation. Beide Muster sind offiziell dokumentiert; es gibt sogar vorgefertigte Blueprints für invitation-based enrollment und Beispiel-Flows für Enrolment mit oder ohne E-Mail-Verifikation. citeturn21view1turn21view2
Wenn du bei der Benutzerführung in Element noch glatter werden willst, kannst du optional in Element Web `sso_redirect_options` setzen, damit Nutzer auf der Login- oder Welcome-Seite sofort in den OIDC-/SSO-Flow gehen. Das ist kein Muss — MAS kann bei einem einzigen Upstream-Provider ohnehin automatisch weiterleiten — aber es verbessert den Eindruck eines „SSO-only“-Setups. citeturn15view4turn26view0
Ein optionaler Element-Web-Block dafür wäre:
```json
{
"sso_redirect_options": {
"on_login_page": true,
"on_welcome_page": true
}
}
```
## Risiken und Fallstricke
Der heikelste Punkt ist die **Abbildung auf bestehende Matrix-Konten**. MAS kann Upstream-Identitäten an vorhandene lokale Nutzer binden, wenn der gemappte `localpart` passt. Standardmäßig verweigert MAS diese Verknüpfung aber; dafür gibt es `claims_imports.localpart.on_conflict` mit Werten wie `set`, `add` oder `replace`. Die Dokumentation warnt ausdrücklich davor, dass das bei unsauberem Mapping ein Account-Takeover-Risiko werden kann. In der Praxis heißt das: `on_conflict` nur dann einschalten, wenn du sicher garantieren kannst, dass Authentik-`preferred_username` oder ein anderes Attribut **eindeutig und dauerhaft** dem gewünschten Matrix-Localpart entspricht. citeturn26view2turn14view0
Der zweite Betriebsfallstrick ist GitOps-spezifisch: Änderungen und Ergänzungen im `upstream_oauth2.providers`-Block synchronisiert MAS beim Start in seine Datenbank, **Entfernungen** aber nicht automatisch. Wenn du einen Provider wieder herausnimmst oder umbenennst, brauchst du zusätzlich `mas-cli config sync --prune`. Für FluxCD ist das wichtig, weil „Config aus Git gelöscht“ hier nicht automatisch „Provider aus MAS-DB gelöscht“ bedeutet. citeturn27search1turn13view5
Der dritte Punkt betrifft Logout-Semantik. Sowohl MAS als auch Authentik können OIDC-Logout verarbeiten, aber du solltest den Modus bewusst wählen. `logout_all` in MAS ist am konsequentesten, kann aber auch Sessions beenden, die aus derselben Upstream-Sitzung stammen und über andere Flows erzeugt wurden. `logout_browser_only` ist meist die konservativere Wahl für einen ersten Rollout. Auf Authentik-Seite kannst du zusätzlich vollständiges Single Logout aktivieren, wenn Logout aus einer Anwendung auch die Authentik-Sitzung selbst und andere verbundene Anwendungen beenden soll. citeturn26view0turn35view1
Falls du Authentik doch nicht einführst und stattdessen bei MAS-Registration-Tokens bleibst, solltest du den ESS-Hinweis ernst nehmen: `account.password_registration_email_required: false` darf auf einem öffentlich föderierenden System nur zusammen mit Einschränkungen wie `registration_token_required: true` benutzt werden, sonst läufst du direkt in Missbrauch und Spam. MAS selbst dokumentiert dieselben Schalter im `account`-Block: Passwort-Registrierung ist standardmäßig aus, E-Mail-Pflicht standardmäßig an, Registration-Token-Pflicht standardmäßig aus. citeturn7view2turn13view0turn13view1turn13view2
## Praktische Entscheidung
Wenn dein Zielbild lautet **„Einladungen, Self-Service, Verifikation, Gruppen, spätere Erweiterbarkeit“**, dann ist meine klare Empfehlung: **Authentik vor MAS**. Das ist in ESS Community sauber integrierbar, nutzt die offiziell vorgesehenen Konfigurationspfade und hält Synapse in der heute empfohlenen MAS-Architektur. citeturn7view2turn31view0turn14view1
Wenn dein Zielbild dagegen nur lautet **„ich will wieder Registration Tokens vergeben können, aber ohne zweiten IdP zu betreiben“**, dann ist die wirtschaftlichere Lösung sehr wahrscheinlich: **MAS Admin API automatisieren statt Authentik einführen**. Funktional ist das möglich; der fehlende Baustein ist nur die Bedienoberfläche, nicht die darunterliegende Token-Funktion. citeturn19search0turn28search0turn28search2
## Offene Punkte und Grenzen
Die produktseitigen Integrationsfragen sind gut durch die offiziellen Dokumentationen abgedeckt. Was ich in diesem Bericht bewusst generisch gehalten habe, sind deine **konkreten Datei- und Pfadnamen im GitOps-Repo** sowie die **genaue Secret-Aufteilung** zwischen ConfigMap und SOPS-Secret. Die technische Empfehlung steht trotzdem fest: MAS bleibt die Matrix-Schicht, Authentik wird der Upstream-IdP, und die Integration erfolgt über `matrixAuthenticationService.additional` in ESS. citeturn7view2turn14view1
+327
View File
@@ -0,0 +1,327 @@
# MISSING_MATRIX_RTC_TRANSPORT in ESS 26.4.0: Ursachenanalyse und vollständiger Fix
**Die Ursache ist Hypothese 1: `wellKnownDelegation.enabled: false` ist der Kill-Switch.** In genau dieser Konfiguration schreibt das ESS-Chart den Schlüssel `org.matrix.msc4143.rtc_foci` normalerweise automatisch in das an `https://axion1337.chat/.well-known/matrix/client` ausgelieferte JSON-Dokument sobald `wellKnownDelegation.enabled: true` **und** `matrixRTC.enabled: true` gesetzt sind. Mit `wellKnownDelegation: false` fällt dieses Dokument komplett weg. Element Web hat damit keinen Discovery-Pfad zur LiveKit-Instanz auf `mrtc.axion1337.chat`, und Matrix-JS-SDK wirft genau deshalb `MISSING_MATRIX_RTC_TRANSPORT`. Hypothese 2 (Synapse-Custom-Keys sind bogus) ist ebenfalls korrekt. Hypothese 4 (Element Web braucht explizites `rtc_foci`) ist **falsch** Element Web liest `rtc_foci` ausschließlich aus Well-Known, niemals aus `config.json`. Hypothese 5 ist ein *sekundäres* Thema: LiveKit UDP-Ports müssen offen sein, aber der Fehler `MISSING_MATRIX_RTC_TRANSPORT` entsteht ausschließlich aus der Discovery, nicht aus Medien-Erreichbarkeit.
## Wie die Fehlerkette konkret abläuft
Element Web ruft beim Start eines Anrufs intern zwei Discovery-Pfade auf: `GET /_matrix/client/v1/rtc/transports` gegen Synapse (MSC4143, ab Synapse 1.140) und als Fallback `GET https://<serverName>/.well-known/matrix/client`, wo der Schlüssel `org.matrix.msc4143.rtc_foci` erwartet wird. Das ESS-Chart befüllt **beide** Pfade automatisch, sobald `matrixRTC.enabled: true` ist: In `wellKnownDelegation` wird das `rtc_foci`-Array injiziert, in Synapse wird der `matrix_rtc.transports`-Block in die `homeserver.yaml` gemerged (chart-intern, siehe ess-helm Changelog Eintrag #855: *"Configure experimental MSC4143 advertisement in Synapse when MatrixRTC is enabled. This is in addition to the MSC4143 advertisement on the client well-known endpoint for now, but it is expected to replace it in time."*). **Beide Mechanismen werden durch `wellKnownDelegation.enabled: false` nicht vollständig deaktiviert** die Synapse-Seite wird zwar weiter gesetzt, aber weil kein Well-Known am Apex existiert, kann Element Web den Homeserver gar nicht über `default_server_name` auflösen und daher auch den Transport-Endpunkt nicht konsistent nutzen. Resultat: leere Transport-Liste, Fehler ausgelöst.
Ein korrektes `/.well-known/matrix/client` sieht so aus (das ist exakt, was das Chart ausliefern würde):
```json
{
"m.homeserver": { "base_url": "https://matrix.axion1337.chat" },
"m.identity_server": { "base_url": "https://vector.im" },
"org.matrix.msc4143.rtc_foci": [
{ "type": "livekit",
"livekit_service_url": "https://mrtc.axion1337.chat" }
]
}
```
Der Schlüsselname ist **`org.matrix.msc4143.rtc_foci`** (MSC-Prefix, kein `m.rtc_foci`). Das Value-Objekt hat zwingend `type: "livekit"` und `livekit_service_url: "https://<matrixRTC.ingress.host>"`.
## Warum die Custom-Synapse-Values aktuell wirkungslos sind
Die Keys `matrix_rtc_enabled` und `matrix_rtc_uri` **existieren in Synapse nicht**. Synapse kennt für MatrixRTC genau zwei Dinge: einen `experimental_features`-Block (`msc3266_enabled`, `msc4140_enabled`, `msc4222_enabled`, und implementierungsabhängig `msc4143_enabled`) und einen Top-Level-Block `matrix_rtc.transports:` für den neuen `/v1/rtc/transports`-Endpunkt. Das ESS-Chart setzt diese **automatisch**, sobald `matrixRTC.enabled: true` ist. Synapse ignoriert unbekannte Top-Level-Keys in der `homeserver.yaml` stillschweigend; der Container crasht nicht, aber es passiert auch exakt gar nichts. Der ganze `rtc-config`-Block muss raus. Der `url-previews`-Block ist dagegen legitim die Keys `url_preview_enabled`, `url_preview_ip_range_blacklist`, `max_spider_size` sind echte Synapse-Optionen.
## Der eigentliche Grund für die "ACME Race Condition"
Das Problem war **nicht** ein Bug, sondern eine vorhersagbare Fehlkonfiguration: Beide Ingresses (Element Web und wellKnownDelegation) forderten über die `cert-manager.io/cluster-issuer`-Annotation **jeweils ein eigenes TLS-Secret** für denselben Host `axion1337.chat` an. Cert-manager erzeugt dann zwei `Certificate`-Objekte mit unterschiedlichen `secretName`s → zwei `Order`-Objekte → HTTP-01-Solver-Ingresses überschreiben sich gegenseitig → Let's-Encrypt-Rate-Limit schlägt zu (5 duplicate certs / 7 Tage). Siehe cert-manager Issue #2342 und Discussion #3511: *"If both ingresses have their own secret to save the certificate tls, cert-manager runs havoc and exhausts the ACME limit very quickly."* Die saubere Lösung ist, dass genau **eine** Zertifikat-Quelle für `axion1337.chat` existiert.
Das ESS-Chart selbst erwartet, dass `elementWeb.ingress.host` **nicht** gleich `serverName` ist. Der Standard-Pattern ist: Element Web auf `chat.<apex>` bzw. `app.<apex>`, Well-Known auf dem Apex, und eine optionale `baseDomainRedirect.url` leitet `/` auf dem Apex zu Element Web weiter.
## Fix 1 (empfohlen): Element Web auf Subdomain umziehen
Das ist die Lösung, für die das Chart designt ist, und erfordert genau eine DNS-Änderung sowie YAML-Anpassungen.
### DNS-Voraussetzungen
| Record | Typ | Ziel | Zweck |
|---|---|---|---|
| `axion1337.chat` | A/AAAA | öffentliche IP des K3s-Nodes | Apex: Well-Known + Cert für Matrix-Server-Delegation |
| `matrix.axion1337.chat` | A/AAAA | K3s-Node-IP | Synapse |
| `account.axion1337.chat` | A/AAAA | K3s-Node-IP | Matrix Authentication Service |
| `mrtc.axion1337.chat` | A/AAAA | K3s-Node-IP | LiveKit-Signaling + lk-jwt-service |
| `chat.axion1337.chat` | A/AAAA | K3s-Node-IP | **NEU**: Element Web |
| `admin.axion1337.chat` | A/AAAA | K3s-Node-IP | Element Admin |
### Angepasster HelmRelease (`apps/production/element-server-suite.yaml`)
```yaml
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: matrix-stack
namespace: matrix
spec:
interval: 1h
chart:
spec:
chart: matrix-stack
version: "26.4.0"
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: axion1337.chat
certManager:
clusterIssuer: letsencrypt-prod
postgres:
enabled: true
synapse:
enabled: true
ingress:
host: matrix.axion1337.chat
matrixAuthenticationService:
enabled: true
ingress:
host: account.axion1337.chat
matrixRTC:
enabled: true
ingress:
host: mrtc.axion1337.chat
# SFU public-IP override empfohlen, falls der Node hinter NAT steht
# und STUN die falsche Adresse zurückgibt:
# sfu:
# useStunToDiscoverPublicIP: false
# manualIP: "<public-IP-of-node>"
elementWeb:
enabled: true
ingress:
host: chat.axion1337.chat # <- NICHT mehr der Apex
elementAdmin:
enabled: true
ingress:
host: admin.axion1337.chat
wellKnownDelegation:
enabled: true # <- zurück auf true (Default)
# Optional: Redirect vom Apex "/" auf Element Web.
# Das Chart-Feature kam in 26.x hinzu; falls dein konkreter Wert-Key
# fehlt, siehe Fix 2 (eigene IngressRoute) als sichere Alternative.
baseDomainRedirect:
url: https://chat.axion1337.chat
```
Mit diesem Setup beansprucht **genau ein** Chart-erzeugter Ingress den Apex `axion1337.chat`, nämlich der der `wellKnownDelegation`. Es gibt nur ein Zertifikat, keine Race Condition. Element Web läuft auf `chat.axion1337.chat` und bekommt sein eigenes, konfliktfreies Cert.
### Bereinigter Synapse-ConfigMap (`apps/production/custom-configs/synapse-values.yaml`)
```yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: ess-synapse-custom
namespace: matrix
data:
values.yaml: |
synapse:
logging:
rootLevel: INFO
levelOverrides:
synapse.media.url_previewer: DEBUG
additional:
# RTC-Block ENTFERNT: matrix_rtc_enabled / matrix_rtc_uri sind keine
# gültigen Synapse-Keys. Das ESS-Chart setzt matrix_rtc.transports und
# die nötigen experimental_features automatisch, wenn matrixRTC.enabled=true.
1-url-previews:
config: |
url_preview_enabled: true
url_preview_ip_range_blacklist:
- '127.0.0.0/8'
- '10.0.0.0/8'
- '172.16.0.0/12'
- '192.168.0.0/16'
- '100.64.0.0/10'
- '192.0.0.0/24'
- '169.254.0.0/16'
- '192.88.99.0/24'
- '198.18.0.0/15'
- '192.0.2.0/24'
- '198.51.100.0/24'
- '203.0.113.0/24'
- '224.0.0.0/4'
- '::1/128'
- 'fe80::/10'
- 'fc00::/7'
- '2001:db8::/32'
- 'ff00::/8'
- 'fec0::/10'
max_spider_size: 10M
```
Wichtige Details zum `additional`-Mechanismus: die Keys werden **alphabetisch sortiert** gemerged, daher die `1-`/`2-`-Namenskonvention. Arrays werden ersetzt, nicht zusammengeführt.
### Element-Web-ConfigMap (`apps/production/custom-configs/element-values.yaml`)
Hier ist **keine** RTC-spezifische Änderung nötig. Element Web hat gemäß `element-web/docs/config.md` keinen `rtc_foci`-Key und entdeckt den Transport ausschließlich über Well-Known. Der optionale `element_call`-Block steuert nur Branding und Verhalten:
```yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: ess-element-custom
namespace: matrix
data:
values.yaml: |
elementWeb:
additional:
config.json: |
{
"brand": "aXion1337.Chat",
"element_call": {
"brand": "aXion1337 Call",
"use_exclusively": true
},
"features": {
"feature_video_rooms": true,
"feature_element_call_video_rooms": true
},
"show_labs_settings": true
}
```
`use_exclusively: true` entfernt die Legacy-Jitsi-Option aus dem UI, sodass Anrufe garantiert über MatrixRTC/LiveKit laufen.
## Fix 2 (Alternative): Apex manuell splitten per Traefik IngressRoute
Falls `baseDomainRedirect` in deiner 26.4.0-Revision nicht greift oder du Element Web auf dem Apex behalten willst, ersetze beide Chart-Ingresses durch eine **einzige Traefik IngressRoute mit einem einzigen Certificate**. Dadurch sieht cert-manager nur noch eine Quelle für `axion1337.chat`, und Traefik routet Pfad-basiert.
```yaml
# apps/production/apex-ingress.yaml
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: axion-apex-tls
namespace: matrix
spec:
secretName: axion-apex-tls
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
dnsNames:
- axion1337.chat
---
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: axion-apex
namespace: matrix
spec:
entryPoints: [websecure]
tls:
secretName: axion-apex-tls
routes:
# Höchste Priorität: /.well-known/matrix/* -> wellKnownDelegation-Service
- match: Host(`axion1337.chat`) && PathPrefix(`/.well-known/matrix`)
kind: Rule
priority: 100
services:
- name: matrix-stack-well-known-delegation # ggf. Service-Namen mit `kubectl get svc -n matrix` verifizieren
port: 8080
# Niedrigere Priorität: alles andere -> Element Web
- match: Host(`axion1337.chat`)
kind: Rule
priority: 10
services:
- name: matrix-stack-element-web
port: 8080
```
Dazu muss in der HelmRelease die automatische Chart-Ingress-Erzeugung für diese beiden Komponenten unterbunden werden setze `wellKnownDelegation.ingress.className: "none"` bzw. `elementWeb.ingress.className: "none"` oder setze die `host`-Werte auf Dummy-Hostnames, sodass die Chart-Ingresses nicht denselben Apex beanspruchen. Fix 1 ist wegen geringerer Komplexität klar zu bevorzugen.
## Netzwerk- und Firewall-Anforderungen für LiveKit
**Das ist der nächste Stolperstein nach dem Well-Known-Fix.** Die HTTPS-Ingress auf `mrtc.axion1337.chat` terminiert nur **Signaling (WSS auf TCP 443)** und die lk-jwt-service-Endpunkte `/sfu/get`, `/get_token`, `/healthz`. WebRTC-Medien laufen **nicht** über den Ingress, sondern direkt zum Node auf UDP/TCP-NodePorts.
Öffne auf deiner K3s-Node-Firewall (und im Router/Cloud-Security-Group):
| Port | Protokoll | Standard ESS CE | Zweck |
|---|---|---|---|
| 80, 443 | TCP | — | Ingress + ACME HTTP-01 |
| `rtcMuxedUdp` NodePort | **UDP** | 30002 (CE) / 30882 (Pro) | **Kritisch**: WebRTC-Medien (UDP-Mux). Ohne das: keine Audio/Video-Daten |
| `rtcTcp` NodePort | TCP | 30881 | ICE/TCP-Fallback für Clients ohne UDP |
| `turn` NodePort | UDP | 30004 | Nur falls `matrixRTC.sfu.exposedServices.turn.enabled: true` |
| `turnTLS` Port | TCP | 31443 oder 443 | Nur falls TURN/TLS aktiv; braucht SNI-Passthrough und eigenes Hostname |
Ermittle die tatsächlichen NodePorts per `kubectl get svc -n matrix | grep rtc`. Wenn dein Node hinter NAT steht und LiveKits STUN-basierte Public-IP-Erkennung die falsche Adresse zurückliefert, setze zusätzlich:
```yaml
matrixRTC:
sfu:
useStunToDiscoverPublicIP: false
manualIP: "<deine-öffentliche-IP>"
```
## Verifikationsschritte
Nach Reconcile (`flux reconcile helmrelease matrix-stack -n matrix`) diese vier Tests durchführen:
**1. Well-Known für Matrix-Client liefert rtc_foci:**
```bash
curl -sS https://axion1337.chat/.well-known/matrix/client | jq .
# Erwartung: JSON mit "m.homeserver" UND "org.matrix.msc4143.rtc_foci"
# das livekit_service_url == "https://mrtc.axion1337.chat" enthält.
curl -sS https://axion1337.chat/.well-known/matrix/server | jq .
# Erwartung: {"m.server": "matrix.axion1337.chat:443"}
```
**2. lk-jwt-service und LiveKit-Signaling sind erreichbar:**
```bash
curl -i https://mrtc.axion1337.chat/healthz
# Erwartung: HTTP/1.1 200 OK
curl -i https://mrtc.axion1337.chat/sfu/get
# Erwartung: HTTP/1.1 405 Method Not Allowed (akzeptiert nur POST)
curl -i -H "Connection: Upgrade" -H "Upgrade: websocket" \
-H "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==" \
-H "Sec-WebSocket-Version: 13" \
https://mrtc.axion1337.chat/
# Erwartung: HTTP/1.1 101 Switching Protocols (WebSocket-Upgrade)
```
**3. Synapse bietet den MSC4143-Endpunkt an:**
```bash
TOKEN="<gültiges-access-token>"
curl -sS -H "Authorization: Bearer $TOKEN" \
https://matrix.axion1337.chat/_matrix/client/v1/rtc/transports | jq .
# Erwartung: {"transports":[{"type":"livekit","livekit_service_url":"https://mrtc.axion1337.chat"}]}
```
**4. UDP-Medien kommen an:** In Element Web Call starten → im Chrome/Firefox-DevTools unter Netzwerk → WS prüfen, dass `wss://mrtc.axion1337.chat/rtc?access_token=...` einen `101`-Upgrade bekommt; unter `chrome://webrtc-internals` nach ICE-Candidate-Paaren mit Status `succeeded` und Typ `host/srflx` schauen. Falls nur Signaling, aber kein Media: UDP-NodePort auf Firewall prüfen.
## ACME-Race dauerhaft vermeiden
Merksatz: **Ein Hostname ⇒ genau ein `Certificate`-Objekt ⇒ genau ein `secretName`.** Drei betriebliche Strategien:
- **Subdomain-Trennung (Fix 1):** Jede Chart-Komponente bekommt einen eigenen FQDN, jeder FQDN genau ein Cert. Keine Kollision möglich. Das ist die vom Chart vorgesehene Form.
- **Geteiltes Secret:** Wenn zwei Ingresses denselben Host tragen müssen, trägt nur **einer** die `cert-manager.io/cluster-issuer`-Annotation. Beide referenzieren `spec.tls[].secretName` auf dasselbe Secret. Traefik wählt das Zertifikat korrekt aus.
- **DNS-01 statt HTTP-01:** Löst den Solver-Ingress-Konflikt grundsätzlich, weil kein zweiter Ingress erzeugt wird. Setzt DNS-Provider-Credentials im ClusterIssuer voraus.
## Vom Benutzer übersehene Chart-Werte
Außer den oben beschriebenen Korrekturen sind diese Keys erwähnenswert, falls du feintunen willst. `matrixRTC.hostAliases` ist ein dokumentierter Workaround gegen Cluster-interne DNS-Probleme, wenn der lk-jwt-service-Pod den Homeserver-Apex bzw. Synapse über den Ingress-Controller statt direkt erreichen muss. `matrixRTC.sfu.exposedServices.<name>.portType` akzeptiert `NodePort`, `HostPort` oder `LoadBalancer` für bare-metal K3s ohne externen LB ist `NodePort` richtig. `matrixRTC.sfu.additional."user-config.yaml".config` erlaubt rohes LiveKit-YAML für Spezialfälle (STUN-Server, Codecs). `wellKnownDelegation.additional.client` / `.server` / `.element` erlauben, in die erzeugten JSON-Dokumente zusätzliche Felder einzumischen normalerweise nicht nötig, aber nützlich für föderative Sonderfälle.
## Fazit
Der eigentliche Defekt war ein einziger Kippschalter: `wellKnownDelegation.enabled: false` hat den einzig existierenden Discovery-Kanal für den LiveKit-Focus gekappt. Der darum herum gebaute Custom-Synapse-Block ist ein Red Herring die zugehörigen Schlüssel existieren in Synapse schlicht nicht und werden ignoriert. Die ursprünglich empfundene ACME-Race ist kein Bug des Charts, sondern die vorhersagbare Folge davon, dass zwei Chart-erzeugte Ingresses (Element Web **auf dem Apex** plus Well-Known) jeweils ein eigenes Let's-Encrypt-Zertifikat für denselben Host anforderten. Sobald Element Web nach `chat.axion1337.chat` wandert und Well-Known wieder aktiv ist, produziert das Chart automatisch das korrekte `org.matrix.msc4143.rtc_foci`-Feld, injiziert den `matrix_rtc.transports`-Block in Synapse, und Element Call funktioniert vorausgesetzt die UDP-NodePort-Firewall ist offen, was nach dem Discovery-Fix der zweite Punkt auf der Prüfliste ist.
+12083
View File
File diff suppressed because it is too large Load Diff
+498
View File
@@ -0,0 +1,498 @@
# 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. citeturn1view2turn20view1turn26search11turn26search5turn35view0
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. citeturn16view0turn16view1turn16view2turn34view0turn35view0turn17search0turn17search2
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. citeturn20view1turn23view0turn23view3turn25search0turn25search13
## 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. citeturn1view2turn1view5turn31view1turn35view0turn24search0
```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. citeturn16view0turn16view1turn16view5
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 citeturn20view1turn12view0turn37view1turn1view2 |
| 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 citeturn36search0turn36search1 |
| Manuelle Admin-Erstellung | Solide als Fallback | Maximal kontrolliert, kein Self-Service nötig | Kein Selbstregistrierungsfluss, hoher Betriebsaufwand | Break-glass, Admin-/Testkonten citeturn27view1turn26search5 |
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. citeturn17search0turn17search2
## 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`. citeturn1view2turn31view1
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. citeturn2view4turn20view0turn1view2
```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. citeturn20view1turn23view0turn23view3
```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. citeturn31view1turn20view1turn23view0
```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. citeturn31view1turn1view2
```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. citeturn16view0turn16view1turn16view2
```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. citeturn24search0turn20view1turn22view0
```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. citeturn38view0turn31view1
## 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. citeturn20view1turn12view0turn16view0turn34view0
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. citeturn20view1turn22view0turn23view0
```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. citeturn12view0turn13view0turn37view0turn37view1turn37view2
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. citeturn13view0turn11view0
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. citeturn20view1turn12view0turn3view1turn2view5
```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. citeturn16view0turn27view0turn34view0turn35view0
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. citeturn27view0turn32search1turn32search0
```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”. citeturn21search1turn9search4
## 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. citeturn2view4turn20view0turn1view2
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 citeturn2view4turn12view0turn37view2 |
| 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 citeturn2view4turn2view5 |
| Halböffentlich | Tokenpflicht + CAPTCHA + E-Mail-Allowlist + restriktivere Rate-Limits | Wenn Einladungen wiederverwendbar oder breiter verteilt werden | Mehr Schutz gegen Bot-/Spam-Missbrauch citeturn3view2turn20view0turn3view1 |
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`. citeturn20view0turn3view1turn3view2turn2view5
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. citeturn17search6turn35view0
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**. citeturn20view1turn32search0turn32search1
## 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. citeturn20view1turn12view0turn16view0turn27view0
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. citeturn31view1turn38view0
- **MAS Admin API erreichbar:** `/api/spec.json` bzw. das Anlegen eines Tokens funktioniert mit `urn:mas:admin`. citeturn20view1turn10view0
- **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. citeturn12view0turn37view2
- **Onboarding korrekt:** Testnutzer landet im Haupt-Space, in Welcome/Announcements und falls aktiviert in weiteren Zielräumen. citeturn16view0turn27view0
- **Missbrauchsschutz greift:** Ablaufzeit, CAPTCHA, Rate-Limits und Policy-Regeln sind aktiv. citeturn3view1turn3view2turn20view0
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 citeturn2view4turn1view2turn38view0 |
| `403` bei Token-Erzeugung | OAuth-Client nicht in `policy.data.admin_clients` oder falscher Scope | Client-/Policy-Fragment korrigieren, neuen Access Token holen citeturn20view1turn23view3 |
| `404` auf `/api/admin/v1/...` | `adminapi` nicht in `http.listeners.resources` aktiviert | Listener-Block korrigieren und Redeploy citeturn20view1 |
| 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 citeturn16view0turn16view1turn16view2 |
| Feingranulare Raumzuweisung klappt nicht | Du versuchst Membership über Claim-Mapping zu lösen | Membership über Auto-Join, Provisioner oder Synapse-Modul abbilden citeturn21search1turn34view0 |
| “Groups” lassen sich nicht mehr sinnvoll zuweisen | Groups/Communities sind im aktuellen Synapse-Modell entfernt | Auf Spaces + Rooms umstellen citeturn17search0turn17search2 |
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`. citeturn13view0turn27view0turn35view0
## 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. citeturn1view2
- ESS Chart-Values zur Bestätigung, dass `matrixAuthenticationService.additional`/`synapse.additional`/`elementWeb.additional` offizielle Wertepfade sind. citeturn31view1turn31view4
- MAS-Konfigurationsreferenz für `account.*`, `captcha`, `policy`, `rate_limiting`, `email`, `clients`, `admin_clients` und HTTP-Listener. citeturn2view4turn20view0turn22view0turn23view0
- MAS Admin API und OpenAPI-Schema für Token-Erzeugung, Update, Revoke, Validitätsbegriff und Automationsmodell. citeturn20view1turn12view0turn13view0turn37view1turn37view2
- MAS Scope- und Access-Token-Doku für den Unterschied zwischen `urn:mas:admin` und benutzergebundenem Synapse-Admin-Zugriff. citeturn32search0turn32search1
- Synapse-Konfigurationsmanual für `auto_join_rooms`, Space-Aliasse, `autocreate_auto_join_rooms` und `auto_join_mxid_localpart`. citeturn16view0turn16view1turn16view2
- Synapse-Admin- und Modul-Doku für Raumzuweisung per API und Callback-Punkte `on_user_registration` / `on_user_login`. citeturn27view0turn34view0turn35view0
- Element-Web-Konfiguration für Welcome-/Branding-/OIDC-nahe UX-Optionen. citeturn24search0
- LiveKit-/Element-Call-Doku als optionale, vom Registrierungsfluss getrennte RTC-Schicht. citeturn25search0turn25search13
- Authentik-Invitationsdoku nur für den Vergleich innerhalb der Optionen-Tabelle. citeturn36search0turn36search1
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. citeturn20view1turn32search0turn38view0
+296
View File
@@ -0,0 +1,296 @@
# 🔄 GitOps ConfigMap Auto-Sync via Checksums
Dieses Dokument erklärt ein Kernproblem mit **Flux CD** und **externe Konfigurationen** und wie wir es gelöst haben.
---
## 📋 Das Problem: Flux erkennt ConfigMap-Änderungen nicht
### Die Situation
Unsere HelmRelease (`element-server-suite.yaml`) nutzt `valuesFrom`, um Konfigurationen aus externen **ConfigMaps** zu laden:
```yaml
spec:
valuesFrom:
- kind: ConfigMap
name: ess-element-custom # ← Diese ConfigMap
valuesKey: values.yaml
- kind: ConfigMap
name: ess-synapse-custom # ← Diese ConfigMap
valuesKey: values.yaml
```
Diese ConfigMaps entstehen aus Dateien im Git-Repo (`apps/production/custom-configs/*.yaml`) und werden von Kustomize in den Cluster deployed.
### Das Kernproblem
**Flux CD reagiert NICHT automatisch, wenn sich eine ConfigMap ändert.**
Warum? Flux überwacht nur das Git-Repository auf Änderungen. Wenn du `element-values.yaml` änderst:
1. ✅ Flux sieht die Änderung im Git-Repo
2. ✅ Kustomize erzeugt eine neue ConfigMap mit den neuen Werten
3. ❌ Aber Helm (der Helm-Controller in Flux) erkennt **nicht**, dass sich die externe ConfigMap geändert hat
4. ❌ Helm deployt das Chart nicht neu → Die alten Themes/Configs bleiben aktiv
Das ist kein Bug, sondern by-design: Helm achtet nur auf:
- Chart-Version (z.B. `26.4.0`)
- Änderungen in den direkten `values:` Blöcken
- Nicht auf externe Quellen wie ConfigMaps
---
## 🔑 Die Lösung: Checksums als Trigger
Wir nutzen **Annotations mit Checksums** als Workaround:
```yaml
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
annotations:
element-config-checksum: "401f8a87d0ef5d91d2e5032d4aede42c" # ← Hash der element-values.yaml
synapse-config-checksum: "e98fe81141f52e7ea833596ca39853b9" # ← Hash der synapse-values.yaml
```
Wenn sich der Annotation-Wert ändert, sieht Flux, dass die **HelmRelease selbst** sich geändert hat und deployt neu.
### Automatisierung via Git Pre-Commit Hook
Das Problem: Wer aktualisiert die Checksums, wenn man `element-values.yaml` editiert?
**Lösung:** Ein Git **pre-commit Hook** tut das automatisch:
```bash
$ vi apps/production/custom-configs/element-values.yaml
# (Farbe ändern, Theme hinzufügen, …)
$ git add apps/production/custom-configs/element-values.yaml
$ git commit -m "feat: new theme"
# ← Der Hook läuft jetzt:
# 1. Berechnet MD5-Hash der neuen element-values.yaml
# 2. Aktualisiert die Annotation in kustomization.yaml
# 3. Added kustomization.yaml zum Commit automatisch
```
Der User muss **nichts Zusätzliches tun** alles läuft automatisch.
---
## 🚀 Installation
Nach dem Repository **klonen**, führe dieses Skript aus:
```bash
cd prod/gitops
./scripts/install-hooks.sh
```
Das Skript:
- ✅ Erstellt einen Symlink `.git/hooks/pre-commit``scripts/hooks/pre-commit`
- ✅ Macht die Hook-Datei ausführbar
- ✅ Gibt dir eine Bestätigungsmeldung
**Einmalig pro lokales Repository.** Neue Klone müssen den Hook auch installieren.
---
## 📝 Workflow: ConfigMaps ändern
So sieht die Praxis aus:
### 1️⃣ Änderung machen
```bash
vi apps/production/custom-configs/element-values.yaml
# Edit: Themes, Farben, Logging-Level, etc.
```
### 2️⃣ Committen (Hook läuft automatisch)
```bash
git add apps/production/custom-configs/element-values.yaml
git commit -m "feat: add Dark Purple theme variant"
# Pre-Commit Hook läuft automatisch:
# → Berechnet neuen Hash
# → Updated kustomization.yaml
# → Added kustomization.yaml zum Commit
```
### 3️⃣ Überprüfung (optional)
```bash
# Schaue, was committet wird:
git show --stat
# Output sollte zeigen:
# element-values.yaml (modified)
# kustomization.yaml (modified) ← vom Hook hinzugefügt
```
### 4️⃣ Pushen & Deployen
```bash
git push
# ← Flux sieht die Änderung an kustomization.yaml
# ← HelmRelease wird neu-synced mit den neuen Checksums
# ← Die neue Config ist in ~5 Minuten aktiv
```
---
## 🔧 Technische Details
### Welche Dateien sind "überwacht"?
Der Hook beobachtet:
| Datei | Annotation | Ziel |
|-------|-----------|------|
| `apps/production/custom-configs/element-values.yaml` | `element-config-checksum` | Element Web Themes, Branding |
| `apps/production/custom-configs/synapse-values.yaml` | `synapse-config-checksum` | Synapse Logging, Federation |
### Hook-Implementation
Der Hook (`scripts/hooks/pre-commit`) macht folgendes:
1. Prüft, ob eine der überwachten Dateien geändert wurde (`git diff --cached`)
2. Berechnet den MD5-Hash der Datei (kompatibel mit macOS/Linux)
3. Ersetzt den Hash-Wert in `apps/production/kustomization.yaml` via `sed`
4. Added `kustomization.yaml` automatisch zum Commit mit `git add`
```bash
# Beispiel: element-values.yaml ändert sich von
value: "401f8a87d0ef5d91d2e5032d4aede42c" # element-config
# zu
value: "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6" # element-config
```
---
## ⚠️ Häufige Fragen
### F: Was passiert, wenn der Hook fehlschlägt?
A: Der Commit wird **abgebrochen**. Du siehst eine Fehlermeldung. Typische Gründe:
- `md5` oder `md5sum` nicht installiert (sehr unwahrscheinlich)
- `sed` Syntax-Fehler (sehr selten)
Lass dich nicht entmutigen rufe denen auf, die das Setup machen 🚀
### F: Kann ich den Hook deaktivieren?
A: Ja, aber bitte nicht! Wenn du temporär ohne Hook arbeiten möchtest:
```bash
git commit --no-verify
```
Das umgeht den Hook einmalig. Danach musst du die Checksums manuell aktualisieren.
### F: Der Hook touched `kustomization.yaml`, obwohl ich sie nicht editiert habe?
A: Das ist **Absicht**. Der Hook updated **nur** die Checksums, nicht den Rest der Datei. Das ist sauber und Git-freundlich.
### F: Was wenn ich mehrere ConfigMaps gleichzeitig ändere?
A: Der Hook aktualisiert **alle** deren Checksums in einem Pass. Keine Probleme.
---
## 🧪 Verifikation: Hook funktioniert?
Teste es selbst:
```bash
# 1. Hook installieren
./scripts/install-hooks.sh
# 2. Eine Teständerung machen
echo "# Test" >> apps/production/custom-configs/element-values.yaml
# 3. Committen
git add apps/production/custom-configs/element-values.yaml
git commit -m "test: verify hook"
# 4. Überprüfung: Wurde kustomization.yaml auto-updated?
git diff HEAD~1 apps/production/kustomization.yaml | grep -i checksum
# Output sollte zeigen, dass ein Checksum sich geändert hat
```
Wenn ja → Hook funktioniert! ✅
Wenn nein → Rufe uns an 📞
---
## 📚 Zusammenhang mit Flux CD
```
┌─────────────────────────────────────────────────────────────┐
│ Git Repository (axion1337.chat-gitops) │
├─────────────────────────────────────────────────────────────┤
│ │
│ apps/production/custom-configs/ │
│ ├── element-values.yaml (du änderst das) │
│ ├── synapse-values.yaml │
│ └── mas-secret.yaml │
│ │
│ apps/production/ │
│ └── kustomization.yaml (Hook updated die Checksums hier) │
│ │
│ apps/production/ │
│ └── element-server-suite.yaml (HelmRelease mit Annotations)│
│ │
└─────────────────────────────────────────────────────────────┘
↓ git push
┌─────────────────────────────────────────────────────────────┐
│ FluxCD im Kubernetes-Cluster │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. GitRepository Source │
│ └─ Fetcht dein Repo alle 10 Minuten │
│ │
│ 2. Kustomization production-apps │
│ └─ Applyt die Kustomize-Overlays │
│ └─ Erzeugt ConfigMaps mit neuen Werten │
│ │
│ 3. HelmRelease matrix-stack │
│ └─ Sieht neue Annotations (Checksums) │
│ └─ Deployt Helm-Chart mit neuen Werten │
│ │
│ 4. Element Web Pods │
│ └─ Restarten automatisch │
│ └─ Laden neue Config aus der ConfigMap │
│ │
└─────────────────────────────────────────────────────────────┘
```
---
## 🚨 Notfall: Manuelle Checksum-Aktualisierung
Falls der Hook ausfällt (z.B. weil Git Hooks in CI deaktiviert sind), kannst du Checksums manuell aktualisieren:
```bash
# 1. Neuen Hash berechnen
md5sum apps/production/custom-configs/element-values.yaml
# Output: a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6 apps/production/custom-configs/element-values.yaml
# 2. Annotation in kustomization.yaml updaten
vi apps/production/kustomization.yaml
# Ändere: value: "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6" # element-config
# 3. Committen
git add apps/production/kustomization.yaml
git commit -m "fix: manual checksum update"
git push
```
---
**Version:** 1.0
**Letztes Update:** 2026-05-14
**Kompatibilität:** FluxCD 2.x, Kustomize 5.x+
+206
View File
@@ -0,0 +1,206 @@
# Element Desktop Setup Scripts
Automatische Konfiguration und Installation von Element Desktop mit Custom Themes für aXion1337.Chat
## 📋 Systemanforderungen
- **Windows 10/11**
- **macOS 10.13+**
- **Linux** (Ubuntu, Debian, Fedora, Arch, openSUSE, etc.)
---
## 🪟 Windows
### Installation
1. **Lade die Datei herunter**: `element-setup-windows.cmd`
2. **Doppelklick** auf die Datei
3. Das Skript wird automatisch:
- ✅ Die config.json erstellen
- ✅ Element Desktop installieren (falls nötig)
- ✅ Element starten
**Oder manuell:**
```cmd
element-setup-windows.cmd
```
### Voraussetzungen
Das Skript versucht automatisch, Element zu installieren über:
- Windows Store (Microsoft Store)
- WinGet (Windows Package Manager)
- Chocolatey
Falls nichts davon vorhanden ist, installiere Element manuell:
https://element.io/download
---
## 🍎 macOS
### Installation
1. **Lade die Datei herunter**: `element-setup-macos.command`
2. **Doppelklick** im Finder
3. Das Skript wird automatisch:
- ✅ Die config.json erstellen
- ✅ Element Desktop installieren (über Homebrew falls nötig)
- ✅ Element starten
**Oder im Terminal:**
```bash
chmod +x element-setup-macos.command
./element-setup-macos.command
```
### Voraussetzungen
Das Skript nutzt **Homebrew** (falls vorhanden):
```bash
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
```
Oder installiere Element manuell:
https://element.io/download
---
## 🐧 Linux
### Installation
1. **Lade die Datei herunter**: `element-setup-linux.sh`
2. **Im Terminal:**
```bash
chmod +x element-setup-linux.sh
./element-setup-linux.sh
```
3. Das Skript wird automatisch:
- ✅ Die config.json erstellen
- ✅ Element Desktop installieren (über apt/dnf/pacman/zypper)
- ✅ Element starten
### Unterstützte Distributionen
- **Ubuntu/Debian**: `apt`
- **Fedora/RHEL**: `dnf`
- **Arch Linux**: `pacman`
- **openSUSE**: `zypper`
---
## ✅ Was die Skripte machen
Alle Skripte führen diese Schritte aus:
1. **config.json erstellen** im richtigen Verzeichnis:
- **Windows**: `%APPDATA%\Element\config.json`
- **macOS**: `~/Library/Application Support/Element/config.json`
- **Linux**: `~/.config/Element/config.json`
2. **Automatische Konfiguration**:
```json
{
"configUrl": "https://axion1337.chat/config.json",
"brand": "aXion1337.Chat",
"default_theme": "aXion1337 Dark",
"show_labs_settings": true,
"features": {
"feature_qr_code_login": true
}
}
```
3. **Element Desktop installieren** (falls nicht vorhanden)
4. **Element starten** und deine Custom Themes laden
---
## 🎨 Themes nach Setup
Nach dem Setup hat dein Element Desktop:
1. **Automatisch geladene Themes** von: `https://axion1337.chat/config.json`
2. **Standard Theme**: aXion1337 Dark
3. **Verfügbare Themes**:
- aXion1337 Dark
- Deep Purple
- Discord Dark
- Electric Blue
- Everforest dark hard
- Gruvbox Dark
- Wal
---
## ⚙️ Manuelle Konfiguration (falls Skript nicht funktioniert)
Falls die Skripte nicht funktionieren, erstelle manuell die config.json:
### Windows
```
%APPDATA%\Element\config.json
```
### macOS
```
~/Library/Application Support/Element/config.json
```
### Linux
```
~/.config/Element/config.json
```
**Inhalt** (copy-paste):
```json
{
"configUrl": "https://axion1337.chat/config.json",
"brand": "aXion1337.Chat",
"default_theme": "aXion1337 Dark",
"show_labs_settings": true,
"features": {
"feature_qr_code_login": true
},
"setting_defaults": {
"custom_themes": []
}
}
```
---
## 🆘 Troubleshooting
### Element startet nicht
- Stelle sicher, dass Element Desktop installiert ist
- Überprüfe den Installationspfad
- Starten manuell: `element` (Terminal/CMD)
### config.json wird nicht geladen
- Überprüfe, dass die Datei im richtigen Verzeichnis ist
- Beende Element vollständig (auch in der Taskbar)
- Starte Element neu
### Themes nicht sichtbar
- Überprüfe Internetverbindung
- `https://axion1337.chat/config.json` sollte erreichbar sein
- Öffne Settings > Appearance > Theme und wähle manuell
---
## 📞 Support
Bei Fragen oder Problemen:
- https://element.io/help
- https://axion1337.chat
---
**Version**: 1.0
**Letztes Update**: 2026-04-23
**Kompatibilität**: Element Desktop 1.11+
+114
View File
@@ -0,0 +1,114 @@
#!/bin/bash
# Element Desktop Setup Script for Linux
# Ausführung: chmod +x element-setup-linux.sh && ./element-setup-linux.sh
echo "========================================"
echo "Element Desktop Konfiguration Setup"
echo "========================================"
echo ""
# Erkenne Linux Distribution
if [ -f /etc/os-release ]; then
. /etc/os-release
OS=$ID
else
OS=$(uname -s)
fi
# Config-Verzeichnis
CONFIG_DIR="$HOME/.config/Element"
CONFIG_FILE="$CONFIG_DIR/config.json"
# Erstelle Verzeichnis falls nicht vorhanden
if [ ! -d "$CONFIG_DIR" ]; then
echo "Erstelle Element Verzeichnis..."
mkdir -p "$CONFIG_DIR"
fi
# Erstelle config.json
echo "Erstelle config.json..."
cat > "$CONFIG_FILE" << 'EOF'
{
"configUrl": "https://axion1337.chat/config.json",
"brand": "aXion1337.Chat",
"default_theme": "aXion1337 Dark",
"show_labs_settings": true,
"features": {
"feature_qr_code_login": true
},
"setting_defaults": {
"custom_themes": []
}
}
EOF
echo "✅ Config erstellt: $CONFIG_FILE"
echo ""
# Versuche Element zu starten/installieren
echo "Überprüfe Element Desktop Installation..."
# Methode 1: Element ist bereits installiert
if command -v element &> /dev/null; then
echo "Element gefunden. Starte Element..."
element &
sleep 2
exit 0
fi
# Methode 2: Apt (Debian/Ubuntu)
if command -v apt &> /dev/null; then
echo ""
echo "Installiere Element über apt..."
sudo apt update
sudo apt install -y element-desktop
echo "Starte Element..."
element &
exit 0
fi
# Methode 3: Pacman (Arch Linux)
if command -v pacman &> /dev/null; then
echo ""
echo "Installiere Element über pacman..."
sudo pacman -S --noconfirm element-web
echo "Starte Element..."
element &
exit 0
fi
# Methode 4: DNF (Fedora/RHEL)
if command -v dnf &> /dev/null; then
echo ""
echo "Installiere Element über dnf..."
sudo dnf install -y element-desktop
echo "Starte Element..."
element &
exit 0
fi
# Methode 5: Zypper (openSUSE)
if command -v zypper &> /dev/null; then
echo ""
echo "Installiere Element über zypper..."
sudo zypper install -y element-desktop
echo "Starte Element..."
element &
exit 0
fi
# Fallback
echo ""
echo "⚠️ Element Desktop konnte nicht automatisch installiert werden."
echo ""
echo "Bitte installiere Element Desktop manuell:"
echo " Ubuntu/Debian: sudo apt install element-desktop"
echo " Fedora/RHEL: sudo dnf install element-desktop"
echo " Arch: sudo pacman -S element-web"
echo " openSUSE: sudo zypper install element-desktop"
echo ""
echo "Oder: https://element.io/download"
echo ""
echo "✅ Deine config.json wurde erstellt unter:"
echo "$CONFIG_FILE"
echo ""
+84
View File
@@ -0,0 +1,84 @@
#!/bin/bash
# Element Desktop Setup Script for macOS
# Doppelklick im Finder zum Ausführen
echo "========================================"
echo "Element Desktop Konfiguration Setup"
echo "========================================"
echo ""
# Config-Verzeichnis
CONFIG_DIR="$HOME/Library/Application Support/Element"
CONFIG_FILE="$CONFIG_DIR/config.json"
# Erstelle Verzeichnis falls nicht vorhanden
if [ ! -d "$CONFIG_DIR" ]; then
echo "Erstelle Element Verzeichnis..."
mkdir -p "$CONFIG_DIR"
fi
# Erstelle config.json
echo "Erstelle config.json..."
cat > "$CONFIG_FILE" << 'EOF'
{
"configUrl": "https://axion1337.chat/config.json",
"brand": "aXion1337.Chat",
"default_theme": "aXion1337 Dark",
"show_labs_settings": true,
"features": {
"feature_qr_code_login": true
},
"setting_defaults": {
"custom_themes": []
}
}
EOF
echo "Config erstellt: $CONFIG_FILE"
echo ""
# Versuche Element zu installieren/starten
echo "Überprüfe Element Desktop Installation..."
# Methode 1: Über Homebrew (falls installiert)
if command -v brew &> /dev/null; then
if brew list element &> /dev/null; then
echo "Element über Homebrew gefunden. Starte Element..."
open -a Element
sleep 2
exit 0
fi
fi
# Methode 2: Direkte App im Applications Folder
if [ -d "/Applications/Element.app" ]; then
echo "Element im Applications Folder gefunden. Starte Element..."
open -a Element
sleep 2
exit 0
fi
# Methode 3: Homebrew Installation
if command -v brew &> /dev/null; then
echo ""
echo "Installiere Element über Homebrew..."
brew install element --cask
echo "Starte Element..."
sleep 2
open -a Element
exit 0
else
echo ""
echo "⚠️ Homebrew nicht gefunden. Bitte installiere zuerst:"
echo "https://brew.sh"
echo ""
echo "Oder installiere Element manuell:"
echo "https://element.io/download"
echo ""
echo "✅ Deine config.json wurde erstellt unter:"
echo "$CONFIG_FILE"
echo ""
fi
# Warte auf Benutzer-Input vor dem Schließen
read -p "Drücke Enter zum Beenden..."
+89
View File
@@ -0,0 +1,89 @@
@echo off
REM Element Desktop Setup Script for Windows
REM Doppelklick zum Ausführen
setlocal enabledelayedexpansion
echo ========================================
echo Element Desktop Konfiguration Setup
echo ========================================
echo.
REM Config-Verzeichnis
set APPDATA_PATH=%APPDATA%\Element
set CONFIG_FILE=%APPDATA_PATH%\config.json
REM Erstelle Verzeichnis falls nicht vorhanden
if not exist "%APPDATA_PATH%" (
echo Erstelle Element Verzeichnis...
mkdir "%APPDATA_PATH%"
)
REM Erstelle config.json
echo Erstelle config.json...
(
echo {
echo "configUrl": "https://axion1337.chat/config.json",
echo "brand": "aXion1337.Chat",
echo "default_theme": "aXion1337 Dark",
echo "show_labs_settings": true,
echo "features": {
echo "feature_qr_code_login": true
echo },
echo "setting_defaults": {
echo "custom_themes": []
echo }
echo }
) > "%CONFIG_FILE%"
echo Config erstellt: %CONFIG_FILE%
echo.
REM Versuche Element zu installieren/starten
echo Überprüfe Element Desktop Installation...
REM Methode 1: Über Microsoft Store (wenn verfügbar)
where element >nul 2>nul
if %ERRORLEVEL% == 0 (
echo Starte Element Desktop...
start element
timeout /t 2 >nul
echo Done!
pause
exit /b 0
)
REM Methode 2: WinGet (Falls installiert)
winget list --name "Element" >nul 2>nul
if %ERRORLEVEL% == 0 (
echo WinGet gefunden. Installiere Element...
winget install Element.Element --silent
echo Starte Element...
timeout /t 3 >nul
start element
pause
exit /b 0
)
REM Methode 3: Chocolatey (Falls installiert)
choco list element >nul 2>nul
if %ERRORLEVEL% == 0 (
echo Chocolatey gefunden. Installiere Element...
choco install element -y
echo Starte Element...
timeout /t 3 >nul
start element
pause
exit /b 0
)
REM Fallback: Manueller Download
echo.
echo Element Desktop konnte nicht automatisch installiert werden.
echo Bitte installiere Element Desktop manuell:
echo https://element.io/download
echo.
echo Deine config.json wurde erstellt unter:
echo %CONFIG_FILE%
echo.
pause
@@ -0,0 +1,256 @@
# 🆕 Authentik: Neuen Invitation Flow erstellen
**Problem**:
- Nur ein `matrix-enrollment` Flow existiert
- Wird für Standard-Signup + Invitations verwendet → Konflikt
- Fehler: "Found existing plan for other flow, deleting plan"
**Lösung**: Separaten `matrix-invitation` Flow für Einladungslinks erstellen.
---
## Schritt 1: Authentik Admin UI öffnen
```bash
kubectl port-forward -n authentik svc/authentik 9000:9000
# Browser: http://localhost:9000/
# Admin: akadmin / (password)
```
---
## Schritt 2: Neuen Flow erstellen
**Navigation**: Admin → Flows & Stages → Flows
1. Klick **"Create"** (oben rechts)
2. Fülle folgendes aus:
```
Name: matrix-invitation
Slug: matrix-invitation
Title: Matrix Enrollment via Invitation
Description: Enrollment flow for users created via invitation links
Designation: enrollment
```
3. **Speichern** (Save)
---
## Schritt 3: Stages zur Invitation Flow hinzufügen
Nach dem Erstellen wirst du auf die Flow-Edit-Seite weitergeleitet.
**Navigation**: Admin → Flows & Stages → Flows → `matrix-invitation` → Edit
Klick auf "Add Stage" und folge dieser Reihenfolge:
### Stage 1: Invite Stage (Invitation verarbeiten)
1. Klick **"Add Stage"**
2. Wähle: **"Invite Stage"**
3. Konfiguriere:
```
Name: Invite
Order: 1
```
4. **Save**
Dann musst du das Binding setzen:
- Klick auf die Stage in der Flow
- Binding: **"Invite"** (oder "Invitation")
- Required: **Yes**
- **Save**
### Stage 2: Identification Stage (Username überprüfen)
1. Klick **"Add Stage"**
2. Wähle: **"Identification Stage"** (nicht "Authenticate Stage")
3. Konfiguriere:
```
Name: Identification
Order: 2
User Fields: username (oder email)
Create Users as Inactive: NO
```
4. **Save**
Binding setzen:
- Binding: **"Identify"**
- Required: **No**
- **Save**
### Stage 3: Prompt Stage (Daten abfragen: Username, Email, Name)
1. Klick **"Add Stage"**
2. Wähle: **"Prompt Stage"**
3. Konfiguriere:
```
Name: User Data
Order: 3
```
4. **Speichern (Save)**
Dann **Fields hinzufügen**:
- Klick auf die Stage
- Klick **"Add Field"** für jedes Feld:
#### Field 1: Username
```
Field Name: username
Label: Username
Type: text
Required: Yes
Placeholder: Choose a username
```
#### Field 2: Email
```
Field Name: email
Label: Email Address
Type: email
Required: Yes
Placeholder: your@email.com
```
#### Field 3: Name (Optional)
```
Field Name: name
Label: Full Name
Type: text
Required: No
Placeholder: Your Name
```
Alle Fields **Save**.
Dann **Stage-Binding setzen**:
- Binding: **"Prompt for data"** (oder "User Data")
- Required: **Yes**
- **Save**
### Stage 4: Write Stage (User in DB erstellen)
1. Klick **"Add Stage"**
2. Wähle: **"Write Stage"** (oder "User Write Stage")
3. Konfiguriere:
```
Name: Create User
Order: 4
```
4. **Speichern (Save)**
Dann **Field Bindings setzen**:
- Klick auf die Stage
- Unter "Field Bindings" oder "User Creation":
- `username` ← mapped von username Feld
- `email` ← mapped von email Feld
- `name` ← mapped von name Feld
- **Save**
Stage-Binding setzen:
- Binding: **"Create or update user"**
- Required: **Yes**
- **Save**
### Stage 5: Finish Stage (Abschluss)
1. Klick **"Add Stage"**
2. Wähle: **"Finish Stage"** (oder "User Login")
3. Konfiguriere:
```
Name: Finish
Order: 5
```
4. **Speichern (Save)**
Stage-Binding:
- Binding: **"Finish"** (oder "Complete enrollment")
- Required: **Yes**
- **Save**
---
## Schritt 4: Flow als Standard-Invitation setzen
**Navigation**: Admin → System → Settings
Suche nach "Invitation Flow" oder "Default Flows":
1. Setze **"Invitation Flow"** auf `matrix-invitation`
2. **Save**
Alternativ:
- Admin → Flows & Stages → Flows
- Für jede Invitation/Group:
- Klick auf Group/Invitation
- Setze "Enrollment Flow" auf `matrix-invitation`
---
## Schritt 5: Test mit neuem Einladungslink
1. **Neuen Einladungslink erstellen**:
- Admin → Users & Groups → Invitations
- Klick **"Create"**
- Expiry: 7 days
- **Create & Copy Link**
2. **Link öffnen** (neuer Browser/Inkognito):
- Link in Browser öffnen
- Sollte jetzt alle Felder zeigen:
- [ ] Username eingeben
- [ ] Email eingeben ← sollte jetzt da sein!
- [ ] Name eingeben (optional)
- [ ] "Weiter" oder "Sign in with Authentik"
3. **Authentik Login** (falls Binding korrekt):
- Mit Authentik anmelden
- Enrollment abgeschlossen
- User sollte in Synapse erstellt sein
---
## Troubleshooting
### Fehler: "Stage not found"
- Stelle sicher, dass alle Stages ein **Binding** haben
- Alle Bindings müssen **eindeutig** sein (nicht doppelt)
- **Save** nach jeder Änderung
### Felder werden nicht angezeigt
- Prompt Stage überprüfen
- Alle Fields müssen **Save** sein
- Ggfs. Browser-Cache löschen
### Fehler nach Enrollment
- MAS Logs: `kubectl logs -f matrix-stack-matrix-authentication-service-6b994b9fcf-qqcxz -n matrix`
- Authentik Logs: `kubectl logs -f -n authentik -l app.kubernetes.io/name=authentik`
---
## Erwartetes Ergebnis
Nach dem Fix:
1. Einladungslink öffnen → `matrix-invitation` Flow
2. Username, Email, Name eingeben
3. "Mit Authentik anmelden"
4. Nach Login: User in Synapse erstellt
5. Login zu ElementWeb möglich
---
## Checkliste
- [ ] `matrix-invitation` Flow erstellt
- [ ] 5 Stages in korrekter Reihenfolge (Invite → Identify → Prompt → Write → Finish)
- [ ] Prompt Stage hat username, email, name Felder
- [ ] Alle Stages haben korrektes Binding
- [ ] `matrix-invitation` als Standard-Invitation-Flow gesetzt
- [ ] Neuen Einladungslink erstellt und getestet
- [ ] Test-User kann Email eingeben
- [ ] Test-User in Synapse DB nach Login
---
**Sollte ca. 10-15 Minuten dauern!** 🚀
@@ -0,0 +1,314 @@
# ✅ Authentik Enrollment Flow Reparatur-Template
Basierend auf häufigen Problemen: Hier sind die wahrscheinlichsten Fixes.
---
## Problem 1: MAS kennt Authentik-OIDC nicht
### Symptom
- MAS zeigt "Sign in with Authentik" Button nicht
- Logs: "upstream provider not configured"
### Lösung
**MAS Secret muss diesen Block enthalten:**
```yaml
# apps/production/custom-configs/mas-secret.yaml (decrypted)
---
matrixAuthenticationService:
upstream_oauth2_config:
issuer: "https://auth.axion1337.chat/application/o/matrix/"
client_id: "{{ CLIENT_ID_FROM_AUTHENTIK }}"
client_secret: "{{ CLIENT_SECRET_FROM_AUTHENTIK }}"
authorization_endpoint: "https://auth.axion1337.chat/application/o/authorize/"
token_endpoint: "https://auth.axion1337.chat/application/o/token/"
userinfo_endpoint: "https://auth.axion1337.chat/application/o/userinfo/"
scopes:
- "openid"
- "profile"
- "email"
user_mapping_provider:
type: "oidc"
config:
localpart_template: "{{ user.preferred_username }}"
display_name_template: "{{ user.name }}"
email_template: "{{ user.email }}"
# Wichtig: Password-Login deaktivieren (da wir nur OIDC verwenden)
passwords:
enabled: false
```
**Wie ausfüllen:**
1. Authentik Admin UI öffnen: `https://auth.axion1337.chat`
2. Admin → Applications → Providers → "matrix-provider" (oder ähnlich)
3. Folgende Werte kopieren:
- **Client ID**: Im Provider-Details zu sehen
- **Client Secret**: Im Provider-Details zu sehen (unter "Credentials")
4. Local entschlüsseln (mit age-key):
```bash
cd gitops/
sops -d -i apps/production/custom-configs/mas-secret.yaml
```
5. Datei öffnen und die Werte einfügen
6. Wieder verschlüsseln:
```bash
sops -e -i apps/production/custom-configs/mas-secret.yaml
```
7. Commiten:
```bash
git add apps/production/custom-configs/mas-secret.yaml
git commit -m "Fix: Configure MAS upstream OIDC for Authentik"
git push
```
8. Flux triggern:
```bash
flux reconcile kustomization production-apps --with-source
```
9. Warten, dass MAS Pod neu startet:
```bash
kubectl get pods -n matrix -l app=matrix-authentication-service -w
```
---
## Problem 2: Authentik Enrollment Flow ist kaputt
### Symptom
- User kommt zu Authentik-Login
- Nach Login: "Error: Enrollment stage not found" oder ähnlich
- Oder: Flow bricht ohne Fehlermeldung ab
### Lösung
**Authentik UI → Flows & Stages → Enrollment Flow überprüfen:**
```
Flows → Enrollment
├── Stage 1: "Identify" (if not exists)
│ └── Binding: "Identification (if not exists)"
├── Stage 2: "Write" (wichtig!)
│ └── Binding: "Create or update user"
│ └── User Creation Policies: ???
├── Stage 3: (optional) Weitere Datenerfassung
└── Stage 4: "Finish"
```
**Häufiger Fehler**: "Write" Stage ist nicht korrekt mit den benötigten Feldern konfiguriert.
**Fix:**
1. Authentik Admin UI: `https://auth.axion1337.chat`
2. **Flows & Stages** → **Stages**
3. Nach "Write" Stage suchen (Filter: "write")
4. Klick auf "Write Stage"
5. **Field Bindings** überprüfen:
- [ ] `username` ← MUSS mit Authentik Username bindbar sein
- [ ] `email` ← MUSS vorhanden sein
- [ ] `name` ← Optional, aber empfohlen
6. Alle sollten "required" sein (nicht optional)
7. **Save**
Dann zurück zu **Flows****Enrollment**:
1. Stages überprüfen (oben)
2. Bindings überprüfen:
- Each Stage hat "Binding" Feld
- "Write" Stage sollte mit "Create or update user" gebunden sein
3. **Save**
---
## Problem 3: OIDC Token werden nicht korrekt zu Synapse weitergeleitet
### Symptom
- User kommt bis zu ElementWeb
- Nach Login zu Authentik: "User not found in Synapse" oder Loop
- Oder: User wird in Authentik angelegt, aber nicht in Synapse
### Lösung
Das ist komplexer und erfordert MAS-Konfiguration + Synapse-Konfiguration.
**MAS Config (upstream_oauth2_config)** muss korrekt sein (siehe Problem 1).
**Zusätzlich in MAS Secret:**
```yaml
# apps/production/custom-configs/mas-secret.yaml
matrixAuthenticationService:
# ... upstream_oauth2_config ...
# User-Provisioning Konfiguration
access:
# Benutzer aus OIDC Provider automatisch erzeugen
auto_provision: true
# Synapse URL
home_server: "https://matrix.axion1337.chat"
```
---
## Problem 4: ElementWeb zeigt nicht automatisch OIDC-Button
### Symptom
- MAS läuft und hat OIDC konfiguriert
- ElementWeb zeigt nur "Sign in with username" oder "SAML login"
- Kein "Sign in with Authentik" Button
### Lösung
ElementWeb muss die MAS-Konfiguration kennen. Das geschieht über `.well-known/matrix/client`:
**Test:**
```bash
curl https://axion1337.chat/.well-known/matrix/client | jq '.authentication'
```
Sollte zurückgeben:
```json
{
"flows": [
{
"stages": ["m.login.sso"]
}
],
"identity_providers": [
{
"id": "authentik",
"name": "Authentik",
"icon": "...",
"brand": "custom"
}
]
}
```
Falls nicht: **ElementWeb Config in Element-Stack aktualisieren:**
```yaml
# apps/production/custom-configs/element-values.yaml
elementWeb:
config:
auth:
sso_redirect_options:
immediate: false
on_welcome_page: true
```
**Dann:**
```bash
git add apps/production/custom-configs/element-values.yaml
git commit -m "Fix: Enable SSO redirect in ElementWeb"
git push
flux reconcile kustomization production-apps --with-source
```
---
## Problem 5: User kann sich anmelden, aber Boje ist nicht in Matrix
### Symptom
- Authentik-Login funktioniert
- MAS zeigt User erfolgreich
- ElementWeb Login funktioniert
- **ABER**: User ist nicht in Synapse (check mit `kubectl exec -n matrix synapse ...`)
### Lösung
Das bedeutet: User wird nicht automatisch in Synapse provisioniert.
**MAS muss User zu Synapse erstellen:**
In MAS Secret, `access` Sektion:
```yaml
matrixAuthenticationService:
access:
# Synapse erlaubt neue User-Erstellung
homeserver: "https://matrix.axion1337.chat"
# Optional: User automatisch erstellen
registration_enabled: true
```
**Synapse-seitig** muss auch User-Erstellung erlaubt sein:
```yaml
# apps/production/custom-configs/synapse-values.yaml
synapse:
additional:
registration:
config: |
enable_registration: true
enable_registration_without_token: false
# Token wird von MAS bereitgestellt
```
---
## Komplettes Test-Szenario
Nach allen Fixes:
1. **Browser 1**: `https://axion1337.chat` öffnen
2. Auf ElementWeb "Sign in" klicken
3. "Sign in with Authentik" klicken (sollte sichtbar sein)
4. Authentik-Login durchführen (akadmin)
5. Nach Login: Enrollment-Flow (nur falls neuer User)
6. Zurück zu ElementWeb
7. **Synapse prüfen**:
```bash
kubectl exec -it -n matrix deployment/synapse -- \
/usr/local/bin/psql -U synapse -d synapse -c "SELECT name, admin FROM users LIMIT 10;"
```
8. Neuer User sollte auftauchen
---
## Debugging-Commands (während Test)
```bash
# Live MAS logs (folgen)
kubectl logs -n matrix -l app=matrix-authentication-service -f
# Authentik logs
kubectl logs -n authentik -l app.kubernetes.io/name=authentik -f
# Port-Forward für manuelles Testen
kubectl port-forward -n matrix svc/matrix-authentication-service 8765:8080
# MAS Secret auslesen (im Cluster)
kubectl get secret ess-mas-values-secret -n matrix -o jsonpath='{.data.values\.yaml}' | base64 -d
# Synapse User-Liste
kubectl exec -it -n matrix deployment/synapse -- \
/usr/local/bin/psql -U synapse -d synapse -c "SELECT name, admin, is_guest FROM users;"
```
---
## Checkliste für erfolgreichen Fix
- [ ] MAS Secret decrypted, upstream_oauth2_config eingetragen
- [ ] Client ID + Secret von Authentik kopiert
- [ ] MAS Secret re-encrypted und commited
- [ ] Authentik Enrollment Flow überprüft
- [ ] OIDC Provider in Authentik aktiv
- [ ] OIDC Application "matrix" in Authentik existiert
- [ ] MAS Pod neu gestartet (nach Secret-Change)
- [ ] ElementWeb zeigt OIDC-Button
- [ ] Test-Login durchgeführt
- [ ] Neuer User in Synapse DB vorhanden
---
**Fragen?** → Siehe `DIAGNOSTIK-AUTHENTIK-FLOW.md` für tiefergehende Diagnose.
@@ -0,0 +1,244 @@
# 🔧 Authentik Invitation Flow Fix Für Einladungslinks
**Problem**:
- Standard Enrollment (akadmin): ✅ funktioniert
- Invitation Flow (Boje über Einladungslink): ❌ Nur Username gefragt, keine Email
- Nach Enrollment: "Fehler fehlende Rechte"
**Root Cause**: Invitation Flow erfasst nicht alle erforderlichen Felder (Email) für OIDC-Token-Generation.
---
## Phase 1: Diagnose im Authentik Admin UI
```bash
# Authentik Admin UI öffnen
kubectl port-forward -n authentik svc/authentik 9000:9000
# Browser: http://localhost:9000/
# Admin credentials: akadmin / (password)
```
### 1.1 Überprüfe: Welche Flows existieren?
**Navigation**: Admin → Flows & Stages → Flows
Suche nach diesen Flows:
- [ ] `enrollment` Standard Enrollment (für akadmin)
- [ ] `invitation` Invitation Flow (für Einladungslinks)
- [ ] `default-authentication-flow` Standard Login
### 1.2 Überprüfe: Standard Enrollment Flow (funktioniert)
**Navigation**: Flows → `enrollment` öffnen
**Stages sollten sein:**
```
1. Identify (if not exists)
└─ Binding: "Identification (if not exists)"
2. Write
└─ Binding: "Create or update user"
└─ Field bindings MUST include:
├─ username
├─ email ← WICHTIG
└─ name (optional)
3. (optional) Weitere Stages
4. Finish
```
**Wichtig**: Alle Felder müssen "required" sein (nicht optional).
### 1.3 Überprüfe: Invitation Flow (wahrscheinlich kaputt)
**Navigation**: Flows → `invitation` öffnen
**Problem**: Wahrscheinlich fehlt die "Email" Stage hier!
**Sollte sein:**
```
1. Invite Stage
└─ Binding: "Invite user"
2. Identification (if not exists)
└─ Binding: "Identify"
3. Prompt Stage (für zusätzliche Daten!)
└─ Binding: "Prompt for data"
└─ Fields: username, email, name, password
4. Write
└─ Binding: "Create or update user"
5. Finish
```
---
## Phase 2: Reparatur der Invitation Flow
### Schritt 1: Neue "Prompt Stage" erstellen (falls nicht existiert)
**Navigation**: Admin → Flows & Stages → Stages
1. Klick "Create"
2. Name: `invitation-prompt` oder ähnlich
3. Type: **"Prompt Stage"**
4. Configure:
- [ ] **Fields to Prompt**:
- Username (required)
- Email (required) ← WICHTIG
- Name (optional)
- Password (optional, da OIDC)
5. Save
### Schritt 2: Invitation Flow reparieren
**Navigation**: Admin → Flows & Stages → Flows → `invitation`
**Stages in dieser Reihenfolge:**
```
Stage 1: Invite Stage
├─ Binding: "Invite"
├─ Required: Yes
Stage 2: Identification Stage
├─ Binding: "Identify" (oder "Identification (if not exists)")
├─ Required: No
Stage 3: [NEUE STAGE] Prompt für Email/Username
├─ Type: "Prompt Stage"
├─ Binding: "Prompt for data"
├─ Fields:
│ ├─ username (required)
│ ├─ email (required) ← ENTSCHEIDEND
│ └─ name (optional)
├─ Required: Yes
Stage 4: Write
├─ Binding: "Create or update user"
├─ User Creation Policies: (standard)
├─ Required: Yes
Stage 5: Finish
├─ Binding: "Finish"
├─ Required: Yes
```
**Speichern** und Testen!
---
## Phase 3: Teste Invitation Flow
### Test 1: Neuen Einladungslink erstellen
**Navigation**: Admin → Users & Groups → Invitations
1. Klick "Create"
2. Expiry: 7 days
3. Create & Copy Link
### Test 2: Einladungslink öffnen (in neuem Browser/Inkognito)
1. Link öffnen
2. Enrollment Flow sollte jetzt:
- [ ] Username eingeben
- [ ] **Email eingeben** ← Das sollte jetzt da sein!
- [ ] Name eingeben (optional)
- [ ] "Sign in with Authentik" klicken (falls Authentik-Binding korrekt)
3. Nach Authentik-Login: User in Synapse erstellt?
```bash
kubectl exec -it -n matrix matrix-stack-postgres-0 -- \
psql -U synapse -d synapse -c "SELECT name FROM users WHERE created_ts > now() - interval '5 minutes';"
```
### Test 3: Prüfe MAS Logs auf Fehler
```bash
kubectl logs -f matrix-stack-matrix-authentication-service-6b994b9fcf-qqcxz -n matrix | grep -i "error\|fail\|boje"
```
---
## Phase 4: Häufige Fehler & Lösungen
### Fehler 1: "Fehlende Rechte" nach Enrollment
**Symptom**: Enrollment abgeschlossen, aber Fehler auf Berechtigungsseite
**Ursachen**:
- [ ] Email-Feld wurde nicht erfasst
- [ ] OIDC-Token hat unvollständige Daten
- [ ] Synapse User konnte nicht erstellt werden (Duplikat?)
**Lösung**:
1. Authentik Logs prüfen: `kubectl logs -n authentik -l app.kubernetes.io/name=authentik -f | grep -i "error\|invitation"`
2. MAS Logs prüfen: `kubectl logs -f matrix-stack-matrix-authentication-service-6b994b9fcf-qqcxz -n matrix | grep -i "boje\|error"`
3. Synapse Logs prüfen: `kubectl logs -f -n matrix matrix-stack-synapse-0 | grep -i "boje\|register"`
### Fehler 2: "Stage not found" oder "Flow invalid"
**Ursache**: Invitation Flow hat Binding-Fehler
**Lösung**:
1. Admin UI → Flows → Invitation Flow öffnen
2. Alle Stages überprüfen, dass sie korrekt gebunden sind
3. Keine leeren/ungültigen Bindings
4. Save & Retry
### Fehler 3: Email-Feld wird nicht angezeigt
**Ursache**: Prompt Stage hat email nicht in Fields
**Lösung**:
1. Admin UI → Flows → Stages → Prompt Stage öffnen
2. Edit → Fields überprüfen
3. Email hinzufügen if missing:
- Field name: `email`
- Type: `email`
- Required: Yes
4. Save
---
## Erwarteter Ablauf nach Fix
1. Browser öffnet Einladungslink → Enrollment Flow
2. "Username eingeben" → z.B. "boje"
3. **"Email eingeben"** ← Sollte jetzt da sein
4. "Name eingeben" (optional)
5. "Weiter" oder "Mit Authentik anmelden"
6. Authentik Login
7. Enrollment abgeschlossen
8. User "boje" in Synapse DB angelegt
9. Login zu ElementWeb möglich
---
## Checkliste
- [ ] Authentik Admin UI geöffnet (port-forward 9000)
- [ ] Standard Enrollment Flow überprüft (funktioniert mit akadmin)
- [ ] Invitation Flow überprüft
- [ ] Prompt Stage existiert mit email field
- [ ] Invitation Flow hat alle 5 Stages in korrekter Reihenfolge
- [ ] Neuen Einladungslink erstellt und getestet
- [ ] Test-User hat Email eingeben können
- [ ] Test-User in Synapse DB nach Login
- [ ] MAS Logs zeigen keine Fehler
---
**Frage**: Stimmt das mit deiner Beobachtung überein - dass bei der Einladung **nur Username** gefragt wurde, aber **nicht die Email**?
Wenn ja, dann ist der Fix:
1. Prompt Stage erstellen/reparieren (mit email field)
2. Zur Invitation Flow hinzufügen
3. Testen
Soll ich dir noch mehr Detailschritte geben?
@@ -0,0 +1,261 @@
# 🔍 Authentik Enrollment Flow Diagnostik & Reparaturplan
**Symptom**:
- `akadmin` wurde manuell in Matrix-DB angelegt (funktioniert)
- `Boje` wurde nur in Authentik erstellt, nicht in Matrix-DB (kaputt)
- Beide sollen denselben Enrollment Flow verwenden
**Vermutung**: Die Authentik → MAS → Synapse Kette ist unterbrochen
---
## Phase 1: Diagnose (Bestandsaufnahme)
### 1.1 Authentik Logs prüfen
```bash
# Authentik Server logs
kubectl logs -n authentik -l app.kubernetes.io/name=authentik -f | grep -i "oauth\|oidc\|enroll\|flow"
# Worker logs
kubectl logs -n authentik -l app.kubernetes.io/component=worker -f | grep -i "enroll"
```
**Worauf achten**:
- Fehler bei "Enrollment create"?
- OIDC Token-Probleme?
- Flow-Validierungsfehler?
### 1.2 MAS (Matrix Authentication Service) Logs prüfen
```bash
# MAS logs
kubectl logs -n matrix -l app=matrix-authentication-service -f | grep -i "oauth\|upstream\|user\|oidc"
```
**Worauf achten**:
- Verbindung zu Authentik erfolgreich?
- Token-Validierung fehlgeschlagen?
- User-Provisioning-Fehler?
### 1.3 Synapse Logs prüfen
```bash
# Synapse logs (für User-Erstellung)
kubectl logs -n matrix -l app=synapse -f | grep -i "user\|provision\|register\|auth"
```
**Worauf achten**:
- User-Registrierungs-Fehler?
- Provisioning-Fehler?
- Authentifizierungsprobleme?
---
## Phase 2: Authentik UI Überprüfung
### 2.1 Enrollment Flow inspizieren
```bash
# Authentik UI öffnen
kubectl port-forward -n authentik svc/authentik 9000:9000
# → Browser: http://localhost:9000
# → Admin UI → Flows & Stages → "Enrollment" Flow suchen
```
**Checklist**:
- [ ] Flow existiert und heißt "Enrollment"
- [ ] Reihenfolge der Stages:
1. Identify (optional)
2. Write (User-Erstellung)
3. Enrollment (if-condition für neuen User)
4. Verification (optional)
- [ ] "Write Stage" bindet sich an:
- [ ] Username
- [ ] Email
- [ ] Name
- [ ] Alle Bindings sind "required" (nicht optional)
### 2.2 OIDC Provider in Authentik prüfen
```bash
# Über Authentik UI:
# Admin → Applications → Providers → "matrix-provider" (oder ähnlich)
```
**Checklist**:
- [ ] Provider existiert
- [ ] Name: z.B. "matrix-provider"
- [ ] Client Type: "Confidential"
- [ ] Redirect URIs enthalten:
- [ ] `https://account.axion1337.chat/upstream/callback/*`
- [ ] `https://account.axion1337.chat/upstream/callback/01KQDJTR1ZVTG8JQ220F5BNBFZ` (exact)
- [ ] Scopes: `openid profile email`
- [ ] Client ID + Secret kopiert?
### 2.3 OIDC Application in Authentik
```bash
# Admin → Applications → Applications → "matrix"
```
**Checklist**:
- [ ] Application existiert mit Slug "matrix"
- [ ] Provider ist zugewiesen
- [ ] Enrollment Flow ist zugewiesen (nicht "deny")
- [ ] Enrollment Flow ist die richtige (die von oben)
### 2.4 Test-User "Boje" inspizieren
```bash
# Admin → Directory → Users → "Boje"
```
**Checklist**:
- [ ] Username: `boje`
- [ ] Email: `boje@...` (vorhanden?)
- [ ] Groups: Falls erforderlich, die richtigen Groups zugewiesen?
- [ ] Status: Active oder Disabled?
- [ ] Sessions: Aktive Logins?
---
## Phase 3: MAS Konfiguration überprüfen
### 3.1 Current MAS Secret auslesen
Da der age-key nicht lokal verfügbar ist, müssen wir die Konfiguration im Cluster prüfen:
```bash
# MAS Config im Cluster auslesen (nicht verschlüsselt)
kubectl get secret ess-mas-values-secret -n matrix -o jsonpath='{.data.values\.yaml}' | base64 -d | yq . | head -100
```
**Worauf achten**:
- [ ] `upstream_oauth2_config` Block existiert
- [ ] `upstream_oauth2_config.issuer`: `https://auth.axion1337.chat/application/o/matrix/`
- [ ] `upstream_oauth2_config.client_id`: Authentik Client ID
- [ ] `upstream_oauth2_config.client_secret`: Authentik Client Secret (*)
- [ ] `upstream_oauth2_config.scopes`: `["openid", "profile", "email"]`
- [ ] `upstream_oauth2_config.user_mapping_provider`:
- `type`: "oidc"
- `config.localpart_template`: `{{ user.preferred_username }}`
- `config.display_name_template`: `{{ user.name }}`
- `config.email_template`: `{{ user.email }}`
### 3.2 MAS Pod exec Config live prüfen
```bash
# MAS Config im laufenden Pod inspizieren
kubectl exec -it -n matrix deployment/matrix-authentication-service -- cat /etc/mas/config.yaml | grep -A50 upstream_oauth2_config
```
**Worauf achten**:
- Config ist syntaktisch korrekt (YAML)?
- Indentierung ist richtig?
- Werte sind vorhanden?
### 3.3 MAS OIDC Discovery prüfen
```bash
# Authentik OIDC Discovery Endpoint
curl -s https://auth.axion1337.chat/application/o/matrix/.well-known/openid-configuration | jq .
# Sollte zurückgeben:
# {
# "issuer": "https://auth.axion1337.chat/application/o/matrix/",
# "token_endpoint": "https://auth.axion1337.chat/application/o/token/",
# "authorization_endpoint": "...",
# ...
# }
```
---
## Phase 4: Login-Flow Testen
### 4.1 MAS Login UI öffnen
```bash
# Port-Forward zu MAS
kubectl port-forward -n matrix svc/matrix-authentication-service 8765:8080
# → Browser: http://localhost:8765
```
**Test**:
1. Auf MAS-Seite: "Sign in with Authentik" klicken
2. Authentik-Login durchführen
3. Auf Enrollment Flow warten
4. Neuen User erstellen (Test-Username, Email, Password)
5. Nach erfolgreicher Registrierung: Matrix home_server erhalten?
### 4.2 Fehlerberichte
Falls Fehler auftritt:
- [ ] Screenshot des Fehlers
- [ ] MAS logs auslesen: `kubectl logs -n matrix -l app=matrix-authentication-service --tail=50`
- [ ] Authentik logs auslesen: `kubectl logs -n authentik -l app.kubernetes.io/name=authentik --tail=50`
---
## Phase 5: Reparaturschritte (nachdem Diagnose klar ist)
### Falls MAS-Config fehlerhaft:
```bash
# 1. Secrets entschlüsseln (lokale Umgebung mit age-key erforderlich)
sops -d apps/production/custom-configs/mas-secret.yaml > /tmp/mas-secret-decrypted.yaml
# 2. Editor öffnen und Konfiguration reparieren
vim /tmp/mas-secret-decrypted.yaml
# → upstream_oauth2_config überprüfen und korrigieren
# 3. Wieder verschlüsseln
sops -e /tmp/mas-secret-decrypted.yaml > apps/production/custom-configs/mas-secret.yaml
# 4. Commiten
git add apps/production/custom-configs/mas-secret.yaml
git commit -m "Fix: Correct MAS upstream_oauth2_config for Authentik integration"
# 5. Flux triggern
flux reconcile kustomization production-apps --with-source
```
### Falls Authentik Enrollment Flow fehlerhaft:
1. Admin UI öffnen: `kubectl port-forward -n authentik svc/authentik 9000:9000`
2. Flows → Enrollment Flow öffnen
3. Stages überprüfen und in richtige Reihenfolge bringen:
- **Identify**: Benutzer identifizieren
- **Write**: Benutzer in DB speichern
- **Enrollment**: Weitere Felder (optional)
- **Finish**: Abschluss
4. Speichern
5. Neuen Test-User erstellen und Enrollment durchlaufen
---
## Erwartete Endergebnisse
Nach erfolgreichem Fix:
1. Benutzer klickt "Sign in with Authentik" auf MAS
2. Authentik-Login-Seite wird angezeigt
3. Nach Login: Enrollment Flow wird angezeigt
4. User füllt Formular aus
5. Nach "Finish": Authentik erstellt User UND verbindet zu Matrix
6. User wird in Matrix-DB angelegt (`_matrix_auth` prefix)
7. User kan sich bei ElementWeb anmelden
---
## 🎯 Nächster Schritt
Bitte folgende Diagnostik durchlaufen und mir die Output berichte:
1. **MAS Logs** (letzten 30 Zeilen)
2. **Authentik Logs** (letzten 30 Zeilen)
3. **MAS Secret (entschlüsselt)** upstream_oauth2_config Block
4. **Authentik OIDC Discovery** Output
5. **Screenshots** der Authentik UI (Enrollment Flow, OIDC Provider, Application)
Damit kann ich dann genau sehen, wo der Bruch in der Kette ist! 🔗
+112
View File
@@ -0,0 +1,112 @@
# 🔧 Troubleshooting Guides
Dieser Ordner enthält detaillierte Troubleshooting- und Reparaturanleitungen für häufige Probleme bei der Authentik/MAS/Matrix Integration.
---
## 📖 Guides
### 1. **DIAGNOSTIK-AUTHENTIK-FLOW.md**
**Für**: Vollständige Diagnose des Authentik Enrollment Flows
**Wann**: Wenn Sie systematisch überprüfen möchten, ob die gesamte OIDC-Kette (Authentik → MAS → Synapse) funktioniert
**Umfasst**:
- Authentik Logs analysieren
- MAS Konfiguration überprüfen
- OIDC Discovery testen
- Enrollment Flow inspizieren
- Fehlersuche mit Debugging-Commands
**Status**: Nutzer Boje - Nur in Authentik erstellt, nicht in Synapse
---
### 2. **AUTHENTIK-FIX-TEMPLATE.md**
**Für**: Konkrete Reparaturen bei MAS/Authentik Integration
**Wann**: Wenn Sie wissen, welches Problem Sie haben und schnelle Lösungen suchen
**Behandelt**:
- Problem 1: MAS kennt Authentik-OIDC nicht
- Problem 2: Authentik Enrollment Flow kaputt
- Problem 3: OIDC Token werden nicht weitergeleitet
- Problem 4: ElementWeb zeigt keinen OIDC-Button
- Problem 5: User in Authentik aber nicht in Synapse
**Status**: Best Practices für häufige Probleme
---
### 3. **AUTHENTIK-INVITATION-FLOW-FIX.md**
**Für**: Reparatur des Invitation Flows bei Einladungslinks
**Wann**: Wenn Nutzer via Einladungslink nicht korrekt erstellt werden
**Problem**: Invitation Flow erfasst nur Username, nicht Email/Name
**Lösung**:
- Prompt Stage mit Email-Feld erstellen/reparieren
- Zur Invitation Flow hinzufügen
- Testen mit neuem Einladungslink
**Status**: Nutzer Klaus - Fehler "kein ausstehender benutzer Anfrage wurde verweigert"
---
### 4. **AUTHENTIK-CREATE-INVITATION-FLOW.md** ⭐ **WICHTIGSTE ANLEITUNG**
**Für**: Neuen separaten Invitation Flow erstellen
**Wann**: Wenn nur ein `matrix-enrollment` Flow existiert (Standard + Invitations gemeinsam)
**Root Cause**: Flow-Konflikt durch gemeinsamen Flow
**Lösung** (Schritt-für-Schritt):
1. Neuen Flow `matrix-invitation` erstellen
2. 5 Stages konfigurieren (Invite → Identify → Prompt → Write → Finish)
3. Email-Feld in Prompt Stage hinzufügen
4. Invitations auf neuen Flow setzen
5. Testen
**Zeitaufwand**: ~20 Minuten
**Status**: Aktuell für Klaus/Boje notwendig
---
## 🎯 Schneller Einstieg
### Szenario 1: "Enrollment funktioniert nicht, ich weiß nicht warum"
**Start**: `DIAGNOSTIK-AUTHENTIK-FLOW.md`
### Szenario 2: "Einladungslink funktioniert nicht"
**Start**: `AUTHENTIK-CREATE-INVITATION-FLOW.md` (wenn nur ein Flow existiert)
→ oder `AUTHENTIK-INVITATION-FLOW-FIX.md` (wenn zwei Flows existieren)
### Szenario 3: "Ich kenne das Problem und brauche Lösungen"
**Start**: `AUTHENTIK-FIX-TEMPLATE.md`
### Szenario 4: "Alles ist kaputt, ich brauche alles Schritt-für-Schritt"
**Start**: `AUTHENTIK-CREATE-INVITATION-FLOW.md``AUTHENTIK-FIX-TEMPLATE.md``DIAGNOSTIK-AUTHENTIK-FLOW.md`
---
## 📋 Bekannte Probleme
| Problem | Nutzer | Guide | Status |
|---------|--------|-------|--------|
| Nur Standard Enrollment funktioniert | akadmin ✅ | - | Resolved |
| User nur in Authentik, nicht in Synapse | Boje | `DIAGNOSTIK-AUTHENTIK-FLOW.md` | In Progress |
| Einladungslink-Fehler: "kein ausstehender benutzer" | Klaus | `AUTHENTIK-CREATE-INVITATION-FLOW.md` | **Fixed (2026-07-27)**`matrix-invitation` Flow hatte nur Invite+Prompt Stage-Bindings, beide auf `order=0`. Write/Password/Login-Stages fehlten komplett. Live gefixt + als Blueprint (`apps/authentik/authentik-blueprints.yaml`) reproduzierbar gemacht. |
| OIDC-Integration unklar | General | `AUTHENTIK-FIX-TEMPLATE.md` | Reference |
---
## 🔗 Verwandte Dokumentation
- `../README.md` Hauptdokumentation & Architektur
- `../TASKS.md` Aufgabenliste & Meilensteine
- `../deployment-guides/` Deployment-Anleitungen für andere Components
---
## 💡 Tipps
- **Immer Logs überprüfen** bevor man herumrät: `kubectl logs -f -n <namespace> <pod>`
- **Browser-Cache löschen** nach Authentik-Änderungen
- **Port-Forward nutzen** für lokales Testen: `kubectl port-forward -n authentik svc/authentik 9000:9000`
- **Kleine Tests machen** (einen User mit Invitation testen, nicht 10 auf einmal)
---
**Zuletzt aktualisiert**: 2026-05-18
**Verfasser**: Claude Code + Thore
+41
View File
@@ -0,0 +1,41 @@
#!/usr/bin/env bash
# GitOps ConfigMap Checksum Hook
# Automatically updates checksum annotations in kustomization.yaml when ConfigMaps change.
# This ensures Flux CD re-deploys the HelmRelease when external ConfigMap sources are modified.
#
# See: docs/ops-configmap-sync.md
set -euo pipefail
REPO_ROOT="$(git rev-parse --show-toplevel)"
ELEMENT_VALUES="$REPO_ROOT/apps/production/custom-configs/element-values.yaml"
SYNAPSE_VALUES="$REPO_ROOT/apps/production/custom-configs/synapse-values.yaml"
KUSTOMIZATION="$REPO_ROOT/apps/production/kustomization.yaml"
# Function to calculate MD5 hash (handles both GNU md5sum and BSD md5)
get_md5() {
local file="$1"
if command -v md5sum &> /dev/null; then
md5sum "$file" | awk '{print $1}'
elif command -v md5 &> /dev/null; then
md5 -q "$file"
else
echo "ERROR: Neither md5sum nor md5 found" >&2
exit 1
fi
}
# Update checksums for ConfigMaps that exist and are staged
if git diff --cached --name-only | grep -q "element-values.yaml"; then
ELEMENT_HASH=$(get_md5 "$ELEMENT_VALUES")
sed -i.bak "s/value: \"[0-9a-f]\{32\}\" *# element-config/value: \"$ELEMENT_HASH\" # element-config/" "$KUSTOMIZATION"
rm -f "$KUSTOMIZATION.bak"
git add "$KUSTOMIZATION"
fi
if git diff --cached --name-only | grep -q "synapse-values.yaml"; then
SYNAPSE_HASH=$(get_md5 "$SYNAPSE_VALUES")
sed -i.bak "s/value: \"[0-9a-f]\{32\}\" *# synapse-config/value: \"$SYNAPSE_HASH\" # synapse-config/" "$KUSTOMIZATION"
rm -f "$KUSTOMIZATION.bak"
git add "$KUSTOMIZATION"
fi
+27
View File
@@ -0,0 +1,27 @@
#!/usr/bin/env bash
# Install Git Hooks for GitOps automation
# Must be run after cloning the repository
REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null)" || {
echo "❌ Not in a git repository"
exit 1
}
HOOKS_DIR="$REPO_ROOT/.git/hooks"
HOOK_SOURCE="$REPO_ROOT/scripts/hooks/pre-commit"
HOOK_DEST="$HOOKS_DIR/pre-commit"
# Verify source exists
if [ ! -f "$HOOK_SOURCE" ]; then
echo "❌ Hook source not found: $HOOK_SOURCE"
exit 1
fi
# Create symlink (force if exists)
mkdir -p "$HOOKS_DIR"
ln -sf "../../scripts/hooks/pre-commit" "$HOOK_DEST"
chmod +x "$HOOK_SOURCE"
echo "✅ Git hooks installed:"
echo " • pre-commit: ConfigMap checksum auto-update"
echo " See: docs/ops-configmap-sync.md"