护栏

护栏是 Bromure Agentic Coding 在宿主机侧对工作区凭据使用方式进行的控制。正如该窗格自身所述:护栏管理此工作区已配置凭据的使用方式。使用前询问会在会话中首次使用某个凭据时弹出宿主机侧的确认。写入策略会在传输过程中剥离或阻止破坏性操作——由代理服务器强制执行,因此虚拟机中被入侵的代理无法绕过它。只有你已配置的凭据才会显示在这里。

工作区设置编辑器的护栏窗格:每个凭据一行并带有使用前询问复选框,随后是“出站连接”出站防火墙区块,含未匹配流量的允许/拒绝控件、添加规则按钮、pf 格式的可展开区域,以及禁用透明拦截开关

该窗格为每个已配置凭据显示一行,其顺序与凭据窗格中列出的顺序一致。每一行显示凭据的标题和宿主机,并带有两个控件:

  • 一个使用前询问复选框(控件本身标注为需要批准才能使用)——每个凭据的许可关卡,从凭据窗格移至此处。
  • 一个内嵌的写入策略选择器,仅在凭据对应的服务支持时显示。

如果工作区没有凭据,该窗格会显示空状态——没有可保护的凭据——引导你返回凭据窗格。强制执行细节、错误格式和审计追踪记录在提示注入与护栏深入解析中有介绍。

使用前询问

每一行都有一个使用前询问复选框,默认关闭。当它开启时,在会话中首次使用该凭据时,代理服务器会暂停并弹出一个许可对话框,提供有时限的授权——5 分钟、1 小时,或本会话剩余时间——或不允许。授权仅存于内存中,会在会话窗口关闭时被清除,每个实时决策都会列在**窗口 → 凭据批准…**中。许可模型、并发提示的合并处理以及批准窗口在凭据与传输边界中有介绍。

此复选框适用于任何凭据,包括纯 API 密钥、SSH 密钥和手动令牌——没有写入策略的行只显示此控件。

写入策略

当凭据对应的服务能够对自身流量进行分类时,该行还会显示一个写入策略选择器。它会在以下情况下出现:

  • Git 代码托管平台——GitHub、GitLab、Bitbucket 令牌。
  • AWS 凭据。
  • DigitalOcean 令牌。
  • 容器镜像仓库(Docker Hub、ghcr.io 等)。
  • Kubernetes 上下文。
  • 数据库端点(MongoDB、ClickHouse、Elasticsearch)。

每种写入策略都提供相同的四种模式:

模式行为
关闭不进行过滤。
写入前提示读取操作直接通过。每次写入都会暂停,弹出宿主机侧的许可对话框,显示确切的操作,并提供有时限的授权(见下文)。
阻止破坏性操作删除、drop 和终止操作被阻止;创建和更新操作通过。
只读每次变更都被阻止;只有读取操作通过。

新工作区在每个支持策略的服务上默认使用写入前提示(通过偏好设置模板)。在护栏功能存在之前创建的工作区——或 JSON 中省略了该字段的配置文件——会被解码为关闭

注意: 写入策略是按服务的,而非按凭据。同一服务的两个凭据——比如两个 GitHub 令牌——共享一个策略,因此在任一行更改选择器都会同时更改两者。

注意: 护栏对操作进行分类;它不隐藏凭据。凭据本身仍会按凭据下的配置由代理服务器注入和替换。将只读写入策略与同一行的使用前询问复选框结合使用,可实现纵深防御。

各服务如何对调用进行分类

服务范围分类
Kubernetes来自此工作区 kubeconfig 的 Kubernetes API 服务器GET/HEAD/OPTIONS = 读取;DELETE = 破坏性(包括 deletecollection);其他动词 = 写入。被阻止的调用会返回一个 Kubernetes Status 403 JSON,kubectl 能够清晰地呈现它。
AWS所有 *.amazonaws.com 宿主机来自 X-Amz-Target 请求头的操作名称(如 DynamoDB 和 Lambda 等 JSON 协议服务)或 Action= 表单参数(如 EC2、IAM、SQS 等查询协议服务)按前缀分类:Delete*/Terminate*/Remove*/Purge*/Destroy*/Deregister*/Revoke* = 破坏性;Get*/List*/Describe* 及类似前缀 = 读取。对于 S3 和 REST 风格的请求,则回退到使用 HTTP 方法。被阻止的调用会返回一个 AccessDeniedException 主体。
DigitalOceanapi.digitalocean.com*.digitalocean.comHTTP 方法:DELETE = 破坏性;GET/HEAD = 读取。
容器镜像仓库在凭据下配置的镜像仓库,以及 Docker Hub 端点拉取(GET)= 读取;推送(PUT/POST)= 写入;DELETE = 破坏性。被阻止的调用会返回一个镜像仓库风格的 DENIED 错误主体。
GitHubgithub.com REST API 和基于 HTTPS 的 gitREST 基于方法。git pushgit-receive-pack)算作写入——在只读下被阻止,在写入前提示下会提示;git fetchgit-upload-pack)始终为读取。
GitLabgitlab.com REST API 和基于 HTTPS 的 git与 GitHub 相同的分类逻辑。
Bitbucketbitbucket.org REST API 和基于 HTTPS 的 git与 GitHub 相同的分类逻辑。

数据库端点按引擎进行分类:

引擎读取写入破坏性
MongoDB (Atlas Data API)findfindOneaggregateinsertupdatereplacedeleteOnedeleteMany
ClickHouseSELECTSHOWDESCRIBEEXPLAINWITH 等为首关键字的 SQL …INSERTCREATEALTERDROPTRUNCATEDELETE,以及 ALTER … DELETE / DROP COLUMN / DROP PARTITION / CLEAR
Elasticsearch_search_msearch_count_mget_sql 以及其他查询端点(即使通过 POST_bulk_update、文档索引DELETE_delete_by_query

当设置了某个策略但没有可供其应用范围的对象时——Kubernetes 上下文没有 kubeconfig、数据库端点没有设置宿主机——会出现橙色内嵌警告,因为此时护栏没有可应用的宿主机。

注意: 对于 ClickHouse,如果请求中看不到 SQL 文本,只读会阻止该请求(Bromure 无法证明它是读取),而阻止破坏性操作会放行(它采取放行的默认策略)。

许可对话框(写入前提示)

写入前提示模式下,读取操作静默通过,每次写入都会暂停并弹出一个标题为 Allow write on "<scope>" from workspace "<name>"? 的宿主机对话框。对话框主体逐字显示确切的操作——数据库的字面 SQL 语句,或 REST 调用的 METHOD /path——因此你批准的是实际将要运行的内容,而非摘要。按钮包括:

  • 允许 15 分钟(默认按钮)
  • 允许一次——刻意不创建任何授权,因此紧接着的下一次写入会重新提示。适用于对一个话痨的代理逐次写入进行审计。
  • 允许本会话剩余时间
  • 不允许——代理会收到与阻止模式所产生的相同的硬错误。该拒绝会被记住 60 秒,这样循环重试同一次写入的代理不会每秒都重新提示。

授权按工作区和协议范围划定:一个 Kubernetes API 宿主机、整个 AWS、一个容器镜像仓库、每个 git 代码托管平台整体,或一个数据库宿主机。在一个宿主机上允许 ClickHouse 写入不会在其他任何地方授予任何权限。并发的相同写入会合并到单个对话框上,所有决策仅存于内存中——会话范围的授权会在会话拆除时被清除。

当工作区通过 SSH 或 CLI 以无界面方式驱动时,相同的四个选项会作为文本提示出现在工作区的 tmux 中;不回答即视为拒绝。

注意: 护栏的写入授权不会列在任何窗口中。凭据批准窗口(窗口菜单 → 凭据批准…)仅显示凭据许可决策——即使用前询问的授权;写入策略授权会按其自身的时钟或在会话拆除时到期。

出站防火墙

在凭据各行下方,该窗格容纳了工作区的出站连接防火墙:一张有序的规则表格,管辖虚拟机究竟可以打开哪些连接——任何协议、任何目的地,而不只是护栏会分类的那些服务。规则自上而下匹配,首个匹配者胜出;一个分段式的未匹配流量控件为所有没有规则匹配的流量选定默认行为:允许(新工作区的默认值),或拒绝以采取锁定式的允许列表姿态。

每条规则有五个部分:

取值含义
动作allow / deny匹配的流量会如何处理。
协议tcp / udp / web / anyweb 指 TCP 之上的 HTTP(S)——唯一其规则还能限制请求方法的协议。
主机 / CIDRany、一个主机名,或一个 IPv4 CIDR主机名按后缀匹配:example.com 覆盖顶点域名及其所有子域名;*.example.com 只覆盖子域名。裸 IP 视为 /32。
端口any、单个端口、范围(8000-8999)或列表留空表示任意端口。
方法HTTP 动词,仅限 web 规则allow 搭配时是允许列表(GET,POST——只有这些动词能通过);与 deny 搭配时是阻止列表(PUT,DELETE——这些动词被拒绝,其余放行)。read-only 是 GET/HEAD/OPTIONS 的简写。

底部的可展开区域会以规范的 pf 格式显示同一套规则——每行一条规则,以 default allowdefault deny 结尾——这正是规则的存储形式,也是 CLI 与受管配置文件所携带的形式:

allow tcp api.github.com:443
allow web api.example.com GET,POST     # 只有这些动词能到达该站点
deny  web api.internal PUT,DELETE      # 阻止这些动词,其余放行
deny  udp any:53
deny  any 10.0.0.0/8
default deny

强制执行在宿主机侧且分层进行,因此被入侵的代理无法绕过它:虚拟网络交换机按目的 IP 和 DNS 嗅探得到的主机名,对每一条流量应用规则(涵盖所有协议,包括明文 TCP 和 UDP);MiTM 代理再按 TLS 服务器名,并针对 web 规则按单个 HTTP 方法,二次应用这些规则。正是这个方法维度,让工作区能够读取一个它无权写入的 API:allow web api.example.com GET,POST 在传输层给予代理查询能力而无变更能力,无论虚拟机内是哪个工具发出该请求。

规则的修改会立即应用到正在运行的会话——保存工作区即可刷新实时策略而无需重启,无界面会话同样如此。每一项判定都会记录为安全时间线窗口中的一行 Firewall 记录;在已注册的安装上,则会成为组织事件流中的一个 egress.firewall 事件。

注意: 本地推理在设计上即被豁免:针对宿主机推理端点(bromure.llm)的一条 allow 规则会被前置到你的规则之前,因此即便是 default deny 也无法切断代理与本地模型的连接。该流量绝不会离开你的 Mac。

透明拦截

如果虚拟机可以直接忽略代理,防火墙就只是建议性的;因此它现在再也做不到了:虚拟交换机会透明地把虚拟机的 80 端口和 443 端口流量转入宿主机代理——无需任何环境变量,客户机也没有任何东西可以取消设置。80 端口上的明文 HTTP 会与 HTTPS 一样被拦截和检查。此功能对每个工作区都默认开启。

逃生出口是规则表格下方的禁用透明拦截开关。打开它会让交换机停止转发 :80/:443,于是护栏和防火墙的 web 规则就只能看到那些自愿使用代理环境变量的流量——请仅在某个工作区确实会因拦截而无法工作时使用(例如内置放行列表未覆盖的大量证书固定),并在其开启期间把防火墙视为仅具建议性质。

调用被阻止时代理看到的内容

被阻止的调用会返回一个协议相应的 403 风格错误主体——Kubernetes Status JSON、AWS AccessDeniedException、镜像仓库 DENIED 载荷——其消息以 "blocked by Bromure Guardrails" 结尾。代理看到的是一个可以报告的干净、普通的 API 失败,而非一个挂起的连接。(供应链阻止改用 HTTP 451,正是为了让两者一眼就能区分——见供应链。)

局限性

  • Git 强制推送在传输过程中无法与普通推送区分开来,因此阻止破坏性操作不会阻止它——只有只读写入前提示会对推送进行关卡控制。通过代码托管平台 REST API 进行的显式删除仍会被捕获。
  • Kubernetes 和容器镜像仓库策略仅应用于从工作区 kubeconfig 和已配置镜像仓库派生的宿主机;数据库策略需要在凭据下设置端点的宿主机。没有可应用范围对象的护栏不会过滤任何内容(该窗格会内嵌警告你)。
  • 写入策略涵盖上面列出的服务;发往其他主机的流量则由出站防火墙的主机/端口/方法规则管辖,而非语义分类。软件包下载见供应链,代理的 AI 流量见提示注入