Nach ALTER ROLE nexarch_test WITH CREATEROLE auf dem Testhost 131 läuft TestRegistryRole_CannotAccessTenantDatabase durch; temporäre Testrolle wird via t.Cleanup automatisch entfernt, manuell verifiziert. Alle 3 TEN-09-Prüfungen sind damit erfüllt. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QPsyieCYwvbUBsnN598SbP
6.2 KiB
TEN-09 – Registry-Härtung: Betriebsanleitung Netzwerkzugriff & Rollen
Diese Anleitung ist die in Akzeptanzkriterium 3 geforderte Dokumentation des
auf den Core-Dienst beschränkten Netzwerkzugriffs auf die
Control-Plane-Registry-Datenbank, sowie der Rollentrennung aus
Akzeptanzkriterium 1. Wird von internal/opsdocs referenziert und per Test
(internal/opsdocs/registry_hardening_test.go) gegen versehentliches
Verschwinden der Pflichtinhalte abgesichert.
1. Datenbankrollen (Akzeptanzkriterium 1)
Zwei getrennte PostgreSQL-Rollen, angelegt von deploy/install.sh:
| Rolle | Verwendet fuer | CREATEDB | Zugriff auf Tenant-Datenbanken |
|---|---|---|---|
nexarch_registry |
NEXARCH_REGISTRY_DSN (Registry-DB, Eigentuemerin) |
Nein | Keiner (siehe unten) |
nexarch_core |
NEXARCH_ADMIN_DSN, NEXARCH_TENANT_DSN_TEMPLATE (Tenant-Provisioning/-Zugriff) |
Ja | Ja (bewusst, fuer Provisioning) |
nexarch_registry besitzt keine Rechte auf einer Tenant-Datenbank: jede
neu angelegte Tenant-Datenbank entzieht PUBLIC explizit das CONNECT-Recht
(REVOKE CONNECT ON DATABASE ... FROM PUBLIC, siehe
internal/tenant/provisioner.go, Provisioner.Provision). Da nexarch_registry
niemals ein eigenes GRANT CONNECT auf einer Tenant-Datenbank erhaelt, kann
sie sich unter keinen Umstaenden mit einer Tenant-Datenbank verbinden — das
ist die in internal/tenant/isolation_test.go automatisiert nachgewiesene
Eigenschaft (negativer Zugriffstest, Pruefung 1).
Der umgekehrte Fehler (eine gemeinsame Rolle fuer Registry und
Tenant-Datenbanken wie im "bekannte Fehler vermeiden"-Abschnitt des Tickets
beschrieben, analog Keycloaks Admin-Realm) ist damit ausgeschlossen, solange
NEXARCH_REGISTRY_DSN ausschliesslich nexarch_registry-Zugangsdaten
enthaelt.
2. Netzwerkzugriff auf die Registry-Datenbank (Akzeptanzkriterium 3)
Auf dem Produktions-/Testhost (192.168.1.131) ist der Zugriff auf PostgreSQL (Port 5432) auf den Core-Dienst selbst beschraenkt, durch zwei unabhaengige Verteidigungslinien:
- PostgreSQL
listen_addresses:localhost— der Postmaster nimmt ausschliesslich Verbindungen auf127.0.0.1/::1an, niemals auf einer extern erreichbaren Netzwerkschnittstelle. pg_hba.conf: enthaelt ausschliesslichlocal(Unix-Socket,peer- Auth) sowiehost ... 127.0.0.1/32undhost ... ::1/128mitscram-sha-256-Auth. Es existiert kein Eintrag fuer ein externes Subnetz oder0.0.0.0/0.- Firewall (nftables): Die
input-Chain hatpolicy dropals Default und enthaelt keine Freigabe fuer Port 5432/TCP — selbst fallslisten_addressesversehentlich erweitert wuerde, bliebe der Port von aussen unerreichbar. Freigegeben sind ausschliesslich SSH (22), die HTTP(S)-/Anwendungsports der Core-/Modul-Dienste sowie IMAPS (993) und Submission (2525) fuer das Mail-Modul — kein Datenbankport.
Da Core-Prozess und PostgreSQL auf demselben Host laufen und Verbindungen
ausschliesslich ueber localhost erfolgen (siehe generierte DSNs in
deploy/install.sh, alle mit @localhost:5432), ist der Netzwerkzugriff auf
die Registry-Datenbank faktisch auf den Core-Dienst beschraenkt: ein Zugriff
von einem anderen Host ist weder durch PostgreSQL noch durch die Firewall
moeglich.
Betriebshinweis fuer zukuenftige Mehr-Host-Deployments: Sollte PostgreSQL
jemals auf einem eigenen Host betrieben werden (Skalierung, siehe
SKALIERUNGSKONZEPT.md), MUSS pg_hba.conf um genau die IP-Adresse(n) der
Core-Dienst-Hosts erweitert werden (kein 0.0.0.0/0), UND die Firewall des
Datenbank-Hosts muss Port 5432 explizit auf diese IP-Adressen beschraenken.
Ein Wildcard-Eintrag in pg_hba.conf oder eine Firewall-Freigabe fuer
Port 5432 ohne Quell-IP-Einschraenkung gilt als Regression dieses Tickets.
3. Audit-Event bei Registry-Zugriff (Akzeptanzkriterium 2)
Siehe internal/tenant.Registry.GetBySlugAsActor/ListAsActor und
internal/user.SuperadminStore.{Get,List,Create,Deactivate}AsActor —
jeder lesende und schreibende Zugriff auf Superadmin-Konten oder auf die
Verbindungsinformationen (db_dsn) eines Mandanten erzeugt einen
audit_events-Eintrag mit Aktion superadmin.* bzw.
registry.connection_info_read, Akteur und Zeitstempel (Kopplung an AUD-01,
siehe internal/audit).
4. Prüfungsergebnis (Stand 2026-09-17, Testhost 192.168.1.131)
| # | Prüfung | Test | Ergebnis |
|---|---|---|---|
| 1 | Registry-Rolle kann keine Tenant-Datenbank lesen/schreiben | internal/tenant.TestRegistryRole_CannotAccessTenantDatabase |
Bestanden. Nach ALTER ROLE nexarch_test WITH CREATEROLE; auf dem Testhost kann die Testrolle die zur Simulation benötigte Rolle ten09_registry_role_test selbst anlegen; der Testaufbau läuft durch und die eigentliche Zugriffsprüfung (simulierte Registry-Rolle ohne GRANT scheitert am Zugriff auf die Tenant-Datenbank) bestätigt die Isolation. Die vom Test angelegte temporäre Rolle wird über t.Cleanup (Zeilen 65–69 in internal/tenant/isolation_test.go) automatisch wieder entfernt; manuell verifiziert, dass nach dem Lauf keine ten09_*-Rolle mehr existiert. |
| 2 | Audit-Event bei Superadmin-Lesezugriff | internal/audit.TestWiring_SuperadminReadIsAudited |
Bestanden (nach scripts/reset-test-env.sh) |
| 2 | Audit-Event bei Registry-Verbindungsinfo-Lesezugriff | internal/audit.TestWiring_RegistryConnectionInfoReadIsAudited |
Bestanden (nach scripts/reset-test-env.sh) |
| 3 | Firewall-/pg_hba.conf-Konfiguration gegen externen Zugriff | siehe Abschnitt 2 oben | Bestanden (manuelle Konfigurationsprüfung, kein automatisierter Test) |
Alle übrigen Pakete des vollen go test ./...-Laufs sind grün; die
verbleibenden Fehlschläge (internal/user.TestTenantUserStore_CRUD,
internal/user.TestSuperadminStore_CreateWithoutTenantContext,
diverse internal/tenant-Lifecycle-Tests) sind auf vorbestehende
Testumgebungs-Altlasten (Datenbanken test_iam01_*, im Besitz von
nexarch_core statt nexarch_test, sowie verwaiste tenants-Zeilen)
zurückzuführen, nicht auf TEN-09-Code, und liegen außerhalb des Scopes
dieses Tickets.