ניהול מקורות אבטחה באמצעות Security Command Center API

במדריך הזה מוסבר איך להשתמש ב-Security Command Center API כדי ליצור מקור ליצירת ממצאים. כשמוסיפים מקור, Security Command Center יוצר מקורות מתאימים ומקצה להם את ההרשאות הרלוונטיות.

אפשר להעניק את תפקידי ה-IAM של Security Command Center ברמת הארגון, התיקייה או הפרויקט. היכולת שלכם להציג, לערוך, ליצור או לעדכן ממצאים, נכסים ומקורות אבטחה תלויה ברמת הגישה שניתנה לכם. מידע נוסף על תפקידים ב-Security Command Center זמין במאמר בקרת גישה.

לפני שמתחילים

לפני שמגדירים מקור, צריך לעבור אימות באמצעות Security Command Center API.

יצירת מקור

בדוגמה הזו מוצג איך ליצור מקור עם שם תצוגה ותיאור ספציפיים שמשמשים ב-Security Command Center.

השרת מקצה באופן אוטומטי מזהה למקור.

REST

ב-API, שולחים בקשה ל-method‏ 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: מזהה הארגון.
  • 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, שולחים בקשה ל-method‏ 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: מזהה הארגון.
  • SOURCE_ID: מזהה המקור. כדי ללמוד איך למצוא מזהה מקור, אפשר לעיין במאמר בנושא קבלת מזהה המקור.
  • 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"
	"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: מזהה הארגון.
  • SOURCE_ID: מזהה המקור.

REST

ב-API, שולחים בקשה ל-method‏ organizations.sources.get:

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

מחליפים את מה שכתוב בשדות הבאים:

  • ORGANIZATION_ID: מזהה הארגון.
  • SOURCE_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, שולחים בקשה ל-method‏ organizations.sources.list:

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

מחליפים את מה שכתוב בשדות הבאים:

  • ORGANIZATION_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

המאמרים הבאים