AIエージェント入門

AIエージェント サンドボックス設計|gVisor比較

AIエージェント サンドボックス設計|gVisor比較

この記事の結論

AIエージェントの隔離実行設計。gVisor・Firecracker・nsjailを比較しコード例で解説。





「AIエージェントにPythonコードを生成させ、そのまま実行してもらう」——この構成を本番に持ち込んだとき、最初に直面するのはサンドボックスの問題だ。

LLMが生成するコードは、良く書かれた意図的な悪意がなくても、予期しないファイルシステム操作やネットワーク接続を起こすことがある。複数のユーザーが同じエージェントを使う環境では、ある実行が別のユーザーのデータに触れる可能性も排除できない。

2026年時点の業界コンセンサスは明快だ。共有カーネルのコンテナ(Docker/runc)だけでは、AIエージェントの実行コードを隔離するには不十分。信頼できないコードは「敵対的なコード」として扱う必要がある。

まず試したい「5分でわかる」隔離レベルの確認

本番設計の前に、自分の環境で今どの程度の隔離が実現しているかを確認しておこう。以下のスクリプトを実行すると、現在のコンテナが持つ権限の範囲が分かる。

#!/bin/bash
# sandbox_audit.sh — 現在のコンテナ隔離レベルを確認するスクリプト
# 動作環境: Linux (Docker内またはホスト)
# 注意: 本番環境で使用する前に、必ずテスト環境で動作確認してください。

echo "=== カーネルバージョン ==="
uname -r

echo ""
echo "=== Seccompフィルターの有効確認 ==="
cat /proc/1/status | grep Seccomp
# 0 = なし, 1 = strict, 2 = filter(bpf)

echo ""
echo "=== 利用可能なCapabilities ==="
cat /proc/1/status | grep Cap
# CapBnd が 0000000000000000 なら制限済み

echo ""
echo "=== ファイルシステムの書き込み可能エリア ==="
mount | grep "rw," | awk '{print $3}' | head -10

echo ""
echo "=== ネットワーク接続の確認 ==="
ip route show 2>/dev/null | head -5 || echo "ネットワーク制限あり"

ポイント: Seccomp: 2(BPFフィルター有効)でも、ホストカーネルを共有している事実は変わらない。カーネル脆弱性が見つかった場合のリスクを消すには、後述のgVisorかFirecrackerが必要になる。

3つのアプローチで考えるサンドボックス設計

name=”td”>マイクロVM
アプローチ 技術 隔離レベル 起動時間 主な用途
強化コンテナ Docker + seccomp/AppArmor 名前空間 + システムコールフィルター 〜50ms 内部自動化、信頼できるコード
ユーザースペースカーネル gVisor(runsc) ユーザースペースでシスコール再実装 〜100ms マルチテナントSaaS、API公開
Firecracker, Kata Containers ハードウェア仮想化(KVM) 〜125〜200ms 信頼できないコード、金融・医療
プロセス隔離 nsjail 名前空間 + seccomp-bpf + cgroupsの組み合わせ 〜10ms 低レイテンシ要件、Google Production

(最終確認日: 2026-04-11。起動時間は環境依存のため参考値。)

gVisorの実装: Kubernetesへの組み込み

gVisorは、コンテナのシステムコールをユーザースペースの「Sentry」プロセスが傍受・再実装することで、ホストカーネルへの攻撃面を大幅に縮小する。GoogleがSearch・Gmail・GKEで採用している実績がある。

KubernetesでのRuntimeClass設定例:

# gvisor-runtimeclass.yaml
# 動作環境: Kubernetes 1.25+, gVisor (runsc) インストール済みノード
# 前提: sudo apt-get install runsc (Debian/Ubuntu)
# 注意: 本番環境で使用する前に、必ずテスト環境で動作確認してください。

---
apiVersion: node.k8s.io/v1
kind: RuntimeClass
metadata:
  name: gvisor
handler: runsc
scheduling:
  nodeClassifier:
    gvisor-enabled: "true"

---
# AIエージェント実行Pod: gVisorを指定
apiVersion: v1
kind: Pod
metadata:
  name: ai-code-executor
  namespace: agent-sandbox
spec:
  runtimeClassName: gvisor  # ← ここでgVisorを指定
  restartPolicy: Never
  containers:
  - name: executor
    image: python:3.11-slim
    # ネットワーク: エグレスのみallowlist
    # ファイルシステム: readOnlyRootFilesystem
    securityContext:
      readOnlyRootFilesystem: true
      runAsNonRoot: true
      runAsUser: 1000
      allowPrivilegeEscalation: false
      capabilities:
        drop: ["ALL"]
    resources:
      limits:
        cpu: "500m"
        memory: "256Mi"
        ephemeral-storage: "100Mi"
      requests:
        cpu: "100m"
        memory: "64Mi"
    command: ["python3", "/sandbox/script.py"]
    volumeMounts:
    - name: script
      mountPath: /sandbox
      readOnly: true
    - name: tmp
      mountPath: /tmp
  volumes:
  - name: script
    configMap:
      name: agent-script
  - name: tmp
    emptyDir:
      medium: Memory  # /tmp をメモリ上に配置(ディスクI/O回避 + 自動クリーンアップ)

ポイント: /tmpemptyDir.medium: Memory にすることで、Pod削除と同時にデータが消える。ディスクに一時ファイルが残るリスクをゼロにできる。

Firecracker microVMの実装: AWS Lambda方式

FirecrackerはAWSがLambdaのために開発したマイクロVMマネージャーだ。1VMあたり5MiB未満のメモリオーバーヘッドで、毎秒150台のVM起動が可能。各VMが独自のLinuxカーネルを持つため、ホストへのカーネル攻撃が理論上不可能になる。

Firecrackerを使った簡易サンドボックスのセットアップ例(Linux、KVM必須):

#!/bin/bash
# firecracker_sandbox.sh — Firecracker microVMで1回限りの実行環境を作る
# 動作環境: Linux, KVM有効, firecracker v1.8+
# 前提: wget https://github.com/firecracker-microvm/firecracker/releases/latest から取得
# 注意: 本番環境で使用する前に、必ずテスト環境で動作確認してください。

FIRECRACKER_SOCKET="/tmp/fc-$(date +%s%N).sock"
KERNEL_IMAGE="/opt/firecracker/vmlinux.bin"
ROOTFS_IMAGE="/opt/firecracker/agent-sandbox.ext4"

# VMソケットのクリーンアップを確保
cleanup() {
    rm -f "$FIRECRACKER_SOCKET"
    echo "[sandbox] VMソケット削除完了"
}
trap cleanup EXIT

# Firecracker VMを起動
firecracker 
    --api-sock "$FIRECRACKER_SOCKET" 
    --config-file /dev/stdin <<EOF
{
  "boot-source": {
    "kernel_image_path": "$KERNEL_IMAGE",
    "boot_args": "console=ttyS0 reboot=k panic=1 pci=off"
  },
  "drives": [
    {
      "drive_id": "rootfs",
      "path_on_host": "$ROOTFS_IMAGE",
      "is_root_device": true,
      "is_read_only": true
    }
  ],
  "machine-config": {
    "vcpu_count": 1,
    "mem_size_mib": 128,
    "smt": false
  },
  "network-interfaces": []
}
EOF

# ポイント: network-interfaces を空配列にすることでネットワーク完全遮断
# ポイント: is_read_only: true でrootfsへの書き込みを禁止

nsjailの実装: Googleが本番で使うプロセス隔離

nsjailはGoogleが開発し、CTFのコード実行環境やバッチジョブの隔離に使われているプロセス隔離ツールだ。Linuxの名前空間、seccomp-bpfシステムコールフィルター、cgroupsリソース制限を組み合わせる。Firecrackerより起動が速く、VM不要で動作する。

#!/bin/bash
# nsjail_run.sh — nsjailでAIエージェントのコードを実行する
# 動作環境: Linux, nsjail v3.4+
# 前提: apt-get install nsjail (Debian/Ubuntu)
# 注意: 本番環境で使用する前に、必ずテスト環境で動作確認してください。

SCRIPT_PATH="$1"  # 実行するPythonスクリプトパス
TIMEOUT_SEC=30    # タイムアウト(秒)

nsjail 
    --mode once                          # 1回実行で終了
    --time_limit "$TIMEOUT_SEC"          # タイムアウト強制
    --chroot /opt/sandbox-rootfs         # ファイルシステムルートの変更
    --user 65534                         # nobody ユーザーで実行
    --group 65534 
    --log /var/log/nsjail/exec.log       # 全操作のログ記録
    
    # ネットワーク設定
    --disable_clone_newnet               # ネットワーク名前空間を無効化
    
    # システムコールのフィルタリング(危険な呼び出しをブロック)
    --seccomp_policy_fd=/etc/nsjail/python-policy.bpf 
    
    # リソース制限
    --rlimit_as 268435456                # メモリ上限: 256MiB
    --rlimit_cpu "$TIMEOUT_SEC"          # CPU時間上限
    --rlimit_fsize 10485760              # ファイルサイズ上限: 10MiB
    --rlimit_nofile 32                   # 開けるファイル数上限
    
    -- python3 "$SCRIPT_PATH"

EXIT_CODE=$?
echo "[nsjail] 終了コード: $EXIT_CODE"

# ログからの異常検知例
if grep -q "SECCOMP" /var/log/nsjail/exec.log; then
    echo "[ALERT] 禁止されたシステムコールが検出されました"
fi

【要注意】サンドボックス設計でよくある失敗パターン

失敗1: 「コンテナ = 安全」という前提で進める

❌ Dockerの--privilegedフラグを省いていれば十分だと判断する
⭕ カーネルを共有していることを前提に、gVisorかFirecrackerで追加のカーネル隔離層を設ける

なぜ重要か: 2024年から2026年にかけて、共有カーネルのコンテナを破るエクスプロイトが複数公開されている。AIエージェントの実行コードは信頼できないコードとして扱うべきだ。

失敗2: ネットワーク制限を後から追加しようとする

❌ まず動くサンドボックスを作り、ネットワーク制限は後でやる
⭕ 設計の最初からエグレスを完全遮断し、必要なエンドポイントだけをallowlistに追加する

なぜ重要か: AIエージェントが生成したコードがC2サーバーにデータを送信するシナリオは、ネットワーク制限がなければ検出が難しい。デフォルトdenyから始めるのが原則だ。

失敗3: タイムアウトを1箇所にしか設定しない

❌ アプリケーションレベルのタイムアウトだけを設定する
⭕ コール単位・タスク単位・サンドボックス単位の3層でタイムアウトを設定する

なぜ重要か: アプリケーションのタイムアウトがバグで機能しない場合でも、カーネルレベルのcgroups CPU制限(--rlimit_cpu)が最終的な安全網として機能する。

失敗4: ログを取らずに稼働させる

❌ サンドボックスの内部で何が起きたかを追跡しない
⭕ nsjailの--log、あるいはOpenTelemetryでシステムコールレベルのイベントを記録する

なぜ重要か: セキュリティインシデント発生時に何が起きたか遡れなければ、再発防止策が打てない。AIエージェントの実行ログは最低30日保持することを推奨する。

Claude Code sandboxから学ぶ設計の思想

Claude Codeが採用している実行環境の設計は、参考になる点が多い。公開情報から読み取れる原則を整理すると:

  • エフェメラルな実行環境: タスク完了後にコンテナを破棄する。状態の持ち越しは明示的なファイル出力のみ
  • 最小権限の認証情報: 永続APIキーではなく、短命のトークンをタスクごとに発行する
  • 推論と実行の分離: LLMの推論インフラとコード実行インフラを別のネットワークセグメントに置く
  • 人間の承認ゲート: ファイルシステムへの書き込みや外部APIの呼び出し前に確認を求める設定が可能

これらはAIエージェントのサンドボックス設計における「デフォルトdeny + 明示的許可」の哲学を体現している。

セキュリティと運用ルール

プロダクションへの導入前に確認すべき項目:

  • seccompポリシーのテスト: straceでアプリケーションが実際に呼ぶシステムコールを計測し、必要最小限のallowlistを作る
  • カーネルバージョン固定: gVisorはホストカーネルバージョンに依存するため、アップグレード時の互換性テストが必要
  • KVMの確認(Firecracker): ls /dev/kvm で存在確認。クラウドのVMではネストされた仮想化が必要
  • イメージの最小化: サンドボックス内のrootfsは必要なバイナリのみに絞る(distrolessが参考になる)

AIエージェントのオブザーバビリティ設計全体については、AIエージェントのオブザーバビリティ設計でまとめている。サンドボックスのログ設計と組み合わせると、実行の可視性が格段に上がる。

エージェントの基本アーキテクチャはAIエージェント構築完全ガイドを参照してほしい。

参考・出典


まとめ:今日から始める3つのアクション

  1. 今日やること: sandbox_audit.shを既存の実行環境で走らせ、Seccompフィルターの有効状態とCapabilitiesを確認する。
  2. 今週中: テスト環境でgVisorのRuntimeClassをKubernetesに追加し、既存のエージェント実行Podを切り替えて動作確認する。
  3. 今月中: nsjailのseccompポリシーをアプリケーション固有にカスタマイズし、不要なシステムコールをすべてブロックした状態で本番リリースする。

あわせて読みたい:


佐藤傑(さとう・すぐる)
株式会社Uravation代表取締役。早稲田大学法学部在学中に生成AIの可能性に魅了され、X(旧Twitter)で活用法を発信(@SuguruKun_ai、フォロワー10万人超)。100社以上の企業向けAI研修・導入支援を展開。著書累計3万部突破。
ご質問・ご相談は お問い合わせフォーム からお気軽にどうぞ。

Need help moving from reading to rollout?

この記事を読んで導入イメージが固まってきた方へ

Uravationでは、AIエージェントの要件整理、PoC設計、社内導入、研修まで一気通貫で支援しています。

この記事をシェア

X Facebook LINE

※ 本記事の情報は2026年4月時点のものです。サービスの料金・仕様は変更される可能性があります。最新情報は各サービスの公式サイトをご確認ください。

関連記事