Files
bf-infra/docs/handoff-20260831-tp16.3-paragraph2-3-durch.md
Christoph Emsenhuber 60360fc3cc TP16.3 §2/§3 durch: cloudflared auf ap1/ap2/ap3, bf-public healthy mit 12 Connectoren
Merge von claude/nervous-williams-acf000 (d5b3ce3) plus Ergebnis der
Vor-Ort-Session vom 2026-08-31.

Ergebnis
- je ein cloudflared 2026.6.1 auf ap1/ap2/ap3, bf-public healthy/12
  (vorher 0 Connectoren), Flash je AP 54-55 MiB frei, WLAN zurueck.

Drei Runbook-Fehler aufgedeckt und korrigiert (§2.2, §3.3, §3.5, §8)
- Flash-Budget war Faktor 2,5 zu optimistisch: cloudflared entpackt
  69,5 MiB (gemessen mit docker export), nicht 28 MB -- das offizielle
  Image passt auf keinen der drei APs. Ersatz: eigenes FROM-scratch-Image
  mit 35,3 MiB aus dem statisch gelinkten Binary, Binary-SHA e93824f6
  gepinnt, Neubau byte-identisch. tools/build-cfd-image.py.
- docker save schreibt seit Docker 25 OCI statt docker-v1 -> RouterOS
  meldet "no config found in manifest". tools/oci2docker.py als Konverter.
- /container/envs nimmt list=, nicht name= (auch MikroMCPs
  manage_container_env ist dadurch fuer dieses Feld unbrauchbar).

Weitere Funde
- Der device-mode-Bestaetigungs-Boot installiert keine Pakete; erst
  Paket-Reboot, dann device-mode.
- "A - AVAILABLE" in /system package print ist ein Download-Katalog,
  kein Paket auf dem Geraet (Gegenprobe am Core: 2,9 MB frei, listet
  10,1 MB wifi-qcom als verfuegbar).
- MikroMCPs export_config exportiert unvollstaendig (301 B statt 6,2 KiB).

Offen und entscheidungsbeduerftig
- Compliance 128/135: §3.4 verlangt Direkt-Writes fuer ip address/nat,
  die unter GC-Kontrolle stehen -> sechs neue extra-Drifts, zwei je AP.
  Inhaltlich richtig, gehoeren ins Soll -- kein Re-Baseline, kein Rueckbau.
- Zwei Nautobot-Tokens sind in dieser Session ins Transkript geraten
  (unzureichende sed-Maskierung) -> Rotation noetig, TP30. Merkregel in
  CLAUDE.md ergaenzt.

Merge-Konflikte aufgeloest
- Nummernkonflikt: beide Branches hatten ein TP37. main behaelt TP37
  (Nautobot als Netzwerkdoku, User-Okay 08-30), OOB-Mgmt wird TP38.
  Braucht noch Christophs Okay.
- Drei CF-Rollback-Snapshots hatten gleichen Namen bei verschiedenem
  Inhalt -> aelterer Stand als *-vor-hostname-schnitt.json erhalten,
  keiner verworfen.

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

7.5 KiB
Raw Blame History

Handoff — TP16.3 §2/§3 durch: drei Connectoren live, eine Entscheidung offen

2026-08-31, Vor-Ort-Session. Nachfolger von handoff-20260831-tp16.3-vorort-session.md. Nicht Teil dieser Session: §5 (CNAME-Flip) und §6 (Abbau).


1. Was jetzt steht

bf-public ist healthy mit 12 Connectoren (3 APs × 4 CF-Edges) — der Tunnel hatte vorher null. Je ein cloudflared 2026.6.1 läuft auf ap1, ap2 und ap3.

ap1 ap2 ap3
container-Paket 7.24.1 ✅ 7.24.1 ✅ 7.24.1 ✅
device-mode container yes ✅ yes ✅ yes ✅
Container R running R running R running
.tar gelöscht ✅ ✅ ✅
Flash frei 54,0 MiB 54,6 MiB 55,0 MiB

WLAN auf allen drei zurück, CAPs state=Ok am Manager. Backups je AP liegen auf dem Gerät (vor-tp16.3-apN.backup) und lokal unter ~/tp16.3/backups/ — inklusive vollständigem /export (nicht der MCP-Export, s. Punkt 4).


2. 🔴 Die eine offene Entscheidung: Compliance steht auf 128/135

Nach vollem Refresh (Backup → Intended → Compliance, nicht die stale Zahl von vorher):

Gerät Regel Inhalt
ap1 / ap2 / ap3 ip-address /ip address add address=172.17.0.1/24 interface=veth-cf
ap1 / ap2 / ap3 ip-firewall-nat masquerade, Kommentar TP31: cloudflared egress
crs326-core wifi-security ft-over-ds — die bekannte, unverändert

Ursache ist ein Widerspruch zwischen zwei Dokumenten, kein Ausrutscher: Runbook §3.4 schreibt Direkt-Writes für veth/ip address/nat vor, die Governance stellt /ip address und /ip firewall unter GC-Pflicht. Ich bin dem Runbook gefolgt.

Die sechs Drifts sind inhaltlich richtig — die Config soll dort stehen, ohne sie läuft kein Container. Der saubere Weg ist, sie ins Soll zu heben (AP-Template bzw. ConfigContext ap-common), damit die Compliance sie als konform sieht. 🔴 Kein Re-Baseline (macht die Drift-Erkennung wertlos) und kein Rückbau (killt die Tunnel). ⚠️ /interface veth erzeugt keine Drift — dafür existiert keine ComplianceRule. Wenn das Soll angepasst wird, gehört die Frage dazu, ob veth überhaupt erfasst sein soll.


3. 🔴 Drei Runbook-Fehler, die diese Session aufgedeckt hat

(a) Die Flash-Rechnung war um Faktor 2,5 zu optimistisch. §3.5 rechnete mit „28 MB .tar

  • ~28 MB entpackt ≈ 39 MB Rest". Die 28 MB sind die komprimierte Archivgrösse; entpackt misst cloudflare/cloudflared:2026.6.1 72.926.208 B = 69,5 MiB. Der Import braucht Tar und entpacktes Abbild gleichzeitig ⇒ 95,7 MiB gegen ~90 MiB frei. Das offizielle Image passt auf keinen der drei APs. ⚠️ docker image inspect .Size führt in die Irre (meldete 27.463.950). Für Flash-Budgets docker export | wc -c messen.

Lösung, ausgerollt: eigenes FROM scratch-Image aus dem — nachweislich statisch gelinkten — Binary (36,8 MB) plus CA-Bundle: 35,3 MiB, Spitzenbedarf 70,6 MiB. Build-Skript liegt jetzt im Repo (tools/build-cfd-image.py), ein Neubau ist byte-identisch (f818bf40…). 🔑 Der Pin ist die Binary-SHA256 e93824f6…, und cloudflared bestätigt sie beim Start selbst (Version 2026.6.1 (Checksum e93824f6…)) — im Log aller drei APs nachlesbar.

(b) docker save taugt seit Docker 25 nicht mehr als Quelle. Mit containerd-Image-Store schreibt es OCI-Layout (blobs/sha256/, gzip-Layer); RouterOS will docker-v1 und meldet no config found in manifest — klingt nach kaputtem Image, ist das Format. (Konverter für den Fall, dass man doch ein Docker-Image braucht: tools/oci2docker.py, prüft die Layer-Digests gegen rootfs.diff_ids.)

(c) /container/envs heisst auf ROS 7.24.1 list=, nicht name=. Mit name= kommt bad parameter name bzw. unknown parameter name. Auch MikroMCPs manage_container_env benutzt name= und ist damit für dieses Feld unbrauchbar.


4. Vier weitere Funde

  • 🔴 Der device-mode-Bestätigungs-Boot installiert KEINE Pakete. Auf ap1 lag die .npk schon bereit, als device-mode gesetzt wurde — der Stromzyklus hat container: yes bestätigt und die Datei verbraucht, ohne zu installieren. Reihenfolge ist Pflicht: erst Paket-Reboot, dann device-mode. (Nachholen ging per SSH, ohne zweiten Gang zum Gerät.)
  • 🟡 A - AVAILABLE in /system package print ist ein Download-Katalog, kein Paket auf dem Gerät. Gegenprobe: crs326-core hat 2,9 MB frei von 16 MB, listet aber wifi-qcom (10,1 MB) und rose-storage (4,3 MB) als „available"; die Grössen sind arch-spezifisch. enable löste einen Internet-Fetch aus — auf ap1/ap2 per device-mode=home gesperrt. Der .npk-Weg bleibt richtig.
  • 🟡 MikroMCPs export_config exportiert unvollständig: 301 Bytes gegen 6,2 KiB bei direktem /export. Ein Rollback-Artefakt, das existiert und trotzdem fast leer ist — dieselbe Klasse wie „Lücke statt Fehlermeldung". Für Backups /export file=… per SSH.
  • ⚠️ WLAN-Abnahme nur teilweise belegbar: main (3 Clients) und iot (27) haben frische Leases; media/untrusted/guest hatten schon vor dem Umbau null Clients. Kein Rückschritt, aber auch kein Beleg — nicht als „grün" buchen.

5. 🔴 Sicherheitsvorfall in dieser Session — Rotation nötig

Beim Suchen des Nautobot-Tokens hat eine unzureichende sed-Maskierung (grep -n … | sed 's/:.*TOKEN/…/' | cut -c1-60) zwei Werte im Klartext ins Transkript geschrieben:

  • NAUTOBOT_SUPERUSER_API_TOKEN
  • NAUTOBOT_GITEA_TOKEN

beide aus docs/nas-vlan20/stacks/210-nautobot.yml. Als verbrannt behandeln → TP30. Das ist exakt die Falle, die seit 2026-08-28 in der CLAUDE.md steht (der SHELLY_DEVICE_PW-Fall). Merkregel, die offenbar noch nicht scharf genug formuliert war: ein Maskierungs-Muster, das auf : ankert, matcht bei grep -n den Zeilennummer-Präfix statt den Wert. Sicher ist nur sed -E 's/(TOKEN|PASSWORD|SECRET|KEY|PASS)=.*/\1=<redacted>/' — auf den Schlüsselnamen ankern, nie auf ein Trennzeichen, das auch im Präfix vorkommt.


6. Repo-Zustand

  • Branch claude/nervous-williams-acf000 ist hier hereingemergt. Konflikte aufgelöst:
    • Nummernkonflikt TP37: main hatte TP37 = Nautobot als Netzwerkdoku (User-Okay 08-30), der Branch TP37 = OOB-Mgmt. Letzterer ist auf TP38 umnummeriert. ⚠️ Braucht dein Okay — Nummernvergabe ist laut Konvention deine Entscheidung.
    • Drei CF-Rollback-Snapshots hatten denselben Dateinamen bei verschiedenem Inhalt (bf-mm v4/6 Regeln vs. v6/2 Regeln, analog bf-nas, bf-pve) — zwei Zeitpunkte, beide „vor-tp16.3-schnitt" genannt. Keiner verworfen: der ältere liegt jetzt als *-vor-hostname-schnitt.json daneben.
  • Neu: tools/build-cfd-image.py, tools/oci2docker.py.
  • mcp/mikromcp/config/routers.yaml meldet weiter rosVersion: "7.24" für alle fünf (live 7.24.1) — kosmetisch, unangetastet gelassen.

7. Nächste Schritte

  1. Entscheidung zu Punkt 2 (sechs Drifts ins Soll heben) — vor dem Flip, sonst geht man mit erklärter, aber ungeklärter Drift in den Produktivpfad.
  2. §5 CNAME-Flip, einzeln mit Abnahme dazwischen, Testname zuerst (§5.1). 🔴 healthy beweist nur, dass der Connector mit Cloudflare redet — nicht, dass das Origin antwortet. Der Streckenbeweis bis Caddy ist §5.1.
  3. §6 Abbau frühestens 7 Tage nach dem Flip.
  4. TP30: die zwei Tokens aus Punkt 5 rotieren.