本指南将介绍如何使用 Security Command Center API 创建和更新发现结果。
准备工作
在创建和更新发现结果之前,您需要完成以下操作:
为完成本指南,必须拥有组织级别的 Identity and Access Management (IAM) Security Center Findings Editor (securitycenter.findingsEditor
) 角色。如需详细了解 Security Command Center 角色,请参阅访问权限控制。
如果要使用安全标志创建发现结果,还必须拥有包含要使用的标记类型的权限的 IAM 角色:
- Asset Security Marks Writer (
securitycenter.assetSecurityMarksWriter
) - Finding Security Marks Writer (
securitycenter.findingSecurityMarksWriter
)
如需详细了解标记,请参阅 使用 Security Command Center 安全标记。
创建发现结果
为来源创建活跃的发现结果。
gcloud
gcloud scc findings create FINDING_NAME \ --organization=PARENT_ID \ --location=LOCATION \ --source=SOURCE_ID \ --state=STATE \ --category=CATEGORY \ --event-time=EVENT_TIME \ --resource-name=RESOURCE_NAME
替换以下内容:
FINDING_NAME
:发现结果的名称。PARENT_ID
:父级组织的数字 ID。LOCATION
:如果数据驻留处于启用状态,则为创建发现结果时使用的 Security Command Center 位置;如果数据驻留处于停用状态,则使用值global
。SOURCE_ID
:发现结果的来源的数字 ID。STATE
:发现结果的状态;如果发现结果需要关注,请使用ACTIVE
;如果发现结果已得到解决,请使用INACTIVE
。CATEGORY
:相应发现所属的分类群组;例如AUDIT_LOGGING_DISABLED
。EVENT_TIME
:事件发生的时间,采用 RFC 822 时间戳或其他 gcloud CLI 支持的时间戳格式。RESOURCE_NAME
:相应发现适用的资源的完整资源名称。
Go
import (
"context"
"fmt"
"io"
"time"
securitycenter "cloud.google.com/go/securitycenter/apiv2"
"cloud.google.com/go/securitycenter/apiv2/securitycenterpb"
"github.com/golang/protobuf/ptypes"
)
// createFinding demonstrates how to create a new security finding in CSCC.
// sourceName is the full resource name of the source the finding should
// be associated with.
func createFinding(w io.Writer, sourceName string) error {
// sourceName := "organizations/111122222444/sources/1234/locations/global"
// 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.
// Use now as the eventTime for the security finding.
eventTime, err := ptypes.TimestampProto(time.Now())
if err != nil {
return fmt.Errorf("TimestampProto: %w", err)
}
req := &securitycenterpb.CreateFindingRequest{
Parent: sourceName,
FindingId: "samplefindingid",
Finding: &securitycenterpb.Finding{
State: securitycenterpb.Finding_ACTIVE,
// Resource the finding is associated with. This is an
// example any resource identifier can be used.
ResourceName: "//cloudresourcemanager.googleapis.com/organizations/11232/sources/-/locations/global",
// A free-form category.
Category: "MEDIUM_RISK_ONE",
// The time associated with discovering the issue.
EventTime: eventTime,
},
}
finding, err := client.CreateFinding(ctx, req)
if err != nil {
return fmt.Errorf("CreateFinding: %w", err)
}
fmt.Fprintf(w, "New finding created: %s\n", finding.Name)
fmt.Fprintf(w, "Event time (Epoch Seconds): %d\n", eventTime.Seconds)
return nil
}
Java
import com.google.cloud.securitycenter.v2.CreateFindingRequest;
import com.google.cloud.securitycenter.v2.Finding;
import com.google.cloud.securitycenter.v2.Finding.FindingClass;
import com.google.cloud.securitycenter.v2.Finding.Mute;
import com.google.cloud.securitycenter.v2.Finding.Severity;
import com.google.cloud.securitycenter.v2.Finding.State;
import com.google.cloud.securitycenter.v2.SecurityCenterClient;
import com.google.cloud.securitycenter.v2.SourceName;
import com.google.protobuf.Timestamp;
import java.io.IOException;
import java.time.Instant;
import java.util.Optional;
import java.util.UUID;
public class CreateFindings {
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}";
// Specify the location to list the findings.
String location = "global";
// The source id corresponding to the finding.
String sourceId = "{source-id}";
// The finding id.
String findingId = "testfindingv2" + UUID.randomUUID().toString().split("-")[0];
// Specify the category.
Optional<String> category = Optional.of("MEDIUM_RISK_ONE");
createFinding(organizationId, location, findingId, sourceId, category);
}
/**
* Creates a security finding within a specific source in the Security Command Center.
*/
public static Finding createFinding(String organizationId, String location, String findingId,
String sourceId, Optional<String> category) throws IOException {
try (SecurityCenterClient client = SecurityCenterClient.create()) {
// Optionally SourceName or String can be used.
// String sourceName = String.format("organizations/%s/sources/%s", organizationId, sourceId);
SourceName sourceName = SourceName.of(organizationId, sourceId);
Instant eventTime = Instant.now();
// The resource this finding applies 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.
String resourceName = String.format("//cloudresourcemanager.googleapis.com/organizations/%s",
organizationId);
// Set up a request to create a finding in a source.
String parent = String.format("%s/locations/%s", sourceName.toString(), location);
Finding finding = Finding.newBuilder()
.setParent(parent)
.setState(State.ACTIVE)
.setSeverity(Severity.LOW)
.setMute(Mute.UNMUTED)
.setFindingClass(FindingClass.OBSERVATION)
.setResourceName(resourceName)
.setEventTime(Timestamp.newBuilder()
.setSeconds(eventTime.getEpochSecond())
.setNanos(eventTime.getNano()))
.setCategory(category.orElse("LOW_RISK_ONE"))
.build();
CreateFindingRequest createFindingRequest = CreateFindingRequest.newBuilder()
.setParent(parent)
.setFindingId(findingId)
.setFinding(finding).build();
// Call the API.
Finding response = client.createFinding(createFindingRequest);
return response;
}
}
}
Node.js
// Imports the Google Cloud client library.
const {SecurityCenterClient} = require('@google-cloud/security-center').v2;
const uuid = require('uuid');
// Create a Security Center client
const client = new SecurityCenterClient();
// TODO(developer): Update the following for your own environment.
const organizationId = '1081635000895';
const location = 'global';
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 applies 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,
},
},
};
// Call the API.
const [finding] = await client.createFinding(createFindingRequest);
console.log('New finding created: %j', finding);
Python
def create_finding(
organization_id, location_id, finding_id, source_name, category
) -> Dict:
"""
cretaes a new finding
Args:
organization_id: organization_id is the numeric ID of the organization. e.g.:organization_id = "111122222444"
source_name: is the resource path for a source that has been created
finding_id: unique identifier provided by the client.
location_id: GCP location id; example: 'global'
category: the additional category group with in findings.
Returns:
Dict: returns the created findings details.
"""
import datetime
from google.cloud import securitycenter_v2
from google.cloud.securitycenter_v2 import Finding
# Create a new client.
client = securitycenter_v2.SecurityCenterClient()
# Use the current time as the finding "event time".
event_time = datetime.datetime.now(tz=datetime.timezone.utc)
# '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_name = f"organizations/{organization_id}/sources/{source_name}"
# category= "MEDIUM_RISK_ONE"
# The resource this finding applies to. The CSCC UI can link
# the findings for a resource to the corresponding Asset of a resource
# if there are matches.
resource_name = (
f"//cloudresourcemanager.googleapis.com/organizations/{organization_id}"
)
finding = Finding(
state=Finding.State.ACTIVE,
resource_name=resource_name,
category=category,
event_time=event_time,
)
parent = source_name + "/locations/" + location_id
# Call The API.
created_finding = client.create_finding(
request={"parent": parent, "finding_id": finding_id, "finding": finding}
)
print(created_finding)
return created_finding
如需了解 Security Command Center 中发现结果数据的存储时长,请参阅发现结果保留。
更新发现结果的状态
Security Command Center 还提供了一个 API,可让您仅更新发现结果的状态。此 API 的存在只是为了提供仅更新发现结果的状态。它是一种简单 API,允许权限主账号只能修改发现结果的状态,而无权修改结果的其他方面。以下示例展示了如何将发现结果的状态更改为非活跃。
gcloud
gcloud scc findings update \ PARENT/PARENT_ID/sources/SOURCE_ID/locations/LOCATION/findings/FINDING_NAME \ --state=STATE
替换以下内容:
PARENT
:相应发现所在的资源层次结构的级别;请使用organizations
、folders
或projects
。PARENT_ID
:父级组织、文件夹或项目的数字 ID,或父级项目的字母数字 ID。SOURCE_ID
:发现结果的来源的数字 ID。LOCATION
:如果数据驻留处于启用状态,则为更新发现结果的 Security Command Center 位置;如果数据驻留处于停用状态,则使用值global
。FINDING_NAME
:要更新的发现。STATE
:发现结果的状态;如果发现结果需要关注,请使用ACTIVE
;如果发现结果已得到解决,请使用INACTIVE
。
Go
import (
"context"
"fmt"
"io"
securitycenter "cloud.google.com/go/securitycenter/apiv2"
"cloud.google.com/go/securitycenter/apiv2/securitycenterpb"
)
// updateFindingState demonstrates how to update a security finding's state
// in CSCC. findingName is the full resource name of the finding to update.
func setFindingState(w io.Writer, findingName string) error {
// findingName := "organizations/111122222444/sources/1234/locations/global"
// 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.SetFindingStateRequest{
Name: findingName,
State: securitycenterpb.Finding_INACTIVE,
// New state is effective immediately.
}
finding, err := client.SetFindingState(ctx, req)
if err != nil {
return fmt.Errorf("SetFindingState: %w", err)
}
fmt.Fprintf(w, "Finding updated: %s\n", finding.Name)
fmt.Fprintf(w, "Finding state: %v\n", finding.State)
fmt.Fprintf(w, "Event time (Epoch Seconds): %d\n", finding.EventTime.Seconds)
return nil
}
Java
import com.google.cloud.securitycenter.v2.Finding;
import com.google.cloud.securitycenter.v2.Finding.State;
import com.google.cloud.securitycenter.v2.FindingName;
import com.google.cloud.securitycenter.v2.SecurityCenterClient;
import com.google.cloud.securitycenter.v2.SetFindingStateRequest;
import java.io.IOException;
public class SetFindingsByState {
public static void main(String[] args) throws IOException {
// organizationId: Google Cloud Organization id.
String organizationId = "{google-cloud-organization-id}";
// Specify the location to list the findings.
String location = "global";
// The source id corresponding to the finding.
String sourceId = "{source-id}";
// The finding id.
String findingId = "{finding-id}";
setFindingState(organizationId, location, sourceId, findingId);
}
// Demonstrates how to update a finding's state
public static Finding setFindingState(String organizationId, String location, String sourceId,
String findingId) 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()) {
// Optionally FindingName or String can be used.
// String findingName = String.format("organizations/%s/sources/%s/locations/%s/findings/%s",
// organizationId,sourceId,location,findingId);
FindingName findingName = FindingName
.ofOrganizationSourceLocationFindingName(organizationId, sourceId, location, findingId);
SetFindingStateRequest request = SetFindingStateRequest.newBuilder()
.setName(findingName.toString())
.setState(State.INACTIVE)
.build();
// Call the API.
Finding finding = client.setFindingState(request);
System.out.println("Updated Finding: " + finding);
return finding;
}
}
}
Node.js
// Imports the Google Cloud client library.
const {SecurityCenterClient} = require('@google-cloud/security-center').v2;
// Creates a new 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, findingId};
}
const {sourceId, findingId} = await createSampleFinding();
// The name is the full resource name of the source the finding should be associated with.
const name = `organizations/${organizationId}/sources/${sourceId}/locations/${location}/findings/${findingId}`;
// Build the request.
const eventTime = new Date();
const findingRequest = {
name,
state: 'INACTIVE',
// use now as the time when the new state takes effect.
startTime: {
seconds: Math.floor(eventTime.getTime() / 1000),
nanos: (eventTime.getTime() % 1000) * 1e6,
},
};
async function setFindingState() {
// Call the API.
const [response] = await client.setFindingState(findingRequest);
console.log('Set finding state: %j', response);
}
await setFindingState();
后续步骤
详细了解如何使用客户端库访问 Security Command Center。