TerraformとIAPで実現する!Cloud Runにメール認証付きのセキュアなアプリをデプロイする方法 🚀
自己紹介
はじめまして、sousquared (そうそう) と言います!
普段は、機械学習エンジニアとして働いております。
興味領域は「AI x クリエイティブ」領域です。
X: @sou_squared
Instagram: @sousquared
Github: sousquared
はじめに
この記事では、Terraform を使用して Google Cloud Run にサンプルコンテナの hello アプリケーションをデプロイし、Identity-Aware Proxy(IAP) を使ってメールアドレスでアクセス制限を行う方法をご紹介します。
以下の記事では、IP制限したClour Runサービスをterraformで構築してみましたが、今回はIAPを使ったセキュアなアクセス制限に挑戦しています。
この記事を通じて、Cloud Run と Terraform を組み合わせたインフラストラクチャのコード化や、IAP を用いたアクセス制御の基本的な手順とポイントを解説します。
全体のコードはこちらに置いています:
※本番環境で使用する場合は、セキュリティに十分注意し、適切な設定を行ってください。
TL;DR
- 目的: Terraform を使って Cloud Run にサンプルの hello アプリケーションをデプロイし、IAP を用いて特定のユーザーのみがアクセスできるようにします
-
手順の概要:
- Terraform ファイルを準備し、サンプルコンテナの hello アプリケーションを指定
- IAP でアクセスを許可するユーザーのメールアドレスを設定
- Terraform コマンド (
terraform init
,terraform plan
,terraform apply
) を実行してリソースを作成 - 作成された URL にアクセスしてデプロイを確認
-
注意点:
- SSL 証明書のプロビジョニングには時間がかかる場合があります(通常 15〜30 分程度)
- 利用後は
terraform destroy
でリソースをクリーンアップします
この記事を読むことで、Terraform を使った Cloud Run サービスのデプロイ方法と、IAP を用いたアクセス制御の設定方法を理解できます。
構成図
構成図を書くと以下のような形になるかと思います。
Terraform の設定
基本的には以下の記事の内容を参考に構築しています。
変更点として、サンプルコンテナの hello アプリケーションを使用することで、より簡単に Terraform を動かせるようにしています。また、IAP を使用してメールアドレスでアクセス制限を行う設定にしています。
Terraform を使って GCP に HTTPS ロードバランサを設置し、IAP を有効化する方法は以下の記事が参考になりました。
サンプルコンテナの hello アプリケーションを Cloud Run に設定する
Cloud Run サービスにデプロイするアプリケーションとして、サンプルコンテナの hello アプリケーションを指定しました。
image = "us-docker.pkg.dev/cloudrun/container/hello:latest"
また、terraform destroy
時に削除できるようにするために、deletion_protection = false
にしています。
# Cloud Run サービス
resource "google_cloud_run_v2_service" "hello_cloud_run" {
name = "hello"
location = var.region
description = "cloud run service"
ingress = "INGRESS_TRAFFIC_INTERNAL_LOAD_BALANCER" # 内部ロードバランサーからのトラフィックのみを許可
template {
containers {
name = "hello"
image = "us-docker.pkg.dev/cloudrun/container/hello:latest"
resources {
cpu_idle = false
}
}
scaling {
min_instance_count = 0
max_instance_count = 1
}
}
traffic {
type = "TRAFFIC_TARGET_ALLOCATION_TYPE_LATEST" # 最新のリビジョンにトラフィックを送信
percent = 100
}
deletion_protection = false # 削除保護を無効化 (terraform destroy 時に削除可能)
}
IAP でアクセスを許可するユーザーの設定
変更が必要な箇所としては、IAP でアクセスを許可するユーザーのメールアドレスを設定することです。
variable "iap_members" {
description = "IAP でアクセスを許可するユーザーのメールアドレスを設定"
type = list(string)
default = [
"user:example@example.com", # 許可するユーザーのメールアドレス
# 複数のユーザーを追加可能
]
}
variables.tf
ファイルの設定
variables.tf
ファイルで以下の変数を設定する必要があります。
hclvariable "project" {
description = "The ID of the project in which resources will be managed."
type = string
}
variable "project_number" {
description = "The number of the project in which resources will be managed."
type = string
}
variable "region" {
description = "The region in which resources will be created."
type = string
default = "asia-northeast1"
}
variable "domain" {
description = "使用するドメイン名を設定"
type = string
}
variable "dns_managed_zone" {
description = "Cloud DNS のゾーン名を設定"
type = string
}
variable "iap_members" {
description = "IAP でアクセスを許可するユーザーのメールアドレスを設定"
type = list(string)
default = [
"user:example@example.com",
# 必要に応じて他のメンバーを追加
]
}
変数の設定方法
これらの変数は以下の方法で設定できます。
-
variables.tf
のデフォルト値を直接編集直接ファイルを編集して変数を設定します。
-
terraform.tfvars
ファイルを作成して値を設定infra
ディレクトリ内にterraform.tfvars
ファイルを作成し、以下のように設定します。# terraform.tfvars project = "your-project-id" # プロジェクト ID project_number = "your-project-number" # プロジェクト番号 region = "asia-northeast1" # リージョン domain = "your-domain.com" # 使用するドメイン名 dns_managed_zone = "your-dns-zone" # Cloud DNS のゾーン名 iap_members = [ "user:your-email@example.com", # 必要に応じて他のメンバーを追加 ]
-
terraform apply
実行時に対話的に入力特に設定を指定しなかった場合、
terraform apply
時にプロンプトで入力を求められます。
Terraform 全体
Terraform ファイル全体をここに載せておきます。
Terraform ファイル全体
provider "google" {
project = var.project
region = var.region
}
variable "project" {
description = "The ID of the project in which resources will be managed."
type = string
}
variable "project_number" {
description = "The number of the project in which resources will be managed."
type = string
}
variable "region" {
description = "The region in which resources will be created."
type = string
default = "asia-northeast1"
}
variable "domain" {
description = "使用するドメイン名を設定"
type = string
}
variable "dns_managed_zone" {
description = "Cloud DNS のゾーン名を設定"
type = string
}
variable "iap_members" {
description = "IAP でアクセスを許可するユーザーのメールアドレスを設定"
type = list(string)
default = [
"user:example@example.com",
# 必要に応じて他のメンバーを追加
]
}
output "certificate_status" {
value = data.google_compute_ssl_certificate.hello_cert.managed.status
description = "SSL 証明書のステータス"
}
output "domain" {
value = var.domain
description = "アクセスするドメイン名"
}
output "lb_ip" {
value = google_compute_global_address.hello_lb_ip.address
description = "ロードバランサーの静的 IP アドレス"
}
output "url" {
value = "https://${var.domain}"
description = "アクセスする URL"
}
# load balancer 用の静的 IP
resource "google_compute_global_address" "hello_lb_ip" {
name = "hello-lb-ip"
description = "load balancer の静的 IP"
address_type = "EXTERNAL"
ip_version = "IPV4"
project = var.project
}
# Cloud Run サービス
resource "google_cloud_run_v2_service" "hello_cloud_run" {
name = "hello"
location = var.region
description = "cloud run service"
ingress = "INGRESS_TRAFFIC_INTERNAL_LOAD_BALANCER" # 内部ロードバランサーからのトラフィックのみを許可
template {
containers {
name = "hello"
image = "us-docker.pkg.dev/cloudrun/container/hello:latest"
resources {
cpu_idle = false
}
}
scaling {
min_instance_count = 0
max_instance_count = 1
}
}
traffic {
type = "TRAFFIC_TARGET_ALLOCATION_TYPE_LATEST"
percent = 100
}
deletion_protection = false # 削除保護を無効化
}
# Load Balancer の serverless NEG
resource "google_compute_region_network_endpoint_group" "hello_neg" {
name = "hello-neg"
network_endpoint_type = "SERVERLESS"
region = var.region
cloud_run {
service = google_cloud_run_v2_service.hello_cloud_run.name
}
}
# IAP クライアントの設定
resource "google_iap_client" "project_client" {
display_name = "Hello Cloud Run Client"
brand = "projects/${var.project}/brands/${var.project_number}"
}
# IAP の IAM バインディング
resource "google_iap_web_backend_service_iam_binding" "binding" {
project = var.project
web_backend_service = google_compute_backend_service.hello_backend_service.name
role = "roles/iap.httpsResourceAccessor"
members = var.iap_members
}
# バックエンドサービスに IAP を有効化
resource "google_compute_backend_service" "hello_backend_service" {
name = "hello-backend-service"
protocol = "HTTP"
port_name = "http"
timeout_sec = 30
load_balancing_scheme = "EXTERNAL_MANAGED"
backend {
group = google_compute_region_network_endpoint_group.hello_neg.self_link
}
iap {
oauth2_client_id = google_iap_client.project_client.client_id
oauth2_client_secret = google_iap_client.project_client.secret
enabled = true
}
}
# IAP サービスアカウントの作成
resource "google_project_service_identity" "iap_sa" {
provider = google-beta
project = var.project
service = "iap.googleapis.com"
}
# IAP サービスアカウントに Cloud Run へのアクセス権を付与
resource "google_cloud_run_v2_service_iam_member" "iap_invoker" {
name = google_cloud_run_v2_service.hello_cloud_run.name
location = var.region
project = var.project
role = "roles/run.invoker"
member = google_project_service_identity.iap_sa.member
depends_on = [
google_project_service_identity.iap_sa
]
}
resource "google_compute_url_map" "hello_url_map" {
name = "hello-lb"
description = "load balancer 用の url map"
default_service = google_compute_backend_service.hello_backend_service.id
path_matcher {
name = "hello-apps"
default_service = google_compute_backend_service.hello_backend_service.id
}
}
# HTTPS プロキシの設定
resource "google_compute_target_https_proxy" "hello_target_https_proxy" {
name = "hello-target-https-proxy"
url_map = google_compute_url_map.hello_url_map.id
ssl_certificates = [google_compute_managed_ssl_certificate.hello_ssl_cert.id]
}
# マネージド SSL 証明書の設定
resource "google_compute_managed_ssl_certificate" "hello_ssl_cert" {
name = "hello-ssl-cert"
managed {
domains = [var.domain]
}
}
# フロントエンドの設定 (HTTPS)
resource "google_compute_global_forwarding_rule" "hello_forwarding_rule_https" {
name = "hello-forwarding-rule-https"
description = "load balancer の forwarding rule (HTTPS)"
load_balancing_scheme = "EXTERNAL_MANAGED"
target = google_compute_target_https_proxy.hello_target_https_proxy.id
ip_address = google_compute_global_address.hello_lb_ip.address
ip_protocol = "TCP"
port_range = "443"
}
# DNS レコードの設定
resource "google_dns_record_set" "hello" {
name = "${var.domain}."
type = "A"
ttl = 300
managed_zone = var.dns_managed_zone
rrdatas = [google_compute_global_address.hello_lb_ip.address]
}
# SSL 証明書の状態を取得するためのデータソース
data "google_compute_ssl_certificate" "hello_cert" {
name = google_compute_managed_ssl_certificate.hello_ssl_cert.name
}
Terraform の実行
1. GCP への認証
まず、GCP への認証を行います。
gcloud auth application-default login
2. Terraform の初期化
infra
ディレクトリに移動し、Terraform を初期化します。
cd infra
terraform init
3. Terraform のプラン作成と適用
terraform plan
や terraform apply
で変数の入力を求められるので、適切に入力します。
terraform plan
実行例:
$ terraform plan
var.project
The ID of the project in which resources will be managed.
Enter a value:
続いて、リソースを作成します。
terraform apply
注意点
-
SSL 証明書のプロビジョニングと検証には時間がかかる場合があります(通常 15〜30 分程度)
-
DNS の伝播にも時間がかかる場合があります(通常 5〜30 分程度)
-
証明書のステータスは以下のコマンドで確認できます
terraform output certificate_status
アクセス
作成された URL にアクセスします。
$ terraform output
certificate_status = {
"create_time" = "2021-09-01T00:00:00.000-07:00"
"expire_time" = "2021-12-01T00:00:00.000-07:00"
"id" = "your-certificate-id"
}
domain = "your-domain.com"
lb_ip = "xxx.xxx.xxx.xxx"
url = "https://your-domain.com"
ブラウザで https://your-domain.com
にアクセスします。
Google のサインイン画面
初回アクセス時に Google のサインイン画面が表示されます。許可されたメールアドレスでサインインしてください。
ログイン画面
ログイン画面で「次へ」をクリックします。
アプリケーションの表示
以下のような画面が表示されたら成功です!
クリーンアップ
Terraform で作成したリソースをすべて削除して、環境をクリーンな状態に戻すには、以下のコマンドを実行します。
terraform destroy
まとめ
この記事では、Terraform を使用して Google Cloud Run にサンプルの hello アプリケーションをデプロイし、IAP を用いて特定のユーザーのみがアクセスできるようにする方法を解説しました。
これにより、インフラストラクチャのコード化とセキュアなアクセス制御を実現できます。
考慮すべき点
-
コスト: 作成したリソースにはコストが発生します。使い終わったら必ず
terraform destroy
で削除しましょう。 - セキュリティ: IAP を使用することで、アプリケーションを保護できますが、適切なユーザー管理が重要です。
Discussion