Files
homelab/services/nginx/README.md
T
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

129 lines
3.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 4915265535 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
```