Files
codex-app-server/chv-guest/chv-data-mount.sh
T
host a859d731d5
ci / test (push) Failing after 47m51s
fix(chv-hybrid): p2 第三轮:attach 轮询等挂盘落地 + detach 走 sudo 窄规则 umount
第 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
文件在位)。
2026-11-09 08:17:01 +00:00

82 lines
4.2 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/sh
# chv-data-mount:把数据盘(LABEL=CHVDATA)挂到 /home/chv/workspace。
#
# 由镜像 99-chv-sync.cfg runcmd 在 chv-sync boot 之前调(final 阶段,
# 用户 chv 已存在,可 chown)。幂等:已挂载即返回;已有 CHVDATA
# 标签的盘即直接挂;无数据盘(copy 模式旧 VM 没有第三块盘)即
# 静默退出 0,不报错、不挡启动。
#
# 找盘不依赖 /dev/vd? 固定名:先按 LABEL 找;找不到则在所有整盘里
# 找一块无文件系统、不是根盘的盘——seed ISO 有 iso9660,不会被选
# 中;根盘本身无 fs,但按持有 / 分区的盘排除;分区(/dev/vda1 等)
# 不参与(glob 只取整盘名)。
#
# 阶段 1(feat-chv-opt-landing):工作区与浏览器 profile 的载体从
# 系统盘 overlay 里剥离出来,独立持久;快照只登记系统盘 overlay,
# 数据盘不进快照(见 cloud/chv/disk.py)。
set -u
MOUNT=/home/chv/workspace
LABEL=CHVDATA
log() { echo "chv-data-mount: $*"; }
mountpoint -q "$MOUNT" 2>/dev/null && { log "already mounted"; exit 0; }
DEV=$(blkid -L "$LABEL" 2>/dev/null || true)
if [ -z "$DEV" ]; then
ROOT_SRC=$(findmnt -n -o SOURCE / 2>/dev/null || echo "")
ROOT_DISK=$(printf '%s' "$ROOT_SRC" | sed -e 's/p[0-9]*$//' -e 's/[0-9]*$//')
for d in /dev/vd[a-z] /dev/sd[a-z] /dev/xvd[a-z]; do
[ -b "$d" ] || continue
[ "$d" = "$ROOT_DISK" ] && continue
TYPE=$(blkid -o value -s TYPE "$d" 2>/dev/null || echo none)
[ "$TYPE" = "none" ] || continue
DEV=$d
break
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 1; }
fi
mkdir -p "$MOUNT"
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、它写不进;
# 此处以 root 预建 profile 目录并定好属主与 0700,服务重启后
# 同一 profile 即登录态跨恢复保留。数据盘不在时浏览器服务
# 自己回落 /var/lib/chv-browser/profile(见 chv-browser.py)。
mkdir -p "$MOUNT/.chv-browser-profile" || true
chown chv-browser:chv-browser "$MOUNT/.chv-browser-profile" 2>/dev/null || true
chmod 0700 "$MOUNT/.chv-browser-profile" 2>/dev/null || true
# feat-chv-opt-landing(阶段 5):具名会话的 storage-state 落
# 数据盘 .chv-browser-sessions/<会话>/storage-state.json。工作区
# 根属 chv,chv-browser 自己建不进子目录,此处以 root 预建并
# 定好属主与 0700;浏览器服务自己建目录失败时只记
# storage_saved=false,不谎报 open/close 成功。
mkdir -p "$MOUNT/.chv-browser-sessions" || true
chown chv-browser:chv-browser "$MOUNT/.chv-browser-sessions" 2>/dev/null || true
chmod 0700 "$MOUNT/.chv-browser-sessions" 2>/dev/null || true
# 阶段 3 live 实证(2026-11-08,构建 #8 驱动):/home/chv 默认
# 0750 chv:chv,chv-browser 穿不过家目录去够数据盘 profile——
# ismount 判假、profile 静默回落本机盘(服务本身不报错)。
# 放到 0751:只放行穿过(+x),不放行列表(无 r),
# chv-browser 能进已知子目录,看不见家目录内容。
chmod 0751 /home/chv 2>/dev/null || true
log "mounted $DEV -> $MOUNT"
exit 0