こんにちは、株式会社タイミーで MLOps エンジニアをしている KY です。普段は ML プラットフォームの構築・運用を担当しています。
私たちのチームでは、機械学習エンジニアやデータサイエンティストが開発に集中できるよう、VS Code のリモート開発(Remote SSH および Dev Container)を活用した開発環境を提供しています。本記事では、その中でも 共通 Dev Container Feature によるガードレール にフォーカスし、各チームが自分たちで開発環境を立ち上げられることを前提にしながら、セキュア・バイ・デフォルトをどう実現しているかをご紹介します。
なぜ Dev Container Feature にガードレールを寄せるのか
この記事を書こうと思ったきっかけは、もともと機械学習エンジニアやデータサイエンティスト向けだった開発環境を、データアナリストをはじめとする別職種のメンバーにも広げ始めたことでした。ユーザー層が広がるにつれ、「どこまでを各自の設定に任せ、どこからを仕組みで縛るか」をあらためて考え直す必要が出てきた、というのが出発点です。あわせて、組織として求められるセキュリティレベルも年々高まってきています。
ML プラットフォーム特有の事情として、ユーザーの専門領域が幅広い、という点があります。機械学習エンジニアやデータサイエンティストはモデリングやデータ分析を主戦場としており、依存パッケージの脆弱性管理やコンテナの権限設計といった領域は、本来の業務の中心ではないことが多いです。だからこそ、これらをユーザー個々の習熟度に委ねるのではなく、プラットフォーム側で初期値を配る方針を取りました。各チームがセルフサービスで開発環境を立ち上げられ、特別な設定をしなくても初期状態でセキュリティのベースラインが担保される状態を目指しています。推奨パスに乗るだけで安全に進められる、いわゆる「ゴールデンパス」の発想であり、セキュア・バイ・デフォルトを仕組みで成立させるアプローチです。
この方針を devcontainer.json レベルで素直に表現できる仕組みが Dev Container Feature でした。Feature を1行足すだけで宣言的にガードレールが適用されるため、「各チームが自律的に環境を立ち上げつつ、危険な操作だけは仕組みで塞ぐ」という設計とよく噛み合っています。
共通 Dev Container Feature によるガードレール
私たちの開発環境では、共通化した Dev Container Feature(以下、共通 Feature)を配っています。まず、ベースイメージと Feature の役割は明確に分けています。Docker Hardened Images(以下、DHI)をベースにした開発用イメージでは、各種開発ツール(Python / uv / gcloud / Claude Code など)をインストールしておきます。共通 Feature では、それらツールの設定ファイル配置とガードレール適用のみを担います。
この前提のもと、各チームの devcontainer.json は以下のようにシンプルで、ベースイメージを指定し、共通 Feature を追加するだけで、後述するガードレールがまとめて適用されます。
{ "image": "asia-northeast1-docker.pkg.dev/<PROJECT>/<CUSTOM_DHI_PATH>:<TAG>", "features": { "asia-northeast1-docker.pkg.dev/<PROJECT>/<CUSTOM_FEATURE_PATH>:<TAG>": {} } }
こうしてレイヤーを分けておくと、ツールの入れ物とポリシーの適用が混ざらずに整理されるため、よりセキュアに締めやすいという体感があります。たとえばポリシー側だけを Renovate で継続的に更新していけるので、イメージの差し替えと独立してセキュリティ設定の追従・レビューを回せます。なお、ベースイメージ側で押さえるべきリスク(OS パッケージの脆弱性など)と、Feature 側で押さえるべきリスク(ツールの権限・設定)をどう切り分けるかといった論点もあります。ただし本記事のスコープ外のため、詳細は割愛します。
この Feature がプロビジョニング時に各種設定ファイルを配置し、ガードレールを自動で効かせます。実際には複数のツール設定を同じ方式で配布していますが、本記事では代表例として AI エージェントの制御を取り上げます。
Claude Code などの AI エージェントの制御
昨今、Claude Code のような AI コーディングエージェントが普及していますが、無制限の権限を与えると破壊的変更や意図しないデータ送信のリスクがあります。共通 Feature は /etc/claude-code/managed-settings.json を自動生成し、システムレベルで制御を行います。
{ "strictKnownMarketplaces": [ { "source": "github", "repo": "<ORGANIZATION>/<REPOSITORY>" } ], "allowedMcpServers": [ { "name": "<APPROVED_MCP_NAME>", "command": "..." } ], "permissions": { "deny": [ "Bash(sudo:*)", "Bash(gcloud:*)", "Read(~/.config/**)" ] } }
※ 実際の設定から一部を抜粋しています。
プラグインマーケットプレイスと MCP サーバーは、社内で承認されたもののみに制限しています(ホワイトリスト形式)。また、sudo や gcloud などの権限昇格・クラウド操作、~/.config/ 配下の機密情報へのアクセスといった危険な操作は、Deny リストでブロックしています。ユーザー側の settings.json では上書きできない managed settings として配置しているため、「うっかり緩めてしまう」ことを構造的に防げます。
Feature に寄せることの嬉しさ
これらを共通 Feature として提供していることで、以下のようなメリットが得られています。
- 各チームの
devcontainer.jsonは Feature を1行足すだけでよく、セキュリティ設定の知識なしにベースラインを満たせる。 - Feature のバージョンを上げるだけで、全社的にガードレールを一括更新できる(Renovate で自動 PR される)。
- 設定の出所が Feature に集約されているため、監査やレビューの対象が明確になる。
実際に運用してみると、Renovate の PR を1本マージするだけで全チームの Claude Code 設定が同時に更新されるのは、想像していた以上に運用が軽くなったと感じています。
補足:周辺で効かせている多層防御
共通 Feature だけで全てを押さえようとせず、周辺の仕組みと組み合わせて多層防御を成立させています。ベースイメージには DHI を採用してコンテナ起動時点でのベースラインを引き上げ、ホストとなる Remote SSH 用 VM 側にも同等のポリシーを展開し、依存関係は Dependabot / Renovate で継続的に追従させる、という具合です。
おわりに
今回は、MLOps チームが 共通 Dev Container Feature を使って、ML 開発環境のガードレールをどのように設計・運用しているかをご紹介しました。
振り返ってみると、ツールは DHI イメージ、設定は共通 Feature、更新は Renovate と責務を分けておくと、それぞれに対するレビューや更新のサイクルを独立して回しやすいのが大きな利点でした。ガードレール自体を作ることよりも、ガードレールを錆びさせない構造に落とすことが、各チームの自律性を損なわずにベースラインを引き上げていくうえでの要だったように思います。
参考文献
- Claude Code - System settings:
/etc/claude-code/managed-settings.jsonに関する公式ドキュメント - Dev Containers - Features: Dev Container Feature の仕様
こうした「セキュア・バイ・デフォルトな ML 開発環境」を、より多くのチームと一緒に磨き込んでいきたいと考えています。
We're Hiring!
タイミーでは、ML プラットフォームの構築・運用やセキュアな開発環境の整備に一緒に取り組んでいただけるエンジニアを募集しています!
少しでも興味を持っていただけましたら、ぜひ以下のリンクから詳細をご覧ください。