Files
codex-app-server/docs/chv-muse-p3-gap.md
T
host f093558c0d fix(chv-muse-r11fix): authd 层 5 去掉 store helper,chv-sync 回落改 fail-closed
round-11 真机测试证实:镜像 /etc/gitconfig 的 [credential]
helper = store 把 authd 释放的长效 token 写进了客内
/home/chv/.git-credentials(0600)。改动:build.sh 不再设
store helper;chv-sync boot 在 authd socket/helper 缺失时直接
die,不再把 token 写进 .git-credentials(旧落盘回落废止);
authd helper 是唯一凭证 helper,且自身绝不落盘。旧镜像上 boot
现在会直接失败,须用含 authd 的新镜像。测试:无 authd 时 boot
失败且 .git-credentials 不产生、干跑不再含 store helper;P3
差距表 §3.5 更新为已关闭。
2026-11-07 04:00:04 +00:00

125 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 对齐 Muse P3 差距表:authd 替身凭证 + Sentinel 式出口审批
依据:`~/workspace/research_notes/meta-muse-ai-architecture-20261101-0755/report.md`
(官方以 Meta security.muse.ai 2026-09-08 为准;第三方拆解只作参考,
逆向细节不得写成官方架构)。Muse 模型一句话:凭证由 VM 内独立服务
authd 保管、代理只见替身令牌;所有连接器动作与网络出口由 Sentinel
唯一审批(allow/deny/ask,L4+L7 评估,SSRF 防护,严格能力绑定);
审批 UI 不在对话内;连接器执行走 privsep worker,按连接器做凭证白名单。
本轨范围只限 P3:authd 式替身凭证 + Sentinel 式出口审批。
不碰 P1(常驻 VM、WarmPool、气球、vsock)、不碰 P2(客机 nspawn 单元、
浏览器子代理独立)。分支 `feat-chv-muse-p3`(从 `feat-chv-cover100`
分出,基线 HEAD `06c3e17`)。
## 1. 现状 vs Muse
| 项 | 现状(实测/代码为准) | Muse 目标 | 本轨做法 |
|---|---|---|---|
| Gitea token 可见面 | 真 token 经 seed 写进 `/etc/chv/guest-env`(0600 root);`build_ssh_command` 在每个任务的远程命令前 `source` 该文件,codex 进程环境里直接含真值;`chv-sync boot` 再把它写进 `/home/chv/.git-credentials` 落盘 | authd 保管凭证,代理只见替身令牌;凭证存入运行时单元之外的安全凭证存储,代理不可见;仅在需要时注入 | 真 token 改走 seed 里的独立文件 `/etc/chv/authd-seed`(0600 root),只给客内 authd 守护进程读;guest-env 不再含 GITEA_TOKEN;任务期 `build_ssh_command` 不再把 GITEA_TOKEN source 进 codex 环境;`chv-sync` 经 git credential helper 按次向 authd 取凭证,只进 git 子进程内存,不落盘、不进 agent 环境/argv/日志 |
| authd 位置 | 无 | 存于用户 VM 内(非 Meta 集中基础设施),在运行时单元之外 | 客内 authd 守护进程(`chv-guest/authd.py`,标准库 Unix socket 服务),以 root 身份跑在单元外;宿主侧不存任何 VM 的 token。选客内不选宿主的理由:Muse 的 authd 就在用户 VM 里;宿主侧方案要走 vsock(P1 范围、未就绪),且会把全部 VM 的 token 集中到宿主,违背“存于用户 VM”的口径 |
| 替身令牌 | 无(真值直给) | authd 签发替身令牌,带作用域与有效期 | 两档:① 本期默认——按次释放:每次 git 操作向 authd 取一次凭证,绑定 {连接器=gitea, 主机, 仓库路径, 操作},单次有效 + 300 秒 TTL,过期即废,释放全程审计(审计里无值);② 真铸 token:authd 可经 Gitea API 铸短期专用 token(`POST /api/v1/users/{user}/tokens`),默认关闭,见诚实边界第 2 条 |
| 凭证白名单(privsep) | 无连接器概念;`_CHV_SECRET_ENV_NAMES` 只有“密钥/非密钥”二分 | 每个 privsep worker 有明确凭证白名单(日历 worker 不能仅改参数就索取邮件凭证) | authd 只认一个连接器 `gitea`,白名单只含 GITEA_TOKEN;未知连接器/越权主机/仓库/操作一律拒绝;SUB2API_API_KEY 仍走 guest-env(见诚实边界第 3 条) |
| 出口审批 | 只有代码门:preflight 要求代理或显式直连 + `validate_egress_proxy` 格式校验;真正的隔离靠 Tinyproxy 白名单 + 宿主防火墙(第三棒落地中);客内 root 绕过代理直连在代码层无第二道门 | Sentinel 是连接器动作与全部网络出口的唯一权限机构;L4+L7 评估(主机名、解析后 IP、端口、协议、HTTP 方法、路径、解码后请求);SSRF 防护;审批是严格能力(绑定连接器/目的地/用例,一次性/会话级/任务级/时限级) | `cloud/chv/sentinel.py` 决策引擎:每动作审批钩子 allow/deny/ask;L4(解析后 IP、端口、协议)+ L7(主机、方法、路径)评估;SSRF 挡截(公开主机名解析到私有/回环/链路本地/CGNAT/保留地址即 deny);能力绑定 {连接器, 主机, 方法, 路径, 有效期, 次数};ask 记入待审批队列、执行停止。本期两个接线点:① wake 时 Gitea 出口预审(解析失败/私网 IP/不在白名单 → 起 VMM 前 fail-closed);② authd 放凭证前先过 Sentinel 评估 |
| 审批 UI | 无 | Sentinel 直接向客户端发审批请求,弹窗在客户端 UI 内,答复直接回 Sentinel;目的:防对话内容伪造审批 | 本期只做设计约束 + 待审批队列(`sentinel.ApprovalQueue`,内存态 pending 记录):ask 出现即停止执行、记入队列,不自动放行;UI 不在对话内这一条记为约束,不在本期实现任何对话内审批入口 |
| 污点出口(eBPF) | 无 | eBPF cgroup 程序做网络拦截与进程归因、LSM hook 做污点传播 | 列为后续,不冒充已做。见诚实边界第 4 条 |
| 审计 | `_emit` 审计只含 VM 名/路径/URL/kind,不含 key 值(M5) | 审计轨迹展示已做与计划做的事 | 沿用 `_emit` 契约:新增 `sentinel_egress_allow/deny/ask`、`authd_credential_released/refused` 事件,detail 只含连接器/主机/路径/ verdict/原因,不含任何凭证值;authd 客内审计文件 `/var/log/chv-authd.log`(0600)同款口径 |
## 2. 改了什么(本轨交付物)
- `cloud/chv/authd.py`(新,叶子模块,标准库):凭证保险库(只活内存,
`__repr__` 不含值)+ 按次释放(作用域绑定、单次有效、300 秒 TTL、
过期即废)+ 连接器白名单(本期只 `gitea`)+ Gitea API 铸 token
辅助函数(可注入 HTTP 客户端,默认关闭)+ 无值审计记录。
- `cloud/chv/sentinel.py`(新,叶子模块,标准库):决策引擎
`evaluate()`(allow/deny/ask)+ L4/L7 评估 + SSRF 挡截
(解析器可注入,默认 `socket.getaddrinfo`)+ 严格能力
`Capability`(连接器/主机/方法/路径/有效期/次数绑定)+
`ApprovalQueue`(ask 待审批队列,内存态)+ 无值审计记录。
- `cloud/chv/hostkeys.py`:`write_seed` 加 `authd_env` 参数——authd
专用密钥写进独立的客内文件 `/etc/chv/authd-seed`(0600 root),
不再与 guest-env 混写;`AUTHD_SEED_PATH` 常量。
- `cloud/chv/gitea_sync.py`:`AUTHD_SECRET_KEYS = {"GITEA_TOKEN"}` +
`split_authd_env()`(把 GITEA_TOKEN 从密钥集里拆出来走 authd 通道)。
- `cloud/chv/ssh_transport.py`:`_CHV_AUTHD_SECRET_NAMES`;`remote_command`
语义不变(仍只 source guest-env 文件)。
- `cloud/chv/executor.py`:`provision_host_key`/`_provision_seed_disk`
透传 `authd_env`;新增 `_authd_delivered` 投递集(与
`_guest_env_delivered` 同锁)——authd 投递的密钥名在
`build_ssh_command` 里不报错、但不 source 进 codex 环境;
`_wake_prechecks` 加 Gitea 出口 Sentinel 预审(deny/ask 即
`sentinel_egress_denied`,起 VMM 前 fail-closed)。
- `cloud/chv/config.py`:`CHV_SENTINEL_ALLOW_HOSTS`(逗号分隔主机名白名单,
默认只放行配置的 Gitea 主机)。
- `chv-guest/authd.py`(新,标准库守护进程):读 `/etc/chv/authd-seed`
持 token 于内存、Unix socket `/run/chv-authd/authd.sock`(0700 目录、
0600 socket)、SO_PEERCRED 只放行 root 与 chv 用户、每次放凭证前先过
Sentinel 评估、审计落盘 0600。
- `chv-guest/git-credential-chv-authd`(新,标准库 helper):git
credential 协议(stdin 读 `protocol/host/path`,stdout 回用户名/口令),
口令只进 git 子进程内存。
- `chv-guest/chv-authd.service`(新):systemd 单元(root 运行,单元外)。
- `chv-guest/chv-sync.sh`:`boot` 在 authd socket 存在时改配
`credential.helper` 走 authd,不再把真 token 写进
`.git-credentials`;同时配 `credential.useHttpPath=true`
(git 默认不把 path 发给 helper,没有 path helper 无法推断
仓库与操作、每次都拒绝——新测试抓到的真问题);socket 不存在
时走旧路径 + stderr 警告(过渡兼容,见诚实边界第 5 条)。
- `cloud/tests/test_chv_muse_p3.py`(新):替身过期、真 token 泄露面、
SSRF 挡截、deny/ask 路径、seed 拆分、wake 预审、helper 端到端
(真 git `credential fill` + 假 authd socket);全量 cloud 套件全过。
- 镜像集成(只留接入说明,不改 `chv-guest/build.sh`,与 P1 同款规矩,
合并时由 P2 一并带进镜像):
`--copy-in chv-guest/authd.py:/usr/local/bin/`、
`--copy-in chv-guest/git-credential-chv-authd:/usr/local/bin/`、
`--copy-in chv-guest/chv-authd.service:/etc/systemd/system/`、
`--copy-in cloud/chv/authd.py:/usr/local/lib/chv-authd/cloud/chv/`、
`--copy-in cloud/chv/sentinel.py:/usr/local/lib/chv-authd/cloud/chv/`、
`--copy-in cloud/__init__.py:/usr/local/lib/chv-authd/cloud/`、
`--copy-in cloud/chv/__init__.py:/usr/local/lib/chv-authd/cloud/chv/`、
`--run-command 'chmod +x /usr/local/bin/chv-authd /usr/local/bin/git-credential-chv-authd'`、
`--run-command 'systemctl enable chv-authd || true'`、
`99-chv-sync.cfg` 的 runcmd 在 `chv-sync boot` 之前加
`systemctl start chv-authd || true`。
## 3. 诚实边界(本轮不宣称的东西)
1. **seed 文件仍是 0600 root 落盘**:真 token 在 `/etc/chv/authd-seed`
里仍以文件形式存在(cloud-init 的唯一投递通道就是写文件),且 seed
ISO 本身挂在客机上。真正让它对 agent 不可见的是 P2 的 nspawn 单元
隔离(单元内 root 映射为非特权用户,读不到单元外的 0600 文件);在
P2 落地之前、codex 仍以客内 root 跑的镜像上,root agent 理论上仍能
读到它。本轮堵住的是“进程环境/argv/日志/`.git-credentials` 落盘”
这条泄露面,不是“客内 root 万能”——后者是 P2 的边界,不是 P3 能修的。
2. **本期默认的“替身”不是 Gitea 侧可独立校验的新 token**:按次释放给
git 子进程的值仍是真 token,只是只进 git 进程内存、不进 agent。
真铸 token(Gitea API 铸短期专用 token、到期吊销)已实现辅助函数
与假客户端测试,但默认关闭——它要求真 token 有建 token 的权限,
现有测试 token 只有 `write:repository`,铸 token 会失败;开它须另配
有铸 token 权限的 token,并做真机验证。不得把本期默认写成“Muse 同款
替身令牌已落地”。
3. **SUB2API_API_KEY 不在本轮**:网关 key 仍走 guest-env 进 codex 环境
(H2 既有行为)。按 Muse 模型它也该走 privsep worker(由 worker 代
codex 调网关、codex 不见 key),那是网关调用链改造,超出本轮范围,
列为后续,不静默声称已覆盖。
4. **eBPF 污点传播未做**:Sentinel 本轮是决策层(wake 预审 + authd 放
凭证前评估),不是网络层强制。客内任意进程的直连仍由 Tinyproxy
白名单 + 宿主防火墙(第三棒)兜底;eBPF cgroup 拦截 + LSM 污点传播
是后续工作,不得写成“出口已全部经 Sentinel”。
5. **过渡兼容已关闭(round-11 真机修复,层 5)**:此前 `chv-sync
boot` 在 authd socket 不存在时仍走旧的 `.git-credentials` 落盘
(只多一句 stderr 警告),为的是 lite9/lite10 旧镜像不被一次
改断。round-11 真机测试证实该回落把 authd 释放的长效 token 写
进了客内磁盘,修复轮已改为无 socket 即 fail-closed(`die`),
同时镜像 `/etc/gitconfig` 的 `credential.helper store` 也已删
除——authd helper 是唯一凭证 helper,且自身绝不落盘。旧镜像
(lite10 及更早)上 boot 现在会直接失败,须用含 authd 的新
镜像;真机复测须验证跑完一次完整 Gitea 链后客内
`~/.git-credentials` 不存在。
6. **无真机验证**:本轮全部是代码 + 本地单测(含真 git helper 端到端,
但 authd 守护进程本身未在真 VM 里跑过)。真机待验证清单:authd 在
lite11 镜像里启动时序(须在 `chv-sync boot` 之前就绪)、SO_PEERCRED
放行 chv 用户、helper 在 pull/push 下的实际表现、wake 预审对真实
Gitea 主机解析的判定、铸 token 的真实 API 行为。测试机(WSL)须等
第三棒 V-1~V-9 让出后再做。