guest: pre-create nested mount points under read-only mounts - #2848
guest: pre-create nested mount points under read-only mounts#2848Jie Chen (jiechen0826) wants to merge 1 commit into
Conversation
|
Which type of parent mounts are targeted here? |
b9d2137 to
1445e2b
Compare
Good catch, thanks. This only targets read-only bind mounts whose source is a writable directory (the CRI volume mounts like emptyDir/configMap). For a read-only SCSI VHD the source is itself a read-only filesystem, so there's nothing to pre-create and runc would fail the same way regardless. I've added a guard that skips any parent whose source isn't writable, so read-only SCSI VHD and dm-verity parents are skipped up front. Does that cover your concern? |
1445e2b to
7d408a1
Compare
runc applies the OCI mounts in spec order and remounts a bind mount read-only as soon as it processes it. When it then processes a child mount whose destination is nested inside that read-only mount and the mount point does not already exist, runc must create it under the now read-only parent and fails with EROFS. This breaks valid configurations where a read-only volume has another volume mounted into a subdirectory of it, for example a read-only /etc/coredns configMap with a custom config volume at /etc/coredns/custom. On a regular Kubernetes node the kubelet creates these subdirectories on the host before handing the spec to the runtime. Inside an LCOW UVM the guest owns the mount setup, so do the equivalent: for each nested mount, create the mount point inside the writable source of its read-only parent before the spec is handed to runc, so it already exists once the parent is made read-only. This is best effort; if a mount point cannot be pre-created it is logged and skipped so runc's own error still surfaces. Resolve each mount point within the parent's source using filepath-securejoin, so a symlink planted in the writable, tenant-controlled source cannot redirect creation outside it. In a shared UVM the guest creates these paths as root across pods, so following such a symlink would otherwise let one pod create paths in another pod's or the UVM's filesystem. Add unit tests for the parent selection and path logic, including the read-only versus writable parent cases, the spec-order dependency, and the symlink clamp, plus root-gated functional tests that reproduce the EROFS failure and verify a child can be mounted under a read-only parent. Signed-off-by: Jie Chen <jiechen3@microsoft.com>
7d408a1 to
59a7d49
Compare
Problem
Containers fail to start with
read-only file system(EROFS) when a volume is mounted into a subdirectory of another volume that is mounted read-only. For example:emptyDirat/mnt/data(readOnly) plus another volume at/mnt/data/subdirconfigMapat/etc/corednsplus a custom config volume at/etc/coredns/customRoot cause
runc applies
spec.Mountsin order and remounts a bind mount read-only as soon as it processes it. When it then processes a child mount nested under it, runc must create the child's mount point under the now read-only parent, which fails with EROFS.On a normal Kubernetes node the kubelet pre-creates these subdirectories on the host before handing the spec to the runtime. Inside an LCOW UVM the guest (GCS) owns the mount setup, so runc creates the mount point at
runc createtime and hits the read-only parent.Reordering the mounts does not help: mounting the child before the parent makes the parent bind mount shadow the child.
Fix
ensureNestedMountTargets(newinternal/guest/runtime/hcsv2/mount.go), called fromHost.CreateContainerjust before the spec is written for runc. For each mount whose destination is nested under a read-only bind mount, it pre-creates the mount point inside that parent's writable source (a directory, or an empty file when the child source is a file), so it already exists once runc makes the parent read-only. This mirrors what the kubelet does on a host node.It is best effort: if a mount point cannot be pre-created it logs and continues, so runc's own error still surfaces and unaffected containers are unchanged.
Testing