このページでは、Spanner 用に顧客管理の暗号鍵(CMEK)を使用する方法について説明します。
CMEK の詳細については、顧客管理の暗号鍵(CMEK)をご覧ください。
CMEK 対応データベースを作成する
Cloud Key Management Service(Cloud KMS)で鍵を作成します。Cloud Spanner では、以下がサポートされます。
鍵は Cloud Spanner インスタンスと同じロケーションにあることが必要です。たとえば、Spanner インスタンス構成が
us-west1
の場合は、キーリングのロケーションもus-west1
でなければなりません。すべての Cloud Spanner マルチリージョン インスタンス構成で、Cloud KMS に対応するキーリング ロケーションが存在するわけではありません。そうした Spanner インスタンスでは、CMEK 対応データベースを作成できません。
すでに適切な場所に Cloud KMS 鍵を配置している場合は、このステップをスキップできます。
詳しくは、次のリソースをご覧ください。
Spanner に鍵へのアクセス権を付与します。
Cloud Shell で、Google が管理するサービス アカウントを作成して表示します。アカウントがすでに存在する場合は表示します。
gcloud beta services identity create --service=spanner.googleapis.com \ --project=my-spanner-project
gcloud ベータ版コマンド コンポーネントのインストールを求められたら、「
Y
」と入力します。インストール後、コマンドが自動的に再開されます。gcloud services ID コマンドは、ユーザーに代わって Spanner が Cloud KMS 鍵にアクセスする際に使用する特別な Google 管理のサービス アカウントを作成または取得します。
サービス アカウント ID は、メールアドレスに類似した形式です。
Service identity created: service-xxx@gcp-sa-spanner.iam.gserviceaccount.com
サービス アカウントに
cloudkms.cryptoKeyEncrypterDecrypter
ロールを付与します。gcloud kms keys add-iam-policy-binding my-kms-key \ --location my-kms-key-location \ --keyring my-kms-key-ring \ --project=my-kms-project \ --member serviceAccount:service-xxx@gcp-sa-spanner.iam.gserviceaccount.com \ --role roles/cloudkms.cryptoKeyEncrypterDecrypter
完了したら以下のように表示されます。
Updated IAM policy for key [my-kms-key]
このロールにより、サービス アカウントには Cloud KMS 鍵による暗号化と復号の両方が許可されます。詳細については、Cloud KMS の権限とロールをご覧ください。
データベースを作成し、Cloud KMS 鍵を指定します。
Console
1. Google Cloud Console で、Cloud Spanner の [インスタンス] ページに移動します。
2. データベースを作成するインスタンス名をクリックします。
3. [データベースを作成] をクリックし、必須のフィールドを入力します。
4. [暗号化オプションを表示する] をクリックします。
5. [顧客管理の暗号鍵(CMEK)を使用する] を選択します。
6 プルダウン リストから鍵を選択します。
鍵のリストは、現在の Google Cloud プロジェクトに限定されます。別の Google Cloud プロジェクトの鍵を使用するには、Google Cloud コンソールではなく gcloud を使用してデータベースを作成します。
データベースが作成されたら、[データベースの詳細] ページを表示すると、データベースが CMEK 対応であることを確認できます。
gcloud
gcloud spanner databases create example-db \ --project=my-spanner-project \ --instance=my-spanner-instance \ --ddl="CREATE TABLE Users (Id INT64 NOT NULL, FirstName STRING(100) NOT NULL, LastName STRING(100) NOT NULL,) PRIMARY KEY (Id)" \ --kms-project=my-kms-project \ --kms-location=my-kms-key-location \ --kms-keyring=my-kms-key-ring \ --kms-key=my-kms-key
データベースが CMEK 対応であることを確認するには:
gcloud spanner databases describe example-db \ --project=my-spanner-project \ --instance=my-spanner-instance
CMEK 対応データベースには、次の例のように
encryptionConfig
のフィールドが含まれます。encryptionConfig: kmsKeyName:projects/my-kms-project/locations/eur5/keyRings/my-kms-key-ring/cryptoKeys/my-kms-key name: projects/my-spanner-project/instances/my-instance/databases/my-db state: READY
C#
using Google.Cloud.Spanner.Admin.Database.V1; using Google.Cloud.Spanner.Common.V1; using System; using System.Threading.Tasks; public class CreateDatabaseWithEncryptionKeyAsyncSample { public async Task<Database> CreateDatabaseWithEncryptionKeyAsync(string projectId, string instanceId, string databaseId, CryptoKeyName kmsKeyName) { // Create a DatabaseAdminClient instance that can be used to execute a // CreateDatabaseRequest with custom encryption configuration options. DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.Create(); // Define create table statement for table #1. var createSingersTable = @"CREATE TABLE Singers ( SingerId INT64 NOT NULL, FirstName STRING(1024), LastName STRING(1024), ComposerInfo BYTES(MAX) ) PRIMARY KEY (SingerId)"; // Define create table statement for table #2. var createAlbumsTable = @"CREATE TABLE Albums ( SingerId INT64 NOT NULL, AlbumId INT64 NOT NULL, AlbumTitle STRING(MAX) ) PRIMARY KEY (SingerId, AlbumId), INTERLEAVE IN PARENT Singers ON DELETE CASCADE"; // Create the CreateDatabase request with encryption configuration and execute it. var request = new CreateDatabaseRequest { ParentAsInstanceName = InstanceName.FromProjectInstance(projectId, instanceId), CreateStatement = $"CREATE DATABASE `{databaseId}`", ExtraStatements = { createSingersTable, createAlbumsTable }, EncryptionConfig = new EncryptionConfig { KmsKeyNameAsCryptoKeyName = kmsKeyName, }, }; var operation = await databaseAdminClient.CreateDatabaseAsync(request); // Wait until the operation has finished. Console.WriteLine("Waiting for the operation to finish."); var completedResponse = await operation.PollUntilCompletedAsync(); if (completedResponse.IsFaulted) { Console.WriteLine($"Error while creating database: {completedResponse.Exception}"); throw completedResponse.Exception; } var database = completedResponse.Result; Console.WriteLine($"Database {database.Name} created with encryption key {database.EncryptionConfig.KmsKeyName}"); return database; } }
C++
Go
Java
ノード
PHP
use Google\Cloud\Spanner\SpannerClient; /** * Creates an encrypted database with tables for sample data. * Example: * ``` * create_database_with_encryption_key($instanceId, $databaseId, $kmsKeyName); * ``` * * @param string $instanceId The Spanner instance ID. * @param string $databaseId The Spanner database ID. * @param string $kmsKeyName The KMS key used for encryption. */ function create_database_with_encryption_key($instanceId, $databaseId, $kmsKeyName) { $spanner = new SpannerClient(); $instance = $spanner->instance($instanceId); if (!$instance->exists()) { throw new \LogicException("Instance $instanceId does not exist"); } $operation = $instance->createDatabase($databaseId, [ 'statements' => [ "CREATE TABLE Singers ( SingerId INT64 NOT NULL, FirstName STRING(1024), LastName STRING(1024), SingerInfo BYTES(MAX) ) PRIMARY KEY (SingerId)", "CREATE TABLE Albums ( SingerId INT64 NOT NULL, AlbumId INT64 NOT NULL, AlbumTitle STRING(MAX) ) PRIMARY KEY (SingerId, AlbumId), INTERLEAVE IN PARENT Singers ON DELETE CASCADE" ], 'encryptionConfig' => ['kmsKeyName' => $kmsKeyName] ]); print('Waiting for operation to complete...' . PHP_EOL); $operation->pollUntilComplete(); $database = $instance->database($databaseId); printf( 'Created database %s on instance %s with encryption key %s' . PHP_EOL, $databaseId, $instanceId, $database->info()['encryptionConfig']['kmsKeyName'] ); }
Python
Ruby
# project_id = "Your Google Cloud project ID" # instance_id = "Your Spanner instance ID" # database_id = "Your Spanner database ID" # kms_key_name = "Database eencryption KMS key" require "google/cloud/spanner" spanner = Google::Cloud::Spanner.new project: project_id instance = spanner.instance instance_id job = instance.create_database database_id, statements: [ "CREATE TABLE Singers ( SingerId INT64 NOT NULL, FirstName STRING(1024), LastName STRING(1024), SingerInfo BYTES(MAX) ) PRIMARY KEY (SingerId)", "CREATE TABLE Albums ( SingerId INT64 NOT NULL, AlbumId INT64 NOT NULL, AlbumTitle STRING(MAX) ) PRIMARY KEY (SingerId, AlbumId), INTERLEAVE IN PARENT Singers ON DELETE CASCADE" ], encryption_config: { kms_key_name: kms_key_name } puts "Waiting for create database operation to complete" job.wait_until_done! database = job.database puts "Database #{database.database_id} created with encryption key #{database.encryption_config.kms_key_name}"
使用している鍵バージョンを表示する
鍵バージョンに関する情報は、データベースの encryption_info
フィールドから取得します。
データベースの鍵バージョンが変更されても、変更は直ちには encryption_info
に伝播されません。変更が情報フィールドに反映されるまでの間に遅延が発生する場合があります。
Console
暗号化の情報は、[データベースの詳細] ページに表示されます。
gcloud
databases describe
または databases list
を呼び出して、データベースの encryption_info
を取得します。次に例を示します。
gcloud spanner databases describe example-db \
--project=my-spanner-project \
--instance=my-spanner-instance
出力は次のとおりです。
name: projects/my-project/instances/test-instance/databases/example-db
encryptionConfig:
kmsKeyName: projects/google.com:cloud-spanner-demo/locations/us-central1/keyRings/cmek_demo/cryptoKeys/backup-key
encryptionInfo:
encryptionType: CUSTOMER_MANAGED_ENCRYPTION
鍵を無効にする
使用中の鍵バージョンは、こちらの手順に沿って無効にしてください。
変更が有効になるまで待ちます。鍵の有効化が伝播されるまでには、最長で 3 時間を要する場合があります。
データにアクセスできなくなったことを確認します。
gcloud spanner databases execute-sql example-db \ --project=my-spanner-project \ --instance=my-spanner-instance \ --sql='SELECT * FROM Users'
KMS key required by the Spanner resource is not accessible.
のエラーが表示されます。
鍵を有効にする
鍵バージョンごとにこちらの手順に沿って、データベースで使用される鍵バージョンを有効にします。
変更が有効になるまで待ちます。鍵の有効化が伝播されるまでには、最長で 3 時間を要する場合があります。
データにアクセスできることを確認します。
gcloud spanner databases execute-sql example-db \ --project=my-spanner-project \ --instance=my-spanner-instance \ --sql='SELECT * FROM Users'
変更が有効になっていると、コマンドは正常に実行されます。
データベースのバックアップ
デフォルトでは、データベースから作成されたバックアップは、データベース自体と同じ暗号化構成ファイルを使用します。必要に応じて、別の暗号化構成ファイルをバックアップに指定することもできます。
バックアップを作成するには:
Console
Cloud Console で [データベースの詳細] ページに移動します。
[バックアップ / 復元] タブで、[作成] をクリックします。
バックアップ名を入力し、有効期限を選択します。
[顧客管理の暗号鍵(CMEK)を使用する] を選択し、プルダウン リストから鍵を選びます。
[作成] をクリックします。
[バックアップ] テーブルに、各バックアップの暗号化情報が表示されます。
gcloud
gcloud spanner backups create my-backup \
--project=my-spanner-project \
--instance=my-spanner-instance \
--database=example-db \
--retention-period=1y --async
作成されたバックアップが CMEK で暗号化されていることを確認するには:
gcloud spanner backups describe my-backup \
--project=my-spanner-project \
--instance=my-spanner-instance
C#
using Google.Cloud.Spanner.Admin.Database.V1;
using Google.Cloud.Spanner.Common.V1;
using Google.Protobuf.WellKnownTypes;
using System;
using System.Threading.Tasks;
public class CreateBackupWithEncryptionKeyAsyncSample
{
public async Task<Backup> CreateBackupWithEncryptionKeyAsync(string projectId, string instanceId, string databaseId, string backupId, CryptoKeyName kmsKeyName)
{
// Create a DatabaseAdminClient instance.
DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.Create();
// Create the CreateBackupRequest with encryption configuration.
CreateBackupRequest request = new CreateBackupRequest
{
ParentAsInstanceName = InstanceName.FromProjectInstance(projectId, instanceId),
BackupId = backupId,
Backup = new Backup
{
DatabaseAsDatabaseName = DatabaseName.FromProjectInstanceDatabase(projectId, instanceId, databaseId),
ExpireTime = DateTime.UtcNow.AddDays(14).ToTimestamp(),
},
EncryptionConfig = new CreateBackupEncryptionConfig
{
EncryptionType = CreateBackupEncryptionConfig.Types.EncryptionType.CustomerManagedEncryption,
KmsKeyNameAsCryptoKeyName = kmsKeyName,
},
};
// Execute the CreateBackup request.
var operation = await databaseAdminClient.CreateBackupAsync(request);
Console.WriteLine("Waiting for the operation to finish.");
// Poll until the returned long-running operation is complete.
var completedResponse = await operation.PollUntilCompletedAsync();
if (completedResponse.IsFaulted)
{
Console.WriteLine($"Error while creating backup: {completedResponse.Exception}");
throw completedResponse.Exception;
}
var backup = completedResponse.Result;
Console.WriteLine($"Backup {backup.Name} of size {backup.SizeBytes} bytes " +
$"was created at {backup.CreateTime} " +
$"using encryption key {kmsKeyName}");
return backup;
}
}
C++
Go
Java
ノード
PHP
use Google\Cloud\Spanner\Admin\Database\V1\CreateBackupEncryptionConfig;
use Google\Cloud\Spanner\Backup;
use Google\Cloud\Spanner\SpannerClient;
/**
* Create an encrypted backup.
* Example:
* ```
* create_backup_with_encryption_key($instanceId, $databaseId, $backupId, $kmsKeyName);
* ```
*
* @param string $instanceId The Spanner instance ID.
* @param string $databaseId The Spanner database ID.
* @param string $backupId The Spanner backup ID.
* @param string $kmsKeyName The KMS key used for encryption.
*/
function create_backup_with_encryption_key($instanceId, $databaseId, $backupId, $kmsKeyName)
{
$spanner = new SpannerClient();
$instance = $spanner->instance($instanceId);
$database = $instance->database($databaseId);
$expireTime = new \DateTime('+14 days');
$backup = $instance->backup($backupId);
$operation = $backup->create($database->name(), $expireTime, [
'encryptionConfig' => [
'kmsKeyName' => $kmsKeyName,
'encryptionType' => CreateBackupEncryptionConfig\EncryptionType::CUSTOMER_MANAGED_ENCRYPTION
]
]);
print('Waiting for operation to complete...' . PHP_EOL);
$operation->pollUntilComplete();
$backup->reload();
$ready = ($backup->state() == Backup::STATE_READY);
if ($ready) {
print('Backup is ready!' . PHP_EOL);
$info = $backup->info();
printf(
'Backup %s of size %d bytes was created at %s using encryption key %s' . PHP_EOL,
basename($info['name']), $info['sizeBytes'], $info['createTime'], $kmsKeyName);
} else {
print('Backup is not ready!' . PHP_EOL);
}
}
Python
Ruby
# project_id = "Your Google Cloud project ID"
# instance_id = "Your Spanner instance ID"
# database_id = "Your Spanner database ID"
# backup_id = "Your Spanner backup ID"
# kms_key_name = "Your backup encryption database KMS key"
require "google/cloud/spanner"
spanner = Google::Cloud::Spanner.new project: project_id
client = spanner.client instance_id, database_id
instance = spanner.instance instance_id
database = instance.database database_id
expire_time = Time.now + 14 * 24 * 3600 # 14 days from now
encryption_config = {
encryption_type: :CUSTOMER_MANAGED_ENCRYPTION,
kms_key_name: kms_key_name
}
job = database.create_backup backup_id, expire_time, version_time: version_time, encryption_config: encryption_config
puts "Backup operation in progress"
job.wait_until_done!
backup = instance.backup backup_id
puts "Backup #{backup.backup_id} of size #{backup.size_in_bytes} bytes was created at #{backup.create_time} using encryption key #{kms_key_name}"
バックアップから復元する
デフォルトでは、バックアップから復元されたデータベースはバックアップ自体と同じ暗号化構成を使用しますが、復元されるデータベースに別の暗号化構成を指定してこの動作をオーバーライドすることもできます。バックアップが CMEK によって保護されている場合、復号できるようにバックアップの作成に使用した鍵バージョンが利用可能な状態にする必要があります。
データベースを復元するには、次の操作を行います。
Console
Cloud Console の [インスタンスの詳細] ページに移動します。
[バックアップ / 復元] タブで、バックアップを選択して [復元] をクリックします。
復元するインスタンスを選択し、復元するデータベースに名前を付けます。
復元されたデータベースで CMEK を使用する場合は、[顧客管理の暗号鍵(CMEK)を使用する] を選択して、プルダウン リストから鍵を選びます。
gcloud
gcloud spanner databases restore --async \
--project=my-spanner-project \
--destination-instance=destination-instance \
--destination-database=example-db-restored \
--source-instance=my-spanner-instance \
--source-backup=my-backup
復元されたデータベースが CMEK で暗号化されていることを確認します。
gcloud spanner databases describe example-db-restored \
--project=my-spanner-project \
--instance=destination-instance
詳細については、バックアップからのデータベースの復元をご覧ください。
C#
using Google.Cloud.Spanner.Admin.Database.V1;
using Google.Cloud.Spanner.Common.V1;
using System;
using System.Threading.Tasks;
public class RestoreDatabaseWithEncryptionAsyncSample
{
public async Task<Database> RestoreDatabaseWithEncryptionAsync(string projectId, string instanceId, string databaseId, string backupId, CryptoKeyName kmsKeyName)
{
// Create a DatabaseAdminClient instance.
DatabaseAdminClient databaseAdminClient = DatabaseAdminClient.Create();
// Create the RestoreDatabaseRequest with encryption configuration.
RestoreDatabaseRequest request = new RestoreDatabaseRequest
{
ParentAsInstanceName = InstanceName.FromProjectInstance(projectId, instanceId),
DatabaseId = databaseId,
BackupAsBackupName = BackupName.FromProjectInstanceBackup(projectId, instanceId, backupId),
EncryptionConfig = new RestoreDatabaseEncryptionConfig
{
EncryptionType = RestoreDatabaseEncryptionConfig.Types.EncryptionType.CustomerManagedEncryption,
KmsKeyNameAsCryptoKeyName = kmsKeyName,
}
};
// Execute the RestoreDatabase request.
var operation = await databaseAdminClient.RestoreDatabaseAsync(request);
Console.WriteLine("Waiting for the operation to finish.");
// Poll until the returned long-running operation is complete.
var completedResponse = await operation.PollUntilCompletedAsync();
if (completedResponse.IsFaulted)
{
Console.WriteLine($"Error while restoring database: {completedResponse.Exception}");
throw completedResponse.Exception;
}
var database = completedResponse.Result;
var restoreInfo = database.RestoreInfo;
Console.WriteLine($"Database {restoreInfo.BackupInfo.SourceDatabase} " +
$"restored to {database.Name} " +
$"from backup {restoreInfo.BackupInfo.Backup} " +
$"using encryption key {database.EncryptionConfig.KmsKeyName}");
return database;
}
}
C++
Go
Java
ノード
PHP
use Google\Cloud\Spanner\Admin\Database\V1\RestoreDatabaseEncryptionConfig;
use Google\Cloud\Spanner\SpannerClient;
/**
* Restore a database from a backup.
* Example:
* ```
* restore_backup_with_encryption_key($instanceId, $databaseId, $backupId, $kmsKeyName);
* ```
* @param string $instanceId The Spanner instance ID.
* @param string $databaseId The Spanner database ID.
* @param string $backupId The Spanner backup ID.
* @param string $kmsKeyName The KMS key used for encryption.
*/
function restore_backup_with_encryption_key($instanceId, $databaseId, $backupId, $kmsKeyName)
{
$spanner = new SpannerClient();
$instance = $spanner->instance($instanceId);
$database = $instance->database($databaseId);
$backup = $instance->backup($backupId);
$operation = $database->restore($backup->name(), [
'encryptionConfig' => [
'kmsKeyName' => $kmsKeyName,
'encryptionType' => RestoreDatabaseEncryptionConfig\EncryptionType::CUSTOMER_MANAGED_ENCRYPTION
]
]);
// Wait for restore operation to complete.
$operation->pollUntilComplete();
// Newly created database has restore information.
$database->reload();
$restoreInfo = $database->info()['restoreInfo'];
$sourceDatabase = $restoreInfo['backupInfo']['sourceDatabase'];
$sourceBackup = $restoreInfo['backupInfo']['backup'];
$encryptionConfig = $database->info()['encryptionConfig'];
printf(
"Database %s restored from backup %s using encryption key %s" . PHP_EOL,
$sourceDatabase, $sourceBackup, $encryptionConfig['kmsKeyName']);
}
Python
Ruby
# project_id = "Your Google Cloud project ID"
# instance_id = "Your Spanner instance ID"
# database_id = "Your Spanner database ID of where to restore"
# backup_id = "Your Spanner backup ID"
# kms_key_name = "Your backup encryption database KMS key"
require "google/cloud/spanner"
spanner = Google::Cloud::Spanner.new project: project_id
instance = spanner.instance instance_id
backup = instance.backup backup_id
encryption_config = {
encryption_type: :CUSTOMER_MANAGED_ENCRYPTION,
kms_key_name: kms_key_name
}
job = backup.restore database_id, encryption_config: encryption_config
puts "Waiting for restore backup operation to complete"
job.wait_until_done!
database = job.database
restore_info = database.restore_info
puts "Database #{restore_info.backup_info.source_database_id} was restored to #{database.database_id} from backup #{restore_info.backup_info.backup_id} using encryption key #{database.encryption_config.kms_key_name}"
Cloud KMS 鍵の監査ログを表示する
プロジェクト内の Cloud KMS API でロギングが有効になっていることを確認します。
Cloud Console のログ エクスプローラに移動します。
クエリビルダーに次の行を追加して、ログエントリを Cloud KMS 鍵に限定します。
resource.type="cloudkms_cryptokey" resource.labels.location="my-kms-key-location" resource.labels.key_ring_id="my-kms-key-ring" resource.labels.crypto_key_id="my-kms-key"
通常のオペレーションでは、暗号化アクションと復号アクションは
INFO
重大度でログに記録されます。これらのエントリは、Spanner インスタンスのゾーンとしてログに記録され、約 5 分ごとに Cloud KMS 鍵をポーリングします。Spanner が鍵へのアクセスに失敗すると、そのオペレーションは
ERROR
としてログに記録されます。