Compare commits

..

No commits in common. "wiki" and "main" have entirely different histories.
wiki ... main

64 changed files with 10450 additions and 60 deletions

BIN
.DS_Store vendored Normal file

Binary file not shown.

56
.devcontainer/Dockerfile Normal file
View File

@ -0,0 +1,56 @@
FROM debian:bookworm-slim
# Install base tools
RUN apt-get update && apt-get install -y --no-install-recommends \
curl \
wget \
git \
ca-certificates \
gnupg \
lsb-release \
apt-transport-https \
vim \
nano \
jq \
yq \
zsh \
sudo \
openssh-client \
&& rm -rf /var/lib/apt/lists/*
# Install kubectl
RUN curl -fsSLo /usr/share/keyrings/kubernetes-archive-keyring.gpg https://packages.cloud.google.com/apt/doc/apt-key.gpg && \
echo "deb [signed-by=/usr/share/keyrings/kubernetes-archive-keyring.gpg] https://apt.kubernetes.io/ kubernetes-xenial main" | tee /etc/apt/sources.list.d/kubernetes.list && \
apt-get update && apt-get install -y kubectl && \
rm -rf /var/lib/apt/lists/*
# Install Helm
RUN curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
# Install Flux CLI
RUN curl -s https://fluxcd.io/install.sh | bash
# Install sops
RUN SOPS_VERSION=$(curl -s https://api.github.com/repos/getsops/sops/releases/latest | grep tag_name | cut -d '"' -f 4) && \
curl -sL -o /usr/local/bin/sops https://github.com/getsops/sops/releases/download/${SOPS_VERSION}/sops-${SOPS_VERSION}.linux.amd64 && \
chmod +x /usr/local/bin/sops
# Install age
RUN apt-get update && apt-get install -y age && \
rm -rf /var/lib/apt/lists/*
# Install Docker CLI (for interacting with Docker daemon)
RUN curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg && \
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null && \
apt-get update && apt-get install -y docker-ce-cli && \
rm -rf /var/lib/apt/lists/*
# Create a non-root user 'vscode' for development
RUN useradd -m -s /bin/bash -G docker vscode && \
echo "vscode ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers.d/vscode
# Install oh-my-zsh for better shell experience
RUN su - vscode -c "sh -c '$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)' '' --unattended"
USER vscode
WORKDIR /workspace

203
.devcontainer/README.md Normal file
View File

@ -0,0 +1,203 @@
# 🐳 DevContainer für ESS Community GitOps
Dieses DevContainer-Setup ermöglicht dir, auf **macOS, Windows und Linux** einheitlich zu entwickeln.
## 🚀 Schnelstart
### VSCode mit Remote Containers Extension
1. **VSCode Extension installieren:**
- Öffne VSCode → Extensions → Suche nach `Dev Containers` (Microsoft)
- Installiere sie
2. **GitOps Verzeichnis öffnen:**
```bash
cd "april mit Ansible/prod/gitops"
code .
```
3. **DevContainer starten:**
- Klick auf `><` Symbol unten links in VSCode
- Wähle `Reopen in Container`
- Warte, bis das Image gebaut wurde (~3-5 Min beim ersten Mal)
### Alternative: Docker + CLI
```bash
docker build -t ess-gitops .devcontainer
docker run -it --rm \
-v ~/.kube:/home/vscode/.kube \
-v ~/.ssh:/home/vscode/.ssh \
-v ~/.age:/home/vscode/.age \
-v /var/run/docker.sock:/var/run/docker.sock \
ess-gitops
```
## 📦 Enthaltene Tools
- **kubectl** - Kubernetes CLI
- **flux** - FluxCD GitOps Controller CLI
- **helm** - Kubernetes Package Manager
- **sops** - Secret Operations (Verschlüsselung)
- **age** - Modern File Encryption
- **docker** - Container CLI (über Host-Socket)
- **git** - Versionskontrolle
- **jq/yq** - JSON/YAML Processing
- **zsh + oh-my-zsh** - Shell mit Plugins
## 🔐 Wichtige Verzeichnis-Binds
Der Container mountet automatisch:
| Host | Container | Zweck |
|------|-----------|-------|
| `~/.kube` | `/home/vscode/.kube` | Kubernetes Config |
| `~/.ssh` | `/home/vscode/.ssh` | SSH Keys |
| `~/.age` | `/home/vscode/.age` | Age Encryption Keys |
| `/var/run/docker.sock` | `/var/run/docker.sock` | Docker Daemon (für `docker` Befehle) |
## ⚙️ Kubeconfig Einrichten
1. **Host-Machine (z.B. macOS):**
```bash
# Stelle sicher, dass ~/.kube/config existiert und den richtigen Cluster enthält
kubectl get nodes
```
2. **Im Container:**
```bash
kubectl get nodes # Sollte jetzt auch dein Cluster zeigen
kubectl config current-context
```
## 🔐 SOPS + Age Setup
Damit du Secrets bearbeiten kannst, brauchst du den privaten `age`-Key. Dieser ist in `.sops.yaml` konfiguriert.
### Schritt 1: Age-Key bereitstellen
```bash
# Host-Machine: Key-Datei erstellen
mkdir -p ~/.age
# Füge deinen privaten Key ein (Format: "age-secret-key-...")
echo "age-secret-key-xxx..." > ~/.age/keys.txt
chmod 600 ~/.age/keys.txt
```
### Schritt 2: Im Container konfigurieren
Der Container mounted `~/.age` automatisch. Setze die Umgebungsvariable:
```bash
# Im Container-Terminal (SOPS_AGE_KEY_FILE ist bereits automatisch gesetzt!)
# Jetzt kannst du Secrets bearbeiten (wird transparent ver-/entschlüsselt):
sops apps/production/custom-configs/mas-secrets.sops.yaml
```
### Schritt 3: VSCode Integration (optional)
Um die Umgebungsvariable beim Start zu setzen, nutze die `.devcontainer/devcontainer.json`:
```json
"remoteEnv": {
"KUBECONFIG": "/home/vscode/.kube/config",
"SOPS_AGE_KEY_FILE": "/home/vscode/.age/keys.txt"
}
```
### Wie es funktioniert
- `.sops.yaml` definiert, dass Secrets mit `age` verschlüsselt werden
- Beim Öffnen mit `sops <datei>` wird die Datei entschlüsselt → du editierst den plaintext in deinem Editor
- Beim Speichern wird alles wieder automatisch verschlüsselt
- **Wichtig:** Niemals den plaintext-Buffer commiten!
## 📝 Nach Container-Start: Git Hooks Installieren
Wichtig für die ConfigMap Auto-Sync (verhindert Merge-Konflikte):
```bash
./scripts/install-hooks.sh
```
Mehr Details: `docs/ops-configmap-sync.md`
## 📝 Nützliche Befehle
```bash
# Status des Deployments
kubectl get pods -n matrix
flux get helmreleases -A
# Secrets bearbeiten (mit verschlüsselung)
sops apps/production/custom-configs/mas-secrets.sops.yaml
# FluxCD Sync erzwingen
flux reconcile kustomization production-apps --with-source
# Zertifikate debuggen
kubectl get certificate -n matrix
kubectl describe certificate matrix-ingress -n matrix
# HelmRelease Status prüfen
flux describe helmrelease matrix-stack -n matrix
```
## 🛠️ Anpassungen für Windows/WSL2
Falls du Windows nutzt:
1. **Docker Desktop installieren** (mit WSL2 Backend)
2. **VSCode mit WSL Extension öffnen**
3. **Im WSL Terminal:**
```bash
cd /mnt/c/path/to/projekt
code .
```
4. Dann `Dev Containers: Reopen in Container`
Das funktioniert seamless, weil Docker Desktop unter WSL2 läuft.
## 🔧 Troubleshooting
### Problem: `SOPS_AGE_KEY_FILE not found`
**Lösung:** Key muss in `~/.age/keys.txt` auf der Host-Machine sein:
```bash
# Host
mkdir -p ~/.age
echo "your-age-private-key" > ~/.age/keys.txt
```
Der Container mountet `~/.age` automatisch → sollte dann funktionieren.
### Problem: `kubectl: connection refused`
**Lösung:** `~/.kube/config` muss auf Host vorhanden sein:
```bash
# Host
kubectl get nodes # Test, ob Zugriff existiert
# Dann Container neustarten
```
### Problem: `HelmChart is not ready: stat ... no such file or directory`
Siehe `README.md`**Issue 1**. Kontrolliere:
- `HelmRepository` nutzt `type: oci`
- URL ist `oci://ghcr.io/element-hq/ess-helm`
### Problem: `values don't meet the specifications of the schema`
Siehe `README.md`**Issue 2**. Häufige Fehler:
- `tls:` darf nicht im `ingress:` Block sein
- `serverName` muss auf Root-Ebene der `values` stehen
- Komponenten-Namen: `camelCase` (z.B. `elementWeb`, `matrixAuthenticationService`)
### Problem: Let's Encrypt `403 Order's status is processing`
Siehe `README.md`**Issue 3**. Kurz:
- `wellKnownDelegation: enabled: false` setzen
- Oder `.well-known/matrix/server` manuell auf `elementWeb` weiterleiten
## 📚 Weitere Ressourcen
- [Dev Containers Docs](https://containers.dev)
- [FluxCD Dokumentation](https://fluxcd.io)
- [SOPS Anleitung](https://github.com/getsops/sops)
- **Projekt-README:** `README.md` (Architektur, Issues, Best Practices)
- **Setup-Docs:** `docs/setup/`
- **Install-Guide:** `docs/install.md`

View File

@ -0,0 +1,65 @@
{
"name": "ESS Community GitOps",
"build": {
"dockerfile": "Dockerfile",
"context": "."
},
"mounts": [
"source=${localEnv:HOME}/.kube,target=/home/vscode/.kube,type=bind,consistency=cached",
"source=${localEnv:HOME}/.ssh,target=/home/vscode/.ssh,type=bind,consistency=cached",
"source=${localEnv:HOME}/.age,target=/home/vscode/.age,type=bind,consistency=cached",
"source=/var/run/docker.sock,target=/var/run/docker.sock,type=bind"
],
"remoteUser": "vscode",
"features": {
"ghcr.io/devcontainers/features/git:1": {},
"ghcr.io/devcontainers/features/github-cli:1": {}
},
"remoteEnv": {
"KUBECONFIG": "/home/vscode/.kube/config",
"SOPS_AGE_KEY_FILE": "/home/vscode/.age/keys.txt"
},
"customizations": {
"vscode": {
"extensions": [
"ms-kubernetes-tools.vscode-kubernetes-tools",
"redhat.vscode-yaml",
"redhat.vscode-commons",
"monokai.theme-monokai-pro-vscode",
"eamodio.gitlens",
"gruntfuggly.todo-tree",
"ms-vscode.makefile-tools",
"GitHub.copilot"
],
"settings": {
"[yaml]": {
"editor.defaultFormatter": "redhat.vscode-yaml",
"editor.formatOnSave": true,
"editor.tabSize": 2
},
"yaml.schemas": {
"https://json.schemastore.org/kustomization.json": "**/kustomization.yaml",
"https://json.schemastore.org/helmrelease.json": "**/*helmrelease*.yaml"
},
"editor.theme": "Monokai Pro",
"todo-tree.general.showActivityBarBadge": true,
"todo-tree.general.tags": [
"TODO",
"FIXME",
"BUG",
"HACK",
"NOTE",
"XXX",
"DONE"
],
"todo-tree.tree.showScanModeButton": true,
"todo-tree.filtering.includeGlobs": [
"**/docs/TASKS.md",
"**/docs/deployment-guides/*.md"
]
}
}
},
"postCreateCommand": "bash .devcontainer/postCreateCommand.sh",
"forwardPorts": []
}

View File

@ -0,0 +1,35 @@
#!/bin/bash
set -e
echo "🚀 Setting up ESS Community GitOps devcontainer..."
# Verify all required tools are installed
echo "✅ Verifying installed tools..."
commands=("kubectl" "flux" "helm" "sops" "age" "git" "docker")
for cmd in "${commands[@]}"; do
if command -v $cmd &> /dev/null; then
version=$($cmd version 2>/dev/null | head -1 || echo "installed")
echo "$cmd: $version"
else
echo "$cmd: NOT FOUND"
exit 1
fi
done
# Create necessary directories
echo "📁 Creating home directories..."
mkdir -p ~/.kube ~/.ssh ~/.age
# Print useful information
echo ""
echo "📚 Useful commands:"
echo " - kubectl get pods -n matrix (check pod status)"
echo " - flux get helmreleases -A (check helm releases)"
echo " - sops apps/production/custom-configs/mas-secrets.sops.yaml (edit secrets)"
echo ""
echo "🔗 For kubeconfig setup:"
echo " - Copy your ~/.kube/config to access the cluster"
echo " - Run: kubectl get nodes"
echo ""
echo "✨ Devcontainer setup complete!"

View File

@ -0,0 +1,50 @@
name: Auto-Deploy on Push
on:
push:
branches:
- main
paths:
- 'apps/**'
- 'clusters/**'
- '.gitea/workflows/**'
jobs:
verify-and-notify:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Check YAML Syntax
run: |
echo "🔍 Validating YAML files..."
find apps clusters -name "*.yaml" -type f | while read file; do
if ! grep -q "^apiVersion:" "$file"; then
echo "⚠️ Warning: $file may not be a valid K8s manifest"
fi
done
echo "✅ YAML validation passed"
- name: Check for SOPS Encryption
run: |
echo "🔐 Checking SOPS status..."
for file in $(git diff --name-only origin/main...HEAD -- '**/secret*.yaml' '**/credentials*.yaml'); do
if grep -q "ENC\[" "$file"; then
echo "✅ $file is encrypted"
else
echo "⚠️ WARNING: $file may not be encrypted!"
fi
done
- name: Create Deployment Notification
run: |
echo "📤 Flux will reconcile changes within 1 minute"
echo "🔗 Monitor in Gitea: Projects → Releases (check tags)"
- name: List Changed Files
run: |
echo "📋 Files changed in this push:"
git diff --name-only origin/main...HEAD

View File

@ -0,0 +1,32 @@
name: Create Release on Milestone Tag
on:
push:
tags:
- 'm*-*-complete'
jobs:
create-release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Extract Milestone Info
id: milestone
run: |
TAG="${GITHUB_REF#refs/tags/}"
TITLE=$(git tag -l "$TAG" -n1 | awk '{print substr($0, index($0, $2))}')
echo "tag=$TAG" >> $GITHUB_OUTPUT
echo "title=$TITLE" >> $GITHUB_OUTPUT
echo "🏷️ Milestone: $TAG"
echo "📝 Title: $TITLE"
- name: Create Release
run: |
echo "📦 Creating release for milestone: ${{ steps.milestone.outputs.tag }}"
echo "${{ steps.milestone.outputs.title }}" > /tmp/release-notes.txt
echo "Created: $(date)" >> /tmp/release-notes.txt
cat /tmp/release-notes.txt

4
.sops.yaml Normal file
View File

@ -0,0 +1,4 @@
creation_rules:
- path_regex: .*.yaml
encrypted_regex: ^(data|stringData)$
age: "age14l0hwfqylwpemz5y2ghh2yxk0phszlnj3qlejhue0fw0kz3tmfgqdsjzdh"

247
README.md Normal file
View File

@ -0,0 +1,247 @@
# 🚀 Element Server Suite (ESS) Community GitOps Deployment Guide
Dieses Repository enthält die Infrastruktur-as-Code (IaC) für den Matrix-Homeserver (basierend auf der Element Server Suite Community Edition), der per **FluxCD** nach GitOps-Prinzipien verwaltet wird.
## 📑 Inhaltsverzeichnis
1. [Voraussetzungen & Lokale Tools](https://www.google.com/search?q=%231-voraussetzungen--lokale-tools)
2. [Architektur & Logik des Stacks](https://www.google.com/search?q=%232-architektur--logik-des-stacks)
3. [Aufbau des Repositories](https://www.google.com/search?q=%233-aufbau-des-repositories)
4. [Das Deployment (Aktueller Stand)](https://www.google.com/search?q=%234-das-deployment-aktueller-stand)
5. [Nützliche Befehle](https://www.google.com/search?q=%235-n%C3%BCtzliche-befehle)
6. [Troubleshooting & Known Issues](https://www.google.com/search?q=%236-troubleshooting--known-issues)
-----
## 1\. Voraussetzungen & Lokale Tools
Um mit diesem Stack zu interagieren (Konfigurationen anzupassen, Secrets zu verschlüsseln, Fehler zu suchen), müssen folgende Tools lokal installiert sein:
### 🛠️ Benötigte CLI-Tools
* **`kubectl`**: Für die direkte Kommunikation mit dem Kubernetes-Cluster.
* **`flux`**: Für das manuelle Anstoßen von GitOps-Synchronisationen.
* **`sops`** & **`age`** (oder GPG): Für die Ver- und Entschlüsselung von Secrets direkt im Git-Repo.
* **`helm`**: (Optional) Zum Inspizieren von Chart-Values.
### 🍏 macOS (via Homebrew)
```bash
brew install kubectl fluxcd/tap/flux sops age helm
```
### 🐧 Linux
```bash
# kubectl & helm via Paketmanager (apt/dnf) oder curl
curl -sLS https://fluxcd.io/install.sh | sudo bash
# SOPS
wget https://github.com/getsops/sops/releases/download/v3.8.1/sops-v3.8.1.linux.amd64
sudo mv sops-v3.8.1.linux.amd64 /usr/local/bin/sops && sudo chmod +x /usr/local/bin/sops
sudo apt install age
```
### 🪟 Windows (via Winget oder WSL2)
*Empfehlung: Nutze WSL2 (Ubuntu) und folge den Linux-Schritten.* Nativ via Winget:
```powershell
winget install Kubernetes.kubectl FluxCD.Flux Mozilla.sops age-encryption.age Helm.Helm
```
### ⚙️ Lokale Konfiguration
1. **Kubeconfig:** Stelle sicher, dass die Datei `~/.kube/config` mit den Zugangsdaten zu deinem K3s-Cluster gefüllt ist. Test: `kubectl get nodes`.
2. **SOPS Key:** Du benötigst den privaten `age`-Key (oder GPG-Key), der in der `.sops.yaml` des Repositories hinterlegt ist, um Secrets bearbeiten zu können.
3. **Git Hooks installieren:** Nach dem Klonen dieses Repositories müssen Git Hooks installiert werden, um ConfigMap-Änderungen automatisch zu tracken:
```bash
cd prod/gitops
./scripts/install-hooks.sh
```
Siehe [📖 GitOps ConfigMap Auto-Sync](docs/ops-configmap-sync.md) für Details.
-----
## 2\. Architektur & Logik des Stacks
Das Setup basiert auf einer modernen, modularen GitOps-Architektur:
### Management-Komponenten
* **K3s**: Die leichtgewichtige Kubernetes-Distribution, die als Fundament dient.
* **FluxCD**: Der GitOps-Controller. Er überwacht dieses Git-Repository. Ändert sich hier eine Datei, wendet Flux die Änderung automatisch im Cluster an.
* **SOPS**: Erlaubt es, Passwörter (z.B. SMTP) verschlüsselt in Git zu speichern. Flux entschlüsselt diese "on the fly" im Cluster.
* **Traefik**: Der Ingress-Controller (Standard bei K3s). Er leitet Traffic von Port 80/443 an die richtigen internen Pods weiter.
* **Cert-Manager**: Spricht mit Let's Encrypt und stellt automatisch gültige TLS-Zertifikate für alle Ingress-Routen aus.
### Matrix Stack (ESS Community v26.4.0)
Die Suite ist ein "Umbrella Chart", das aus mehreren Microservices besteht:
* **Synapse (`matrix.`):** Das eigentliche Backend (Homeserver) für die Chat-Nachrichten.
* **Matrix Authentication Service (MAS) (`account.`):** Der OIDC-basierte Login-Server. Zwingend erforderlich für moderne Matrix-Clients.
* **Element Web (`domain.tld`):** Der Web-Client für die Endnutzer.
* **Matrix RTC (`mrtc.`):** Die SFU (Selective Forwarding Unit) für Audio-/Video-Calls.
* **PostgreSQL:** Die relationale Datenbank für Synapse und MAS.
-----
## 3\. Aufbau des Repositories
Das Repository ist strikt nach "Infrastruktur" und "Applikation" getrennt, um Abhängigkeiten korrekt zu laden.
```text
gitops/
├── .sops.yaml # Definiert, wie Secrets verschlüsselt werden
├── clusters/matrix/ # Der Einstiegspunkt für FluxCD
├── apps/
│ ├── base/
│ │ ├── infra/ # Core-Dienste (Cert-Manager, Namespaces)
│ │ └── matrix/ # Die OCI Helm-Repository Definition für ESS
│ └── production/ # Das eigentliche Matrix-Deployment
│ ├── kustomization.yaml # Inhaltsverzeichnis
│ ├── element-server-suite.yaml # Das HelmRelease (Bestellung an Flux)
│ ├── cert-issuer.yaml # Let's Encrypt Konfiguration
│ ├── matrix-postgres-auth.yaml # DB-Passwörter
│ └── custom-configs/ # Eigene Anpassungen (Themes, Logging)
│ ├── synapse-values.yaml # Als ConfigMap
│ ├── element-values.yaml # Als ConfigMap
│ └── mas-secrets.sops.yaml # Als verschlüsseltes SOPS-Secret
```
**Abhängigkeits-Logik:** Flux installiert erst `infra-apps` (damit Namespaces und Repositories existieren) und danach `production-apps` (das eigentliche ESS-Chart).
-----
## 4\. Das Deployment (Aktueller Stand)
### Das HelmRepository (OCI)
Element verteilt die Community-Edition modern über die GitHub Container Registry (`ghcr.io`). Klassische HTTP-Helm-Repos werfen hier oft 404-Fehler.
```yaml
# apps/base/matrix/ess-repo.yaml
apiVersion: source.toolkit.fluxcd.io/v1
kind: HelmRepository
metadata:
name: element-ess-oci
spec:
type: oci
url: oci://ghcr.io/element-hq/ess-helm
```
### Das HelmRelease (Das Herzstück)
Das ESS-Chart (`v26.4.0`) hat ein extrem striktes JSON-Schema. Konfigurationen müssen exakt sitzen:
* `serverName` muss an der Wurzel stehen.
* Komponenten werden in `camelCase` geschrieben (`elementWeb`, `synapseAdmin`).
* Zertifikate werden durch `certManager: true` automatisch gemanaged. **Keine manuellen TLS-Einträge im Ingress-Block\!**
<!-- end list -->
```yaml
# apps/production/element-server-suite.yaml (Auszug)
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: matrix-stack
spec:
chart:
spec:
chart: matrix-stack
version: "26.4.0"
valuesFrom:
- kind: ConfigMap
name: ess-synapse-custom
valuesKey: values.yaml
- kind: Secret
name: ess-mas-custom-secrets
valuesKey: values.yaml
values:
serverName: axion1337.chat
certManager: true
postgres:
enabled: true
synapse:
enabled: true
ingress: { host: matrix.axion1337.chat }
matrixAuthenticationService:
enabled: true
ingress: { host: account.axion1337.chat }
elementWeb:
enabled: true
ingress: { host: axion1337.chat }
wellKnownDelegation:
enabled: false # ! WICHTIG (Siehe Troubleshooting)
```
-----
## 5\. Nützliche Befehle
### 🔄 Flux / GitOps Sync erzwingen
Wenn man nicht auf den automatischen 10-Minuten-Timer von Flux warten will:
```bash
flux reconcile kustomization flux-system --with-source
flux reconcile kustomization production-apps --with-source
```
### 🔍 Status des Deployments prüfen
```bash
# Zeigt, ob Flux das Chart akzeptiert und angewendet hat
flux get helmreleases -A
# Zeigt an, ob die Pods erfolgreich starten
kubectl get pods -n matrix
```
### 🔐 Zertifikate (Let's Encrypt) debuggen
```bash
# Sind die Zertifikate da und gültig?
kubectl get certificate -n matrix
# Wo hängt der Request? (403 Fehler etc.)
kubectl get certificaterequest -n matrix
kubectl get challenges -n matrix
kubectl describe challenge <name> -n matrix
```
### 🛡️ Secrets mit SOPS bearbeiten
Um ein Passwort im GitOps-Repo zu ändern, editiert man die verschlüsselte Datei direkt via SOPS (sie wird transparent entschlüsselt und beim Speichern wieder verschlüsselt):
```bash
sops apps/production/custom-configs/mas-secrets.sops.yaml
```
-----
## 6\. Troubleshooting & Known Issues
### Issue 1: `HelmChart is not ready: stat ... no such file or directory`
* **Ursache:** Falscher Versuch, das Chart direkt aus dem GitHub-Repo-Code (als GitRepository) zu lesen. Das Chart erfordert Sub-Charts, die so nicht gerendert werden können.
* **Lösung:** Immer das OCI-Repository (`oci://ghcr.io/...`) und den Chartnamen `matrix-stack` verwenden.
### Issue 2: `values don't meet the specifications of the schema(s)`
* **Ursache:** Ab Version 26.x hat ESS ein sehr rigides JSON-Schema.
* **Lösung:** Logs genau lesen.
* `tls` darf nicht in den Ingress-Block der Komponenten.
* `serverName` muss ins Top-Level, nicht unter `synapse`.
* Keine `config:` Blöcke für Core-Komponenten.
### Issue 3: Let's Encrypt Error `403 Order's status is processing` auf der Hauptdomain
* **Ursache (Die ACME Race Condition):** Wenn `elementWeb` (auf `axion1337.chat`) und `wellKnownDelegation` (ebenfalls auf `axion1337.chat`) gleichzeitig aktiviert sind, fordert `cert-manager` zeitgleich zwei Zertifikate für dieselbe Domain an. Let's Encrypt blockt den zweiten Versuch und das Ingress-Setup hängt sich auf.
* **Lösung:** `wellKnownDelegation: enabled: false` im Helm-Chart setzen. Das `.well-known/matrix/server` File muss stattdessen entweder als statische JSON-Datei auf dem Webserver der Hauptdomain hinterlegt oder per Ingress-Route (Traefik Middleware) direkt auf den Synapse-Dienst umgebogen werden.
### Issue 4: Fehlende Zertifikate (`No resources found`)
* **Ursache:** Manuelle Kustomize-Patches kollidieren mit dem Helm-Chart.
* **Lösung:** Manuelle Patches löschen und das native Feature des Charts nutzen: `certManager: true` auf der obersten (Root-)Ebene der `values` setzen. Das Chart erstellt daraufhin die korrekten Ingress-Annotations und Secrets von selbst.

BIN
apps/.DS_Store vendored Normal file

Binary file not shown.

View File

@ -0,0 +1,24 @@
apiVersion: v1
kind: Secret
metadata:
name: authentik-credentials
namespace: authentik
stringData:
secret_key: ENC[AES256_GCM,data:yIyQapbFtFM11LynFtkV3ffExhaDfN9QHeFbI1T0xkIhgsV+9sjg3qwMVmeBlAe7xZl8gsAM4kDj2Q6O91OdDg==,iv:+Cl8vOcxG9/mgRheaCO0bLWyCJXN+f1F2DD3oeHbPFY=,tag:711ytyKf6/tmXomBLoffGA==,type:str]
pg-password: ENC[AES256_GCM,data:3w8R9mRjMXMJDLjrC8QYaXFHsCU3yYZs2PcaFQNp3Z4=,iv:G/aXgoGz3vBOzZ5K3Y+DDJsqer4F5gvcMmtkzRx93CU=,tag:dXPs1pY/APvnMlxdvB1EkA==,type:str]
smtp-password: ENC[AES256_GCM,data:JpMgaQFPkBzOg5WjvpmhM0kPwvZkH+4tQjT17RJHjG14WjmWtfG9Bg==,iv:zjQRLIlrxKv5hbd4JZowNUEiibiCUMf79acZY0+dYAc=,tag:ORPafTPyOQJvVvHWQGmqhA==,type:str]
sops:
age:
- recipient: age14l0hwfqylwpemz5y2ghh2yxk0phszlnj3qlejhue0fw0kz3tmfgqdsjzdh
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBRekJuZythYzliTFJ3RlhS
R2p6TG9NeFdabFlPRWtpNHJMYVVxTWZEcmlRClk0WUorSzdxNlcyWHYwWFBTMnlq
TlM4dENSSit2S3VGSzJCeTRTYU52dmcKLS0tIEF0WkV0M25OSEo1N0FEYXI5Q0Z6
QXVrY1NTeHZkeTlPRWNlVThzWno3T0kKC0KBoLT64GNqb8Ri9u69G7nqb1KftwwP
/24aVHrPxKi9d4ij9n3bvCYDF4rhtfexhrE4n7CfuKn2DcSiuTniuw==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2026-04-29T21:43:59Z"
mac: ENC[AES256_GCM,data:Y+dJppkaVZ5NOhlvwbbsF5+vDFqGUI1Ps8IcE4J7FIW4HIdMVf6RKM0EInvPUW1LaBlmelCitcE30w0As7ysNRhLY8yUDaKUvuU6mRejlNUIF8wAHzhciL2jTvAQsArHjybJatEig28+wM9VcY8JEa/d/CmuiB9Nq4WbIV+JXlA=,iv:UQj2rIVLNPjtYp3d/jRyNfJyyyUsZ3+NDCgpI4aztzc=,tag:cwiCzG/A+rfRFfLjXVt82w==,type:str]
encrypted_regex: ^(data|stringData)$
version: 3.12.2

View File

@ -0,0 +1,87 @@
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: authentik
namespace: authentik
spec:
interval: 1h
chart:
spec:
chart: authentik
version: "2026.2.3"
sourceRef:
kind: HelmRepository
name: goauthentik
namespace: flux-system
install:
remediation:
retries: 3
upgrade:
remediation:
retries: 3
valuesFrom:
- kind: Secret
name: authentik-credentials
valuesKey: secret_key
targetPath: authentik.secret_key
- kind: Secret
name: authentik-credentials
valuesKey: pg-password
targetPath: authentik.postgresql.password
- kind: Secret
name: authentik-credentials
valuesKey: pg-password
targetPath: postgresql.auth.password
- kind: Secret
name: authentik-credentials
valuesKey: smtp-password
targetPath: authentik.email.password
values:
global:
security:
allowInsecureImages: true
authentik:
log_level: info
error_reporting:
enabled: false
email:
host: smtp.ionos.de
port: 587
username: gamemaster@axion1337.chat
use_tls: true
from: "Authentik <gamemaster@axion1337.chat>"
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

View File

@ -0,0 +1,12 @@
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: auth-axion1337-chat-cert
namespace: authentik
spec:
secretName: auth-axion1337-chat-tls
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
dnsNames:
- auth.axion1337.chat

View File

@ -0,0 +1,8 @@
apiVersion: source.toolkit.fluxcd.io/v1
kind: HelmRepository
metadata:
name: goauthentik
namespace: flux-system
spec:
interval: 1h
url: https://charts.goauthentik.io

View File

@ -0,0 +1,16 @@
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: authentik
namespace: authentik
spec:
entryPoints:
- websecure
tls:
secretName: auth-axion1337-chat-tls
routes:
- match: Host(`auth.axion1337.chat`)
kind: Rule
services:
- name: authentik-server
port: 80

View File

@ -0,0 +1,9 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- namespace.yaml
- helm-repo.yaml
- authentik-secret.yaml
- certificate.yaml
- authentik.yaml
- ingress.yaml

View File

@ -0,0 +1,4 @@
apiVersion: v1
kind: Namespace
metadata:
name: authentik

BIN
apps/base/.DS_Store vendored Normal file

Binary file not shown.

View File

@ -0,0 +1,8 @@
apiVersion: source.toolkit.fluxcd.io/v1
kind: HelmRepository
metadata:
name: jetstack
namespace: flux-system
spec:
interval: 1h
url: https://charts.jetstack.io

View File

@ -0,0 +1,22 @@
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: cert-manager
namespace: cert-manager
spec:
releaseName: cert-manager
interval: 30m
chart:
spec:
chart: cert-manager
version: "v1.14.0" # Oder aktuellste stabile Version
sourceRef:
kind: HelmRepository
name: jetstack
namespace: flux-system
install:
createNamespace: true
remediation:
retries: 3
values:
installCRDs: true

View File

@ -0,0 +1,8 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ns-cert-manager.yaml
- ns-matrix.yaml
- cert-manager-repo.yaml
- cert-manager.yaml
- ../matrix/ess-repo.yaml

View File

@ -0,0 +1,4 @@
apiVersion: v1
kind: Namespace
metadata:
name: cert-manager

View File

@ -0,0 +1,4 @@
apiVersion: v1
kind: Namespace
metadata:
name: matrix

View File

@ -0,0 +1,17 @@
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: element-operator
namespace: matrix
spec:
releaseName: element-operator
interval: 30m
chart:
spec:
chart: element-operator
sourceRef:
kind: HelmRepository
name: element-charts
namespace: flux-system
install:
createNamespace: true

View File

@ -0,0 +1,9 @@
apiVersion: source.toolkit.fluxcd.io/v1
kind: HelmRepository
metadata:
name: element-ess-oci
namespace: flux-system
spec:
type: oci
url: oci://ghcr.io/element-hq/ess-helm
interval: 1h

View File

@ -0,0 +1,135 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: alloy-config
namespace: monitoring
data:
config.alloy: |
// Kubernetes pod discovery
discovery.kubernetes "k8s_pods" {
role = "pod"
}
// Relabel for Prometheus scrape
discovery.relabel "prometheus_pods" {
targets = discovery.kubernetes.k8s_pods.targets
rule {
source_labels = ["__meta_kubernetes_pod_annotation_prometheus_io_scrape"]
action = "keep"
regex = "true"
}
rule {
source_labels = ["__meta_kubernetes_pod_annotation_prometheus_io_path"]
action = "replace"
target_label = "__metrics_path__"
regex = "(.+)"
}
rule {
source_labels = ["__address__", "__meta_kubernetes_pod_annotation_prometheus_io_port"]
action = "replace"
regex = "([^:]+)(?::\\d+)?;(\\d+)"
replacement = "$1:$2"
target_label = "__address__"
}
rule {
source_labels = ["__meta_kubernetes_namespace"]
action = "replace"
target_label = "namespace"
}
rule {
source_labels = ["__meta_kubernetes_pod_name"]
action = "replace"
target_label = "pod"
}
}
// Scrape Flux controllers (flux-system namespace, port 8080)
discovery.kubernetes "flux_pods" {
role = "pod"
namespaces {
names = ["flux-system"]
}
}
discovery.relabel "flux_scrape" {
targets = discovery.kubernetes.flux_pods.targets
rule {
source_labels = ["__meta_kubernetes_pod_container_port_number"]
action = "keep"
regex = "8080"
}
rule {
source_labels = ["__meta_kubernetes_namespace"]
action = "replace"
target_label = "namespace"
}
rule {
source_labels = ["__meta_kubernetes_pod_name"]
action = "replace"
target_label = "pod"
}
}
// Scrape kube-state-metrics
prometheus.scrape "kube_state_metrics" {
targets = [{
__address__ = "kube-state-metrics.monitoring.svc.cluster.local:8080",
}]
forward_to = [prometheus.remote_write.selendis.receiver]
scrape_interval = "30s"
scrape_timeout = "10s"
}
// Scrape Flux controllers
prometheus.scrape "flux" {
targets = discovery.relabel.flux_scrape.output
forward_to = [prometheus.remote_write.selendis.receiver]
scrape_interval = "30s"
scrape_timeout = "10s"
job_name = "flux"
}
// Scrape node-exporter DaemonSet
prometheus.scrape "node_exporter" {
targets = [{
__address__ = "prometheus-node-exporter.monitoring.svc.cluster.local:9100",
}]
forward_to = [prometheus.remote_write.selendis.receiver]
scrape_interval = "30s"
scrape_timeout = "10s"
}
// Scrape Synapse metrics
prometheus.scrape "synapse" {
targets = [{
__address__ = "matrix-stack-synapse-main.matrix.svc.cluster.local:9000",
}]
forward_to = [prometheus.remote_write.selendis.receiver]
scrape_interval = "30s"
scrape_timeout = "10s"
}
// Kubernetes pod logs to Loki
loki.source.kubernetes "k8s_logs" {
targets = discovery.kubernetes.k8s_pods.targets
forward_to = [loki.write.selendis.receiver]
}
// Remote write to Selendis Prometheus
prometheus.remote_write "selendis" {
endpoint {
url = "http://10.0.0.3:9090/api/v1/write"
write_relabel_config {
source_labels = ["__name__"]
regex = "go_.*|process_.*"
action = "drop"
}
}
}
// Remote write logs to Selendis Loki
loki.write "selendis" {
endpoint {
url = "http://10.0.0.3:3100/loki/api/v1/push"
}
}

View File

@ -0,0 +1,33 @@
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: alloy
namespace: monitoring
spec:
interval: 1h
chart:
spec:
chart: alloy
version: "0.x"
sourceRef:
kind: HelmRepository
name: grafana
namespace: flux-system
values:
alloy:
configMap:
name: alloy-config
replicaCount: 1
serviceAccount:
create: true
name: alloy
rbac:
create: true
podAnnotations:
prometheus.io/scrape: "false"
resources:
limits:
memory: 512Mi
requests:
cpu: 100m
memory: 256Mi

View File

@ -0,0 +1,18 @@
apiVersion: source.toolkit.fluxcd.io/v1
kind: HelmRepository
metadata:
name: prometheus-community
namespace: flux-system
spec:
interval: 1h
url: https://prometheus-community.github.io/helm-charts
---
apiVersion: source.toolkit.fluxcd.io/v1
kind: HelmRepository
metadata:
name: grafana
namespace: flux-system
spec:
interval: 1h
url: https://grafana.github.io/helm-charts

View File

@ -0,0 +1,22 @@
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: kube-state-metrics
namespace: monitoring
spec:
interval: 1h
chart:
spec:
chart: kube-state-metrics
version: "5.x"
sourceRef:
kind: HelmRepository
name: prometheus-community
namespace: flux-system
values:
replicas: 1
service:
port: 8080
prometheus:
monitor:
enabled: false

View File

@ -0,0 +1,9 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- namespace.yaml
- helm-repos.yaml
- kube-state-metrics.yaml
- node-exporter.yaml
- alloy-config.yaml
- alloy.yaml

View File

@ -0,0 +1,4 @@
apiVersion: v1
kind: Namespace
metadata:
name: monitoring

View File

@ -0,0 +1,29 @@
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: prometheus-node-exporter
namespace: monitoring
spec:
interval: 1h
chart:
spec:
chart: prometheus-node-exporter
version: "4.x"
sourceRef:
kind: HelmRepository
name: prometheus-community
namespace: flux-system
values:
hostNetwork: true
hostPID: true
hostRootFsMount:
enabled: true
service:
port: 9100
targetPort: 9100
prometheus:
monitor:
enabled: false
tolerations:
- effect: NoSchedule
operator: Exists

View File

@ -0,0 +1,44 @@
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: axion-apex-tls
namespace: matrix
spec:
secretName: axion-apex-tls
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
dnsNames:
- axion1337.chat
---
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: axion-apex
namespace: matrix
spec:
entryPoints: [websecure]
tls:
secretName: axion-apex-tls
routes:
# Höchste Priorität: /.well-known/matrix/* -> wellKnownDelegation-Service
- match: Host(`axion1337.chat`) && PathPrefix(`/.well-known/matrix`)
kind: Rule
priority: 100
services:
- name: matrix-stack-well-known
port: 8010
# Element Desktop Setup Skripte
- match: Host(`axion1337.chat`) && PathPrefix(`/docs/setup`)
kind: Rule
priority: 50
services:
- name: element-web-docs
port: 80
# Niedrigere Priorität: alles andere -> Element Web
- match: Host(`axion1337.chat`)
kind: Rule
priority: 10
services:
- name: matrix-stack-element-web
port: 80

View File

@ -0,0 +1,14 @@
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: gamemaster@axion1337.de
privateKeySecretRef:
name: letsencrypt-prod-account-key
solvers:
- http01:
ingress:
class: traefik # K3s nutzt standardmäßig Traefik

View File

@ -0,0 +1,22 @@
apiVersion: v1
kind: Secret
metadata:
name: coturn-secret
namespace: matrix
stringData:
TURN_SECRET: ENC[AES256_GCM,data:SILIqMB+fmAMFITAL7lG1hOgICec6BJf1mOcK0gdmnCHWYqRuJv7jgjfGylG25xzQKi+zE7Qual9PnkZG2KiOA==,iv:+GZqLGusE4Q0x2jEEtFxj06rryyQmQhXdkTy4eE8ZHw=,tag:OpSZkinPTAi1ZKWyo8OX3A==,type:str]
sops:
age:
- recipient: age14l0hwfqylwpemz5y2ghh2yxk0phszlnj3qlejhue0fw0kz3tmfgqdsjzdh
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAyRk1mK3NWc1l4T0JCOFpF
S0RuQ3ViZmo3QTNVL2JvZ0hzMy91R2l0TEhzCk01a1VGdk1sdVg4aWswTzRibXI4
ZlJtNFF5MjBONEZOaWVpeU5taHl2bkEKLS0tIGxpUHY3NUFLWFBaWm1QSlZiVFkx
MEJleHFnd3oyT3VPL2dsYkpMUlRkOWMKcKUIgsQ/ff49pGGXMnYwJmwqPVC7woAR
IEzvhcNX97xx746SnrxZe5t2YadsYMkYIl0nvqBPJhSlvqMNafpQbQ==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2026-04-29T21:06:21Z"
mac: ENC[AES256_GCM,data:UhyR5m1HYWrZHwNLW5sg2PxbpaydWbP5cekghGlzSpQg7CYEcvZw3tJ/qB8zA19xZSM7tdSHOXdD+QytRq6qW59M1unqMaumA43B6JxQg1C1NdXAW0mkSc2WiNchvgpVii9P/TVlzSSIRwC3YGCQUsfa3SSfNzI4Z6fMuBnhYLE=,iv:4HYxbrYSRJLe1KcQ6q8bpee8/Lx1m3pPmisb/L2Mu64=,tag:l7n3u+Pg6533OzwtNUZvNw==,type:str]
encrypted_regex: ^(data|stringData)$
version: 3.12.2

162
apps/production/coturn.yaml Normal file
View File

@ -0,0 +1,162 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: coturn-config
namespace: matrix
data:
turnserver.conf: |
# TURN Server configuration
realm=axion1337.chat
# Listen ports
listening-port=3478
listening-ip=0.0.0.0
alt-listening-port=5349
alt-listening-ip=0.0.0.0
# External IPs (for clients behind NAT)
relay-ip=49.13.132.245
external-ip=49.13.132.245
# Relay port range
min-bps=0
bps-capacity=0
# Authentication
use-auth-secret
static-auth-secret=$TURN_SECRET
# HTTPS/TLS
cert=/etc/coturn/tls/tls.crt
pkey=/etc/coturn/tls/tls.key
# Performance tuning
max-bps=0
bps-capacity=0
log-file=stdout
# Logging
verbose
---
apiVersion: v1
kind: Service
metadata:
name: coturn
namespace: matrix
spec:
type: ClusterIP
ports:
- name: stun-udp
port: 3478
protocol: UDP
- name: stun-tcp
port: 3478
protocol: TCP
- name: turns-tcp
port: 5349
protocol: TCP
selector:
app: coturn
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: coturn
namespace: matrix
spec:
replicas: 1
selector:
matchLabels:
app: coturn
template:
metadata:
labels:
app: coturn
annotations:
prometheus.io/scrape: "false"
spec:
hostNetwork: true
dnsPolicy: ClusterFirstWithHostNet
initContainers:
- name: init-config
image: busybox:1.28
command:
- sh
- -c
- |
TURN_SECRET=$(cat /etc/coturn-secret/TURN_SECRET)
sed "s|\$TURN_SECRET|$TURN_SECRET|g" /etc/coturn-template/turnserver.conf > /etc/coturn/turnserver.conf
chmod 644 /etc/coturn/turnserver.conf
resources:
limits:
cpu: 100m
memory: 64Mi
requests:
cpu: 50m
memory: 32Mi
volumeMounts:
- name: config-template
mountPath: /etc/coturn-template
- name: config
mountPath: /etc/coturn
- name: secret
mountPath: /etc/coturn-secret
readOnly: true
containers:
- name: coturn
image: coturn/coturn:latest
imagePullPolicy: IfNotPresent
ports:
- name: stun-udp
containerPort: 3478
protocol: UDP
- name: stun-tcp
containerPort: 3478
protocol: TCP
- name: turns-tcp
containerPort: 5349
protocol: TCP
volumeMounts:
- name: config
mountPath: /etc/coturn
- name: tls
mountPath: /etc/coturn/tls
readOnly: true
resources:
limits:
cpu: 500m
memory: 256Mi
requests:
cpu: 100m
memory: 128Mi
livenessProbe:
exec:
command:
- /bin/sh
- -c
- "netstat -uln | grep 3478 || exit 1"
initialDelaySeconds: 30
periodSeconds: 10
volumes:
- name: config
emptyDir: {}
- name: config-template
configMap:
name: coturn-config
- name: secret
secret:
secretName: coturn-secret
defaultMode: 0400
- name: tls
secret:
secretName: turn-axion1337-chat-tls
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
preference:
matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- matrix

View File

@ -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"

View File

@ -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"
]
}
}
]
}
}

View File

@ -0,0 +1,22 @@
apiVersion: v1
kind: Secret
metadata:
name: ess-mas-values-secret
namespace: matrix
stringData:
values.yaml: ENC[AES256_GCM,data:B9nRZWCUnWxWhnlOTzSdOZrO+aiKwotKZhjYTwTBlgy9zVGoAn81yy6s2QJySeAI/rXcKamQ/P3fewDsLDzX8zxbvHS1GZ/F1fPx7H4tan9/1tu7XvNx52DWMCO8UtmpwNb1gAaa0kLbs+u1dTKA3Bk9b1gWsmP5OGo+C43knmhF3YvDOwv5wO96iaAzMwpjpxB78AkOvkshLEbP/o+z/DKsLxtoeQ0tPzkXjQj8d/2Jaj+3Ve8uqSNbnAEoL6gTDr+qvQs2JU/jvilK/fKgkR4hF54opthEMzKV7pbUYia2a+5yOmmQaBXQ5cUqi7wrTVyle/GyXHHOLAU37sGYhKyIGjgMu8rGTHMQgm3c5rGt4wLps48+LN4/S74tbTgyBf8V6hTGfNXIpMDkjXpPujZLzCKSx8V+7NlQDcs8FGobOLdmmcBzj3WGVMJLt/IA1/hv4xj4sP7CeQqG0UpWhJLXxY+ghCGdGJ7dA2wme+hreb1ywrhgVVnGH+plSG9BmTIye4sgCJwnZ9ER6T42ptsw70GaqfFazsgebeKEM+grJLfx+f4YyPexDaGZbrrZ5nxvOtvVz6d8Cx4zuP7jt+7RwzZiXPlYjsgvwwypuafIh/Uk0ULeFiSRLKLEGItRcOTdlVUbOOP9bW1Is42PzsnDnx0Mc0v4LzQCQTYJL3amfgOeGVJ1I8zS6okM8sZE3F0jzGvaFTNbeMiIkealUhjQrhF/5MweiNUIOUzAp/DLFT2mBg9g24imLtDCglEV0WwVQACd9GzQLNRnjNmaId7ISeYfUH/gORqyUJlICaRFB1xH+MMzy09tP2zuYcY+sIFJotYF29J0wC8oQS2KsKQiHEPVTLYTzDsB0RU4pGD6471rgIByXQrlmawo9HtjZxfZuzw7+AEa9uOBohbNkOwDUQGoLqgssAefAz4H/LC0NmBONR7iVzYcWv3RgDhsiafkUq6PgyXYEleB7Euk1wxX9mxUEqfuVMbeDceog2Q41vWBmFkFnSUYXHbN/Ff8DfWe+zjcSKDzjSchU54iYTbuuKnDo+G/jEk/FNGcp/xOh0/JyYWcqO5DozBPlIJ5QxgWkQTWC8oSM1QmTxiSj+FYDZFdyOXSXsqF8a9BJaEh5nACUbqwpx+UnRnJpcIcOlYasDwThNSNZIkpqqf2e7vsZHMTakkTbHOcWB7IiYWOCQLdzStQn3URWIobKn3T+mbbDerWzF2a9UEBbCwlTbA/OoyeWW4j1uPo29ek6F6Lb7JdxibNO6U5JIZkYP92Ci8W7skFAn7rEe203qnlvzoxhKxE+dA5rM+Tr4FWz9qAa/u3EhPxVvPK+0Wpb6HAUbFGbm+MSD7uqlcnvVxZfU2dSbpb/J56Bq8vzmJKcaStxoE4lDG+uwa2I/o0f1BMdwWvnmKEAJa593NYS9lw4s2dR3IwFkeE8hp3Q6yxaFk5v9m5/ivXOwiaOJ238HqDBJrfCQidIe+RaqydPxqqOj7WV1YZFU6DxZJ30LcyH45MJQqC5TGz2W1Unwljl3arh/hQRb1G3eGNw4kO86CipioKTeDBPpoKfgNFbz8qiNPSCRXPtJve+OOptDojLJ5iR+HO6olmSu+OGww7+BwTvBraW/xIAGq7y/gzZ8aMgsqIMyHJZiuC9+uQwquKiFEZ5pD5vAe0R9ROdFHgWsdKdEXwJtaCqBAfqWrZkrgoeWFsPYFwT3BhusQfMyWKv6SSOmWk2bx4FZ74+HeZcTsJWVcCmF4dbqfDvM5sz3b4Mek0wjUZGKtVyfFQ1VoZm6/G6UgCEt25A37AO11H/sW90Bs2nSpbOJFbgeBsNkXMptWrCyyHehof+JWeGuTFTg3etsaBgW4b1rJRzbJUQHJKVXjIOPZIQNd0WS7y+IDheDovQobfjSWn6TRVnXvEnzaD5WxY54EqOivAyv1U7/r7KMzo26Bp9s7ZQX/UeNmTn1dgkZoWNpTV8LkcrbRgIpkSNG9Pj+6GOtdUIxobtLeZ9h5Oso/TSKoDaHbkfoSVhW3FgQ7aJHHvx0j2qp/hVQfvABCPILW2DJvtNyIOU5zgV7yHJF/Dw7PpJfw636WxQHaTehbOav1ImE0FX0VF6NmkhuEpPEVHr0lejGse+0XkVzMuxoi/bE4h7WFIfbiqdlw=,iv:KnhgvxrFd+6BEBuBUKtQgKEfx81G9uJ+CY4Wrm43Pjw=,tag:1OQs0MQ1Gvf1LwXLaGwyjQ==,type:str]
sops:
age:
- recipient: age14l0hwfqylwpemz5y2ghh2yxk0phszlnj3qlejhue0fw0kz3tmfgqdsjzdh
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB2RHgzUE5QVVVCdkJ5aWNj
VmFHTUs2TXpXYzE2VmJnMHFNek1RT0FnUVVFCmJqRHUzY2F3WWt1L1FLbVpDWWFO
QzN1akRQdjBBVENIWHhGOGgrZFdqZGsKLS0tIEVRMGRrS05zS1ErdFZZUmZ4TUVv
U1dXMWltRlY1cGx2TFI4YjRreVBPQVkK9UFiAiSANa7HekQxufsFSkMQoL18kGmi
cP0jf27NGFpAjC8AmuMWgMydYDGXyRgFRU5JDqGCYAsgZsrGgjIWkA==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2026-05-15T09:05:44Z"
mac: ENC[AES256_GCM,data:gFPsb3LCjoPglcPEmLEe8hFQSsrcsZCMtVCf7L8jNLEgsL5XUIEF/BEoT7I+wPisRclAtq2qOkBd3TqmxQWAaPbRQ0+RDHU49XD5rWavDv7/CA1QUCLL/RNTbuURyS9iri/F4xneeYLwKJxJCgmMEiaqRPaAnHioxFtPreEiREg=,iv:FgWNZJUOydGY/m0SlZLWtWefIstMG7ccju6h8BLuVho=,tag:MMXoxQIA8ZNl5qBJjuzdpw==,type:str]
encrypted_regex: ^(data|stringData)$
version: 3.12.2

View File

@ -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

View File

@ -0,0 +1,84 @@
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: matrix-stack
namespace: matrix
spec:
interval: 5m
chart:
spec:
chart: matrix-stack
version: "26.4.0"
sourceRef:
kind: HelmRepository
name: element-ess-oci
namespace: flux-system
# NEU: Hier zieht Flux deine Puzzleteile zusammen
valuesFrom:
- kind: ConfigMap
name: ess-synapse-custom
valuesKey: values.yaml
- kind: ConfigMap
name: ess-element-custom
valuesKey: values.yaml
- kind: Secret
name: ess-mas-values-secret
valuesKey: values.yaml
values:
# Top-Level: serverName das ist dein Matrix-Homeserver-Name
serverName: axion1337.chat
# Cert-Manager für automatische Zertifikatsgenerierung
certManager:
clusterIssuer: letsencrypt-prod
# Interner Postgres an (default ist eh true, hier nur zur Klarheit)
postgres:
enabled: true
# Synapse API auf matrix.axion1337.chat
synapse:
enabled: true
ingress:
host: matrix.axion1337.chat
additional:
oembed:
config: |
oembed_enabled: true
# Matrix Authentication Service braucht eine Subdomain
matrixAuthenticationService:
enabled: true
ingress:
host: account.axion1337.chat
# Matrix RTC (Element Call) braucht auch eine Subdomain
matrixRTC:
enabled: true
ingress:
host: mrtc.axion1337.chat
# Element Web
elementWeb:
enabled: true
image:
registry: rohana.axion1337.de
repository: sorb/threadnet-web
tag: v0.1.0
ingress:
host: axion1337.chat
# Element Admin
elementAdmin:
enabled: true
ingress:
host: admin.axion1337.chat
# Well-Known auf der Apex-Domain (axion1337.chat/.well-known/matrix/*)
# Aktiviert notwendig für MatrixRTC-Discovery
wellKnownDelegation:
enabled: true
ingress:
className: "none" # Deaktiviert den Chart-Ingress, wir erstellen einen eigenen

View File

@ -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"

View 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;
}
}

View File

@ -0,0 +1,32 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
# Patch: Fügt einen Checksum der element-values.yaml zur HelmRelease hinzu
# Damit wird Flux die HelmRelease neu-synced wenn sich die ConfigMap ändert
patches:
- target:
kind: HelmRelease
name: matrix-stack
namespace: matrix
patch: |-
- op: add
path: /metadata/annotations/element-config-checksum
value: "401f8a87d0ef5d91d2e5032d4aede42c"
resources:
- matrix-postgres-auth.yaml
- cert-issuer.yaml
- matrix-certificates.yaml
# Neue Dateien:
- custom-configs/synapse-values.yaml
- custom-configs/element-values.yaml
- custom-configs/mas-secret.yaml
- element-web-docs-configmap.yaml
- element-web-docs-server.yaml
# TURN Server für WebRTC
- coturn-secret.yaml
- coturn.yaml
# HelmRelease (muss ganz unten stehen, damit die ConfigMaps vorher da sind!)
- element-server-suite.yaml
# Custom Apex Ingress für Element Web + Well-Known auf axion1337.chat
- apex-ingress.yaml # Custom Apex Ingress für Element Web + Well-Known auf axion1337.chat

View File

@ -0,0 +1,77 @@
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: matrix-axion1337-chat-cert
namespace: matrix
spec:
secretName: matrix-axion1337-chat-tls
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
dnsNames:
- matrix.axion1337.chat
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: account-axion1337-chat-cert
namespace: matrix
spec:
secretName: account-axion1337-chat-tls
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
dnsNames:
- account.axion1337.chat
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: mrtc-axion1337-chat-cert
namespace: matrix
spec:
secretName: mrtc-axion1337-chat-tls
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
dnsNames:
- mrtc.axion1337.chat
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: axion1337-chat-cert
namespace: matrix
spec:
secretName: axion1337-chat-tls
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
dnsNames:
- axion1337.chat
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: admin-axion1337-chat-cert
namespace: matrix
spec:
secretName: admin-axion1337-chat-tls
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
dnsNames:
- admin.axion1337.chat
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: turn-axion1337-chat-cert
namespace: matrix
spec:
secretName: turn-axion1337-chat-tls
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
dnsNames:
- turn.axion1337.chat

View File

@ -0,0 +1,22 @@
apiVersion: v1
data:
postgresql-password: ENC[AES256_GCM,data:euLWStKb/Bu+cgyvyxLAwZiLk5VjnHl+meZxcbZS1TahlbavOMEdlg==,iv:Gg2fW0vQ752tsm62n1r3Hzc/NRFBBMdGYOkbfV1jGIs=,tag:77pv1fEP2nkcEL1Dsx+hGQ==,type:str]
kind: Secret
metadata:
name: matrix-postgres-auth
namespace: matrix
sops:
age:
- recipient: age14l0hwfqylwpemz5y2ghh2yxk0phszlnj3qlejhue0fw0kz3tmfgqdsjzdh
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA1d3dKNGE1SGRabm1TaWhK
L2FBUHlWQWVaalRzS2FlTFJqNVNlT3JyUEZ3CmJPa1Mxa1VWSTZCTVlqRFk1WU5s
ZDJDc05obGtEdkV5cTFRa3dvbXJNM00KLS0tIHdCeHBLb090c3M4M2g5VlkzeStJ
ZGVhbnJYMHFjSDFPR1lWSmlBTXUzQUkK3ROf7Vu/SsPmNob/eKcPS7BwWUQrMXMW
YiuPlzNpT80nrtXRgm8AHbua6adV25eEVvFbroGvqkNtay8r5NUDkg==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2026-04-21T12:45:16Z"
mac: ENC[AES256_GCM,data:yTdP4L0ffQe3pc1gwbWgTSM+mbDgIxPajJDumvXwsM4eWJO+hlmH/nONIG+cnpVCOEDtGTm0CeJd342fU5qw8tL+1tS/OTp3FNgOv3Ms5o45crgaej4LY1JI1jCi5TAz0Kpt5YR15Aeh83OU7xig2fGbSAW5riPM/wqDHGPzBhM=,iv:MK1p9VEN6x3Q0y+YveDif2L5TPzvnvGTIUAY28AB0tM=,tag:1UM56b9Qg4WnggMQVThrMQ==,type:str]
encrypted_regex: ^(data|stringData)$
version: 3.12.2

BIN
clusters/.DS_Store vendored Normal file

Binary file not shown.

BIN
clusters/matrix/.DS_Store vendored Normal file

Binary file not shown.

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -1,7 +1,7 @@
# aXion1337.Chat Task List & Meilensteine # aXion1337.Chat Task List & Meilensteine
**Last Updated**: 2026-05-14 **Last Updated**: 2026-05-15
**Statusübersicht**: [✅ 6 Abgeschlossen] [🔄 1 In Progress] [📋 15+ Pending] [🔒 10 Security] **Statusübersicht**: [✅ 9 Abgeschlossen] [🔄 0 In Progress] [📋 11+ Pending] [🔒 10 Security]
--- ---
@ -9,10 +9,10 @@
| Kategorie | Count | Status | Details | | Kategorie | Count | Status | Details |
|-----------|-------|--------|---------| |-----------|-------|--------|---------|
| **Completed** | 6 | ✅ Done | K3S, Flux, ESS, Themes, Desktop, Monitoring, TURN | | **Completed** | 9 | ✅ Done | K3S, Flux, ESS, Themes, Desktop, Monitoring, TURN, Authentik, Firewall, SSH |
| **In Progress** | 1 | 🔄 Blocked | Authentik Stage 2 (awaiting manual config) | | **In Progress** | 0 | 🔄 — | — |
| **Backlog** | 15+ | 📋 Pending | Element Call Fork, DB Backups, NetworkPolicies, etc. | | **Backlog** | 11+ | 📋 Pending | DB Backups, E2E Test, Element Call Fork, PostgreSQL Migration, NetworkPolicies |
| **Security Tasks** | 10 | 🔒 Pending | Firewall, SSH, auditd, Kernel hardening, CrowdSec, Falco | | **Security Tasks** | 5 | 🔒 Pending | auditd, Kernel hardening, CrowdSec, Falco, WAF |
### Priority Distribution ### Priority Distribution
@ -28,35 +28,34 @@
## 🎯 Next Steps (Priorisiert) ## 🎯 Next Steps (Priorisiert)
### 🔴 **THIS WEEK CRITICAL** ### 🔴 **THIS WEEK CRITICAL**
1. **Authentik Stage 2 abschließen** 1. **Authentik Stage 2 abschließen**
- Manual: OIDC Provider + Application in Authentik UI erstellen - Manual: OIDC Provider + Application in Authentik UI erstellt
- Code: `upstream_oauth2_config` in `mas-secret.yaml` einfügen - Code: `upstream_oauth2_config` in `mas-secret.yaml` eingefügt
- Code: `passwords: enabled: false` aktivieren - Code: `passwords: enabled: false` aktiviert
- Commit: `enable-authentik-oidc-integration-in-mas` - ✅ Commit: `cdfbf7d` - Enable Authentik OIDC integration in MAS
- Est. Time: 12 hours - ✅ Verified: Login mit Authentik funktioniert (2026-05-15)
- Blocker: Manual Authentik config (user action) - **Status**: COMPLETE
2. **Hetzner Cloud Firewall Default-Deny Setup** 2. ✅ **Hetzner Cloud Firewall Optimierte Konfiguration**
- Ingress: Allow 80/443 only - ✅ Ingress: 80/443 + TURN/STUN + RTC Services
- Allow SSH from your IP or via WireGuard/Tailscale - ✅ SSH: Spezifische IPs (port 2248, nicht 22)
- Est. Time: 30 min - ✅ Default-Deny für nicht-definierte Traffic
- Cost: Free - **Status**: COMPLETE (optimiert über Plan)
- Impact: Blocks 99% of internet background noise
3. **SSH Hardening** 3. ✅ **SSH Hardening**
- Disable password auth (key-only) - ✅ PasswordAuthentication: no (key-only)
- Disable root login - ✅ PermitRootLogin: no (root disabled)
- MaxAuthTries 3 - ✅ MaxAuthTries: 3 (verified 2026-05-15)
- Est. Time: 12 hours - **Status**: COMPLETE
- Priority: HIGH
4. **Database Backup Strategy Decision & First Backup** 4. **Database Backup Strategy Decision & First Backup**
- Decision: CloudNativePG (on K3S) or Hetzner Postgres (managed)? - Decision: CloudNativePG (on K3S) or Hetzner Postgres (managed)?
- Setup: Daily automated backups - Setup: Daily automated backups
- Setup: Off-site storage (S3 / Storage Box) - Setup: Off-site storage (S3 / Storage Box)
- Setup: Monthly verified restores - Setup: Monthly verified restores
- Est. Time: 23 days - Est. Time: 23 days
- Priority: CRITICAL (disaster recovery) - Priority: CRITICAL (disaster recovery)
- **Status**: NEXT
### 🟠 **NEXT 12 WEEKS HIGH** ### 🟠 **NEXT 12 WEEKS HIGH**
1. **Authentik End-to-End Test** 1. **Authentik End-to-End Test**
@ -152,15 +151,18 @@
- Cert-Manager für TLS - Cert-Manager für TLS
- Commit: `deploy-authentik-as-identity-provider-for-matrix-stage-1` - Commit: `deploy-authentik-as-identity-provider-for-matrix-stage-1`
- Status: ✅ Deployed - Status: ✅ Deployed
- Manual: Admin-Passwort setzen + OIDC Provider erstellen (erforderlich) - Manual: Admin-Passwort + OIDC Provider + Application + Enrollment Flow erstellt ✅
🔄 **[IN PROGRESS] Authentik Stage 2 MAS Integration** - [x] **Authentik Stage 2 MAS Integration**
- [ ] **MAS Upstream OIDC Konfiguration** - ✅ Authentik Admin UI: OIDC Provider erstellt (Authentik)
- Client ID/Secret aus Authentik Admin UI kopieren - ✅ Authentik Admin UI: Application mit Slug `matrix` erstellt
- `upstream_oauth2_config` in `mas-secret.yaml` einfügen - ✅ Authentik Admin UI: Enrollment Flow mit Invitation Stage konfiguriert
- `passwords: enabled: false` - ✅ Client ID + Secret kopiert
- Commit: (pending) - ✅ MAS `upstream_oauth2_config` mit Client Credentials aktualisiert
- Status: ⏳ Wartet auf manuelle Authentik-Konfiguration - ✅ `passwords: enabled: false` aktiviert
- ✅ Commit: `cdfbf7d` - Enable Authentik OIDC integration in MAS
- ✅ Verified: Login mit Authentik funktioniert (2026-05-15)
- Status: ✅ Deployed & Verified
### Phase 6: Dokumentation ### Phase 6: Dokumentation
- [x] **Deployment Guides erstellen** - [x] **Deployment Guides erstellen**
@ -170,24 +172,39 @@
- Commit: `add-comprehensive-deployment-configuration-documentation` - Commit: `add-comprehensive-deployment-configuration-documentation`
- Status: ✅ Deployed - Status: ✅ Deployed
- [x] **Gitea Wiki erstellen**
- Home.md mit Navigation
- Alle Deployment Guides in Root
- Operations + Archive Dokumentation
- Wiki Branch gepusht zu rohana.axion1337.de
- Status: ✅ Live
- [x] **Gitea Issues & Project Board**
- 8 Issues erstellt (#3-#10): 4 CRITICAL + 4 HIGH
- Priority Labels: critical, high
- Area Labels: authentik, security, database, infrastructure, element
- Status: ✅ Tracking
### Phase 7: Infrastructure Security (Critical)
- [x] **Hetzner Cloud Firewall Configuration**
- SSH: Spezifische IPs (port 2248)
- HTTP/HTTPS: Any IPv4/IPv6
- TURN/STUN: WebRTC Ports
- RTC Services: SFU + Auth Ports
- Status: ✅ Optimiert & Deployed
- [x] **SSH Hardening**
- PasswordAuthentication: no (key-only)
- PermitRootLogin: no
- MaxAuthTries: 3
- Verified: 2026-05-15
- Status: ✅ Complete
--- ---
## 🔄 In Progress / Blocked ## 🔄 In Progress / Blocked
### Authentik Stage 2 MAS Integration (⏳ Depends on Manual Config) **None** Alle CRITICAL Tasks erledigt! Nächster Focus: Database Backups
**Beschreibung**: Authentik OIDC Provider muss manuell im Authentik Admin UI konfiguriert werden, bevor Stage 2 Deployment möglich ist.
**Schritte**:
1. ✅ Authentik Stage 1 Deployment (done)
2. ⏳ Authentik Admin UI: OIDC Provider erstellen (MANUAL - user action)
3. ⏳ Authentik Admin UI: Application mit Slug `matrix` erstellen (MANUAL - user action)
4. ⏳ Authentik Admin UI: Enrollment Flow mit Invitation Stage (MANUAL - user action)
5. ⏳ Authentik Admin UI: Client ID + Secret kopieren (MANUAL - user action)
6. 📋 MAS `upstream_oauth2_config` mit Client Credentials aktualisieren
7. 📋 `passwords: enabled: false` aktivieren
8. 📋 Commit + Push
**Blocker**: Manuelle Authentik-Konfiguration (wartet auf Benutzer)
--- ---

View File

@ -0,0 +1,256 @@
# 🆕 Authentik: Neuen Invitation Flow erstellen
**Problem**:
- Nur ein `matrix-enrollment` Flow existiert
- Wird für Standard-Signup + Invitations verwendet → Konflikt
- Fehler: "Found existing plan for other flow, deleting plan"
**Lösung**: Separaten `matrix-invitation` Flow für Einladungslinks erstellen.
---
## Schritt 1: Authentik Admin UI öffnen
```bash
kubectl port-forward -n authentik svc/authentik 9000:9000
# Browser: http://localhost:9000/
# Admin: akadmin / (password)
```
---
## Schritt 2: Neuen Flow erstellen
**Navigation**: Admin → Flows & Stages → Flows
1. Klick **"Create"** (oben rechts)
2. Fülle folgendes aus:
```
Name: matrix-invitation
Slug: matrix-invitation
Title: Matrix Enrollment via Invitation
Description: Enrollment flow for users created via invitation links
Designation: enrollment
```
3. **Speichern** (Save)
---
## Schritt 3: Stages zur Invitation Flow hinzufügen
Nach dem Erstellen wirst du auf die Flow-Edit-Seite weitergeleitet.
**Navigation**: Admin → Flows & Stages → Flows → `matrix-invitation` → Edit
Klick auf "Add Stage" und folge dieser Reihenfolge:
### Stage 1: Invite Stage (Invitation verarbeiten)
1. Klick **"Add Stage"**
2. Wähle: **"Invite Stage"**
3. Konfiguriere:
```
Name: Invite
Order: 1
```
4. **Save**
Dann musst du das Binding setzen:
- Klick auf die Stage in der Flow
- Binding: **"Invite"** (oder "Invitation")
- Required: **Yes**
- **Save**
### Stage 2: Identification Stage (Username überprüfen)
1. Klick **"Add Stage"**
2. Wähle: **"Identification Stage"** (nicht "Authenticate Stage")
3. Konfiguriere:
```
Name: Identification
Order: 2
User Fields: username (oder email)
Create Users as Inactive: NO
```
4. **Save**
Binding setzen:
- Binding: **"Identify"**
- Required: **No**
- **Save**
### Stage 3: Prompt Stage (Daten abfragen: Username, Email, Name)
1. Klick **"Add Stage"**
2. Wähle: **"Prompt Stage"**
3. Konfiguriere:
```
Name: User Data
Order: 3
```
4. **Speichern (Save)**
Dann **Fields hinzufügen**:
- Klick auf die Stage
- Klick **"Add Field"** für jedes Feld:
#### Field 1: Username
```
Field Name: username
Label: Username
Type: text
Required: Yes
Placeholder: Choose a username
```
#### Field 2: Email
```
Field Name: email
Label: Email Address
Type: email
Required: Yes
Placeholder: your@email.com
```
#### Field 3: Name (Optional)
```
Field Name: name
Label: Full Name
Type: text
Required: No
Placeholder: Your Name
```
Alle Fields **Save**.
Dann **Stage-Binding setzen**:
- Binding: **"Prompt for data"** (oder "User Data")
- Required: **Yes**
- **Save**
### Stage 4: Write Stage (User in DB erstellen)
1. Klick **"Add Stage"**
2. Wähle: **"Write Stage"** (oder "User Write Stage")
3. Konfiguriere:
```
Name: Create User
Order: 4
```
4. **Speichern (Save)**
Dann **Field Bindings setzen**:
- Klick auf die Stage
- Unter "Field Bindings" oder "User Creation":
- `username` ← mapped von username Feld
- `email` ← mapped von email Feld
- `name` ← mapped von name Feld
- **Save**
Stage-Binding setzen:
- Binding: **"Create or update user"**
- Required: **Yes**
- **Save**
### Stage 5: Finish Stage (Abschluss)
1. Klick **"Add Stage"**
2. Wähle: **"Finish Stage"** (oder "User Login")
3. Konfiguriere:
```
Name: Finish
Order: 5
```
4. **Speichern (Save)**
Stage-Binding:
- Binding: **"Finish"** (oder "Complete enrollment")
- Required: **Yes**
- **Save**
---
## Schritt 4: Flow als Standard-Invitation setzen
**Navigation**: Admin → System → Settings
Suche nach "Invitation Flow" oder "Default Flows":
1. Setze **"Invitation Flow"** auf `matrix-invitation`
2. **Save**
Alternativ:
- Admin → Flows & Stages → Flows
- Für jede Invitation/Group:
- Klick auf Group/Invitation
- Setze "Enrollment Flow" auf `matrix-invitation`
---
## Schritt 5: Test mit neuem Einladungslink
1. **Neuen Einladungslink erstellen**:
- Admin → Users & Groups → Invitations
- Klick **"Create"**
- Expiry: 7 days
- **Create & Copy Link**
2. **Link öffnen** (neuer Browser/Inkognito):
- Link in Browser öffnen
- Sollte jetzt alle Felder zeigen:
- [ ] Username eingeben
- [ ] Email eingeben ← sollte jetzt da sein!
- [ ] Name eingeben (optional)
- [ ] "Weiter" oder "Sign in with Authentik"
3. **Authentik Login** (falls Binding korrekt):
- Mit Authentik anmelden
- Enrollment abgeschlossen
- User sollte in Synapse erstellt sein
---
## Troubleshooting
### Fehler: "Stage not found"
- Stelle sicher, dass alle Stages ein **Binding** haben
- Alle Bindings müssen **eindeutig** sein (nicht doppelt)
- **Save** nach jeder Änderung
### Felder werden nicht angezeigt
- Prompt Stage überprüfen
- Alle Fields müssen **Save** sein
- Ggfs. Browser-Cache löschen
### Fehler nach Enrollment
- MAS Logs: `kubectl logs -f matrix-stack-matrix-authentication-service-6b994b9fcf-qqcxz -n matrix`
- Authentik Logs: `kubectl logs -f -n authentik -l app.kubernetes.io/name=authentik`
---
## Erwartetes Ergebnis
Nach dem Fix:
1. Einladungslink öffnen → `matrix-invitation` Flow
2. Username, Email, Name eingeben
3. "Mit Authentik anmelden"
4. Nach Login: User in Synapse erstellt
5. Login zu ElementWeb möglich
---
## Checkliste
- [ ] `matrix-invitation` Flow erstellt
- [ ] 5 Stages in korrekter Reihenfolge (Invite → Identify → Prompt → Write → Finish)
- [ ] Prompt Stage hat username, email, name Felder
- [ ] Alle Stages haben korrektes Binding
- [ ] `matrix-invitation` als Standard-Invitation-Flow gesetzt
- [ ] Neuen Einladungslink erstellt und getestet
- [ ] Test-User kann Email eingeben
- [ ] Test-User in Synapse DB nach Login
---
**Sollte ca. 10-15 Minuten dauern!** 🚀

View File

@ -0,0 +1,314 @@
# ✅ Authentik Enrollment Flow Reparatur-Template
Basierend auf häufigen Problemen: Hier sind die wahrscheinlichsten Fixes.
---
## Problem 1: MAS kennt Authentik-OIDC nicht
### Symptom
- MAS zeigt "Sign in with Authentik" Button nicht
- Logs: "upstream provider not configured"
### Lösung
**MAS Secret muss diesen Block enthalten:**
```yaml
# apps/production/custom-configs/mas-secret.yaml (decrypted)
---
matrixAuthenticationService:
upstream_oauth2_config:
issuer: "https://auth.axion1337.chat/application/o/matrix/"
client_id: "{{ CLIENT_ID_FROM_AUTHENTIK }}"
client_secret: "{{ CLIENT_SECRET_FROM_AUTHENTIK }}"
authorization_endpoint: "https://auth.axion1337.chat/application/o/authorize/"
token_endpoint: "https://auth.axion1337.chat/application/o/token/"
userinfo_endpoint: "https://auth.axion1337.chat/application/o/userinfo/"
scopes:
- "openid"
- "profile"
- "email"
user_mapping_provider:
type: "oidc"
config:
localpart_template: "{{ user.preferred_username }}"
display_name_template: "{{ user.name }}"
email_template: "{{ user.email }}"
# Wichtig: Password-Login deaktivieren (da wir nur OIDC verwenden)
passwords:
enabled: false
```
**Wie ausfüllen:**
1. Authentik Admin UI öffnen: `https://auth.axion1337.chat`
2. Admin → Applications → Providers → "matrix-provider" (oder ähnlich)
3. Folgende Werte kopieren:
- **Client ID**: Im Provider-Details zu sehen
- **Client Secret**: Im Provider-Details zu sehen (unter "Credentials")
4. Local entschlüsseln (mit age-key):
```bash
cd gitops/
sops -d -i apps/production/custom-configs/mas-secret.yaml
```
5. Datei öffnen und die Werte einfügen
6. Wieder verschlüsseln:
```bash
sops -e -i apps/production/custom-configs/mas-secret.yaml
```
7. Commiten:
```bash
git add apps/production/custom-configs/mas-secret.yaml
git commit -m "Fix: Configure MAS upstream OIDC for Authentik"
git push
```
8. Flux triggern:
```bash
flux reconcile kustomization production-apps --with-source
```
9. Warten, dass MAS Pod neu startet:
```bash
kubectl get pods -n matrix -l app=matrix-authentication-service -w
```
---
## Problem 2: Authentik Enrollment Flow ist kaputt
### Symptom
- User kommt zu Authentik-Login
- Nach Login: "Error: Enrollment stage not found" oder ähnlich
- Oder: Flow bricht ohne Fehlermeldung ab
### Lösung
**Authentik UI → Flows & Stages → Enrollment Flow überprüfen:**
```
Flows → Enrollment
├── Stage 1: "Identify" (if not exists)
│ └── Binding: "Identification (if not exists)"
├── Stage 2: "Write" (wichtig!)
│ └── Binding: "Create or update user"
│ └── User Creation Policies: ???
├── Stage 3: (optional) Weitere Datenerfassung
└── Stage 4: "Finish"
```
**Häufiger Fehler**: "Write" Stage ist nicht korrekt mit den benötigten Feldern konfiguriert.
**Fix:**
1. Authentik Admin UI: `https://auth.axion1337.chat`
2. **Flows & Stages** → **Stages**
3. Nach "Write" Stage suchen (Filter: "write")
4. Klick auf "Write Stage"
5. **Field Bindings** überprüfen:
- [ ] `username` ← MUSS mit Authentik Username bindbar sein
- [ ] `email` ← MUSS vorhanden sein
- [ ] `name` ← Optional, aber empfohlen
6. Alle sollten "required" sein (nicht optional)
7. **Save**
Dann zurück zu **Flows****Enrollment**:
1. Stages überprüfen (oben)
2. Bindings überprüfen:
- Each Stage hat "Binding" Feld
- "Write" Stage sollte mit "Create or update user" gebunden sein
3. **Save**
---
## Problem 3: OIDC Token werden nicht korrekt zu Synapse weitergeleitet
### Symptom
- User kommt bis zu ElementWeb
- Nach Login zu Authentik: "User not found in Synapse" oder Loop
- Oder: User wird in Authentik angelegt, aber nicht in Synapse
### Lösung
Das ist komplexer und erfordert MAS-Konfiguration + Synapse-Konfiguration.
**MAS Config (upstream_oauth2_config)** muss korrekt sein (siehe Problem 1).
**Zusätzlich in MAS Secret:**
```yaml
# apps/production/custom-configs/mas-secret.yaml
matrixAuthenticationService:
# ... upstream_oauth2_config ...
# User-Provisioning Konfiguration
access:
# Benutzer aus OIDC Provider automatisch erzeugen
auto_provision: true
# Synapse URL
home_server: "https://matrix.axion1337.chat"
```
---
## Problem 4: ElementWeb zeigt nicht automatisch OIDC-Button
### Symptom
- MAS läuft und hat OIDC konfiguriert
- ElementWeb zeigt nur "Sign in with username" oder "SAML login"
- Kein "Sign in with Authentik" Button
### Lösung
ElementWeb muss die MAS-Konfiguration kennen. Das geschieht über `.well-known/matrix/client`:
**Test:**
```bash
curl https://axion1337.chat/.well-known/matrix/client | jq '.authentication'
```
Sollte zurückgeben:
```json
{
"flows": [
{
"stages": ["m.login.sso"]
}
],
"identity_providers": [
{
"id": "authentik",
"name": "Authentik",
"icon": "...",
"brand": "custom"
}
]
}
```
Falls nicht: **ElementWeb Config in Element-Stack aktualisieren:**
```yaml
# apps/production/custom-configs/element-values.yaml
elementWeb:
config:
auth:
sso_redirect_options:
immediate: false
on_welcome_page: true
```
**Dann:**
```bash
git add apps/production/custom-configs/element-values.yaml
git commit -m "Fix: Enable SSO redirect in ElementWeb"
git push
flux reconcile kustomization production-apps --with-source
```
---
## Problem 5: User kann sich anmelden, aber Boje ist nicht in Matrix
### Symptom
- Authentik-Login funktioniert
- MAS zeigt User erfolgreich
- ElementWeb Login funktioniert
- **ABER**: User ist nicht in Synapse (check mit `kubectl exec -n matrix synapse ...`)
### Lösung
Das bedeutet: User wird nicht automatisch in Synapse provisioniert.
**MAS muss User zu Synapse erstellen:**
In MAS Secret, `access` Sektion:
```yaml
matrixAuthenticationService:
access:
# Synapse erlaubt neue User-Erstellung
homeserver: "https://matrix.axion1337.chat"
# Optional: User automatisch erstellen
registration_enabled: true
```
**Synapse-seitig** muss auch User-Erstellung erlaubt sein:
```yaml
# apps/production/custom-configs/synapse-values.yaml
synapse:
additional:
registration:
config: |
enable_registration: true
enable_registration_without_token: false
# Token wird von MAS bereitgestellt
```
---
## Komplettes Test-Szenario
Nach allen Fixes:
1. **Browser 1**: `https://axion1337.chat` öffnen
2. Auf ElementWeb "Sign in" klicken
3. "Sign in with Authentik" klicken (sollte sichtbar sein)
4. Authentik-Login durchführen (akadmin)
5. Nach Login: Enrollment-Flow (nur falls neuer User)
6. Zurück zu ElementWeb
7. **Synapse prüfen**:
```bash
kubectl exec -it -n matrix deployment/synapse -- \
/usr/local/bin/psql -U synapse -d synapse -c "SELECT name, admin FROM users LIMIT 10;"
```
8. Neuer User sollte auftauchen
---
## Debugging-Commands (während Test)
```bash
# Live MAS logs (folgen)
kubectl logs -n matrix -l app=matrix-authentication-service -f
# Authentik logs
kubectl logs -n authentik -l app.kubernetes.io/name=authentik -f
# Port-Forward für manuelles Testen
kubectl port-forward -n matrix svc/matrix-authentication-service 8765:8080
# MAS Secret auslesen (im Cluster)
kubectl get secret ess-mas-values-secret -n matrix -o jsonpath='{.data.values\.yaml}' | base64 -d
# Synapse User-Liste
kubectl exec -it -n matrix deployment/synapse -- \
/usr/local/bin/psql -U synapse -d synapse -c "SELECT name, admin, is_guest FROM users;"
```
---
## Checkliste für erfolgreichen Fix
- [ ] MAS Secret decrypted, upstream_oauth2_config eingetragen
- [ ] Client ID + Secret von Authentik kopiert
- [ ] MAS Secret re-encrypted und commited
- [ ] Authentik Enrollment Flow überprüft
- [ ] OIDC Provider in Authentik aktiv
- [ ] OIDC Application "matrix" in Authentik existiert
- [ ] MAS Pod neu gestartet (nach Secret-Change)
- [ ] ElementWeb zeigt OIDC-Button
- [ ] Test-Login durchgeführt
- [ ] Neuer User in Synapse DB vorhanden
---
**Fragen?** → Siehe `DIAGNOSTIK-AUTHENTIK-FLOW.md` für tiefergehende Diagnose.

View File

@ -0,0 +1,244 @@
# 🔧 Authentik Invitation Flow Fix Für Einladungslinks
**Problem**:
- Standard Enrollment (akadmin): ✅ funktioniert
- Invitation Flow (Boje über Einladungslink): ❌ Nur Username gefragt, keine Email
- Nach Enrollment: "Fehler fehlende Rechte"
**Root Cause**: Invitation Flow erfasst nicht alle erforderlichen Felder (Email) für OIDC-Token-Generation.
---
## Phase 1: Diagnose im Authentik Admin UI
```bash
# Authentik Admin UI öffnen
kubectl port-forward -n authentik svc/authentik 9000:9000
# Browser: http://localhost:9000/
# Admin credentials: akadmin / (password)
```
### 1.1 Überprüfe: Welche Flows existieren?
**Navigation**: Admin → Flows & Stages → Flows
Suche nach diesen Flows:
- [ ] `enrollment` Standard Enrollment (für akadmin)
- [ ] `invitation` Invitation Flow (für Einladungslinks)
- [ ] `default-authentication-flow` Standard Login
### 1.2 Überprüfe: Standard Enrollment Flow (funktioniert)
**Navigation**: Flows → `enrollment` öffnen
**Stages sollten sein:**
```
1. Identify (if not exists)
└─ Binding: "Identification (if not exists)"
2. Write
└─ Binding: "Create or update user"
└─ Field bindings MUST include:
├─ username
├─ email ← WICHTIG
└─ name (optional)
3. (optional) Weitere Stages
4. Finish
```
**Wichtig**: Alle Felder müssen "required" sein (nicht optional).
### 1.3 Überprüfe: Invitation Flow (wahrscheinlich kaputt)
**Navigation**: Flows → `invitation` öffnen
**Problem**: Wahrscheinlich fehlt die "Email" Stage hier!
**Sollte sein:**
```
1. Invite Stage
└─ Binding: "Invite user"
2. Identification (if not exists)
└─ Binding: "Identify"
3. Prompt Stage (für zusätzliche Daten!)
└─ Binding: "Prompt for data"
└─ Fields: username, email, name, password
4. Write
└─ Binding: "Create or update user"
5. Finish
```
---
## Phase 2: Reparatur der Invitation Flow
### Schritt 1: Neue "Prompt Stage" erstellen (falls nicht existiert)
**Navigation**: Admin → Flows & Stages → Stages
1. Klick "Create"
2. Name: `invitation-prompt` oder ähnlich
3. Type: **"Prompt Stage"**
4. Configure:
- [ ] **Fields to Prompt**:
- Username (required)
- Email (required) ← WICHTIG
- Name (optional)
- Password (optional, da OIDC)
5. Save
### Schritt 2: Invitation Flow reparieren
**Navigation**: Admin → Flows & Stages → Flows → `invitation`
**Stages in dieser Reihenfolge:**
```
Stage 1: Invite Stage
├─ Binding: "Invite"
├─ Required: Yes
Stage 2: Identification Stage
├─ Binding: "Identify" (oder "Identification (if not exists)")
├─ Required: No
Stage 3: [NEUE STAGE] Prompt für Email/Username
├─ Type: "Prompt Stage"
├─ Binding: "Prompt for data"
├─ Fields:
│ ├─ username (required)
│ ├─ email (required) ← ENTSCHEIDEND
│ └─ name (optional)
├─ Required: Yes
Stage 4: Write
├─ Binding: "Create or update user"
├─ User Creation Policies: (standard)
├─ Required: Yes
Stage 5: Finish
├─ Binding: "Finish"
├─ Required: Yes
```
**Speichern** und Testen!
---
## Phase 3: Teste Invitation Flow
### Test 1: Neuen Einladungslink erstellen
**Navigation**: Admin → Users & Groups → Invitations
1. Klick "Create"
2. Expiry: 7 days
3. Create & Copy Link
### Test 2: Einladungslink öffnen (in neuem Browser/Inkognito)
1. Link öffnen
2. Enrollment Flow sollte jetzt:
- [ ] Username eingeben
- [ ] **Email eingeben** ← Das sollte jetzt da sein!
- [ ] Name eingeben (optional)
- [ ] "Sign in with Authentik" klicken (falls Authentik-Binding korrekt)
3. Nach Authentik-Login: User in Synapse erstellt?
```bash
kubectl exec -it -n matrix matrix-stack-postgres-0 -- \
psql -U synapse -d synapse -c "SELECT name FROM users WHERE created_ts > now() - interval '5 minutes';"
```
### Test 3: Prüfe MAS Logs auf Fehler
```bash
kubectl logs -f matrix-stack-matrix-authentication-service-6b994b9fcf-qqcxz -n matrix | grep -i "error\|fail\|boje"
```
---
## Phase 4: Häufige Fehler & Lösungen
### Fehler 1: "Fehlende Rechte" nach Enrollment
**Symptom**: Enrollment abgeschlossen, aber Fehler auf Berechtigungsseite
**Ursachen**:
- [ ] Email-Feld wurde nicht erfasst
- [ ] OIDC-Token hat unvollständige Daten
- [ ] Synapse User konnte nicht erstellt werden (Duplikat?)
**Lösung**:
1. Authentik Logs prüfen: `kubectl logs -n authentik -l app.kubernetes.io/name=authentik -f | grep -i "error\|invitation"`
2. MAS Logs prüfen: `kubectl logs -f matrix-stack-matrix-authentication-service-6b994b9fcf-qqcxz -n matrix | grep -i "boje\|error"`
3. Synapse Logs prüfen: `kubectl logs -f -n matrix matrix-stack-synapse-0 | grep -i "boje\|register"`
### Fehler 2: "Stage not found" oder "Flow invalid"
**Ursache**: Invitation Flow hat Binding-Fehler
**Lösung**:
1. Admin UI → Flows → Invitation Flow öffnen
2. Alle Stages überprüfen, dass sie korrekt gebunden sind
3. Keine leeren/ungültigen Bindings
4. Save & Retry
### Fehler 3: Email-Feld wird nicht angezeigt
**Ursache**: Prompt Stage hat email nicht in Fields
**Lösung**:
1. Admin UI → Flows → Stages → Prompt Stage öffnen
2. Edit → Fields überprüfen
3. Email hinzufügen if missing:
- Field name: `email`
- Type: `email`
- Required: Yes
4. Save
---
## Erwarteter Ablauf nach Fix
1. Browser öffnet Einladungslink → Enrollment Flow
2. "Username eingeben" → z.B. "boje"
3. **"Email eingeben"** ← Sollte jetzt da sein
4. "Name eingeben" (optional)
5. "Weiter" oder "Mit Authentik anmelden"
6. Authentik Login
7. Enrollment abgeschlossen
8. User "boje" in Synapse DB angelegt
9. Login zu ElementWeb möglich
---
## Checkliste
- [ ] Authentik Admin UI geöffnet (port-forward 9000)
- [ ] Standard Enrollment Flow überprüft (funktioniert mit akadmin)
- [ ] Invitation Flow überprüft
- [ ] Prompt Stage existiert mit email field
- [ ] Invitation Flow hat alle 5 Stages in korrekter Reihenfolge
- [ ] Neuen Einladungslink erstellt und getestet
- [ ] Test-User hat Email eingeben können
- [ ] Test-User in Synapse DB nach Login
- [ ] MAS Logs zeigen keine Fehler
---
**Frage**: Stimmt das mit deiner Beobachtung überein - dass bei der Einladung **nur Username** gefragt wurde, aber **nicht die Email**?
Wenn ja, dann ist der Fix:
1. Prompt Stage erstellen/reparieren (mit email field)
2. Zur Invitation Flow hinzufügen
3. Testen
Soll ich dir noch mehr Detailschritte geben?

View File

@ -0,0 +1,261 @@
# 🔍 Authentik Enrollment Flow Diagnostik & Reparaturplan
**Symptom**:
- `akadmin` wurde manuell in Matrix-DB angelegt (funktioniert)
- `Boje` wurde nur in Authentik erstellt, nicht in Matrix-DB (kaputt)
- Beide sollen denselben Enrollment Flow verwenden
**Vermutung**: Die Authentik → MAS → Synapse Kette ist unterbrochen
---
## Phase 1: Diagnose (Bestandsaufnahme)
### 1.1 Authentik Logs prüfen
```bash
# Authentik Server logs
kubectl logs -n authentik -l app.kubernetes.io/name=authentik -f | grep -i "oauth\|oidc\|enroll\|flow"
# Worker logs
kubectl logs -n authentik -l app.kubernetes.io/component=worker -f | grep -i "enroll"
```
**Worauf achten**:
- Fehler bei "Enrollment create"?
- OIDC Token-Probleme?
- Flow-Validierungsfehler?
### 1.2 MAS (Matrix Authentication Service) Logs prüfen
```bash
# MAS logs
kubectl logs -n matrix -l app=matrix-authentication-service -f | grep -i "oauth\|upstream\|user\|oidc"
```
**Worauf achten**:
- Verbindung zu Authentik erfolgreich?
- Token-Validierung fehlgeschlagen?
- User-Provisioning-Fehler?
### 1.3 Synapse Logs prüfen
```bash
# Synapse logs (für User-Erstellung)
kubectl logs -n matrix -l app=synapse -f | grep -i "user\|provision\|register\|auth"
```
**Worauf achten**:
- User-Registrierungs-Fehler?
- Provisioning-Fehler?
- Authentifizierungsprobleme?
---
## Phase 2: Authentik UI Überprüfung
### 2.1 Enrollment Flow inspizieren
```bash
# Authentik UI öffnen
kubectl port-forward -n authentik svc/authentik 9000:9000
# → Browser: http://localhost:9000
# → Admin UI → Flows & Stages → "Enrollment" Flow suchen
```
**Checklist**:
- [ ] Flow existiert und heißt "Enrollment"
- [ ] Reihenfolge der Stages:
1. Identify (optional)
2. Write (User-Erstellung)
3. Enrollment (if-condition für neuen User)
4. Verification (optional)
- [ ] "Write Stage" bindet sich an:
- [ ] Username
- [ ] Email
- [ ] Name
- [ ] Alle Bindings sind "required" (nicht optional)
### 2.2 OIDC Provider in Authentik prüfen
```bash
# Über Authentik UI:
# Admin → Applications → Providers → "matrix-provider" (oder ähnlich)
```
**Checklist**:
- [ ] Provider existiert
- [ ] Name: z.B. "matrix-provider"
- [ ] Client Type: "Confidential"
- [ ] Redirect URIs enthalten:
- [ ] `https://account.axion1337.chat/upstream/callback/*`
- [ ] `https://account.axion1337.chat/upstream/callback/01KQDJTR1ZVTG8JQ220F5BNBFZ` (exact)
- [ ] Scopes: `openid profile email`
- [ ] Client ID + Secret kopiert?
### 2.3 OIDC Application in Authentik
```bash
# Admin → Applications → Applications → "matrix"
```
**Checklist**:
- [ ] Application existiert mit Slug "matrix"
- [ ] Provider ist zugewiesen
- [ ] Enrollment Flow ist zugewiesen (nicht "deny")
- [ ] Enrollment Flow ist die richtige (die von oben)
### 2.4 Test-User "Boje" inspizieren
```bash
# Admin → Directory → Users → "Boje"
```
**Checklist**:
- [ ] Username: `boje`
- [ ] Email: `boje@...` (vorhanden?)
- [ ] Groups: Falls erforderlich, die richtigen Groups zugewiesen?
- [ ] Status: Active oder Disabled?
- [ ] Sessions: Aktive Logins?
---
## Phase 3: MAS Konfiguration überprüfen
### 3.1 Current MAS Secret auslesen
Da der age-key nicht lokal verfügbar ist, müssen wir die Konfiguration im Cluster prüfen:
```bash
# MAS Config im Cluster auslesen (nicht verschlüsselt)
kubectl get secret ess-mas-values-secret -n matrix -o jsonpath='{.data.values\.yaml}' | base64 -d | yq . | head -100
```
**Worauf achten**:
- [ ] `upstream_oauth2_config` Block existiert
- [ ] `upstream_oauth2_config.issuer`: `https://auth.axion1337.chat/application/o/matrix/`
- [ ] `upstream_oauth2_config.client_id`: Authentik Client ID
- [ ] `upstream_oauth2_config.client_secret`: Authentik Client Secret (*)
- [ ] `upstream_oauth2_config.scopes`: `["openid", "profile", "email"]`
- [ ] `upstream_oauth2_config.user_mapping_provider`:
- `type`: "oidc"
- `config.localpart_template`: `{{ user.preferred_username }}`
- `config.display_name_template`: `{{ user.name }}`
- `config.email_template`: `{{ user.email }}`
### 3.2 MAS Pod exec Config live prüfen
```bash
# MAS Config im laufenden Pod inspizieren
kubectl exec -it -n matrix deployment/matrix-authentication-service -- cat /etc/mas/config.yaml | grep -A50 upstream_oauth2_config
```
**Worauf achten**:
- Config ist syntaktisch korrekt (YAML)?
- Indentierung ist richtig?
- Werte sind vorhanden?
### 3.3 MAS OIDC Discovery prüfen
```bash
# Authentik OIDC Discovery Endpoint
curl -s https://auth.axion1337.chat/application/o/matrix/.well-known/openid-configuration | jq .
# Sollte zurückgeben:
# {
# "issuer": "https://auth.axion1337.chat/application/o/matrix/",
# "token_endpoint": "https://auth.axion1337.chat/application/o/token/",
# "authorization_endpoint": "...",
# ...
# }
```
---
## Phase 4: Login-Flow Testen
### 4.1 MAS Login UI öffnen
```bash
# Port-Forward zu MAS
kubectl port-forward -n matrix svc/matrix-authentication-service 8765:8080
# → Browser: http://localhost:8765
```
**Test**:
1. Auf MAS-Seite: "Sign in with Authentik" klicken
2. Authentik-Login durchführen
3. Auf Enrollment Flow warten
4. Neuen User erstellen (Test-Username, Email, Password)
5. Nach erfolgreicher Registrierung: Matrix home_server erhalten?
### 4.2 Fehlerberichte
Falls Fehler auftritt:
- [ ] Screenshot des Fehlers
- [ ] MAS logs auslesen: `kubectl logs -n matrix -l app=matrix-authentication-service --tail=50`
- [ ] Authentik logs auslesen: `kubectl logs -n authentik -l app.kubernetes.io/name=authentik --tail=50`
---
## Phase 5: Reparaturschritte (nachdem Diagnose klar ist)
### Falls MAS-Config fehlerhaft:
```bash
# 1. Secrets entschlüsseln (lokale Umgebung mit age-key erforderlich)
sops -d apps/production/custom-configs/mas-secret.yaml > /tmp/mas-secret-decrypted.yaml
# 2. Editor öffnen und Konfiguration reparieren
vim /tmp/mas-secret-decrypted.yaml
# → upstream_oauth2_config überprüfen und korrigieren
# 3. Wieder verschlüsseln
sops -e /tmp/mas-secret-decrypted.yaml > apps/production/custom-configs/mas-secret.yaml
# 4. Commiten
git add apps/production/custom-configs/mas-secret.yaml
git commit -m "Fix: Correct MAS upstream_oauth2_config for Authentik integration"
# 5. Flux triggern
flux reconcile kustomization production-apps --with-source
```
### Falls Authentik Enrollment Flow fehlerhaft:
1. Admin UI öffnen: `kubectl port-forward -n authentik svc/authentik 9000:9000`
2. Flows → Enrollment Flow öffnen
3. Stages überprüfen und in richtige Reihenfolge bringen:
- **Identify**: Benutzer identifizieren
- **Write**: Benutzer in DB speichern
- **Enrollment**: Weitere Felder (optional)
- **Finish**: Abschluss
4. Speichern
5. Neuen Test-User erstellen und Enrollment durchlaufen
---
## Erwartete Endergebnisse
Nach erfolgreichem Fix:
1. Benutzer klickt "Sign in with Authentik" auf MAS
2. Authentik-Login-Seite wird angezeigt
3. Nach Login: Enrollment Flow wird angezeigt
4. User füllt Formular aus
5. Nach "Finish": Authentik erstellt User UND verbindet zu Matrix
6. User wird in Matrix-DB angelegt (`_matrix_auth` prefix)
7. User kan sich bei ElementWeb anmelden
---
## 🎯 Nächster Schritt
Bitte folgende Diagnostik durchlaufen und mir die Output berichte:
1. **MAS Logs** (letzten 30 Zeilen)
2. **Authentik Logs** (letzten 30 Zeilen)
3. **MAS Secret (entschlüsselt)** upstream_oauth2_config Block
4. **Authentik OIDC Discovery** Output
5. **Screenshots** der Authentik UI (Enrollment Flow, OIDC Provider, Application)
Damit kann ich dann genau sehen, wo der Bruch in der Kette ist! 🔗

View File

@ -0,0 +1,112 @@
# 🔧 Troubleshooting Guides
Dieser Ordner enthält detaillierte Troubleshooting- und Reparaturanleitungen für häufige Probleme bei der Authentik/MAS/Matrix Integration.
---
## 📖 Guides
### 1. **DIAGNOSTIK-AUTHENTIK-FLOW.md**
**Für**: Vollständige Diagnose des Authentik Enrollment Flows
**Wann**: Wenn Sie systematisch überprüfen möchten, ob die gesamte OIDC-Kette (Authentik → MAS → Synapse) funktioniert
**Umfasst**:
- Authentik Logs analysieren
- MAS Konfiguration überprüfen
- OIDC Discovery testen
- Enrollment Flow inspizieren
- Fehlersuche mit Debugging-Commands
**Status**: Nutzer Boje - Nur in Authentik erstellt, nicht in Synapse
---
### 2. **AUTHENTIK-FIX-TEMPLATE.md**
**Für**: Konkrete Reparaturen bei MAS/Authentik Integration
**Wann**: Wenn Sie wissen, welches Problem Sie haben und schnelle Lösungen suchen
**Behandelt**:
- Problem 1: MAS kennt Authentik-OIDC nicht
- Problem 2: Authentik Enrollment Flow kaputt
- Problem 3: OIDC Token werden nicht weitergeleitet
- Problem 4: ElementWeb zeigt keinen OIDC-Button
- Problem 5: User in Authentik aber nicht in Synapse
**Status**: Best Practices für häufige Probleme
---
### 3. **AUTHENTIK-INVITATION-FLOW-FIX.md**
**Für**: Reparatur des Invitation Flows bei Einladungslinks
**Wann**: Wenn Nutzer via Einladungslink nicht korrekt erstellt werden
**Problem**: Invitation Flow erfasst nur Username, nicht Email/Name
**Lösung**:
- Prompt Stage mit Email-Feld erstellen/reparieren
- Zur Invitation Flow hinzufügen
- Testen mit neuem Einladungslink
**Status**: Nutzer Klaus - Fehler "kein ausstehender benutzer Anfrage wurde verweigert"
---
### 4. **AUTHENTIK-CREATE-INVITATION-FLOW.md** ⭐ **WICHTIGSTE ANLEITUNG**
**Für**: Neuen separaten Invitation Flow erstellen
**Wann**: Wenn nur ein `matrix-enrollment` Flow existiert (Standard + Invitations gemeinsam)
**Root Cause**: Flow-Konflikt durch gemeinsamen Flow
**Lösung** (Schritt-für-Schritt):
1. Neuen Flow `matrix-invitation` erstellen
2. 5 Stages konfigurieren (Invite → Identify → Prompt → Write → Finish)
3. Email-Feld in Prompt Stage hinzufügen
4. Invitations auf neuen Flow setzen
5. Testen
**Zeitaufwand**: ~20 Minuten
**Status**: Aktuell für Klaus/Boje notwendig
---
## 🎯 Schneller Einstieg
### Szenario 1: "Enrollment funktioniert nicht, ich weiß nicht warum"
**Start**: `DIAGNOSTIK-AUTHENTIK-FLOW.md`
### Szenario 2: "Einladungslink funktioniert nicht"
**Start**: `AUTHENTIK-CREATE-INVITATION-FLOW.md` (wenn nur ein Flow existiert)
→ oder `AUTHENTIK-INVITATION-FLOW-FIX.md` (wenn zwei Flows existieren)
### Szenario 3: "Ich kenne das Problem und brauche Lösungen"
**Start**: `AUTHENTIK-FIX-TEMPLATE.md`
### Szenario 4: "Alles ist kaputt, ich brauche alles Schritt-für-Schritt"
**Start**: `AUTHENTIK-CREATE-INVITATION-FLOW.md``AUTHENTIK-FIX-TEMPLATE.md``DIAGNOSTIK-AUTHENTIK-FLOW.md`
---
## 📋 Bekannte Probleme
| Problem | Nutzer | Guide | Status |
|---------|--------|-------|--------|
| Nur Standard Enrollment funktioniert | akadmin ✅ | - | Resolved |
| User nur in Authentik, nicht in Synapse | Boje | `DIAGNOSTIK-AUTHENTIK-FLOW.md` | In Progress |
| Einladungslink-Fehler: "kein ausstehender benutzer" | Klaus | `AUTHENTIK-CREATE-INVITATION-FLOW.md` | In Progress |
| OIDC-Integration unklar | General | `AUTHENTIK-FIX-TEMPLATE.md` | Reference |
---
## 🔗 Verwandte Dokumentation
- `../README.md` Hauptdokumentation & Architektur
- `../TASKS.md` Aufgabenliste & Meilensteine
- `../deployment-guides/` Deployment-Anleitungen für andere Components
---
## 💡 Tipps
- **Immer Logs überprüfen** bevor man herumrät: `kubectl logs -f -n <namespace> <pod>`
- **Browser-Cache löschen** nach Authentik-Änderungen
- **Port-Forward nutzen** für lokales Testen: `kubectl port-forward -n authentik svc/authentik 9000:9000`
- **Kleine Tests machen** (einen User mit Invitation testen, nicht 10 auf einmal)
---
**Zuletzt aktualisiert**: 2026-05-18
**Verfasser**: Claude Code + Thore

41
scripts/hooks/pre-commit Executable file
View File

@ -0,0 +1,41 @@
#!/usr/bin/env bash
# GitOps ConfigMap Checksum Hook
# Automatically updates checksum annotations in kustomization.yaml when ConfigMaps change.
# This ensures Flux CD re-deploys the HelmRelease when external ConfigMap sources are modified.
#
# See: docs/ops-configmap-sync.md
set -euo pipefail
REPO_ROOT="$(git rev-parse --show-toplevel)"
ELEMENT_VALUES="$REPO_ROOT/apps/production/custom-configs/element-values.yaml"
SYNAPSE_VALUES="$REPO_ROOT/apps/production/custom-configs/synapse-values.yaml"
KUSTOMIZATION="$REPO_ROOT/apps/production/kustomization.yaml"
# Function to calculate MD5 hash (handles both GNU md5sum and BSD md5)
get_md5() {
local file="$1"
if command -v md5sum &> /dev/null; then
md5sum "$file" | awk '{print $1}'
elif command -v md5 &> /dev/null; then
md5 -q "$file"
else
echo "ERROR: Neither md5sum nor md5 found" >&2
exit 1
fi
}
# Update checksums for ConfigMaps that exist and are staged
if git diff --cached --name-only | grep -q "element-values.yaml"; then
ELEMENT_HASH=$(get_md5 "$ELEMENT_VALUES")
sed -i.bak "s/value: \"[0-9a-f]\{32\}\" *# element-config/value: \"$ELEMENT_HASH\" # element-config/" "$KUSTOMIZATION"
rm -f "$KUSTOMIZATION.bak"
git add "$KUSTOMIZATION"
fi
if git diff --cached --name-only | grep -q "synapse-values.yaml"; then
SYNAPSE_HASH=$(get_md5 "$SYNAPSE_VALUES")
sed -i.bak "s/value: \"[0-9a-f]\{32\}\" *# synapse-config/value: \"$SYNAPSE_HASH\" # synapse-config/" "$KUSTOMIZATION"
rm -f "$KUSTOMIZATION.bak"
git add "$KUSTOMIZATION"
fi

27
scripts/install-hooks.sh Executable file
View File

@ -0,0 +1,27 @@
#!/usr/bin/env bash
# Install Git Hooks for GitOps automation
# Must be run after cloning the repository
REPO_ROOT="$(git rev-parse --show-toplevel 2>/dev/null)" || {
echo "❌ Not in a git repository"
exit 1
}
HOOKS_DIR="$REPO_ROOT/.git/hooks"
HOOK_SOURCE="$REPO_ROOT/scripts/hooks/pre-commit"
HOOK_DEST="$HOOKS_DIR/pre-commit"
# Verify source exists
if [ ! -f "$HOOK_SOURCE" ]; then
echo "❌ Hook source not found: $HOOK_SOURCE"
exit 1
fi
# Create symlink (force if exists)
mkdir -p "$HOOKS_DIR"
ln -sf "../../scripts/hooks/pre-commit" "$HOOK_DEST"
chmod +x "$HOOK_SOURCE"
echo "✅ Git hooks installed:"
echo " • pre-commit: ConfigMap checksum auto-update"
echo " See: docs/ops-configmap-sync.md"

View File

@ -1,9 +0,0 @@
# aXion1337.Chat Wiki
Welcome to the aXion1337.Chat deployment wiki!
## 📚 Contents
- [Deployment Guides](deployment-guides)
- [Tasks & Roadmap](tasks)
- [Architecture](architecture)