|
|
@@ -161,7 +161,7 @@ pub fn resolve_sandbox_status(config: &SandboxConfig, cwd: &Path) -> SandboxStat
|
|
|
#[must_use]
|
|
|
pub fn resolve_sandbox_status_for_request(request: &SandboxRequest, cwd: &Path) -> SandboxStatus {
|
|
|
let container = detect_container_environment();
|
|
|
- let namespace_supported = cfg!(target_os = "linux") && command_exists("unshare");
|
|
|
+ let namespace_supported = cfg!(target_os = "linux") && unshare_user_namespace_works();
|
|
|
let network_supported = namespace_supported;
|
|
|
let filesystem_active =
|
|
|
request.enabled && request.filesystem_mode != FilesystemIsolationMode::Off;
|
|
|
@@ -282,6 +282,27 @@ fn command_exists(command: &str) -> bool {
|
|
|
.is_some_and(|paths| env::split_paths(&paths).any(|path| path.join(command).exists()))
|
|
|
}
|
|
|
|
|
|
+/// Check whether `unshare --user` actually works on this system.
|
|
|
+/// On some CI environments (e.g. GitHub Actions), the binary exists but
|
|
|
+/// user namespaces are restricted, causing silent failures.
|
|
|
+fn unshare_user_namespace_works() -> bool {
|
|
|
+ use std::sync::OnceLock;
|
|
|
+ static RESULT: OnceLock<bool> = OnceLock::new();
|
|
|
+ *RESULT.get_or_init(|| {
|
|
|
+ if !command_exists("unshare") {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ std::process::Command::new("unshare")
|
|
|
+ .args(["--user", "--map-root-user", "true"])
|
|
|
+ .stdin(std::process::Stdio::null())
|
|
|
+ .stdout(std::process::Stdio::null())
|
|
|
+ .stderr(std::process::Stdio::null())
|
|
|
+ .status()
|
|
|
+ .map(|s| s.success())
|
|
|
+ .unwrap_or(false)
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
#[cfg(test)]
|
|
|
mod tests {
|
|
|
use super::{
|