Compare commits

..
139 Commits
Author SHA1 Message Date
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
Scrublord MacBad e663dcc0ec fix duplicated mapping ind kustomization file 2026-04-22 16:13:48 +02:00
Scrublord MacBad 2bc536d9d8 fix config security 2026-04-22 15:50:52 +02:00
Scrublord MacBad bf9c510d76 feat: MSC3889/MSC4880 2026-04-22 15:27:10 +02:00
Scrublord MacBad 90b41b37b1 fix mrtc 2026-04-22 15:22:48 +02:00
Scrublord MacBad 9b5c77bb8e fix secret 2026-04-22 15:15:28 +02:00
Scrublord MacBad 4a5f62d17a fix ingress 2026-04-22 15:11:58 +02:00
Scrublord MacBad 245cfcc486 fix valuemapping 2026-04-22 15:04:23 +02:00
Scrublord MacBad 37327eebe2 fix: use correct MAS SMTP config per documentation 2026-04-22 13:55:19 +02:00
Scrublord MacBad b7356b8a2b fix: use IONOS recommended port 587 instead of 465 2026-04-22 13:48:15 +02:00
Scrublord MacBad 6847c52309 fix: add mode field to MAS email configuration 2026-04-22 13:43:43 +02:00
Scrublord MacBad 47768c6761 revert: restore original Element Web config structure 2026-04-22 13:36:27 +02:00
Scrublord MacBad 361f02eddb fix: replace SOPS secret with plain secret for MAS configuration 2026-04-22 13:23:29 +02:00
Scrublord MacBad 9e23202971 fix: remove SOPS secret from valuesFrom, use only ConfigMap 2026-04-22 13:20:27 +02:00
Scrublord MacBad 584adf59d0 fix: reorder kustomization resources to ensure mas-values overrides mas-secrets 2026-04-22 12:45:31 +02:00
Scrublord MacBad dc0652ffe7 fix: add mas-values ConfigMap for email config with transport field 2026-04-22 12:42:16 +02:00
Scrublord MacBad 18d23441be fix: add transport field to MAS email config and update Element Web theme structure 2026-04-22 12:42:16 +02:00
sorb d7c996b0c9 Update docs/install.md 2026-04-22 10:00:16 +00:00
sorb 00c2c7b114 Add docs/install.md 2026-04-22 10:00:01 +00:00
Scrublord MacBad 59eb11d029 fix element values 2026-04-22 01:20:01 +02:00
Scrublord MacBad eb82a385a4 fix synapse values 2026-04-22 00:43:06 +02:00
Scrublord MacBad 189c5a8d2e fix synapse values 2026-04-22 00:35:24 +02:00
Scrublord MacBad 13bb4814b8 fix filename mas 2026-04-22 00:26:11 +02:00
Scrublord MacBad dd9fdfdca7 feat matrix_rtc 2026-04-22 00:19:48 +02:00
sorb c468ba67e9 Update README.md 2026-04-21 21:44:22 +00:00
sorb c376daf1e3 Add README.md 2026-04-21 21:43:52 +00:00
Scrublord MacBad b83fce9035 feat: add encrypted SMTP secrets for MAS 2026-04-21 23:32:23 +02:00
Scrublord MacBad 0e6bf71db9 feat: add encrypted SMTP secrets for MAS 2026-04-21 23:31:54 +02:00
Scrublord MacBad 68aec4fc69 fix: disable wellKnownDelegation to resolve ACME cert conflict on axion1337.chat 2026-04-21 23:00:48 +02:00
Scrublord MacBad 2f4e4c9b26 fix: use chart's native certManager configuration instead of manual patches 2026-04-21 22:55:38 +02:00
Scrublord MacBad 007c6e76d0 fix: use JSON6902 patches to inject secretName into ingress TLS 2026-04-21 22:48:28 +02:00
Scrublord MacBad fb471fad46 fix: add tlsSecretName to all ingress configurations 2026-04-21 22:46:59 +02:00
Scrublord MacBad 6fe98f4e87 fix: add TLS config to ingress via kustomize patch 2026-04-21 22:40:14 +02:00
Scrublord MacBad 49eb04022c fix: add explicit Certificate resources for cert-manager 2026-04-21 22:35:48 +02:00
Scrublord MacBad acd1d9dcdf fix: remove unsupported tls property from per-service ingress config 2026-04-21 22:29:38 +02:00
Scrublord MacBad a21afd98b4 fix: add per-service TLS and cert-manager annotations 2026-04-21 22:24:34 +02:00
Scrublord MacBad 16fe49a53e fix ki slop 2026-04-21 21:28:49 +02:00
Scrublord MacBad 760f8830a8 fix: resolve schema violations for postgres and well-known 2026-04-21 20:33:02 +02:00
Scrublord MacBad 1dab3790af fix chart compliancy 2026-04-21 20:22:23 +02:00
Scrublord MacBad a1160d8229 fix: structure values according to ESS component schem 2026-04-21 20:14:56 +02:00
Scrublord MacBad 12bb7e0220 fix: schema alignment with snake_case 2026-04-21 20:10:28 +02:00
Scrublord MacBad 9f30fdcfa0 fix: align values with matrix-stack schema 26.4.0 2026-04-21 20:05:58 +02:00
Scrublord MacBad 8fc3be6319 fix: use official OCI chart matrix-stack from ghcr.io 2026-04-21 20:02:32 +02:00
Scrublord MacBad 1bbcaf1f22 fix: correct chart path and values formatting 2026-04-21 18:04:17 +02:00
Scrublord MacBad 640a4b9e56 feat ESS config 2026-04-21 17:54:26 +02:00
Scrublord MacBad c62db8e017 feat: switch to community helm charts from git source 2026-04-21 17:49:00 +02:00
Scrublord MacBad f973b0679f fix: downgrade HelmRepository to v1 to match cluster CRDs 2026-04-21 17:30:52 +02:00
Scrublord MacBad 268a9378c1 fix: definitive switch to OCI for element chart 2026-04-21 17:11:03 +02:00
Scrublord MacBad 36bea343e1 fix 2026-04-21 17:05:21 +02:00
Scrublord MacBad 309e1fe753 chore: switch to OCI registry for element charts 2026-04-21 16:57:18 +02:00
Scrublord MacBad 097fa09273 fix: move matrix namespace to infra and fix repo URL 2026-04-21 16:44:35 +02:00
Scrublord MacBad fe65e915f4 move namespace.yaml 2026-04-21 16:41:43 +02:00
Scrublord MacBad 957e1d94fe fix namespace.yaml 2026-04-21 16:37:05 +02:00
Scrublord MacBad 58c7476b3d create namespace.yaml 2026-04-21 16:35:32 +02:00
Scrublord MacBad b648ab9203 fix ess helm source 2026-04-21 16:27:13 +02:00
Scrublord MacBad f5ada2a28b refactor: move ESS operator to infra to fix CRD dependency 2026-04-21 16:14:40 +02:00
Scrublord MacBad ff5d34350b feat: deploy element server suite operator and instance 2026-04-21 16:00:51 +02:00
Scrublord MacBad a811b20557 feat: add cert-manager namespace 2026-04-21 15:56:53 +02:00
Scrublord MacBad 962500c37d fix: add sync files to flux-system kustomization 2026-04-21 15:53:23 +02:00
Scrublord MacBad 4864e0cbec fix: sync file locations 2026-04-21 15:48:52 +02:00
Scrublord MacBad 75650fd895 fix: typo in kustomization file 2026-04-21 15:47:32 +02:00
Scrublord MacBad bf648e2d90 move: sync files to the correct flux-system folder 2026-04-21 15:43:52 +02:00
Scrublord MacBad 3f181a4567 refactor: Infra von Apps getrennt und Abhängigkeit hinzugefügt 2026-04-21 15:34:42 +02:00
Scrublord MacBad 908f400671 feat: Cert-Manager infra und ClusterIssuer hinzugefügt 2026-04-21 15:14:32 +02:00
Scrublord MacBad 0973a07dc0 feat: ESS Repo und Operator Base hinzugefügt 2026-04-21 14:51:29 +02:00
Scrublord MacBad 8844f30086 postgres sops 2026-04-21 14:49:35 +02:00
Scrublord MacBad a583aeef05 fix 2026-04-21 14:43:48 +02:00
Scrublord MacBad 32aea697c8 fix sops 2026-04-21 14:42:45 +02:00
Scrublord MacBad 3cbd30b75e fix sops 2026-04-21 14:39:17 +02:00
Scrublord MacBad 094c292145 sops fix 2026-04-21 14:38:36 +02:00
Scrublord MacBad 46204b0097 feat: Matrix Namespace und verschlüsseltes DB-Passwort hinzugefügt 2026-04-21 14:30:50 +02:00
Scrublord MacBad 7f3acce851 Fix Prodsync 2 2026-04-21 14:20:38 +02:00
Scrublord MacBad c890aad88b Fix Prodsync 2026-04-21 14:20:38 +02:00
sorb d3976eca6e fix: Leere resources entfernt 2026-04-21 12:12:42 +00:00
Scrublord MacBad cb4a8943e0 Ordnerstruktur und Production-Sync hinzugefügt 2026-04-21 13:53:04 +02:00
FluxandFlux 099ecf0f24 Add Flux sync manifests 2026-04-21 13:14:41 +02:00
FluxandFlux e0b1cccc9b Add Flux v2.8.5 component manifests 2026-04-21 13:14:35 +02:00
62 changed files with 9195 additions and 13034 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
+4
View File
@@ -0,0 +1,4 @@
creation_rules:
- path_regex: .*.yaml
encrypted_regex: ^(data|stringData)$
age: "age14l0hwfqylwpemz5y2ghh2yxk0phszlnj3qlejhue0fw0kz3tmfgqdsjzdh"
+247
View File
@@ -0,0 +1,247 @@
# 🚀 Element Server Suite (ESS) Community GitOps Deployment Guide
Dieses Repository enthält die Infrastruktur-as-Code (IaC) für den Matrix-Homeserver (basierend auf der Element Server Suite Community Edition), der per **FluxCD** nach GitOps-Prinzipien verwaltet wird.
## 📑 Inhaltsverzeichnis
1. [Voraussetzungen & Lokale Tools](https://www.google.com/search?q=%231-voraussetzungen--lokale-tools)
2. [Architektur & Logik des Stacks](https://www.google.com/search?q=%232-architektur--logik-des-stacks)
3. [Aufbau des Repositories](https://www.google.com/search?q=%233-aufbau-des-repositories)
4. [Das Deployment (Aktueller Stand)](https://www.google.com/search?q=%234-das-deployment-aktueller-stand)
5. [Nützliche Befehle](https://www.google.com/search?q=%235-n%C3%BCtzliche-befehle)
6. [Troubleshooting & Known Issues](https://www.google.com/search?q=%236-troubleshooting--known-issues)
-----
## 1\. Voraussetzungen & Lokale Tools
Um mit diesem Stack zu interagieren (Konfigurationen anzupassen, Secrets zu verschlüsseln, Fehler zu suchen), müssen folgende Tools lokal installiert sein:
### 🛠️ Benötigte CLI-Tools
* **`kubectl`**: Für die direkte Kommunikation mit dem Kubernetes-Cluster.
* **`flux`**: Für das manuelle Anstoßen von GitOps-Synchronisationen.
* **`sops`** & **`age`** (oder GPG): Für die Ver- und Entschlüsselung von Secrets direkt im Git-Repo.
* **`helm`**: (Optional) Zum Inspizieren von Chart-Values.
### 🍏 macOS (via Homebrew)
```bash
brew install kubectl fluxcd/tap/flux sops age helm
```
### 🐧 Linux
```bash
# kubectl & helm via Paketmanager (apt/dnf) oder curl
curl -sLS https://fluxcd.io/install.sh | sudo bash
# SOPS
wget https://github.com/getsops/sops/releases/download/v3.8.1/sops-v3.8.1.linux.amd64
sudo mv sops-v3.8.1.linux.amd64 /usr/local/bin/sops && sudo chmod +x /usr/local/bin/sops
sudo apt install age
```
### 🪟 Windows (via Winget oder WSL2)
*Empfehlung: Nutze WSL2 (Ubuntu) und folge den Linux-Schritten.* Nativ via Winget:
```powershell
winget install Kubernetes.kubectl FluxCD.Flux Mozilla.sops age-encryption.age Helm.Helm
```
### ⚙️ Lokale Konfiguration
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.
-----
## 2\. Architektur & Logik des Stacks
Das Setup basiert auf einer modernen, modularen GitOps-Architektur:
### Management-Komponenten
* **K3s**: Die leichtgewichtige Kubernetes-Distribution, die als Fundament dient.
* **FluxCD**: Der GitOps-Controller. Er überwacht dieses Git-Repository. Ändert sich hier eine Datei, wendet Flux die Änderung automatisch im Cluster an.
* **SOPS**: Erlaubt es, Passwörter (z.B. SMTP) verschlüsselt in Git zu speichern. Flux entschlüsselt diese "on the fly" im Cluster.
* **Traefik**: Der Ingress-Controller (Standard bei K3s). Er leitet Traffic von Port 80/443 an die richtigen internen Pods weiter.
* **Cert-Manager**: Spricht mit Let's Encrypt und stellt automatisch gültige TLS-Zertifikate für alle Ingress-Routen aus.
### Matrix Stack (ESS Community v26.4.0)
Die Suite ist ein "Umbrella Chart", das aus mehreren Microservices besteht:
* **Synapse (`matrix.`):** Das eigentliche Backend (Homeserver) für die Chat-Nachrichten.
* **Matrix Authentication Service (MAS) (`account.`):** Der OIDC-basierte Login-Server. Zwingend erforderlich für moderne Matrix-Clients.
* **Element Web (`domain.tld`):** Der Web-Client für die Endnutzer.
* **Matrix RTC (`mrtc.`):** Die SFU (Selective Forwarding Unit) für Audio-/Video-Calls.
* **PostgreSQL:** Die relationale Datenbank für Synapse und MAS.
-----
## 3\. Aufbau des Repositories
Das Repository ist strikt nach "Infrastruktur" und "Applikation" getrennt, um Abhängigkeiten korrekt zu laden.
```text
gitops/
├── .sops.yaml # Definiert, wie Secrets verschlüsselt werden
├── clusters/matrix/ # Der Einstiegspunkt für FluxCD
├── apps/
│ ├── base/
│ │ ├── infra/ # Core-Dienste (Cert-Manager, Namespaces)
│ │ └── matrix/ # Die OCI Helm-Repository Definition für ESS
│ └── production/ # Das eigentliche Matrix-Deployment
│ ├── kustomization.yaml # Inhaltsverzeichnis
│ ├── element-server-suite.yaml # Das HelmRelease (Bestellung an Flux)
│ ├── cert-issuer.yaml # Let's Encrypt Konfiguration
│ ├── matrix-postgres-auth.yaml # DB-Passwörter
│ └── custom-configs/ # Eigene Anpassungen (Themes, Logging)
│ ├── synapse-values.yaml # Als ConfigMap
│ ├── element-values.yaml # Als ConfigMap
│ └── mas-secrets.sops.yaml # Als verschlüsseltes SOPS-Secret
```
**Abhängigkeits-Logik:** Flux installiert erst `infra-apps` (damit Namespaces und Repositories existieren) und danach `production-apps` (das eigentliche ESS-Chart).
-----
## 4\. Das Deployment (Aktueller Stand)
### Das HelmRepository (OCI)
Element verteilt die Community-Edition modern über die GitHub Container Registry (`ghcr.io`). Klassische HTTP-Helm-Repos werfen hier oft 404-Fehler.
```yaml
# apps/base/matrix/ess-repo.yaml
apiVersion: source.toolkit.fluxcd.io/v1
kind: HelmRepository
metadata:
name: element-ess-oci
spec:
type: oci
url: oci://ghcr.io/element-hq/ess-helm
```
### Das HelmRelease (Das Herzstück)
Das ESS-Chart (`v26.4.0`) hat ein extrem striktes JSON-Schema. Konfigurationen müssen exakt sitzen:
* `serverName` muss an der Wurzel stehen.
* Komponenten werden in `camelCase` geschrieben (`elementWeb`, `synapseAdmin`).
* Zertifikate werden durch `certManager: true` automatisch gemanaged. **Keine manuellen TLS-Einträge im Ingress-Block\!**
<!-- end list -->
```yaml
# apps/production/element-server-suite.yaml (Auszug)
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: matrix-stack
spec:
chart:
spec:
chart: matrix-stack
version: "26.4.0"
valuesFrom:
- kind: ConfigMap
name: ess-synapse-custom
valuesKey: values.yaml
- kind: Secret
name: ess-mas-custom-secrets
valuesKey: values.yaml
values:
serverName: axion1337.chat
certManager: true
postgres:
enabled: true
synapse:
enabled: true
ingress: { host: matrix.axion1337.chat }
matrixAuthenticationService:
enabled: true
ingress: { host: account.axion1337.chat }
elementWeb:
enabled: true
ingress: { host: axion1337.chat }
wellKnownDelegation:
enabled: false # ! WICHTIG (Siehe Troubleshooting)
```
-----
## 5\. Nützliche Befehle
### 🔄 Flux / GitOps Sync erzwingen
Wenn man nicht auf den automatischen 10-Minuten-Timer von Flux warten will:
```bash
flux reconcile kustomization flux-system --with-source
flux reconcile kustomization production-apps --with-source
```
### 🔍 Status des Deployments prüfen
```bash
# Zeigt, ob Flux das Chart akzeptiert und angewendet hat
flux get helmreleases -A
# Zeigt an, ob die Pods erfolgreich starten
kubectl get pods -n matrix
```
### 🔐 Zertifikate (Let's Encrypt) debuggen
```bash
# Sind die Zertifikate da und gültig?
kubectl get certificate -n matrix
# Wo hängt der Request? (403 Fehler etc.)
kubectl get certificaterequest -n matrix
kubectl get challenges -n matrix
kubectl describe challenge <name> -n matrix
```
### 🛡️ Secrets mit SOPS bearbeiten
Um ein Passwort im GitOps-Repo zu ändern, editiert man die verschlüsselte Datei direkt via SOPS (sie wird transparent entschlüsselt und beim Speichern wieder verschlüsselt):
```bash
sops apps/production/custom-configs/mas-secrets.sops.yaml
```
-----
## 6\. Troubleshooting & Known Issues
### Issue 1: `HelmChart is not ready: stat ... no such file or directory`
* **Ursache:** Falscher Versuch, das Chart direkt aus dem GitHub-Repo-Code (als GitRepository) zu lesen. Das Chart erfordert Sub-Charts, die so nicht gerendert werden können.
* **Lösung:** Immer das OCI-Repository (`oci://ghcr.io/...`) und den Chartnamen `matrix-stack` verwenden.
### Issue 2: `values don't meet the specifications of the schema(s)`
* **Ursache:** Ab Version 26.x hat ESS ein sehr rigides JSON-Schema.
* **Lösung:** Logs genau lesen.
* `tls` darf nicht in den Ingress-Block der Komponenten.
* `serverName` muss ins Top-Level, nicht unter `synapse`.
* Keine `config:` Blöcke für Core-Komponenten.
### Issue 3: Let's Encrypt Error `403 Order's status is processing` auf der Hauptdomain
* **Ursache (Die ACME Race Condition):** Wenn `elementWeb` (auf `axion1337.chat`) und `wellKnownDelegation` (ebenfalls auf `axion1337.chat`) gleichzeitig aktiviert sind, fordert `cert-manager` zeitgleich zwei Zertifikate für dieselbe Domain an. Let's Encrypt blockt den zweiten Versuch und das Ingress-Setup hängt sich auf.
* **Lösung:** `wellKnownDelegation: enabled: false` im Helm-Chart setzen. Das `.well-known/matrix/server` File muss stattdessen entweder als statische JSON-Datei auf dem Webserver der Hauptdomain hinterlegt oder per Ingress-Route (Traefik Middleware) direkt auf den Synapse-Dienst umgebogen werden.
### Issue 4: Fehlende Zertifikate (`No resources found`)
* **Ursache:** Manuelle Kustomize-Patches kollidieren mit dem Helm-Chart.
* **Lösung:** Manuelle Patches löschen und das native Feature des Charts nutzen: `certManager: true` auf der obersten (Root-)Ebene der `values` setzen. Das Chart erstellt daraufhin die korrekten Ingress-Annotations und Secrets von selbst.
BIN
View File
Binary file not shown.
+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
+87
View File
@@ -0,0 +1,87 @@
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: authentik
namespace: authentik
spec:
interval: 1h
chart:
spec:
chart: authentik
version: "2026.2.2"
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>"
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
+9
View File
@@ -0,0 +1,9 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- namespace.yaml
- helm-repo.yaml
- authentik-secret.yaml
- certificate.yaml
- authentik.yaml
- ingress.yaml
+4
View File
@@ -0,0 +1,4 @@
apiVersion: v1
kind: Namespace
metadata:
name: authentik
BIN
View File
Binary file not shown.
+8
View File
@@ -0,0 +1,8 @@
apiVersion: source.toolkit.fluxcd.io/v1
kind: HelmRepository
metadata:
name: jetstack
namespace: flux-system
spec:
interval: 1h
url: https://charts.jetstack.io
+22
View File
@@ -0,0 +1,22 @@
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: cert-manager
namespace: cert-manager
spec:
releaseName: cert-manager
interval: 30m
chart:
spec:
chart: cert-manager
version: "v1.14.0" # Oder aktuellste stabile Version
sourceRef:
kind: HelmRepository
name: jetstack
namespace: flux-system
install:
createNamespace: true
remediation:
retries: 3
values:
installCRDs: true
+8
View File
@@ -0,0 +1,8 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ns-cert-manager.yaml
- ns-matrix.yaml
- cert-manager-repo.yaml
- cert-manager.yaml
- ../matrix/ess-repo.yaml
+4
View File
@@ -0,0 +1,4 @@
apiVersion: v1
kind: Namespace
metadata:
name: cert-manager
+4
View File
@@ -0,0 +1,4 @@
apiVersion: v1
kind: Namespace
metadata:
name: matrix
+17
View File
@@ -0,0 +1,17 @@
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: element-operator
namespace: matrix
spec:
releaseName: element-operator
interval: 30m
chart:
spec:
chart: element-operator
sourceRef:
kind: HelmRepository
name: element-charts
namespace: flux-system
install:
createNamespace: true
+9
View File
@@ -0,0 +1,9 @@
apiVersion: source.toolkit.fluxcd.io/v1
kind: HelmRepository
metadata:
name: element-ess-oci
namespace: flux-system
spec:
type: oci
url: oci://ghcr.io/element-hq/ess-helm
interval: 1h
+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
+44
View File
@@ -0,0 +1,44 @@
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
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
priority: 10
services:
- name: matrix-stack-element-web
port: 80
+14
View File
@@ -0,0 +1,14 @@
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: gamemaster@axion1337.de
privateKeySecretRef:
name: letsencrypt-prod-account-key
solvers:
- http01:
ingress:
class: traefik # K3s nutzt standardmäßig Traefik
+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
+162
View File
@@ -0,0 +1,162 @@
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:
exec:
command:
- /bin/sh
- -c
- "netstat -uln | grep 3478 || exit 1"
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"
@@ -0,0 +1,192 @@
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",
"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"
]
}
}
]
}
}
@@ -0,0 +1,22 @@
apiVersion: v1
kind: Secret
metadata:
name: ess-mas-values-secret
namespace: matrix
stringData:
values.yaml: ENC[AES256_GCM,data:tPLcPvoTa2Qs49JSDW6CqiYMIrjsoKrExCl+hOm5R3/o+O9Lf4UqyMC4QY6T303m7GMU3tIDGX4js8NS7Fdcs9YOKoInUKlYqCYzNio+BRsl1DGtzEtcUWhXZQC58RHbI8jiLhXsI1x2vSYsMj5MkymVe9Kmjw91vxEivzn8dC2zae4rFGk/LyLoI5BHzSCT6csUGtGZe01rN/DsGpHxkYNbzinBK3uFM43IgcjCrkBK8jzsxUYA4JWvzBJ1tB5TyyTgZBBe2Baxj7vHf9SysGwbRl4TBzY5at8cPKKnmh7qaHQO3FHCCqfHi1ymt1vNRtPPGRGAKWqC8AroXTPUUe4oSQ7wakhuQwPAhQJn0FHjIJNbp2J7M9K3nWnf0eugDPcp1pQ7Iv0tUQ/2hd5Q8Xmiv7qCCBuhOGnAokUwkRnZu124PEhPwDa/PSA6bB/5dCwj8rzZOyEXJsz12cRtSPK8Kqff+bp2MSFEA8iETm40k/BxybyC2odWEhkPDlRl+YDe8rJxkGGLwP5PEgiQp5RK4ZftQ6Cgc5loUHDAar10tMsqg++iQeyeb/o=,iv:PSo85CoDdWajU3j4vHsaNCHI6UbMbII01nskXNyotVU=,tag:OBVkKsBnCv9bloORukDgcQ==,type:str]
sops:
age:
- recipient: age14l0hwfqylwpemz5y2ghh2yxk0phszlnj3qlejhue0fw0kz3tmfgqdsjzdh
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBWOVd0enFxNE85WnN1eTRD
MjdVSE90NUd6MWFBbCtzZFRTK0NQYllUTGdVCk9aWGNGLzNvLzU3S09la2RiY1hv
bXdNZjFxaVM5aUF3MTA1bWx4WU1TR0EKLS0tIFp6RmdCVlE5Zk53RjF3MnZveHo4
eUpzQTBCRjM0a2FmZzNkdmFKWUVPODQKqpA3drI6JV67Y3P/l8Ql5xwtohVi9D3P
6iAcFoqrVZMSKkkiHDvAcdUexIO/BKddjC5N608MLUz7tcxyWfMqeg==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2026-04-23T13:17:31Z"
mac: ENC[AES256_GCM,data:V4l8oScpWwC95gg9UQpaV0oKn292Y6WoRZdWlqF3I8BWCGV4LVvLE7KxC9lqHdt/Mcgb6yuaDSv3ZMERl81QOMSMcPVfk/F0LoabP/dFiz1ogxOezHOfJJ2mTu+4yAOkK73RQY68ui5UGAV5FFu3tecE0AAouSt0YrOTBALtLpY=,iv:WBFy/v6gRBil30Oqdew3JW5XVz5wmaO0Uj7J+MfSrss=,tag:CvTEdnbs4dJ0qlnefvXIag==,type:str]
encrypted_regex: ^(data|stringData)$
version: 3.12.2
@@ -0,0 +1,72 @@
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:
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'
- '::1/128'
- 'fe80::/10'
- 'fc00::/7'
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
+84
View File
@@ -0,0 +1,84 @@
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: matrix-stack
namespace: matrix
spec:
interval: 5m
chart:
spec:
chart: matrix-stack
version: "26.4.0"
sourceRef:
kind: HelmRepository
name: element-ess-oci
namespace: flux-system
# NEU: Hier zieht Flux deine Puzzleteile zusammen
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:
# Top-Level: serverName das ist dein Matrix-Homeserver-Name
serverName: axion1337.chat
# Cert-Manager für automatische Zertifikatsgenerierung
certManager:
clusterIssuer: letsencrypt-prod
# Interner Postgres an (default ist eh true, hier nur zur Klarheit)
postgres:
enabled: true
# Synapse API auf matrix.axion1337.chat
synapse:
enabled: true
ingress:
host: matrix.axion1337.chat
additional:
oembed:
config: |
oembed_enabled: true
# Matrix Authentication Service braucht eine Subdomain
matrixAuthenticationService:
enabled: true
ingress:
host: account.axion1337.chat
# Matrix RTC (Element Call) braucht auch eine Subdomain
matrixRTC:
enabled: true
ingress:
host: mrtc.axion1337.chat
# Element Web
elementWeb:
enabled: true
image:
registry: rohana.axion1337.de
repository: sorb/threadnet-web
tag: v0.1.0
ingress:
host: axion1337.chat
# Element Admin
elementAdmin:
enabled: true
ingress:
host: admin.axion1337.chat
# Well-Known auf der Apex-Domain (axion1337.chat/.well-known/matrix/*)
# Aktiviert notwendig für MatrixRTC-Discovery
wellKnownDelegation:
enabled: true
ingress:
className: "none" # Deaktiviert den Chart-Ingress, wir erstellen einen eigenen
@@ -0,0 +1,402 @@
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>
</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>
# 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"
@@ -0,0 +1,92 @@
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/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;
}
}
+32
View File
@@ -0,0 +1,32 @@
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
+77
View File
@@ -0,0 +1,77 @@
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: matrix-axion1337-chat-cert
namespace: matrix
spec:
secretName: matrix-axion1337-chat-tls
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
dnsNames:
- matrix.axion1337.chat
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: account-axion1337-chat-cert
namespace: matrix
spec:
secretName: account-axion1337-chat-tls
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
dnsNames:
- account.axion1337.chat
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: mrtc-axion1337-chat-cert
namespace: matrix
spec:
secretName: mrtc-axion1337-chat-tls
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
dnsNames:
- mrtc.axion1337.chat
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: axion1337-chat-cert
namespace: matrix
spec:
secretName: axion1337-chat-tls
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
dnsNames:
- axion1337.chat
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: admin-axion1337-chat-cert
namespace: matrix
spec:
secretName: admin-axion1337-chat-tls
issuerRef:
name: letsencrypt-prod
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
+22
View File
@@ -0,0 +1,22 @@
apiVersion: v1
data:
postgresql-password: ENC[AES256_GCM,data:euLWStKb/Bu+cgyvyxLAwZiLk5VjnHl+meZxcbZS1TahlbavOMEdlg==,iv:Gg2fW0vQ752tsm62n1r3Hzc/NRFBBMdGYOkbfV1jGIs=,tag:77pv1fEP2nkcEL1Dsx+hGQ==,type:str]
kind: Secret
metadata:
name: matrix-postgres-auth
namespace: matrix
sops:
age:
- recipient: age14l0hwfqylwpemz5y2ghh2yxk0phszlnj3qlejhue0fw0kz3tmfgqdsjzdh
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA1d3dKNGE1SGRabm1TaWhK
L2FBUHlWQWVaalRzS2FlTFJqNVNlT3JyUEZ3CmJPa1Mxa1VWSTZCTVlqRFk1WU5s
ZDJDc05obGtEdkV5cTFRa3dvbXJNM00KLS0tIHdCeHBLb090c3M4M2g5VlkzeStJ
ZGVhbnJYMHFjSDFPR1lWSmlBTXUzQUkK3ROf7Vu/SsPmNob/eKcPS7BwWUQrMXMW
YiuPlzNpT80nrtXRgm8AHbua6adV25eEVvFbroGvqkNtay8r5NUDkg==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2026-04-21T12:45:16Z"
mac: ENC[AES256_GCM,data:yTdP4L0ffQe3pc1gwbWgTSM+mbDgIxPajJDumvXwsM4eWJO+hlmH/nONIG+cnpVCOEDtGTm0CeJd342fU5qw8tL+1tS/OTp3FNgOv3Ms5o45crgaej4LY1JI1jCi5TAz0Kpt5YR15Aeh83OU7xig2fGbSAW5riPM/wqDHGPzBhM=,iv:MK1p9VEN6x3Q0y+YveDif2L5TPzvnvGTIUAY28AB0tM=,tag:1UM56b9Qg4WnggMQVThrMQ==,type:str]
encrypted_regex: ^(data|stringData)$
version: 3.12.2
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
@@ -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
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,27 @@
# This manifest was generated by flux. DO NOT EDIT.
---
apiVersion: source.toolkit.fluxcd.io/v1
kind: GitRepository
metadata:
name: flux-system
namespace: flux-system
spec:
interval: 1m0s
ref:
branch: main
secretRef:
name: flux-system
url: https://rohana.axion1337.de/sorb/axion1337.chat-gitops.git
---
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: flux-system
namespace: flux-system
spec:
interval: 10m0s
path: ./clusters/matrix
prune: true
sourceRef:
kind: GitRepository
name: flux-system
@@ -0,0 +1,12 @@
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: infra-apps
namespace: flux-system
spec:
interval: 1m
path: ./apps/base/infra
prune: true
sourceRef:
kind: GitRepository
name: flux-system
@@ -0,0 +1,9 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
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
@@ -0,0 +1,18 @@
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: production-apps
namespace: flux-system
spec:
interval: 1m
path: ./apps/production
prune: true
sourceRef:
kind: GitRepository
name: flux-system
decryption:
provider: sops
secretRef:
name: sops-age
dependsOn:
- name: infra-apps
-117
View File
@@ -1,117 +0,0 @@
# 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
@@ -1,327 +0,0 @@
# 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
@@ -1,498 +0,0 @@
# 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
+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"
-9
View File
@@ -1,9 +0,0 @@
# aXion1337.Chat Wiki
Welcome to the aXion1337.Chat deployment wiki!
## 📚 Contents
- [Deployment Guides](deployment-guides)
- [Tasks & Roadmap](tasks)
- [Architecture](architecture)