feat(bootstrap): staging role — the host archetype for box-minted staging VMs
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
1b68d3ca49
commit
c4d64fb037
3 changed files with 41 additions and 8 deletions
9
bin/rig
9
bin/rig
|
|
@ -8,11 +8,12 @@ usage() {
|
||||||
usage: rig <command> [args]
|
usage: rig <command> [args]
|
||||||
|
|
||||||
commands:
|
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,
|
OS plumbing on a pristine Debian box: hardening, unattended-upgrades,
|
||||||
tailscale join. Prompts for a single-use tailnet pre-auth key
|
tailscale join. Prompts for a single-use TAGGED tailnet pre-auth key
|
||||||
(TS_AUTHKEY env overrides the prompt). Run as root. Role runner
|
(TS_AUTHKEY env overrides the prompt); the key's tags are the tailnet
|
||||||
defaults to tag:ci and refuses tag:server.
|
tag, verified after join. Run as root. Roles runner and staging
|
||||||
|
refuse tag:server.
|
||||||
coolify install --version <pin>
|
coolify install --version <pin>
|
||||||
Pinned Coolify install (AUTOUPDATE=false). Control-plane box only.
|
Pinned Coolify install (AUTOUPDATE=false). Control-plane box only.
|
||||||
coolify backup install [options]
|
coolify backup install [options]
|
||||||
|
|
|
||||||
|
|
@ -13,10 +13,15 @@ die() { printf 'rig-bootstrap: ERROR: %s\n' "$1" >&2; exit "${2:-1}"; }
|
||||||
|
|
||||||
usage() {
|
usage() {
|
||||||
cat <<'EOF'
|
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)
|
--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,
|
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
|
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
|
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) ---------
|
# --- args (validated before the root check, so errors are testable) ---------
|
||||||
ROLE="${1:-}"
|
ROLE="${1:-}"
|
||||||
case "$ROLE" in
|
case "$ROLE" in
|
||||||
control-plane|workload|runner) shift ;;
|
control-plane|workload|runner|staging) shift ;;
|
||||||
-h|--help) usage; exit 0 ;;
|
-h|--help) usage; exit 0 ;;
|
||||||
"") usage >&2; die "role required (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)" 2 ;;
|
*) die "unknown role: $ROLE (want control-plane|workload|runner|staging)" 2 ;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
TS_HOSTNAME="$ROLE"
|
TS_HOSTNAME="$ROLE"
|
||||||
|
|
@ -71,6 +76,12 @@ if [ -r /etc/os-release ]; then
|
||||||
else
|
else
|
||||||
warn "cannot read /etc/os-release; proceeding anyway"
|
warn "cannot read /etc/os-release; proceeding anyway"
|
||||||
fi
|
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
|
# 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
|
# 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)."
|
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
|
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' ' ')"
|
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>"
|
log "next: rig coolify install --version <pin>"
|
||||||
elif [ "$ROLE" = "runner" ]; then
|
elif [ "$ROLE" = "runner" ]; then
|
||||||
log "next: rig runner install --repo <owner/repo> --version <pin>"
|
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
|
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
|
"$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" \
|
check "bootstrap: --ts-tag is removed (no value), exit 2" 2 "comes from the pre-auth key" \
|
||||||
"$ROOT/commands/bootstrap.sh" runner --ts-tag
|
"$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
|
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: 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: 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
|
else
|
||||||
echo "skip: bootstrap non-root refusals (running as root)"
|
echo "skip: bootstrap non-root refusals (running as root)"
|
||||||
fi
|
fi
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue