Problem
prepare_filesystem() in crates/openshell-sandbox/src/lib.rs chowns every path in the policy's read_write list to run_as_user:run_as_group before spawning the child process. This happens unconditionally — even for directories that already exist in the container image with intentional ownership.
This means an image author who sets root:root on a read_write directory (e.g., to protect immutable subdirectories via DAC while allowing writes to others) has that ownership silently overridden at sandbox start.
Example (NemoClaw)
Our Dockerfile sets /sandbox/.nemoclaw to root:root 755 to prevent the agent from renaming the root-owned blueprints/ subdirectory. The policy lists it as read_write because sandbox-owned subdirs (state/, migration/) inside it need to be writable.
At sandbox start, prepare_filesystem() chowns /sandbox/.nemoclaw to sandbox:sandbox, removing the DAC protection. QA confirmed the ownership flip at runtime in NVIDIA/NemoClaw#1607 / nvbug 6059437.
We've worked around this with the sticky bit (chmod 1755) in NVIDIA/NemoClaw#1121, which prevents the sandbox user from renaming or deleting root-owned entries even after the ownership flip. But the root cause is the unconditional chown.
Context
This came up during security hardening review for NemoClaw PR #1121 (read-only sandbox filesystem). The NemoClaw team discussed this with our tech lead and agreed the sticky bit is a sufficient short-term workaround, but that prepare_filesystem() respecting existing ownership would be the cleaner long-term fix — both for us and for any other OpenShell image author who intentionally hardens read_write paths.
Proposed Change
Only chown directories that prepare_filesystem() creates. If a directory already exists in the image, respect the image author's ownership:
for path in &policy.filesystem.read_write {
if let Ok(meta) = std::fs::symlink_metadata(path) {
if meta.file_type().is_symlink() {
return Err(...); // existing symlink check
}
// Directory exists — image author set ownership intentionally, leave it
} else {
// Directory doesn't exist — create and chown
std::fs::create_dir_all(path).into_diagnostic()?;
chown(path, uid, gid).into_diagnostic()?;
}
}
This is backwards-compatible — directories that don't exist in the image still get created and chowned. Only pre-existing directories are left alone.
Problem
prepare_filesystem()incrates/openshell-sandbox/src/lib.rschowns every path in the policy'sread_writelist torun_as_user:run_as_groupbefore spawning the child process. This happens unconditionally — even for directories that already exist in the container image with intentional ownership.This means an image author who sets
root:rooton aread_writedirectory (e.g., to protect immutable subdirectories via DAC while allowing writes to others) has that ownership silently overridden at sandbox start.Example (NemoClaw)
Our Dockerfile sets
/sandbox/.nemoclawtoroot:root 755to prevent the agent from renaming the root-ownedblueprints/subdirectory. The policy lists it asread_writebecause sandbox-owned subdirs (state/,migration/) inside it need to be writable.At sandbox start,
prepare_filesystem()chowns/sandbox/.nemoclawtosandbox:sandbox, removing the DAC protection. QA confirmed the ownership flip at runtime in NVIDIA/NemoClaw#1607 / nvbug 6059437.We've worked around this with the sticky bit (
chmod 1755) in NVIDIA/NemoClaw#1121, which prevents the sandbox user from renaming or deleting root-owned entries even after the ownership flip. But the root cause is the unconditional chown.Context
This came up during security hardening review for NemoClaw PR #1121 (read-only sandbox filesystem). The NemoClaw team discussed this with our tech lead and agreed the sticky bit is a sufficient short-term workaround, but that
prepare_filesystem()respecting existing ownership would be the cleaner long-term fix — both for us and for any other OpenShell image author who intentionally hardensread_writepaths.Proposed Change
Only chown directories that
prepare_filesystem()creates. If a directory already exists in the image, respect the image author's ownership:This is backwards-compatible — directories that don't exist in the image still get created and chowned. Only pre-existing directories are left alone.