Restricted incus tier: per-user projects converged onto hardened boxnet (#74) #75
3 changed files with 6 additions and 4 deletions
|
|
@ -22,8 +22,8 @@ which records not just what changed but what each drill run proved.
|
|||
call, `setup-host` and `doctor` answer at the caller's tier. Rehearsed
|
||||
end-to-end by `drill/multiuser.sh` (criteria a–l: confinement, lifecycle,
|
||||
cross-user visibility, name collisions, the in-box isolation contract,
|
||||
escape hatches, re-sync survival, revoke) — 41/41 on the design host, in
|
||||
both container and VM mode.
|
||||
escape hatches, re-sync survival, revoke incl. the live-session case) —
|
||||
42/42 on the design host, in both container and VM mode.
|
||||
- **CI runs the multi-user rehearsal on a real Incus** — a second `rehearsal`
|
||||
job stands up the full stack on the runner (setup-host, doctor, then
|
||||
`multiuser.sh --container`), so every PR proves the tier's semantics
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
# Restricted incus tier — design and measured results (#74)
|
||||
|
|
||||
|
||||
**Status: implemented and rehearsed.** 41/41 rehearsal criteria green on the
|
||||
**Status: implemented and rehearsed.** 42/42 rehearsal criteria green on the
|
||||
design host (Debian 13 trixie, Incus 6.0.4, nested KVM), in container mode
|
||||
and VM mode. This doc records the design, what was measured, and why each
|
||||
and VM mode — and green in CI on ubuntu-latest / incus 6.0.0 (whose one
|
||||
version-drift false FAIL is MU-4 in `drill/RUNS.md`). This doc records the design, what was measured, and why each
|
||||
decision fell where it did. It supersedes the vetoed #72 design
|
||||
(`docs/plans/2026-07-17-multiuser-hosts.md` on `feat/restricted-tier-wip`).
|
||||
|
||||
|
|
|
|||
|
|
@ -195,6 +195,7 @@ No listener is needed, and none should be started: see trap 3.
|
|||
|
||||
| Run | Result | What it cost |
|
||||
| --- | --- | --- |
|
||||
| MU-4 | 41/1 on CI (ubuntu-latest, incus 6.0.0) → 42/0 | the CI job earned its keep on its second-ever run: the attach-refusal probe matched 6.0.4's error wording and 6.0.0 words it differently (and refuses at a different stage) — a version-drift false FAIL the design host could never show. The probe now asserts the OUTCOME (nothing running on the private bridge) and prints the refusal as evidence. Also this round, from adversarial review: revoke survives the live-session case (groups are read at login; --purge now terminates sessions first, else a stale-group process could recreate the project UNHARDENED after the purge), grant backs out its own group-add on failure, and the session-dies-with-the-tier check is criterion 42 |
|
||||
| **MU-3** | **41/0 (containers) + 41/0 (VMs) — the tier holds** | `multiuser.sh` (#74) from zero on Debian 13 / Incus 6.0.4: grant, confinement, lifecycle, the in-box contract with the cross-user sibling drop, closed escape hatches, re-sync survival, scoped revoke. The VM pass needed right-sized boxes (1GiB): two 2GiB nested VMs on a 7.7GiB host measured swap, not isolation |
|
||||
| MU-2 | 40/41 (VMs) | the egress probe cried broken over a TLS handshake that outlived its 8s budget under nested-KVM memory pressure — every isolation probe (which PASSES on silence) was immune. Probe budgets raised, reachable-probes retry once, and the mint keeps its log for the post-mortem |
|
||||
| MU-1 | 39/41 (containers) | both FAILs were the instrument: a `box new` rc swallowed with its narration (the drill's oldest sin, relearned), and the enumeration probe resolving the box's OWN name — /etc/hosts answered, not the gateway. Probe a SIBLING's name or you measure cloud-init's hostname hygiene. Found for real along the way: `box restore` dispatched `incus restore`, which does not exist in Incus 6 — broken for every tier since the rename |
|
||||
|
|
|
|||
Loading…
Reference in a new issue
On a shared
boxnetthis contract is now doing tenant isolation, not just defense-in-depth. Note that of these four, onlysecurity.port_isolation(L2) and the nft bridge drop actually stop box→box — the ACL is L3/L4 and, perprofiles/box-net.yaml's own comment, was silently bypassed at L2. So the multi-tenant boundary rests on those two mechanisms. Worth stating that explicitly here.This widens access to include
boxnetbut (per #74)incusbr-<uid>stays in the access list too. Does the convergence also remove/repoint the auto default profile so a restricted user can'tincus launchonto their unhardenedincusbr-<uid>, skipping the box-net contract entirely? If not, the isolation stack is opt-in for anyone with rawincusin their project. Please spell out how the per-user bridge is neutralized — and whether the widenedrestricted.networks.accesssurvives an incus-user re-sync.For criteria (a)–(f), please include an active cross-user probe, not just "user A can't see user B's instances": from restricted user A's box, ARP/SYN/broadcast at user B's box on the same
boxnetsubnet, and assert silence. The single-owner drill already trustsport_isolation; multi-tenant is exactly where an L2 gap would bite, and #74's own reachability claim is the thing under test.CI (
.github/workflows/ci.yml) explicitly does not run the drill or the multiuser rehearsal —ubuntu-latestcan't reliably nest Incus +/dev/kvm. So "Test suite expansion + CI wiring" needs a concrete story: a self-hosted runner, or gated/manual drill jobs with shellcheck-only on GH-hosted. Otherwise "wired into CI" overstates what actually gates a merge.