在 AI 时代,运行不可信代码已成为刚性需求。无论是 AI Agent 执行用户提供的脚本、CI/CD 流水线运行第三方构建工具,还是低代码平台动态执行逻辑,都需要一种轻量级、可配置的进程隔离方案。Zerobox 正是为这一场景设计 —— 它基于 Linux namespace 与 seccomp 构建,提供命令行级别的沙箱隔离能力,且不依赖 Docker 或虚拟机,开销仅约 10 毫秒。

核心架构:三层隔离模型

Zerobox 在 Linux 平台上的沙箱实现由三层机制组成,彼此协同形成纵深防御。

第一层:Linux Namespace 隔离。Zerobox 利用 unshare 系统调用创建独立的命名空间,包括 PID Namespace(进程隔离)、Mount Namespace(文件系统隔离)、Network Namespace(网络隔离)、UTS Namespace(主机名隔离)和 User Namespace(用户隔离)。在默认配置下,sandboxed 进程拥有独立的进程树,无法看到 host 上的进程;文件系统视图被限制在仅必要的挂载点,敏感目录如 /home/root 默认不可访问;网络层面则创建独立的网络栈,默认禁止所有出站流量。

第二层:Seccomp 系统调用过滤。在 Namespace 基础上,Zerobox 加载 seccomp BPF 过滤器,对进程可调用的系统调用进行白名单控制。默认策略允许常见的读、写、内存映射等基本 syscalls,阻止可能危害宿主机的特权调用如 mountunsharesetuid 等。这一层是安全的关键边界 —— 即使攻击者突破了 Namespace 隔离试图执行特权操作,seccomp 也会在系统调用层面将其拦截。

第三层:Bubblewrap 底层封装。Zerobox 底层使用 bubblewrap(bwrap)作为 Linux 沙箱的执行引擎。bubblewrap 是一个无特权的沙箱工具,被 Flatpak 等项目广泛采用,它将 namespace、chroot、bind mount 等操作封装为简单的命令行接口。Zerobox 在此基础上增加了代理层,实现网络流量的拦截与凭证注入。

这种架构的优势在于:所有隔离机制均在内核层面完成,无虚拟化开销;bubblewrap 以非特权用户运行,即使存在内核漏洞,攻击者也无法轻易提权;seccomp 过滤器在系统调用层面切断攻击路径,提供了独立于 Namespace 的第二道防线。

核心配置参数与工程落地

Zerobox 的设计哲学是「默认拒绝」,所有权限需要显式授予。以下是关键配置参数的工程化实践。

文件系统访问控制

--allow-read--deny-read 控制读权限。默认允许读取系统库和可执行文件,用户数据目录则需要显式列出。工程实践中建议采用白名单模式:--allow-read=/tmp/input,/data 仅开放业务所需的输入目录。--deny-read 用于排除特定敏感路径,例如 --deny-read=/etc/ssl/private,优先级高于 allow。

--allow-write--deny-write 控制写权限。默认禁止所有写入操作。需要输出结果时,--allow-write=/tmp/output 允许写入临时目录;若不指定路径,--allow-write 允许写入整个文件系统,但这将显著降低安全等级。构建场景中常见配置为 --allow-write=./dist,仅允许写入构建产物目录。

网络访问控制

--allow-net--deny-net 实现域名级别的网络过滤。默认完全禁止网络访问,这确保了不可信代码无法外传数据。典型配置如 --allow-net=api.openai.com 仅允许调用特定 API 端点;--allow-net(无参数)允许所有网络访问,适用于需要访问多个外部服务的场景。

对于 AI Agent 场景,建议采用最严格的网络策略 —— 仅开放 Agent 实际需要调用的域名,并在 DNS 层面验证域名解析结果,防止 IP 绕过。

环境变量与凭证注入

Zerobox 的环境变量策略同样遵循默认拒绝原则。默认仅继承 PATHHOMEUSERSHELLTERMLANG 等 essentials 变量,其他变量需要通过 --allow-env 显式传递。

凭证注入是 Zerobox 最具特色的安全能力。通过 --secret 传入敏感信息时,sandboxed 进程看到的是占位符(如 ZEROBOX_SECRET_a1b2c3d4e5...),而非真实密钥。真实凭证仅在网络代理层针对授权域名进行替换。例如:

zerobox --secret OPENAI_API_KEY=sk-proj-123 \
        --secret-host OPENAI_API_KEY=api.openai.com \
        -- node agent.js

在此配置下,进程内部 $OPENAI_API_KEY 为占位符;当进程向 api.openai.com 发起请求时,代理层自动替换为真实密钥。这意味着即使用户代码尝试 console.log(process.env.OPENAI_API_KEY),泄露的也只是无效占位符。

严格模式与调试

--strict-sandbox 强制要求完整沙箱功能。当底层系统不支持 bubblewrap(如某些容器环境)时,该参数会导致命令失败而非降级到弱隔离模式,避免意外暴露攻击面。

--debug 参数在开发调试阶段非常有用,它将沙箱配置和代理决策输出到 stderr,帮助理解权限规则的生效情况。

性能开销与资源监控

根据官方基准测试,在 Apple M5 Pro 上,sandboxing 的时间开销约为 10 毫秒,内存增量约为 7MB。对于 IO 密集型命令如 echo hello, Bare 执行 <1ms,sandboxed 约 10ms;对于 Node.js 和 Python 解释器启动,额外开销同样稳定在 10ms 量级。

生产环境中建议监控以下指标:sandboxed 进程的系统调用被 seccomp 拦截的次数(可通过 /proc/[pid]/status 中的 Seccomp 字段或审计日志获取);进程的 namespace 隔离状态(检查 /proc/[pid]/ns/* 是否与 host 分离);网络代理层的请求拦截与替换次数。

与其他沙箱方案的对比

传统方案中,Docker 容器提供进程隔离但启动成本高(秒级)、资源占用大(数十 MB 到数百 MB);Firejail 侧重于桌面应用沙箱,配置粒度较粗;gVisor 或 Firecracker 提供更强的隔离但需要专用内核模块或微型虚拟机。

Zerobox 的定位是轻量级命令沙箱 —— 它不追求容器级别的完整系统隔离,而是针对「运行一条不可信命令」这一高频场景,提供足够安全但极低开销的解决方案。10ms 的启动延迟使其可以直接嵌入 CI 流水线或 AI Agent 的每次工具调用中,而无需预先启动常驻进程。

工程实践建议

首次将 Zerobox 集成到现有系统时,建议采用渐进式策略。首先以 --allow-all 运行,确认业务逻辑正常工作;而后逐步收紧权限:首先添加 --deny-write 禁止所有写入,再通过 --allow-write 精确开放必要目录;最后根据实际网络需求添加 --allow-net 域名白名单。每一轮调整后运行完整的端到端测试,确保功能无损。

对于 AI Agent 场景,推荐为每个 Tool Call 创建独立的 Sandbox 实例 ——reader 沙箱仅允许读取输入文件,writer 沙箱允许写入输出目录,fetcher 沙箱仅允许网络访问目标域名。这种 per-tool 隔离策略将攻击面分解到最小单元,单个沙箱的突破无法直接影响其他工具链。

Zerobox 代表了轻量级进程沙箱的发展方向 —— 不依赖重型虚拟化技术,而是在内核提供的隔离机制基础上构建精细化的控制平面。随着 AI 代码执行场景的普及,这类工具将成为安全架构中不可或缺的组成部分。


参考资料