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>
This commit is contained in:
59
tools/build-cfd-image.py
Executable file
59
tools/build-cfd-image.py
Executable file
@@ -0,0 +1,59 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Baut ein minimales docker-v1-Image-Archiv (nur cloudflared + CA-Bundle),
|
||||
wie RouterOS /container es einliest. Kein Docker noetig, damit das Format
|
||||
nicht vom containerd-Store der Build-Maschine abhaengt."""
|
||||
import hashlib, io, json, os, sys, tarfile, time
|
||||
|
||||
binp, cap, out = sys.argv[1], sys.argv[2], sys.argv[3]
|
||||
MTIME = 0 # feste mtime -> reproduzierbares Archiv
|
||||
|
||||
def add(tf, name, data, mode, typ=tarfile.REGTYPE):
|
||||
ti = tarfile.TarInfo(name)
|
||||
ti.mode, ti.mtime, ti.uid, ti.gid, ti.type = mode, MTIME, 0, 0, typ
|
||||
ti.size = len(data) if typ == tarfile.REGTYPE else 0
|
||||
tf.addfile(ti, io.BytesIO(data) if typ == tarfile.REGTYPE else None)
|
||||
|
||||
# --- Layer bauen ---
|
||||
layer = io.BytesIO()
|
||||
with tarfile.open(fileobj=layer, mode="w", format=tarfile.GNU_FORMAT) as tf:
|
||||
for d in ("usr/", "usr/local/", "usr/local/bin/", "etc/", "etc/ssl/",
|
||||
"etc/ssl/certs/", "tmp/"):
|
||||
add(tf, d, b"", 0o755 if d != "tmp/" else 0o1777, tarfile.DIRTYPE)
|
||||
add(tf, "usr/local/bin/cloudflared", open(binp, "rb").read(), 0o755)
|
||||
add(tf, "etc/ssl/certs/ca-certificates.crt", open(cap, "rb").read(), 0o644)
|
||||
layer_bytes = layer.getvalue()
|
||||
diff_id = "sha256:" + hashlib.sha256(layer_bytes).hexdigest()
|
||||
|
||||
# --- Image-Config ---
|
||||
cfg = {
|
||||
"architecture": "arm64",
|
||||
"os": "linux",
|
||||
"config": {
|
||||
"Env": ["PATH=/usr/local/bin:/usr/bin:/bin",
|
||||
"SSL_CERT_FILE=/etc/ssl/certs/ca-certificates.crt"],
|
||||
"Entrypoint": ["/usr/local/bin/cloudflared"],
|
||||
"Cmd": ["version"],
|
||||
"WorkingDir": "/",
|
||||
},
|
||||
"rootfs": {"type": "layers", "diff_ids": [diff_id]},
|
||||
"history": [{"created": "1970-01-01T00:00:00Z",
|
||||
"created_by": "bf-infra TP16.3: cloudflared + CA, FROM scratch"}],
|
||||
}
|
||||
cfg_raw = json.dumps(cfg, sort_keys=True, separators=(",", ":")).encode()
|
||||
cfg_name = hashlib.sha256(cfg_raw).hexdigest() + ".json"
|
||||
|
||||
manifest = json.dumps([{"Config": cfg_name,
|
||||
"RepoTags": ["cfd-min:2026.6.1"],
|
||||
"Layers": ["layer/layer.tar"]}]).encode()
|
||||
|
||||
with tarfile.open(out, "w", format=tarfile.GNU_FORMAT) as tf:
|
||||
add(tf, cfg_name, cfg_raw, 0o644)
|
||||
add(tf, "layer/", b"", 0o755, tarfile.DIRTYPE)
|
||||
add(tf, "layer/VERSION", b"1.0", 0o644)
|
||||
add(tf, "layer/json", json.dumps({"id": "layer"}).encode(), 0o644)
|
||||
add(tf, "layer/layer.tar", layer_bytes, 0o644)
|
||||
add(tf, "manifest.json", manifest, 0o644)
|
||||
|
||||
print(f"Layer roh : {len(layer_bytes)} B ({len(layer_bytes)/1048576:.1f} MiB)")
|
||||
print(f"Archiv : {os.path.getsize(out)} B ({os.path.getsize(out)/1048576:.1f} MiB)")
|
||||
print(f"diff_id : {diff_id}")
|
||||
Reference in New Issue
Block a user