本指南將逐步說明如何使用 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