# 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: ```bash 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: ```nginx 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: ```bash 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`: ```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: ```bash 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: ```bash 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: ```nginx 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: ```nginx proxy_read_timeout 3600s; proxy_send_timeout 3600s; proxy_buffering off; ``` ## Prüfen ```bash 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. ```bash sudo ufw allow 80,443/tcp sudo ufw allow 3478,5349/tcp sudo ufw allow 3478,5349/udp sudo ufw allow 49152:65535/udp ```