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
@@ -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:
|
||||
|
||||
Reference in new issue
Block a user