ByteNoteByteNote
landlock-run:Agent 子进程沙箱启动器解析
字

字节笔记本

2026年10月6日 · 约 5 分钟读完

landlock-run:Agent 子进程沙箱启动器解析

API中转
¥120

当 Agent 框架要在 Linux 上替用户执行命令时,总会撞上同一个难题:既要限制子进程能读写哪些路径,又不能把宿主进程自己一并关进笼子。开源项目 landlock-run(npm 包名 @deepseek-ai/node-addon-landlock-run,BSD 3-Clause 协议)就是为此而生的方案:它基于 Linux 内核 5.13 起提供的 Landlock 机制,做成一个先自我限制、再 exec 的启动器,全部实现只有约 300 行 C11,直接对着内核 UAPI 编写,静态链接 musl,产出一个不依赖任何运行时的独立二进制。

landlock-run 包家族与解析关系

机制归它,策略归你

这个项目的架构文档开宗明义:仓库只拥有禁锢机制,不拥有策略。哪些路径允许读、哪些路径允许写,由上层消费者(Agent harness 或沙箱能力层)决定;这个包家族只提供两样东西,一个是负责强制执行这些授权的启动器,另一组是解析它、与它对话的 JavaScript API。打包方式参考了 esbuild 等 native 包的按平台拆包模型,只是把 Node 原生插件换成了独立的静态可执行文件。

双层包结构:入口包加平台包

整个家族由一个入口包加若干平台二进制包组成。

入口包是纯 ESM 的 JavaScript 包,拥有 CLI 契约的全部内容:launcherPath 负责把平台包解析成本机启动器的路径,probe 负责功能探测,grantArgs 负责把授权清单拼成命令行参数,另有契约常量。它的 tarball 里随包带着 C 源码,方便审计,并把每个平台包都列成 optionalDependencies。

平台包(linux-x64 与 linux-arm64 两个)里面没有任何 JavaScript,只有一个放在 bin 目录下的预编译静态二进制,外加一份声明它的 prebuilds.json。npm 的 os 与 cpu 字段让安装器只拉取与本机匹配的那一个,入口包把它解析成文件路径即可,没有东西需要 import。

这样拆的关键动机是版本对齐:CLI 解析器和二进制在同一个包家族里同版本发布,解析器永远不会落后于二进制支持的参数语法。至于共享加载器,项目明确决定不抽:平台包里本来就没有可加载的东西,等第二个工具真的需要共享 JS 时再提取也不迟。

探测是唯一可用性信号

launcherPath 把平台包解析为包内 bin/landlock-run;当平台包不可解析时,它返回入口包自身 node_modules 里一个确定性生成、但永不存在的回退路径。两侧的存在性都刻意不做检查:probe 才是唯一可用性信号,缺失的二进制与不强制 Landlock 的内核一样返回 unusable,消费者因此只有一条降级路径,而不是两条。

probe 之所以是功能探测而非版本比对,是因为版本检查会漏掉一类内核:syscall 都在,却拒绝实际强制。probe 会在一个短命子进程里真实构建并强制一个最大规则集,把 full、partial、unusable 三档结果如实带回来。

处处失败关闭

landlock-run 的执行与失败关闭路径

启动器把失败关闭贯彻到了每个环节:用法错误、内核不强制、授权根目录打不开、exec 失败,任何启动器级失败都以 125 退出,且绝不执行被包裹的命令。部分强制(较老 ABI 只管得住一部分访问)会被接受,在 stderr 上报告一行,并由 probe 标记为 partial,至于每一档该承诺什么,由消费者自己的模式词汇决定。另外,二进制和入口包都不读任何环境变量:由哪个二进制来禁锢进程,永远不会被环境暗中决定。

构建与发布:无交叉工具链

构建只在本机原生进行:用发行版的 musl-gcc 静态编译,产出的一个二进制在 glibc 和 musl 发行版上都能跑。CI 的各架构 runner 是记录在案的构建者,仓库里不存在交叉工具链。发布质量由三道门禁把守:平台包 prepack 拒绝缺失或 ELF 架构不符的二进制;入口包 prepack 拒绝未构建的 lib;发布管线再把安装产物与打包来源的工作区构建做逐字节比对。

平台矩阵本身是签入仓库的元数据(prebuilds.json 加 os/cpu 字段),一个脚本从中派生出 CI 与 Release 矩阵,新增平台不需要改 workflow。

边界划得很清楚

明确不支持的平台也写在明面上:macOS 用户通常直接用系统自带的 sandbox-exec,没有二进制可发;Windows 的禁锢是另一套机制,理应另立仓库;其他 CPU 架构在没有原生 runner 证明能构建之前,一律不加包。落在这之外的消费者会解析到不存在的路径,probe 报 unusable,照旧失败关闭。

对正在给 Agent 做命令执行加固的团队来说,这套设计提供了一个值得参考的样板:机制与策略分离、契约随二进制同版本演进、可用性只有一条判定路径、失败默认关闭。安装也简单,npm install 即可,宿主机跑着带 Landlock 的 Linux 内核就能用。

相关文章

分享: