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