これの続きみたいなものなんだけど、ドメインの設定後にもともとやりたかった IAP が設定できたのであらためて書いてみる。
やりたいこと
- IAP で cloud run にアクセスできる人を限定したい
- 自分のドメインを設定したい
参考記事
terraform 公式 docs のほか、これらのブログにめっちゃ助けられた
ポイント
IAP の service identity
terraform 経由で IAP の設定をすると、↓にある通り IAP 用の "service identity" ってやつが作れらないので、それも別途作成する必要がある
terraform はこう
data "google_project" "project" {} resource "google_project_service_identity" "iap" { provider = google-beta project = data.google_project.project.project_id service = "iap.googleapis.com" }
Cloud Domain, OAuth consent screen, OAuth client は手動でつくる
タイトルの通りで、 API 非対応のリソースは手でつくる必要がある。terraform には
- ドメイン名
- oauth client id
- oauth client secret
を渡した
つくるリソースは Cloud Run の integrations からわかる
load balancer まわりのリソースは作るものが多くてわかりにくいけど、実は Cloud Run の integrations タブから Custom domains - Google Cloud Load Balancing を選ぶと、リソースの一覧がみれる
OAuth client の redirect URI/Javascript Origin
...ってのがどこにも書いてなかったんだけど、たぶんこう。
- javascript origin:
https://iap.https://iap.googleapis.com
- redirect URI:
https://iap.googleapis.com/v1/oauth/clientIds/<YOUR_OAUTH_CLIENT_ID>:handleRedirect
どうやって見つけたかというと、両方に最初自分のアプリケーションの URL を設定していたんだけど、アクセスすると 400 redirect_uri_mismatch
が出て、そのエラーメッセージに "redirect URI はこれだよ" って書いてあった。
コード
Cloud Run
Cloud Run まわりでは、 Cloud Run そのものと、invoke する権限を Cloud Run 用の service account と allUsers
(つまり public に公開する) に与える。ingress は internal (i.e. VPC内) か load balancer からに限定することで、 IAP をバイパスされないようにしていて、 allUsers
といっても IAP で許可された人のみに限定してる。
resource "google_cloud_run_v2_service" "app" { name = "app" location = "asia-northeast1" ingress = "INGRESS_TRAFFIC_INTERNAL_LOAD_BALANCER" template { containers { image = var.app_image_url resources { limits = { cpu = "2" memory = "1024Mi" } } # TODO: set environment variables } service_account = var.app_service_account_email } } resource "google_cloud_run_v2_service_iam_member" "app_member" { name = google_cloud_run_v2_service.app.name role = "roles/run.invoker" member = "serviceAccount:${var.app_service_account_email}" } resource "google_cloud_run_v2_service_iam_member" "app_access_unauthenticated" { name = google_cloud_run_v2_service.app.name role = "roles/run.invoker" member = "allUsers" }
Cloud DNS
DNS ではまず先に Cloud Domains でドメインをとっておく。すると、ドメインと managed zone が作られるので、
- ドメイン名
app_domain_name
- zone 名
app_dns_managed_zone_name
をそれぞれ terraform に渡す。
その後、 terraform では load balancer 用に IP を払い出すのでそれを A レコードにセットして、SSL 証明書もつくっておく。
resource "google_compute_global_address" "app_load_balancer_ip" { name = "app-load-balancer-ip" ip_version = "IPV4" # is default } resource "google_dns_record_set" "app_a_record" { managed_zone = var.app_dns_managed_zone_name name = "${var.app_domain_name}." type = "A" ttl = 300 rrdatas = [google_compute_global_address.app_load_balancer_ip.address] } resource "google_compute_managed_ssl_certificate" "app" { name = "app-ssl-certificate" managed { domains = [var.app_domain_name] } }
load balancer
ここはリソースをいっぱいつくるところで、それぞれの関係性はこんなイメージ↓
IAP の設定は backend service に対して実施して、ここで oauth client の id + secret を渡す。
resource "google_compute_url_map" "app" { name = "app-url-map" default_service = google_compute_backend_service.app.id host_rule { hosts = [var.app_domain_name] path_matcher = "allpaths" } path_matcher { name = "allpaths" # NOTE: default service is required at the this level too default_service = google_compute_backend_service.app.id } } resource "google_compute_target_https_proxy" "app" { name = "app-https-proxy" ssl_certificates = [google_compute_managed_ssl_certificate.app.id] url_map = google_compute_url_map.app.id } resource "google_compute_global_forwarding_rule" "app" { name = "app-forwarding-rule" load_balancing_scheme = "EXTERNAL" ip_address = google_compute_global_address.app_load_balancer_ip.address ip_protocol = "TCP" port_range = "443-443" target = google_compute_target_https_proxy.app.id } resource "google_compute_backend_service" "app" { name = "app-backend-service" backend { group = google_compute_region_network_endpoint_group.app.id } iap { oauth2_client_id = var.app_oauth_client_id oauth2_client_secret = var.app_oauth_client_secret } }
IAP の許可設定
許可するメールアドレスを locals で定義して、それらに対して IAP-secured Web App User
のロールを付与する。メアドのリストは多分 terraform 直書きしない方がいいと思うけど、まあ一旦..
locals { app_user_emails = toset([ "user-1@example.com", "user-2@example.com", ]) } resource "google_iap_web_backend_service_iam_member" "app_users" { for_each = { for email in local.app_user_emails : email => email } web_backend_service = google_compute_backend_service.app.name role = "roles/iap.httpsResourceAccessor" member = "user:${each.value}" }
IAP の service identity
最初に書いた通り、 service identity をつくる。
data "google_project" "project" {} # see: https://cloud.google.com/iap/docs/enabling-cloud-run#troubleshooting_errors resource "google_project_service_identity" "iap" { provider = google-beta project = data.google_project.project.project_id service = "iap.googleapis.com" }
動作確認
以上を設定して、
- 自分のアプリにアクセスして
- OAuth 画面が出てくる
- Google login してアプリ画面が表示される
ってなれば OK