Der Branch enthaelt jetzt origin/main (0680cca, TP25 Fund E / TP27) und die Korrekturc8b2a15aus claude/nervous-williams-acf000. Inhaltliche Korrektur an meinen eigenen Dateien - Ich hatte die wifi-security-Drift (ft-over-ds=no auf sec-main) dem 7.24.1-Upgrade zugeschrieben. Falsch: gc-transaktion.py status fuehrt TP25-fund-e-ft-over-ds als OFFEN -- der Hebel wurde 08:09 bewusst gesetzt, der Core erst 14:36 gehoben. Sie gehoert einer laufenden TP25-Session, braucht keine Entscheidung und darf nicht gruen gemacht werden. Korrigiert im §5-Prompt und im §2/§3-Handoff. - Merkregel in CLAUDE.md: vor jeder Drift-Bewertung gc-transaktion.py status lesen. Dazu die Abgrenzung Transaktion (befristeter Versuch) vs. Soll/Template (Dauerzustand) -- die sechs AP-Drifts aus TP16.3 §3.4 sind Dauerzustand und gehoeren deshalb ins Template, nicht in eine Transaktion. - Notiert: gc-transaktion.py findet den Token aus einem Worktree heraus nicht (210-nautobot.yml liegt nur im Haupt-Checkout); die Transaktionsliste kommt trotzdem, nur die Compliance-Spalte fehlt. Konflikt in teilprojekte.md (ROS-7.24.1-Zeile) zusammengefuehrt statt eine Seite zu verwerfen: TP27-Erledigung samt Zeitstempeln aus main plus die npk-Folge fuer TP16.3 aus diesem Branch. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
147 lines
8.1 KiB
Markdown
147 lines
8.1 KiB
Markdown
# 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` — **offene Transaktion `TP25-fund-e-ft-over-ds`**, kein Versehen |
|
||
|
||
**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.
|
||
|
||
⚠️ **Korrektur 2026-08-31 (nachgetragen):** die siebte Drift wurde hier zuerst dem 7.24.1-Upgrade
|
||
zugeschrieben — falsch. `gc-transaktion.py status` führt **`TP25-fund-e-ft-over-ds` als OFFEN**
|
||
(gesetzt 08:09, Core-Upgrade erst 14:36). Sie ist Absicht einer laufenden Messung und braucht **keine**
|
||
Entscheidung. **Merkregel: vor jeder Drift-Bewertung `gc-transaktion.py status` lesen** — Drift aus
|
||
einem laufenden Versuch sieht identisch aus wie ein Versehen.
|
||
|
||
**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.
|
||
✅ **Von Christoph bestätigt (2026-08-31).**
|
||
- **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).
|
||
▸ Fertiger Übergabeprompt: [`tp16.3-paragraph5-session-handoff-prompt.md`](tp16.3-paragraph5-session-handoff-prompt.md).
|
||
🔴 `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.
|