fix(chv-hybrid): p2 第三轮:attach 轮询等挂盘落地 + detach 走 sudo 窄规则 umount
ci / test (push) Failing after 47m51s

第 2 轮复测(0dc8328)p2 仍 FAIL 的两处根因:
1. 挂盘时序竞态:add-disk 返回 06:59:10,root hotplug 路径
   (cloud-init-hotplugd → cloud-final runcmd → chv-data-mount)
   06:59:14–16 才挂上;旧 attach 以 chv 调 chv-data-mount 并判
   rc==0,但 /dev/vd? 属 root:disk、chv 的 blkid/mkfs 必败,
   脚本又静默 exit 0,rc 检查是空洞的成功假象。
2. 卸载权限:挂载是 root 做的,chv 的 plain umount 恒 rc=32
   must be superuser,release 按现写法永远失败、VM 留置。

修法选 (a) 轮询,不选 (b) root 特权挂载:挂盘本身已有 root
路径在跑,host 侧不另开新的 root 挂载通道(新特权面只扩
攻击面);轮询以 chv 只读判定挂载态,零新增特权。
- attach:删 chv 调 chv-data-mount,改为轮询 mountpoint -q
  /home/chv/workspace && test -w(默认 60 秒/2 秒,可经
  CHV_DATA_MOUNT_TIMEOUT_SECONDS / CHV_DATA_MOUNT_INTERVAL_SECONDS
  注入短值);超时记 data_disk_mount_timeout 审计并抛同名
  kind,不写归属标记。attach 返回成功 = 挂盘真实发生。
  resume_seconds 因此真实包含挂盘等待(§2.1 口径),≤2 秒
  目标不改口径、不改目标。
- detach:改走 sudo -n /usr/bin/umount /home/chv/workspace;
  失败即 False,release 留置 VM 不回收(fail-closed)。
- chv-data-mount.sh:盘已找到后 mkfs/mount 失败改 exit 1
  (原全部失败路径 exit 0);无盘仍 exit 0(启动期 no-op,
  不挡 boot)。本脚本只由 root 跑(service/runcmd)。
- build.sh:apt 加 sudo;写 /etc/sudoers.d/chv-data-umount
  窄规则(chv 仅能以 root 跑 umount /home/chv/workspace
  这一条精确 argv,NOPASSWD,无 shell/其它命令/挂载权),
  0440 + visudo -cf 作构建门;buildinfo/lockfile 登记
  sudo 与 chv-data-umount。

特权边界:本轮唯一新增提权面就是上述一条 sudoers 规则;
attach 零提权(只读轮询),挂载权仍只在 root 的
hotplug/service 路径手里。旧镜像(无 sudo/无规则)上
detach 返回 False、release 留置,须重建镜像才恢复回收。

测试:新增 10 项(attach 等三次轮询才成功、超时 loud 失败、
永不以 chv 调 chv-data-mount、detach 精确 sudo argv、
sudo 失败 False、卸载失败 release 留置、客机脚本 exit 码、
build.sh sudo/窄规则/buildinfo 登记);另修 2 处旧断言
(attach 不再调挂载脚本)与 stage4 lockfile 夹具(补 sudo
dpkg 条目),并把 attach 热插段抽为 _hotplug_tenant_disk
(50 行函数上限)。全量 Ran 4688,OK (skipped=15,均为
环境 skip:无 PG DSN/驱动、缺 SearXNG_URL、fallback state
文件在位)。
This commit is contained in:
host committed 2026-11-09 08:17:01 +00:00
1 parent 0dc8328ecc
commit a859d731d5
8 files changed
+351 -37

No files matched your search

+12 -1
View File
@@ -277,7 +277,7 @@ run qemu-img convert -f qcow2 -O raw "$IMG" "$OUT/disk.raw"
# 写进 /app/guest-versions-chromium.txt)。
GT_INSTALL_CMD="curl -fsSL -o /tmp/gt-install.sh https://gitea.com/gitea/cli/raw/branch/main/scripts/install.sh && echo \"${GT_INSTALL_SH_SHA256} /tmp/gt-install.sh\" | sha256sum -c - && GITEA_CLI_VERSION=${GT_VERSION} GITEA_CLI_INSTALL_DIR=/usr/local/bin sh /tmp/gt-install.sh && rm -f /tmp/gt-install.sh && /usr/local/bin/gt version"
VC_ARGS=(-a "$OUT/disk.raw"
--run-command 'apt-get update -qq && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends git openssh-server openssh-client cloud-init python3 python3-pip curl ca-certificates xz-utils fonts-wqy-microhei fontconfig systemd-container iproute2 iptables && apt-get clean'
--run-command 'apt-get update -qq && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends git openssh-server openssh-client cloud-init python3 python3-pip curl ca-certificates xz-utils fonts-wqy-microhei fontconfig systemd-container iproute2 iptables sudo && apt-get clean'
--copy-in "$CODEX_BIN_DIR/codex:/usr/local/bin/"
--copy-in "$CODEX_BIN_DIR/codex-code-mode-host:/usr/local/bin/"
--copy-in "$CODEX_BIN_DIR/codex-resources:/usr/local/bin/"
@@ -298,6 +298,17 @@ VC_ARGS=(-a "$OUT/disk.raw"
--run-command 'mv /usr/local/bin/chv-sync.sh /usr/local/bin/chv-sync && chmod +x /usr/local/bin/chv-sync'
--copy-in "$SCRIPT_DIR/chv-data-mount.sh:/usr/local/bin/"
--run-command 'mv /usr/local/bin/chv-data-mount.sh /usr/local/bin/chv-data-mount && chmod +x /usr/local/bin/chv-data-mount'
# p2 第三轮(2026-11-09 第 2 轮复测根因 2):挂载是 root
# 做的,chv 身份 plain umount 恒 rc=32。唯一提权入口是这条
# sudoers 窄规则:chv 仅能以 root 跑 /usr/bin/umount
# /home/chv/workspace 这一条精确 argv(NOPASSWD,无密码;
# 无 shell、无其它命令、无挂载权——挂载仍只走 root 的
# hotplug/service 路径,host 侧 attach 轮询等它落地,不
# 另开 root 挂载通道)。0440 + visudo -cf 是构建门:
# 规则写错/权限不对即构建失败,不把坏 sudoers 带进镜像
# (坏 sudoers 会让整台客机 sudo 报错)。executor 的
# detach 以 `sudo -n` 调它(-n 绝不交互问密码)。
--run-command 'printf "chv ALL=(root) NOPASSWD: /usr/bin/umount /home/chv/workspace\n" > /etc/sudoers.d/chv-data-umount && chown root:root /etc/sudoers.d/chv-data-umount && chmod 0440 /etc/sudoers.d/chv-data-umount && visudo -cf /etc/sudoers.d/chv-data-umount'
--copy-in "$SCRIPT_DIR/chv-cell.py:/usr/local/bin/"
--copy-in "$SCRIPT_DIR/chv-broker.py:/usr/local/bin/"
--copy-in "$SCRIPT_DIR/chv-browser.py:/usr/local/bin/"