使用 VEX 创建例外

漏洞可利用性交换 (VEX) 是一种用于记录软件包或产品环境中漏洞的标准格式。Docker Scout 支持 VEX 文档,用于为镜像中的漏洞创建例外

注意

您也可以使用 Docker Scout 仪表盘或 Docker Desktop 创建例外。GUI 提供了一个用户友好的界面来创建例外,并且可以轻松管理多个镜像的例外。它还允许您一次性为多个镜像或整个组织创建例外。更多信息,请参见 使用 GUI 创建例外

先决条件

要使用 OpenVEX 文档创建例外,您需要

  • 最新版本的 Docker Desktop 或 Docker Scout CLI 插件
  • vexctl 命令行工具。
  • 必须启用 containerd 镜像存储
  • 对存储镜像的注册表仓库具有写入权限

VEX 简介

VEX 标准由美国网络安全与基础设施安全局 (CISA) 的工作组定义。VEX 的核心是可利用性评估。这些评估描述了给定 CVE 对产品的状态。VEX 中可能的漏洞状态为:

  • 不受影响:不需要针对此漏洞进行任何补救。
  • 受影响:建议采取措施来补救或解决此漏洞。
  • 已修复:这些产品版本包含此漏洞的修复程序。
  • 正在调查:目前尚不清楚这些产品版本是否受此漏洞的影响。将在以后的版本中提供更新。

VEX 的实现和格式多种多样。Docker Scout 支持 OpenVex 实现。无论具体的实现方式如何,核心思想都是相同的:提供一个描述漏洞影响的框架。 无论实现如何,VEX 的关键组件都包括:

VEX 文档
一种用于存储 VEX 语句的安全公告。文档的格式取决于具体的实现。
VEX 语句
描述产品中漏洞的状态,是否可利用,以及是否存在补救方法。
理由和影响
根据漏洞状态,语句包含一个理由或影响陈述,描述为什么产品受到或未受到影响。
操作语句
描述如何修复或减轻漏洞。

vexctl 示例

以下示例命令创建一个 VEX 文档,声明:

  • 此 VEX 文档描述的软件产品是 Docker 镜像 example/app:v1
  • 该镜像包含 npm 包 [email protected]
  • 该 npm 包受已知漏洞影响:CVE-2022-24999
  • 该镜像不受 CVE 影响,因为易受攻击的代码从未在运行此镜像的容器中执行。
$ vexctl create \
  --author="[email protected]" \
  --product="pkg:docker/example/app@v1" \
  --subcomponents="pkg:npm/[email protected]" \
  --vuln="CVE-2022-24999" \
  --status="not_affected" \
  --justification="vulnerable_code_not_in_execute_path" \
  --file="CVE-2022-24999.vex.json"

以下是此示例中选项的描述:

--author
VEX 文档作者的电子邮件地址。
--product
Docker 镜像的包 URL (PURL)。PURL 是镜像的标准化格式标识符,在 PURL 规范 中定义。

Docker 镜像 PURL 字符串以 pkg:docker 类型前缀开头,后跟镜像仓库和版本(镜像标签或 SHA256 哈希值)。与镜像标签不同的是,镜像标签的版本指定方式为 example/app:v1,而在 PURL 中,镜像仓库和版本由 @ 分隔。

--subcomponents
镜像中易受攻击包的 PURL。在此示例中,漏洞存在于 npm 包中,因此 --subcomponents PURL 是 npm 包名称和版本的标识符 (pkg:npm/[email protected])。

如果相同的漏洞存在于多个包中,vexctl 允许您为单个 create 命令多次指定 --subcomponents 标志。

您也可以省略 --subcomponents,在这种情况下,VEX 语句适用于整个镜像。

--vuln
VEX 语句处理的 CVE ID。
--status
这是漏洞的状态标签。它描述了软件 (--product) 和 CVE (--vuln) 之间的关系。OpenVEX 中状态标签的可能值为:
  • not_affected
  • affected
  • fixed
  • under_investigation

在此示例中,VEX 语句断言 Docker 镜像不受漏洞影响。not_affected 状态是唯一导致 CVE 抑制的状态,其中 CVE 会从分析结果中过滤掉。其他状态对文档目的有用,但不能用于创建例外。有关所有可能状态标签的更多信息,请参阅 状态标签 OpenVEX 规范。

--justification
证明 not_affected 状态标签,说明为什么产品不受漏洞影响。在本例中,给出的理由是 vulnerable_code_not_in_execute_path,表示产品使用的漏洞无法执行。

在 OpenVEX 中,状态理由可以具有五个可能的值:

  • component_not_present
  • vulnerable_code_not_present
  • vulnerable_code_not_in_execute_path
  • vulnerable_code_cannot_be_controlled_by_adversary
  • inline_mitigations_already_exist

有关这些值及其定义的更多信息,请参阅 状态理由 OpenVEX 规范。

--file
VEX 文档输出的文件名。

示例 JSON 文档

以下是此命令生成的 OpenVEX JSON:

{
  "@context": "https://openvex.dev/ns/v0.2.0",
  "@id": "https://openvex.dev/docs/public/vex-749f79b50f5f2f0f07747c2de9f1239b37c2bda663579f87a35e5f0fdfc13de5",
  "author": "[email protected]",
  "timestamp": "2024-05-27T13:20:22.395824+02:00",
  "version": 1,
  "statements": [
    {
      "vulnerability": {
        "name": "CVE-2022-24999"
      },
      "timestamp": "2024-05-27T13:20:22.395829+02:00",
      "products": [
        {
          "@id": "pkg:docker/example/app@v1",
          "subcomponents": [
            {
              "@id": "pkg:npm/[email protected]"
            }
          ]
        }
      ],
      "status": "not_affected",
      "justification": "vulnerable_code_not_in_execute_path"
    }
  ]
}

理解 VEX 文档的结构可能有点困难。 OpenVEX 规范 描述了文档和语句的格式和所有可能的属性。有关完整详细信息,请参阅规范,以了解有关可用字段以及如何创建格式正确的 OpenVEX 文档的更多信息。

要了解有关 vexctl CLI 工具的可用标志和语法以及如何安装它的更多信息,请参阅 vexctl GitHub 仓库

验证 VEX 文档

要测试您创建的 VEX 文档是否格式正确并产生预期结果,请使用带有 --vex-location 标志的 docker scout cves 命令,将 VEX 文档应用于使用 CLI 的本地镜像分析。

以下命令调用本地镜像分析,该分析包含指定位置中的所有 VEX 文档,使用 --vex-location 标志。在此示例中,CLI 指令是在当前工作目录中查找 VEX 文档。

$ docker scout cves <IMAGE> --vex-location .

docker scout cves 命令的输出显示结果,其中 --vex-location 位置中找到的任何 VEX 语句都已计入结果。例如,分配状态为 not_affected 的 CVE 会从结果中过滤掉。如果输出似乎没有考虑 VEX 语句,则表示 VEX 文档可能以某种方式无效。

需要注意的事项包括:

  • Docker 镜像的 PURL 必须以 pkg:docker/ 开头,后跟镜像名称。
  • 在 Docker 镜像 PURL 中,镜像名称和版本由 @ 分隔。名为 example/myapp:1.0 的镜像具有以下 PURL:pkg:docker/example/[email protected]
  • 记住指定 author(它是 OpenVEX 中的必填字段)。
  • OpenVEX 规范 描述了如何在 VEX 文档中使用 justificationimpact_statement 和其他字段。不正确地指定这些字段会导致文档无效。确保您的 VEX 文档符合 OpenVEX 规范。

将 VEX 文档附加到镜像

创建 VEX 文档后,您可以通过以下方式将其附加到您的镜像:

一旦 VEX 文档添加到镜像中,就无法将其删除。对于作为声明附加的文档,您可以创建一个新的 VEX 文档并再次将其附加到镜像。这样做会覆盖之前的 VEX 文档(但不会删除声明)。对于将 VEX 文档嵌入到镜像文件系统的镜像,需要重新构建镜像才能更改 VEX 文档。

证明

要将 VEX 文档作为声明附加,可以使用 docker scout attestation add CLI 命令。使用声明是使用 VEX 将异常附加到镜像的首选方法。

您可以将声明附加到已推送到注册表的镜像。无需再次构建或推送镜像。此外,将异常作为声明附加到镜像意味着消费者可以直接从注册表检查镜像的异常。

要将声明附加到镜像:

  1. 构建镜像并将其推送到注册表。

    $ docker build --provenance=true --sbom=true --tag <IMAGE> --push .
    
  2. 将异常作为声明附加到镜像。

    $ docker scout attestation add \
      --file <cve-id>.vex.json \
      --predicate-type https://openvex.dev/ns/v0.2.0 \
      <IMAGE>
    

    此命令的选项为:

    • --file:VEX 文档的位置和文件名。
    • --predicate-type:OpenVEX 的 in-toto predicateType

镜像文件系统

如果您提前知道异常(在构建镜像之前),则将 VEX 文档直接嵌入到镜像文件系统中是一个不错的选择。它相对容易;只需在您的 Dockerfile 中将 VEX 文档 COPY 到镜像即可。

这种方法的缺点是以后无法更改或更新异常。镜像层是不可变的,因此您放入镜像文件系统中的任何内容都会永久存在。将文档作为 声明 附加提供了更好的灵活性。

注意

对于具有声明的镜像,不会考虑嵌入在镜像文件系统中的 VEX 文档。如果您的镜像有任何声明,Docker Scout 将只在声明中查找异常,而不是在镜像文件系统中。

如果要使用图像文件系统中嵌入的 VEX 文档,必须先从图像中移除证明。请注意,图像的来源证明可能会自动添加。为确保不会向图像添加任何证明,可以在构建图像时使用--provenance=false--sbom=false 标志显式禁用 SBOM 和来源证明。

要在图像文件系统中嵌入 VEX 文档,请在图像构建过程中将文件COPY 到图像中。以下示例演示如何将构建上下文中的.vex/ 下的所有 VEX 文档复制到图像中的/var/lib/db

# syntax=docker/dockerfile:1

FROM alpine
COPY .vex/* /var/lib/db/

VEX 文档的文件名必须与*.vex.json通配符模式匹配。在图像文件系统的哪个位置存储文件无关紧要。

请注意,复制的文件必须是最终图像文件系统的一部分。对于多阶段构建,文档必须保留在最终阶段。