容器镜像构成了云原生环境中的标准应用交付格式,基于容器镜像广泛分布和部署的特性,需要一套最新的安全实践来确保镜像的完整性。
通过镜像扫描来检查操作系统和软件语言包中是否有漏洞,这是确保镜像安全的基石。了解容器镜像生命周期中每个阶段的风险,有助于了解应该构建和使用怎样的镜像,从而加固和提高企业的安全态势。
镜像的构建安全
基础设施安全
如果容器镜像中包含恶意软件,在运行时就会构成直接威胁。因此,构建阶段基础设施的安全性与生产环境中的安全性同等重要,可以防止将外部漏洞引入镜像当中。在构建和持续集成阶段,可以采取以下这些办法来确保镜像安全:
•限制对构建基础设施的访问。
•只开放必要的网络入口。
•认真管理secrets,只授予最小权限。
•认真审查从第三方网站拉取的源文件或其他文件,通过网络防火墙限制只有受信任网站在白名单访问之列。
•确保镜像扫描工具的安全性和可靠性,以获得相对全面而精准的扫描结果。
基础镜像安全
如果在现有的第三方基础镜像之上构建镜像,需要考虑以下几个因素:
•基础镜像的来源和主机的可信度。该镜像是否来自一家知名的公司或开源团队?镜像仓库可信吗?镜像中是否有所有组件的Docker文件和源代码?
•更新频率。避免使用长期不更新的镜像,尤其是未对相关漏洞披露做出回应的镜像。
•默认安装的软件。选择利用最小基础镜像来构建镜像,然后选择性地安装应用程序所需的工具,这比从已有镜像中删除软件包更加可靠。建议选择精简的基础镜像,比如:
Google Distroless。这是一种最小基础镜像,预制了几种常用语言。但由于缺乏软件包安装程序,所以如果需要安装软件,需要把文件复制到镜像中。
Red Hat Universal Base Image (UBI)。基于RHEL镜像,无需Red Hat订阅,该镜像分为最小、标准平台和多服务三个层次,能覆盖多语言镜像。
此外,还可以从头开始构建一个基础镜像。
删除非必要的软件
尽量减少容器中提供的工具,防止恶意攻击者设法进入后对工具进行漏洞利用。同时,尽可能保持镜像最小化,减少0day漏洞的概率,加快镜像存储和拉取的速度。此外,限制镜像中只包含必要的二进制文件、库和配置文件,并避免安装以下工具(如果已安装,则将其删除)。
•软件包管理工具,例如apt、yum、apk
•Unix shells,例如sh、bash。删除shells会妨碍在运行时使用shell脚本,要尽可能使用编译语言。
•编译器和调试器。只在容器创建和开发环节使用,不要在生产环境中使用。
容器创建与运行时
生成和编译应用程序的构建工具在生产系统上运行时可能会被利用。由于容器的生命周期非常短,因此,不太可能给正在运行的容器打补丁或进行变更,而是要构建一个新镜像来取代过时的容器部署。通过Dockerfile多阶段构建的镜像包含了编译软件所需的所有内容,实现编译环境和运行环境的分离。
FROM build-image as build
# Build my stuff
# …
FROM base-image
# Install my packages
# …
COPY –from=build /out/my-app /bin/my-app
Secrets
即使镜像仅供内部使用,也不要在镜像中嵌入任何secrets,包括TLS证书秘钥、云服务商凭证、SSH私钥和数据库密码等,因为任何有权限拉取镜像的操作者都能提取secret。另外只在运行时提供敏感数据,还可确保操作者能在不同的运行时环境中使用相同的镜像,这样无需重建镜像,就能通过使用不同的凭证,来简化更新过期秘钥或吊销秘钥的流程。
作为secrets管理的替代方案,利用Kubernetes Secret可以解决敏感数据的配置问题,避免把敏感数据暴露在Kubernetes Pods中,或者使用其他的 secrets管理系统。
镜像扫描
镜像中的软件若含有漏洞,会大大增加在运行时的攻击面。在CI管道中构建镜像时,必须要通过镜像扫描,方可通过构建阶段。不安全的镜像不应该被推送到生产环境中的镜像仓库中。
虽然已经有许多开源和专用的镜像扫描工具以及云扫描服务,但这些工具的镜像扫描水平不尽相同。一些扫描工具只检查已安装的操作系统包,一些扫描工具只扫描某些编程语言的已安装运行库,一些扫描工具只提供额外的二进制指纹或其他文件内容的测试。在为CI管道提供镜像扫描工具时,要确保其能够满足所需要的扫描覆盖范围,并支持基础镜像的软件包安装数据库和应用程序使用的编程语言。
此外,确定可接受的漏洞风险层级,以便能够通过构建。例如任何低于某一严重程度的漏洞或高于某一严重程度的漏洞,都会导致构建失败,这就需要扫描工具提供一个能兼容的API或工具,可以配置进CI管道,并提供数据来评估构建的标准。
镜像的存储安全
选择镜像仓库
构建好安全的容器镜像后,就要将其存储至镜像仓库。使用私有镜像仓库可最大限制的确保安全配置,但需要仔细管理镜像仓库的基础设施和访问控制权限。这种支持私有存储库的镜像仓库会为组织减轻大量的管理费用。安全工程师和构建工程师可以依据组织的安全需求和基础设施资源,为组织选择最佳的安全解决方案。
镜像管控
一些镜像仓库支持额外的镜像管控功能,支持在镜像上使用不可变标签,防止同一标签的镜像在镜像仓库的多个版本中被重复使用,强制执行镜像的确定性运行时间。许多审计认证机构要求要准确地知道在确定时间内部署的是哪个版本的镜像,从而也就知道了在某个时间运行的是哪个版本的应用程序,避免了每次镜像拉取时都使用最新镜像签名。
镜像签名能够为镜像提供更好的保护。有了镜像签名,镜像仓库会生成一个标记镜像内容的校验和,然后使用私人秘钥加密来创建一个带有镜像元数据的加密签名。客户端仍然可以在不验证签名的情况下拉取并运行镜像,但安全运行环境下应支持镜像验证要求。镜像验证使用签名秘钥的公钥对来解密镜像签名内容,可以将其与拉取的镜像进行比较,确保镜像内容没有被修改。
镜像的运行安全
镜像扫描
镜像扫描是标准CI过程的一部分,在构建阶段已经进行过镜像扫描并不意味着运行时阶段就不需要进行镜像扫描,相反,运行时扫描更为重要。无论是使用任何第三方镜像还是组织内部的镜像都可能含有新发现的漏洞,这可以通过使用自定义的或第三方的Kubernetes集群中的准入控制器,来防止不安全的容器镜像的调度。
虽然有些扫描工具已经支持在数据库或缓存中存储扫描结果,但用户也必须要考虑对过期镜像内容的接受度,以及对每个镜像拉取进行实时扫描所带来的延迟等因素,以免影响扫描结果的输出。
镜像仓库和镜像信任
配置和使用安全的镜像仓库很重要,但如果不在客户端执行,这些保护措施也会被削弱。由于Kubernetes不提供对使用安全镜像拉取选项的本地支持,所以需要部署一个Kubernetes准入控制器,来验证Pod可以使用受信任的镜像仓库。此外,准入控制器还需要能够验证镜像签名。
镜像持续运营维护
在镜像的整个生命周期内进行漏洞扫描至关重要,这需要权衡组织的风险容忍度,保持开发速度,这就要求每个组织都需要有自己的政策和程序来处理镜像安全和漏洞管理问题。
首先要确定“哪些是不安全的镜像”的标准。参考指标包括:
•漏洞的严重性
•漏洞数量
•漏洞是否有打补丁或可用的修复程序
•因配置错误所导致的漏洞
接下来,需要确定服务目标和处理镜像的程序,是否要建立替代镜像并设定一个期限将其部署到生产环境中?最后期限是否会根据漏洞的严重程序而变化?当发现新的漏洞时,是否要阻止已有镜像的容器调度?此外,还需要定义程序来处理已经在生产中运行的脆弱性镜像的容器。
结语
通过参考以上最佳实践,在容器的整个生命周期内,制定一个有效的镜像安全策略,可以保护容器安全。作为云原生安全的领军企业,青藤自主研发的青藤蜂巢•云原生安全平台支持在业务流程中进行安全卡点检查,以“准入”+“准出”进行安全管控,落地安全左移,减少攻击面。
安全卡点要确保镜像在各阶段的安全性。青藤蜂巢镜像检查能力,覆盖容器“构建-分发-运行”全生命周期,对镜像(Image)及其构建文件(Dockerfile、Yaml文件等)进行深度的安全和合规性检查。
青藤蜂巢的镜像扫描又快又准,每千个镜像扫描时间<2分钟。相比开源扫描,蜂巢的镜像检测能力覆盖全、检查能力深,不仅能检查Dockerfile不安全配置问题,更能深入发现镜像中的安全补丁、应用组件漏洞、木马病毒、敏感信息、受信镜像、是否来源基础镜像、是否使用黑名单应用等问题,且具备镜像阻断和修复建议等内容,实现管理闭环。
此外,青藤蜂巢检查能力以API和插件形式集成到生产流程中,支持CI/CD集成。
除了静态扫描外,青藤蜂巢还对镜像运行后的工作负载进行动态安全检查,比如应用和组件漏洞、微服务风险、弱密码等,开创了国内首个针对运行态容器风险检查的安全方案。
实现安全左移还要进行“准入”和“准出”安全管控,凡是不合格镜像都“不准入”到测试环境;同时,持续检查生产仓库和测试节点镜像,凡是不合格镜像都“不准出”到生产环境。
青藤蜂巢·云原生安全平台,在镜像进入生产环境之前就确保镜像安全,在运行时阶段持续进行镜像漏洞扫描,保护镜像全生命周期的安全。