You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat: sandbox --best-effort for container environments (#289)
Adds graceful degradation when user namespace creation is blocked
(k8s default seccomp, AppArmor). Landlock and seccomp containment
layers still apply. Network scanning uses proxy-based routing.
- --best-effort flag on sandbox and mcp proxy commands
- --env flag for passing env vars to sandboxed processes
- Pure Go netlink loopback (no ip binary dependency)
- io_uring returns EPERM instead of KILL (Node.js 22 compat)
- readlink syscall added to seccomp allowlist
- secretDirs checks existence (container compat)
- Dynamic bridge proxy port in best-effort mode
- Config: sandbox.best_effort with hot-reload + enterprise merge
- Docs: sandbox setup guide with deployment environments table
Copy file name to clipboardExpand all lines: CHANGELOG.md
+14Lines changed: 14 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -7,6 +7,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
8
8
## [Unreleased]
9
9
10
+
### Added
11
+
- Sandbox `--best-effort` flag: gracefully degrades when user namespace creation is blocked (e.g. k8s containers with default seccomp). Landlock and seccomp containment layers still apply. Network scanning uses proxy-based routing instead of kernel-enforced namespace isolation.
12
+
- Sandbox `--env` flag: pass environment variables to sandboxed processes (KEY or KEY=VALUE, repeatable). Validates against dangerous keys (LD_PRELOAD, NODE_OPTIONS, etc.) that could subvert containment.
13
+
- MCP proxy `--sandbox-best-effort` flag: parity with `pipelock sandbox --best-effort` for MCP stdio wrapping mode.
14
+
- Pure Go netlink loopback: sandbox uses raw netlink syscalls to bring up loopback inside network namespaces. No `ip` binary required. Works in minimal container images without iproute2.
15
+
16
+
### Fixed
17
+
- Seccomp io_uring handling: changed from KILL_PROCESS to EPERM so runtimes like Node.js 22 that probe io_uring at startup can gracefully fall back to epoll instead of crashing.
18
+
- Seccomp `readlink` syscall: added `SYS_READLINK` (nr 89) to the allowlist. Node.js/libuv uses the legacy readlink syscall directly, not readlinkat.
19
+
- Sandbox secret dir validation: `secretDirs()` now only protects directories that actually exist. Prevents false validation errors in containers.
20
+
- Sandbox bridge proxy dynamic port: in best-effort mode, uses dynamic port instead of hardcoded 8888.
21
+
- Config reload detection: `sandbox.best_effort` changes detected during hot reload. Per-agent `best_effort` propagated through enterprise merge.
22
+
- Config validation: `sandbox.best_effort` and `sandbox.strict` mutually exclusive.
@@ -233,12 +232,13 @@ DLP runs before DNS resolution, designed to catch secrets before any DNS query l
233
232
234
233
See [docs/bypass-resistance.md](docs/bypass-resistance.md) for the full evasion test matrix.
235
234
236
-
### Process Sandbox (Linux)
235
+
### Process Sandbox
237
236
238
-
Unprivileged process containment using Landlock LSM, network namespaces, and seccomp. No root, no Docker, no containers. The agent runs in an isolated environment with controlled filesystem access, no direct network egress, and a filtered syscall set. Works with any command — MCP servers, standalone agents, or arbitrary processes.
237
+
Unprivileged process containment using OS-native kernel primitives. On Linux: Landlock LSM restricts filesystem access, seccomp filters dangerous syscalls, and network namespaces force all traffic through pipelock's scanner (no direct egress). On macOS: sandbox-exec profiles restrict filesystem and network. In containers, use `--best-effort` for Landlock + seccomp containment when namespace creation is restricted (network scanning uses proxy-based routing instead of kernel enforcement).
Copy file name to clipboardExpand all lines: docs/configuration.md
+27-5Lines changed: 27 additions & 5 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1405,32 +1405,37 @@ rules:
1405
1405
1406
1406
## Sandbox
1407
1407
1408
-
Process containment for agent commands using Linux kernel primitives. The agent runs in a restricted namespace with controlled filesystem access, no direct network, and a filtered syscall set.
1408
+
Process containment for agent commands using Linux kernel primitives. The agent runs in a restricted environment with controlled filesystem access, no direct network, and a filtered syscall set.
1409
1409
1410
1410
```yaml
1411
1411
sandbox:
1412
1412
enabled: true
1413
+
best_effort: false # degrade gracefully when namespace isolation unavailable
1414
+
strict: false # error if any layer unavailable (mutually exclusive with best_effort)
1413
1415
workspace: /home/user/project # agent working directory (default: CWD)
1414
1416
filesystem: # optional Landlock overrides (default policy works for most agents)
If `filesystem` is omitted, the default Landlock policy is used (safe for Python/Node/Go agents without config). Read access grants execute (Landlock bundling). Write paths are also executable.
1429
1434
1430
1435
**Containment layers:**
1431
-
- **Landlock LSM:** Restricts filesystem access to declared paths. Read-only and read-write grants are explicit. Allowlist model.
1432
-
- **Network namespaces:** Agent runs in an isolated network namespace. For MCP (stdio), no network is needed. For standalone agents, traffic routes through pipelock's bridge proxy.
- **Landlock LSM:** Restricts filesystem access to declared paths. Allowlist model. Protected directories (`~/.ssh`, `~/.aws`, `~/.kube`, etc.) are denied. Only dirs that exist on the system are checked.
1437
+
- **Network namespaces:** Agent runs in an isolated network namespace. All traffic is kernel-forced through pipelock's bridge proxy. Raw socket bypass is impossible. For MCP (stdio), no network is needed.
1438
+
- **Seccomp BPF:** Syscall allowlist (~130 safe syscalls for Go/Python/Node.js). Blocks ptrace, mount, module loading, kexec (KILL). io_uring returns EPERM (allows runtimes like Node.js 22 to fall back to epoll). Clone flags filtered to prevent namespace escape.
**Requirements:** Linux 5.13+ (Landlock ABI v1). Unprivileged — no root, no Docker, no special capabilities. Not available on macOS or Windows (hard error with explanation).
1458
+
**Environments:**
1459
+
1460
+
| Environment | Layers | Notes |
1461
+
|-------------|--------|-------|
1462
+
| Bare metal / VM (Linux) | 3/3 | Full containment: Landlock + seccomp + network namespace |
| macOS | sandbox-exec | Apple SBPL profiles for filesystem + network restriction |
1465
+
1466
+
**Requirements:** Linux 5.13+ (Landlock ABI v1). Unprivileged on bare metal. macOS 13+ for sandbox-exec. Containers may need `--best-effort` if default seccomp blocks `CLONE_NEWUSER`.
0 commit comments