「AIAgent沙箱nono拆解」ClaudeCode/MCP权限最小化,内核级隔离怎么落地

admin 2026-09-28 05:09:24 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 本文拆解开源项目nono,一个用Rust编写的AIAgent运行时安全沙箱,定位为能力控制模型而非隔离边界。它通过macOS的sandbox_init和Linux的Landlock与seccomp实现内核级权限最小化,为每个工具调用建立独立子沙箱,默认拒绝未授权访问。文章详述了工具沙箱、网络凭证管理、上手命令及七条避坑指南,强调其失败关闭设计、审计边界和平台差异,适合需在真机运行AIAgent并保护敏感凭证的团队。 综合评分: 85 文章分类: AI安全,安全工具,红队,安全建设


「AI Agent 沙箱 nono 拆解」Claude Code / MCP 权限最小化,内核级隔离怎么落地

原创

句芒安全实验室 句芒安全实验室

句芒安全实验室

2026年9月25日 11:15 上海

在小说阅读器读本章

去阅读

在公众号小说中沉浸阅读

昨天句芒拆的是 Beelzebub,思路是”拦不住就把它引到假地方”。今天回到更靠前的一层:如果从一开始就不想让 Agent 碰到某个文件、某把密钥、某个 API,除了靠它自觉,还能靠什么?

GitHub 上把这件事做得比较彻底的开源项目叫 nono。它给自己的定位只有一句:agent runtime security,zero trust, zero setup, zero latency。

先核身份

按老规矩,发布前用 GitHub API 当天核实。仓库 nolabs-ai/nono,仓库 id 1146550474,4212 颗星、285 个 fork、209 个 open issue、16 个 watcher;主语言 Rust,协议 Apache-2.0,2026 年 1 月 31 日建仓,最近一次推送是 2026 年 9 月 24 日,9 月 11 日以来有 62 次提交。最新 release 是 v0.78.0(2026-09-16)。贡献者 100+,排第一的是 lukehinds。文档站在 docs.nono.sh,预置 profile 放在 registry.nono.sh。

一句话定位直接抄 README:几秒内、零配置地把 AI Agent 跑进一个零延迟沙箱——Claude Code、Codex、Pi、Copilot、Hermes、OpenCode、OpenClaw 都在支持列表里,不需要 daemon、不需要容器、不需要虚拟机,也不额外占磁盘。README 还写着这是”做出 Sigstore 的那个团队”做的,并贴了 Datadog 安全工程师和 Okta 工程师的两段使用评价。这两段属于官方自述,信不信由你,但至少方向对得上:它管的就是”给 Agent 发权限”这件事。

它和”把 Agent 塞进容器 / 微 VM”不是一回事

这句话值得单独说,因为很多团队的第一反应是”套个容器不就行了”。

nono 的官方安全模型里把这件事讲得很清楚:它提供的是 capability control(能力控制) 沙箱,而不是 guest/host 隔离边界沙箱。原话是:“这是一个能力模型,不是虚拟机模型。”

差别在哪?微 VM 的围墙很硬,但一旦进去,里面是平的:Agent 能自由摸到项目文件、无关文件、凭证、其他进程,因为 guest 系统自己不区分这些东西。nono 反过来,它不给整台机器加围墙,而是管住 Agent 运行上下文里的每一项资产:每个能碰到的文件、凭证、进程,都是一次显式授权的产物;没被授权的,即使跑在同一个内核上,也够不到。

为什么非要这样?因为自主 Agent 需要调用真实的开发工具、访问指定的服务、偶尔还要临时申请更多权限。这种”运行时委派”跟”永久封死的进程模型”是冲突的——一个既承诺绝对密封、又承诺细粒度运行时委派的模型,本身是自相矛盾的。

所以它的安全目标被写成了这样一句:让每一次权限授予都显式、够窄、可强制、可审计,并且发生在进程和它的子进程 fork 出来的那个点上。

内核级怎么落地

macOS 和 Linux 走的是两套内核机制,但性质一样:在 exec 之前就把沙箱装好,之后由内核执行,进程自己删不掉。

macOS 上用的是 Apple 的私有 sandbox_init() API(跟公开的 sandbox-exec 底层是同一套机制),nono 先生成一份 Scheme 风格的 Seatbelt profile,开头就是 (deny default),再 sandbox_init() 应用,调用之后限制对该进程永久生效。Linux 上用 Landlock 做文件系统白名单和网络规则,用 seccomp 只拦 openat 和 openat2 两个系统调用,read、write 这些热路径全部放行;官方给的数字是每次文件打开 3 到 10 微秒开销。

这里有个细节值得注意:seccomp 只在 openat 处设卡,意味着 stat 和 access 没被拦——被关住的进程仍然能枚举文件系统的结构,而不触发任何 supervisor 通知。官方承认这对”会配合的 Agent”(它的目标场景)可以接受,但对真正的对抗性代码,这就是一条侦察路径。

内核能力还分版本:完整的文件系统控制要内核 6.2+,网络过滤要 Landlock ABI v4(6.7+),设备 ioctl 过滤要 ABI v5(6.10+),信号和抽象 UNIX socket 作用域要 ABI v6。内核不够新的时候,nono 是失败关闭的:要么拒绝启动,要么拒绝执行受限的那部分,不会悄悄降级成”没沙箱”。

失败关闭这条,是它整个安全模型里最该记住的一句:“出任何问题,子进程都拿不到访问权。”

工具沙箱:真正把边界挪到”调用”上

这部分是 nono 跟多数沙箱拉开差距的地方。

普通沙箱的做法是”把 Agent 关进盒子”,然后盒子里的工具全部共享同一套宽权限。nono 的说法是:Agent 会把活派给 git、gh、curl、kubectl、包管理器、构建脚本、MCP 客户端/服务端——而这些工具才是密钥、网络和副作用真正出现的地方。

于是它给每个受控命令单独开一个子沙箱:Agent 有自己的会话沙箱;当它调用一个受控工具时,由 broker 用一个独立的策略把工具拉起来——独立的文件系统授权、独立的网络规则、独立的凭证。子进程不继承外层会话的 --allow、CWD 权限、宽泛的 profile 组、原始凭证路径和网络访问,除非它自己的策略写了。

profile 里能表达出来的规则长这样:Agent 可以调 git,但 git 只能拿到仓库、受信任的 git 配置和对象存储;Agent 可以调 gh,但 gh 只通过 nono 的凭证代理拿到一个 GitHub token,而这个 token 通过 L7 过滤只能用于指定的 GitHub API 方法和路径(默认 deny);git 可以在一条链式策略下调 ssh,而 Agent 直接调 ssh 依然被拒。

这套策略写在 profile 里,不写在给模型的对话上下文里。Agent 可以申请调一个工具,但它没法把那个工具的沙箱撑大、没法自己签新密钥、也没法从会话内部绕过端点策略。 对做 AI 安全的人来说,这句话比”我们支持 MCP”重要得多:它把”MCP 工具被提示注入后拿去干坏事”的收益面直接砍掉了。

顺带说一句它的治理思路:被禁用的命令(比如 rm、chmod、sudo)在 Tool Sandbox 下是 deny-only 条目,走 shim 会被拒;绕过 shim 直接写 /bin/rm 这种绝对路径调用,同样按”直接执行绕过”拒绝。另外,凡是能被沙箱写入的可执行文件及其父目录,Tool Sandbox 一律不认。

网络和凭证:默认放行,是这套设计里最需要自己动手的部分

必须说清楚:nono 默认允许网络。想要拦住出网得自己加 --block-net,它会装一个静态内核过滤器,而且不起代理。

要按域名管,就得开代理模式:代理跑在未被沙箱的 supervisor 进程里,域名策略和凭证策略都从这里过。代理只把域名白名单当入口,还有一层 DNS rebinding 防护;上游 DNS 解析不出地址时失败关闭。Linux 上代理模式还会额外加一层 seccomp 用户通知,检查每一次 connect() 的真实目的地址——因为 Landlock 自己的网络规则压根没有目的地址这个概念,所以这层在每个 Landlock ABI 上都是常开的。

凭证那块走的是 phantom token 的路子:真实密钥不落到子进程手里,由代理在转发时注入。README 里也写了 keyring、--env-credential、1Password 和 Apple Passwords 的接法。

这里有两个坑值得先记住:一是 --open-port 3000 在内核 6.7+ 上只按端口过滤,管不住目的 IP,等于放行”任何 IP 的 3000 端口”;二是 WSL2 的内核是 6.6,只带 Landlock V3,没有按端口的 TCP 过滤,而且 WSL2 自己的 seccomp notify 会返回 EBUSY,导致代理式凭证注入默认被封,只能显式写 wsl2_proxy_policy: "insecure_proxy" 降级执行。

上手:三条命令

安装:curl -fsSL https://nono.sh/install.sh | sh,或者 brew install nono。装完先搜再跑:

nono search claude
nono run --profile nolabs-ai/claude -- claude

跑起来之后,Agent 对当前目录有读写,其他文件一概看不到。想改权限就把 profile 导出来自己改:

nono profile init claude --extends nolabs-ai/claude
nono profile validate claude
nono run --profile claude -- claude

调试和给 Agent 自诊断用 nono why:nono why --path ~/.ssh/id_rsa --op read 会告诉你这是 sensitive_path 拒绝;在沙箱里面可以 nono why --self --path /var --op write --json 拿一份结构化 JSON,说明为什么被拒、怎么改。这一条对 AI 安全挺关键——它把”被拒绝”变成 Agent 能读懂的语言,而不是让 Agent 去猜、去硬闯。

另外它还有 --rollback:执行前后各打一次快照,内容寻址 + SHA-256 去重 + Merkle 树校验完整性,macOS 上 APFS 用 clonefile() 写时复制,nono rollback show/restore/verify/cleanup 一套命令。以及 --audit、--audit-integrity、--audit-sign-key 这套审计签名。Rust、Python、TypeScript、Go 都提供了 FFI 绑定。

避坑:七条

第一,它不是隔离边界。 官方写得很直白:这是能力模型,不是 VM 模型;它降低的是进程和子进程的权限,不提供独立内核或硬件边界。要挡内核漏洞,得自己在外面再套一层容器或微 VM,官方也把这个当作”可选外层边界”。

第二,逃逸面就是内核漏洞。 文档列的”不防”清单里有:内核漏洞、隐蔽信道、TOCTOU 竞争窗口、以及”授权路径内部的数据”——你放行了某个目录,Agent 就能读写这个目录里的一切。另外 CPU 和磁盘没有被限流,Linux 上只有 cgroup v2 的 memory.max 和 pids.max,macOS 还没有对应机制,恶意 Agent 仍可以拿资源打拒绝服务。

第三,别把 Agent 自己的沙箱和它叠着开。 Claude Code 自己带沙箱,而 Anthropic 文档里明确写了一个”有意留的逃生舱”:命令因为沙箱限制失败时,Claude 可以在走完自己的权限流程后用 dangerouslyDisableSandbox 重试。官方建议是把 Claude Code 自带的沙箱关掉,让 nono 当唯一的边界;如果非要开着,就设 allowUnsandboxedCommands: false。这条对做 AI 安全的读者最有价值:Agent 自带的”权限系统”和内核级边界是两码事,前者可以被 Agent 自己绕开。

第四,审计的边界要读清楚。--audit-sign-key 签的是会话结束时那一次 Merkle root,不是每一条事件;--audit-integrity 记录的是受跟踪可写路径的文件系统状态根,不含回滚对象;而且可执行文件的哈希是在 exec 之前算的,中间还有一个很小的 hash-to-exec 窗口,官方说还没做到从同一个已打开的 fd 上 fexecve。另外它不证明进程的完整运行时闭包——共享库、解释器、动态加载的插件都不在里面。

第五,macOS 上的信任校验是启动时一次性的。 校验通过的文件会被写进 Seatbelt profile 里一条 (deny file-write-data ...),会话内结构上不可改;但 macOS 没有 Linux 那种系统调用级的文件打开拦截,所以沙箱应用之后才出现、且父目录有读权限的文件,照样能被读到。

第六,它在快速迭代,而且改过名。 仓库还在 1.0 之前,官方自己提示 API 还在稳定中;光 v0.78.0 一个版本的变更记录里就列了 5 条 GHSA 安全公告——这既说明响应快,也说明这类内核级沙箱的细节很容易出问题,升级前先读变更说明。另外 registry 命名空间从 always-further 迁到了 nolabs-ai,老命名空间要被下线;如果你的脚本或 CI 里还写着老名字,表现是”静默拿不到 pack”,而不是报错。

第七,注意平台的降级差异。--block-net 在每个支持的内核上都能用,但按端口的例外要 Landlock ABI v4;老内核上这类例外是失败关闭的。nono wrap 是直接 exec 的,所以不接受代理相关参数,也不接受 --memory、--max-processes 这类资源限制。

适合谁

需要在真机上跑 AI Agent、又不想把自己的 ~/.ssh、云凭证、K8s 配置、包管理器 token 交给它的人;已经在跑 MCP、又担心工具调用被间接提示注入拿去干别的安全团队;需要一套”策略写在配置里、能审计、能回滚、能自证”的 Agent 运行环境的团队;以及想研究”Agent 权限边界到底该画在哪”的研究者。

只想看效果的话,brew install nono 加 nono run --profile nolabs-ai/claude -- claude 两条命令就够,先看它列出来的能力清单,再拿 nono why 去问几个自己关心的路径,比如 ~/.ssh 和 ~/.aws,看它答不答得上来。

回到开头那个问题。让 Agent 自觉不碰密钥,是应用层权限系统的思路——而应用层的检查代码,跟 Agent 跑在同一个进程里,Agent 能改、能绕、能忽略。nono 选的是另一条路:把限制装进内核,让 Agent 连”删掉自己限制”的代码都找不到。


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。

本文转载自:句芒安全实验室 句芒安全实验室 句芒安全实验室《「AI Agent 沙箱 nono 拆解」Claude Code / MCP 权限最小化,内核级隔离怎么落地》

评论:0   参与:  0