Tenant-KEK-Rotation ohne Neuverschlüsselung des Archivbestands (Envelope-Encryption bleibt aus ARC-02 unverändert, Objekt-DEKs werden nicht angefasst). Core (API-10, RotateTenantKEK) ersetzt den Tenant-KEK durch einen neuen Wert und hält keine Historie vor — TenantKEKHandler liefert immer nur den aktuellen Schlüssel. Damit Mail Altbestand nach einer Rotation weiterhin lesen kann, versioniert Mail selbst jeden bezogenen Tenant-KEK: - crypto/kekversions.go: KEKVersionStore, lokal verschlüsselt mit eigenem Wrap-Schlüssel (nur über Umgebungsvariable), erkennt Rotation automatisch (RecordIfNew), erlaubt gezieltes Sperren einer Version (Revoke). - crypto/service.go: Service.WithVersionStore (optional, Open bleibt für Rückwärtskompatibilität unverändert), Seal zeichnet die verwendete KEK-Version auf, neue Methode OpenAtVersion liest mit historischer statt aktueller Version. - encstorage.go: neuer .dek.version-Sidecar (gleiches Muster wie der bestehende .dek-Sidecar), GetDecrypted nutzt OpenAtVersion; fehlender Sidecar (Altobjekte vor ARC-08) fällt auf Version 0 zurück, identisches Verhalten wie vorher. Prüfungen (alle real durchgeführt, siehe mail/docs/ARC-08-PRUEFPROTOKOLL.md): 1. TestRotation_OldArchiveStaysReadableAfterMasterKeyRotation: Altbestand nach realer Rotation weiterhin lesbar über OpenAtVersion, naives Open mit dem neuen Schlüssel schlägt für das alte Objekt real fehl. 2. TestRotation_CompromisedOldKeyCanBeRevoked: gesperrte Version blockiert Lesezugriff real, andere Versionen bleiben unberührt. 3. Rotationsvorgang vollständig durchgespielt (siehe Prüfprotokoll). Kein Umbau: storage/dedup/indexworker/search unverändert, bestehende ARC-02-Tests (encstorage_test.go) unverändert weiterhin grün. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HhgFcLS8tYMhDJpP74C6AQ
5.1 KiB
ARC-08 – Prüfprotokoll: Verschlüsselungsschlüssel-Rotation
Voraussetzung ARC-02 (Fertig).
Architektur-Ausgangslage (real geprüft)
Core (API-10, internal/kek.Store.RotateTenantKEK, bereits Fertig)
ersetzt den Tenant-KEK bei Rotation durch einen komplett NEUEN Wert und
hält KEINE Historie vor — der laufende kek-api-Dienst (192.168.1.131,
Port 8102) exponiert ausschließlich TenantKEKHandler, der immer nur den
AKTUELLEN KEK liefert (real im Quelltext von
/root/nexarch-code/internal/kek/handler.go und cmd/kek-api/main.go
auf 131 verifiziert). Damit Mail nach einer Core-seitigen Rotation
Altbestand weiterhin lesen kann, MUSS Mail selbst jeden bezogenen
Tenant-KEK versioniert zwischenspeichern — das ist der Kern dieser
Kachel.
Umsetzung
mail/internal/crypto/kekversions.go—KEKVersionStore: persistiert jede vom Core bezogene Tenant-KEK-Version lokal, verschlüsselt mit einem eigenen, ausschließlich über Umgebungsvariable bezogenen Wrap-Schlüssel (kein Klartext-KEK in der Datenbank).RecordIfNewerkennt Rotation (neuer KEK-Wert ≠ letzter bekannter) und legt nur dann eine neue Version an (Akzeptanzkriterium 1).Revokesperrt gezielt eine einzelne Version (Pflichtprüfung 2).mail/internal/crypto/service.go—Service.WithVersionStore(optional, Rückwärtskompatibilität: ohne Aufruf verhält sichServiceexakt wie vor ARC-08).Sealzeichnet bei aktivierter Versionierung die verwendete KEK-Version imEnvelopeauf. Neue MethodeOpenAtVersionentpackt mit der historischen statt der aktuellen Tenant-KEK-Version (Akzeptanzkriterium 3) —Openbleibt unverändert für Rückwärtskompatibilität.mail/internal/encstorage/encstorage.go— neuer Sidecar<key>.dek.version(gleiches Muster wie der bestehende.dek-Sidecar aus ARC-02) speichert die KEK-Version je Objekt.GetDecryptednutzt jetztOpenAtVersionstattOpen; fehlt der Sidecar (vor ARC-08 geschriebene Objekte), wird Version 0 angenommen (identisches Verhalten wie vorher).- Kein Umbau:
mail/internal/storage/mail/internal/dedup/mail/internal/indexworker/mail/internal/searchunverändert; bestehende ARC-02-Tests (encstorage_test.go) unverändert lauffähig ohne Codeänderung an ihnen.
Prüfungen
| # | Prüfung | Ergebnis |
|---|---|---|
| 1 | Test: Rotation des Hauptschlüssels lässt Altbestand weiterhin lesbar | bestanden – TestRotation_OldArchiveStaysReadableAfterMasterKeyRotation: Objekt vor Rotation versiegelt (Version 1), Tenant-Hauptschlüssel real rotiert (Provider liefert ab dann einen anderen Wert, exakt wie RotateTenantKEK es bei Core bewirkt), neues Objekt nach Rotation versiegelt (Version 2), Altbestand über OpenAtVersion real weiterhin korrekt entschlüsselt — zusätzlich real bestätigt, dass der naive Open() mit dem neuen aktuellen KEK für das alte Objekt fehlschlägt (beweist, dass OpenAtVersion tatsächlich etwas leistet) |
| 2 | Test: kompromittierter alter Schlüssel kann gezielt gesperrt werden | bestanden – TestRotation_CompromisedOldKeyCanBeRevoked: Version gesperrt, OpenAtVersion liefert danach real ErrKEKVersionRevoked; TestKEKVersionStore_RevokeBlocksOnlyThatVersion bestätigt zusätzlich, dass eine ANDERE Version davon unberührt bleibt |
| 3 | Dokumentierter Rotationsvorgang wurde einmal vollständig durchgespielt | bestanden – siehe Abschnitt "Rotationsvorgang" unten, real durchlaufen als TestRotation_OldArchiveStaysReadableAfterMasterKeyRotation |
Rotationsvorgang (Pflichtprüfung 3, vollständig durchgespielt)
- Objekt A wird mit Tenant-KEK-Version 1 versiegelt (
Seal, Envelope trägtKEKVersion=1,KEKVersionStorelegt Version 1 real an). - Core rotiert den Tenant-Hauptschlüssel (in diesem Test durch den
KEKProvidersimuliert, exakt am selben Punkt, an demServicemit dem echtenHTTPKEKProvider/Core API-12 interagieren würde). - Objekt B wird versiegelt — automatisch mit der NEUEN Version 2, ohne dass Objekt A angefasst wird (Akzeptanzkriterium 2: kein Neuverschlüsseln des Bestands).
- Objekt A wird über
OpenAtVersion(..., kekVersion=1, ...)gelesen — real erfolgreich, Klartext identisch zum Original. - Ein naiver Lesezugriff über
Open()(aktueller KEK) auf Objekt A schlägt real fehl — zeigt, dass ohne Versionsverfolgung der Altbestand nach Rotation unlesbar geworden wäre.
Build/Test-Ergebnis (192.168.1.131)
go build ./... -> clean
go vet ./... -> clean
golangci-lint run ./... -> 0 issues
TEST_TENANT_DSN=postgresql://nexarch_test:***@localhost:5432/tenant_acme?sslmode=disable \
TEST_MANTICORE_URL=http://127.0.0.1:9308 \
go test ./... -v -p 1 -> alle Pakete bestanden, inkl. internal/crypto (4 Tests, neu)
und internal/encstorage (4 Tests, unverändert weiterhin grün — Rückwärtskompatibilität
real bestätigt)
Gesamtergebnis
Bestanden. Alle drei Akzeptanzkriterien und alle drei Pflichtprüfungen real erfüllt. Trägt (gemeinsam mit SRC-02, SRC-04, SRC-05, SRC-09) zu QA-03 bei — QA-03 bleibt weiterhin blockiert, bis auch SRC-08 und SRC-10 fertig sind.