イメージの脆弱性とメタデータを取得する

は、

Container Analysis は、Container Registry に格納されているコンテナ イメージの脆弱性情報などのメタデータを提供します。メタデータはメモとして保存されます。イメージに関連付けられたメモが検出されるたびにオカレンスが作成されます。

このページでは、メモとオカレンスを表示、フィルタリングしたり、その通知を受けたりするための方法について説明します。

始める前に

  1. プロジェクトで Container Analysis API を有効にします。この API を有効にすると、Container Registry に格納されているすべてのイメージを対象に脆弱性スキャンが自動的に実行されます。

    既存のプロジェクトで API を有効にすることも、新しいプロジェクトを作成してから API を有効にすることもできます。

    Container Analysis API を有効にする

  2. 必要な IAM 権限を有効にします。

    • オカレンスを表示するには、次の権限が必要です。

      • containeranalysis.notes.listOccurrences

      または、必要な権限が自動的に付与されるように、次の事前定義された IAM の役割を付与することもできます。

      • Container Analysis 実行回数の閲覧者
    • オカレンスの通知を受け取るには、適切な Pub/Sub 権限を構成する必要があります。詳細については、通知をご覧ください。

  3. Container Analysis の概要をお読みください。

脆弱性と他のオカレンスの表示

Container Registry に格納されているイメージのオカレンスを表示するには、GCP Console、gcloud コマンドライン ツールまたは Container Analysis API を使用します。

イメージの脆弱性オカレンスを表示するには:

Console

  1. GCP Console で Container Registry ページを開きます。

    Container Registry ページを開く

  2. イメージ名をクリックします。

    イメージの脆弱性の数が [脆弱性] 列に表示されます。

    脆弱性のあるイメージのスクリーンショット

  3. イメージで検出された脆弱性の一覧を表示するには、[脆弱性] 列のリンクをクリックします。

    脆弱性リストには、重大度、修正プログラムが入手可能かどうか、脆弱性が存在するパッケージの名前が表示されます。

  4. 脆弱性ソースから特定の脆弱性の詳細を確認するには、[ドキュメント] 列のリンクをクリックします。

gcloud コマンド

脆弱性情報の概要を表示するには、次のコマンドを実行します。

gcloud beta container images list-tags --show-occurrences \
    [HOSTNAME]/[PROJECT_ID]/[IMAGE_ID]

ここで

  • [PROJECT_ID] は、イメージを含むプロジェクトの ID です。
  • [IMAGE_ID] は、脆弱性を表示するイメージの ID です。
  • [HOSTNAME] は、レジストリ名で説明されているオプションの 1 つです。

このコマンドではイメージタグを指定できません。

タグまたはレイヤに関する情報を表示するには、次のコマンドを実行します。

 gcloud beta container images describe [HOSTNAME]/[PROJECT_ID]/[IMAGE_ID]:[TAG]

  or

 gcloud beta container images describe [HOSTNAME]/[PROJECT_ID]/[IMAGE_ID]@sha256:[HASH]

ここで

  • [PROJECT_ID] は、イメージを含むプロジェクトの ID です。
  • [IMAGE_ID] は、脆弱性を表示するイメージの ID です。
  • [TAG] は、情報を取得するイメージのタグです。
  • [HASH] は、イメージのダイジェストです。
  • [HOSTNAME] は、レジストリ名で説明されているオプションの 1 つです。

脆弱性オカレンスをフィルタするには、次のコマンドを実行します。

 gcloud beta container images list-tags --show-occurrences \
     [HOSTNAME]/[PROJECT_ID]/[IMAGE_ID] --occurrence-filter=[FILTER_EXPRESSION]

ここで

  • [PROJECT_ID] は、イメージを含むプロジェクトの ID です。
  • [IMAGE_ID] は、脆弱性オカレンスを表示するイメージの ID です。
  • [FILTER_EXPRESSION] は、脆弱性オカレンスのフィルタで説明されている形式のサンプル フィルタ式です。
  • [HOSTNAME] は、レジストリ名で説明されているオプションの 1 つです。

API

cURL を使用する

発生箇所を取得するには、次のように記述します。

 curl -X GET -H "Content-Type: application/json" -H \
    "Authorization: Bearer $(gcloud auth print-access-token)" \
    https://containeranalysis.googleapis.com/v1beta1/projects/[PROJECT_ID]/occurrences/[OCCURRENCE_ID]

Java

Container Registry 用のクライアント ライブラリをインストールして使用する方法については、Container Registry のクライアント ライブラリをご覧ください。詳細については、Container Registry Java API のリファレンス ドキュメントをご覧ください。

/**
 * Retrieves and prints a specified Occurrence from the server
 * @param client The Grafeas client used to perform the API requests.
 * @param occurrenceName the name of the Occurrence to delete
 *                       format: "projects/[PROJECT_ID]/occurrences/[OCCURRENCE_ID]"
 * @return the requested Occurrence object
 */
public static Occurrence getOccurrence(GrafeasV1Beta1Client client, String occurrenceName) {
  Occurrence occ = client.getOccurrence(occurrenceName);
  System.out.println(occ);
  return occ;
}

Go

Container Registry 用のクライアント ライブラリをインストールして使用する方法については、Container Registry のクライアント ライブラリをご覧ください。詳細については、Container Registry Go API のリファレンス ドキュメントをご覧ください。

// getOccurrence retrieves and prints a specified Occurrence from the server.
// occurrenceName should be in the following format: "projects/[PROJECT_ID]/occurrences/[OCCURRENCE_ID]"
func getOccurrence(ctx context.Context, client *containeranalysis.GrafeasV1Beta1Client, occurrenceName string) (*grafeaspb.Occurrence, error) {
	req := &grafeaspb.GetOccurrenceRequest{Name: occurrenceName}
	occ, err := client.GetOccurrence(ctx, req)
	fmt.Println(occ)
	return occ, err
}

オカレンスのフィルタリング

gcloud コマンドや Container Analysis API のフィルタ文字列を使用すれば、表示するオカレンスを絞り込むことができます。以降では、サポートされている検索フィルタについて説明します。

特定の種類のオカレンスを表示する

kind フィールドは、オカレンスの種類を特定します。このフィールドを使用して、オカレンスのリストを特定の種類(脆弱性やデプロイメントなど)に限定します。

1 つのイメージを対象に特定の種類のオカレンスを取得するには、次のフィルタ式を使用します。

kind="[NOTE_KIND]" AND resourceUrl="[RESOURCE_URL]"

ここで

  • [NOTE_KIND] はメモの種類です。
    • たとえば、検出オカレンスを表示するには、DISCOVERY を使用します。これらは、イメージが最初に Container Registry に push されたときに作成されます。
    • 脆弱性オカレンスを表示するには、VULNERABILITY を使用します。
  • [RESOURCE_URL] はイメージの完全な URL です。次に例を示します。 https://[HOSTNAME]/[PROJECT_ID]/[IMAGE_ID]@sha256:[HASH]

複数のイメージを対象に特定の種類のオカレンスを取得するには、次のフィルタ式を使用します。

kind="[NOTE_KIND]" AND has_prefix(resourceUrl, "[RESOURCE_URL_PREFIX]")

ここで

  • [RESOURCE_URL_PREFIX] は、イメージの URL 接頭辞です。
    • 1 つのイメージのすべてのバージョンを表示する場合: https://[HOSTNAME]/[PROJECT_ID]/[IMAGE_ID]@
    • プロジェクト内のすべてのイメージを表示する場合: https://[HOSTNAME]/[PROJECT_ID]/

検出オカレンスの表示

イメージが Container Registry に最初に push されるときに、コンテナ イメージの初期スキャンに関する情報を含めた検出オカレンスが作成されます。

イメージの検出オカレンスを取得するには、次のフィルタ式を使用します。

kind="DISCOVERY" AND resourceUrl="[RESOURCE_URL]"

次のスニペットでは、上記のフィルタ式を使用してイメージの検出オカレンスを表示する方法を示しています。

API

検出オカレンスを取得するには、次のように上記のフィルタ式を URL エンコード型にして、GET リクエストに埋め込む必要があります。

GET https://containeranalysis.googleapis.com/v1beta1/projects/PROJECT_ID/occurrences?filter=kind%3D%22DISCOVERY%22%20AND%20resourceUrl%3D%22ENCODED_RESOURCE_URL%22

詳細については、projects.occurrences.get API エンドポイントをご覧ください。

Java

Container Registry 用のクライアント ライブラリをインストールして使用する方法については、Container Registry のクライアント ライブラリをご覧ください。詳細については、Container Registry Java API のリファレンス ドキュメントをご覧ください。

/**
 * Retrieves and prints the Discovery Occurrence created for a specified image
 * The Discovery Occurrence contains information about the initial scan on the image
 * @param client The Grafeas client used to perform the API requests.
 * @param imageUrl the Container Registry URL associated with the image
 *                 example: "https://gcr.io/project/image@sha256:foo"
 * @param projectId the GCP project the image belongs to
 */
public static void getDiscoveryInfo(GrafeasV1Beta1Client client, String imageUrl,
    String projectId) {
  String filterStr = "kind=\"DISCOVERY\" AND resourceUrl=\"" + imageUrl + "\"";
  final String projectName = ProjectName.format(projectId);

  for (Occurrence o : client.listOccurrences(projectName, filterStr).iterateAll()) {
    System.out.println(o);
  }
}

Go

Container Registry 用のクライアント ライブラリをインストールして使用する方法については、Container Registry のクライアント ライブラリをご覧ください。詳細については、Container Registry Go API のリファレンス ドキュメントをご覧ください。

// getDiscoveryInfo retrieves and prints the Discovery Occurrence created for a specified image.
// The Discovery Occurrence contains information about the initial scan on the image.
func getDiscoveryInfo(ctx context.Context, client *containeranalysis.GrafeasV1Beta1Client, imageURL, projectID string) error {
	req := &grafeaspb.ListOccurrencesRequest{
		Parent: fmt.Sprintf("projects/%s", projectID),
		Filter: fmt.Sprintf(`kind="DISCOVERY" AND resourceUrl=%q`, imageURL),
	}
	it := client.ListOccurrences(ctx, req)
	for {
		occ, err := it.Next()
		if err == iterator.Done {
			break
		}
		if err != nil {
			return err
		}
		fmt.Println(occ)
	}
	return nil
}

脆弱性オカレンスの表示

特定のイメージの脆弱性オカレンスを表示するには、次のフィルタ式を使用してクエリを作成します。

kind="VULNERABILITY" AND resourceUrl="[RESOURCE_URL]"

イメージのすべてのオカレンスを表示するには、次のフィルタ式を使用します。

API

次のように、必要なリソースを URL エンコード型にして、GET リクエストに埋め込む必要があります。

GET https://containeranalysis.googleapis.com/v1beta1/projects/PROJECT_ID/occurrences?filter=resourceUrl%3D%22ENCODED_RESOURCE_URL%22

詳細については、projects.occurrences.get API エンドポイントをご覧ください。

Java

Container Registry 用のクライアント ライブラリをインストールして使用する方法については、Container Registry のクライアント ライブラリをご覧ください。詳細については、Container Registry Java API のリファレンス ドキュメントをご覧ください。

/**
 * Retrieves all the Occurrences associated with a specified image
 * Here, all Occurrences are simply printed and counted
 * @param client The Grafeas client used to perform the API requests.
 * @param imageUrl the Container Registry URL associated with the image
 *                 example: "https://gcr.io/project/image@sha256:foo"
 * @param projectId the GCP project to search for Occurrences in
 * @return number of Occurrences found
 */
public static int getOccurrencesForImage(GrafeasV1Beta1Client client, String imageUrl,
    String projectId) {
  final String filterStr = "resourceUrl=\"" + imageUrl + "\"";
  final String projectName = ProjectName.format(projectId);
  int i = 0;

  for (Occurrence o : client.listOccurrences(projectName, filterStr).iterateAll()) {
    // Write custom code to process each Occurrence here
    System.out.println(o.getName());
    i = i + 1;
  }
  return i;
}

Go

Container Registry 用のクライアント ライブラリをインストールして使用する方法については、Container Registry のクライアント ライブラリをご覧ください。詳細については、Container Registry Go API のリファレンス ドキュメントをご覧ください。

// getOccurrencesForImage retrieves all the Occurrences associated with a specified image.
// Here, all Occurrences are simply printed and counted.
func getOccurrencesForImage(ctx context.Context, client *containeranalysis.GrafeasV1Beta1Client, imageURL, projectID string) (int, error) {
	req := &grafeaspb.ListOccurrencesRequest{
		Parent: fmt.Sprintf("projects/%s", projectID),
		Filter: fmt.Sprintf("resourceUrl=%q", imageURL),
	}
	it := client.ListOccurrences(ctx, req)
	count := 0
	for {
		occ, err := it.Next()
		if err == iterator.Done {
			break
		}
		if err != nil {
			return -1, err
		}
		// Write custom code to process each Occurrence here.
		fmt.Println(occ)
		count = count + 1
	}
	return count, nil
}

特定のメモに関連付けられているイメージを表示する

特定のメモ ID に関連付けられているリソースのリストを取得できます。たとえば、特定の CVE の脆弱性を含むイメージを一覧表示できます。

プロジェクト内で特定のメモに関連付けられているすべてのイメージを一覧表示するには、次のフィルタ式を使用します。

noteProjectId="[PROVIDER_PROJECT_ID]" AND noteId="[NOTE_ID]"

特定のイメージのメモを確認するには、次のフィルタ式を使用します。

resourceUrl="[RESOURCE_URL]" AND noteProjectId="[PROVIDER_PROJECT_ID]" \
    AND noteId="[NOTE_ID]"

ここで

  • [PROVIDER_PROJECT_ID] は、プロバイダ プロジェクトの ID です。たとえば、goog-vulnz はデフォルトの脆弱性分析を提供します。
  • [NOTE_ID] はメモの ID です。多くの場合、セキュリティ関連のメモは CVE-2019-12345 という形式になります。
  • [RESOURCE_URL] はイメージの完全な URL です。次に例を示します。 https://[HOSTNAME]/[PROJECT_ID]/[IMAGE_ID]@sha256:[HASH]

たとえば、Google による分析で CVE-2017-16231 のオカレンスがあるすべてのイメージを確認するには、次のフィルタ式を使用します。

noteProjectId="goog-vulnz" AND noteId="CVE-2017-16231"

デプロイメントを表示する

実行中のデプロイメントまたは特定の期間中に実行されていたデプロイメントを一覧表示できます。

現在実行中のデプロイメントを表示するには、次のフィルタ式を使用します。

kind="DEPLOYABLE" AND resourceUrl="[RESOURCE_URL]" AND \
    isNull(deployment.undeployTime)

特定の期間中に実行されていたデプロイメントを表示するには、次のフィルタ式を使用します。

kind="DEPLOYABLE" AND resourceUrl="[RESOURCE_URL]" AND \
    deployment.deployTime<"[DEPLOY_TIME]" AND \
    deployment.undeployTime>"[UNDEPLOY_TIME]"

ここで

  • [RESOURCE_URL] はイメージの完全な URL です。次に例を示します。 https://[HOSTNAME]/[PROJECT_ID]/[IMAGE_ID]@sha256:[HASH]
  • [DEPLOY_TIME] はデプロイメント期間の開始時間です。RFC 3339 UTC Zulu 形式のタイムスタンプ(精度はナノ秒)を使用します。たとえば "2014-10-02T15:01:23.045123456Z" です。
  • [UNDEPLOY_TIME] はデプロイメント期間の終了時間です。RFC 3339 UTC Zulu 形式のタイムスタンプ(精度はナノ秒)を使用します。

Cloud Pub/Sub の通知

通知を受け取るには、適切な IAM 権限を構成する必要があります。必要な権限と役割については、Pub/Sub アクセス制御の情報をご覧ください。

このセクションでは、メモとオカレンスの更新通知を設定する方法について説明します。

Container Registry は、脆弱性や他のメタデータをスキャンするたびに、Cloud Pub/Sub 経由で通知を送信します。Container Analysis API を有効にすると、次の Cloud Pub/Sub トピックがプロジェクトに作成されます。

  • container-analysis-notes-v1beta1
  • container-analysis-occurrences-v1beta1

メモまたは発生箇所が作成または更新されるたびに、それぞれのトピックにメッセージがパブリッシュされます。

Pub/Sub ペイロードは JSON 形式で、スキーマは次のとおりです。

メモ:

{
    "name": "projects/[PROJECT_ID]/notes/[NOTE_ID]",
    "kind": "[NOTE_KIND]",
    "notificationTime": "[NOTIFICATION_TIME]",
}

オカレンス:

{
    "name": "projects/[PROJECT_ID]/occurrences/[OCCURRENCE_ID]",
    "kind": "[NOTE_KIND]",
    "notificationTime": "[NOTIFICATION_TIME]",
}

ここで

  • [NOTE_KIND] は、NoteKind の値の 1 つです。
  • [NOTIFICATION_TIME] は、RFC 3339 UTC Zulu 形式のタイムスタンプ(精度はナノ秒)です。

これらのイベントをリッスンするには、そのトピックに関連付けられた Pub/Sub サブスクリプションを作成します。

API

Cloud Pub/Sub イベントを受信するには、まず container-analysis-occurrences-v1beta1 トピックに関連付けられたサブスクリプションを作成する必要があります。

gcloud beta pubsub subscriptions create \
    --topic container-analysis-occurrences-v1beta1 occurrences

新しいサブスクリプションを使用して、オカレンスに関するメッセージを pull できるようになります。

gcloud beta pubsub subscriptions pull \
    --auto-ack occurrences

Java

Container Registry 用のクライアント ライブラリをインストールして使用する方法については、Container Registry のクライアント ライブラリをご覧ください。詳細については、Container Registry Java API のリファレンス ドキュメントをご覧ください。

/**
 * Handle incoming Occurrences using a Cloud Pub/Sub subscription
 * @param subId the user-specified identifier for the Pub/Sub subscription
 * @param timeout the amount of time to listen for Pub/Sub messages (in seconds)
 * @param projectId the GCP project the Pub/Sub subscription belongs to
 * @return number of Occurrence Pub/Sub messages received before exiting
 * @throws InterruptedException on errors with the subscription client
 */
public static int pubSub(String subId, int timeout, String projectId)
    throws InterruptedException {
  Subscriber subscriber = null;
  MessageReceiverExample receiver = new MessageReceiverExample();

  try {
    // Subscribe to the requested Pub/Sub channel
    ProjectSubscriptionName subName = ProjectSubscriptionName.of(projectId, subId);
    subscriber = Subscriber.newBuilder(subName, receiver).build();
    subscriber.startAsync().awaitRunning();
    // Listen to messages for 'timeout' seconds
    for (int i = 0; i < timeout; i++) {
      sleep(1000);
    }
  } finally {
    // Stop listening to the channel
    if (subscriber != null) {
      subscriber.stopAsync();
    }
  }
  // Print and return the number of Pub/Sub messages received
  System.out.println(receiver.messageCount);
  return receiver.messageCount;
}

/**
 * Custom class to handle incoming Pub/Sub messages
 * In this case, the class will simply log and count each message as it comes in
 */
static class MessageReceiverExample implements MessageReceiver {
  public int messageCount = 0;

  @Override
  public synchronized void receiveMessage(PubsubMessage message, AckReplyConsumer consumer) {
    // Every time a Pub/Sub message comes in, print it and count it
    System.out.println("Message " + messageCount + ": " + message.getData().toStringUtf8());
    messageCount += 1;
    // Acknowledge the message
    consumer.ack();
  }
}

/**
 * Creates and returns a Pub/Sub subscription object listening to the Occurrence topic
 * @param subId the identifier you want to associate with the subscription
 * @param projectId the GCP project to create the subscription under
 * @throws IOException thrown on errors with the subscription client
 * @throws StatusRuntimeException if subscription already exists
 *
 */
public static Subscription createOccurrenceSubscription(String subId, String projectId)
    throws IOException, StatusRuntimeException {
  // This topic id will automatically receive messages when Occurrences are added or modified
  String topicId = "container-analysis-occurrences-v1beta1";
  SubscriptionAdminClient client = SubscriptionAdminClient.create();
  PushConfig config = PushConfig.getDefaultInstance();
  ProjectTopicName topicName = ProjectTopicName.of(projectId, topicId);
  ProjectSubscriptionName subName = ProjectSubscriptionName.of(projectId, subId);
  Subscription sub = client.createSubscription(subName, topicName, config, 0);
  return sub;
} 

Go

Container Registry 用のクライアント ライブラリをインストールして使用する方法については、Container Registry のクライアント ライブラリをご覧ください。詳細については、Container Registry Go API のリファレンス ドキュメントをご覧ください。

// occurrencePubsub handles incoming Occurrences using a Cloud Pub/Sub subscription.
func occurrencePubsub(ctx context.Context, subscriptionID string, timeout int, projectID string) (int, error) {
	var mu sync.Mutex
	client, err := pubsub.NewClient(ctx, projectID)
	if err != nil {
		return -1, err
	}
	// Subscribe to the requested Pub/Sub channel.
	sub := client.Subscription(subscriptionID)
	count := 0

	// Listen to messages for 'timeout' seconds.
	toctx, cancel := context.WithTimeout(ctx, time.Duration(timeout)*time.Second)
	defer cancel()
	err = sub.Receive(toctx, func(ctx context.Context, msg *pubsub.Message) {
		mu.Lock()
		count = count + 1
		fmt.Printf("Message %d: %q\n", count, string(msg.Data))
		msg.Ack()
		mu.Unlock()
	})
	if err != nil {
		return -1, err
	}
	// Print and return the number of Pub/Sub messages received.
	fmt.Println(count)
	return count, nil
}

// createOccurrenceSubscription creates and returns a Pub/Sub subscription object listening to the Occurrence topic.
func createOccurrenceSubscription(ctx context.Context, subscriptionID, projectID string) error {
	client, err := pubsub.NewClient(ctx, projectID)
	if err != nil {
		return err
	}

	// This topic id will automatically receive messages when Occurrences are added or modified
	topicID := "container-analysis-occurrences-v1beta1"
	topic := client.Topic(topicID)
	config := pubsub.SubscriptionConfig{Topic: topic}
	_, err = client.CreateSubscription(ctx, subscriptionID, config)
	return err
}

サブスクライバー アプリケーションで使用できるのは、サブスクリプションの作成後にトピックにパブリッシュしたメッセージだけです。

次のステップ

  • Container Analysis を使用して顧客のメタデータを格納および管理する方法については、イメージ用のメタデータの提供をご覧ください。

  • Binary Authorization と脆弱性スキャンを統合することで、既知のセキュリティ問題を伴うイメージがデプロイメント環境で実行されないようにすることができます。これを行う手順については、脆弱性スキャンの統合をご覧ください。

このページは役立ちましたか?評価をお願いいたします。

フィードバックを送信...