Skip to content

strict: a UID of its own for every sandbox (#27) - #47

Merged
chxmxii merged 1 commit into
mainfrom
strict-slots
Oct 12, 2026
Merged

chxmxii merged 1 commit into
mainfrom
strict-slots

Conversation

@chxmxii

@chxmxii chxmxii commented Oct 12, 2026

Copy link
Copy Markdown
Member

Closes #27. Part of #30 and the v0.5 milestone.

Every isolation: strict sandbox ran its VMM as the same subordinate UID, so a guest that escaped both its VM and its VMM could reach every other strict sandbox's /data. Now each strict sandbox runs as a UID of its own.

How it works

  • The user's subordinate range is cut into slots of 65536 IDs. Each strict sandbox gets the lowest free slot, recorded in its directory: it survives rename and is freed on destroy. A fork shares its parent's slot, since they share files.
  • podman's --uidmap/--gidmap, the Firecracker jail's process identity, its /data disk and sockets all use the slot.
  • /data belongs to the slot's UID with your group and mode 0750: you can still read it, no other sandbox's UID can. The Firecracker disk is 0600.
  • No free slot → box refuses to start the sandbox and prints the exact usermod line that adds a non-overlapping range, instead of letting two sandboxes share a UID. box doctor shows how many strict sandboxes the range holds.

Fixed on the way

  • Files imported (box data import) or restored into a strict sandbox's /data were left owned by you, so the guest could read but not change them. They are now given to the sandbox.
  • A destroyed sandbox's kept /data is handed back to you, so the next sandbox given its slot does not own it.

Tests

  • Unit: slot allocation, no sharing, refusal with the fix, freeing on destroy, stable across rename, counted even without a Boxfile, ID mapping across multiple ranges.
  • Real VMs (WSL2, KVM): a strict sandbox's /data is 524288:1000 0750; a second strict sandbox is refused with the fix on a one-slot range; destroying the first hands its data back and the second gets the freed slot; imported files are owned by the sandbox and writable by its guest.
  • Escape suite, new other sandboxes section: the VMM's UID owns this sandbox's /data; /data is closed to others; a UID from another slot cannot read it. podman strict and firecracker strict: no escapes.

Not yet verified

The live "read as another slot's UID" check needs a range with two or more slots; on the test machine (65536 IDs) it is skipped and reported as skipped. Run security/escape-test.sh ./box strict on a machine with a larger /etc/subuid range to exercise it.

Upgrade note

Default /etc/subuid ranges are 65536 IDs: one strict sandbox. A second one now stops with the fix instead of sharing a UID. Release notes for v0.5 must say so.

Every isolation: strict sandbox ran its VMM as the same subordinate UID, so a
guest that escaped both its VM and its VMM could reach every other strict
sandbox's /data. Now each strict sandbox gets its own slot of 65536 IDs from
the user's subordinate range, recorded with the sandbox and freed on destroy;
a fork shares its parent's. podman's --uidmap, the Firecracker jail's process
identity, its disk and sockets all use the slot.

/data belongs to the slot's UID, with your group and mode 0750, so you can
still read it and no other sandbox's UID can. When the range has no free slot,
box refuses to start the sandbox and prints the usermod line that adds one,
rather than let two sandboxes share a UID; box doctor shows the capacity.

Also fixed on the way: files imported or restored into a strict sandbox's
/data were left owned by you, so the guest could read but not change them;
they are now given to the sandbox. A destroyed sandbox's kept /data is handed
back to you, so the next sandbox given its slot does not own it. The slot
allocator counts every sandbox directory, Boxfile or not.

The escape suite gains an "other sandboxes" section: the VMM's UID owns this
sandbox's /data, /data is closed to others, and a UID from another slot
cannot read it (skipped, and said so, when the range has one slot). podman
and firecracker strict: no escapes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@chxmxii
chxmxii merged commit b31ac59 into main Oct 12, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

A UID per strict sandbox

1 participant