🔬

AIが暴走しても事故らない構成 --- プロンプトではなく仕組みで守る

に公開

はじめに

ClaudeCodeやCodexなどAIエージェントを開発や運用に使うのは当たり前になってきましたね。ただ、利用が進む中で、AIエージェントが セキュリティガードレールを越えて本番環境を変更 してしまったり、最悪破壊する、なんて話もSNSを中心にちらほらと聞くようになりました。

https://news.yahoo.co.jp/articles/610966832a9a7b415f6048e2d6a0ec67fa701fab

こうしたケースのガードレールは多くの場合、「更新系のオペレーションは禁止」 とか 「本番を触る時は許可を求める」 といった プロンプトによる指示 をAGENTS.mdやAgent Skillsに記載するものです。これは人間で言えば 「会社の決まりでそうなっている」 という状態に近いですね。私も個人環境であれば、こんな感じの禁止ルールがAIエージェントに無視されたこともあり、ヒヤリとしましたね><

とはいえ、これは新しい課題ではありません 。昔からルールを守らない人は一定数居ましたし、ルールを守らない人が居ることを前提としたシステムの開発/運用の仕組みもありました。

alt text

というわけで今回はプロンプトによる禁止ルールをAIエージェントが無視しても、仕組みでプロダクトを守る方法を考えていきたいと思います。

Human in the loopと権限

Human in the loop(HITL) という言葉がありますよね? これはAIの学習や運用プロセスに人間の判断を組み込むというもので、AIの精度をあげるというものですが、近年ではAIエージェントの文脈で、人間とAIが伴走してレビューや軌道修正を行うガードレールの一種として語られることも多いと思います。

そして、ガードレールとしてみた場合には、人間に確認をしないとどうなるのか? が重要です。例えば優秀だけどちょっとやんちゃな新人を想像してみてください。

  • 新人: 「動きが微妙だったので修正したコードを本番にデプロイしときました!」
  • 上司: 「え? 本番にリリースするときは事前に承認もらってって言ったよね!?」
  • 新人: 「でも、上司さん忙しそうだったし、面倒だったので。。。大丈夫、私がみた限りはバグってないです!」

さて、これが原因で障害が発生して、ルールを守らなかった新人はペナルティを受け、会社も大きく信頼を損ないました。良くありそうな話ですね?

この問題はなんでしょうか? ルールを守らなかったヤンチャな新人はもちろん悪いかもしれませんが、本当に対策するべきは、この新人が本番環境に自分でデプロイできること です。レビュープロセスやテストによる プロセスで信頼性を確保 する、という観点では同じ人が開発もリリースもできるべきではありません。もっというと、いかなる人間でもプロセスをスキップしてリリースができるべきではありません。

そのためにもっとも簡単なのは 開発者に本番リリースの権限を付与しない ことです。はい、忌まわしき運開分離ですね! 金融やエンタープライズではお馴染みの開発者と運用担当者を分けるプラクティスです。これは様々な問題がありますが、こうした特定担当者のやらかしを防ぐ、という観点では効果のある手法です。

ここで重要なのは開発者たるAIエージェントに 過剰な権限 を与えない、という考え方です。今の我々にはCI/CDがあるので、開発者に本番環境などの権限を与えなくても、テストやレビューといったしかるべきゲートを必ず通ることを仕組みで保証できます。ちなみにエンタープライズでも、ちゃんとしたところは伝統的で欠点だらけの運開分離を別組織でやるのではなく、CICDと承認者管理でやるのは実践されています。これと同じことをAIエージェントに対してもやればよいわけですね。

AIエージェントとCI/CD

一応、見出しは作りましたが、これに関しては当たり前すぎてあまり話すことはない気がしますね。

GitHubなどを利用したCI/CDパイプラインであれば通常はmainブランチ等には直接コミットする権限は開発者には与えられておらず、Pull Requestを出してレビューを行ったり、Actionsでテストを回すことでGateを進めていきますよね。

alt text
AIエージェントに関しても、これは全く同じです。ただ、あえて言えばAIエージェントを人間を越えて活動させるためには人間によるコードレビューは邪魔です。

可能な限りAIエージェントの品質を人間の手を介入させずに保証することが大事で、テストの拡充はもちろん、別なAIによるレビューと、それを開発用のAIが参照できるようにすることは重要でしょう。

そのためには、PRのコメントを見れるようにghコマンドなどのCLIを叩ける権限をAIに渡しておくことは重要です。ただし、コーディング用のエージェント自身が承認やマージをできないように渡す権限はもちろん、GitHub上での承認者の制御などの基本的な設定をしっかりとやっておくことが重要です。

CI/CDプロセスは単なる開発コードのデプロイだけではなく、ChefやAnsible、Terraformを活用したIaCによるインフラ管理の観点でも重要です。これにより人間同様に本番環境をAIに直接触らせずにリリースなども含めて任せることができます。

PAMによる柔軟なAIエージェントの権限管理

さて、CI/CDは当たり前すぎるプラクティスだったかもしれませんが、PAMによるAIエージェントの権限管理も紹介します。どちらかというと、こちらが今回の記事の本命です。

CI/CDでできるのは、たとえIaCと組み合わせてもあくまでプロビジョニングのため、本番環境のデータをみたり、変更したりといった 作業 には対応できません。また、すべての環境が完全なIaCで運用できているとも限らないでしょう。

SREエージェント系には必要な作業で、なんとなく多くのAIエージェント系が本番環境を書き換えてしまった系の事故も、こうした作業に由来していると思います。そこで役立つのがPAMです。

PAM(Privileged Access Management) とは特権アクセス管理とも日本語で訳されますが、こちらもエンタープライズ界隈で好まれるアカウントの権限管理のプラクティスです。

製品により細かい所は異なりますが、基本的にはオペレータは最小権限に設定されており、例えば特定のサーバにログインしたいとか、データベースを参照したいとか、設定の変更をしたい、とかそうした作業に見合った権限をリクエストして承認ベースで権限を付与する、JIT(Just in Time)アクセス を実現します。

alt text

ちなみに、JavaなどのJITコンパイラとは同じJust In Timeでももちろん関係はないです。最初に見たときには「なぜここでJIT!?」と困惑しましたw

このPAMによるJITをうまく使うことで、AIエージェントには**「原則として参照権限しか与えない」**という運用が可能になります。これにより、万が一AIがプロンプトの指示を無視して勝手な変更を試みても、権限エラーで確実にブロックできます。

そもそもAIであれ人間であれ、**「基本的には参照のみで十分だが、特定のタスクだけは更新権限が必要」**というケースは多々あります。しかし、その「稀に発生する作業」のために強い権限を持たせっぱなしにすることが、重大な事故を引き起こす原因となります。

そこで、「普段は権限を最小限に抑えつつ、必要な時だけ一時的に特権を付与する」。これがJITアクセスの基本的な考え方です。

どうしてもAIエージェントに変更操作を任せたい場面でのみ、PAMを通じて権限のエスカレーションを申請させ、人間の承認のもとで一時的に変更を許可するというフローが実現できます。

GCP PAMによるAIエージェントとの連携

まあ、論よりコードという話もあるので、そろそろ実際に設定してみます。PAMは各種クラウド環境ネイティブのもの(AWS TEAM, Azure PIM)やサードパーティのものがありますが、今回はGCP PAMを使います。

https://zenn.dev/koduki/articles/47e433c12c5fad

では、gcloudコマンドで実際に設定してみましょう!

PAMの設定

事前準備

まずはPAMのAPIを有効にします。これは一度やっておけばOKですね。

gcloud services enable privilegedaccessmanager.googleapis.com \
    --project=YOUR_PROJECT_ID

また、今回はテストのために管理者権限からサービスアカウントにスイッチします。これによりサービスアカウントキーを不要にすることができます。
ただ、簡単に元の権限に戻れるのでエージェントが勝手に自分のサービスアカウントを変更できないように、Workload Identity やサービスアカウントキーを使うほうが今回の目的にはあってる気がします。

# 人間がテスト用になりかわるための権限付与
gcloud iam service-accounts add-iam-policy-binding ai-agent@YOUR_PROJECT_ID.iam.gserviceaccount.com \
    --member="user:YOUR_EMAIL@example.com" \
    --role="roles/iam.serviceAccountTokenCreator"

サービスアカウントの作成と初期権限付与

つづいて、AIエージェントが使用するサービスアカウントを作成します。PAMの考え方は「普段は最小権限 / ZSP」なので、通常時はroles/viewerと、PAMで権限エスカレーションをするためのroles/privilegedaccessmanager.viewer、そして今回はterraform planが扱えるようにroles/storage.adminをステート用バケットに付与しておきます。

# サービスアカウントの作成
gcloud iam service-accounts create ai-agent \
    --display-name="AI Agent Service Account" \
    --project=YOUR_PROJECT_ID

# 通常時の権限はViewerを付与
gcloud projects add-iam-policy-binding YOUR_PROJECT_ID \
    --member="serviceAccount:ai-agent@YOUR_PROJECT_ID.iam.gserviceaccount.com" \
    --role="roles/viewer"

# PAMの申請権限(Viewer)を付与
gcloud projects add-iam-policy-binding YOUR_PROJECT_ID \
    --member="serviceAccount:ai-agent@YOUR_PROJECT_ID.iam.gserviceaccount.com" \
    --role="roles/privilegedaccessmanager.viewer"

# Terraformでplanをする場合にロックファイルを作るので対象バケットへの書き込み権限が必要
gcloud storage buckets add-iam-policy-binding gs://YOUR_TFSTATE_BUCKET \
    --member="serviceAccount:ai-agent@YOUR_PROJECT_ID.iam.gserviceaccount.com" \
    --role="roles/storage.admin"

これでAIエージェントは普段、リソースの参照しかできない状態になります。変更系の操作をしようとすると権限エラーになるので、プロンプトのルールを無視しても仕組みとして変更を防げます。

Entitlement(利用資格)の作成

PAMのEntitlementは「誰が・どのロールに・どの承認フローで」昇格できるかを定義したものです。GUIからも設定できますが、今回はCLIでやるので、YAMLファイルで定義します。

# entitlement-with-approval.yaml
privilegedAccess:
  gcpIamAccess:
    resourceType: cloudresourcemanager.googleapis.com/Project
    resource: //cloudresourcemanager.googleapis.com/projects/YOUR_PROJECT_ID
    roleBindings:
      - role: roles/compute.instanceAdmin.v1  
      - role: roles/run.admin 
      - role: roles/iam.serviceAccountUser
maxRequestDuration: 1800s  # 最小30分
eligibleUsers:
  - principals:
      - "serviceAccount:ai-agent@YOUR_PROJECT_ID.iam.gserviceaccount.com"
requesterJustificationConfig:
  notMandatory: {}
approvalWorkflow:
  manualApprovals:
    steps:
      - approvers:
          - principals:
              - "user:admin@example.com"
        approvalsNeeded: 1

以下のコマンドで作成したYAMLを適用してPAMの資格を作成します。

gcloud pam entitlements create ai-agent-compute-access \
    --project=YOUR_PROJECT_ID \
    --location=global \
    --entitlement-file=entitlement-with-approval.yaml

作成済みEntitlementの一覧は以下で確認できます。

gcloud pam entitlements list \
    --project=YOUR_PROJECT_ID \
    --location=global

AIエージェントが権限昇格をリクエスト(Grant作成)

AIエージェントが実際に権限を必要とするときは gcloud pam grants create でリクエストします。ここでいうjustification(理由)は監査ログにも残るので、何の作業かを明記しておくとあとで追跡しやすいです。

gcloud pam grants create \
    --project=YOUR_PROJECT_ID \
    --location=global \
    --entitlement=ENTITLEMENT_ID \
    --requested-duration=1800s \
    --justification="Production incident investigation by SRE agent"

承認者が承認する

承認が必要なEntitlementの場合、承認者は以下のコマンドで承認します。GRANT_IDgcloud pam grants list で確認できます。

# Grant一覧の確認
gcloud pam grants list \
    --project=YOUR_PROJECT_ID \
    --location=global \
    --entitlement=ENTITLEMENT_ID

# 承認
gcloud pam grants approve GRANT_ID \
    --project=YOUR_PROJECT_ID \
    --location=global \
    --entitlement=ENTITLEMENT_ID \
    --reason="Approved for incident response"

GUIから以下のようにも見えます。
alt text

承認されると、指定した --requested-duration の間だけIAMロールが一時的に付与され、時間が来ると自動で剥奪されます。もちろんこの一連の操作はすべてAudit Logsに記録されます。

AIエージェントからPAMの利用

PAMの基本設定ができたので、実際にAIエージェントから利用していきます。

準備:サービスアカウントでログイン

まずはサービスアカウントであるAI Agent Service Accountでgcloudコマンドが実行できるように設定します。いくつか方法はありますが、今回はお手軽にimpersonateで。実運用では鍵ファイルでの認証やWorkload Identityなどの方が良いでしょう。

# サービスアカウントへのなりかわりを有効化
gcloud config set auth/impersonate_service_account ai-agent@YOUR_PROJECT_ID.iam.gserviceaccount.com

# なりかわりが正しく設定されているか確認
gcloud config list auth/impersonate_service_account

上記コマンドを実行し、impersonate_service_account にサービスアカウント名が表示されていれば成功です。ADCの場合は以下のように実施します。

# ADCもなりかわり状態で設定
gcloud auth application-default login --impersonate-service-account=ai-agent@YOUR_PROJECT_ID.iam.gserviceaccount.com \
    --scopes="https://www.googleapis.com/auth/cloud-platform,https://www.googleapis.com/auth/userinfo.email"

TARGET_SA="ai-agent@YOUR_PROJECT_ID.iam.gserviceaccount.com"

# なりかわりが正しく設定されているか確認
[ "$(curl -s "https://oauth2.googleapis.com/tokeninfo?access_token=$(gcloud auth application-default print-access-token)" | jq -r .azp)" = \
  "$(gcloud iam service-accounts describe $TARGET_SA --format="value(uniqueId)")" ] \
  && echo "ADC Verification: OK" || echo "ADC Verification: FAILED"

これ以降のコマンドは自動的にサービスアカウントの権限で実行されます。

未承認でApplyした場合

まず、承認なしでterraform applyをした場合は、Planは問題なく見えますが、Applyでエラーになります。

$ terraform apply

- -

Plan: 0 to add, 2 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

google_cloud_run_v2_job.crawler: Modifying... [id=projects/{YOUR_PROJECT}/locations/asia-northeast1/jobs/crawler-job]
google_cloud_run_v2_service.api_server: Modifying... [id=projects/{YOUR_PROJECT}/locations/asia-northeast1/services/api-server]

 Error: Error updating Service "projects/{YOUR_PROJECT}/locations/asia-northeast1/services/api-server": googleapi: Error 403: Permission 'run.services.update' denied on resource 'projects/{YOUR_PROJECT}/locations/asia-northeast1/services/api-server' (or resource may not exist).
 
   with google_cloud_run_v2_service.api_server,
   on api_gateway.tf line 4, in resource "google_cloud_run_v2_service" "api_server":
    4: resource "google_cloud_run_v2_service" "api_server" {
 

PAMでエスカレーションするSkillの作成

そこで、以下のようなSkillを作成して、Apply前に権限エスカレーションをするようにします。以下のようなSkillを作ります。

---
name: terraform-pam-escalation
description: >-
  Terraform applyを実行する前に、エージェントがアクティブなPAM(Privileged Access Manager)権限昇格状態にあることを確認します。
  'ai-agent-compute-access' の有効なGrantが存在するかチェックし、存在しない場合はGrantを作成してユーザーに承認を促します。
---

# PAM Escalation for Terraform

Terraformの実行に必要なPAM権限昇格を処理するスキルです。プロジェクトIDはアクティブな設定から動的に取得します。

## ワークフロー

1.  **アクティブなGrantの確認:**
    有効なGrantがあるか確認します。
    ```bash
    gcloud pam grants list \
      --location=global \
      --entitlement=ai-agent-compute-access \
      --filter="state:ACTIVE" \
      --format="value(name)"
    ```
    出力があれば(ACTIVEなGrantが存在すれば)、そのまま `terraform apply` を実行します。

2.  **Grantの作成 (ACTIVEがない場合):**
    新しいGrantを作成します。
    ```bash
    PROJECT_ID=$(gcloud config get-value project)
    gcloud config set auth/impersonate_service_account ai-agent@${PROJECT_ID}.iam.gserviceaccount.com
    
    gcloud pam grants create \
      --location=global \
      --entitlement=ai-agent-compute-access \
      --requested-duration=1800s \
      --justification="Terraform apply by SRE agent"
    ```

3.  **ユーザー承認と適用:**
    Grant作成後、発行された `GRANT_ID` を用いてユーザーに以下の承認コマンドを提示し、承認を依頼します。
    
    *ユーザー提示用コマンド:*
    ```bash
    gcloud config unset auth/impersonate_service_account
    gcloud pam grants approve <GRANT_ID> --location=global --entitlement=ai-agent-compute-access
    ```
    
    ユーザーから承認完了の合図があれば、状態の再確認は省略し、そのまま `terraform apply` を実行します。

そして、AIエージェントからのリクエストを元に、人間の承認者が承認をおこないます。

alt text

これにより、今回であれば30分ほど権限が付きます。

PAMで得た権限で作業を実施

PAMで権限を得た後に同じくAIエージェントがTerraformを実行すると、以下のように今度は成功します。権限が付与されたことがわかりますね!

$ terraform apply

- -

Plan: 0 to add, 2 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

google_cloud_run_v2_service.api_server: Modifying... [id=projects/{YOUR_PROJECT}/locations/asia-northeast1/services/api-server]
google_cloud_run_v2_job.crawler: Modifying... [id=projects/{YOUR_PROJECT}/locations/asia-northeast1/jobs/crawler-job]
google_cloud_run_v2_service.api_server: Modifications complete after 1s [id=projects/{YOUR_PROJECT}/locations/asia-northeast1/services/api-server]
google_cloud_run_v2_job.crawler: Still modifying... [id=projects/{YOUR_PROJECT}/locations/asia-northeast1/jobs/crawler-job, 00m10s elapsed]
google_cloud_run_v2_job.crawler: Modifications complete after 11s [id=projects/{YOUR_PROJECT}/locations/asia-northeast1/jobs/crawler-job]

Apply complete! Resources: 0 added, 2 changed, 0 destroyed.

プロンプトのルールは無視されても、権限が無ければ変更は出来ません。そして変更が必要な場合は必ず人間の承認を経由する、という仕組みをPAMで確実に実現できます。

人間向けのPAMでの限界とNext Steps

GCPのPAMは、現時点では人間向けサービスとして登場しているので、AIエージェントとの連携にはいくつかの課題があります。冒頭で新しい課題ではない、とあえて言いましたが、主に量の問題で全く同じでもやはりないので。

まず、最小の権限付与時間が30分 であるため、AIエージェントの暴走を完全に防ぐという観点では長すぎです。ここはもう少しAIエージェントの特性に特化して、何かしらのオペレーション認証単位になると便利そうです。少なくとももっと短くしてほしいですよね。

もうひとつ、これは人間でも起こりがちですが、*承認疲れです。AIが頻繁に権限昇格をリクエストするため、人間が承認を続けるのが負担になる可能性があります。

特に、AIエージェントの導入目的は生産性向上でしょうから、その場合は人間の承認フローがボトルネックになり過ぎても良くありません。PAMの認証自体はAPIで出来るので、本当にリスクが高い作業以外は、別のAIやルールエンジンを活用したHuman-on-the-Loopに寄せた承認の自動化も必要です。実行するAIと承認するAIを明確に分ければ、ゲートとしては十分機能するので。

このあたりは、今後に期待ですが、結構急速に進化しそうな気もしています。

まとめ

今回は、Google CloudのPAMサービスである**Privileged Access Manager (PAM)**を用いて、AIエージェントの実行権限をコントロールする方法を紹介しました。

AIエージェントがプロンプトの指示に従わなくても、仕組みで防いでいれば、本番環境を破壊される、などの致命的な問題は十分に防げます。

現時点では人間向けのPAMでは理想の運用にならないところも残りますが、それでも大きく改善しますし、おそらくですが今後この手の機能も各社大きく進化させてくると思うので、注目しておきたいですね。

それではHappy Hacking!

Discussion