Nachtrag Branko — TLS-NativePHP_v1: Server-Version, Review-Dummy, Test auf frischer Ubuntu-22.04

Von: Branko / Ansible-Katze → OZMAI
Stand: 14. Oktober 2026
Bezug: 2026-10-13_ANTWORT-OZMAI_TLS-GATE-V3-16-NETBOX-LIVE.md (§3 „ein roter Test kann der PrĂŒfer sein“), Handout HANDOUT_NODE_OHNE_DOCKER.md
Charakter: Befund und Beleg. Keine Aussage ĂŒber die RZ-VM.

1. Was jetzt existiert

Das PHP-Frontend (native_php, v3.16.0) ist als eigenes Repo TLS-NativePHP_v1 herausgelöst und nur noch fĂŒr die Node gebaut:
Ubuntu 22.04, Apache + php-fpm, PHP 8.4, MySQL 8. SQLite, der Mock-Modus (TLSGATE_MOCK) und die Installation im Projektbaum sind
entfernt. Commits 9b7d88f, b734884, b72f32e, 2d9b215, a3b1721, 618216e, 0d7bfc2, 52eefb6, 1860fb8, 369e3d6,
gepusht auf git.crumbforest.org.

Neu: deploy/node/build.sh (Node bauen), review-dummy.sh (Wegwerf-Daten), review-check.sh (Gegenprobe), bin/seed_cli.php
(Beispielbestand in der DB statt Mock-Datei: eigener abgeschalteter Zugang sample, löschbar, im Audit), bin/tg (die CLIs
mit der Umgebung der Instanz aufrufen; ohne TLS_GATE_STATE_DIR endet die CLI jetzt mit Exit 2 und klarer Meldung).

2. Belegt

Aussage Beleg
15 Suiten, 0 rot gegen MySQL 8.0.46 (Docker, lokal) tests/run_all.sh
build.sh lÀuft auf frischer Ubuntu-22.04 durch, zweimal (zweiter Lauf Àndert nichts); MySQL dort 8.0.x aus dem Ubuntu-Repo, Version nicht festgehalten Container mit systemd
Doktor nach dem Bau: keine Fehler, 3 Warnungen bin/TLS-node-doktor.bash
Login mit Passwort + TOTP durch Apache → FPM → MySQL review-check.sh, 49 grĂŒn, 0 rot
Zwei Gruppen (bwa, ozm), je sechs Benutzer (admin, group_admin, 2×user, readonly, Erst-Login) sehen den Bestand der eigenen Gruppe und keine Order der anderen review-check.sh
Beide MFA-Wege: code (enrolltes Secret) und qr (neues Secret nach jedem Passwort) je Gruppe einer, Stand zum Zeitpunkt von review-check.sh; bwa wurde danach ĂŒber die OberflĂ€che geĂ€ndert (§4a)
Browser-Zugriff https://localhost:8443: Anmeldung admin@bwa.review.test, danach katalog.php, credentials.php, netbox.php, auditlog.php „sieht gut aus“ ATTESTED_BY_BRANKO (Seiten per Browser, Inhalte nicht von mir gelesen)
Drei abgelehnte Codes desselben Kontos (bad_code, 18:42:02, 18:42:27, 18:43:15 UTC) stehen im Audit mit Zeit, Konto und Grund; ZĂ€hler failed_count = 3, Sperre bei 5; Entsperren per CLI gesehen in audit_log und users (Container)

3. Befunde aus MySQL 8.0.46 (vorher nur gegen MariaDB gelaufen)

  1. Fehler 1419 bei migrate mit getrenntem DDL-Benutzer: Binlog ist an, Trigger brauchen SET_USER_ID (oder log_bin_trust_function_creators=1). Reproduziert und beides geprĂŒft.
  2. Zweiter migrate-Lauf und --status brachen ab: information_schema.CHECK_CONSTRAINTS hat in MySQL 8 keine Spalte TABLE_NAME.
  3. Falscher Abbruch auf frischer DB: MySQL liefert die CHECK-Literale mit Backslash (\'revoked\'), der Vergleich traf nicht.
  4. Migration 0003 hatte dieselben beiden Fehler. Sie war auf MySQL 8 nie gelaufen. Der Upgrade-Pfad 2–7 ist jetzt getestet (aus dem letzten Stand ohne Versionsangabe: Schema == frisch, Daten erhalten).
  5. groups ist reserviert (bekannt, 35451b0); in vier Test-Suiten (auth, credentials, credentials_ui, katalog) und in
    den alten Schema-Fixtures stand es noch unquotiert. Eine dieser Abfragen (test_auth, Dubletten-PrĂŒfung) hĂ€tte bei Fehler
    eine leere Antwort mit einer leeren Antwort verglichen und wĂ€re grĂŒn geblieben; gefunden durch Lesen, nicht durch einen roten Test.

4. Ihre Formeln an diesem Lauf

RED_TEST ⇏ PRODUCT_DEFECT

Die drei zunĂ€chst roten Suiten (auth, mfa, credentials_ui) waren veraltete Tests: Der MFA-Default fĂŒr neue Gruppen ist
inzwischen qr, die Tests erwarteten code. Das Produkt war nicht kaputt.

DOKTOR_FAIL ⇏ PRODUCT_DEFECT

Der Doktor meldete „0 von 16 Triggern“. In der DB waren alle 16. Der PrĂŒfer lĂ€uft als App-Benutzer und sieht ohne TRIGGER-Recht keine
Trigger in information_schema. Das ist jetzt eine Warnung „nicht sichtbar“, kein Fehler. Gleiche Fehlerklasse wie der Locale-Fall.

Dazu zwei kleine Verifier-Fehler im Doktor: eine Variable G ĂŒberschrieb die Farbvariable (die Ausgabe zeigte Rechte statt ok),
und stat folgte Symlinks nicht.

LOGIN_OK ⇏ IDENTITAET_FESTGESTELLT

Die Testbenutzer sind erzeugt, die Passwörter zufÀllig und im Klartext in einer Datei. Der Test belegt den Mechanismus
(Passwort + zweiter Faktor, Gruppentrennung der Seite), nicht, dass dahinter eine Person steht.

NOT_SEEN ⇏ NOT_ACCESSIBLE

„Sieht nichts aus Gruppe X“ ist geprĂŒft fĂŒr die Seite bestand.php per Textmuster der Order-Nummern. Direkte Objektverweise
(IDs in anderen Seiten und Formularen) sind nicht geprĂŒft.

4a. Fall gadmin@bwa.review.test: „Anmeldung fehlgeschlagen oder Konto vorĂŒbergehend gesperrt“

Die Meldung der OberflÀche unterscheidet falsches Passwort und Sperre bewusst nicht. Das Audit tut es:

Zeit (UTC) Eintrag Quelle
18:46:39 admin.group_mfa ok fĂŒr Gruppe bwa (MFA-Modus geĂ€ndert; neuer Wert nicht festgehalten, Container abgebaut) Web
19:30:23 auth.login fail bad_password Web
19:32:46 admin.user_reset_mfa ok (Admin ĂŒber die OberflĂ€che) Web
19:34:00 auth.login fail bad_password Web
19:38:53 – 19:41:04 cli.user_enroll ok, 2× cli.user_code ok CLI (root)
19:41:39 auth.login fail bad_password Web
19:51:03 admin.user_passwd ok (Admin ĂŒber die OberflĂ€che) Web
19:52:06 – 19:52:25 auth.password ok (next: enroll), auth.mfa_enroll ok, auth.login ok Web
19:53:19 auth.logout ok Web
  • Belegt: Das Passwort aus der Testdatei passte zum Hash in der DB. Der Server war also nicht der Grund, das eingegebene Passwort
    war ein anderes. Konto weder gesperrt (locked_until leer) noch inaktiv. ZĂ€hler 2 zum Zeitpunkt der PrĂŒfung (ca. 19:37 UTC), danach ein
    dritter Fehlversuch (19:41:39); Sperre erst bei 5.
  • Nicht festgestellt: welches Passwort eingegeben wurde. Das Passwort hat 29 Zeichen und endet auf -Aa9!; Abtippfehler sind
    wahrscheinlich, aber nicht belegt.
  • Beobachtet, aber vermengt: Nach dem Passwortwechsel durch den Admin (19:51:03) verlangte die App wieder die Einrichtung
    (next: enroll), obwohl das Konto vorher per CLI enrollt war. Dazwischen liegt die Änderung des MFA-Modus der Gruppe bwa
    (18:46:39). Steht bwa danach auf qr, verlangt jeder Passwort-Login die Einrichtung, unabhÀngig vom Passwortwechsel. Welcher
    Wert gesetzt wurde, ist nicht festgehalten. Es folgt daraus keine Aussage, ob ein Passwortwechsel den zweiten Faktor
    zurĂŒcksetzt.
  • Auditlage: Jede Handlung steht mit Zeit, Konto, Grund und Quelle da (Web mit Adresse, CLI mit cli:root). Das ist der Grund,
    warum sich der Fall nachtrĂ€glich in Minuten klĂ€ren ließ.
  • Client-Adresse: Im Audit stand fĂŒr alle Web-EintrĂ€ge 172.17.0.1, das Docker-Gateway, nicht die Adresse des Browsers. Das ist
    das Verhalten hinter einer Weiterleitung und zugleich ein Beispiel fĂŒr den offenen Punkt „Client-IP hinter den Proxies“ (§8): Ohne
    weitergereichte Adresse benennt das Audit den Proxy statt der Quelle.
GENERIC_UI_MESSAGE ⇏ ACCOUNT_LOCKED

Die OberflĂ€che sagt bewusst „fehlgeschlagen oder gesperrt“ (kein Hinweis, ob das Konto existiert). Welche der beiden Ursachen
vorliegt, erkennt nur, wer das Audit lesen darf. Das spricht fĂŒr die Frage in §7: Wer darf auditlog.php lesen, und in welcher Tiefe?

4b. Operator-Weg im qr-Modus (geprĂŒft)

Nach dem Passwort verweigert user code („MFA nicht aktiv“), solange die Einrichtung offen ist. user enroll <email> setzt ein neues
Secret und aktiviert es, danach liefert user code <email> den Code, und die Anmeldung geht durch (Redirect auf index.php). Das
zeigt auch: Wer die CLI bedienen darf (root/sudo), kann den zweiten Faktor jedes Kontos setzen und den Code erzeugen. Das ist die
Guard-Grenze, nicht der Faktor.

5. Grenzen

  • Container, keine VM. Die Apache-Kette der RZ-VM (mehrere verschachtelte Configs), das echte Zertifikat und die Client-IP hinter
    den Proxies waren nicht dabei.
  • MySQL lokal (mysql-server aus dem Ubuntu-Repo, 8.0.x), nicht die externe/RZ-Instanz.
  • PHP 8.4 kam aus ppa:ondrej/php; auf der Node ist 8.4.26 vorhanden, die Quelle dort ist nicht identisch belegt.
  • Gegenprobe der RZ-NetBox (VM, N7) und Ort der Node-CA bleiben offen (Nachtrag vom 13.10.).
  • Belege nicht mehr einsehbar. Container, Image, Testdaten und die Zugangsdatei des Dummys sind abgebaut. Die LĂ€ufe sind nur durch
    Neubau reproduzierbar (build.sh, review-dummy.sh, review-check.sh); die EintrÀge aus audit_log und users, auf die sich §2
    und §4a stĂŒtzen, sind in diesem Nachtrag festgehalten und nicht mehr am System nachzulesen.

5a. Test-Setup: Docker-Hinweise (tests/README.md, 369e3d6)

Die 15 Suiten brauchen einen MySQL-8-Server. Lokal ist das ein Wegwerf-Container (mysql:8.0, rund 1,1 GB beim ersten Start,
Datenverzeichnis im tmpfs, nur auf 127.0.0.1:33306, Admin-Benutzer mit Test-Passwort). Das ist der Verifier, und er gehört
zum Interpretationsrahmen eines Ergebnisses (Ihr §3):

  • Version: Der Docker-Container meldete 8.0.46, die Node ebenfalls (Ubuntu-Paket 8.0.46-0ubuntu0.22.04.4, ATTESTED_BY_BRANKO).
    Gleiche Versionsnummer, nicht dasselbe Build: Konfiguration (Binlog, sql_mode, Collations) kann abweichen.
  • Wo die Befunde auftraten: Fehler 1419 und die CHECK_CONSTRAINTS-Spalte zeigten sich im Docker-Image mysql:8.0. Im
    Ubuntu-Container hatte build.sh SET_USER_ID vorab vergeben (1419 trat dort nicht auf), und die Spaltenfehler waren schon
    behoben. Auf der Node selbst ist migrate noch nicht gelaufen; ob dort dasselbe geschieht, ist nicht belegt.
  • Bereitschaft: Das offizielle Image startet beim Initialisieren einen Zwischen-Server ohne Netzwerk. Ein Ping ĂŒber den Socket
    meldet sich dann schon, die Suiten bekommen aber noch „connection refused“. Das README wartet deshalb per TCP.
    Belegt ist das nur als dokumentiertes Verhalten des Images; die Warteschleife selbst ist nach der ErgÀnzung nicht erneut gelaufen.
  • Rechte des Test-Benutzers: Die Suiten laufen als Admin (CREATE/DROP DATABASE). Sie belegen die App-Logik, nicht die
    Rechtetrennung tlsgate_ddl/tlsgate_app. Diese Trennung ist nur im build.sh-Lauf (Container-Node) und im Doktor belegt.
  • AufrĂ€umen: docker rm -f tlsgate-mysql-test, Image mit docker rmi mysql:8.0. Die Daten liegen im tmpfs und sind danach weg.
TESTS_GRUEN ⇏ RECHTETRENNUNG_GEPRUEFT

6. VorschlĂ€ge fĂŒr PrĂŒffĂ€lle

  • P-REV-1: Gruppentrennung jenseits der Seite: fremde cid/group_id/Buchungs-ID in credentials.php, katalog.php,
    booking-Aktionen (die Suiten credentials_ui und katalog decken Teile ab, ein gemeinsamer Fall mit zwei echten Konten fehlt).
  • P-REV-2: qr-Modus: Was belegt ein erneutes Passwort-Login? Es erzeugt ein neues Secret. Der alte zweite Faktor gilt dann nicht
    mehr, das ist gewollt, aber im Audit sichtbar zu machen.
  • P-REV-3: Doktor-Sicht: Welche Rolle darf prĂŒfen, ob die Trigger da sind? Aktuell nur root per SQL, nicht der App-Benutzer.
  • P-REV-4: Meldungen: OberflĂ€che allgemein, Audit konkret (bad_password, Sperre, inaktiv). PrĂŒfen, dass die OberflĂ€che in
    keinem Pfad (auch Zeitverhalten, Statuscode) Konto-Existenz oder Sperre verrÀt.

7. Frage

Welche Aussage darf „Review-Dummy grĂŒn“ tragen? Mein Vorschlag: „Mechanismus fĂŒr Anmeldung, Gruppentrennung der Bestandsseite und
Schema funktionieren in einer frischen Ubuntu-22.04-Umgebung mit MySQL 8.0 (Docker-Image 8.0.46; Version des Ubuntu-Pakets nicht
festgehalten)“
, und ausdrĂŒcklich nicht: „die RZ-VM ist bereit“.

8. Offen

  • ❓ Apache-Kette der RZ-VM, TLS-Terminierung, Client-IP.
  • ❓ WurzelschlĂŒssel: neu erzeugen oder aus der Dev-VM ĂŒbernehmen.
  • ✅ Code-Eingabe im Browser und Seiten danach: von Branko als „sieht gut aus“ gemeldet (siehe §2). Welche Quelle der erste, abgelehnte Code
    hatte, ist nicht festgestellt (Uhren gleich, Secret in Datei und DB gleich, also kein Serverfehler; Eingabe nach Ablauf wahrscheinlich).
  • ⬜ P-REV-1 bis P-REV-4.
  • ❓ Ort der Node-CA, VM-Gegenprobe (N7), tenant_group.