09. Cloud KMS / Secret Manager
GCP の暗号化キー管理は Cloud KMS、シークレット(パスワード/API キー)は Secret Manager。AWS の KMS + Secrets Manager に対応します。KMS には「KeyRing」という独自の階層あり。
Cloud KMS の階層
Project
└── Location(リージョン or global)
└── KeyRing ← キーをグループ化する箱
└── CryptoKey ← 個別のキー
└── CryptoKeyVersion ← キーのバージョン
AWS KMS には KeyRing がなく、Key が直接プロジェクト直下。GCP は KeyRing で論理グループを作る のが特徴。
KeyRing と CryptoKey は削除不可
terraform destroy しても API レベルで削除できません。CryptoKey のバージョンは「破棄スケジュール」になるだけ。実質的に永続的なリソースなので、命名と作成は慎重に。
KeyRing と CryptoKey
resource "google_kms_key_ring" "main" {
name = "kr-myapp-prd"
location = "asia-northeast1" # global / リージョン / マルチリージョン
}
resource "google_kms_crypto_key" "app" {
name = "app-encryption"
key_ring = google_kms_key_ring.main.id
purpose = "ENCRYPT_DECRYPT"
rotation_period = "7776000s" # 90 日でローテーション
version_template {
algorithm = "GOOGLE_SYMMETRIC_ENCRYPTION"
protection_level = "SOFTWARE" # or HSM(ハードウェア暗号化、料金高)
}
lifecycle {
prevent_destroy = true # 誤削除防止
}
}
自動ローテーション
rotation_period を設定すれば、自動で新しい CryptoKeyVersion が生成され、それ以降の暗号化は新版で行われます(既存暗号化データの復号は古い版で可能)。
| 期間 | 秒数 | 用途 |
|---|---|---|
| 30 日 | "2592000s" | 機密性が極めて高い |
| 90 日 | "7776000s" | 標準 |
| 1 年 | "31536000s" | 緩め |
他リソースの暗号化
# Cloud Storage のバケット暗号化
resource "google_kms_crypto_key_iam_member" "gcs_use_key" {
crypto_key_id = google_kms_crypto_key.app.id
role = "roles/cloudkms.cryptoKeyEncrypterDecrypter"
# GCS の service account(Project ごとに固定)
member = "serviceAccount:service-${data.google_project.current.number}@gs-project-accounts.iam.gserviceaccount.com"
}
resource "google_storage_bucket" "data" {
# ...
encryption {
default_kms_key_name = google_kms_crypto_key.app.id
}
depends_on = [google_kms_crypto_key_iam_member.gcs_use_key]
}
# Compute Engine のディスク暗号化
resource "google_compute_disk" "data" {
name = "disk-data-001"
zone = "asia-northeast1-a"
size = 100
disk_encryption_key {
kms_key_self_link = google_kms_crypto_key.app.id
}
}
data "google_project" "current" {}
Secret Manager
resource "google_secret_manager_secret" "db_password" {
secret_id = "db-password"
replication {
user_managed {
replicas {
location = "asia-northeast1"
}
replicas {
location = "asia-southeast1" # DR 用
}
}
}
# 自前 CMEK で暗号化
rotation {
next_rotation_time = "2026-12-31T00:00:00Z"
rotation_period = "7776000s" # 90 日
}
labels = local.common_labels
}
resource "random_password" "db" {
length = 32
special = true
}
resource "google_secret_manager_secret_version" "db_password" {
secret = google_secret_manager_secret.db_password.id
secret_data = random_password.db.result
}
# Cloud Run の SA に Secret Accessor 権限
resource "google_secret_manager_secret_iam_member" "app_access" {
secret_id = google_secret_manager_secret.db_password.id
role = "roles/secretmanager.secretAccessor"
member = "serviceAccount:${google_service_account.app.email}"
}
アプリから取得
Cloud Run の環境変数として注入(推奨)
resource "google_cloud_run_v2_service" "api" {
# ...
template {
containers {
env {
name = "DB_PASSWORD"
value_source {
secret_key_ref {
secret = google_secret_manager_secret.db_password.secret_id
version = "latest"
}
}
}
}
}
}
これなら Terraform は値を直接読まないので、state に平文で残りません。Cloud Run がランタイムで Secret Manager から取得。
CLI から取得(運用確認用)
gcloud secrets versions access latest --secret="db-password"
Terraform で値を読む(非推奨)
data "google_secret_manager_secret_version" "db" {
secret = "db-password"
}
# 使い方: data.google_secret_manager_secret_version.db.secret_data
# ⚠ state に平文で書かれる
推奨パターン
① Secret Manager で値を保管 → ② SA にロール付与 → ③ Cloud Run / Cloud Functions の
value_source.secret_key_ref で参照。Terraform は ARN・名前だけ扱う。