Files
betreuungs-cockpit/.env.example
T
Florian Wilde 609aa5d27d Anmeldung haerten: Einmalcodes, Anschluss, Host, Passkey-Vorzimmer
Vier Stellen, an denen eine Zusage nicht gehalten wurde.

1. Ein TOTP-Code galt mehrfach. Der Kommentar in totp.ts versprach seit
   dem Anfang, der Aufrufer speichere das getroffene Zeitfenster - getan
   hat es niemand. Damit liess sich ein abgelesener Code innerhalb von 90
   Sekunden noch einmal einloesen. Jetzt geht jede Pruefung durch
   zweitenFaktorEinloesen(), und die Bedingung steht im WHERE von
   updateMany: Zwei gleichzeitige Anmeldungen mit demselben Code treffen
   nicht beide eine Zeile.

2. Der Anschluss kam aus dem ERSTEN Eintrag von "x-forwarded-for". Das
   ist der Teil, den der Aufrufer selbst schreibt. Wer bei jeder Anfrage
   eine andere Zahl hineinschrieb, sah fuer die Anmeldesperre wie tausend
   Anschluesse aus - die Zaehlung war nicht nur wirkungslos, sondern
   irrefuehrend. Jetzt von hinten gelesen, und nur wenn VERTRAUE_PROXY
   gesetzt ist. Ohne Proxy ist der Kopf fremdbestimmt; dann lieber keine
   Zaehlung nach Anschluss als eine, die eine Kopfzeile aushebelt. Beim
   ersten Mal steht eine Zeile im Log, damit das Fehlen auffaellt.

3. trustHost stand fest auf true. Ist AUTH_URL gesetzt - der
   dokumentierte Betrieb setzt es, Passkeys brauchen es -, gibt es keinen
   Grund, die Adresse aus einer Kopfzeile von aussen abzuleiten.
   AUTH_TRUST_HOST bleibt wirksam, damit die dokumentierte Einstellung
   nicht stillschweigend uebergangen wird.

4. Passkeys liessen sich ohne erneute Bestaetigung anlegen und entfernen.
   Ein Passkey ist ein vollwertiger Zugang, der einen Passwortwechsel
   ueberlebt - an einem unbeaufsichtigten Rechner waren das zwei Klicks.
   Jetzt dasselbe Vorzimmer wie beim Vollexport. Dabei musste
   alsFehler() lernen, die Weiterleitung von Next durchzulassen: Sonst
   verschwand die Zwischenfrage in "Der Passkey konnte nicht geprueft
   werden".

Refs #38
2026-08-24 08:03:33 +02:00

123 lines
5.1 KiB
Bash

# =============================================================================
# Betreuungs-Cockpit - Beispielkonfiguration
# Kopiere diese Datei nach .env und fuelle die Werte aus.
# Alle Geheimnisse erzeugst du mit: npm run schluessel
# =============================================================================
# --- Datenbank --------------------------------------------------------------
# Lokal (Docker Compose): Host ist der Servicename "db".
POSTGRES_USER=cockpit
POSTGRES_PASSWORD=bitte-aendern
POSTGRES_DB=betreuungs_cockpit
DATABASE_URL="postgresql://cockpit:bitte-aendern@localhost:5432/betreuungs_cockpit?schema=public"
# --- Auth.js ----------------------------------------------------------------
# 32 zufaellige Bytes, base64. Erzeugen: npm run schluessel
AUTH_SECRET=
# Oeffentliche Basis-URL der Anwendung (ohne abschliessenden Slash).
# Hinter einem Proxy die Adresse, die im Browser in der Adresszeile steht -
# nicht die interne. Passkeys pruefen dagegen.
AUTH_URL=http://localhost:3000
# Darf der Host-Kopf der Anfrage die Adresse bestimmen?
#
# Solange AUTH_URL gesetzt ist: nein, und die Zeile bleibt weg. Die Adresse
# ist dann bekannt und muss nicht aus einer Kopfzeile abgeleitet werden, die
# von aussen kommt. Ein "X-Forwarded-Host: fremde.example" schlaegt sonst in
# erzeugte Adressen durch.
# AUTH_TRUST_HOST=true
# --- Erreichbarkeit: HIER MUSST DU DICH ENTSCHEIDEN -------------------------
#
# Auf welcher Adresse soll die Anwendung erreichbar sein? Das haengt davon
# ab, WO dein Reverse Proxy laeuft. Waehl genau eine Zeile aus:
#
# Proxy auf DEMSELBEN Server
# APP_BIND=127.0.0.1
# Von aussen kommt niemand heran, nur der Proxy nebenan. Sicherste
# Variante - aber ein Proxy auf einem anderen Rechner erreicht die
# Anwendung damit NICHT.
#
# Proxy auf einem ANDEREN Server, privates Netz oder WireGuard vorhanden
# APP_BIND=10.0.0.5 <- die Adresse DIESES Servers in dem Netz
# Erste Wahl, wenn der Proxy woanders steht.
#
# Proxy auf einem ANDEREN Server, kein privates Netz
# APP_BIND=0.0.0.0
# Nur zusammen mit einer Firewallregel, die den Port ausschliesslich
# fuer die IP des Proxy-Servers oeffnet:
# ufw allow from <IP-des-Proxys> to any port 3000 proto tcp
# ufw deny 3000/tcp
#
# Zwischen Proxy und Anwendung laeuft unverschluesseltes HTTP - die
# Anwendung beendet kein TLS. Dieser Weg darf nie durchs offene Netz gehen.
# Einzelheiten: docs/INSTALLATION.md, Schritt 7.
APP_BIND=127.0.0.1
APP_PORT=3000
# Die Konsole des mitgelieferten Objektspeichers. Nur noetig, wenn auf dem
# Server schon etwas auf 9001 laeuft.
MINIO_KONSOLE_PORT=9001
# --- Anwendungsseitige Feldverschluesselung ---------------------------------
# AES-256-GCM. 32 Bytes, base64. OHNE DIESEN SCHLUESSEL SIND DIE VERSCHLUESSELTEN
# FELDER UNWIEDERBRINGLICH VERLOREN - unbedingt getrennt vom Backup sichern!
# Schluesselrotation: neuen Schluessel als ENC_KEY_V2 ergaenzen und
# ENC_KEY_AKTIV=2 setzen. Alte Schluessel bleiben zum Entschluesseln stehen.
ENC_KEY_V1=
ENC_KEY_AKTIV=1
# Separater Schluessel fuer Blind-Indizes (HMAC-SHA256).
# Erlaubt die Suche nach z.B. einer IBAN, ohne sie im Klartext zu speichern.
INDEX_KEY=
# --- Sicherheit -------------------------------------------------------------
# Leerlauf bis zur automatischen Abmeldung (Sekunden). 30 Minuten.
SESSION_MAX_AGE=1800
# Wie lange eine erneute Anmeldung fuer sensible Aktionen gilt (Sekunden).
REAUTH_MAX_AGE=300
# Login-Sperre: max. Fehlversuche pro Zeitfenster (Sekunden).
LOGIN_MAX_VERSUCHE=5
LOGIN_FENSTER_SEKUNDEN=900
LOGIN_SPERRE_SEKUNDEN=900
# Geheimnis fuer die Ersteinrichtung. OHNE DIESEN WERT LAESST SICH DER ERSTE
# ZUGANG NICHT ANLEGEN - das ist Absicht.
#
# Zwischen dem ersten Start und dem Anlegen des Zugangs stand die
# Einrichtungsseite sonst offen: Wer sie in diesem Fenster findet, wird
# Administrator. Erzeugen: npm run schluessel
ERSTEINRICHTUNG_GEHEIMNIS=
# Steht ein Reverse Proxy davor?
#
# Nur mit "true" wird "X-Forwarded-For" geglaubt - und dann von HINTEN
# gelesen, weil der vordere Teil der Liste vom Aufrufer selbst kommt.
# Ohne diese Zeile zaehlt die Anmeldesperre nur nach E-Mail-Adresse.
#
# Der dokumentierte Betrieb (Reverse Proxy davor) braucht sie also:
VERTRAUE_PROXY=true
# Bei mehreren gestaffelten Proxys deren Anzahl. Sonst weglassen.
# VERTRAUE_PROXY_STUFEN=1
# --- Objektspeicher (S3-kompatibel, EU!) ------------------------------------
# Wird ab Etappe 3 verwendet. Beispiel: Hetzner Object Storage, Falkenstein.
# ACHTUNG: Mit dem Anbieter muss ein AV-Vertrag nach Art. 28 DSGVO bestehen.
S3_ENDPOINT=https://fsn1.your-objectstorage.com
S3_REGION=eu-central
S3_BUCKET=betreuungs-cockpit
S3_ACCESS_KEY_ID=
S3_SECRET_ACCESS_KEY=
# --- E-Mail-Erinnerungen (optional, ab Etappe 2) ----------------------------
SMTP_HOST=
SMTP_PORT=587
SMTP_USER=
SMTP_PASSWORD=
SMTP_FROM="Betreuungs-Cockpit <cockpit@example.de>"
# --- Erstanmeldung ----------------------------------------------------------
# Wird nur vom Seed benutzt, um den ersten Betreuer-Zugang anzulegen.
SEED_ADMIN_EMAIL=fw@wildeflorian.de
SEED_ADMIN_PASSWORT=bitte-beim-ersten-login-aendern