複数のAIエージェントを連携させて業務を自動化する構成を検討している、あるいはすでに運用しているインフラ担当者に向けた内容です。オーケストレーター(複数のAIエージェントにタスクを振り分ける司令塔役のエージェント)から権限がワーカーエージェントに委譲されていく構成で、権限がどこまで絞り込まれているかを把握できているかどうか、確認の参考になれば幸いです。
何が起きるか:権限が委譲のたびに膨らんでいく
オーケストレーターが複雑なタスクをサブタスクに分解し、DBクエリボット・レポート生成エンジン・アラート送信エージェントなどの専門ワーカーに委譲する構成は、すでに珍しくありません。Google ADK(Agent Development Kit、Googleが提供するエージェント開発フレームワーク)やCloud Runを使った構成もその一例です。
この構成で起きやすい問題が「暗黙の権限昇格(Implicit Privilege Escalation)」です。委譲チェーンをたどるうちに、本来read権限しか持たないはずのエージェントがwrite権限やadmin権限を要求・取得してしまう状態を指します。
影響範囲は静的なRBAC(Role-Based Access Control、役割ベースのアクセス制御)を使った従来型システムより深刻です。単一の侵害されたエージェント経由で、DBの書き込みや通知の乱発といった破壊的操作にまで到達しうるからです。しかもエージェント間のやり取りはログの粒度が粗くなりがちで、どのホップ(委譲の1段階)でどの権限が付与されたのか、事後に追いにくいという副作用もあります。
なぜ起きるか:権限モデルの前提が壊れている
原因を分解すると、大きく3つの層に分かれます。
1つ目は「スコープが狭まる保証がない」ことです。委譲のたびに権限の集合が親と同じか、それ以上に広がってしまう実装は少なくありません。本来は「要求スコープ ∩ 呼び出し元スコープ ∩ 対象システムの上限」という積集合の形で、権限は委譲のたびに狭まるべきものです。この制約をコードで強制していないと、権限は自然に膨張します。
2つ目は「危険な操作を止める仕組みがない」ことです。read専用のはずのクエリエージェントがwrite操作を試みても、それを検知して即座に止める層がなければ、実行されてから初めて問題に気づくことになります。操作の重大度をスコアリングして閾値で遮断する仕組み(read=1、write=4、admin=10のような重み付け)がない構成は、実質的に事後対応しかできません。
3つ目は「監査ログが改ざん検知できない」ことです。委譲の許可・拒否の記録が単なるテキストログやDBレコードのままだと、ストレージ層が侵害されたときにログ自体を書き換えられてしまいます。コンプライアンス監査で「このログは改ざんされていない」と数学的に証明する手段がないと、非否認性(non-repudiation、後から「やっていない」と言えないようにする性質)を担保できません。
自分のプロジェクトが該当するか確認する
以下の観点で、手元の構成を点検してみてください。
- エージェント間の権限委譲を実装しているコードで、子エージェントのスコープが親のスコープの部分集合であることを検証する処理があるか(
issubsetのような集合演算の有無をgrepで探す) - IAM(Identity and Access Management)のサービスアカウント設計を確認し、
gcloud iam service-accounts get-iam-policy <SA名>でロールを洗い出し、1つのサービスアカウントに複数エージェント分の権限が同居していないか - Cloud Runなどでエージェントをデプロイしている場合、
gcloud run services describe <サービス名> --format='value(spec.template.spec.serviceAccountName)'で紐づくサービスアカウントを確認し、最小権限(例:roles/cloudsql.viewerのみ)になっているか - 委譲ログにHMAC(Hash-based Message Authentication Code、共通鍵を使った改ざん検知用の署名)などの署名フィールドがあるか、テーブル定義やログスキーマを確認する
- 危険操作を遮断する層(ファイアウォール的な中間層)がコールスタックに存在するか、それとも直接APIを呼び出しているか
どれか1つでも「ない」に該当するなら、次の対策手順の該当部分から着手する価値があります。
対策の手順
1. スコープの積集合演算をコードに落とし込む
権限を(リソース, アクション)のペア(例: cloudsql:orders:read)として集合で表現し、委譲時に必ず積集合を取る処理を挟みます。
@dataclass(frozen=True)
class ScopeSet:
scopes: frozenset
def intersect(self, other: "ScopeSet") -> "ScopeSet":
return ScopeSet(self.scopes & other.scopes)子エージェントに渡すスコープは「要求スコープ ∩ 呼び出し元スコープ ∩ 対象システムの上限」の3つの積で決まるようにします。これにより、委譲チェーンをどれだけ深くたどっても権限が広がることはなくなります。
2. 危険操作を遮断する中間層を挟む
操作の重大度を数値化し、閾値を超えたら実行前に止める仕組みを入れます。read=1、audit=2、write=4、send=6、admin=10のような重み付けをして、許可されたスコープにない操作が来たらQuarantineErrorのような専用例外で止め、理由をログに残します。この層をオーケストレーターとワーカーの間に必ず挟むことがポイントです。エージェント側の実装に検証ロジックを分散させると、実装漏れが発生しやすくなります。
3. 委譲ログにHMAC-SHA256署名を付与する
委譲の1ホップごとに、許可・拒否にかかわらず署名付きレコードを残します。
import hmac, hashlib
def sign(payload: str, secret: str) -> str:
return hmac.new(secret.encode(), payload.encode(), hashlib.sha256).hexdigest()検証時はhmac.compare_digestで期待値と突き合わせ、一致しなければ改ざんとして扱います。ログの保存先はCloud LoggingでもBigQueryでも構いませんが、署名の元になる秘密鍵はSecret Managerのようなシークレット管理サービスに置き、アプリケーションコードや環境変数にべた書きしないようにします。
4. Terraformでサービスアカウントを1エージェント1ロールに分離する
IaC(Infrastructure as Code)でエージェントごとのサービスアカウントとロールを明示的に分離します。
resource "google_service_account" "db_query_agent" {
account_id = "db-query-agent-sa"
}
resource "google_project_iam_member" "db_query_agent_role" {
project = var.project_id
role = "roles/cloudsql.viewer"
member = "serviceAccount:${google_service_account.db_query_agent.email}"
}通知専用のエージェントにはクラウドリソースへのアンビエント権限(常時付与された環境依存の権限)を一切与えない、といった設計をTerraformの差分(terraform plan)で毎回確認できる状態にしておくと、権限の膨張をレビューの段階で検知できます。
まとめ
多段エージェント構成のインフラを見る際は、次の3点をまず点検してみてください。
- 委譲時にスコープの積集合演算が入っているか、コード上で確認する
- 危険操作を実行前に遮断する中間層(ファイアウォール的な層)が存在するか
- 委譲ログにHMACなどの署名があり、改ざん検知が可能か
これらはCloud Runやサーバーレス関数でエージェントを分散デプロイする構成に限らず、Kubernetes上でマイクロサービス化したエージェント群でも同じ原則が当てはまります。まずはgcloud iam service-accounts get-iam-policyとTerraformのplan結果を見比べるところから始めてみてください。