OpenAI Codex · 程式碼模式

網路與憑證代理:模型看不見的那把鑰匙

出站要過幾道門。真 token 不進子行程。模型看見的是假值和一句 403。

課程目標讀完能說清三件事。域名怎麼被判:先 deny,再本地,最後 allowlist。真 token 在 spawn 前換成同形狀假值,出站時代理按 host 換回去。不肯走代理的客戶端,會被作業系統沙箱按進同一條漏斗。
先玩一遍 · 一個帶憑證的請求過代理
換域名、換方法,看請求在哪一關停下,假值在哪一步換成真值
域名
方法
模式
先看假值怎麼換成真值。再換成 POST,或讓 Go 繞過代理。
客戶端
這次請求
GET https://api.github.com/repos/you/app/issues
Authorization
Bearer github_pat_dmy8f3a2
關卡 1 · 沙箱待命
只放行代理埠。直連 loopback 在這一關停。
關卡 2 · 域名待命
先 deny,再本地或私有,最後 allowlist。
關卡 3 · 方法待命
Limited 只放 GET、HEAD、OPTIONS。
關卡 4 · 憑證待命
請求頭帶著假值,才換成經紀裡的真 token。
子行程環境GH_TOKEN = github_pat_dmy8f3a2
經紀記憶體真 token 鎖在這裡
模型上下文看不見真值
點播放,看這個請求依次走過四道關,假值在哪一步換成真值。
邏輯軌跡 · 動畫每一步對應原始碼裡的哪一段
  1. 拉起命令前,把真 token 換成同形狀假值credential_broker.rs L92
  2. 沙箱只放行代理埠,直連 loopback 到此為止seatbelt.rs L325
  3. 解析 host,deny 先判且恆勝runtime.rs L553
  4. 本地字面量沒有精確允許,按私有地址攔runtime.rs L578
  5. 域名解析到非公網,即便在名單裡也攔runtime.rs L582
  6. allowlist 為空或未命中,回 NotAllowedruntime.rs L598
  7. 只有 NotAllowed 才問決策器network_policy.rs L349
  8. Limited 下非 GET、HEAD、OPTIONS 就攔config.rs L311
  9. HTTPS 要看見內層方法,MITM 缺失就攔http_proxy.rs L312
  10. 請求頭帶著假值,才注入對應的真憑證mitm.rs L301
點播放,看這個請求依次走過四道關,假值在哪一步換成真值。
域名先判deny 恆勝。本地字面量要精確允許。解析到內網的名字,寫進名單也過不了。
真鑰匙不出經紀子行程環境和模型上下文始終是假值。真 token 只在出站那一步被換上去。
沙箱是漏斗口Go 繞過代理時,策略函式不會跑。作業系統只放行代理埠,直連在第一關就停。
教學示意:名單、假值形狀與解析結果為課程化設定。邏輯軌跡右側行號對應 openai/codex 倉庫 commit 4f39251a01。
思路一 · 域名怎麼被判
它解決什麼問題

你讓模型去 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 的域名,精確寫進名單也攔。本地字面量要精確寫上 localhost127.0.0.1,通配 * 不算。

出處:codex-rs/network-proxy/src/policy.rs 第 321 至 331 行;codex-rs/network-proxy/src/runtime.rs 第 1028 至 1043 行

解析 host 失敗也攔 1 deny 命中即停 403 denylist 2 本地或私有 字面量要精確 403 allowlist 3 allowlist 空名單也攔
教學化結構圖:三步順序固定。Denied 和 NotAllowedLocal 不會去問決策器。

被攔時回給命令行程 403,頭是 x-proxy-error,正文是一句人話。not_allowednot_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_TOKENOPENAI_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 行

模型 看不見真 token 子行程環境 同形狀假值 代理 MITM 按 host 換真值 上游 真 token 真值只在代理行程記憶體裡出現,不進 rollout,也不進模型下一輪上下文
教學化時序:假值走全程可見面,真值只在出站那一跳出現。
子行程口袋裡是假票根,櫃檯後面鎖著真票。
為什麼長期成立

出網策略寫錯,模型還能憑 403 改主意。憑證進上下文,撤銷成本高。所以真值在 spawn 前拿走,回填發生在代理行程記憶體裡。假值保持形狀,是為了讓校驗格式的客戶端還能啟動。

思路三 · 沙箱把流量按進代理
它解決什麼問題

Go 的 net/http 對 loopback 繞過 HTTP_PROXY。你以為流量進了代理,其實它直連 127.0.0.1。本地有管理口、docker socket。策略檔案寫了名單,請求根本沒走到那一層。

思路是什麼

沙箱只放行代理埠。macOS 受限 Seatbelt 只寫 localhost:{port}。Linux 走 ProxyOnlyallow_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 把閥門放在所有子行程出口,所以要和沙箱綁在一起。

兩側均已核對原始碼 · 2026-08-22 · 出處:web_fetch/domain.rs 第 110 至 144 行;web_fetch/ssrf.rs 第 17 至 19 行

DSH:配置只存變數名,spawn 時按鍵名擦環境

DSH 把金鑰做成引用。設定檔案只帶環境變數名,provider 在每次操作時 resolve。配置表面從不看見值。

子行程另有一層擦除。名字像 KEYTOKEN 的鍵和所有 DSH_* 會被丟掉。它擋得住配置檔案裡出現真值。模型若還能 printenv,除非 spawn 用了這層擦除,環境裡仍可能有真 token。

兩側均已核對原始碼 · 2026-08-22 · 出處:packages/credentials/credentials/src/index.ts 第 1 至 7 行;packages/subprocess/subprocess/src/index.ts 第 60 至 66 行 · DSH · 憑據每次現取
課堂練習
01

星號放行 localhost 嗎

allow_local_binding = false,allowlist 只有 *。對 127.0.0.1 調一次判定,期望是什麼。對公網 IP 8.8.8.8 再調一次,期望又是什麼。

提示:本地字面量拒絕通配。allowlist 編譯顯式允許全域 *,denylist 編譯拒絕它。

Takeaway:出站預設拒絕,deny 恆勝,內網要精確開門。真金鑰不要進模型可見的環境,spawn 前換成假值,出站再換。代理擋不住繞過它的客戶端,下一層必須是只放行代理埠的沙箱。