Secret Manager 的事件通知

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

概览

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

事件通知在 Secret Manager 中的工作原理

密文最多可以配置 10 个 Pub/Sub 主题列表。每当执行修改密文或其某个版本的操作时,Secret Manager 都会自动向该密文上的每个 Pub/Sub 主题发布消息。调用 Get、List 和 Access 不会发布消息。

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_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_DELETE 通知,数据字段中包含的元数据代表删除前的对象元数据。对于其他所有通知,数据字段中包含的元数据代表发生更改后的对象元数据。

限制

只能通过 Secret Manager v1 API 和 gcloud 命令行工具获得事件通知。

准备工作

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

  • Secret Manager:

    • 创建或使用现有项目来保存您的 Secret Manager 资源。
    • 如有必要,请完成 Secret Manager 快速入门的配置 Secret Manager 部分中的步骤。
  • Pub/Sub:

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

向 Google Cloud 进行身份验证:

$ gcloud auth login --update-adc

创建服务代理身份

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

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

$ 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 快速入门在 Cloud Console 中的 Pub/Sub 项目中创建主题。或者,您也可以使用 gcloud 命令行工具创建主题,如以下示例所示。

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

如果您要在密文上创建多个 Pub/Sub 主题,请重复此操作多次。

向 Secret Manager 的服务帐号授予针对刚创建的主题进行发布的权限。此操作可以通过 Cloud Console 或 gcloud 命令行工具来完成。以下命令会向服务帐号授予对 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 快速入门在 Cloud Console 中的 Pub/Sub 项目中创建订阅。或者,您也可以使用 gcloud 命令行工具创建订阅,如以下示例所示。

$ 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,请先安装或升级到 Cloud SDK 版本 338.0.0 或更高版本。在 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

更新密文主题

通过使用新的 Pub/Sub 主题资源名称更新密文,修改在密文上配置的 Pub/Sub 主题。您可以使用 gcloud 命令行工具在密文中添加或移除一个或多个主题,以及从密文中清除所有主题。

添加主题

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

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

移除主题

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

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

清除主题

从密文中移除所有主题。

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

通过 Cloud Functions 使用事件通知

事件通知可用于创建任意工作流,方法是创建 Cloud Functions 函数来使用 Pub/Sub 消息。如需查看完整指南,请参阅 Cloud Functions 文档。以下示例代码适用于 Cloud Functions 函数,每当 UPDATE_SECRET 事件发布到主题时,该函数都会输出密文元数据。

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
    {
        public Task HandleAsync(CloudEvent cloudEvent, MessagePublishedData data, CancellationToken cancellationToken)
        {
            if (data.Message.Attributes["eventType"] == "SECRET_UPDATE") {
                string secretId = data.Message.Attributes["secretId"];
                string secretMetadata = data.Message.TextData;
                Console.WriteLine($"Secret {secretId} was updated. Its new metadata is: {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

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

package com.example;

import com.example.Example.PubSubMessage;
import com.google.cloud.functions.BackgroundFunction;
import com.google.cloud.functions.Context;
import java.util.Base64;
import java.util.Map;
import java.util.logging.Logger;

// 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).
public class Example implements BackgroundFunction {
  private static final Logger logger = Logger.getLogger(Example.class.getName());

  @Override
  public void accept(PubSubMessage message, Context context) {
    if (message.attributes.get("eventType").equals("SECRET_UPDATE")) {
      String secretId = message.attributes.get("secretId");
      String data = new String(Base64.getDecoder().decode(message.data));
      logger.info(String.format("Secret %s was updated. Its new metadata is: %s", secretId, data));
    }
  }

  public static class PubSubMessage {
    String data;
    Map attributes;
    String messageId;
    String publishTime;
  }
}

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) => {
  event_type = event.attributes.eventType;
  if (event_type == 'SECRET_UPDATE') {
    const secretID = event.attributes.secretId;
    const secretMetadata = Buffer.from(event.data, 'base64').toString();
    console.log(`Secret ${secretID} was updated. Its new metadata is: ${secretMetadata}`);
  }
};

Python

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

def consume_event_notification(event, unused_context):
    """
    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")
    return f"Received {event_type} for {secret_id}. New metadata: {secret_metadata}"

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"]
  if message["attributes"]["eventType"] == "SECRET_UPDATE"
    secret_id = message["attributes"]["secretId"]
    message_data = Base64.decode64 message["data"]
    FunctionsFramework.logger.info "Secret %s was updated. Its new metadata is: %s" % [secret_id, message_data]
  end
end

主题配置错误

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

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

后续步骤