GCP IAP protected Cloud Run Application by Terraform
2023-01-18
GCPCloudRunTerraformGoogle Cloud(GCP) has Identity-Aware Proxy that uses ID and contexts to protect applications and VMs from unexpected access.
Thanks to IAP, it's a way easy to protect an application running on Cloud Run by requiring Google login within the organization, for example.
Preparation
Before writing Terraform codes, IAP requires one-shot operation in Cloud console.
IAP API can be enabled by this terraform.
resource "google_project_service" "project" {
project = var.gcp_project
service = "iamcredentials.googleapis.com"
}
Then, you can find OAuth consent screen
configuration in cloud console under APIs & Services
group.
If you'd like to create an application that requires Google sign-in within your organization, make User Type
internal instead of external.
After creating OAuth consent screen, its brand name is needed in after step.
It can be fetched by gcloud
command.
$ gcloud iap oauth-brands list
---
applicationTitle: project-xxx
name: projects/xxx/brands/yyy
orgInternalOnly: true
supportEmail: hoge@example.com
Terraform
In Terraform, variables should be declared in variables.tf but use locals
here instead for simplicity.
locals {
gcp_project = "awesome-project"
region = "asia-northeast1"
app_name = "myapp"
app_image_name = "us-docker.pkg.dev/cloudrun/container/hello"
user_emails = ["hoge@example.com"]
iap_brand_name = "projects/xxx/brands/yyy"
}
Next is to create google_iap_client
that provision Identity-Aware Proxy.
# Enable API
resource "google_project_service" "iap" {
project = local.gcp_project
service = "iap.googleapis.com"
}
resource "google_iap_client" "project_client" {
display_name = "IAP client"
brand = local.iap_brand_name
}
Then, define a Cloud Run application and related resources in Terraform.
- google_service_account | Resources | hashicorp/google | Terraform Registry
- google_cloud_run_service | Resources | hashicorp/google | Terraform Registry
- google_iap_client | Resources | hashicorp/google | Terraform Registry
# service account for the cloud run application
resource "google_service_account" "app" {
account_id = "app"
display_name = "ServiceAccount for CloudRun App"
}
resource "google_service_account_iam_member" "app" {
service_account_id = google_service_account.app.name
role = "roles/run.serviceAgent"
member = "serviceAccount:${google_service_account.app.email}"
}
resource "google_service_account_iam_member" "app" {
service_account_id = google_service_account.app.name
role = "roles/iam.serviceAccountUser"
member = "serviceAccount:${google_service_account.app.email}"
}
In addition, google_iap_brand resource exists but it seems creating IAP brand has been completed by creating OAuth consent screen.
The below snippet is to provision a Cloud Run application with related network stuff.
resource "google_cloud_run_service" "app" {
provider = google-beta
name = local.app_name
location = local.region
autogenerate_revision_name = true
metadata {
annotations = {
"run.googleapis.com/ingress" = "internal-and-cloud-load-balancing"
}
}
template {
spec {
service_account_name = google_service_account.app.email
containers {
image = local.app_image_name
}
}
}
}
# provision related network for Cloud Run
resource "google_compute_region_network_endpoint_group" "app" {
name = "${local.app_name}-neg"
network_endpoint_type = "SERVERLESS"
region = local.region
cloud_run {
service = google_cloud_run.app.name
}
}
resource "google_compute_backend_service" "app" {
depends_on = [
google_iap_client.project_client
]
name = "${local.app_name}-service"
protocol = "HTTP"
port_name = "http"
timeout_sec = 30
backend {
group = google_compute_region_network_endpoint_group.app.id
}
enable_cdn = false
# enable IAP protection on Cloud Run
iap {
oauth2_client_id = google_iap_client.project_client.client_id
oauth2_client_secret = google_iap_client.project_client.secret
}
}
# allow access to restricted users
resource "google_iap_web_backend_service_iam_binding" "cloud_run_iap_binding" {
web_backend_service = google_compute_backend_service.app.name
role = "roles/iap.httpsResourceAccessor"
members = [for email in local.user_emails : "user:${email}"]
}
data "google_iam_policy" "limited_access" {
binding {
role = "roles/run.invoker"
# > IAM must be configured to grant allUsers
# https://cloud.google.com/iap/docs/enabling-cloud-run
members = [
"allUsers",
]
}
}
resource "google_cloud_run_service_iam_policy" "limited_access" {
location = google_cloud_run_service.app.location
project = google_cloud_run_service.app.project
service = google_cloud_run_service.app.name
policy_data = data.google_iam_policy.limited_access.policy_data
}
As a result, these Terraform codes create a Cloud Run application being protected by IAP which requires Google sign-in within the Google organization. In addition, for disclaimer, these Terraform snippets are pulled out from my application codes so that these should be essentially true but not guaranteed in case copy-paste-ed these code.