WorkTime ProHandbuch
Zur Anwendung

HTTPS einrichten

Die Anwendung bringt kein TLS mit. Sie spricht HTTP auf den Ports 3000 und 4000; die Verschlüsselung übernimmt ein vorgeschalteter Server — nginx, Caddy, Traefik.

Warum HTTPS im Betrieb nicht verhandelbar ist

Das Erneuerungs-Token liegt in einem Cookie, und dieses Cookie wird im Produktivbetrieb mit dem Merkmal secure gesetzt. Ein secure-Cookie schickt der Browser nur über HTTPS. Läuft die Anwendung mit NODE_ENV=production über blankes HTTP, kommt das Cookie nie an — die Sitzung endet nach der Laufzeit des Zugangs-Tokens, also nach fünfzehn Minuten, und niemand versteht, warum.

In der Entwicklung entfällt das Merkmal; deshalb läuft dort alles über HTTP.

Alles unter einer Herkunft

Die Oberfläche und die API müssen unter derselben Herkunft erreichbar sein. Das ist keine Bequemlichkeit, sondern Voraussetzung: Das Erneuerungs-Cookie trägt SameSite=Strict und würde an eine fremde Herkunft gar nicht erst mitgeschickt.

Next leitet /api/* serverseitig an den API-Dienst weiter. Nach außen genügt deshalb ein einziger Name:

https://zeit.example.de/          → web:3000
https://zeit.example.de/api/…     → web:3000 → api:4000

Der kurze Weg: das mitgelieferte Caddyfile

Im Repositorium liegt unter deploy/Caddyfile eine fertige Konfiguration — Weiterleitung, HSTS, Kompression, Zugriffsprotokoll mit sieben Tagen Aufbewahrung. Caddy holt das Zertifikat von selbst und erneuert es von selbst; es gibt keinen Zeitauftrag, den man vergessen kann.

sudo cp deploy/Caddyfile /etc/caddy/Caddyfile
sudo sed -i 's/zeit\.ihrbetrieb\.de/IHR-NAME.example.de/' /etc/caddy/Caddyfile
sudo caddy validate --config /etc/caddy/Caddyfile
sudo systemctl reload caddy

Dazu gehört, die Anwendung nur noch an die Loopback-Adresse zu binden — sonst bleibt der Klartextzugang auf Port 3000 daneben offen und der Proxy davor nützt nichts. Dafür genügt eine Zeile in der .env:

WEB_BIND=127.0.0.1

Der ganze Weg vom leeren Server bis zur laufenden Adresse — DNS-Eintrag, Firewall, Einrichtung, Nachprüfung — steht in docs/ON-PREMISE.md.

Ein Beispiel mit nginx

server {
    listen 443 ssl http2;
    server_name zeit.example.de;

    ssl_certificate     /etc/letsencrypt/live/zeit.example.de/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/zeit.example.de/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:3000;
        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;
    }
}

server {
    listen 80;
    server_name zeit.example.de;
    return 301 https://$host$request_uri;
}

Die weitergereichte Adresse

Die Ratenbegrenzung zählt vor der Anmeldung nach IP-Adresse. Hinter einem vorgeschalteten Server steht die echte Adresse in X-Forwarded-For; ohne diesen Kopf zählt jede Anfrage auf dieselbe Adresse, und die Anmeldung wäre nach zehn Versuchen für alle gesperrt. Siehe Taktsperre.

APP_URL setzen

APP_URL nennt die erlaubte Herkunft für Browseranfragen. Im Produktivbetrieb ist sie Pflicht — ohne sie bricht die API beim Start ab:

APP_URL ist nicht gesetzt. Ohne erlaubte Herkunft dürfen keine Anfragen aus dem Browser zugelassen werden.

Ohne diese Schranke spiegelte der Server jede fremde Herkunft wider und erlaubte zugleich Anmeldedaten. Mehrere Herkünfte lassen sich kommagetrennt angeben.

Die Schnittstellendokumentation

GET /api/docs legt die gesamte Schnittstelle offen und ist im Produktivbetrieb abgeschaltet. Wer sie braucht, setzt ENABLE_API_DOCS=true — und sollte den Pfad dann im vorgeschalteten Server absichern.