#226: add the §52 VSA delimitation statement
Some checks failed
CI / Build container images (pull_request) Successful in 1m10s
CI / Auth e2e pack (pull_request) Successful in 7m46s
CI / Import/export fidelity gate (pull_request) Successful in 56s
CD / Deploy to Test (push) Blocked by required conditions
CD / Build and push images (push) Has been cancelled
CI / Lint, typecheck, test (pull_request) Successful in 4m43s
CD / Smoke tests against Test (push) Blocked by required conditions
CD / Promote to Int (push) Blocked by required conditions
CI / Auth e2e pack (push) Blocked by required conditions
CI / Import/export fidelity gate (push) Blocked by required conditions
CI / Build container images (push) Blocked by required conditions
CI / Lint, typecheck, test (push) Has been cancelled

Reviewer-facing document derived from ADR 0019: per base function
(encryption, media protection, network termination, authentication,
integrity) what the application does, what it deliberately does not,
and which party provides it — every claim traceable to code via the
ist-aufnahme. Includes the operator-duty handover (with the IndexedDB
endpoint copy named explicitly, I-25) and the delta list mapping every
divergence from the target state to its closing issue.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0168Ph5uBmHm8X28CSVpbpnJ
This commit is contained in:
Claude Fable 5 2026-07-30 06:29:05 +02:00
parent fd07f716f6
commit 3e377aaa57
2 changed files with 319 additions and 2 deletions

View File

@ -181,9 +181,9 @@ _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
- [x] **Abgrenzungserklärung §52 VSA** — welche Sicherheitsgrundfunktionen die
Anwendung _nicht_ erbringt und wem sie zufallen. Wichtigstes
Einzeldokument. · 3 AT · #226
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

View File

@ -0,0 +1,317 @@
# Abgrenzungserklärung nach §52 VSA — Dorfteich
> Liefergegenstand zu Issue #226 (Meilenstein `M30`, compliance
> documentation). Grundlage: ADR 0019 (Entwurfsfassung dieser
> Erklärung); bei inhaltlicher Abweichung wird zuerst das ADR geändert,
> dann diese Erklärung nachgezogen — nie umgekehrt.
> Code-Stand der Belege: `main` = `fd07f71` (Prod v0.12.0); Fundorte in
> der Form `datei:zeile` beziehen sich auf diesen Stand und sind in
> `docs/vs-nfd/10-ist-aufnahme.md` ausführlich hergeleitet.
> Status: Entwurf zur Prüfung. Die juristische Prüfung durch einen
> Rechtsbeistand steht aus und ist nicht Teil dieses Dokuments.
## 1. Zweck und Adressat
Dorfteich soll in einer nach der Verschlusssachenanweisung (VSA)
freigegebenen IT-Umgebung einer Behörde betrieben werden können, für
Inhalte bis einschließlich VS-NUR FÜR DEN DIENSTGEBRAUCH (VS-NfD). Eine
BSI-Zulassung von Dorfteich selbst wird **nicht** angestrebt.
§51 VSA unterwirft IT-Produkte, die eine _Sicherheitsgrundfunktion_
erbringen, der Zulassungspflicht. §52 VSA benennt diese Grundfunktionen:
**Verschlüsselung, Datenträgerschutz, Netzabschluss und
Authentisierung**. Diese Erklärung legt dar, dass Dorfteich keine dieser
Grundfunktionen selbst erbringt, welche Partei sie stattdessen erbringt,
und wo der heutige Code-Stand dieser Erklärung noch nicht entspricht
(Abschnitt 7).
Adressat sind Geheimschutzbeauftragte und IT-Sicherheitsbeauftragte
der betreibenden Behörde sowie prüfende Stellen. Die Erklärung ist
zugleich die schriftliche Übergabe der Betreiberpflichten (Abschnitt 6):
was die Anwendung bewusst nicht leistet, muss die Plattform der Behörde
leisten — das ist die Arbeitsteilung, die §52 VSA vorzeichnet, keine
Lücke der Anwendung.
## 2. Geltungsbereich und Betriebsmodell
Die Erklärung gilt für die Anwendung Dorfteich in der
Referenz-Zusammensetzung aus `deploy/compose/docker-compose.yml`: die
vier eigenen Dienste web, api, collab und backup sowie die Sidecars
PostgreSQL, pandoc und Gotenberg (Caddy optional als Compose-Profil).
Vorausgesetzt wird das Betriebsmodell aus ADR 0019:
- **Eine Instanz je Einstufungsniveau.** Die Anwendung trennt
Einstufungsniveaus nicht selbst; die Trennung geschieht außerhalb der
Anwendung durch getrennte Instanzen. Innerhalb einer Instanz
_kennzeichnet_ die Anwendung eingestufte Inhalte lediglich (ADR 0022).
- Die Instanz ist Single-Tenant (33 Prisma-Modelle, kein Mandanten-Modell;
Ist-Aufnahme I-35) — Teiche sind Container innerhalb einer Instanz,
keine Sicherheitsgrenze zwischen Einstufungsniveaus.
- Betrieb in der VS-NfD-Referenzkonfiguration des Härtungsleitfadens
(#227): lokale Anmeldung aus, Public-API aus, MCP aus, Feeds aus,
Plugins aus, Backup nur lokal.
## 3. Grundsatz
**Dorfteich erbringt keine Sicherheitsgrundfunktion im Sinne von
§52 VSA. Verschlüsselung, Datenträgerschutz, Netzabschluss und
Authentisierung fallen der Plattform des Betreibers zu** (ADR 0019).
Das ist Architektur, nicht Unterlassung: Jede dieser Funktionen in der
Anwendung selbst würde Dorfteich zum Träger einer Grundfunktion machen
und in die Zulassungspflicht nach §51 VSA ziehen. Die Anwendung hält sich
deshalb bewusst frei von Inhaltsverschlüsselung, Backup-Verschlüsselung,
eigenem MFA und eigenen Krypto-Primitiven — und jeder künftige
Funktionsvorschlag wird an ADR 0019 gemessen, bevor er gebaut wird.
Was die Anwendung an Sicherheit selbst beiträgt, ist ein anderer, ihr
zufallender Satz von Eigenschaften (Abschnitt 5): zentrales
default-closed-Berechtigungsmodell, vollständige Abwesenheit ausgehender
Verbindungen, Kennzeichnung eingestufter Inhalte in jedem Ausgabekanal
und ein Audit-Trail.
## 4. Die Grundfunktionen im Einzelnen
Je Grundfunktion: was die Anwendung tut, was sie bewusst nicht tut, und
wer die Funktion erbringt. Jede Aussage ist am Code belegt.
### 4.1 Verschlüsselung
**Was die Anwendung tut.** Die vollständige Inventur eigener
Krypto-Aufrufe (Ist-Aufnahme, Abschnitt 3.1):
| Zweck | Verfahren | Fundort |
| ------------------------ | ------------------------------ | --------------------------------------------------- |
| Passwörter | Argon2id (Bibliothek `argon2`) | `apps/api/src/users/password.ts:925` |
| Session-Ids | SHA-256 über das Token | `apps/api/src/auth/sessions.service.ts:84` |
| Auth-Tokens (Mail-Flows) | SHA-256 | `apps/api/src/auth/auth-tokens.service.ts:55` |
| PATs | SHA-256 | `apps/api/src/public-api/api-tokens.service.ts:149` |
| Feed-Tokens | SHA-256 | `apps/api/src/public/feed-tokens.service.ts:73` |
| Collab-Tokens (TTL 60 s) | HS256-JWT | `packages/shared/src/token-crypto.ts` |
| Unsubscribe-Tokens | HMAC-SHA-256 | `apps/api/src/notifications/unsubscribe-token.ts` |
Sämtliche Verfahren dienen der Absicherung von Zugangsdaten und
kurzlebigen Tokens — **nirgends im Code werden Inhalte ver- oder
entschlüsselt**.
**Was die Anwendung bewusst nicht tut.** Keine Verschlüsselung von
Inhalten in Datenbank oder Dateisystem (Anhänge liegen unverschlüsselt
unter `UPLOADS_DIR/<pondId>/<fileId>`,
`apps/api/src/files/file-storage.service.ts:21,25`). Keine
Backup-Verschlüsselung (`apps/backup/`, ADR 0015, ADR 0026). Keine
TLS-Implementierung. Keine eigenen kryptographischen Primitive über die
obige Tabelle hinaus; die vorhandene Token-Signatur wird auf eine
geprüfte Bibliothek mit zweckgebundenen Schlüsseln umgestellt (ADR 0020,
#188) — eine Verengung bestehender Krypto, keine neue Grundfunktion.
**Wer die Funktion erbringt.** Die Plattform des Betreibers:
Vertraulichkeit ruhender Daten durch Volume- bzw.
Festplattenverschlüsselung der Hosts (Datenbank, Upload-Verzeichnis,
Backup-Ablage), Transportverschlüsselung durch die vom Betreiber
betriebene TLS-Terminierung (Abschnitt 4.3).
### 4.2 Datenträgerschutz
**Was die Anwendung tut.** Sie hält die Zahl und Lebensdauer der
Datenkopien klein und benennt sie vollständig (Ist-Aufnahme,
Abschnitt 3.2): Inhalte liegen ausschließlich in PostgreSQL (fünf
Repräsentationen je Seite), Anhänge im Dateisystem, Backups in der
konfigurierten Ablage mit konfigurierbarer Retention. Löschung ist
zweistufig (Papierkorb → Purge-Job), der Seiten-Purge erfasst Anhänge,
Content-Cache, Update-Log und abhängige Tabellen (Ist-Aufnahme I-42).
Der PWA-Service-Worker cached **keine** Inhalte, nur Build-Assets
(`apps/web/vite.config.ts:1220`). Künftig beschränkt die Anwendung,
_wohin_ Backups gehen dürfen (Deploy-Allowlist, leere Liste = nur lokal;
ADR 0026, #192) — mehr nicht.
**Was die Anwendung bewusst nicht tut.** Keine Verschlüsselung ruhender
Daten (s. 4.1), keine sichere Vernichtung physischer Datenträger, kein
Schutz der Endgeräte.
**Wer die Funktion erbringt.** Der Betreiber: Festplatten-/
Volume-Verschlüsselung der Server, Behandlung und Vernichtung von
Datenträgern (einschließlich Backup-Medien) nach den Regeln der Behörde,
und — ausdrücklich — der **Schutz der Endgeräte**:
> **Inhaltskopie auf dem Endgerät (Betreiberpflicht).** Jede im Editor
> geöffnete Seite wird als Yjs-Dokument in die IndexedDB des Browsers
> gespiegelt (`apps/web/src/editor/use-collab-provider.ts:76`, Datenbank
> `dorfteich-page-<pageId>`; Ist-Aufnahme I-25). Beim Verlassen der Seite
> wird die Kopie gelöscht, sobald der Server synchronisiert hat; bei
> unsynchronisierten Offline-Änderungen bleibt sie **absichtlich**
> stehen, und bei einem Browser-Abbruch läuft die Aufräum-Routine nicht.
> Die Vertraulichkeit dieser Kopie ist Datenträgerschutz des Endgeräts —
> eine Funktion der Plattform (Endgeräteverschlüsselung, Gerätehärtung
> nach den VS-NfD-Regeln der Behörde), nicht der Anwendung. Die Anwendung
> behauptet ausdrücklich **nicht**, keine Spuren auf Endgeräten zu
> hinterlassen.
### 4.3 Netzabschluss
**Was die Anwendung tut.** Nichts — und das belegbar auch in der
Gegenrichtung: **Die Anwendung baut keine Verbindungen nach außen auf.**
Jeder ausgehende Aufruf im Code geht an einen internen Sidecar
(`PANDOC_URL`, `GOTENBERG_URL`), an den konfigurierten SMTP-Server oder
an das konfigurierte Backup-Ziel (Ist-Aufnahme I-31, mit vollständiger
Fundortliste). Keine Telemetrie, keine Update-Checks, kein Analytics,
kein Crash-Reporting, keine Link-Vorschauen, kein oEmbed, keine externen
Schriften, Karten oder Skripte; drawio ist vendored, Mermaid und
Excalidraw sind gebündelte Workspace-Pakete, und die CSP
`default-src 'self'` des Web-Tiers würde Nachlader ohnehin blocken.
**Was die Anwendung bewusst nicht tut.** Keine TLS-Terminierung, keine
Netzsegmentierung, keine Firewall-Funktion, kein VPN. Die Dienste bieten
unverschlüsseltes HTTP/WebSocket auf ihren Compose-internen Ports an.
**Wer die Funktion erbringt.** Der Betreiber: Netzabschluss, Zonierung
und Zugangskontrolle seines Netzes; TLS-Terminierung am
Reverse-Proxy der Behörde. Der Netzplan mit allen Ports und
Vertrauensgrenzen ist Gegenstand der Sicherheitsdokumentation (#228);
die Vertrauensstellung zum Reverse-Proxy wird in #215 ausgestaltet.
### 4.4 Authentisierung
**Zielbild (das diese Erklärung beschreibt).** Authentisierung ist an
den Identity-Provider des Betreibers delegiert (ADR 0021): Anmeldung per
OIDC Authorization Code mit PKCE gegen den IdP der Behörde (#214),
lokale Anmeldung einschließlich Registrierung, Passwort-Reset, PATs und
Feed-Tokens per hartem Schalter `auth.local.enabled = false` vollständig
abgeschaltet (#216), Gruppen-/Rollen-Mapping aus dem IdP (#217). MFA,
Passwort-Richtlinien und Konto-Lebenszyklus sind damit Sache des IdP.
**Was die Anwendung heute tut — und der heutige Stand entspricht dem
Zielbild noch nicht** (Ist-Aufnahme I-01/I-02, dort als BLOCKIEREND
bewertet): Es existiert nur die lokale Anmeldung
(Benutzername/Passwort, Argon2id) mit eigener serverseitiger
Session-Verwaltung (Session-Id = SHA-256-Hash des Tokens,
`httpOnly`/`sameSite: lax`/`secure`-Cookie,
`apps/api/src/auth/sessions.service.ts`, `auth.guard.ts:5258`). Eine
OIDC-Anbindung ist vorbereitet (`UserIdentity.provider`,
`apps/api/prisma/schema.prisma:562577`), aber nicht implementiert; die
lokale Anmeldung ist nicht abschaltbar. **Bis #214/#216 umgesetzt sind,
ist Dorfteich faktisch Träger der Grundfunktion Authentisierung** — die
Delta-Liste (Abschnitt 7) weist das als die wichtigste offene Position
aus.
**Was die Anwendung dauerhaft behält.** Die _Autorisierung_ (wer darf
welche Inhalte sehen und ändern) bleibt anwendungsseitig — sie ist keine
Grundfunktion nach §52 VSA, sondern Fachlogik: zentrales
default-closed-Berechtigungsmodell mit deny-wins, durchgesetzt über
Guards und abgesichert durch einen Route-Enumeration-Test
(`apps/api/src/permissions/`; Ist-Aufnahme I-33). Die serverseitige
Session-Haltung bleibt als Sitzungsverwaltung _nach_ erfolgter
Authentisierung ebenfalls bestehen (konfigurierbare Lebensdauer und
Idle-Timeout: #190).
**Wer die Funktion erbringt.** Der Identity-Provider des Betreibers,
einschließlich MFA und aller Credential-Richtlinien. Für Umgebungen mit
Client-Zertifikaten oder vorgelagerter Proxy-Authentisierung ist #215
der vorgesehene Pfad.
### 4.5 Integrität (soweit anwendbar)
Integrität ist in §52 VSA keine eigene Grundfunktion, wird hier aber der
Vollständigkeit halber abgegrenzt, weil sie zwischen Plattform und
Anwendung geteilt ist:
**Bei der Anwendung** liegt, was nur sie leisten kann (ADR 0019):
- **Kennzeichnung eingestufter Inhalte** — nur die Anwendung kennt die
Einstufung einer Seite (ADR 0022; Einführung des Metadatums #204/#205,
Aufdruck in allen Ausgabekanälen #206#212).
- **Integrität der eigenen Nutzdaten**: SHA-256-Prüfsummen auf Anhängen
(#199, heute nicht vorhanden — Ist-Aufnahme I-18) und Hash-Pinning
für Plugin-Bundles statt Code-Signierung (ADR 0025, #232).
**Bei der Plattform** liegt die Integrität von Betriebssystem,
Container-Runtime, Netz und Datenträgern. Die Reproduzierbarkeit der
Auslieferung (Image-Digests #203, Node-Pin #236, Offline-Build #219)
ist Zulieferung der Anwendung an die Integritätskette des Betreibers,
keine eigene Grundfunktion.
## 5. Was die Anwendung selbst beiträgt
Die Sicherheitsleistung der Anwendung besteht — neben der Abwesenheit
der Grundfunktionen — aus vier Eigenschaften, die sie selbst trägt und
belegt:
1. **Zentrales Berechtigungsmodell**, default-closed, deny-wins, mit
Route-Enumeration-Test; auch Export, Suche, MCP und die Collab-Ebene
laufen über den zentralen `PermissionService` (Ist-Aufnahme I-33,
I-41; unautorisierte Reads antworten 404, Existenz wird versteckt).
2. **Keine ausgehenden Verbindungen** außer Sidecars, SMTP und
Backup-Ziel (Ist-Aufnahme I-31) — die Grundlage der
Airgap-Tauglichkeit, deren empirischer Nachweis als Testlauf #220
geführt wird.
3. **Kennzeichnung** eingestufter Inhalte in jedem Ausgabekanal
(ADR 0022, #204#212 — geplant, s. Delta-Liste).
4. **Audit-Trail** für Authentisierungs- und Administrations-Ereignisse
(`audit_log`, `apps/api/src/audit/audit.service.ts`), künftig mit
konfigurierbarer Retention (#196), stabilem Ereigniskatalog für
SIEM-Anbindung (#201) und einem zuschaltbaren Lesetrail für
eingestufte Inhalte (ADR 0023, #222#225). Logs gehen als
pino-JSON auf stdout mit Redaction von `authorization`/`cookie`
(`apps/api/src/app.module.ts:8493`); die Weiterleitung ins SIEM ist
bewusst Sache der Container-Runtime des Betreibers.
## 6. Betreiberpflichten (Übergabe)
Aus der Arbeitsteilung folgt, was der Betreiber leisten muss. Diese
Liste ist die schriftliche Übergabe; die Referenzkonfiguration und die
Prüf-Checkliste dazu liefert der Härtungsleitfaden (#227):
| Nr. | Pflicht | Grundfunktion |
| --- | ------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------- |
| B1 | Volume-/Festplattenverschlüsselung der Hosts für Datenbank, Upload-Verzeichnis und Backup-Ablage | Verschlüsselung |
| B2 | TLS-Terminierung vor der Anwendung; kein unverschlüsselter Zugriff außerhalb der Compose-internen Netze | Verschlüsselung |
| B3 | Netzabschluss, Zonierung und Zugangskontrolle; Egress-Blockade bis auf SMTP und ggf. Backup-Ziel | Netzabschluss |
| B4 | Identity-Provider einschließlich MFA, Passwort-Richtlinien und Konto-Lebenszyklus (sobald #214/#216 umgesetzt; bis dahin s. Delta-Liste) | Authentisierung |
| B5 | Endgeräteschutz einschließlich Geräteverschlüsselung — Seiteninhalte liegen als IndexedDB-Kopie auf jedem Endgerät, das sie geöffnet hat (s. 4.2) | Datenträgerschutz |
| B6 | Behandlung und Vernichtung von Datenträgern und Backup-Medien nach den Regeln der Behörde | Datenträgerschutz |
| B7 | Betrieb **einer Instanz je Einstufungsniveau**; keine Mischinstanz | (Betriebsmodell) |
| B8 | Log-Weiterleitung und -Aufbewahrung (stdout → SIEM/Syslog der Behörde) | (Protokollierung) |
| B9 | Betrieb in der VS-NfD-Referenzkonfiguration (#227) und Nachvollzug von Abweichungen | (Konfiguration) |
## 7. Delta-Liste — wo der heutige Stand dieser Erklärung noch nicht entspricht
Diese Erklärung beschreibt das Zielbild. Am Code-Stand `fd07f71` bestehen
folgende Abweichungen; jede ist einem Issue zugeordnet, das sie schließt
(Meilensteine `M24``M31`, Herleitung `docs/vs-nfd/10-ist-aufnahme.md`):
| Abweichung vom Zielbild | Befund | Issue |
| ------------------------------------------------------------------------------------------------------------- | ---------------- | --------------------- |
| Lokale Anmeldung ist der einzige und nicht abschaltbare Auth-Pfad — Grundfunktion liegt noch in der Anwendung | I-01, I-02 | #214, #216 |
| IdP-Gruppen-/Rollen-Mapping fehlt | I-01 | #217 |
| Kein Einstufungsmetadatum, kein Aufdruck in irgendeinem Ausgabekanal | I-03, I-04 | #204#212 |
| Backup-Ziel frei wählbar (keine Allowlist, kein Deploy-Kill-Switch) | I-11 | #192 |
| Ein Secret signiert zwei Token-Zwecke; JWT handgeschrieben statt Bibliothek | I-06, I-07 | #188 |
| CSRF-Origin-Prüfung fail-open bei fehlenden Headern | I-08 | #189 |
| Session 30 Tage gleitend, hart kodiert, ohne Idle-Timeout | I-09 | #190 |
| Feed-Token im Query-Parameter; Feeds nicht abschaltbar | I-10 | #191 |
| Plugins nicht instanzweit abschaltbar | I-19 | #200 |
| Keine Security-Response-Header, CORS-Haltung nicht als Entscheidung dokumentiert | I-16 | #197 |
| Keine Integritäts-Prüfsummen auf Anhängen | I-18 | #199 |
| Kein Plugin-Hash-Pinning | — | #232 |
| `audit_log` ohne Retention; Ereigniskatalog nicht stabil; kein Lesetrail | I-15, I-20, I-21 | #196, #201, #222#225 |
| Teich-Papierkorb ohne endgültiges Löschen; Papierkorb-Inhalte im Suchindex | I-12, I-14 | #193, #195 |
| Rohdokument-Bytes und Mail-Outbox unbefristet; Slug-Residuum nach Purge | I-22I-24 | #233#235 |
| Container-Images nicht per Digest gepinnt, Node-Version nicht gepinnt, Airgap unverifiziert | I-05, I-26, I-28 | #203, #218#221, #236 |
Bewusst offene Punkte, die keine Abweichung, sondern akzeptierte
Restrisiken sind (z. B. Seitentitel in Logs und Digest-Mails,
Slug-Residuum falls in #235 so entschieden), führt die Restrisikoliste
(#231) — nicht diese Erklärung.
## 8. Pflege dieser Erklärung
- Maßgeblich ist ADR 0019; Abweichungen werden dort aufgelöst, diese
Erklärung folgt dem ADR.
- Jedes Feature, das die Anwendung zum Träger einer Grundfunktion machen
würde, erfordert zuerst eine Änderung von ADR 0019 — und damit dieser
Erklärung.
- Die Delta-Liste (Abschnitt 7) wird mit jedem geschlossenen Issue
gekürzt; die Erklärung ist erst dann uneingeschränkt gültig, wenn die
Delta-Liste leer ist oder alle Restpositionen in der Restrisikoliste
(#231) vom Betreiber gebilligt sind.
- Querbezüge: Härtungsleitfaden (#227), Sicherheitsdokumentation (#228),
Betriebshandbuch (#229), IT-Grundschutz-Zuarbeit (#230).