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)
- Lingua principale
- Python
- Stelle
- 145k
- Fork
- 23.1k
- Metriche di merge delle PR
- Metriche PR in attesa
Descrizione
### 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.
Guida per i contributori
Nessuna guida per i contributori indicizzata per questo repository
Direzione di ricerca
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.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Valutazione
- Stack tecnologico
- bash, linux, ubuntu
- Ambito
- cli, operating-systems, security
- Tipo di issue
- Bug
- Difficoltà
- 4/5
- Tempo stimato
- 3-5 giorni
- Stato di attività
- Attiva
- Chiarezza
- Da chiarire
- Idoneità per principianti
- 35/100