如何检查 Docker 镜像是否存在漏洞

Posted crazy_itman

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何检查 Docker 镜像是否存在漏洞相关的知识,希望对你有一定的参考价值。

Docker 可以将应用程序及其依赖项打包到一个虚拟容器中,该容器可以在任何 Linux、Windows 或 macOS 计算机上运行。这使应用程序可以在各种位置运行,例如本地、公共(请参阅分散计算、分布式计算和云计算)或私有云。在 Linux 上运行时,Docker 使用Linux 内核的资源隔离特性(例如cgroups和内核命名空间)和支持联合的文件系统(例如OverlayFS)允许容器在单个 Linux 实例中运行,避免启动和维护虚拟机的开销。macOS上的 Docker使用 Linux虚拟机来运行容器。由于 Docker 容器是轻量级的,单个服务器或虚拟机可以同时运行多个容器。 2018 年的一项分析发现,典型的 Docker 用例涉及每台主机运行 8 个容器,四分之一的分析组织每台主机运行 18 个或更多容器。它也可以安装在像Raspberry Pi这样的单板计算机上。Linux 内核对名称空间的支持主要隔离了应用程序对操作环境的看法,包括进程树、网络、用户 ID 和已安装的文件系统,而内核的 cgroups 为内存和 CPU 提供资源限制。从 0.9 版开始,Docker 除了通过libvirt、LXC和systemd-nspawn使用抽象的虚拟化接口外,还包括自己的组件(称为“ libcontainer ”)以使用 Linux 内核直接提供的虚拟化设施。​Docker 实现了一个高级API来提供独立运行进程的轻量级容器。

定期检查pipeline中的漏洞非常重要。要执行的步骤之一是对 Docker 映像执行漏洞扫描。在本文中,您将了解如何执行漏洞扫描、如何修复漏洞以及如何将其添加到您的 Jenkins pipeline中。

一、简介

首先,为什么要检查漏洞?您必须及时了解最新的安全修复程序。许多安全漏洞是公开的所以很容易被利用。因此,必须尽快修复安全漏洞,以最大程度地减少攻击面,但是我们平时主要专注于业务,不想从事修复安全漏洞的全职工作。这就是为什么自动扫描应用程序和 Docker 镜像很重要。Grype 可以帮助扫描您的 Docker 镜像,Grype 将检查操作系统漏洞以及特定语言包(例如 Java JAR 文件)中的漏洞并报告它们。这样,您就有了一个很棒的工具,可以为您自动执行安全检查。请注意,Grype 不仅限于扫描 Docker 镜像。它还可以扫描文件和目录,因此可用于扫描您的源代码。

在本文中,我们将创建一个包含 Spring Boot 应用程序的易受攻击的 Docker 映像,安装并使用 Grype 以扫描Docker镜像并修复漏洞。最后,我将介绍如何将扫描添加到您的 Jenkins pipeline中。

二、先决条件

  • 基本的 Linux 知识

  • 基本的 Docker 知识

  • 基本的 Java 和 Spring Boot 知识

三、易受攻击的应用程序

找到Spring Initializr并选择 Maven 构建、Java 17、Spring Boot 2.7.6 和 Spring Web 依赖项。这不会是一个非常脆弱的应用程序,因为 Spring 已经确保您使用最新的 Spring Boot 版本。因此,将 Spring Boot 版本更改为 2.7.0。可以使用以下命令构建 Spring Boot 应用程序,该命令将为您创建 jar 文件:

$ mvn clean verify

我们接着扫描一个 Docker 镜像,因此需要创建一个 Dockerfile。您将使用一个非常基本的 Dockerfile,它只包含创建镜像所需的最少指令。

FROM eclipse-temurin:17.0.1_12-jre-alpineWORKDIR /opt/appARG JAR_FILECOPY target/$JAR_FILE app.jarENTRYPOINT ["java", "-jar", "app.jar"]

为了构建 Docker 镜像,dockerfile-maven-plugin将使用 Spotify 的一个分支。因此,将以下片段添加到pom文件中。

<plugin>  <groupId>com.xenoamess.docker</groupId>  <artifactId>dockerfile-maven-plugin</artifactId>  <version>1.4.25</version>  <configuration>    <repository>mydeveloperplanet/mygrypeplanet</repository>    <tag>$project.version</tag>    <buildArgs>      <JAR_FILE>$project.build.finalName.jar</JAR_FILE>    </buildArgs>  </configuration></plugin>

使用此插件的好处是您可以轻松地重用配置。可以通过单个 Maven 命令创建 Docker 映像​,如下所示:

$ mvn dockerfile:build

我们现在已经准备好开始使用 grype。

四、安装

可以通过执行以下脚本来安装 grype:

$ curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sh -s -- -b /usr/local/bin

通过执行以下命令验证安装:

$ grype versionApplication:          grypeVersion:              0.54.0Syft Version:         v0.63.0BuildDate:            2022-12-13T15:02:51ZGitCommit:            93499eec7e3ce2704755e9f51457181b06b519c5GitDescription:       v0.54.0Platform:             linux/amd64GoVersion:            go1.18.8Compiler:             gcSupported DB Schema:  5

五、扫描镜像

扫描 Docker 镜像是通过调用grype后跟上docker:来完成的,表示您要扫描来自 Docker 守护程序的镜像、镜像和标签:

$ grype docker:mydeveloperplanet/mygrypeplanet:0.0.1-SNAPSHOTApplication:          grypeVersion:              0.54.0Syft Version:         v0.63.0 Vulnerability DB        [updated] Loaded image             Parsed image             Cataloged packages      [50 packages] Scanned image           [42 vulnerabilities]NAME              INSTALLED  FIXED-IN   TYPE          VULNERABILITY        SEVERITY busybox           1.34.1-r3  1.34.1-r5  apk           CVE-2022-28391       High      jackson-databind  2.13.3                java-archive  CVE-2022-42003       High      jackson-databind  2.13.3                java-archive  CVE-2022-42004       High      jackson-databind  2.13.3     2.13.4     java-archive  GHSA-rgv9-q543-rqg4  High      jackson-databind  2.13.3     2.13.4.1   java-archive  GHSA-jjjh-jjxp-wpff  High      java              17.0.1+12             binary        CVE-2022-21248       Low       java              17.0.1+12             binary        CVE-2022-21277       Medium    java              17.0.1+12             binary        CVE-2022-21282       Medium    java              17.0.1+12             binary        CVE-2022-21283       Medium    java              17.0.1+12             binary        CVE-2022-21291       Medium    java              17.0.1+12             binary        CVE-2022-21293       Medium    java              17.0.1+12             binary        CVE-2022-21294       Medium    java              17.0.1+12             binary        CVE-2022-21296       Medium    java              17.0.1+12             binary        CVE-2022-21299       Medium    java              17.0.1+12             binary        CVE-2022-21305       Medium    java              17.0.1+12             binary        CVE-2022-21340       Medium    java              17.0.1+12             binary        CVE-2022-21341       Medium    java              17.0.1+12             binary        CVE-2022-21360       Medium    java              17.0.1+12             binary        CVE-2022-21365       Medium    java              17.0.1+12             binary        CVE-2022-21366       Medium    libcrypto1.1      1.1.1l-r7             apk           CVE-2021-4160        Medium    libcrypto1.1      1.1.1l-r7  1.1.1n-r0  apk           CVE-2022-0778        High      libcrypto1.1      1.1.1l-r7  1.1.1q-r0  apk           CVE-2022-2097        Medium    libretls          3.3.4-r2   3.3.4-r3   apk           CVE-2022-0778        High      libssl1.1         1.1.1l-r7             apk           CVE-2021-4160        Medium    libssl1.1         1.1.1l-r7  1.1.1n-r0  apk           CVE-2022-0778        High      libssl1.1         1.1.1l-r7  1.1.1q-r0  apk           CVE-2022-2097        Medium    snakeyaml         1.30                  java-archive  GHSA-mjmj-j48q-9wg2  High      snakeyaml         1.30       1.31       java-archive  GHSA-3mc7-4q67-w48m  High      snakeyaml         1.30       1.31       java-archive  GHSA-98wm-3w3q-mw94  Medium    snakeyaml         1.30       1.31       java-archive  GHSA-c4r9-r8fh-9vj2  Medium    snakeyaml         1.30       1.31       java-archive  GHSA-hhhw-99gj-p3c3  Medium    snakeyaml         1.30       1.32       java-archive  GHSA-9w3m-gqgf-c4p9  Medium    snakeyaml         1.30       1.32       java-archive  GHSA-w37g-rhq8-7m4j  Medium    spring-core       5.3.20                java-archive  CVE-2016-1000027     Critical  ssl_client        1.34.1-r3  1.34.1-r5  apk           CVE-2022-28391       High      zlib              1.2.11-r3  1.2.12-r0  apk           CVE-2018-25032       High      zlib              1.2.11-r3  1.2.12-r2  apk           CVE-2022-37434       Critical 

这个输出告诉我们如下信息:

  • NAME: 易受攻击包的名称

  • INSTALLED: 安装哪个版本

  • FIXED-IN: 在哪个版本修复了漏洞

  • TYPE:依赖类型,例如,JDK 的二进制等。

  • VULNERABILITY: 漏洞的标识符;使用此标识符,您可以获得有关 CVE 数据库中漏洞的更多信息

  • SEVERITY:不言而喻,可以忽略不计、低、中、高或严重。

当您仔细查看输出时,您会注意到并非每个漏洞都有已确认的修复。那么在这种情况下你会怎么做?Grype 提供了一个选项,以便仅显示已确认修复的漏洞。添加--only-fixed标志就可以了。

$ grype docker:mydeveloperplanet/mygrypeplanet:0.0.1-SNAPSHOT --only-fixed Vulnerability DB        [no update available] Loaded image             Parsed image             Cataloged packages      [50 packages] Scanned image           [42 vulnerabilities] NAME              INSTALLED  FIXED-IN   TYPE          VULNERABILITY        SEVERITY busybox           1.34.1-r3  1.34.1-r5  apk           CVE-2022-28391       High      jackson-databind  2.13.3     2.13.4     java-archive  GHSA-rgv9-q543-rqg4  High      jackson-databind  2.13.3     2.13.4.1   java-archive  GHSA-jjjh-jjxp-wpff  High      libcrypto1.1      1.1.1l-r7  1.1.1n-r0  apk           CVE-2022-0778        High      libcrypto1.1      1.1.1l-r7  1.1.1q-r0  apk           CVE-2022-2097        Medium    libretls          3.3.4-r2   3.3.4-r3   apk           CVE-2022-0778        High      libssl1.1         1.1.1l-r7  1.1.1n-r0  apk           CVE-2022-0778        High      libssl1.1         1.1.1l-r7  1.1.1q-r0  apk           CVE-2022-2097        Medium    snakeyaml         1.30       1.31       java-archive  GHSA-3mc7-4q67-w48m  High      snakeyaml         1.30       1.31       java-archive  GHSA-98wm-3w3q-mw94  Medium    snakeyaml         1.30       1.31       java-archive  GHSA-c4r9-r8fh-9vj2  Medium    snakeyaml         1.30       1.31       java-archive  GHSA-hhhw-99gj-p3c3  Medium    snakeyaml         1.30       1.32       java-archive  GHSA-9w3m-gqgf-c4p9  Medium    snakeyaml         1.30       1.32       java-archive  GHSA-w37g-rhq8-7m4j  Medium    ssl_client        1.34.1-r3  1.34.1-r5  apk           CVE-2022-28391       High      zlib              1.2.11-r3  1.2.12-r0  apk           CVE-2018-25032       High      zlib              1.2.11-r3  1.2.12-r2  apk           CVE-2022-37434       Critical 

六、修复漏洞

在这种情况下,修复漏洞非常容易。首先,您需要更新 Docker 基础映像。更改 Docker 镜像中的第一行:

FROM eclipse-temurin:17.0.1_12-jre-alpine

进入:

FROM eclipse-temurin:17.0.5_8-jre-alpine

构建镜像并再次运行扫描:

$ mvn dockerfile:build...$ grype docker:mydeveloperplanet/mygrypeplanet:0.0.1-SNAPSHOT --only-fixed Vulnerability DB        [no update available] Loaded image             Parsed image             Cataloged packages      [62 packages] Scanned image           [14 vulnerabilities]NAME              INSTALLED  FIXED-IN  TYPE          VULNERABILITY        SEVERITY jackson-databind  2.13.3     2.13.4    java-archive  GHSA-rgv9-q543-rqg4  High      jackson-databind  2.13.3     2.13.4.1  java-archive  GHSA-jjjh-jjxp-wpff  High      snakeyaml         1.30       1.31      java-archive  GHSA-3mc7-4q67-w48m  High      snakeyaml         1.30       1.31      java-archive  GHSA-98wm-3w3q-mw94  Medium    snakeyaml         1.30       1.31      java-archive  GHSA-c4r9-r8fh-9vj2  Medium    snakeyaml         1.30       1.31      java-archive  GHSA-hhhw-99gj-p3c3  Medium    snakeyaml         1.30       1.32      java-archive  GHSA-9w3m-gqgf-c4p9  Medium    snakeyaml         1.30       1.32      java-archive  GHSA-w37g-rhq8-7m4j  Medium 

正如您在输出中看到的,只有java-archive漏洞仍然存在。其他漏洞已经解决。

接下来修复Spring Boot的依赖漏洞。在 POM 中将 Spring Boot 的版本从 2.7.0 更改为 2.7.6。

<parent>  <groupId>org.springframework.boot</groupId>  <artifactId>spring-boot-starter-parent</artifactId>  <version>2.7.6</version>  <relativePath/> <!-- lookup parent from repository --></parent>

构建 JAR 文件,构建 Docker 镜像,然后再次运行扫描:

$ mvn clean verify...$ mvn dockerfile:build...$ grype docker:mydeveloperplanet/mygrypeplanet:0.0.1-SNAPSHOT --only-fixed Vulnerability DB        [no update available] Loaded image             Parsed image             Cataloged packages      [62 packages] Scanned image           [10 vulnerabilities]NAME       INSTALLED  FIXED-IN  TYPE          VULNERABILITY        SEVERITY snakeyaml  1.30       1.31      java-archive  GHSA-3mc7-4q67-w48m  High      snakeyaml  1.30       1.31      java-archive  GHSA-98wm-3w3q-mw94  Medium    snakeyaml  1.30       1.31      java-archive  GHSA-c4r9-r8fh-9vj2  Medium    snakeyaml  1.30       1.31      java-archive  GHSA-hhhw-99gj-p3c3  Medium    snakeyaml  1.30       1.32      java-archive  GHSA-9w3m-gqgf-c4p9  Medium    snakeyaml  1.30       1.32      java-archive  GHSA-w37g-rhq8-7m4j  Medium 

因此,您修复了jackson-databind漏洞,但没有修复snakeyaml漏洞。那么,在哪个依赖项中snakeyaml 1.30使用了?你可以通过dependency:treeMaven命令找到。为简洁起见,此处仅显示部分输出:

$ mvnd dependency:tree... com.mydeveloperplanet:mygrypeplanet:jar:0.0.1-SNAPSHOT[INFO] +- org.springframework.boot:spring-boot-starter-web:jar:2.7.6:compile[INFO] |  +- org.springframework.boot:spring-boot-starter:jar:2.7.6:compile[INFO] ||  +- org.springframework.boot:spring-boot:jar:2.7.6:compile[INFO] ||  +- org.springframework.boot:spring-boot-autoconfigure:jar:2.7.6:compile[INFO] ||  +- org.springframework.boot:spring-boot-starter-logging:jar:2.7.6:compile[INFO] ||  |  +- ch.qos.logback:logback-classic:jar:1.2.11:compile[INFO] |  |  |  |  \\- ch.qos.logback:logback-core:jar:1.2.11:compile[INFO] |  |  |  +- org.apache.logging.log4j:log4j-to-slf4j:jar:2.17.2:compile[INFO] ||  ||  \\- org.apache.logging.log4j:log4j-api:jar:2.17.2:compile[INFO] ||  |  \\- org.slf4j:jul-to-slf4j:jar:1.7.36:compile[INFO] |  |  +- jakarta.annotation:jakarta.annotation-api:jar:1.3.5:compile[INFO] |  |  \\- org.yaml:snakeyaml:jar:1.30:compile...

输出信息告诉我们依赖是spring-boot-starter-web依赖的一部分。那么,你如何解决这个问题?严格来说是Spring需要解决的。但如果你不想等待解决方案,你可以自己解决。

解决方案 1:您不需要依赖项。这是最简单的修复方法,风险很低。spring-boot-starter-web只需从pom.xml 中的依赖项中排除依赖项即可。

<dependency>  <groupId>org.springframework.boot</groupId>  <artifactId>spring-boot-starter-web</artifactId>  <exclusions>    <exclusion>      <groupId>org.yaml</groupId>      <artifactId>snakeyaml</artifactId>    </exclusion>  </exclusions></dependency>

构建 JAR 文件,构建 Docker 镜像,然后再次运行扫描:

$ mvn clean verify...$ mvn dockerfile:build...$ grype docker:mydeveloperplanet/mygrypeplanet:0.0.1-SNAPSHOT --only-fixed Vulnerability DB        [no update available] Loaded image             Parsed image             Cataloged packages      [61 packages] Scanned image           [3 vulnerabilities]No vulnerabilities found

解决方案 2:您确实需要依赖项。您可以通过dependencyManagement在 pom.xml 中替换此传递依赖项。这有点棘手,因为更新的传递依赖没有使用spring-boot-starter-web依赖进行测试。是否要这样做是一种权衡。将以下部分添加到 pom:

<dependencyManagement>  <dependencies>    <dependency>      <groupId>org.yaml</groupId>      <artifactId>snakeyaml</artifactId>      <version>1.32</version>    </dependency>  </dependencies></dependencyManagement>

构建 jar 文件,构建 Docker 镜像,然后再次运行扫描:

$ mvn clean verify...$ mvn dockerfile:build...$ grype docker:mydeveloperplanet/mygrypeplanet:0.0.1-SNAPSHOT --only-fixed Vulnerability DB        [no update available] Loaded image             Parsed image             Cataloged packages      [62 packages] Scanned image           [3 vulnerabilities]No vulnerabilities found

同样,不再存在漏洞。

解决方案 3:当您不想做任何事情或不管是否是误报通知时,可以使用本解决方案。创建一个.grype.yaml文件,您可以在其中排除高严重性的漏洞,并使用标志执行扫描,--config后跟.grype.yaml包含排除项的文件。

该.grype.yaml文件如下所示:

ignore:  - vulnerability: GHSA-3mc7-4q67-w48m

再次运行扫描:

$ grype docker:mydeveloperplanet/mygrypeplanet:0.0.1-SNAPSHOT --only-fixed Vulnerability DB        [no update available] Loaded image             Parsed image             Cataloged packages      [62 packages] Scanned image           [10 vulnerabilities]NAME       INSTALLED  FIXED-IN  TYPE          VULNERABILITY        SEVERITY snakeyaml  1.30       1.31      java-archive  GHSA-98wm-3w3q-mw94  Medium    snakeyaml  1.30       1.31      java-archive  GHSA-c4r9-r8fh-9vj2  Medium    snakeyaml  1.30       1.31      java-archive  GHSA-hhhw-99gj-p3c3  Medium    snakeyaml  1.30       1.32      java-archive  GHSA-9w3m-gqgf-c4p9  Medium    snakeyaml  1.30       1.32      java-archive  GHSA-w37g-rhq8-7m4j  Medium 

该High漏洞不再显示。

七、持续集成

现在您知道如何手动扫描 Docker 镜像了。但是,您可能希望将扫描镜像作为持续集成pipeline的一部分。本节给出使用Jenkins作为CI平台时的解决方案。

要回答的第一个问题是当发现漏洞时如何进行通知。到目前为止,您只能通过查看标准输出来注意到漏洞。这不是 CI pipeline的解决方案。你想得到通知,这可以通过构建失败来完成。Grype 有用于此目的的标志--fail-on <severity>。当发现具有严重性的negligible漏洞时,您可能不希望pipeline失败了。

让我们看看当手动执行此操作时会发生什么。首先再次介绍一下Spring Boot应用和Docker镜像中的漏洞。

构建 JAR 文件,构建 Docker 镜像并使用--fail-on标志运行扫描:

$ mvn clean verify...$ mvn dockerfile:build...$ grype docker:mydeveloperplanet/mygrypeplanet:0.0.1-SNAPSHOT --only-fixed --fail-on high...1 error occurred:        * discovered vulnerabilities at or above the severity threshold

此处并未显示所有输出,仅显示重要部分。而且,如您所见,在输出的末尾,会显示一条消息,表明扫描产生了错误。这将导致 Jenkins pipeline失败,并通知开发人员出现问题。

为了将其添加到 Jenkins pipeline中,存在多种选择。这里选择创建 Docker 镜像并从 Maven 中执行 grype Docker 扫描。grype 没有单独的 Maven 插件,但可以使用exec-maven-plugin 间接实现此目的。将以下内容添加到POM 的构建插件部分。

<build>  <plugins>    <plugin>      <groupId>org.codehaus.mojo</groupId>      <artifactId>exec-maven-plugin</artifactId>      <version>3.1.0</version>      <configuration>        <executable>grype</executable>          <arguments>            <argument>docker:mydeveloperplanet/mygrypeplanet:$project.version</argument>            <argument>--scope</argument>            <argument>all-layers</argument>            <argument>--fail-on</argument>            <argument>high</argument>            <argument>--only-fixed</argument>            <argument>-q</argument>          </arguments>      </configuration>    </plugin>  </plugins></build>

这里添加了两个额外的标志:

  • --scope all-layers:这将扫描 Docker 镜像中涉及的所有层。

  • -q:这将使用quiet的日志记录,并且只会显示漏洞和可能的故障。

可以使用以下命令进行调用:

$ mvnd exec:exec

您可以将其添加到withMaven wrapper内的 Jenkinsfile 中:

withMaven()   sh 'mvn dockerfile:build dockerfile:push exec:exec'

八、结论

在本文中,介绍了如何使用 grype 扫描 Docker 镜像。Grype 有一些有趣的、用户友好的特性,可以让你有效地将它们添加到你的 Jenkins pipeline中。此外,安装 grype 非常简单。Grype 绝对是对 Anchor Engine 的巨大改进。

以上是关于如何检查 Docker 镜像是否存在漏洞的主要内容,如果未能解决你的问题,请参考以下文章

如何检查 Docker 镜像是否存在漏洞

如何检查 Docker 镜像是否存在漏洞

如何检查本地是不是存在带有特定标签的 Docker 镜像?

Docker图像保存如何在加载前检查是不是存在?

如何检查docker镜像是否包含所有文件?

docker镜像-运行