Files
felhom-agent/configs/build-golden.sh
T
admin 6e036e4c75 slice 7: golden host-key unit path fix + live-validation results (v0.8.0)
Live validation on demo-felhom caught the felhom-regen-hostkeys unit failing with
203/EXEC: ExecStart was /usr/sbin/ssh-keygen but on Debian 13 ssh-keygen is at
/usr/bin/ssh-keygen. Fixed build-golden.sh, rebuilt the golden, re-validated — host
keys now regenerate on first boot by the baked unit (agent issues no ssh-keygen).

All three live scenarios green: provision (fresh MAC, host keys via unit, machine-id,
Docker, DHCP), dr (continuity: hostname + host keys preserved), Recover (killed
mid-restore -> orphan rolled back idempotently). REPORT + CHANGELOG updated.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 21:37:40 +02:00

92 lines
4.1 KiB
Bash

#!/usr/bin/env bash
# build-golden.sh — build the Felhom golden base LXC archive (slice 7).
#
# Produces a minimal Debian + Docker, unprivileged, nesting=1,keyctl=1, overlayfs LXC, baked
# identity-clean, and archives it for a token-restore by the bring-up reconcile job
# (internal/reconcile/bringup.go). Run as root@pam on a Proxmox host (the keyctl=1 feature flag
# is root-only — phase3 #1; this is the ONE root step, off the per-customer path).
#
# Grounded by documentation/tests/slice7-bringup-spike-findings.md (commit 3342993):
# - F3: removing the SSH host keys does NOT auto-regenerate them on Debian (pct restore runs no
# keygen hook), so a baked, Condition-gated first-boot unit regenerates them — keeping the
# agent's front half host-side-only. The gate (ConditionPathExists=!…) makes it fire on a
# provision (golden, keys absent) and no-op on a DR restore (customer backup, keys present),
# 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]
set -euo pipefail
VMID="${1:-9100}"
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}"
echo "[golden] creating build LXC $VMID (nesting=1,keyctl=1, unprivileged) …"
pct create "$VMID" "$TEMPLATE" \
--hostname felhom-golden --unprivileged 1 \
--features nesting=1,keyctl=1 \
--rootfs "${ROOTFS_STORAGE}:8" --cores 2 --memory 2048 \
--net0 "name=eth0,bridge=${BRIDGE},ip=dhcp" --onboot 0
echo "[golden] starting + installing Docker (official repo, trixie channel) …"
pct start "$VMID"
# wait for DHCP/DNS
for i in $(seq 1 30); do
if pct exec "$VMID" -- getent hosts download.docker.com >/dev/null 2>&1; then break; fi
sleep 1
done
pct exec "$VMID" -- bash -c '
set -e
export DEBIAN_FRONTEND=noninteractive
apt-get update -qq
apt-get install -y -qq ca-certificates curl >/dev/null
install -m0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc
echo "deb [signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian trixie stable" \
> /etc/apt/sources.list.d/docker.list
apt-get update -qq
apt-get install -y -qq docker-ce docker-ce-cli containerd.io >/dev/null
'
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 first-boot SSH host-key regeneration unit (F3) …"
pct exec "$VMID" -- bash -c 'cat > /etc/systemd/system/felhom-regen-hostkeys.service <<UNIT
[Unit]
Description=Regenerate SSH host keys on first boot if absent
ConditionPathExists=!/etc/ssh/ssh_host_ed25519_key
DefaultDependencies=no
After=local-fs.target
Before=ssh.service sshd.service
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/bin/ssh-keygen -A
[Install]
WantedBy=multi-user.target
UNIT
systemctl enable felhom-regen-hostkeys.service'
echo "[golden] identity-clean + minimize …"
pct exec "$VMID" -- bash -c '
systemctl stop docker containerd 2>/dev/null || true
apt-get clean; rm -rf /var/lib/apt/lists/*
rm -f /etc/ssh/ssh_host_* # regenerated on first boot by the baked unit (F3)
truncate -s 0 /etc/machine-id # systemd regenerates on first boot (free)
rm -f /var/lib/dbus/machine-id; ln -sf /etc/machine-id /var/lib/dbus/machine-id
rm -rf /var/log/*; : > /root/.bash_history
rm -f /etc/hostname # set per-guest at provision (host-side token config)
'
echo "[golden] stop + archive …"
pct stop "$VMID"
vzdump "$VMID" --storage "$ARCHIVE_STORAGE" --mode stop --compress zstd
VOLID=$(pvesm list "$ARCHIVE_STORAGE" --content backup 2>/dev/null | awk -v v="$VMID" '$1 ~ ("vzdump-lxc-" v "-") {print $1}' | sort | tail -1)
echo "[golden] DONE. golden archive volid: ${VOLID:-<check ${ARCHIVE_STORAGE} dump dir>}"
echo "[golden] (the build guest $VMID is stopped; destroy it with: pct destroy $VMID --purge)"