#!/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 旧版"