GCPでVPCの利用とファイアウォール・ルートの管理を分離できる共有VPCを試してみた。
試してみたこと
- 共有VPC有効化とプロジェクトの関連付け
- 共有対象のVPC・サブネットの作成
- サブネットの利用権限をサービスプロジェクトのユーザーにアサイン
注意事項
- 共有VPCは組織内プロジェクトでのみ使える
先にCloud Identityに組織を作ってプロジェクトを紐づけておく(記事はこちら) - Terraformによる権限アサインでは既存権限の消失に注意
構成手順
ホストプロジェクトとサービスプロジェクトの定義
- ホストプロジェクト…共有VPCを配置するプロジェクト
resource "google_compute_shared_vpc_host_project" "vpc_host_project" {
project = var.host_project_id
}
- サービスプロジェクト…サブネットを共有VPCから借りて使うプロジェクト
resource "google_compute_shared_vpc_service_project" "vpc_service_project_1" {
host_project = google_compute_shared_vpc_host_project.vpc_host_project.id
service_project = var.service_project_id
}
403 forbiddenと権限の追加
共有VPCを有効化するには"compute.organizations.enableXpnHost"権限が組織のスコープで必要となる。共有VPCを含むロール"roles/compute.xpnAdmin"をホストプロジェクトの管理者に割り当ててやればよいが、Terraformでの割り当てには少々コツが必要となる。
google_compute_shared_vpc_host_project.vpc_host_project: Creating...
╷
│ Error: Error enabling Shared VPC Host "cloudibledotjp": googleapi: Error 403: Required 'compute.organizations.enableXpnHost' permission for 'projects/cloudibledotjp', forbidden
良い例
google_organization_iam_bindingを使って権限アサインを追加する。
resource "google_organization_iam_binding" "cloudibledotjp_iam_binding" {
org_id = var.organization_id
role = "roles/compute.xpnAdmin"
members = [
"user:${var.adminuser}"
]
}
悪い例
google_organization_iam_policyで権限アサインを行う場合、既存の権限セットがpolicy_dataの内容でまるっとリプレースされてしまうので注意が必要だ。以下の例では既存のIAMバインドはすべてクリアされ、"user:${var.adminuser}"へに対する"roles/compute.xpnAdmin"の割り当てだけの状態となる。そうなると特権管理者に復旧を依頼しなければならなず、帰り際などにうっかり実行してしまうと絶望を味わえる。
resource "google_organization_iam_policy" "cloudibledotjp" {
org_id = var.organization_id
policy_data = data.google_iam_policy.xpn.policy_data
}
data "google_iam_policy" "xpn" {
binding {
role = "roles/compute.xpnAdmin"
members = [
"user:${var.adminuser}"
]
}
}
サービスプロジェクトが存在しても404 not found
サービスプロジェクトでCompute APIが有効になっていない場合、プロジェクトが見つからないというエラーでデプロイに失敗する。サービスプロジェクトを新規作成したばかりのような状況ではAPIが無効となっており、エラーメッセージの内容からは原因に気づきにくいので注意。
│ Error: googleapi: Error 404: The resource 'projects/superb-webapp' was not found, notFound
│
│ with google_compute_shared_vpc_service_project.vpc_service_project_1,
│ on vpc.tf line 8, in resource "google_compute_shared_vpc_service_project" "vpc_service_project_1":
│ 8: resource "google_compute_shared_vpc_service_project" "vpc_service_project_1" {
Compute API有効化の定義はこんな感じになる。
resource "google_project_service" "service_project_1_required_api_services" {
project = var.service_project_id
disable_dependent_services = true
for_each = toset([
"compute.googleapis.com"
])
service = each.value
}
これでホストプロジェクトにあるVPCを共有できる状態となった。
共有VPCとサブネットの定義
続いて共有するVPCとサブネットを定義する。
VPC
resource "google_compute_network" "shared_vpc" {
project = var.host_project_id
name = "shared-vpc"
auto_create_subnetworks = false
mtu = 1460
}
サブネット
resource "google_compute_subnetwork" "app_subnet" {
name = "app-subnetwork"
ip_cidr_range = "10.2.0.0/16"
region = "asia-northeast1"
network = google_compute_network.shared_vpc.id
}
サービスプロジェクトのユーザーに権限付与
ホストプロジェクト上のサブネットをサービスプロジェクトから使えるように必要な権限をサービスプロジェクトのユーザーにアサインする。
resource "google_compute_subnetwork_iam_binding" "superb_webappp_iam_binding" {
project = var.service_project_id
region = google_compute_subnetwork.app_subnet.region
subnetwork = google_compute_subnetwork.app_subnet.id
role = "roles/compute.networkUser"
members = [
"user:${var.testuser}"
]
}
共有VPCの完成
ここまできたらterraform applyを実行し、共有VPCを作成する。
サービスプロジェクトからtestuserでVMを作っていく。ネットワーク設定のところにホストプロジェクトのサブネットが共有ネットワークのプルダウンリストに現れる。
サービスプロジェクト側にはもちろんVPCもサブネットも存在しない。
dave@cloudshell:~ (superb-webapp)$ gcloud compute networks list
Listed 0 items.
dave@cloudshell:~ (superb-webapp)$ gcloud compute networks subnets list
Listed 0 items.
今回の構成ではサービスプロジェクトのtestuserは新規VPCの作成ができず、VM作成時にホストプロジェクトに用意される共有VPCからサブネットを利用する形式としてある。
サービスプロジェクトに作成したVMのネットワークインターフェイス。
あとがき
共有VPCによりデータやアプリチームのVMデプロイに対する自由度を維持しながら、FWルールやルートはわかるメンバーがしっかり管理することで設定ミスによる通信断やセキュリティインシデントといったリスクの回避を実現できる。データの分析方法に集中するあまりVMが世界に公開されていたなんていう事故を減らすことができるかもしれない。