Cloud Spannerの暗号化とCMEK(顧客管理暗号鍵)の極限設計:データ保護とパフォーマンスの二律背反を断つ
こんにちは。チーフアーキテクトの私だ。
今日のコードレビュー、あるいは新規基盤のアーキテクチャ設計レビューで、こんな質問を受けたとしよう。
> 「ねえ、Spannerってデフォルトで暗号化されてるよね? わざわざ面倒なKMS(CMEK)を挟む意味って、コンプライアンス以外の何があるの? パフォーマンス落ちない?」
この質問に、ただ「セキュリティ要件です」と答えた瞬間、君のシニアとしての価値は半分になる。
Cloud Spannerにおける保存時暗号化(Encryption at Rest)とCMEK(Customer-Managed Encryption Keys)のアーキテクチャは、単なる「鍵の管理場所の話」ではない。分散ストレージのレイテンシ、可用性、そしてセキュリティガバナンスの境界線をどこに引くかという、極めてエンプラ向けの深遠なトレードオフなのだ。
今日は、Spannerの内部ストレージ構造から暗号化の仕組み、そしてCMEKを導入する際の「実務で絶対に踏んではならない地雷」まで、一切の妥協なく解説しよう。
—
1. Cloud Spannerのデフォルト暗号化:なぜ「何もしなくても堅牢」なのか
まず前提として、Cloud Spannerはユーザーが何も設定しなくても、すべての保存データ(テーブルデータ、セカンダリインデックス、トランザクションログ、バックアップ)が強固に暗号化されている。Googleが管理する鍵(Google-managed encryption keys: GMEK)による暗号化だ。
暗号化の階層構造(Envelope Encryption)
Spannerのストレージエンジンは、LSMツリーベースの分散構造をとっているが、その物理ブロック(SSTableやログ)は、Envelope Encryption(封筒暗号化)というパターンで保護されている。
1. データ鍵 (DEK: Data Encryption Key): 個々のデータブロックやファイルを暗号化する軽量な鍵。
2. 鍵暗号鍵 (KEK: Key Encryption Key): DEKを暗号化するための鍵。
3. ルート鍵 (Root Key): 最終的な保護層。
デフォルト(GMEK)の場合、Googleの内部キー管理サービスであるBorgstore / Tink等の基盤上で、これら鍵のライフサイクルが完全自動かつ透明に管理されている。数ペタバイト規模の分散ノード群において、データの書き込み・読み出しパス(I/Oパス)上でハードウェア支援によるAES-256暗号化/復号が、スループットを落とすことなく実行されている。
では、なぜこれほど完璧なデフォルトがあるのに、我々はCMEKを検討しなければならないのか?
—
2. CMEK(顧客管理の暗号鍵)のアーキテクチャ:何が変わるのか?
CMEK(Cloud KMSとの統合)を有効化すると、上記の階層における「ルート鍵」または「KEK」の所有権と制御権が、Googleからあなた(組織のセキュリティ管理者)に移譲される。
CMEK有効時のデータフロー
[Client] —> (SQL Query) —> [Spanner Node]
│
(Data Encryption/Decryption)
│
[Cloud KMS (CMEK)]
- 鍵の有効性/アクセス権の検証
ここで重要なアーキテクチャ上の事実がある。
「SpannerのノードがすべてのI/OごとにCloud KMSに問い合わせにいっているわけではない」ということだ。もし毎回の読み書きでKMSへRPCを飛ばしていたら、Spannerの代名詞であるミリ秒未満のレイテンシ(低遅延)は崩壊し、KMSのレートリミットに即座に撃墜されるだろう。
実際には、Spannerの各ノードはラップされたDEKをメモリ上にキャッシュし、暗号化オペレーションをローカルのCPU(AES-NI等のハードウェアアクセラレーション)で完結させている。KMSへの通信が発生するのは、主に以下のタイミングだ:
- インスタンス起動時 / ノードのスケールアウト・イン時
- キャッシュされた鍵の有効期限切れ(ローテーションやポリシー変更の伝播)
- 明示的な鍵の無効化・破棄イベント
—
3. 実務で直面する「CMEKの3大リスク」と設計パターン
さて、ここからが本題だ。CMEKを導入する際、開発チームが陥りがちな罠と、それを回避するための設計パターンを授ける。
リスク1: 「鍵の失効」による全データ即座のロスト(可用性の罠)
CMEKの最大にして最強の特性は、「鍵を無効化(Disable)あるいは削除(Destroy)すると、Spannerインスタンス内のデータへのアクセスが瞬時に、かつ不可逆的に遮断される」ことだ。
誤って鍵のバージョンを破棄した場合、Googleのエンジニアであってもデータを復元することはできない。
【設計パターン:鍵のライフサイクルとIAMの分離】
- Principle of Least Privilege: Spannerのサービスアカウント(例: `service-{project-number}@gcp-sa-spanner.iam.gserviceaccount.com`)に付与するロールは、最小限に絞る。必要なのは `roles/cloudkms.cryptoKeyEncrypterDecrypter` のみだ。
- 削除保護の強制: Cloud KMSのキーリングおよび暗号鍵には、`Destruction Window`(削除遅延期間、最低30日)を必ず設定し、即時削除ができないガバナンスを組織ポリシー(Organization Policy)で強制すること。
リスク2: 外部キープロバイダ(Cloud External Key Manager / EKM)利用時のレイテンシと障害伝播
最高度のセキュリティ要件(金融機関や政府系など)では、Googleのクラウド外にあるサードパーティ製HSM(Thales, Gemaltoなど)をCloud EKM経由で連携させることがある。
【設計パターン:フェイルオープン・フェイルクローズの理解】
EKMを利用する場合、オンプレミスや外部の鍵管理システムがダウンした際の影響がSpannerに直結する。
- EKM障害時、Spannerへの新規書き込みや、キャッシュ切れを起こしたデータの読み込みは即座に失敗(Fail Closed)する。
- グローバル分散システムを設計する際、鍵を提供する外部インフラのSLAがSpanner自体の99.999%のSLAのボトルネックになるという矛盾を忘れてはならない。マルチリージョン構成をとる場合は、EKM側も地理的に冗長化されたエンドポイントを持つ設計が必須である。
—
4. Terraformによる堅牢なCMEKインスタンス構築コード
口で言うだけなら誰でもできる。プロダクション環境でそのまま適用できる、Cloud KMSキーとCMEK適用済みSpannerインスタンスのTerraformコードの骨子を示そう。
terraform {
required_version = “>= 1.5.0”
required_providers {
google = {
source = “hashicorp/google”
version = “~> 5.0”
}
}
}
1. CMEK用のキーリング(リージョンはSpannerの配置に合わせる)
resource “google_kms_key_ring” “spanner_ring” {
name = “prod-spanner-keyring”
location = “asia-northeast1”
}
2. 暗号鍵の定義(30日間の削除保護を強制)
resource “google_kms_crypto_key” “spanner_cmek” {
name = “prod-spanner-cmek”
key_ring = google_kms_key_ring.spanner_ring.id
rotation_period = “7776000s” # 90日ごとに鍵をローテーション
lifecycle {
prevent_destroy = true
}
}
3. SpannerのサービスエージェントにKMS暗号化/復号権限を付与
data “google_project” “project” {}
resource “google_kms_crypto_key_iam_member” “spanner_kms_binding” {
crypto_key_id = google_kms_crypto_key.spanner_cmek.id
role = “roles/cloudkms.cryptoKeyEncrypterDecrypter”
member = “serviceAccount:service-${data.google_project.project.number}@gcp-sa-spanner.iam.gserviceaccount.com”
}
4. CMEKを適用したCloud Spannerインスタンスの作成
resource “google_spanner_instance” “production_db” {
name = “prod-core-db”
config = “regional-asia-northeast1”
display_name = “Production Core Database”
num_nodes = 1
# ここがCMEKの核心部
encryption_config {
kms_key_name = google_kms_crypto_key.spanner_cmek.id
}
# 依存関係の明示(IAMバインディングが完了してからインスタンスを作成させる)
depends_on = [
google_kms_crypto_key_iam_member.spanner_kms_binding
]
}
このコードのレビューポイント
1. `depends_on` の罠: Spannerインスタンス作成時にKMSのIAM権限伝播が間に合わないというGCP特有のレースコンディションを、`depends_on` で明示的に防いでいる。これを忘れると「Permission denied on resource」でデプロイが爆死する。
2. リージョンの一致: Spannerインスタンスのロケーション(例: `asia-northeast1`)とKMSキーリングのロケーションは、レイテンシとデータ主権(Data Residency)の観点から完全に一致させる必要がある。マルチリージョンの場合は、マルチリージョン対応のKMSキーを選択すること。
—
5. チーフアーキテクトからの最終提言
Cloud SpannerのCMEK設計において、君たちが常に意識すべきは以下の3点だ:
1. デフォルトのGMEKで十分なケースを見極めよ
「なんとなくセキュリティ上必要だから」という理由だけでCMEKを導入すると、鍵のローテーション運用コスト、IAM管理の複雑性、そして鍵消失リスクという「負債」を背負うことになる。規制(PCI-DSS, HIPAAなど)や厳格な社内コンプライアンス要件がない限り、Googleの最高峰エンジニアリングに保護されたGMEKを信頼するのも立派なアーキテクチャの選択だ。
2. インシデント・レスポンス(IR)計画に「鍵の無効化訓練」を組み込め
万が一のデータ漏洩シグネチャを検知した際、CMEKの鍵を即座に無効化してデータへのアクセスを断つ「キルスイッチ」としての訓練を、運用チームと必ず行っておけ。
3. パフォーマンスへの影響は「ほぼゼロ」だが「障害時は一発レッド」
通常時のレイテンシやスループットにペナルティはない。しかし、KMS側の障害や権限剥奪は、Spanner全体の可用性を直撃する。高可用性(HA)の定義の起点が、Spannerのマルチゾーン構成からKMSの可用性に依存するようになる点を忘れてはならない。
理論と実装の裏付けを持った者だけが、真にレジリエントな分散システムを構築できる。
次の設計レビューでは、これらの知見をもとにチームを圧倒して見せてくれ。期待している。
コメント