TLS-Gate v3.0.0: Stand, Herkunftsmarken, Node-CA mit Widerruf, Migration

Von: Branko / CrumbBot (Claude, 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 (✅🧪), Grenzen und offenen Fragen (⬜ ❓). Ein Thema.
Quelle: Tag v3.0.0 = Commit 10c2db9 auf main (git.crumbforest.org/branko/TLS-Gate-Mock-Pipeline). Vorgänger in dieser Inbox:
2026-09-29_TLS-GATE-WURZELSCHLUESSEL-UND-BRUECKE-ZUR-OZM-CA_BRANKO.md (Stand fe4d559).

Legende: ✅ real/geprüft · 🧪 Test belegt es · 🛠 Vorschlag/Entwurf · ⬜ OPEN · ❓ UNKNOWN

Keine Secrets, keine Kunden-Domains, keine Reseller-IDs. Beispiele im Repo nutzen example.org/.invalid.


1. Was seit der letzten Notiz dazugekommen ist

Aussage Status Beleg
SOAP-Zugänge je Gruppe, versiegelt in der DB, gebunden an Gruppe + Label. Globale Umgebungsvariablen entfernt ✅🧪 src/Security/CredentialStore.php, tests/test_credentials.sh (59)
Kein stiller Mock: ohne Zugang oder bei Störung „SOAP nicht verfügbar“, Beispieldaten nur mit ausdrücklichem Schalter ✅🧪 dito, tests/test_auth.sh (73)
CLI nur für root/sudo, Akteur (SUDO_USER) im Audit; eine eingeschränkte Klasse für den Dienst-User (Cron) ✅🧪 src/Security/Guard.php, tests/test_guard.sh (40)
Bestellzustand als sprachneutraler Vertrag (database/order_states.json, Version 4), Ereignisse append-only mit Herkunftsmarke ✅🧪 src/Orders/, tests/test_orders.sh (116)
Cron-Tick: je Bestellung höchstens ein Schritt, nur lesend, bestellt nie ✅🧪 bin/order_tick.php, tests/test_tick.sh (71)
Einseitiger Export (Outbox) im Format des vorhandenen HTML-Monitors, mit Herkunft je Zertifikat und optionaler Außensicht ✅🧪 bin/export.php, tests/test_export.sh (72)
Eigene Test-CA der Node, Widerruf mit echter X.509-CRL ✅🧪 bin/node_ca.php, tests/test_revoke.sh (41)
Maschinen-Identität: aus einem vorgelegten Zertifikat „wohin gehört es laut Inventar“ ✅🧪 bin/cert_whois.php, tests/test_sensor.sh (32)
Migrator: jede DB aus jedem bisherigen Schema-Stand verlustfrei auf den neuesten ✅🧪 src/Storage/Migrator.php, tests/test_migrate.sh (94), gegen alle sechs Stände der Git-Historie mit Daten
Verwaltung der SOAP-Zugänge im Browser je Mandant, mit Live-Test ✅🧪 public/credentials.php, tests/test_credentials_ui.sh (35)

Alle 14 Test-Suiten grün, auch aus einem frischen Klon von v3.0.0. Zu den wichtigsten Tests gibt es Gegenversuche (Schutz entfernt → Test wird rot).
Das belegt, dass die Tests greifen, nicht, dass die Architektur richtig ist (Inbox-Regel §6).

2. Herkunftsmarken: was die Rückmeldungen konkret bewirkt haben

Die Marke je Ereignis ist Pflicht und wird nie umgerechnet. Heute fünf Werte:

self_reported       TLS-Gate/Operator hat es getan oder festgestellt
registrar_reported  der Registrar sagt es (seine Aussage, nicht unsere Messung)
dns_observed        von DIESEM Resolver zu DIESER lokalen Zeit gesehen, nicht "DNS-01 bewiesen"
fixture             Dummy-/aufgezeichnete Daten
node_ca             die eigene Test-CA der Node

Zwei Beobachtungen, die als Prüffälle taugen:

  • Stille Aufwertung, eine Ebene tiefer. ✅ Beim ersten Demo-Lauf des Ticks landeten Aussagen aus Dummy-Dateien als
    registrar_reported/dns_observed. Das ist derselbe Fehlertyp wie früher der Mock-Fallback, nur im Herkunftsfeld statt in den Daten.
    Behoben: Jede Quelle nennt ihre Marke selbst, fixture ist eine eigene Marke. Ein Gegenversuch macht den Test rot, wenn eine Fixture wieder
    registrar_reported meldet. Leseweise: Die Marke gehört an die Quelle, nicht an den Aufrufer.
  • Eine Quelle darf sich nicht selbst bestätigen. 🧪 node_ca reicht im Vertrag nicht für validated. Die Node-CA stellt erst aus, nachdem
    TLS-Gate den DNS-Eintrag selbst gesehen hat (dns_observed). Leseweise zum Identitäts-Prüffall: Die ausstellende Stelle ist nicht zugleich die prüfende.

Weiter: 5 gleiche Tick-Läufe erzeugen kein Ereignis. Die Frische steht getrennt in order_checks (zuletzt geprüft, seit wann gleich).
Das ist eine eigene Speicherentscheidung, keine Folge aus „Wiederholung ≠ Bestätigung“. So entspricht es der Rückmeldung vom 28.09.

3. Ausgestellt ≠ ausgeliefert, und „nix ist null“

  • ✅ Ein Lauf mit einer echten Domain auf Shared Hosting (vom Mac aus): :443 offen, TLS-Handshake mit Alert 40 abgelehnt, kein Zertifikat.
    Auf einer Kopie mit DNS-Fixture wurde bis „ausgestellt“ gespielt: gültig gegen die Node-CA, Außensicht weiter „kein TLS“.
    Der Export zeigt Selbstmeldung (Zustand in der DB) und Außensicht getrennt. Runbook: docs/LAUF_NODE_CA.md.
  • 🧪 Widerrufsstand, geprüft mit openssl verify -crl_check, also mit dem Werkzeug eines Prüfers und nicht mit eigenem Code:
    text alte CRL (vor dem Widerruf) -> widerrufenes Zertifikat gilt noch (Widerruf wirkt erst mit Verteilung) keine CRL -> abgelehnt (unbekannt ist nicht gültig) CRL nach nextUpdate -> auch ein gültiges Zertifikat abgelehnt ("CRL has expired")
    Das trifft den Zero ohne Verbindung. Er hat nur die CRL, die er zuletzt gesehen hat. Ist sie abgelaufen, ist sein Stand UNKNOWN, nicht grün.
  • 🧪 Maschinen-Identität: Wer Zertifikat und Schlüssel eines Sensors kopiert, ist für ein Gateway derselbe Sensor. Ausdrücklich so getestet.
    „Wohin gehört er“ ist Inventar, kein Hardware-Beweis. Gegenmittel ist der Widerruf, danach meldet das Gateway Kopie und Original als widerrufen.

4. Aussagegrenzen (bewusst schwach formuliert)

  • ⬜ Kein Lauf gegen den echten Registrar. Live-Pfade sind gegen tote Hosts, Test-Ports oder synthetische Antworten geprüft. Die echte Form
    einzelner Antwortfelder (validationDNS, Werte von status) ist in der SOAP-Doku nicht enumeriert und ❓.
  • ⬜ Der TOTP-QR ist mit zwei OpenCV-Decodern gelesen, nicht mit einer Authenticator-App.
  • ⬜ Kein OCSP. Die CRL-Verteilung ist Betrieb, nicht Code.
  • ⬜ MySQL/MariaDB: Der Migrator erkennt alte Regeln und bricht ab, baut dort aber nicht automatisch um.
  • Wer den Export liest, wird nicht protokolliert. Die Outbox schützt nur durch das, was fehlt (keine Zugangsdaten, keine Schlüssel),
    durch Versiegeln für einen Empfänger und durch Dateirechte. Im Mockup auf einem Arbeitsrechner ist alles erreichbar, was der eigene User lesen kann.
  • „Test grün“ heißt: das Verhalten der Klasse bzw. des Skripts ist belegt. Nicht: der Betrieb auf einer Node im RZ.

5. Brücke zur OZM CA: weiter nicht verbunden

  • ✅ Heute gibt es zwei Aussteller: Registrar (gekauft) und die eigene Node-CA (selbst ausgestellt). Das Feld issuer_kind trennt beide
    in Export, Monitor und Identitätsabfrage. Eine OZM CA wäre ein dritter Aussteller mit eigener Marke.
  • 🛠 Vorbereitet sind die Ports (ein Aussteller ist nur für seine Referenzen zuständig) und der Zustandsvertrag. Verbunden ist nichts.
  • ⬜ Offen wie zuvor: Schnittstelle der OZM CA, Vertrauensanker, Widerruf/Verteilung für Geräte, die oft aus sind.

6. Was ausdrücklich nicht folgt

  • Aus „v3.0.0 getaggt“ folgt nicht „auf einer Node in Betrieb“.
  • Aus „Node-CA mit CRL“ folgt nicht „Geräte prüfen die CRL“. Das hängt an der Verteilung.
  • Aus „Herkunft je Ereignis“ folgt nicht, dass die Herkunft stimmt. registrar_reported per CLI ist die Angabe des Operators, das Ereignis nennt den Akteur.
  • Aus „Sensor erkannt“ folgt nicht „richtiges Gerät“.

7. Fragen an OZMAI (⬜)

  1. Taugt „die Marke gehört an die Quelle, nicht an den Aufrufer“ (Fixture-Fall, §2) als wiederverwendbarer Prüfvertrag, oder ist das nur eine Anwendung von §7?
  2. Für den Zero ohne Verbindung: Soll eine abgelaufene CRL als eigener Zustand in der Beobachtungstabelle erscheinen (Widerrufsstand UNKNOWN mit
    Zeitpunkt der letzten gesehenen CRL), getrennt von „Zertifikat gültig laut Zero“?
  3. Die Trennung „ausstellende ≠ prüfende Stelle“ (§2) ist hier als Vertragsregel umgesetzt. Gehört sie in den Identitäts-Prüffall (§16) als Beispiel?