为 Pub/Sub 启用发现结果通知

本页面介绍了如何启用 Security Command Center API 通知。

通知会在几分钟内将发现结果和发现结果更新发送到 Pub/Sub 主题。Security Command Center API 通知包含 Security Command Center 在Google Cloud 控制台中显示的所有发现结果信息。

您可以将 Pub/Sub 中的 Security Command Center 通知直接关联到 Cloud Run functions 操作。如需查看有助于响应、丰富和补救的示例函数,请参阅 Cloud Run functions 代码的 Security Command Center 开源代码库。 代码库包含解决方案,可帮助您对安全发现结果采取自动操作。

或者,您可以将发现结果导出到 BigQuery,也可以在 Google Cloud 控制台中为 Pub/Sub 设置持续导出

准备工作

  1. 如需获得设置和配置 Security Command Center API 通知所需的权限,请让您的管理员为您授予以下 IAM 角色:

    • 在其中激活 Security Command Center 的组织或项目的 Security Center Admin (roles/securitycenter.admin)
    • 将在其中创建 Pub/Sub 主题的项目的 Project IAM Admin (roles/resourcemanager.projectIamAdmin)

    如需详细了解如何授予角色,请参阅管理对项目、文件夹和组织的访问权限

    您也可以通过自定义角色或其他预定义角色来获取所需的权限。

  2. Enable the Security Command Center API:

    gcloud services enable securitycenter.googleapis.com

数据驻留和通知

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

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

由于持续导出中使用的过滤条件可能包含受居住地控制条件约束的数据,因此请务必在创建过滤条件之前指定正确的位置。 Security Command Center 不会限制您在哪个位置创建导出。

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

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

如需了解如何在启用数据驻留的情况下使用 Security Command Center,请参阅 Security Command Center 区域端点

设置 Pub/Sub 主题

在此任务中,您将创建并订阅要向其发送通知的 Pub/Sub 主题。

第 1 步:设置 Pub/Sub

如需设置和订阅 Pub/Sub 主题,请执行以下操作:

  1. 前往 Google Cloud 控制台。

    前往 Google Cloud 控制台

  2. 选择您已在其中启用 Security Command Center API 的项目。

  3. 点击激活 Cloud Shell

  4. 可选:如需创建新的 Pub/Sub 主题,请运行以下命令:

    gcloud pubsub topics create TOPIC_ID 

    TOPIC_ID 替换为主题名称。

  5. 创建对主题的订阅:

    gcloud pubsub subscriptions create SUBSCRIPTION_ID --topic=TOPIC_ID 

    替换以下内容:

    • SUBSCRIPTION_ID:订阅 ID
    • TOPIC_ID:主题 ID

如需详细了解如何设置 Pub/Sub,请参阅管理主题和订阅

第 2 步:授予 Pub/Sub 主题的角色

如需创建 NotificationConfig,您需要对已创建订阅的 Pub/Sub 主题具有 Pub/Sub Admin 角色 (roles/pubsub.admin)。

如需授予此角色,请执行以下操作:

  1. 前往 Google Cloud 控制台。

    前往 Google Cloud 控制台

  2. 选择启用了 Security Command Center API 的项目。

  3. 点击激活 Cloud Shell

  4. 向您的 Google 账号授予 Pub/Sub 主题所需的角色:

    gcloud pubsub topics add-iam-policy-binding \     projects/PUBSUB_PROJECT/topics/TOPIC_ID \     --member="user:GOOGLE_ACCOUNT" \     --role="roles/pubsub.admin" 

    替换以下内容:

    • PUBSUB_PROJECT:包含您的 Pub/Sub 主题的 Google Cloud 项目
    • TOPIC_ID:主题 ID
    • GOOGLE_ACCOUNT:您的 Google 账号的邮箱

创建 NotificationConfig

创建 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 格式指定。
  • LOCATIONSecurity Command Center 位置;如果启用了数据驻留,请使用 eusaus;否则,请使用值 global
  • NOTIFICATION_DESCRIPTION:通知的说明,不超过 1,024 个字符。
  • PUBSUB_TOPIC:将接收通知的 Pub/Sub 主题。其格式为 projects/PROJECT_ID/topics/TOPIC
  • FILTER:您定义的表达式,用于选择要发送到 Pub/Sub 的发现结果。例如 state=\"ACTIVE\"

Terraform

为组织创建 NotificationConfig

resource "google_pubsub_topic" "scc_v2_organization_notification_config" {   name = "my-topic" }  resource "google_scc_v2_organization_notification_config" "custom_organization_notification_config" {   config_id    = "my-config"   organization = "123456789"   location     = "global"   description  = "My custom Cloud Security Command Center Finding Organization Notification Configuration"   pubsub_topic = google_pubsub_topic.scc_v2_organization_notification_config.id    streaming_config {     filter = "category = \"OPEN_FIREWALL\" AND state = \"ACTIVE\""   } } 

为文件夹创建 NotificationConfig

resource "google_folder" "folder" {   parent       = "organizations/123456789"   display_name = "folder-name" }  resource "google_pubsub_topic" "scc_v2_folder_notification_config" {   name = "my-topic" }  resource "google_scc_v2_folder_notification_config" "custom_notification_config" {   config_id    = "my-config"   folder       = google_folder.folder.folder_id   location     = "global"   description  = "My custom Cloud Security Command Center Finding Notification Configuration"   pubsub_topic =  google_pubsub_topic.scc_v2_folder_notification_config.id    streaming_config {     filter = "category = \"OPEN_FIREWALL\" AND state = \"ACTIVE\""   } } 

为项目创建 NotificationConfig

resource "google_pubsub_topic" "scc_v2_project_notification" {   name = "my-topic" }  resource "google_scc_v2_project_notification_config" "custom_notification_config" {   config_id    = "my-config"   project      = "my-project-name"   location     = "global"   description  = "My custom Cloud Security Command Center Finding Notification Configuration"   pubsub_topic =  google_pubsub_topic.scc_v2_project_notification.id    streaming_config {     filter = "category = \"OPEN_FIREWALL\" AND 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 角色。需要此服务账号角色才能接收通知。

在 VPC Service Controls 中授予边界访问权限

如果您使用 VPC Service Controls,并且 Pub/Sub 主题属于服务边界内的项目,则必须授予对项目的访问权限,以便创建通知。

如需授予对项目的访问权限,请为用于创建通知的主账号和项目创建入站和出站规则。这些规则允许访问受保护的资源,并可让 Pub/Sub 验证用户是否具有对 Pub/Sub 主题的 setIamPolicy 权限。

创建 NotificationConfig 之前

在完成创建 NotificationConfig 中的步骤之前,请完成以下步骤。

控制台

  1. 在 Google Cloud 控制台中,前往 VPC Service Controls 页面。

    转到 VPC Service Controls

  2. 选择您的组织或项目。
  3. 如果您选择了某个组织,请点击选择访问权限政策,然后选择与要更新的边界关联的访问权限政策。
  4. 点击要更新的边界的名称。

    如需查找您需要修改的服务边界,您可以查看日志中是否存在显示 RESOURCES_NOT_IN_SAME_SERVICE_PERIMETER 违规行为的条目。在这些条目中,检查 servicePerimeterName 字段:

    accessPolicies/ACCESS_POLICY_ID/servicePerimeters/SERVICE_PERIMETER_NAME
  5. 点击 修改边界
  6. 点击出站流量政策
  7. 点击添加出站流量规则
  8. FROM 部分中,设置以下详细信息:

    1. 对于身份部分,选择选择身份和群组
    2. 点击添加身份
    3. 输入用于调用 Security Command Center API 的主账号的邮箱。

    4. 选择主账号或按 ENTER 键,然后点击添加身份
  9. 目标部分中,设置以下详细信息:

    1. 对于项目,选择所有项目
    2. 对于操作或 IAM 角色,请选择选择操作
    3. 点击添加操作,然后添加以下操作:

      • 添加 pubsub.googleapis.com 服务。
        1. 点击所有方法
        2. 点击添加所有方法
  10. 点击入站流量政策
  11. 点击添加入站流量规则
  12. FROM 部分中,设置以下详细信息:

    1. 对于身份部分,选择选择身份和群组
    2. 点击添加身份
    3. 输入用于调用 Security Command Center API 的主账号的邮箱。

    4. 选择主账号或按 ENTER 键,然后点击添加身份
    5. 来源部分,选择所有来源
  13. 目标部分中,设置以下详细信息:

    1. 对于项目,选择选择项目
    2. 点击添加项目,然后添加包含 Pub/Sub 主题的项目。
    3. 对于操作或 IAM 角色,请选择选择操作
    4. 点击添加操作,然后添加以下操作:

      • 添加 pubsub.googleapis.com 服务。
        1. 点击所有方法
        2. 点击添加所有方法
  14. 点击保存

gcloud

  1. 如果尚未设置配额项目,请进行设置。选择已启用 Access Context Manager API 的项目。

    gcloud config set billing/quota_project QUOTA_PROJECT_ID

    QUOTA_PROJECT_ID 替换为您要用于结算和配额的项目的 ID。

  2. 创建名为 egress-rule.yaml 且包含以下内容的文件:

    - egressFrom:     identities:     - PRINCIPAL_ADDRESS   egressTo:     operations:     - serviceName: pubsub.googleapis.com       methodSelectors:       - method: '*'     resources:     - '*'

    PRINCIPAL_ADDRESS 替换为用于调用 Security Command Center API 的主账号的地址。

  3. 创建名为 ingress-rule.yaml 且包含以下内容的文件:

    - ingressFrom:     identities:     - PRINCIPAL_ADDRESS     sources:     - accessLevel: '*'   ingressTo:     operations:     - serviceName: pubsub.googleapis.com       methodSelectors:       - method: '*'     resources:     - '*'

    PRINCIPAL_ADDRESS 替换为用于调用 Security Command Center API 的主账号的地址。

  4. 将出站流量规则添加到边界:

    gcloud access-context-manager perimeters update PERIMETER_NAME \     --set-egress-policies=egress-rule.yaml

    替换以下内容:

    • PERIMETER_NAME:边界的名称。例如 accessPolicies/1234567890/servicePerimeters/example_perimeter

      如需查找您需要修改的服务边界,您可以查看日志中是否存在显示 RESOURCES_NOT_IN_SAME_SERVICE_PERIMETER 违规行为的条目。在这些条目中,检查 servicePerimeterName 字段:

      accessPolicies/ACCESS_POLICY_ID/servicePerimeters/SERVICE_PERIMETER_NAME
  5. 将入站规则添加到边界:

    gcloud access-context-manager perimeters update PERIMETER_NAME \     --set-ingress-policies=ingress-rule.yaml

    替换以下内容:

    • PERIMETER_NAME:边界的名称。例如 accessPolicies/1234567890/servicePerimeters/example_perimeter

      如需查找您需要修改的服务边界,您可以查看日志中是否存在显示 RESOURCES_NOT_IN_SAME_SERVICE_PERIMETER 违规行为的条目。在这些条目中,检查 servicePerimeterName 字段:

      accessPolicies/ACCESS_POLICY_ID/servicePerimeters/SERVICE_PERIMETER_NAME

如需了解详情,请参阅入站流量和出站流量规则

为 NotificationConfig 创建入站流量规则

如需为 NotificationConfig 创建入站流量规则,请完成创建 NotificationConfig 中的说明,然后完成以下步骤。

控制台

  1. 重新打开上一部分中的服务边界。

    转到 VPC Service Controls

  2. 点击入站流量政策
  3. 点击添加入站流量规则
  4. FROM 部分中,设置以下详细信息:

    1. 对于身份部分,选择选择身份和群组
    2. 点击添加身份
    3. 输入 NotificationConfig 服务代理的邮箱。 服务代理的地址采用以下格式:

      service-org-ORGANIZATION_ID@gcp-sa-scc-notification.iam.gserviceaccount.com

      ORGANIZATION_ID 替换为您的组织 ID。

    4. 选择服务代理或按 ENTER 键,然后点击添加身份
    5. 来源部分,选择所有来源
  5. 目标部分中,设置以下详细信息:

    1. 对于项目,选择选择项目
    2. 点击添加项目,然后添加包含 Pub/Sub 主题的项目。
    3. 对于操作或 IAM 角色,请选择选择操作
    4. 点击添加操作,然后添加以下操作:

      • 添加 pubsub.googleapis.com 服务。
        1. 点击所有方法
        2. 点击添加所有方法
  6. 点击保存

gcloud

  1. 如果尚未设置配额项目,请进行设置。选择已启用 Access Context Manager API 的项目。

    gcloud config set billing/quota_project QUOTA_PROJECT_ID

    QUOTA_PROJECT_ID 替换为您要用于结算和配额的项目的 ID。

  2. 创建名为 ingress-rule.yaml 且包含以下内容的文件:

    - ingressFrom:     identities:     - serviceAccount:service-org-ORGANIZATION_ID@gcp-sa-scc-notification.iam.gserviceaccount.com     sources:     - accessLevel: '*'   ingressTo:     operations:     - serviceName: pubsub.googleapis.com       methodSelectors:       - method: '*'     resources:     - '*'

    ORGANIZATION_ID 替换为您的组织 ID。

  3. 将入站规则添加到边界:

    gcloud access-context-manager perimeters update PERIMETER_NAME \     --set-ingress-policies=ingress-rule.yaml

    替换以下内容:

    • PERIMETER_NAME:边界的名称。例如 accessPolicies/1234567890/servicePerimeters/example_perimeter

      如需查找您需要修改的服务边界,您可以查看日志中是否存在显示 RESOURCES_NOT_IN_SAME_SERVICE_PERIMETER 违规行为的条目。在这些条目中,检查 servicePerimeterName 字段:

      accessPolicies/ACCESS_POLICY_ID/servicePerimeters/SERVICE_PERIMETER_NAME

如需了解详情,请参阅入站流量和出站流量规则

选定的项目、用户和服务账号现在可以访问受保护的资源并创建通知。

如果您已按照本指南中的所有步骤操作,并且通知正常工作,您现在可以删除以下内容:

  • 主账号的入站规则
  • 主账号的出站规则

这些规则只需要配置 NotificationConfig。但是,若要使通知继续正常工作,您必须保留 NotificationConfig 的入站规则,该规则允许将通知发布到服务边界后面的 Pub/Sub 主题。

后续步骤