slice 8A (agent half): local-API server + provisioning back-half (v0.10.0)
internal/localapi: per-guest local-API server (doc 03 §6) — 7 self-scoped endpoints, hashed per-guest token store, persisted self-signed leaf with stable SHA-256 pin, optional 6th daemon goroutine. internal/provision: back-half — mint token, render bootstrap.json (no registry cred), write 0600, chown 100000:100000, attach pct-set bind mount (host-side, F3, no pct exec). --selftest=provision. build-golden.sh bakes the controller image + bootstrap unit. sudoers FELHOM_PROVISION; firewall narrowing artifact. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+61
-1
@@ -14,7 +14,15 @@
|
||||
# symmetric with machine-id.
|
||||
# - machine-id: truncated; systemd regenerates it on first boot for free (no unit needed).
|
||||
#
|
||||
# Usage: build-golden.sh [VMID] [TEMPLATE_VOLID] [ROOTFS_STORAGE] [ARCHIVE_STORAGE] [BRIDGE]
|
||||
# Slice 8A — the golden now also BAKES the in-guest controller (decision: image baked at golden
|
||||
# build on the trusted host, so NO registry credential ever enters a customer guest at deploy) and
|
||||
# a controller-bootstrap unit that, on boot, deploys the baked image from the agent-populated
|
||||
# config mount (/etc/felhom-bootstrap/bootstrap.json) — no docker login/pull at deploy. Refreshing
|
||||
# the golden bumps the controller baseline; controller self-update covers in-between drift.
|
||||
#
|
||||
# Usage: build-golden.sh [VMID] [TEMPLATE_VOLID] [ROOTFS_STORAGE] [ARCHIVE_STORAGE] [BRIDGE] [CONTROLLER_IMAGE]
|
||||
# Build-time registry login for the controller pull (used ONCE inside the build guest, then logged
|
||||
# out — never baked): set REGISTRY_USER + REGISTRY_TOKEN in the environment.
|
||||
set -euo pipefail
|
||||
|
||||
VMID="${1:-9100}"
|
||||
@@ -22,6 +30,8 @@ TEMPLATE="${2:-local:vztmpl/debian-13-standard_13.1-2_amd64.tar.zst}"
|
||||
ROOTFS_STORAGE="${3:-local-lvm}"
|
||||
ARCHIVE_STORAGE="${4:-local}"
|
||||
BRIDGE="${5:-vmbr0}"
|
||||
CONTROLLER_IMAGE="${6:-gitea.dooplex.hu/admin/felhom-controller:v0.35.0}"
|
||||
REGISTRY_HOST="${CONTROLLER_IMAGE%%/*}"
|
||||
|
||||
echo "[golden] creating build LXC $VMID (nesting=1,keyctl=1, unprivileged) …"
|
||||
pct create "$VMID" "$TEMPLATE" \
|
||||
@@ -52,6 +62,56 @@ pct exec "$VMID" -- bash -c '
|
||||
echo "[golden] verifying Docker works in the build guest …"
|
||||
pct exec "$VMID" -- bash -c 'systemctl start docker; sleep 2; docker run --rm hello-world >/dev/null && echo " docker OK ($(docker info 2>/dev/null | sed -n "s/.*Storage Driver: //p"))"'
|
||||
|
||||
echo "[golden] baking the in-guest controller image $CONTROLLER_IMAGE (no registry cred at deploy) …"
|
||||
# docker login is used ONCE here on the trusted build host, then logged out before archiving so
|
||||
# the credential is NEVER baked into the golden. The IMAGE is what gets baked (in Docker storage).
|
||||
if [ -n "${REGISTRY_USER:-}" ] && [ -n "${REGISTRY_TOKEN:-}" ]; then
|
||||
pct exec "$VMID" -- bash -c "systemctl start docker; sleep 1; echo '$REGISTRY_TOKEN' | docker login '$REGISTRY_HOST' -u '$REGISTRY_USER' --password-stdin >/dev/null"
|
||||
fi
|
||||
pct exec "$VMID" -- bash -c "docker pull '$CONTROLLER_IMAGE'"
|
||||
pct exec "$VMID" -- bash -c "docker logout '$REGISTRY_HOST' >/dev/null 2>&1 || true; rm -f /root/.docker/config.json"
|
||||
# Record the baked image ref for the bootstrap unit (so the unit needs no login/pull).
|
||||
pct exec "$VMID" -- bash -c "printf '%s\n' '$CONTROLLER_IMAGE' > /etc/felhom-controller-image"
|
||||
|
||||
echo "[golden] baking the controller-bootstrap unit (deploys the BAKED controller from the config mount) …"
|
||||
pct push "$VMID" /dev/stdin /usr/local/sbin/felhom-controller-bootstrap.sh --perms 700 <<'BOOTSH'
|
||||
#!/bin/bash
|
||||
# felhom controller-bootstrap (slice 8A): the host agent's back-half populated the read-only
|
||||
# config mount /etc/felhom-bootstrap; this golden-baked oneshot deploys the BAKED controller image
|
||||
# with that config. NO docker login / NO docker pull — the image is already in this golden's Docker
|
||||
# storage (and self-update handles version drift). Host-side only; the agent never enters the guest.
|
||||
set -euo pipefail
|
||||
CFG=/etc/felhom-bootstrap/bootstrap.json
|
||||
[ -r "$CFG" ] || { echo "[ctrl-bootstrap] no $CFG — not provisioned, nothing to do"; exit 0; }
|
||||
IMAGE=$(cat /etc/felhom-controller-image 2>/dev/null || true)
|
||||
[ -n "$IMAGE" ] || { echo "[ctrl-bootstrap] FATAL: /etc/felhom-controller-image missing"; exit 1; }
|
||||
echo "[ctrl-bootstrap] deploying $IMAGE from $CFG"
|
||||
docker rm -f felhom-controller >/dev/null 2>&1 || true
|
||||
docker run -d --name felhom-controller --restart unless-stopped \
|
||||
-e FELHOM_BOOTSTRAP_PATH=/etc/felhom-bootstrap/bootstrap.json \
|
||||
-v /etc/felhom-bootstrap:/etc/felhom-bootstrap:ro \
|
||||
-v felhom-controller-data:/opt/docker/felhom-controller \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||
"$IMAGE"
|
||||
echo "[ctrl-bootstrap] controller started"
|
||||
BOOTSH
|
||||
pct exec "$VMID" -- bash -c 'cat > /etc/systemd/system/felhom-controller-bootstrap.service <<UNIT
|
||||
[Unit]
|
||||
Description=Felhom controller bootstrap (deploy the baked controller from the agent-populated config mount)
|
||||
After=docker.service network-online.target
|
||||
Wants=docker.service network-online.target
|
||||
ConditionPathExists=/etc/felhom-bootstrap/bootstrap.json
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
RemainAfterExit=yes
|
||||
ExecStart=/usr/local/sbin/felhom-controller-bootstrap.sh
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
UNIT
|
||||
systemctl enable felhom-controller-bootstrap.service'
|
||||
|
||||
echo "[golden] baking the first-boot SSH host-key regeneration unit (F3) …"
|
||||
pct exec "$VMID" -- bash -c 'cat > /etc/systemd/system/felhom-regen-hostkeys.service <<UNIT
|
||||
[Unit]
|
||||
|
||||
Reference in New Issue
Block a user