Addresses codex (1538) and kimi (1539): the sweep caught the reported
incident and not the class. Both are right, and there were four sites, not
three.
forgejo-runner-install useradd -> useradd usermod (usermod -aG docker,
reached only after the token has been spent)
users-apply useradd usermod -> + groupadd (called two lines
into convergence), and visudo when a role needs it
bootstrap-tenant NEW site (kimi) — usermod -aG docker runs AFTER
docker and node are installed, so an unguarded
PATH fails it mid-convergence on a changed machine
runner-install unchanged: useradd is the only admin binary it
calls, and declaring more would refuse boxes that
are fine
visudo is checked after the sudo-install block rather than beside the root
check, because until sudo is installed its absence has an innocent cause.
Below that block it does not: sudo is present, so a missing visudo means
/usr/sbin is off PATH. That case is the quiet one — the sudoers block reads
`command -v visudo` as "no sudo on the box means no role needed it", so
apply reported success having granted roles without the escalation those
roles exist for. The other three sites at least crash.
Measured which binaries this covers (Debian 13): useradd, usermod, groupadd,
userdel, groupdel and visudo are /usr/sbin; gpasswd is /usr/bin and so is
NOT affected and deliberately not preflighted. visudo shares the directory
but ships in `sudo`, not `passwd` — which is why it needs its own treatment.
Tests: the sbin-less fixtures could only ever prove the FIRST binary is
named, since useradd wins every race. Six new checks use partial PATHs that
resolve the earlier binaries and withhold exactly one, plus the ordering
assertions (no token prompt, no group created) and the negative case — a
users file needing no sudo must NOT be refused for a missing visudo.
Refs #139
47 lines
2.6 KiB
Bash
47 lines
2.6 KiB
Bash
#!/usr/bin/env bash
|
|
# admin-path.sh — assert the admin binaries are REACHABLE, not merely that we
|
|
# are root.
|
|
#
|
|
# Being uid 0 and being able to find useradd are different facts, and rig
|
|
# asserted only the first. `su` without `-`, sudo with a sanitised secure_path,
|
|
# and several container images all hand you a root shell whose PATH carries no
|
|
# /usr/sbin — which is where useradd, usermod and groupadd live on Debian. The
|
|
# result was a bare `useradd: command not found` naming a line number inside a
|
|
# versioned install root, emitted AFTER a registration token had been read off
|
|
# the operator's terminal (#139).
|
|
#
|
|
# Which binaries this covers is measured, not assumed (Debian 13, 2026-08-01):
|
|
#
|
|
# useradd usermod groupadd userdel groupdel /usr/sbin package: passwd
|
|
# visudo /usr/sbin package: sudo
|
|
# gpasswd /usr/bin package: passwd
|
|
#
|
|
# Two consequences worth keeping written down. `gpasswd` is in the same PACKAGE
|
|
# as useradd but a different DIRECTORY, so it is reachable on a PATH-shorn root
|
|
# and does not belong in any of these preflights — do not add it for symmetry.
|
|
# And `visudo` shares the directory but not the package, so its absence has a
|
|
# second, innocent cause (sudo simply not installed) that the others do not, so
|
|
# `rig users apply` checks it separately, after the point where that cause is
|
|
# ruled out. See the comment there.
|
|
#
|
|
# (Spelled without the `.sh` on purpose: test/cli.sh pins that exactly one file
|
|
# under commands/ names that script, to catch a second caller appearing. A
|
|
# comment is not a caller, but the pin is deliberately blunt and cheap.)
|
|
#
|
|
# It REFUSES rather than repairing PATH itself. A command that quietly prepends
|
|
# /usr/sbin teaches the operator nothing and leaves a misconfigured host
|
|
# misconfigured; the same reason bootstrap refuses rather than guessing. The
|
|
# message carries the fix so the refusal costs one paste, not an investigation.
|
|
|
|
# require_admin_bins <bin>... — die unless every one resolves on PATH.
|
|
require_admin_bins() {
|
|
local missing=() b
|
|
for b in "$@"; do
|
|
command -v "$b" >/dev/null 2>&1 || missing+=("$b")
|
|
done
|
|
[ "${#missing[@]}" -eq 0 ] && return 0
|
|
# Names the REMEDY, not this script: the operator typed a `rig ...` command,
|
|
# and echoing the internal path back at them is the unhelpful half of the
|
|
# original `useradd: command not found`.
|
|
die "cannot find ${missing[*]} on PATH — it lives in /usr/sbin, which this root shell does not carry (a 'su' without '-' does this, and so do some container images). Re-run the same rig command with: PATH=/usr/sbin:/sbin:\$PATH"
|
|
}
|