13 checks failed on any box with a Forgejo runner installed — which is exactly a box that has been drilled or used as a ci-box. Unmodified main, 773/13 on this machine. Seven bootstrap --undo checks fell through to the real host scan. The production code already ships the escape hatch and documents it — bootstrap-undo.sh:33, "RIG_FORGEJO_RUNNER_DIR mirrors RIG_RUNNER_DIR above so tests can point this at a fixture" — and the suite simply never set it. One check rebuilt its env by hand instead of using undo(), so it needed the same variable a second time. Six ci-box checks drove the real template installer, which correctly exits 0 when /usr/local/bin/forgejo-runner already exists. CIBOX_BIN is a test-only override in the same spirit; the production default is untouched and remains the only path the mechanism uses. Neither guard is disarmed: driven for real, undo still refuses while a runner exists, and the template installer still does nothing when the binary is present. Removing either override brings its failures straight back (7 and 6). Closes #136 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
129 lines
6.6 KiB
Bash
Executable file
129 lines
6.6 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
# ci-box — the forgejo-runner binary. Run BY THE MECHANISM as root, with
|
|
# TENANT_USER/TENANT_HOME/TENANT_GROUP/ROLE exported.
|
|
#
|
|
# This lands the BINARY ONLY. Registration is deliberately not here: it needs a
|
|
# registration token from the Forgejo instance, and a tenant install is
|
|
# creds-free by contract — box auto-runs it at mint, holding nothing. The
|
|
# operator registers afterwards, out loud:
|
|
#
|
|
# Deliberately NOT described the way the GitHub sibling describes its own
|
|
# registration token, which really does expire in about an hour. That adjective
|
|
# must not cross this forge boundary — a test pins its absence from this file
|
|
# and from creds.md, so do not reintroduce it by copying from `rig runner`.
|
|
# Forgejo's ActionRunnerToken carries no expiry field at all;
|
|
# NewRunnerToken invalidates prior tokens only when a replacement is minted at
|
|
# the same scope, and Register leaves the one it was handed active. It is
|
|
# reusable until replaced, so a leak stays live. See creds.md, which is the
|
|
# copy an agent inside the box actually reads.
|
|
#
|
|
# box shell ci-box
|
|
# sudo rig forgejo-runner install --instance https://forgejo.example.com
|
|
#
|
|
# Same split as staging-box's tailnet join, for the same reason.
|
|
#
|
|
# Root-owned under /usr/local/bin rather than the tenant's home: unlike an
|
|
# agent CLI, this binary is run by a systemd unit as the tenant user, and a
|
|
# tenant-writable binary that root's unit executes is a trivial path to root
|
|
# inside the box.
|
|
set -euo pipefail
|
|
|
|
# fetch_and_verify_sha256 <asset-url> <file> <sumfile> <label>
|
|
#
|
|
# The whole checksum POLICY, in one place: fetch the published .sha256 beside
|
|
# an asset and prove the download matches it. Prints the reason on stderr and
|
|
# returns 1 on any failure; the caller supplies the refusal in its own voice.
|
|
#
|
|
# BYTE-IDENTICAL to the copy in commands/forgejo-runner-install.sh, diffed by
|
|
# test/cli.sh — the valid_version / templates_archive_urls precedent. The two
|
|
# downloaders cannot share a lib: that one sources commands/lib/, and this one
|
|
# is a REGISTRY DEFINITION that runs standalone inside a mint from a fetched
|
|
# tarball, with rig's tree nowhere in reach. So the pin is the only mechanism
|
|
# that keeps one policy from becoming two.
|
|
#
|
|
# Review !110 is the evidence for why that matters: a fail-open branch lived in
|
|
# BOTH copies while a grep for "checksum mismatch" passed against both, because
|
|
# the string it looked for sat right beside the branch it could not see. The
|
|
# next checksum-policy change must not be able to land in one file only.
|
|
#
|
|
# AN UNFETCHABLE CHECKSUM REFUSES — it is a gate, not a courtesy. The earlier
|
|
# reasoning ("do not let an upstream layout change break installs") reasons
|
|
# about the wrong failure: a layout change moves the BINARY url too, so the
|
|
# download would already have died. "Binary yes, checksum no" is not what a
|
|
# layout change looks like — it is what an interfered fetch looks like, which
|
|
# is precisely what a checksum exists to catch. Failing open would hand an
|
|
# unverified root install to anyone able to block a single URL. There is
|
|
# deliberately no bypass flag: if upstream really does move its assets, that is
|
|
# a rig PR editing the URL, not an operator improvising past a security gate.
|
|
fetch_and_verify_sha256() {
|
|
local url="$1" file="$2" sumfile="$3" label="$4" want got
|
|
if ! curl -fsSL "${url}.sha256" -o "$sumfile" 2>/dev/null; then
|
|
printf 'no published .sha256 for %s at %s.sha256 — the binary itself downloaded, so this is not an upstream layout change; check what is intercepting the fetch\n' "$label" "$url" >&2
|
|
return 1
|
|
fi
|
|
# The published .sha256 names the asset, not our temp path. Compare the
|
|
# digest itself rather than rewriting the file into sha256sum -c's format:
|
|
# one comparison, no parsing of a file we did not write.
|
|
want="$(tr -d '\r' < "$sumfile" 2>/dev/null | awk '{print $1}' | head -n1)"
|
|
got="$(sha256sum "$file" | awk '{print $1}')"
|
|
if [ -z "$want" ]; then
|
|
printf 'the published checksum for %s is unreadable — a fetch that succeeds but returns nothing usable is not a verified download\n' "$label" >&2
|
|
return 1
|
|
fi
|
|
if [ "$want" != "$got" ]; then
|
|
printf 'checksum mismatch for %s: published %s, downloaded %s\n' "$label" "$want" "$got" >&2
|
|
return 1
|
|
fi
|
|
printf 'checksum verified (%s)\n' "$got"
|
|
}
|
|
|
|
# CIBOX_BIN is a TEST-ONLY override, in the same spirit as bootstrap-undo.sh's
|
|
# RIG_FORGEJO_RUNNER_DIR: the production default is the only path the mechanism
|
|
# ever uses, but test/cli.sh must be able to drive this script on a box that
|
|
# already has a real runner installed. Without it the early-exit below fires
|
|
# against the host and the checksum checks silently test nothing (#136).
|
|
BIN="${CIBOX_BIN:-/usr/local/bin/forgejo-runner}"
|
|
|
|
if [ -x "$BIN" ]; then
|
|
exit 0
|
|
fi
|
|
|
|
case "$(uname -m)" in
|
|
x86_64) ARCH="amd64" ;;
|
|
aarch64) ARCH="arm64" ;;
|
|
*) echo "ci-box install: unsupported arch: $(uname -m)" >&2; exit 1 ;;
|
|
esac
|
|
|
|
# The latest release, resolved by following the releases/latest redirect — no
|
|
# API call, no token, no JSON to parse on a dependency-free guest. A pinned
|
|
# version belongs to `rig forgejo-runner install --version`, which is where an
|
|
# operator who needs a deterministic install already is; a pin baked into the
|
|
# registry would go stale in a repo nobody watches.
|
|
LATEST_URL="$(curl -fsSLI -o /dev/null -w '%{url_effective}' \
|
|
https://code.forgejo.org/forgejo/runner/releases/latest)" \
|
|
|| { echo "ci-box install: could not resolve the latest forgejo-runner release" >&2; exit 1; }
|
|
VERSION="${LATEST_URL##*/}"
|
|
VERSION="${VERSION#v}"
|
|
case "$VERSION" in
|
|
""|*[!0-9.]*) echo "ci-box install: could not parse a version from ${LATEST_URL}" >&2; exit 1 ;;
|
|
esac
|
|
|
|
ASSET="forgejo-runner-${VERSION}-linux-${ARCH}"
|
|
URL="https://code.forgejo.org/forgejo/runner/releases/download/v${VERSION}/${ASSET}"
|
|
|
|
WORKDIR="$(mktemp -d)"
|
|
cleanup() { rm -rf "$WORKDIR"; }
|
|
trap cleanup EXIT
|
|
|
|
echo "ci-box install: downloading forgejo-runner ${VERSION} (${ARCH})"
|
|
curl -fsSL "$URL" -o "$WORKDIR/forgejo-runner" \
|
|
|| { echo "ci-box install: could not download ${URL}" >&2; exit 1; }
|
|
|
|
# Forgejo publishes a .sha256 beside each binary. Verifying it costs one
|
|
# request and makes the install auditable; this file executes as root inside
|
|
# every future mint, so an unverified download is the last thing it should do.
|
|
fetch_and_verify_sha256 "$URL" "$WORKDIR/forgejo-runner" "$WORKDIR/forgejo-runner.sha256" "$ASSET" \
|
|
|| { echo "ci-box install: refusing to install an unverified ${ASSET} — it lands as root inside every mint. See the checksum failure above." >&2; exit 1; }
|
|
|
|
install -m 0755 -o root -g root "$WORKDIR/forgejo-runner" "$BIN"
|
|
echo "ci-box install: installed ${BIN}"
|