使用 Security Command Center API 管理安全防護機制來源

本指南將逐步說明如何使用 Security Command Center API 建立來源,以產生發現項目。新增來源時,Security Command Center 會建立適當的來源,並指派相關權限。

您可以在機構、資料夾或專案層級授予 Security Command Center 的 IAM 角色。您能否查看、編輯、建立或更新發現項目、資產和安全性來源,取決於您獲准的存取層級。如要進一步瞭解 Security Command Center 角色,請參閱存取權控管

事前準備

設定來源前,請先透過 Security Command Center API 進行驗證

建立來源

本範例說明如何建立來源,並指定 Security Command Center 使用的顯示名稱和說明。

伺服器會自動為來源指派 ID。

REST

透過 API 向 organizations.sources.create 方法提出要求。要求主體包含 Source 的例項。

   POST https://securitycenter.googleapis.com/v2/organizations/ORGANIZATION_ID/sources    {     "name": "SOURCE_NAME",     "description": "SOURCE_DESCRIPTION",     "displayName": "DISPLAY_NAME"   } 

更改下列內容:

  • ORGANIZATION_ID:您的機構 ID。
  • SOURCE_NAME:來源名稱。
  • SOURCE_DESCRIPTION:來源說明 (最多 1,024 個半形字元)。
  • DISPLAY_NAME:來源的顯示名稱 (一到 64 個字元)。

Go

import ( 	"context" 	"fmt" 	"io"  	securitycenter "cloud.google.com/go/securitycenter/apiv2" 	"cloud.google.com/go/securitycenter/apiv2/securitycenterpb" )  // createSource creates a new source for organization orgID. orgID is // the numeric identifier of the organization func createSource(w io.Writer, orgID string) error { 	// orgID := "12321311" 	// Instantiate a context and a security service client to make API calls. 	ctx := context.Background() 	client, err := securitycenter.NewClient(ctx) 	if err != nil { 		return fmt.Errorf("securitycenter.NewClient: %w", err) 	} 	defer client.Close() // Closing the client safely cleans up background resources.  	req := &securitycenterpb.CreateSourceRequest{ 		Source: &securitycenterpb.Source{ 			DisplayName: "Customized Display Name", 			Description: "A new custom source that does X", 		}, 		Parent: fmt.Sprintf("organizations/%s", orgID), 	} 	source, err := client.CreateSource(ctx, req) 	if err != nil { 		return fmt.Errorf("CreateSource: %w", err) 	}  	fmt.Fprintf(w, "New source created: %s\n", source.Name) 	fmt.Fprintf(w, "Display Name: %s\n", source.DisplayName) 	return nil } 

Java

 import com.google.cloud.securitycenter.v2.CreateSourceRequest; import com.google.cloud.securitycenter.v2.OrganizationName; import com.google.cloud.securitycenter.v2.SecurityCenterClient; import com.google.cloud.securitycenter.v2.Source; import java.io.IOException;  public class CreateSource {    public static void main(String[] args) throws IOException {     // TODO: Replace the sample resource name     // organizationId: Google Cloud Organization id.     String organizationId = "{google-cloud-organization-id}";      createSource(organizationId);   }    /**    * Creates a new "source" in the Security Command Center.    */   public static Source createSource(String organizationId) throws IOException {     try (SecurityCenterClient client = SecurityCenterClient.create()) {       // Start setting up a request to create a source in an organization.       OrganizationName organizationName = OrganizationName.of(organizationId);        Source source =           Source.newBuilder()               .setDisplayName("Custom display name")               .setDescription("A source that does X")               .build();        CreateSourceRequest createSourceRequest =           CreateSourceRequest.newBuilder()               .setParent(organizationName.toString())               .setSource(source)               .build();        // The source is not visible in the Security Command Center dashboard       // until it generates findings.       Source response = client.createSource(createSourceRequest);       return response;     }   } }

Node.js

// Import the Google Cloud client library. const {SecurityCenterClient} = require('@google-cloud/security-center').v2;  // Create a new Security Center client const client = new SecurityCenterClient();  // TODO(developer): Update for your own environment. const organizationId = '1081635000895';  // Resource name of the new source's parent. Format is: // "organizations/[organization_id]". const parent = client.organizationPath(organizationId);  // The source object. const source = {   displayName: 'Customized Display Name V2',   description: 'A new custom source that does X', };  // Build the create source request. const createSourceRequest = {   parent,   source, };  // The source is not visible in the Security Command Center dashboard // until it generates findings. // Call the API async function createSource() {   const [source] = await client.createSource(createSourceRequest);   console.log('New Source created: %j', source); }  await createSource();

Python

def create_source(organization_id) -> Dict:     """     Create a new findings source     Args:         organization_id: organization_id is the numeric ID of the organization. e.g.:organization_id = "111122222444"     Returns:          Dict: returns the created findings source details.     """     from google.cloud import securitycenter_v2      client = securitycenter_v2.SecurityCenterClient()     org_name = f"organizations/{organization_id}"      response = client.create_source(         request={             "parent": org_name,             "source": {                 "display_name": "Customized Display Name",                 "description": "A new custom source that does X",             },         }     )     print(f"Created Source: {response.name}")     return response  

來源產生調查結果後,才會顯示在 Security Command Center 控制台中。如要確認來源是否已建立,請按照「取得特定來源」一節中的操作說明進行。

更新來源

建立來源後,您可以更新來源的顯示名稱和說明。您也可以使用欄位遮罩,只更新一個欄位。下列範例使用欄位遮罩,只更新顯示名稱,說明則維持不變。

REST

透過 API 向 organizations.sources.patch 方法提出要求。要求主體包含 Source 的例項。

   PATCH https://securitycenter.googleapis.com/v2/organizations/ORGANIZATION_ID/sources/SOURCE_ID?updateMask=displayName -d    {     "description": "SOURCE_DESCRIPTION",     "displayName": "DISPLAY_NAME",   } 

更改下列內容:

  • ORGANIZATION_ID:您的機構 ID。
  • SOURCE_ID:來源 ID。如要瞭解如何找出來源 ID,請參閱「取得來源 ID」。
  • SOURCE_DESCRIPTION:來源說明 (最多 1024 個字元)。
  • DISPLAY_NAME:來源的顯示名稱 (一到 64 個字元)。

Go

import ( 	"context" 	"fmt" 	"io"  	securitycenter "cloud.google.com/go/securitycenter/apiv2" 	"cloud.google.com/go/securitycenter/apiv2/securitycenterpb" 	"google.golang.org/genproto/protobuf/field_mask" )  // updateSource changes a sources display name to "New Display Name" for a // specific source. sourceName is the full resource name of the source to be // updated. func updateSource(w io.Writer, sourceName string) error { 	// sourceName := "organizations/111122222444/sources/1234" 	// Instantiate a context and a security service client to make API calls. 	ctx := context.Background() 	client, err := securitycenter.NewClient(ctx) 	if err != nil { 		return fmt.Errorf("securitycenter.NewClient: %w", err) 	} 	defer client.Close() // Closing the client safely cleans up background resources.  	req := &securitycenterpb.UpdateSourceRequest{ 		Source: &securitycenterpb.Source{ 			Name:        sourceName, 			DisplayName: "New Display Name", 		}, 		// Only update the display name field (if not set all mutable 		// fields of the source will be updated. 		UpdateMask: &field_mask.FieldMask{ 			Paths: []string{"display_name"}, 		}, 	} 	source, err := client.UpdateSource(ctx, req) 	if err != nil { 		return fmt.Errorf("UpdateSource: %w", err) 	} 	fmt.Fprintf(w, "Source Name: %s, ", source.Name) 	fmt.Fprintf(w, "Display name: %s, ", source.DisplayName) 	fmt.Fprintf(w, "Description: %s\n", source.Description)  	return nil } 

Java

 import com.google.cloud.securitycenter.v2.SecurityCenterClient; import com.google.cloud.securitycenter.v2.Source; import com.google.cloud.securitycenter.v2.SourceName; import com.google.cloud.securitycenter.v2.UpdateSourceRequest; import com.google.protobuf.FieldMask; import java.io.IOException;  public class UpdateSource {    public static void main(String[] args) throws IOException {     // TODO: Replace the below variables.     // organizationId: Google Cloud Organization id.     String organizationId = "{google-cloud-organization-id}";      // Specify the source-id.     String sourceId = "{source-id}";      updateSource(organizationId, sourceId);   }    // Demonstrates how to update a source.   public static Source updateSource(String organizationId, String sourceId) 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.     try (SecurityCenterClient client = SecurityCenterClient.create()) {        // Start setting up a request to get a source.       SourceName sourceName = SourceName.ofOrganizationSourceName(organizationId, sourceId);       Source source = Source.newBuilder()           .setDisplayName("Updated Display Name")           .setName(sourceName.toString())           .build();        // Set the update mask to specify which properties should be updated.       // If empty, all mutable fields will be updated.       // For more info on constructing field mask path, see the proto or:       // https://cloud.google.com/java/docs/reference/protobuf/latest/com.google.protobuf.FieldMask       FieldMask updateMask = FieldMask.newBuilder()           .addPaths("display_name")           .build();        UpdateSourceRequest request = UpdateSourceRequest.newBuilder()           .setSource(source)           .setUpdateMask(updateMask)           .build();        // Call the API.       Source response = client.updateSource(request);        System.out.println("Updated Source: " + response);       return response;     }   } }

Node.js

// npm install '@google-cloud/security-center' const {SecurityCenterClient} = require('@google-cloud/security-center').v2;  const client = new SecurityCenterClient();  // TODO(developer): Update the following for your own environment. const organizationId = '1081635000895'; const location = 'global';  async function createSampleFinding() {   const uuid = require('uuid');    const [source] = await client.createSource({     source: {       displayName: 'Customized Display Name V2',       description: 'A new custom source that does X',     },     parent: client.organizationPath(organizationId),   });    const sourceId = source.name.split('/')[3];    // Resource name of the new finding's parent. Examples:   //  - `organizations/[organization_id]/sources/[source_id]`   //  - `organizations/[organization_id]/sources/[source_id]/locations/[location_id]`   const parent = `organizations/${organizationId}/sources/${sourceId}/locations/${location}`;    // The resource this finding applied to. The Cloud Security Command Center UI can link the   // findings for a resource to the corresponding asset of a resource if there are matches.   const resourceName = `//cloudresourcemanager.googleapis.com/organizations/${organizationId}`;    // Unique identifier provided by the client within the parent scope.   // It must be alphanumeric and less than or equal to 32 characters and   // greater than 0 characters in length.   const findingId = uuid.v4().replace(/-/g, '');    // Get the current timestamp.   const eventDate = new Date();    // Finding category.   const category = 'MEDIUM_RISK_ONE';    // Build the finding request object.   const createFindingRequest = {     parent: parent,     findingId: findingId,     finding: {       resourceName,       category,       state: 'ACTIVE',       // The time associated with discovering the issue.       eventTime: {         seconds: Math.floor(eventDate.getTime() / 1000),         nanos: (eventDate.getTime() % 1000) * 1e6,       },     },   };    await client.createFinding(createFindingRequest);   return sourceId; }  const sourceId = await createSampleFinding();  /**  *  Required. The source resource to update.  */ const sourceName = client.organizationSourcePath(organizationId, sourceId);  // Set the update mask to specify which properties should be updated. // If empty, all mutable fields will be updated. // For more info on constructing field mask path, see the proto or: // https://cloud.google.com/java/docs/reference/protobuf/latest/com.google.protobuf.FieldMask const updateMask = {   paths: ['display_name'], };  // Build the request.  const source = {   name: sourceName,   displayName: 'New Display Name', };  async function updateSource() {   const [response] = await client.updateSource({updateMask, source});   console.log('Updated Source: %j', response); }  await updateSource();

Python

def update_source(source_name) -> Dict:     """     Updates a source's display name.     Args:         source_name: is the resource path for a source that has been created     Returns:          Dict: returns the details of updated source.     """     from google.cloud import securitycenter_v2     from google.protobuf import field_mask_pb2      client = securitycenter_v2.SecurityCenterClient()      # Field mask to only update the display name.     field_mask = field_mask_pb2.FieldMask(paths=["display_name"])      # 'source_name' is the resource path for a source that has been     # created previously (you can use list_sources to find a specific one).     # Its format is:     # source_name = "organizations/{organization_id}/sources/{source_id}"     # e.g.:     # source_name = "organizations/111122222444/sources/1234"     updated = client.update_source(         request={             "source": {"name": source_name, "display_name": "Updated Display Name"},             "update_mask": field_mask,         }     )     print(f"Updated Source: {updated}")     return updated  

取得特定來源

使用來源的絕對資源名稱查詢 Security Command Center,確認來源已正確建立或更新:

gcloud

gcloud scc sources describe ORGANIZATION_ID --source=SOURCE_ID

更改下列內容:

  • ORGANIZATION_ID:您的機構 ID。
  • SOURCE_ID:來源 ID。

REST

透過 API 向 organizations.sources.get 方法提出要求:

 GET https://securitycenter.googleapis.com/v2/organizations/ORGANIZATION_ID/sources/SOURCE_ID 

更改下列內容:

  • ORGANIZATION_ID:您的機構 ID。
  • SOURCE_ID:來源 ID。

Go

import ( 	"context" 	"fmt" 	"io"  	securitycenter "cloud.google.com/go/securitycenter/apiv2" 	"cloud.google.com/go/securitycenter/apiv2/securitycenterpb" )  // getSource retrieves a source by its resource name and print it to w. // sourceName is the full resource name of the source to be updated. func getSource(w io.Writer, sourceName string) error { 	// sourceName := "organizations/111122222444/sources/1234" 	// Instantiate a context and a security service client to make API calls. 	ctx := context.Background() 	client, err := securitycenter.NewClient(ctx) 	if err != nil { 		return fmt.Errorf("securitycenter.NewClient: %w", err) 	} 	defer client.Close() // Closing the client safely cleans up background resources.  	req := &securitycenterpb.GetSourceRequest{ 		Name: sourceName, 	} 	source, err := client.GetSource(ctx, req) 	if err != nil { 		return fmt.Errorf("GetSource: %w", err) 	} 	fmt.Fprintf(w, "Source: %v\n", source.Name) 	fmt.Fprintf(w, "Display Name: %v\n", source.DisplayName) 	fmt.Fprintf(w, "Description: %v\n", source.Description) 	return nil } 

Java

 import com.google.cloud.securitycenter.v2.GetSourceRequest; import com.google.cloud.securitycenter.v2.SecurityCenterClient; import com.google.cloud.securitycenter.v2.Source; import com.google.cloud.securitycenter.v2.SourceName; import java.io.IOException;  public class GetSource {    public static void main(String[] args) throws IOException {     // TODO: Replace the below variables.     // organizationId: Google Cloud Organization id.     String organizationId = "{google-cloud-organization-id}";      // Specify the source-id.     String sourceId = "{source-id}";      getSource(organizationId, sourceId);   }    // Demonstrates how to retrieve a specific source.   public static Source getSource(String organizationId, String sourceId) 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.     try (SecurityCenterClient client = SecurityCenterClient.create()) {        // Start setting up a request to get a source.       SourceName sourceName = SourceName.ofOrganizationSourceName(organizationId, sourceId);        GetSourceRequest request = GetSourceRequest.newBuilder()           .setName(sourceName.toString())           .build();        // Call the API.       Source response = client.getSource(request);        System.out.println("Source: " + response);       return response;     }   } }

Node.js

// Imports the Google Cloud client library. const {SecurityCenterClient} = require('@google-cloud/security-center').v2;  // Create a Security Center client const client = new SecurityCenterClient();  // TODO(developer): Update the following for your own environment. const organizationId = '1081635000895'; const location = 'global';  async function createSampleFinding() {   const uuid = require('uuid');    const [source] = await client.createSource({     source: {       displayName: 'Customized Display Name V2',       description: 'A new custom source that does X',     },     parent: client.organizationPath(organizationId),   });    const sourceId = source.name.split('/')[3];    // Resource name of the new finding's parent. Examples:   //  - `organizations/[organization_id]/sources/[source_id]`   //  - `organizations/[organization_id]/sources/[source_id]/locations/[location_id]`   const parent = `organizations/${organizationId}/sources/${sourceId}/locations/${location}`;    // The resource this finding applied to. The Cloud Security Command Center UI can link the   // findings for a resource to the corresponding asset of a resource if there are matches.   const resourceName = `//cloudresourcemanager.googleapis.com/organizations/${organizationId}`;    // Unique identifier provided by the client within the parent scope.   // It must be alphanumeric and less than or equal to 32 characters and   // greater than 0 characters in length.   const findingId = uuid.v4().replace(/-/g, '');    // Get the current timestamp.   const eventDate = new Date();    // Finding category.   const category = 'MEDIUM_RISK_ONE';    // Build the finding request object.   const createFindingRequest = {     parent: parent,     findingId: findingId,     finding: {       resourceName,       category,       state: 'ACTIVE',       // The time associated with discovering the issue.       eventTime: {         seconds: Math.floor(eventDate.getTime() / 1000),         nanos: (eventDate.getTime() % 1000) * 1e6,       },     },   };    await client.createFinding(createFindingRequest);   return sourceId; }  const sourceId = await createSampleFinding();  // Relative resource name of the source. Its format is // "organizations/[organization_id]/source/[source_id]". const name = `organizations/${organizationId}/sources/${sourceId}`;  // Build the request. const getSourceRequest = {   name, };  async function getSource() {   // Call the API.   const [source] = await client.getSource(getSourceRequest);   console.log('Source: %j', source); }  await getSource();

Python

def get_source(source_name) -> Dict:     """     Gets the details of an existing source.     Args:         source_name: is the resource path for a source that has been created     Returns:          Dict: returns the details of existing source.     """     from google.cloud import securitycenter_v2      client = securitycenter_v2.SecurityCenterClient()      # 'source_name' is the resource path for a source that has been     # created previously (you can use list_sources to find a specific one).     # Its format is:     # source_name = "organizations/{organization_id}/sources/{source_id}"     # e.g.:     # source_name = "organizations/111122222444/sources/1234"     source = client.get_source(request={"name": source_name})      print(f"Source: {source}")     return source  

列出來源

您可以使用 Security Command Center 列出特定來源,或機構中所有可用的來源:

REST

透過 API 向 organizations.sources.list 方法提出要求:

 GET https://securitycenter.googleapis.com/v2/organizations/ORGANIZATION_ID/sources 

更改下列內容:

  • ORGANIZATION_ID:您的機構 ID。

Go

import ( 	"context" 	"fmt" 	"io"  	securitycenter "cloud.google.com/go/securitycenter/apiv2" 	"cloud.google.com/go/securitycenter/apiv2/securitycenterpb" 	"google.golang.org/api/iterator" )  // listSources prints all sources in  orgID to w.  orgID is the numeric // identifier of the organization. func listSources(w io.Writer, orgID string) error { 	// orgID := "12321311" 	// Instantiate a context and a security service client to make API calls. 	ctx := context.Background() 	client, err := securitycenter.NewClient(ctx) 	if err != nil { 		return fmt.Errorf("securitycenter.NewClient: %w", err) 	} 	defer client.Close() // Closing the client safely cleans up background resources.  	req := &securitycenterpb.ListSourcesRequest{ 		// Parent must be in one of the following formats: 		//		"organizations/{orgId}" 		//		"projects/{projectId}" 		//		"folders/{folderId}" 		Parent: fmt.Sprintf("organizations/%s", orgID), 	} 	it := client.ListSources(ctx, req) 	for { 		source, err := it.Next() 		if err == iterator.Done { 			break 		} 		if err != nil { 			return fmt.Errorf("it.Next: %w", err) 		} 		fmt.Fprintf(w, "Source Name: %s, ", source.Name) 		fmt.Fprintf(w, "Display name: %s, ", source.DisplayName) 		fmt.Fprintf(w, "Description: %s\n", source.Description) 	} 	return nil } 

Java

 import com.google.cloud.securitycenter.v2.OrganizationLocationName; import com.google.cloud.securitycenter.v2.OrganizationName; import com.google.cloud.securitycenter.v2.SecurityCenterClient; import com.google.cloud.securitycenter.v2.SecurityCenterClient.ListSourcesPagedResponse; import com.google.cloud.securitycenter.v2.Source; import java.io.IOException; import java.util.ArrayList; import java.util.List;  public class ListSources {    public static void main(String[] args) throws IOException {     // TODO: Replace the below variables.     // organizationId: Google Cloud Organization id.     String organizationId = "{google-cloud-organization-id}";      listSources(organizationId);   }    // Demonstrates how to list all security sources in an organization.   public static List<Source> listSources(String organizationId) 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.     try (SecurityCenterClient client = SecurityCenterClient.create()) {        // Start setting up a request to get a source.       OrganizationName parent = OrganizationName.of(organizationId);        // Call the API.       List<Source> sourcesList = new ArrayList<>();       ListSourcesPagedResponse response = client.listSources(parent);       response.iterateAll().forEach(sourcesList::add);        for (Source source : sourcesList) {         System.out.println("List sources: " + source);       }       return sourcesList;     }   } }

Node.js

// Imports the Google Cloud client library. const {SecurityCenterClient} = require('@google-cloud/security-center').v2;  const client = new SecurityCenterClient();  // TODO(developer): Update the following for your own environment. const organizationId = '1081635000895';  // Required. Resource name of the parent of sources to list. Its format should // be "organizations/[organization_id]", "folders/[folder_id]", or // "projects/[project_id]". const parent = `organizations/${organizationId}`;  // Build the request. const listSourcesRequest = {   parent, };  async function listAllSources() {   // Call the API.   const iterable = client.listSourcesAsync(listSourcesRequest);   let count = 0;   console.log('Sources:');   for await (const response of iterable) {     console.log(`${++count} ${response.name} ${response.description}`);   } }  await listAllSources();

Python

def list_source(organization_id) -> int:     """     lists the findings source     Args:         organization_id: organization_id is the numeric ID of the organization. e.g.:organization_id = "111122222444"     Returns:          Dict: returns the count of the findings source     """     count = -1     from google.cloud import securitycenter_v2      # Create a new client.     client = securitycenter_v2.SecurityCenterClient()     # 'parent' must be in one of the following formats:     #   "organizations/{organization_id}"     #   "projects/{project_id}"     #   "folders/{folder_id}"     parent = f"organizations/{organization_id}"      # Call the API and print out each existing source.     for count, source in enumerate(client.list_sources(request={"parent": parent})):         print(count, source)     return count  

後續步驟