[app][linux] Documented AppArmor remediation breaks bubblewrap when another vendor's profile is already installed
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?
<p class="font-claude-response-body break-words whitespace-normal" dir="ltr" data-sourcepos="3:1-6:57;104-422"><strong>Component:</strong> ChatGPT desktop app for Linux (preview) — documentation, and packaging
<strong>Affected doc:</strong> <a class="underline underline underline-offset-2 decoration-1 decoration-current/40 hover:decoration-current focus:decoration-current" href="https://learn.chatgpt.com/codex/sandboxing">https://learn.chatgpt.com/codex/sandboxing</a> (Linux/WSL2 prerequisites)
<strong>Severity:</strong> Low direct impact on Codex; moderate collateral impact on the host system
<strong>Status:</strong> Reproducible, with an automated test harness</p>
<hr class="border-border-200 border-t-0.5 my-3 mx-1.5">
<h2 dir="ltr" class="mt-3 -mb-1 text-[1.125rem] font-bold" data-sourcepos="10:1-10:11;429-439">Summary</h2>
<p class="font-claude-response-body break-words whitespace-normal" dir="ltr" data-sourcepos="12:1-12:401;441-841">The <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">bwrap-userns-restrict</code> AppArmor profile that OpenAI's Codex sandboxing documentation instructs users to install declares an attachment to <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">/usr/bin/bwrap</code>. If another package has already installed a profile attached to the same binary — Anthropic's <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">claude-desktop</code> package does exactly this — <strong>neither profile attaches</strong>, and bubblewrap stops working for all unconfined callers on the machine.</p>
<p class="font-claude-response-body break-words whitespace-normal" dir="ltr" data-sourcepos="14:1-14:446;843-1288">The documentation does gate the step on a symptom — <em>"On Ubuntu 24.04, Codex may still warn that it can't create the needed user namespace"</em> — 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 <strong>already works</strong>, and applying it on a working system <strong>causes</strong> the failure it is intended to prevent.</p>
<p class="font-claude-response-body break-words whitespace-normal" dir="ltr" data-sourcepos="16:1-16:286;1290-1575">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.</p>
<hr class="border-border-200 border-t-0.5 my-3 mx-1.5">
<h2 dir="ltr" class="mt-3 -mb-1 text-[1.125rem] font-bold" data-sourcepos="20:1-20:20;1582-1601">Platform details</h2>
<div dir="ltr" class="overflow-x-auto w-full pl-[var(--msg-block-inset,0.5rem)] pr-2 mb-6 print:overflow-x-visible" data-sourcepos="22:1-39:27;1603-2423">
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
</div>
<p class="font-claude-response-body break-words whitespace-normal" dir="ltr" data-sourcepos="127:1-127:55;5796-5850">Kernel audit confirms both the unload and the restore:</p>
<div role="group" aria-label="Code" tabindex="0" data-sourcepos="129:1-134:4;5852-6189" class="relative group/copy bg-bg-000/50 border-0.5 border-border-400 rounded-lg focus:outline-none focus-visible:ring-2 focus-visible:ring-accent-100"><div class="sticky opacity-0 group-hover/copy:opacity-100 group-focus-within/copy:opacity-100 top-2 py-2 h-12 w-0 float-right"><div class="absolute right-0 h-8 px-2 items-center inline-flex z-10"></div></div><div class="overflow-x-auto"><pre class="code-block__code !my-0 !rounded-lg !text-sm !leading-relaxed p-3.5" style="color: rgb(20, 24, 31); background: transparent; font-family: var(--font-mono);"><code style="color: rgb(20, 24, 31); background: transparent; font-family: var(--font-mono); white-space: pre-wrap;">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"</code></pre></div></div>
<p class="font-claude-response-body break-words whitespace-normal" dir="ltr" data-sourcepos="136:1-136:276;6191-6466"><strong>This sequence was run twice on separate occasions, with identical results.</strong> Profile state reported by <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">aa-status</code> at each stage matched expectation both times, and both bwrap profiles were confirmed loaded in <strong>enforce</strong> mode (0 in complain mode) before the baseline test.</p>
<p class="font-claude-response-body break-words whitespace-normal" dir="ltr" data-sourcepos="138:1-138:96;6468-6563"><strong>Minimal manual reproduction</strong> (restores itself; run on a machine with both profiles present):</p>
<div role="group" aria-label="bash code" tabindex="0" data-sourcepos="140:1-155:4;6565-7159" class="relative group/copy bg-bg-000/50 border-0.5 border-border-400 rounded-lg focus:outline-none focus-visible:ring-2 focus-visible:ring-accent-100"><div class="sticky opacity-0 group-hover/copy:opacity-100 group-focus-within/copy:opacity-100 top-2 py-2 h-12 w-0 float-right"><div class="absolute right-0 h-8 px-2 items-center inline-flex z-10"></div></div><div class="text-text-500 font-small p-3.5 pb-0">bash</div><div class="overflow-x-auto"><pre class="code-block__code !my-0 !rounded-lg !text-sm !leading-relaxed p-3.5" style="color: rgb(20, 24, 31); background: transparent; font-family: var(--font-mono);"><code class="language-bash" style="color: rgb(20, 24, 31); background: transparent; font-family: var(--font-mono); white-space: pre;"><span style="color: rgb(110, 118, 135);"># baseline — expect failure</span>
<span style="color: rgb(0, 81, 194);">bwrap</span> <span style="color: rgb(0, 128, 0);">--ro-bind</span> <span style="color: rgb(0, 128, 0);">/</span> <span style="color: rgb(0, 128, 0);">/</span> <span style="color: rgb(0, 128, 0);">--unshare-user</span> <span style="color: rgb(0, 128, 0);">--unshare-pid</span> <span style="color: rgb(0, 128, 128);">true</span> <span style="color: rgb(43, 48, 59);">&&</span> <span style="color: rgb(0, 81, 194);">echo</span> <span style="color: rgb(0, 128, 0);">OK</span>
<span style="color: rgb(110, 118, 135);"># remove Ubuntu's profile: move the FILE first, or a reload re-adds it</span>
<span style="color: rgb(0, 81, 194);">sudo</span> <span style="color: rgb(0, 128, 0);">cp</span> <span style="color: rgb(0, 128, 0);">-a</span> <span style="color: rgb(0, 128, 0);">/etc/apparmor.d/bwrap-userns-restrict</span> <span style="color: rgb(0, 128, 0);">/root/bwrap.bak</span>
<span style="color: rgb(0, 81, 194);">sudo</span> <span style="color: rgb(0, 128, 0);">mv</span> <span style="color: rgb(0, 128, 0);">/etc/apparmor.d/bwrap-userns-restrict</span> <span style="color: rgb(0, 128, 0);">/tmp/bwrap-test</span>
<span style="color: rgb(0, 81, 194);">sudo</span> <span style="color: rgb(0, 128, 0);">apparmor_parser</span> <span style="color: rgb(0, 128, 0);">-R</span> <span style="color: rgb(0, 128, 0);">/tmp/bwrap-test</span>
<span style="color: rgb(110, 118, 135);"># retest as an unprivileged user — expect success</span>
<span style="color: rgb(0, 81, 194);">bwrap</span> <span style="color: rgb(0, 128, 0);">--ro-bind</span> <span style="color: rgb(0, 128, 0);">/</span> <span style="color: rgb(0, 128, 0);">/</span> <span style="color: rgb(0, 128, 0);">--unshare-user</span> <span style="color: rgb(0, 128, 0);">--unshare-pid</span> <span style="color: rgb(0, 128, 128);">true</span> <span style="color: rgb(43, 48, 59);">&&</span> <span style="color: rgb(0, 81, 194);">echo</span> <span style="color: rgb(0, 128, 0);">OK</span>
<span style="color: rgb(110, 118, 135);"># restore</span>
<span style="color: rgb(0, 81, 194);">sudo</span> <span style="color: rgb(0, 128, 0);">mv</span> <span style="color: rgb(0, 128, 0);">/root/bwrap.bak</span> <span style="color: rgb(0, 128, 0);">/etc/apparmor.d/bwrap-userns-restrict</span>
<span style="color: rgb(0, 81, 194);">sudo</span> <span style="color: rgb(0, 128, 0);">apparmor_parser</span> <span style="color: rgb(0, 128, 0);">-r</span> <span style="color: rgb(0, 128, 0);">/etc/apparmor.d/bwrap-userns-restrict</span></code></pre></div></div>
<p class="font-claude-response-body break-words whitespace-normal" dir="ltr" data-sourcepos="157:1-157:276;7161-7436">A fuller self-restoring harness (<code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">bwrap-collision-test.sh</code>) checks the baseline first, drops to <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">$SUDO_USER</code> for the test since running it under <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">sudo</code> passes regardless of policy, and restores via an <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">EXIT</code>/<code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">INT</code>/<code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">TERM</code> trap so an interrupt cannot leave the system modified.</p>
<hr class="border-border-200 border-t-0.5 my-3 mx-1.5">
<h2 dir="ltr" class="mt-3 -mb-1 text-[1.125rem] font-bold" data-sourcepos="161:1-161:10;7443-7452">Impact</h2>
<p class="font-claude-response-body break-words whitespace-normal" dir="ltr" data-sourcepos="163:1-163:77;7454-7530"><strong>Codex is unaffected.</strong> Verified by having the agent inspect its own shell:</p>
<div role="group" aria-label="bash code" tabindex="0" data-sourcepos="165:1-167:4;7532-7610" class="relative group/copy bg-bg-000/50 border-0.5 border-border-400 rounded-lg focus:outline-none focus-visible:ring-2 focus-visible:ring-accent-100"><div class="sticky opacity-0 group-hover/copy:opacity-100 group-focus-within/copy:opacity-100 top-2 py-2 h-12 w-0 float-right"><div class="absolute right-0 h-8 px-2 items-center inline-flex z-10"></div></div><div class="text-text-500 font-small p-3.5 pb-0">bash</div><div class="overflow-x-auto"><pre class="code-block__code !my-0 !rounded-lg !text-sm !leading-relaxed p-3.5" style="color: rgb(20, 24, 31); background: transparent; font-family: var(--font-mono);"><code class="language-bash" style="color: rgb(20, 24, 31); background: transparent; font-family: var(--font-mono); white-space: pre;"><span style="color: rgb(0, 81, 194);">grep</span> <span style="color: rgb(0, 128, 0);">Seccomp</span> <span style="color: rgb(0, 128, 0);">/proc/self/status</span><span style="color: rgb(43, 48, 59);">;</span> <span style="color: rgb(0, 81, 194);">readlink</span> <span style="color: rgb(0, 128, 0);">/proc/self/ns/user</span><span style="color: rgb(43, 48, 59);">;</span> <span style="color: rgb(0, 81, 194);">id</span> <span style="color: rgb(0, 128, 0);">-u</span></code></pre></div></div>
<p class="font-claude-response-body break-words whitespace-normal" dir="ltr" data-sourcepos="168:1-168:83;7611-7693">Two independent runs, on different days, while the shell <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">bwrap</code> test was failing:</p>
<div role="group" aria-label="Code" tabindex="0" data-sourcepos="170:1-176:4;7695-7922" class="relative group/copy bg-bg-000/50 border-0.5 border-border-400 rounded-lg focus:outline-none focus-visible:ring-2 focus-visible:ring-accent-100"><div class="sticky opacity-0 group-hover/copy:opacity-100 group-focus-within/copy:opacity-100 top-2 py-2 h-12 w-0 float-right"><div class="absolute right-0 h-8 px-2 items-center inline-flex z-10"></div></div><div class="overflow-x-auto"><pre class="code-block__code !my-0 !rounded-lg !text-sm !leading-relaxed p-3.5" style="color: rgb(20, 24, 31); background: transparent; font-family: var(--font-mono);"><code style="color: rgb(20, 24, 31); background: transparent; font-family: var(--font-mono); white-space: pre-wrap;">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</code></pre></div></div>
<p class="font-claude-response-body break-words whitespace-normal" dir="ltr" data-sourcepos="178:1-178:70;7924-7993">Host initial user namespace on the same machine: <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">user:[4026531837]</code>.</p>
<p class="font-claude-response-body break-words whitespace-normal" dir="ltr" data-sourcepos="180:1-180:210;7995-8204">Both runs are in a non-initial user namespace, and the <strong>namespace inode differs between them</strong>, indicating a fresh user namespace per agent invocation rather than a single long-lived one created at app start.</p>
<p class="font-claude-response-body break-words whitespace-normal" dir="ltr" data-sourcepos="182:1-182:444;8206-8649">Seccomp-BPF filter active, non-initial user namespace, running unprivileged. <strong>Observed:</strong> the Codex agent sandbox worked correctly at the same time as the shell <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">bwrap</code> test was failing on the same machine. <strong>Likely explanation:</strong> the app is confined by the <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">/etc/apparmor.d/chatgpt</code> profile the package ships, so <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">bwrap</code> spawned from within it does not follow the unconfined transition path. This explanation was not separately instrumented.</p>
<p class="font-claude-response-body break-words whitespace-normal" dir="ltr" data-sourcepos="184:1-184:312;8651-8962"><strong>Other bubblewrap consumers break.</strong> Anything invoking <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">bwrap</code> 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.</p>
<p class="font-claude-response-body break-words whitespace-normal" dir="ltr" data-sourcepos="186:1-186:306;8964-9269"><strong>Diagnostic confusion.</strong> 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.</p>
<hr class="border-border-200 border-t-0.5 my-3 mx-1.5">
<h2 dir="ltr" class="mt-3 -mb-1 text-[1.125rem] font-bold" data-sourcepos="190:1-190:60;9276-9335">Instructions that should not be followed unconditionally</h2>
<p class="font-claude-response-body break-words whitespace-normal" dir="ltr" data-sourcepos="192:1-192:293;9337-9629">From the "Ubuntu AppArmor note" in the Linux/WSL2 prerequisites at <a class="underline underline underline-offset-2 decoration-1 decoration-current/40 hover:decoration-current focus:decoration-current" href="https://learn.chatgpt.com/codex/sandboxing">https://learn.chatgpt.com/codex/sandboxing</a>. The doc introduces it with <em>"On Ubuntu 24.04, Codex may still warn that it can't create the needed user namespace after <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">bubblewrap</code> is installed. Copy and load the extra profile:"</em></p>
<div role="group" aria-label="bash code" tabindex="0" data-sourcepos="194:1-201:4;9631-9894" class="relative group/copy bg-bg-000/50 border-0.5 border-border-400 rounded-lg focus:outline-none focus-visible:ring-2 focus-visible:ring-accent-100"><div class="sticky opacity-0 group-hover/copy:opacity-100 group-focus-within/copy:opacity-100 top-2 py-2 h-12 w-0 float-right"><div class="absolute right-0 h-8 px-2 items-center inline-flex z-10"></div></div><div class="text-text-500 font-small p-3.5 pb-0">bash</div><div class="overflow-x-auto"><pre class="code-block__code !my-0 !rounded-lg !text-sm !leading-relaxed p-3.5" style="color: rgb(20, 24, 31); background: transparent; font-family: var(--font-mono);"><code class="language-bash" style="color: rgb(20, 24, 31); background: transparent; font-family: var(--font-mono); white-space: pre;"><span style="color: rgb(0, 81, 194);">sudo</span> <span style="color: rgb(0, 128, 0);">apt</span> <span style="color: rgb(0, 128, 0);">update</span>
<span style="color: rgb(0, 81, 194);">sudo</span> <span style="color: rgb(0, 128, 0);">apt</span> <span style="color: rgb(0, 128, 0);">install</span> <span style="color: rgb(0, 128, 0);">apparmor-profiles</span> <span style="color: rgb(0, 128, 0);">apparmor-utils</span>
<span style="color: rgb(0, 81, 194);">sudo</span> <span style="color: rgb(0, 128, 0);">install</span> <span style="color: rgb(0, 128, 0);">-m</span> <span style="color: rgb(0, 128, 128);">0644</span> <span style="color: rgb(0, 128, 128);">\</span>
<span style="color: rgb(0, 128, 0);">/usr/share/apparmor/extra-profiles/bwrap-userns-restrict</span> <span style="color: rgb(0, 128, 128);">\</span>
<span style="color: rgb(0, 128, 0);">/etc/apparmor.d/bwrap-userns-restrict</span>
<span style="color: rgb(0, 81, 194);">sudo</span> <span style="color: rgb(0, 128, 0);">apparmor_parser</span> <span style="color: rgb(0, 128, 0);">-r</span> <span style="color: rgb(0, 128, 0);">/etc/apparmor.d/bwrap-userns-restrict</span></code></pre></div></div>
<p class="font-claude-response-body break-words whitespace-normal" dir="ltr" data-sourcepos="203:1-203:452;9896-10347">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 <strong>Ubuntu 25.04</strong> the <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">bwrap-userns-restrict</code> profile ships in the <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">apparmor</code> package at <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">/etc/apparmor.d/bwrap-userns-restrict</code>. On such a system the profile may already be present, making a manual copy redundant and a manual <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">apparmor_parser -r</code> potentially disruptive.</p>
<p class="font-claude-response-body break-words whitespace-normal" dir="ltr" data-sourcepos="205:1-205:31;10349-10379">The documentation also offers:</p>
<div role="group" aria-label="bash code" tabindex="0" data-sourcepos="207:1-209:4;10381-10454" class="relative group/copy bg-bg-000/50 border-0.5 border-border-400 rounded-lg focus:outline-none focus-visible:ring-2 focus-visible:ring-accent-100"><div class="sticky opacity-0 group-hover/copy:opacity-100 group-focus-within/copy:opacity-100 top-2 py-2 h-12 w-0 float-right"><div class="absolute right-0 h-8 px-2 items-center inline-flex z-10"></div></div><div class="text-text-500 font-small p-3.5 pb-0">bash</div><div class="overflow-x-auto"><pre class="code-block__code !my-0 !rounded-lg !text-sm !leading-relaxed p-3.5" style="color: rgb(20, 24, 31); background: transparent; font-family: var(--font-mono);"><code class="language-bash" style="color: rgb(20, 24, 31); background: transparent; font-family: var(--font-mono); white-space: pre;"><span style="color: rgb(0, 81, 194);">sudo</span> <span style="color: rgb(0, 128, 0);">sysctl</span> <span style="color: rgb(0, 128, 0);">-w</span> <span style="color: rgb(0, 128, 0);">kernel.apparmor_restrict_unprivileged_userns=</span><span style="color: rgb(0, 128, 128);">0</span></code></pre></div></div>
<p class="font-claude-response-body break-words whitespace-normal" dir="ltr" data-sourcepos="211:1-211:215;10456-10670">This works but disables the restriction <strong>system-wide for every application</strong>, not only for bubblewrap. It should carry a stronger warning, or be removed in favour of the profile approach plus a precondition check.</p>
<hr class="border-border-200 border-t-0.5 my-3 mx-1.5">
<h2 dir="ltr" class="mt-3 -mb-1 text-[1.125rem] font-bold" data-sourcepos="215:1-215:32;10677-10708">How to check before applying</h2>
<div role="group" aria-label="bash code" tabindex="0" data-sourcepos="217:1-219:4;10710-10752" class="relative group/copy bg-bg-000/50 border-0.5 border-border-400 rounded-lg focus:outline-none focus-visible:ring-2 focus-visible:ring-accent-100"><div class="sticky opacity-0 group-hover/copy:opacity-100 group-focus-within/copy:opacity-100 top-2 py-2 h-12 w-0 float-right"><div class="absolute right-0 h-8 px-2 items-center inline-flex z-10"></div></div><div class="text-text-500 font-small p-3.5 pb-0">bash</div><div class="overflow-x-auto"><pre class="code-block__code !my-0 !rounded-lg !text-sm !leading-relaxed p-3.5" style="color: rgb(20, 24, 31); background: transparent; font-family: var(--font-mono);"><code class="language-bash" style="color: rgb(20, 24, 31); background: transparent; font-family: var(--font-mono); white-space: pre;"><span style="color: rgb(0, 81, 194);">sudo</span> <span style="color: rgb(0, 128, 0);">aa-status</span> | <span style="color: rgb(0, 81, 194);">grep</span> <span style="color: rgb(0, 128, 0);">-i</span> <span style="color: rgb(0, 128, 0);">bwrap</span></code></pre></div></div>
<p class="font-claude-response-body break-words whitespace-normal" dir="ltr" data-sourcepos="221:1-221:79;10754-10832">If any profile is already listed for bwrap, verify whether it grants <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">userns</code>:</p>
<div role="group" aria-label="bash code" tabindex="0" data-sourcepos="223:1-225:4;10834-10898" class="relative group/copy bg-bg-000/50 border-0.5 border-border-400 rounded-lg focus:outline-none focus-visible:ring-2 focus-visible:ring-accent-100"><div class="sticky opacity-0 group-hover/copy:opacity-100 group-focus-within/copy:opacity-100 top-2 py-2 h-12 w-0 float-right"><div class="absolute right-0 h-8 px-2 items-center inline-flex z-10"></div></div><div class="text-text-500 font-small p-3.5 pb-0">bash</div><div class="overflow-x-auto"><pre class="code-block__code !my-0 !rounded-lg !text-sm !leading-relaxed p-3.5" style="color: rgb(20, 24, 31); background: transparent; font-family: var(--font-mono);"><code class="language-bash" style="color: rgb(20, 24, 31); background: transparent; font-family: var(--font-mono); white-space: pre;"><span style="color: rgb(0, 81, 194);">grep</span> <span style="color: rgb(0, 128, 0);">-l</span> <span style="color: rgb(0, 128, 0);">'userns'</span> <span style="color: rgb(0, 128, 0);">/etc/apparmor.d/</span><span style="color: rgb(129, 0, 194);"></span><span style="color: rgb(0, 128, 0);">bwrap</span><span style="color: rgb(129, 0, 194);"></span> 2><span style="color: rgb(0, 128, 0);">/dev/null</span></code></pre></div></div>
<p class="font-claude-response-body break-words whitespace-normal" dir="ltr" data-sourcepos="227:1-227:102;10900-11001">And test directly, as an unprivileged user (never under <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">sudo</code>, which succeeds regardless of policy):</p>
<div role="group" aria-label="bash code" tabindex="0" data-sourcepos="229:1-231:4;11003-11079" class="relative group/copy bg-bg-000/50 border-0.5 border-border-400 rounded-lg focus:outline-none focus-visible:ring-2 focus-visible:ring-accent-100"><div class="sticky opacity-0 group-hover/copy:opacity-100 group-focus-within/copy:opacity-100 top-2 py-2 h-12 w-0 float-right"><div class="absolute right-0 h-8 px-2 items-center inline-flex z-10"></div></div><div class="text-text-500 font-small p-3.5 pb-0">bash</div><div class="overflow-x-auto"><pre class="code-block__code !my-0 !rounded-lg !text-sm !leading-relaxed p-3.5" style="color: rgb(20, 24, 31); background: transparent; font-family: var(--font-mono);"><code class="language-bash" style="color: rgb(20, 24, 31); background: transparent; font-family: var(--font-mono); white-space: pre;"><span style="color: rgb(0, 81, 194);">bwrap</span> <span style="color: rgb(0, 128, 0);">--ro-bind</span> <span style="color: rgb(0, 128, 0);">/</span> <span style="color: rgb(0, 128, 0);">/</span> <span style="color: rgb(0, 128, 0);">--unshare-user</span> <span style="color: rgb(0, 128, 0);">--unshare-pid</span> <span style="color: rgb(0, 128, 128);">true</span> <span style="color: rgb(43, 48, 59);">&&</span> <span style="color: rgb(0, 81, 194);">echo</span> <span style="color: rgb(0, 128, 0);">OK</span></code></pre></div></div>
<p class="font-claude-response-body break-words whitespace-normal" dir="ltr" data-sourcepos="233:1-233:97;11081-11177"><strong>If that prints <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">OK</code>, no AppArmor change is needed and the documented step should be skipped.</strong></p>
<hr class="border-border-200 border-t-0.5 my-3 mx-1.5">
<h2 dir="ltr" class="mt-3 -mb-1 text-[1.125rem] font-bold" data-sourcepos="237:1-237:14;11184-11197">Workaround</h2>
<p class="font-claude-response-body break-words whitespace-normal" dir="ltr" data-sourcepos="239:1-239:148;11199-11346">If the collision has already occurred, disable Ubuntu's profile using the distribution's own mechanism, which survives <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">systemctl reload apparmor</code>:</p>
<div role="group" aria-label="bash code" tabindex="0" data-sourcepos="241:1-246:4;11348-11599" class="relative group/copy bg-bg-000/50 border-0.5 border-border-400 rounded-lg focus:outline-none focus-visible:ring-2 focus-visible:ring-accent-100"><div class="sticky opacity-0 group-hover/copy:opacity-100 group-focus-within/copy:opacity-100 top-2 py-2 h-12 w-0 float-right"><div class="absolute right-0 h-8 px-2 items-center inline-flex z-10"></div></div><div class="text-text-500 font-small p-3.5 pb-0">bash</div><div class="overflow-x-auto"><pre class="code-block__code !my-0 !rounded-lg !text-sm !leading-relaxed p-3.5" style="color: rgb(20, 24, 31); background: transparent; font-family: var(--font-mono);"><code class="language-bash" style="color: rgb(20, 24, 31); background: transparent; font-family: var(--font-mono); white-space: pre;"><span style="color: rgb(0, 81, 194);">sudo</span> <span style="color: rgb(0, 128, 0);">apparmor_parser</span> <span style="color: rgb(0, 128, 0);">-R</span> <span style="color: rgb(0, 128, 0);">/etc/apparmor.d/bwrap-userns-restrict</span>
<span style="color: rgb(0, 81, 194);">sudo</span> <span style="color: rgb(0, 128, 0);">mkdir</span> <span style="color: rgb(0, 128, 0);">-p</span> <span style="color: rgb(0, 128, 0);">/etc/apparmor.d/disable</span>
<span style="color: rgb(0, 81, 194);">sudo</span> <span style="color: rgb(0, 128, 0);">ln</span> <span style="color: rgb(0, 128, 0);">-sf</span> <span style="color: rgb(0, 128, 0);">/etc/apparmor.d/bwrap-userns-restrict</span> <span style="color: rgb(0, 128, 0);">/etc/apparmor.d/disable/</span>
<span style="color: rgb(0, 81, 194);">bwrap</span> <span style="color: rgb(0, 128, 0);">--ro-bind</span> <span style="color: rgb(0, 128, 0);">/</span> <span style="color: rgb(0, 128, 0);">/</span> <span style="color: rgb(0, 128, 0);">--unshare-user</span> <span style="color: rgb(0, 128, 0);">--unshare-pid</span> <span style="color: rgb(0, 128, 128);">true</span> <span style="color: rgb(43, 48, 59);">&&</span> <span style="color: rgb(0, 81, 194);">echo</span> <span style="color: rgb(0, 128, 0);">OK</span></code></pre></div></div>
<p class="font-claude-response-body break-words whitespace-normal" dir="ltr" data-sourcepos="248:1-248:62;11601-11662">This returns the system to the state Ubuntu ships by default.</p>
<p class="font-claude-response-body break-words whitespace-normal" dir="ltr" data-sourcepos="250:1-250:258;11664-11921"><strong>Caveat for users applying this workaround:</strong> if the other vendor's package is later uninstalled, its profile is removed with it and nothing will grant <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">userns</code> to bubblewrap. Re-enable Ubuntu's profile at that point by deleting the symlink and re-parsing.</p>
<hr class="border-border-200 border-t-0.5 my-3 mx-1.5">
<h2 dir="ltr" class="mt-3 -mb-1 text-[1.125rem] font-bold" data-sourcepos="254:1-254:19;11928-11946">Suggested fixes</h2>
<p class="font-claude-response-body break-words whitespace-normal" dir="ltr" data-sourcepos="256:1-256:25;11948-11972">In rough order of value:</p>
<ol dir="ltr" class="[li_&]:mb-0 [li_&]:mt-1 [li_&]:gap-1 [&:not(:last-child)_ul]:pb-1 [&:not(:last-child)_ol]:pb-1 list-decimal flex flex-col gap-1 pl-8 mb-3 print:block print:space-y-1" data-sourcepos="258:1-268:289;11974-13440">
<li class="font-claude-response-body whitespace-normal break-words pl-2" data-sourcepos="258:1-258:269;11974-12242"><strong>Add a precondition check to the documentation.</strong> Before the install step, instruct the user to run <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">bwrap --ro-bind / / --unshare-user --unshare-pid true</code> and skip the remediation if it succeeds. This is a one-sentence change and resolves the issue for new users.</li>
<li class="font-claude-response-body whitespace-normal break-words pl-2" data-sourcepos="260:1-260:154;12244-12397"><strong>Document the collision.</strong> Note that a profile from another package may already attach to <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">/usr/bin/bwrap</code>, and that adding a second one breaks both.</li>
<li class="font-claude-response-body whitespace-normal break-words pl-2" data-sourcepos="262:1-262:330;12399-12728"><strong>Consider shipping the profile from the package rather than documenting a manual step</strong>, following the pattern Google Chrome and <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">claude-desktop</code> use: write it from <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">postinst</code>, gate on AppArmor 4.0 by testing for the <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">abi/4.0</code> feature file, check for an existing attachment first, and never fail the install on parser errors.</li>
<li class="font-claude-response-body whitespace-normal break-words pl-2" data-sourcepos="264:1-264:294;12730-13023"><strong>Consider adding <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">bubblewrap</code> to <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">Depends:</code>.</strong> The current <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">Depends:</code> line contains 42 comma-separated entries, all libraries and desktop-integration helpers, and does not include <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">bubblewrap</code>. A bundled fallback helper exists, so this is a robustness improvement rather than a defect fix.</li>
<li class="font-claude-response-body whitespace-normal break-words pl-2" data-sourcepos="266:1-266:126;13025-13150"><strong>Reconsider the <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">sysctl</code> suggestion</strong>, or attach a clearer warning that it disables the protection for the entire system.</li>
<li class="font-claude-response-body whitespace-normal break-words pl-2" data-sourcepos="268:1-268:289;13152-13440"><strong>Improve the in-app diagnostic.</strong> 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.</li>
</ol>
<hr class="border-border-200 border-t-0.5 my-3 mx-1.5">
<h2 dir="ltr" class="mt-3 -mb-1 text-[1.125rem] font-bold" data-sourcepos="272:1-272:25;13447-13471">Scope and limitations</h2>
<ul dir="ltr" class="[li_&]:mb-0 [li_&]:mt-1 [li_&]:gap-1 [&:not(:last-child)_ul]:pb-1 [&:not(:last-child)_ol]:pb-1 list-disc flex flex-col gap-1 pl-8 mb-3 print:block print:space-y-1" data-sourcepos="274:1-277:153;13473-14064">
<li class="font-claude-response-body whitespace-normal break-words pl-2" data-sourcepos="274:1-274:219;13473-13691"><strong>Tested on one machine</strong>, 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.</li>
<li class="font-claude-response-body whitespace-normal break-words pl-2" data-sourcepos="275:1-275:141;13692-13832"><strong>Not tested with <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">bubblewrap</code> absent.</strong> The documentation notes a bundled fallback helper; its behaviour under this collision is unknown.</li>
<li class="font-claude-response-body whitespace-normal break-words pl-2" data-sourcepos="276:1-276:79;13833-13911"><strong>Not tested under Wayland.</strong> All measurements were taken in an X11 session.</li>
<li class="font-claude-response-body whitespace-normal break-words pl-2" data-sourcepos="277:1-277:153;13912-14064">The <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">unpriv_bwrap</code> profile is also declared in <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">bwrap-userns-restrict</code> and is removed with it; its role in the contention was not separately isolated.</li>
</ul>
<h2 dir="ltr" class="mt-3 -mb-1 text-[1.125rem] font-bold" data-sourcepos="279:1-279:9;14066-14074">Notes</h2>
<ul dir="ltr" class="[li_&]:mb-0 [li_&]:mt-1 [li_&]:gap-1 [&:not(:last-child)_ul]:pb-1 [&:not(:last-child)_ol]:pb-1 list-disc flex flex-col gap-1 pl-8 mb-3 print:block print:space-y-1" data-sourcepos="281:1-283:166;14076-14599">
<li class="font-claude-response-body whitespace-normal break-words pl-2" data-sourcepos="281:1-281:154;14076-14229">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.</li>
<li class="font-claude-response-body whitespace-normal break-words pl-2" data-sourcepos="282:1-282:204;14230-14433">The specific second profile here comes from <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">claude-desktop</code>, but nothing about the mechanism is vendor-specific. Any package installing a profile attached to <code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">/usr/bin/bwrap</code> produces the same result.</li>
<li class="font-claude-response-body whitespace-normal break-words pl-2" data-sourcepos="283:1-283:166;14434-14599">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.</li></ul>
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