🛡️

Gemini CLI でセキュアで堅牢な開発をするためのプラクティス集

に公開

先日、クラウドネイティブ × Gemini CLIというイベントで『Gemini CLI でもセキュアで堅牢な開発をしたい!』というタイトルで登壇させていただきました。
時間都合で端折ってしまった部分が多かったため、本記事で行間を埋めつつ最新の状況をお伝えします。
登壇の内容は全て記載するため、イベントに参加されなかった方も読んでいただければと思います。

はじめに

本記事は Gemini CLI を個人レベルではなく企業やチームとして使いたい方を対象とします。
そのため、Gemini CLI の基本的な部分(例えばどのようにインストールするか、settings.jsonとは何か、基本的な使い方など)は割愛させていただきます。

本記事を読むことで、Gemini CLI の機能や関連ツールを使ってセキュアで堅牢な開発を実現するためのプラクティスをお伝えできればと思います。

注意事項

本記事の内容はv0.10.0を前提にしています。

また本記事の内容を全て対応するのが全てのケースでベストなわけではありません。
セキュリティも絡む話題ですので、企業によっては不足、もしくは過剰ということは当然考えられます。
ご自身の企業やチームであればどこまで必要か、実現できそうかを考えながら読んでいただければと思います。

問題提起:コーディングエージェントの抱える課題

コーディングエージェントは非常に便利な存在です。
しかし無計画に利用し始めてしまうと、以下のような脆く不安定な開発体制に陥ってしまいます。

  • 便利だと思って使っていたら、実は入力が学習利用されていた
  • バイブコーディングによって
    • 品質の悪いコードが生成されてしまった
    • 意図と異なる機能ができた
  • YOLO で実行していたらローカルマシンやクラウドの環境が変更、破壊されてしまった
  • (偉い人視点で)開発者がどんな MCP サーバを使っているのか把握できていない

これらの問題は Gemini CLI の機能や関連ツールを使いこなすことで解消、軽減することができます。

プラクティス 1:適切なプランを選択する

認証方法とプラン

Gemini CLI には認証方法が 3 つ、プランは数え方にもよりますが 9 つあります。[1]
ここでは各認証方法で有料のプランと無料のプランがあるということを押さえてください。
(プランの詳細については後述します)

認証方法 無料プラン 有料プラン
Google ログイン Gemini Code Assist for individuals Google AI Pro/Ultra、Gemini Code Assist Standard/Enterprise edition
Gemini API キー Unpaid Paid
Vertex AI Express Mode Regular Mode

企業やチームで利用するにあたりどのプランを選択するかについて、2 つの観点があります。

観点 1:入力の学習利用

1 つ目は入力の学習利用です。
これについては基本的に「無料は学習利用される、有料はされない」と思ってください。[2]
(正確には Vertex AI Express Mode での利用は無料、かつ学習利用されませんがトライアルなので気にしなくて良いです)

観点 2:監査ログ

2 つ目は監査ログです。
Gemini CLI ではどの認証方法であっても推論リクエストは Google Cloud で処理されます。
そのため監査ログは Google Cloud のデータアクセス監査ログとして記録されます。
これはいつ、誰が(どのプリンシパルが)、何をしたか(どの API を実行したか)を記録するものです。
プランとの関係で言うと、Gemini Code Assist か Vertex AI で利用時に課金で紐づいているプロジェクトで記録されます。
Gemini API キーで利用時は有料であっても記録されません。

結論

結論としては上記を考慮すると、以下の 2 つのプランのどちらかでの利用を推奨します。[3]

  • Gemini Code Assist Standard/Enterprise edition
    • 定額での利用
    • ユーザごとのクォータ制限あり、モデル指定不可
  • Vertex AI (Regular Mode)
    • 従量課金
    • ユーザのクォータ制限なし、モデル指定可能

Vertex AI での利用は従量課金になるので導入の検証に向いています。
また制限がないためヘビーユース向けです。
一方で開発目的でチーム利用する場合には定額の Gemini Code Assist がおすすめです。

補足:OpenTelemetry でツールや MCP の利用を記録する

監査ログが記録されるプランを利用していても記録されるのは Google Cloud の API を叩いた時だけです。
ツールや MCP の利用時には記録するにはテレメトリ機能を有効にする必要があります。[4]
設定やどんな情報を収集できるかについては、同じイベントで登壇された@pHaya72さんの記事が分かりやすかったので、そちらをご覧ください。

https://zenn.dev/t_hayashi/articles/0fce56bbba5b6f

プラクティス 2:設定の統制を取る

settings.jsonの優先順位

Gemini CLI の設定ファイルであるsettings.jsonは 4 種類の配置場所があり、後のものほど高優先度です。[5]

  • システムデフォルト
  • ユーザ設定(~/.gemini/settings.json
  • ワークスペース設定(<project>/.gemini/settings.json
  • システムオーバーライド(Linux なら/etc/gemini-cli/settings.json

最も優先されるシステムオーバーライドはシステムディレクトリに配置するので、一般ユーザに root 権限を与えなければ管理者が Gemini CLI の設定をコントロールすることができます。

マージ戦略

設定の統制を取る上で知っておくべきことは、各設定ファイルで設定の重複があった場合にどのようにマージされるか(マージ戦略)です。[6]
Gemini CLI の設定には 4 つのマージ戦略があります。

1. SHALLOW_MERGE

オブジェクトに対して浅いマージをします。
(Gemini CLI は TypeScript 製なので「浅いマージ」は JavaScript の浅いマージです)
キー名が同じ設定があった場合には高優先度で上書きされます。
該当する設定はmcpServersです。

例えば管理者がシステムオーバーライドで

/etc/gemini-cli/settings.json
{
  "mcpServers": {
    "server1": {
      "command": "foo.py"
    }
  }
}

としていたとします。
このときに一般ユーザが以下のようにmcpServers.server1の変更とmcpServers.server2の追加を試みます。

~/.gemini/settings.json
{
  "mcpServers": {
    "server1": {
      "command": "bar.py"
    },
    "server2": {
      "command": "baz.py"
    }
  }
}

最終的なマージ結果は以下のようになります。
mcpServers.server2を追加することはできますが、mcpServers.server1を変更することはできません。
これがなぜ嬉しいかは後述するので、ここではそういう動きになるんだという理解で大丈夫です。

{
  "mcpServers": {
    "server1": {
      "command": "foo.py"
    },
    "server2": {
      "command": "baz.py"
    }
  }
}

2. CONCAT

リストに対して単純な結合をします。
該当する設定はcontext.includeDirectoriesです。

例えば管理者がシステムオーバーライドで

/etc/gemini-cli/settings.json
{
  "context": {
    "includeDirectories": ["path/to/some/dir/1"]
  }
}

としていたとします。
このとき一般ユーザがパスの追加を試みます。

~/.gemini/settings.json
{
  "context": {
    "includeDirectories": ["path/to/some/dir/1", "path/to/some/dir/2"]
  }
}

最終的なマージ結果は以下のようになります。
"path/to/some/dir/1"の重複は排除されず、"path/to/some/dir/2"を追加することができます。

{
  "context": {
    "includeDirectories": [
      "path/to/some/dir/1",
      "path/to/some/dir/1",
      "path/to/some/dir/2"
    ]
  }
}

3. UNION

リストに対して重複を排除して結合します。
該当する設定はtools.excludeadvanced.excludedEnvVarsextensions.disabledextensions.workspacesWithMigrationNudgeです。

例えば管理者がシステムオーバーライドで

/etc/gemini-cli/settings.json
{
  "tools": {
    "exclude": ["ShellTool(sudo)"]
  }
}

としていたとします。
このとき一般ユーザが除外ツールの追加を試みます。

~/.gemini/settings.json
{
  "tools": {
    "exclude": ["ShellTool(sudo)", "ShellTool(rm)"]
  }
}

最終的なマージ結果は以下のようになります。
"ShellTool(sudo)"の重複は解消され、"ShellTool(rm)"を追加することができます。

{
  "tools": {
    "exclude": ["ShellTool(sudo)", "ShellTool(rm)"]
  }
}

4. REPLACE

高優先度で上書きします。
該当する設定は上記以外の残り全てです。

例えば管理者がシステムオーバーライドで

/etc/gemini-cli/settings.json
{
  "tools": {
    "core": ["ReadFileTool"]
  }
}

としていたとします。
このとき一般ユーザが利用可能ツールの追加を試みます。

~/.gemini/settings.json
{
  "tools": {
    "core": ["ReadFileTool", "GlobTool", "ShellTool(ls)"]
  }
}

最終的なマージ結果は以下のようになります。
REPLACEでは高優先度で上書きされるため、ユーザは利用可能ツールを追加できません。

{
  "tools": {
    "core": ["ReadFileTool"]
  }
}

ツールの統制

ツールの統制を取るには以下のどちらかをシステムオーバーライドで設定します。

  • tools.core:記載されたツールのみを使用可能になる
  • tools.exclude:記載されたツールは使用不可になる

tools.coreREPLACE方式なので、先ほど見たように一般ユーザは許可ツールを追加できません。
tools.excludeUNION方式なので先ほど見たように追加は可能ですが、利用できるツールが増えるわけではないので安全です。

MCP サーバの統制

MCP サーバの統制を取るには以下の両方をシステムオーバーライドで設定します。

  • mcpServers:MCP サーバの定義
  • mcp.allowed:記載された MCP サーバのみを使用可能になる

mcpServersSHALLOW_MERGE方式なので一般ユーザも追加可能ですが、実際に利用するためにはmcp.allowedにも追加する必要があります。
mcp.allowedREPLACE方式なので追加できず、利用可能な MCP サーバを制限することができます。
例えば管理者がシステムオーバーライドで

/etc/gemini-cli/settings.json
{
  "mcp": {
    "allowed": ["server1"]
  },
  "mcpServers": {
    "server1": {
      "command": "foo.py"
    }
  }
}

としていたとします。
このときに一般ユーザが以下のように"server1"の変更と"server2"の追加を試みます。

~/.gemini/settings.json
{
  "mcp": {
    "allowed": ["server1", "server2"]
  },
  "mcpServers": {
    "server1": {
      "command": "bar.py"
    },
    "server2": {
      "command": "baz.py"
    }
  }
}

最終的なマージ結果は以下のようになります。
mcpServers.server2を追加することはできますが、mcpServers.server1を変更することはできません。
またmcp.allowedも変更することはできません。
結果としてユーザは MCP サーバを追加、変更できず統制を取ることができます。

{
  "mcp": {
    "allowed": ["server1"]
  },
  "mcpServers": {
    "server1": {
      "command": "foo.py"
    },
    "server2": {
      "command": "baz.py"
    }
  }
}

認証方法の統制

認証方法の統制を取るにはsecurity.auth.enforcedTypeをシステムオーバーライドで設定します。

{
  "security": {
    "auth": {
      "enforcedType": "vertex-ai"
    }
  }
}

異なる認証方法を使った場合には Gemini CLI を使用できません。

プラクティス 3:安全に実行する

サンドボックス

意図しない開発環境の変更を減らすため、サンドボックスでの利用をお勧めします。[8]
Gemini CLI では Docker、Podman、sandbox-exec での実行をサポートしています。
ここでは Docker を使う想定で説明します。

カスタムサンドボックス

実際の開発では Gemini CLI に利用させたいコマンドや MCP サーバをサンドボックスにインストールすることになります。

カスタムイメージの作成ではベースとして公式のus-docker.pkg.dev/gemini-code-dev/gemini-cli/sandboxが利用可能です。
タグは Gemini CLI のバージョンと同じものがあるので固定しましょう。
Dockerfile の配置場所:.gemini/sandbox.Dockerfileです。
以下のような設定、コマンド実行をすると Gemini CLI がカスタムサンドボックスで起動します。

  • ビルド:docker build -t gemini-cli-sandbox -f .gemini/sandbox.Dockerfile .gemini
  • 設定
    {
      "tools": {
        "sandbox": "docker"
      }
    }
    
  • 起動:GEMINI_SANDBOX_IMAGE=gemini-cli-sandbox gemini

またサンドボックスコンテナにファイルをマウントしたり環境変数を設定したりしたくなることがあると思います。
その場合はSANDBOX_FLAGSdocker runのオプションを渡すことができます。

ローカルでもアプリをコンテナとして動かしている場合

アプリコンテナをサンドボックスコンテナ上で実行するためにはサンドボックスコンテナを特権モードで実行する必要があり本末転倒です。
そのため、サンドボックスではなく GitHub Codespaces や Cloud Workstations などのクラウド開発環境を利用するのがベターです。

ツールやコマンドの自動承認

サンドボックスを利用するだけでは、外部環境を変更する可能性のあるコマンドには無防備です。
例えばcurlwgetgit pushdocker pushkubectl applyterraform applyといったコマンドです。
一方で全てのコマンドの実行を毎回承認していては生産性があがりません。
安全なコマンドは自動承認し、危険な可能性のあるコマンドは手動承認する必要があります。

まず大雑把な設定として--approval-modeである程度のコントロールが可能です。

  • default:読み取り専用ツール(list_directoryread_fileglobsearch_file_contentread_many_filesgoogle_web_search)が自動承認される。それ以外(例えばwrite_file)は承認が必要なので非効率。
  • yolo:全てのツールが承認なしで実行される。非推奨。
  • auto_editdefaultで自動承認されるツールに加えてwrite_filereplaceweb_fetchが自動承認される。run_shell_command は承認が必要。バランスが良いので推奨。

細かいコントロールはtools.allowedで設定可能です。
auto_editで実行する場合にはここでシェルツールを指定します。
注意点としてuv runbun runなどのコマンドは任意のコマンドを実行可能です。
そのため許可は細かく行う必要があります。

NG 例

{
  "tools": {
    "allowed": ["ShellTool(uv run)"]
  }
}

OK 例

{
  "tools": {
    "allowed": [
      "ShellTool(uv run ruff)",
      "ShellTool(uv run pyright)",
      "ShellTool(uv run pytest)"
    ]
  }
}

プラクティス 4:品質を保つ

仕様駆動開発

皆さんお気づきだと思いますが、1 往復や 1 セッションでは高品質なアウトプットは出ません。
そのためタスクを何らかの方法で管理してあげる必要があります。
自前の方法で管理しても良いですが、世の中では仕様駆動開発という便利なツールがあるのでそれに乗っかるのが良いです。
Gemini CLI で利用可能な仕様駆動開発ツールには以下があります。

仕様駆動開発ツール メリット デメリット
Spec Kit GitHub 製、技術選定もドキュメントに残る 英語のみ対応、カスタムコマンド打つのが面倒かも
Spec Workflow MCP 日本語対応、コマンド不要で体験が良い 個人開発
cc-sdd 日本語対応、Kiro 互換、成果物が日本企業に合っている 個人開発、カスタムコマンド打つのが面倒かも

どれを選択するについてですが、個人的な意見ですがどれでも良いので使ってください。
例えば、開発元が信頼できるので Spec Kit、成果物が既存プロセスにあっているので cc-sdd、体験が良いので Spec Workflow MCP にする、といった感じで良いと思います。

ちなみに選定理由として日本語対応は重要でないと考えています。
私が検証した際には日本語対応を謳っている Spec Workflow MCP と cc-sdd でそれぞれ 1 回英語で出力してきたことがありました。
逆に Spec Kit に関してはカスタムコマンドを Gemini CLI に日本語化させることで日本語で利用できています。

プレコミットフック

Claude Code や Kiro には Hooks という機能があります。
これはファイル編集後などのタイミングでコマンドを実行する機能です。
Gemini CLI にはありませんが、このような決定論的な仕組みは非常に有用なので、Git Hooks で代用すると良いです。
具体的には pre-commit でフォーマット、リント、型チェック、単体テストを実行させます。

まとめ

コーディングエージェントが抱えるいくつかの課題に対し、適切なプラン選択、設定の統制、サンドボックス、自動承認、仕様駆動開発、プレコミットフックを紹介しました。
もしかしたら本格利用にハードルを感じてしまったかもしれませんが、業務利用でなければ気軽に使うべきです。
例えば、本記事ではドキュメントに記載のない内容も紹介しましたが、これらは Gemini CLI に Gemini CLI 自身のコードを調査させて得られた知見です。
ぜひ積極的に Gemini CLI を使っていただければと思います。

脚注
  1. https://geminicli.com/docs/get-started/authentication/ ↩︎

  2. https://geminicli.com/docs/tos-privacy/ ↩︎

  3. https://geminicli.com/docs/quota-and-pricing/ ↩︎

  4. https://geminicli.com/docs/cli/telemetry/ ↩︎

  5. https://geminicli.com/docs/get-started/configuration/ ↩︎

  6. https://github.com/google-gemini/gemini-cli/blob/v0.10.0/packages/cli/src/config/settingsSchema.ts ↩︎

  7. https://geminicli.com/docs/tools/ ↩︎

  8. https://geminicli.com/docs/cli/sandbox/ ↩︎

  9. https://geminicli.com/docs/tools/shell/ ↩︎

Discussion