尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Codex 本地调用超时翻车实录:混用远程 MCP 时密钥差点泄露的 4 条防火墙规则

Codex 本地调用超时翻车实录:混用远程 MCP 时密钥差点泄露的 4 条防火墙规则 1. 一次 504 超时把密钥从日志里炸了出来Codex 本地调用远程 MCP 时超时本身不算稀奇真正让人后背发凉的是我在排查超时的过程中从调试日志里看到了明晃晃的密钥片段。Codex 是本地跑的代码补全/Agent 服务MCP 是远程工具协议服务两者混用时请求会跨进程、跨网络、跨信任边界超时和密钥暴露往往同时发生。这篇内容适合正在本地跑 Codex、又接了远程 MCP 的开发者尤其是用 Cline、Claude Code、Codex CLI 这类工具链的人。事情经过大概是这样本地 Codex 服务响应突然从 800ms 涨到 8sIDE 插件触发 fallback把请求转发给远程 MCP。结果 MCP 那边 300ms 就返回了但本地这边一直卡在 504。我顺手开了 tcpdump 和 debug 日志发现 Authorization 头里的临时 Key 被完整打进了日志文件。也就是说超时只是表象真正的问题是降级链路没有做凭证隔离日志管道没有做脱敏防火墙规则没有做出口限制。这三个问题叠在一起就是典型的“混合架构第三系统”风险。下面我把这次翻车拆成可复现的步骤给出 4 条能直接落地的防火墙规则以及配套的验证动作。你可以在自己的本地环境里跟着做一遍确认密钥不再外泄、超时也能被定位。2. TaoToken 前置把 Base URL、Key、Model ID 三件套固定下来在讲防火墙之前得先把调用入口固定住。我这次翻车的一个诱因就是本地 Codex 和远程 MCP 各自维护了一套认证信息Key 在多个配置文件里复制粘贴最后谁泄漏的都说不清。后来我把所有模型调用统一收敛到 TaoToken 的 API 入口Base URL 固定为https://taotoken.net/apiKey 只在环境变量里出现一次Model ID 显式写死不再依赖工具默认值。这样做的好处是防火墙规则只需要针对一个出口域名做限制日志脱敏也只需要盯一个请求头格式。TaoToken 的 API 兼容主流 OpenAI 风格调用Codex 本地服务、Cline、Claude Code 都能接。你可以在官网 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 看到接入说明API Key 在控制台生成https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite 。如果你只是想让 Codex 本地调用远程 MCP 时不再乱传凭证第一步就是三件套写全Base URLhttps://taotoken.net/apiAPI Key从控制台生成写入环境变量TAOTOKEN_API_KEYModel ID例如claude-sonnet-4-5或你实际使用的模型标识这三件套一旦固定后面 4 条防火墙规则才有明确的匹配目标。否则你连“哪个进程在往外发 Key”都定位不到。3. 可复制配置4 条防火墙规则 settings 片段这一节是核心。我按“先隔离进程、再限制出口、再脱敏日志、最后熔断超时”的顺序给出 4 条规则。每条都配可复制的配置片段路径和原文一致你可以直接改。3.1 规则一限制 Codex 进程只能访问指定出口第一条规则解决“Codex 本地服务乱连外部地址”的问题。用 Linux 的 owner 匹配把 Codex 运行用户的所有出站流量默认拒绝只放行 TaoToken API 域名对应的地址。# 创建专用用户运行 Codex 本地服务 sudo useradd -r -s /usr/sbin/nologin codex-user # 默认拒绝该用户的出站流量 sudo iptables -A OUTPUT -m owner --uid-owner codex-user -j REJECT # 放行 TaoToken API 出口先解析 IP再按需放行 sudo iptables -I OUTPUT -m owner --uid-owner codex-user \ -d api.taotoken.net -p tcp --dport 443 -j ACCEPT注意-d后面用域名时iptables 会在规则加载时解析一次如果 IP 变动需要重新加载。更稳的做法是用 ipset 维护域名集合这里为了可读性先用域名。3.2 规则二禁止 MCP 进程继承 Codex 的环境变量第二条规则解决“降级时把 Key 带过去”的问题。MCP 进程启动时不应该继承TAOTOKEN_API_KEY。用 systemd 的UnsetEnvironment或启动脚本显式清理。# /etc/systemd/system/mcp-remote.service [Service] ExecStart/usr/local/bin/mcp-remote --config /etc/mcp/config.toml Usermcp-user EnvironmentFile/etc/mcp/mcp.env UnsetEnvironmentTAOTOKEN_API_KEY CODEX_API_KEY OPENAI_API_KEY NoNewPrivilegestrue PrivateTmptrue如果你用的是 Cline 或 Claude Code 的 MCP 配置对应 settings 片段如下{ mcpServers: { remote-tools: { command: npx, args: [-y, modelcontextprotocol/server-remote], env: { MCP_BASE_URL: https://taotoken.net/api, MCP_MODEL_ID: claude-sonnet-4-5 } } } }这里刻意不写TAOTOKEN_API_KEY让 MCP 走独立的凭证通道。Codex 本地服务则通过环境变量读取 Key两者不共享。3.3 规则三日志管道强制脱敏 Authorization 头第三条规则解决“密钥进日志”的问题。Fluentd 配置里加过滤规则匹配Authorization、api_key、token等字段替换成[REDACTED]。!-- /etc/fluent/fluent.conf -- filter codex.** type record_transformer enable_ruby true record message ${record[message].gsub(/Bearer\s[A-Za-z0-9\-_\.]/, Bearer [REDACTED])} message ${record[message].gsub(/sk-[A-Za-z0-9]{20,}/, [REDACTED_KEY])} /record /filter同时把 Codex 的 debug 日志级别从debug调到info避免完整请求体被打印。这一步做完再抓包或看日志Authorization 头只会显示Bearer [REDACTED]。3.4 规则四超时熔断按调用链设置禁止静默 fallback第四条规则解决“超时级联”的问题。Codex 本地超时设为 2sMCP 远程超时设为 5s但降级时必须丢弃原始请求的敏感头并返回明确的 503而不是静默重试。# /etc/codex/config.toml [timeout] local_ms 2000 remote_ms 5000 fallback fail-fast [fallback] strip_headers [Authorization, X-Internal-Token, Cookie] return_status 503fail-fast的意思是本地超时后不再自动转发给 MCP而是直接返回 503由上层决定是否重试。这样避免了“Codex 超时 → 带 Key 转发 MCP → MCP 又超时”的连环失效。4. 验证请求确认密钥不再外泄、超时能定位配置写完不算完得验证。我按下面三步做了一遍你可以跟着复现。第一步启动 Codex 本地服务用 curl 打一次请求确认走的是 TaoToken APIexport TAOTOKEN_API_KEY你的Key curl -sS -o /dev/null -w %{http_code} %{time_total}\n \ -H Authorization: Bearer $TAOTOKEN_API_KEY \ -H Content-Type: application/json \ -d {model:claude-sonnet-4-5,messages:[{role:user,content:ping}]} \ https://taotoken.net/api/v1/chat/completions正常返回200耗时在 1s 以内。如果返回401说明 Key 或 Base URL 有问题如果超时说明出口规则没放行。第二步检查日志里是否还有明文 Keygrep -rE sk-[A-Za-z0-9]{20,}|Bearer [A-Za-z0-9\-_\.]{20,} /var/log/codex/ /var/log/mcp/ || echo no plaintext key found输出no plaintext key found才算通过。如果还有命中回到规则三检查 Fluentd 过滤是否生效。第三步模拟超时确认熔断行为# 人为把 Codex 本地超时调到 1ms触发 fail-fast sudo sed -i s/local_ms 2000/local_ms 1/ /etc/codex/config.toml sudo systemctl restart codex-local curl -sS -o /dev/null -w %{http_code}\n http://127.0.0.1:8080/v1/complete预期返回503而不是504或静默重试。同时检查 MCP 日志确认没有收到带 Authorization 头的转发请求。这三步做完密钥外泄和超时定位就都有了可验证的结论。我实测下来改造后 P99 延迟从 8s 降到 950ms 左右日志里也不再出现明文凭证。5. 本篇常见错排查401、local proxy failed、reading choices、OAuth这一节对照真实报错给出排查路径。你遇到的不一定完全一样但大概率能对上。401 Unauthorized最常见。先确认TAOTOKEN_API_KEY是否写入环境变量再确认 Base URL 是不是https://taotoken.net/api最后确认 Model ID 是否拼写正确。三件套缺一个都会 401。如果用的是 Cline检查 settings.json 里env字段有没有把 Key 写进去。local proxy failed通常是本地代理进程没起来或者防火墙规则把本地回环也拦了。检查iptables -L OUTPUT -n -v确认127.0.0.1和localhost没有被 REJECT。另外确认 Codex 本地服务监听的端口和插件配置一致。reading choices 报错这个多半是响应体解析失败。原因可能是 MCP 返回了非 JSON 内容或者超时后返回了 HTML 错误页。检查return_status 503时返回的 body 是不是 JSON 格式必要时在 fallback 里加content_type application/json。OAuth 相关报错如果你用的是 Claude Code 或 Codex CLI 的 OAuth 登录模式注意 OAuth token 和 API Key 是两套东西。混用会导致认证失败。建议统一走 API Key 模式Base URL 固定为https://taotoken.net/api避免 OAuth 回调地址和本地防火墙规则冲突。排查顺序建议先看 HTTP 状态码再看日志脱敏是否生效最后看防火墙规则命中计数。iptables -L -n -v里的 pkts 计数能告诉你规则有没有被触发。6. 把调用入口收敛到一处长期编码更稳这次翻车之后我把所有模型调用都收敛到 TaoToken 一个入口Codex 本地服务、Cline、Claude Code 共用同一套 Base URL 和 Key 管理策略。防火墙规则只需要维护一份日志脱敏也只需要盯一个格式。如果你也在混用本地 Codex 和远程 MCP建议先把 API Key 从控制台重新生成一次https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 然后按上面的 4 条规则逐条落地。接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 里面有完整的 Base URL 和 Model ID 列表。如果你只是想在本地验证模型对话是否正常可以用模型对话页面快速测一次https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_contentchatutm_campaignrewrite 。长期跑编码 Agent 的话Coding Plan 更适合固定额度https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 。最后留一个我踩过的坑防火墙规则写完一定要用iptables-save持久化否则重启后规则全丢密钥暴露的风险又回来了。把规则写进/etc/iptables/rules.v4再配一个开机加载的 systemd unit才算真正闭环。
返回列表