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 更新为已关闭。
12 KiB
12 KiB
对齐 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 端到端 (真 gitcredential 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. 诚实边界(本轮不宣称的东西)
- 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 能修的。 - 本期默认的“替身”不是 Gitea 侧可独立校验的新 token:按次释放给
git 子进程的值仍是真 token,只是只进 git 进程内存、不进 agent。
真铸 token(Gitea API 铸短期专用 token、到期吊销)已实现辅助函数
与假客户端测试,但默认关闭——它要求真 token 有建 token 的权限,
现有测试 token 只有
write:repository,铸 token 会失败;开它须另配 有铸 token 权限的 token,并做真机验证。不得把本期默认写成“Muse 同款 替身令牌已落地”。 - SUB2API_API_KEY 不在本轮:网关 key 仍走 guest-env 进 codex 环境 (H2 既有行为)。按 Muse 模型它也该走 privsep worker(由 worker 代 codex 调网关、codex 不见 key),那是网关调用链改造,超出本轮范围, 列为后续,不静默声称已覆盖。
- eBPF 污点传播未做:Sentinel 本轮是决策层(wake 预审 + authd 放 凭证前评估),不是网络层强制。客内任意进程的直连仍由 Tinyproxy 白名单 + 宿主防火墙(第三棒)兜底;eBPF cgroup 拦截 + LSM 污点传播 是后续工作,不得写成“出口已全部经 Sentinel”。
- 过渡兼容已关闭(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不存在。 - 无真机验证:本轮全部是代码 + 本地单测(含真 git helper 端到端,
但 authd 守护进程本身未在真 VM 里跑过)。真机待验证清单:authd 在
lite11 镜像里启动时序(须在
chv-sync boot之前就绪)、SO_PEERCRED 放行 chv 用户、helper 在 pull/push 下的实际表现、wake 预审对真实 Gitea 主机解析的判定、铸 token 的真实 API 行为。测试机(WSL)须等 第三棒 V-1~V-9 让出后再做。