fix(chv-hybrid): p2 第三轮:attach 轮询等挂盘落地 + detach 走 sudo 窄规则 umount
ci / test (push) Failing after 47m51s
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:
1 parent
0dc8328ecc
commit
a859d731d5
8 files changed
+351
-37
No files matched your search
+12
-1
@@ -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/"
|
||||
|
||||
@@ -37,6 +37,10 @@ BASE_CONTENTS = [
|
||||
"xz-utils", "fonts-wqy-microhei", "fontconfig", "codex",
|
||||
"codex-code-mode-host", "codex-resources/bwrap",
|
||||
"/app/host.py", "gt", "chv-sync",
|
||||
# p2 第三轮:sudo(dpkg 包,版本走 dpkg freeze)+
|
||||
# chv-data-umount sudoers 窄规则(版本随 repo_commit,
|
||||
# 见 check/generate_lockfile.py 的 DECLARED_SOURCES)。
|
||||
"sudo", "chv-data-umount",
|
||||
# 对齐 Muse P2:nspawn 单元启动器 + broker + 浏览器子代理。
|
||||
"systemd-container", "chv-cell", "chv-broker", "chv-browser",
|
||||
# feat-chv-opt-landing(阶段 1):数据盘挂载脚本。
|
||||
|
||||
@@ -33,6 +33,9 @@ DECLARED_SOURCES = {
|
||||
# feat-chv-opt-landing(阶段 1):数据盘挂载脚本,版本随
|
||||
# repo_commit(与 chv-sync 同款)。
|
||||
"chv-data-mount": "repo_commit",
|
||||
# p2 第三轮:卸载 sudoers 窄规则(build.sh 内联写入,
|
||||
# 内容随仓库走,版本随 repo_commit)。
|
||||
"chv-data-umount": "repo_commit",
|
||||
# feat-chv-opt-landing(阶段 2):客机运行时三分服务,版本
|
||||
# 随 repo_commit(与 chv-sync 同款)。
|
||||
"chv-daemon": "repo_commit",
|
||||
|
||||
@@ -36,13 +36,24 @@ if [ -z "$DEV" ]; then
|
||||
done
|
||||
fi
|
||||
|
||||
# p2 第三轮退出码契约(2026-11-09 第 2 轮复测):无数据盘
|
||||
# 即静默退出 0(启动期 no-op:干净池 VM 与 copy 模式旧 VM
|
||||
# 本来就没有第三块盘,boot 服务绝不能因此失败);但盘已
|
||||
# 找到后 mkfs/mount 任一步失败即 exit 1 并记日志——旧版
|
||||
# 全部失败路径 exit 0 是“成功假象”的另一半:host 侧曾以
|
||||
# chv 调本脚本并判 rc==0,chv 无权读 /dev/vd?、mkfs 必败
|
||||
# 却被报成成功。host 侧现已改走轮询等 root hotplug 路径
|
||||
# 落地(见 executor.py attach),不再以 chv 调本脚本;
|
||||
# 本脚本只由 root 跑(chv-data-mount.service 与
|
||||
# cloud-final runcmd),失败 loud 不挡启动(runcmd 行带
|
||||
# || true,service 失败可见于 systemctl status)。
|
||||
[ -n "$DEV" ] || { log "no data disk found, skip"; exit 0; }
|
||||
if [ "$(blkid -o value -s LABEL "$DEV" 2>/dev/null || echo "")" != "$LABEL" ]; then
|
||||
log "formatting $DEV as $LABEL"
|
||||
mkfs.ext4 -L "$LABEL" "$DEV" || { log "mkfs failed on $DEV"; exit 0; }
|
||||
mkfs.ext4 -L "$LABEL" "$DEV" || { log "mkfs failed on $DEV"; exit 1; }
|
||||
fi
|
||||
mkdir -p "$MOUNT"
|
||||
mount "LABEL=$LABEL" "$MOUNT" || { log "mount failed"; exit 0; }
|
||||
mount "LABEL=$LABEL" "$MOUNT" || { log "mount failed"; exit 1; }
|
||||
chown chv:chv "$MOUNT" || true
|
||||
# feat-chv-opt-landing(阶段 3):浏览器持久 profile 落数据盘。
|
||||
# chv-browser 以 chv-browser 身份跑,工作区根属 chv、它写不进;
|
||||
|
||||
@@ -54,6 +54,9 @@ DECLARED_SOURCES = {
|
||||
# feat-chv-opt-landing(阶段 1):数据盘挂载脚本,版本随
|
||||
# repo_commit(与 chv-sync 同款)。
|
||||
"chv-data-mount": "repo_commit",
|
||||
# p2 第三轮:卸载 sudoers 窄规则(build.sh 内联写入,
|
||||
# 内容随仓库走,版本随 repo_commit)。
|
||||
"chv-data-umount": "repo_commit",
|
||||
# feat-chv-opt-landing(阶段 2):客机运行时三分服务,版本
|
||||
# 随 repo_commit(与 chv-sync 同款)。
|
||||
"chv-daemon": "repo_commit",
|
||||
|
||||
Reference in new issue
Block a user