Files
felhom.eu/scripts/felhom-bundle-bootstrap.sh
T

55 lines
3.0 KiB
Bash

#!/bin/bash
# felhom-bundle-bootstrap.sh — the ONE by-hand act that lets an installed box take config bundles (R-840, `11` §5.4.2).
#
# Why it exists: a signed agent_config_update is installed by the box's ROOT-OWNED felhom-os-apply. A box installed
# before agent v0.143.0 has an older felhom-os-apply that has no bundle mode, and no signed job can write a root file
# on such a box (that gap IS R-840). So the first bundle needs this one step, as root, once per box. After it, every
# later change to the box's root files arrives by the signed route.
#
# What it does: downloads the config bundle of AGENT_VERSION, checks its sha256 against the one you pass (the vouched
# one — the hub's Configuration page or the release output), takes out felhom-os-apply, checks it against the bundle's
# own entry and that it parses, installs it (0755 root:root, the old copy kept beside it), and runs its self-check.
# It changes NOTHING else: no sudoers, no unit, no restart, no app, no Docker.
#
# Usage (as root on the Proxmox host): bash felhom-bundle-bootstrap.sh <agent-version> <bundle-sha256>
set -euo pipefail
VER="${1:-}"; SHA="${2:-}"
[[ "$VER" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] || { echo "usage: $0 <agent-version> <bundle-sha256>" >&2; exit 2; }
[[ "$SHA" =~ ^[0-9a-f]{64}$ ]] || { echo "the bundle sha256 must be 64 lowercase hex characters" >&2; exit 2; }
[[ $EUID -eq 0 ]] || { echo "run as root" >&2; exit 2; }
URL="https://gitea.dooplex.hu/api/packages/admin/generic/felhom-agent/$VER/felhom-config-bundle.json"
DST=/usr/local/sbin/felhom-os-apply
T=$(mktemp -d); trap 'rm -rf "$T"' EXIT
echo "1/4 download $URL"
curl -fsS -o "$T/bundle.json" "$URL"
got=$(sha256sum "$T/bundle.json" | awk '{print $1}')
[[ "$got" == "$SHA" ]] || { echo "STOP: the bundle's sha256 is $got, not $SHA — nothing changed" >&2; exit 1; }
echo " sha256 OK ($SHA)"
echo "2/4 take felhom-os-apply out of the bundle and check it"
python3 - "$T/bundle.json" "$T/os-apply" "$VER" <<'PY'
import ast, base64, hashlib, json, sys
b = json.load(open(sys.argv[1]))
assert b.get("agent_version") == sys.argv[3], f"the bundle is for {b.get('agent_version')}, not {sys.argv[3]}"
e = [f for f in b["files"] if f["path"] == "/usr/local/sbin/felhom-os-apply"][0]
data = base64.b64decode(e["content_b64"])
assert hashlib.sha256(data).hexdigest() == e["sha256"], "felhom-os-apply does not match its own sha in the bundle"
text = data.decode()
ast.parse(text)
assert 'BUNDLE_OP = "agent_config_update"' in text, "this felhom-os-apply has no bundle mode"
open(sys.argv[2], "wb").write(data)
print(" felhom-os-apply sha256", e["sha256"])
PY
echo "3/4 install it (the previous copy is kept as $DST.pre-bundle)"
[[ -f "$DST" ]] && cp -p "$DST" "$DST.pre-bundle"
install -m 0755 -o root -g root "$T/os-apply" "$DST.new.$$"
mv "$DST.new.$$" "$DST"
echo "4/4 self-check"
out=$(python3 "$DST" --self-check)
echo " $out"
[[ "$out" == *"bundle-format=1"* ]] || { echo "STOP: the self-check failed — put the old copy back: mv $DST.pre-bundle $DST" >&2; exit 1; }
echo "DONE. This box can now take signed config bundles. Nothing else was changed."