Files
bf-infra/docs/handoff-20260831-fund-e-hebel1-gezogen.md
Christoph Emsenhuber 6f8ba8c8b5 sshpass -p repo-weit weg + vier TP15-Cutover-Skripte archiviert
sshpass -p schreibt das Passwort in argv, wo es in ps und jedem argv-Trace
steht -- die Klasse, an der am 2026-08-22 das Core-Passwort verbrannt ist.
Nach gc-transaktion.py (voriger Commit) jetzt auch die fuenf unter routeros/:
SSHPASS-Env + sshpass -e.

Nachweis, gemessen statt gelesen:
- alle 13 subprocess.run mit sshpass tragen env= (per AST geprueft; ohne env
  waere SSHPASS leer und alles still kaputt)
- Signaturen passen zu den Aufrufen (AST-Arity; py_compile findet das NICHT)
- echter Lauf des gepatchten Codes gegen den lebenden Core: backup-core.py
  importiert, IP zur Laufzeit auf 10.40.99.1 -> "name: crs326-core", rc=0
- Passwort in argv? nein. In ps waehrend der Verbindung? nein, 40 Stichproben
- Negativkontrolle: derselbe Test mit einem Dummy-Wert ueber die alte -p-Form
  -> 36 Treffer. Die Probe ist also nicht blind, das "nein" hat Aussagekraft

Mitgefixt, weil es im umgebauten Code lag: apply-core-cutover-l3.py griff in
run_silent auf ein pw zu, das dort gar nicht existierte -- jeder Fehlerfall
endete in einem NameError statt in der gedachten Meldung.

Fuer TP30 aendert das NICHTS: der Weg ist zu, der Wert bleibt verbrannt.
Posten #1 (MikroTik-admin) bleibt faellig. Im TP so hingeschrieben, damit es
niemand als erledigt liest.

--- Archivierung (Christoph-Entscheid) ---

Beim Fixen fiel auf, dass alle vier Cutover-Skripte auf tote Adressen zeigten:
  apply-core-cutover-l3.py  10.40.41.211    tot (VLAN41 seit 2026-08-27 weg)
  backup-core.py            10.40.41.211    tot
  restore-core.py           192.168.88.1    tot (Werks-Temp-Pfad)
  backup-ap1.py             10.40.10.1      ANTWORTET -- ist aber der CORE

backup-ap1.py war der gefaehrliche: er waere nicht gescheitert, er haette den
Core gesichert und die Datei ap1-pre-core-cutover-*.backup genannt. Wer das
spaeter zurueckspielt, spielt Core-Config auf einen AP.

Entscheid war archivieren, nicht reparieren -- der Cutover ist am 2026-08-08
durch, die Skripte haben ihre Aufgabe erfuellt. Sie liegen jetzt unter
routeros/archiv-tp15-cutover/ mit README nach dem Muster von switch/.
Die Pfad-Nebenwirkungen des Verschiebens sind ABSICHTLICH nicht repariert
(backup-core.py schriebe jetzt ins Archivverzeichnis, apply-core-cutover-l3.py
faende seine .rsc nicht mehr) -- sie zu reparieren hiesse, die Skripte wieder
lauffaehig zu machen, und das ist gerade nicht die Absicht.

Der sshpass-Fix wurde trotzdem VOR dem Verschieben gemacht: Archiv-Code wird
abgeschrieben, also soll dort das richtige Muster stehen.

import-core-rsc.py bleibt, wo es ist -- als einziges generisch nuetzlich. Sein
Default-Host ist ebenfalls tot (--host mitgeben), und in --host steckt ein
zweiter latenter Bug: sys.argv[1] wird als RSC-Pfad gelesen, bevor auf --host
geprueft wird. NICHT gefixt, eigene Runde.

Zeiger gesetzt in: CLAUDE.md (sshpass-Gotcha), teilprojekte.md (TP30),
teilprojekte-archiv.md (TP15), Handoff 2026-08-31.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-31 08:34:08 +02:00

10 KiB
Raw Blame History

Handoff 2026-08-31 — TP25 Fund E: Hebel 1 ist gezogen, die Uhr läuft

Vorgänger: handoff-20260831-wlan-fund-e.md (Befund + Messrezept). Dieses Dokument ist der Stand danach. Es gibt genau eine offene Aufgabe: messen und entscheiden.

Was am 2026-08-31 um 08:09 passiert ist

Eine Änderung, sonst nichts:

/interface wifi security set [find name="sec-main"] ft-over-ds=no      # vorher: yes
/interface wifi capsman remote-cap provision [find]

ft=yes bleibt — Fast Transition läuft weiter over-the-air, nur nicht mehr over-the-DS. Das nimmt exakt die Änderung vom 2026-08-17 zurück, mehr nicht. Kein weiterer Funk-Parameter ist angefasst worden (Stop-Bedingung: wer zwei Hebel zieht, weiß hinterher nichts).

Abnahme — was tatsächlich geprüft wurde

Prüfung Ergebnis
Wirkt der Hebel? am Manager, alle Master-VAPs cap-wifi3/4/5/6 → ft=true ft-over-ds=false. cap-wifi1/2 (= ap1) leer, weil ap1 funkstill ist.
CAPs zurück? alle drei state=Ok
VAPs laufen? am CAP gelesen, nicht am Manager ap2 und ap3: alle 9 Interfaces running=yes
Frische DHCP-Lease (die vorgeschriebene Abnahme) iPhone F2:62:4C:E1:AF:09 nach 71 s wieder auf dirtyvader-main, 10.40.10.196, auth-type=ft-wpa3-psk, −54 dBm
Clients gesamt 30 zurück
Roaming funktioniert noch? ja — Log zeigt um 08:13:20 ein sauberes roamed to zwischen zwei APs auf main
Compliance 134/135, Diff exakt ft-over-ds=yes → no. Kein Kollateral-Drift.

Geräte-Backup vor dem Eingriff: pre-tp25-funde-ftoverds.backup auf dem Core (08:07:58).

🔴 Warum Compliance auf 134/135 steht und das RICHTIG ist

Die Transaktion TP25-fund-e-ft-over-ds ist offen (Scope wifi-security, Ablauf 2026-09-03). gc-transaktion.py status weist den einen Drift als erwartet aus — genau dafür gibt es das Werkzeug.

Das Template wird bewusst NICHT vorher nachgezogen. Wer es jetzt grün macht, macht den laufenden Versuch unsichtbar, und status kann ihn nicht mehr von einem Versehen unterscheiden. gc-transaktion.py erzwingt die richtige Reihenfolge ohnehin: es verweigert den Commit, solange der Scope driftet. 135/135 kommt beim Commit zurück — nach der Messung, nicht davor.

🔴 Die cap-wifiN-Karte hat sich beim Re-Provisionieren VERSCHOBEN

Wer die Logs vom 31.08. auswertet und das übersieht, ordnet Ereignisse dem falschen AP zu:

vor 08:09 (Logzeilen 07:xx) ab 08:09
ap1 (funkstill) — cap-wifi1 / cap-wifi2
ap2 cap-wifi1 / cap-wifi2 cap-wifi3 / cap-wifi5
ap3 cap-wifi5 / cap-wifi6 cap-wifi4 / cap-wifi6

Gelesen mit /interface wifi radio print am Manager. Das ist dieselbe Instabilität, die tp25-walk-messung.sh als ap-map.txt einfriert — hier von Hand.

⚠️ Korrektur am Befund vom Vormittag

Der Vorgänger-Handoff sagt, main unterscheide sich von den anderen fünf SSIDs in genau einem Attribut. Live nachgelesen sind es zwei:

sec-main       authentication-types=wpa3-psk               ft=yes ft-over-ds=yes
sec-infra …    authentication-types=wpa2-psk,wpa3-psk      ft=no

sec-main ist auch die einzige SSID ohne wpa2-psk. Die Kontrollgruppe trennt FT und WPA3-only also nicht voneinander. Das ändert die Hebelreihenfolge nicht — die Hypothese nennt gerade die Kombination WPA3+FT — aber es ändert den Plan B:

⇒ Wenn Hebel 1 und Hebel 2 (ft=no) beide nichts bewegen, ist der nächste Kandidat authentication-types=wpa2-psk,wpa3-psk auf sec-main — NICHT signal-range.

Die einzige offene Aufgabe: messen

ssh root@10.40.20.100 "pct exec 400 -- sh -c 'cat /opt/tp5-syslog/log/10.40.20.1/2026-0*-*.log |
  grep reauthenticating | grep -oE \"([0-9A-F]{2}:){5}[0-9A-F]{2}\" | sort | uniq -c'"

Baseline: 93, alle vom iPhone F2:62:4C:E1:AF:09, alle auf dirtyvader-main. Davon 79 am 31.08., sämtlich in Stunde 07, der letzte um 07:34:43 — also vor dem Eingriff. Zwischen 08:00 und 08:15 kein einziges. Das ist noch kein Ergebnis, nur der saubere Schnitt.

Ein Gegencheck gehört dazu — er beweist, dass nicht einfach das Logging kaputt ist:

ssh root@10.40.20.100 "pct exec 400 -- sh -c '
  grep wireless /opt/tp5-syslog/log/10.40.20.1/2026-09-01.log | wc -l;
  grep wireless /opt/tp5-syslog/log/10.40.20.1/2026-09-01.log | grep -oE \"\\(dirtyvader-[a-z]+\\)\" | sort | uniq -c'"

Die Entscheidung am 2026-09-01

Messung Schluss Nächster Schritt
iPhone-reauthenticating ~0, andere SSIDs unverändert Hebel 1 hat gegriffen Template mikrotik_routeros.j2 Z. 220 auf ft-over-ds=no ziehen → gc-transaktion.py commit --change-id TP25-fund-e-ft-over-ds → Compliance zurück auf 135/135
Zahl unverändert Hebel 1 hat nicht gegriffen Rollback ODER stehen lassen und Hebel 2 ft=no als eigene Transaktion. Nicht beides gleichzeitig.
Zahl runter, aber neue Ereignisse auf anderen SSIDs Nebenwirkung Rollback, dann neu bewerten

Rollback-Weg (der Snapshot liegt bereit): python3 nautobot/gc-transaktion.py rollback --change-id TP25-fund-e-ft-over-ds — erzeugt einen Vorschlag, der gelesen werden will, und pusht bewusst nicht selbst.

✅ Nebenbefund erledigt: sshpass -p ist repo-weit weg (2026-08-31, zweiter Durchgang)

sshpass -p <PW> schreibt das Passwort in argv, wo es in ps und jedem argv-Trace steht — die Klasse, an der am 2026-08-22 das Core-Passwort verbrannt ist. Alle sechs betroffenen Skripte laufen jetzt über SSHPASS-Env + sshpass -e:

nautobot/gc-transaktion.py         (im ersten Durchgang, weil es laufen musste)
routeros/import-core-rsc.py
routeros/archiv-tp15-cutover/apply-core-cutover-l3.py   ┐
routeros/archiv-tp15-cutover/backup-core.py             │ am selben Tag archiviert,
routeros/archiv-tp15-cutover/restore-core.py            │ s. unten -- aber VOR dem
routeros/archiv-tp15-cutover/backup-ap1.py              ┘ Verschieben noch gefixt

Warum Archiv-Code trotzdem gefixt wurde: Archiv wird abgeschrieben. Wer sich dort ein sshpass-Muster holt, soll das richtige finden.

Nachweis — gemessen, nicht gelesen:

Prüfung Ergebnis
Alle 13 subprocess.run, die sshpass starten, tragen env= ✓ per AST geprüft (ohne env wäre SSHPASS leer und alles kaputt)
Signaturen passen zu den Aufrufen ✓ per AST, Arity-Abgleich — py_compile findet so etwas nicht
Echter Lauf des gepatchten Codes gegen den lebenden Core ✓ backup-core.py importiert, IP zur Laufzeit auf 10.40.99.1, Antwort name: crs326-core, rc=0
Passwort in argv? nein
Passwort in ps während der Verbindung? nein, 40 Stichproben
Negativkontrolle — Dummy-Wert über die alte -p-Form 36 Treffer ⇒ die Probe ist nicht blind, das „nein" oben hat Aussagekraft

🔴 Für TP30 ändert das nichts. Der Weg ist zu, der Wert bleibt verbrannt — ein Passwort, das einmal in einem Transkript stand, wird nicht dadurch sauber, dass die Leitung dicht ist. Rotation von Posten #1 bleibt fällig.

⚠️ Mitgefixt, weil es in umgebautem Code lag: apply-core-cutover-l3.py griff in run_silent auf ein pw zu, das dort gar nicht existierte — jeder Fehlerfall endete in einem NameError statt in der gedachten Meldung.

🔴 Und dabei aufgefallen: die IPs in diesen fünf Skripten sind Schrott

Live geprüft am 2026-08-31, bewusst NICHT stillschweigend geändert — nur je ein lauter Header in die Datei:

Skript Ziel-IP Realität
import-core-rsc.py, apply-core-cutover-l3.py, backup-core.py 10.40.41.211 tot — VLAN41 gibt es seit 2026-08-27 nicht mehr
restore-core.py 192.168.88.1 tot — Werks-Temp-Pfad; dazu ein hartkodierter Backup-Dateiname vom 2026-08-06
backup-ap1.py 10.40.10.1 🔴 antwortet — ist aber der CORE (VLAN10-SVI, tcp/22 offen). ap1 ist 10.40.99.11.

backup-ap1.py ist der gefährliche: er scheitert nicht, er sichert das falsche Gerät und legt die Datei als ap1-pre-core-cutover-*.backup ab. Wer dieses Backup später zurückspielt, spielt eine Core-Config auf einen AP.

✅ Entschieden (Christoph, 2026-08-31): archiviert, nicht repariert. Die vier liegen jetzt unter routeros/archiv-tp15-cutover/ mit README nach dem Muster von switch/ — der Cutover ist am 2026-08-08 durchgezogen, die Skripte haben ihre Aufgabe erfüllt.

⚠️ Die Pfad-Nebenwirkungen des Verschiebens sind absichtlich NICHT repariert (backup-core.py schriebe jetzt ins Archivverzeichnis, apply-core-cutover-l3.py fände seine .rsc nicht mehr). Sie zu reparieren hieße, die Skripte wieder lauffähig zu machen — das ist gerade nicht die Absicht.

import-core-rsc.py bleibt, wo es ist: als einziges generisch nützlich. Sein Default-Host ist aber ebenfalls tot ⇒ --host mitgeben. (Und in --host steckt ein zweiter latenter Bug: sys.argv[1] wird als RSC-Pfad gelesen, bevor auf --host geprüft wird — die Option kann so, wie sie dokumentiert ist, nie greifen. Nicht gefixt, gehört zu einer eigenen Runde.)

Entwarnung zum Snapshot: /export auf ROS 7.24 gibt WLAN-Passphrasen gar nicht aus (0 Vorkommen im Vollexport, live geprüft). Die Transaktionsakte im Git ist sauber.

🔧 Zweiter Nebenbefund: Worktrees können gc-*.py nicht ohne Handgriff ausführen

docs/nas-vlan20/stacks/210-nautobot.yml ist gitignored und existiert deshalb nur im Haupt-Checkout. In einem Worktree scheitern gc-transaktion.py und gc-configplan.py am Nautobot-Token. Behelf in dieser Session war ein Symlink auf den Haupt-Checkout — der gehört nicht committet. Dauerhafte Antwort wäre NAUTOBOT_TOKEN aus ~/.secrets/exports.zsh (gc-configplan liest die Variable bereits; gc-transaktion liest nur die Stack-Datei).

Unverändert offen (nicht Teil dieser Session)

  1. TP25-Messrunde mit Rundgang (bash docs/tp25-runde.sh) — ⚠️ die ap-map neu einfrieren, s.o.
  2. Passwort-Rotation TP30 (Admin-PW am 08-30 verbrannt; fehlende MCP_MIKROMCP_*_PASS in exports.zsh)
  3. MikroMCP-Neustart (Registry inkl. ap3 wird nur beim Start gelesen)
  4. ap1-Umzug ans Sideboard (core ether3, 5260/Ce, Frequenz-Liste statt Einzelwert, tx-power vorher messen)
  5. Versions-Angleichung 7.24 → 7.24.1