rig admin commands assume /usr/sbin is on PATH — a stock su root shell dies with 'useradd: command not found', after prompting for a token #139
Labels
No labels
attention
blocked
blocker:ci-red
blocker:conflict
blocker:drill-pending
blocker:unrequested
bug
claimed
documentation
enhancement
epic
merge-next
needs-ruling
needs-triage
offsite
post-merge
ready
release
scope:bootstrap
scope:coolify
scope:db
scope:docs
scope:drill
scope:installer
scope:labels
scope:platform
scope:runner
scope:users
stale
state:addressing
state:bots-reviewing
state:building
state:needs-human
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: heavy-duty/rig#139
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Context
rig forgejo-runner installdies with a bare shell error when root'sPATHlacks/usr/sbin. Reported by @andres on a real ci-box, 2026-08-01:Reproduced exactly:
suwithout-,sudowith a sanitisedsecure_path, and several container images all produce a root shell whosePATHcarries no/usr/sbin. rig checksid -uand concludes it can administer the machine — but being root and being able to find the admin binaries are different facts, and only the first is asserted.Why it matters more than the one-line failure suggests
The failure lands after the registration token has been read from the operator's terminal, so a secret has already been typed for a run that cannot succeed. The token survives (registration never happened), but nothing in the message says so, and the operator's reasonable next move — mint a fresh token — is wasted work.
The message is also the kind rig exists not to emit:
useradd: command not foundnames neither the cause nor the fix, and points at a line number inside a versioned install root.Scope — three call sites, one class
No rig command guards this today.
users-applyis the one that worries me most: it runs mid-convergence, so a PATH-shorn root could fail it partway through a user sweep rather than before it starts.Other
/usr/sbinbinaries rig invokes should be audited in the same pass (usermod,groupadd,visudo,sshd,systemctlis/usr/binon Debian 13 but not everywhere).Spec
Assert the capability, not just the identity, and fail before anything is spent.
dienaming which one, why (/usr/sbinmissing fromPATH), and the fix.bootstraprefuse rather than guess. An operator toldPATH=/usr/sbin:/sbin:$PATHlearns something; a command that quietly rewritesPATHteaches nothing and hides a misconfigured host.Tasks
forgejo-runner-install.sh,runner-install.sh,users-apply.sh, beside each root check/usr/sbininvocations and include the ones foundtest/cli.sh: a PATH without/usr/sbinrefuses, names the binary, and refuses before promptingchangelog.d/<n>.mdAcceptance criteria
/usr/sbinoffPATH, each of the three commands exits non-zero naming the missing binary and the remedyPATH, behaviour is byte-unchangedPATHon the operator's behalfbash test/cli.shpasses; CI'sshellcheck -xstep is cleanTest plan
env PATH=/usr/local/bin:/usr/bin:/binand assert the refusal text.FORGEJO_RUNNER_TOKEN, the PATH-shorn run must exit without reading a token.Dependencies
None. Independent of #115; it makes #115's step 3 work on a stock
suroot shell.@andres — filed from your ci-box report. Immediate workaround while this lands:
Your token is still good — registration never ran, so nothing was consumed.
Panel status on !140
@andres @cluade-reviewer-andresmgsl — re-reviewed !140:
7f2501dBlocking: preflight every admin bin each command actually uses (
usermod/groupadd/visudo), coverbootstrap-tenant.sh, partial-PATH tests. Rebase ontomainfor #144 greencheck.Watching until close.
Park declared — the claim waits on a review round, not on me
Per BUILDER.md a park is declared, never inferred, so: my claim on this issue is in shape 2 — the live review round on !140, where the one outstanding verdict belongs to someone else.
7aed6eaci / check,ci / install,ci / db-integrationsuccess;labelsred is #145, red onmaintooBoth earlier REQUEST_CHANGES (@codex 1538, @kimi 1539) and @grok's withdrawal-and-re-request (1540) are answered in
7aed6ea: the preflight now covers every admin binary each command actually calls, across four call sites — @kimi'sbootstrap-tenant.shwas the one nobody had counted — plus thevisudocase @codex found, where a PATH-shorn root with sudo installed exits 0 having silently skipped the sudoers drop-in a role needs.Nothing is blocked on @andres yet. When @codex answers, the next moves are the
state:needs-humanhandoff and then the merge, which is yours.I will not merge or close anything.
Verified on merged
main—cb4de44@andres merged !140 and closed this at 07:57. Re-verified against
mainitself rather than carrying the PR's runs forward:main7aed6eais an ancestor ofcb4de44— nothing rewritten at mergeforgejo-runner-install(useradd usermod),users-apply(useradd usermod groupadd+visudo),bootstrap-tenant(usermod)ci / checkonmainci / install·ci / db-integration·releaselabels / labelsThe incident this issue was filed on —
useradd: command not found, emitted after the registration token had been read off the operator's terminal — now refuses before the prompt, on all four sites, with the remedy in the message.What the review round added, since it is the part worth keeping
I shipped three call sites and called that the sweep. @codex, @kimi and @grok independently said it caught the incident and not the class, and they were right — there were four. @kimi found
bootstrap-tenant.sh, which was the worst of them: it fails after installing docker and node, on a machine it has already changed.@codex's
visudocatch was the most serious thing on the PR, and it was not a missing preflight. On a PATH-shorn root with sudo installed,command -v visudoreads false, sousers applylogged "sudo not installed and no role needs it" — both halves false — and exited 0 having created the users and their groups and never written the sudoers drop-in those roles exist for. The other three sites crash. That one succeeded while being wrong, which is rig's own #12 class in its quietest form.Every guard carries a must-fail that was run, not asserted: reverting each of the four fails exactly its own check and nothing else. @kimi reproduced one independently.
Closed by @andres; nothing further from me.