dorfteich/docs/vs-nfd/97-isolationslauf-protokoll.md
Claude Fable 5 ccffcaadd6
All checks were successful
CI / Lint, typecheck, test (pull_request) Successful in 6m16s
CI / Build container images (pull_request) Successful in 1m26s
CI / Auth e2e pack (pull_request) Successful in 8m27s
CI / Import/export fidelity gate (pull_request) Successful in 58s
CD / Build and push images (push) Successful in 21s
CD / Deploy to Test (push) Successful in 14s
CD / Smoke tests against Test (push) Successful in 1m17s
CD / Promote to Int (push) Successful in 12s
CI / Lint, typecheck, test (push) Successful in 6m22s
CI / Build container images (push) Has been skipped
CI / Auth e2e pack (push) Successful in 8m12s
CI / Import/export fidelity gate (push) Successful in 57s
#220: protocol of the isolated deployment run
Full deployment exercised in a compose stack whose networks are all
internal: true — setup, login, live collaboration, search, upload, all
export formats, backup and restore. tcpdump full capture on both
bridges: zero packets leave the isolated subnets; the only outbound
attempt the application makes is SMTP, which fails contained in the
outbox (5 retries, then FAILED) while the instance stays fully
functional. The restore finding became #288, fixed earlier in this
chain and re-verified in the same stack. Plan checkbox P1-3 and the
I-28 open question ticked; operations handbook airgap section updated.

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

6.6 KiB
Raw Permalink Blame History

Isolationslauf-Protokoll — Deployment in netzisolierter Umgebung (Issue #220)

Nachweis für Maßnahmenplan P1-3 (ADR 0024): ein vollständiges Deployment läuft mit blockiertem Egress. Beantwortet zugleich die offene Frage der Ist-Aufnahme I-28 („Was bricht ohne Internetzugang?"). Belegstufe: live verifiziert — alle Ergebnisse stammen aus dem beschriebenen Lauf, Abweichungen sind ausgewiesen.

  • Datum: 2026-07-31
  • Host: lokaler Docker-Host (aarch64); Compose-Projekt dorfteich-isolated aus dem Produktions-Compose (deploy/compose/docker-compose.yml) plus Override.
  • Netzisolation: beide Compose-Netze (frontend, internal) mit internal: true — Docker legt die Bridges ohne NAT/Default-Route an; es existiert kein Egress-Pfad. Port-Publishing ist dadurch wirkungslos, alle Prüfzugriffe erfolgen aus einem Treiber-Container (node:22.15.1-alpine, fetch-basierte Prüfskripte) im frontend-Netz gegen http://api:3000.
  • Eigene Images: dorfteich-iso-{web,api,collab,backup}:isolated, lokal gebaut aus main@a758c9d; das backup-Image nach dem Befund unten neu aus 4f6596e (#288). Image-IDs: web 9d65c54f05fc, api 1826dedd662a, collab 8a1b2841b91c, backup 117ff18c7e71.
  • Dritt-Images (digest-gepinnt, Compose-Pins): postgres:17.5-alpine@sha256:6567bca8…, pandoc/core:3.6@sha256:5b8a29d9…, gotenberg/gotenberg:8@sha256:67097317….
  • Egress-Beweisführung: tcpdump -n -l not arp auf beiden Bridge-Interfaces des Hosts über die gesamte Laufzeit (Setup bis nach dem Restore-Re-Run).

Prüfschritte und Ergebnisse

Schritt Ergebnis
Installation / First-Run-Setup migrate-on-start (41 Migrationen) + Pre-Seed-Setup über SETUP_*-Env (Registrierung closed)
/readyz alle Checks ok (backup-Check erst nach dem ersten Backup-Lauf, s. Abweichung 1)
Login (Session-Cookie) Status 200
Seite anlegen Seite 3331f670… im Personal-Teich
Live-Kollaboration echter Collab-Client (HocuspocusProvider) über den collab-Dienst, Inhalt synced + persistiert
Markdown-Export 78 Zeichen, Inhalt vorhanden
Suche (Postgres-Volltext) 1 Treffer auf den Seiteninhalt
Datei-Upload + Download 201 / 200
DOCX-Export (pandoc-Sidecar) 9 926 Bytes
PDF-Export (Gotenberg-Sidecar) 20 903 Bytes
Mail-Enqueue 204 — Zustellverhalten s. eigener Abschnitt
Backup Set 20260731-132200 (Dump + Volume-Archiv), readyz-backup-Check danach ok
Restore nach Fix #288 — Exit 0, Inhalt exakt der Backup-Stand (s. Abweichung 2)

Egress-Bilanz

Vollständige Capture-Auswertung (alle Pakete, beide Bridges):

Bridge Pakete Ziel-Verteilung
internal 37 897 ausnahmslos 192.168.156.0/24192.168.156.0/24 (api/collab/backup ↔ db)
frontend 448 ausnahmslos 192.168.158.0/24192.168.158.0/24 (Treiber ↔ api/Sidecars)

Einzige weitere Frames: 2 × IPv6 Link-Local → ff02::16 (MLDv2 Multicast Listener Report, Kernel-Housekeeping der Bridge — kein Egress). Kein einziges Paket an ein Ziel außerhalb der isolierten Subnetze, keine DNS-Anfragen auf den Bridges. Die Anwendung unternimmt genau einen nach außen gerichteten Verbindungsversuch: SMTP (s. u.). Damit ist I-28 beantwortet: ohne Internetzugang bricht nichts außer der Mail-Zustellung; Telemetrie-, Update-, CDN- oder Font-Abrufe existieren nicht (bestätigt die Ist-Aufnahme §0.2).

SMTP-Verhalten ohne Egress

Die einzige nach außen gerichtete Verbindung; genau der Punkt, den eine Behörde zulassen oder untersagen wird:

  • POST-Auslöser antwortet 204 — die Outbox entkoppelt Annahme und Zustellung; kein Nutzer-Request hängt oder scheitert an fehlendem Egress.
  • Der Zustell-Worker scheitert bei jedem Versuch an der DNS-Auflösung: getaddrinfo EAI_AGAIN smtp.egress-nachweis.example (Dockers embedded DNS löst in internal-Netzen nicht extern auf; auf den Bridges ist dabei kein Paket sichtbar).
  • Retry-Leiter: 5 Versuche, danach Log mail delivery failed permanently (Level warn), Outbox-Status FAILED mit last_error. Die Instanz bleibt voll funktionsfähig.
  • Betriebsempfehlung: entweder ein von der Behörde zugelassenes internes SMTP-Relay konfigurieren oder bewusst ohne Mail betreiben (Einschränkung: keine Verifikations-/Benachrichtigungs-Mails; Site-Admin legt Konten dann manuell verifiziert an).

Abweichungen und Befunde

  1. readyz-backup-Check warn vor dem ersten Backup-Lauf („no backup status recorded yet") — erwartetes Verhalten des Status-File-Checks, nach dem ersten Lauf ok. Keine Maßnahme.
  2. Restore meldete FAILED trotz inhaltlich korrekter Wiederherstellung → Befund #288: pg_restore --clean scheiterte an den geerbten Partitions-PKs von read_events (partitioniert seit #224), Exit ≠ 0 bei 4 ignorierten Fehlern. Fix (Schema-Reset vor pg_restore, macht den Restore zugleich exakt) in derselben PR-Kette; Re-Run im selben isolierten Stack: Exit 0, bewusst angelegte Post-Backup-Tabelle entfernt, Seiteninhalt und alle read_events-Partitionen intakt, /readyz danach vollständig grün.

Rückfluss (Akzeptanzkriterium)

  • #288 — aus dem Lauf entstandener Fix, in der M28-PR-Kette.
  • #221 — der Offline-Update-Pfad wird in derselben isolierten Umgebung mit denselben Images geprobt.
  • #229 — Betriebshandbuch §1 (Airgap-Variante) auf diesen Nachweis aktualisiert.