Skip to content

Commit bec39d9

Browse files
committed
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>
1 parent 6461677 commit bec39d9

2 files changed

Lines changed: 27 additions & 7 deletions

File tree

crates/openshell-sandbox/src/main.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,9 @@ fn main() -> Result<()> {
222222

223223
let args = Args::parse();
224224

225+
#[cfg(target_os = "linux")]
226+
openshell_supervisor_process::process::prepare_supervisor_identity_mount_namespace_from_env()?;
227+
225228
// Try to open a rolling log file; fall back to stderr-only logging if it fails
226229
// (e.g., /var/log is not writable in custom workload images).
227230
// Rotates daily, keeps the 3 most recent files to bound disk usage.

crates/openshell-supervisor-process/src/process.rs

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -235,8 +235,17 @@ pub fn prepare_supervisor_identity_mount_namespace_from_env() -> Result<()> {
235235
let _ = SUPERVISOR_IDENTITY_MOUNT_NS.set(None);
236236
return Ok(());
237237
};
238-
let namespace = SupervisorIdentityMountNamespace::from_socket_path(&socket_path)?;
239-
let _ = SUPERVISOR_IDENTITY_MOUNT_NS.set(namespace);
238+
match SupervisorIdentityMountNamespace::from_socket_path(&socket_path) {
239+
Ok(namespace) => {
240+
let _ = SUPERVISOR_IDENTITY_MOUNT_NS.set(namespace);
241+
}
242+
Err(err) => {
243+
eprintln!(
244+
"WARN: supervisor identity mount namespace setup failed ({err}), continuing without isolation"
245+
);
246+
let _ = SUPERVISOR_IDENTITY_MOUNT_NS.set(None);
247+
}
248+
}
240249
Ok(())
241250
}
242251

@@ -336,16 +345,24 @@ fn create_supervisor_identity_mount_namespace(target: &Path) -> Result<OwnedFd>
336345
.map_err(|err| miette::miette!("failed to open sanitized mount namespace: {err}"))
337346
})();
338347

339-
set_mount_namespace(original_ns.as_raw_fd()).map_err(|restore_err| {
348+
if let Err(restore_err) = set_mount_namespace(original_ns.as_raw_fd()) {
349+
// Cannot restore the original mount namespace. Undo the tmpfs
350+
// overlay so the SPIFFE workload API socket stays reachable in the
351+
// namespace PID 1 is stuck in.
352+
#[allow(unsafe_code)]
353+
unsafe {
354+
libc::umount2(target.as_ptr(), libc::MNT_DETACH);
355+
}
340356
let result_msg = result.as_ref().err().map_or_else(
341357
|| "sanitized namespace was created".to_string(),
342358
ToString::to_string,
343359
);
344-
miette::miette!(
360+
return Err(miette::miette!(
345361
"failed to restore original mount namespace after supervisor identity isolation setup: \
346-
{restore_err}; setup result: {result_msg}"
347-
)
348-
})?;
362+
{restore_err}; undid tmpfs overlay so SPIFFE socket remains accessible; \
363+
setup result: {result_msg}"
364+
));
365+
}
349366

350367
result
351368
}

0 commit comments

Comments
 (0)