Repository navigation
Conversation
Expose individual host device nodes inside the sandbox behind a repeatable --device /dev/NAME flag. Nodes are validated at launch (absolute path under /dev, existing character or block device only; symlinks, symlinked parent directories and empty/./.. path components are refused) and bound with --dev-bind at the same path in the private /dev, sharing the host inode so the node keeps its host mode, owner, group and ACLs — access inside matches the host exactly, and the launcher never recreates nodes, changes permissions or runs sudo. Missing parent directories (e.g. /dev/snd) are created per launch and duplicate paths are bound once. A launch-time advisory warns when the node's mode bits deny the invoking user both read and write on the host (e.g. root:dialout 0660 without dialout membership) and names the host-side fix (udev rule or usermod); POSIX-ACL nodes are skipped, and the bind still happens either way. The synthetic /etc/passwd and /etc/group now include root and nobody plus every host group of the invoking user (id -G via getent), so group and owner lookups (getent, initgroups) resolve inside the sandbox while unmapped ids keep rendering as 65534/nobody; the access advisory relies on the same USER_GROUPS data. Device mounts follow the private /dev, so explicit --with/--with-ro binds can still override them and the OpenCode v2 isolation overlay stays last. The feature is default-off; without --device no device probes or mounts are added. Ported from the sandbox-podman-persistent-tmp working tree, scoped to the device feature only (no version bump — release numbering is the maintainer's call). Tests are self-contained against tests/conftest.py (34 new tests, including the NSS identity content check; full suite: 96 passed) so this PR does not depend on the GPU PR.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a new opt-in
--device /dev/NAMEswitch (repeatable) that exposes individual host device nodes to programs running directly inside opencodebox — serial ports, webcams, sound cards, etc. — with the same mode, owner, group, and ACLs as on the host. The launcher never recreates device nodes, changes device permissions, or runssudo; access inside the sandbox is authorized exactly like on the host.This addresses setups like a typical RHEL 9 server, where there is no graphical login, systemd-logind grants no per-seat ACLs, and device access comes from group membership (
dialout,video,audio) that the host user may be missing.Behavior
--dev-bindat the same path inside the sandbox's private/dev(the bind shares the host inode)./devpointing at an existing character or block device; symlinks, symlinked parent directories, and paths with empty,., or..components are refused, and validation failure aborts the launch./dev/snd) are created inside the sandbox before the node is bound; repeating an identical--devicebinds it once.root:dialout 0660withoutdialoutmembership) and names the host-side fix (udev rule orusermod -aG); POSIX-ACL nodes are skipped, and the bind still happens either way./etc/passwdand/etc/groupnow includerootandnobodyplus every host group of the invoking user, so group and owner lookups (getent, initgroups) resolve inside the sandbox; ids not mapped into the sandbox's user namespace still render as65534/nobody, which is a harmless display artifact. The advisory relies on the sameid -Gdata./devbut before explicit user binds and the OpenCode 2 isolation overlay, so later bindings still win.--device, no device probes or mounts are added.Verification
Full test suite: 96 passed (62 existing + 34 new). New
tests/test_device.pycovers the standalone helper contract (path/type validation, dedup, parent-directory creation, symlink refusal) and the CLI contract (parsing errors, advisory hints, composition with seccomp/isolation, NSS identity contents); it is self-contained againsttests/conftest.pyand does not depend on #12.Live checks from a checkout:
Notes for review
--gpu); the two PRs share only one identical, additive hunk intests/conftest.py(run_launcher(script=...)) and adjacent README insertions, so they can merge in either order.--dev-bindsemantics are unchanged across those versions; host permissions decide whether a bound node can be opened.