feat(chv): 对齐 Muse P3 收尾——authd 替身凭证 + Sentinel 出口审批
- 新测试 cloud/tests/test_chv_muse_p3.py 30 项:替身过期、真 token 泄露面、SSRF 挡截、deny/ask 路径、authd-seed 拆分、wake 预审、 客内 authd 守护进程、git credential helper 端到端(真 git fill) - 8 个旧测试按新 authd 行为改写(未删测试、未放宽断言): GITEA_TOKEN 改走 authd-seed,build_ssh_command 不再 source 进 codex 环境;wake 测试补 Sentinel 白名单 + 假解析器 - 7 处超 50 行函数拆到 50 行内(不改行为);补 authd/sentinel 公共 API 注解,mypy 在改动文件上干净 - 真问题修复:git 默认不把 path 发给 helper,chv-sync boot 加 credential.useHttpPath=true,否则 authd 路径每次都拒绝 - 全量 cloud 套件 1897 项 OK(skipped=8) 真机待验证(未做,等第三棒让出 WSL 测试机):见 docs/chv-muse-p3-gap.md §3.6 清单
This commit is contained in:
1 parent
06c3e172fe
commit
a90cf78d84
15 files changed
+2370
-93
No files matched your search
@@ -0,0 +1,118 @@
|
||||
# 对齐 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. **过渡兼容留了旧路径**:`chv-sync boot` 在 authd socket 不存在时仍走
|
||||
旧的 `.git-credentials` 落盘(只多一句 stderr 警告),为的是 lite9/
|
||||
lite10 旧镜像不被一次改断。旧镜像退役后应删掉该回落,改为无 socket
|
||||
即 fail-closed;删除前不得声称“旧路径已关闭”。
|
||||
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 让出后再做。
|
||||
Reference in new issue
Block a user