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

2 篇文章 0 订阅

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

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

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

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

什么是容器镜像扫描

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

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

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

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

12 种镜像扫描最佳实践

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

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

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

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

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

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

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

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

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

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

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

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

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

4.使用 Kubernetes 准入控制器

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

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

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

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

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

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

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

5.固定的镜像版本

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

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

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

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

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

ubuntu:@sha256:d5a6519d9f048100123c568eb83f7ef5bfcad69b01424f420f17c932b00dea76

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

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

6.扫描操作系统漏洞

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

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

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

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

7.使用 distroless 镜像

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

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

FROM ubuntu:focal

COPY main /

ENTRYPOINT ["/main"]

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

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

FROM gcr.io/distroless/static-debian10

COPY main /

ENTRYPOINT ["/main"]

现在,我们只发现了两个可以忽略不计的漏洞,此外,镜像大小减小到只有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 中的漏洞

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

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

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

  • 检测新漏洞并进行符合我们策略的更改。
  • 向相应团队报告这些发现,以便他们尽快修复镜像。

 

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

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

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

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

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

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

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

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

  • 按需扩展资源:我们可以首先扫描几个镜像,然后随着容器应用程序的扩展而增长,无需担心后端数据管理。
  • 快速实施:我们可以将扫描嵌入到CI/CD管道中,并在几分钟内启动并运行,而本地应用程序则需要更多时间来安装和设置。
  • 轻松升级和维护:SaaS提供商处理并推出补丁程序不需要我们手动升级或更新新功能。
  • 无需基础设施或人员成本:我们可以避免为拥有永久所有权的内容硬件和软件许可证付费,也不需要现场维护和支持应用程序。

 

结论

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

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

原文链接:https://sysdig.com/blog/image-scanning-best-practices/

 

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
制作标准容器镜像时,以下是一些最佳实践建议: 1. 使用官方基础镜像:选择官方提供的基础镜像作为起点,如Alpine、Ubuntu等。这些镜像经过广泛测试和维护,具备良好的稳定性和安全性。 2. 最小化镜像大小:在构建镜像时,尽量减少不必要的软件包和依赖项,保持镜像的最小化。这有助于减少镜像的大小,提高部署效率和安全性。 3. 使用版本控制:明确指定所使用的软件包和依赖项的版本号,避免依赖项的自动更新可能引发的不兼容问题。 4. 单一职责原则:每个容器应只包含一个应用程序或服务,并尽量做到单一职责。这有助于隔离和管理容器,简化维护和更新过程。 5. 避免使用root用户:在容器避免使用root用户,而是创建一个非特权用户来运行应用程序。这可以减少潜在的安全风险,限制容器内部操作的权限。 6. 清理不必要的文件:在构建镜像时,确保删除不必要的临时文件、日志文件和缓存文件等,以减少镜像大小。 7. 安全更新和漏洞扫描:定期更新基础镜像和应用程序,确保镜像的软件包和依赖项都是最新的。同时,进行漏洞扫描和安全审查,及时修复发现的安全漏洞。 8. 使用环境变量:通过使用环境变量来配置容器的参数和设置,提高容器的可配置性和灵活性。 9. 编写清晰的Dockerfile:使用清晰、简洁的Dockerfile来构建镜像,并遵循最佳实践的规范。合理使用多个阶段构建、缓存和分层,优化构建过程和镜像大小。 10. 文档和版本管理:为镜像提供清晰的文档,包括构建过程、使用说明和配置选项等。同时,使用版本控制系统对镜像进行管理和追踪。 这些最佳实践可以帮助您创建更安全、高效和可维护的标准容器镜像。根据自己的需求和实际情况,可以进一步优化和调整这些实践

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值