Commit graph

3 commits

Author SHA1 Message Date
dan-claude-bot
88243dcdbb fix(revoke): purge re-checks the incus-user state dir — as root, not as a hopeful stat
/var/lib/incus/users/<uid> was removed for releases without being re-checked,
and a bare [ -d ] from a non-root admin answers 'absent' for a directory that
is very much there. Both the removal guard and the absence assert now test
through $SUDO, closing the one residue the purge promised gone but never
proved.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 16:01:57 +00:00
dan-claude-bot
ec5e898094 revoke/grant: survive the live-session case — the review's one real hole (#74)
Supplementary groups are read at LOGIN, so 'gpasswd -d' does nothing to a
session the user already holds — and after --purge, a stale-group process
could touch incus-user and lazily RECREATE the project with stock defaults:
the unhardened NAT bridge, un-narrowed, strictly worse than the granted
state. Adversarial review caught it; verified live, then closed:

- revoke --purge terminates the user's sessions first (loginctl, then
  pkill), and refuses to purge under processes it cannot kill
- bare revoke says out loud that held sessions keep the socket until they
  end, and names the loginctl command — instead of claiming a lockout it
  did not deliver (help/README/design doc reworded to match)
- a failed grant backs out its own group-add on exit (trap, disarmed on
  success): no half-granted user holding an un-narrowed socket while the
  admin reads the error. Verified by injecting a bad profile YAML
- the rehearsal now holds a session open across the purge and demands it
  dies with the tier (criterion l, 42nd check)

Smaller review findings, same pass: the escape-hatch probes assert the
refusal's REASON instead of any nonzero exit (an image hiccup must not read
as 'the escape is closed'); probe_from maps an outer-timeout kill to
dropped, not reachable; the rehearsal cleanup keeps the account when a purge
fails so doctor can name the leftovers; the purge asserts the trust
certificate's absence; cmd_new distinguishes a dead daemon from a missing
stack before prescribing setup-host; grant's success message names the
user-<uid> bridge variant correctly on big-uid hosts.

Rehearsal after: 42/42 (containers). test/cli.sh: 76 checks.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 05:11:20 +00:00
dan-claude-bot
0429a11020 feat: the restricted tier — box grant/revoke converge users onto the hardened boxnet (#74)
incus-user confines an incus-group user to their own project, but its
defaults miss box's contract three measured ways (Debian 13 / Incus 6.0.4):
a private UNHARDENED NAT bridge per user (ipv6.nat=true, no ACL, no DNS
isolation), snapshots blocked, and the box-net profile invisible to their
project. So the tier is an admin-run idempotent convergence:

  box grant <user>   # incus group; touch incus-user (the project is lazy);
                     # drop the private-bridge eth0 from their default
                     # profile; restricted.networks.access=boxnet — and ONLY
                     # boxnet, or the unhardened bridge stays one --network
                     # flag away; restricted.snapshots=allow; install the
                     # shipped box-net profile into their project
  box revoke <user>  # group removal closes the socket, boxes keep running
         --purge     # ...or delete their world, and assert the absence

box_tier() (live credentials, argless id -nG; byte-identical copy in
setup-host.sh) drives the tier-aware surface: new pre-flights the profile
and names the right fix per tier, expose refuses before any daemon call
(without the guard the failure is a lie — restricted certs cannot read
boxnet's redacted config, so box_net_ip claims a running box has no
address), setup-host exits 0 with the honest note, doctor judges only what
the caller can see.

Also fixed while the rehearsal exercised the lifecycle: box restore
dispatched 'incus restore', which does not exist in Incus 6 (it is
'incus snapshot restore') — the verb had never worked. Fixed for every tier.

Convergence survives incus-user restarts by that tool's own design (it
configures a project only at creation) — read in its source, then measured.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-18 04:09:48 +00:00