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:
host committed 2026-11-06 21:21:55 +00:00
1 parent 06c3e172fe
commit a90cf78d84
15 files changed
+2370 -93

No files matched your search

+62 -12
View File
@@ -10,7 +10,8 @@ VM、不联网、不含任何真实 token(测试值全是假值)。
5 键进 guest-env 集;
- hostkeys.write_seed:guest_env 与 secret_env 合并写同一
条目、仍无 runcmd、0600;
- 执行器 wake_vm:完整同步变量进 seed user-data;缺失必填
- 执行器 wake_vm:完整同步变量进 seed(5 键 guest-env +
GITEA_TOKEN 走 authd-seed);缺失必填
键时起 VMM 前 ChvFailure 且不启动;无同步变量时正常启动
且不写 guest-env;seed 投递后 build_ssh_command 才能用
GITEA_TOKEN 且 argv 里绝无 token 值;
@@ -117,6 +118,13 @@ def _guest_env_lines(user_text):
assert len(guest) == 1, entries
return guest[0]["content"], guest[0]["permissions"]
def _authd_seed_lines(user_text):
parsed = yaml.safe_load(user_text)
entries = parsed["write_files"]
authd = [e for e in entries if e["path"] == "/etc/chv/authd-seed"]
assert len(authd) == 1, entries
return authd[0]["content"], authd[0]["permissions"]
# -- (1) gitea_sync 纯函数 ---------------------------------------------
class GiteaSyncPureTest(unittest.TestCase):
@@ -226,27 +234,39 @@ class SshTransportSyncTest(unittest.TestCase):
# -- (3) hostkeys.write_seed -------------------------------------------
class WriteSeedGuestEnvTest(unittest.TestCase):
def test_guest_env_and_secret_merge_into_one_entry_no_runcmd(self):
# feat-chv-muse-p3:GITEA_TOKEN 不再进 guest-env,改走
# authd_env(独立的 authd-seed 条目,0600);guest-env
# 条目里只剩非 authd 密钥与同步普通变量。guest_env 里
# 直接给 GITEA_TOKEN 即 ValueError(调用方须先
# split_authd_env 拆出来,旧的“同名键 secret 优先”语义
# 已由“authd 通道只装 authd 密钥”取代)。
with tempfile.TemporaryDirectory() as tmp:
seed_dir = os.path.join(tmp, "seed")
write_seed(seed_dir, "vm1", "PRIV", "ssh-ed25519 AAAAx",
secret_env={"SUB2API_API_KEY": "gw-key"},
guest_env={"GITEA_URL": "https://gitea.tose.top",
"GITEA_USER": "admin",
"SYNC_REPO": "admin/chv-sync-test",
"GITEA_TOKEN": "dup-should-lose"})
"SYNC_REPO": "admin/chv-sync-test"},
authd_env={"GITEA_TOKEN": "dup-should-lose"})
with open(os.path.join(seed_dir, "user-data")) as fh:
content = fh.read()
self.assertEqual(content.count("write_files:"), 1)
parsed = yaml.safe_load(content)
self.assertNotIn("runcmd", parsed)
self.assertEqual(len(parsed["write_files"]), 3)
self.assertEqual(len(parsed["write_files"]), 4)
text, perms = _guest_env_lines(content)
self.assertEqual(perms, "0600")
self.assertIn("GITEA_URL=https://gitea.tose.top", text)
self.assertIn("SUB2API_API_KEY=gw-key", text)
# 同名键以 secret_env 为准,不出现两行 GITEA_TOKEN。
self.assertEqual(text.count("GITEA_TOKEN="), 1)
self.assertIn("GITEA_TOKEN=dup-should-lose", text)
self.assertNotIn("GITEA_TOKEN", text)
authd_text, authd_perms = _authd_seed_lines(content)
self.assertEqual(authd_perms, "0600")
self.assertIn("GITEA_TOKEN=dup-should-lose", authd_text)
with tempfile.TemporaryDirectory() as tmp:
seed_dir = os.path.join(tmp, "seed")
with self.assertRaises(ValueError):
write_seed(seed_dir, "vm1", "PRIV", "ssh-ed25519 AAAAx",
guest_env={"GITEA_TOKEN": "dup-should-lose"})
def test_guest_env_only_still_no_runcmd(self):
with tempfile.TemporaryDirectory() as tmp:
@@ -307,7 +327,10 @@ class WakeGuestEnvCase(unittest.TestCase):
def make_exec(self, env_extra=None):
env = {"CHV_EGRESS_PROXY": "http://10.0.0.1:8888",
"CHV_HOST_KEY_MODE": "provisioned",
"CHV_SSH_FORWARD": "1"}
"CHV_SSH_FORWARD": "1",
# feat-chv-muse-p3:wake 前 Sentinel 预审要求
# Gitea 主机在白名单内;解析器用假的(测试不联网)。
"CHV_SENTINEL_ALLOW_HOSTS": "gitea.tose.top"}
if env_extra:
env.update(env_extra)
ex = CloudHypervisorExecutor(
@@ -317,7 +340,8 @@ class WakeGuestEnvCase(unittest.TestCase):
cpuinfo_path=self.cpuinfo, state_dir=self.state_dir,
runner=self._runner, launcher=self._launcher,
api_client=lambda s, m, p, payload=None: (200, {}),
sleeper=lambda s: None, env=env)
sleeper=lambda s: None, env=env,
sentinel_resolver=lambda host, port: ["93.184.216.34"])
ex.audit_callback = lambda e, t, d, k=None: self.audit.append(
(e, t.get("id") if isinstance(t, dict) else t, d, k))
return ex
@@ -331,8 +355,15 @@ class WakeGuestEnvCase(unittest.TestCase):
self.assertNotIn("runcmd", yaml.safe_load(text))
guest_text, perms = _guest_env_lines(text)
self.assertEqual(perms, "0600")
for key, value in FAKE_SYNC.items():
self.assertIn("%s=%s" % (key, value), guest_text)
# feat-chv-muse-p3:六键仍全进 seed,但分两路——
# 5 个普通键进 guest-env,GITEA_TOKEN 只进 authd-seed。
for key in ("GITEA_URL", "GITEA_USER", "SYNC_REPO",
"SYNC_BRANCH", "GITEA_EMAIL"):
self.assertIn("%s=%s" % (key, FAKE_SYNC[key]), guest_text)
self.assertNotIn("GITEA_TOKEN", guest_text)
authd_text, authd_perms = _authd_seed_lines(text)
self.assertEqual(authd_perms, "0600")
self.assertIn("GITEA_TOKEN=fake-token-123", authd_text)
# seed.iso 已制盘且 0600。
seed_iso = os.path.join(self.state_dir, "chv-sync1", "seed.iso")
self.assertTrue(os.path.exists(seed_iso))
@@ -351,8 +382,14 @@ class WakeGuestEnvCase(unittest.TestCase):
self.assertEqual(out["state"], "running")
guest_text, _ = _guest_env_lines(
_seed_user_text(self.state_dir, "chv-sync2"))
self.assertIn("GITEA_TOKEN=fake-token-123", guest_text)
# feat-chv-muse-p3:GITEA_TOKEN 不再进 guest-env,
# 改走 authd-seed;其余同步变量仍在 guest-env 里。
self.assertNotIn("GITEA_TOKEN", guest_text)
self.assertIn("SYNC_BRANCH=main", guest_text)
authd_text, authd_perms = _authd_seed_lines(
_seed_user_text(self.state_dir, "chv-sync2"))
self.assertEqual(authd_perms, "0600")
self.assertIn("GITEA_TOKEN=fake-token-123", authd_text)
def test_wake_partial_sync_fails_closed_before_launch(self):
ex = self.make_exec()
@@ -387,11 +424,24 @@ class WakeGuestEnvCase(unittest.TestCase):
def test_build_ssh_command_after_wake_sources_guest_env(self):
ex = self.make_exec()
ex.wake_vm({"id": "sync5", "gitea_sync": dict(FAKE_SYNC)})
# feat-chv-muse-p3:GITEA_TOKEN 走 authd 投递——任务期
# 认“已投递、不报错”,但绝不 source 进 codex 进程环境,
# 所以命令里既无 token 值、也不 source 任何客内密钥文件。
cmd = ex.build_ssh_command(
{"id": "sync5"}, ["true"],
extra_env={"GITEA_TOKEN": "fake-token-123"})
joined = " ".join(cmd)
self.assertNotIn("fake-token-123", joined)
self.assertNotIn("/etc/chv/guest-env", joined)
self.assertNotIn("/etc/chv/authd-seed", joined)
# 非 authd 密钥仍走 guest-env:wake 时投递,任务期 source。
ex.wake_vm({"id": "sync5b"},
extra_env={"SUB2API_API_KEY": "gw-key"})
cmd = ex.build_ssh_command(
{"id": "sync5b"}, ["true"],
extra_env={"SUB2API_API_KEY": "gw-key"})
joined = " ".join(cmd)
self.assertNotIn("gw-key", joined)
self.assertIn("/etc/chv/guest-env", joined)
# 未 wake 投递的 VM 用密钥即拒绝。
with self.assertRaises(ChvFailure) as ctx: