Hyper.SuidHelper.ChrootJail (Hyper v0.1.0)

Copy Markdown View Source

Privileged chroot/jail lifecycle, via the setuid helper's chroot-jail subcommands (prepare / remove). These are built into the helper (no external binary), so there is no separate test_system/0 - Hyper.SuidHelper.test_system/0 already checks the helper itself is present.

Summary

Functions

Hand the firecracker API socket to the node user so the unprivileged controller can connect() to it. The jailer drops firecracker to a per-VM uid/gid and chroots it, so the socket it creates is owned by that per-VM id and the node (a different uid) gets EACCES on connect. The helper chowns just that one socket to its caller (the node user) and chmods it 0660, leaving the rest of the per-VM isolation intact.

Hand the firecracker vsock socket to the node user so the unprivileged controller can set up the guest AF_VSOCK connection. Firecracker creates the vsock Unix-domain socket inside the jail owned by the per-VM uid/gid; the node (a different uid) gets EACCES. The helper chowns just that one socket to its caller and chmods it 0660, leaving the rest of the per-VM isolation intact.

Prepare chroot_root's boot artifacts: stage the kernel file in and mknod a node mirroring the rootfs device, both owned uid:gid. The helper places them at the fixed in-jail names (/vmlinux, /rootfs) and reads the device's major:minor itself.

Remove a VM's stale jail before (re)launch: recursively delete the per-VM chroot dir and rmdir its (empty) cgroup leaf. Idempotent - a first boot with no prior state is a no-op. The helper confines chroot under the jail base and cgroup under /sys/fs/cgroup (see native/suidhelper).

Types

err()

@type err() :: Hyper.SuidHelper.err()

Functions

grant_api(socket)

@spec grant_api(Path.t()) :: :ok | {:error, :socket_pending} | {:error, err()}

Hand the firecracker API socket to the node user so the unprivileged controller can connect() to it. The jailer drops firecracker to a per-VM uid/gid and chroots it, so the socket it creates is owned by that per-VM id and the node (a different uid) gets EACCES on connect. The helper chowns just that one socket to its caller (the node user) and chmods it 0660, leaving the rest of the per-VM isolation intact.

Returns {:error, :socket_pending} while firecracker has not yet created the socket, so the caller can keep waiting.

grant_vsock(socket)

@spec grant_vsock(Path.t()) :: :ok | {:error, :socket_pending} | {:error, err()}

Hand the firecracker vsock socket to the node user so the unprivileged controller can set up the guest AF_VSOCK connection. Firecracker creates the vsock Unix-domain socket inside the jail owned by the per-VM uid/gid; the node (a different uid) gets EACCES. The helper chowns just that one socket to its caller and chmods it 0660, leaving the rest of the per-VM isolation intact.

Returns {:error, :socket_pending} while firecracker has not yet created the vsock socket, so the caller can keep waiting.

prepare(chroot_root, kernel, device, uid, gid)

@spec prepare(Path.t(), Path.t(), Path.t(), non_neg_integer(), non_neg_integer()) ::
  :ok | {:error, err()}

Prepare chroot_root's boot artifacts: stage the kernel file in and mknod a node mirroring the rootfs device, both owned uid:gid. The helper places them at the fixed in-jail names (/vmlinux, /rootfs) and reads the device's major:minor itself.

remove(chroot, cgroup)

@spec remove(Path.t(), Path.t()) :: :ok | {:error, err()}

Remove a VM's stale jail before (re)launch: recursively delete the per-VM chroot dir and rmdir its (empty) cgroup leaf. Idempotent - a first boot with no prior state is a no-op. The helper confines chroot under the jail base and cgroup under /sys/fs/cgroup (see native/suidhelper).