Recursive Read-Only Mounts With mount_setattr Need a Verified Boundary
A bind mount can expose a directory without exposing the whole filesystem. Security reviews sometimes treat the bind as a sandbox and then assume that marking the top-level mount read-only protects everything below it. A writable submount can remain writable unless the policy is applied recursively and the surrounding namespace is checked.
Linux mount_setattr() operates on a mount file descriptor. With AT_RECURSIVE, it can apply selected mount attributes to the mount and its descendants. MOUNT_ATTR_RDONLY is one available attribute. This is useful when a service needs a read-only view of a directory tree, including nested mounts created by the host.
struct mount_attr attr = {
.attr_set = MOUNT_ATTR_RDONLY | MOUNT_ATTR_NOSUID,
};
mount_setattr(mntfd, "", AT_EMPTY_PATH | AT_RECURSIVE,
&attr, sizeof(attr));
The descriptor should refer to the exact mount the program inspected. Opening a pathname again after validation can select a different mount after a rename or namespace change. The program should also decide how to handle propagation, user namespaces and id-mapped mounts. Those are separate from the read-only flag and can change which objects a process can reach.
A read-only mount is not a guarantee that no writable route exists. The same filesystem may be mounted writable elsewhere, a process may have a writable bind descriptor, or the mount may be changed later. A container runtime or service manager needs to review the complete mount table and remove unintended aliases. Read-only is a property of a mount instance, not an exclusive lock on the backing device.
NOSUID matters when files cross trust boundaries. A read-only but setuid-enabled mount can still let a crafted binary gain elevated identity when another control is wrong. NOEXEC can reduce exposure further where executable content is unnecessary. These flags should reflect the workload, not be copied as a universal bundle.
The operation requires privileges and should be performed during namespace setup, before untrusted code runs. After setup, the service should verify the resulting attributes rather than trusting the setup call alone. A status command should show the mount hierarchy, flags and propagation state. Monitoring should alert when a required read-only mount becomes writable.
Error handling needs to fail closed. If recursive attribute application returns an error for one descendant, starting the workload with a partially applied policy is unsafe. Tear down the namespace, report the failing mount and retry setup only after the host state is understood.
Use tests with nested bind mounts, a writable child, a symlink to an outside path and a concurrent remount attempt. The test should check behavior as well as the configuration string. A process should attempt a write, a device-node operation and an execution where the policy forbids them.
The boundary is meaningful when descriptor identity, recursive attributes, alias review and post-setup verification agree. Missing one of those pieces turns a read-only label into a claim the kernel was never asked to enforce.
Top comments (0)