dorfteich/docs/vs-nfd/50-haertungsleitfaden.md
Claude Fable 5 4d9f913845
All checks were successful
CI / Lint, typecheck, test (pull_request) Successful in 6m18s
CI / Build container images (pull_request) Successful in 4m3s
CI / Auth e2e pack (pull_request) Successful in 8m43s
CI / Import/export fidelity gate (pull_request) Successful in 1m0s
CD / Build and push images (push) Successful in 23s
CD / Smoke tests against Test (push) Successful in 1m22s
CD / Deploy to Test (push) Successful in 12s
CD / Promote to Int (push) Successful in 13s
CI / Lint, typecheck, test (push) Successful in 6m27s
CI / Build container images (push) Has been skipped
CI / Auth e2e pack (push) Successful in 8m24s
CI / Import/export fidelity gate (push) Successful in 58s
#246: mode enforced — reject profile-violating configuration writes
In enforced mode the ONE settings write path every caller uses rejects
catalog-violating values with the stable code vs_nfd_profile_violation
(403 — the request is well-formed, the policy says no). Existing
violating values are reported at startup (log line, database-less boots
must not fail) and on the admin card, never auto-changed. The UI
renders as in hidden (#245 already keys on hidden|enforced). The
hardening guide now names enforced as the recommended mode for VS-NfD
reference operation. Tests: violating write rejected with the stable
code and nothing stored; compliant writes pass; the same violating
write passes in marked and hidden (own app boots); pre-existing
violation reported and untouched. Full api suite 100 files / 555 green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AUtYMxwTCMHG9mVHnwbFg8
2026-07-31 19:48:37 +02:00

24 KiB
Raw Blame History

Härtungsleitfaden — Referenzkonfiguration „VS-NfD-Betrieb" (Issue #227)

Zweck: eine benannte Konfiguration, die ein Betreiber als Ganzes übernehmen kann. Jeder Eintrag nennt den exakten Schalter, den Wert und das Warum — wer abweicht, tut es wissentlich. Der Leitfaden macht zugleich die in M1/M2/M4 gebauten Schalter prüfbar.

Pflegeregel (verbindlich): Jeder PR, der einen neuen Instanz- oder Deploy-Schalter einführt, ergänzt diesen Leitfaden im selben PR um dessen Referenzwert. Ein Schalter ohne Leitfaden-Zeile gilt im Review als unvollständig. (Gleiches Muster wie der Ereigniskatalog-Zaun #201.)

Geltungsbereich: Konfiguration der Anwendung. Die Härtung der Plattform (Betriebssystem, Netz, Reverse Proxy, Datenträger) ist Betreibersache (Abgrenzungserklärung 40-abgrenzungserklaerung.md — von dort wird hierher verwiesen; das IT-Grundschutz-Mapping 80-grundschutz-mapping.md nimmt diese Referenzkonfiguration als Produkt-Beleg).


1 Referenzkonfiguration

1.1 Instanz-Settings (Site-Admin → Einstellungen; Tabelle instance_settings)

Nach jeder Änderung an Instanz-Settings die api neu starten — der Settings-Cache ist in-process (operations.md).

Setting Referenzwert Default Warum
auth.registrationMode closed open Konten entstehen in einer VS-Umgebung nur kontrolliert; Selbstregistrierung öffnet den Nutzerkreis unkontrolliert.
api.enabled false false Public REST API ist ein zusätzlicher Egress-Kanal; ohne dokumentierten Bedarf bleibt er zu (404 auf allen /api/public/v1-Routen).
mcp.enabled false false gleiches Argument für den MCP-Endpoint (/api/mcp); unabhängiger Schalter.
feeds.enabled false true explizit setzen — Atom-Feeds liefern Inhalte an Reader außerhalb der Kontrolle der Instanz (Feed-Token umgehen die Session); Kopien in Feed-Readern sind nicht einholbar (Kopienliste, Sicherheitsdokumentation §5). Schaltet Routen UND Feed-Token-Verwaltung auf 404.
plugins.enabled false true explizit setzen — kein Fremdcode in der VS-Zone (#200): alle Plugin-Flächen 404, Dropzone quarantänisiert; bestehende Blöcke degradieren zu ihrem deklarierten Text-Fallback. Hash-Pinning ist verschoben (#232, Restrisikoliste) — der Kill-Switch deckt das Risiko für diesen Betriebsmodus vollständig.
classification.newPageDefault vs_nfd unclassified in einer VS-NfD-Instanz beginnt nichts unmarkiert (#204); die Vererbung (#205) hält den Baum konsistent.
classification.uploadPolicy block warn Anhänge können die Kennzeichnung im Inhalt nicht tragen (#212) — die Referenzkonfiguration lehnt Uploads auf eingestufte Seiten serverseitig ab (403 classified_upload_blocked, #213) statt nur zu warnen.
upload.svgPolicy reject sanitize SVG ist aktiver Inhalt; die Sanitisierung ist gut getestet, aber Ablehnen ist die kleinere Angriffsfläche. Abweichung vertretbar, wenn SVG gebraucht wird.
upload.allowedExtensions nur das dienstlich Nötige (z. B. pdf) Standardliste jede zusätzliche Endung vergrößert die Menge nicht prüfbarer Binärformate im Bestand. Bilder sind davon unabhängig immer erlaubt (Magic-Byte-geprüft).
backup.nextcloud.enabled false false „Backup nur lokal": kein Anwendungs-Upload von Restore-Sets zu Drittdiensten. Fernspiegel regelt ausschließlich die Deploy-Allowlist (1.2).
trash.retentionDays, audit.retentionDays, conversion.payloadRetentionDays, mail.outboxRetentionDays Defaults (30/365/30/30) ebd. Aufbewahrung bewusst begrenzt; Verkürzung nach Betreiber-Löschkonzept zulässig (Betriebshandbuch §5).
readTrail.enabled true false explizit setzen — der Lesetrail (#222#225) evidenziert Lesezugriffe auf eingestufte Seiten; Default aus, weil Lesebeobachtung mitbestimmungsrelevant ist. Einschalten NUR zusammen mit der Zweckbindung (Sicherheitsdokumentation §7); die api meldet die Schalterstellung beim Start.
readTrail.dedupWindowMinutes Default (5) 5 Dedup-Fenster des Lesetrails (#223): je (Sitzung, Seite, Kanal) ein Ereignis pro Fenster — begrenzt die Ereignisflut einer Live-Sitzung auf ~12/h. Kleiner = feineres Protokoll und mehr Zeilen; Änderung mit dem Zweckbindungs-Dokument (#225) abstimmen.
readTrail.retentionDays Default (365) 365 eigene Aufbewahrung des Lesetrails (#224), bewusst getrennt von audit.retentionDays; Löschläufe sind selbst auditiert (read_trail.pruned). Dauer mit der Zweckbindung (#225) und dem Betreiber-Löschkonzept abstimmen.
idpMapping.rules Gruppen→Rollen der Behörde abbilden [] deklaratives Claim-Mapping (#217): IdP-Gruppen werden bei jedem OIDC-Login auf Teich-Rollen und das Site-Admin-Flag abgeglichen — sonst pflegt die Behörde Berechtigungen doppelt und die zweite Kopie driftet. Mapping fasst nur eigene Grants an (manuell gewinnt); Details: permissions.md §IdP claim mapping.
legal.imprint, legal.privacyPolicy befüllt leer Betreiberpflicht; leere Seiten zeigen einen Warnbanner.

1.2 Deploy-Konfiguration (.env / Compose — nur Plattformzugriff, bewusst nicht per Admin-UI)

Variable Referenzwert Warum
BACKUP_ALLOWED_TARGETS leer lassen oder exakt der eine freigegebene Spiegel-Host leere Allowlist schaltet ALLE Fernziele hart ab (ADR 0026, #192) — „Backup nur lokal" ist damit deploy-seitig erzwungen und vom Site-Admin nicht aufweichbar (Rollentrennung, Betriebshandbuch §6).
SESSION_ABSOLUTE_HOURS 12 (Default 168) eine Sitzung überdauert keinen Arbeitstag; Neuanmeldung am nächsten Tag ist der Preis.
SESSION_IDLE_HOURS 2 (Default 72) unbeaufsichtigte, noch angemeldete Arbeitsplätze fallen schnell zurück auf die Anmeldemaske.
SMTP_HOST etc. unkonfiguriert lassen (oder internes Relay) ohne SMTP verlassen keinerlei Inhaltstitel die Instanz per Mail (Digest-Restrisiko I-23 entfällt vollständig). Konsequenz ehrlich benannt: dann gibt es keine Verifikations- und Passwort-Reset-Mails — Kontenpflege läuft über den Site-Admin. Wer Mail braucht, nutzt ein internes Relay und akzeptiert I-23 (Restrisikoliste).
WEB_PORT/API_PORT/COLLAB_PORT Defaults (127.0.0.1-gebunden) Anwendungscontainer sind nie direkt exponiert; einzige Eintrittsstelle ist der Reverse Proxy (Sicherheitsdokumentation §2).
LOG_LEVEL info Audit-Zeilen (audit: -Präfix) müssen den Collector erreichen; debug nur zur Störungssuche.
OIDC_ISSUER, OIDC_CLIENT_ID, OIDC_CLIENT_SECRET, OIDC_SCOPES, OIDC_PROVIDER_LABEL IdP der Behörde konfigurieren Fremdauthentisierung (#214, ADR 0021): Authorization Code + PKCE gegen den IdP der Umgebung; deploy-seitig, weil die Authentisierungshoheit Plattformsache ist. Erst-Login legt Konten just-in-time an; bestehende lokale Konten werden NIE stillschweigend per E-Mail übernommen (expliziter Link-Flow). Konfigurationsdetails: security.md §External authentication.
AUTH_PROXY_HEADER, AUTH_PROXY_TRUSTED_PEERS, AUTH_PROXY_MAP, AUTH_PROXY_MODE, AUTH_PROXY_DN_ATTRIBUTE nur bei Perimeter-Authentisierung setzen Alternativpfad (#215): Identität aus dem Proxy-Header, gültig NUR vom TCP-Peer der Allowlist; fremder Peer mit Header wird abgewiesen und auditiert (auth.proxy_rejected). Der Proxy MUSS den Header aus eingehendem Verkehr strippen. mTLS-Variante über weitergereichten Zertifikats-DN (mtls-dn). Ohne Perimeter-Auth: unkonfiguriert lassen (Header wirkungslos).
AUTH_LOCAL_ENABLED false (Default true) der harte Schalter aus ADR 0021 (#216, deploy-seitige Realisierung von auth.local.enabled): false schaltet JEDEN lokalen Credential-Flow auf 404 (Login, Signup, Verifikation, Passwort vergessen/zuruecksetzen/aendern) — Anmeldung ausschliesslich ueber OIDC (#214) bzw. Perimeter-Auth (#215). Deploy-seitig, damit ein kompromittierter Site-Admin ihn nicht zurueckdrehen kann. Reihenfolge: erst Setup/Bootstrap (SETUP_ADMIN_*), dann auf false. PAT-/Feed-Token-Ausgabe bleibt fuer IdP-authentisierte Nutzer verfuegbar (eigene Schalter api.enabled/feeds.enabled; bewusste Entscheidung #216).
VS_NFD_MODE enforced (Default off) Härtungsprofil-Modus (#243, ADR 0027): die Anwendung kennt diese Referenzkonfiguration als maschinenlesbaren Katalog und bewertet die laufende Konfiguration dagegen (Site-Admin → Einstellungen). marked markiert Abweichungen, hidden blendet abweichende Optionen aus, enforced weist abweichende Schreibzugriffe serverseitig ab — alle drei Behandlungsstufen sind umgesetzt (#244#246); für den VS-NfD-Referenzbetrieb ist enforced der empfohlene Modus — nur er hält auch skriptgesteuerte Clients an das Profil. Bestehende Abweichungen werden beim Start und in der Admin-Ansicht gemeldet, nie automatisch geändert. Deploy-seitig wie BACKUP_ALLOWED_TARGETS: ein kompromittierter Site-Admin kann den Modus nicht aufweichen. Außerhalb von VS-Kontexten bleibt der Default off — keinerlei Markierung.

1.3 Noch nicht verfügbar (Regel: landet hier im selben PR)

Derzeit leer — auth.local.enabled ist mit #216 als AUTH_LOCAL_ENABLED (1.2) scharfgestellt.

Schalter Referenzwert (geplant) Status

2 Verifikations-Checkliste

Auf der laufenden Instanz (ersetze HOST); Erwartung jeweils dahinter. Die vier 404-Prüfungen laufen unauthentifiziert:

curl -s -o /dev/null -w '%{http_code}\n' https://HOST/api/public/v1/ponds   # 404 (api.enabled=false)
curl -s -o /dev/null -w '%{http_code}\n' -X POST https://HOST/api/mcp       # 404 (mcp.enabled=false)
curl -s -o /dev/null -w '%{http_code}\n' https://HOST/api/v1/public/IRGENDEIN-TEICH/feed.xml   # 404 (feeds.enabled=false)
curl -s -o /dev/null -w '%{http_code}\n' https://HOST/api/v1/admin/plugins  # 401/404, nie 200 ohne Session
curl -s -o /dev/null -w '%{http_code}\n' -X POST https://HOST/api/v1/auth/login   # 404 (AUTH_LOCAL_ENABLED=false)
curl -s https://HOST/api/v1/readyz                                          # status ok

Als Site-Admin (UI → Administration bzw. GET /api/v1/admin/settings):

  • Registrierung „geschlossen"; neue Seite entsteht mit Kennzeichnung (Einstufung neuer Seiten = VS-NfD); Upload auf eingestufte Seite wird abgelehnt (403).
  • Plugin-Verwaltung antwortet 404 (Kill-Switch aktiv).
  • Backup-Panel zeigt keine aktiven Fernziele; auf dem Host ist BACKUP_ALLOWED_TARGETS leer bzw. exakt der freigegebene Spiegel.
  • Eine Testsitzung läuft nach SESSION_IDLE_HOURS Inaktivität ab.
  • Kennzeichnungs-Stichprobe: eingestufte Seite zeigt den Aufdruck in Web, Druckvorschau und PDF-Export (Konventionen: Kommentare auf Issue #228 bzw. 60-sicherheitsdokumentation.md §3.5).

3 Querbezüge

Abgrenzungserklärung (40-abgrenzungserklaerung.md, verweist hierher); IT-Grundschutz-Mapping (80-grundschutz-mapping.md, nutzt dieses Profil als Produkt-Beleg); Betriebshandbuch (70-betriebshandbuch.md, Installation §1 wendet dieses Profil an); Restrisikoliste (90-restrisiken.md — Abweichungen von der Referenzkonfiguration gehören dorthin, wenn sie dauerhaft sind).