为 Secret 设置通知

本主题讨论了对 Secret Manager 中的事件通知的支持。

概览

事件通知会将密钥和密钥版本的更改信息发送到 Pub/Sub。这些通知可用于触发任意工作流,例如在添加新的 Secret 版本时重启应用,或者在删除 Secret 时通知安全工程师。如需详细了解如何使用这些通知触发工作流,请参阅 Pub/Sub 文档

Secret Manager 中事件通知的运作方式

密文最多可配置 10 个 Pub/Sub 主题的列表。每当执行修改密钥或其某个版本的操作时,Secret Manager 都会自动向该密钥的每个 Pub/Sub 主题发布一条消息。GetListAccess 调用不导致消息发布。

Pub/Sub 消息具有一组“特性”键值对(包含有关事件的元数据)以及“数据”字段(包含已创建或修改的 SecretSecretVersion 资源的完整 JSON 序列化)。此 JSON 是采用 UTF-8 编码的字符串,该字符串以 Secret Manager 公共 API 指定的形式表示 SecretSecretVersion 资源,并按照 proto3 JSON 映射中指定的进行 JSON 编码。

事件类型

Secret Manager 支持的事件类型的列表如下:

事件类型 说明
SECRET_CREATE 在成功创建新密文时发送。
SECRET_UPDATE 在成功更新新密文时发送。
SECRET_DELETE 当密钥因用户发起的请求或密钥到期而被删除时发送。
SECRET_VERSION_ADD 在成功添加新的密文版本时发送。
SECRET_VERSION_ENABLE 在启用密文版本时发送。
SECRET_VERSION_DISABLE 在停用密文版本时发送。
SECRET_VERSION_DESTROY 在销毁密文版本时发送。
SECRET_VERSION_DESTROY_SCHEDULED 在密钥上配置销毁延迟时长,并且用户尝试销毁密钥版本时发送。
SECRET_ROTATE 在轮替密文时发送。如需了解详情,请参阅为密文创建和管理轮替政策
TOPIC_CONFIGURED

这是一条测试消息,不包含除 eventType: TOPIC_CONFIGURED 以外的正文或特性。在创建或更新具有 Pub/Sub 主题列表的密文时,系统会发送此消息,但这并表示操作成功。

如果操作成功,系统将之后立即发送 SECRET_CREATESECRET_UPDATE 消息。

每当在密文上更新主题时,系统都会向密文上的所有主题(包括已存在的主题)发送 TOPIC_CONFIGURED 消息。

通知格式

发送到 Pub/Sub 主题的通知由以下两部分组成:

  • 属性:描述事件的一组键值对。
  • 数据:包含已更改对象的元数据的字符串。

特性

属性是指 Secret Manager 发送到 Pub/Sub 主题的通知中包含的键值对。始终(TOPIC_CONFIGURED 条测试消息除外)的所有通知 包含下列一组键值对,而不考虑通知的 数据:

特性名称 示例 说明
eventType SECRET_CREATE 刚刚发生的事件的类型。要查看可能的值的列表,请参阅事件类型
dataFormat JSON_API_V1 对象数据的格式。
secretId projects/p/secrets/my-secret 在其中发生事件的密文的完整资源名称。
timestamp 2021-01-20T11:17:45.081104-08:00 事件发生的时间。

此外,通知有时会包含下列一组键值对:

特性名称 示例 说明
versionId projects/p/secrets/my-secret/versions/456

在其中发生事件的密文版本的名称。

此特性仅存在于 SECRET_VERSION_ADDSECRET_VERSION_ENABLESECRET_VERSION_DISABLESECRET_VERSION_DESTROY 事件通知中。

deleteType REQUESTED 用户请求删除 (REQUESTED) 还是由于密文过期删除 (EXPIRATION)。此特性仅存在于 SECRET_DELETE 事件通知中。

数据

数据字段是一个 UTF-8 字符串,其中包含已更改对象的元数据。数据是 Secret 或 Secret 版本。

对于 SECRET_DELETE 通知,数据字段中包含的元数据代表删除前的对象元数据。对于所有其他通知, 数据字段表示对象元数据, 更改发生的时间。

限制

事件通知仅在 Secret Manager v1 API 和 Google Cloud CLI 中提供。

准备工作

您可以选择将所有资源存储在同一项目中,也可以将密文和 Pub/Sub 主题存储在不同的项目中。满足以下前提条件才能设置 Secret Manager 和 Pub/Sub:

  • Secret Manager:

    • 创建或使用现有项目来保存您的 Secret Manager 资源。
    • 如有必要,请完成 Secret Manager 指南中的启用 Secret Manager API 页面。
  • Pub/Sub:

    • 创建项目或使用现有项目来保存 Pub/Sub 资源。
    • 如有必要,请启用 Pub/Sub API

向 Google Cloud 进行身份验证:

$ gcloud auth login --update-adc

创建服务代理身份

您需要为每个需要包含事件通知的密文的项目创建服务代理身份。

如需使用 Google Cloud CLI 创建服务身份,请运行以下命令:

$ gcloud beta services identity create \
    --service "secretmanager.googleapis.com" \
    --project "PROJECT_ID"

上述命令会按以下格式返回服务账号名称:

service-PROJECT_NUMBER@gcp-sa-secretmanager.iam.gserviceaccount.com

您将向此服务账号授予针对为密文配置的 Pub/Sub 主题进行发布的权限。

将服务账号名称保存为环境变量:

# This is from the output of the command above
$ export SM_SERVICE_ACCOUNT="service-...."

在遵循此流程的全过程中,您必须设置 Secret Manager 项目、Pub/Sub 项目和 Secret Manager 服务账号的环境变量。

创建 Pub/Sub 主题

按照 Pub/Sub 快速入门中的说明,在 Google Cloud 控制台中的 Pub/Sub 项目中创建主题。或者,您也可以使用 Google Cloud CLI 创建主题,如下例所示。

$ gcloud pubsub topics create "projects/PUBSUB_PROJECT_ID/topics/PUBSUB_TOPIC_NAME"

如果您想为 Secret 创建多个 Pub/Sub 主题,请重复此操作多次。

向 Secret Manager 的服务账号授予针对刚创建的主题进行发布的权限。您可以通过 Google Cloud 控制台或 Google Cloud CLI 执行此操作。以下命令将 my-topic Pub/Sub 主题的 Pub/Sub Publisher 角色 (roles/pubsub.publisher) 授予服务账号。

$ gcloud pubsub topics add-iam-policy-binding PUBSUB_TOPIC_NAME \
    --member "serviceAccount:${SM_SERVICE_ACCOUNT}" \
    --role "roles/pubsub.publisher"

创建 Pub/Sub 订阅

如需查看发布到某个主题的消息,您还必须为该主题创建一个订阅。按照 Pub/Sub 快速入门操作,通过 Google Cloud 控制台在 Pub/Sub 项目中创建订阅。或者,您也可以使用 Google Cloud CLI 创建订阅,如以下示例所示。

$ gcloud pubsub subscriptions create "projects/PUBSUB_PROJECT_ID/subscriptions/PUBSUB_SUBSCRIPTION_NAME" \
    --topic "projects/PUBSUB_PROJECT_ID/topics/PUBSUB_TOPIC_NAME"

创建配置了主题的密文

创建一个最多配置了 10 个主题列表的密文。如果密文或其某个版本发生更改,则在密文上配置的所有主题都会收到事件通知。以下命令会创建一个配置了 my-topic 的密文。

gcloud

如需在命令行中使用 Secret Manager,请先 安装或升级到 378.0.0 或更高版本的 Google Cloud CLI。 在 Compute Engine 或 GKE 上,您必须使用 cloud-platform 范围进行身份验证

$ gcloud secrets create SECRET_ID --topics TOPIC_NAME

API

这些示例使用 curl 来使用 API 演示。 您可以使用 gcloud auth print-access-token 生成访问令牌。在 Compute Engine 或 GKE 上,您必须使用 cloud-platform 范围进行身份验证

$ curl "https://secretmanager.googleapis.com/v1/projects/PROJECT_ID/secrets?secretId=SECRET_ID" \
    --request "POST" \
    --header "Content-Type: application/json" \
    --header "Authorization: Bearer $(gcloud auth print-access-token)" \
    --data-binary @- <<EOF
{
  "replication":{
    "automatic":{}
  },
  "topics":{
    "name": "TOPIC_NAME"
  }
}
EOF

更新 Secret 主题

通过使用新的 Pub/Sub 主题资源名称更新密文,修改在密文上配置的 Pub/Sub 主题。借助 Google Cloud CLI,您可以在 Secret 中添加或移除一个或多个主题,以及清除 Secret 中的所有主题。

添加主题

为密文添加一个或多个主题。添加已存在的主题将无效。

$ gcloud secrets update "SECRET_ID" \
    --project "PROJECT_ID" \
    --add-topics "projects/PUBSUB_PROJECT_ID/topics/my-topic-2,projects/PUBSUB_PROJECT_ID/topics/PUBSUB_TOPIC_NAME"

移除主题

从 Secret 中移除一个或多个主题。移除不存在的主题将无效。

$ gcloud secrets update "SECRET_ID" \
    --project "PROJECT_ID" \
    --remove-topics "projects/PUBSUB_PROJECT_ID/topics/PUBSUB_TOPIC_NAME,projects/PUBSUB_PROJECT_ID/topics/PUBSUB_OTHER_TOPIC_NAME"

清除主题

从 Secret 中移除所有主题。

$ gcloud secrets update SECRET_ID \
    --project "PROJECT_ID" \
    --clear-topics

通过 Cloud Run 函数使用事件通知

您可以通过创建 Cloud Run 函数来使用事件通知触发任意工作流,以便使用 Pub/Sub 消息。如需查看完整指南,请参阅 Cloud Run functions 文档。以下示例代码适用于 Cloud Functions 函数,该函数会在有事件发布到主题时输出 eventType、secretId 和元数据。您可以在此处找到 Secret Manager 所有事件类型的列表。

C#

如需运行此代码,请先设置 C# 开发环境安装 Secret Manager C# SDK。在 Compute Engine 或 GKE 上,您必须使用 cloud-platform 范围进行身份验证

using CloudNative.CloudEvents;
using Google.Cloud.Functions.Framework;
using Google.Events.Protobuf.Cloud.PubSub.V1;
using System;
using System.Threading;
using System.Threading.Tasks;

// Triggered from a message on a Cloud Pub/Sub topic.
// The printed value will be visible in Cloud Logging
// (https://cloud.google.com/functions/docs/monitoring/logging).
namespace PubSubSample
{
    public class Function : ICloudEventFunction<MessagePublishedData>
    {
        public Task HandleAsync(CloudEvent cloudEvent, MessagePublishedData data, CancellationToken cancellationToken)
        {
          string eventType = data.Message.Attributes["eventType"];
          string secretId = data.Message.Attributes["secretId"];
          string secretMetadata = data.Message.TextData;
          Console.WriteLine($"Received {eventType} for {secretId}. New metadata: {secretMetadata}.");
          return Task.CompletedTask;
        }
    }
}

Go

如需运行此代码,请先设置 Go 开发环境安装 Secret Manager Go SDK。 在 Compute Engine 或 GKE 上,您必须使用 cloud-platform 范围进行身份验证

import (
	"context"
	"fmt"
)

// PubSubMessage is the payload of a Pub/Sub event.
type PubSubMessage struct {
	Attributes PubSubAttributes `json:"attributes"`
	Data       []byte           `json:"data"`
}

// PubSubAttributes are attributes from the Pub/Sub event.
type PubSubAttributes struct {
	SecretId  string `json:"secretId"`
	EventType string `json:"eventType"`
}

// ConsumeEventNotification demonstrates how to consume and process the Pub/Sub
// notification from Secret Manager.
func ConsumeEventNotification(ctx context.Context, m PubSubMessage) (string, error) {
	// The printed value will be visible in Cloud Logging:
	//
	//     https://cloud.google.com/functions/docs/monitoring/logging
	//
	eventType := m.Attributes.EventType
	secretID := m.Attributes.SecretId
	data := m.Data

	return fmt.Sprintf("Received %s for %s. New metadata: %q.",
		eventType, secretID, data), nil
}

Java

如需了解如何安装和使用 Secret Manager 客户端库,请参阅 Secret Manager 客户端库

如需向 Secret Manager 进行身份验证,请设置应用默认凭据。 如需了解详情,请参阅为本地开发环境设置身份验证


import java.util.Base64;
import java.util.Map;
import java.util.logging.Logger;
import lombok.Data;

// Demonstrates how to consume and process a Pub/Sub notification from Secret Manager. Triggered
// by a message on a Cloud Pub/Sub topic.
// Ideally the class should implement a background function that accepts a Pub/Sub message.
// public class ConsumeEventNotification implements BackgroundFunction<PubSubMessage> { }
public class ConsumeEventNotification {

  // You can configure the logs to print the message in Cloud Logging.
  private static final Logger logger = Logger.getLogger(ConsumeEventNotification.class.getName());

  // Accepts a message from a Pub/Sub topic and writes it to logger.
  public static String accept(PubSubMessage message) {
    String eventType = message.attributes.get("eventType");
    String secretId = message.attributes.get("secretId");
    String data = new String(Base64.getDecoder().decode(message.data));
    String log = String.format("Received %s for %s. New metadata: %s", eventType, secretId, data);
    logger.info(log);
    return log;
  }

  // Event payload. Mock of the actual Pub/Sub message.
  @Data
  public static class PubSubMessage {

    byte[] data;
    Map<String, String> attributes;
    String messageId;
    String publishTime;
    String orderingKey;
  }
}

Node.js

要运行此代码,请先设置 Node.js 开发环境安装 Secret Manager Node.js SDK。 在 Compute Engine 或 GKE 上,您必须使用 cloud-platform 范围进行身份验证

/**
* Triggered from a message on a Cloud Pub/Sub topic.
* The printed value will be visible in Cloud Logging
* (https://cloud.google.com/functions/docs/monitoring/logging).
*
* @param {!Object} event Event payload.
* @param {!Object} context Metadata for the event.
*/
exports.smEventsFunction = (event, context) => {
  const eventType = event.attributes.eventType;
  const secretID = event.attributes.secretId;
  const secretMetadata = Buffer.from(event.data, 'base64').toString();
  console.log(`Received ${eventType} for ${secretID}. New metadata: ${secretMetadata}.`);
};

Python

如需运行此代码,请先设置 Python 开发环境安装 Secret Manager Python SDK。 在 Compute Engine 或 GKE 上,您必须使用 cloud-platform 范围进行身份验证

import base64


def consume_event_notification(event: dict, unused_context: None) -> str:
    """
    consume_event_notification demonstrates how to consume and process a
    Pub/Sub notification from Secret Manager.
    Args:
          event (dict): Event payload.
          unused_context (google.cloud.functions.Context): Metadata for the event.
    """
    event_type = event["attributes"]["eventType"]
    secret_id = event["attributes"]["secretId"]
    secret_metadata = base64.b64decode(event["data"]).decode("utf-8")
    event_notification = (
        f"Received {event_type} for {secret_id}. New metadata: {secret_metadata}"
    )
    print(event_notification)
    return event_notification

Ruby

要运行此代码,请先设置 Ruby 开发环境安装 Secret Manager Ruby SDK。 在 Compute Engine 或 GKE 上,您必须使用 cloud-platform 范围进行身份验证

require "functions_framework"
require "base64"

# Triggered from a message on a Cloud Pub/Sub topic.
# The printed value will be visible in Cloud Logging
# (https://cloud.google.com/functions/docs/monitoring/logging).
FunctionsFramework.cloud_event "sm_events_function" do |event|
  message = event.data["message"]
  event_type = message["attributes"]["eventType"]
  secret_id = message["attributes"]["secretId"]
  message_data = Base64.decode64 message["data"]
  FunctionsFramework.logger.info "Received %s for %s. New metadata: %s." % [event_type, secret_id, message_data]
end

主题配置有误

如果在“创建或更新”操作中向密文添加了 Pub/Sub 主题,但 Secret Manager 因配置错误而无法向主题发布消息,则操作将失败并显示错误消息,表明发布失败的原因。例如,如果主题不存在,或者 Secret Manager 服务账号没有发布权限,则可能会发生这种情况。

如果向密文添加了 Pub/Sub 主题,之后主题发生更改,使 Secret Manager 无法再发布消息(例如,主题被删除或 Secret Manager 服务账号权限被移除),则 Secret Manager 会将日志写入 Secret Manager Secret 资源,其中包含表明发布失败的原因的消息。

后续步骤