test: drive bootstrap undo safety gates

This commit is contained in:
codex-bot-andresmgsl 2026-07-24 01:33:53 +00:00
parent 8b4a55a712
commit c0272cbae6
3 changed files with 101 additions and 7 deletions

View file

@ -237,11 +237,33 @@ the only shapes it manages — every other role refuses an effective
`tag:server` after join, one rule instead of per-role exceptions. `tag:server` after join, one rule instead of per-role exceptions.
After the tag verification passes, bootstrap writes `/etc/rig/role` — one After the tag verification passes, bootstrap writes `/etc/rig/role` — one
line, `role=… root-door=… host=… join=…` — recording the **effective** traits, line, `role=… root-door=… host=… join=… join-by=…` — recording the
overrides and all, so an overridden role never lies to the commands that read **effective** traits, overrides and all, plus whether this run performed the
the marker later (`rig users` keys root policy off `root-door=`). Written tailnet join. `join-by=rig` means bootstrap called `tailscale up`;
post-join and cmp-guarded, so a marker never describes a box that failed to `join-by=preexisting` means it found the node already joined. Old markers name
become what it claims. neither and are treated as unknown, never as permission to remove a join.
Written post-join and cmp-guarded, so a marker never describes a box that
failed to become what it claims.
### `rig bootstrap --undo`
```sh
sudo rig bootstrap --undo
```
Leaves the tailnet and then removes `/etc/rig/role`, but only when the marker
says `join-by=rig`. A pre-existing join, an old marker with no provenance, or
no marker at all is refused without calling `tailscale logout`; the refusal
names the manual repair. Re-running bootstrap writes the current marker shape.
Undo also refuses while a GitHub runner is installed and points at
`rig runner remove`, because restoring the local machine while leaving an
off-box runner registration would create a ghost in the repository. If
`tailscale logout` fails, the marker stays in place so the command is retryable.
This is intentionally not a general rollback. It does not uninstall packages,
reverse sshd hardening, remove Docker, Node, agent CLIs, or users. Those changes
are convergent rather than transactional and cannot be safely inferred away.
Immediately after it, bootstrap stamps `/etc/rig/manifest`**provenance**: Immediately after it, bootstrap stamps `/etc/rig/manifest`**provenance**:
which rig converged this box and when (see [`rig which rig converged this box and when (see [`rig

View file

@ -6,12 +6,21 @@ log() { printf 'rig-bootstrap: %s\n' "$*"; }
die() { printf 'rig-bootstrap: ERROR: %s\n' "$*" >&2; exit 1; } die() { printf 'rig-bootstrap: ERROR: %s\n' "$*" >&2; exit 1; }
MARKER="${RIG_ROLE_MARKER:-/etc/rig/role}" MARKER="${RIG_ROLE_MARKER:-/etc/rig/role}"
RUNNER_DIR="${RIG_RUNNER_DIR:-/home/github-runner/actions-runner}"
[ "$(id -u)" -eq 0 ] || die "must run as root" [ "$(id -u)" -eq 0 ] || die "must run as root"
[ -e "$MARKER" ] || die "no /etc/rig/role marker — refusing to touch the tailnet" [ -e "$MARKER" ] || die "no /etc/rig/role marker — refusing to touch the tailnet"
if [ -e "$RUNNER_DIR/.runner" ]; then runner_installed=0
if [ -n "${RIG_RUNNER_DIR:-}" ]; then
[ -e "$RIG_RUNNER_DIR/.runner" ] && runner_installed=1
else
for runner_config in /home/*/actions-runner/.runner /root/actions-runner/.runner; do
[ -e "$runner_config" ] && runner_installed=1
done
compgen -G '/etc/systemd/system/actions.runner.*.service' >/dev/null \
&& runner_installed=1
fi
if [ "$runner_installed" -eq 1 ]; then
die "a GitHub runner is installed — run 'rig runner remove' first so undo does not leave a ghost runner in the repository" die "a GitHub runner is installed — run 'rig runner remove' first so undo does not leave a ghost runner in the repository"
fi fi

View file

@ -143,6 +143,69 @@ check "bootstrap: login verify fails closed on a stalled backend" 0 "" \
# The marker is the traits' ground truth for rig users; assert the write exists. # The marker is the traits' ground truth for rig users; assert the write exists.
check "bootstrap: role marker write is present" 0 "" \ check "bootstrap: role marker write is present" 0 "" \
grep -q "/etc/rig/role" "$ROOT/commands/bootstrap.sh" grep -q "/etc/rig/role" "$ROOT/commands/bootstrap.sh"
check "bootstrap: role marker records join provenance" 0 "join-by=%s" \
grep -F "join-by=%s" "$ROOT/commands/bootstrap.sh"
# shellcheck disable=SC2016
check "bootstrap: both first-join paths record join-by=rig" 0 "2" \
bash -c 'test "$(grep -c "^[[:space:]]*JOIN_BY=rig$" "$1")" -eq 2; echo 2' _ \
"$ROOT/commands/bootstrap.sh"
check "bootstrap: already-joined path defaults to join-by=preexisting" 0 "JOIN_BY=preexisting" \
grep -F "JOIN_BY=preexisting" "$ROOT/commands/bootstrap.sh"
# Drive the narrow inverse end to end. Every refusal also asserts the tailscale
# shim was NOT called: exit status alone would miss the destructive regression.
UNDO_FIX="$(mktemp -d)"
UNDO_BIN="$UNDO_FIX/bin"
UNDO_MARKER="$UNDO_FIX/role"
UNDO_RUNNER="$UNDO_FIX/runner"
UNDO_CALLS="$UNDO_FIX/tailscale.calls"
mkdir -p "$UNDO_BIN" "$UNDO_RUNNER"
cat > "$UNDO_BIN/tailscale" <<'SH'
#!/usr/bin/env bash
printf '%s\n' "$*" >> "$UNDO_CALLS"
if [ "${TAILSCALE_LOGOUT_FAIL:-0}" = 1 ]; then exit 1; fi
SH
cat > "$UNDO_BIN/id" <<'SH'
#!/usr/bin/env bash
if [ "${1:-}" = -u ]; then printf '0\n'; else exec /usr/bin/id "$@"; fi
SH
chmod +x "$UNDO_BIN/tailscale" "$UNDO_BIN/id"
undo() {
env PATH="$UNDO_BIN:$PATH" UNDO_CALLS="$UNDO_CALLS" \
RIG_ROLE_MARKER="$UNDO_MARKER" RIG_RUNNER_DIR="$UNDO_RUNNER" \
"$ROOT/bin/rig" bootstrap --undo
}
undo_untouched() {
: > "$UNDO_CALLS"
if undo >/tmp/rig-undo.out 2>&1; then return 1; fi
[ ! -s "$UNDO_CALLS" ]
}
rm -f "$UNDO_MARKER"
check "bootstrap --undo: no marker refuses without touching tailnet" 0 "" undo_untouched
printf '%s\n' 'role=workload-server root-door=open host=no join=authkey' > "$UNDO_MARKER"
check "bootstrap --undo: old marker names missing provenance" \
1 "marker predates join-by provenance" undo
check "bootstrap --undo: old marker leaves tailnet untouched" 0 "" undo_untouched
printf '%s\n' 'role=workload-server root-door=open host=no join=authkey join-by=preexisting' > "$UNDO_MARKER"
check "bootstrap --undo: pre-existing join refuses by name" 1 "join-by=preexisting" undo
check "bootstrap --undo: pre-existing join leaves tailnet untouched" 0 "" undo_untouched
printf '%s\n' 'role=runner-server root-door=open host=no join=authkey join-by=rig' > "$UNDO_MARKER"
printf '%s\n' '{}' > "$UNDO_RUNNER/.runner"
check "bootstrap --undo: installed runner points at its removal verb" \
1 "rig runner remove" undo
check "bootstrap --undo: installed runner leaves tailnet untouched" 0 "" undo_untouched
rm -f "$UNDO_RUNNER/.runner"
check "bootstrap --undo: failed logout is loud" \
1 "role marker kept" env TAILSCALE_LOGOUT_FAIL=1 PATH="$UNDO_BIN:$PATH" \
UNDO_CALLS="$UNDO_CALLS" RIG_ROLE_MARKER="$UNDO_MARKER" \
RIG_RUNNER_DIR="$UNDO_RUNNER" "$ROOT/bin/rig" bootstrap --undo
check "bootstrap --undo: failed logout preserves the marker" 0 "" test -e "$UNDO_MARKER"
: > "$UNDO_CALLS"
check "bootstrap --undo: proven rig join succeeds" 0 "tailnet join removed" undo
check "bootstrap --undo: successful logout was called" 0 "logout" cat "$UNDO_CALLS"
check "bootstrap --undo: success removes the marker" 1 "" test -e "$UNDO_MARKER"
check "bootstrap --undo: second run refuses cleanly" 1 "no /etc/rig/role marker" undo
rm -rf "$UNDO_FIX" /tmp/rig-undo.out
# ...and that it is written in the CURRENT vocabulary (#77). New markers say # ...and that it is written in the CURRENT vocabulary (#77). New markers say
# root-door=; the retired class= spelling is something rig READS forever and # root-door=; the retired class= spelling is something rig READS forever and
# WRITES never, so a marker line that reintroduces it must not ship green. # WRITES never, so a marker line that reintroduces it must not ship green.