# 对齐 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 让出后再做。