Ubuntu 24.04のCodex CLIでbubblewrapがPermission deniedになる原因と対処 ― AppArmorの制限を影響範囲を抑えて解消する
Ubuntu 24.04 LTS 上でローカルの OpenAI Codex CLI を使っていたところ、Codex がコマンドを実行しようとした段階でサンドボックスの初期化に失敗し、コマンド実行が止まるようになりました。
原因を追っていくと、Codex CLI が Linux でサンドボックスを作るために使う bubblewrap(bwrap)と、Ubuntu 24.04 の AppArmor による unprivileged user namespace 制限の組み合わせでした。
最終的には、AppArmor の制限はシステム全体で維持したまま、Ubuntu のパッケージに用意されている bwrap 専用の AppArmor プロファイルを有効化して解決しました。この記事では、調査の流れと、実際に取得したログ・コマンド結果をまとめます。
同じような症状、たとえば「kernel.unprivileged_userns_clone = 1 なのに bwrap や unshare が uid_map の書き込みで失敗する」という方の参考になれば幸いです。
遭遇した状況
Codex CLI は、Linux 上でコマンドを隔離して実行するために bubblewrap を使います。OpenAI 公式の Sandboxing ドキュメントでも、Linux / WSL2 では事前に bubblewrap をインストールするよう案内されています。
どの bwrap が使われるかは環境によります。Codex のリポジトリにある linux-sandbox の README によると、Codex は PATH 上で見つかった bwrap(カレントディレクトリ配下のものを除く)を優先し、見つからない場合は Codex に同梱された bwrap を使います。
今回の環境では、後述する AppArmor のログに execpath="/usr/bin/bwrap" が記録されていたため、システムの /usr/bin/bwrap が実際に実行されていたことが分かります。
Codex がコマンド実行を試みると、サンドボックスの作成段階で権限エラーになり、タスクが先に進まない状態でした。
動作環境
OS: Ubuntu 24.04.5 LTS (Noble Numbat)
Kernel: 7.0.0-31-generic(執筆時点)
AppArmor: 4.0.1
bubblewrap: 0.9.0
Codex CLI: codex-cli 0.160.0(npm でインストール)
確認に使ったコマンドです。
cat /etc/os-release
uname -r
apparmor_parser --version
bwrap --version
codex --version
調査1:user namespace 自体は有効か
非特権ユーザーでサンドボックスを作れないとき、まず疑うのはカーネルの user namespace 設定です。
sysctl kernel.unprivileged_userns_clone
kernel.unprivileged_userns_clone = 1
カーネル側では、unprivileged user namespace の機能は有効になっています。
では、最小限のコマンドで user namespace を作り、root へのUIDマッピングができるか試してみます。
unshare --user --map-root-user true
unshare: 書き込みに失敗しました /proc/self/uid_map: 許可されていない操作です
機能は有効なのに、UIDマッピング(/proc/self/uid_map への書き込み)が拒否されるという、一見すると設定と挙動が噛み合わない結果です。
通常のファイルパーミッションの問題とは考えにくいため、LSM(Linux Security Module)、Ubuntu であれば AppArmor が介入している可能性を疑いました。
調査2:Ubuntu 24.04 の AppArmor による user namespace 制限
AppArmor による unprivileged user namespace の制限は Ubuntu 23.10 で導入され、Ubuntu 24.04 LTS で改善されたうえで既定で有効になっています。
Ubuntu 24.04 のリリースノートと Ubuntu 公式ブログによると、24.04 の既定の挙動は次のとおりです。
- unconfined なアプリケーションでも、user namespace の作成自体は許可される
- ただし、作成した user namespace の中で capability を使うことは拒否される
設定値を確認します。
sysctl kernel.apparmor_restrict_unprivileged_userns
kernel.apparmor_restrict_unprivileged_userns = 1
制限は有効です。
AppArmor は bwrap の起動自体は許しており、拒否はその後の段階で起きていました。流れは次のとおりです。
bwrap を起動(profile: unconfined)
↓
user namespace を作成(ここまでは許可される)
↓
汎用の unprivileged_userns プロファイルへ遷移
↓
namespace 内で capability の使用や uid_map への書き込みを試みる
↓
DENIED
この流れを裏付けるため、カーネルの audit ログを確認しました。
調査3:AppArmor の audit ログから原因を特定する
直近のカーネルログから AppArmor 関連のイベントを抜き出します。
sudo journalctl -k --since "5 minutes ago" \
| grep -Ei 'apparmor|DENIED|userns|bwrap'
実際のログから、必要なフィールドだけを抜粋します。
最初は、bwrap が user namespace を作成し、unprivileged_userns プロファイルへ遷移したことを示すログです。
apparmor="AUDIT" operation="userns_create" profile="unconfined"
comm="bwrap" requested="userns_create" target="unprivileged_userns"
execpath="/usr/bin/bwrap"
operation="userns_create" が AUDIT(記録のみ)になっており、namespace の作成自体は通っています。target="unprivileged_userns" から、以降は汎用プロファイルの下で動くことが分かります。
続いて、namespace 内での capability の使用が拒否されたログです。
apparmor="DENIED" operation="capable" profile="unprivileged_userns"
comm="bwrap" capname="setpcap"
apparmor="DENIED" operation="capable" profile="unprivileged_userns"
comm="bwrap" capname="net_admin"
そして原因の特定につながったのが次のログです。
apparmor="DENIED" operation="open" profile="unprivileged_userns"
name="proc/.../uid_map" comm="bwrap"
requested_mask="wr" denied_mask="wr"
ここから、bwrap がサンドボックスを作るときに必要な uid_map への書き込みが、汎用の unprivileged_userns プロファイルによって拒否されていたことが分かりました。
原因:bwrap 専用の AppArmor プロファイルがロードされていなかった
Ubuntu では、user namespace を正当に必要とするアプリケーション向けに、個別の AppArmor プロファイルが用意されています。bwrap 用のプロファイルが読み込まれているか確認します。
sudo aa-status | grep -Ei 'bwrap|unshare'
lxc-unshare
lxc-unshare はありますが、bwrap 向けのプロファイルはロードされていませんでした。
apparmor-profiles パッケージの追加プロファイル置き場を見てみます。
ls -l /usr/share/apparmor/extra-profiles/bwrap-userns-restrict
-rw-r--r-- 1 root root 1936 7月 31 12:50 /usr/share/apparmor/extra-profiles/bwrap-userns-restrict
プロファイル自体は、すでにディスク上に存在していました。このファイルは追加プロファイル置き場にあるだけで、既定ではロードされません。
ファイルが存在しない場合は、OpenAI 公式ドキュメントの案内どおり、パッケージをインストールします。
sudo apt update
sudo apt install apparmor-profiles apparmor-utils
解決策:bwrap 専用の AppArmor プロファイルを有効化する
1. プロファイルを配置する
/etc/apparmor.d/ にコピーします(OpenAI 公式ドキュメントと同じく install でパーミッションを指定しています)。
sudo install -m 0644 \
/usr/share/apparmor/extra-profiles/bwrap-userns-restrict \
/etc/apparmor.d/bwrap-userns-restrict
2. プロファイルをロードする
-r を付けると、プロファイルをロード(すでにロードされていれば置き換え)します。
sudo apparmor_parser -r /etc/apparmor.d/bwrap-userns-restrict
3. ロードされたことを確認する
sudo aa-status | grep -i bwrap
bwrap
unpriv_bwrap
bwrap と unpriv_bwrap の2つのプロファイルがロードされました。
このプロファイルは何を許可しているのか
プロファイルの中身を読むと、2段構えの設計になっています。
bwrapプロファイル(/usr/bin/bwrapに適用)bwrap本体には、user namespace の作成と、namespace 内での capability の使用を広く許可しています。uid_mapへの書き込みや mount など、サンドボックスを組み立てるための操作はここで通るようになります。- プロファイル冒頭のコメントにも「ほぼすべてを許可している(allows almost everything)」と明記されています。
unpriv_bwrapプロファイル(bwrapが起動する子プロセスに適用)bwrapが中でプログラムを exec すると、子プロセスにはbwrapとunpriv_bwrapが重ねて(stack)適用されます。unpriv_bwrapにはaudit deny capability,が指定されており、子プロセスは capability を使えません。
つまり、bwrap 本体にはサンドボックスの構築に必要な操作を許可し、その中で動くプログラムには capability を持たせないという設計です。プロファイルのコメントでは、これによって bwrap が user namespace 制限を勝手に回避する手段として使われることを防ぐ、と説明されています。
動作確認
プロファイルを適用したあと、段階的に確認しました。
ステップ1:bubblewrap 単体
bwrap \
--unshare-user \
--uid 0 \
--gid 0 \
--ro-bind / / \
/usr/bin/true
echo "exit=$?"
exit=0
ステップ2:Codex CLI のサンドボックス
Codex CLI には、コマンドを Codex のサンドボックス内で実行する codex sandbox サブコマンドがあります。
codex sandbox /usr/bin/true
echo "exit=$?"
exit=0
codex sandbox linuxについて古い情報では
codex sandbox linux <COMMAND>という書き方が紹介されていることがあります。codex-cli 0.160.0 ではcodex sandbox [COMMAND]...の形式で、linuxを付けると実行するコマンド名として扱われ、Failed to execvp linuxで異常終了しました(exit=101)。手元のバージョンの書式はcodex sandbox --helpで確認してください。
さらに、Codex のサンドボックス内のプロセスにどの AppArmor プロファイルが適用されているかも確認しました。
codex sandbox /bin/sh -c 'cat /proc/self/attr/current'
bwrap//&unpriv_bwrap (enforce)
サンドボックス内のプロセスは bwrap と unpriv_bwrap を重ねたプロファイルの下で動いています。Codex が /usr/bin/bwrap を使い、今回有効化したプロファイルが実際に効いていることが確認できます。
ステップ3:Codex CLI の実タスク
最後に、サンドボックスの初期化エラーで止まっていた Codex のタスクを再実行しました。サンドボックスは正常に初期化され、コマンドの実行と結果の取得まで完了しました。
ハマりやすい注意点
修正直後のログには古い DENIED が残っている
修正直後に journalctl -k --since "2 minutes ago" などを実行すると、修正前の DENIED が表示されることがあります。今回の時系列は次のとおりでした。
02:35:06 DENIED ... (修正前の実行)
02:35:09 profile_load name="bwrap" (プロファイルのロード)
02:35:09 profile_load name="unpriv_bwrap" (プロファイルのロード)
ログに DENIED があっても、すぐに「直っていない」と即断せず、以下の点を確認してみてください。
- ログのタイムスタンプを見る
- プロファイルのロード以降に新しい DENIED が出ていないかを見る
exit=0などの実行結果も合わせて確認する
unshare は引き続き失敗する
プロファイルの適用後も、次のコマンドは失敗します。
unshare --user --map-root-user true
unshare: 書き込みに失敗しました /proc/self/uid_map: 許可されていない操作です
今回有効にしたのは /usr/bin/bwrap 用のプロファイルだけで、それ以外のプログラムに対する制限はそのまま残っているためです。修正できたかどうかは、bwrap と codex sandbox の結果で判断します。
なぜ sysctl でシステム全体の制限を解除しなかったのか
制限そのものを無効にする方法もあります。
sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0
この方法は Ubuntu 24.04 のリリースノートでも、OpenAI 公式ドキュメントでも、紹介されています。ただし、以下の観点から今回は採用しませんでした。
- unprivileged user namespace に対する AppArmor の制限が、システム上のすべてのプログラムで解除される
- Ubuntu 24.04 が既定で有効にしている保護を、広範囲で失ってしまう
- 今回必要だったのは、
bwrapがサンドボックスを作るための操作だけだった
| 方式 | 内容 | 影響範囲 |
|---|---|---|
| 全体で解除 | kernel.apparmor_restrict_unprivileged_userns=0 |
すべてのプログラムで制限が外れる |
| 専用プロファイル(今回) | bwrap-userns-restrict をロード |
/usr/bin/bwrap だけが対象。それ以外の制限は維持 |
全体で解除した場合
kernel.apparmor_restrict_unprivileged_userns = 0
└── すべてのプログラム:制限なし
専用プロファイルを使った場合(今回)
kernel.apparmor_restrict_unprivileged_userns = 1
├── 一般のプログラム:制限を維持
└── /usr/bin/bwrap:専用プロファイル bwrap
└── bwrap が起動する子プロセス:unpriv_bwrap(capability は拒否)
OpenAI 公式ドキュメントが案内している2つの方法のうち、影響範囲が狭いほうを選んだ、という位置づけです。
元に戻す方法
OS のセキュリティ設定を変える作業なので、戻し方も書いておきます。
削除する前に、/etc/apparmor.d/ にあるファイルが今回コピーしたものと同じか確認します。何も出力されなければ同じ内容です。
diff /usr/share/apparmor/extra-profiles/bwrap-userns-restrict \
/etc/apparmor.d/bwrap-userns-restrict
プロファイルをアンロードし、ファイルを削除します。
sudo apparmor_parser -R /etc/apparmor.d/bwrap-userns-restrict
sudo rm /etc/apparmor.d/bwrap-userns-restrict
sudo aa-status | grep -i bwrap で何も表示されなければ、元の状態に戻っています。
再起動後の確認
/etc/apparmor.d/ に置いたプロファイルは、起動時に AppArmor が読み込みます。再起動後は次のコマンドで確認できます。
sudo aa-status | grep -i bwrap
bwrap と unpriv_bwrap が表示されていれば、プロファイルは引き続き有効です。
まとめ
- Ubuntu 24.04 上で Codex CLI のサンドボックス(bubblewrap)が、
Permission deniedで初期化に失敗した kernel.unprivileged_userns_clone = 1で user namespace 機能が有効でも、この問題は起こる- Ubuntu 24.04 では AppArmor による追加の制限があり、user namespace 内での capability の使用などが既定で拒否される
- 原因の特定には
journalctl -kの AppArmor audit ログが役立った。今回はprofile="unprivileged_userns"によるuid_mapへの書き込みの DENIED で原因の特定にいたった kernel.apparmor_restrict_unprivileged_userns=0で全体の制限を解除することなく、専用プロファイルで対処できた- Ubuntu のパッケージに含まれる
bwrap-userns-restrictを有効化することで、bwrap単体とcodex sandboxがexit=0になり、Codex の実タスクも正常に動くようになった
Ubuntu 24.04 上で、Codex CLI に限らず bubblewrap を使うツールが Permission denied や Operation not permitted でサンドボックスの初期化に失敗する場合は、通常の UNIX パーミッションだけでなく、AppArmor の audit ログも確認すると原因の特定につながります。