# Linux 沙箱技术全景:从 chroot 到 application kernel
> 调研时间:2026-05
> 范围:bubblewrap、firejail、nsjail、minijail、gVisor、Landlock、boxsh、container-use、Apple Container 等
> 视角:内核机制 → 工程实现 → 信任模型 → 选型决策
---
## 0. 一句话定调
把"沙箱"从一个口号拆开来看,本质是**给一个进程套一层"它能看到/能动用什么"的限制壳**。Linux 上这层壳过去 30 年走了三代:
1. **第一代(陷阱型)**:chroot、setuid——把进程关进文件系统某个角落。轻易被绕过。
2. **第二代(机制型)**:namespaces + seccomp + cgroups + capabilities——内核给进程做"分身",每个分身看到的内核资源是隔离的子集。bubblewrap、firejail、nsjail、minijail 都属于这一代,差别只在**编排策略**。
3. **第三代(拦截型)**:gVisor 直接在用户态写一个 Linux ABI 兼容的内核,把宿主内核当成 hypervisor 用。安全边界从"内核 syscall 表面"挪到"sentry 进程边界"。
第二代占据 95% 的实际部署。第三代只在"运行真正不可信代码"的场景下值得开销。**boxsh 是第二代的最新一代封装**——把 namespace 编排 + COW overlay + JSON-RPC 接口打成一个二进制,专门服务 AI agent 这种"既要能跑命令又不能搞坏 host"的新场景。
---
## 1. 内核机制层:先把工具箱摸清
所有用户态沙箱都是这五样积木的组合:
### 1.1 Namespaces(命名空间)
Linux 把内核全局资源(进程表、挂载点、网络栈、UID/GID 映射、IPC、UTS、时钟、cgroup)切成"隔离域"。`unshare(2)` / `clone(2) | CLONE_NEW*` 创建新的隔离域,进程进去之后看到的就是空的或克隆的资源。
- `CLONE_NEWUSER`:用户命名空间。最关键——非 root 进程进入新 user ns 后**在该 ns 内拥有 root 权限**,但映射到外面还是普通 UID。这是"无特权沙箱"的根基。
- `CLONE_NEWNS`:挂载命名空间。进去之后随便挂卸载不影响 host。
- `CLONE_NEWPID`:进程命名空间。`ps` 里只看到自己 ns 内的进程,PID 1 是自己 ns 的"init"。
- `CLONE_NEWNET`:网络命名空间。新的 lo、新的路由表,默认隔绝外网。
- `CLONE_NEWUTS`、`CLONE_NEWIPC`、`CLONE_NEWCGROUP`、`CLONE_NEWTIME`:相对次要。
> User namespace 是 Linux 3.8(2013)才稳定下来的,之前的沙箱只能靠 setuid 帮助程序。bubblewrap 的诞生正好踩着这个时间点。
### 1.2 seccomp-bpf
进程级 syscall 过滤器。用 BPF 字节码定义"哪些 syscall 允许 / 拒绝 / 返回错误码 / 杀掉进程 / 通知 supervisor"。
- 主流是 `SECCOMP_SET_MODE_FILTER`,单线程注入一次永久生效(`PR_SET_NO_NEW_PRIVS` 配合)
- Google 的 [Kafel](https://github.com/google/kafel) 是 seccomp policy 的高级 DSL,nsjail 用它写规则
- libseccomp 是 C 抽象层,firejail/minijail 都用
### 1.3 cgroups (v2)
不是隔离机制,是**资源配额机制**。CPU 时间、内存上限、PID 数量、IO 带宽——给沙箱进程一个"配额房间"。nsjail、firejail 都集成了 cgroup v1/v2。
### 1.4 Capabilities
把 root 权限拆成 40+ 个 bit(CAP_NET_ADMIN、CAP_SYS_ADMIN、CAP_NET_RAW...),可以单独 drop。沙箱启动时通常 `bounding set` 全清,再按需 add。
### 1.5 Landlock(LSM 路径访问控制)
Linux 5.13(2021)合入的内核 LSM。**进程自愿声明"我之后只能读/写这些路径"**,类似进程版的 AppArmor。和 namespaces 互补:namespaces 改变"可见性",Landlock 限制"可操作性"。go-landlock、rust landlock crate 都有绑定。
### 1.6 LSM(AppArmor / SELinux)
系统级强制访问控制。**进程不能自己 opt-out**,只能管理员配置。firejail 可以叠加 AppArmor/SELinux profile,bubblewrap 不动它(专注 user-namespace 路径)。
---
## 2. 项目横向对比表
| 项目 | 主仓 | Star | 语言 | 起家年份 | 设计目标 | SUID 依赖 | seccomp | cgroup | overlayfs | DSL/profile |
|---|---|---|---|---|---|---|---|---|---|---|
| **bubblewrap** | containers/bubblewrap | 7.3k | C | 2016 | Flatpak 的最小 user-ns 沙箱 | ❌ 历史上有,已移除 | 接受外部 BPF | ❌ | ✅(按需) | 无(命令行 flag 拼接)|
| **firejail** | netblue30/firejail | 7.4k | C | 2015 | 桌面应用一键沙箱 | ✅ SUID | ✅ libseccomp | ✅ | ✅ | profile 文件(数百预置)|
| **nsjail** | google/nsjail | 3.9k | C++ | 2015 | CTF / fuzz / RPC 服务隔离 | ❌ | ✅ Kafel DSL | ✅ | ❌(chroot/pivot_root)| protobuf config |
| **minijail** | google/minijail | 369(镜像)| C | 2011(ChromeOS)| ChromeOS / Android 系统服务沙箱 | 可选 | ✅ libminijail | ✅ | ❌ | 命令行 + policy 文件 |
| **gVisor** | google/gvisor | 18.4k | Go | 2018 | OCI runtime,跑不可信容器 | ❌ | 内部用 seccomp 锁死 sentry | OCI cgroup | ✅ 9P/gofer | OCI runtime spec |
| **Landlock** | torvalds/linux | 内核 | C | 2021 (5.13) | 进程自愿沙箱(无特权)| ❌ | 互补 | ❌ | ❌ | ruleset API |
| **boxsh** | xicilion/boxsh | 305 | JS+C | 2026 | AI agent / MCP server 沙箱 | ❌ | macOS Seatbelt + Linux ns/seccomp | ❌ | ✅ COW 内置 | 命令行 + JSON-RPC |
| **container-use** | dagger/container-use | 新 | Go | 2025 | dagger 容器化 dev env for agents | ❌(用 Docker)| 容器自带 | 容器自带 | 容器自带 | dagger config |
| **Apple container** | apple/container | 新 | Swift | 2025 | macOS 原生 OCI runtime(Virtualization.framework)| 不适用 | macOS 沙箱 | 不适用 | ✅ | OCI |
> 注:minijail 在 GitHub 上的 369 star 是镜像仓库,主仓在 chromium.googlesource.com,实际部署量是 ChromeOS + Android 的全部出货设备——量级远超表面数字。
---
## 3. 项目深度剖析
### 3.1 bubblewrap (`bwrap`)
**身份证**:Red Hat / GNOME 系出品,Flatpak 的执行内核。代码继承自 xdg-app-helper → linux-user-chroot 一脉。
**核心立场**:**只做机制,不定策略**。README 第一段就划清:
> bubblewrap is a tool for constructing sandbox environments. bubblewrap is **not** a complete, ready-made sandbox with a specific security policy.
调用者必须自己拼命令行,bwrap 只负责按 flag 拉 namespace、做 mount、drop privilege、exec。
**典型调用**:
```sh
bwrap \
--ro-bind /usr /usr \
--ro-bind /lib /lib --ro-bind /lib64 /lib64 \
--proc /proc --dev /dev \
--tmpfs /tmp \
--unshare-all --share-net \
--die-with-parent \
--new-session \
/usr/bin/bash
```
**关键设计**:
- 完全无 SUID(user namespace 路径)
- `PR_SET_NO_NEW_PRIVS` 关 setuid 突破
- `--die-with-parent`:父进程死了沙箱跟着死,避免僵尸
- `--bind` / `--ro-bind` / `--dev-bind` / `--tmpfs` / `--proc`:构建任意 mount 视图
**被谁用**:Flatpak、Toolbox、distrobox、各类 AI agent runner。**生态最广,最稳定**。
**适合**:当你要在更上层框架(自己的 agent runtime / 包管理器 / IDE)里做精确控制,不想要预置策略干扰。
### 3.2 firejail
**身份证**:纯桌面玩家友好向的沙箱前端。SUID 二进制,自带 ~1000 个预置 profile(firefox、chromium、libreoffice、thunderbird、vlc 等)。
**核心立场**:**给桌面应用一键穿沙箱,profile 即配置**。`firejail firefox` 直接读 `/etc/firejail/firefox.profile` 拉起。
**关键设计**:
- SUID 路径(Linux capabilities + setuid)+ 可选 user-ns 路径
- profile 是声明式 DSL:`noblacklist`、`whitelist`、`seccomp.drop`、`netfilter`、`x11 none`、`apparmor`...
- 集成 AppArmor / SELinux / cgroups
- `--net=none`、`--private`、`--private-tmp`、`--protocol=unix,inet,inet6`
**SUID 的代价**:firejail 历史上 CVE 不少,因为 SUID 二进制是攻击面。新版本主推 user-ns 路径,但 SUID 路径仍保留兼容。
**适合**:非容器化的桌面应用一键加固。**不适合**作为不可信代码执行容器。
### 3.3 nsjail
**身份证**:Google 安全团队出品,专为 **CTF infrastructure / 模糊测试 / RPC 服务隔离** 设计。
**核心立场**:**精确控制 + 高吞吐**。四种执行模式:
- `-Mo` ONCE:跑一次退出(典型一次性 RPC)
- `-Ml` LISTEN:TCP 监听,每个连接 fork 一个隔离实例(CTF judging server)
- `-Mr` RERUN:反复执行(fuzzing target wrapper)
- `-Me` EXECVE:直接 execve,无 supervisor
**关键设计**:
- protobuf 配置文件(声明式)
- Kafel DSL 写 seccomp 策略——比手写 BPF 易读 100 倍
- 内置 IPv4/v6 MACVLAN 克隆、pasta userland 网络
- cgroup v1/v2 双支持
- `pivot_root` 而不是 `chroot`(更难逃逸)
**Kafel 例子**:
```
POLICY safe_compute {
ALLOW { read, write, exit, exit_group, brk, mmap, mprotect }
KILL { ptrace, mount, reboot, kexec_load }
}
USE safe_compute DEFAULT KILL
```
**适合**:你写了一个会跑用户提交代码的服务(CTF、judge、playground、AI exec sandbox),nsjail 是接近最佳选择。
### 3.4 minijail
**身份证**:ChromeOS 与 Android 的内置沙箱基础设施。源码主仓在 chromium.googlesource.com,GitHub 是镜像。
**核心立场**:**给已知良性二进制套壳**。README 自己写得明白:
> Minijail is **not designed for safely running malicious code**, including attacker-controlled binaries.
它的威胁模型是"减小 confused deputy / 单点 bug 的爆炸半径"——不是"对抗主动入侵"。
**关键设计**:
- 既是 CLI 工具又是 C 库(`libminijail`),可以让程序自己 sandbox 自己
- 简单 policy 文件:`@frequency`、`allow @list`、`return errno`
- 集成 ChromeOS 的 minijail0 wrapper、systemd unit 模板
**适合**:你在写一个长期运行的 daemon(sshd、网络服务),想给它套壳但不想引入 firejail 这种重型框架。**Linux 系统编程经典案例库**。
### 3.5 gVisor
**身份证**:Google 出品的"容器用应用内核"。OCI runtime `runsc` 直接和 Docker / Kubernetes 接。
**核心立场**:**别再用 seccomp 修修补补了,重写一个 Linux**。README 直白:
> gVisor is **not a syscall filter**, nor a wrapper over Linux isolation primitives.
工作方式:
1. 应用进程发起 syscall
2. 一个叫 **Sentry** 的用户态 Go 进程拦截(用 ptrace 或 KVM)
3. Sentry 实现了大部分 Linux ABI(fs/net/mm/signal...)
4. 真正需要触底的 syscall 走一个**极小白名单**走到宿主内核
宿主内核暴露面从"全部 ~400 syscall"压到"几十个被 sentry 调用的"。即使应用拿到了任意代码执行,也只能攻击 sentry,攻不到宿主。
**代价**:
- IO 重的 workload 慢 30-200%(每个 syscall 走两次切换)
- 网络栈是用户态重写的(gonet),有些边缘 case 行为和 Linux 不一致
- 启动比 runc 慢 3-5 倍
**适合**:跑真正不可信的 workload(用户提交的容器、function-as-a-service、CI runner)。**当你需要"逃逸 = 业务事故"级别的隔离**时上这个。
### 3.6 Landlock
**身份证**:内核 LSM,2021 年合入 5.13。Mickaël Salaün 主导。
**核心立场**:**进程自我约束的访问控制**。和 namespaces / seccomp 完全互补——后者控"看到什么",Landlock 控"能动哪条路径"。
**API 形态**:
```c
struct landlock_ruleset_attr ruleset_attr = {
.handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE
| LANDLOCK_ACCESS_FS_WRITE_FILE
| LANDLOCK_ACCESS_FS_REMOVE_FILE,
};
int ruleset_fd = landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
struct landlock_path_beneath_attr beneath = {
.allowed_access = LANDLOCK_ACCESS_FS_READ_FILE,
.parent_fd = open("/usr", O_PATH | O_CLOEXEC),
};
landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH, &beneath, 0);
prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
landlock_restrict_self(ruleset_fd, 0);
```
**关键属性**:
- 无 root 要求
- 不可逆——一旦 enforce 当前进程及子进程永远受限
- 只管 path,不管网络(5.19 加了 TCP socket 限制,仍弱)
**适合**:库作者给自己的程序加自我保护层。`go-landlock`、`rust-landlock` 都很活跃。绝不是"完整沙箱",但作为一个内置 layer 加上去几乎零成本。
### 3.7 boxsh —— AI agent 时代的新代际
**身份证**:xicilion 出品(同人写过 fibjs),2026 年 4 月开源。**305 star,但定位独特,值得单独剖析**。
**核心立场**:**给 AI agent 一个"能任意跑命令但不会搞坏 host"的 shell**。把这件事产品化成一个二进制,自带 MCP server 接口。
**架构三层**:
1. **Shell 层**:内嵌 dash 0.5.12,自己就是 `/bin/sh` 替身。任何 POSIX 脚本能跑。
2. **沙箱层**:Linux 走 user/mount/PID/network namespaces + seccomp;macOS 走 Seatbelt(sandbox_init + SBPL profile)。**不调外部工具**,namespace/seccomp 都直接 syscall 进。
3. **MCP server 层**:JSON-RPC 2.0,9 个工具(bash / read / write / edit / run_in_terminal / send_to_terminal / get_terminal_output / kill_terminal / list_terminals)。VS Code、Claude Desktop、Cursor 都能直接连。
**最值得说的设计——COW workspace**:
```sh
boxsh --sandbox \
--bind cow:/path/to/project:/path/to/dst \
--new-net-ns
```
`cow:SRC:DST` 在 `DST` 上挂一个 overlayfs,`SRC` 是只读底层。agent 进去之后随便改文件,所有写操作都落到 `DST`,原项目根本碰不到。退出后想要的话从 `DST` 同步回去,不要的话整个 `DST` 删掉就完事——**事务式编辑**。
更进一步的 `--try` 模式:
```
$ cd my-project
$ boxsh --try
boxsh: changes will be saved in /tmp/boxsh-try-abc123/work
# 这里给你一个 root shell,写啥都安全
$ rm important-file.txt
$ exit
$ ls important-file.txt # 还在
$ ls /tmp/boxsh-try-abc123/work/
.wh.important-file.txt # whiteout 在这里
```
**预 fork worker pool**:MCP 模式下 `--workers N` 预先 fork N 个 worker,每个 worker 已经穿好沙箱。请求来了直接派发。worker 崩了(OOM、超时、segfault)coordinator 检测 `POLLHUP` 立刻补一个新的。
**包装第三方 MCP server**:boxsh 可以**透明套在任何 MCP server 外面**:
```json
{
"command": "boxsh",
"args": [
"--sandbox",
"--bind", "ro:/path/to/project",
"--new-net-ns",
"--", "npx", "-y", "@anthropic/some-mcp-server"
]
}
```
stdio 透传,但目标进程被关进 namespace。这是 MCP 生态里少见的"用 wrapper 给社区 server 兜底"思路。
**和 nsjail 比**:nsjail 也能做大部分事,但 boxsh 把 MCP / COW workspace / 跨 OS(Linux + macOS)打包好了,更面向 "AI agent host environment" 这个具体场景。
**和 firejail 比**:firejail 是 SUID + profile 桌面侧;boxsh 是 user-ns + 命令行 + RPC,agent 侧。
**和 gVisor 比**:boxsh 仍是第二代(namespaces);gVisor 是第三代(重写内核)。boxsh 假定底层 Linux 内核可信,目标是"防止 agent 误操作";gVisor 假定容器内一切不可信,目标是"防止恶意逃逸"。
### 3.8 container-use(Dagger)
dagger.io 的 [container-use](https://github.com/dagger/container-use) 走另一条路:**直接给每个 agent 一个 ephemeral container**。本质是用 Docker / containerd 做隔离层,配套 dagger 的 typed pipeline DSL。比 boxsh 重很多(要 Docker daemon),但隔离边界更硬(容器 + dagger 的 file-system 抽象)。
适合:你已经在用 dagger / 已经有 Docker;想要 "agent A 跑 stack X,agent B 跑 stack Y" 的并行环境。
### 3.9 Apple Container
[apple/container](https://github.com/apple/container) 2025 年才开源。**每个容器一个微 VM**(基于 macOS Virtualization.framework),不是 Linux 沙箱思路而是 hypervisor 思路。在 Apple Silicon 上启动只要几十毫秒。
适合:mac 开发机上想跑 Linux container 又不想要 Docker Desktop 重型方案。和 Linux 沙箱不在一个赛道但同问题域。
---
## 4. 选型决策树
```
要在哪运行不被信任的代码?
├── 桌面应用一键加固(firefox / vscode)
│ └── firejail(用预置 profile)
│
├── 系统 daemon 自我加固(sshd / web-server / db)
│ ├── 自己代码可改 → minijail(C 库 / libminijail)
│ ├── 已经在 Linux daemon → systemd 自带 sandboxing 选项
│ └── 还要文件路径白名单 → 叠加 Landlock
│
├── 跑用户提交代码(CTF / judge / playground)
│ ├── 单条命令 / 一次性 → nsjail -Mo + Kafel policy
│ └── 高并发 RPC 服务 → nsjail -Ml + protobuf config
│
├── OCI 容器跑不可信镜像(多租户云)
│ └── gVisor (runsc) — 接受 30% 性能损失换硬隔离
│
├── Flatpak / 应用打包系统
│ └── bubblewrap(Flatpak 已经在用)
│
├── AI agent 跑命令编辑代码
│ ├── 想要 COW workspace + MCP 协议 → boxsh
│ ├── 已有 Docker / dagger → container-use
│ └── mac 上想要 micro-VM → Apple container
│
└── 库作者给自己代码加 layer
└── Landlock(自愿声明路径白名单)
```
---
## 5. 信任模型对照
哪些你信、哪些你防——决定了选哪个工具。
| 你信任 | 你防的对象 | 推荐工具 |
|---|---|---|
| host kernel + 自己的代码 | 自己代码的 bug 被利用 | minijail / Landlock |
| host kernel + 已审过的二进制 | 二进制有未知 CVE | firejail / bubblewrap |
| host kernel | LLM agent 误操作搞坏文件 | boxsh / bubblewrap + COW |
| host kernel | 用户提交的代码主动作恶 | nsjail(外加 Landlock)|
| 啥都不信 | 容器内 0day 逃逸 | gVisor |
---
## 6. 共性踩坑
无论用哪个第二代工具,绕不开这几个坑:
1. **`/proc` 必须 fresh mount**,否则沙箱内能读到 host 的 `/proc/sys/kernel/*`,相当于裸奔。bwrap `--proc /proc`,nsjail `--proc_rw 0`。
2. **`/dev` 不能直接 bind**。要么 `--dev-bind`(bwrap,极简)要么手动建 tmpfs + mknod。
3. **TIOCSTI 隧道**:sandboxed 进程能往父 TTY 写指令。`bwrap --new-session` / `setsid` 必须有。
4. **uid_map / gid_map race**:early days 的 user namespace 实现有 setgid 绕过 bug。`PR_SET_NO_NEW_PRIVS` 是必须的。
5. **abstract Unix socket 泄漏**:`@/tmp/something` 这种 abstract namespace socket 在 net ns 里**仍是隔离的**,但同 host net ns 的兄弟沙箱之间能通。要 `--unshare-net`。
6. **Mount propagation**:默认 mount propagation 是 shared,沙箱里挂载会传到 host。必须 `mount --make-rslave /` 或类似。
7. **systemd cgroup v2 unified**:以前 v1 hybrid mode 沙箱可以自己接管 cgroup 控制;现在 unified mode 必须走 systemd-run delegate。
---
## 7. 趋势观察
**2024-2026 这两年的几个变化**:
- **AI agent 把沙箱从基础设施推到工程师日常**。boxsh 和 container-use 都是 2025-2026 出现的新工具,命题是"给 LLM 一个可控的执行环境"——这是 2023 年之前不存在的产品定位。
- **macOS 加入战场**。Apple container(2025)+ boxsh 的 macOS Seatbelt 后端,意味着开发机沙箱不再是 Linux 专属。
- **Landlock 进化**。5.13 的 fs only → 5.19 加 TCP → 6.7 加 truncate → 6.10 加 IPC——慢慢补齐。配合 namespaces 已经能做到非常细的沙箱。
- **gVisor 之后没出现新的"应用内核"**。WASI / WasmTime / WasmEdge 是另一条路(WebAssembly 当沙箱),但还在不同 niche。
- **boxsh 这种"shell + MCP + 沙箱"打包形态**,技术上没有 nsjail / bubblewrap 做不到的事,胜在产品形态。这预示 LLM agent 工具链会涌现一批**重新打包旧机制**的项目,比拼的是"对 agent 工作流的契合度",不是底层创新。
---
## 8. 与 agents-series 的对话
把这份调研放到 series 阅读线里看:
- **Anatomy of Agent Harness**(series 第 3 篇)列了 12 组件,其中"sandbox / execution environment"组件正是这套东西的位置。boxsh 把 harness 的"execution"层做成了独立产品——可以和任何 harness(Claude / Codex / 自研)解耦协作。
- **Generative Agents**(series 第 2 篇)的 Smallville sandbox 是"语义沙箱"——agent 在其中"做饭""上班"。本调研是"系统沙箱"——agent 在其中真的执行命令。两者形成对照:前者隔离的是行为符号,后者隔离的是 syscall。**真实部署的 agent 系统两层都需要**:语义沙箱限定 agent 能想什么,系统沙箱限定 agent 能做什么。
- **5k Agents 实验**(series 第 5 篇)把 5000 个 agent 关在 HKU 的内部框架里跑——那个框架本质上是 nsjail 级别的进程隔离 + 消息总线。再放大十倍就要上 gVisor 或专用 Linux/Bot kernel。
不要再以为"agent sandbox = Docker container"了——Docker 是其中一档(中等隔离 + 重量)。这条光谱从 Landlock(最轻 + 协作型)一路到 gVisor(最重 + 对抗型),按你的威胁模型选。