Files
x3 4a55944aa8 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
2026-08-31 17:56:02 +00:00

3.6 KiB
Raw Permalink Blame History

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