#!/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}")