🚫

IP制限したClour Runサービスをterraformで構築してみた

2024/10/06に公開

はじめに

この記事では、Terraform を使用して Google Cloud Run にサンプルコンテナの hello アプリケーションをデプロイする方法をご紹介します。基本的には、こちらの記事の内容を参考にしていますが、サンプルコンテナを使うことで、よりシンプルかつ手軽に Terraform を動かせるように工夫しています。

Cloud Run と Terraform を組み合わせることで、インフラストラクチャのコード化やデプロイの自動化が可能になります。この記事を通じて、その基本的な手順とポイントを解説します。

全体のコードはこちらに置いています:
https://github.com/sousquared/practice-ip-restricted-cloud-run

※本番環境で使用する場合は、Cloud IAMの認証などの設定も適切に行い、セキュリティには十分注意してください。

TL;DR

  • 目的: Terraform を使って Cloud Run にサンプルの hello アプリケーションをデプロイし、ロードバランサと Cloud Armor でアクセス制御を設定します
  • 手順の概要:
    1. Terraform ファイルを準備し、サンプルコンテナの hello アプリケーションを指定
    2. Cloud Armor ポリシーに自分のグローバル IP アドレスを設定してアクセスを制限
    3. Terraform コマンド (terraform initterraform planterraform apply) を実行してリソースを作成
    4. 作成されたロードバランサの IP アドレスにアクセスしてデプロイを確認
  • 注意点:
    • ロードバランサや Cloud Armor の反映には時間がかかる場合があります
    • 利用後は terraform destroy でリソースをクリーンアップします

この記事を読むことで、Terraform を使った Cloud Run サービスのデプロイ方法と、基本的なIPアクセス制御の設定方法を理解できます。

構成図

構成図を書くと以下のような形になるかと思います。

terraform設定

基本的には以下の記事の内容を参考に構築しています。
https://zenn.dev/hosimesi/articles/36fedaa5425c7b

変更点として、サンプルコンテナのhelloアプリケーションを使用することで、より簡単にterraformを動かせるようにしてみました。

また、terraform を使って GCP にロードバランサを設置する方法は以下の記事が参考になりました。
https://qiita.com/ktoshi/items/a43a54005aa73be1254d
今回は設定していないですが、HTTPS化する方法についても解説してあります。
https://qiita.com/ktoshi/items/1fd3c62b15993adce93c

サンプルコンテナのhelloアプリケーションをCloud Runに設定する

cloud runサービスにデプロイするアプリケーションとして、 サンプルコンテナのhelloアプリケーションをimageに設定しました。

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時に削除できるようにする)
}

許可するIPアドレスの設定

変更が必要な箇所としては、cloud armor policyのsrc_ip_rangesに自宅のグローバルIPを設定することです。

# Load Balancerのcloud armor policy
resource "google_compute_security_policy" "hello_policy" {
  name        = "inference-policy"
  description = "Load Balancer用のcloud armor policy"
  rule {
    action   = "allow"
    priority = 1000
    match {
      versioned_expr = "SRC_IPS_V1"
      config {
        # FIXME: your ip address
        src_ip_ranges = ["YOUR_IP_ADDRESS"]
      }
    }
    description = "my home global ip address"
  }
  rule {
    action   = "deny(403)"
    priority = 2147483647
    match {
      versioned_expr = "SRC_IPS_V1"
      config {
        src_ip_ranges = ["*"]
      }
    }
    description = "default rule"
  }
  adaptive_protection_config {
    layer_7_ddos_defense_config {
      enable = true
    }
  }
}

自宅のPCのグローバルIPの調べ方は以下を参照しました。(mac)
https://qiita.com/rimorimo/items/8052bcfb32ac640a8796

terraform全体

terraformファイル全体をここに載せておきます。

terraformファイル全体
main.tf
provider "google" {
  project = var.project
  region  = var.region
}
variables.tf
variable "project" {
  description = "The ID of the project in which resources will be managed."
  type        = string
}

variable "region" {
  description = "The region in which resources will be created."
  type        = string
}
output.tf
output "load_balancer_ip" {
  value       = google_compute_global_address.hello_lb_ip.address
  description = "ロードバランサーの静的IPアドレス"
}
modules.tf
# 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  # 削除保護を無効にする (terraform destroy時に削除できるようにする)
}

# Cloud Runの未認証呼び出し許可policy (本番環境ではmembersに適切な値を設定すること)
data "google_iam_policy" "noauth" {
  binding {
    role = "roles/run.invoker"
    members = [
      "allUsers",
    ]
  }
}

# Cloud Runの未認証呼び出し許可を付与
resource "google_cloud_run_service_iam_policy" "noauth" {
  location = google_cloud_run_v2_service.hello_cloud_run.location
  project  = var.project
  service  = google_cloud_run_v2_service.hello_cloud_run.name

  policy_data = data.google_iam_policy.noauth.policy_data
}

# Load Balancerのserverless NEG
resource "google_compute_region_network_endpoint_group" "hello_neg" {
  name                  = "hello-neg"
  network_endpoint_type = "SERVERLESS"
  region                = "asia-northeast1"
  # cloud runのserviceを指定
  cloud_run {
    service = google_cloud_run_v2_service.hello_cloud_run.name
  }
}

# Load Balancerのcloud armor policy
resource "google_compute_security_policy" "hello_policy" {
  name        = "hello-policy"
  description = "Load Balancer用のcloud armor policy"
  rule {
    action   = "allow"
    priority = 1000
    match {
      versioned_expr = "SRC_IPS_V1"
      config {
        # FIXME: your ip address
        src_ip_ranges = ["YOUR_IP_ADDRESS"]
      }
    }
    description = "my home global ip address"
  }
  rule {
    action   = "deny(403)"
    priority = 2147483647
    match {
      versioned_expr = "SRC_IPS_V1"
      config {
        src_ip_ranges = ["*"]
      }
    }
    description = "default rule"
  }
  adaptive_protection_config {
    layer_7_ddos_defense_config {
      enable = true
    }
  }
}

# load balancerのbackend service
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"

  # cloud armor policyを指定
  security_policy = google_compute_security_policy.hello_policy.id

  backend {
    group = google_compute_region_network_endpoint_group.hello_neg.self_link
  }
}

# url map
resource "google_compute_url_map" "hello_url_map" {
  name        = "hello-lb"
  description = "load balancer用のlb"

  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
  }
}

resource "google_compute_target_http_proxy" "hello_target_http_proxy" {
  name    = "predictor-target-http-proxy"
  url_map = google_compute_url_map.hello_url_map.id
}

# フロントエンドの設定(http)
resource "google_compute_global_forwarding_rule" "hello_forwarding_rule_http" {
  name                  = "hello-forwarding-rule-http"
  description           = "load balancerのforwarding rule(http)"
  load_balancing_scheme = "EXTERNAL_MANAGED"
  target                = google_compute_target_http_proxy.hello_target_http_proxy.id
  ip_address            = google_compute_global_address.hello_lb_ip.address
  ip_protocol           = "TCP"
  port_range            = "80"
}

terraform実行

cd infra
terraform init
terraform plan
terraform apply

terraform planやterraform applyでvariablesの入力を求められるので、適切に入力します。

$ terraform plan
var.project
  The ID of the project in which resources will be managed.

  Enter a value:

※ロードバランサやCloud Armorの反映に少し時間がかかることがあります。

アクセス

作成されたIPにアクセスします。

$ terraform output
load_balancer_ip = {IP_ADDRESS}

http://{IP_ADDRESS} にアクセス

以下の画面が出てきたら成功です!

クリーンアップ

Terraformで作成したリソースをすべて削除して、環境をクリーンな状態に戻すには、以下のコマンドを実行します。

terraform destroy

Discussion