教程:获取映像漏洞

本教程介绍如何使用 Container Analysis 扫描存储在 Container Registry 中的容器映像的漏洞,然后列出映像的高严重级别漏洞。

目标

本教程包括以下步骤:

  1. 构建一个具有已知漏洞的 Docker 映像
  2. 将映像推送到项目的 Container Registry
  3. 轮询 Container Analysis,以完成初始扫描和映像分析
  4. 查看 Container Analysis 找到的漏洞
  5. 列出映像中高严重级别的漏洞

费用

本教程使用 Google Cloud 的收费组件。如需了解价格信息,请参阅价格

注意:当您启用 Container Scanning API 后,系统会立即开始计费。一旦您启用该 API,Container Analysis 将自动扫描每个新推送的映像,但不会自动扫描现有映像。如需扫描现有映像,必须再次推送。

Google Cloud 新用户可能有资格申请免费试用

准备工作

如需使用 Container Analysis,您必须启用并设置 Container Analysis 和 Container Registry。

  1. 登录您的 Google 帐号。

    如果您还没有 Google 帐号,请注册一个新帐号

  2. 在 Cloud Console 的项目选择器页面上,选择或创建 Cloud 项目。

    转到项目选择器页面

  3. 确保您的 Google Cloud 项目已启用结算功能。 了解如何确认您的项目已启用结算功能

  4. 启用 Container Registry and Container Scanning API。

    启用 API

  5. 安装并初始化 Cloud SDK
  6. 如果您尚未获取所需的 IAM 权限,请执行以下任一操作。 如果您是项目所有者,请跳过此步骤。
    • 如需查看发生实例,您必须具有以下权限:
      containeranalysis.notes.listOccurrences
    • 也可以授予以下预定义 IAM 角色,该角色将自动提供必需的权限:
      Container Analysis Occurrences Viewer
  7. 安装 Docker。如果您使用的是基于 Linux 的操作系统(例如 Ubuntu 或 Debian),请将您的用户名添加到 docker 群组,这样您就可以在不使用 sudo 的情况下运行 Docker:
    sudo usermod -a -G docker ${USER}

    在将您自己添加到 docker 群组之后,您可能需要重启系统。

  8. 打开 Docker。如需确保 Docker 正在运行,请运行以下 Docker 命令以返回当前时间和日期:
  9. docker run busybox date

构建 Docker 映像

在本教程中,您需要构建以下 Docker 映像,以保证您拥有一个可以推送到 Container Registry 的映像。您将使用具有可被 Container Analysis 识别的已知漏洞的 Python 运行时。 此 Docker 映像包含一个小型 Python Web 应用。该应用使用 Flask Web 框架来呈现显示“Hello, World!”消息的网页

如需创建 Docker 映像,请执行以下操作:

  1. 创建一个用于存储三个 Docker 映像文件的目录。

  2. 在此目录中,创建 Dockerfilerequirements.txtapp.py 这三个文件。请参阅以下示例,了解您将需要在每个文件的内容中提供哪些信息:

Dockerfile

# The file Dockerfile defines the image's environment
# Import Python runtime and set up working directory
# This specified runtime has known vulnerabilities so that vulnerability
# scanning can be tested.
FROM python:3.5-slim
WORKDIR /app
ADD . /app

# Install any necessary dependencies
RUN pip install -r requirements.txt

# Open port 80 for serving the webpage
EXPOSE 80

# Run app.py when the container launches
CMD ["python", "app.py"]

requirements.txt

# The file requirements.txt defines the image's dependencies
Flask

app.py

# The Docker image contains the following code in app.py
from flask import Flask
import os
import socket

app = Flask(__name__)

@app.route("/")
def hello():
    html = "<h3>Hello, World!</h3>"
    return html

if __name__ == "__main__":
  app.run(host='0.0.0.0', port=80)

如需构建 Docker 映像,请从包含映像文件的目录运行以下 Docker 命令:

docker build -t vulnerabilities-tutorial-image .

现在,您已经在本地机器上创建了一个 Docker 映像。

将映像添加到 Container Registry

docker 配置为使用 gcloud 命令行工具作为凭据帮助程序

如需推送或拉取映像,您必须将 Docker 配置为使用 gcloud 命令行工具对向 Container Registry 发出的请求进行身份验证。为此,请运行以下命令(您只需要执行此操作一次):

gcloud auth configure-docker

使用注册表名称标记映像

如需将 Docker 映像推送到 Container Registry,您需要使用注册表名称来标记该映像。使用注册表名称标记 Docker 映像,会将 docker push 命令配置为将该映像推送到某个特定位置。在本快速入门中,主机位置为 gcr.io

如需标记 Docker 映像,请运行以下命令:

docker tag vulnerabilities-tutorial-image gcr.io/[PROJECT-ID]/vulnerabilities-tutorial-image:tag1

其中:

  • [PROJECT-ID] 是您的 Google Cloud Console 项目 ID,您需要将此 ID 添加到命令中
  • gcr.io 是主机名
  • vulnerabilities-tutorial-image 是 Docker 映像的名称
  • tag1 是要添加到 Docker 映像的标记。如果您没有指定标记,Docker 将应用默认标记 latest

现在您可以将映像推送到 Container Registry 了。

将映像推送到 Container Registry

docker 配置为使用 gcloud 作为凭据帮助程序,并使用注册表名称标记本地映像后,您便可将该映像推送到 Container Registry。

如需推送 Docker 映像,请运行以下命令:

docker push gcr.io/[PROJECT-ID]/vulnerabilities-tutorial-image:tag1

其中,[PROJECT-ID] 是您的 Google Cloud Console 项目 ID。如果您的项目 ID 包含英文冒号 (:),请参阅网域级项目

当您将映像推送到新的主机位置时,该服务会为您的项目创建独有的底层存储分区。您可以通过 Cloud Console 查看由 Container Registry 托管的映像,也可以通过在网络浏览器中输入映像的注册表名称来查看这些映像:http://gcr.io/[PROJECT-ID]/vulnerabilities-tutorial-image

查看映像的摘要

在下一步中,您将需要映像的 SHA256 摘要。这是将映像推送到注册表时生成的映像的哈希值。

虽然您可以使用标记在不同的时间点标记不同的版本,但摘要始终是映像版本独有的。

curl -H "Authorization: Bearer $(gcloud auth print-access-token)" https://gcr.io/v2/[PROJECT-ID]/vulnerabilities-tutorial-image/tags/list

返回的响应是一个 JSON 对象,其中包含有关您上传的映像的不同版本的信息,以及相关的标记。

{
  "child": [],
  "manifest": {
    "sha256:1e70b945e7266268e35b3db3ddc5c13e88fe4e42ee6ffbfa8962ec0b1562b51a": {
      "imageSizeBytes": "2803075",
      "layerId": "",
      "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
      "tag": [],
      "timeCreatedMs": "1583923219860",
      "timeUploadedMs": "1583923231051"
    },
    "sha256:95d270793da3f9eb77ae6c0ebe84beaaa84a66c331c3fb2205f8645646568a98": {
      "imageSizeBytes": "61212796",
      "layerId": "",
      "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
      "tag": [
        "tag1"
      ],
      "timeCreatedMs": "1583923518920",
      "timeUploadedMs": "1583923552668"
    }
  },
  "name": "[PROJECT-ID]/vulnerabilities-tutorial-image",
  "tags": [
    "tag1"
  ]
}

例如,可以使用 JQ 工具对其进行解析:

curl -H "Authorization: Bearer $(gcloud auth print-access-token)" https://gcr.io/v2/[PROJECT-ID]/vulnerabilities-tutorial-image/tags/list | jq  '.manifest | to_entries[] | select(.value.tag | contains(["tag1"])) | .key'

轮询以进行初始扫描和获得分析结果

Container Analysis 将自动执行映像初始扫描和分析。它会创建一个扫描结果实例,其中包含从初始扫描收集的信息。Container Analysis 还会使用扫描的当前状态更新扫描结果实例。

以下代码示例会轮询以获取初始扫描的当前状态,从而了解何时可以查看漏洞信息。

Java

如需了解如何安装和使用 Container Registry 客户端库,请参阅 Container Registry 客户端库。 如需了解详情,请参阅 Container Registry Java API 参考文档

import com.google.cloud.devtools.containeranalysis.v1.ContainerAnalysisClient;
import io.grafeas.v1.DiscoveryOccurrence;
import io.grafeas.v1.DiscoveryOccurrence.AnalysisStatus;
import io.grafeas.v1.GrafeasClient;
import io.grafeas.v1.Occurrence;
import io.grafeas.v1.ProjectName;
import java.io.IOException;
import java.lang.InterruptedException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

public class PollDiscoveryOccurrenceFinished {
  // Repeatedly query the Container Analysis API for the latest discovery occurrence until it is
  // either in a terminal state, or the timeout value has been exceeded
  public static Occurrence pollDiscoveryOccurrenceFinished(String resourceUrl, String projectId,
      long timeoutSeconds) throws IOException, TimeoutException, InterruptedException {
    // String resourceUrl = "https://gcr.io/project/image@sha256:123";
    // String projectId = "my-project-id";
    // long timeoutSeconds = 30;
    final String projectName = ProjectName.format(projectId);
    long deadline = System.currentTimeMillis() + timeoutSeconds * 1000;

    // Initialize client that will be used to send requests. After completing all of your requests,
    // call the "close" method on the client to safely clean up any remaining background resources.
    GrafeasClient client = ContainerAnalysisClient.create().getGrafeasClient();

    // find the discovery occurrence using a filter string
    Occurrence discoveryOccurrence = null;
    // vulbnerability discovery occurrences are always associated with the
    // PACKAGE_VULNERABILITY note in the "goog-analysis" GCP project
    String filter =  String.format("resourceUrl=\"%s\" AND noteProjectId=\"%s\" AND noteId=\"%s\"",
        resourceUrl, "goog-analysis",  "PACKAGE_VULNERABILITY");
    while (discoveryOccurrence == null) {
      for (Occurrence o : client.listOccurrences(projectName, filter).iterateAll()) {
        if (o.getDiscovery() != null) {
          // there should be only one valid discovery occurrence returned by the given filter
          discoveryOccurrence = o;
        }
      }
      TimeUnit.SECONDS.sleep(1);
      // check for timeout
      if (System.currentTimeMillis() > deadline) {
        throw new TimeoutException("discovery occurrence not found");
      }
    }

    // wait for discovery occurrence to enter a terminal state
    AnalysisStatus status = AnalysisStatus.PENDING;
    while (status != AnalysisStatus.FINISHED_SUCCESS
        && status != AnalysisStatus.FINISHED_FAILED
        && status != AnalysisStatus.FINISHED_UNSUPPORTED) {
      // update the occurrence state
      discoveryOccurrence = client.getOccurrence(discoveryOccurrence.getName());
      status = discoveryOccurrence.getDiscovery().getAnalysisStatus();
      TimeUnit.SECONDS.sleep(1);
      // check for timeout
      if (System.currentTimeMillis() > deadline) {
        throw new TimeoutException("discovery occurrence not in terminal state");
      }
    }
    return discoveryOccurrence;
  }
}

Go

如需了解如何安装和使用 Container Registry 客户端库,请参阅 Container Registry 客户端库。 如需了解详情,请参阅 Container Registry Go API 参考文档


import (
	"context"
	"fmt"
	"time"

	containeranalysis "cloud.google.com/go/containeranalysis/apiv1"
	"google.golang.org/api/iterator"
	grafeaspb "google.golang.org/genproto/googleapis/grafeas/v1"
)

// pollDiscoveryOccurrenceFinished returns the discovery occurrence for a resource once it reaches a finished state.
func pollDiscoveryOccurrenceFinished(resourceURL, projectID string, timeout time.Duration) (*grafeaspb.Occurrence, error) {
	// resourceURL := fmt.Sprintf("https://gcr.io/my-project/my-image")
	// timeout := time.Duration(5) * time.Second
	ctx, cancel := context.WithTimeout(context.Background(), timeout)
	defer cancel()

	client, err := containeranalysis.NewClient(ctx)
	if err != nil {
		return nil, fmt.Errorf("NewClient: %v", err)
	}
	defer client.Close()

	// ticker is used to poll once per second.
	ticker := time.NewTicker(1 * time.Second)
	defer ticker.Stop()

	// Find the discovery occurrence using a filter string.
	var discoveryOccurrence *grafeaspb.Occurrence
	for discoveryOccurrence == nil {
		select {
		case <-ctx.Done():
			return nil, fmt.Errorf("timeout while retrieving discovery occurrence")
		case <-ticker.C:
			req := &grafeaspb.ListOccurrencesRequest{
				Parent: fmt.Sprintf("projects/%s", projectID),
				// Vulnerability discovery occurrences are always associated with the
				// PACKAGE_VULNERABILITY note in the "goog-analysis" GCP project.
				Filter: fmt.Sprintf(`resourceUrl=%q AND noteProjectId="goog-analysis" AND noteId="PACKAGE_VULNERABILITY"`, resourceURL),
			}
			it := client.GetGrafeasClient().ListOccurrences(ctx, req)
			// Only one occurrence should ever be returned by ListOccurrences
			// and the given filter.
			result, err := it.Next()
			if err == iterator.Done {
				break
			}
			if err != nil {
				return nil, fmt.Errorf("it.Next: %v", err)
			}
			if result.GetDiscovery() != nil {
				discoveryOccurrence = result
			}
		}
	}

	// Wait for the discovery occurrence to enter a terminal state.
	for {
		select {
		case <-ctx.Done():
			return nil, fmt.Errorf("timeout waiting for terminal state")
		case <-ticker.C:
			// Update the occurrence.
			req := &grafeaspb.GetOccurrenceRequest{Name: discoveryOccurrence.GetName()}
			updated, err := client.GetGrafeasClient().GetOccurrence(ctx, req)
			if err != nil {
				return nil, fmt.Errorf("GetOccurrence: %v", err)
			}
			switch updated.GetDiscovery().GetAnalysisStatus() {
			case grafeaspb.DiscoveryOccurrence_FINISHED_SUCCESS,
				grafeaspb.DiscoveryOccurrence_FINISHED_FAILED,
				grafeaspb.DiscoveryOccurrence_FINISHED_UNSUPPORTED:
				return discoveryOccurrence, nil
			}
		}
	}
}

Node.js

如需了解如何安装和使用 Container Registry 客户端库,请参阅 Container Registry 客户端库。 如需了解详情,请参阅 Container Registry Node.js API 参考文档

/**
 * TODO(developer): Uncomment these variables before running the sample
 */
// const projectId = 'your-project-id', // Your GCP Project ID
// const imageUrl = 'https://gcr.io/my-project/my-image:123', // Image to attach metadata to
// const retries = 5 // The number of retries to listen for the new Pub/Sub messages

// Import the library and create a client
const {ContainerAnalysisClient} = require('@google-cloud/containeranalysis');
const client = new ContainerAnalysisClient();

const formattedParent = client.getGrafeasClient().projectPath(projectId);

let filter = `resourceUrl="${imageUrl}" AND noteProjectId="goog-analysis" AND noteId="PACKAGE_VULNERABILITY"`;

// Repeatedly query the Container Analysis API for the latest discovery occurrence until it is
// either in a terminal state, or the timeout value has been exceeded
const pRetry = require('p-retry');
const discoveryOccurrence = await pRetry(
  async () => {
    const [occurrences] = await client.getGrafeasClient().listOccurrences({
      parent: formattedParent,
      filter: filter,
    });
    if (occurrences.length < 0) {
      throw new Error('No occurrences found for ' + imageUrl);
    }
    return occurrences[0];
  },
  {
    retries: retries,
  }
);

// Wait for discovery occurrence to enter a terminal state or the timeout value has been exceeded
const finishedOccurrence = await pRetry(
  async () => {
    let status = 'PENDING';
    const [updated] = await client.getGrafeasClient().getOccurrence({
      name: discoveryOccurrence.name,
    });
    status = updated.discovery.analysisStatus;
    if (
      status !== 'FINISHED_SUCCESS' &&
      status !== 'FINISHED_FAILED' &&
      status !== 'FINISHED_UNSUPPORTED'
    ) {
      throw new Error('Timeout while retrieving discovery occurrence');
    }
    return updated;
  },
  {
    retries: retries,
  }
);
console.log(
  `Found discovery occurrence ${finishedOccurrence.name}.  Status: ${finishedOccurrence.discovery.analysisStatus}`
);

Ruby

如需了解如何安装和使用 Container Registry 客户端库,请参阅 Container Registry 客户端库。 如需了解详情,请参阅 Container Registry Ruby API 参考文档

# resource_url    = "The URL of the resource associated with the occurrence."
#                   # e.g. https://gcr.io/project/image@sha256:123
# timeout_seconds = "The number of seconds to wait for the discovery occurrence"
# project_id      = "Your Google Cloud project ID"

require "google/cloud/container_analysis"

deadline = Time.now + timeout_seconds

# Initialize the client
client = Google::Cloud::ContainerAnalysis.container_analysis.grafeas_client
parent = client.project_path project: project_id

# Find the discovery occurrence using a filter string
discovery_occurrence = nil
while discovery_occurrence.nil?
  begin
    filter = "resourceUrl=\"#{resource_url}\" " \
             'AND noteProjectId="goog-analysis" ' \
             'AND noteId="PACKAGE_VULNERABILITY"'
    # Only the discovery occurrence should be returned for the given filter
    discovery_occurrence = client.list_occurrences(parent: parent, filter: filter).first
  rescue StandardError # If there is an error, keep trying until the deadline
    puts "discovery occurrence not yet found"
  ensure
    # check for timeout
    sleep 1
    raise "Timeout while retrieving discovery occurrence." if Time.now > deadline
  end
end

# Wait for the discovery occurrence to enter a terminal state
status = Grafeas::V1::DiscoveryOccurrence::AnalysisStatus::PENDING
until [:FINISHED_SUCCESS, :FINISHED_FAILED, :FINISHED_UNSUPPORTED].include? status
  # Update occurrence
  begin
    updated = client.get_occurrence name: discovery_occurrence.name
    status = updated.discovery.analysis_status
  rescue StandardError # If there is an error, keep trying until the deadline
    puts "discovery occurrence not yet in terminal state"
  ensure
    # check for timeout
    sleep 1
    raise "Timeout while retrieving discovery occurrence." if Time.now > deadline
  end
end
puts "Found discovery occurrence #{updated.name}."
puts "Status: #{updated.discovery.analysis_status}"

Python

如需了解如何安装和使用 Container Registry 客户端库,请参阅 Container Registry 客户端库。 如需了解详情,请参阅 Container Registry Python API 参考文档

def poll_discovery_finished(resource_url, timeout_seconds, project_id):
    """Returns the discovery occurrence for a resource once it reaches a
    terminal state."""
    # resource_url = 'https://gcr.io/my-project/my-image@sha256:123'
    # timeout_seconds = 20
    # project_id = 'my-gcp-project'

    import time
    from grafeas.grafeas_v1.gapic.enums import DiscoveryOccurrence
    from google.cloud.devtools import containeranalysis_v1

    deadline = time.time() + timeout_seconds

    client = containeranalysis_v1.ContainerAnalysisClient()
    grafeas_client = client.get_grafeas_client()
    project_name = grafeas_client.project_path(project_id)

    discovery_occurrence = None
    while discovery_occurrence is None:
        time.sleep(1)
        filter_str = 'resourceUrl="{}" \
                      AND noteProjectId="goog-analysis" \
                      AND noteId="PACKAGE_VULNERABILITY"'.format(resource_url)
        result = grafeas_client.list_occurrences(project_name, filter_str)
        # only one occurrence should ever be returned by ListOccurrences
        # and the given filter
        for item in result:
            discovery_occurrence = item
        if time.time() > deadline:
            raise RuntimeError('timeout while retrieving discovery occurrence')

    status = DiscoveryOccurrence.AnalysisStatus.PENDING
    while status != DiscoveryOccurrence.AnalysisStatus.FINISHED_UNSUPPORTED \
            and status != DiscoveryOccurrence.AnalysisStatus.FINISHED_FAILED \
            and status != DiscoveryOccurrence.AnalysisStatus.FINISHED_SUCCESS:
        time.sleep(1)
        updated = grafeas_client.get_occurrence(discovery_occurrence.name)
        status = updated.discovery.analysis_status
        if time.time() > deadline:
            raise RuntimeError('timeout while waiting for terminal state')
    return discovery_occurrence

当扫描状态为 FINISHED_SUCCESS 时,您就可以查看漏洞列表了。

列出与映像关联的所有漏洞

已知漏洞以备注形式存储。Container Analysis 会为在分析映像期间找到的备注的每个实例创建一个发生实例。Container Analysis 收集有关漏洞的新信息时会自动更新发生实例列表。

您可以使用 GCP Console、gcloud 命令行工具或 Container Analysis API 检索备注和发生实例的相关信息。此外,API 还支持许多过滤条件表达式,用于检索有关备注或发生实例的特定信息。

以下代码示例检索为映像找到的漏洞。

Java

如需了解如何安装和使用 Container Registry 客户端库,请参阅 Container Registry 客户端库。 如需了解详情,请参阅 Container Registry Java API 参考文档

import com.google.cloud.devtools.containeranalysis.v1.ContainerAnalysisClient;
import io.grafeas.v1.GrafeasClient;
import io.grafeas.v1.Occurrence;
import io.grafeas.v1.ProjectName;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;

public class VulnerabilityOccurrencesForImage {
  // Retrieve a list of vulnerability occurrences assoviated with a resource
  public static List<Occurrence> findVulnerabilityOccurrencesForImage(String resourceUrl,
      String projectId) throws IOException {
    // String resourceUrl = "https://gcr.io/project/image@sha256:123";
    // String projectId = "my-project-id";
    final String projectName = ProjectName.format(projectId);
    String filterStr = String.format("kind=\"VULNERABILITY\" AND resourceUrl=\"%s\"", resourceUrl);

    // Initialize client that will be used to send requests. After completing all of your requests,
    // call the "close" method on the client to safely clean up any remaining background resources.
    GrafeasClient client = ContainerAnalysisClient.create().getGrafeasClient();
    LinkedList<Occurrence> vulnerabilitylist = new LinkedList<Occurrence>();
    for (Occurrence o : client.listOccurrences(projectName, filterStr).iterateAll()) {
      vulnerabilitylist.add(o);
    }
    return vulnerabilitylist;
  }
}

Go

如需了解如何安装和使用 Container Registry 客户端库,请参阅 Container Registry 客户端库。 如需了解详情,请参阅 Container Registry Go API 参考文档


import (
	"context"
	"fmt"

	containeranalysis "cloud.google.com/go/containeranalysis/apiv1"
	"google.golang.org/api/iterator"
	grafeaspb "google.golang.org/genproto/googleapis/grafeas/v1"
)

// findVulnerabilityOccurrencesForImage retrieves all vulnerability Occurrences associated with a resource.
func findVulnerabilityOccurrencesForImage(resourceURL, projectID string) ([]*grafeaspb.Occurrence, error) {
	// resourceURL := fmt.Sprintf("https://gcr.io/my-project/my-image")
	ctx := context.Background()
	client, err := containeranalysis.NewClient(ctx)
	if err != nil {
		return nil, fmt.Errorf("NewClient: %v", err)
	}
	defer client.Close()

	req := &grafeaspb.ListOccurrencesRequest{
		Parent: fmt.Sprintf("projects/%s", projectID),
		Filter: fmt.Sprintf("resourceUrl = %q kind = %q", resourceURL, "VULNERABILITY"),
	}

	var occurrenceList []*grafeaspb.Occurrence
	it := client.GetGrafeasClient().ListOccurrences(ctx, req)
	for {
		occ, err := it.Next()
		if err == iterator.Done {
			break
		}
		if err != nil {
			return nil, fmt.Errorf("occurrence iteration error: %v", err)
		}
		occurrenceList = append(occurrenceList, occ)
	}

	return occurrenceList, nil
}

Node.js

如需了解如何安装和使用 Container Registry 客户端库,请参阅 Container Registry 客户端库。 如需了解详情,请参阅 Container Registry Node.js API 参考文档

/**
 * TODO(developer): Uncomment these variables before running the sample
 */
// const projectId = 'your-project-id', // Your GCP Project ID
// const imageUrl = 'https://gcr.io/my-project/my-image:123' // Image to attach metadata to

// Import the library and create a client
const {ContainerAnalysisClient} = require('@google-cloud/containeranalysis');
const client = new ContainerAnalysisClient();

const formattedParent = client.getGrafeasClient().projectPath(projectId);

// Retrieve a list of vulnerability occurrences assoviated with a resource
const [occurrences] = await client.getGrafeasClient().listOccurrences({
  parent: formattedParent,
  filter: `kind = "VULNERABILITY" AND resourceUrl = "${imageUrl}"`,
});

if (occurrences.length) {
  console.log(`All Vulnerabilities for ${imageUrl}`);
  occurrences.forEach(occurrence => {
    console.log(`${occurrence.name}:`);
  });
} else {
  console.log('No occurrences found.');
}

Ruby

如需了解如何安装和使用 Container Registry 客户端库,请参阅 Container Registry 客户端库。 如需了解详情,请参阅 Container Registry Ruby API 参考文档

# resource_url = "The URL of the resource associated with the occurrence
#                e.g. https://gcr.io/project/image@sha256:123"
# project_id   = "The Google Cloud project ID of the vulnerabilities to find"

require "google/cloud/container_analysis"

# Initialize the client
client = Google::Cloud::ContainerAnalysis.container_analysis.grafeas_client

parent = client.project_path project: project_id
filter = "resourceUrl = \"#{resource_url}\" AND kind = \"VULNERABILITY\""
client.list_occurrences parent: parent, filter: filter

Python

如需了解如何安装和使用 Container Registry 客户端库,请参阅 Container Registry 客户端库。 如需了解详情,请参阅 Container Registry Python API 参考文档

def find_vulnerabilities_for_image(resource_url, project_id):
    """"Retrieves all vulnerability occurrences associated with a resource."""
    # resource_url = 'https://gcr.io/my-project/my-image@sha256:123'
    # project_id = 'my-gcp-project'

    from google.cloud.devtools import containeranalysis_v1

    client = containeranalysis_v1.ContainerAnalysisClient()
    grafeas_client = client.get_grafeas_client()
    project_name = grafeas_client.project_path(project_id)

    filter_str = 'kind="VULNERABILITY" AND resourceUrl="{}"'\
        .format(resource_url)
    return list(grafeas_client.list_occurrences(project_name, filter_str))

列出高严重级别漏洞

每个漏洞都有关联的严重级别。Container Analysis 使用由受支持的 Linux 发行版提供的严重级别信息。 如果特定于发行版的严重级别不可用,则根据 CVSS 评分指定严重级别。

如需确定修复漏洞的方案的优先级,您可以优化漏洞列表,使之仅包括高严重级别的漏洞。

以下代码示例检索严重级别指定为“高”或“严重”的映像漏洞列表。

Java

如需了解如何安装和使用 Container Registry 客户端库,请参阅 Container Registry 客户端库。 如需了解详情,请参阅 Container Registry Java API 参考文档

import com.google.cloud.devtools.containeranalysis.v1.ContainerAnalysisClient;
import io.grafeas.v1.GrafeasClient;
import io.grafeas.v1.Occurrence;
import io.grafeas.v1.ProjectName;
import io.grafeas.v1.Severity;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;

public class HighVulnerabilitiesForImage {
  // Retrieve a list of vulnerability occurrences with a severity level of 'HIGH' or greater
  public static List<Occurrence> findHighSeverityVulnerabilitiesForImage(String resourceUrl,
      String projectId) throws IOException {
    // String resourceUrl = "https://gcr.io/project/image@sha256:123";
    // String projectId = "my-project-id";
    final String projectName = ProjectName.format(projectId);
    String filterStr = String.format("kind=\"VULNERABILITY\" AND resourceUrl=\"%s\"", resourceUrl);

    // Initialize client that will be used to send requests. After completing all of your requests,
    // call the "close" method on the client to safely clean up any remaining background resources.
    GrafeasClient client = ContainerAnalysisClient.create().getGrafeasClient();
    LinkedList<Occurrence> vulnerabilitylist = new LinkedList<Occurrence>();
    for (Occurrence o : client.listOccurrences(projectName, filterStr).iterateAll()) {
      Severity severity = o.getVulnerability().getEffectiveSeverity();
      if (severity == Severity.HIGH || severity == Severity.CRITICAL) {
        vulnerabilitylist.add(o);
      }
    }
    return vulnerabilitylist;
  }
}

Go

如需了解如何安装和使用 Container Registry 客户端库,请参阅 Container Registry 客户端库。 如需了解详情,请参阅 Container Registry Go API 参考文档


import (
	"context"
	"fmt"

	containeranalysis "cloud.google.com/go/containeranalysis/apiv1"
	"google.golang.org/api/iterator"
	grafeaspb "google.golang.org/genproto/googleapis/grafeas/v1"
)

// findHighSeverityVulnerabilitiesForImage retrieves a list of only high vulnerability occurrences associated with a resource.
func findHighSeverityVulnerabilitiesForImage(resourceURL, projectID string) ([]*grafeaspb.Occurrence, error) {
	// resourceURL := fmt.Sprintf("https://gcr.io/my-project/my-image")
	ctx := context.Background()
	client, err := containeranalysis.NewClient(ctx)
	if err != nil {
		return nil, fmt.Errorf("NewClient: %v", err)
	}
	defer client.Close()

	req := &grafeaspb.ListOccurrencesRequest{
		Parent: fmt.Sprintf("projects/%s", projectID),
		Filter: fmt.Sprintf("resourceUrl = %q kind = %q", resourceURL, "VULNERABILITY"),
	}

	var occurrenceList []*grafeaspb.Occurrence
	it := client.GetGrafeasClient().ListOccurrences(ctx, req)
	for {
		occ, err := it.Next()
		if err == iterator.Done {
			break
		}
		if err != nil {
			return nil, fmt.Errorf("occurrence iteration error: %v", err)
		}

		severityLevel := occ.GetVulnerability().GetEffectiveSeverity()
		if severityLevel == grafeaspb.Severity_HIGH || severityLevel == grafeaspb.Severity_CRITICAL {
			occurrenceList = append(occurrenceList, occ)
		}
	}

	return occurrenceList, nil
}

Node.js

如需了解如何安装和使用 Container Registry 客户端库,请参阅 Container Registry 客户端库。 如需了解详情,请参阅 Container Registry Node.js API 参考文档

/**
 * TODO(developer): Uncomment these variables before running the sample
 */
// const projectId = 'your-project-id', // Your GCP Project ID
// const imageUrl = 'https://gcr.io/my-project/my-image:123' // Image to attach metadata to

// Import the library and create a client
const {ContainerAnalysisClient} = require('@google-cloud/containeranalysis');
const client = new ContainerAnalysisClient();

const formattedParent = client.getGrafeasClient().projectPath(projectId);

// Retrieve a list of vulnerability occurrences with a severity level of 'HIGH' or greater
const [occurrences] = await client.getGrafeasClient().listOccurrences({
  parent: formattedParent,
  filter: `kind = "VULNERABILITY" AND resourceUrl = "${imageUrl}"`,
});

if (occurrences.length) {
  console.log(`High Severity Vulnerabilities for ${imageUrl}`);
  occurrences.forEach(occurrence => {
    if (
      occurrence.vulnerability.effective_severity === 'HIGH' ||
      occurrence.vulnerability.effective_severity === 'CRITICAL'
    ) {
      console.log(`${occurrence.name}:`);
    }
  });
} else {
  console.log('No occurrences found.');
}

Ruby

如需了解如何安装和使用 Container Registry 客户端库,请参阅 Container Registry 客户端库。 如需了解详情,请参阅 Container Registry Ruby API 参考文档

# resource_url = "The URL of the resource associated with the occurrence,
#                 e.g. https://gcr.io/project/image@sha256:123"
# project_id   = "The Google Cloud project ID of the vulnerabilities to find"

require "google/cloud/container_analysis"

# Initialize the client
client = Google::Cloud::ContainerAnalysis.container_analysis.grafeas_client

parent = client.project_path project: project_id
filter = "resourceUrl = \"#{resource_url}\" AND kind = \"VULNERABILITY\""
vulnerability_list = client.list_occurrences parent: parent, filter: filter
# Filter the list to include only "high" and "critical" vulnerabilities
vulnerability_list.select do |item|
  [:HIGH, :CRITICAL].include? item.vulnerability.effective_severity
end

Python

如需了解如何安装和使用 Container Registry 客户端库,请参阅 Container Registry 客户端库。 如需了解详情,请参阅 Container Registry Python API 参考文档

def find_high_severity_vulnerabilities_for_image(resource_url, project_id):
    """Retrieves a list of only high vulnerability occurrences associated
    with a resource."""
    # resource_url = 'https://gcr.io/my-project/my-image@sha256:123'
    # project_id = 'my-gcp-project'

    from grafeas.grafeas_v1.gapic.enums import Severity
    from google.cloud.devtools import containeranalysis_v1

    client = containeranalysis_v1.ContainerAnalysisClient()
    grafeas_client = client.get_grafeas_client()
    project_name = grafeas_client.project_path(project_id)

    filter_str = 'kind="VULNERABILITY" AND resourceUrl="{}"'\
        .format(resource_url)
    vulnerabilities = grafeas_client.list_occurrences(project_name, filter_str)
    filtered_list = []
    for v in vulnerabilities:
        if v.effective_severity == Severity.HIGH or v.effective_severity == Severity.CRITICAL:
            filtered_list.append(v)
    return filtered_list

清理

为避免系统因本教程中使用的资源向您的 Google Cloud Platform 帐号收取费用,请执行以下操作:

删除映像

如果您只想移除已添加的 Docker 映像,请运行以下命令,从 Container Registry 中删除 Docker 映像。

gcloud container images delete gcr.io/[PROJECT-ID]/vulnerabilities-tutorial-image:tag1 --force-delete-tags

其中,[PROJECT-ID] 是您的 Google Cloud Console 项目 ID。如果您的项目 ID 包含英文冒号 (:),请参阅网域级项目

删除项目

为了避免产生费用,最简单的方法是删除您为本教程创建的项目。

如需删除项目,请执行以下操作:

  1. 在 Cloud Console 中,转到管理资源页面。

    转到“管理资源”页面

  2. 在项目列表中,选择要删除的项目,然后点击删除
  3. 在对话框中输入项目 ID,然后点击关闭以删除项目。

后续步骤