Claude CodeをDevContainerで安全に動かす完全実装 — sandbox設計と認証情報の遮断
Claude CodeをDevContainer / Docker sandbox内で動かし、ホストの認証情報や本番接続から隔離する手順。--dangerously-skip-permissions を安全に使う前提環境をまとめます。
要点
Claude Codeをコンテナの中で動かすと、Bash だけでなく、ファイル編集ツール・MCPサーバー・フックまで同じ境界の内側に入ります。組み込みのBashサンドボックスが囲むのはシェルコマンドだけで、MCPサーバーとフックはホスト上でそのまま動くためです。--dangerously-skip-permissions を使う無人実行では、この違いが効いてきます。Colima上のコンテナで再認証後にエラーが出る場合は、Unsupported platformの切り分けを参照してください。
ただしコンテナは万能ではありません。コンテナの中にあるものは、Claude Codeからすべて見えます。~/.claude に置かれた認証情報も例外ではありません。この記事では、DevContainerと自前Dockerの組み方に加えて、「何を渡さないか」「ネットワークをどう絞るか」の設計を、v2.1.287の --help の実出力とあわせて扱います。すでにコンテナで隔離している環境で二重の隔離を避けたい場合は、sandbox.filesystem.disabledはファイルシステム分離だけ外す設定が参考になります。
自分の状況ならどの隔離を選ぶか
Claude Codeの隔離は、守る範囲の広さで三つに分かれます。
守る範囲で選ぶ三つの隔離
Bashサンドボックス
Docker不要で、/sandbox から有効にします。囲むのはBashなどのコマンドとその子プロセスで、MCPサーバーとフックは対象外です。無人実行の境界としては足りません。
DevContainer
Claude Code全体をDockerコンテナに入れます。.devcontainer/ をリポジトリに置けば全員が同じ環境になります。Docker前提で、設定の手間は中程度です。
自前のDockerコンテナ
ネットワークポリシー、マウント、seccompプロファイルを自分で決められます。既存のコンテナ基盤やCIランナーがある組織で選ばれやすい形です。
信頼できないリポジトリを扱う場合は、専用の仮想マシンか、Claudeのサブスクリプションがあればクラウドセッション(Anthropic管理のVM)を使います。
DockerなしでMCPサーバーやフックまで囲みたいときは、サンドボックスランタイムを npx @anthropic-ai/sandbox-runtime claude で起動します。ファイル編集ツールを含むClaude Codeのプロセス全体が境界の内側に入ります。ベータの研究プレビューです。
既定では通信と書き込みが閉じているため、~/.srt-settings.json でプロジェクト・~/.claude・~/.claude.json への書き込みとAPIの宛先を許可してから起動します。Linux/WSL2では、bubblewrap・socat・ripgrepも必要です。
DevContainerに入れる手順
最短の構成は、公式のDev Container Featureを使う形です。Featureが claude のインストールと、VS Code用拡張の追加までやってくれます。
DevContainerでClaude Codeを動かす流れ
- 1
devcontainer.json に Feature を足す
.devcontainer/devcontainer.jsonのfeaturesにghcr.io/anthropics/devcontainer-features/claude-code:1.0を追加します。末尾の:1.0は、インストールスクリプトの版を示しています。Claude Code本体の版ではありません。 - 2
コンテナを再ビルドする
VS Codeならコマンドパレットの
Dev Containers: Rebuild Containerを実行します。Node.jsが無いベースイメージでは、Featureが自分でNode.jsを入れます。それでも失敗してFailed to install Node.js and npmで止まったら、Node.jsのFeatureを先に並べます。 - 3
コンテナ内でサインインする
ターミナルで
claudeを実行し、表示に従って認証します。ブラウザのコールバックがコンテナに届かないときは、ブラウザに出たコードをPaste code here if promptedの欄へ貼り付けます。
{
"image": "mcr.microsoft.com/devcontainers/base:ubuntu",
"features": {
"ghcr.io/anthropics/devcontainer-features/claude-code:1.0": {}
}
}image の行は、プロジェクトのベースイメージに置き換えます。Dockerfileを使っている場合は、この行ごと外します。
バージョンの固定と自動更新
このFeatureは常に最新のClaude Codeを入れ、コンテナ内でも自動更新が有効です。再現性が必要なら、FeatureをやめてDockerfileで npm install -g @anthropic-ai/claude-code@X.Y.Z と版を指定し、containerEnv に DISABLE_AUTOUPDATER を "1" で入れます。同じ containerEnv に CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC を足すと、テレメトリーとエラー報告も止まります。この変数はフィーチャーフラグの取得も止めるため、Remote Controlなど、その取得に依存する機能は使えなくなります。
再ビルドしても認証を残す
コンテナのホームディレクトリは、再ビルドのたびに消えます。トークン・設定・会話履歴は ~/.claude にありますが、OAuthアカウントと個人用MCPサーバーは別ファイルの ~/.claude.json に入ります。そのため ~/.claude だけをボリュームにしても、サインインは保たれません。
"mounts": [
"source=claude-code-config,target=/home/node/.claude,type=volume"
],
"containerEnv": {
"CLAUDE_CONFIG_DIR": "/home/node/.claude"
}CLAUDE_CONFIG_DIR を同じパスに向けると、.claude.json もボリュームの中に書かれます。/home/node は remoteUser のホームに読み替えてください。プロジェクトごとに状態を分けたいときは、ボリューム名に ${devcontainerId} を含めます。GitHub Codespacesでは、停止と再開では ~/.claude が残り、再ビルドで消えます。codespaceをまたいで認証を持ち越すなら、ANTHROPIC_API_KEY か claude setup-token で作ったトークンを CLAUDE_CODE_OAUTH_TOKEN として、Codespacesのシークレットに入れます。Codespacesでの具体的な流れはClaude Code CodespacesでDevContainer開発を始める手順にあります。
コンテナの中でも漏れるもの
コンテナが防ぐのは「ホストのファイルに触れること」です。「コンテナ内にあるものの持ち出し」は防げません。公式ドキュメントも、--dangerously-skip-permissions で動かすと、悪意あるプロジェクトがコンテナ内のあらゆるものを外へ送り出せると警告しています。~/.claude に保存されたClaude Codeの認証情報も、その対象に含まれます。
ここから、渡し方の原則が決まります。
~/.sshやクラウドの認証情報ファイルを、コンテナにマウントしない- 渡すなら、リポジトリ単位や短期間で失効するトークンにする
- 信頼できるリポジトリでだけ使う
クラウドの認証情報を渡す場合も、ファイルのマウントより環境変数のほうが範囲を絞りやすくなります。Bedrock・Vertex・Foundryを使う構成では、containerEnv、Codespacesのシークレット、クラウドのワークロードIDのいずれかで渡すのが公式の案内です。
どうしてもAWSの設定を見せる場合は、読み取り専用のマウントにして、中身を最小権限のプロファイルだけにします。
"mounts": [
"source=${localEnv:HOME}/.aws-sandbox,target=/home/node/.aws,type=bind,readonly"
]この例では、本番プロファイルを含む ~/.aws を直接マウントせず、コンテナ用に切り出した別ディレクトリを見せています。マウント先は、コンテナの実行ユーザー(ここでは node)のホーム配下にします。/root/.aws に置くと、非rootで動くClaude Codeからは読めないことが多くなります。
自前Dockerで立てる(CI・自動化向け)
CIや、セッションを完全に独立させたい場合は、Dockerfileと起動スクリプトを自分で用意します。認証情報の永続化とヘッドレス実行に絞った手順は、Claude Code Docker実行ガイドにあります。
FROM node:20-bookworm-slim
RUN apt-get update && apt-get install -y --no-install-recommends \
git \
jq \
curl \
ca-certificates \
&& rm -rf /var/lib/apt/lists/*
RUN npm install -g @anthropic-ai/claude-code
# 非 root ユーザーで動かす
RUN useradd -m -s /bin/bash claude && \
mkdir -p /workspace /home/claude/.claude && \
chown claude:claude /workspace /home/claude/.claude
USER claude
WORKDIR /workspace
ENTRYPOINT ["claude"]実行ファイル名は claude です。npm install -g @anthropic-ai/claude-code はパッケージ名で、コマンド名ではありません。
#!/bin/bash
# scripts/run-claude-sandbox.sh
set -euo pipefail
REPO_PATH="$(git rev-parse --show-toplevel)"
docker run --rm -it \
--name claude-sandbox \
--cap-drop=ALL \
--read-only \
--tmpfs /tmp \
-v claude-config:/home/claude/.claude \
-e CLAUDE_CONFIG_DIR=/home/claude/.claude \
-v "$REPO_PATH:/workspace:rw" \
-e ANTHROPIC_API_KEY \
claude-sandbox:latest \
--dangerously-skip-permissions各オプションの役割は次のとおりです。
--read-only: ルートファイルシステムを読み取り専用にします。ただしClaude Codeは設定の保存先とランタイムファイルの置き場に書き込むため、~/.claudeはボリューム、/tmpは--tmpfsで書けるようにします。Dockerfileで~/.claudeをclaudeユーザーの所有にしてあるので、新しい名前付きボリュームにもその所有者が引き継がれます--cap-drop=ALL: Linux capabilityを全部外し、CAP_SYS_ADMINのような特権を渡しません-v "$REPO_PATH:/workspace:rw": 作業対象のディレクトリだけが書き込み可能です-e ANTHROPIC_API_KEY: 値を書かずに変数名だけを指定すると、ホストの環境変数がそのまま渡ります。キーがコマンドラインやスクリプトに残りません
--network は指定していません。Dockerの既定のbridgeネットワークは、外部へ自由に通信できる設定で、隔離ではないためです。
このコンテナでも、作業ディレクトリは書き込み可能です。変更はそのままホストのリポジトリに現れるので、無人実行のあとは git diff に加えて .git/hooks と .mcp.json、.claude/commands、.claude/agents の変更も見ておきます。サンドボックスランタイムは、これらを既定で書き込み禁止にしています。書き込めると、次回以降にサンドボックス外で動く設定を仕込めてしまうためです。bind mountした作業ディレクトリは、書き込みが許可されたままです。
ネットワークをどう絞るか
--network none にすればすべての通信が止まりますが、APIにも届かなくなり、Claude Codeは動きません。実用的なのは、必要な宛先だけを許可する方式です。
v2.1.287の --help は、このフラグを次のように説明しています。
--dangerously-skip-permissions Bypass all permission checks.
Recommended only for sandboxes with no
internet access.「インターネットに出られないサンドボックスだけで推奨」という書き方です。一方、DevContainerの公式ページは、許可リスト方式のファイアウォールを組み合わせる構成を案内しています。参照用コンテナには init-firewall.sh があり、許可した宛先以外への送信を止めます。ファイアウォールをコンテナ内で動かすには NET_ADMIN と NET_RAW のcapabilityが要りますが、Claude Code自体には不要です。独自のネットワーク制御があるなら、省いて構いません。許可リストの組み方はinit-firewall.shの解説で扱っています。
必要なドメインは、推論と認証で決まります。APIキーで認証するなら、OAuth用の claude.ai と platform.claude.com は外せます。MCPサーバーを使うなら、リモートサーバーのドメインも許可リストに加えます。
rootでは起動しない
--dangerously-skip-permissions は、rootで起動すると拒否されます。LinuxとmacOSでの仕様です。DevContainerでは remoteUser、自前Dockerでは USER を非rootにします。上のDockerfileで USER claude を入れているのは、このためです。
チームで運用するときの設定
チームで同じコンテナを配るときの要点は三つです。
.devcontainer/をgitで管理し、全員が同じ設定で動かす- ポリシーは
/etc/claude-code/managed-settings.jsonに置き、DockerfileからCOPYする。Linuxでは最上位の優先度で読まれ、個人の~/.claudeやプロジェクトの.claude/より強く効く - MCPサーバーはリポジトリ直下の
.mcp.json(プロジェクトスコープ)に書き、コンテナ構成と一緒にチェックインする
注意点が一つあります。DockerfileはリポジトリにあるのでGitの書き込み権限を持つ人なら、この COPY を外せます。エンジニアに迂回させたくないポリシーは、サーバー管理設定かMDMで配ります。--dangerously-skip-permissions そのものを禁止するには、管理設定で permissions.disableBypassPermissionsMode を "disable" にします。管理設定の全体像はClaude Codeの管理設定ガイドにあります。
プロンプトを減らしたいだけなら、--dangerously-skip-permissions の代わりに、分類器が操作を審査するオートモードという選択肢もあります。
危険コマンドの誤実行への最終防御として
Claude Codeが期待通りに動かない10シナリオのシナリオ4(危険コマンドの誤実行)に対しては、コンテナ境界が最後の防御になります。フックで危険なコマンドを止める方法はHooks実例カタログのレシピ3にあります。フックがコマンドの中身を見て止め、コンテナが届く範囲を絞る、という役割分担です。
まとめ
無人実行を前提にするなら、境界はコンテナかVMに置き、そこへ渡す認証情報とネットワークの宛先を最小にします。コンテナ内の秘密は持ち出せるので、「何をマウントしないか」が設計の中心です。
参照した一次ソース
この記事の記述は、次の一次ソースと照合しています。更新時に参照先が変わった場合は一覧も更新します。