本サイトは、快適にご利用いただくためにクッキー(Cookie)を使用しております。
Cookieの使用に同意いただける場合は「同意する」ボタンを押してください。
なお本サイトのCookie使用については、「個人情報保護方針」をご覧ください。
お久しぶりです。AT部の廣田です。
最近はペネトレーションテストをやる傍ら、自分の欲しいものを勝手に実装する仕事をしています。 今回はそんな中で生まれた社内ツールをご紹介します。
Claude CodeのようなAIコーディングエージェントをチームに配りたい。 でも、各メンバーの手元マシンで野放しに動かすのは怖いし、APIキーの配布・管理も面倒——。 devconは、そんな課題に対する「ユーザーごとに隔離されたサンドボックスを、Webからワンクリックで払い出す」というアプローチの実装です。
(Claude Codeにコードを読み込ませて出力させた煽り文句)
この記事では、まず全体像をざっくり紹介し、そのあとで工夫したポイントを細かく解説します。
概要
devconは一言でいうとAI利用者向けのサンドボックス管理システムです。
以下のような特徴があります。
- Web上の操作だけで、AIの暴走やサプライチェーン攻撃の影響を一定程度軽減するサンドボックスを作成できる
- 慣れ親しんだSSH接続で入れて、ポートフォワードで任意のポートと通信できる
- システム専用APIキーと、それを用いてLLM/GitHubへのアクセスをProxyするコンポーネントにより、APIキーの漏えいリスクを抑えられる
このシステムでは、最近リリースされたAWS Lambda MicroVMsを使っています。
参考:完全なライフサイクル制御による分離されたサンドボックスの実行: AWS Lambda が MicroVM を導入
ユーザーから見た利用の流れ
使い方は以下の通りです。
- ユーザーはOffice 365(Microsoft Entra ID)アカウントでOIDC認証してWeb UIにログインし、「作成」ボタンを押す
- ユーザーに紐づいた各GW向けのトークンが発行され、MicroVMへ渡される。Web UI上では、一連の作成作業が終わるとポート番号が表示される
ssh -p <port> root@<host>で接続すると、Claude Code CLIやgitコマンドなどがセットアップ済みの開発環境にアクセスできる- Claude Codeはサンドボックスごとに自動発行されたAPIキーを用いてAmazon BedrockやGitHubにアクセスするので、ユーザーが生のAPIキーを扱うことはない
- curlコマンドやdnfコマンドなどによるインターネットアクセスは自由に行える
Webからボタンをいくつか押すとMicroVMが立ち上がるので、あとはgitで開発中のプロジェクトを取得し、Claude Codeなどを動かして開発を進められます。 なお私は、LLMを使わない通常の開発や、お試しでGitHub上にあるコードを動かしてみる際などにもこのシステムを利用しています。
全体アーキテクチャ
ここからは、技術的に掘り下げて説明していきます。
システムは大きく3つのコンポーネントで構成されています。
- manager: 認証・DB(MySQL)・Web UI・APIを持つ中枢コンポーネント。サンドボックスの「誰のものか」「いつ使われたか」を管理し、LLMやGitHubへのアクセスをProxyする
- サンドボックス: ユーザーが実際に触る環境。起動時にSSH鍵の配置、トンネル設定の投入、監視エージェントの導入が行われる
- sandbox-api: サンドボックス実体のライフサイクルを管理するコンポーネント。AWS Lambda MicroVMやDockerとの通信を行う
各コンポーネントを順に紹介します。
manager
managerは、認証・課金・キー発行・Proxyを一手に担うコンポーネントです。
FastAPI製のAPIのエンドポイントとWeb UIを持ち、ユーザーからのアクセスに応じてsandbox-apiへサンドボックス作成の指示を出したり、LLMやGitHubへのアクセスに必要なAPIキーを作ったり、ユーザー管理をしたりできます。
ログインはMicrosoft Entra IDによるシングルサインオンです。 組織で既に使っているID基盤にそのまま乗れるので、ユーザー登録作業は不要です。
これにより、「Web上の操作だけで、AIの暴走やサプライチェーン攻撃の影響を一定程度軽減するサンドボックスを作成できる」 のWeb UI部分を実現しています。
APIエンドポイントは、APIキーによる認証にも対応しています。 これは主にCIやスクリプトからサンドボックス操作を行うことを意図した設計です。 例えば「報告書レビュー用サンドボックスをAPI経由で作成し、手元にある報告書をアップロードして、最新のレビュー用ルールをリポジトリから取得し、LLMを使ったレビューを実行して結果を返し、サンドボックスを破棄する」という処理を行う.ps1ファイルを配布したとします。すると、各自のREST APIキーを渡すだけで、報告書レビューができるPowerShellスクリプトになります。
さらに、LLMやGitHubへの中継も行っています。 サンドボックス作成時にユーザーに紐づけてAPIキーを発行し、それを付与してアクセスすることで、Amazon Bedrockや認証が必要な社内GitHubを利用できます。 このAPIキーはサンドボックス作成時にsandbox-apiへ渡され、あらかじめサンドボックス内に配置された状態でユーザーの手元に届きます。そのため、サンドボックス内ではClaude Codeやgitコマンドをそのまま打つだけで使えます。 なお、このAPIキーは奪われてもサンドボックス以外からは使えません。起動時に紐づけたLLMやGitHubリポジトリしか参照できないため、悪用されても被害は最小限に抑えられ、無効化も容易です。
これにより、「システム専用APIキーと、それを用いてLLM/GitHubへのアクセスをProxyするコンポーネントにより、APIキーの漏えいリスクを抑えられる」 を実現しています。
サンドボックス
サンドボックスの実装には、Docker版とMicroVM版があります。 Docker版では、コンテナ内でDockerを使えません。DinDやDooDといった手段はあるものの、リスクがあるため選定から外しました。 一方、MicroVMはVMであるためDockerを使えますが、ARM64のためビルドできないイメージがあります。これは今後の課題ですが、AWSがきっと解決してくれると信じています。
どちらの場合も、ユーザーから見た操作はほぼ同じで、SSH接続してシェルを操作する点も変わりません。
Docker版とMicroVM版が生まれた理由
実は、もともとはDocker版しかありませんでした。
XやFacebookを見ていると、AIにシェルアクセスを与えてコーディングしていたら重要なフォルダを消された、コミットに本番情報を混ぜてプッシュしていた、といった悲しい事案がよく流れてきます。 これらへの対策はいろいろありますが、個人的には開発環境をコンテナに丸ごと詰め込んで作業することが多いため、そんな感じで手軽に使える環境があればなぁ……というのが当初の開発動機でした。 コンテナ内なら、普段使っている環境と異なり、各クラウドにアクセスするためのAPIキーも報告書やテストログもありません。そのため、AIが丸ごと消そうと公開リポジトリにプッシュしようと、影響は最小限に抑えられるという寸法です。
ただ、コンテナ仮想化はVM仮想化と比べてカーネルを共有する分、セキュリティ上の懸念がないわけではありません。どうしようかな……と考えているうちにLambda MicroVMsが使えるようになり、乗るしかないこのビッグウェーブに!!ということで、MicroVM版が誕生しました。 VMなのでコンテナと比べて強固に分離され、かつそれをAWSが担保してくれるため、最近流行しているnpmやpipのパッケージを狙ったサプライチェーン攻撃を受けた場合でも、影響を一定程度軽減できます。
これにより、「Web上の操作だけで、AIの暴走やサプライチェーン攻撃の影響を一定程度軽減するサンドボックスを作成できる」 のサンドボックス部分を実現しています。
ただ、あくまで一定程度の軽減であり、侵害されたサンドボックス内からの不正なLLMアクセスや、コード自体の流出への根本的な対策とはなりません。 このシステム単体で守れる範囲ではないため、今後、サンドボックスのテンプレートや出口となるインターネット側と境界に対策となる仕組みを導入しようと考えています。
sandbox-api
sandbox-apiには、Docker版とMicroVM版の2種類があり、それぞれ別の実装です。 managerから実際のサンドボックス作成処理を切り分けるためのコンポーネントで、自由に差し替えられます。 どの実装でも、SSH用のポートが返され、そこにつなぐだけでサンドボックスにSSH接続できます。 SSHさえつながってしまえば、あとはポートフォワードで任意のポートに接続できるため、自由に扱えます。
Docker版の実装
Docker版は、サンドボックスごとに専用Dockerネットワークを切って相互通信を遮断する設計です。 各コンテナからは169.254.169.254へ接続できないようにしています。
managerから作成実行の指示があった場合、指定されたイメージのコンテナを作成します。 この際、現在使われていないポートをコンテナ内のSSHとマッピングして起動します。 また、起動時に引数としてLLMやGitHubにアクセスするためのAPIキーを渡しているため、前述の通り透過的に扱えます。
これにより、Docker版における 「慣れ親しんだSSH接続で入れて、ポートフォワードで任意のポートと通信できる」 を実現しています。
MicroVM版の実装
MicroVM版では各サンドボックスが独立したVMとして動くため、隔離境界が一段強くなります。
ただし、MicroVMが外部に公開するのは専用のHTTPSエンドポイントだけで、任意ポートへの接続はAWSの仕様上できません。
しかしサンドボックスとしては「いつもの ssh -p <port>」で入れてほしい。そこで、MicroVM側から外向きに接続する設計としました。
managerから作成実行の指示があった場合、Docker経由でSSH接続用のトンネルコンテナを、AWS API経由でMicroVMを、それぞれ起動します。
この際、APIキー、SSH公開鍵、トンネル接続のためのSSH秘密鍵、フォワードすべきポート番号は、RunMicrovm APIのrunHookPayload引数に詰めて渡します。VM内のinitスクリプトがこれを読み取って、よしなに処理する設計です。
このinitスクリプトが、LLM/GitHubへのアクセスを透過的に処理できるようにし、sandbox-apiサーバー上のトンネルコンテナへ自身のSSHポートをポートフォワードします。
流れは以下の通りです。
- サンドボックス作成時、sandbox-apiがそのVM専用のトンネルコンテナをdocker.sock経由で起動する(1 VM = 1コンテナ)
- MicroVMは起動時に、payloadで渡された秘密鍵を使って自分専用のトンネルコンテナへSSH接続し、
-R 10022:localhost:22のリモートポートフォワードで自分のsshdをマッピングする - トンネルコンテナはホストのポートとして10022を公開しているので、ユーザーから見れば普通に
ssh -p <port> root@<host>でVMに入れる
もちろん、鍵にはコマンド実行を行えない制限をかけた上で、特定のポートしかListenできないようにしています。
1つのコンテナに複数のSSHポートをマッピングしても良かったのですが、以下の理由から面倒だったので、富豪的にプログラミングしました。
- MicroVM版ではDocker版と異なり、ホスト上に多数のコンテナが展開されることがないため、自由にコンテナを立ち上げられる
- 鍵の追加・削除が面倒
- フォワードする可能性のあるポートをあらかじめ
docker run -pでListenする必要があり、広範囲を指定するとiptablesが膨れ上がって?動かなくなる
これにより、MicroVM版における 「慣れ親しんだSSH接続で入れて、ポートフォワードで任意のポートと通信できる」 を実現しています。
なお、SSH接続をさらにポートフォワードしているため動作が重くなるように思えますが、今のところネットワーク周りで重さは感じません。 むしろ、MicroVM自体のスペックによる重さのほうが気になるくらいです。 dockerdがcontainerdの起動を待ち続けてタイムアウトする問題も発生しており、力技で解決しましたが、立ち上がりの遅さは若干気になるところです。
その他
ここまで読んだ方は、あることに気付くかもしれません。
「これ、MicroVMやコンテナをバックエンドにしたOpenHandsや、Webから操作できるClaude Codeのコンテナに、SSH接続を足せばよいだけでは?」
その通りです!!!! ですが、こういう仕組みを自分で考えること自体に価値があるのではないかと思う今日この頃。
細かいので本文では触れませんでしたが、実際には自動停止機能や使いすぎ防止機能(APIキーの無効化)など、いろいろな機能を載せています。 また、AIの暴走対策として、LLMへのGateWay部分に弊社開発中のツールを導入する計画もあります。 ペンテスターがオマケで作った社内システムとは異なり、AIチームが本気で作ったツールなので大変興味があるのですが、まだ未完成とのことで、完成を心待ちにしているところです。
こういった別チームのツールとの連携や、自チーム内の要件を盛り込もうとすると、この形のほうが良いと考えています。 あくまで個人的意見なので、どちらの実装方針もありだと思います。
まとめ
devconは、AIエージェントが暴れても手元のマシンや会社資産に影響が出ないようにするサンドボックス環境を、Web上の操作だけで払い出すための仕組みです。 Entra IDでログインして「作成」を押せば自分専用のサンドボックスが立ち上がり、あとはいつも通りSSHで入ってClaude Codeを叩くだけ。生のAPIキーは、一度もユーザーの手元に渡りません。
一方で、内側に持ち込んだコードや、そこから正規の経路で叩けるLLMまでは守れません。 また、ARM64でビルドできないイメージ、立ち上がりの遅さ、そしてサンドボックスから外へ出ていく通信の制御といった今後の課題もあります。 このあたりが片付いたら、また記事にしようと思います。 そういえば、育休前に明けたら別角度から記事にすると言ったきりの記事も、そろそろ書かないと……。 十数年前に書いた学部卒論よろしく、Future Workということでここはひとつ。
ちなみに当時の研究テーマだった「Webアプリはアクセス毎にコンテナ的な仕組みの中で動かせばいい感じになるのでは?」というネタは卒論発表の年にLambdaの一般リリースで回収され、さらに今回もDocker版の弱点をLambdaが解決してくれており、今後も何か不便だなと思ったことはAWSが圧倒的パワーで解決してくれるんじゃないかと思ってます。 ということで、未執筆の記事とテストと実装も、きっとAWSが解決してくれるでしょう。
なお、現状は社内システムとして運用していますが、もっと詳しい話が聞きたいという方は、弊社までお気軽にお問い合わせ下さい。 Web診断やペネトレの案件相談と一緒だと、とっても嬉しいです! とくに、AIが暴走した場合を想定しAIと同等の権限を人間に与えてペネトレする、のような特殊なテストの相談も受け付けておりますので、ぜひよろしくお願いします。
おすすめ記事