客户管理的 Cloud KMS 密钥

默认情况下,BigQuery 会对以静态方式存储的内容进行加密。BigQuery 会为您处理和管理这项默认加密作业,您无需进行任何其他操作。首先,系统会使用“数据加密密钥”(DEK) 对 BigQuery 表中的数据进行加密。然后,通过"密钥加密密钥" (KEK) 来对 DEK 进行加密(这称为信封加密)。密钥加密密钥不直接加密数据,而是用于对 Google 用来加密数据的数据加密密钥进行加密。如需了解详情,请参阅 Cloud Key Management Service (KMS)。

如果您想自行控制加密,可为 BigQuery 使用客户管理的加密密钥 (CMEK)。您自行控制和管理 Cloud KMS 中保护您数据的密钥加密密钥 (KEK),而不是由 Google 管理。本文档详细介绍了这一方法。

详细了解 Google Cloud 的加密选项。 如需了解 CMEK 的具体信息(包括其优点和限制),请参阅客户管理的加密密钥

准备工作

  • 驻留在 BigQuery 托管存储中的所有数据资源都支持 CMEK。但是,如果您还查询存储在外部数据源(例如具有 CMEK 加密数据的 Cloud Storage),则数据加密由 Cloud Storage 管理。例如,BigLake 表支持在 Cloud Storage 中使用 CMEK 加密的数据。

    BigQuery 和 BigLake 表不支持客户提供的加密密钥 (CSEK)。

  • 确定您要在同一个 Google Cloud 项目中还是在不同项目中运行 BigQuery 和 Cloud KMS。为了方便说明,本文的示例将采用下列惯例:

    • PROJECT_ID:运行 BigQuery 的项目的 ID
    • PROJECT_NUMBER:运行 BigQuery 的项目的编号
    • KMS_PROJECT_ID:运行 Cloud KMS 的项目的 ID(即使该项目就是运行 BigQuery 的同一个项目)
    如需了解 Google Cloud 项目 ID 和项目编号,请参阅识别项目

  • 新项目中会自动启用 BigQuery。如果您使用现有项目来运行 BigQuery,请启用 BigQuery API

  • 对于运行 Cloud KMS 的 Google Cloud 项目:

    1. 启用 Cloud Key Management Service API
    2. 按照创建密钥环和密钥中的说明创建密钥环和密钥。请在与 BigQuery 数据集的位置匹配的位置创建密钥环:
      • 任何多区域数据集都应使用来自匹配位置的多区域密钥环。例如,应使用区域 us 中的密钥环保护位于区域 US 的数据集,使用区域 europe 中的密钥环保护位于区域 EU 的数据集。
      • 区域数据集应使用匹配的区域密钥。例如,应使用区域 asia-northeast1 中的密钥环保护位于区域 asia-northeast1 的数据集。
      • 在 Google Cloud 控制台中为 BigQuery 配置 CMEK 时,您不能使用 global 区域。但是,在使用 bq 命令行工具或 GoogleSQL 为 BigQuery 配置 CMEK 时,您可以使用 global 区域。
      如需详细了解支持 BigQuery 和 Cloud KMS 的位置,请参阅 Cloud 位置

每次查询 CMEK 加密表时,都会使用 Cloud KMS 执行一次解密调用。如需了解详情,请参阅 Cloud KMS 价格

加密规范

用于保护您在 BigQuery 中的数据的 Cloud KMS 密钥是 AES-256 密钥。这类密钥在 BigQuery 中被用作 KEK,其用途是对加密您数据的 DEK 进行加密。

授予加密和解密权限

要使用 CMEK 密钥保护您的 BigQuery 数据,请向 BigQuery 服务账号授予使用该密钥进行加密和解密的权限。Cloud KMS CryptoKey Encrypter/Decrypter 角色可授予此权限。

确保已创建您的服务账号,然后使用 Google Cloud 控制台确定 BigQuery 服务账号 ID。接下来,为该服务账号提供适当的角色,以便使用 Cloud KMS 进行加密和解密。

触发服务账号的创建

创建项目时,系统最初不会创建 BigQuery 服务账号。如需触发服务账号的创建,请输入一个使用该服务账号的命令(例如 bq show --encryption_service_account 命令),或调用 projects.getServiceAccount API 方法。 例如:

bq show --encryption_service_account --project_id=PROJECT_ID

确定服务账号 ID

BigQuery 服务账号 ID 的格式如下:

bq-PROJECT_NUMBER@bigquery-encryption.iam.gserviceaccount.com

以下方法展示了如何确定您项目的 BigQuery 服务账号 ID。

控制台

  1. 转到 Google Cloud 控制台中的信息中心页面

    转到“信息中心”页面

  2. 点击页面顶部的选择项目下拉列表。在出现的选择项目窗口中,选择您的项目。

  3. 项目 ID 和项目编号显示在项目信息中心的项目信息卡片上:

    项目信息卡片

  4. 在以下字符串中,将 PROJECT_NUMBER 替换为您的项目编号。新字符串会识别您的 BigQuery 服务账号 ID。

    bq-PROJECT_NUMBER@bigquery-encryption.iam.gserviceaccount.com
    

bq

使用带有 --encryption_service_account 标志的 bq show 命令确定服务账号 ID:

bq show --encryption_service_account

该命令显示服务账号 ID:

                  ServiceAccountID
-------------------------------------------------------------
bq-PROJECT_NUMBER@bigquery-encryption.iam.gserviceaccount.com

分配加密者/解密者角色

将 Cloud KMS CryptoKey Encrypter/Decrypter 角色分配给您已复制到剪贴板的 BigQuery 系统服务账号。该账号的格式如下:

bq-PROJECT_NUMBER@bigquery-encryption.iam.gserviceaccount.com

控制台

  1. 在 Google Cloud 控制台中,打开加密密钥页面。

    打开“加密密钥”页面

  2. 点击包含密钥的密钥环的名称。

  3. 点击要向其添加角色的加密密钥对应的复选框。此时会打开权限标签。

  4. 点击添加成员

  5. 输入服务账号的电子邮件地址 bq-PROJECT_NUMBER@bigquery-encryption.iam.gserviceaccount.com

    • 如果该服务账号已经在成员列表中,则表明它具有现有角色。点击 bq-PROJECT_NUMBER@bigquery-encryption.iam.gserviceaccount.com 服务账号的当前角色下拉列表。
  6. 点击选择角色下拉列表,点击 Cloud KMS,然后点击 Cloud KMS CryptoKey Encrypter/Decrypter 角色。

  7. 点击保存,将角色应用到 bq-PROJECT_NUMBER@bigquery-encryption.iam.gserviceaccount.com 服务账号。

gcloud

您可以使用 Google Cloud CLI 分配角色:

gcloud kms keys add-iam-policy-binding \
--project=KMS_PROJECT_ID \
--member serviceAccount:bq-PROJECT_NUMBER@bigquery-encryption.iam.gserviceaccount.com \
--role roles/cloudkms.cryptoKeyEncrypterDecrypter \
--location=KMS_KEY_LOCATION \
--keyring=KMS_KEY_RING \
KMS_KEY

替换以下内容:

  • KMS_PROJECT_ID:运行 Cloud KMS 的 Google Cloud 项目的 ID
  • PROJECT_NUMBER:运行 BigQuery 的 Google Cloud 项目的编号(而非项目 ID)
  • KMS_KEY_LOCATION:Cloud KMS 密钥的位置名称
  • KMS_KEY_RING:Cloud KMS 密钥的密钥环名称
  • KMS_KEY:Cloud KMS 密钥的名称

密钥资源 ID

使用 CMEK 时需要 Cloud KMS 密钥的资源 ID,如示例中所示。此密钥区分大小写,格式如下:

projects/KMS_PROJECT_ID/locations/LOCATION/keyRings/KEY_RING/cryptoKeys/KEY

检索密钥资源 ID

  1. 在 Google Cloud 控制台中,打开加密密钥页面。

    打开“加密密钥”页面

  2. 点击包含密钥的密钥环的名称。

  3. 找到要检索其资源 ID 的密钥,点击更多

  4. 点击复制资源名称。 密钥的资源 ID 会复制到剪贴板。资源 ID 也称为资源名称。

创建受 Cloud KMS 保护的表

要创建受 Cloud KMS 保护的表,请执行以下操作:

控制台

  1. 在 Google Cloud 控制台中打开 BigQuery 页面。

    转到 BigQuery 页面

  2. 浏览器面板中,展开您的项目并选择数据集。

  3. 展开 操作选项,然后点击打开

  4. 在详情面板中,点击创建表

  5. Create table 页面上,填写创建具有架构定义的空表所需的信息。在您点击 Create table 之前,请设置加密类型并指定要用于表的 Cloud KMS 密钥:

    1. 点击高级选项
    2. 点击客户管理的密钥
    3. 选择密钥。如果要使用的密钥未列出,请输入密钥的资源 ID
  6. 点击创建表

SQL

使用带有 kms_key_name 选项的 CREATE TABLE 语句

  1. 在 Google Cloud 控制台中,转到 BigQuery 页面。

    转到 BigQuery

  2. 在查询编辑器中,输入以下语句:

    CREATE TABLE DATASET_ID.TABLE_ID (
      name STRING, value INT64
    ) OPTIONS (
        kms_key_name
          = 'projects/KMS_PROJECT_ID/locations/LOCATION/keyRings/KEY_RING/cryptoKeys/KEY');
    

  3. 点击 运行

如需详细了解如何运行查询,请参阅运行交互式查询

bq

您可以使用带有 --destination_kms_key 标志的 bq 命令行工具以创建表。--destination_kms_key 标志指定要用于表的密钥的资源 ID

如需创建具有架构的空表,请使用以下命令:

bq mk --schema name:string,value:integer -t \
--destination_kms_key projects/KMS_PROJECT_ID/locations/LOCATION/keyRings/KEY_RING/cryptoKeys/KEY \
DATASET_ID.TABLE_ID

要基于查询创建表,请使用以下命令:

bq query --destination_table=DATASET_ID.TABLE_ID \
--destination_kms_key projects/KMS_PROJECT_ID/locations/LOCATION/keyRings/KEY_RING/cryptoKeys/KEY \
"SELECT name,count FROM DATASET_ID.TABLE_ID WHERE gender = 'M' ORDER BY count DESC LIMIT 6"

如需详细了解 bq 命令行工具,请参阅使用 bq 命令行工具

Terraform

使用 google_bigquery_table 资源。

以下示例创建了一个名为 mytable 的表,并且还使用 google_kms_crypto_keygoogle_kms_key_ring 资源来指定表的 Cloud Key Management Service 密钥

如需运行此示例,您必须启用 Cloud Resource Manager APICloud Key Management Service API

resource "google_bigquery_dataset" "default" {
  dataset_id                      = "mydataset"
  default_partition_expiration_ms = 2592000000  # 30 days
  default_table_expiration_ms     = 31536000000 # 365 days
  description                     = "dataset description"
  location                        = "US"
  max_time_travel_hours           = 96 # 4 days

  labels = {
    billing_group = "accounting",
    pii           = "sensitive"
  }
}

resource "google_bigquery_table" "default" {
  dataset_id          = google_bigquery_dataset.default.dataset_id
  table_id            = "mytable"
  deletion_protection = false # set to "true" in production

  schema = <<EOF
[
  {
    "name": "ID",
    "type": "INT64",
    "mode": "NULLABLE",
    "description": "Item ID"
  },
  {
    "name": "Item",
    "type": "STRING",
    "mode": "NULLABLE"
  }
]
EOF

  encryption_configuration {
    kms_key_name = google_kms_crypto_key.crypto_key.id
  }

  depends_on = [google_project_iam_member.service_account_access]
}

resource "google_kms_crypto_key" "crypto_key" {
  name     = "example-key"
  key_ring = google_kms_key_ring.key_ring.id
}

resource "random_id" "default" {
  byte_length = 8
}

resource "google_kms_key_ring" "key_ring" {
  name     = "${random_id.default.hex}-example-keyring"
  location = "us"
}

# Enable the BigQuery service account to encrypt/decrypt Cloud KMS keys
data "google_project" "project" {
}

resource "google_project_iam_member" "service_account_access" {
  project = data.google_project.project.project_id
  role    = "roles/cloudkms.cryptoKeyEncrypterDecrypter"
  member  = "serviceAccount:bq-${data.google_project.project.number}@bigquery-encryption.iam.gserviceaccount.com"
}

如需在 Google Cloud 项目中应用 Terraform 配置,请完成以下部分中的步骤。

准备 Cloud Shell

  1. 启动 Cloud Shell
  2. 设置要在其中应用 Terraform 配置的默认 Google Cloud 项目。

    您只需为每个项目运行一次以下命令,即可在任何目录中运行它。

    export GOOGLE_CLOUD_PROJECT=PROJECT_ID

    如果您在 Terraform 配置文件中设置显式值,则环境变量会被替换。

准备目录

每个 Terraform 配置文件都必须有自己的目录(也称为“根模块”)。

  1. Cloud Shell 中,创建一个目录,并在该目录中创建一个新文件。文件名必须具有 .tf 扩展名,例如 main.tf。在本教程中,该文件称为 main.tf
    mkdir DIRECTORY && cd DIRECTORY && touch main.tf
  2. 如果您按照教程进行操作,可以在每个部分或步骤中复制示例代码。

    将示例代码复制到新创建的 main.tf 中。

    (可选)从 GitHub 中复制代码。如果端到端解决方案包含 Terraform 代码段,则建议这样做。

  3. 查看和修改要应用到您的环境的示例参数。
  4. 保存更改。
  5. 初始化 Terraform。您只需为每个目录执行一次此操作。
    terraform init

    (可选)如需使用最新的 Google 提供程序版本,请添加 -upgrade 选项:

    terraform init -upgrade

应用更改

  1. 查看配置并验证 Terraform 将创建或更新的资源是否符合您的预期:
    terraform plan

    根据需要更正配置。

  2. 通过运行以下命令并在提示符处输入 yes 来应用 Terraform 配置:
    terraform apply

    等待 Terraform 显示“应用完成!”消息。

  3. 打开您的 Google Cloud 项目以查看结果。在 Google Cloud 控制台的界面中找到资源,以确保 Terraform 已创建或更新它们。

Go

试用此示例之前,请按照 BigQuery 快速入门:使用客户端库中的 Go 设置说明进行操作。如需了解详情,请参阅 BigQuery Go API 参考文档

如需向 BigQuery 进行身份验证,请设置应用默认凭据。 如需了解详情,请参阅为客户端库设置身份验证

import (
	"context"
	"fmt"

	"cloud.google.com/go/bigquery"
)

// createTableWithCMEK demonstrates creating a table protected with a customer managed encryption key.
func createTableWithCMEK(projectID, datasetID, tableID string) error {
	// projectID := "my-project-id"
	// datasetID := "mydatasetid"
	// tableID := "mytableid"
	ctx := context.Background()

	client, err := bigquery.NewClient(ctx, projectID)
	if err != nil {
		return fmt.Errorf("bigquery.NewClient: %v", err)
	}
	defer client.Close()

	tableRef := client.Dataset(datasetID).Table(tableID)
	meta := &bigquery.TableMetadata{
		EncryptionConfig: &bigquery.EncryptionConfig{
			// TODO: Replace this key with a key you have created in Cloud KMS.
			KMSKeyName: "projects/cloud-samples-tests/locations/us/keyRings/test/cryptoKeys/test",
		},
	}
	if err := tableRef.Create(ctx, meta); err != nil {
		return err
	}
	return nil
}

Java

试用此示例之前,请按照 BigQuery 快速入门:使用客户端库中的 Java 设置说明进行操作。如需了解详情,请参阅 BigQuery Java API 参考文档

如需向 BigQuery 进行身份验证,请设置应用默认凭据。 如需了解详情,请参阅为客户端库设置身份验证

import com.google.cloud.bigquery.BigQuery;
import com.google.cloud.bigquery.BigQueryException;
import com.google.cloud.bigquery.BigQueryOptions;
import com.google.cloud.bigquery.EncryptionConfiguration;
import com.google.cloud.bigquery.Field;
import com.google.cloud.bigquery.Schema;
import com.google.cloud.bigquery.StandardSQLTypeName;
import com.google.cloud.bigquery.StandardTableDefinition;
import com.google.cloud.bigquery.TableDefinition;
import com.google.cloud.bigquery.TableId;
import com.google.cloud.bigquery.TableInfo;

// Sample to create a cmek table
public class CreateTableCMEK {

  public static void runCreateTableCMEK() {
    // TODO(developer): Replace these variables before running the sample.
    String datasetName = "MY_DATASET_NAME";
    String tableName = "MY_TABLE_NAME";
    String kmsKeyName = "MY_KEY_NAME";
    Schema schema =
        Schema.of(
            Field.of("stringField", StandardSQLTypeName.STRING),
            Field.of("booleanField", StandardSQLTypeName.BOOL));
    // i.e. projects/{project}/locations/{location}/keyRings/{key_ring}/cryptoKeys/{cryptoKey}
    EncryptionConfiguration encryption =
        EncryptionConfiguration.newBuilder().setKmsKeyName(kmsKeyName).build();
    createTableCMEK(datasetName, tableName, schema, encryption);
  }

  public static void createTableCMEK(
      String datasetName, String tableName, Schema schema, EncryptionConfiguration configuration) {
    try {
      // Initialize client that will be used to send requests. This client only needs to be created
      // once, and can be reused for multiple requests.
      BigQuery bigquery = BigQueryOptions.getDefaultInstance().getService();

      TableId tableId = TableId.of(datasetName, tableName);
      TableDefinition tableDefinition = StandardTableDefinition.of(schema);
      TableInfo tableInfo =
          TableInfo.newBuilder(tableId, tableDefinition)
              .setEncryptionConfiguration(configuration)
              .build();

      bigquery.create(tableInfo);
      System.out.println("Table cmek created successfully");
    } catch (BigQueryException e) {
      System.out.println("Table cmek was not created. \n" + e.toString());
    }
  }
}

Python

试用此示例之前,请按照 BigQuery 快速入门:使用客户端库中的 Python 设置说明进行操作。如需了解详情,请参阅 BigQuery Python API 参考文档

如需向 BigQuery 进行身份验证,请设置应用默认凭据。 如需了解详情,请参阅为客户端库设置身份验证

在创建新表之前,将 Table.encryption_configuration 属性设置为 EncryptionConfiguration 对象,以使用客户管理的加密密钥保护该表。
from google.cloud import bigquery

client = bigquery.Client()

# TODO(dev): Change table_id to the full name of the table you want to create.
table_id = "your-project.your_dataset.your_table_name"

# Set the encryption key to use for the table.
# TODO: Replace this key with a key you have created in Cloud KMS.
kms_key_name = "projects/your-project/locations/us/keyRings/test/cryptoKeys/test"

table = bigquery.Table(table_id)
table.encryption_configuration = bigquery.EncryptionConfiguration(
    kms_key_name=kms_key_name
)
table = client.create_table(table)  # API request

print(f"Created {table_id}.")
print(f"Key: {table.encryption_configuration.kms_key_name}.")

查询受 Cloud KMS 密钥保护的表

查询受 Cloud KMS 保护的表无需特殊操作安排。BigQuery 会存储用于加密表内容的密钥的名称,并在受 Cloud KMS 保护的表被查询时使用该密钥。

只要 BigQuery 有权访问用于加密表内容的 Cloud KMS 密钥,所有现有工具、BigQuery 控制台和 bq 命令行工具的工作方式都将与采用默认加密方式的表相同。

使用 Cloud KMS 密钥保护查询结果

默认情况下,查询结果存储在使用 Google 管理的密钥加密的临时表中。如需改为使用 Cloud KMS 密钥对查询结果进行加密,请选择以下选项之一:

控制台

  1. 在 Google Cloud 控制台中打开 BigQuery 页面。

    转到 BigQuery 页面

  2. 点击编写新查询

  3. 在查询文本区域中输入有效的 GoogleSQL 查询。

  4. 点击更多,点击查询设置,然后点击高级选项

  5. 选择客户管理的加密

  6. 选择密钥。如果要使用的密钥未列出,请输入密钥的资源 ID

  7. 点击保存

  8. 点击运行

bq

使用 Cloud KMS 密钥指定标记 --destination_kms_key 以保护目标表或查询结果(如果使用临时表)。--destination_kms_key 标志指定用于目标表或生成的表的密钥的资源 ID

您可以选择使用 --destination_table 标志为查询结果指定目标。如果未使用 --destination_table,则查询结果会写入临时表中。

如需查询表,请使用以下命令:

bq query \
--destination_table=DATASET_ID.TABLE_ID \
--destination_kms_key projects/KMS_PROJECT_ID/locations/LOCATION/keyRings/KEY_RING/cryptoKeys/KEY \
"SELECT name,count FROM DATASET_ID.TABLE_ID WHERE gender = 'M' ORDER BY count DESC LIMIT 6"

如需详细了解 bq 命令行工具,请参阅使用 bq 命令行工具

Go

import (
	"context"
	"fmt"
	"io"

	"cloud.google.com/go/bigquery"
	"google.golang.org/api/iterator"
)

// queryWithDestinationCMEK demonstrates saving query results to a destination table and protecting those results
// by specifying a customer managed encryption key.
func queryWithDestinationCMEK(w io.Writer, projectID, dstDatasetID, dstTableID string) error {
	// projectID := "my-project-id"
	// datasetID := "mydataset"
	// tableID := "mytable"
	ctx := context.Background()
	client, err := bigquery.NewClient(ctx, projectID)
	if err != nil {
		return fmt.Errorf("bigquery.NewClient: %v", err)
	}
	defer client.Close()

	q := client.Query("SELECT 17 as my_col")
	q.Location = "US" // Location must match the dataset(s) referenced in query.
	q.QueryConfig.Dst = client.Dataset(dstDatasetID).Table(dstTableID)
	q.DestinationEncryptionConfig = &bigquery.EncryptionConfig{
		// TODO: Replace this key with a key you have created in Cloud KMS.
		KMSKeyName: "projects/cloud-samples-tests/locations/us-central1/keyRings/test/cryptoKeys/test",
	}
	// Run the query and print results when the query job is completed.
	job, err := q.Run(ctx)
	if err != nil {
		return err
	}
	status, err := job.Wait(ctx)
	if err != nil {
		return err
	}
	if err := status.Err(); err != nil {
		return err
	}
	it, err := job.Read(ctx)
	for {
		var row []bigquery.Value
		err := it.Next(&row)
		if err == iterator.Done {
			break
		}
		if err != nil {
			return err
		}
		fmt.Fprintln(w, row)
	}
	return nil
}

Java

试用此示例之前,请按照 BigQuery 快速入门:使用客户端库中的 Java 设置说明进行操作。如需了解详情,请参阅 BigQuery Java API 参考文档

如需向 BigQuery 进行身份验证,请设置应用默认凭据。 如需了解详情,请参阅为客户端库设置身份验证

在创建新表之前,将 Table.encryption_configuration 属性设置为 EncryptionConfiguration 对象,以使用客户管理的加密密钥保护该表。
import com.google.cloud.bigquery.BigQuery;
import com.google.cloud.bigquery.BigQueryException;
import com.google.cloud.bigquery.BigQueryOptions;
import com.google.cloud.bigquery.EncryptionConfiguration;
import com.google.cloud.bigquery.QueryJobConfiguration;
import com.google.cloud.bigquery.TableResult;

// Sample to query on destination table with encryption key
public class QueryDestinationTableCMEK {

  public static void runQueryDestinationTableCMEK() {
    // TODO(developer): Replace these variables before running the sample.
    String datasetName = "MY_DATASET_NAME";
    String tableName = "MY_TABLE_NAME";
    String kmsKeyName = "MY_KMS_KEY_NAME";
    String query =
        String.format("SELECT stringField, booleanField FROM %s.%s", datasetName, tableName);
    EncryptionConfiguration encryption =
        EncryptionConfiguration.newBuilder().setKmsKeyName(kmsKeyName).build();
    queryDestinationTableCMEK(query, encryption);
  }

  public static void queryDestinationTableCMEK(String query, EncryptionConfiguration encryption) {
    try {
      // Initialize client that will be used to send requests. This client only needs to be created
      // once, and can be reused for multiple requests.
      BigQuery bigquery = BigQueryOptions.getDefaultInstance().getService();

      QueryJobConfiguration config =
          QueryJobConfiguration.newBuilder(query)
              // Set the encryption key to use for the destination.
              .setDestinationEncryptionConfiguration(encryption)
              .build();

      TableResult results = bigquery.query(config);

      results
          .iterateAll()
          .forEach(row -> row.forEach(val -> System.out.printf("%s,", val.toString())));
      System.out.println("Query performed successfully with encryption key.");
    } catch (BigQueryException | InterruptedException e) {
      System.out.println("Query not performed \n" + e.toString());
    }
  }
}

Python

from google.cloud import bigquery

# Construct a BigQuery client object.
client = bigquery.Client()

# TODO(developer): Set table_id to the ID of the destination table.
# table_id = "your-project.your_dataset.your_table_name"

# Set the encryption key to use for the destination.
# TODO(developer): Replace this key with a key you have created in KMS.
# kms_key_name = "projects/{}/locations/{}/keyRings/{}/cryptoKeys/{}".format(
#     your-project, location, your-ring, your-key
# )

job_config = bigquery.QueryJobConfig(
    destination=table_id,
    destination_encryption_configuration=bigquery.EncryptionConfiguration(
        kms_key_name=kms_key_name
    ),
)

# Start the query, passing in the extra configuration.
query_job = client.query(
    "SELECT 17 AS my_col;", job_config=job_config
)  # Make an API request.
query_job.result()  # Wait for the job to complete.

table = client.get_table(table_id)  # Make an API request.
if table.encryption_configuration.kms_key_name == kms_key_name:
    print("The destination table is written using the encryption configuration")

加载受 Cloud KMS 保护的表

要将数据文件加载到受 Cloud KMS 保护的表中,请使用以下方式:

控制台

在加载表时指定密钥,以使用 CMEK 保护加载作业的目标表。

  1. 在 Google Cloud 控制台中打开 BigQuery 页面。

    转到 BigQuery 页面

  2. 浏览器面板中,展开您的项目并选择数据集。

  3. 在详细信息面板中,点击创建表

  4. 输入要用于加载表的选项,但在点击创建表之前,请先点击高级选项

  5. 加密下选择客户管理的密钥

  6. 点击选择客户管理的密钥下拉列表,然后选择要使用的密钥。如果您没有看到任何可用的密钥,请输入密钥资源 ID

    高级选项。

  7. 点击创建表

bq

设置 --destination_kms_key 标志,以使用 CMEK 保护加载作业的目标表。

bq --location=LOCATION load \
--autodetect \
--source_format=FORMAT \
--destination_kms_key projects/KMS_PROJECT_ID/locations/LOCATION/keyRings/KEY_RING/cryptoKeys/KEY \
DATASET.TABLE \
path_to_source
例如:
bq load \
--autodetect \
--source_format=NEWLINE_DELIMITED_JSON \
--destination_kms_key projects/KMS_PROJECT_ID/locations/LOCATION/keyRings/KEY_RING/cryptoKeys/KEY \
test2.table4 \
gs://cloud-samples-data/bigquery/us-states/us-states.json

Go

import (
	"context"
	"fmt"

	"cloud.google.com/go/bigquery"
)

// importJSONWithCMEK demonstrates loading newline-delimited JSON from Cloud Storage,
// and protecting the data with a customer-managed encryption key.
func importJSONWithCMEK(projectID, datasetID, tableID string) error {
	// projectID := "my-project-id"
	// datasetID := "mydataset"
	// tableID := "mytable"
	ctx := context.Background()
	client, err := bigquery.NewClient(ctx, projectID)
	if err != nil {
		return fmt.Errorf("bigquery.NewClient: %v", err)
	}
	defer client.Close()

	gcsRef := bigquery.NewGCSReference("gs://cloud-samples-data/bigquery/us-states/us-states.json")
	gcsRef.SourceFormat = bigquery.JSON
	gcsRef.AutoDetect = true
	loader := client.Dataset(datasetID).Table(tableID).LoaderFrom(gcsRef)
	loader.WriteDisposition = bigquery.WriteEmpty
	loader.DestinationEncryptionConfig = &bigquery.EncryptionConfig{
		// TODO: Replace this key with a key you have created in KMS.
		KMSKeyName: "projects/cloud-samples-tests/locations/us-central1/keyRings/test/cryptoKeys/test",
	}

	job, err := loader.Run(ctx)
	if err != nil {
		return err
	}
	status, err := job.Wait(ctx)
	if err != nil {
		return err
	}

	if status.Err() != nil {
		return fmt.Errorf("job completed with error: %v", status.Err())
	}

	return nil
}

Java

import com.google.cloud.bigquery.BigQuery;
import com.google.cloud.bigquery.BigQueryException;
import com.google.cloud.bigquery.BigQueryOptions;
import com.google.cloud.bigquery.EncryptionConfiguration;
import com.google.cloud.bigquery.FormatOptions;
import com.google.cloud.bigquery.Job;
import com.google.cloud.bigquery.JobInfo;
import com.google.cloud.bigquery.LoadJobConfiguration;
import com.google.cloud.bigquery.TableId;

// Sample to load JSON data with configuration key from Cloud Storage into a new BigQuery table
public class LoadJsonFromGCSCMEK {

  public static void runLoadJsonFromGCSCMEK() {
    // TODO(developer): Replace these variables before running the sample.
    String datasetName = "MY_DATASET_NAME";
    String tableName = "MY_TABLE_NAME";
    String kmsKeyName = "MY_KMS_KEY_NAME";
    String sourceUri = "gs://cloud-samples-data/bigquery/us-states/us-states.json";
    // i.e. projects/{project}/locations/{location}/keyRings/{key_ring}/cryptoKeys/{cryptoKey}
    EncryptionConfiguration encryption =
        EncryptionConfiguration.newBuilder().setKmsKeyName(kmsKeyName).build();
    loadJsonFromGCSCMEK(datasetName, tableName, sourceUri, encryption);
  }

  public static void loadJsonFromGCSCMEK(
      String datasetName, String tableName, String sourceUri, EncryptionConfiguration encryption) {
    try {
      // Initialize client that will be used to send requests. This client only needs to be created
      // once, and can be reused for multiple requests.
      BigQuery bigquery = BigQueryOptions.getDefaultInstance().getService();

      TableId tableId = TableId.of(datasetName, tableName);
      LoadJobConfiguration loadConfig =
          LoadJobConfiguration.newBuilder(tableId, sourceUri)
              // Set the encryption key to use for the destination.
              .setDestinationEncryptionConfiguration(encryption)
              .setFormatOptions(FormatOptions.json())
              .setAutodetect(true)
              .build();

      // Load data from a GCS JSON file into the table
      Job job = bigquery.create(JobInfo.of(loadConfig));
      // Blocks until this load table job completes its execution, either failing or succeeding.
      job = job.waitFor();
      if (job.isDone()) {
        System.out.println("Table loaded succesfully from GCS with configuration key");
      } else {
        System.out.println(
            "BigQuery was unable to load into the table due to an error:"
                + job.getStatus().getError());
      }
    } catch (BigQueryException | InterruptedException e) {
      System.out.println("Column not added during load append \n" + e.toString());
    }
  }
}

Python

LoadJobConfig.destination_encryption_configuration 属性设置为 EncryptionConfiguration 并加载表,以使用 CMEK 保护加载作业的目标表。

from google.cloud import bigquery

# Construct a BigQuery client object.
client = bigquery.Client()

# TODO(developer): Set table_id to the ID of the table to create.
# table_id = "your-project.your_dataset.your_table_name

# Set the encryption key to use for the destination.
# TODO: Replace this key with a key you have created in KMS.
# kms_key_name = "projects/{}/locations/{}/keyRings/{}/cryptoKeys/{}".format(
#     "cloud-samples-tests", "us", "test", "test"
# )

job_config = bigquery.LoadJobConfig(
    autodetect=True,
    source_format=bigquery.SourceFormat.NEWLINE_DELIMITED_JSON,
    destination_encryption_configuration=bigquery.EncryptionConfiguration(
        kms_key_name=kms_key_name
    ),
)

uri = "gs://cloud-samples-data/bigquery/us-states/us-states.json"

load_job = client.load_table_from_uri(
    uri,
    table_id,
    location="US",  # Must match the destination dataset location.
    job_config=job_config,
)  # Make an API request.

assert load_job.job_type == "load"

load_job.result()  # Waits for the job to complete.

assert load_job.state == "DONE"
table = client.get_table(table_id)

if table.encryption_configuration.kms_key_name == kms_key_name:
    print("A table loaded with encryption configuration key")

将数据流式插入到受 Cloud KMS 保护的表

您可以将数据流式插入到受 CMEK 保护的 BigQuery 表中,而无需指定任何其他参数。请注意,系统将使用 Cloud KMS 密钥对缓冲区和最终位置中的此类数据进行加密。在将数据流式插入到 CMEK 表之前,请先查看密钥可用性和可访问性上所述的要求。

如需详细了解流式插入,请参阅将数据流式插入到 BigQuery 中

将表从默认加密更改为 Cloud KMS 保护

bq

您可以搭配使用 bq cp 命令和 --destination_kms_key 标志,将受默认加密保护的表复制到受 Cloud KMS 保护的新表或原始表中。--destination_kms_key 标志指定要用于目标表的密钥的资源 ID

要将采用默认加密方式的表复制到受 Cloud KMS 保护的新表,请使用以下命令:

bq cp \
--destination_kms_key projects/KMS_PROJECT_ID/locations/LOCATION/keyRings/KEY_RING/cryptoKeys/KEY \
SOURCE_DATASET_ID.SOURCE_TABLE_ID DESTINATION_DATASET_ID.DESTINATION_TABLE_ID

要将采用默认加密方式的表复制到受 Cloud KMS 保护的相同表,请使用以下命令:

bq cp -f \
--destination_kms_key projects/KMS_PROJECT_ID/locations/LOCATION/keyRings/KEY_RING/cryptoKeys/KEY \
DATASET_ID.TABLE_ID DATASET_ID.TABLE_ID

如果您想将表格从 Cloud KMS 保护更改为默认加密,请在不使用 --destination_kms_key 标记的情况下运行 bq cp,将文件复制到自身。

如需详细了解 bq 命令行工具,请参阅使用 bq 命令行工具

Go

import (
	"context"
	"fmt"

	"cloud.google.com/go/bigquery"
)

// copyTableWithCMEK demonstrates creating a copy of a table and ensuring the copied data is
// protected with a customer managed encryption key.
func copyTableWithCMEK(projectID, datasetID, tableID string) error {
	// projectID := "my-project-id"
	// datasetID := "mydataset"
	// tableID := "mytable"
	ctx := context.Background()
	client, err := bigquery.NewClient(ctx, projectID)
	if err != nil {
		return fmt.Errorf("bigquery.NewClient: %v", err)
	}
	defer client.Close()

	srcTable := client.DatasetInProject("bigquery-public-data", "samples").Table("shakespeare")
	copier := client.Dataset(datasetID).Table(tableID).CopierFrom(srcTable)
	copier.DestinationEncryptionConfig = &bigquery.EncryptionConfig{
		// TODO: Replace this key with a key you have created in Cloud KMS.
		KMSKeyName: "projects/cloud-samples-tests/locations/us-central1/keyRings/test/cryptoKeys/test",
	}
	job, err := copier.Run(ctx)
	if err != nil {
		return err
	}
	status, err := job.Wait(ctx)
	if err != nil {
		return err
	}
	if err := status.Err(); err != nil {
		return err
	}
	return nil
}

Java

import com.google.cloud.bigquery.BigQuery;
import com.google.cloud.bigquery.BigQueryException;
import com.google.cloud.bigquery.BigQueryOptions;
import com.google.cloud.bigquery.CopyJobConfiguration;
import com.google.cloud.bigquery.EncryptionConfiguration;
import com.google.cloud.bigquery.Job;
import com.google.cloud.bigquery.JobInfo;
import com.google.cloud.bigquery.TableId;

// Sample to copy a cmek table
public class CopyTableCMEK {

  public static void runCopyTableCMEK() {
    // TODO(developer): Replace these variables before running the sample.
    String destinationDatasetName = "MY_DESTINATION_DATASET_NAME";
    String destinationTableId = "MY_DESTINATION_TABLE_NAME";
    String sourceDatasetName = "MY_SOURCE_DATASET_NAME";
    String sourceTableId = "MY_SOURCE_TABLE_NAME";
    String kmsKeyName = "MY_KMS_KEY_NAME";
    EncryptionConfiguration encryption =
        EncryptionConfiguration.newBuilder().setKmsKeyName(kmsKeyName).build();
    copyTableCMEK(
        sourceDatasetName, sourceTableId, destinationDatasetName, destinationTableId, encryption);
  }

  public static void copyTableCMEK(
      String sourceDatasetName,
      String sourceTableId,
      String destinationDatasetName,
      String destinationTableId,
      EncryptionConfiguration encryption) {
    try {
      // Initialize client that will be used to send requests. This client only needs to be created
      // once, and can be reused for multiple requests.
      BigQuery bigquery = BigQueryOptions.getDefaultInstance().getService();

      TableId sourceTable = TableId.of(sourceDatasetName, sourceTableId);
      TableId destinationTable = TableId.of(destinationDatasetName, destinationTableId);

      // For more information on CopyJobConfiguration see:
      // https://googleapis.dev/java/google-cloud-clients/latest/com/google/cloud/bigquery/JobConfiguration.html
      CopyJobConfiguration configuration =
          CopyJobConfiguration.newBuilder(destinationTable, sourceTable)
              .setDestinationEncryptionConfiguration(encryption)
              .build();

      // For more information on Job see:
      // https://googleapis.dev/java/google-cloud-clients/latest/index.html?com/google/cloud/bigquery/package-summary.html
      Job job = bigquery.create(JobInfo.of(configuration));

      // Blocks until this job completes its execution, either failing or succeeding.
      Job completedJob = job.waitFor();
      if (completedJob == null) {
        System.out.println("Job not executed since it no longer exists.");
        return;
      } else if (completedJob.getStatus().getError() != null) {
        System.out.println(
            "BigQuery was unable to copy table due to an error: \n" + job.getStatus().getError());
        return;
      }
      System.out.println("Table cmek copied successfully.");
    } catch (BigQueryException | InterruptedException e) {
      System.out.println("Table cmek copying job was interrupted. \n" + e.toString());
    }
  }
}

Python

试用此示例之前,请按照 BigQuery 快速入门:使用客户端库中的 Python 设置说明进行操作。如需了解详情,请参阅 BigQuery Python API 参考文档

如需向 BigQuery 进行身份验证,请设置应用默认凭据。 如需了解详情,请参阅为客户端库设置身份验证

QueryJobConfig.destination_encryption_configuration 属性设置为 EncryptionConfiguration 并复制表,以使用 CMEK 保护表的副本目标。

from google.cloud import bigquery

# Construct a BigQuery client object.
client = bigquery.Client()

# TODO(developer): Set dest_table_id to the ID of the destination table.
# dest_table_id = "your-project.your_dataset.your_table_name"

# TODO(developer): Set orig_table_id to the ID of the original table.
# orig_table_id = "your-project.your_dataset.your_table_name"

# Set the encryption key to use for the destination.
# TODO(developer): Replace this key with a key you have created in KMS.
# kms_key_name = "projects/{}/locations/{}/keyRings/{}/cryptoKeys/{}".format(
#     your-project, location, your-ring, your-key
# )

job_config = bigquery.CopyJobConfig(
    destination_encryption_configuration=bigquery.EncryptionConfiguration(
        kms_key_name=kms_key_name
    )
)
job = client.copy_table(orig_table_id, dest_table_id, job_config=job_config)
job.result()  # Wait for the job to complete.

dest_table = client.get_table(dest_table_id)  # Make an API request.
if dest_table.encryption_configuration.kms_key_name == kms_key_name:
    print("A copy of the table created")

确定表是否受 Cloud KMS 保护

  1. 在 Google Cloud 控制台中,点击数据集左侧的蓝色箭头以展开数据集,或双击数据集名称。这将显示数据集中的表和视图。

  2. 点击表名称。

  3. 点击 DetailsTable Details 页面会显示表说明和表信息。

  4. 如果表受 Cloud KMS 保护,则客户管理的加密密钥字段会显示密钥资源 ID。

    受保护的表。

更改 BigQuery 表的 Cloud KMS 密钥

如需更改受 CMEK 保护的现有表的 Cloud KMS 密钥,您可以运行 ALTER TABLE 查询、使用 API 或使用 bq 命令行工具。有两种使用 API 和 bq 命令行工具修改 Cloud KMS 密钥的方法:updatecp

如果使用 update,则可以更改用于受 CMEK 保护的表的 Cloud KMS 密钥。

如果使用 cp,则可以更改用于受 CMEK 保护的表的 Cloud KMS 密钥,将表从默认加密更改为 CMEK 保护,也可以将表从 CMEK 保护更改为默认加密。

update 的一个优点是速度比 cp 更快,并且支持使用表修饰器

SQL

使用 ALTER TABLE SET OPTIONS 语句更新表的 kms_key_name 字段:

  1. 在 Google Cloud 控制台中,转到 BigQuery 页面。

    转到 BigQuery

  2. 在查询编辑器中,输入以下语句:

    ALTER TABLE DATASET_ID.mytable
    SET OPTIONS (
      kms_key_name
        = 'projects/KMS_PROJECT_ID/locations/LOCATION/keyRings/KEY_RING/cryptoKeys/KEY');
    

  3. 点击 运行

如需详细了解如何运行查询,请参阅运行交互式查询

bq

您可以将 bq cp 命令与 --destination_kms_key 标志一起使用,以更改受 Cloud KMS 保护的表的密钥。--destination_kms_key 标志指定要用于表的密钥的资源 ID

bq update \
--destination_kms_key projects/KMS_PROJECT_ID/locations/LOCATION/keyRings/KEY_RING/cryptoKeys/KEY \
-t DATASET_ID.TABLE_ID

Go

import (
	"context"
	"fmt"

	"cloud.google.com/go/bigquery"
)

// updateTableChangeCMEK demonstrates how to change the customer managed encryption key that protects a table.
func updateTableChangeCMEK(projectID, datasetID, tableID string) error {
	// projectID := "my-project-id"
	// datasetID := "mydatasetid"
	// tableID := "mytableid"
	ctx := context.Background()

	client, err := bigquery.NewClient(ctx, projectID)
	if err != nil {
		return fmt.Errorf("bigquery.NewClient: %v", err)
	}
	defer client.Close()

	tableRef := client.Dataset(datasetID).Table(tableID)
	meta, err := tableRef.Metadata(ctx)
	if err != nil {
		return err
	}
	update := bigquery.TableMetadataToUpdate{
		EncryptionConfig: &bigquery.EncryptionConfig{
			// TODO: Replace this key with a key you have created in Cloud KMS.
			KMSKeyName: "projects/cloud-samples-tests/locations/us-central1/keyRings/test/cryptoKeys/otherkey",
		},
	}
	if _, err := tableRef.Update(ctx, update, meta.ETag); err != nil {
		return err
	}
	return nil
}

Java

import com.google.cloud.bigquery.BigQuery;
import com.google.cloud.bigquery.BigQueryException;
import com.google.cloud.bigquery.BigQueryOptions;
import com.google.cloud.bigquery.EncryptionConfiguration;
import com.google.cloud.bigquery.Table;
import com.google.cloud.bigquery.TableId;

// Sample to update a cmek table
public class UpdateTableCMEK {

  public static void runUpdateTableCMEK() {
    // TODO(developer): Replace these variables before running the sample.
    String datasetName = "MY_DATASET_NAME";
    String tableName = "MY_TABLE_NAME";
    String kmsKeyName = "MY_KEY_NAME";
    // Set a new encryption key to use for the destination.
    // i.e. projects/{project}/locations/{location}/keyRings/{key_ring}/cryptoKeys/{cryptoKey}
    EncryptionConfiguration encryption =
        EncryptionConfiguration.newBuilder().setKmsKeyName(kmsKeyName).build();
    updateTableCMEK(datasetName, tableName, encryption);
  }

  public static void updateTableCMEK(
      String datasetName, String tableName, EncryptionConfiguration encryption) {
    try {
      // Initialize client that will be used to send requests. This client only needs to be created
      // once, and can be reused for multiple requests.
      BigQuery bigquery = BigQueryOptions.getDefaultInstance().getService();

      Table table = bigquery.getTable(TableId.of(datasetName, tableName));
      bigquery.update(table.toBuilder().setEncryptionConfiguration(encryption).build());
      System.out.println("Table cmek updated successfully");
    } catch (BigQueryException e) {
      System.out.println("Table cmek was not updated. \n" + e.toString());
    }
  }
}

Python

Table.encryption_configuration 属性更改为新的 EncryptionConfiguration 对象并更新该表,以更改表的 CMEK。

# from google.cloud import bigquery
# client = bigquery.Client()

assert table.encryption_configuration.kms_key_name == original_kms_key_name

# Set a new encryption key to use for the destination.
# TODO: Replace this key with a key you have created in KMS.
updated_kms_key_name = (
    "projects/cloud-samples-tests/locations/us/keyRings/test/cryptoKeys/otherkey"
)
table.encryption_configuration = bigquery.EncryptionConfiguration(
    kms_key_name=updated_kms_key_name
)

table = client.update_table(table, ["encryption_configuration"])  # API request

assert table.encryption_configuration.kms_key_name == updated_kms_key_name
assert original_kms_key_name != updated_kms_key_name

设置数据集默认密钥

您可以设置数据集范围的默认 Cloud KMS 密钥,以应用于数据集中所有新创建的表,除非在创建表时指定了不同的 Cloud KMS 密钥。默认密钥不会应用到现有的表。如果更改默认密钥,则系统不会修改现有的任何表,并且更改后的密钥只会应用到更改默认密钥后创建的新表。

您可以通过以下方式应用、更改或移除数据集的默认密钥:

  • 当您调用 datasets.insertdatasets.patch 方法时,在 EncryptionConfiguration.kmsKeyName 字段中指定默认密钥

  • 当您运行 bq mk --dataset 命令时在 --default_kms_key 标志中指定默认密钥。

    bq mk \
    --default_kms_key projects/KMS_PROJECT_ID/locations/LOCATION/keyRings/KEY_RING/cryptoKeys/KEY \
    --dataset DATASET_ID
    
    bq update \
    --default_kms_key projects/KMS_PROJECT_ID/locations/LOCATION/keyRings/KEY_RING/cryptoKeys/KEY \
    --dataset DATASET_ID
    

设置项目默认密钥

除非指定其他 Cloud KMS 密钥,否则您可以设置项目范围的默认 Cloud KMS 密钥,此默认密钥将应用于项目中所有查询结果和新创建的表。默认密钥不会应用到现有的表。如果更改默认密钥,则系统不会修改现有的任何表,并且更改后的密钥只会应用到更改默认密钥后创建的新表。

SQL

使用 ALTER PROJECT SET OPTIONS 语句更新项目的 default_kms_key_name 字段:您可以在 Cloud KMS 页面上找到密钥的资源名称。

  1. 在 Google Cloud 控制台中,转到 BigQuery 页面。

    转到 BigQuery

  2. 在查询编辑器中,输入以下语句:

    ALTER PROJECT PROJECT_ID
    SET OPTIONS (
      region-us.default_kms_key_name
        = 'projects/KMS_PROJECT_ID/locations/LOCATION/keyRings/KEY_RING/cryptoKeys/KEY');
    

  3. 点击 运行

如需详细了解如何运行查询,请参阅运行交互式查询

bq

您可以使用 bq 命令运行 ALTER PROJECT SET OPTIONS 语句,以更新项目的 default_kms_key_name 字段:

bq query --nouse_legacy_sql \
  'ALTER PROJECT PROJECT_ID
  SET OPTIONS (
  `region-us.default_kms_key_name`
    ="projects/KMS_PROJECT_ID/locations/LOCATION/keyRings/KEY_RING/cryptoKeys/KEY");'

使用 CMEK 保护 BigQuery ML 模型

BigQuery ML 支持 CMEK。除了 BigQuery 提供的默认加密之外,您还可以使用自己的 Cloud Key Management Service 密钥来加密机器学习模型,包括导入的 TensorFlow 模型。

使用 Cloud KMS 密钥创建加密模型

如需创建加密模型,请使用 CREATE MODEL 语句并在训练选项中指定 KMS_KEY_NAME

    CREATE MODEL my_dataset.my_model
    OPTIONS(
      model_type='linear_reg',
      input_label_cols=['your_label'],
      kms_key_name='projects/my_project/locations/my_location/keyRings/my_ring/cryptoKeys/my_key')
    AS SELECT * FROM my_dataset.my_data

同样的语法也适用于导入的 TensorFlow 模型:

    CREATE MODEL my_dataset.my_model
    OPTIONS(
      model_type='tensorflow',
      path='gs://bucket/path/to/saved_model/*',
      kms_key_name='projects/my_project/locations/my_location/keyRings/my_ring/cryptoKeys/my_key')
    AS SELECT * FROM my_dataset.my_data

限制

加密机器学习模型时,客户管理的加密密钥具有以下限制:

将模型从默认加密方式改为受 Cloud KMS 保护

您可以使用带有 --destination_kms_key 标志的 bq cp 命令,以将受默认加密保护的模型复制到受 Cloud KMS 保护的新模型中。或者,您可以使用带有 -f 标志的 bq cp 命令,以覆盖受默认加密保护的模型,并将其更新为使用 Cloud KMS 保护。--destination_kms_key 标志指定要用于目标模型的密钥的资源 ID

如需将采用默认加密的表复制到受 Cloud KMS 保护的新表,请执行以下操作:

bq cp \
--destination_kms_key projects/KMS_PROJECT_ID/locations/LOCATION/keyRings/KEY_RING/cryptoKeys/KEY \
SOURCE_DATASET_ID.SOURCE_MODEL_ID DESTINATION_DATASET_ID.DESTINATION_MODEL_ID

如需将采用默认加密的模型覆盖到受 Cloud KMS 保护的相同模型,请执行以下操作:

bq cp -f \
--destination_kms_key projects/KMS_PROJECT_ID/locations/LOCATION/keyRings/KEY_RING/cryptoKeys/KEY \
DATASET_ID.MODEL_ID DATASET_ID.MODEL_ID

如需将模型从 Cloud KMS 保护更改为默认加密,请执行以下操作:

bq cp -f \
DATASET_ID.MODEL_ID DATASET_ID.MODEL_ID

如需详细了解 bq 命令行工具,请参阅使用 bq 命令行工具

确定模型是否受 Cloud KMS 保护

使用 bq show 命令查看模型是否受 Cloud KMS 密钥保护。加密密钥位于 kmsKeyName 字段。

bq show -m my_dataset.my_model

您还可以使用 Google Cloud 控制台找到加密模型的 Cloud KMS 密钥。CMEK 信息位于模型详情窗格的模型详情部分中的客户管理的密钥字段中。

更改加密模型的 Cloud KMS 密钥

使用带有 --destination_kms_key 标志的 bq update 命令更改受 Cloud KMS 保护的模型的密钥:

bq update --destination_kms_key \
projects/my_project/locations/my_location/keyRings/my_ring/cryptoKeys/my_key \
-t my_dataset.my_model

使用默认项目或数据集键

如果您在项目或数据集级层设置了默认的 Cloud KMS 密钥,则 BigQuery ML 会在创建模型时自动使用此密钥。如果您不想使用默认密钥,请使用 CREATE MODEL 语句指定用于加密模型的其他密钥。

将 BigQuery ML 函数与加密模型配合使用

您可以将所有 BigQuery ML 函数与加密模型结合使用,而无需指定加密密钥。

使用 CMEK 保护 BigQuery Connection API

对于 Cloud SQL 连接,您可以使用 CMEK 保护 BigQuery Connection API 凭据。

如需详细了解如何创建受 CMEK 保护的连接,请参阅创建 Cloud SQL 连接

撤消 BigQuery 对 Cloud KMS 密钥的访问权限

您可以随时移除 BigQuery 对 Cloud KMS 密钥的访问权限,方法是撤消该密钥的 Identity and Access Management (IAM) 权限。

如果 BigQuery 失去对 Could KMS 密钥的访问权限,用户体验会受到严重影响,并且数据可能会丢失:

  • 这些受 CMEK 保护的表中的数据将不再能够访问:querycpextracttabledata.list 全部都将失败。

  • 新数据无法再添加到这些受 CMEK 保护的表中。

  • 重新授予访问权限后,对这些表所执行的查询的性能也可能在数天内都有所下降。

使用组织政策控制 CMEK 使用情况

BigQuery 集成了 CMEK 组织政策限制条件,可让您指定组织中 BigQuery 资源的加密合规性要求。

借助此集成,您可以执行以下操作:

  • 要求为项目中的所有 BigQuery 资源使用 CMEK。

  • 限制哪些 Cloud KMS 密钥可用于保护项目中的项目资源。

所有资源都需要 CMEK

常见政策是要求使用 CMEK 来保护特定项目集中的所有资源。您可以使用 constraints/gcp.restrictNonCmekServices 限制条件在 BigQuery 中强制执行此政策。

如果设置,此组织政策会导致没有指定 Cloud KMS 密钥的所有资源创建请求失败。

设置此政策后,它仅适用于项目中的新资源。未设置 Cloud KMS 密钥的所有现有资源将继续存在,并且可以正常访问。

控制台

  1. 打开组织政策页面。

    转到组织政策

  2. 过滤条件字段中,输入 constraints/gcp.restrictNonCmekServices,然后点击限制哪些服务可以在没有 CMEK 的情况下创建资源

  3. 点击 修改

  4. 依次选择自定义替换,然后点击添加规则

  5. 选择自定义,然后点击拒绝

  6. 自定义值字段中,输入 is:bigquery.googleapis.com

  7. 点击完成,然后点击保存

gcloud

  gcloud resource-manager org-policies --project=PROJECT_ID \
    deny gcp.restrictNonCmekServices is:bigquery.googleapis.com

如需验证政策是否已成功应用,您可以尝试在项目中创建表。除非您指定 Cloud KMS 密钥,否则该过程将失败。

此政策也适用于项目中的查询结果表。您可以指定项目默认密钥,这样用户就无需每次在项目中执行查询时手动指定密钥。

限制 BigQuery 项目的 Cloud KMS 密钥

您可以使用 constraints/gcp.restrictCmekCryptoKeyProjects 限制条件来限制可用于保护 BigQuery 项目中的资源的 Cloud KMS 密钥。

您可以指定规则,例如“对于 projects/my-company-data-project 中的所有 BigQuery 资源,此项目中使用的 Cloud KMS 密钥必须来自 projects/my-company-central-keys 或 projects/team-specific-keys”。

控制台

  1. 打开组织政策页面。

    转到组织政策

  2. 过滤条件字段中,输入 constraints/gcp.restrictCmekCryptoKeyProjects,然后点击限制哪些项目可以为 CMEK 提供 KMS CryptoKey

  3. 点击 修改

  4. 依次选择自定义替换,然后点击添加规则

  5. 选择自定义,然后点击允许

  6. 自定义值字段中,输入 under:projects/<var>KMS_PROJECT_ID</var>

  7. 点击完成,然后点击保存

gcloud

  gcloud resource-manager org-policies --project=PROJECT_ID \
    allow gcp.restrictCmekCryptoKeyProjects under:projects/KMS_PROJECT_ID

如需验证政策是否已成功应用,您可以尝试使用其他项目中的 Cloud KMS 密钥创建表。该过程将失败。

组织政策的限制

设置组织政策时存在一些限制。

传播延迟

设置或更新组织政策后,新政策最多可能需要 15 分钟才能生效。BigQuery 会缓存政策,以免对查询和表创建延迟时间产生负面影响。

设置组织政策所需的权限

出于测试目的,可能难以获取设置或更新组织政策的权限。必须具有 Organization Policy Administrator 角色,该角色只能在组织级层(而不是项目或文件夹级层)授予。

虽然该角色必须在组织级层授予,但仍然可以指定仅应用于特定项目或文件夹的政策。

Cloud KMS 密钥轮替的影响

当与表关联的 Cloud KMS 密钥轮替时,BigQuery 不会自动轮替表加密密钥。现有表中的所有数据都会继续受创建它们所使用的密钥版本的保护。

任何新创建的表在创建时都使用主键版本。

如需更新表以使用最新的密钥版本,请将表更改为使用其他 Cloud KMS 密钥,然后更改回使用原始密钥。

对 Cloud KMS 结算的影响

创建或截断受 CMEK 保护的表时,BigQuery 会生成中间密钥加密密钥,然后使用指定的 Cloud KMS 密钥对其进行加密。

对于计费而言,这意味着对 Cloud KMS 的调用及其关联的费用不会随表大小而增减。对于受 CMEK 保护的表,预计每项表创建或截断操作调用一次 Cloud KMS cryptoKeys.encrypt,查询中涉及的每个表调用一次 Cloud KMS cryptoKeys.decrypt。这些方法都属于 Cloud KMS 价格中列出的“密钥操作:加密”类别。

读取或写入受 CMEK 保护的现有表会调用 Cloud KMS cryptoKeys.decrypt,因为中间密钥必须进行解密。

限制

BigQuery 对 Cloud KMS 密钥的访问权限

如果符合以下条件,Cloud KMS 密钥会被视为可由 BigQuery 使用和访问:

  • 密钥已启用
  • BigQuery 服务账号拥有该密钥的加密和解密权限

以下部分介绍当密钥不可访问时,对流式插入和长期无法访问的数据所造成的影响。

对流式插入的影响

在流式插入请求之后的 48 小时内,Cloud KMS 密钥必须至少连续 24 小时可用且可访问。如果不能使用和访问密钥,流式插入的数据可能会无法完整保留,并且可能会丢失。如需详细了解流式插入,请参阅将数据流式插入到 BigQuery 中

对长期无法访问的数据的影响

由于 BigQuery 提供托管式存储空间,因此长期无法访问的数据与 BigQuery 的架构是不兼容的。如果连续 60 天无法使用和访问特定 BigQuery 表的 Cloud KMS 密钥,BigQuery 可能会选择删除该表及其关联的数据。在删除数据之前,BigQuery 会提前至少 7 天向与结算账号关联的电子邮件地址发送电子邮件。

使用外部数据源

如果您要查询存储在外部数据源(例如,使用 CMEK 加密数据的 Cloud Storage)中的数据,则数据加密由 Cloud Storage 管理。例如,BigLake 表支持在 Cloud Storage 中使用 CMEK 加密的数据。

BigQuery 和 BigLake 表不支持客户提供的加密密钥 (CSEK)。

在由 CMEK 保护的加密与默认加密之间切换

您无法在默认加密和 CMEK 加密之间就地切换表。如需切换加密,请复制表并使用目标加密信息集,或使用 SELECT * 查询通过 WRITE_TRUNCATE 处理方式选择表本身。

使用表修饰器

如果您使用 Cloud KMS 保护表,然后使用 loadcpquery 操作的值 WRITE_TRUNCATE 替换表中的数据,则范围修饰器不适用于加密更改边界。您仍然可以使用表修饰器(包括范围修饰器)查询边界之前或之后的数据,或者查询某个时间点的快照。

通配符表查询

无法使用通配符后缀查询受 CMEK 保护的表。

版本支持

对 BigQuery 的 CMEK 支持仅适用于 BigQuery 企业 Plus 版和 BigQuery 按需版。2023 年 7 月 5 日之前的旧版统一费率预留 BigQuery 客户会保留企业版层级对 CMEK 的所有现有支持。

BigQuery Studio 支持

BigQuery Studio 代码资产(包括已保存的查询笔记本)不支持 CMEK。

常见问题解答

谁需要 Cloud KMS 密钥的权限?

使用 CMEK 时,您无需重复指定权限。只要 BigQuery 服务账号有权使用 Cloud KMS 密钥进行加密和解密,任何拥有 BigQuery 表权限的人都可以访问数据(即使无法直接访问 Cloud KMS 密钥)。

使用哪个服务账号?

与表的 Google Cloud 项目关联的 BigQuery 服务账号将用于解密该表的数据。BigQuery 服务账号对每个项目都是唯一的。如果作业将数据写入受 Cloud KMS 保护的匿名表中,则将使用该作业的项目的服务账号。

例如,假设有三个受 CMEK 保护的表:table1table2table3。如需在将 {project3.table3} 用作目标表的情况下查询 {project1.table1, project2.table2} 中的数据,请按以下指示操作:

  • 对于 project1.table1,使用 project1 服务账号
  • 对于 project2.table2,使用 project2 服务账号
  • 对于 project3.table3,使用 project3 服务账号

BigQuery 可以通过哪些方式使用我的 Cloud KMS 密钥?

BigQuery 使用 Cloud KMS 密钥来解密数据,以响应用户查询,例如 tabledata.listjobs.insert

BigQuery 还可以将密钥用于数据维护和存储优化任务,例如将数据转换为读取优化格式。

使用了哪些加密库?

BigQuery 依赖于 Cloud KMS 来实现 CMEK 功能。 Cloud KMS 使用 Tink 进行加密。

如何获取更多帮助?

如果您还有尚未得到解答的其他疑问,请参阅 BigQuery 支持

排查错误

下面描述了常见错误和建议的缓解措施。

错误 建议
请授予 Cloud KMS 加密密钥加密者/解密者角色 与您项目关联的 BigQuery 服务账号没有足够的 IAM 权限,无法对指定的 Cloud KMS 密钥执行操作。请按照错误消息或本文档中的说明操作,授予适当的 IAM 权限。
现有表加密设置与请求中指定的加密设置不匹配 如果目标表的加密设置与请求中的加密设置不匹配,则可能会发生这种情况。作为缓解措施,可使用 TRUNCATE 这一写入处置方式来替换该表,或指定其他目标表。
此区域不受支持 Cloud KMS 密钥的区域与目标表的 BigQuery 数据集的区域不匹配。作为缓解措施,可选择与数据集匹配的区域中的密钥,或加载与密钥区域匹配的数据集。
您的管理员要求您为项目 PROJECT_ID. 中的查询指定加密密钥 组织政策阻止创建资源或运行查询。如需详细了解此政策,请参阅要求 BigQuery 项目中的所有资源使用 CMEK
您的管理员会阻止使用项目 KMS_PROJECT_ID 中的 KMS 密钥来保护项目 PROJECT_ID 中的资源。 组织政策阻止创建资源或运行查询。如需详细了解此政策,请参阅限制 BigQuery 项目的 Cloud KMS 密钥