fix(marmel): allow /nix/store in the Landlock sandbox
The upstream ruleset allow-lists FHS paths only (/usr, /bin, /lib, /opt, /etc, /var), and every executable here resolves below the store: /bin/sh is a symlink into /nix/store/<hash>-bash-5.3p9/bin/sh. Landlock matches the resolved target rather than the symlinked view, so the harness re-entry (`marmel --internal-sandbox-exec`) cannot exec the shell and every shell tool call comes back as "Failed to exec shell in sandbox: Permission denied (os error 13)" with exit 1. The sandbox is on by default, so the only workaround on a Nix host is MARMEL_DISABLE_SANDBOX=1, which drops the protection altogether. The patch adds "/nix/store" to the read-only+execute list, guarded by exists() like its neighbours. Store paths are world-readable by design, so the added reach is nothing a local user did not already have. Checked against the built binary: sandboxed exec works, git runs, workspace writes work, ~/.ssh and /root stay blocked. Tested: nix build .#marmel passes, lib suite 966 passed / 0 failed with one filter. test_internal_sandbox_cross_directory_rename no longer needs a skip and passes; test_internal_sandbox_exec_dev_null_and_dns stays skipped for its own reason, the no-network build sandbox has no /etc/resolv.conf. Temporary downstream fix, drop it once upstream takes the store path (nix-overlay-5ml).
This commit is contained in:
@@ -0,0 +1,29 @@
|
||||
--- a/src/harness/sandbox.rs
|
||||
+++ b/src/harness/sandbox.rs
|
||||
@@ -414,8 +414,25 @@
|
||||
}
|
||||
|
||||
// 5. System toolchains, device nodes, runtime files (DNS /run/systemd/resolve), and binaries (Read-Only + Execute)
|
||||
+ //
|
||||
+ // On NixOS and Guix /bin, /usr/bin and /lib are symlink farms: every executable
|
||||
+ // resolves below the package store, and Landlock matches the resolved target rather
|
||||
+ // than the symlinked view, so the store has to be listed here or nothing can be exec'd
|
||||
+ // at all. Store paths are world-readable by design, so read+execute there adds no
|
||||
+ // reach that a local user does not already have.
|
||||
let ro_paths = [
|
||||
- "/usr", "/bin", "/lib", "/lib64", "/opt", "/etc", "/dev", "/proc", "/sys", "/run", "/var",
|
||||
+ "/usr",
|
||||
+ "/bin",
|
||||
+ "/lib",
|
||||
+ "/lib64",
|
||||
+ "/opt",
|
||||
+ "/etc",
|
||||
+ "/dev",
|
||||
+ "/proc",
|
||||
+ "/sys",
|
||||
+ "/run",
|
||||
+ "/var",
|
||||
+ "/nix/store",
|
||||
];
|
||||
for p in ro_paths {
|
||||
if Path::new(p).exists()
|
||||
Reference in New Issue
Block a user