Proteja uma base de dados com chaves de encriptação geridas pelo cliente (CMEK)

Esta página descreve como usar chaves de encriptação geridas pelo cliente (CMEK) criadas manualmente para o Spanner.

Para saber mais sobre a CMEK, consulte a vista geral das chaves de encriptação geridas pelo cliente (CMEK).

Crie uma base de dados com CMEK ativadas

  1. Crie uma chave no Cloud Key Management Service (Cloud KMS). Pode usar os seguintes tipos de chaves que têm diferentes níveis de proteção no Spanner:

    A chave tem de estar na mesma localização que a instância do Spanner. Por exemplo, se a configuração da instância do Spanner estiver em us-west1, a localização do conjunto de chaves do Cloud KMS também tem de ser us-west1.

    Nem todas as configurações de instâncias multirregionais do Spanner têm uma localização do conjunto de chaves do Cloud KMS correspondente. Para as bases de dados do Spanner em configurações de instâncias personalizadas, de dupla região ou multirregião, pode usar várias chaves do Cloud KMS regionais (de região única) para proteger a sua base de dados. Por exemplo:

    • Se a sua base de dados do Spanner estiver na configuração de instância multirregião nam14, pode criar chaves do Cloud KMS em us-east4, northamerica-northeast1 e us-east1.
    • Se a sua base de dados estiver numa configuração de instância personalizada que use nam3 como configuração de instância base com uma réplica só de leitura adicional em us-central2, pode criar chaves do Cloud KMS em us-east4, us-east1, us-central1 e us-central2.

    Opcional: para ver uma lista das localizações das réplicas na configuração da instância do Spanner, use o comando gcloud spanner instances get-locations:

    gcloud spanner instances get-locations <var>INSTANCE_ID</var>
    

    Para obter mais informações, consulte os seguintes recursos:

  2. Conceda acesso do Spanner à chave.

    1. No Cloud Shell, crie e apresente o agente de serviço ou apresente-o se a conta já existir:

      gcloud beta services identity create --service=spanner.googleapis.com \
          --project=PROJECT_ID

      Se lhe for pedido que instale o componente gcloud Beta Commands, escreva Y. Após a instalação, o comando é reiniciado automaticamente.

      O comando gcloud services identity cria ou obtém o agente de serviço que o Spanner pode usar para aceder à chave do Cloud KMS em seu nome.

      O ID da conta de serviço está formatado como um endereço de email:

      Service identity created: service-xxx@gcp-sa-spanner.iam.gserviceaccount.com
      
    2. Conceda a função encriptar/desencriptar do CryptoKey do Cloud KMS(cloudkms.cryptoKeyEncrypterDecrypter) à conta de serviço para cada região (--location) na configuração da instância do Spanner. Para o fazer, execute o comando gcloud kms keys add-iam-policybinding:

      gcloud kms keys add-iam-policy-binding KMS_KEY \
          --location KMS_KEY_LOCATION \
          --keyring KMS_KEY_RING \
          --project=PROJECT_ID \
          --member serviceAccount:service-xxx@gcp-sa-spanner.iam.gserviceaccount.com \
          --role roles/cloudkms.cryptoKeyEncrypterDecrypter

      Segue-se um exemplo de saída:

      Updated IAM policy for key [KMS_KEY]
      

      Se estiver a usar várias chaves do Cloud KMS para proteger a sua base de dados, execute o comando gcloud kms keys add-iam-policybinding para todas as chaves.

      Esta função garante que a conta de serviço tem autorização para encriptar e desencriptar com a chave do Cloud KMS. Para mais informações, consulte o artigo Autorizações e funções do Cloud KMS.

  3. Crie a base de dados e especifique a sua chave do Cloud KMS.

Consola

Use a consola para criar bases de dados em configurações de instâncias regionais.

  1. Na Google Cloud consola, aceda à página Instâncias.

    Aceda às instâncias do Spanner

  2. Clique na instância onde quer criar uma base de dados.

  3. Clique em Criar base de dados e preencha os campos obrigatórios.

  4. Clique em Mostrar opções de encriptação.

  5. Selecione Chave do Cloud KMS.

  6. Selecione uma chave na lista pendente.

    A lista de chaves está limitada ao Google Cloud projeto atual. Para usar uma chave de um Google Cloud projeto diferente, crie a base de dados com a CLI gcloud em vez daGoogle Cloud consola.

    Depois de criar a base de dados, pode verificar se a mesma tem a CMEK ativada na página Vista geral da base de dados.

    Captura de ecrã que mostra o tipo de encriptação e a chave de uma base de dados

gcloud

Para criar uma base de dados com a CMEK numa configuração de instância regional, personalizada ou multirregional, execute o comando gcloud spanner databases create:

gcloud spanner databases create DATABASE \
  --project=SPANNER_PROJECT_ID \
  --instance=INSTANCE_ID \
  --ddl="CREATE TABLE Users (Id INT64 NOT NULL, FirstName STRING(100) NOT NULL, LastName STRING(100) NOT NULL,) PRIMARY KEY (Id)" \
  --kms-project=KMS_PROJECT_ID \
  --kms-location=KMS_KEY_LOCATION \
  --kms-keyring=KMS_KEYRING \
  --kms-keys=KMS_KEY_1[, KMS_KEY_2 ... ]

Para verificar se uma base de dados tem a CMEK ativada, execute o comando gcloud spanner databases describe:

gcloud spanner databases describe DATABASE \
  --project=SPANNER_PROJECT_ID \
  --instance=INSTANCE_ID

As bases de dados com a CMEK ativada incluem um campo para encryptionConfig, conforme mostrado no exemplo de saída seguinte:

encryptionConfig:
  kmsKeyNames: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

Bibliotecas cliente

C#

Para criar uma base de dados com CMEK numa configuração de instância regional:


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;
    }
}

Para criar uma base de dados com a CMEK ativada numa configuração de instância de várias regiões:


using Google.Cloud.Spanner.Admin.Database.V1;
using Google.Cloud.Spanner.Common.V1;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

public class CreateDatabaseWithMultiRegionEncryptionAsyncSample
{
    public async Task<Database> CreateDatabaseWithMultiRegionEncryptionAsync(string projectId, string instanceId, string databaseId, IEnumerable<CryptoKeyName> kmsKeyNames)
    {
        // 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
            {
                KmsKeyNamesAsCryptoKeyNames = { kmsKeyNames },
            },
        };
        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 keys {string.Join(", ", kmsKeyNames)}");

        return database;
    }
}

C++

Para criar uma base de dados com CMEK numa configuração de instância regional:

void CreateDatabaseWithEncryptionKey(
    google::cloud::spanner_admin::DatabaseAdminClient client,
    std::string const& project_id, std::string const& instance_id,
    std::string const& database_id,
    google::cloud::KmsKeyName const& encryption_key) {
  google::cloud::spanner::Database database(project_id, instance_id,
                                            database_id);
  google::spanner::admin::database::v1::CreateDatabaseRequest request;
  request.set_parent(database.instance().FullName());
  request.set_create_statement("CREATE DATABASE `" + database.database_id() +
                               "`");
  request.add_extra_statements(R"""(
      CREATE TABLE Singers (
          SingerId   INT64 NOT NULL,
          FirstName  STRING(1024),
          LastName   STRING(1024),
          SingerInfo BYTES(MAX),
          FullName   STRING(2049)
              AS (ARRAY_TO_STRING([FirstName, LastName], " ")) STORED
      ) PRIMARY KEY (SingerId))""");
  request.add_extra_statements(R"""(
      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)""");
  request.mutable_encryption_config()->set_kms_key_name(
      encryption_key.FullName());
  auto db = client.CreateDatabase(request).get();
  if (!db) throw std::move(db).status();
  std::cout << "Database " << db->name() << " created";
  std::cout << " using encryption key " << encryption_key.FullName();
  std::cout << ".\n";
}

Para criar uma base de dados com a CMEK ativada numa configuração de instância de várias regiões:

void CreateDatabaseWithMRCMEK(
    google::cloud::spanner_admin::DatabaseAdminClient client,
    std::string const& project_id, std::string const& instance_id,
    std::string const& database_id,
    std::vector<google::cloud::KmsKeyName> const& encryption_keys) {
  google::cloud::spanner::Database database(project_id, instance_id,
                                            database_id);
  google::spanner::admin::database::v1::CreateDatabaseRequest request;
  request.set_parent(database.instance().FullName());
  request.set_create_statement("CREATE DATABASE `" + database.database_id() +
                               "`");
  request.add_extra_statements(R"""(
      CREATE TABLE Singers (
          SingerId   INT64 NOT NULL,
          FirstName  STRING(1024),
          LastName   STRING(1024),
          SingerInfo BYTES(MAX),
          FullName   STRING(2049)
              AS (ARRAY_TO_STRING([FirstName, LastName], " ")) STORED
      ) PRIMARY KEY (SingerId))""");
  request.add_extra_statements(R"""(
      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)""");
  for (google::cloud::KmsKeyName const& encryption_key : encryption_keys) {
    request.mutable_encryption_config()->add_kms_key_names(
        encryption_key.FullName());
  }
  auto db = client.CreateDatabase(request).get();
  if (!db) throw std::move(db).status();
  std::cout << "Database " << db->name() << " created";
  PrintKmsKeys(encryption_keys);
}

Ir

Para criar uma base de dados com CMEK numa configuração de instância regional:

import (
	"context"
	"fmt"
	"io"
	"regexp"

	database "cloud.google.com/go/spanner/admin/database/apiv1"
	adminpb "cloud.google.com/go/spanner/admin/database/apiv1/databasepb"
)

func createDatabaseWithCustomerManagedEncryptionKey(ctx context.Context, w io.Writer, db, kmsKeyName string) error {
	// db = `projects/<project>/instances/<instance-id>/database/<database-id>`
	// kmsKeyName = `projects/<project>/locations/<location>/keyRings/<key_ring>/cryptoKeys/<kms_key_name>`
	matches := regexp.MustCompile("^(.+)/databases/(.+)$").FindStringSubmatch(db)
	if matches == nil || len(matches) != 3 {
		return fmt.Errorf("createDatabaseWithCustomerManagedEncryptionKey: invalid database id %q", db)
	}
	instanceName := matches[1]
	databaseId := matches[2]

	adminClient, err := database.NewDatabaseAdminClient(ctx)
	if err != nil {
		return fmt.Errorf("createDatabaseWithCustomerManagedEncryptionKey.NewDatabaseAdminClient: %w", err)
	}
	defer adminClient.Close()

	// Create a database with tables using a Customer Managed Encryption Key
	req := adminpb.CreateDatabaseRequest{
		Parent:          instanceName,
		CreateStatement: "CREATE DATABASE `" + databaseId + "`",
		ExtraStatements: []string{
			`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: &adminpb.EncryptionConfig{KmsKeyName: kmsKeyName},
	}
	op, err := adminClient.CreateDatabase(ctx, &req)
	if err != nil {
		return fmt.Errorf("createDatabaseWithCustomerManagedEncryptionKey.CreateDatabase: %w", err)
	}
	dbObj, err := op.Wait(ctx)
	if err != nil {
		return fmt.Errorf("createDatabaseWithCustomerManagedEncryptionKey.Wait: %w", err)
	}
	fmt.Fprintf(w, "Created database [%s] using encryption key %q\n", dbObj.Name, dbObj.EncryptionConfig.KmsKeyName)
	return nil
}

Para criar uma base de dados com a CMEK ativada numa configuração de instância de várias regiões:


import (
	"context"
	"fmt"
	"io"

	database "cloud.google.com/go/spanner/admin/database/apiv1"
	adminpb "cloud.google.com/go/spanner/admin/database/apiv1/databasepb"
)

// createDatabaseWithCustomerManagedMultiRegionEncryptionKey creates a new database with tables using a Customer Managed Multi-Region Encryption Key.
func createDatabaseWithCustomerManagedMultiRegionEncryptionKey(ctx context.Context, w io.Writer, projectID, instanceID, databaseID string, kmsKeyNames []string) error {
	// projectID = `my-project`
	// instanceID = `my-instance`
	// databaseID = `my-database`
	// kmsKeyNames := []string{"projects/my-project/locations/locations/<location1>/keyRings/<keyRing>/cryptoKeys/<keyId>",
	//	 "projects/my-project/locations/locations/<location2>/keyRings/<keyRing>/cryptoKeys/<keyId>",
	//	 "projects/my-project/locations/locations/<location3>/keyRings/<keyRing>/cryptoKeys/<keyId>",
	// }

	adminClient, err := database.NewDatabaseAdminClient(ctx)
	if err != nil {
		return fmt.Errorf("createDatabaseWithCustomerManagedMultiRegionEncryptionKey.NewDatabaseAdminClient: %w", err)
	}
	defer adminClient.Close()

	// Create a database with tables using a Customer Managed Multi-Region Encryption Key
	req := adminpb.CreateDatabaseRequest{
		Parent:          fmt.Sprintf("projects/%s/instances/%s", projectID, instanceID),
		CreateStatement: "CREATE DATABASE `" + databaseID + "`",
		ExtraStatements: []string{
			`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: &adminpb.EncryptionConfig{KmsKeyNames: kmsKeyNames},
	}
	op, err := adminClient.CreateDatabase(ctx, &req)
	if err != nil {
		return fmt.Errorf("createDatabaseWithCustomerManagedMultiRegionEncryptionKey.CreateDatabase: %w", err)
	}
	dbObj, err := op.Wait(ctx)
	if err != nil {
		return fmt.Errorf("createDatabaseWithCustomerManagedMultiRegionEncryptionKey.Wait: %w", err)
	}
	fmt.Fprintf(w, "Created database [%s] using multi-region encryption keys %q\n", dbObj.Name, dbObj.EncryptionConfig.GetKmsKeyNames())
	return nil
}

Java

Para criar uma base de dados com CMEK numa configuração de instância regional:


import com.google.cloud.spanner.Spanner;
import com.google.cloud.spanner.SpannerExceptionFactory