fix(sandbox): undo tmpfs overlay when setns fails during SPIFFE mount namespace setup

On OpenShift and other container runtimes that do not grant
CAP_SYS_ADMIN, setns(CLONE_NEWNS) fails with EPERM after
create_supervisor_identity_mount_namespace has already mounted
an empty tmpfs over the SPIFFE workload API socket directory.
PID 1 remains stuck in the new mount namespace where the tmpfs
hides the socket, making it invisible to all processes.

Fix: when setns() fails, call umount2(MNT_DETACH) to remove
the tmpfs overlay before returning the error, so the SPIFFE
socket stays accessible even without namespace isolation.

Also wire up prepare_supervisor_identity_mount_namespace_from_env
in main.rs and make it gracefully degrade on error instead of
aborting the supervisor.

Signed-off-by: Adel Zaalouk <azaalouk@redhat.com>
This commit is contained in:
Adel Zaalouk
2026-07-08 15:27:55 +02:00
parent 6461677c32
commit bec39d9594
2 changed files with 27 additions and 7 deletions
+3
View File
@@ -222,6 +222,9 @@ fn main() -> Result<()> {
let args = Args::parse();
#[cfg(target_os = "linux")]
openshell_supervisor_process::process::prepare_supervisor_identity_mount_namespace_from_env()?;
// Try to open a rolling log file; fall back to stderr-only logging if it fails
// (e.g., /var/log is not writable in custom workload images).
// Rotates daily, keeps the 3 most recent files to bound disk usage.
@@ -235,8 +235,17 @@ pub fn prepare_supervisor_identity_mount_namespace_from_env() -> Result<()> {
let _ = SUPERVISOR_IDENTITY_MOUNT_NS.set(None);
return Ok(());
};
let namespace = SupervisorIdentityMountNamespace::from_socket_path(&socket_path)?;
let _ = SUPERVISOR_IDENTITY_MOUNT_NS.set(namespace);
match SupervisorIdentityMountNamespace::from_socket_path(&socket_path) {
Ok(namespace) => {
let _ = SUPERVISOR_IDENTITY_MOUNT_NS.set(namespace);
}
Err(err) => {
eprintln!(
"WARN: supervisor identity mount namespace setup failed ({err}), continuing without isolation"
);
let _ = SUPERVISOR_IDENTITY_MOUNT_NS.set(None);
}
}
Ok(())
}
@@ -336,16 +345,24 @@ fn create_supervisor_identity_mount_namespace(target: &Path) -> Result<OwnedFd>
.map_err(|err| miette::miette!("failed to open sanitized mount namespace: {err}"))
})();
set_mount_namespace(original_ns.as_raw_fd()).map_err(|restore_err| {
if let Err(restore_err) = set_mount_namespace(original_ns.as_raw_fd()) {
// Cannot restore the original mount namespace. Undo the tmpfs
// overlay so the SPIFFE workload API socket stays reachable in the
// namespace PID 1 is stuck in.
#[allow(unsafe_code)]
unsafe {
libc::umount2(target.as_ptr(), libc::MNT_DETACH);
}
let result_msg = result.as_ref().err().map_or_else(
|| "sanitized namespace was created".to_string(),
ToString::to_string,
);
miette::miette!(
return Err(miette::miette!(
"failed to restore original mount namespace after supervisor identity isolation setup: \
{restore_err}; setup result: {result_msg}"
)
})?;
{restore_err}; undid tmpfs overlay so SPIFFE socket remains accessible; \
setup result: {result_msg}"
));
}
result
}