沙盒策略、客户机沙盒与内核哨兵

每台机器本来就在传输边界之后运行:代理手上只有替身凭据,它发出的一切都会经过 Mac 上 Bromure 的代理服务器与虚拟交换机(见凭据与传输边界)。沙盒策略决定一台机器还能做什么——它的 VM 可以连到哪里,以及(如果你愿意)它的代理在 VM 里可以碰什么。一台机器(在设置编辑器中即工作区)在设置 › 沙盒策略中从两种沙盒引擎择一:

Bromure FirewallOpenShell + Sentry
你要写的一张有顺序的允许/拒绝规则表(pf 风格),可选择默认拒绝一份 NVIDIA OpenShell 沙盒策略(YAML,架构 v1)
未匹配流量的默认允许(或拒绝)拒绝
请求检查每台主机的 HTTP 方法(web 规则)REST、WebSocket、GraphQL、MCP 与 JSON-RPC,逐个请求,采用 enforce 或 audit
VM 内部无客户机沙盒(文件系统、进程、二进制文件)、内核哨兵、没有 root 的软件包
解除拦截限时规则、安全时间线快捷操作、Sandbox 抽屉Sandbox 抽屉、位于 http://policy.local 的策略顾问
最适合在不改变机器运作方式的前提下收窄它的网络你想从头到尾都受限、受监看且可审计的机器

新机器一开始使用 Bromure 防火墙。两种引擎都在代理碰不到的 Mac 上执行,每一项判定都是安全时间线中的一行。

有五项防护在任一引擎下都有效,位于护栏 › 防护:

  • 策略拦截时告知代理;
  • 代理看门狗;
  • 凭据严格模式;
  • 透明拦截;
  • 逐请求的不安全绕道。

Bromure 防火墙

防火墙是一份规则列表,涵盖 VM 打开的每一条连接:主机、IP/CIDR、协议、端口,以及网页流量的 HTTP 方法。规则自上而下比对,第一个命中者说话,规则也可以关闭,或限时开启。它在虚拟交换机上检查(依目的地地址,以及 VM 查询的名称),并在代理服务器中再检查一次(依 TLS 服务器名称,web 规则还依方法)。VM 的 80 与 443 端口会被转进代理服务器,所以里面的任何东西都无法自行退出。规则语法与编辑器记载于沙盒策略 › Bromure 防火墙。

OpenShell 策略

NVIDIA OpenShell 是一种把 AI 代理放进沙盒的开放格式:用一份 YAML 策略说明代理可以连到哪些端点、它的请求如何被检查、它可以碰哪些文件,以及哪些程序可以这么做。Bromure 原生执行 OpenShell 策略。你可以带来一份为 OpenShell 编写的策略,或从 Bromure 的初始策略开始,得到相同的判定。Bromure 的评估器已在超过 125,000 个差异测试用例中与 OpenShell 自己的评估器比对,没有任何不一致。

在机器设置中选择 OpenShell + Sentry,会加载你组织的默认策略,或初始策略(GitHub、只读的 npm 与 PyPI、Ubuntu 的软件包镜像站),并开启内核哨兵。从此之后,除非有 network_policies 规则允许,否则一律拒绝。

策略在哪里执行

在哪里决定什么
虚拟交换机每一条连接,依地址与端口(所有协议),并在开启 Enforce binaries 时依打开它的程序。
TLS 服务器名称/HTTP Host一条连接属于哪个端点,包括绕过代理设置的连接。
代理服务器经由 HTTPS_PROXY 建立的连接。
每个请求带有 protocol(rest、websocket、graphql、mcp、json-rpc)的端点会逐个请求检查:方法与路径(access: read-only、read-write、full,或明确的规则与 deny_rules)、查询参数、GraphQL 操作、MCP 工具与 JSON-RPC 方法。enforcement: enforce 会拦截;audit 会记录并放行。

有些目的地不论策略怎么写都会被拒绝:

  • 环回、链路本地与云元数据地址;
  • Kubernetes 控制平面的端口(2379、2380、6443、10250、10255)。

私有范围(10/8、172.16/12、192.168/16、100.64/10)只能通过精确的主机名或 allowed_ips 连到。网络规则会立即应用到运行中的机器。文件系统、进程与哨兵的设置在下次 VM 启动时生效。

被拒绝的请求会收到带有 OpenShell 自己 policy_denied 正文的 HTTP 403,所以懂 OpenShell 的工具与代理会有同样的反应。

Bromure 自动添加的规则

机器仍然必须连得到它的代理的模型提供方、它的凭据所属的主机、它的 Kubernetes 集群、AWS,以及它的本地模型。如同 OpenShell 的提供方规则,Bromure 会为这些添加 _provider_… 规则。它们列在 Governance 卡片的 Rules Bromure adds automatically 之下,且不包含在导出的策略中。

OpenShell 下的凭据

Bromure 的凭据保护没有改变:VM 持有的是替身,真正的值在传输上换入,而且只针对该凭据自己的主机。OpenShell 机器的凭据窗格中多了两项:

  • OpenShell 提供方配置文件——导入一份配置文件(openshell profile export),输入它的值,每个值都会成为绑定到配置文件主机与路径的机器凭据,配置文件的端点则以规则的形式加入。
  • Use OpenShell credential placeholders——代理看到的是 openshell:resolve:env:NAME,而不是 Bromure 那种以假乱真的令牌,就像在 OpenShell 下一样。

策略自己的凭据字段(credential_binding、credential_signing 等)会被忽略并附上警告:Bromure 会把每个凭据绑定到它的主机,并自行签署 AWS 请求。

与 OpenShell 的差异

Bromure 遵循 OpenShell 的语义,但有几处刻意的差异:

  • **没有 filesystem_policy,就没有文件系统沙盒。**少了这个分区的策略不会限制 VM 的文件;OpenShell 则会把代理限制在它的工作文件夹中。
  • **binaries 需要 Enforce binaries。**没有它时,binaries 只是参考,编辑器也会如此警告:代理在 VM 中持有 root,所以进程的身份不可信。
  • **中间件。**只有内置的 openshell/regex 中间件在 redact 模式下会运行。外部中间件会依它的 fail_open / fail_closed 被放行或拦截。
  • **提供方配置文件。**会 refresh 或使用 token_grant 的凭据,会被当成静态值使用。
  • **Seccomp。**过滤器经过调整,让 VM 的终端中可以使用作业控制。

客户机沙盒

网络策略限制 VM 可以连到哪里。客户机沙盒则限制代理在 VM 里可以做什么。它来自策略的三个部分:

策略分区Bromure 应用什么
filesystem_policy(+ landlock)代理只能读写策略列出的路径,以 Linux Landlock 执行——连 VM 中的 root 都无法解除。landlock.compatibility: best_effort 在路径无法使用时会降级;hard_requirement 则失败即关闭。
process(run_as_user、run_as_group)代理以那个账号运行,没有 sudo 或 Docker。它仍把 /home/ubuntu 当成主目录,所以它的配置照样适用。
Enforce binaries(严格沙盒)VM 中一个属于 root 的证明程序会把每条连接归属到建立它的程序,而规则的 binaries 必须点名它。每个程序的哈希值在首次使用时固定下来,被改动过的二进制文件会被拒绝。为了让这份报告可信,代理会失去每一条通往 root 的路:没有 sudo、没有 Docker,所有流量都经过透明拦截。

每个部分只有在存在时才会应用。没有 filesystem_policy 也没有 process 分区的策略,在 VM 中什么也不限制。有 process 分区就会自动开启 Enforce binaries。

配置组合你得到什么
只有网络策略网络受限;VM 不受限。
有 filesystem_policy,二进制文件未强制文件受限,即使面对 root 也一样。sudo 仍可使用,所以代理可能停掉 Bromure 在 VM 中的辅助程序——Sandbox 卡片会对此发出警告。
Enforce binaries,或有 process 分区文件、系统调用与权限都受限。代理无法成为 root,也无法干扰 Bromure 的进程。

Bromure 会添加它自己的管线所需的少数路径,除非必须写入,否则都是只读,并列在 Sandbox 卡片上(Added for Bromure: …):

  • 终端的 socket;
  • meta 共享文件夹;
  • /proc;
  • DNS 配置;
  • 位于 /usr 之外的代理安装文件夹(Grok 在 /opt/grok,Oh My Pi 的运行时在 /opt/bun)。

它们永远不会被并入你自己的列表。

失败即关闭

沙盒永远不会悄悄退回到不受限的运行:

  • 如果严格沙盒没有完全应用,就不会启动任何会话,二进制文件规则会拒绝一切,Sandbox 卡片也会以红色说明。重启 VM 会再试一次。
  • Bromure 替你在机器中运行的命令——终端、vm exec、文件操作、git、规划器——会等沙盒就绪(单纯的命令等 90 秒)。若沙盒没有就绪,命令会以 sandbox unavailable: … 失败。
  • Bromure 自己在机器中调用的 git 一律在受限状态下运行,因为仓库可以让 git 指向它所选的任何程序。

没有 root 的软件包

受限的代理没有 root,所以全系统的安装行不通。Sandbox 卡片上的两个开关(默认都开启)让安装仍能运作:

  • Packages in the home folder——pip install 会装到 ~/.local(npm 本来就全局安装到 ~/.npm-global)。Bromure 自己在 VM 中的进程会忽略主目录中的软件包,所以代理无法在其中植入代码。

  • System packages on request——当代理运行 apt-get install(不论有没有 sudo),请求会等你决定,而不是失败。代理看到:

    bromure: this sandbox has no root. Asked the user to approve installing: libpq-dev
    bromure: waiting for their decision (Sandbox drawer › Proposals)…
    

    打开 Sandbox 抽屉的 Proposals 标签页,选择Install或 Reject。批准的软件包由 VM 的 root 监督程序从 Ubuntu 镜像站安装,而且只依精确名称:不接受选项、文件路径、.deb 文件,也不接受 apt 会解读成模式或删除的名称。被拒绝的请求会告诉代理不要重试。每个请求及其结果都是安全时间线中的一行 Guest sandbox。

内核哨兵

内核哨兵是一个小型的 Linux 内核模块,从 VM 的内核内部报告那里发生了什么。它通过一条私有通道报告给 Mac,代理无法靠终止某个进程让它噤声:

  • 程序启动;
  • 连接,以及每条连接背后的进程链;
  • 权限变更;
  • 模块与 BPF 加载;
  • 客户机沙盒拒绝的一切。

哨兵启动之后,内核会被锁定,连 VM 中的 root 都无法加载代码来干扰。

有了哨兵,以下这些才得以实现:

  • 网络溯源链——每条连接是由哪个提示、哪个工具调用、哪个进程建立的。
  • 时间线与 Sandbox 抽屉 Files 标签页中的沙盒拒绝。
  • 篡改检测——哨兵编号串流中的缺口、被解除的探针、伪造的重新连接、未签名模块的加载,或被调低的 lockdown,都会以红色标为 tampering suspected,并送进代理看门狗。
  • Enforce binaries 所需的二进制文件身份。

它有两种模式,在 Kernel sentry 卡片中设置:

模式开机时哨兵无法加载时
Best effort哨兵在后台加载;会话不会等它。机器在没有哨兵的情况下运行,卡片会说明原因。
Required在哨兵就位之前,VM 中什么都不会运行。内核会被进一步锁定,因此也排除了 bpftrace 与 perf 这类内核追踪工具。如果 VM 说明了原因(例如它的内核没有对应的模块),机器会在没有哨兵的情况下启动,并附上警告。始终没有连上、也没有说明的哨兵会被视为篡改。

你的组织可以要求最低模式。机器会以两者中较严格的那个运行。

按需提供的模块

内核模块只能加载到它所编译的那个确切内核,而机器随时都可能通过 apt upgrade 换到新的 Ubuntu 内核。所以模块不随应用程序一起提供。Bromure 每天为每个 Ubuntu 内核编译模块,并发布在 dl.bromure.io。目录与每个模块都有签名,Bromure 会验证签名、校验和,以及目录不比它已有的版本旧。

  • 启动时,Bromure 会获取你的机器已知会运行的内核所需的模块,并在每次开机前准备好。
  • 当 VM 以 Bromure 没见过的内核开机时,VM 会提出请求,Bromure 会获取模块并在几秒内交给它。
  • 若该内核还没有任何模块,VM 会立刻得知,卡片会显示 Couldn't start: no module published for 内核 yet。请一天后再试,或以前一个内核开机。

下载的模块保存在 ~/Library/Application Support/BromureAC/sentry-modules/。

Sandbox 抽屉

覆盖在机器窗口上的 Sandbox 抽屉:标题 Sandbox、Web app · OpenShell + Sentry、Network、Files 与 Proposals 标签页、api.stripe.com:443 与 pypi.org:443 等被拦截的目的地(各附次数与时间、一个 Allow 按钮与一个关闭叉号),以及底部的 Sandbox Policy… 按钮。

每台机器的窗口在工具栏上都有一个盾牌按钮(“Sandbox: what it blocked, ready to allow”)。它会在舞台上方打开 Sandbox 抽屉。你也可以按 ⌃⌘N,或选择显示 › Toggle Sandbox Drawer。按钮上的红色标记会计算自你上次查看以来被拦截的目的地数量。抽屉会列出机器的沙盒在过去两小时内拒绝的东西,随时可以允许:

标签页引擎内容
Network(防火墙下为 Blocked)两者每个被拦截的目的地,附尝试连接的程序(若已知)、次数与时间。Allow 会为该主机与端口添加一条规则。在防火墙下,它提供始终、15 分钟、1 小时或直到工作区停止。在开启 Enforce binaries 的 OpenShell 下,规则会点名尝试连接的程序。
FilesOpenShell,且有 filesystem_policy文件系统沙盒拒绝的东西,按文件夹分组,附操作与文件。Allow 会以只读、Read and write,或只针对那个文件加入该文件夹。文件访问在下次启动时才改变:会出现一张重启卡片。
ProposalsOpenShell软件包请求(Install / Reject),以及策略顾问正等你决定的规则(Approve / Reject)。
Temporary防火墙限时开启的规则,附结束时间,以及 End。

从抽屉添加的规则会存储到机器并立即生效。它们和其他任何编辑一样,会与你的组织边界比对。底部的 Sandbox Policy… 会打开机器的设置。

策略顾问

碰壁的代理通常很清楚自己需要什么。在 OpenShell 下,它可以开口要求:当请求被拦截时,代理可以向 OpenShell 的顾问 API http://policy.local 提出能允许该请求的最窄规则,并等待你的决定。它收到的 403 会告诉它怎么做。你在沙盒策略 › Policy advisor › Agent proposals 中选择它能做到什么程度:

模式会发生什么
Off(默认)代理不能提出规则。被拦截的连接仍会为你草拟成提案。
Ask me每个提案都会在 Sandbox 抽屉与 Policy advisor 卡片中等候,直到你 Approve 或 Reject。拒绝的理由会发回给代理。
Approve if risk-free没有风险发现的提案会立即批准。连到机器持有凭据的主机、在那里增加方法,或使用通配符、私有地址、数据库端口或高位端口的提案,一律等你决定。会超出你组织边界的提案也一样。

代理只能提出 rest 或 websocket 规则,而且永远不能提出 tls: skip。批准的规则会加入策略、实时生效,并保留在策略的修订历史中。每个提案与每项决定都是安全时间线中的一行 Firewall。

告知代理:策略引导

只看到“connection refused”或“permission denied”的代理,往往会重试,接着另找门路。从外面看,这就像一个失控的代理。开启 Tell the agent when policy blocks it(位于护栏 › 防护,OpenShell 机器默认开启)时,代理会在它的对话中被告知拦截了什么、为什么,例如:

[Bromure policy] This workspace's network policy blocked connecting to api.stripe.com:443 — no network policy allows api.stripe.com:443. … This is a deliberate restriction, not a fault: don't retry it or reach the same thing another way … If your task needs it, stop and tell the user what you need and why so they can decide.

  • Claude Code 会在碰到拦截的那条命令之后立刻收到通知,就在回合进行中。
  • Codex、Grok、Kimi 与 Oh My Pi 会在它们的回合结束后收到输入的通知。
  • 通知会利用内核哨兵的进程链,发给建立了被拦截连接或文件访问的那个代理。
  • 当 Bromure 无法判断是哪个代理时,网络拦截会告知机器中的所有代理,文件拦截则只告知唯一的代理。
  • 同一个拦截每五分钟只告知一次,一个代理每分钟最多收到五则通知。代理自己的后台流量,以及常见的遥测主机,永远不会被报告。

通知中来自 VM 的值(主机、路径)会以数据的形式引用,所以程序无法利用拦截来替代理“代言”。

代理看门狗

看门狗从 VM 外部,把会话当成一个整体来看:

  • 一连串的拦截;
  • 不属于这里的凭据;
  • 哨兵遭篡改的迹象;
  • 失败的沙盒;
  • 提示注入;
  • 会话稳定之后出现的新目的地;
  • 远高于基线的出站流量。

一分钟内超过阈值时,它会记录一项发现(Alert),或切断 VM 的网络、将它暂停并询问你要怎么做(Quarantine)。见护栏 › 代理看门狗。它默认关闭;没有它时,篡改警报仍会记录在安全时间线与 OCSF 日志中。

组织边界

组织可以设置一道边界:任何机器的 OpenShell 策略所能允许的上限。在已注册的 Mac 上它来自 bromure.io(每十分钟更新一次,并缓存供离线使用),或来自这台 Mac 上的一个文件(沙盒策略 › Governance › Organization boundary)。每次更改策略的存储,不论来自设置、Sandbox 抽屉、策略顾问或 API,都会与它比对。装有 NVIDIA 的 openshell-prover 时 Bromure 会用它,否则用自己的检查。

  • 在编辑器中,输入时会显示绿色或红色的判定(“within boundary”,或附上它会允许之事例的“exceeds boundary: …”),而存储会等到策略符合为止。
  • 在其他地方,超出边界的存储会以“outside the organization boundary — …”被拒绝。
  • 先前存储、如今已不符合的策略会失败即关闭:只有 Bromure 自己添加的规则仍连得到。

通过同一条通道,组织还可以:

  • 设置新 OpenShell 机器的默认策略;
  • 要求 Enforce binaries 或严格凭据;
  • 设置内核哨兵的最低模式;
  • 为策略顾问的模式设上限;
  • 要求把代理的推理连同网络溯源链一起上传。

请参阅企业版。

每次更改策略的存储,也都会连同更改来源保留在策略的 Revision history 中;恢复 可以取回任何版本。

相关章节