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: ソースの表示名(1~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 コンソールにソースは表示されません。特定のソースの取得の手順に沿って、作成されたことを確認できます。

ソースの更新

ソースの表示名と説明は作成後に更新できます。また、フィールド マスクを使用して、更新するフィールドを 1 つのみにすることもできます。次の例では、フィールド マスクを使用して表示名のみを更新し、説明は変更していません。

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: ソースの説明(最大 1,024 文字)。
  • DISPLAY_NAME: ソースの表示名(1~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();

特定のソースの取得

ソースの絶対リソース名を指定して 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();

次のステップ

SDK を使用した Security Command Center へのアクセスについて詳細を確認する。