openai / openai/codex

[app][linux] Documented AppArmor remediation breaks bubblewrap when another vendor's profile is already installed

Open
#38,715 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

app documentation sandbox
Dominant language
Rust
Stars
125k
Forks
19.5k
PR merge metrics
PR metrics pending

Description

bwrap-collision-test.sh

What version of the Codex App are you using (From “About Codex” dialog)?

Version 26.810.50856 Aug 14, 2026 chatgpt unified desktop app

What subscription do you have?

chatgpt plus

What platform is your computer?

Linux 6.8.0-137-generic x86_64 x86_64 Ubuntu 24.04.4 LTS

What issue are you seeing?

Component: ChatGPT desktop app for Linux (preview) — documentation, and packaging Affected doc: https://learn.chatgpt.com/codex/sandboxing (Linux/WSL2 prerequisites) Severity: Low direct impact on Codex; moderate collateral impact on the host system Status: Reproducible, with an automated test harness


Summary

The bwrap-userns-restrict AppArmor profile that OpenAI's Codex sandboxing documentation instructs users to install declares an attachment to /usr/bin/bwrap. If another package has already installed a profile attached to the same binary — Anthropic's claude-desktop package does exactly this — neither profile attaches, and bubblewrap stops working for all unconfined callers on the machine.

The documentation does gate the step on a symptom — "On Ubuntu 24.04, Codex may still warn that it can't create the needed user namespace" — but that gate is unreliable in practice. A user who sees any userns-related warning, or who is following the Linux setup steps pre-emptively, will apply it. There is no check for whether bubblewrap already works, and applying it on a working system causes the failure it is intended to prevent.

Codex itself is not affected — verified by measurement, see Impact below. The damage falls on other bubblewrap consumers invoked from unconfined processes. Flatpak is the most widely deployed of these; it was not directly tested here, but it uses bubblewrap for exactly this operation.


Platform details

Item | Value -- | -- Distribution | Ubuntu 24.04.4 LTS Kernel | 6.8.0-137-generic Architecture | amd64 Desktop / session | GNOME, X11 ChatGPT desktop package | chatgpt 26.810.50856 (About panel: released 2026-08-14) Install root | /usr/lib/chatgpt Installed size | 1.29 GiB, 5153 files Package source | Official .deb, OpenAI apt repository apt repository | https://persistent.oaistatic.com/codex-app-prod/linux/deb suite stable Bundled Chromium | 151.0.7922.137 Bundled Node.js | v24.14.0 app.asar declared Electron | 42.3.0 bubblewrap | 0.9.0 (/usr/bin/bwrap, Ubuntu package) kernel.apparmor_restrict_unprivileged_userns | 1 (Ubuntu 23.10+ default) Other AI desktop app present | claude-desktop 1.26832.0 Test date | 2026-08-15

Kernel audit confirms both the unload and the restore:

apparmor="STATUS" operation="profile_remove" profile="unconfined" name="bwrap"
apparmor="STATUS" operation="profile_remove" profile="unconfined" name="unpriv_bwrap"
apparmor="STATUS" operation="profile_load"   profile="unconfined" name="bwrap"
apparmor="STATUS" operation="profile_load"   profile="unconfined" name="unpriv_bwrap"

This sequence was run twice on separate occasions, with identical results. Profile state reported by aa-status at each stage matched expectation both times, and both bwrap profiles were confirmed loaded in enforce mode (0 in complain mode) before the baseline test.

Minimal manual reproduction (restores itself; run on a machine with both profiles present):

bash
# baseline — expect failure
bwrap --ro-bind / / --unshare-user --unshare-pid true && echo OK

# remove Ubuntu's profile: move the FILE first, or a reload re-adds it
sudo cp -a /etc/apparmor.d/bwrap-userns-restrict /root/bwrap.bak
sudo mv /etc/apparmor.d/bwrap-userns-restrict /tmp/bwrap-test
sudo apparmor_parser -R /tmp/bwrap-test

# retest as an unprivileged user — expect success
bwrap --ro-bind / / --unshare-user --unshare-pid true && echo OK

# restore
sudo mv /root/bwrap.bak /etc/apparmor.d/bwrap-userns-restrict
sudo apparmor_parser -r /etc/apparmor.d/bwrap-userns-restrict

A fuller self-restoring harness (bwrap-collision-test.sh) checks the baseline first, drops to $SUDO_USER for the test since running it under sudo passes regardless of policy, and restores via an EXIT/INT/TERM trap so an interrupt cannot leave the system modified.


Impact

Codex is unaffected. Verified by having the agent inspect its own shell:

bash
grep Seccomp /proc/self/status; readlink /proc/self/ns/user; id -u

Two independent runs, on different days, while the shell bwrap test was failing:

run 1 (2026-08-14)          run 2 (2026-08-15)
Seccomp:         2          Seccomp:         2
Seccomp_filters: 1          Seccomp_filters: 1
user:[4026533805]           user:[4026533795]
1000                        1000

Host initial user namespace on the same machine: user:[4026531837].

Both runs are in a non-initial user namespace, and the namespace inode differs between them, indicating a fresh user namespace per agent invocation rather than a single long-lived one created at app start.

Seccomp-BPF filter active, non-initial user namespace, running unprivileged. Observed: the Codex agent sandbox worked correctly at the same time as the shell bwrap test was failing on the same machine. Likely explanation: the app is confined by the /etc/apparmor.d/chatgpt profile the package ships, so bwrap spawned from within it does not follow the unconfined transition path. This explanation was not separately instrumented.

Other bubblewrap consumers break. Anything invoking bwrap from an unconfined process fails. Flatpak is the most significant such consumer and would be expected to be affected, though it was not directly tested on this machine. The user has no indication that following the ChatGPT documentation caused it.

Diagnostic confusion. Because the obvious verification command fails while the sandbox it is meant to verify is working, users are likely to conclude their Codex sandbox is broken when it is not — or to disable the AppArmor restriction system-wide, which is materially worse than the original problem.


Instructions that should not be followed unconditionally

From the "Ubuntu AppArmor note" in the Linux/WSL2 prerequisites at https://learn.chatgpt.com/codex/sandboxing. The doc introduces it with "On Ubuntu 24.04, Codex may still warn that it can't create the needed user namespace after bubblewrap is installed. Copy and load the extra profile:"

bash
sudo apt update
sudo apt install apparmor-profiles apparmor-utils
sudo install -m 0644 \
  /usr/share/apparmor/extra-profiles/bwrap-userns-restrict \
  /etc/apparmor.d/bwrap-userns-restrict
sudo apparmor_parser -r /etc/apparmor.d/bwrap-userns-restrict

This is correct on a system with no other bwrap profile, and harmful on a system that already has one. The preceding sentence in the doc is also worth revisiting: it states that on Ubuntu 25.04 the bwrap-userns-restrict profile ships in the apparmor package at /etc/apparmor.d/bwrap-userns-restrict. On such a system the profile may already be present, making a manual copy redundant and a manual apparmor_parser -r potentially disruptive.

The documentation also offers:

bash
sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0

This works but disables the restriction system-wide for every application, not only for bubblewrap. It should carry a stronger warning, or be removed in favour of the profile approach plus a precondition check.


How to check before applying

bash
sudo aa-status | grep -i bwrap

If any profile is already listed for bwrap, verify whether it grants userns:

bash
grep -l 'userns' /etc/apparmor.d/*bwrap* 2>/dev/null

And test directly, as an unprivileged user (never under sudo, which succeeds regardless of policy):

bash
bwrap --ro-bind / / --unshare-user --unshare-pid true && echo OK

If that prints OK, no AppArmor change is needed and the documented step should be skipped.


Workaround

If the collision has already occurred, disable Ubuntu's profile using the distribution's own mechanism, which survives systemctl reload apparmor:

bash
sudo apparmor_parser -R /etc/apparmor.d/bwrap-userns-restrict
sudo mkdir -p /etc/apparmor.d/disable
sudo ln -sf /etc/apparmor.d/bwrap-userns-restrict /etc/apparmor.d/disable/
bwrap --ro-bind / / --unshare-user --unshare-pid true && echo OK

This returns the system to the state Ubuntu ships by default.

Caveat for users applying this workaround: if the other vendor's package is later uninstalled, its profile is removed with it and nothing will grant userns to bubblewrap. Re-enable Ubuntu's profile at that point by deleting the symlink and re-parsing.


Suggested fixes

In rough order of value:

  1. Add a precondition check to the documentation. Before the install step, instruct the user to run bwrap --ro-bind / / --unshare-user --unshare-pid true and skip the remediation if it succeeds. This is a one-sentence change and resolves the issue for new users.
  2. Document the collision. Note that a profile from another package may already attach to /usr/bin/bwrap, and that adding a second one breaks both.
  3. Consider shipping the profile from the package rather than documenting a manual step, following the pattern Google Chrome and claude-desktop use: write it from postinst, gate on AppArmor 4.0 by testing for the abi/4.0 feature file, check for an existing attachment first, and never fail the install on parser errors.
  4. Consider adding bubblewrap to Depends:. The current Depends: line contains 42 comma-separated entries, all libraries and desktop-integration helpers, and does not include bubblewrap. A bundled fallback helper exists, so this is a robustness improvement rather than a defect fix.
  5. Reconsider the sysctl suggestion, or attach a clearer warning that it disables the protection for the entire system.
  6. Improve the in-app diagnostic. If Codex emits a startup warning when it cannot create a user namespace, that warning could point at the audit log and at the correct in-process verification rather than at a shell command whose result does not reflect the agent's actual confinement.

Scope and limitations

  • Tested on one machine, one distribution, one architecture, one app build. Not verified on Ubuntu 26.04, Debian 13, Fedora 43/44, ARM64, or WSL2 — all of which the documentation's AppArmor guidance may also reach.
  • Not tested with bubblewrap absent. The documentation notes a bundled fallback helper; its behaviour under this collision is unknown.
  • Not tested under Wayland. All measurements were taken in an X11 session.
  • The unpriv_bwrap profile is also declared in bwrap-userns-restrict and is removed with it; its role in the contention was not separately isolated.

Notes

  • All observations are from a single machine. The collision requires a second vendor's profile to be present, so it will not reproduce on a clean system.
  • The specific second profile here comes from claude-desktop, but nothing about the mechanism is vendor-specific. Any package installing a profile attached to /usr/bin/bwrap produces the same result.
  • Reported in the spirit of a preview bug report. The Linux app is explicitly preview software, and the underlying Codex sandbox implementation is working correctly.
What steps can reproduce the bug?

full described in main report window

What is the expected behavior?

The bwrap-userns-restrict AppArmor profile that OpenAI's Codex sandboxing documentation instructs users to install declares an attachment to /usr/bin/bwrap. If another package has already installed a profile attached to the same binary — Anthropic's claude-desktop package does exactly this — neither profile attaches, and bubblewrap stops working for all unconfined callers on the machine.

The documentation does gate the step on a symptom — "On Ubuntu 24.04, Codex may still warn that it can't create the needed user namespace" — but that gate is unreliable in practice. A user who sees any userns-related warning, or who is following the Linux setup steps pre-emptively, will apply it. There is no check for whether bubblewrap already works, and applying it on a working system causes the failure it is intended to prevent.

Codex itself is not affected — verified by measurement, see Impact below. The damage falls on other bubblewrap consumers invoked from unconfined processes. Flatpak is the most widely deployed of these; it was not directly tested here, but it uses bubblewrap for exactly this operation.

Additional information

bwrap-collision-test.sh

Tests whether Ubuntu's bwrap-userns-restrict profile is COLLIDING with
another vendor's profile (e.g. claude-desktop-bwrap) that also attaches to
/usr/bin/bwrap — with the result that NEITHER attaches and bwrap runs
unconfined, falling into Ubuntu's restrictive unprivileged_userns profile.

It unloads Ubuntu's profile, retests, and ALWAYS restores the original state
via an EXIT trap — including on Ctrl-C or on error.

Usage: sudo ./bwrap-collision-test.sh

Duration: a few seconds. Fully reversible.

https://gist.github.com/knormoyle/43fbfc184e226e5c7c1b3bd18e7e8f9c

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Review the Linux/WSL2 prerequisites in the linked Codex sandboxing documentation and inspect the attached bwrap-collision-test.sh harness. Reproduce the collision on Ubuntu 24.04 with another AppArmor profile installed, then verify the documented remediation no longer breaks existing bubblewrap consumers. Done means the guidance handles the collision safely and the harness passes.

Written by the indexing model from the issue text.

Assessment

Tech stack
linux, shell
Domain
documentation, operating-systems
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
42/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.