Security Command Center API를 사용하여 발견 항목 관리

이 가이드에서는 Security Command Center API를 사용하여 발견 항목을 만들고 업데이트하는 방법을 안내합니다.

시작하기 전에

발견 항목을 만들고 업데이트하기 전에 다음 작업을 완료해야 합니다.

이 가이드를 완료하려면 조직 수준에서 Identity and Access Management(IAM) 보안 센터 발견 항목 편집자(securitycenter.findingsEditor) 역할이 있어야 합니다. Security Command Center 역할에 대한 자세한 내용은 액세스 제어를 참조하세요.

보안 표시를 사용하는 발견 항목을 만들려면 사용할 표시 종류의 권한이 포함된 IAM 역할도 있어야 합니다.

  • 애셋 보안 표시 작성자(securitycenter.assetSecurityMarksWriter)
  • 발견 항목 보안 표시 작성자(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
}

자바


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는 권한 있는 주 구성원이 발견 항목의 다른 부분이 아닌 상태만 수정할 수 있도록 하는 간단한 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
}

자바


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에 액세스 자세히 알아보기