feat(bootstrap): staging role — host archetype for box-minted staging VMs #23
3 changed files with 41 additions and 8 deletions
9
bin/rig
9
bin/rig
|
|
@ -8,11 +8,12 @@ usage() {
|
|||
usage: rig <command> [args]
|
||||
|
||||
commands:
|
||||
bootstrap <control-plane|workload|runner> [--hostname <name>] [--ts-tag <tag>]
|
||||
bootstrap <control-plane|workload|runner|staging> [--hostname <name>]
|
||||
OS plumbing on a pristine Debian box: hardening, unattended-upgrades,
|
||||
tailscale join. Prompts for a single-use tailnet pre-auth key
|
||||
(TS_AUTHKEY env overrides the prompt). Run as root. Role runner
|
||||
defaults to tag:ci and refuses tag:server.
|
||||
tailscale join. Prompts for a single-use TAGGED tailnet pre-auth key
|
||||
(TS_AUTHKEY env overrides the prompt); the key's tags are the tailnet
|
||||
tag, verified after join. Run as root. Roles runner and staging
|
||||
refuse tag:server.
|
||||
coolify install --version <pin>
|
||||
Pinned Coolify install (AUTOUPDATE=false). Control-plane box only.
|
||||
coolify backup install [options]
|
||||
|
|
|
|||
|
|
@ -13,10 +13,15 @@ die() { printf 'rig-bootstrap: ERROR: %s\n' "$1" >&2; exit "${2:-1}"; }
|
|||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
usage: rig bootstrap <control-plane|workload|runner> [--hostname <name>]
|
||||
usage: rig bootstrap <control-plane|workload|runner|staging> [--hostname <name>]
|
||||
|
||||
--hostname system + tailnet hostname (default: the role name)
|
||||
|
||||
Role staging is the host for box-minted staging VMs (Incus guests converged
|
||||
from inside with `rig bootstrap workload`). Mint its key with tag:local: the
|
||||
host is never managed by the control plane — its guest VMs are — so a staging
|
||||
host may not carry tag:server.
|
||||
|
||||
The tailnet tag is NOT a rig argument. A pre-auth key is minted WITH its tags,
|
||||
so the key is the single source of truth: rig no longer requests a tag it might
|
||||
disagree with. After the box joins, rig reads the tag control actually GRANTED
|
||||
|
|
@ -31,10 +36,10 @@ EOF
|
|||
# --- args (validated before the root check, so errors are testable) ---------
|
||||
ROLE="${1:-}"
|
||||
case "$ROLE" in
|
||||
control-plane|workload|runner) shift ;;
|
||||
control-plane|workload|runner|staging) shift ;;
|
||||
-h|--help) usage; exit 0 ;;
|
||||
"") usage >&2; die "role required (control-plane|workload|runner)" 2 ;;
|
||||
*) die "unknown role: $ROLE (want control-plane|workload|runner)" 2 ;;
|
||||
"") usage >&2; die "role required (control-plane|workload|runner|staging)" 2 ;;
|
||||
*) die "unknown role: $ROLE (want control-plane|workload|runner|staging)" 2 ;;
|
||||
esac
|
||||
|
||||
TS_HOSTNAME="$ROLE"
|
||||
|
|
@ -71,6 +76,12 @@ if [ -r /etc/os-release ]; then
|
|||
else
|
||||
warn "cannot read /etc/os-release; proceeding anyway"
|
||||
fi
|
||||
# A staging host exists to run VMs, so no /dev/kvm deserves a loud note — but
|
||||
# only a note: the role is rehearsed in containers, where /dev/kvm is
|
||||
# legitimately absent, and rig cannot tell a rehearsal from a misconfigured box.
|
||||
if [ "$ROLE" = "staging" ] && [ ! -e /dev/kvm ]; then
|
||||
warn "/dev/kvm is absent — a staging host is expected to run VMs. Harmless in a container rehearsal; on real hardware, enable virtualization (VT-x/AMD-V) in firmware."
|
||||
fi
|
||||
|
||||
# The pre-auth key is acquired LATER, in the tailscale block — and only if the
|
||||
# box has not already joined. rig is convergent by contract, so re-running it to
|
||||
|
|
@ -216,6 +227,15 @@ verify_effective_tag() {
|
|||
die "role runner joined with tag:server (effective tags: $(printf '%s' "$tags" | tr '\n' ' ')). The key you used grants tag:server to repo-controlled code; that must never happen. Re-run bootstrap with a key minted for a CI tag (e.g. tag:ci)."
|
||||
fi
|
||||
|
||||
# Same policy, staging flavor: a staging HOST is never managed by the control
|
||||
# plane — its guest VMs are, each registered there as its own server. The
|
||||
# fleet has already been bitten by a host wrongly carrying tag:server, which
|
||||
# extends every server grant to a box the control plane does not even know.
|
||||
# Refused, never warned; rig can DETECT this but not FIX it, so name the repair.
|
||||
if [ "$ROLE" = "staging" ] && printf '%s\n' "$tags" | grep -qx 'tag:server'; then
|
||||
die "role staging joined with tag:server (effective tags: $(printf '%s' "$tags" | tr '\n' ' ')). A staging host is never managed by the control plane — its guest VMs are. Re-run bootstrap with a key minted for tag:local."
|
||||
fi
|
||||
|
||||
log "verified effective tailnet tag(s): $(printf '%s' "$tags" | tr '\n' ' ')"
|
||||
}
|
||||
|
||||
|
|
@ -272,4 +292,6 @@ if [ "$ROLE" = "control-plane" ]; then
|
|||
log "next: rig coolify install --version <pin>"
|
||||
elif [ "$ROLE" = "runner" ]; then
|
||||
log "next: rig runner install --repo <owner/repo> --version <pin>"
|
||||
elif [ "$ROLE" = "staging" ]; then
|
||||
log "next: install the box CLI and run 'box setup-host' to prepare Incus, then mint staging boxes with 'box new --template staging'"
|
||||
fi
|
||||
|
|
|
|||
10
test/cli.sh
10
test/cli.sh
|
|
@ -47,9 +47,19 @@ check "bootstrap: --ts-tag is removed (with value), exit 2" 2 "comes from the pr
|
|||
"$ROOT/commands/bootstrap.sh" runner --ts-tag tag:server
|
||||
check "bootstrap: --ts-tag is removed (no value), exit 2" 2 "comes from the pre-auth key" \
|
||||
"$ROOT/commands/bootstrap.sh" runner --ts-tag
|
||||
check "bootstrap: staging + removed --ts-tag exits 2" 2 "comes from the pre-auth key" \
|
||||
"$ROOT/commands/bootstrap.sh" staging --ts-tag tag:server
|
||||
# The staging tag:server refusal rides the EFFECTIVE tag, inside
|
||||
# verify_effective_tag — a path that needs a real tailnet, so it belongs to the
|
||||
# rehearsal. What the harness CAN prove is that the refusal exists in the
|
||||
# shipped script: grep the die message, so a deleted guard cannot ship green
|
||||
# (the same reason the runner-install repo guard is grepped below).
|
||||
check "bootstrap: staging effective-tag refusal is present" 0 "" \
|
||||
grep -q "role staging joined with tag:server" "$ROOT/commands/bootstrap.sh"
|
||||
if [ "$(id -u)" -ne 0 ]; then
|
||||
check "bootstrap: refuses non-root" 1 "must run as root" env TS_AUTHKEY=x "$ROOT/commands/bootstrap.sh" workload
|
||||
check "bootstrap: runner role parses, refuses non-root" 1 "must run as root" env TS_AUTHKEY=x "$ROOT/commands/bootstrap.sh" runner
|
||||
check "bootstrap: staging role parses, refuses non-root" 1 "must run as root" env TS_AUTHKEY=x "$ROOT/commands/bootstrap.sh" staging
|
||||
else
|
||||
echo "skip: bootstrap non-root refusals (running as root)"
|
||||
fi
|
||||
|
|
|
|||
Loading…
Reference in a new issue