TLS / Reverse-Proxy¶
Drei Pfade.
Pfad 1 — Let's-Encrypt vom Setup-Wizard¶
Wenn BASE_URL eine Domain enthält und Port 80 von außen erreichbar ist, beantragt der Setup-Wizard automatisch ein Let's-Encrypt-Cert. Renewal läuft im Hintergrund, kein zusätzlicher Aufwand.
Voraussetzungen:
- DNS A/AAAA-Record auf den Server
- Port 80 + 443 von außen erreichbar
- Nichts anderes auf Port 80 (kein bestehendes nginx)
Failure-Mode: Wenn die Validierung scheitert, fällt Vesana auf ein selbstsigniertes Cert zurück und zeigt Browser-Warnung. Kann später manuell neu beantragt werden in Admin → System → TLS.
Pfad 2 — Eigenes Zertifikat¶
Du hast ein Cert von einer eigenen CA oder einem Hardware-Cert.
sudo cp dein-cert.pem /opt/vesana/ssl/cert.pem
sudo cp dein-key.pem /opt/vesana/ssl/key.pem
sudo chmod 600 /opt/vesana/ssl/key.pem
docker compose -f /opt/vesana/docker-compose.prod.yml exec nginx nginx -s reload
Cert-Format: PEM (X.509 mit Intermediate-Chain). Key: unverschlüsselt PEM. Bei Cert-Erneuerung wieder kopieren + reload.
Pfad 3 — Reverse-Proxy davor¶
Wenn schon ein nginx / Traefik / HAProxy / Caddy vor dem Server steht und TLS dort terminiert wird:
In .env:
TRUST_PROXY=true
HTTP_PORT=8080 # statt 80
HTTPS_PORT=8443 # statt 443 — wir brauchen TLS hier nicht, aber Container will den Port
Im externen Reverse-Proxy:
server {
listen 443 ssl http2;
server_name vesana.example.com;
ssl_certificate /etc/letsencrypt/live/vesana.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/vesana.example.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
# Receiver-Endpoint für Agent / Collector
location /receiver {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_request_buffering off;
client_max_body_size 50m; # für komprimierte Log-Pakete
}
}
Wichtig: TRUST_PROXY=true setzen, sonst sieht das Backend nicht die echte Client-IP, sondern die des Reverse-Proxys — Audit-Log und Rate-Limit wären verfälscht.
Test-Self-Host (Beispiel)¶
Auf der Test-Instanz test.dailycrust.it läuft ein nginx davor, der TLS terminiert und auf den Stack auf Ports 8180/8181 proxiert:
server {
listen 443 ssl;
server_name test.dailycrust.it;
ssl_certificate /etc/letsencrypt/live/test.dailycrust.it/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/test.dailycrust.it/privkey.pem;
location / {
proxy_pass https://127.0.0.1:8181;
proxy_ssl_verify off; # interner Stack hat self-signed
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto https;
}
}
proxy_ssl_verify off ist akzeptabel für intern, weil der Traffic auf 127.0.0.1 nicht öffentlich.
CSP¶
Frontend setzt strikte CSP. Wenn du eigene Iframes oder eingebettete Inhalte willst, must die iframe_allowlist in system_settings ergänzen.
Header-Hygiene¶
Der eingebaute nginx setzt:
Strict-Transport-Security: max-age=31536000; includeSubDomainsX-Content-Type-Options: nosniffX-Frame-Options: SAMEORIGINReferrer-Policy: strict-origin-when-cross-originPermissions-Policy: geolocation=(), microphone=()
Bei eigenem Reverse-Proxy: gleichen Set einbauen.
TLS-Test¶
Externe Werkzeuge:
- SSL Labs Test für Cert-Chain und Cipher-Suite-Bewertung
testssl.shfür lokale Validierung
Ziel: A oder A+ bei SSL Labs.