
軽量かつ多層的な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 | 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 | 複雑 | 複雑 | 簡単 | 中程度 |
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つの名前空間が作成されます:
| 名前空間 | フラグ | 目的 |
|---|---|---|
| マウント | CLONE_NEWNS | 分離されたファイルシステムツリー |
| PID | CLONE_NEWPID | プロセスID空間 (子プロセスはpid 1) |
| ネット | CLONE_NEWNET | ネットワークインターフェースなし |
| IPC | CLONE_NEWIPC | 共有メモリ / セマフォなし |
| UTS | CLONE_NEWUTS | 独立したホスト名 |
初期名前空間で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:
| システムコール | 番号 | 備考 |
|---|---|---|
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 | ファイルメタデータ |
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...]
| フラグ | 説明 |
|---|---|
--root=<dir> | サンドボックスルートディレクトリ (必須) |
--seccomp-enforce | seccomp-BPFシステムコールホワイトリストを有効化 |
--self-hash=<hex> | バイナリが期待されるBLAKE2b-256ハッシュと一致するか確認 |
--quiet | 監査出力を抑制 |
--verbose | デバッグログを有効化 |
--version | ビルドIDを表示 (Z-Jail/v1+dev) |
--help | 使い方を表示して終了 |
# すべての保護機能を有効にして静的バイナリを実行
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