Repository navigation
Description
Problem
Tools that run untrusted workloads (e.g. coding agents) in libkrun VMs often share a user's working directories with the guest as writable virtio-fs shares. Some paths inside those shares are later consumed by the host outside the sandbox and must stay immutable from the guest. The motivating case is Git: .git/hooks/*, .git/config, .git/config.worktree and .git gitfiles are executed or interpreted by git on the host, so a guest that can rewrite them gets code execution on the host.
Protecting these paths inside the guest does not hold against guest root: bind mounts can be unmounted or shadowed, mount namespaces and containers inside the guest bring their own view, and guest LSMs leave gaps (usermode helpers, BPF, kernel bugs). The only enforcement point the guest cannot reach is the virtio-fs server in the VMM, but today it offers only read_only for a whole share (#623).
Proposal
An optional, per-share write policy enforced by the virtio-fs server. Guest requests are filtered; the host user is not affected.
| Rule | Effect for guest requests |
|---|---|
protect:<path> |
no write open, truncate, setattr, xattr changes, fallocate, unlink, rename, link |
seal:<path> |
protect for a directory and its whole subtree; no entries can be created inside |
pin:<path> |
directory cannot be removed, renamed or replaced |
reserve:<path> |
name that does not exist yet cannot be created |
Paths are absolute host paths under the shared directory and are resolved once when the device is created.
Matching
- Rules are keyed by (parent host
st_dev/st_ino, name), and the host IDs of matching children are learned onlookup. Atomic replacement on the host (git configwritesconfig.lockand renames it overconfig) gives the file a new inode; the next guest lookup classifies the new inode, so protection survives. - Sealed directories propagate to children on
lookup, so the subtree does not need to be walked upfront. - Checks use host identity, so hard links or other names for a protected inode are covered too.
Implementation sketch
-
devices/src/virtio/fs/protected.rs:ProtectedFs<F: FileSystem>wrapper next toPassthroughFsRo, reusingread_only_open_flags. It guardsopen,create,mkdir,mknod,symlink,link,unlink,rmdir,rename(incl.RENAME_EXCHANGE),setattr,setxattr,removexattr,fallocate,setupmappingwith write access, andioctl, and forwards everything else. Denied requests fail withEROFS(writes into sealed or reserved locations) orEPERM(protected or pinned entries). -
worker.rs: newFsServer::Protected(Server<AugmentFs<ProtectedFs<PassthroughFs>>>)variant. -
FsDeviceConfig.protect: Option<ProtectPolicy>. -
C API:
int32_t krun_add_virtiofs_protected(uint32_t ctx_id, const char *c_tag, const char *c_path, uint64_t shm_size, const char *const c_policy[]);
c_policyis a NULL-terminated array of"<rule>:<path>"strings.
PassthroughFs does not change, and the wrapper works the same with the Linux and macOS backends.
Non-goals
- General ACLs, glob or pattern rules. Pattern rules do not help the Git case:
git init/clonemust be able to create.git/configand hooks, and gitfiles,--separate-git-dirand bare repositories avoid any name pattern. - Changing the policy while the VM runs.
- Confining the VMM process itself (that is the job of a host sandbox such as Landlock).
Open questions
- Is a new
krun_add_virtiofs*entry point acceptable, or would you prefer a config struct / builder for share options? - Are all readdirplus entries guaranteed to go through
FileSystem::lookup, so classification on lookup is complete? - On macOS, a file the host replaces via
rename()reads asENOENTfor up toentry_timeout(virtio-fs (macOS): a file the host replaces viarename()reads as ENOENT in the guest for up toentry_timeout(5s) #888). Protection is unaffected, but should protected names use a shorter entry timeout? - Errno:
EROFSvsEPERMvsEACCESfor denied operations?
Context
Consumer: seznam/jailoc#274, where in-guest protection (read-only bind mounts plus no_new_privs) currently has to disable sudo and Docker in the guest. We are happy to send the PR (with unit tests for each guarded operation, Linux and macOS) if the direction is acceptable.
Activity