Skip to content

Commit 3d98def

Browse files
committed
fix(sandbox): grant Landlock execute access on read-only paths
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: rossoctl/rossoctl#1855 Assisted-By: Claude (Anthropic AI) <noreply@anthropic.com> Signed-off-by: Paolo Dettori <dettori@us.ibm.com>
1 parent c00ea7b commit 3d98def

2 files changed

Lines changed: 17 additions & 4 deletions

File tree

crates/openshell-sandbox/src/process.rs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -320,7 +320,14 @@ impl ProcessHandle {
320320
}
321321

322322
let child = cmd.spawn().into_diagnostic()?;
323-
let pid = child.id().unwrap_or(0);
323+
let pid = child.id().ok_or_else(|| {
324+
miette::miette!(
325+
"Entrypoint process exited immediately after spawn (exec failed). \
326+
This typically means Landlock denied execute access to the entrypoint binary. \
327+
Check that the sandbox policy grants execute permission on the entrypoint path. \
328+
[program={program}]"
329+
)
330+
})?;
324331
register_managed_child(pid);
325332

326333
debug!(pid, program, "Process spawned");
@@ -416,7 +423,13 @@ impl ProcessHandle {
416423
}
417424

418425
let child = cmd.spawn().into_diagnostic()?;
419-
let pid = child.id().unwrap_or(0);
426+
let pid = child.id().ok_or_else(|| {
427+
miette::miette!(
428+
"Entrypoint process exited immediately after spawn (exec failed). \
429+
This typically means the sandbox denied execute access to the entrypoint binary. \
430+
[program={program}]"
431+
)
432+
})?;
420433
#[cfg(target_os = "linux")]
421434
register_managed_child(pid);
422435

crates/openshell-sandbox/src/sandbox/linux/landlock.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ pub fn prepare(policy: &SandboxPolicy, workdir: Option<&str>) -> Result<Option<P
176176

177177
let result: Result<PreparedRuleset> = (|| {
178178
let access_all = AccessFs::from_all(abi);
179-
let access_read = AccessFs::from_read(abi);
179+
let access_read = AccessFs::from_read(abi) | AccessFs::Execute;
180180

181181
let mut ruleset = Ruleset::default();
182182
ruleset = ruleset
@@ -189,7 +189,7 @@ pub fn prepare(policy: &SandboxPolicy, workdir: Option<&str>) -> Result<Option<P
189189

190190
for path in &read_only {
191191
if let Some(path_fd) = try_open_path(path, compatibility)? {
192-
debug!(path = %path.display(), "Landlock allow read-only");
192+
debug!(path = %path.display(), "Landlock allow read-only+execute");
193193
ruleset = ruleset
194194
.add_rule(PathBeneath::new(path_fd, access_read))
195195
.into_diagnostic()?;

0 commit comments

Comments
 (0)