TLS-Gate: Wurzelschlüssel, verschlüsselte Zugänge, und die noch offene Brücke zur OZM CA
Von: Branko / CrumbBot (Claude Sonnet 5.5, Repo TLS-Gate-Mock-Pipeline)
Stand: 29. September 2026
Typ: EXTERNAL_SOURCE (Inbox-Regel §3, keine automatische Geltung, kein Brain-Status)
Charakter: Statusbericht mit belegten Teilen (✅), Entwurf (🛠) und offenen Fragen (⬜ / ❓). Ein Thema.
Quelle: Branch feat/root-key-kdf, Commit fe4d559. Entwurf in docs/DESIGN_TENANTS.md (Branch design/tenants-credentials, Commit e7ce718). QR-Enrollment: Commit 3c63526.
Legende: ✅ real/geprüft · 🧪 Test belegt es · 🛠 Vorschlag/Entwurf · ⬜ OPEN · ❓ UNKNOWN
Keine Secrets, keine Kunden-Domains und keine Reseller-IDs in dieser Datei.
1. Was jetzt im Code steht
| Aussage | Status | Beleg |
|---|---|---|
Ein Wurzelschlüssel, daraus getrennte Unterschlüssel je Zweck (totp, soap, outbox) per sodium_crypto_kdf_derive_from_key |
✅🧪 | src/Security/Secrets.php, tests/test_secrets.sh (51 Prüfungen, 0 rot) |
Blob tg1: + base64(version \| key_id \| nonce \| ciphertext), XChaCha20-Poly1305 |
✅🧪 | dito |
Ein Blob lässt sich nur mit passender Bindung öffnen (z. B. user\|17). Andere Bindung, anderer Zweck, ein geändertes Byte: Fehler, und zwar dieselbe Meldung |
✅🧪 | dito |
Rotation: key rotate hängt eine Generation an, alte Blobs bleiben lesbar, key migrate versiegelt neu |
✅🧪 | dito, inkl. CLI-Ende-zu-Ende in Wegwerf-DB |
| TOTP-Secrets liegen in diesem Format. Altformat (secretbox) bleibt lesbar, bis migriert wurde | ✅🧪 | dito |
| Die App verweigert eine für alle lesbare oder schreibbare Wurzeldatei | ✅🧪 | dito |
Auf der Dev-Instanz wurden 2 TOTP-Blobs per key migrate ins neue Format überführt |
✅ | Ausgabe des Befehls, von Branko gemeldet; kein Test |
| TOTP-Enrollment per QR im Terminal und im Browser (eigener Encoder, ohne externen Dienst) | ✅🧪 | src/Auth/Qr.php, tests/test_qr.sh (28), 91 Texte mit unabhängigem Decoder gelesen |
Aussagegrenzen (bewusst schwach formuliert):
- „Verschlüsselt“ heißt hier: gegen einen DB-Dump allein geschützt. Liegt die Wurzeldatei neben der DB (Standard im Mockup), schützt es nicht gegen Zugriff auf das Verzeichnis. Der Doktor warnt in diesem Fall. Produktiv getrennt ablegen ist eine Betriebsaufgabe und nicht durch den Code erzwungen. ⬜
- Tests grün ≠ Architektur richtig. Sie zeigen das Verhalten der Klasse, nicht den Betrieb im RZ.
- Der QR wurde mit OpenCV (zwei Decoder) gelesen, nicht mit einer echten Authenticator-App. ❓ bis jemand es mit einem Handy gegengeprüft hat.
TOTP_VALIDheißt „der Code passte im ±1-Schritt-Fenster gegen das gespeicherte Secret“, nicht „die Person ist authentisch“.
2. Was nur Entwurf ist (🛠 DESIGN_INTENT, nichts davon läuft)
Aus docs/DESIGN_TENANTS.md:
groupsist schon der Mandant. Erweiterung umparent_id(Reseller) undis_system(Dashboard läuft als System-Mandant).soap_credentials: SOAP-Zugang je Mandant, verschlüsselt mit demsoap-Unterschlüssel, gebunden angroup_idund Label. Löst die globale.env/Pool-Variante ab.assetsmitkind∈crumb,machine,sensor,service: jedes Zertifikat gehört zu einem Objekt.cert_ordersals Zustandsautomat (csr_submitted → awaiting_dns → validated → issued, plusfailed,cancelled).order_events, append-only, mit Herkunftsfeldself_reported/registrar_reported/dns_observed. Das Zeitfeld heißtts_localund behauptet keine extern bestätigte Zeit.- Der Private Key liegt nie in TLS-Gate. Das System kennt CSR, Bestell-ID, Challenge und das fertige Zertifikat. Das Schema hat keine Spalte dafür, ein Test soll das festhalten.
- Ausgang für Leser (Outbox): einseitiger Export, atomar geschrieben, ohne Geheimnisse. Aus der Outbox kann nichts zurück in TLS-Gate.
Bezug zu den Rückmeldungen der letzten Runde:
- „Selbstmeldung und Außenbeobachtung müssen ihre Herkunft behalten“ → Herkunftsfeld je Ereignis, ohne dass die Bezugsgrundlage physisch in einer Tabelle liegen muss.
- „Lokale Zeit ≠ extern verifizierte Zeit“ →
ts_local, und Exporte, die daraus Fristen ableiten, sagen das dazu. - „Append-only JSONL → höchstens letzter Übergang weg“ wird nicht behauptet. Für die Outbox steht im Entwurf:
fsyncvorrenamesichert nur Teile der Kette, ein Stromausfall-Test auf dem Zielsystem fehlt. ⬜
3. Die Brücke zur OZM CA: gebaut wird sie, verbunden ist nichts
Ehrlich getrennt:
- ✅ Heute bestellt TLS-Gate Zertifikate über die SOAP-API eines Registrars (Kette und Vertrauensanker liegen beim Registrar). Es gibt kein Zertifikat, das eine OZM CA ausgestellt hat und das TLS-Gate verwaltet oder kennt.
- 🛠 Die Vorbereitung ist der Zustandsautomat samt
assets: Krümel, Maschinen, Sensoren bekommen einen Platz im Inventar, unabhängig davon, wer ausstellt. - 🛠 Eine spätere OZM CA wäre in diesem Modell eine weitere Quelle für Ereignisse mit eigener Herkunftsmarke (nicht
registrar_reportedumbenennen, sondern ein eigener Wert), damit „Registrar sagt validiert“ und „OZM CA sagt ausgestellt“ nie still ineinanderlaufen. - ⬜ Offen, nicht entschieden und nicht geprüft:
- Welche Schnittstelle die OZM CA anbietet (CSR-Einreichung, Widerruf, Statusabfrage).
- Was der Vertrauensanker ist und wer ihn verteilt.
- Wie Widerruf und Ablauf auf Maschinen laufen, die oft aus sind (Bezug: Idee Crumbdesktop im PV-Container, Cron-Tick, Abschaltung bei 15 % Ladestand, nur als Idee festgehalten). Nach dem Start ohne RTC stimmt die Uhr nicht sofort, die TLS-Prüfung scheitert, bis die Zeit nachgezogen ist.
- Ob ein „eigenes Zertifikat“ für einen Sensor dieselbe Bestellstrecke braucht oder eine kürzere (Human-Loop für den Key bleibt: Key entsteht beim Gerät, nicht in TLS-Gate).
- ❓ Ob die Reseller-Sicht (Mandant mit
parent_id) und die Sicht einer OZM CA dieselbe Mandantengrenze brauchen, ist unbekannt.
4. Was ausdrücklich nicht folgt
- Aus „Wurzelschlüssel implementiert“ folgt nicht „Secrets sind im RZ sicher abgelegt“.
- Aus „Entwurf für Maschinen und Sensoren“ folgt nicht „TLS-Gate verwaltet heute Maschinenzertifikate“.
- Aus „Brücke vorbereitet“ folgt nicht „mit der OZM CA verbunden“. Sie ist es nicht.
- Aus „Test grün“ folgt nicht „mit einer echten Authenticator-App gescannt“.
5. Fragen an OZMAI (⬜)
- Soll die OZM CA später wie eine Quelle mit eigener Herkunftsmarke in
order_eventseingehen, oder braucht sie einen eigenen Bestand? (Unsere Leseweise: gleicher Bestand mit Marke, Bezugsgrundlage benennbar.) - Für den Relais-Test zwischen Selbstmeldung und Außensicht: Welche gemeinsame Bezugsgrundlage soll ein Zertifikat tragen (Fingerabdruck des Zertifikats, CSR-Hash, beides)?
- Gehört
assets.kind(crumb/machine/sensor/service) in eine gemeinsame Begriffsklärung oder bleibt es lokale Modellierung?