はじめに
AIエージェントのコーディング環境として、Docker Sandboxesを使っています。
本稿執筆時点ではまだExperimentalな扱いなので、まとめ記事を書くタイミングが難しいのだけど、とは言え「やってみた」以上の情報がまだまだWeb上に少なく、周りに勧めづらいなぁというのもあるので、自分なりの使い方や設定のカスタマイズ方法などをまとめておきます。開発環境は人それぞれだと思うので、事例の1つとして誰かの参考になればよいのではなかろうか。
ちなみにこの記事は当初思ったよりもずいぶん長くなってしまいましたが、全部人間が書いております。
Docker Sandboxesとは
Docker社が開発している、AIエージェント向けのサンドボックス機能です。クローズドソースですが、本稿執筆時点ではDocker Desktopのライセンスなしでも無料で商用利用可能です。一部のガバナンス関連の機能だけ有償となっています。
一般的にAIエージェント向けのサンドボックスと呼ばれているものには、アーキテクチャレベルでいくつかの種類があります。
たとえばClaude Codeのサンドボックスモードは、OSのセキュリティ機能(macOSであればApple Seatbelt)を利用して、ファイルシステムやネットワークアクセスを制限します。これでも最低限の保護は得られますが、AIエージェントに組み込まれたサンドボックス機能の安全性は、AIエージェントの実装次第です。Claude Codeのサンドボックスモードを使ってみた人はたぶん気づいてると思いますが、あれはBashツールにしか制限がかかっておらず、通信先を制限しているつもりでも、他のツール呼び出し経由ですり抜けてきます。多層防御は否定しませんが、Claude Codeの最新版はよくバグってるので、保護レイヤとして信頼がおけるのかという構造的な問題もある。
で、次に思いつく方法はコンテナによる隔離です。開発にDockerコンテナを使っている人には自然な発想ですが、いわゆるLinuxのコンテナというのは本質的に名前空間の分離によるリソース制限であって、セキュリティの境界にはなり得ません。技術的な側面だけ見ると以前からそうだったのですが、この感覚はまだ一般には共有されていないかもしれません。というのも、これまで開発者が手書きしていたコードは暗黙に信頼されていましたが、AIがコードを生成してその場で実行する時代に前提が変わってしまいました。またAIによるセキュリティ監査の発展に伴いLinuxの特権昇格の脆弱性が現在進行系でいっぱい見つかっています。ホストとカーネルを共有しているコンテナ内で、AIエージェントが生成する信頼できないコードを実行するのは危険ですし、開発/テストでAIエージェントにdockerコマンドを許可すると、実質的にホストでの任意コマンド実行を許可しているのと同じです。AIエージェントのallow設定でdockerコマンドを許可すべきかどうか悩んだことある人も多いはず。
第3の選択肢がMicroVMです。一般的な仮想マシン(VM)は汎用的な多くの機能をエミュレーションする必要があり、巨大な実装になりがちで、結果として起動も遅くなります。しかしながら、現代の開発において例えばフロッピーディスクは必要か?と改めて前提を疑うと、多くのユースケースではそうでもないかなというのもまた真です。というわけで、仮想マシンをエミュレーションするのに最低限必要な機能セットを見直してスリム化することで、起動の高速化や攻撃面積を極小化したMicroVMという選択肢があります。MicroVMは小さくても独立した仮想マシンであるので、ホストとカーネルを共有しません。カーネルを共有するコンテナと比較して、MicroVMのハイパーバイザの隔離を貫通する脆弱性は技術的に難易度が高く、信頼できないユーザコードを動かすFaaSやCI/CDサービスの裏側で使われてきました。Docker SandboxesはこのMicroVMレベルでの隔離を使っています。構造上dockerデーモンも共有しないので、AIエージェントにdockerコマンドを許可しても影響をMicroVM内に閉じ込めることができます。
ローカル環境を触らせたくないのであれば、完全にクラウド側に寄せてしまうという案もあります。これは好みの問題でもありますが、個人的には細かい微調整を手元でしたいことも多く、サンドボックスぐらいのレベル感が自分はちょうどいいです。もちろんクラウドと併用できないわけでもなく、逆にローカルでまったく開発をしないと割り切れるのでなければ、道具箱に入れておいて損はないはず。
ナマのMicroVMを直接扱うのと比較して、Docker SandboxesはAIエージェント用に最適化されているので、作業ディレクトリをMicroVM内にマウントしたり、通信先ドメインを許可リストにしたり、シークレットをサンドボックス内に持ち込まずプロキシで注入したりなど便利機能があります。
以下は、Docker Sandboxesの公式ドキュメントSecurity modelの概要図からの抜粋です。
シークレットを環境変数渡しではなく、プロキシでHTTPヘッダに注入するあたりは、AIエージェントにナマのシークレットを触らせないという設計思想が見えて、面白い方向性だなと思うのですが、仕組み上対応範囲が限定的なのはそれはそう。

出典: https://docs.docker.com/ai/sandboxes/security/
環境
前置きが長くなってしまいましたが、説明はこれぐらいにして、とりあえず触ってみましょう。
手元の環境は以下のとおりです。
- macOS: Tahoe 26.5
- Docker Desktop: 4.82.0
- Docker Sandboxes: 0.35.0
本稿執筆時点では、Docker Sandboxesはアクティブに開発されており、まだExperimentalな扱いなので、わりとカジュアルに破壊的変更があります。残念ながらソースコードは公開されていませんが、困ったらGitHubのリリースノートやissueを見に行きましょう。
https://github.com/docker/sbx-releases
1点補足として、現時点ではDocker Sandboxesはsbxコマンドとして独立したCLIツールであり、直接的にはDocker Desktopに依存していませんが、本稿ではカスタムVMイメージを作るところでdockerコマンドを使っている箇所があります。おそらくOCI互換のコンテナイメージであればなんでもよいはずですが、ライセンスの都合などでDocker Desktopが使えない環境の場合は、適宜代替ツールに読み替えて下さい。
インストール
macの場合はbrew経由でインストールできます。
他の環境は公式ドキュメントを参照して下さい。
https://docs.docker.com/ai/sandboxes/
$ brew trust docker/tap
$ brew install docker/tap/sbx
$ sbx version
sbx version: v0.35.0 01e01520456e4126a9653471e7072e4d9b280321
ちなみに微妙に古いバージョンをインストールしたい場合は、caskがバージョンごとに分かれているので、バージョン指定でインストールすることも可能です。バージョンアップで挙動が変わってしまって、一時的に戻したいときなどに覚えておくとよいでしょう。
$ brew install docker/tap/sbx@0.34.0
基本的な使い方
Docker Sandboxesは今のところ無料で使えますが、Dockerアカウントでログインは必須です。
以下のコマンドでログインします。
$ sbx login
初回はブラウザでDockerのログイン画面に飛ばされるので、Dockerアカウントでログインして、デバイス認証のユーザコードを承認して下さい。
使用するAIエージェントは、claudeやcodexなどいくつか代表的なものがデフォルトで組み込まれているので、適当なプロジェクトのディレクトリ移動して、sbx run [agent] で起動できます。本稿ではclaudeを使います。
$ sbx run claude
初回だけポリシーの初期設定を聞かれます。ポリシーの調整は後述しますが、ここではとりあえず疎通確認するのに、「Balanced」というプリセットを選択します。もし聞かれない場合は sbx policy reset すると選択できます。
Initialize the global network policy for your sandboxes:
Applies to all sandboxes, current and future — change it later with
"sbx policy allow/deny/rm". Kits, including built-in agent kits, may
also add per-sandbox rules.
1. Open — All network traffic allowed, no restrictions.
❯ 2. Balanced — Default deny, with common dev sites allowed.
3. Locked Down — All network traffic blocked unless you allow it.
Use ↑/↓ or 1–3 to navigate, Enter to confirm, Esc to cancel.
初回起動はサンドボックスのVMイメージのダウンロードが発生するので、ちょっと時間がかかります。
無事に起動すると、Claudeの初期画面が出てくるので、あとはサブスク利用であればAnthropicアカウントでログインして下さい。サンドボックの中からブラウザが開けないのでデバイス認証のURLはコピペする必要があります。もしAPIキー認証を使いたい場合は後述のシークレットとして注入することも可能です。
╭─── Claude Code v2.1.211 ────────────────────────────────────────────────────────────────────────────╮
│ │ What's new │
│ Welcome back! │ Added `--forward-subagent-text` flag and `CLA… │
│ │ Fixed permission previews relayed to chat cha… │
│ ▐▛███▜▌ │ Fixed auto mode overriding a PreToolUse hook'… │
│ ▝▜█████▛▘ │ /release-notes for more │
│ ▘▘ ▝▝ │ │
│ │ │
│ Opus 4.8 (1M context) · API Usage Billing │ │
│ /…/minamijoyo/src/github.com/minamijoyo/examples │ │
╰─────────────────────────────────────────────────────────────────────────────────────────────────────╯
───────────────────────────────────────────────────────────────────────────────────────────────────────
❯
───────────────────────────────────────────────────────────────────────────────────────────────────────
⏵⏵ bypass permissions on (shift+tab to cycle) · ← for agents Not logged in · Run /login
● high · /effort
とりあえずデフォルト設定で使うだけだったら簡単ですね。
ここでは sbx run で起動しましたが、sbx create でサンドボックスを作成してから、sbx exec でシェルのセッションを立ち上げて、claude コマンドを実行することも可能です。起動中のサンドボックスは sbx ls で確認でき、 sbx stop で停止できます。サンドボックス自体を作り直したい場合は、一旦 sbx rm でサンドボックスを削除して下さい。なんかエラーが出て調子が悪いときは、 sbx daemon stop && sbx daemon start -d で手動でデーモンを再起動も試してみて下さい。
dockerコマンドに意図的に寄せている感じがあるので、dockerコマンドに馴染みがあれば、 sbx --help で引数を調べつつ勘で使えるコマンド体系になっています。
$ sbx --help
Docker Sandboxes creates isolated sandbox environments for AI agents, powered by Docker.
Run without a command to launch interactive mode, or pass a command for CLI usage.
Usage:
sbx
sbx [command]
Available Commands:
completion Generate the autocompletion script for the specified shell
cp Copy files or directories between a sandbox and the host
create Create a sandbox for an agent
daemon Manage sandboxd daemon
diagnose Diagnose common issues with your sbx installation
exec Execute a command inside a sandbox
help Help about any command
kit (Experimental) Manage kit artifacts
login Sign in to Docker
logout Stop all running sandboxes and sign out of Docker
ls List sandboxes
policy Manage sandbox policies
ports Manage sandbox port publishing
reset Reset all sandboxes and clean up state
rm Remove one or more sandboxes
run Run an agent in a sandbox
secret Manage stored secrets
setup (Experimental) Detect host configuration and prepare Docker Sandboxes
stop Stop one or more sandboxes without removing them
template Manage sandbox templates
tui Open the interactive TUI dashboard
version Show Docker Sandboxes version information
Flags:
-D, --debug Enable debug logging
-h, --help help for sbx
Use "sbx [command] --help" for more information about a command.
現状、網羅的なコマンドリファレスンスは存在しないのだけど、簡単な使い方が以下にまとまっています。
https://docs.docker.com/ai/sandboxes/usage/
サンドボックスをカスタマイズする
デフォルト設定で動かすだけなら簡単だけど、実際に使おうとすると、細かいところをカスタマイズしたくなるので、知っておいたほうがよさそうなことを順番に説明していきます。
YOLOモードをOFFにする
先ほどの sbx run claude で起動するとデフォルト設定では、claude --dangerously-skip-permissions で承認をスキップする、いわゆるYOLO(You Only Live Once)モードが有効化されています。「サンドボックスに隔離されているので」ということを強調したい意図なのだろうけど、追加の引数を与えたいのであれば、sbx run claude -- AGENT_ARGS のように指定できるので、なぜこれをデフォルト値にしたのか理解に苦しみます。
というわけで、カスタマイズの第一歩として、引数をデフォルトにリセットしてみましょう。サンドボックスの設定はKitという仕組みでカスタマイズできます。
ここでは例として ccbase という名前のKitとして、 sbx/kits/ccbase/spec.yaml に以下を作成します。ディレクトリ名をそれっぽいものにしてますが、特に配置場所の決まりはありません。
schemaVersion: "1"
kind: sandbox
name: ccbase
sandbox:
image: "docker/sandbox-templates:claude-code-docker"
aiFilename: CLAUDE.md
entrypoint:
run: [claude]
network:
serviceDomains:
api.anthropic.com: anthropic
console.anthropic.com: anthropic
serviceAuth:
anthropic:
headerName: x-api-key
valueFormat: "%s"
allowedDomains:
- "claude.com:443"
credentials:
sources:
anthropic:
env:
- ANTHROPIC_API_KEY
environment:
variables:
IS_SANDBOX: "1"
カスタマイズ元の参考として、組み込みのclaudeのKitのspecが以下にあります。entrypointのrunの引数を変更しています。
Anthropicのサブスク契約の場合は、credentialsのAPIキー認証の箇所は設定不要です。
https://docs.docker.com/ai/sandboxes/customize/kits/#example-the-built-in-claude-agent
sbx run するときに、AIエージェント名を claude ではなくKit名の ccbase にしつつ、 --kit フラグでKit定義のディレクトリを指定することで、カスタマイズした設定で起動できます。
$ sbx run ccbase --kit ./sbx/kits/ccbase
spec.yamlに schemaVersion: "1" と書いてありますが、本稿執筆時点では、仕様がまだFixしておらず、バージョン1のままちょいちょい仕様が変わるので注意して下さい。specのリファレンスは以下にあります。
https://docs.docker.com/ai/sandboxes/customize/kit-reference/
あとKitの実装例が以下のリポジトリにいろいろあるので、書き方の参考にするとよいでしょう。
https://github.com/docker/sbx-kits-contrib
サンドボックスのVMイメージを差し替える
開発環境なので、当然あのコマンドがないのでインストールしたいとかはありがちです。サンドボックスのシェルセッションを開いて手動でインストールしてもいいんですが、サンドボックスを作り直したりする都度インストールし直すのは面倒です。サンドボックスのVMイメージはDockerイメージを使うことができ、ベースとなるVMイメージのテンプレートはAIエージェントごとに用意されています。
https://docs.docker.com/ai/sandboxes/customize/templates/
先ほどのKitの例で指定していた docker/sandbox-templates:claude-code-docker はClaude CodeのDockerあり版です。これをベースイメージとしてDockerfileを書けばVMイメージを差し替えられます。Claude Code自体もバージョンアップが速いので、ビルド時に最新化しておくとよいでしょう。書き方は普通のDockerfileです。
FROM docker/sandbox-templates:claude-code-docker
RUN claude update
RUN sudo apt update -y && sudo apt install -y \
build-essential
で、カスタマイズする上で、そもそもベースイメージがどうなっているのか調べたくなると思うのだけど、残念ながらベーステンプレートのDockerfileは公開されていません。ただdocker historyコマンドで中身は読めるので、各自でリバースエンジニアリングするとよいんではなかろうか。
$ docker history --no-trunc docker/sandbox-templates:claude-code-docker
Dockerfileをビルドします。ここでは、イメージ名は minamijoyo/ccbase としました。
$ docker build -t minamijoyo/ccbase ./sbx/kits/ccbase
サンドボックスにどのVMイメージを使うのかは、Kitのimageに指定します。
schemaVersion: "1"
kind: sandbox
name: ccbase
sandbox:
image: "minamijoyo/ccbase"
aiFilename: CLAUDE.md
entrypoint:
run: [claude]
ここで1つ重要なポイントがあります。手元でdocker buildしたイメージは、そのままではsbx run側から見えません。イメージの保存場所が異なるからです。もしVMイメージをチームで共有するのであれば、一度Docker HubなどのRegistryにdocker pushして、sbx側から読み込むのが正攻法だと思うのですが、ベースとなっている docker/sandbox-templates:claude-code-docker のイメージが2.7GBぐらいあって、ちょっと手元で設定いじって検証する都度ネット越しにpush/pullするのは非効率です。
この問題の回避策として、sbx template load という docker image load 相当のコマンドがあるので、ネットを経由せず、ローカルの名前付きパイプを介して、dockerからsbxにイメージを読み込みます。
$ docker build -t minamijoyo/ccbase ./sbx/kits/ccbase \
&& sbx template load <(docker image save minamijoyo/ccbase)
sbx template ls でサンドボックスのテンプレートとして登録されていることが確認できます。
$ sbx template ls
REPOSITORY TAG IMAGE ID FLAVOR CREATED
docker.io/minamijoyo/ccbase latest 250ed94f51d9 claude-code-docker 2 months ago
レジストリにpushしていませんが、レジストリ名を省略すると docker.io (Docker Hub) にあるように見えています。 で、sbx runはローカルにキャッシュがあればダウンロードせずにキャッシュを読むので、これでローカルでビルドしたサンドボックスのVMイメージを起動できます。
$ sbx run ccbase --kit ./sbx/kits/ccbase
サンドボックスに名前を付ける
ところで環境のカスタマイズし始めると、claudeを直接起動するよりも、シェルのセッションを開いていろいろ確認したくなるケースが増えてくるでしょう。その場合は、sbx createしてからsbx execするとよいでしょう。サンドボックス名はデフォルトで、エージェント名+ディレクトリ名になりますが、--name で適当な名前を付けられます。
$ sbx create ccbase --name test --kit ./sbx/kits/ccbase .
$ sbx exec -it test bash
同じディレクトリで複数のエージェントを使い分けたいということがなければ、以下のようにディレクトリ名から自動で生成してもよいでしょう。
$ sbx create ccbase --name $(basename $(pwd)) ./sbx/kits/ccbase .
モノレポで使う
モノレポで使う場合には、ワークスペースのマウントの動作を理解しておく必要があります。というのも、サンドボックス内のディレクトリ構造はホストのディレクトリ構造と対応するようにできており、sbx createコマンドの引数で指定したパスをマウントします。マウントしたディレクトリよりも親のディレクトリ構造は自動で作成されますが、マウントポイントよりも親のディレクトリはホストと共有されません。つまりリポジトリルートをマウントしておかないと、 .git/ などがサンドボックスの中から見えなくなってしまいます。
sbx exec -w でシェルのセッションを開くタイミングでカレントディレクトリを指定することは可能なので、モノレポの場合は、sbx createはリポジトリルートで実行しつつ、sbx execは作業ディレクトリから実行するというようなかんじにするとよいでしょう。
環境変数を設定する
サンドボックス内でシークレットではない環境変数を設定したいことがあると思いますが、ここにも若干罠があり、普通にDockerfileのENVで設定しても一部の環境変数は効きません。たぶんどこかの初期化タイミングで上書きされているのだろうけど、全部じゃなくて一部なので分かりづらい。サンドボックス内の環境変数を設定したい場合は、 /etc/sandbox-persistent.sh という設定ファイルに書き込むと反映されます。
たとえばデフォルトで設定されているBashシェルのプロンプトが長くて冗長なので、シンプルなものに差し替えたい場合は、環境変数PS1を設定します。これをDockerfileの中で設定するには、以下のようにリダイレクトで /etc/sandbox-persistent.sh で書き込みます。
RUN echo 'export PS1="[\W@\h]\$ "' >> /etc/sandbox-persistent.sh
サンドボックスのビルドタイミングではなく、起動タイミングで環境変数を差し込みたい場合は、 sbx exec -e で設定できます。
$ sbx exec -it -e FOO=foo $(basename $(pwd)) bash
$ echo $FOO
foo
設定ファイルを配布する
サンドボックスの中で $HOME は /home/agent に設定されています。設定をカスタマイズするのに、 ~/.claude/settings.json を配布したいとかありますよね。
Kitの中のfiles/homeというディレクトリは特別扱いされており、この下にファイルを置くと、サンドボックスの中でHOMEの下に置いてくれます。先ほどのccbaseのKitの例で言うと、 sbx/kits/ccbase/files/home/.claude/settings.json のようなパスに置いておくと、サンドボックスの作成時に配布されます。ドキュメントによると初期化タイミングの都合で ~/.claude/settings.json は上書きされるので配布できないという注意書きがあるのだけど、試したら配布されているように見えるので、ドキュメントが古いだけな気がしている。
通信先を制限する
サンドボックスはPolicyという仕組みでネットワークやファイルのアクセスを制限できます。
サンドボックス内にマウントされるファイルを原則リポジトリルート配下とする運用にしていると、ファイルアクセスに関してはあまり細かいポリシーを設定する必要性は薄いですが、少なくとも通信先のドメインは制限したくなりますよね。というわけで通信先ドメインを許可リストで管理してみます。
初期設定のところで「Balanced」のプリセットを選択したのをリセットして、「Locked Down」に変更します。
$ sbx policy reset
Initialize the global network policy for your sandboxes:
Applies to all sandboxes, current and future — change it later with
"sbx policy allow/deny/rm". Kits, including built-in agent kits, may
also add per-sandbox rules.
1. Open — All network traffic allowed, no restrictions.
2. Balanced — Default deny, with common dev sites allowed.
❯ 3. Locked Down — All network traffic blocked unless you allow it.
Use ↑/↓ or 1–3 to navigate, Enter to confirm, Esc to cancel.
試しにサンドボックス内からcurlしてみましょう。
$ curl https://github.com
Blocked by network policy: domain github.com:443
detail: no matching allow rule — blocked by default deny policy
ネットワークポリシーでブロックされたというエラーメッセージが出ていますが、sbx policy log でブロックされたものと許可されたものが確認できます。
通信を許可するドメインは sbx policy allow network で追加できます。
$ sbx policy allow network github.com:443
ポリシーはサンドボックス名を省略するとグローバルに設定されますが、サンドボックスを指定して設定することも可能です。ただサンドボックスを作り直すたびに設定し直すのも面倒なので、疎通確認はトライアンドエラーで sbx policy allow network でポリシーを調整しつつ、必要なドメインをKitの設定に追記していくのがよいでしょう。
network:
allowedDomains:
- github.com:443
最後にポリシーをリセットして、サンドボックスを作り直して、通信できるか確認します。
ちなみに前述の組み込みのclaudeのKitのspecでは claude.com:443 を許可しているのだけど、実際にLocked Downモードで試すと、以下のドメインと通信しているので、通信がブロックされたら都度sbx policy logを見て、実態に合わせるでよいかなと思います。
network:
allowedDomains:
- api.anthropic.com:443
- platform.claude.com:443
言うのは簡単ですが、サンドボックス環境整備で一番地味で煩雑な作業です。普段意識していなかったけど、こんなにいろんなところと通信していたのだなという気付きがあります。
ただ自分が依存している通信先ドメイン一覧をコードとして管理できるようになると、CI/CDでのEgress制限など他のツールにも流用できるので、時代の流れ的にマルウェア対策などでも必要になる作業だと割り切ってやっていきましょう。
オプショナルな依存
あるプロジェクトでは使うが、別のプロジェクトでは使わない依存があります。自分しか使わないのであれば全部入りという戦略も取れますが、共有するコンテキストをまたぐと混ぜるの微妙じゃねというやつ。
これまでKitのspec.yamlでは特に説明せず、 kind: sandbox と書いていましたが、これはサンドボックスのベーステンプレートの定義です。kind: mixin というのもあって、オプショナルな依存を個別に適用できます。
たとえばCATOというゼロトラストネットワークのCA証明書をインストールしてみましょう。なんか突然個別具体的な話になってきましたね?こーゆーのをベーステンプレートに混ぜるの気持ち悪くないですか?ということで、mixinとして sbx/kits/cato/spec.yaml に作成します。
schemaVersion: "1"
kind: mixin
name: cato
network:
allowedDomains:
- clientdownload.catonetworks.com:443
- clients.catonetworks.com:443
commands:
install:
- description: Install CATO CA certificate
user: "0"
command: |
curl -fsSL https://clientdownload.catonetworks.com/public/certificates/CatoNetworksTrustedRootCA.pem -o /tmp/cato-root-ca.crt \
&& install -m 0644 /tmp/cato-root-ca.crt /usr/local/share/ca-certificates/cato-root-ca.crt \
&& update-ca-certificates
commandsのinstall に指定したコマンドはサンドボックスの作成時に実行されます。 user: "0" はuidなのでrootで実行するという意味です。他に、サンドボックスの起動時に発火する startup というのと、ファイルを作成する initFiles というのもあります。
sbx createの --kit フラグは複数指定できるので、mixinを使いたいときだけ追加します。
$ sbx create ccbase --name $(basename $(pwd)) \
--kit ./sbx/kits/ccbase \
--kit ./sbx/kits/cato \
.
ghコマンドを使う
本稿執筆時点では、 docker/sandbox-templates:claude-code-docker にデフォルトで入ってるghコマンドがv2.46.0で微妙に古いです。特にgh issueコマンドがv2.82.1以降でないとProject v2 APIに対応しておらず、gh issueコマンドが使えないのは大変きびしい。
というわけで、Dockerfileの中で最新版を入れておきます。
# Install GitHub CLI
RUN (type -p wget >/dev/null || (sudo apt update && sudo apt install wget -y)) \
&& sudo mkdir -p -m 755 /etc/apt/keyrings \
&& out=$(mktemp) && wget -nv -O$out https://cli.github.com/packages/githubcli-archive-keyring.gpg \
&& cat $out | sudo tee /etc/apt/keyrings/githubcli-archive-keyring.gpg > /dev/null \
&& sudo chmod go+r /etc/apt/keyrings/githubcli-archive-keyring.gpg \
&& sudo mkdir -p -m 755 /etc/apt/sources.list.d \
&& echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | sudo tee /etc/apt/sources.list.d/github-cli.list > /dev/null \
&& sudo apt update \
&& sudo apt install gh -y
ccbaseのKitのspec.yamlで通信を許可するドメインを追加しておきます。
network:
allowedDomains:
- github.com:443
- api.github.com:443
- raw.githubusercontent.com:443
GITHUB_TOKENはシークレットとして登録することで、サンドボックスの中に持ち込まずに、プロキシでAPIリクエストヘッダを書き換えて、注入してくれます。
$ echo "$(gh auth token)" | sbx secret set -g github
上記のように -g でグローバルスコープに登録することもできますが、サンドボックス名を指定してサンドボックススコープにも登録できます。
必要なサンドボックスにだけ登録すべしという最小権限の原則もありますが、それ以外に、グローバルスコープに登録するとサンドボックスを作り直さないと反映されませんが、サンドボックススコープであれば即時に反映されます。有効期限のあるトークンを使う場合は、サンドボックススコープでないと更新できなくて困ります。
有効期限とスコープを絞ったGitHubトークンはGitHub AppのDevice Flowを使って生成するのがオススメです。GitHub AppのDevice Flowついて説明し始めると長くなってしまうので、詳細は以前書いた以下を参照。
たとえば、ghtknで生成したトークンをsbxのシークレットとしてサンドボックススコープで登録するには、以下のようにします。ここではサンドボックス名はカレントディレクトリ名と一致すると仮定しています。
$ ghtkn get minamijoyo/read | sbx secret set $(basename $(pwd)) github -f
サンドボックスの中で gh issue list などが使えることを確認してみて下さい。
ところで、ネットワークポリシーでは api.github.com のようなドメイン単位の粒度でしかアクセス制限できず、リクエストボディを監査する必要があるリポジトリ単位の制限ができないのだけど、GitHub AppのDevice Flowを使った副次的な効果として、publicリポジトリであってもGitHub Appをインストールしたリポジトリにしかwrite操作ができないので、GitHub Appのインストール範囲を制限することで他のリポジトリへの誤爆リスクを下げられます。汎用的な方法ではありませんが、誤爆リスクはトークンの権限側で制御するしかないかなという認識です。
あとgitに関連するトピックとしてここに説明を混ぜてしまいますが、sbx createでサンドボックスを作成したタイミングで、プロジェクト内の .git/config に自動で [user] の name と email が登録されます。通常の使い方であれば特に問題ない挙動だと思いますが、メアドをorgごとに使い分けているような場合は、現状どれを使うか設定する箇所がなく、デフォルトのメアドが自動で登録されてしまいます。現状よい解決策を見いだせていないのですが、事象としては認識しているのでご注意下さい。
awsコマンドを使う
awsコマンドは、 docker/sandbox-templates:claude-code-docker にデフォルトでは入っていないので、インストールする必要があります。
Dockerfileでインストールするならこんなかんじ。
# Install AWS CLI v2
RUN ARCH="$(uname -m)" && curl "https://awscli.amazonaws.com/awscli-exe-linux-$ARCH.zip" -o "awscliv2.zip" \
&& unzip awscliv2.zip \
&& sudo ./aws/install \
&& rm -rf aws awscliv2.zip
ENV AWS_PAGER=""
ネットワークポリシーは扱うサービスやリージョン次第ですが、たとえばこんなかんじ。
network:
allowedDomains:
- "*.ap-northeast-1.amazonaws.com:443"
- "*.ap-northeast-3.amazonaws.com:443"
- "*.us-east-1.amazonaws.com:443"
- "*.us-east-2.amazonaws.com:443"
- "*.s3.ap-northeast-1.amazonaws.com:443"
- ap-northeast-1.signin.aws.amazon.com:443 # aws login --remote
- iam.amazonaws.com:443
- sts.amazonaws.com:443
- 169.254.169.254:80 # AWS metadata service
metadataのエンドポイントはどうせ通信できないんですが、SDKがフェッチしようとしてブロックするログが出るのがノイズなのでallowに入れてます。
で、問題はAWSのアクセスキーをどうやって渡すかです。sbx secretは組み込みで対応しているサービスの他、未対応のサービスでも特定のHTTPヘッダを書き換える設定で拡張が可能です。逆に言うと、APIリクエストの認証がHTTPヘッダにトークンを設定するような仕組みでないと使えません。つまり、awsコマンドのようにリクエストに署名する必要があるケースではsbx secretは使えません。
現状特によい解決策はないのですが、素朴に環境変数としてAWSのアクセスキーを渡すことは可能です。サンドボックスの中にシークレットを持ち込んでしまうので、若干セキュリティレベルが下がってますが、今までDockerコンテナの中でやってたのと同じようなかんじではある。
たとえばaws-vaultを使っている場合は、以下のようにサンドボックス起動時に -e で渡す環境変数を指定する。
$ aws-vault exec dev -- sbx exec -it \
-e AWS_REGION -e AWS_ACCESS_KEY_ID -e AWS_SECRET_ACCESS_KEY -e AWS_SESSION_TOKEN \
-w $(pwd) $(basename $(pwd)) bash
サンドボックスの中で aws sts get-caller-identity が動くか試してみて下さい。
ちなみに、sbx exec --env-fileという.env形式のファイルを読み込むオプションもあって、以前は以下のようにもう少しスマートに書けたのだけど、本稿執筆時点のv0.35.0では壊れていて使えません。そのうち治ると思いますが。
$ sbx exec -it -e AWS_REGION=ap-northeast-1 \
--env-file=<(aws --profile=dev configure export-credentials --format env-no-export) \
-w $(pwd) $(basename $(pwd)) bash
miseを使う
プロジェクトごとに使う言語のバージョンが違ったりすることはあるので、ローカルではmiseでバージョンを管理しています。というわけで、サンドボックスの中でもmiseを使いたいというのは自然な流れ。
これに関してはそんなにハマりポイントはないですが、Dockerfileの中で、miseのアクティベーションを /etc/sandbox-persistent.sh に仕込んでおくことぐらいでしょうか。
# Install mise
RUN sudo apt update -y && sudo apt install -y curl \
&& sudo install -dm 755 /etc/apt/keyrings \
&& curl -fSs https://mise.en.dev/gpg-key.pub | sudo tee /etc/apt/keyrings/mise-archive-keyring.asc 1> /dev/null \
&& echo "deb [signed-by=/etc/apt/keyrings/mise-archive-keyring.asc] https://mise.en.dev/deb stable main" | sudo tee /etc/apt/sources.list.d/mise.list \
&& sudo apt update -y \
&& sudo apt install -y mise \
&& echo 'eval "$(mise activate bash)"' >> /etc/sandbox-persistent.sh
ENV MISE_MINIMUM_RELEASE_AGE=7d
ネットワークの許可リストはインストールするもの次第ではあるのですが、最低限miseのインデックスは許可してあげる必要はあります。mise installしてブロックされた通信を随時許可して下さい。
network:
allowedDomains:
- mise-versions.jdx.dev:443
- release-assets.githubusercontent.com:443
miseの設定を調整したい場合は sbx/kits/ccbase/files/home/.config/mise/config.toml に設定ファイルを置いておけば、前述の通りHOMEの下に配布されます。
おわりに
AIエージェントのコーディング環境として、Docker Sandboxesを紹介し、設定のカスタマイズ方法などを説明しました。とりあえず触れるファイルや通信先を制限できるだけでも圧倒的な安心感があるので、試してみてね。