feat(sandbox): am-sandbox —— 给命令套 Landlock 内核边界(界内可写、界外 EACCES)
「工作区档」此前名不副实:档位表写「本目录内可动、越界要问人」,而 pi 这一路只能按 **工具名**判(bash/write/edit 一律问人),因为命令的影响范围无法从文本静态判定 (`cd /工作区 && rm -rf /opt/x` 以"进工作区"开头)。pi 自己**故意**不内置沙箱 (docs/security.md §No Built-in Sandbox:进程内的部分沙箱会被误解成安全边界, "Real isolation needs to come from the operating system or a container boundary")—— dsh 是把沙箱放进自己的运行时;这个二进制补的是 pi 这一侧的**宿主**部分。 ## 语义 写:只有 `--rw` 列出的目录(含子树)与 `--rw-file` 列出的文件可写,其余写操作一律 EACCES(内核判)。读与执行不限制 —— Landlock 只做白名单式加法,要连读都挡住得靠 容器/挂载命名空间。套不上边界时 **fail closed**(退出码 126,不执行命令):静默裸跑 会让"档位=workspace"变成谎话,而谎话比做不到更危险。 ## 判据 - 行为 `deploy/check-sandbox.sh`:19 项全绿 —— 界内可写/子目录递归/rename+删除、 读界外、执行、写 /dev/null;界外新建/建目录/覆盖/删除/删目录/跨边界 rename/ 软链接逃逸/子进程继承;**对照**(不套边界时那些"必须被拒"的命令必须成功,否则 判据可能只是环境本来就只读);fail-closed 那条(rw 不存在 ⇒ 126 且命令未执行)。 - 算法 `cmd/am-sandbox` 单测:按 ABI 逐位裁剪、文件目标不得带目录类权限、 allowed ⊂ handled、参数解析。 ## 过程中撞到两个"看着能过"的坑 1. `--rw-file` 任意文件 → add_rule **EINVAL**:文件目标只能用文件类权限 (MAKE_*/REMOVE_*/REFER 是目录类),而错误信息只有 "invalid argument", 看不出是权限位不匹配。单测里"文件权限必须是 handled 的子集"就是拦它的。 2. ★ 判据自己翻车:`"$SB" … 2>&1 | grep -q "Permission denied"` 在 `set -o pipefail` 下 —— **grep 命中即退出 → 左侧 EPIPE 失败 → 整条管道判失败** ⇒ 7 条"界外必须被拒"全被判成"没被拒"。改成先收输出再匹配,顺带打印真实输出。 ## 已知边界(写进文件头 + 判据输出留痕,不假装没有) - 元数据(chmod/chown/utimes)不受 Landlock 管辖:界外文件**内容**改不了, 但**模式位**能改 - 网络出向不受限 - 界内**已存在**的、指向界外文件的硬链,经界内路径写入会改到界外(路径式沙箱固有) - 本机 ABI=2(无 TRUNCATE);实测 `truncate` 越界仍被拒(coreutils 先 open 写 ⇒ WRITE_FILE 挡住),所以那个缺口比纸面上窄 - **读不限制** ⇒ 以 root 跑的 agent 仍能读整机(含 /etc/agentmail/*.env)。沙箱在这里 的价值是"界外留不下脚印",不是"拿不到东西" —— 要后者得上容器 ## 接线 `redeploy-gateway.sh` / `install.sh` 在**边界判据真跑绿了**之后才装到 `/opt/agentmail/bin/am-sandbox`(装一个套不上边界的工具等于发空头支票)。 pi 桥的接线(按档位把 worker 套进边界)还没做 —— 见随后的报告。
This commit is contained in:
@ -170,6 +170,30 @@ if ! install -m 0755 "$REPO/deploy/service-failure-notify.mjs" "$PREFIX/bin/serv
|
||||
fi
|
||||
ok "故障通知脚本已装到 /opt/agentmail/bin/"
|
||||
|
||||
# 边界工具 am-sandbox(Landlock)—— 只有**本机真的能建立边界**时才装。
|
||||
#
|
||||
# 为什么不无条件装:`am-sandbox` 自己 fail closed(套不上边界就拒绝执行命令,退出码 126),
|
||||
# 所以在没有 Landlock 的机器上装它等于给"档位=workspace"发一张空头支票 —— 装一个
|
||||
# 用不了的工具比不装更坑。这里先真跑一遍边界判据(19 项:界内可写/界外被拒/软链接
|
||||
# 逃逸/子进程继承/对照),绿了才 install。
|
||||
#
|
||||
# 不把判据失败当致命错误:它可能只是内核老 —— 那种机器仍然可以部署网关,
|
||||
# 只是沙箱接线会在运行时 fail closed(那一步自己会叫)。
|
||||
say "1b. 边界工具(Landlock 沙箱)"
|
||||
SANDBOX_STAGE="/tmp/am-sandbox-build-$TS"
|
||||
rm -f "$SANDBOX_STAGE"
|
||||
if ( cd "$REPO/server" && go build -trimpath -o "$SANDBOX_STAGE" ./cmd/am-sandbox ); then
|
||||
if AM_SANDBOX="$SANDBOX_STAGE" bash "$REPO/deploy/check-sandbox.sh" >/tmp/am-sandbox-check.log 2>&1; then
|
||||
install -m 0755 "$SANDBOX_STAGE" "$PREFIX/bin/am-sandbox"
|
||||
ok "边界工具已装(判据 $(grep -c '\[ OK \]' /tmp/am-sandbox-check.log) 项全绿)"
|
||||
else
|
||||
warn "本机建立不起 Landlock 边界:不装 am-sandbox(见 /tmp/am-sandbox-check.log)"
|
||||
fi
|
||||
else
|
||||
warn "am-sandbox 构建失败(不影响网关本身)"
|
||||
fi
|
||||
rm -f "$SANDBOX_STAGE"
|
||||
|
||||
# ---------------------------------------------------------------- 2 静态检查与测试
|
||||
say "2. 构建前检查"
|
||||
if [ "$DRY_RUN" = 1 ]; then
|
||||
|
||||
Reference in New Issue
Block a user