Linux 沙箱技术全景:从 chroot 到 application kernel
调研时间:2026-05 范围:bubblewrap、firejail、nsjail、minijail、gVisor、Landlock、boxsh、container-use、Apple Container 等 视角:内核机制 → 工程实现 → 信任模型 → 选型决策
0. 一句话定调
把"沙箱"从一个口号拆开来看,本质是给一个进程套一层"它能看到/能动用什么"的限制壳。Linux 上这层壳过去 30 年走了三代:
- 第一代(陷阱型):chroot、setuid——把进程关进文件系统某个角落。轻易被绕过。
- 第二代(机制型):namespaces + seccomp + cgroups + capabilities——内核给进程做"分身",每个分身看到的内核资源是隔离的子集。bubblewrap、firejail、nsjail、minijail 都属于这一代,差别只在编排策略。
- 第三代(拦截型):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 是 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。
典型调用:
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 服务隔离 设计。
核心立场:精确控制 + 高吞吐。四种执行模式:
-MoONCE:跑一次退出(典型一次性 RPC)-MlLISTEN:TCP 监听,每个连接 fork 一个隔离实例(CTF judging server)-MrRERUN:反复执行(fuzzing target wrapper)-MeEXECVE:直接 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.
工作方式:
- 应用进程发起 syscall
- 一个叫 Sentry 的用户态 Go 进程拦截(用 ptrace 或 KVM)
- Sentry 实现了大部分 Linux ABI(fs/net/mm/signal...)
- 真正需要触底的 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 形态:
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 接口。
架构三层:
- Shell 层:内嵌 dash 0.5.12,自己就是
/bin/sh替身。任何 POSIX 脚本能跑。 - 沙箱层:Linux 走 user/mount/PID/network namespaces + seccomp;macOS 走 Seatbelt(sandbox_init + SBPL profile)。不调外部工具,namespace/seccomp 都直接 syscall 进。
- 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:
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 外面:
{
"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 走另一条路:直接给每个 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 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. 共性踩坑
无论用哪个第二代工具,绕不开这几个坑:
-
/proc必须 fresh mount,否则沙箱内能读到 host 的/proc/sys/kernel/*,相当于裸奔。bwrap--proc /proc,nsjail--proc_rw 0。 -
/dev不能直接 bind。要么--dev-bind(bwrap,极简)要么手动建 tmpfs + mknod。 -
TIOCSTI 隧道:sandboxed 进程能往父 TTY 写指令。
bwrap --new-session/setsid必须有。 -
uid_map / gid_map race:early days 的 user namespace 实现有 setgid 绕过 bug。
PR_SET_NO_NEW_PRIVS是必须的。 -
abstract Unix socket 泄漏:
@/tmp/something这种 abstract namespace socket 在 net ns 里仍是隔离的,但同 host net ns 的兄弟沙箱之间能通。要--unshare-net。 -
Mount propagation:默认 mount propagation 是 shared,沙箱里挂载会传到 host。必须
mount --make-rslave /或类似。 -
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(最重 + 对抗型),按你的威胁模型选。