/var/lib/docker/volumes/venture_venture-workspace/_data/ ├── user-alice/ ├── user-bob/ └── probe-user-1/
用 named volume 而非 bind mount:容器重建不丢数据,也不把宿主目录结构暴露给容器。
volumes: - venture-workspace:/workspace # rw,每用户一个子目录 容器内实际: /workspace/ drwxr-xr-x venture venture ├── user-alice/ ← alice 的工作区 ├── user-bob/ ← bob 的工作区 └── probe-user-1/ /app/app/worker/kb/ ← 只读知识库(镜像里 COPY 进去,不在 volume) /app/app/ ← 我们的源码(agent 不该看到)
| 项 | 值 |
|---|---|
| 容器用户 | venture uid=10001(非 root) |
| 目录属主 | venture:venture(volume 首建时 chown 过) |
| 用户目录何时建 | 每轮请求带 user_id 进来时 ensure_user_workspace() 建(内含 sanitize 防 ../ 穿越) |
bubblewrap 为每一轮对话起一个沙盒,把该用户目录 bind 进去。让 alice 的 agent 自己汇报:
pwd → /workspace/user-alice ← 落脚点就是自己的工作区 whoami → venture ls -la → a2.txt alice.txt f.txt .claude/ ← 只有自己的文件 ls /workspace → user-alice ← ⭐ 只有自己!别人的目录不存在 ls / → app bin ... workspace ← 根目录还在(只读)
| agent 尝试 | 结果 |
|---|---|
| 在自己目录读写 / 跑脚本 | ✅ 正常 |
ls /workspace 想看有谁 | ✅ 只看到自己 |
cat /workspace/user-bob/xx | ✅ No such file or directory(不是"拒绝访问",是根本不存在) |
| 自行关沙盒重试 | ✅ 被 allowUnsandboxedCommands:false 挡住 |
注:根文件系统仍可见(只读),沙盒的边界是写权限 + 用户目录可见性,不是把整个系统藏起来。
| 配置 | 作用 |
|---|---|
cwd=/workspace/{user_id} | agent 落脚点 |
filesystem.allowWrite=[自己目录] | 只能写自己的 |
filesystem.denyRead=[/workspace]filesystem.allowRead=[自己目录, kb] | ⭐ 工作区根整体禁读,只放行自己的 + 知识库 → 别人的目录消失 |
allowUnsandboxedCommands=false | ⭐ 禁止 agent 逃出沙盒重试(实测抓到过它这么干) |
compose security_opt: systempaths=unconfined | 解除 docker 对 /proc 屏蔽,bwrap 才能起(替代 privileged) |