Cloud KMSを使用したGKEクラスタのetcdのデータ暗号化について

このブログシリーズ 「クラウドセキュリティ 実践集」 では、一般的なセキュリティ課題を取り上げ、「なぜ危険なのか?」 というリスクの解説から、 「どうやって直すのか?」 という具体的な修復手順(コンソール、Google Cloud CLI、Terraformなど)まで、分かりやすく解説します。
この記事では、Cloud KMSを使用したGKEクラスタのetcdのデータ暗号化について、具体的なセキュリティリスクと実践的な対策方法を解説します。etcdに保存される機密データをCloud KMSで暗号化する機能で、Kubernetesの機密情報を強力に保護し、コンプライアンス要件を満たすことができます。

ポリシーの説明
GKEクラスタでApplication-layer Secrets Encryption(ALSE)を有効化し、etcd内の機密データをCloud KMSを使用して暗号化します。
リスク
GKEクラスタでApplication-layer Secrets Encryption(ALSE)が無効化されている場合、以下のような深刻なセキュリティリスクが発生します:
- 機密データの露出: etcdに保存されるKubernetes Secretsが暗号化されずに保存され、データベースレベルでの侵害時に全ての機密情報が露出する可能性があります(注:ALSEはSecretsとTokenRequestのみを暗号化し、ConfigMapは暗号化対象外です)
- 内部脅威への脆弱性: etcdのバックアップやスナップショットへのアクセス権を持つ内部関係者が、機密データを読み取れる可能性があります
- ディスク盗難のリスク: 物理的なディスクが盗難された場合、etcdデータベースから機密情報を抽出される可能性があります
etcdには以下のような重要なデータが保存されています:
- アプリケーションのパスワード、APIキー(Secrets)
- サービスアカウントトークン
- TLS証明書
- データベース接続情報
修復方法
コンソールでの修復手順
Google Cloud コンソールを使用して、GKEクラスタでKMS暗号化を有効化します。
重要: 既存のクラスタでは、Application-layer Secrets Encryption(ALSE)を後から有効化できません。ALSEを有効化するには新しいクラスタを作成する必要があります。ただし、GKE Autopilotクラスタでは、デフォルトでALSEが有効化されています。
- Cloud KMS暗号化キーの準備
- 「セキュリティ」→「鍵管理」に移動
- 「キーリングを作成」をクリック
- キーリング名(例:gke-encryption-keyring)とリージョンを指定
- 「鍵を作成」で暗号化キーを作成(例:gke-etcd-encryption-key)
- 鍵の目的は「対称暗号化/復号化」を選択
- ローテーション期間を設定(推奨:90日)
- 新しいGKEクラスタの作成
- 「Kubernetes Engine」→「クラスタ」→「作成」に移動
- 「Standard」モードを選択
- 基本設定を入力後、「セキュリティ」セクションに移動
- 「Application-layer Secrets Encryption」セクションで「有効にする」にチェック
- 作成したCloud KMS暗号化キーを選択
- クラスタの作成には5~10分程度かかります
- 暗号化の確認
- クラスタの詳細ページで「Application-layer Secrets Encryption」が有効になっていることを確認
- 使用されているKMS鍵が表示されていることを確認
Terraformでの修復手順
GKEクラスタでALSEを有効化するTerraformコードと、主要な修正ポイントを説明します。
# ============== Cloud KMS設定 ==============
# KMSキーリングの作成
resource "google_kms_key_ring" "gke_keyring" {
name = "gke-encryption-keyring"
location = var.region
}
# KMS暗号化キーの作成
resource "google_kms_crypto_key" "gke_etcd_key" {
name = "gke-etcd-encryption-key"
key_ring = google_kms_key_ring.gke_keyring.id
rotation_period = "7776000s" # 90日でローテーション
purpose = "ENCRYPT_DECRYPT"
lifecycle {
prevent_destroy = true # 誤削除防止
}
version_template {
algorithm = "GOOGLE_SYMMETRIC_ENCRYPTION"
}
}
# GKEサービスアカウント用のIAMバインディング
data "google_project" "project" {}
resource "google_kms_crypto_key_iam_member" "gke_sa_encrypt_decrypt" {
crypto_key_id = google_kms_crypto_key.gke_etcd_key.id
role = "roles/cloudkms.cryptoKeyEncrypterDecrypter"
# GKEサービスアカウント(プロジェクト番号を使用)
# 注:Autopilotクラスタの場合は異なるサービスアカウントが使用されます
member = "serviceAccount:service-${data.google_project.project.number}@container-engine-robot.iam.gserviceaccount.com"
}
# ============== GKEクラスタ(KMS暗号化有効) ==============
resource "google_container_cluster" "encrypted_cluster" {
name = "gke-encrypted-cluster"
location = var.zone
# 最小ノード数(後でノードプールで管理)
initial_node_count = 1
remove_default_node_pool = true
# 最小バージョン要件(ALSEサポート)
min_master_version = "1.11.0-gke.0"
# ============== Application-layer Secrets Encryption(重要) ==============
database_encryption {
state = "ENCRYPTED"
key_name = google_kms_crypto_key.gke_etcd_key.id
}
# ============== その他のセキュリティ設定 ==============
# Workload Identity
workload_identity_config {
workload_pool = "${data.google_project.project.project_id}.svc.id.goog"
}
# プライベートクラスタ
private_cluster_config {
enable_private_nodes = true
enable_private_endpoint = false
master_ipv4_cidr_block = "172.16.0.0/28"
}
# IPアクセス制限
master_authorized_networks_config {
cidr_blocks {
cidr_block = var.authorized_network_cidr
display_name = "Authorized Network"
}
}
# Binary Authorization
binary_authorization {
evaluation_mode = "PROJECT_SINGLETON_POLICY_ENFORCE"
}
# ネットワークポリシー
network_policy {
enabled = true
}
# ログの設定
logging_config {
enable_components = ["SYSTEM_COMPONENTS", "WORKLOADS"]
}
# 監査ログ
cluster_telemetry {
type = "ENABLED"
}
# メンテナンスウィンドウの設定(推奨)
maintenance_policy {
recurring_window {
start_time = "2024-01-01T00:00:00Z"
end_time = "2024-01-01T04:00:00Z"
recurrence = "FREQ=WEEKLY;BYDAY=SA"
}
}
depends_on = [
google_kms_crypto_key_iam_member.gke_sa_encrypt_decrypt
]
}
# ============== セキュアなノードプール ==============
resource "google_container_node_pool" "encrypted_pool" {
name = "encrypted-node-pool"
cluster = google_container_cluster.encrypted_cluster.id
node_count = var.node_count
node_config {
preemptible = false
machine_type = "n2-standard-4"
# カスタムサービスアカウント(最小権限)
service_account = google_service_account.gke_node_sa.email
oauth_scopes = [
"<https://www.googleapis.com/auth/logging.write>",
"<https://www.googleapis.com/auth/monitoring>",
]
# Shielded VM
shielded_instance_config {
enable_secure_boot = true
enable_integrity_monitoring = true
}
# Workload Identityメタデータ
workload_metadata_config {
mode = "GKE_METADATA"
}
# セキュリティメタデータ
metadata = {
disable-legacy-endpoints = "true"
}
}
management {
auto_repair = true
auto_upgrade = true
}
}
# ============== 暗号化検証用リソース ==============
resource "null_resource" "verify_encryption" {
provisioner "local-exec" {
command = <<-EOT
echo "Verifying ALSE is enabled..."
# クラスタの暗号化状態を確認
ENCRYPTION_STATE=$(gcloud container clusters describe ${google_container_cluster.encrypted_cluster.name} \
--zone=${google_container_cluster.encrypted_cluster.location} \
--format="value(databaseEncryption.state)")
if [ "$ENCRYPTION_STATE" = "ENCRYPTED" ]; then
echo "SUCCESS: Application-layer Secrets Encryption is enabled"
# 使用されているKMSキーを確認
KMS_KEY=$(gcloud container clusters describe ${google_container_cluster.encrypted_cluster.name} \
--zone=${google_container_cluster.encrypted_cluster.location} \
--format="value(databaseEncryption.keyName)")
echo "Using KMS key: $KMS_KEY"
# KMSキーのバージョンも確認
KEY_VERSION=$(gcloud kms keys describe gke-etcd-encryption-key \
--location=us-central1 \
--keyring=gke-encryption-keyring \
--format="value(primary.name)")
echo "Active key version: $KEY_VERSION"
else
echo "ERROR: ALSE is not enabled!"
exit 1
fi
EOT
}
depends_on = [google_container_cluster.encrypted_cluster]
}
# ============== 暗号化キー使用状況のモニタリング ==============
resource "google_monitoring_alert_policy" "kms_key_usage" {
display_name = "GKE KMS Key Usage Alert"
combiner = "OR"
conditions {
display_name = "High KMS key operations"
condition_threshold {
filter = <<-EOT
resource.type = "cloudkms.googleapis.com/CryptoKey"
resource.labels.key_ring_id = "${google_kms_key_ring.gke_keyring.name}"
resource.labels.crypto_key_id = "${google_kms_crypto_key.gke_etcd_key.name}"
metric.type = "cloudkms.googleapis.com/cryptokey/request_count"
EOT
duration = "300s"
comparison = "COMPARISON_GT"
threshold_value = 10000 # 5分間で10,000リクエスト以上
aggregations {
alignment_period = "60s"
per_series_aligner = "ALIGN_RATE"
}
}
}
notification_channels = [google_monitoring_notification_channel.security_team.id]
}
# ============== 追加セキュリティ: キーローテーション監視 ==============
resource "google_logging_metric" "kms_key_rotation" {
name = "gke-kms-key-rotation"
description = "Tracks KMS key rotation events for GKE"
filter = <<-EOT
resource.type="cloudkms.googleapis.com/CryptoKey"
protoPayload.methodName="RotateCryptoKey"
resource.labels.key_ring_id="${google_kms_key_ring.gke_keyring.name}"
EOT
metric_descriptor {
metric_kind = "DELTA"
value_type = "INT64"
}
}
# ============== 変数定義 ==============
variable "region" {
description = "GCP region"
type = string
default = "us-central1"
}
variable "zone" {
description = "GCP zone"
type = string
default = "us-central1-a"
}
variable "authorized_network_cidr" {
description = "CIDR block for authorized networks"
type = string
default = "10.0.0.0/8"
}
variable "node_count" {
description = "Number of nodes in the node pool"
type = number
default = 3
}
# ============== サンプル: 暗号化されたSecretの作成 ==============
resource "kubernetes_secret" "encrypted_app_secret" {
metadata {
name = "app-secrets"
namespace = "default"
annotations = {
"description" = "This secret is encrypted at rest using Cloud KMS"
}
}
data = {
database_password = base64encode(var.database_password)
api_key = base64encode(var.api_key)
}
# 注:このSecretはALSEによって自動的にCloud KMSで暗号化されてetcdに保存されます
type = "Opaque"
depends_on = [google_container_node_pool.encrypted_pool]
}
最後に
この記事では、GKEクラスタのApplication-layer Secrets Encryption(ALSE)について、リスクと対策を解説しました。ALSEの有効化により、etcd内のSecretsをCloud KMSで暗号化し、機密データの保護を大幅に強化できます。
この問題の検出は弊社が提供するSecurifyのCSPM機能で簡単に検出及び管理する事が可能です。 運用が非常に楽に出来る製品になっていますので、ぜひ興味がある方はお問い合わせお待ちしております。 最後までお読みいただきありがとうございました。この記事が皆さんの役に立てば幸いです。
参考情報
公式ドキュメント
- Google Cloud – Application-layer Secrets Encryption
- Google Cloud – Cloud KMS の概要
- Kubernetes – Encrypting Secret Data at Rest