dorfteich/docs/vs-nfd/20-massnahmenplan.md
Claude Fable 5 183faf7710
All checks were successful
CI / Lint, typecheck, test (pull_request) Successful in 5m42s
CI / Build container images (pull_request) Successful in 3m56s
CI / Auth e2e pack (pull_request) Successful in 8m17s
CI / Import/export fidelity gate (pull_request) Successful in 56s
CD / Build and push images (push) Successful in 24s
CD / Deploy to Test (push) Successful in 10s
CI / Lint, typecheck, test (push) Successful in 6m17s
CD / Smoke tests against Test (push) Successful in 3m32s
CI / Build container images (push) Has been skipped
CD / Promote to Int (push) Successful in 13s
CI / Auth e2e pack (push) Successful in 8m20s
CI / Import/export fidelity gate (push) Successful in 55s
#204: classification as first-class page metadata (ADR 0022)
Enum field on Page (UNCLASSIFIED default, VS_NFD), migration backfills
existing pages. New pages take the instance-wide default from
classification.newPageDefault (admin-visible, de+en). The value rides in
every PageView, so no channel needs an extra request. The field is a
marking, not a protection mechanism: a test pins that permission
decisions are unchanged by it. The marking wording is fixed in ADR 0022
and sourced solely from classificationMarking() in @dorfteich/shared.

Co-Authored-By: Claude Fable 5 (1M context) <noreply@anthropic.com>
2026-07-31 06:01:50 +02:00

13 KiB
Raw Permalink Blame History

Maßnahmenplan VS-NfD — Dorfteich (Rev. 2, volle Tiefe)

Ziel: einsetzbar in einer nach VSA freigegebenen Umgebung, keine eigene BSI-Zulassung. Diese Revision zieht die vormals als Roadmap geführten Punkte in die Planung und weist die Terminwirkung aus.

Aufwand in Arbeitstagen (AT) für eine Person mit Claude Code. Erfahrungswert: Implementierung ist der kleinere Teil, Test und Dokumentation der größere. Die Schätzungen enthalten beides.


Stand 2026-07-30: In Meilensteine M24M31 und Issues #188#236 überführt (Volltexte und Anlage-Protokoll: 31-issue-entwurf.md, Befundgrundlage: 10-ist-aufnahme.md). Issue-Nummern stehen an den Checkboxen, Meilensteine an den Phasen.

Phase 0 — Nicht bauen (Leitplanken)

Diese Dinge machen die Situation schlechter, weil sie die Anwendung zur Trägerin einer Sicherheitsgrundfunktion nach §52 VSA machen würden:

  • Keine eigene Backup-Verschlüsselung — Datenträgerschutz ist Plattformsache
  • Keine Verschlüsselung von Inhalten in DB oder Filesystem
  • Kein eigenes MFA/TOTP, keine eigene Passwort-Policy-Engine
  • Keine neuen Krypto-Primitive
  • Keine anwendungsseitige Trennung von Einstufungsniveaus

Stattdessen: delegieren und dokumentieren.


Phase 1 — Blocker · Summe 3038 AT

P1-1 Fremdauthentisierung + lokale Auth abschaltbar · 1012 AT

Meilenstein: M27 — VS-NfD: external authentication

  • OIDC Authorization Code + PKCE gegen UserIdentity.provider (ADR 0007 ausbauen), Keycloak als Referenz-IdP · 56 AT · #214
  • Alternativpfad vertrauenswürdiger Reverse-Proxy-Header bzw. mTLS- Client-Zertifikat · 2 AT · #215
  • Harter Schalter auth.local.enabled = false inkl. Reset- und Registrierungs-Flows, PATs und Feed-Tokens · 2 AT · #216
  • Gruppen-/Rollen-Mapping aus IdP-Claims auf das Permission-Modell · 23 AT · #217

P1-2 Einstufung als First-Class-Metadatum · 1418 AT

Meilenstein: M26 — VS-NfD: classification metadata

  • Enum-Feld classification an Page, Migration, Default aus Instance-Setting · 2 AT · #204
  • Vererbung im Seitenbaum, Herabstufung nur mit eigenem Recht + Audit · 3 AT · #205
  • Durchreichen in alle Ausgabekanäle · 812 AT · #206#212
    • Web-Ansicht (Kopf/Fuß) · 1 AT · #206
    • Print-CSS (@media print, Kopf/Fuß je Seite) — fehlt komplett · 1 AT · #207
    • PDF via gotenberg (pdf-html.ts Header/Footer-Template) · 1 AT · #208
    • DOCX/ODT via pandoc (Reference-Doc mit Kopf-/Fußzeile) · 23 AT · #209
    • Markdown-ZIP (Frontmatter + Aufdruck) · 1 AT · #210
    • Atom-Feeds, Public-API, Suchergebnisse, No-JS-Shell · 23 AT · #211
    • Attachment-Download (Dateiname-Präfix + Begleitdatei) · 12 AT · #212
  • Warnung/Sperre beim Anhängen an eingestufte Seiten · 1 AT · #213

P1-3 Verifizierter Offline-/Airgap-Pfad · 810 AT ⟵ neu aus Roadmap

Meilenstein: M28 — VS-NfD: offline/airgap deployment — das Digest-Pinning (#203) läuft vorgezogen in M25

Hochgezogen, weil das eine Frage im ersten Behördengespräch ist. „Sollte gehen" ist dort eine schlechtere Antwort als „getestet, hier ist die Anleitung".

  • Alle Images auf Digest pinnen (schließt den gotenberg:8-Punkt ein) · 1 AT · #203
  • Mirror-Verfahren in interne Registry dokumentieren · 1 AT · #218
  • Build ohne Netz reproduzierbar (pnpm Offline-Store / reine Prebuilt-Images) · 23 AT · #219
  • Testlauf in netzisolierter Umgebung, Protokoll als Beleg · 2 AT · #220
  • Offline-Update-Pfad inkl. Migrationen · 23 AT · #221

Phase 2 — Billig, hohe Prüfer-Signalwirkung · Summe 2228 AT

Meilensteine: M24 — VS-NfD: security quick wins; die nachgezogenen Punkte (#199, #200, #201, #202) in M25 — VS-NfD: hardening & supply chain

  • Schlüsseltrennung COLLAB_TOKEN_SECRET per HKDF (zweckgebundene Subkeys) — echter Fund, vor allen Features · 12 AT · #188
  • Eigenbau-HMAC-JWT durch jose ersetzen · +23 AT · #188 ⟵ neu aus Roadmap Gebündelt mit der Zeile darüber, weil dieselbe Datei (packages/shared/src/token-crypto.ts). Einzeln wären es 56 AT. Achtung: Unsubscribe-Tokens leben lang in versandten Mails → Dual-Verify-Fenster einplanen.
  • CSRF fail-closed — fehlendes Origin und Referer wird derzeit durchgelassen · 1 AT · #189
  • Session-Timeout konfigurierbar, Default deutlich unter 30 Tagen, separates Idle-Timeout · 12 AT · #190
  • Feed-Token raus aus dem Query-Parameter, alternativ Feeds hart abschaltbar · 2 AT · #191
  • Backup-Ziele einschränkbar — Allowlist, WebDAV/rsync per Deploy vollständig deaktivierbar · 2 AT · #192
  • Pond-Purge implementieren — getrashte Ponds bleiben ewig liegen · 3 AT · #193
  • Orphan-File-Sweep implementieren, Attachment.deletedAt nutzen oder entfernen · 2 AT · #194
  • Papierkorb aus dem Suchindex entfernen statt query-seitig filtern · 2 AT · #195
  • Retention-Job für audit_log · 1 AT · #196
  • Security-Header (helmet), CORS explizit restriktiv · 1 AT · #197
  • SBOM in CI (CycloneDX/syft) + Lizenzreport als Artefakt · 12 AT · #202
  • deploy/compose/.env prüfen, Beispieldatei statt Realdatei · 0,5 AT · #198
  • Attachment-Integritätshashes · +23 AT · #199 ⟵ neu aus Roadmap SHA-256-Spalte, Berechnung beim Upload, Prüfung beim Download, Backfill-Migration. Nebennutzen: Orphan-Sweep, Dedup, Backup-Verifikation.
  • Plugins hart abschaltbar (plugins.enabled = false) · +2 AT · #200 ⟵ neu Deckt das Risiko „Codeausführung in der VS-Zone" für den Angebotsstand vollständig ab. Hash-Pinning siehe Phase 4.
  • Syslog/SIEM: Ereigniskatalog · +34 AT · #201 ⟵ neu aus Roadmap Der Code-Anteil ist klein (stdout-JSON reicht meist). Wert liegt im stabilen Ereigniskatalog: feste Event-IDs, dokumentierte Semantik und Felder, damit die Behörde SIEM-Regeln schreiben kann.

Phase 3 — Beweissicherung / Lesezugriffe · 820 AT ⟵ neu aus Roadmap

Meilenstein: M29 — VS-NfD: read-access audit trail (Variante A)

Der aufwändigste der nachgezogenen Punkte, und der mit dem größten Gestaltungsspielraum. Zwei Varianten:

Variante A (empfohlen): nur eingestufte Inhalte · 810 AT

Protokolliert werden Lesezugriffe ausschließlich auf Seiten mit classification = VS_NFD. Setzt P1-2 voraus.

  • Instrumentierung der Lesepfade: Seitenansicht, Public-API-GET, Attachment-Download, Export, No-JS-Shell, Collab-WS-Join · 4 AT · #222
  • Dedup-Fenster (eine Sitzung + eine Seite innerhalb N Minuten = ein Ereignis), sonst erzeugt Yjs-Sync eine Ereignisflut · 2 AT · #223
  • Getrennte Tabelle mit eigener Retention und Partitionierung · 2 AT · #224
  • Abschaltbar, Zweckbindung dokumentiert · 12 AT · #225

Vorteil über den Aufwand hinaus: Die Zweckbindung ist sauber begründbar („nur eingestufte Inhalte"), was die Personalrats-Diskussion beim Kunden erheblich entschärft.

Variante B: alle Lesezugriffe · 1820 AT

Zusätzlich Volumen-, Latenz- und Aufbewahrungsprobleme: gepufferte Schreibung ohne Ereignisverlust (ein verlorenes Ereignis ist eine Lücke in der Beweissicherung), Suchtreffer als eigene Ereignisklasse, Partitionierung zwingend.

Einordnung: Für „einsetzbar in zugelassener Umgebung" ist das kein zwingendes Produktmerkmal — Beweissicherung kann die Plattform erbringen. In der Praxis kann Plattform-Logging aber nicht beantworten, welche eingestufte Seite gelesen wurde (Proxy-Logs kennen URLs, nicht Einstufungen). In Leistungsbeschreibungen taucht das als Muss-Kriterium auf. Deshalb rein — aber in Variante A.


Phase 4 — Verbleibende Roadmap

Meilenstein: M31 — VS-NfD: backlog

Nur noch ein Punkt bleibt draußen:

  • Plugin-Allowlist mit Hash-Pinning · 810 AT · #232 Manifest mit SHA-256, Allowlist in instance_settings, Prüfung beim Laden, Admin-UI. Bleibt zurückgestellt, weil Phase 2 mit der harten Abschaltung das Risiko bereits schließt — und weil echte Code-Signierung ohne juristische Person ohnehin nicht verfügbar ist. Hash-Pinning ist die richtige Antwort, aber nicht die dringendste.

Phase 5 — Dokumentation · 1520 AT (vorher 1215)

Meilenstein: M30 — VS-NfD: compliance documentation

Wächst um ca. 25 %, weil jede neue Funktion Handbuch- und Härtungsabschnitte nach sich zieht.

  • Abgrenzungserklärung §52 VSA — welche Sicherheitsgrundfunktionen die Anwendung nicht erbringt und wem sie zufallen. Wichtigstes Einzeldokument. · 3 AT · #226 → 40-abgrenzungserklaerung.md
  • Härtungsleitfaden mit Referenzkonfiguration „VS-NfD-Betrieb": lokale Auth aus, Public-API aus, MCP aus, Feeds aus, Plugins aus, Backup nur lokal · 3 AT · #227
  • Sicherheitsdokumentation: Architektur, Datenflüsse, Netzplan, Ports/Dienste, Vertrauensgrenzen · 4 AT · #228
  • Betriebshandbuch: Installation (inkl. Airgap), Update, Backup/Restore, Löschung und Vernichtung, Rollentrennung · 45 AT · #229
  • Zuarbeit IT-Grundschutz APP.3.1 und CON.11.1, je Anforderung „Produkt / Betreiber / nicht anwendbar" · 34 AT · #230
  • Restrisikoliste mit bewusst offenen Punkten · 1 AT · #231

Terminwirkung

Block vorher Rev. 2
Phase 1 Blocker 2228 AT 3038 AT
Phase 2 Billigblock 1518 AT 2228 AT
Phase 3 Beweissicherung 810 AT (Var. A)
Phase 5 Dokumentation 1215 AT 1520 AT
Summe 4961 AT 7596 AT

Bei 4 produktiven Tagen pro Woche:

  • vorher: ca. 33,5 Monate
  • Rev. 2 mit Variante A: ca. 4,55,5 Monate
  • Rev. 2 mit Variante B: ca. 5,56,5 Monate
  • Plus Phase 4 (Hash-Pinning): weitere ~0,5 Monate

Der Zuwachs von ~2635 AT verteilt sich zu etwa zwei Dritteln auf Beweissicherung und Airgap-Verifikation. Beides sind Punkte, nach denen gefragt wird — nicht Punkte, die man erklären muss.

Empfehlung zur Reihenfolge: Termin für den Angebotsstand nicht verschieben. Phase 5 und der Billigblock sind nach ca. 3 Monaten fertig — das genügt, um Gespräche zu führen. Phase 1-3 laufen dahinter weiter. Ein Angebot mit belegter Dokumentation und laufender Umsetzung ist besser als ein fertiges Produkt ohne Gesprächspartner.


Zu klärende Punkte aus der Ist-Aufnahme

  • drawio-Plugin: geklärt — vendored unter packages/plugins/drawio/vendor/, lädt keine externe Editor-URL; kein Ausschlusskriterium (Ist-Aufnahme I-40)
  • Fließen Labels heute in Exporte? Neinexport.service.ts lädt labelIds nur für die Permission-Filterung (Ist-Aufnahme I-03a)
  • Was bricht ohne Internetzugang? → wird durch den Testlauf #220 beantwortet (Ist-Aufnahme I-28)
  • MCP-Gate-Duplikat: divergiert nicht — nutzt den zentralen PermissionService, eigenständig sind nur die Schalter (Ist-Aufnahme I-33)
  • Collab-WebSocket: WS-Ebene prüft nur das Token, aber Tokens leben 60 s und Grant-Entzug schließt Verbindungen per pg_notify; Randbedingung für #222 (Ist-Aufnahme I-41)

Ergänzungen aus der Ist-Aufnahme (2026-07-30)

Befunde der nachgezogenen 10-ist-aufnahme.md, die in diesem Plan fehlten — als Issues angelegt:

  • Conversion-Job-Payloads prunen — Rohbytes jedes Im-/Exports liegen unbefristet in conversion_jobs · 2 AT · #233 (M24, I-22)
  • Retention für mail_outbox — Digest-Mails tragen Seitentitel · 1 AT · #234 (M24, I-23)
  • page_links.target_slug-Residuum nach Purge entscheiden · 0,5 AT · #235 (M24, I-24)
  • Node-Version pinnen — Voraussetzung für #219 · 0,5 AT · #236 (M25, I-26)

Ohne eigenes Issue: IndexedDB-Kopie auf Endgeräten (I-25) — als Akzeptanzkriterium in #226 (Abgrenzungserklärung) und #231 (Restrisikoliste) verankert.


Vorhandene Stärken (im Angebot nach vorne stellen)

Nicht ausbauen, sondern belegen:

  • Keine Telemetrie, keine Update-Checks, keine CDNs, kein Runtime-Nachladen, Fonts self-hosted, CSP default-src 'self' — hier scheitern die meisten Konkurrenzprodukte
  • Zentrales, default-closed Berechtigungsmodell mit deny-wins und Route-Enumeration-Test — ein prüfbares Artefakt
  • Volltextsuche in Postgres statt externer Suchmaschine
  • Single-Tenant — passt zur empfohlenen Betriebsform „eine Instanz pro Einstufungsniveau"
  • Public-API und MCP zur Laufzeit hart abschaltbar, Default aus
  • Open Source unter MIT — Quelloffenheit ist im Prüfprozess ein Vorteil
  • Keine Verschlüsselung im Code = korrekte Architektur, nicht fehlende Funktion. So argumentieren.