Homelab-Dokumentation und Konfigurationsvorlagen

Dokumentation aus dem Wiki uebernommen und um die Dienste auf dem Server
erweitert. Alle Anleitungen sind so geschrieben, dass sich der jeweilige
Dienst allein daraus neu aufsetzen laesst.

docs/      Dienste im Heimnetz (aus dem Wiki, anonymisiert)
services/  Dienste auf dem Server mit Konfigurationsvorlagen
           - matrix-synapse: Homeserver, Postgres, Well-Known-Delegation
           - coturn:         TURN-Relay fuer Anrufe
           - element-web:    Web-Client
           - wikijs, gitea, nginx

Durchgehend anonymisiert: echte Domain durch example.com ersetzt, IP-Adressen
und E-Mail-Adressen durch Platzhalter. Konfigurationsdateien liegen nur als
.example mit Platzhaltern statt echter Secrets vor.

Die Anleitungen halten die Stolpersteine fest, die beim Aufbau tatsaechlich
aufgetreten sind, unter anderem:
- Synapse verlangt LC_COLLATE=C, sonst startet es nicht
- server_name ist nachtraeglich nicht aenderbar -> Delegation noetig
- register_new_matrix_user liest conf.d nicht, Secret muss per -k kommen
- coturn braucht external-ip, sonst kommen keine Medien durch
- Gruppenmitgliedschaft fuer den Zertifikatszugriff wirkt erst beim Neustart
- nginx vererbt add_header nicht in location-Bloecke mit eigenen Direktiven
This commit is contained in:
x3
2026-08-31 17:56:02 +00:00
commit 4a55944aa8
32 changed files with 1848 additions and 0 deletions
+270
View File
@@ -0,0 +1,270 @@
# Matrix-Homeserver (Synapse)
Eigener Matrix-Server mit PostgreSQL, nginx als TLS-Terminierung und
Well-Known-Delegation, damit die Benutzer-IDs kurz bleiben.
| Punkt | Wert |
|---|---|
| Software | Synapse (Paket `matrix-synapse-py3` von packages.matrix.org) |
| Datenbank | PostgreSQL |
| Interner Listener | `127.0.0.1:8008` (kein TLS, das macht nginx) |
| Öffentlich | `https://matrix.example.com` |
| Benutzer-IDs | `@name:example.com` |
## Warum zwei Domains?
Das ist der Punkt, an dem die meisten Anleitungen unnötig kompliziert werden.
Der `server_name` ist der **hintere Teil jeder Benutzer-ID**. Setzt man ihn auf
`matrix.example.com`, heißen alle Benutzer `@name:matrix.example.com` unschön
und nicht mehr änderbar, denn der `server_name` lässt sich nachträglich **nicht**
umstellen, ohne alle Konten und Räume zu verlieren.
Deshalb:
- `server_name: example.com` → IDs lauten `@name:example.com`
- Synapse läuft aber auf `matrix.example.com`
- Zwei kleine JSON-Dokumente unter `https://example.com/.well-known/matrix/…`
verweisen andere Server und Clients dorthin
Die Apex-Domain muss dafür **nur diese zwei Dateien** ausliefern, sonst nichts.
## Voraussetzungen
- Debian/Ubuntu mit root-Rechten
- PostgreSQL
- nginx
- certbot
- DNS-A-Records auf den Server: `example.com`, `matrix.example.com`
## 1. Paketquelle und Installation
```bash
sudo install -d -m 0755 /etc/apt/keyrings
sudo curl -fsSL -o /etc/apt/keyrings/matrix-org-archive-keyring.gpg \
https://packages.matrix.org/debian/matrix-org-archive-keyring.gpg
# <suite> ist der Codename der Distribution, z. B. noble, trixie, resolute
echo "deb [signed-by=/etc/apt/keyrings/matrix-org-archive-keyring.gpg] \
https://packages.matrix.org/debian/ <suite> main" \
| sudo tee /etc/apt/sources.list.d/matrix-org.list
sudo apt-get update
```
Ob es für die eigene Distribution ein Paket gibt, lässt sich vorher prüfen:
```bash
curl -s https://packages.matrix.org/debian/dists/<suite>/main/binary-amd64/Packages \
| grep -E "^Package:|^Version:"
```
Installation. Der `server_name` wird per debconf vorbelegt, damit die
Installation nicht interaktiv nachfragt:
```bash
echo "matrix-synapse-py3 matrix-synapse/server-name string example.com" \
| sudo debconf-set-selections
echo "matrix-synapse-py3 matrix-synapse/report-stats boolean false" \
| sudo debconf-set-selections
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y matrix-synapse-py3
```
> **Wichtig:** Der hier gesetzte `server_name` ist endgültig. Er landet in
> `/etc/matrix-synapse/conf.d/server_name.yaml`.
## 2. Datenbank
Synapse besteht auf `C`-Collation. Eine mit den üblichen `de_DE.UTF-8`-Vorgaben
angelegte Datenbank wird beim Start **abgelehnt**:
```bash
sudo -u postgres psql <<'SQL'
CREATE ROLE synapse_user LOGIN PASSWORD 'HIER-EIN-STARKES-PASSWORT';
CREATE DATABASE synapse
ENCODING 'UTF8'
LC_COLLATE='C'
LC_CTYPE='C'
template=template0
OWNER synapse_user;
SQL
```
Kontrolle unter `Collate`/`Ctype` muss `C` stehen:
```bash
sudo -u postgres psql -c "\l synapse"
```
## 3. Konfiguration
Eigene Einstellungen gehören nach `/etc/matrix-synapse/conf.d/`. Die
`homeserver.yaml` wird vom Paket verwaltet und bei Upgrades überschrieben.
Der von der Installation angelegte SQLite-Block in `homeserver.yaml` muss
entfernt oder auskommentiert werden, sonst kollidiert er mit der conf.d-Datei:
```bash
sudo sed -i '/^database:/,/database: \/var\/lib\/matrix-synapse\/homeserver.db/d' \
/etc/matrix-synapse/homeserver.yaml
```
Dann die Vorlagen aus diesem Verzeichnis kopieren und die Platzhalter füllen:
```bash
sudo cp conf.d/database.yaml.example /etc/matrix-synapse/conf.d/database.yaml
sudo cp conf.d/homelab.yaml.example /etc/matrix-synapse/conf.d/homelab.yaml
sudo chown root:matrix-synapse /etc/matrix-synapse/conf.d/*.yaml
sudo chmod 640 /etc/matrix-synapse/conf.d/*.yaml
```
### Secrets explizit setzen
Fehlen `macaroon_secret_key` und `form_secret`, leitet Synapse sie aus dem
Signing-Key ab und warnt beim Start. Wird der Signing-Key später ersetzt, sind
schlagartig **alle Access-Tokens ungültig**. Deshalb einmalig festlegen:
```bash
sudo tee /etc/matrix-synapse/conf.d/secrets.yaml >/dev/null <<EOF
macaroon_secret_key: "$(openssl rand -base64 48 | tr -d '\n')"
form_secret: "$(openssl rand -base64 48 | tr -d '\n')"
EOF
sudo chown root:matrix-synapse /etc/matrix-synapse/conf.d/secrets.yaml
sudo chmod 640 /etc/matrix-synapse/conf.d/secrets.yaml
```
Registrierungs-Secret separat ablegen, damit es nicht in einer YAML steht:
```bash
openssl rand -hex 32 | sudo tee /etc/matrix-synapse/registration_shared_secret >/dev/null
sudo chown root:matrix-synapse /etc/matrix-synapse/registration_shared_secret
sudo chmod 640 /etc/matrix-synapse/registration_shared_secret
```
Start:
```bash
sudo systemctl restart matrix-synapse
sudo systemctl status matrix-synapse
curl -s http://127.0.0.1:8008/_matrix/client/versions
```
## 4. nginx
`nginx/matrix.conf` nach `/etc/nginx/sites-available/` kopieren, verlinken und
ein Zertifikat holen:
```bash
sudo certbot certonly --webroot -w /var/www/html -d matrix.example.com
sudo ln -s /etc/nginx/sites-available/matrix.example.com \
/etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
```
Zwei Details in der Konfiguration sind nicht optional:
- `client_max_body_size` muss mindestens so groß sein wie `max_upload_size` in
Synapse, sonst brechen Uploads mit einem nginx-413 ab, bevor Synapse sie sieht.
- `proxy_read_timeout` muss großzügig sein. Clients halten `/sync` als
Long-Poll offen; beim nginx-Standard von 60 Sekunden reißt die Verbindung
ständig ab.
## 5. Well-Known-Delegation
Im vHost der **Apex-Domain** (`example.com`):
```nginx
location = /.well-known/matrix/server {
default_type application/json;
add_header Access-Control-Allow-Origin *;
return 200 '{"m.server": "matrix.example.com:443"}';
}
location = /.well-known/matrix/client {
default_type application/json;
add_header Access-Control-Allow-Origin *;
return 200 '{"m.homeserver": {"base_url": "https://matrix.example.com"}}';
}
```
Der `Access-Control-Allow-Origin`-Header ist beim Client-Dokument
zwingend ohne ihn verweigern Browser-Clients die Abfrage.
Prüfen:
```bash
curl -s https://example.com/.well-known/matrix/server
curl -s https://example.com/.well-known/matrix/client
```
## 6. Admin-Benutzer anlegen
Die Registrierung ist bewusst geschlossen (`enable_registration: false`), sonst
wird der Server binnen Tagen als Spam-Relay missbraucht. Benutzer werden per
Shared Secret angelegt.
`register_new_matrix_user` liest **nur** die mit `-c` übergebene Datei. Liegt
das Secret in `conf.d/`, findet es der Befehl nicht deshalb direkt übergeben:
```bash
SECRET=$(sudo cat /etc/matrix-synapse/registration_shared_secret)
sudo register_new_matrix_user -k "$SECRET" \
-u admin -p 'PASSWORT' -a http://127.0.0.1:8008
```
`-a` macht den Benutzer zum Server-Admin. Kontrolle:
```bash
sudo -u postgres psql -d synapse -c "SELECT name, admin FROM users;"
```
## 7. Funktionsprüfung
```bash
# Login über die öffentliche URL
curl -s -X POST https://matrix.example.com/_matrix/client/v3/login \
-H 'Content-Type: application/json' \
-d '{"type":"m.login.password",
"identifier":{"type":"m.id.user","user":"admin"},
"password":"PASSWORT"}'
# Föderation von außen prüfen (offizieller Tester)
curl -s "https://federationtester.matrix.org/api/report?server_name=example.com" \
| python3 -c "import json,sys; print('FederationOK:', json.load(sys.stdin)['FederationOK'])"
```
`FederationOK: True` ist das Ziel. Schlägt es fehl, liegt es fast immer an der
Well-Known-Datei oder an einer unvollständigen Zertifikatskette.
## Betrieb
```bash
sudo systemctl status matrix-synapse
sudo journalctl -u matrix-synapse -f
```
Datenbank sichern:
```bash
sudo -u postgres pg_dump -Fc synapse > synapse-$(date +%F).dump
```
Mitzusichern sind außerdem:
- `/etc/matrix-synapse/homeserver.signing.key` **ohne diesen Schlüssel ist der
Server für andere Server eine andere Identität.** Nicht ersetzbar.
- `/etc/matrix-synapse/conf.d/` enthält die Secrets
- `/var/lib/matrix-synapse/media` hochgeladene Dateien
## Stolpersteine
| Symptom | Ursache |
|---|---|
| Start bricht mit Collation-Fehler ab | Datenbank ohne `LC_COLLATE='C'` angelegt |
| „Config is missing macaroon_secret_key“ | `secrets.yaml` fehlt |
| Uploads scheitern mit 413 | `client_max_body_size` < `max_upload_size` |
| Clients verlieren dauernd die Verbindung | `proxy_read_timeout` zu klein |
| Föderation schlägt fehl | Well-Known falsch oder Zertifikatskette unvollständig |
| IDs lauten `@name:matrix.example.com` | `server_name` falsch gesetzt nicht mehr korrigierbar |
@@ -0,0 +1,12 @@
# -> /etc/matrix-synapse/conf.d/database.yaml (root:matrix-synapse, 0640)
database:
name: psycopg2
txn_limit: 10000
args:
user: synapse_user
password: HIER-DAS-DATENBANK-PASSWORT
dbname: synapse
host: 127.0.0.1
port: 5432
cp_min: 5
cp_max: 10
@@ -0,0 +1,51 @@
# -> /etc/matrix-synapse/conf.d/homelab.yaml (root:matrix-synapse, 0640)
#
# Liegt bewusst in conf.d: homeserver.yaml wird vom Paket verwaltet und bei
# Upgrades ueberschrieben.
# Synapse lauscht nur lokal; TLS und Port 443 uebernimmt nginx davor.
listeners:
- port: 8008
tls: false
type: http
x_forwarded: true
bind_addresses: ['127.0.0.1']
resources:
- names: [client, federation]
compress: false
public_baseurl: https://matrix.example.com/
# Offene Registrierung aus: sonst wird der Server als Spam-Relay missbraucht.
enable_registration: false
registration_shared_secret_path: /etc/matrix-synapse/registration_shared_secret
max_upload_size: 100M
url_preview_enabled: true
# Ohne diese Sperrliste laesst sich der Link-Vorschau-Dienst benutzen, um das
# interne Netz des Servers zu scannen.
url_preview_ip_range_blacklist:
- '127.0.0.0/8'
- '10.0.0.0/8'
- '172.16.0.0/12'
- '192.168.0.0/16'
- '100.64.0.0/10'
- '169.254.0.0/16'
- '::1/128'
- 'fe80::/64'
- 'fc00::/7'
# --------------------------- VoIP / TURN ----------------------------------
turn_uris:
- "turn:turn.example.com:3478?transport=udp"
- "turn:turn.example.com:3478?transport=tcp"
- "turns:turn.example.com:5349?transport=udp"
- "turns:turn.example.com:5349?transport=tcp"
# Muss exakt dem static-auth-secret in der turnserver.conf entsprechen.
turn_shared_secret: "HIER-DAS-TURN-SECRET"
turn_user_lifetime: 86400000
turn_allow_guests: false
trusted_key_servers:
- server_name: "matrix.org"
suppress_key_server_warning: true
@@ -0,0 +1,7 @@
# -> /etc/matrix-synapse/conf.d/secrets.yaml (root:matrix-synapse, 0640)
#
# Mit "openssl rand -base64 48" erzeugen. Fehlen die Werte, leitet Synapse sie
# aus dem Signing-Key ab; ein spaeterer Wechsel des Signing-Keys wuerde dann
# alle Access-Tokens ungueltig machen.
macaroon_secret_key: "HIER-EIN-ZUFALLSWERT"
form_secret: "HIER-EIN-ZUFALLSWERT"
@@ -0,0 +1,41 @@
# Synapse-Client- und Federation-API. Synapse selbst lauscht nur auf
# 127.0.0.1:8008; TLS und Port 443 macht nginx.
server {
listen 80;
listen [::]:80;
server_name matrix.example.com;
location /.well-known/acme-challenge/ { root /var/www/html; }
location / { return 301 https://$host$request_uri; }
}
server {
listen 443 ssl;
listen [::]:443 ssl;
http2 on;
server_name matrix.example.com;
ssl_certificate /etc/letsencrypt/live/matrix.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/matrix.example.com/privkey.pem;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
# Muss mindestens so gross sein wie max_upload_size in Synapse.
client_max_body_size 100M;
location ~ ^(/_matrix|/_synapse/client) {
proxy_pass http://127.0.0.1:8008;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $host;
proxy_http_version 1.1;
# Long-Polling (/sync) darf nicht nach 60s abgeschnitten werden.
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
proxy_buffering off;
}
location / {
return 200 'Matrix homeserver. Web client: https://element.example.com\n';
default_type text/plain;
}
}