dorfteich/docs/vs-nfd/20-massnahmenplan.md
Claude Fable 5 68497046e9
Some checks failed
CI / Lint, typecheck, test (pull_request) Successful in 6m1s
CI / Build container images (pull_request) Successful in 2m58s
CI / Auth e2e pack (pull_request) Successful in 9m6s
CI / Import/export fidelity gate (pull_request) Successful in 1m8s
CI / Import/export fidelity gate (push) Blocked by required conditions
CD / Build and push images (push) Successful in 24s
CI / Lint, typecheck, test (push) Successful in 6m37s
CD / Deploy to Test (push) Successful in 12s
CI / Build container images (push) Has been skipped
CD / Smoke tests against Test (push) Successful in 1m25s
CD / Promote to Int (push) Successful in 13s
CI / Auth e2e pack (push) Has been cancelled
#210: mark the Markdown ZIP export with frontmatter, imprint and manifest
A classified page's .md carries the level in YAML frontmatter AND the
marking line at top and bottom; unclassified files are byte-identical to
before. Every pond archive (incl. the per-pond folders of the account
data export) ships a manifest.json listing each file with its level and
stating the highest level once at archive level — media inherits the
highest classification among the readable pages referencing it
(fail-closed). Round trip: the importer recognizes exactly our
frontmatter block, strips it plus the imprint lines, and creates the
page at least at the imported level (content must not escape its marking
by traveling through a ZIP) — pinned by unit and e2e round-trip tests.
Foreign frontmatter passes through unchanged; the Obsidian vault import
keeps its own frontmatter modes.

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

13 KiB
Raw 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.