こんにちは。

先日Cloud FunctionsをCloud Buildから(重要)デプロイしようとしたら、次のような見慣れたログが流れました。

ERROR: (gcloud.beta.functions.deploy) HTTPError 403: <?xml version='1.0' encoding='UTF-8'?><Error>
<Code>SecurityPolicyViolated</Code><Message>Request violates VPC Service Controls.</Message>
<Details>Request is prohibited by organization's policy. vpcServiceControlsUniqueIdentifier: 
WEXRqkxuMhUmx_iswqGNFwDCLaeWUvF-RndpE3jG5bmYABVi-eUrLA</Details></Error>

現在VPCSCではCloud Storage APIのみを制限していて、Access Context Managerで次のORルールAccess Levelが設定されています。

- ipSubnetworks:
  - <自宅や社用VPNのIPアドレス>
- members:
  - serviceAccount:<YOUR_PROJECT_NUMBER>@cloudbuild.gserviceaccount.com

突然ですが、問題です。
Access LevelでCloud Buildサービスアカウントを許可しているにも関わらず、VPCSCがCloud Functionsのデプロイを拒否してしまうのはなぜでしょうか?


正解を回りくどく書くと
「gcf-admin-robotサービスアカウントがサービス境界外に対してCloud Storage APIを叩くから」です。

このことは、Loggingで該当のエラーを見にいくとわかります。
一部を下に抜粋します。

authenticationInfo: {
  principalEmail: "service-<YOUR_PROJECT_NUMBER>@gcf-admin-robot.iam.gserviceaccount.com"
}
...(省略)
ingressViolations: [
  0: {
    source: "projects/985686202211"
    targetResource: "projects/397608059693/buckets/gcf-upload-asia-northeast1-44ded550-83b9-412b-b5b9-  4972948cc1f1/objects/9a55b4b5-5a64-480c-ba13-7be880b20b5c.zip"
    servicePerimeter: "accessPolicies/***/servicePerimeters/restrict_contents_access"
  }
]
...
labels: {
  service: "storage.googleapis.com"
  method: "google.storage.objects.create"
  project_id: "<YOUR_PROJECT_ID>"
}

どうやらCloud Functionsをデプロイする際には、gcf-admin-robotサービスアカウントがサービス境界外であるGoogleが管理しているCloud Storageバケットにオブジェクトを作成しようとしていて、そのAPIリクエストがVPCSCによって拒否されるようです。
該当のサービスアカウントをAccess Levelに加えることでサービス境界外に対してCloud Storage APIがリクエストできるようになり、無事にデプロイができました。

- ipSubnetworks:
  - <自宅や社用VPNのIPアドレス>
- members:
  - serviceAccount:<YOUR_PROJECT_NUMBER>@cloudbuild.gserviceaccount.com
  - serviceAccount:service-<YOUR_PROJECT_NUMBER>@gcf-admin-robot.iam.gserviceaccount.com

ところで、やりたいことはできたのですが疑問が残りました。
gcf-admin-robotがサービス境界外に対してgoogle.storage.objects.createを呼び出すのは、Cloud Buildかrデプロイをする時だけでした。

対象のバケットは projects/397608059693/buckets/gcf-upload-asia-northeast1-44ded550-83b9-412b-b5b9-4972948cc1f1で表されるリソースなのですが、なぜここにソースファイルを作成する必要があるのかはわかりませんでした。

言い換えると、なぜローカルPCとCloud Buildでソースファイルをアップロードするバケットが異なるのか。
特に知らなくても問題無さそうなのですが、ちょっとした振る舞いの違いがVPCSCでサービス境界を設定していると思わぬエラーを引き起こす事例でした。