Linux 沙箱技术全景:从 chroot 到 application kernel

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* 创建新的隔离域,进程进去之后看到的就是空的或克隆的资源。

User namespace 是 Linux 3.8(2013)才稳定下来的,之前的沙箱只能靠 setuid 帮助程序。bubblewrap 的诞生正好踩着这个时间点。

1.2 seccomp-bpf

进程级 syscall 过滤器。用 BPF 字节码定义"哪些 syscall 允许 / 拒绝 / 返回错误码 / 杀掉进程 / 通知 supervisor"。

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

关键设计

被谁用: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 的代价:firejail 历史上 CVE 不少,因为 SUID 二进制是攻击面。新版本主推 user-ns 路径,但 SUID 路径仍保留兼容。

适合:非容器化的桌面应用一键加固。不适合作为不可信代码执行容器。

3.3 nsjail

身份证:Google 安全团队出品,专为 CTF infrastructure / 模糊测试 / RPC 服务隔离 设计。

核心立场精确控制 + 高吞吐。四种执行模式:

关键设计

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 的爆炸半径"——不是"对抗主动入侵"。

关键设计

适合:你在写一个长期运行的 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,攻不到宿主。

代价

适合:跑真正不可信的 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);

关键属性

适合:库作者给自己的程序加自我保护层。go-landlockrust-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

boxsh --sandbox \
      --bind cow:/path/to/project:/path/to/dst \
      --new-net-ns

cow:SRC:DSTDST 上挂一个 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. 共性踩坑

无论用哪个第二代工具,绕不开这几个坑:

  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 这两年的几个变化


8. 与 agents-series 的对话

把这份调研放到 series 阅读线里看:

不要再以为"agent sandbox = Docker container"了——Docker 是其中一档(中等隔离 + 重量)。这条光谱从 Landlock(最轻 + 协作型)一路到 gVisor(最重 + 对抗型),按你的威胁模型选。