Utiliser des clés de chiffrement gérées par le client (CMEK)

Cette page explique comment utiliser les clés de chiffrement gérées par le client (CMEK, Customer-Managed Encryption Keys) pour Cloud Spanner.

Pour en savoir plus sur le chiffrement CMEK, consultez la page Clés de chiffrement gérées par le client (CMEK, Customer-Managed Encryption Keys).

Créer une base de données pour laquelle CMEK est activé

  1. Créez une clé dans Cloud Key Management Service (Cloud KMS). Cloud Spanner est compatible avec:

    La clé doit être dans le même emplacement que votre instance Cloud Spanner. Par exemple, si la configuration de votre instance Cloud Spanner est us-west1, l'emplacement de votre trousseau de clés doit également être us-west1.

    Les configurations d'instances multirégionales dans Cloud Spanner ne comportent pas toutes un emplacement de trousseau de clés correspondant dans Cloud KMS. Vous ne pourrez pas créer de bases de données activées avec CMEK dans ces instances Cloud Spanner.

    Si vous disposez déjà d'une clé Cloud KMS au bon emplacement, vous pouvez ignorer cette étape.

  2. Accordez à Cloud Spanner l'accès à la clé.

    1. Cette commande crée et affiche le compte de service géré par Google, ou l'affiche si le compte existe déjà.

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

      L'ID du compte de service est au format d'une adresse e-mail:

      Service identity created: service-xxx@gcp-sa-spanner.iam.gserviceaccount.com
      

      La commande gcloud services identity crée ou obtient le compte de service géré par Google que Cloud Spanner peut utiliser pour accéder au cloud. clé KMS en votre nom.

    2. Attribuez le rôle cloudkms.cryptoKeyEncrypterDecrypter au compte de service.

      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
      

      Les informations suivantes s'affichent:

      Updated IAM policy for key [my-kms-key]
      

      Ce rôle garantit que le compte de service est autorisé à chiffrer et déchiffrer des données avec la clé Cloud KMS. Pour en savoir plus, consultez la page Autorisations et rôles Cloud KMS.

  3. Créez la base de données et spécifiez votre clé Cloud KMS.

    Console

    1. Accédez à la page Instances de Cloud Spanner dans Google Cloud Console.

    Accéder à Cloud Console

    2. Cliquez sur le nom de l'instance dans laquelle créer la base de données.

    3. Cliquez sur Créer une base de données et renseignez les champs obligatoires.

    4. Cliquez sur Afficher les options de chiffrement.

    5. Sélectionnez Utiliser une clé de chiffrement gérée par le client (CMEK).

    6. Sélectionnez votre clé dans la liste déroulante.

    La liste des clés est limitée au projet Google Cloud en cours. Pour utiliser une clé provenant d'un autre projet Google Cloud, créez la base de données à l'aide de gcloud au lieu de Cloud Console.

    Une fois la base de données créée, vous pouvez vérifier qu'elle est activée à l'aide de CMEK en consultant la page Détails de la base de données.

    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
    

    Pour vérifier qu'une base de données est CMEK, procédez comme suit:

    gcloud spanner databases describe example-db \
        --project=my-spanner-project \
        --instance=my-spanner-instance
    

    Les bases de données pour lesquelles CMEK est activé incluent un champ pour encryptionConfig, comme indiqué dans l'exemple suivant:

    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++

    void CreateDatabaseWithEncryptionKey(
        google::cloud::spanner::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);
      std::vector<std::string> extra_statements;
      extra_statements.emplace_back(R"""(
          CREATE TABLE Singers (
              SingerId   INT64 NOT NULL,
              FirstName  STRING(1024),
              LastName   STRING(1024),
              SingerInfo BYTES(MAX)
          ) PRIMARY KEY (SingerId))""");
      extra_statements.emplace_back(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)""");
      auto db =
          client
              .CreateDatabase(
                  database, std::move(extra_statements),
                  google::cloud::spanner::CustomerManagedEncryption(encryption_key))
              .get();
      if (!db) throw std::runtime_error(db.status().message());
      std::cout << "Database " << db->name() << " created";
      std::cout << " using encryption key " << encryption_key.FullName();
      std::cout << ".\n";
    }

    Go

    import (
    	"context"
    	"fmt"
    	"io"
    	"regexp"
    
    	database "cloud.google.com/go/spanner/admin/database/apiv1"
    	adminpb "google.golang.org/genproto/googleapis/spanner/admin/database/v1"
    )
    
    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: %v", 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: %v", err)
    	}
    	dbObj, err := op.Wait(ctx)
    	if err != nil {
    		return fmt.Errorf("createDatabaseWithCustomerManagedEncryptionKey.Wait: %v", err)
    	}
    	fmt.Fprintf(w, "Created database [%s] using encryption key %q\n", dbObj.Name, dbObj.EncryptionConfig.KmsKeyName)
    	return nil
    }
    

    Java

    
    import com.google.api.gax.longrunning.OperationFuture;
    import com.google.cloud.spanner.Database;
    import com.google.cloud.spanner.DatabaseAdminClient;
    import com.google.cloud.spanner.DatabaseId;
    import com.google.cloud.spanner.Spanner;
    import com.google.cloud.spanner.SpannerExceptionFactory;
    import com.google.cloud.spanner.SpannerOptions;
    import com.google.cloud.spanner.encryption.EncryptionConfigs;
    import com.google.spanner.admin.database.v1.CreateDatabaseMetadata;
    import java.util.Arrays;
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.TimeoutException;
    
    public class CreateDatabaseWithEncryptionKey {
    
      static void createDatabaseWithEncryptionKey() {
        // TODO(developer): Replace these variables before running the sample.
        String projectId = "my-project";
        String instanceId = "my-instance";
        String databaseId = "my-database";
        String kmsKeyName =
            "projects/" + projectId + "/locations/<location>/keyRings/<keyRing>/cryptoKeys/<keyId>";
    
        try (Spanner spanner =
            SpannerOptions.newBuilder().setProjectId(projectId).build().getService()) {
          DatabaseAdminClient adminClient = spanner.getDatabaseAdminClient();
          createDatabaseWithEncryptionKey(
              adminClient,
              projectId,
              instanceId,
              databaseId,
              kmsKeyName);
        }
      }
    
      static Void createDatabaseWithEncryptionKey(DatabaseAdminClient adminClient,
          String projectId, String instanceId, String databaseId, String kmsKeyName) {
        final Database databaseToCreate = adminClient
            .newDatabaseBuilder(DatabaseId.of(projectId, instanceId, databaseId))
            .setEncryptionConfig(EncryptionConfigs.customerManagedEncryption(kmsKeyName))
            .build();
        final OperationFuture<Database, CreateDatabaseMetadata> operation = adminClient
            .createDatabase(databaseToCreate, Arrays.asList(
                "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"
            ));
        try {
          System.out.println("Waiting for operation to complete...");
          Database createdDatabase = operation.get(120, TimeUnit.SECONDS);
    
          System.out.printf(
              "Database %s created with encryption key %s%n",
              createdDatabase.getId(),
              createdDatabase.getEncryptionConfig().getKmsKeyName()
          );
        } catch (ExecutionException e) {
          // If the operation failed during execution, expose the cause.
          throw SpannerExceptionFactory.asSpannerException(e.getCause());
        } catch (InterruptedException e) {
          // Throw when a thread is waiting, sleeping, or otherwise occupied,
          // and the thread is interrupted, either before or during the activity.
          throw SpannerExceptionFactory.propagateInterrupt(e);
        } catch (TimeoutException e) {
          // If the operation timed out propagates the timeout
          throw SpannerExceptionFactory.propagateTimeout(e);
        }
        return null;
      }
    }

    Nœud

    // Imports the Google Cloud client library
    const {Spanner} = require('@google-cloud/spanner');
    
    /**
     * TODO(developer): Uncomment the following lines before running the sample.
     */
    // const projectId = 'my-project-id';
    // const instanceId = 'my-instance';
    // const databaseId = 'my-database';
    // const keyName =
    //   'projects/my-project-id/my-region/keyRings/my-key-ring/cryptoKeys/my-key';
    
    // Creates a client
    const spanner = new Spanner({
      projectId: projectId,
    });
    
    // Gets a reference to a Cloud Spanner instance
    const instance = spanner.instance(instanceId);
    
    const request = {
      encryptionConfig: {
        kmsKeyName: keyName,
      },
    };
    
    // Creates a database
    const [database, operation] = await instance.createDatabase(
      databaseId,
      request
    );
    
    console.log(`Waiting for operation on ${database.id} to complete...`);
    await operation.promise();
    
    console.log(`Created database ${databaseId} on instance ${instanceId}.`);
    
    // Get encryption key
    const [data] = await database.get();
    
    console.log(
      `Database encrypted with key ${data.metadata.encryptionConfig.kmsKeyName}.`
    );

    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

    def create_database_with_encryption_key(instance_id, database_id, kms_key_name):
        """Creates a database with tables using a Customer Managed Encryption Key (CMEK)."""
        spanner_client = spanner.Client()
        instance = spanner_client.instance(instance_id)
    
        database = instance.database(
            database_id,
            ddl_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},
        )
    
        operation = database.create()
    
        print("Waiting for operation to complete...")
        operation.result(120)
    
        print("Database {} created with encryption key {}".format(
            database.name, database.encryption_config.kms_key_name))
    
    

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

Afficher les versions de clé en cours d'utilisation

Les informations sur les versions de clé proviennent du champ encryption_info de la base de données.

Lorsque la version de clé d'une base de données change, la modification n'est pas immédiatement propagée à encryption_info. Il peut y avoir un délai avant que la modification ne soit reflétée dans le champ d'informations.

Console

Les informations de chiffrement sont affichées sur la page Détails de la base de données.

gcloud

Vous pouvez obtenir la valeur encryption_info d'une base de données en appelant databases describe ou databases list. Exemple :

gcloud spanner databases describe example-db \
    --project=my-spanner-project \
    --instance=my-spanner-instance

Voici le résultat :

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

Désactiver la clé

  1. Désactivez la ou les versions de clé utilisées en suivant ces instructions pour chaque version.

  2. Attendez que la modification soit prise en compte. La propagation d'une clé peut prendre jusqu'à trois heures.

    Vérifiez que les données ne sont plus accessibles:

    gcloud spanner databases execute-sql example-db \
        --project=my-spanner-project \
        --instance=my-spanner-instance \
        --sql='SELECT * FROM Users'
    

    Vous verrez l'erreur suivante : KMS key required by the Spanner resource is not accessible.

Activer la clé

  1. Activez les versions de clé utilisées par la base de données en suivant ces instructions pour chaque version.

  2. Attendez que la modification soit prise en compte. L'activation d'une clé peut prendre jusqu'à trois heures.

    Vérifiez que les données sont accessibles:

    gcloud spanner databases execute-sql example-db \
        --project=my-spanner-project \
        --instance=my-spanner-instance \
        --sql='SELECT * FROM Users'
    

    Si la modification a pris effet, la commande est exécutée correctement.

Sauvegarder une base de données

Par défaut, les sauvegardes créées à partir d'une base de données utilisent la même configuration de chiffrement que la base de données elle-même. Vous pouvez éventuellement spécifier une configuration de chiffrement différente pour une sauvegarde.

Pour créer une sauvegarde, procédez comme suit:

Console

  1. Accédez à la page Détails de la base de données dans Cloud Console.

    Accéder à Cloud Console

  2. Dans l'onglet Sauvegarde/Restauration, cliquez sur Créer.

  3. Saisissez un nom de sauvegarde et sélectionnez une date d'expiration.

  4. Sélectionnez Utiliser une clé de chiffrement gérée par le client (CMEK) et choisissez une clé dans la liste déroulante.

  5. Cliquez sur Create (Créer).

Le tableau Sauvegardes affiche les informations de chiffrement pour chaque sauvegarde.

gcloud

gcloud spanner backups create my-backup \
    --project=my-spanner-project \
    --instance=my-spanner-instance \
    --database=example-db  \
    --retention-period=1y --async

Pour vérifier que la sauvegarde créée est chiffrée par le chiffrement 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++

void CreateBackupWithEncryptionKey(
    google::cloud::spanner::DatabaseAdminClient client,
    std::string const& project_id, std::string const& instance_id,
    std::string const& database_id, std::string const& backup_id,
    google::cloud::spanner::Timestamp expire_time,
    google::cloud::spanner::Timestamp version_time,
    google::cloud::KmsKeyName const& encryption_key) {
  google::cloud::spanner::Database database(project_id, instance_id,
                                            database_id);
  auto backup =
      client
          .CreateBackup(
              database, backup_id, expire_time, version_time,
              google::cloud::spanner::CustomerManagedEncryption(encryption_key))
          .get();
  if (!backup) throw std::runtime_error(backup.status().message());
  std::cout
      << "Backup " << backup->name() << " of " << backup->database()
      << " of size " << backup->size_bytes() << " bytes"
      << " was created at "
      << google::cloud::spanner::MakeTimestamp(backup->create_time()).value()
      << " using encryption key " << encryption_key.FullName() << ".\n";
}

Go


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

	database "cloud.google.com/go/spanner/admin/database/apiv1"
	pbt "github.com/golang/protobuf/ptypes/timestamp"
	adminpb "google.golang.org/genproto/googleapis/spanner/admin/database/v1"
)

func createBackupWithCustomerManagedEncryptionKey(ctx context.Context, w io.Writer, db, backupID, kmsKeyName string) error {
	// db = `projects/<project>/instances/<instance-id>/database/<database-id>`
	// backupID = `my-backup-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("createBackupWithCustomerManagedEncryptionKey: invalid database id %q", db)
	}
	instanceName := matches[1]

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

	expireTime := time.Now().AddDate(0, 0, 14)
	// Create a backup for a database using a Customer Managed Encryption Key
	req := adminpb.CreateBackupRequest{
		Parent:   instanceName,
		BackupId: backupID,
		Backup: &adminpb.Backup{
			Database:   db,
			ExpireTime: &pbt.Timestamp{Seconds: expireTime.Unix(), Nanos: int32(expireTime.Nanosecond())},
		},
		EncryptionConfig: &adminpb.CreateBackupEncryptionConfig{
			KmsKeyName:     kmsKeyName,
			EncryptionType: adminpb.CreateBackupEncryptionConfig_CUSTOMER_MANAGED_ENCRYPTION,
		},
	}
	op, err := adminClient.CreateBackup(ctx, &req)
	if err != nil {
		return fmt.Errorf("createBackupWithCustomerManagedEncryptionKey.CreateBackup: %v", err)
	}
	// Wait for backup operation to complete.
	backup, err := op.Wait(ctx)
	if err != nil {
		return fmt.Errorf("createBackupWithCustomerManagedEncryptionKey.Wait: %v", err)
	}

	// Get the name, create time, backup size and encryption key from the backup.
	backupCreateTime := time.Unix(backup.CreateTime.Seconds, int64(backup.CreateTime.Nanos))
	fmt.Fprintf(w,
		"Backup %s of size %d bytes was created at %s using encryption key %s\n",
		backup.Name,
		backup.SizeBytes,
		backupCreateTime.Format(time.RFC3339),
		kmsKeyName)
	return nil
}

Java


import com.google.api.gax.longrunning.OperationFuture;
import com.google.cloud.Timestamp;
import com.google.cloud.spanner.Backup;
import com.google.cloud.spanner.BackupId;
import com.google.cloud.spanner.DatabaseAdminClient;
import com.google.cloud.spanner.DatabaseId;
import com.google.cloud.spanner.Spanner;
import com.google.cloud.spanner.SpannerExceptionFactory;
import com.google.cloud.spanner.SpannerOptions;
import com.google.cloud.spanner.encryption.EncryptionConfigs;
import com.google.spanner.admin.database.v1.CreateBackupMetadata;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.threeten.bp.LocalDateTime;
import org.threeten.bp.OffsetDateTime;

public class CreateBackupWithEncryptionKey {

  static void createBackupWithEncryptionKey() throws InterruptedException {
    // TODO(developer): Replace these variables before running the sample.
    String projectId = "my-project";
    String instanceId = "my-instance";
    String databaseId = "my-database";
    String backupId = "my-backup";
    String kmsKeyName =
        "projects/" + projectId + "/locations/<location>/keyRings/<keyRing>/cryptoKeys/<keyId>";

    try (Spanner spanner =
        SpannerOptions.newBuilder().setProjectId(projectId).build().getService()) {
      DatabaseAdminClient adminClient = spanner.getDatabaseAdminClient();
      createBackupWithEncryptionKey(
          adminClient,
          projectId,
          instanceId,
          databaseId,
          backupId,
          kmsKeyName);
    }
  }

  static Void createBackupWithEncryptionKey(DatabaseAdminClient adminClient,
      String projectId, String instanceId, String databaseId, String backupId, String kmsKeyName)
      throws InterruptedException {
    // Set expire time to 14 days from now.
    final Timestamp expireTime = Timestamp.ofTimeMicroseconds(TimeUnit.MICROSECONDS.convert(
        System.currentTimeMillis() + TimeUnit.DAYS.toMillis(14), TimeUnit.MILLISECONDS));
    final Backup backupToCreate = adminClient
        .newBackupBuilder(BackupId.of(projectId, instanceId, backupId))
        .setDatabase(DatabaseId.of(projectId, instanceId, databaseId))
        .setExpireTime(expireTime)
        .setEncryptionConfig(EncryptionConfigs.customerManagedEncryption(kmsKeyName))
        .build();
    final OperationFuture<Backup, CreateBackupMetadata> operation = adminClient
        .createBackup(backupToCreate);

    Backup backup;
    try {
      System.out.println("Waiting for operation to complete...");
      backup = operation.get(1200, TimeUnit.SECONDS);
    } catch (ExecutionException e) {
      // If the operation failed during execution, expose the cause.
      throw SpannerExceptionFactory.asSpannerException(e.getCause());
    } catch (InterruptedException e) {
      // Throw when a thread is waiting, sleeping, or otherwise occupied,
      // and the thread is interrupted, either before or during the activity.
      throw SpannerExceptionFactory.propagateInterrupt(e);
    } catch (TimeoutException e) {
      // If the operation timed out propagates the timeout
      throw SpannerExceptionFactory.propagateTimeout(e);
    }

    System.out.printf(
        "Backup %s of size %d bytes was created at %s using encryption key %s%n",
        backup.getId().getName(),
        backup.getSize(),
        LocalDateTime.ofEpochSecond(
            backup.getProto().getCreateTime().getSeconds(),
            backup.getProto().getCreateTime().getNanos(),
            OffsetDateTime.now().getOffset()),
        kmsKeyName
    );

    return null;
  }
}

Nœud

// Imports the Google Cloud client library and precise date library
const {Spanner} = require('@google-cloud/spanner');
const {PreciseDate} = require('@google-cloud/precise-date');

/**
 * TODO(developer): Uncomment the following lines before running the sample.
 */
// const projectId = 'my-project-id';
// const instanceId = 'my-instance';
// const databaseId = 'my-database';
// const backupId = 'my-backup';
// const versionTime = Date.now() - 1000 * 60 * 60 * 24; // One day ago
// const keyName =
//   'projects/my-project-id/my-region/keyRings/my-key-ring/cryptoKeys/my-key';

// Creates a client
const spanner = new Spanner({
  projectId: projectId,
});

// Gets a reference to a Cloud Spanner instance and database
const instance = spanner.instance(instanceId);
const database = instance.database(databaseId);

const backup = instance.backup(backupId);

// Creates a new backup of the database
try {
  console.log(`Creating backup of database ${database.formattedName_}.`);
  const databasePath = database.formattedName_;
  // Expire backup 14 days in the future
  const expireTime = Date.now() + 1000 * 60 * 60 * 24 * 14;
  // Create a backup of the state of the database at the current time.
  const [, operation] = await backup.create({
    databasePath: databasePath,
    expireTime: expireTime,
    encryptionConfig: {
      encryptionType: 'CUSTOMER_MANAGED_ENCRYPTION',
      kmsKeyName: keyName,
    },
  });

  console.log(`Waiting for backup ${backup.formattedName_} to complete...`);
  await operation.promise();

  // Verify backup is ready
  const [backupInfo] = await backup.getMetadata();
  if (backupInfo.state === 'READY') {
    console.log(
      `Backup ${backupInfo.name} of size ` +
        `${backupInfo.sizeBytes} bytes was created at ` +
        `${new PreciseDate(backupInfo.createTime).toISOString()} ` +
        `using encryption key ${backupInfo.encryptionInfo.kmsKeyVersion}`
    );
  } else {
    console.error('ERROR: Backup is not ready.');
  }
} catch (err) {
  console.error('ERROR:', err);
} finally {
  // Close the database when finished.
  await database.close();
}

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

def create_backup_with_encryption_key(instance_id, database_id, backup_id, kms_key_name):
    """Creates a backup for a database using a Customer Managed Encryption Key (CMEK)."""
    from google.cloud.spanner_admin_database_v1 import CreateBackupEncryptionConfig

    spanner_client = spanner.Client()
    instance = spanner_client.instance(instance_id)
    database = instance.database(database_id)

    # Create a backup
    expire_time = datetime.utcnow() + timedelta(days=14)
    encryption_config = {
        'encryption_type': CreateBackupEncryptionConfig.EncryptionType.CUSTOMER_MANAGED_ENCRYPTION,
        'kms_key_name': kms_key_name,
    }
    backup = instance.backup(backup_id, database=database, expire_time=expire_time, encryption_config=encryption_config)
    operation = backup.create()

    # Wait for backup operation to complete.
    operation.result(1200)

    # Verify that the backup is ready.
    backup.reload()
    assert backup.is_ready() is True

    # Get the name, create time, backup size and encryption key.
    backup.reload()
    print(
        "Backup {} of size {} bytes was created at {} using encryption key {}".format(
            backup.name, backup.size_bytes, backup.create_time, kms_key_name
        )
    )

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

Restaurer à partir d'une sauvegarde

Par défaut, les bases de données restaurées à partir d'une sauvegarde utilisent la même configuration de chiffrement que la sauvegarde elle-même, mais vous pouvez ignorer ce comportement en spécifiant une autre configuration de chiffrement pour la base de données restaurée. Si la sauvegarde est protégée par une clé CMEK, la version de clé utilisée pour créer la sauvegarde doit être disponible afin de pouvoir la déchiffrer.

Pour restaurer une base de données, procédez comme suit :

Console

  1. Accédez à la page Détails de l'instance dans Cloud Console.

    Accéder à Cloud Console

  2. Dans l'onglet Sauvegarde/Restauration, sélectionnez une sauvegarde, puis cliquez sur Restaurer.

  3. Sélectionnez l'instance à restaurer et nommez la base de données restaurée.

  4. Si vous souhaitez utiliser le chiffrement CMEK avec la base de données restaurée, sélectionnez Utiliser une clé de chiffrement gérée par le client (CMEK) et sélectionnez une clé dans la liste déroulante.

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

Vérifiez que la base de données restaurée est chiffrée par CMEK:

gcloud spanner databases describe example-db-restored \
    --project=my-spanner-project \
    --instance=destination-instance

Pour en savoir plus, consultez la page Restaurer une base de données à partir d'une sauvegarde.

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++

void RestoreDatabaseWithEncryptionKey(
    google::cloud::spanner::DatabaseAdminClient client,
    std::string const& project_id, std::string const& instance_id,
    std::string const& database_id, std::string const& backup_id,
    google::cloud::KmsKeyName const& encryption_key) {
  google::cloud::spanner::Database database(project_id, instance_id,
                                            database_id);
  google::cloud::spanner::Backup backup(database.instance(), backup_id);
  auto restored_db =
      client
          .RestoreDatabase(
              database, backup,
              google::cloud::spanner::CustomerManagedEncryption(encryption_key))
          .get();
  if (!restored_db) {
    throw std::runtime_error(restored_db.status().message());
  }
  std::cout << "Database";
  if (restored_db->restore_info().source_type() ==
      google::spanner::admin::database::v1::BACKUP) {
    auto const& backup_info = restored_db->restore_info().backup_info();
    std::cout << " " << backup_info.source_database() << " as of "
              << google::cloud::spanner::MakeTimestamp(
                     backup_info.version_time())
                     .value();
  }
  std::cout << " restored to " << restored_db->name();
  std::cout << " from backup " << backup.FullName();
  std::cout << " using encryption key " << encryption_key.FullName();
  std::cout << ".\n";
}

Go


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

	database "cloud.google.com/go/spanner/admin/database/apiv1"
	adminpb "google.golang.org/genproto/googleapis/spanner/admin/database/v1"
)

func restoreBackupWithCustomerManagedEncryptionKey(ctx context.Context, w io.Writer, db, backupID, kmsKeyName string) error {
	// db = `projects/<project>/instances/<instance-id>/database/<database-id>`
	// backupID = `my-backup-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("restoreBackupWithCustomerManagedEncryptionKey: invalid database id %q", db)
	}
	instanceName := matches[1]
	databaseID := matches[2]
	backupName := instanceName + "/backups/" + backupID

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

	// Restore a database from a backup using a Customer Managed Encryption Key.
	restoreOp, err := adminClient.RestoreDatabase(ctx, &adminpb.RestoreDatabaseRequest{
		Parent:     instanceName,
		DatabaseId: databaseID,
		Source: &adminpb.RestoreDatabaseRequest_Backup{
			Backup: backupName,
		},
		EncryptionConfig: &adminpb.RestoreDatabaseEncryptionConfig{
			EncryptionType: adminpb.RestoreDatabaseEncryptionConfig_CUSTOMER_MANAGED_ENCRYPTION,
			KmsKeyName:     kmsKeyName,
		},
	})
	if err != nil {
		return fmt.Errorf("restoreBackupWithCustomerManagedEncryptionKey.RestoreDatabase: %v", err)
	}
	// Wait for restore operation to complete.
	restoredDatabase, err := restoreOp.Wait(ctx)
	if err != nil {
		return fmt.Errorf("restoreBackupWithCustomerManagedEncryptionKey.Wait: %v", err)
	}
	// Get the information from the newly restored database.
	backupInfo := restoredDatabase.RestoreInfo.GetBackupInfo()
	fmt.Fprintf(w, "Database %s restored from backup %s using encryption key %s\n",
		backupInfo.SourceDatabase,
		backupInfo.Backup,
		restoredDatabase.EncryptionConfig.KmsKeyName)

	return nil
}

Java


import com.google.api.gax.longrunning.OperationFuture;
import com.google.cloud.spanner.BackupId;
import com.google.cloud.spanner.Database;
import com.google.cloud.spanner.DatabaseAdminClient;
import com.google.cloud.spanner.DatabaseId;
import com.google.cloud.spanner.Restore;
import com.google.cloud.spanner.Spanner;
import com.google.cloud.spanner.SpannerExceptionFactory;
import com.google.cloud.spanner.SpannerOptions;
import com.google.cloud.spanner.encryption.EncryptionConfigs;
import com.google.spanner.admin.database.v1.RestoreDatabaseMetadata;
import java.util.concurrent.ExecutionException;

public class RestoreBackupWithEncryptionKey {

  static void restoreBackupWithEncryptionKey() {
    // TODO(developer): Replace these variables before running the sample.
    String projectId = "my-project";
    String instanceId = "my-instance";
    String databaseId = "my-database";
    String backupId = "my-backup";
    String kmsKeyName =
        "projects/" + projectId + "/locations/<location>/keyRings/<keyRing>/cryptoKeys/<keyId>";

    try (Spanner spanner =
        SpannerOptions.newBuilder().setProjectId(projectId).build().getService()) {
      DatabaseAdminClient adminClient = spanner.getDatabaseAdminClient();
      restoreBackupWithEncryptionKey(
          adminClient,
          projectId,
          instanceId,
          backupId,
          databaseId,
          kmsKeyName);
    }
  }

  static Void restoreBackupWithEncryptionKey(DatabaseAdminClient adminClient,
      String projectId, String instanceId, String backupId, String restoreId, String kmsKeyName) {
    final Restore restore = adminClient
        .newRestoreBuilder(
            BackupId.of(projectId, instanceId, backupId),
            DatabaseId.of(projectId, instanceId, restoreId))
        .setEncryptionConfig(EncryptionConfigs.customerManagedEncryption(kmsKeyName))
        .build();
    final OperationFuture<Database, RestoreDatabaseMetadata> operation = adminClient
        .restoreDatabase(restore);

    Database database;
    try {
      System.out.println("Waiting for operation to complete...");
      database = operation.get();
    } catch (ExecutionException e) {
      // If the operation failed during execution, expose the cause.
      throw SpannerExceptionFactory.asSpannerException(e.getCause());
    } catch (InterruptedException e) {
      // Throw when a thread is waiting, sleeping, or otherwise occupied,
      // and the thread is interrupted, either before or during the activity.
      throw SpannerExceptionFactory.propagateInterrupt(e);
    }

    System.out.printf(
        "Database %s restored to %s from backup %s using encryption key %s%n",
        database.getRestoreInfo().getSourceDatabase(),
        database.getId(),
        database.getRestoreInfo().getBackup(),
        database.getEncryptionConfig().getKmsKeyName()
    );
    return null;
  }
}

Nœud

// Imports the Google Cloud client library and precise date library
const {Spanner} = require('@google-cloud/spanner');

/**
 * TODO(developer): Uncomment the following lines before running the sample.
 */
// const projectId = 'my-project-id';
// const instanceId = 'my-instance';
// const databaseId = 'my-database';
// const backupId = 'my-backup';
// const keyName =
//   'projects/my-project-id/my-region/keyRings/my-key-ring/cryptoKeys/my-key';

// Creates a client
const spanner = new Spanner({
  projectId: projectId,
});

// Gets a reference to a Cloud Spanner instance and database
const instance = spanner.instance(instanceId);
const database = instance.database(databaseId);

// Restore the database
console.log(
  `Restoring database ${database.formattedName_} from backup ${backupId}.`
);
const [, restoreOperation] = await database.restore(
  `projects/${projectId}/instances/${instanceId}/backups/${backupId}`,
  {
    encryptionConfig: {
      encryptionType: 'CUSTOMER_MANAGED_ENCRYPTION',
      kmsKeyName: keyName,
    },
  }
);

// Wait for restore to complete
console.log('Waiting for database restore to complete...');
await restoreOperation.promise();

console.log('Database restored from backup.');
const restoreInfo = await database.getRestoreInfo();
const [data] = await database.get();
console.log(
  `Database ${restoreInfo.backupInfo.sourceDatabase} was restored ` +
    `to ${databaseId} from backup ${restoreInfo.backupInfo.backup} ` +
    `using encryption key ${data.metadata.encryptionConfig.kmsKeyName}.`
);

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

def restore_database_with_encryption_key(instance_id, new_database_id, backup_id, kms_key_name):
    """Restores a database from a backup using a Customer Managed Encryption Key (CMEK)."""
    from google.cloud.spanner_admin_database_v1 import RestoreDatabaseEncryptionConfig

    spanner_client = spanner.Client()
    instance = spanner_client.instance(instance_id)

    # Start restoring an existing backup to a new database.
    backup = instance.backup(backup_id)
    encryption_config = {
        'encryption_type': RestoreDatabaseEncryptionConfig.EncryptionType.CUSTOMER_MANAGED_ENCRYPTION,
        'kms_key_name': kms_key_name,
    }
    new_database = instance.database(new_database_id, encryption_config=encryption_config)
    operation = new_database.restore(backup)

    # Wait for restore operation to complete.
    operation.result(1600)

    # Newly created database has restore information.
    new_database.reload()
    restore_info = new_database.restore_info
    print(
        "Database {} restored to {} from backup {} with using encryption key {}.".format(
            restore_info.backup_info.source_database,
            new_database_id,
            restore_info.backup_info.backup,
            new_database.encryption_config.kms_key_name,
        )
    )

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

Afficher les journaux d'audit pour la clé Cloud KMS

  1. Assurez-vous que la journalisation est activée pour l'API Cloud KMS dans votre projet.

  2. Accédez à la visionneuse de journaux dans Cloud Console.

    Accéder à Cloud Console

  3. Limitez les entrées de journal à votre clé Cloud KMS en ajoutant les lignes suivantes au générateur de requêtes:

    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"
    
  4. En mode de fonctionnement normal, les actions de chiffrement et de déchiffrement sont consignées avec le niveau de gravité INFO. Ces entrées sont consignées en tant que zones d'instances Cloud Spanner interroge la clé Cloud KMS toutes les cinq minutes environ.

  5. Si Cloud Spanner ne parvient pas à accéder à la clé, les opérations sont consignées comme ERROR.