結論:AIエージェントには「檻」が要る
AIエージェントにホストマシンのフルアクセスを渡していた過去の自分を殴りたい──Docker Sandbox と MCP で権限を最小化した開発環境を構築した全記録です。
この記事を読むと、以下がわかります。
- Claude Code をDockerコンテナ内に閉じ込め、ホスト環境を守る構成の作り方
- MCP(Model Context Protocol)でファイルアクセスやネットワークを最小権限に絞る方法
- 3ヶ月運用して得た6つの教訓(パフォーマンス、コスト、チーム展開)
環境・前提条件
| 項目 | バージョン / 仕様 |
|---|---|
| OS | Ubuntu 24.04 LTS(ホスト) |
| Docker | 27.x + Docker Compose v2 |
| Claude Code | 最新版(CLI) |
| Node.js | 22 LTS(MCP Server 実行用) |
| MCP SDK |
@modelcontextprotocol/sdk 最新 |
Claude Code の CLI インストールとAnthropicのAPIキーは取得済みとします。
なぜ Sandbox が必要か──Claude Code が rm -rf した日
ある日、私はClaude Code に「不要なビルド成果物を掃除して」と指示しました。Claude Code はプロジェクトルートから再帰的にファイルを削除し、.git ディレクトリごと吹き飛ばしました。幸いリモートリポジトリがあったので復旧できましたが、ローカルの未コミット変更は消滅しました。
問題の本質は、AIエージェントがホストのファイルシステムに直接アクセスできる状態だったことです。人間のエンジニアなら「.git を消すのはまずい」と判断できますが、LLM はコンテキストの解釈次第でどんなコマンドも実行します。
Claude Code には許可されたコマンドを自動実行する仕組みがありますが、それでもホスト環境への直接アクセスは根本的なリスクです。Sandbox で物理的に隔離する方が、設定ミスに対して圧倒的に堅牢です。
アーキテクチャ概要
構成のポイントは3つです。
- Claude Code の実行環境をDockerコンテナに閉じ込める
- ホスト側リソースへのアクセスはMCPサーバー経由に限定する
- ネットワーク・シークレット・ボリュームを最小権限で設計する
赤い境界線が最も重要な隔離ラインです。Claude Code はこの中でしか動作できず、ホストファイルシステムへのアクセスはMCP Filesystemサーバーが許可したディレクトリだけに制限されます。
手順1:Dockerfile と docker-compose.yml の設計
Dockerfile(最小権限イメージ)
# ---- Sandbox: Claude Code 実行環境 ----
FROM node:22-slim AS claude-sandbox
# セキュリティ: root 以外のユーザーで実行
RUN groupadd -r claude && useradd -r -g claude -m -s /bin/bash claude
# 必要最小限のツールのみインストール
RUN apt-get update && apt-get install -y --no-install-recommends \
git \
curl \
jq \
ca-certificates \
&& rm -rf /var/lib/apt/lists/*
# Claude Code CLI をグローバルインストール
RUN npm install -g @anthropic-ai/claude-code
# 作業ディレクトリ
WORKDIR /workspace
RUN chown claude:claude /workspace
# 非 root ユーザーに切り替え
USER claude
# Claude Code の設定ディレクトリを永続化ポイントとして用意
RUN mkdir -p /home/claude/.claude
ENTRYPOINT ["claude"]
重要なのは USER claude で非rootユーザーに切り替えている点です。万が一コンテナ内でエスケープが発生しても、ホスト側への影響を最小限に抑えます。
docker-compose.yml
version: "3.9"
services:
claude-sandbox:
build:
context: .
dockerfile: Dockerfile
container_name: claude-sandbox
stdin_open: true
tty: true
volumes:
# プロジェクトディレクトリのみマウント(読み書き)
- ./project:/workspace:rw
# Claude Code 設定の永続化
- claude-config:/home/claude/.claude
environment:
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}
networks:
- claude-net
# セキュリティ強化
security_opt:
- no-new-privileges:true
read_only: false
tmpfs:
- /tmp:size=512M
# リソース制限
deploy:
resources:
limits:
memory: 4G
cpus: "2.0"
mcp-filesystem:
image: node:22-slim
container_name: mcp-filesystem
working_dir: /app
volumes:
- ./mcp-servers/filesystem:/app:ro
- ./project:/data/project:ro # 読み取り専用でマウント
- ./project/output:/data/output:rw # 出力だけ書き込み可
command: ["node", "server.js"]
networks:
- claude-net
security_opt:
- no-new-privileges:true
deploy:
resources:
limits:
memory: 512M
cpus: "0.5"
networks:
claude-net:
driver: bridge
internal: true # 外部ネットワークアクセスを遮断
volumes:
claude-config:
internal: true が重要です。このネットワーク内のコンテナはインターネットに直接アクセスできません。外部APIへのアクセスが必要な場合の対処法は手順3で説明します。
手順2:MCP Filesystem Server の設定
MCP Filesystem Server を使うことで、Claude Code がアクセスできるディレクトリを厳密に制御します。
MCP サーバーの設定ファイル
Claude Code の MCP 設定(.claude/mcp.json)を以下のように記述します。
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-filesystem",
"/data/project",
"/data/output"
],
"env": {}
}
}
}
この設定により、Claude Code は /data/project(読み取り専用)と /data/output(書き込み可能)のみにアクセスでき、それ以外のパスへのファイル操作は拒否されます。
アクセス制御の流れ
こうすることで、たとえ Claude Code が「/etc/passwd を編集して」というような危険な操作を試みても、MCP サーバーの層で確実にブロックされます。
手順3:ネットワークポリシーと秘密情報の隔離
外部アクセスが必要な場合の構成
internal: true にすると外部APIにもアクセスできなくなります。Anthropic APIへのアクセスが必要なので、プロキシコンテナを挟みます。
# 外部アクセス用プロキシ(許可リスト方式)
api-proxy:
image: nginx:alpine
container_name: api-proxy
volumes:
- ./nginx/allowlist.conf:/etc/nginx/conf.d/default.conf:ro
networks:
- claude-net # 内部ネットワーク
- external-net # 外部ネットワーク
deploy:
resources:
limits:
memory: 128M
networks:
claude-net:
driver: bridge
internal: true
external-net:
driver: bridge
# internal を指定しない → 外部アクセス可
Nginx 許可リスト
# allowlist.conf - 許可するドメインのみプロキシ
server {
listen 8080;
# Anthropic API のみ許可
location /anthropic/ {
proxy_pass https://api.anthropic.com/;
proxy_set_header Host api.anthropic.com;
proxy_ssl_server_name on;
}
# GitHub API のみ許可
location /github/ {
proxy_pass https://api.github.com/;
proxy_set_header Host api.github.com;
proxy_ssl_server_name on;
}
# それ以外は全て拒否
location / {
return 403 "Access denied by allowlist policy";
}
}
シークレットの隔離
APIキーやSSH鍵は環境変数で直接渡さず、Docker Secrets を使います。
secrets:
anthropic_api_key:
file: ./secrets/anthropic_api_key.txt
services:
claude-sandbox:
secrets:
- anthropic_api_key
environment:
# ファイルパスを環境変数で参照
- ANTHROPIC_API_KEY_FILE=/run/secrets/anthropic_api_key
絶対にやってはいけないこと:
-
.envファイルをコンテナ内にマウントする - SSH秘密鍵をイメージにベイクする
- APIキーを
docker-compose.ymlにハードコードする
教訓1-3:インフラ運用で踏んだ落とし穴
教訓1:コンテナ再起動時の状態保持
Claude Code はセッション中にコンテキストを蓄積します。コンテナが再起動するとすべて消えます。
解決策:
volumes:
# Claude の設定とセッション情報を永続化
- claude-config:/home/claude/.claude
# 作業中のファイルはホスト側にマウント
- ./project:/workspace:rw
加えて、Claude Code の --resume フラグでセッションを継続できるようにしておくと、コンテナ再起動後もスムーズに復帰できます。
教訓2:ボリュームマウントのパフォーマンス
macOS の Docker Desktop でバインドマウントを使うと、ファイルI/Oが著しく遅くなります。特にnode_modulesのような大量の小さいファイルがあるディレクトリで顕著です。
解決策:
volumes:
# node_modules は名前付きボリュームにする(パフォーマンス改善)
- node_modules:/workspace/node_modules
volumes:
node_modules:
Linux ホストでは問題になりにくいですが、macOS で開発する場合は名前付きボリュームか :delegated オプションの活用を検討してください。
教訓3:GPU パススルー
ローカルでLLMを動かすわけではないので基本的にGPUは不要ですが、Claude Code にコード生成させた機械学習モデルのテスト実行でGPUが必要になるケースがありました。
services:
claude-sandbox:
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: 1
capabilities: [gpu]
NVIDIA Container Toolkit のインストールが前提です。必要なときだけGPU付きプロファイルを使う docker compose --profile gpu up の運用がおすすめです。
教訓4-6:運用と組織展開
教訓4:コスト管理
コンテナを起動しっぱなしにしていると、Claude Code が待機中でもリソースを消費します。また、APIコールの回数管理も重要です。
自動停止スクリプトの例:
#!/bin/bash
# 30分間 Claude Code プロセスのCPU使用率が1%未満なら停止
IDLE_THRESHOLD=1800 # 秒
while true; do
CPU=$(docker stats claude-sandbox --no-stream --format '{{.CPUPerc}}' | tr -d '%')
if (( $(echo "$CPU < 1.0" | bc -l) )); then
IDLE_COUNT=$((IDLE_COUNT + 60))
if [ $IDLE_COUNT -ge $IDLE_THRESHOLD ]; then
docker compose stop claude-sandbox
echo "$(date): Auto-stopped due to inactivity" >> /var/log/claude-sandbox.log
exit 0
fi
else
IDLE_COUNT=0
fi
sleep 60
done
教訓5:ログ収集
Claude Code の入出力ログは、プロンプト改善の宝庫です。しかし、コンテナ内のログはデフォルトでは揮発します。
services:
claude-sandbox:
logging:
driver: json-file
options:
max-size: "50m"
max-file: "5"
volumes:
# Claude Code のセッションログを永続化
- claude-logs:/home/claude/.claude/logs
さらに、以下のような wrapper スクリプトを ENTRYPOINT にすると、全セッションの入出力をタイムスタンプ付きで記録できます。
#!/bin/bash
# entrypoint.sh
LOG_DIR="/home/claude/.claude/logs"
SESSION_LOG="${LOG_DIR}/session_$(date +%Y%m%d_%H%M%S).log"
mkdir -p "$LOG_DIR"
echo "=== Session started: $(date) ===" >> "$SESSION_LOG"
claude "$@" 2>&1 | tee -a "$SESSION_LOG"
echo "=== Session ended: $(date) ===" >> "$SESSION_LOG"
教訓6:チームでの共有テンプレート化
個人の環境で動くだけでは不十分です。チームメンバーが git clone → docker compose up で同じ環境を再現できる必要があります。
テンプレートリポジトリの構成:
claude-sandbox-template/
├── docker-compose.yml
├── docker-compose.gpu.yml # GPU プロファイル
├── Dockerfile
├── .env.example # シークレットのテンプレート
├── nginx/
│ └── allowlist.conf
├── mcp-servers/
│ └── filesystem/
│ ├── server.js
│ └── package.json
├── project/ # ここにプロジェクトを配置
│ └── .gitkeep
├── scripts/
│ ├── setup.sh # 初期セットアップ
│ ├── auto-stop.sh
│ └── entrypoint.sh
├── CLAUDE.md # Claude Code 用プロジェクト指示
└── README.md
setup.sh でシークレットファイルの生成と初回ビルドを自動化しておくと、オンボーディングがスムーズになります。
#!/bin/bash
# setup.sh
echo "=== Claude Code Sandbox Setup ==="
if [ ! -f ./secrets/anthropic_api_key.txt ]; then
mkdir -p ./secrets
read -sp "Enter your Anthropic API Key: " API_KEY
echo "$API_KEY" > ./secrets/anthropic_api_key.txt
echo ""
echo "✅ API key saved"
fi
docker compose build
echo "✅ Setup complete. Run: docker compose up -d"
まとめ
- AIエージェントにはDocker Sandboxによる物理的な隔離が必須──設定ベースの権限制御だけでは、設定ミスひとつでホスト環境が破壊されるリスクがある
- MCP Filesystem Server でアクセス可能なディレクトリをホワイトリスト方式で制限することで、LLMの誤操作を確実にブロックできる
-
テンプレート化してチームに展開することが最終ゴール──
git clone→docker compose upで誰でも同じセキュアな環境を再現できる状態を目指す
テンプレートリポジトリは近日中にGitHubで公開予定です。今後の改善計画としては、Devcontainer対応によるVS Code / Cursor統合、MCPサーバーの動的な権限変更(プロジェクトごとのポリシーファイル)、コスト分析ダッシュボードの追加を予定しています。