生产中的 12 种容器镜像扫描最佳实践

作者:Pawan Shankar

翻译:Bach(才云)

校对:bot(才云)、星空下的文仔(才云)

现在很多团队面临着这么一个挑战:如何在不减慢应用交付速度的情况下,管理好安全风险。有种方法可以解决该问题,就是采用安全的 DevOps 工作流程

安全的 DevOps(也称为 DevSecOps)会在从开发到生产的整个应用程序生命周期中提供安全性以及监控功能,帮助我们交付安全、稳定和高性能的应用程序。如果我们把该工作流程插入现有的工具链中,可以为 DevOps、开发人员和安全团队最大程度地提高效率。

生产中的 12 种容器镜像扫描最佳实践

DevSecOps 五个基本工作流程包括镜像扫描、运行安全、合规性、Kubernetes 和容器的监控以及应用程序和云服务监控。

镜像扫描是嵌入到 DevSecOps 工作流程中的一项关键功能。作为第一道防线,它可以帮助我们在漏洞被利用之前检测到漏洞并阻止,另外,它还易于实现并可自动化。本文将介绍多个镜像扫描的最佳实践和技巧,帮助大家采用有效的容器镜像扫描策略。

K8sMeetup

什么是容器镜像扫描

镜像扫描是指分析容器镜像的内容和构建过程,以检测安全问题、漏洞或错误实践。

我们可以从多个 Feed(NVD、Alpine、Canonical 等)中收集“通用漏洞披露(CVE)”信息,以检查镜像是否容易受到***,其中有些还提供了开箱即用的扫描规则,以查找最常见的安全问题和错误实践。

镜像扫描可以轻松被集成到 DevSecOps 工作流程的多个步骤中,例如集成到 CI/CD 管道中,阻止漏洞到达注册表;集成在注册表中,防止第三方镜像中的漏洞;也可以在运行时集成,防止新发现的 CVE。

当我们遵循并执行最佳实践时,镜像扫描可确保团队不会因部署应用程序而导致交付速度变慢。现在让我们深入了解这 12 种镜像扫描最佳实践。

K8sMeetup

12 种镜像扫描最佳实践

1.将镜像扫描嵌入到 CI/CD 管道中

构建容器镜像时,我们应格外小心,并在镜像发布之前对其进行扫描。我们可以在 DevOps 工作流程中使用已经构建好的 CI/CD 管道,并增加镜像扫描的步骤。

CI/CD 管道上镜像扫描的架构如下:

生产中的 12 种容器镜像扫描最佳实践

测试和构建镜像后,我们不必将镜像推送到生产库,可以先推送到暂存库,然后运行镜像扫描工具。这些工具通常会返回一份报告,列出发现的问题,并为每个问题分配不同级别的严重性。在 CI/CD 管道中,我们可以检查这些镜像扫描结果,并在出现任何严重问题时停止构建。

有一点要记住,自动化是最关键的,这是 DevOps 的核心概念,也适用于保护 DevOps。通过自动化 CI/CD 管道中的安全性检查,我们可以在漏洞进入注册表之前就将其捕获,这样就不会给漏洞任何机会。

2.采用 inline 扫描以保护隐私

在上一步中,我们看到了 CI/CD 管道中的镜像扫描与临时注册表的关系,但如果镜像包含一些错误凭证该怎么办?它们可能会出现错误,最终导致数据泄漏。所以更进一步,我们可以实施 inline 镜像扫描,不使用暂存库,直接从 CI/CD 管道扫描镜像。

使用 inline 镜像扫描,它会仅扫描发送到扫描工具的元数据,从而帮助保护隐私。

生产中的 12 种容器镜像扫描最佳实践

使用 inline 镜像扫描,可以在 CI/CD 管道内扫描镜像。代码推送后,在不离开 CI/CD 管道的情况下即可构建并扫描镜像。镜像元数据会被发送到镜像扫描器,然后结果会发送回 CI/CD 管道。如果该镜像遵循安全策略,那它将被推送到生产镜像存储库。

3.在注册表中执行镜像扫描

开始实施镜像扫描时,要将其嵌入到注册表中,这是第一步。

生产中的 12 种容器镜像扫描最佳实践

通常,我们会有一个用于发布镜像的专用存储库,还有一些用于从第三方下载镜像的公共存储库。我们需要扫描这两个储存库的镜像。

我们部署的所有镜像都将从注册表中提取。通过在那里扫描镜像,我们至少可以确保它们在运行之前已经被扫描过了。

4.使用 Kubernetes 准入控制器

如果想在使用镜像之前先对其进行检查,以防止将未扫描或易受***的镜像部署到集群上,我们可以使用准入控制器。

Kubernetes 准入控制器是强大的 Kubernetes 原生功能,可以帮助我们自定义在集群上允许运行的内容。在对请求进行身份验证和授权之后,在对象持久化存储 etcd 之前,准入控制器可以拦截并处理对 Kubernetes API 的请求。

生产中的 12 种容器镜像扫描最佳实践

将 Deployment 的创建请求发送到 Kuberenetes 后,准入控制器将调用 Webhook 并发送镜像元数据。镜像扫描器会将扫描结果发回准入控制器,准入控制器在扫描通过后才会保留 Deployment。

扫描工具通常会提供一个验证 Webhook,该 Webhook 可以按需触发镜像扫描,然后返回验证决策。

准入控制器可以在调度镜像之前调用此 Webhook。Webhook 返回的安全性验证决策将传回 API 服务器,该服务器会回复原始请求者,并且仅在镜像通过检查后才将对象持久保存在 etcd 数据库中。不过,该决定是由镜像扫描器做出的,并没有任何集群中有关情况的上下文(context),这里我们可以使用 OPA 改进。

Open Policy Agent(OPA)是一种开放源代码通用策略引擎,它是一种叫 Rego 的高级声明性语言编写的。OPA 关键思想之一是将决策与政策执行脱钩。

使用 OPA,我们可以在 Kubernetes 集群而不是镜像扫描器中做出准入决定,这样就可以在决策过程中使用集群信息,例如命名空间、Pod 元数据等。

5.固定的镜像版本

有时,我们扫描的镜像与在 Kubernetes 集群中部署的镜像不同,例如在使用可变标签(比如“latest”或“staging”)时,就可能会发生这种情况。此类标签会不断更新版本,从而使得我们很难知道最新的扫描结果是否仍然有效。

生产中的 12 种容器镜像扫描最佳实践

标签“:latest”会指向镜像的最新版本,除了最后一个版本外,其他所有版本均会被扫描。

使用可变标签可能会导致使用同一个镜像却部署了不同版本容器的情况。除了扫描结果带来的安全问题外,这可能还会导致难以调试。

为了代替 ubuntu:focal,我们应该尽可能使用不可变标签,例如 ubuntu:focal-20200423。

这里要记住,version 标签(对于某些镜像)往往会进行较小的、不间断的更新,因此确保可重复性唯一的选择就是使用实际镜像 ID:

ubuntu:@sha256:d5a6519d9f048100123c568eb83f7ef5bfcad69b01424f420f17c932b00dea76

这里可能有点超出镜像扫描最佳实践的范围,但我们要知道,这些不仅会影响 Dockerfiles 中的 FROM 命令,还会影响 Kubernetes Deployment 文件以及几乎所有放置镜像名称的地方。

从镜像扫描的角度来看,我们能做什么?我们可以通过结合使用 Kubernetes 准入控制器、镜像扫描器和 OPA 引擎来实施此策略。

6.扫描操作系统漏洞

作为一般的镜像扫描最佳实践,请牢记这一点:“镜像越轻越好。” 越轻的镜像意味着更快的构建、更快的扫描以及更少的潜在漏洞依赖性。

新的 Docker 镜像通常是在现有基础镜像的基础上构建的,或在现有基础镜像之上添加一层。该基础镜像是由 Dockerfile 镜像中的 FROM 语句定义的,这样分层的体系结构设计,可以在最常见的任务中节省大量时间。例如,在镜像扫描时,我们只需要扫描一次基础镜像。如果父镜像易受***,那么在该父镜像之上构建的任何其他镜像也是易受***的。

生产中的 12 种容器镜像扫描最佳实践

WordPress 和 PHP 镜像基于 Apache,而 Apache 基于 Ubuntu 镜像。如果 Apache 上存在漏洞,那么 WordPress 和 PHP 镜像都将容易受到***。

即使我们没有在镜像中引入新漏洞,但基础镜像中的漏洞也很容易让我们受到***。这就是为什么扫描工具应主动跟踪已知漏洞文件的漏洞源,并在我们使用其中的漏洞时进行通知。

7.使用 distroless 镜像

distroless 镜像仅允许我们将应用程序及其依赖项打包在轻量级容器镜像中。将运行时容器中的内容严格限制为必需内容可以最大程度地减少***面。另外,它还可以改善扫描器的信噪比(例如 CVE),并根据需要减轻负担。

以下示例显示了用于“Hello world”应用程序的 Dockerfile,该应用程序在 Ubuntu 和 Distroless 上运行。

FROM ubuntu:focal``COPY main /``ENTRYPOINT ["/main"]

生产中的 12 种容器镜像扫描最佳实践

对其进行扫描后,我们发现了 24 个操作系统漏洞,其中两个严重程度为中。这样一个简单的应用程序,镜像大小居然有 77.98MB 这么大。

现在,基于 distroless 镜像的同一应用程序:

FROM gcr.io/distroless/static-debian10``COPY main /``ENTRYPOINT ["/main"]

生产中的 12 种容器镜像扫描最佳实践

现在,我们只发现了两个可以忽略不计的漏洞。此外,镜像大小减小到只有 6.93MB,这更适合此应用程序。

这表明,distroless 容器没有任何不必要的程序包,这些程序包可能导致更多的漏洞而被利用。

8.扫描第三方库中的漏洞

应用程序使用了大量的库,以至于这些库最终提供的代码行比团队编写的实际代码还多。这意味着我们不仅需要知道代码中的漏洞,还需要知道其所有依赖项中的漏洞。

不过扫描器检测操作系统漏洞的相同漏洞源,会跟踪这些漏洞,但并非所有工具都能像扫描镜像中的库一样深入,因此请确保镜像扫描器已深入挖掘并向我们警告这些漏洞。

9.优化镜像层顺序

谨慎使用 Dockerfile 中的 RUN 命令可以进一步优化镜像。RUN 命令的顺序可能会对最终镜像产生重大影响,因为它决定了镜像层的顺序。

我们可以首先放置较大的层(通常是不变的),最后放置变化最大的文件(例如已编译的应用程序)来优化 Docker 缓存的使用。这将有利于现有层的使用,加快构建镜像的速度,并间接加快镜像扫描的速度。

10.扫描 Dockerfile 中的配置错误

如我们所见,Docker 镜像构建过程遵循 Dockerfile 指令清单。

我们可以遵循以下几种 Dockerfile 最佳实践来检测常见的安全性错误配置:

  • 以特权(root)用户身份运行,会访问比所需更多的资源。

  • 暴露不安全的端口,例如不应在容器上打开 ssh 22 端口。

  • 由于错误的“COPY”或“ADD”命令不小心嵌入了私人文件。

  • 不通过环境变量或安装注入(泄露)secret 或凭据。允许用户将选项传递给 Entrypoint 和 CMD 是一个好习惯。

  • 特定策略定义许多其他内容,例如被阻止的软件包、允许的基本镜像、是否已添加 SUID 文件等。

在这样的 Dockerfile 中:

FROM ubuntu:focal-20200423``USER root``RUN curl http://my.service/?auth=ABD54F0356FA0054``EXPOSE 80/tcp``EXPOSE 22/tcp``ENTRYPOINT ["/main"]

我们的镜像扫描可以自动检测到以下问题:

USER root

我们以 root 身份运行:

EXPOSE 22/tcp

在这里,我们将暴露通常用于 ssh 的 22 端口,这是容器不应该包含的工具。另外,我们还将公开 90 端口,但这个没问题,这就像 HTTP 服务器的通用端口一样。

RUN curl http://my.service/auth=ABD54F0356FA005432D45D0056AF5400

此命令使用一个 auth 密钥,任何人都可以使用它来给我们造成危险,因此我们应该改用某种变量。这样的密钥不仅可以在 Dockerfile 上,还可以在镜像中存在的任何文件中使用正则表达式进行检测。作为一项额外措施,我们还可以检查已知可存储凭证的文件名。

11.快速标记 Kubernetes Deployment 中的漏洞

通过扫描的镜像并不是绝对安全的。如果在扫描后,我们部署了镜像,但此时发现了一个新漏洞,虽然我们可以立即加强安全策略,但是那些已经运行的镜像会怎么样?

生产中部署的易受***镜像的时间轴:

生产中的 12 种容器镜像扫描最佳实践

因此我们要连续扫描镜像以达到以下目标:

  • 检测新漏洞并进行符合我们策略的更改。

  • 向相应团队报告这些发现,以便他们尽快修复镜像。

当然,实施运行时扫描可以帮助我们减少这些漏洞的影响。以 CVE-2019-14287 为例, 我们可以编写一些 Falco 规则来检测该漏洞是否已被利用,但为每个漏洞编写特定规则是一项耗时的工作,应将其作为最后一道防线。因此,我们要连续扫描集群中正在运行的镜像。

安全工具会使用不同的策略进行存档,最简单的方法是每隔几个小时重新扫描一次所有镜像。理想情况下,我们希望在漏洞列表更新后立即重新扫描受影响的镜像。不过某些工具能够存储镜像元数据,无需完全重新扫描即可检测新漏洞。

一旦在运行中的容器中发现漏洞,我们就应尽快修复。这里的关键是有效的漏洞报告,这样每个人都可以专注于有关的信息。

实现此目标的一种方法是使用可查询的漏洞数据库,该数据库让 DevOps 安全团队可以在其庞大的镜像、程序包和 CVE 目录中进行一些排序。他们将搜索诸如 CVE age、是否有可用的修复程序、软件版本等参数。最后,如果可以下载这些报告并与漏洞管理团队、CISO 共享,那么将非常有用。

让我们用一个例子来说明,现在有一个查询包含了以下内容:所有的漏洞在 prod 命名空间,严重性为高,CVE>30 天,修复可用。

借助这种漏洞报告功能,团队可以轻松地识别出他们可以实际修复的易受***镜像,并在漏洞被利用之前开始着手解决方案。

12.选择基于 SaaS 的扫描解决方案

在本地解决方案上选择基于 SaaS 的扫描服务有很多好处:

  • 按需扩展资源:我们可以首先扫描几个镜像,然后随着容器应用程序的扩展而增长,无需担心后端数据管理。

  • 快速实施:我们可以将扫描嵌入到 CI/CD 管道中,并在几分钟内启动并运行,而本地应用程序则需要更多时间来安装和设置。

  • 轻松升级和维护:SaaS 提供商处理并推出补丁程序不需要我们手动升级或更新新功能。

  • 无需基础设施或人员成本:我们可以避免为拥有永久所有权的内部硬件和软件许可证付费,也不需要现场维护和支持应用程序。

K8sMeetup

结论

镜像扫描是 DevSecOps 工作流程中的第一道防线。通过使其自动化,我们可以最大程度地发挥其潜力,并在问题有机会变大之前发现问题。遵循镜像扫描最佳实践将帮助大家将安全性嵌入其中,并且交付速度不会因此降低。

最后,要记住镜像扫描不该仅用一次,而要在工作流程中连续检查,包括在构建时、在注册表上、在部署之前以及容器已经运行时。

原文链接:https://mp.weixin.qq.com/s/5ix7QCne4PwSwESw08v-Gw

猜你喜欢

转载自blog.51cto.com/14133165/2518712