网络与凭据代理:模型看不见的那把钥匙
出站要过几道门。真 token 不进子进程。模型看见的是假值和一句 403。
- 拉起命令前,把真 token 换成同形状假值credential_broker.rs L92
- 沙箱只放行代理端口,直连 loopback 到此为止seatbelt.rs L325
- 解析 host,deny 先判且恒胜runtime.rs L553
- 本地字面量没有精确允许,按私有地址拦runtime.rs L578
- 域名解析到非公网,即便在名单里也拦runtime.rs L582
- allowlist 为空或未命中,回 NotAllowedruntime.rs L598
- 只有 NotAllowed 才问决策器network_policy.rs L349
- Limited 下非 GET、HEAD、OPTIONS 就拦config.rs L311
- HTTPS 要看见内层方法,MITM 缺失就拦http_proxy.rs L312
- 请求头带着假值,才注入对应的真凭据mitm.rs L301
你让模型去 GitHub 提一个 issue。它写出 curl,环境里有真 token。下一秒域名换成 evil.example,或者打到 169.254.169.254。只拦「不在名单里的域名」,deny 条目和内网地址会漏。空名单若默认放行,忘写配置等于全开。
判定顺序写死。先 deny,再本地或私有,最后 allowlist。空名单和没命中,都拦。决策器只能翻 allowlist 漏掉的案,翻不了 denylist,也翻不了本地网段。
出处:codex-rs/network-proxy/src/runtime.rs 第 549 至 552 行;codex-rs/network-proxy/src/network_policy.rs 第 346 至 378 行;codex-rs/network-proxy/README.md 第 161 至 162 行
*.example.com 不含 apex,**.example.com 才含。解析到私有 IP 的域名,精确写进名单也拦。本地字面量要精确写上 localhost 或 127.0.0.1,通配 * 不算。
出处:codex-rs/network-proxy/src/policy.rs 第 321 至 331 行;codex-rs/network-proxy/src/runtime.rs 第 1028 至 1043 行
被拦时回给命令进程 403,头是 x-proxy-error,正文是一句人话。not_allowed 和 not_allowed_local 对外都叫 blocked-by-allowlist,正文才能分开「不在名单」和「沙箱拦了本地」。这条 403 变成 exec 输出喂回模型,采样循环不会停。
出处:codex-rs/network-proxy/src/responses.rs 第 52 至 83 行
同一 pattern 既 allow 又 deny,有效值取大的。枚举序是 None < Allow < Deny。
出处:codex-rs/network-proxy/src/config.rs 第 19 至 27 行
默认拒绝、deny 恒胜、内网要显式开门,这是 SSRF 防护的通用形状。换语言重写,该问的还是同一组问题:没写名单时怎么办,冲突时谁赢,解析到内网的名字算不算放行。
token 跟着命令进 rollout。下一轮模型还能看见它,再下一轮可能进日志。凭据一旦进入模型上下文,后面每一层脱敏都是补救。
拉起命令前,凭据经纪把 GH_TOKEN、OPENAI_API_KEY 换成同长度、同前缀的假值。模型若 printenv,看见的是假的。假值进上下文,对上游无用。
出处:codex-rs/network-proxy/src/credential_broker.rs 第 92 至 119 行
出站时 MITM 按 host 过滤。请求头里必须带着假值,才换成真值。hook 的剥头、注头发生在经纪之后,可以剥掉刚注入的 Authorization。用户改过的环境值不还原。标记当不可信输入。经纪只覆盖 GitHub 和 OpenAI。
出处:codex-rs/network-proxy/src/mitm.rs 第 297 至 302 行
出网策略写错,模型还能凭 403 改主意。凭据进上下文,撤销成本高。所以真值在 spawn 前拿走,回填发生在代理进程内存里。假值保持形状,是为了让校验格式的客户端还能启动。
Go 的 net/http 对 loopback 绕过 HTTP_PROXY。你以为流量进了代理,其实它直连 127.0.0.1。本地有管理口、docker socket。策略文件写了名单,请求根本没走到那一层。
沙箱只放行代理端口。macOS 受限 Seatbelt 只写 localhost:{port}。Linux 走 ProxyOnly。allow_local_binding 为假时,NO_PROXY 写成空串,loopback 字面量也必须走代理。打开本地绑定等于承认 loopback 不再经过 allowlist。
出处:codex-rs/sandboxing/src/seatbelt.rs 第 309 至 336 行;codex-rs/network-proxy/src/proxy.rs 第 450 至 460 行
应用层代理挡不住不肯走代理的客户端。下一层必须是操作系统或防火墙,只留一个口。三道门叠在一起才构成托管网络。单独一道都挡不住另一道漏掉的面。
Grok:阀门在工具入口,loopback 默认放行
Grok 的 web_fetch 在工具内部做域名名单和解析后 IP 检查。空名单拦所有 URL,和 Codex 的 allowlist-first 同方向。path 还可以写成前缀,收窄到某一段文档。
SSRF 检查拦 RFC1918、link-local、CGNAT。loopback 明确放行,注释写 local development。bash 里的 curl 不走这张名单。Codex 把阀门放在所有子进程出口,所以要和沙箱绑在一起。
web_fetch/domain.rs 第 110 至 144 行;web_fetch/ssrf.rs 第 17 至 19 行
DSH:配置只存变量名,spawn 时按键名擦环境
DSH 把密钥做成引用。设置文件只带环境变量名,provider 在每次操作时 resolve。配置表面从不看见值。
子进程另有一层擦除。名字像 KEY、TOKEN 的键和所有 DSH_* 会被丢掉。它挡得住配置文件里出现真值。模型若还能 printenv,除非 spawn 用了这层擦除,环境里仍可能有真 token。
packages/credentials/credentials/src/index.ts 第 1 至 7 行;packages/subprocess/subprocess/src/index.ts 第 60 至 66 行 · DSH · 凭据每次现取
星号放行 localhost 吗
allow_local_binding = false,allowlist 只有 *。对 127.0.0.1 调一次判定,期望是什么。对公网 IP 8.8.8.8 再调一次,期望又是什么。
提示:本地字面量拒绝通配。allowlist 编译显式允许全局 *,denylist 编译拒绝它。