From bec39d95949d56c2ef9096f0863603b4a772c19d Mon Sep 17 00:00:00 2001 From: Adel Zaalouk Date: Wed, 8 Jul 2026 15:27:55 +0200 Subject: [PATCH] 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 --- crates/openshell-sandbox/src/main.rs | 3 ++ .../src/process.rs | 31 ++++++++++++++----- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/crates/openshell-sandbox/src/main.rs b/crates/openshell-sandbox/src/main.rs index 91b145c2e0..be421ad20b 100644 --- a/crates/openshell-sandbox/src/main.rs +++ b/crates/openshell-sandbox/src/main.rs @@ -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. diff --git a/crates/openshell-supervisor-process/src/process.rs b/crates/openshell-supervisor-process/src/process.rs index fcd7ae69c1..2463e6dcf1 100644 --- a/crates/openshell-supervisor-process/src/process.rs +++ b/crates/openshell-supervisor-process/src/process.rs @@ -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 .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 }