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
nginx und TLS
nginx ist der einzige Dienst, der von außen erreichbar ist. Alle Anwendungen
lauschen nur auf 127.0.0.1 und werden von hier weitergereicht.
Aufteilung
| Domain | Ziel |
|---|---|
example.com |
statische Seite + Matrix-Well-Known |
matrix.example.com |
Synapse auf 127.0.0.1:8008 |
element.example.com |
statische Dateien /var/www/element |
wiki.example.com |
Wiki.js auf 127.0.0.1:3000 |
git.example.com |
Gitea auf 127.0.0.1:3001 |
turn.example.com |
kein vHost – coturn nutzt das Zertifikat direkt |
turn.example.com läuft bewusst nicht über nginx. coturn spricht kein HTTP; es
braucht nur ein gültiges Zertifikat für Port 5349. Der Name muss trotzdem in
einem Zertifikat stehen, deshalb wird er beim Ausstellen mit angegeben.
Zertifikate
Ein Zertifikat für mehrere Namen:
sudo certbot certonly --webroot -w /var/www/html \
-d matrix.example.com -d element.example.com -d turn.example.com
Für die Erstausstellung muss Port 80 die ACME-Anfrage beantworten können:
server {
listen 80;
server_name matrix.example.com element.example.com turn.example.com;
root /var/www/html;
location /.well-known/acme-challenge/ { allow all; }
}
Enthaltene Namen prüfen:
sudo openssl x509 -in /etc/letsencrypt/live/matrix.example.com/fullchain.pem \
-noout -ext subjectAltName
Erneuerung
certbot erneuert automatisch. Dienste, die das Material selbst lesen (hier
coturn), merken das nicht – sie halten es seit dem Start im Speicher. Deshalb
ein Deploy-Hook nach
/etc/letsencrypt/renewal-hooks/deploy/10-reload-services.sh:
#!/bin/sh
set -e
chgrp -R ssl-cert /etc/letsencrypt/live /etc/letsencrypt/archive || true
chmod -R g+rX /etc/letsencrypt/live /etc/letsencrypt/archive || true
find /etc/letsencrypt/archive -name 'privkey*.pem' -exec chmod 640 {} \; || true
systemctl reload nginx || true
systemctl restart coturn || true
Trockenlauf:
sudo certbot renew --dry-run
Zertifikate für Nicht-HTTP-Dienste freigeben
Standardmäßig darf nur root den privaten Schlüssel lesen. Statt die Rechte
global zu öffnen, eine Gruppe verwenden und nur die betroffenen Dienstbenutzer
aufnehmen:
sudo groupadd -f ssl-cert
sudo usermod -aG ssl-cert turnserver
sudo chgrp -R ssl-cert /etc/letsencrypt/live /etc/letsencrypt/archive
sudo chmod -R g+rX /etc/letsencrypt/live /etc/letsencrypt/archive
sudo find /etc/letsencrypt/archive -name 'privkey*.pem' -exec chmod 640 {} \;
Gruppenmitgliedschaften wirken erst beim Start eines Prozesses – danach ist ein
restart nötig, kein reload.
Zwei wiederkehrende Fallstricke
add_header wird nicht vererbt. Sobald ein location-Block eine eigene
add_header-Direktive enthält, verliert er alle geerbten. Sicherheitsheader
gehören deshalb in ein Snippet, das in jeden betroffenen Block eingebunden wird:
include /etc/nginx/snippets/element-security.conf;
Zeitüberschreitungen bei Long-Polling. Matrix-Clients halten /sync offen.
Mit dem Standardwert von 60 Sekunden bricht die Verbindung ständig ab:
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
proxy_buffering off;
Prüfen
sudo nginx -t
sudo systemctl reload nginx
curl -s -o /dev/null -w "%{http_code}\n" https://example.com/
curl -s -o /dev/null -D - https://element.example.com/ | grep -iE '^x-|^strict'
Firewall
Freizugeben: 80 und 443 TCP, dazu für coturn 3478 und 5349 (je TCP und UDP) sowie 49152–65535 UDP.
sudo ufw allow 80,443/tcp
sudo ufw allow 3478,5349/tcp
sudo ufw allow 3478,5349/udp
sudo ufw allow 49152:65535/udp