ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

WuKongIM 云仿真分析:在本地用加密 Analysis Session 运行 Codex,彻底不落盘任何云端 AI 凭据

WuKongIM 云仿真分析:在本地用加密 Analysis Session 运行 Codex,彻底不落盘任何云端 AI 凭据
  • 即时通讯
  • 后端

【免费下载链接】WuKongIM

More than just IM 不只是即时通讯(IM)

项目地址:https://gitcode.com/gh_mirrors/wu/WuKongIM
点击查看免费下载

导读

本文围绕 WuKongIM 仓库中的架构决策记录 ADR-0037:Run Codex locally with an encrypted Analysis Session 展开,完整讲解其核心设计:云仿真(Cloud Simulation)的分析环节全部在操作者本地机器完成——本地 Codex CLI 通过 ChatGPT 订阅登录,云端只充当"身份中介"(cloud-identity broker),GitHub 与云厂商不保存、不执行任何 OpenAI API Key、Codexauth.json或 ChatGPT 会话。读完后你将掌握:Analysis Token 的签发与 RSA-OAEP 加密交接协议、本地 Codex 最小权限沙箱的构建方式、analyze.sh/finalize.sh的完整分析流程、结构化 Diagnosis Result 的语义,以及可选的独立修复工作流(Draft PR)如何与云资源清理解耦。

为什么要把 Codex 从云端拉回本地

在 ADR-0037 之前,仓库先后采纳过 0003、0005、0018、0019、0023、0027、0029、0030 等多条 ADR,它们共同构成了"云上分析、云上诊断、GitHub 持有 API 凭据"的早期方案。ADR-0037 一次性取代了这些决策,确立了新的边界:

  • GitHub 不持有OpenAI API Key、Codexauth.json或 ChatGPT 会话;
  • 云厂商不执行分析过程,只承载被分析的工作负载本身;
  • 本地机器负责推理、诊断与(可选的)代码修复;
  • 云端只做一件事:证明身份与存活证据,并把一次性的 Analysis Token 加密交给请求方。

这套设计的直接收益是:无论代码库还是云端流水线泄露,攻击者都无法拿到可复用的 AI 凭据;AI 分析消耗的是操作者自己的 ChatGPT 订阅额度,不依赖任何专用 API 凭据的托管与轮换。

Analysis Session 工作流:云端只当"身份中介"

ADR-0037 把整个分析会话定义为一条严格的六步流水线:

  1. Run Locator 校验:云端基于 provider inventory(云厂商资源清单)证明操作者请求的那个精确 Run(保留的 Run Locator)真实存在;
  2. GitHub OIDC 交换:用 GitHub Actions OIDC 换取一个短期、run 作用域的 Analysis Token;
  3. RSA-OAEP 加密交接:云端把该 Token 用操作者本地生成的临时公钥按RSA-OAEP SHA-256加密后交付;
  4. 准入窗口:云端只在 Token 过期前,向请求方客户端的公开/32地址开放 MCP 入口;
  5. 本地持有私钥:私钥始终留在本地进程内,Token 密文只有本地能解开;
  6. 精确校验:本地钉住 run 证书、检出实际部署的源 SHA,通过白名单只读 MCP 调用该版本的 Analysis Skill。

加密交接的源码落点

Token 交换协议的 HTTP 适配器位于 internal/access/cloudanalysismcp/token.go:NewTokenExchangeHandler只接受POST,先经Verify(GitHub OIDC 校验)再调Issue,返回access_token、token_type与expires_at。它的测试 token_test.go 验证了方法限制、缺失/非法身份返回 401、会话不可用返回 409 的 fail-closed 语义。

会话存储与签发在 cmd/wkanalysis/session.go:

  • Token 为 32 字节加密随机数,Base64 URL 编码;
  • 默认有效期45 分钟,且被"Run Lease 到期前 5 分钟"这一上限钳制;
  • 若 Run Lease 剩余不足 30 分钟则拒绝签发(errInvalidAnalysisSession);
  • 服务端只保存 token 的 SHA-256 摘要,签发时顺手清理已过期条目;
  • Token不可续期(non-renewable),Verify只接受仍存活、长度 ≥32 的摘要。

TestAnalysisSessionStoreIssuesNonRenewableLeaseBoundToken(session_test.go)直接验证了"租约 40 分钟 → Token 35 分钟"的钳制行为与过期即失效。

OIDC 身份校验的严格性

OIDC 校验器在 cmd/wkanalysis/oidc.go 中实现了精确声明匹配:除了 RS256 签名、Issuerhttps://token.actions.githubusercontent.com、Audience 与过期时间外,还逐一比对repository、ref、job_workflow_ref、environment以及两种 subject 之一。配置端(oidc.go)只接受两类被评审过的会话方言:

  • 云仿真:Audiencewukongim-cloud-sim:<run_id>,工作流cloud-sim-analyze.yml@refs/heads/<ref>,环境cloud-sim-analysis;
  • Chat Lifecycle 云租约:Audiencewukongim-cloud-lease:<run_id>,工作流cloud-lease-analyze.yml@refs/heads/<ref>,环境cloud-lease-provision。

测试 oidc_test.go 专门验证了"交叉环境""未评审工作流"等组合都会被拒绝,JWKS 公钥加载则遵循 fail-closed 且带 10 分钟缓存(oidc.go)。

网关进程:wkanalysis

Analysis 网关由 cmd/wkanalysis/main.go 组装:启用 OIDC 时创建analysisSessionStore并把sessions.Verify挂到网关的 MCP Token 校验器上,同时暴露POST /analysis/token、GET /healthz与GET /self-check(后者会主动探测 Prometheus、三个节点与 Manager 的就绪状态)。其运行期配置全部来自环境变量,核心参数如下:

环境变量默认值说明
WK_ANALYSIS_RUN_ID必填Run 身份,缺失即启动失败
WK_ANALYSIS_MCP_TOKEN/WK_ANALYSIS_GITHUB_OIDC_*二选一32 字节 MCP Token,或 GitHub OIDC 配置组
WK_ANALYSIS_SCENARIO_PATH必填生效场景文件,要求wkbench/v1且随机种子非零
WK_ANALYSIS_SCENARIO_DIGEST可选场景摘要校验,不匹配即拒绝启动
WK_ANALYSIS_RUN_LOCATOR_PATH可选Run Locator,须与WK_ANALYSIS_FAKE_INVENTORY_PATH成对出现
WK_ANALYSIS_RUN_EXPIRES_AT当前时间 + 2hRun Lease 到期,须距当前 ≥30 分钟
WK_ANALYSIS_TOKEN_EXPIRES_AT45 分钟(受租约钳制)仅非 OIDC 模式可用,不得超过租约前 5 分钟
WK_ANALYSIS_LISTEN_ADDR0.0.0.0:19092监听地址(MCP 端口 19092;Chat Lease 为 19444)
WK_ANALYSIS_TLS_CERT_FILE/WK_ANALYSIS_TLS_KEY_FILE空云 OIDC 模式强制要求 TLS 证书密钥
WK_ANALYSIS_NODE_API_URLS三个默认节点JSON 对象,必须同时包含节点 1、2、3
WK_ANALYSIS_PROVIDER/WK_ANALYSIS_REGION/WK_ANALYSIS_SOURCE_SHAlocal-compose/local/ 空若提供 Run Locator,须与 Locator 完全一致
WK_ANALYSIS_MANAGER_BASE_URLhttp://wk-node1:5301Manager 地址
WK_ANALYSIS_PROMETHEUS_BASE_URLhttp://prometheus:9090Prometheus 地址

配置解析的边界校验(如节点 URL 个数、过期时间先后、租约与 Locator 一致性)集中在 cmd/wkanalysis/config.go,并有 runtime_contract_test.go 逐项覆盖"缺场景、摘要不匹配、租约过短、节点 URL 非法"等拒绝路径。

本地编排入口:analyze.sh 与 finalize.sh

本地侧的核心编排脚本都位于 scripts/cloud-sim/,它们共享 local-runtime.sh 提供的有界进程组执行器wk_run_bounded:每条本地命令都落在独立进程组、带墙钟截止时间,超时统一返回 124,并在退出前强杀整个孤儿组——这是本地编排的安全地基。

analyze.sh:一次精确 Run 的完整分析

analyze.sh 的用法:

./scripts/cloud-sim/analyze.sh RUN_ID [options] # --repository OWNER/REPO 指定 GitHub 仓库(默认从当前 checkout 探测) # --chat-request-id REQUEST_ID 分析精确的 chat-lifecycle Cloud Lease 交接 # --diagnostic-focus TEXT 可选的受限诊断关注点(仅作为主题数据,不是指令) # --allow-fix-pr 兼容标志:修复延后到 provider 资源释放之后 # --result-file ABSOLUTE_PATH 原子写出结构化分析结果

一次本地前提:gh auth login+codex login(选择ChatGPT),无需任何 OpenAI API Key。Codex 版本要求 ≥ 0.140.0,脚本会从 PATH 与 ChatGPT App 内置路径中挑选最高兼容版本(WK_CODEX_BIN可显式覆盖),并校验登录态必须是Logged in using ChatGPT(analyze.sh 与 #L726-L750)。

主流程按顺序执行以下阶段:

  1. Inspect(preflight):通过 GitHub Actions 派发cloud-sim-analyze.yml的inspect操作,下载并校验 preflight 描述符。若云厂商已确认released(资源为空),直接输出"已自动销毁"并成功退出;unknown_run与insufficient_evidence则是不同的显式失败(analyze.sh);
  2. 生成临时密钥:openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:3072生成本地私钥(analyze.sh);
  3. 确认本机公网 IPv4:通过公网 echo 服务或同主机 Cloud View 状态接口探测,随后把client_ipv4与client_public_key作为prepare操作的输入派发——云端据此只向该/32开放入口。若同主机探测发现出口 IP 不一致,脚本会执行一次"close → rebind → prepare"重绑定(analyze.sh);
  4. 准备会话并解密 Token:下载session.json、encrypted-token.bin、pinned-ca.pem,校验 MCP URL 精确匹配https://<公网IP>:19092/mcp、CA 指纹为sha256:...,然后用私钥执行pkeyutl -decrypt -pkeyopt rsa_padding_mode:oaep -pkeyopt rsa_oaep_md:sha256解密 Token,解密成功后立即删除私钥与密文文件(analyze.sh);
  5. 健康探测:绕开代理直连(--noproxy '*')并使用钉住的 CA 证书,12 次尝试确认healthz返回.status=="ok"且run_id匹配(#L795-L825);
  6. 本地桥接:go build编译 cmd/wkcloudanalysisbridge,在127.0.0.1随机端口发布一个仅回环的 MCP 反向代理(#L827-L847);
  7. 检出精确源:确保本地已有source_sha(缺失则无凭据提示地静默 fetch 一次),git worktree add --detach检出部署版本,然后拒绝项目级 Codex 覆盖——若 worktree 中存在.codex/config.toml或.codex/hooks.json直接失败(#L849-L856,函数见 #L171-L179);
  8. 启动隔离的 Codex:env -i清空调用方环境,仅注入PATH、私有HOME、CODEX_HOME与WK_ANALYSIS_MCP_TOKEN;--ephemeral --ignore-user-config --ignore-rules --strict-config,并通过严格权限配置把文件系统限制为:minimal=read+ workspace 根只读、network.enabled=false、shell_environment_policy.inherit="none",MCP 服务器只允许 14 个白名单只读工具(#L863-L900);
  9. Schema 校验:用wkclouddiagnosis validate对 JSON Schema 做严格校验,并核对run_identity(run_id/source_sha/scenario_digest)与会话一致(#L934-L947),随后显式 close 会话、原子写出--result-file。
# 关键 Codex 调用(节选自 analyze.sh,字段语义不变) env -i PATH="$PATH" HOME="$analysis_home" CODEX_HOME="$local_codex_home" \ WK_ANALYSIS_MCP_TOKEN="$analysis_token" \ "$codex_bin" exec --ephemeral --ignore-user-config --ignore-rules --strict-config \ -C "$analysis_worktree" \ -c 'default_permissions="cloud-analysis"' \ -c 'permissions.cloud-analysis.filesystem={":minimal"="read",":workspace_roots"={"."="read"}}' \ -c 'permissions.cloud-analysis.network.enabled=false' \ -c 'shell_environment_policy.inherit="none"' \ -c 'shell_environment_policy.set={PATH=...,HOME=...}' \ -c 'mcp_servers.wukongim_cloud_analysis.url="http://127.0.0.1:PORT/mcp"' \ -c 'mcp_servers.wukongim_cloud_analysis.bearer_token_env_var="WK_ANALYSIS_MCP_TOKEN"' \ -c 'mcp_servers.wukongim_cloud_analysis.required=true' \ -c 'mcp_servers.wukongim_cloud_analysis.startup_timeout_sec=10' \ -c 'mcp_servers.wukongim_cloud_analysis.tool_timeout_sec=70' \ -c 'mcp_servers.wukongim_cloud_analysis.enabled_tools=[...14 个只读工具...]' \ --output-schema "$REPO_ROOT/.github/cloud-sim/diagnosis.schema.json" \ -o "$diagnosis_json" "$prompt"

本地桥接为何需要"钉住证书"

cmd/wkcloudanalysisbridge 的职责是让 Codex 只需连本地回环地址,由桥接转发到云端精确端点。它做了四重收紧:

  • 只接受 HTTPS 上游,且主机必须是一个全局公网 IPv4(拒绝私网、回环、链路本地、多播),端口只能是 19092 或 19444(main.go);
  • 证书精确钉住:TLS 握手时用 SHA-256 指纹比对叶子证书、校验有效期窗口与 IP SAN,而不是依赖系统信任链(main.go);
  • 剥离转发头:清除Forwarded/X-Forwarded-*,防止放大攻击面;
  • 回环监听:仅绑定127.0.0.1:0,不接收任何远程绑定参数。

其边界测试 main_boundary_test.go 验证了"指纹不匹配、IP SAN 错误、证书过期"分别失败,且错误响应绝不泄露上游细节(#L182-L206)。FLOW 文档 cmd/wkcloudanalysisbridge/FLOW.md 对边界做了补充说明。

Diagnosis Result:有结构、有身份、语义明确

Codex 的输出必须落到 .github/cloud-sim/diagnosis.schema.json 定义的wukongim/cloud-simulation-diagnosis/v1结构里,字段完全封闭(additionalProperties: false)。关键语义(由 analyze.sh 的 prompt 强制):

verdictseverityroot_cause_scope是否可修复
healthynonenone否
insufficient_evidencenoneunknown否
product_defect非 noneproduct是(唯一可修复)
infrastructure_interrupted非 noneinfrastructure否
scenario_invalid非 nonescenario否

observation_references中只有workload_inspect工具可携带state/status,且遵循"completed必须complete=true;其他工具一律为null"。这份结果是已完成命令(completed command)——无论 verdict 如何,只要身份匹配并通过 schema 校验就算成功完成;只有 Session preflight 失败、超时、schema 非法、身份不匹配才构成非零退出。健康与否只由结构化 verdict 确立。

Schema 校验器是 cmd/wkclouddiagnosis/main.go(wkclouddiagnosis validate FILE),底层走 internal/usecase/cloudanalysis 的DecodeDiagnosisResult。

finalize.sh:等待、重试、然后证明"账单已停"

Provision(供给)阶段还会发布一份最小的非诊断型 Finalization Schedule(wukongim/cloud-simulation-finalize/v1,含active_until/analysis_ready_at/expires_at三个有序截止时间)。finalize.sh 的流程:

./scripts/cloud-sim/finalize.sh RUN_ID [--repository OWNER/REPO] [--diagnostic-focus TEXT] [--allow-fix-pr] # 前提:gh auth login;全程无需 OpenAI API Key;保持本命令在本地运行
  1. 下载并校验 Finalization Schedule,wait_until等待第一个有界的分析时间窗;
  2. 复用同一个加密 Analysis Session 调用analyze.sh尝试终态分析;
  3. 若校验后的workload_inspect状态仍是in_progress且租约还能安全接纳下一次会话,则保留 run 并在 60 秒后重试(minimum_retry_lease_seconds=2100,即重试前必须给后续清理留足时间余量);
  4. 无论分析成功与否,随后必定执行精确 Cleanup:派发cloud-sim-cleanup.yml(带run_id与finalize-<hex>关联 ID)停止计费,再通过analyze.sh的 released 形态做独立 provider 释放验证,要求结构化结果满足.state=="released" && .provider.state=="released" && .provider.resources==[];
  5. 若 provider 已确认released(自动销毁),则打印终态提示并在 Codex 运行前成功退出——unknown_run与"活证据不可达"仍是两种不同的失败;
  6. 诊断失败或可选修复失败不会抑制清理;而 provider 已确认释放的 run 则直接终止、不再发起第二次清理请求。

中途 Ctrl-C(HUP/INT/TERM)也不会跳过计费兜底:信号处理器会把"精确清理"标记为必需,EXIT 陷阱确保清理与释放验证在退出前完成(finalize.sh 与 #L256-L277)。即使本地清理全部失败,远端租约与定时 sweeper 仍是最后一道账单防线。

可选修复:与诊断完全隔离的独立进程

修复只在访问确认关闭之后才开始,并且运行在:

  • 独立的本地 worktree 与独立的 Codex 进程;
  • 隔离的HOME,且没有 GitHub 凭据、云身份、MCP 配置或 Analysis Token;
  • 严格权限:只写修复 worktree 与隔离的 Go cache/temp 根,只读测试所需的 Go 工具链/模块缓存,禁用工具网络访问。

修复的门槛是高置信度的、可归因于仓库的产品缺陷 + 回归覆盖(对应 Diagnosis Result 中的remediation_eligibility:repository_attributable与testable都为真)。本地编排绝不以操作者主机权限直接执行生成测试:它先推送受保护分支,等待该精确 commit 通过仓库既有的只读 CI Workflow,然后才打开一个正确标注的 Draft PR,PR 正文不含任何模型生成的诊断文本,且自动合并始终被禁止。这也是--allow-fix-pr被标为"兼容标志"的原因:修复天然延后到付费资源清理完成之后。

安全模型小结与可验证依据

把 ADR-0037 与实现对照,可以归纳出四层防护:

  1. 凭据不落盘:云端只经 RSA-OAEP 交付一次性 Token,本地解密后立即删除私钥与密文;GitHub 无 API Key、无auth.json、无 ChatGPT 会话;
  2. 访问窗口有界:Token ≤45 分钟且受租约钳制、只对请求方/32开放、不可续期、有独立 close 操作与窗口过期兜底;
  3. 分析沙箱最小权限:env -i+ 只读 worktree + 禁网 + 只读工具白名单 + 拒绝项目级.codex配置覆盖(fail-closed);
  4. 清理不可绕过:诊断/修复失败不抑制清理,Interrupt 也不抑制清理,最终以 provider 确认的空资源清单作为账单已停的证据。

想深入验证上述事实,可以直接阅读:

  • 决策记录:docs/adr/0037-run-codex-locally-with-an-encrypted-analysis-session.md
  • 网关与会话:cmd/wkanalysis/main.go、cmd/wkanalysis/session.go、cmd/wkanalysis/oidc.go、cmd/wkanalysis/config.go
  • Token 交换:internal/access/cloudanalysismcp/token.go
  • 本地编排:scripts/cloud-sim/analyze.sh、scripts/cloud-sim/finalize.sh、scripts/cloud-sim/local-runtime.sh
  • 证书钉住桥接:cmd/wkcloudanalysisbridge/main.go 与其测试 main_boundary_test.go
  • 结果契约:.github/cloud-sim/diagnosis.schema.json、cmd/wkclouddiagnosis/main.go
  • 工具链固定:.github/cloud-sim/toolchain.env(Go 1.25.11、GitHub CLI 2.96.0 等,均带 SHA-256 校验)

相关 ADR 时间线可参考 docs/adr/0037 的 supersedes 列表,以及同目录下的 0040-separate-deployment-from-cloud-lease-lifecycle.md、0044-use-separate-deployment-and-codex-ssh-identities.md、0047-retain-one-lease-across-deployment-repair.md 等后续演进。

  • 即时通讯
  • 后端

【免费下载链接】WuKongIM

More than just IM 不只是即时通讯(IM)

项目地址:https://gitcode.com/gh_mirrors/wu/WuKongIM
点击查看免费下载
上一篇:rust-cpython 跨平台部署指南:Windows、Linux与macOS的完整解决方案
下一篇:Cruise Control for Apache Kafka 使用教程

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进