From 3d98def3f70b688780dc91a1c8a1424e188b61ec Mon Sep 17 00:00:00 2001 From: Paolo Dettori Date: Mon, 8 Jun 2026 11:00:23 -0400 Subject: [PATCH] fix(sandbox): grant Landlock execute access on read-only paths MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On Landlock ABI v2+ (RHEL/OpenShift kernels), file execution requires explicit AccessFs::Execute permission — separate from read access. Without it, the entrypoint binary (e.g. `sleep infinity`) fails to exec inside the sandbox, leaving entrypoint_pid at 0 and causing the proxy to deny all CONNECT requests before OPA evaluation runs. - Add AccessFs::Execute to read-only Landlock rules so binaries in /usr/bin, /bin, etc. can be executed by the entrypoint and SSH sessions - Replace child.id().unwrap_or(0) with a proper error when exec fails, making the failure immediately visible instead of silently degrading Fixes: kagenti/kagenti#1855 Assisted-By: Claude (Anthropic AI) Signed-off-by: Paolo Dettori --- crates/openshell-sandbox/src/process.rs | 17 +++++++++++++++-- .../src/sandbox/linux/landlock.rs | 4 ++-- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/crates/openshell-sandbox/src/process.rs b/crates/openshell-sandbox/src/process.rs index 76786a84d..d004bb7d4 100644 --- a/crates/openshell-sandbox/src/process.rs +++ b/crates/openshell-sandbox/src/process.rs @@ -320,7 +320,14 @@ impl ProcessHandle { } let child = cmd.spawn().into_diagnostic()?; - let pid = child.id().unwrap_or(0); + let pid = child.id().ok_or_else(|| { + miette::miette!( + "Entrypoint process exited immediately after spawn (exec failed). \ + This typically means Landlock denied execute access to the entrypoint binary. \ + Check that the sandbox policy grants execute permission on the entrypoint path. \ + [program={program}]" + ) + })?; register_managed_child(pid); debug!(pid, program, "Process spawned"); @@ -416,7 +423,13 @@ impl ProcessHandle { } let child = cmd.spawn().into_diagnostic()?; - let pid = child.id().unwrap_or(0); + let pid = child.id().ok_or_else(|| { + miette::miette!( + "Entrypoint process exited immediately after spawn (exec failed). \ + This typically means the sandbox denied execute access to the entrypoint binary. \ + [program={program}]" + ) + })?; #[cfg(target_os = "linux")] register_managed_child(pid); diff --git a/crates/openshell-sandbox/src/sandbox/linux/landlock.rs b/crates/openshell-sandbox/src/sandbox/linux/landlock.rs index 6b121e0ca..e7f37ce4f 100644 --- a/crates/openshell-sandbox/src/sandbox/linux/landlock.rs +++ b/crates/openshell-sandbox/src/sandbox/linux/landlock.rs @@ -176,7 +176,7 @@ pub fn prepare(policy: &SandboxPolicy, workdir: Option<&str>) -> Result = (|| { let access_all = AccessFs::from_all(abi); - let access_read = AccessFs::from_read(abi); + let access_read = AccessFs::from_read(abi) | AccessFs::Execute; let mut ruleset = Ruleset::default(); ruleset = ruleset @@ -189,7 +189,7 @@ pub fn prepare(policy: &SandboxPolicy, workdir: Option<&str>) -> Result