Claude Media
VPC Service Controlsの違反をClaude Codeで読み、ingress・egressルールを直す

VPC Service Controlsの違反をClaude Codeで読み、ingress・egressルールを直す

VPC Service Controlsが拒否した通信を監査ログから取り出し、ingress・egressルールのYAML案をClaude Codeに作らせ、dry-runで確かめてから強制に移す手順です。

VPC Service Controls(サービス境界)を入れた環境では、ある日突然 Request is prohibited by organization's policy で通信が止まります。原因は監査ログに残っていますが、読み解いてingressルールにするかegressルールにするかを決め、YAMLに落とすまでが手間です。

ここでは、違反ログの取り出しから修正案のYAML作成、dry-runでの確認までを順に進めます。ログの整理とYAMLの下書きはClaude Codeに任せ、境界の設定を書き換える操作は人の手に残します。

違反ログの何を見ればingressかegressか分かるか

見る場所は、ポリシー拒否の監査ログ(cloudaudit.googleapis.com/policy)にある protoPayload.metadata の中です。ここに拒否の理由 violationReason と、違反の中身を示す ingressViolations / egressViolations が入っています。

方向の判定は次の2点で決まります。

  • ingressは、境界の外のクライアントが境界内のリソースへ届こうとして拒否された場合
  • egressは、境界内のクライアントが境界外のリソースへ出ようとした場合、または1回のリクエストに境界内外のリソースが混ざった場合

violationReason ごとの対処は、次のとおりです。

violationReason起きていること対処
NETWORK_NOT_IN_SAME_SERVICE_PERIMETER起きていることVPCネットワークのクライアントが、別境界のプロジェクトへ出る(egress)か、境界外から入る(ingress)対処方向に合わせてegressまたはingressルールを作る
RESOURCES_NOT_IN_SAME_SERVICE_PERIMETER起きていること1回のリクエストが、同じ境界に属さない複数のリソースに触れた対処egressルールを作る
NO_MATCHING_ACCESS_LEVEL起きていることIP・デバイス・IDがどのingressルールにもアクセスレベルにも合わない対処アクセスレベルより、細かく制御できるingressルールが推奨されている
SERVICE_NOT_ALLOWED_FROM_VPC起きていることVPCから呼んだサービスが、境界のVPCアクセス可能サービスで許可されていない対処ルールではなく、VPCアクセス可能サービスの設定を見直す

最後の行が落とし穴です。拒否が出たからといって、すべてがルール追加で直るわけではありません。SERVICE_NOT_ALLOWED_FROM_VPC はingress / egressの話ではないので、YAMLを書かせる前に仕分けておきます。

違反の中身は、たとえばegressならこの形で記録されます。以下はGoogleのトラブルシューティング記事にある例です。

egressViolations:
- sourceType: "Resource"
  source: "projects/5678"
  targetResource: "projects/4321/buckets/external-storage-bucket/objects/corp-resources.json"
  servicePerimeter: "accessPolicies/123456789/servicePerimeters/prod-perimeter"

source が境界内、targetResource が境界外です。egressルールの egressFrom(誰が・どこから)と egressTo(どのリソースに何をするか)に、そのまま対応します。

gcloud logging readで違反ログを取り出す

境界で保護しているプロジェクトのポリシー拒否ログを、gcloud logging read で読みます。フィルタは、Logs Explorer用にGoogleが示している条件をそのまま使えます。

gcloud logging read \
  'log_id("cloudaudit.googleapis.com/policy") severity=ERROR
   resource.type="audited_resource"
   protoPayload.metadata."@type"="type.googleapis.com/google.cloud.audit.VpcServiceControlAuditMetadata"' \
  --project=PROJECT_ID --freshness=3d --limit=200 \
  --format=json > vpcsc-raw.json

ログが1日より古いときは --freshness を付けます。付けないと、直近1日分だけが返ります。

ログの閲覧には roles/logging.viewer で足ります。ポリシー拒否ログ(Policy Denied)はこのロールの対象に入っています。

Claude Codeに渡す前に絞る

生のログは1件が長く、同じ違反が何十件も並びます。そのまま渡すと、読ませたい部分が埋もれます。claude -p へのパイプには入力サイズの上限もあるため、ログを渡すときの上限と絞り方も先に見ておくと安全です。

jq で必要なフィールドだけ残し、重複をまとめます。

jq '[.[] | {
    reason: .protoPayload.metadata.violationReason,
    dryRun: .protoPayload.metadata.dryRun,
    service: .protoPayload.serviceName,
    method: .protoPayload.methodName,
    principal: .protoPayload.authenticationInfo.principalEmail,
    callerIp: .protoPayload.requestMetadata.callerIp,
    ingress: .protoPayload.metadata.ingressViolations,
    egress: .protoPayload.metadata.egressViolations
  }] | unique' vpcsc-raw.json > vpcsc-violations.json

残すのは、方向の判定に使う reason と ingress / egress、ルールの operations に書く service と method、identities に書く principal の4種です。callerIp は NO_MATCHING_ACCESS_LEVEL の切り分けに使います。

修正案のYAMLをClaude Codeに書かせる

claude -p で、絞ったログを標準入力から渡します。権限は最小にします。

cat vpcsc-violations.json | claude -p \
  --append-system-prompt "あなたはGoogle Cloudのネットワークセキュリティ担当です。ingress/egressルールのYAMLだけを提案し、gcloudでの設定変更は行いません。" \
  "vpcsc-violations.jsonの違反を、violationReasonで仕分けて。ingress/egressルールで直せるものは ingress-proposal.yaml と egress-proposal.yaml に、公式のYAML形式で書く。ルールで直せないものは理由を一行ずつ出す。methodは推測せず、ログにあるmethodNameだけを使う。" \
  --allowedTools "Read,Write"

claude -p の基本と --bare、権限の組み合わせは別記事にまとめています。ここで押さえるのは、次の3点です。

  • --allowedTools "Read,Write" にしておくと、Bashは自動承認されません。-p で承認が必要な呼び出しは、承認する仕組みがなければ拒否されます
  • ログを読ませるだけならBashは要りません。ログの取得は人かスクリプトが先に済ませておきます
  • 境界を書き換える gcloud access-context-manager perimeters update は、Claudeに実行させない設計にします

最後の点を設定でも固めるなら、settings.json の permissions.deny に Bash(gcloud access-context-manager perimeters *) を入れます。ただしBashのdenyルールは、Claudeが普通に書くコマンド形式を止めるもので、他の呼び出し方まで防ぐ境界ではありません。実効性のある境界はIAM側です。実行するアカウントには roles/logging.viewer と roles/accesscontextmanager.policyReader までを与え、roles/accesscontextmanager.editor を渡さなければ、Claudeが誤って更新コマンドを出しても通りません。両ロールとも、違反分析ツール(violation analyzer)の利用に必要なロールとしてGoogleが挙げています。

権限ルールの書き方と評価順は、denyがallowより先に評価される点から確認しておくと設定を誤りにくくなります。

出てくるYAMLの形を知っておく

提案が公式の形式から外れていないかは、人が見て判断します。形式の基準は、ingress / egressルールのリファレンスです。egressルールの骨格は次のとおりです(公式の例に沿った形です)。

- title: allow-copy-to-external-bucket
  egressFrom:
    identities:
    - serviceAccount:SA_EMAIL
  egressTo:
    operations:
    - serviceName: storage.googleapis.com
      methodSelectors:
      - method: "*"
    resources:
    - projects/4321

先ほどの egressViolations と並べると、対応が分かります。

  • targetResource の projects/4321 は、egressTo.resources に入る
  • ログの principal は、egressFrom.identities に入る
  • serviceName はログの serviceName と一致させる

identities に入れるIDの書式は、Googleの「Supported identities for ingress and egress rules」で確認します。identityType: ANY_SERVICE_ACCOUNT のような広い指定は書式が楽ですが、他組織のサービスアカウントも許してしまいます。リファレンスにも、組織では絞らないと明記されています。

ingressルールなら、ingressFrom に sources(アクセスレベルまたはプロジェクト・VPCネットワーク)と identities、ingressTo に operations と resources を置きます。NO_MATCHING_ACCESS_LEVEL でも、アクセスレベルを足すよりingressルールを推奨している点は、さきほどの表のとおりです。

dry-runで確かめてから強制に移す

提案をそのまま本番の境界に適用するのは避けます。dry-runモードでは、境界に違反する通信が拒否されず、ログに残るだけです。まずdry-run構成にルールを入れます。

gcloud access-context-manager perimeters dry-run update PERIMETER_NAME \
  --policy=POLICY_NAME \
  --set-egress-policies=egress-proposal.yaml

--set-ingress-policies も同じ形で使えます。強制中の構成に直接入れるコマンドは perimeters update ですが、先に使うのは perimeters dry-run update です。

適用後、dry-run構成で拒否される通信は次の条件で見つけます。

gcloud logging read \
  'log_id("cloudaudit.googleapis.com/policy") AND severity="error" AND protoPayload.metadata.dryRun="true"' \
  --project=PROJECT_ID --freshness=1d --format=json

出力が空になるまで、同じ手順(絞り込み、提案、dry-run更新)を繰り返します。残った違反が ingress / egress のどちらなのかを、前節の表で仕分け直します。

dry-runのログで勘違いしやすい点

dry-runのログには、強制構成が先に評価される性質があります。

  • 強制構成で拒否された通信は、dry-runでは再度ログに出ない。強制構成が許し、dry-run構成が拒否したものだけが、dry-run違反として記録される
  • 強制構成で拒否され、dry-run構成なら許される通信はログに出ない。つまり、dry-runに入れたルールで拒否が消えたように見えても、その通信が本番では通ると証明されたわけではない
  • 境界は初期状態で、強制構成をdry-run側が引き継ぐ。引き継いだ状態では、dry-run構成の変更が本番の拒否を変えない

そのため、今まさに本番で止まっている通信を直す場合は、強制構成の側で拒否ログを再現し、ルール追加後に同じ操作を試す、という確認も併用します。

ルールを絞り込む用途にも、dry-runは向いています。Googleも、メソッドを細かく指定したいときは、ルールなしのdry-run境界で呼び出されたメソッドを集め、ルールへ段階的に足す方法を挙げています。呼び出しの連鎖で別のサービスのメソッドが走ることがあるためです(BigQueryがバケットからテーブルを読み込む場合などが例に挙がっています)。最初の method: "*" を、この過程で実際に観測したメソッドへ置き換えます。

強制構成に反映する

dry-runで違反が消えたら、強制します。1つの境界なら dry-run enforce、全境界なら dry-run enforce-all です。

gcloud access-context-manager perimeters dry-run enforce PERIMETER_NAME \
  --policy=POLICY_NAME

強制できるのは、変更を加えたdry-run構成だけです。変更していない構成に対しては実行できません。enforce-all は、変更済みのすべてのdry-run構成に適用されるため、同じ組織の他の人が触った境界まで反映します。他の担当者と調整してから使います。

詰まりやすいところ

  • アクセスレベルにdry-runは無い。アクセスレベルの変更を試すには、変更後の内容で新しいアクセスレベルを作り、境界のdry-run構成に当てます
  • 権限は同じoperationにまとめる。1回のリクエストが複数の権限を要求するとき(例: bigquery.jobs.create と bigquery.tables.create)は、同じoperationにすべて書かないとegressルールが効きません。コンソールで同じリソースに権限を別々に足すと、別operationになります
  • egressの sources を使うなら sourceRestriction が必要。値を書かないと sources は無視され、制限なしと同じ扱いになります
  • ルールが効かない操作がある。ANY_SERVICE_ACCOUNT と ANY_USER_ACCOUNT では許可できない操作として、Container Registryの全操作、署名付きURLでのCloud Storage操作、Cloud LoggingシンクからCloud Storageへのエクスポートなどが一覧にあります。ここに当たる違反は、YAMLを直しても止まります
  • 複数のルールはORで評価される。どれか1つに合致すれば通ります。広い許可を1つ入れると、他のルールの絞り込みが意味を失います

違反分析ツールとの使い分け

Google側にも、拒否に対する修正案を出す機能があります。違反分析ツールの自動修正提案(Preview)は、拒否イベントに対してingress・egressルールやアクセスレベルの案を示し、Access Context Manager編集者のロールがあれば、その画面から適用までできます。

提案の生成は違反分析ツールだけでも済む場面があります。Claude Codeが向くのは、次のような場合です。

  • 数十件の違反を、境界ごと・サービスごとにまとめて、重複のない1つのYAMLにしたい
  • 修正案をTerraformやGit管理のYAMLに合わせた形で、差分として出したい
  • ルールで直せない違反(VPCアクセス可能サービスの話など)を仕分けたい

どちらの案も、適用前にdry-runを通す点は同じです。サービス境界の変更は、データを守る設定そのものの変更です。Claudeの提案は、人がレビューする下書きの位置づけに保ちます。

KMS側の関連では、ClaudeのCMEKをGoogle Cloud KMSで設定する手順にも、鍵のプロジェクトがサービス境界の内側にある場合の注意があります。AWS側で同種の「ログから原因を絞り、設定案を作らせる」流れは、NAT Gatewayの料金調査で扱っています。

参照した一次ソース

この記事の記述は、次の一次ソースと照合しています。更新時に参照先が変わった場合は一覧も更新します。

  1. cloud.google.com/vpc-service-controls/docs/troubleshooting
  2. cloud.google.com/vpc-service-controls/docs/ingress-egress-rules
  3. cloud.google.com/vpc-service-controls/docs/configuring-ingress-egress-polic…
  4. cloud.google.com/vpc-service-controls/docs/dry-run-mode
  5. cloud.google.com/vpc-service-controls/docs/manage-dry-run-configurations
  6. cloud.google.com/vpc-service-controls/docs/audit-logging
  7. cloud.google.com/vpc-service-controls/docs/violation-analyzer
  8. code.claude.com/docs/en/headless
  9. code.claude.com/docs/en/permissions
この記事を共有:XはてブLinkedIn