
軽量かつ多層的なLinuxサンドボックスで、namespaces、pivot_root、seccomp-bpf、capability dropping、そしてエビデンスベースの判定エンジン(Truthimatics Public Version)を組み合わせ、安全で監査可能なコード実行を実現します。
Linux上でのネイティブコード実行のためのマルチレイヤーサンドボックス。
7つの独立した防御層 — 外部依存関係なし、約73 KiBのPIEバイナリ。
┌──────────────────────────────────────────────────────┐
│ Z-Jail │
├──────────────────────────────────────────────────────┤
│ Truthimatics PV (エビデンスベースの判定エンジン) │
│ Namespaces (mount, pid, net, ipc, uts) │
│ pivot_root (chrootの強化版) │
│ Capabilities (すべて削除、securebitsをロック) │
│ NO_NEW_PRIVS (権限昇格を禁止) │
│ seccomp-BPF (ホワイトリスト: 15システムコールのみ) │
│ Audit (JSONログ + BLAKE2bハッシュ) │
└──────────────────────────────────────────────────────┘
git clone https://github.com/Division-36/Z-Jail.git
cd Z-Jail
make
sudo ./z_jail --root=/path/to/rootfs --seccomp-enforce -- /bin/ls
--rootディレクトリには、ターゲットバイナリとその依存関係を含む最小限のファイルシステムが必要です(静的バイナリの場合はバイナリのみで十分です)。
既存のサンドボックス化ソリューションにはトレードオフがあります:
Z-Jailはbwrap(最小限、デフォルトでseccompなし)とnsjail(高機能、重い依存関係)の間のニッチを埋めます。CIパイプライン、CTFジャイルチャレンジ、軽量コード評価のために設計されており、コンテナランタイムを導入せずに多層防御を実現します。
flowchart LR
CLI[CLI引数] --> P[parse_args]
P --> C{名前空間をクローン}
C -->|子プロセス| CR[child_run]
C -->|親プロセス| W[waitpid]
CR --> RL[setrlimit]
RL --> FD[fd >= 3 を閉じる]
FD --> DUMP[PR_SET_DUMPABLE=0]
DUMP --> PV[pivot_root]
PV --> NNP[PR_SET_NO_NEW_PRIVS]
NNP --> CAP[ケーパビリティを削除]
CAP --> SC[seccomp-BPF]
SC --> SIG[シグナルを親に送信]
SIG --> EX[execve ターゲット]
W --> A[監査JSON]
A --> EXIT[終了]
各レイヤーは、後のレイヤーが前のレイヤーによって元に戻されないように順序付けられています:
sequenceDiagram
participant P as 親プロセス
participant C as 子プロセス
P->>C: clone (NEWNS|NEWPID|NEWNET|NEWIPC|NEWUTS)
Note over C: setrlimit(CPU, AS, NOFILE, NPROC)
Note over C: close(3以上のすべてのfd)
Note over C: PR_SET_DUMPABLE=0
Note over C: pivot_root → chdir("/") → umount -l
Note over C: PR_SET_NO_NEW_PRIVS
Note over C: capset(すべてゼロ) + securebits
Note over C: seccomp(SECCOMP_MODE_FILTER, ホワイトリスト)
C->>P: write(pipe, ready=1)
Note over C: execve(ターゲット)
P->>P: waitpid
P->>P: 監査JSONを書き込み
エビデンスベースの判定エンジン。実行されたバイナリに関する重み付き観測を収集し、最終的な判定(DETERMINISTIC、REJECT、またはUNCERTAIN)を決定します。各観測には重みがあり、任意の単一観測の重みが合計の50%を超えると判定が決まります。
clone()を介して5つの名前空間が作成されます:
初期名前空間でCAP_SYS_ADMINが必要です。
マウント名前空間のルートを--rootディレクトリに置き換えます:
MS_BIND|MS_REC)pivot_root(new_root, put_old) — マウントツリーを交換chdir("/") — 新しいルートに移動umount2("/.pivot_old", MNT_DETACH) — 古いルートを切り離しrmdir("/.pivot_old") — クリーンアップこれはchroot(2)よりも厳密に強力です — サンドボックス化されたプロセスがホストルートに戻る方法はありません。サンドボックス内部からのCLONE_NEWNS(すでにseccompでブロックされています)を使用しても不可能です。
すべてのケーパビリティは以下によって削除されます:
capset(hdr, data) // data = {0, 0, 0}
prctl(SECBIT_KEEP_CAPS_LOCKED | SECBIT_NO_SETUID_FIXUP | ...)
プロセスはcapsetの前にsetuid/setgidを削除し、CAP_SETUIDがまだ保持されている間にuid変更を有効にします。capset後、すべてのケーパビリティはなくなり、securebitsはロックされます — 再有効化は不可能です。
prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
プロセスまたはその子プロセスが、setuidバイナリ、ファイルケーパビリティ、またはLSM遷移を介して新しい特権を取得するのを防ぎます。不可逆的。
15のシステムコールの許可リスト — リストにないものはSECCOMP_RET_KILL:
BPFフィルタは動的に生成されます: 各ホワイトリストエントリに対して、システムコールが一致すれば許可、そうでなければKILLにフォールスルーするジャンプチェーンが出力されます。アーキテクチャが最初にチェックされます (AUDIT_ARCH_X86_64)。
フィルタはスタンドアロンテスト (tests/seccomp_filter_test.c、8/8合格) によって独立して検証され、root権限を必要とせずに実際のprctl(PR_SET_SECCOMP)に対してテストケースをfork+execveします。
実行ごとにJSON監査レコードが生成されます:
{
"schema": "z-jail.audit/v1",
"build_id": "Z-Jail/v1+dev",
"timestamp": 1749000000,
"duration_ns": 8500000,
"executable": "/bin/ls",
"verdict": "DETERMINISTIC",
"exit_code": 0,
"sandbox": {
"seccomp_filter": "whitelist-v1",
"seccomp_whitelist_size": 15,
"seccomp_arg_rules_size": 2,
"namespaces": ["mount","pid","net","ipc","uts"],
"pivot_root": "/var/run/z-jail/roots/default",
"no_new_privs": true,
"capabilities_dropped": true
},
"content_fingerprint": "0e5751c026e543b2e8ab2eb06099daa1..."
}
build/audits/<binary-name>.audit.json に書き込まれます。content_fingerprintは、子プロセスが終了した後に親によって計算されたターゲットバイナリのBLAKE2b-256ハッシュです。
z_jail --root=<dir> [--seccomp-enforce] [--self-hash=<hex>]
[--quiet] [--verbose] -- <program> [args...]
# すべての保護機能を有効にして静的バイナリを実行
sudo z_jail --root=./roots --seccomp-enforce -- bin/hello_static
# バイナリ整合性検証付きで実行
sudo z_jail --root=./roots --seccomp-enforce \
--self-hash=$(sha256sum z_jail | cut -c1-64) -- bin/program
# クワイエットモード (監査JSONなし)
sudo z_jail --root=./roots --quiet -- bin/program
make # z_jailをビルド (~130 KiB PIEバイナリ)
make install # /usr/local/bin + manページにインストール
make clean # ビルド成果物を削除
make dist # リリースtarballを作成
make check # スモークテスト (--version + --help)
バイナリは位置独立実行可能ファイルとしてビルドされ、-fstack-protector-strong、-D_FORTIFY_SOURCE=2、完全なRELRO、-z nowが有効です。
make CC=clang CFLAGS="-O3 -march=native" # カスタムコンパイラ/フラグ
# seccompフィルタロジック (8テスト)
tests/build/seccomp_filter_test
# BLAKE2b既知解テスト
tests/build/blake2b_known
これらはrootを必要とせず、100ミリ秒未満で実行されます。
make -C tests setup # ペイロード + テストルートをビルド
sudo bash tests/run_tests.sh # 17シナリオ
名前空間作成のためにrootが必要です。テストスイートの対象:
WSL2 (カーネル 6.18.x-microsoft-standard-WSL2、Kali Linux) で測定。
ツールあたり50サンプル、統一ワークロード (本体がexit_group(0)であるフリースタンディング静的バイナリ)、getrusageハーネスで計時。方法論については docs/BENCHMARKS.md を参照。
| メトリクス | 値 |
|---|---|
| バイナリサイズ | 約73 KiB (ストリップ前) / 約28 KiB (ストリップ後) |
| 平均サンドボックスレイテンシ | 5.85 ± 1.45 ms (95% CI [5.45, 6.25]) |
| ピークRSS | 1.62 MiB |
| コード行数 (コア) | ~900 |
テスト条件下では、Z-Jailは3つのプロセスレベルサンドボックスの中で最も小さいRSSを持ち、レイテンシはbwrapとnsjailの中間です。Bubblewrapは最速ですが、デフォルトではseccompフィルタリングを行わないため、セットアップ作業が少なくて済みます。Z-Jailは実行ごとにseccompホワイトリストのインストール、ケーパビリティの削除、pivot_rootを実行します。gVisor (runsc)はこのWSL2カーネルでセグメンテーションフォールトが発生し、測定できませんでした。FirecrackerはマイクロVMによる分離 (VMコールドブート、異なるメトリクス) を行うため、fork-to-execテーブルから除外されています。これらは単一ホストの数値です — 相対的なものとして扱ってください。
注意: 以前文書化された値 (~8 ms、~4 MiB、~130 KiB) は現在の
makeビルド (~73 KiB、~5.9 ms) と一致せず、不正確だったようです。上記の数値はこのコードベースで再測定されました。Truthimaticsは依然としてコードの一部であり、削除されていません (古いaxiom_jailレポートは単に異なるバイナリ名とハーネスを使用していました)。ベンチマーク中に見つかったマウント伝搬バグはsrc/sandbox.cで修正されました (MS_REC|MS_PRIVATEをバインドマウントの前に追加); 再帰的な再マウントが測定されたレイテンシの一部に寄与しています。
chroot、mount、ptrace、socket、process_vm_writevを介した脱出RLIMIT_CPU)、メモリ枯渇 (RLIMIT_AS)execveをまたぐファイル記述子リークsetuid / 動的リンカ / LD_PRELOADによる昇格/proc、/sysマウントを介した同一ホストVM脱出CLONE_NEWNET + ブロックされたsocketが提供する以上のネットワークエグレスclone(CLONE_NEWNS|CLONE_NEWPID|...)が成功する (CAP_SYS_ADMINが必要)--root内で利用可能)--self-hash=<hex>が設定されているMIT — 全文はLICENSEを参照してください。
Z-JailはWSL2 (Kali Linux、GCC 15.2.0) 上でビルドされ、Linux 5.4+をターゲットとしています。 Division-36 によってメンテナンスされています。問題は課題トラッカー に報告してください。
| Z-Jail | Firecracker | gVisor | bwrap | nsjail |
|---|
| 外部依存関係 | ゼロ | libc, seccomp | Goランタイム | libc | libc, protobuf |
| バイナリサイズ | 約73 KiB | 20+ MiB | 40+ MiB | ~70 KiB | ~1 MiB |
| VM分離 | なし | あり (microVM) | なし (サンドボックス) | なし | なし |
| seccompホワイトリスト | あり | なし | あり | オプション | あり |
| コンテンツハッシュ | あり | なし | なし | なし | なし |
| Audit JSON | あり | なし | あり | なし | 一部 |
| ビルドの複雑さ | 1つのmake | 複雑 | 複雑 | 簡単 | 中程度 |
| 名前空間 | フラグ | 目的 |
|---|
| マウント | CLONE_NEWNS | 分離されたファイルシステムツリー |
| PID | CLONE_NEWPID | プロセスID空間 (子プロセスはpid 1) |
| ネット | CLONE_NEWNET | ネットワークインターフェースなし |
| IPC | CLONE_NEWIPC | 共有メモリ / セマフォなし |
| UTS | CLONE_NEWUTS | 独立したホスト名 |
| システムコール | 番号 | 備考 |
|---|
read | 0 | 標準入力 |
write | 1 | 標準出力/標準エラー + レポートパイプ |
openat | 257 | ファイルアクセス (openではない) |
close | 3 | — |
lseek | 8 | — |
brk | 12 | ヒープ管理 |
mmap | 9 | 引数制限: flags & 4 == 0 (MAP_SHAREDなし), flags == 0x22 (MAP_PRIVATE|MAP_ANONYMOUS) |
munmap | 11 | — |
execve | 59 | 起動時の単一exec |
exit_group | 231 | クリーンなプロセス終了 |
rt_sigaction | 13 | シグナルハンドラ |
rt_sigprocmask | 14 | シグナルマスキング |
getrandom | 318 | 乱数ソース |
clock_gettime | 228 | タイミング |
fstat | 5 | ファイルメタデータ |
| フラグ | 説明 |
|---|
--root=<dir> | サンドボックスルートディレクトリ (必須) |
--seccomp-enforce | seccomp-BPFシステムコールホワイトリストを有効化 |
--self-hash=<hex> | バイナリが期待されるBLAKE2b-256ハッシュと一致するか確認 |
--quiet | 監査出力を抑制 |
--verbose | デバッグログを有効化 |
--version | ビルドIDを表示 (Z-Jail/v1+dev) |
--help | 使い方を表示して終了 |
| コード | 意味 |
|---|
| 0 | 子プロセスが正常終了 (判定: DETERMINISTIC) |
| 1 | 子プロセスがシグナルで強制終了 (判定: REJECT) |
| 2 | 自己ハッシュ: 不正な16進文字列、またはファイルが読み取れない |
| 3 | 自己ハッシュ: 不一致 (バイナリが改ざんされている) |
| 101 | 子プロセスのセットアップエラー (rlimitなど) |
| 102 | 子プロセスのseccompフィルタインストール失敗 |
| 103 | 子プロセスのexecve失敗 (バイナリが見つからない、実行権限なし) |
| 104 | 子プロセスのpivot_root失敗 |
| 105 | 子プロセスのケーパビリティ削除失敗 |
| 125 | 名前空間の作成失敗 (rootで実行? カーネルサポート?) |
| # | シナリオ | タイプ | テスト内容 |
|---|
| 0 | blake2b_regress | 既知解 | BLAKE2b実装の正確性 |
| 1 | seccomp_filter | スタンドアロンBPF | BPFフィルタロジックの8サブテスト |
| 2 | hello_static | ok | 基本的な静的バイナリ実行 |
| 3 | hello_dynamic | ok | ld-linux + libcを使用した動的バイナリ |
| 4 | execve_replacement | ok | サンドボックス内のexecve (seccompでブロック) |
| 5 | fd_inherited_read | ok | 標準入力/標準出力が正しく継承される |
| 6 | mmap_bad_flags | killed | MAP_SHARED付きmmapをブロック |
| 7 | mmap_good_allowed | ok | MAP_PRIVATE|ANONYMOUS付きmmapを許可 |
| 8 | mmap_prot_exec | killed | PROT_EXEC付きmmapをブロック |
| 9 | mmap_self_modify | killed | 自己変更コードをブロック |
| 10 | ptrace | killed | ptraceをブロック |
| 11 | socket | killed | ソケット作成をブロック |
| 12 | chroot_escape | killed | chrootシステムコールをブロック |
| 13 | double_chroot | killed | 二重chrootをブロック |
| 14 | mount_replay | killed | マウントシステムコールをブロック |
| 15 | cpu_exhaust | killed | RLIMIT_NPROCがフォーク爆弾をブロック |
| 16 | signal_parent | killed | 親へのシグナルをブロック |
| 17 | self_hash | ok | バイナリ整合性検証 |
| ツール | レイテンシ平均 ± 標準偏差 | ピークRSS | デフォルトseccomp |
|---|
| Z-Jail | 5.85 ± 1.45 ms | 1.62 MiB | あり |
| bwrap | 3.56 ± 0.40 ms | 2.19 MiB | なし |
| nsjail | 8.98 ± 1.68 ms | 7.91 MiB | あり |
| ファイル | 説明 |
|---|
README.md | このファイル |
docs/ARCHITECTURE.md | アーキテクチャ概要 |
docs/SANDBOX.md | レイヤーごとのサンドボックス内部 |
docs/SECCOMP.md | seccomp-BPFホワイトリスト設計 |
docs/AUDIT_SCHEMA.md | 監査JSONスキーマリファレンス |
docs/THREAT_MODEL.md | セキュリティ前提条件と範囲 |
docs/BLAKE2B.md | BLAKE2b実装詳細 |
docs/BENCHMARKS.md | パフォーマンスベンチマーク |
docs/BUILD.md | ビルド手順 |
docs/adr/ | アーキテクチャ決定記録 (4文書) |
man/z_jail.1 | マニュアルページ |
SECURITY.md | セキュリティポリシーと報告 |
CONTRIBUTING.md | 貢献方法 |
CHANGELOG.md | リリース履歴 |
ROADMAP.md | 将来計画 |
TODO.md | 既知のギャップと計画中の作業 |