From cbd5ab7c0bdce37987e86c4b42cd2eafb30f4245 Mon Sep 17 00:00:00 2001 From: host Date: Fri, 6 Nov 2026 12:05:43 +0000 Subject: [PATCH] =?UTF-8?q?fix(chv-guest):=20=E9=A6=96=E5=90=AF=E7=BC=BA?= =?UTF-8?q?=20key=20=E6=97=B6=E6=89=8D=E7=94=9F=E6=88=90=20host=20key?= =?UTF-8?q?=EF=BC=88B-23=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Minimal 底座 cloud.cfg 不含 ssh 模块,build.sh 又写 ssh_genkeytypes: [] 彻底关掉首启生成:客机无 host key、 sshd -t 失败、ssh.service 连败 5 次,SSH 全程不通(真机 冒烟 131s 未通,console 已到 login、ping 通、tap 正常)。 修正:99-chv-hostkey.cfg 改写首启 bootcmd,只在缺 ed25519 key 时 ssh-keygen -A;ssh_deletekeys: false 保留, 绝不重生成已预置的 per-VM key(bootcmd 跑在 seed write_files 之前,预置 key 随后覆盖生成值)。README §5 同步修正;新增 BuildScriptTest 回归断言。 --- chv-guest/README.md | 10 ++++++---- chv-guest/build.sh | 11 ++++++++--- cloud/tests/test_chv_gaps.py | 19 +++++++++++++++++++ 3 files changed, 33 insertions(+), 7 deletions(-) diff --git a/chv-guest/README.md b/chv-guest/README.md index 4c09c2d..be3b8b8 100644 --- a/chv-guest/README.md +++ b/chv-guest/README.md @@ -84,10 +84,12 @@ KVM;全量模式在无 /dev/kvm 的机器上第一步就明确退出 第二块只读盘)由执行器在 VM 启动前生成,含 meta-data 与 user-data); - 首启时先应用 seed 再启动 sshd,seed 里的 user-data 把该 VM 的 host key 对写进 /etc/ssh/ 并重启 sshd; -- 镜像里已钉死 `ssh_deletekeys: false` 与 - `ssh_genkeytypes: []`(build.sh 已写),首启不得无条件 - 重新生成 host key——重生成会使预置的 known_hosts 立即 - 失效,SSH 钉死直接拒绝,这是设计,不是 bug; +- 镜像里已钉死 `ssh_deletekeys: false`(build.sh 已写),首启 + 不得无条件重新生成 host key——重生成会使预置的 known_hosts + 立即失效,SSH 钉死直接拒绝,这是设计,不是 bug;无预置 key + 时首启 bootcmd 只在缺 key 时 `ssh-keygen -A` 生成(B-23: + 曾写 `ssh_genkeytypes: []` 彻底关掉首启生成,客机 sshd 起 + 不来,已删,绝不再写); - sshd 只允许 key 登录,禁止密码登录(build.sh 写入 /etc/ssh/sshd_config.d/99-chv.conf:PasswordAuthentication no、 PermitRootLogin prohibit-password、ChallengeResponseAuthentication no、 diff --git a/chv-guest/build.sh b/chv-guest/build.sh index 1c1bb17..0fb84d3 100755 --- a/chv-guest/build.sh +++ b/chv-guest/build.sh @@ -51,8 +51,13 @@ # 客机必须装 cloud-init 且支持 NoCloud seed,首启先应用 # seed 再启动 sshd,且不得无条件重新生成 host key,否则 # 执行器的 per-VM 预置钉死即失效。本脚本在镜像里写 -# /etc/cloud/cloud.cfg.d/99-chv-hostkey.cfg(ssh_deletekeys: -# false、ssh_genkeytypes: []),把“不重生成”钉进镜像。 +# /etc/cloud/cloud.cfg.d/99-chv-hostkey.cfg:ssh_deletekeys: +# false(绝不删已有 key)+ 首启 bootcmd 只在缺 key 时 +# ssh-keygen -A(bootcmd 跑在 seed write_files 之前,预置 +# key 随后覆盖生成值,最终仍是预置 key)。 +# B-23:曾写 ssh_genkeytypes: [],彻底关掉首启生成;Minimal +# 底座 cloud.cfg 又不含 ssh 模块,客机无 host key、sshd -t +# 失败、ssh.service 连败,SSH 全程不通——[] 已删,绝不再写。 # # 注:Minimal 底座虚拟盘 3.5G(根分区 2.4G,实测 1.9G 空闲), # qemu-img convert 出的 disk.raw 表观 3.5G、实际占用约 0.6G @@ -243,7 +248,7 @@ VC_ARGS=(-a "$OUT/disk.raw" --run-command 'git config --system user.name chv && git config --system user.email chv@localhost && git config --system credential.helper store && git config --system init.defaultBranch main' --run-command "$GT_INSTALL_CMD" --run-command '/usr/local/bin/gt version > /app/guest-versions-gt.txt' - --run-command 'printf "ssh_deletekeys: false\nssh_genkeytypes: []\n" > /etc/cloud/cloud.cfg.d/99-chv-hostkey.cfg; systemctl enable ssh || true' + --run-command 'printf "ssh_deletekeys: false\nbootcmd:\n - \"[ -f /etc/ssh/ssh_host_ed25519_key ] || ssh-keygen -A\"\n" > /etc/cloud/cloud.cfg.d/99-chv-hostkey.cfg; systemctl enable ssh || true' --run-command 'printf "PasswordAuthentication no\nPermitRootLogin prohibit-password\nChallengeResponseAuthentication no\nKbdInteractiveAuthentication no\nPubkeyAuthentication yes\n" > /etc/ssh/sshd_config.d/99-chv.conf && mkdir -p /run/sshd && ssh-keygen -A && sshd -t && grep -q "^PasswordAuthentication no" /etc/ssh/sshd_config.d/99-chv.conf && grep -q "^PermitRootLogin prohibit-password" /etc/ssh/sshd_config.d/99-chv.conf && rm -f /etc/ssh/ssh_host_* && test -z "$(ls /etc/ssh/ssh_host_* 2>/dev/null)"' --run-command 'dpkg-query -W -f "\${Package} \${Version}\n" > /app/guest-versions-dpkg.txt; pip3 freeze > /app/guest-versions-pip.txt || true') if [ "${PLAYWRIGHT:-1}" = "1" ]; then diff --git a/cloud/tests/test_chv_gaps.py b/cloud/tests/test_chv_gaps.py index a395453..471fb0e 100644 --- a/cloud/tests/test_chv_gaps.py +++ b/cloud/tests/test_chv_gaps.py @@ -434,6 +434,25 @@ class BuildScriptTest(unittest.TestCase): % self.repo_commit, r.stdout) self.assertFalse(os.path.exists(os.path.join(out_dir, "disk.raw"))) + def test_hostkey_dropin_generates_missing_keys_at_first_boot(self): + # B-23:Minimal 底座的 cloud.cfg 不含 ssh 模块,build.sh + # 曾写 ssh_genkeytypes: [] 彻底关掉首启 host key 生成, + # 客机无 key、sshd -t 失败、ssh.service 连败 5 次,SSH + # 全程不通。修正:首启 bootcmd 只在缺 key 时生成,绝不 + # 重生成已预置的 per-VM key;写盘的命令里 [] 绝不再出现 + #(注释里提历史可以,功能行里不行)。 + with open(self.script) as fh: + text = fh.read() + hostkey_lines = [ln for ln in text.splitlines() + if "99-chv-hostkey.cfg" in ln + and "printf" in ln] + self.assertEqual(len(hostkey_lines), 1) + cmd = hostkey_lines[0] + self.assertIn("ssh_deletekeys: false", cmd) + self.assertIn("[ -f /etc/ssh/ssh_host_ed25519_key ] " + "|| ssh-keygen -A", cmd) + self.assertNotIn("ssh_genkeytypes", cmd) + def test_full_mode_without_kvm_fails_fast(self): # 本 VM 容器内无 /dev/kvm,全量模式必须明确失败, # 绝不声称已建成镜像。