GCPのShared VPC構成ではホストプロジェクトにあるサブネットに対してサービスプロジェクトのユーザーがVMなどを設置する権限を払い出すことができます。
このときに注意しないといけないのは、IAMによって権限を払い出す対象はプロジェクトではなくユーザーであるということです。
そのため、複数のプロジェクトでVMを設置する権限を持っているユーザーによって予期しない形でVMが建てられることがあります。
例えば、以下の構成を考えます。
resource "google_compute_shared_vpc_service_project" "service-project-a" {
host_project = var.host-project
service_project = "service-project-a"
}
resource "google_compute_subnetwork" "subnet-a" {
name = "subnet-a"
...
}
data "google_iam_policy" "policy-a" {
binding {
role = "roles/compute.networkUser"
members = ["taro@example.com"]
}
}
resource "google_compute_subnetwork_iam_policy" "policy-a" {
project = var.host-project
subnetwork = google_compute_subnetwork.subnet-a.name
policy_data = data.google_iam_policy.policy-a.policy_data
}
resource "google_compute_shared_vpc_service_project" "service-project-b" {
host_project = var.host-project
service_project = "service-project-b"
}
resource "google_compute_subnetwork" "subnet-b" {
name = "subnet-b"
...
}
data "google_iam_policy" "policy-b" {
binding {
role = "roles/compute.networkUser"
members = ["taro@example.com"]
}
}
resource "google_compute_subnetwork_iam_policy" "policy-b" {
project = var.host-project
subnetwork = google_compute_subnetwork.subnet-b.name
policy_data = data.google_iam_policy.policy-b.policy_data
}
この時、service-project-aはsubnet-a、service-project-bはsubnet-bのみを利用することを意図しているとします。
しかし、両方のサブネットに対して権限を渡している taro@example.com
はservice-project-aのVMをsubnet-bに建てることができます。
サブネット毎に権限を払い出すユーザーの重複がないようにすればこのような自体を防ぐことはできますが、複数プロジェクトにアサインされているメンバーのことを考えると現実的に厳しいケースもあります。
解決策
GCPのOrganization Policyを使うことでこの問題を解決することができます。
Organization Policyは特定のOrganizationやFolderやProjectに対する操作を制限することができる機能です。
aws Organizationを使ったことがある人ならば、サービスコントロールポリシー(SCP)に近しいものという説明がわかりやすいでしょう。
以下のコードを反映するとproject-aが使用できるサブネットワークがsubnet-aに限定されます。また、同様にproject-bとsubnet-bの間でも同様の設定がされます。
resource "google_project_organization_policy" "restrict-shared-vpc-subnetworks-a" {
project = "service-project-a"
constraint = "compute.restrictSharedVpcSubnetworks"
list_policy {
inherit_from_parent = false
allow {
values = [
"projects/${var.host-project}/regions/${google_compute_subnetwork.subnet-a.region}/subnetworks/${google_compute_subnetwork.subnet-a.name}"
]
}
}
}
resource "google_project_organization_policy" "restrict-shared-vpc-subnetworks-b" {
project = "service-project-b"
constraint = "compute.restrictSharedVpcSubnetworks"
list_policy {
inherit_from_parent = false
allow {
values = [
"projects/${var.host-project}/regions/${google_compute_subnetwork.subnet-b.region}/subnetworks/${google_compute_subnetwork.subnet-b.name}"
]
}
}
}
なお、この時にTerraformに対して割り当てる権限はホストプロジェクトのOwner権限だけでは不十分であり、Organizationに対するroles/orgpolicy.policyAdminのRoleを割り当てる必要があります。
https://cloud.google.com/iam/docs/understanding-roles#organization-policy-roles