TLS-Gate — Crew-Handout v2

Stand 2026-09-30 · aktuell v3.0.0 (Patch 3.0.1 im RZ-Test, noch ohne Tag) · Details zu allen Änderungen stehen in CHANGELOG.md.
Hinweis: Dieses Handout beschreibt im Kern den Stand v2.0.0. FĂŒr Mandanten, Bestellzustand, Export und Guard gelten docs/DESIGN_TENANTS.md und CHANGELOG.md.

Auf einen Blick

v1 ist das Werkzeug ohne Login (Stand bis 24.9., lÀuft so in der Test-VM). v2 ist die erste
Version mit Login und Audit, dazu eine Verzeichnisstruktur, die sich sauber auf einen Produktionshost
bringen lÀsst:

  • Login-Pflicht: Passwort + 6-stelliger TOTP-Code. Ohne DB bleibt die OberflĂ€che zu (503).
  • Admins haben im Header Benutzer (Konten/Gruppen verwalten) und Audit (wer, wann, was).
  • Bestellungen brauchen ein Pflichtfeld Grund/Zweck. Der Grund landet im Audit und geht nicht an die CA.
  • Neue Struktur: Nur public/ ist per Web erreichbar. Klassen liegen in src/, Werkzeuge in bin/.
  • Deploy-Vorlagen fĂŒr Apache und nginx, mit Hinweisen pro Betriebssystem, liegen in deploy/.
  • Die Version steht oben im Seitenkopf (B.W.A // TLS-GATE // v2.0.0), damit v1 und v2 nicht verwechselt werden.

Was sich fĂŒr euch Ă€ndert

Vorher (v1) Jetzt (v2)
php -S 127.0.0.1:8080 router.php bin/serve.sh oder php -S 127.0.0.1:8080 -t public router.php
./dashboard.sh 
 bin/dashboard.sh 

php soap_cli.php 
 php bin/soap_cli.php 

php user_cli.php 
 php bin/user_cli.php 

./test_auth.sh tests/test_auth.sh
./TLS-doktor.bash bin/TLS-doktor.bash
vhost DocumentRoot /srv/tlsgate DocumentRoot /srv/tlsgate/public (bzw. nginx root 
/public)
sample.json per Web abrufbar resources/mock/sample.json, nur lokal
Doku im Repo-Root docs/ (README, CHANGELOG, VERSION bleiben im Root)

Die Web-Adressen bleiben gleich: /, /login.php, /users.php, /auditlog.php.

Verzeichnisse — die Regeln in drei SĂ€tzen

  1. Seiten gehören nach public/, Klassen nach src/<Modul>/ (Namespace TlsGate\<Modul>),
    Werkzeuge nach bin/.
  2. Pfade und Umgebung werden nur ĂŒber TlsGate\Config aufgelöst. Nichts mit __DIR__ . '/state' in der Fachlogik.
  3. Nichts Geheimes unter public/. DB, Key und Stammdaten liegen in TLS_GATE_STATE_DIR, produktiv /var/lib/tlsgate.

AusfĂŒhrlich: docs/STRUKTUR.md. Die Case-Falle vom 26.9. (audit.php hat die Klasse Audit.php
ĂŒberschrieben) ist damit strukturell erledigt, weil Klassen und Seiten nie mehr im selben Verzeichnis liegen.

Sicherheit & Leitplanken

  • Login: Argon2id, ≄ 15 Zeichen. TOTP ist Pflicht fĂŒr alle Rollen, jeder Code gilt nur einmal. Konten
    werden nach 5 Fehlversuchen gesperrt, eine IP nach 10.
  • Verwaltung: nur admin. Niemand kann sich selbst aussperren, der letzte Admin ist geschĂŒtzt, und es
    gibt kein Hard-Delete (disable = Soft-Delete).
  • Audit: nur anhĂ€ngbar, per DB-Trigger. Jede SSL-Aktion und jede VerwaltungsĂ€nderung wird mit Benutzer,
    Gruppe und IP protokolliert.
  • Auslieferung: Der Webroot ist public/, und PHP fĂŒhrt nur existierende Dateien aus. tests/test_auth.sh
    prĂŒft per Gegenprobe, dass das alte Layout die DB ausliefern wĂŒrde und das neue nicht.

Warum RFC 6238 — der Anker unter dem 2FA

Der Zwei-Faktor-Schritt steht auf RFC 6238 (TOTP). Das stand bisher nur als Kommentar in
src/Auth/Totp.php:2 — nirgends im Handout, das ansonsten rein die Mechanik beschreibt. Ein Verweis auf
eine Norm ist aber noch keine KonformitÀt: eingelöst ist die Behauptung erst, wenn der Code den
veröffentlichten Testvektoren der Norm antwortet.

tests/test_totp_rfc6238.sh fĂ€hrt genau das — die sechs Appendix-B-Vektoren (Referenz-Seed ASCII
12345678901234567890) gegen den echten Totp::code():

T (Sekunden) RFC 6238 (8-stellig) TLS-Gate (6-stellig)
59 94287082 287082
1111111109 07081804 081804
1111111111 14050471 050471
1234567890 89005924 005924
2000000000 69279037 279037
20000000000 65353130 353130

(6-stellig = n mod 10^6 = die letzten sechs Stellen der RFC-8-Codes.) ZusĂ€tzlich prĂŒft der Test
die verify()-Mechanik: ±1-Schritt-Toleranz, +2 abgelehnt, falscher Code abgelehnt — und der
Kern: ein bereits getroffener Zeitschritt wird ĂŒber step > lastStep monoton verbraucht.

Warum das hier steht und nicht nur im Code: dieser „einmal gĂŒltig, dann verbraucht"-Schutz ist
kein TOTP-Detail, sondern dasselbe Muster wie der Rest des Stacks — der Audit-Trail ist
append-only (kein UPDATE/DELETE), die Bestellung ist eine Passkante (Grund → audit_log, sonst
rejected). Ein TOTP ist ein zeit-gebundener Beweis: gĂŒltig nur im 30-s-Fenster, danach tot.
RFC 6238 ist die 2011 standardisierte, testvektor-geprĂŒfte Form genau dieser Idee — und damit der
Kontext, den ein rein code-basiertes Handout nicht sieht.

Getestet (v2.0.0, lokal PHP 8.5.10)

  • tests/test_auth.sh — 66/66: nur public/ erreichbar (inkl. .., %2e%2e, //state), Login,
    TOTP-Einrichtung, Replay, CSRF, Rollen, Sperren, Entzug laufender Sessions, Audit nur anhÀngbar, fail closed.
  • tests/test_totp_rfc6238.sh — 11/11: RFC-6238-Vektoren + verify()-Fenster/Replay.
  • tests/test_smoke.sh — 44/44: Vertrag sample.json + dashboard.sh.
  • bin/TLS-doktor.bash — 0 Probleme. Die Warnungen sind bekannt: Key neben DB, state/ im Checkout, Web-User _www.
  • Nicht getestet: die Vorlagen in deploy/ gegen echtes Apache/nginx (apachectl -t / nginx -t auf dem
    Zielhost ausfĂŒhren) und ein Klick-Durchlauf im Browser nach dem Umbau.

Bedienung

  1. Server starten (lokal, http), aus dem eigenen Terminal:
    bin/serve.sh # restart | stop | status
  2. Anmelden: http://127.0.0.1:8080/login.php, E-Mail + Passwort, dann den 6-stelligen Code.
  3. Code holen im Dev, ohne App:
    php bin/user_cli.php user code <email>
  4. Verwalten: oben rechts Benutzer oder Audit (Chip SSL bzw. nur Bestellungen).
  5. Bestellen: Tab Order Certificate. Grund/Zweck ist Pflicht und erscheint danach im Audit.

Produktion / Node

Der Ablauf steht in deploy/README.md. Kurzfassung:

  1. git checkout v3.0.0 (oder den Stand, den das RZ-Team freigibt). Webserver-Vorlage aus deploy/ nehmen, root = 
/public.
  2. State nach /var/lib/tlsgate legen (gehört dem Web-User), WurzelschlĂŒssel nach /etc/tlsgate/root.key,
    kein SOAP-Zugang in Pool oder vhost.
  3. migrate, den ersten Admin und den SOAP-Zugang (cred add, dann cred test) als root/sudo anlegen (CLI-Guard), danach
    chown -R <WEBUSER> /var/lib/tlsgate. Ein Aufruf als Web-User wird mit Exit 3 abgelehnt, solange ihm noch nicht alles gehört; danach ist er
    Besitzer der Instanz und darf (Grenze: docs/SECURITY.md, CLI-Guard).
  4. bin/TLS-doktor.bash mit denselben Pfaden grĂŒn ziehen.
  5. RHEL-Familie: SELinux-Kontexte + httpd_can_network_connect. Ohne das zeigt die App „SOAP nicht verfĂŒgbar“ (kein stiller Mock mehr).

Die v1-VM bleibt unverÀndert als Referenz stehen, bis v2 auf dem Zielhost lÀuft.

Offen / To-do

  • ~~Phase 2~~ erledigt in v2.1.0: SslController + templates/, strikte CSP. Offen bleibt, users.php/auditlog.php genauso umzubauen.
  • Phase 3: SslHandler in SOAP-Gateway, Mock-Quelle und Normalisierung aufteilen, $_POST raus aus der Klasse.
  • group_admin-Scope: darf nur die eigene Gruppe verwalten (M2/M3).
  • „Grund“ auch bei VerlĂ€ngern/Authcode? Bisher nur bei Bestellungen Pflicht.
  • Leak-Liste fĂŒr Passwörter (docs/SECURITY.md 1c), WebAuthn/Passkeys.
  • Key-Custody: Key getrennt von der DB ablegen (BWA-AUD-22).