feat: Add support for Landlock based Sandbox for Linux (#238)

* feat: Initial implementation of landlock based sandbox driver

* fix: Handle seccom probe failure

* fix: Remove unnecessary seccomp probe

* fix: Use file based policy load

* fix: Keep bpf filter in memory

* fix: Use TSYNC for seccom filter

* fix: Use TSYNC for seccom filter

* fix: Update landlock translator

* fix: Landlock sandbox implementation

* fix: Landlock + seccomp based sandboxing on Linux

* fix: Misc fixes

* fix: Cleanup sandbox files

* fix: Handle mandatory deny API change post merge

* fix: Landlock write access translation

* chore: Fix linter issues

* ci: Use /tmp for npm cache for landlock
This commit is contained in:
Abhisek Datta
2026-05-07 12:42:28 +05:30
committed by GitHub
parent 5122a1594c
commit 4c42ceca0e
27 changed files with 4356 additions and 122 deletions
+57 -3
View File
@@ -48,7 +48,8 @@ like `${HOME}/**` do not opt out of `${HOME}/.aws`. The unnamed absolute form st
## Requirements
- Bubblewrap on Linux
- Linux kernel 5.13+ with Landlock enabled (default, no external dependencies)
- Bubblewrap on Linux (fallback for kernels < 5.13, or when `PMG_SANDBOX_DRIVER=bubblewrap` is set)
- Seatbelt on MacOS
<details>
@@ -203,13 +204,66 @@ Next time you run `pmg pnpm install`, the custom policy template will be used in
| Platform | Supported | Implementation |
| -------- | --------- | ----------------------------------- |
| MacOS | Yes | Seatbelt sandbox-exec |
| Linux | Yes | Bubblewrap with namespace isolation |
| Linux | Yes | Landlock (default, kernel 5.13+) or Bubblewrap (fallback) |
| Windows | No | Not yet supported |
### Platform-Specific Limitations
<details>
<summary>Linux (Bubblewrap)</summary>
<summary>Linux (Landlock, default)</summary>
**Default sandbox on kernel 5.13+**: Landlock provides kernel-native filesystem access control
without requiring external binaries or unprivileged user namespaces.
For the architecture, design tradeoffs, and known limitations see
[sandbox-landlock.md](./sandbox-landlock.md).
**Deny enforcement**: Deny rules (DenyRead, DenyWrite, DenyExec) are enforced via seccomp
user notifications. This introduces a small TOCTOU window (microseconds) between reading
the path and responding.
**Deny enforcement across the process tree**: seccomp-notify resolves the path argument of
an intercepted `openat(2)` by reading `/proc/<pid>/mem` of the trapping process. PMG ships
this in a two-stage architecture so enforcement applies to direct targets AND every
descendant (grandchildren, great-grandchildren, etc.):
1. The helper process (`pmg __landlock_sandbox_exec`) clones a tiny shim
(`pmg __landlock_shim`) with `CLONE_NEWUSER` and a uid/gid map of `0 -> host uid`.
The shim runs as uid 0 inside a fresh user namespace so it has `CAP_SYS_ADMIN` in that
namespace.
2. The shim installs the seccomp-notify filter **without** `PR_SET_NO_NEW_PRIVS` (permitted
by `CAP_SYS_ADMIN` in the ns). It then applies Landlock and `execve`s the real target.
3. Because `NO_NEW_PRIVS` was never set, subsequent `execve` calls in the tree do **not**
reset `dumpable` to 0, so the helper can keep opening `/proc/<pid>/mem` for any
descendant. Deny rules like `~/.ssh` are enforced for the full process tree.
The user namespace is purely a capability vehicle. Host uid/gid are preserved through the
mapping, so targets see the same filesystem ownership they normally would. Tools that
refuse to run as root (npm's root-in-container warning) are unaffected because the
outside-view uid never changes.
**Requirements**: unprivileged user namespaces must be enabled (`unprivileged_userns_clone=1`
on Debian/Ubuntu; default on most modern distros). If disabled, the helper fails with an
EPERM on `clone()` and the sandbox falls back to Bubblewrap.
**Network filtering**: Not enforced. Landlock supports TCP port filtering only (V4+, no hostname).
Use `--proxy-mode` for network control.
**PID/IPC namespace isolation**: Applied best-effort via `CLONE_NEWPID|CLONE_NEWIPC|CLONE_NEWNS`.
If unavailable, a warning is printed and the command continues. Set `PMG_SANDBOX_DRIVER=bubblewrap`
to force Bubblewrap if namespace isolation is required.
**`/proc` access**: The sandbox supervisor requires `/proc` read access. When PID namespace
isolation succeeds, `/proc` is scoped to the child's namespace. When it fails, `/proc`
exposes all system processes.
**Fallback**: If Landlock is unavailable (kernel < 5.13), Bubblewrap is used automatically.
Set `PMG_SANDBOX_DRIVER=bubblewrap` to force Bubblewrap.
</details>
<details>
<summary>Linux (Bubblewrap, fallback)</summary>
**Filesystem permissions are coarse-grained**: [Bubblewrap](https://github.com/containers/bubblewrap) uses bind mounts for filesystem isolation.