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:
@@ -0,0 +1,128 @@
|
||||
# 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
|
||||
```
|
||||
Reference in New Issue
Block a user