排查 Ubuntu 上 Claude Code 沙箱启动失败
Ubuntu 24.04 的 AppArmor 用户命名空间限制,可能导致依赖已安装的 Claude Code 沙箱仍无法启动。文章介绍新版诊断提示,并讨论全局关闭保护之外的较小范围调整及其局限。
AI 深度解读
Ubuntu 24.04 上,Claude Code 的沙箱启动失败,未必是少装了依赖。即使 bubblewrap 和 socat 都在,系统默认启用的 AppArmor 限制也可能让它无法工作;更容易被忽略的是,启动失败后,命令默认会回到普通权限审批流程。
作者解释了这道限制为什么会挡住沙箱,以及两种修复方式各自放开了什么。最有用的结论是:给 bwrap 单独放行比关闭整机限制更收敛,但它仍然留着一个其他程序可以借用的入口。
依赖装齐了,为什么沙箱还是起不来
在 Linux 和 WSL2 上,Claude Code 的 Bash 沙箱依赖 bubblewrap 隔离文件系统,再通过 socat 把流量转交给沙箱代理。沙箱命令运行在独立的网络命名空间里,不能直接连接原来的网络。
bubblewrap 不靠 root 身份启动,旧的 setuid 模式也已移除。它依赖用户命名空间(user namespace):进程在宿主系统中仍是普通用户,进入新命名空间后,却能拥有内部的用户 ID 0,以及管理该命名空间资源所需的能力。
这个「内部 root」的权限有范围,不能直接等同于宿主机 root,但足以帮助沙箱建立私有文件系统视图和网络环境。
Ubuntu 限制的恰好是这些能力。相关机制从 Ubuntu 23.10 引入,自 24.04 LTS 起默认开启,对应 Launchpad #2046477。开关是 kernel.apparmor_restrict_unprivileged_userns,默认值为 1。
它并非一律禁止创建用户命名空间,而是允许普通、未受专属配置约束的程序创建命名空间,随后拒绝它使用内部的 capabilities。只有程序自己的 AppArmor profile 包含 userns, 规则,才会获得完整能力。
于是 bubblewrap 可以创建命名空间,却无法完成需要 CAP_SETUID 的 uid_map 写入。王冠戴上了,管理权限没拿到。

这项限制有安全动机。Canonical 引用的 Google 报告称,其观察到的漏洞利用中,44% 的利用链需要非特权用户命名空间。这个数字指向特定报告观察到的样本,不能解读成所有 Linux 攻击的占比。
先确认失败原因,再选择放开多大范围
Claude Code 从 2.1.310 开始,才会在 /sandbox 和启动警告中明确指出 Ubuntu 的这项 AppArmor 限制。更早版本没有在启动时检查它,因此面板没报出来,也不能排除这个原因。
排查先运行 sysctl kernel.apparmor_restrict_unprivileged_userns。按 Anthropic 文档,输出 kernel.apparmor_restrict_unprivileged_userns = 1 表示限制开启;其他输出应转向别的原因,并可通过 /feedback 报告。
作者还给出两个复现命令:unshare -U -r -m /bin/sh,以及 bwrap --unshare-user --unshare-net --ro-bind / / true。前者在 Qualys 的记录中报出 unshare: write failed /proc/self/uid_map: Operation not permitted;后者可能出现 bwrap: setting up uid map: Permission denied 或 bwrap: loopback: Failed RTM_NEWADDR: Operation not permitted。
这里必须分清证据来源:作者没有亲自在虚拟机里运行这些复现步骤,输出来自 Qualys 和 Anthropic 文档。
第一种修复是执行 sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0,临时关闭限制,重启失效。持久化方式是执行 echo 'kernel.apparmor_restrict_unprivileged_userns=0' | sudo tee /etc/sysctl.d/60-apparmor-namespace.conf,随后重启。
它影响整台机器上的所有程序。专门跑 Agent 的一次性虚拟机,可能接受这个取舍;同时保存 SSH 密钥、运行浏览器的开发电脑,则需要考虑更大的影响范围。
第二种方式,是为 /usr/bin/bwrap 添加专属 AppArmor profile:
sudo tee /etc/apparmor.d/bwrap > /dev/null <<'EOF'
abi <abi/4.0>,
include <tunables/global>
profile bwrap /usr/bin/bwrap flags=(unconfined) {
userns,
include if exists <local/bwrap>
}
EOF
sudo systemctl reload apparmor
关键是 userns,,它恢复命名空间内部能力;flags=(unconfined) 则让 bwrap 在其他方面保持不受该 profile 限制。
配置按路径匹配。先用 command -v bwrap 检查实际位置:Claude Code 默认运行 PATH 中第一个 bwrap,除非托管设置指定了 sandbox.bwrapPath。原文转述的文档建议,路径不是 /usr/bin/bwrap 时采用 sysctl 方案。
但专属 profile 不能理解成「只允许 Claude Code 使用」。任何本地程序都可能调用 /usr/bin/bwrap,借它启动任意二进制并获得完整用户命名空间。Ubuntu Desktop 24.04 默认安装了 bwrap,却没有为它提供这个放行 profile,原因就在这里;unshare 和 setns 也未获放行。

沙箱失败后,任务应该继续吗
默认情况下,沙箱不可用时,Claude Code 会在无沙箱状态下运行命令,沿用允许规则和权限模式,未被覆盖的操作再等待审批。
审批仍然存在,但它与文件系统、网络隔离承担的职责不同。若团队把沙箱当作运行前提,就不能只依赖「已经开启」这个设置,还要明确启动失败时的行为。
原文建议在 ~/.claude/settings.json 中加入:
{
"sandbox": {
"enabled": true,
"failIfUnavailable": true
}
}
要看的是 failIfUnavailable:按文档,设为 true 后,沙箱不可用会阻止 Claude Code 启动。
另一个开关则要谨慎对待。Claude Code 2.1.305 新增了 sandbox.allowNestedUserNamespaces,用于 Chromium 等需要在沙箱内部再创建用户命名空间的工具。文档警告,开启后,沙箱命令中的代码可能最终在沙箱外运行。因此,浏览器测试失败不能直接成为开启它的理由。
原文还介绍了作者开发的 Hermes IDE。它直接启动 Agent 自己的 CLI,支持 Linux 的 .deb 和 AppImage,也支持 macOS、Windows;Claude Code 仍读取相同的配置文件,仍会撞上同一道 AppArmor 限制。
Hermes 的 ⌘N,在 Linux 上为 Ctrl+Shift+N,可以为任务创建独立的 hermes/ 分支和 git worktree。这能减少多个 Agent 共用检出目录造成的文件冲突,却不能阻止命令读取目录外的文件。Hermes 自身不提供 Agent 沙箱,安全层仍由厂商的沙箱承担。
我的判断
这篇最有价值的地方,是把命名空间、权限审批、worktree 三种容易混在一起的机制拆开了。尤其是默认回退行为:界面中启用了沙箱,不代表当前命令就一定受到隔离。
给 bwrap 单独配置 profile,在规则覆盖范围上确实比关闭全局 sysctl 更小。但它是通用工具,其他程序也能调用,不能把这个方案当成只对某个 Agent 开放的白名单。作者没有提供两种方案的攻击面量化对照,「更收敛」有机制依据,具体安全收益则没有测量结果。
Ubuntu 的限制也不是完整防线。Qualys 在 2025 年 3 月公布过通过 aa-exec、busybox,以及向 Nautilus 预加载库的三种绕过方式;这些绕过获得的是命名空间内部能力,并非宿主机 root。原文称 Ubuntu 的说明覆盖了关闭前两种路径的加固措施,但没有明确列出本文所述环境的补丁状态,不能据此判断某台机器今天是否仍可绕过。
Hermes 部分带有作者推广自己产品的立场。它以 BSL 1.1 提供可查看的源码,不能因此直接等同于通常意义上的开源软件;不过,作者明确承认 worktree 不构成安全边界,这个区分是准确且有用的。
能拿来做什么
- 在 Ubuntu 24.04 或 WSL2 上遇到沙箱启动错误时,先核对 Claude Code 版本、sysctl 输出和具体报错,再决定是否调整 AppArmor,避免反复重装 bubblewrap、socat。
- 如果日常 AI 编程要求命令必须在沙箱中运行,就配置
sandbox.failIfUnavailable: true,让启动失败成为明确中断,并确认后续会话实际启用了隔离。 - 在共享开发机上选择 bwrap profile 前,先检查实际二进制路径,并把「其他本地程序也能借用 bwrap」纳入判断;一次性 Agent 虚拟机可以单独评估全局 sysctl 方案。
- 遇到 Chromium 相关测试失败时,先确认是否由嵌套用户命名空间引起,再评估
sandbox.allowNestedUserNamespaces;并行任务使用 worktree 时,也应另外确认沙箱状态。