users apply cannot tell 'remove everyone' from 'I truncated the file' #65
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
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: heavy-duty/rig#65
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?
rig users applycannot tell "remove everyone" from "I truncated the file"The situation
rig users apply <file>converges a box to the users file exactly: users in thefile are created and converged, users not in it are revoked — account
expired,
authorized_keysrenamed toauthorized_keys.revoked-by-rig, home anduid kept for attribution.
That is correct and deliberate. Removing a name from the file is how you
off-board someone.
But it means an empty file is a valid instruction to revoke every operator on
the box — and it is indistinguishable from an empty file produced by accident.
A stray
>in a shell, a failed generation step in a private infra repo, atruncated copy: all produce a file that says, in rig's language, "nobody should
have access to this machine."
Why the current safeguards do not cover it
Per-user warnings arrive after the decision and scale wrong. apply does warn
as it revokes each user. But twenty operators means twenty lines of scrollback,
which reads as noise rather than as "you are about to lock every human out of
this box." The signal is loudest exactly when it is least likely to be read.
#57/#59 fixed the adjacent case, deliberately not this one.
rig bootstrapnow refuses an empty users file, because bootstrap asserts who lives on a box
— an empty answer there is a self-contradiction, and a flat refusal costs
nothing.
rig users applyis a convergence verb where "converge to zero" is acomplete, legitimate instruction, so the same flat refusal would break real
de-provisioning. The two commands genuinely need different answers.
The distinction worth drawing
rig already keeps a ledger at
/etc/rig/usersof the users it manages. So it cantell these apart:
Only the second is dangerous, and rig has everything it needs to recognize it.
Proposed shape
A ledger-gated confirmation, not a refusal: when the file names zero users
and the ledger is non-empty, say plainly how many operators are about to be
revoked and require explicit confirmation.
The part that needs its own design round
The non-interactive case is the whole difficulty. apply runs unattended —
from
rig bootstrap, from CI, from a config-management run with no TTY. Aconfirmation that cannot be answered must not silently proceed and must not
silently hang. That means an explicit flag (
--yes/RIG_YES=1, matching box'sBOX_YESconvention) with a documented non-interactive contract, on the modelof
box revoke --purge, which already solves exactly this problem: destructive,confirmed on a TTY, refuses without one unless the env var says yes.
Worth designing against that precedent rather than inventing a new shape.
Open question
Should this apply to any mass revocation, not just the empty-file case? A file
that drops nineteen of twenty operators is nearly as alarming and currently just
as quiet. A threshold ("this run revokes N of M managed users") may be the more
honest gate — but it introduces a number someone has to justify, where "the file
is empty" is a bright line that needs none.
Context
Raised by the agent implementing #57 and recorded there rather than actioned,
since it changes a different command's contract than the one that PR touches.