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

140 lines
7.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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`](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.