zcbot/deploy/sandbox/probe_chromium_bisect.sh

65 lines
3.9 KiB
Bash

#!/usr/bin/env bash
# 二分定位:chromium 150(trixie)在 sandbox hardening 下首调即崩(SIGTRAP,rc=133)。
# 背景:2026-07-11 pids-limit 调 1024 后,全新容器里 mmdc 仍 "Failed to launch the
# browser process: Code: null"(task 9dcae061);dbus 报错是干扰项。本脚本把线上
# docker run 的 hardening flag 逐个关掉,找到翻转成功的那一个。
#
# 用法(服务器 repo 根,需 docker 权限):
# bash deploy/sandbox/probe_chromium_bisect.sh [镜像名] # 默 zcbot-sandbox:latest
# 每个 case 跑两条:mmdc 渲一张最小图(走 wrapper 注入的 puppeteer-config)+ 直接跑
# chromium(看裸 rc:133=SIGTRAP,139=SEGV)。全程忠实复现线上 exec 路径:
# --user zcbot + HOME=/tmp(executor_docker._CONTAINER_ENV 同款)。
set -u
IMG="${1:-zcbot-sandbox:latest}"
TEST_MMDC='printf "flowchart TB\n A-->B\n" > /tmp/d.mmd; timeout 60 mmdc -i /tmp/d.mmd -o /tmp/d.png >/tmp/mmdc.log 2>&1; rc=$?; if [ -s /tmp/d.png ]; then echo "[PASS] mmdc rendered png"; else echo "[FAIL] mmdc rc=$rc"; tail -4 /tmp/mmdc.log; fi'
TEST_CHROME='timeout 30 /usr/lib/chromium/chromium --no-sandbox --headless --disable-gpu --disable-dev-shm-usage --dump-dom about:blank >/tmp/c.log 2>&1; rc=$?; if [ $rc -eq 0 ]; then echo "[PASS] chromium rc=0"; else echo "[FAIL] chromium rc=$rc (133=SIGTRAP 124=timeout)"; grep -iv "dbus\|crashpad" /tmp/c.log | tail -4; fi'
echo "===== host env ====="
docker --version
uname -r
C=$(docker ps --filter "label=zcbot.product=sandbox" --format '{{.Names}}' | head -1)
if [ -n "$C" ]; then
docker inspect "$C" --format "live container $C: pids={{.HostConfig.PidsLimit}} shm={{.HostConfig.ShmSize}} readonly={{.HostConfig.ReadonlyRootfs}} nnp={{.HostConfig.SecurityOpt}}"
else
echo "(no live sandbox container)"
fi
run_case() {
local name="$1"; shift
echo
echo "===== $name ====="
docker run --rm --user zcbot -e HOME=/tmp "$@" --entrypoint bash "$IMG" -c "$TEST_MMDC"
docker run --rm --user zcbot -e HOME=/tmp "$@" --entrypoint bash "$IMG" -c "$TEST_CHROME"
}
# 线上 pool.py 的 hardening 全款(iptables init / network 与本问题无关,不带)
FULL=(--read-only --tmpfs /tmp:exec,size=512m,mode=1777 --shm-size=512m
--cap-drop=ALL --security-opt=no-new-privileges --pids-limit=1024
--memory=2g --cpus=1.0)
run_case "0 baseline 无 hardening(预期 PASS;FAIL=镜像/chromium 本身坏)"
run_case "1 full hardening 线上同款(预期复现 FAIL)" "${FULL[@]}"
run_case "2 full 去 no-new-privileges" \
--read-only --tmpfs /tmp:exec,size=512m,mode=1777 --shm-size=512m \
--cap-drop=ALL --pids-limit=1024 --memory=2g --cpus=1.0
run_case "3 full 去 read-only(tmpfs 保留)" \
--tmpfs /tmp:exec,size=512m,mode=1777 --shm-size=512m \
--cap-drop=ALL --security-opt=no-new-privileges --pids-limit=1024 --memory=2g --cpus=1.0
run_case "4 full 去 cap-drop=ALL" \
--read-only --tmpfs /tmp:exec,size=512m,mode=1777 --shm-size=512m \
--security-opt=no-new-privileges --pids-limit=1024 --memory=2g --cpus=1.0
run_case "5 full 去 memory/cpus 限制" \
--read-only --tmpfs /tmp:exec,size=512m,mode=1777 --shm-size=512m \
--cap-drop=ALL --security-opt=no-new-privileges --pids-limit=1024
run_case "6 full + seccomp=unconfined(只定根因,不作为修法)" "${FULL[@]}" \
--security-opt seccomp=unconfined
echo
echo "===== 判读 ====="
echo "0 FAIL => 镜像里的 chromium 150 本身跑不了,与 hardening 无关:pin 回旧版 chromium 或换 puppeteer 自带 chrome"
echo "1 PASS => docker run 层没问题,差异在 exec 路径/容器运行态:进活容器跑 probe_mermaid.sh 对比"
echo "1 FAIL, 某个 N PASS => 被去掉的那个 flag 就是根因,针对性放宽"
echo "只有 6 PASS => 宿主 docker 默认 seccomp profile 太老,不认 chromium 150 的新 syscall:升级 docker-ce 或给 sandbox 换新版 seccomp profile"
echo "全部 FAIL 含 6 => 再看内核版本(uname -r)与 chromium 150 的兼容性,考虑 pin 旧版"