沙箱与权限 · 把 agent 关进操作系统级的笼子

光靠「问一句要不要执行」不够。grok 用 xai-grok-sandbox 在操作系统层面把 agent 关进笼子:Linux 用 Landlock + bubblewrap,macOS 用 Seatbelt,子进程网络用 seccomp 逐个封。这一章拆它的 profile、平台差异、fail-closed,以及沙箱如何让权限层敢少问。

作者 David更新于 第 23 篇(共 60 篇)

沙箱与权限 · 把 agent 关进操作系统级的笼子

一个会跑任意 shell 的 agent,光靠权限确认问一句「要执行吗」是不够的——模型可能被诱导、命令可能有副作用。grok 的 xai-grok-sandbox 把防线下沉到操作系统层。

内核级、一次性、不可逆

沙箱靠 nono crate 落到内核:Linux 用 Landlock,macOS 用 Seatbelt。它在进程启动时应用一次,之后不可撤销——进程内的文件访问、子进程,统统被罩住。进程级网络保持开放(agent 得连模型 API),但子进程网络另外用 seccomp 逐个封(见下)。

Profile:几档预设

ProfileName 有几档:Workspace(默认)、Devbox、ReadOnly、Strict、Off、Custom。每档 resolve() 成一组「可读路径 / 可写路径 / 拒绝路径 / 是否限网」:

profile可写限网
Workspace工作区 + ~/.grok + 临时目录否
Devbox除 /data 外几乎全部否
ReadOnly仅 ~/.grok + 临时是
Strict工作区 + ~/.grok/sessions + 临时;读只放行系统目录等白名单,其余默认不可读是

Custom 可以 extends 一个内置档再叠加覆盖。配置从 ~/.grok/sandbox.toml(全局)+ 工作区的 .grok/sandbox.toml 合并,但工作区配置是只增不改的——一个安全属性:工作区不能重定义或掏空全局的 custom profile。

平台差异:一个精妙的 Linux 绕道

这里有个真实的内核限制值得讲:Landlock 无法「拒绝一个已允许目录树下的子路径」。所以 grok 在 Linux 上,遇到需要「读拒绝」的 profile,改用 bubblewrap(bwrap)重新 exec 自己,靠挂载命名空间实现:对拒写路径 --ro-bind、对拒读路径 --ro-bind 一个 chmod-000 的占位符……然后 re-exec current_exe。macOS 那侧 Seatbelt 能直接写拒绝规则,但也有讲究——要为每个具体写子动作(create/unlink/rename…)都写拒绝,还要处理 /tmp↔/private/tmp 这类 firmlink 别名,防绕过。

子进程网络:每个 fork 一道 seccomp

install_child_network_filter() 必须在 pre_exec(fork 后、exec 前)跑:一段手写的 seccomp BPF(在父进程里预先建好),对 connect/bind/sendto/sendmsg/... 连同 io_uring_* 这类绕路的调用一起返回 EPERM、其余放行,再 PR_SET_NO_NEW_PRIVS + PR_SET_SECCOMP。上一章的两条执行路径现在都调 restrict_child_network(cmd)——它在 should_restrict_child_network() 为真时替命令挂上这个 pre_exec。于是限网 profile 下,agent 跑的每条命令都联不了网。

fail-closed 是底线

整套设计处处 fail-closed:遇到无法转义的 Seatbelt 路径、超限的 glob、缺失的 bwrap——一律报错或 exit(1),绝不静默地「没沙箱就裸奔」。启动时如果需要读拒绝(或 hook 写保护)而 bwrap 起不来,直接 exit(1),并提示去装 bubblewrap。

后来补上的几道加固

  • 命名空间锁定:bwrap 里再装一层 seccomp,禁掉 mount/unshare/setns 等调用,防止读拒绝的挂载被改回去;read_deny_verify.rs 还会校验读拒绝确实生效。
  • hook 写保护(hook_write_deny.rs):非 devbox 档不许改写 grok 的全局 hook 来源(~/.grok/hooks/、hooks-paths 及其登记的目标),做不到就 fail-closed。
  • 容器运行时 socket(runtime_sockets.rs):限网档启动时屏蔽 docker/podman/containerd 的 API socket——否则子进程能借它们绕过网络限制。

沙箱让权限层敢少问

最后一环把两者接起来(xai-grok-workspace/src/permission/manager/mod.rs):打开 [sandbox] auto_allow_bash(默认关)后,沙箱激活时 bash 调用可以被自动放行(sandbox_may_auto_allow_bash)——除非命中「危险命令下限」才还要问你。逻辑很干净:既然 OS 已经把笼子焊死了,权限层就能安全地少弹确认框。沙箱不是权限确认的替代,而是它的底座——有了硬隔离,软确认才能松绑。

和《从 LLM 到 Coding Agent》对照

对照 permissions:教学实现的权限是「执行前问一句」。grok 的增量是把安全做成两层:上层是可交互的权限确认,下层是不可逆的 OS 级沙箱(Landlock/Seatbelt/seccomp)。这是「玩具靠自觉,产品靠内核」的又一个硬核例子。工具系统还剩几件小而有料的——下一章看 LSP 代码智能。


上一篇:Shell 执行与 PTY · 下一篇:LSP 代码智能

本页目录