【Cloud Spanner IAM・アクセス制御】
伝説のチーフアーキテクトが伝授する、きめ細やかな権限管理の極意
テックリードの君、よく来てくれた。
今日のコードレビュー、あるいはアーキテクチャ設計レビューを始める前に、一つ聞こう。
「Cloud Spannerのアクセス制御、とりあえずプロジェクト全体の `roles/spanner.admin` や `roles/spanner.databaseAdmin` をサービスアカウントにブチ込んで終わりにしていないか?」
もし心当たりがあるなら、今すぐそのキーボードから手を離してほしい。
金融、ヘルスケア、あるいはグローバル規模のSaaSを支えるミッションクリティカルなシステムにおいて、「過剰な権限(Over-privileged)」は、いつ爆発するか分からない時限爆弾だ。バグったアプリケーションが、あるいは万が一コンプロマイズされたクレデンシャルが、本番データベースの全スキーマをドロップしたり、全顧客のPII(個人特定情報)を流出させたりするリスクを、プロのエンジニアが看過してはならない。
Cloud Spannerは、GCPのIAMと完全に統合されており、インスタンスレベルから、行・列レベルのきめ細やかなアクセス制御(Fine-grained access control: FGAC)まで、極めて堅牢な防壁を構築できる。
今回は、実務の現場で「どう設計し、どう実装すべきか」を、私からロジカルかつシャープに伝授しよう。
—
1. Cloud Spanner IAM階層構造の全体像
まず、SpannerにおけるIAMのスコープを正しく理解しているか?
Spannerの権限管理は、大きく分けて以下の3つの階層に分かれている。
[Google Cloud Project]
└── [Spanner Instance] (インスタンスレベルIAM)
└── [Spanner Database] (データベースレベルIAM)
└── [Table / Row / Column] (きめ細やかなアクセス制御: FGAC)
それぞれのレイヤーで「誰に・何を(最小権限の原則)」許可すべきか、設計の鉄則を見ていこう。
① インスタンスレベル (`roles/spanner.instanceAdmin` など)
- 対象: インスタンス自体の作成、削除、ノード数のスケーリング、バックアップの管理など。
- 誰に与えるか: インフラストラクチャエンジニア、SRE、あるいはTerraform等のIaCを実行するCI/CDパイロット用の特権サービスアカウント。
- アンチパターン: アプリケーションランタイム(GKEのPodやCloud Run)にインスタンス管理権限を持たせること。論外だ。
② データベースレベル (`roles/spanner.databaseUser` など)
- 対象: DML(SELECT, INSERT, UPDATE, DELETE)の実行、DDL(スキーマ変更)の実行。
- 誰に与えるか: アプリケーションのランタイム、DBA、データアナリスト。
- 実践のポイント: スキーマ変更(DDL)を行う権限(`roles/spanner.databaseAdmin`)と、通常のデータ読み書きを行う権限(`roles/spanner.databaseUser`)は、絶対に分離しろ。本番アプリのプロセスがDDLを実行できる状態になっていること自体がセキュリティインシデントの萌芽だ。
—
2. 実務で直面する「きめ細やかなアクセス制御(FGAC)」の真髄
さて、ここからが本題だ。
マルチテナントアーキテクチャや、外部のBIツール、一部のサードパーティ製サービスにSpannerの一部データを安全に公開したいとき、君ならどうする?「専用のレプリカDBを作る」「アプリケーション層でフィルタリングする」――待て、それはいずれもコストやセキュリティの面で悪手だ。
Cloud Spannerには、データベース内の行(Row)や列(Column)単位でアクセスを制限する FGAC(Fine-grained access control) が備わっている。
FGACのアーキテクチャ構成要素
1. データベースロール(Database Role): GCPのIAMプリンシパル(ユーザーやサービスアカウント)とは別に、Spannerのデータベース内部で定義する論理的なロール。
2. グラント(GRANT): そのロールに対して、どのテーブルの、どの列(カラム)に対して、どのような操作(SELECT, INSERT, UPDATE)を許可するかを定義するSQL構文。
実装ハンズオン:テナント分離の例
例えば、`Tenants` テーブルがあり、テナントごとにデータが格納されている世界を想像してほしい。特定のテナント(例: `tenant_a`)のサポート担当者や外部ツールには、彼らのテナントのデータ行しか見せたくない。
Step 1: データベースロールの作成と権限付与 (DDL)
まず、Spannerのコンソールや `gcloud spanner databases ddl update` を使って、ロールとアクセス権を定義する。
— 1. リード専用のデータベースロールを作成
CREATE ROLE tenant_a_reader;
— 2. 特定のテーブルに対するSELECT権限を付与
— (※実際の行フィルタリングには、行レベルセキュリティ (Row-level security) と組み合わせるのがモダンだ)
GRANT SELECT ON TABLE Tenants TO ROLE tenant_a_reader;
GRANT SELECT ON TABLE Orders TO ROLE tenant_a_reader;
Step 2: 行レベルセキュリティ(Row-level security)の適用
Cloud Spannerでは、行レベルのフィルターを述語(Predicate)としてテーブルに直接定義できる。さらに強力なのは、現在のセッションユーザー(データベースロール)に応じた動的なフィルタリングが可能という点だ。
— Orders テーブルに対して、テナントIDが一致する行のみを可視化するポリシーを定義
ALTER TABLE Orders SET ROW FILTER
(TenantId = CURRENT_ROLE());
※注意: 上記は概念的な例だ。実際には `CURRENT_ROLE()` やセッションパラメータ(`SESSION_USER()` など)をマッピングする関数や、アプリケーション側でセッション変数を設定する仕組みを利用してテナントIDをバインドする。
Step 3: GCP IAM プリンシパルとデータベースロールの紐付け
ここが肝心だ。GCPのサービスアカウントと、先ほど作ったSpannerのデータベースロールをマッピングする。
gcloud spanner database iam-policies set-iam-policy my-database \
policy.yaml
`policy.yaml` の中身はこう書く:
bindings:
- members:
- serviceAccount: support-tool-sa@my-project.iam.gserviceaccount.com
role: roles/spanner.databaseUser
# 注意: FGACを使う場合、条件(Conditions)やデータベースロールのバインディングを適切に構成する
正確には、Spannerのデータベースロールへのバインディングは、IAMの制限付きグラント、あるいはセッション開始時の `SET ROLE` ステートメントを通じて行う。アプリケーション(クライアントライブラリ)側で接続時に以下のように明示的にロールを切り替えるのが定石だ:
// JavaでのSpanner接続時にデータベースロールを指定するスニペット
SpannerOptions options = SpannerOptions.newBuilder().build();
Spanner spanner = options.getService();
DatabaseId dbId = DatabaseId.of(“my-project”, “my-instance”, “my-database”);
// セッション設定でロールを切り替える(概念コード)
DatabaseClient dbClient = spanner.getDatabaseClient(dbId);
// 接続プールやトランザクション内で “SET ROLE tenant_a_reader” を実行するセッション構成を行う
—
3. 堅牢な設計パターン:プロダクション環境の推奨構成
チーフアーキテクトとして、数々の修羅場をくぐり抜けてきた私が推奨する「最高に堅牢なIAM設計パターン」を授けよう。
パターンA: アプリケーションランタイムの権限分離
- Web/API Backend (GKE / Cloud Run):
- 紐付けるSA: `app-backend-sa@…`
- 必要なGCP IAM: `roles/spanner.databaseUser` (対象DBのみ)
- 原則: DDL権限は絶対にもたせない。接続文字列や環境変数には、不必要なインスタンスIDを含めず、最小限のスコープに絞る。
- Migration / Schema Deployer (Cloud Build / Argo CD):
- 紐付けるSA: `db-migration-sa@…`
- 必要なGCP IAM: `roles/spanner.databaseAdmin` (スキーマ変更時のみ一時的に付与、普段は権限を剥奪しておくのが理想)
パターンB: 監査ログ(Audit Logging)の有効化
セキュリティの担保は「制限する」だけでは不十分だ。「誰が、いつ、どのデータにアクセスしたか」を監査できなければ、エンタープライズの基準はクリアできない。
Google Cloudの Cloud Audit Logs を必ず有効化せよ。
- Admin Activity Logs: デフォルトで有効。誰がインスタンスやデータベースを作ったか。
- Data Access Logs: デフォルトでは無効になっていることが多い。Spannerのデータ読み書き(SELECT, INSERT等)のログを監査するためには、GCPプロジェクトのIAM監査ログ設定で `DATA_READ` および `DATA_WRITE` を明示的に有効化する必要がある。
- アーキテクトの警告: Data Access Logsを有効にするとログの量(コスト)が跳ね上がる。秘匿性の高いデータベース(PⅡや財務データを扱うDB)に絞って有効化するのが費用対効果の観点から正しい。
—
4. パフォーマンス上の注意点:IAMとFGACの罠
最後に、パフォーマンスにうるさい君たちに向けて、セキュリティ機能がもたらすオーバーヘッドの話をしておこう。
1. IAMキャッシュとコネクションの再利用:
Spannerのクライアントライブラリは、IAMの認証トークンやセッションをキャッシュする。毎回接続のたびにIAMの権限チェックがグローバルで走るわけではないが、動的なロール切り替え(`SET ROLE`)をトランザクションの度に頻繁に行うと、セッションの再利用効率(Session Pooling)が低下し、レイテンシの悪化を招く。ロールの切り替えは、コネクション確立時やリクエストのライフサイクルの最初に行い、プール内で適切に管理しろ。
2. FGACのクエリプランへの影響:
行レベルセキュリティ(Row-level security)を導入すると、Spannerのクエリパーサーは内部的に述語(フィルタ条件)をクエリに付加して実行計画を組み立てる。インデックス設計が不十分な状態でFGACを導入すると、予期せぬフルスキャン(あるいは非効率なインデックスシーク)が発生し、スループットが急低下する。
対策: FGACのフィルタ条件に含まれるカラム(例: `TenantId`)には、必ず先行する形で複合インデックスのプレフィックスを張っておけ。
—
結びの言葉
セキュリティとは、面倒くさい手続きではない。システムがスケールし、ビジネスが成長したときに、君のアーキテクチャが会社と顧客を守るための「最強の盾」なのだ。
「動けばいい」というアマチュアのコードレビューは今日で終わりだ。
次の設計レビューでは、最小権限の原則に基づいたIAM設計と、必要に応じたFGACの組み込みが完璧になされたアーキテクチャを持ってきてもらう。期待しているぞ。
コメント