Skip to content

cmd/create: stop bind-mounting the host /dev , fixing VirtualBox issues#1778

Open
Knogle wants to merge 1 commit intocontainers:mainfrom
Knogle:fix-no-host-dev-bind-mount
Open

cmd/create: stop bind-mounting the host /dev , fixing VirtualBox issues#1778
Knogle wants to merge 1 commit intocontainers:mainfrom
Knogle:fix-no-host-dev-bind-mount

Conversation

@Knogle
Copy link
Copy Markdown

@Knogle Knogle commented Apr 4, 2026

Toolbx currently bind-mounts the host /dev wholesale into the container.

That breaks container startup on hosts where the user has group-only access to device
subtrees under /dev, for example VirtualBox's /dev/vboxusb/* owned by
root:vboxusers with 0750 permissions. In that situation the OCI runtime ends up
walking the host /dev during container startup and fails before the Toolbx container
can be entered.

Instead of trying to preserve host supplementary groups inside the container, this
change takes a different approach:

  • stop bind-mounting the host /dev into the container during toolbox create
  • let the container runtime provide the container's own /dev
  • during init-container, project host devices from /run/host/dev into /dev
    on a best-effort basis
  • skip runtime-managed pseudo devices such as null, zero, urandom, tty,
    ptmx, pts, shm, stdin, stdout, and stderr

This keeps the startup path away from the problematic host /dev bind mount while
preserving the existing Toolbx user model inside the container, including the current
wheel/sudo behaviour.

Why this approach

The previous keep-groups direction fixes the startup failure, but it also changes
the container's user/group semantics and breaks existing expectations around
wheel and sudo.

This patch is intentionally narrower:

  • it fixes the default goal of "Toolbx should still start on hosts with problematic
    /dev subtrees"
  • it does not rely on inheriting host supplementary groups into the container
  • it leaves the current Toolbx user setup unchanged

This is a startup fix, not a full host-device-access policy.

Behavioural impact

With this change:

  • Toolbx no longer bind-mounts the host /dev wholesale
  • containers can be created and started on hosts where /dev/vboxusb exists and
    the user is in vboxusers
  • wheel membership and sudo inside the container continue to work

This does not automatically grant usable access to VirtualBox USB device nodes
inside the container as an unprivileged user. The goal here is to prevent Toolbx from
failing to start in the first place.

Tests

Manual checks on a host with:

  • /dev/vboxusb present
  • /dev/vboxusb/* owned by root:vboxusers with 0750
  • user in vboxusers

Verified that:

  • toolbox create succeeds with this branch where the old /dev bind mount path
    was the problematic part
  • the resulting container has no global /dev bind mount (podman inspect only
    shows /dev/pts)
  • groups inside the container still shows the expected wheel membership
  • sudo id inside the container still works
  • Stuff like /dev/urandom still exists and is readable inside the container

Also added a system test that checks the host /dev is no longer bind-mounted
into the container.

Related

Fixes: #1348
Related: #1297, #247
Superseeds: #1732

Signed-off-by: Fabian Druschke <fdruschke@outlook.com>
@Knogle Knogle force-pushed the fix-no-host-dev-bind-mount branch from 42690e9 to 727292d Compare April 4, 2026 19:42
Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request changes how host devices are handled by removing the global bind-mount of /dev and instead projecting individual devices from /run/host/dev during container initialization. This approach excludes runtime-managed devices and symbolic links. Feedback suggests optimizing the device projection loop by utilizing the existing DirEntry information instead of re-stating files and considering the use of the unix.Mount syscall to improve performance when handling a large number of device nodes.

Comment on lines +1101 to +1105
fileInfo, err := os.Lstat(source)
if err != nil {
logrus.Debugf("%s: failed to lstat %s: %s", logPrefix, source, err)
continue
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Calling os.Lstat here is redundant and inefficient because os.ReadDir already provides a DirEntry which contains the file type information. You can use entry.Type() and entry.IsDir() directly, or entry.Info() if you need the full FileInfo.

Suggested change
fileInfo, err := os.Lstat(source)
if err != nil {
logrus.Debugf("%s: failed to lstat %s: %s", logPrefix, source, err)
continue
}
fileInfo, err := entry.Info()
if err != nil {
logrus.Debugf("%s: failed to get info for %s: %s", logPrefix, source, err)
continue
}

Comment on lines +1118 to +1120
if err := mountBind(containerPath, source, flags); err != nil {
logrus.Debugf("%s: failed to bind %s to %s: %s", logPrefix, containerPath, source, err)
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Executing the mount command in a loop for every entry in /dev can be quite slow due to the overhead of process creation (fork/exec). Since init-container already imports golang.org/x/sys/unix, consider using the unix.Mount syscall directly for better performance, especially when dealing with a large number of device nodes.

@softwarefactory-project-zuul
Copy link
Copy Markdown

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.

Toolbox can't start if user has any group-only permissions for device nodes in folders

1 participant