创建和管理通知配置

本页面介绍如何使用 Security Command Center API 通知功能,包括以下示例:

  • 创建 NotificationConfig
  • 获取 NotificationConfig
  • 更新 NotificationConfig
  • 删除 NotificationConfig
  • 列出 NotificationConfig
  • 接收 Pub/Sub 通知

或者,Security Command Center Premium 客户可以在 Security Command Center 中为 Pub/Sub 设置持续导出

准备工作

如需使用此页面上的示例,您需要完成设置发现结果通知指南。

要执行以下示例,您需要具有适当权限的 Identity and Access Management (IAM) 角色:

  • 创建 NotificationConfig:Security Center Notification Configurations Editor (roles/securitycenter.notificationConfigEditor)
  • 获取并列出 NotificationConfig:Security Center Notification Configurations Viewer (roles/securitycenter.notificationConfigViewer) 或 Security Center Notification Configurations Editor (roles/securitycenter.notificationConfigEditor)
  • 更新和删除 NotificationConfig:Security Center Notification Configurations Editor (roles/securitycenter.notificationConfigEditor)

如需向访问 notificationConfig 的主账号授予适当的角色,您必须具有以下 IAM 角色之一:

  • Organization Administrator (roles/resourcemanager.organizationAdmin)
  • Folder IAM Admin (roles/resourcemanager.folderIamAdmin)
  • Project IAM Admin (roles/resourcemanager.projectIamAdmin)

Security Command Center 的 IAM 角色可以在组织、文件夹或项目级层授予。您能否查看、修改、创建或更新发现结果、资产和安全来源,取决于您获授予的访问权限级别。如需详细了解 Security Command Center 角色,请参阅访问权限控制

数据驻留和通知

如果为 Security Command Center 启用了数据驻留,则用于定义持续导出到 Pub/Sub 的配置(notificationConfig 资源)会受到数据驻留控制,并存储在您的 Security Command Center 位置中。

如需将 Security Command Center 位置中的发现结果导出到 Pub/Sub,您必须在发现结果所在的 Security Command Center 位置中配置持续导出。

由于在持续导出中使用的过滤器可能会包含受驻留地控制的数据,因此请务必先指定正确的位置,然后再创建过滤器。Security Command Center 不会限制您在哪个位置创建导出内容。

持续导出内容仅存储在其创建的位置,无法在其他位置查看或修改。

创建持续导出后,您无法更改其位置。如需更改位置,您需要删除持续导出,然后在新位置重新创建。

如需使用 API 调用检索连续导出内容,您需要在 notificationConfig 的完整资源名称中指定位置。例如:

GET https://securitycenter.googleapis.com/v2/organizations/123/locations/eu/notificationConfigs/my-pubsub-export-01

同样,如需使用 gcloud CLI 检索连续导出内容,您需要使用 --location 标志指定位置。例如:

gcloud scc notifications describe myContinuousExport --organization=123 \
    --location=us

创建 NotificationConfig

要创建 NotificationConfig,您必须具有:

  • 您要向其发送通知的现有 Pub/Sub 主题。
  • 创建 notificationConfig 的主账号所需的 IAM 角色。

如需了解详情,请参阅设置发现结果通知指南中的设置 Pub/Sub 主题步骤。

创建 NotificationConfig 之前,请注意每个组织只能有一定数量的 NotificationConfig 文件。如需了解详情,请参阅配额和限制

NotificationConfig 包含一个 filter 字段,用于限制对有用事件的通知。此字段接受 Security Command Center API findings.list 方法中提供的所有过滤条件。

创建 NotificationConfig 时,您需要从 Google Cloud 资源层次结构中为 NotificationConfig 指定父级,即组织、文件夹或项目。如果您稍后需要检索、更新或删除 NotificationConfig,则需要在引用时添加父级组织、文件夹或项目的数字 ID。

在 Google Cloud 控制台中,某些 NotificationConfig 资源可能带有旧版标签,这表示它们是使用 v1 Security Command Center API 创建的。您可以使用 Google Cloud 控制台、gcloud CLI、v1 Security Command Center API 或 Security Command Center 的 v1 客户端库管理这些 NotificationConfig 资源。

如需使用 gcloud CLI 管理这些 NotificationConfig 资源,您在运行 gcloud CLI 命令时不得指定位置。

如需使用您选择的语言或平台创建 NotificationConfig,请执行以下操作:

gcloud

gcloud scc notifications create NOTIFICATION_NAME \
  --PARENT=PARENT_ID \
  --location=LOCATION
  --description="NOTIFICATION_DESCRIPTION" \
  --pubsub-topic=PUBSUB_TOPIC \
  --filter="FILTER"

替换以下内容:

  • NOTIFICATION_NAME:通知的名称。 必须介于 1 到 128 个字符之间,并且只能包含字母数字字符、下划线或连字符。
  • PARENT:通知适用的资源层次结构中的范围,organizationfolderproject
  • PARENT_ID:父级组织、文件夹或项目的 ID,格式为 organizations/123folders/456projects/789
  • LOCATION:如果数据驻留处于启用状态,则为创建 NotificationConfig 时使用的 Security Command Center 位置;如果数据驻留处于停用状态,则使用值 global
  • NOTIFICATION_DESCRIPTION:通知的说明,不超过 1,024 个字符。
  • PUBSUB_TOPIC:将接收通知的 Pub/Sub 主题。其格式为 projects/PROJECT_ID/topics/TOPIC
  • FILTER:您定义的用于选择要发送到 Pub/Sub 的发现结果的表达式。例如 state=\"ACTIVE\"

Go

import (
	"context"
	"fmt"
	"io"

	securitycenter "cloud.google.com/go/securitycenter/apiv2"
	"cloud.google.com/go/securitycenter/apiv2/securitycenterpb"
)

func createNotificationConfig(w io.Writer, orgID string, pubsubTopic string, notificationConfigID string) error {
	// orgID := "your-org-id"
	// pubsubTopic := "projects/{your-project}/topics/{your-topic}"
	// notificationConfigID := "your-config-id"

	ctx := context.Background()
	client, err := securitycenter.NewClient(ctx)

	if err != nil {
		return fmt.Errorf("securitycenter.NewClient: %w", err)
	}
	defer client.Close()

	req := &securitycenterpb.CreateNotificationConfigRequest{
		// Parent must be in one of the following formats:
		//		"organizations/{orgId}/locations/global"
		//		"projects/{projectId}/locations/global"
		//		"folders/{folderId}/locations/global"
		Parent:   fmt.Sprintf("organizations/%s/locations/global", orgID),
		ConfigId: notificationConfigID,
		NotificationConfig: &securitycenterpb.NotificationConfig{
			Description: "Go sample config",
			PubsubTopic: pubsubTopic,
			NotifyConfig: &securitycenterpb.NotificationConfig_StreamingConfig_{
				StreamingConfig: &securitycenterpb.NotificationConfig_StreamingConfig{
					Filter: `state = "ACTIVE"`,
				},
			},
		},
	}

	notificationConfig, err := client.CreateNotificationConfig(ctx, req)
	if err != nil {
		return fmt.Errorf("Failed to create notification config: %w", err)
	}
	fmt.Fprintln(w, "New NotificationConfig created: ", notificationConfig)

	return nil
}

Java


package vtwo.notifications;

import com.google.cloud.securitycenter.v2.LocationName;
import com.google.cloud.securitycenter.v2.NotificationConfig;
import com.google.cloud.securitycenter.v2.SecurityCenterClient;
import java.io.IOException;

public class CreateNotification {

  public static void main(String[] args) throws IOException {
    // parentId: must be in one of the following formats:
    //    "organizations/{organization_id}"
    //    "projects/{project_id}"
    //    "folders/{folder_id}"
    String parentId = "{parent-id}";
    String topicName = "{your-topic}";
    String notificationConfigId = "{your-notification-id}";
    // Specify the location of the notification config.
    String location = "global";

    createNotificationConfig(parentId, location, topicName, notificationConfigId);
  }

  // Crete a notification config.
  // Ensure the ServiceAccount has the "pubsub.topics.setIamPolicy" permission on the new topic.
  public static NotificationConfig createNotificationConfig(
      String parentId, String location, String topicName, String notificationConfigId)
      throws IOException {
    // Initialize client that will be used to send requests. This client only needs to be created
    // once, and can be reused for multiple requests. After completing all of your requests, call
    // the "close" method on the client to safely clean up any remaining background resources.
    try (SecurityCenterClient client = SecurityCenterClient.create()) {

      String pubsubTopic = String.format("projects/%s/topics/%s", parentId, topicName);

      NotificationConfig notificationConfig = NotificationConfig.newBuilder()
          .setDescription("Java notification config")
          .setPubsubTopic(pubsubTopic)
          .setStreamingConfig(
              NotificationConfig.StreamingConfig.newBuilder().setFilter("state = \"ACTIVE\"")
                  .build())
          .build();

      NotificationConfig response = client.createNotificationConfig(
          LocationName.of(parentId, location), notificationConfig, notificationConfigId);

      System.out.printf("Notification config was created: %s%n", response);
      return response;
    }
  }
}

Node.js

// npm install '@google-cloud/security-center'
const {SecurityCenterClient} = require('@google-cloud/security-center').v2;
const uuidv1 = require('uuid').v1;

const client = new SecurityCenterClient();
/*
 *  Required. Resource name of the new notification config's parent. Its format
 *  is "organizations/[organization_id]/locations/[location_id]",
 *  "folders/[folder_id]/locations/[location_id]", or
 *  "projects/[project_id]/locations/[location_id]".
 */
const parent = `projects/${projectId}/locations/${location}`;

/**
 *  Required.
 *  Unique identifier provided by the client within the parent scope.
 *  It must be between 1 and 128 characters and contain alphanumeric
 *  characters, underscores, or hyphens only.
 */
const configId = 'notif-config-test-node-create-' + uuidv1();

// pubsubTopic = "projects/{your-project}/topics/{your-topic}";
const pubsubTopic = `projects/${projectId}/topics/${topicName}`;

/**
 *  Required. The notification config being created. The name and the service
 *  account will be ignored as they are both output only fields on this
 *  resource.
 */
const notificationConfig = {
  description: 'Sample config for node v2',
  pubsubTopic: pubsubTopic,
  streamingConfig: {filter: 'state = "ACTIVE"'},
};

// Build the request.
const createNotificationRequest = {
  parent: parent,
  configId: configId,
  notificationConfig: notificationConfig,
};

async function createNotificationConfig() {
  const [response] = await client.createNotificationConfig(
    createNotificationRequest
  );
  console.log('Notification configuration creation successful: %j', response);
}

await createNotificationConfig();

Python

def create_notification_config(
    parent_id, location_id, pubsub_topic, notification_config_id
) -> NotificationConfig:
    """
    This method is used to create the Notification Config.
    Args:
        parent_id: must be in one of the following formats:
            "organizations/{organization_id}"
            "projects/{project_id}"
            "folders/{folder_id}"
        location_id: "global"
        pubsub_topic: "projects/{your-project-id}/topics/{your-topic-id}"
        notification_config_id: "your-config-id"


    Ensure this ServiceAccount has the "pubsub.topics.setIamPolicy" permission on the new topic.
    """
    from google.cloud import securitycenter_v2 as securitycenter_v2

    client = securitycenter_v2.SecurityCenterClient()
    parent_id = parent_id + "/locations/" + location_id
    response = client.create_notification_config(
        request={
            "parent": parent_id,
            "config_id": notification_config_id,
            "notification_config": {
                "description": "Notification for active findings",
                "pubsub_topic": pubsub_topic,
                "streaming_config": {"filter": 'state = "ACTIVE"'},
            },
        }
    )
    print(f"create notification config response:{response}")
    return response

通知现已发布到您指定的 Pub/Sub 主题。

如需发布通知,系统会以 service-org-ORGANIZATION_ID@gcp-sa-scc-notification.iam.gserviceaccount.com 的形式为您创建一个服务账号。此服务账号是在您创建第一个 NotificationConfig 时由系统创建的,并在创建通知配置时被自动授予 PUBSUB_TOPIC 的 IAM 政策上的 securitycenter.notificationServiceAgent 角色。需要此服务账号角色才能接收通知。

获取 NotificationConfig

如需获取 NotificationConfig,您必须具有包含 securitycenter.notification.get 权限的 IAM 角色。

gcloud

gcloud scc notifications describe NOTIFICATION_NAME \
  --PARENT_TYPE=PARENT_ID \
  --location=LOCATION

替换以下内容:

  • NOTIFICATION_NAME:通知配置的名称。
  • PARENT_TYPE 指定配置的资源层次结构的级别;使用 organizationfolderproject
  • PARENT_ID:父级资源的数字 ID。
  • LOCATION:如果数据驻留已启用,则为获取 NotificationConfigSecurity Command Center 位置;如果未启用数据驻留,则使用值 global

更新 NotificationConfig

要更新 NotificationConfig,您必须具有包含 securitycenter.notification.update 权限的 IAM 角色。

使用字段掩码更新时,只有指定的字段才会更新。如果您不使用字段掩码,则 NotificationConfig 中的所有可变字段都将替换为新值。您可以使用字段掩码来更新 Pub/Sub 主题和说明。

如需完成此示例,您必须订阅新主题,并且您的通知服务账号必须具有该主题的 pubsub.topics.setIamPolicy 权限。

授予必要的权限后,使用您选择的语言更新 NotificationConfig 说明、Pub/Sub 主题和过滤条件:

gcloud

gcloud scc notifications update NOTIFICATION_NAME \
  --PARENT_TYPE=PARENT_ID \
  --location=LOCATION \
  --description="NOTIFICATION_DESCRIPTION" \
  --pubsub-topic=PUBSUB_TOPIC \
  --filter="FILTER"

替换以下内容:

  • NOTIFICATION_NAME:通知配置的名称。
  • PARENT_TYPE 指定配置的资源层次结构的级别;使用 organizationfolderproject
  • PARENT_ID:父级资源的数字 ID。
  • LOCATION:如果数据驻留已启用,则为更新 NotificationConfigSecurity Command Center 位置;如果未启用数据驻留,请使用值 global
  • NOTIFICATION_DESCRIPTION:通知的说明,不得超过 1,024 个字符。
  • PUBSUB_TOPIC:将接收通知的 Pub/Sub 主题。其格式为 projects/PROJECT_ID/topics/TOPIC
  • FILTER:您定义的用于选择要发送到 Pub/Sub 的发现结果的表达式。例如 state="ACTIVE"

删除 NotificationConfig

要删除 NotificationConfig,您必须具有包含 securitycenter.notification.delete 权限的 IAM 角色。

删除 NotificationConfig 时,securitycenter.notificationServiceAgent 角色将保留在 Pub/Sub 主题上。如果您在任何其他 NotificationConfig 中使用 Pub/Sub 主题,请从主题中移除该角色。如需了解详情,请参阅访问控制

使用您选择的语言删除 NotificationConfig

gcloud

gcloud scc notifications delete NOTIFICATION_NAME \
  --PARENT_TYPE=PARENT_ID \
  --location=LOCATION

替换以下内容:

  • NOTIFICATION_NAME:通知配置的名称。
  • PARENT_TYPE 指定配置的资源层次结构的级别;使用 organizationfolderproject
  • PARENT_ID:父级资源的数字 ID。
  • LOCATION:如果数据驻留处于启用状态,则为要删除 NotificationConfigSecurity Command Center 位置;如果数据驻留处于停用状态,则使用值 global

列出 NotificationConfig

要列出 NotificationConfigs,您必须拥有一个包含 securitycenter.notification.list 权限的 IAM 角色。

所有 Security Command Center API 列表均已分页。每个响应都会返回一个结果页面和一个返回下一页的令牌。默认 pageSize 为 10。您可以将页面大小配置为最小值 1,最大值 1000。

使用您选择的语言列出 NotificationConfigs

gcloud

gcloud scc notifications list PARENT_TYPE/PARENT_ID \
  --location=LOCATION

替换以下内容:

  • PARENT_TYPE 指定配置的资源层次结构的级别;使用 organizationsfoldersprojects
  • PARENT_ID:父级资源的数字 ID。
  • LOCATION:如果数据驻留已启用,则为列出 NotificationConfig 资源的 Security Command Center 位置;如果未启用数据驻留,请使用值 global

接收 Pub/Sub 通知

本部分提供了一个示例通知消息和示例,展示如何将 Pub/Sub 消息转换为包含发现结果的 NotificationMessage

通知以 JSON 格式发布到 Pub/Sub。以下是通知消息的示例:

{
   "notificationConfigName": "organizations/ORGANIZATION_ID/notificationConfigs/CONFIG_ID",
   "finding": {
     "name": "organizations/ORGANIZATION_ID/sources/SOURCE_ID/findings/FINDING_ID",
     "parent": "organizations/ORGANIZATION_ID/sources/SOURCE_ID",
     "state": "ACTIVE",
     "category": "TEST-CATEGORY",
     "securityMarks": {
       "name": "organizations/ORGANIZATION_ID/sources/SOURCE_ID/findings/FINDING_ID/securityMarks"
     },
     "eventTime": "2019-07-26T07:32:37Z",
     "createTime": "2019-07-29T18:45:27.243Z"
   }
 }

使用您选择的语言将 Pub/Sub 消息转换为 NotificationMessage

gcloud

gcloud CLI 不支持将 Pub/Sub 消息转换为 NotificationMessage。您可以使用 gcloud CLI 获取 NotificationMessage 并直接在终端中输出 JSON

  # The subscription used to receive published messages from a topic
  PUBSUB_SUBSCRIPTION="projects/PROJECT_ID/subscriptions/SUBSCRIPTION_ID"

  gcloud pubsub subscriptions pull $PUBSUB_SUBSCRIPTION

替换以下内容:

  • PROJECT_ID 替换为您的项目 ID。
  • SUBSCRIPTION_ID 替换为您的订阅 ID。

Go

import (
	"bytes"
	"context"
	"fmt"
	"io"

	"cloud.google.com/go/pubsub"
	"cloud.google.com/go/securitycenter/apiv2/securitycenterpb"
	"github.com/golang/protobuf/jsonpb"
)

func receiveMessages(w io.Writer, projectID string, subscriptionName string) error {
	// projectID := "your-project-id"
	// subsriptionName := "your-subscription-name"

	ctx := context.Background()

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

	sub := client.Subscription(subscriptionName)
	cctx, cancel := context.WithCancel(ctx)
	err = sub.Receive(cctx, func(ctx context.Context, msg *pubsub.Message) {
		var notificationMessage = new(securitycenterpb.NotificationMessage)
		jsonpb.Unmarshal(bytes.NewReader(msg.Data), notificationMessage)

		fmt.Fprintln(w, "Got finding: ", notificationMessage.GetFinding())
		msg.Ack()
		cancel()
	})
	if err != nil {
		return fmt.Errorf("Receive: %w", err)
	}

	return nil
}

后续步骤