anthropics / anthropics/claude-code
[BUG] Bash sandbox fails to initialize on Ubuntu 24.04+ - conflict with the distro's default bwrap-userns-restrict AppArmor profile (not a Claude Code regression)
- Ngôn ngữ chính
- Python
- Star
- 145k
- Fork
- 23.1k
- Chỉ số merge pull request
- Chỉ số pull request đang chờ
Mô tả
### Preflight Checklist
- [x] I have searched [existing issues](https://github.com/anthropics/claude-code/issues?q=is%3Aissue%20state%3Aopen%20label%3Abug) and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
### What's Wrong?
On a stock Ubuntu 26.04 host with `bubblewrap` and `socat` installed exactly as the
sandboxing docs describe, the Bash sandbox cannot start. **Every** Bash tool call fails
during sandbox setup, before the command itself ever runs, leaving the Bash tool entirely
unusable.
**To be clear up front: this is not a regression in Claude Code.** Claude Code 2.1.234
behaves the same as earlier versions here. The breaking change comes from Ubuntu's
progressive user-namespace hardening:
- **Ubuntu 24.04** introduced `kernel.apparmor_restrict_unprivileged_userns = 1`, which
blocks unprivileged processes from creating user namespaces. The Claude Code docs already
cover this and recommend adding an AppArmor profile for `bwrap`.
- **Later Ubuntu releases** (26.04 here) now *ship and enable by default* a profile at
`/etc/apparmor.d/bwrap-userns-restrict`. This moves the goalposts: `bwrap` itself is now
permitted to create a user namespace, so the documented check and remedy look satisfied -
but the profile stacks every process `bwrap` executes into a child profile,
`unpriv_bwrap`, which contains `audit deny capability,`.
Claude Code's sandbox then tries to create a **nested** user namespace inside `bwrap` (its
`apply-seccomp` step). That needs `CAP_SYS_ADMIN`, which `unpriv_bwrap` has just stripped
by design. The profile's own comment confirms the intent:
> `bwrap` is allowed access to user namespaces and capabilities within the user namespace,
> but its children do not have capabilities, blocking `bwrap` from being able to be used to
> arbitrarily by-pass the user namespace restrictions.
So Ubuntu deliberately allows exactly one level of user-namespace creation and forbids
nesting a second - and Claude Code's sandbox requires that second level.
Two things make the impact worse than a plain incompatibility:
1. **The documented AppArmor fix no longer applies.** The docs suggest creating
`profile bwrap /usr/bin/bwrap flags=(unconfined) { userns, ... }`, but
`bwrap-userns-restrict` *already declares* `profile bwrap /usr/bin/bwrap`. Two
declarations for the same binary don't compose - the shipped profile has to be disabled
first - so following the docs doesn't fix it.
2. **With Strict sandbox mode active, there is no fallback.** The unsandboxed-retry escape
hatch is suppressed and `dangerouslyDisableSandbox: true` is ignored (correctly, per the
docs). The result is a completely non-functional Bash tool, reported only as a low-level
error that never mentions AppArmor, bwrap, or sandbox misconfiguration. Recovering
required editing settings through the file tools, since Bash was dead.
### What Should Happen?
Either of the following would be acceptable:
1. The sandbox initializes and Bash commands run confined, on a stock Ubuntu install with
the documented dependencies present; **or**
2. Claude Code detects that it cannot create the nested namespace and reports a clear,
actionable diagnostic naming AppArmor, the `unpriv_bwrap` profile, and the required
capability - ideally at startup or in `/doctor` / the `/sandbox` panel, rather than as a
per-call failure with no path forward.
What should *not* happen is the current outcome: an opaque `apply-seccomp` error on every
Bash invocation, with no fallback and no indication of the cause.
### Error Messages/Logs
```shell
Every Bash tool call fails with one of the following, depending on configuration.
Default configuration (`{"sandbox": {"enabled": true}}`):
apply-seccomp: write /proc/self/setgroups (nested userns is capability-restricted; caller must provide CAP_SYS_ADMIN): Permission denied
With `"enableWeakerNestedSandbox": true` - the setting takes effect and changes the code
path, but does not resolve the problem:
apply-seccomp: unshare(CLONE_NEWUSER): Invalid argument
**Kernel audit log** - the decisive evidence, timestamped to the exact moments of the
failed Bash calls (PIDs redacted):
kernel: audit: type=1400 audit(...): apparmor="DENIED" operation="capable" class="cap" \
profile="unpriv_bwrap" pid= comm="3" capability=21 capname="sys_admin"
`capability=21` is `CAP_SYS_ADMIN`; the profile is `unpriv_bwrap`.
**The relevant part of the shipped Ubuntu profile** (`/etc/apparmor.d/bwrap-userns-restrict`):
profile bwrap /usr/bin/bwrap flags=(attach_disconnected,mediate_deleted) {
allow capability, # bwrap itself: capabilities allowed
allow userns,
...
allow pix /** -> &bwrap//&unpriv_bwrap, # children stacked into unpriv_bwrap
}
profile unpriv_bwrap flags=(attach_disconnected,mediate_deleted) {
allow userns,
...
audit deny capability, # ...where all capabilities are stripped
}
**Both profiles confirmed loaded and enforcing:**
$ sudo aa-status | grep -E 'bwrap|unpriv_bwrap'
bwrap
unpriv_bwrap
/usr/bin/bwrap () bwrap
**Related but distinct**, worth noting since it is easy to hit: if
`sandbox.filesystem.denyRead` includes the filesystem root (e.g. `["/**", "~/**"]`), the
sandbox hides `/bin`, `/usr`, and `/lib` from the mount view and Bash fails with:
bwrap: execvp /bin/bash: No such file or directory
This one is arguably user error, but the message gives no hint that a `denyRead` rule
caused it.
```
### Steps to Reproduce
1. Install Ubuntu 24.04 or later (reproduced on 26.04) with the default AppArmor policy -
i.e. `kernel.apparmor_restrict_unprivileged_userns = 1` and the `bwrap-userns-restrict`
profile loaded. Both are distribution defaults; no hardening was added by hand.
2. `sudo apt-get install bubblewrap socat`
3. Confirm the dependencies are healthy - `/sandbox` shows no Dependencies tab, and a
standalone bubblewrap isolation test works correctly:
```bash
bwrap --unshare-all --die-with-parent --ro-bind /tmp /tmp cat /etc/hostname
# correctly fails with "execvp: No such file or directory" - proving the mount
# namespace really is isolated, so bwrap itself is functional
```
4. Enable the sandbox in `.claude/settings.json`:
```json
{ "sandbox": { "enabled": true } }
```
5. Restart Claude Code and ask it to run any Bash command, e.g. `echo ok`.
**Result:** the command fails with the `apply-seccomp` error above. Reproduced across
several full process restarts.
### Claude Model
Opus
### Is this a regression?
No, this never worked
### Last Working Version
_No response_
### Claude Code Version
2.1.234
### Platform
Anthropic API
### Operating System
Ubuntu/Debian Linux
### Terminal/Shell
Other
### Additional Information
## Environment
| Component | Version |
|---|---|
| Claude Code | 2.1.234 |
| OS | Ubuntu 26.04 LTS |
| Kernel | 7.0.0-29-generic |
| Node.js | v24.16.0 |
| bubblewrap | 0.11.1 (`0.11.1-1ubuntu0.1`) |
| socat | 1.8.1.1 |
| apparmor_parser | 5.0.0~beta1 |
```
kernel.unprivileged_userns_clone = 1
kernel.apparmor_restrict_unprivileged_userns = 1
```
Running as a normal unprivileged user - not root, not in a container, on bare metal.
## Hypotheses ruled out
Listing these so this isn't triaged as one of the more common setup problems:
| Hypothesis | Result |
|---|---|
| `bubblewrap` not installed | Installed (0.11.1), on `PATH` |
| `socat` not installed | Installed (1.8.1.1); installing it did not fix the issue |
| Optional `@anthropic-ai/sandbox-runtime` seccomp filter missing | Not installed globally; `apply-seccomp` is bundled in the Claude Code binary and not resolvable via `which` or `find` |
| `bwrap` can't create user namespaces at all | False - standalone isolation test succeeds (step 3 above) |
| AppArmor blocks `bwrap` itself | False - `bwrap` has its own profile with `allow capability` and `allow userns`; only its *children* are restricted |
| Process already inside a user namespace (e.g. via `su`) | False - invoking shell and the `claude` process share the same host userns inode `user:[........837]` |
| Running inside a container | False - bare metal |
| Stale session/config state | False - reproduced across multiple full restarts |
| Multithreaded process can't call `unshare(CLONE_NEWUSER)` | Plausible for the `EINVAL` variant specifically, but the audit log identifies the AppArmor capability denial as the primary cause |
## Workaround
Adding a rule-priority override in the distribution's `local/` include directory clears the
capability denial without replacing the shipped profile. The shipped `unpriv_bwrap` block
already ends with `include if exists `. Requires AppArmor ≥ 4 for
`priority=` support:
```bash
sudo tee /etc/apparmor.d/local/unpriv_bwrap > /dev/null <<'EOF'
priority=1 capability sys_admin,
EOF
sudo apparmor_parser -r /etc/apparmor.d/bwrap-userns-restrict
```
**This is a real security trade-off, not a free fix.** It re-opens the nested-user-namespace
escalation path Ubuntu's hardening was written to close, for *all* `bwrap` consumers on the
machine (Flatpak, GNOME image loaders, and others), not just Claude Code. It should not be
presented to users as a routine setup step without that caveat. It is narrower than the
`flags=(unconfined)` profile the docs currently suggest, which would remove capability
confinement for `bwrap` children entirely.
## Documentation issues
1. **The recommended profile collides with the shipped one.** Both declare
`profile bwrap /usr/bin/bwrap`; the distro profile must be disabled for the docs' profile
to apply. The docs don't mention this.
2. **It's broader than necessary.** `flags=(unconfined)` removes confinement for all `bwrap`
children system-wide, when the actual blocker is a single capability.
3. **The gating check is now misleading.** The docs say to run
`sysctl kernel.apparmor_restrict_unprivileged_userns` and apply the profile if it returns
`1`. It does return `1` here - but the documented remedy is no longer the right one,
because Ubuntu now ships a `bwrap`-specific profile that already grants `userns` to
`bwrap`. The failure has moved down a level, to the stacked child profile.
## Suggested improvements
1. Detect this condition and surface a diagnostic naming AppArmor, `unpriv_bwrap`, and the
required capability - in `/doctor` or the `/sandbox` Dependencies tab - instead of the raw
`apply-seccomp` error.
2. Fail at startup rather than per-call. A sandbox that cannot initialize under Strict mode
bricks Bash with no recovery path from inside the session.
3. Update the AppArmor documentation for Ubuntu releases shipping `bwrap-userns-restrict`,
including the `local/unpriv_bwrap` override and its trade-off.
4. Consider whether the nested user namespace can be avoided - the outer `bwrap` namespace
already establishes the isolation boundary, and dropping the nesting requirement would
make Claude Code compatible with Ubuntu's default hardening rather than requiring users to
weaken it.
## Possibly separate issue
`sandbox.allowUnsandboxedCommands: true` set in project-level `.claude/settings.json` did not
appear to take effect: `/sandbox` continued to report Strict sandbox mode, and an explicit
`dangerouslyDisableSandbox: true` call still failed identically. This may be correct
precedence behavior (a higher-precedence source enforcing strict mode) or a settings-scope
bug - it was not isolated further, and is reported here only as an observation.
Hướng dẫn đóng góp
Chưa lập chỉ mục được hướng dẫn đóng góp cho kho mã nguồn này
Hướng nghiên cứu
Start by reproducing the failure using the documented bubblewrap and socat setup, then inspect the sandbox initialization path around the reported apply-seccomp errors and the /doctor or /sandbox entry points. Done means Bash either initializes under Ubuntu's bwrap-userns-restrict profile or presents an actionable diagnostic naming the blocking AppArmor profile and capability.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Đánh giá
- Công nghệ
- bash, linux, ubuntu
- Lĩnh vực
- cli, operating-systems, security
- Loại issue
- Lỗi
- Độ khó
- 4/5
- Thời gian dự kiến
- 3-5 ngày
- Mức độ hoạt động
- Sôi nổi
- Độ rõ ràng
- Cần làm rõ
- Mức phù hợp với người mới
- 35/100