容器彻底改变了我们开发和部署应用程序的方式,提供了封装应用程序及其依赖项的轻量级和可移植环境。但我们如何保证它们的安全呢?
需要解决的关键之一是容器运行时 - 负责启动和管理容器的软件。
虽然 Docker 和 Containerd 等容器运行时被广泛使用,但它们与主机操作系统的紧密耦合可能会带来风险。在本文中,我们将深入探讨容器运行时的工作原理、为什么紧密耦合的运行时会在攻击者逃离容器时导致主机接管,以及使用 gVisor 和 Kata Containers 等安全容器运行时的重要性。
了解容器运行时
容器运行时编排容器、管理其生命周期并将其与主机和其他容器隔离。通过利用命名空间和 cgroup 等 Linux 内核功能,运行时围绕容器创建安全边界。
然而,传统的运行时与主机的内核紧密相关,这存在潜在的安全漏洞。如果攻击者设法逃离容器,该攻击者就可获取对底层主机操作系统的未经授权的访问,从而危及整个系统的安全。
紧密耦合的容器运行时继承了主机操作系统的安全态势和攻击面。运行时或主机内核中的任何漏洞及其利用都会成为攻击者的潜在切入点。
在多租户环境或运行不受信任的工作负载时,这种风险尤其严重。为了减轻这种威胁,使用安全的容器运行时(例如 gVisor 和 Kata Containers)至关重要。
这类安全容器运行时提供了额外的隔离和安全层,采用创新技术来增强容器化工作负载的安全性。例如,gVisor 使用用户空间内核实现,而 Kata Containers则通过轻量级虚拟机实现。这些安全运行时将容器与主机操作系统隔离,防止攻击者未经授权访问底层基础设施,并降低主机接管的风险。
当下主流的一些容器运行时介绍
容器运行时提供了创建、部署和执行容器运行所需的工具和库。这些容器运行时处理诸如创建和管理容器映像、容器的启动和停止、资源隔离、网络和安全性等任务。它们构成了容器化技术的基础,对于跨不同计算环境一致运行应用程序至关重要。以下是一些最流行的容器运行时。
Docker
Docker 是一种广泛使用的容器运行时,可为构建、打包和运行容器提供完整的生态系统。它包括管理容器生命周期的 Docker 引擎和提供与容器交互的命令行界面的 Docker CLI。
在底层,Docker 使用 runC 作为默认的底层容器运行时。runC 基于开放容器计划 (OCI) 运行时规范生成和管理容器。
Containerd
Containerd 是由 Docker 开发的开源容器运行时,侧重于提供稳定、高性能和可移植的强大运行时。Ccontainerd旨在作为容器编排系统的核心组件,可以与 Kubernetes 等更上层的编排平台集成。
与 Docker 类似,Containerd 使用 runC 作为默认的底层容器运行时来创建和管理容器。
runC
runC 由 OCI 开发,是一个符合 OCI 运行时规范的轻量级底层运行时,通过在隔离的沙箱中启动容器来提供基本的容器执行环境。Docker和Containerd都利用runC的功能来处理容器生命周期管理、进程隔离、文件系统挂载和其他底层容器操作。
CRI-O
CRI-O是专为Kubernetes设计的轻量级容器运行时。它实现了 Kubernetes容器运行时接口(CRI),并为 Kubernetes 与容器交互提供了接口。CRI-O 在底层使用 runc 和Containerd 等技术。
安全容器运行时:gVisor和 Kata Containers
gVisor 是 Google 开发的开源容器运行时,使用轻量级用户空间内核“Sandbox”为容器提供安全的执行环境。
gVisor 不是直接在主机内核上运行容器,而是在隔离的沙箱中运行容器,增加了额外的安全和隔离层。沙箱拦截来自容器的系统调用,并应用自身的类内核实现,提供针对内核级漏洞的防御机制。
Kata Containers开源项目将轻量级虚拟机 (VM) 与容器运行时相结合,采用硬件虚拟化技术为每个容器进程启动单独的VM,提供容器之间的强隔离。
每个虚拟机都运行一个极简的轻量级客户操作系统,例如精简版 Linux 内核。Kata Containers 旨在保证容器的性能优势,同时提升虚拟机的安全性和工作负载隔离。
gVisor 和 Kata Containers都解决了与传统容器运行时相关的某些安全问题,有助于降低容器逃逸攻击的风险,在某些情形下,攻击者可利用容器运行时或内核中的漏洞获得对主机系统的未经授权的访问。通过添加额外的隔离和安全控制层,这些运行时强化了对容器化工作负载提供的保护。
gVisor 和 Kata Containers 并不相互排斥;事实上,它们可以一起使用,Kata Containers可使用 gVisor 作为其运行时,这种组合通过将虚拟机层面的隔离优势与 gVisor 所提供的额外安全措施相结合,进一步增强了安全性和隔离性。
这些安全容器运行时在运行不受信任或潜在易受攻击的工作负载的情况下特别有用,例如在多租户环境中或处理不受信任的第三方代码时。
在安全运行时中运行容器
使用 gVisor 和 Kata Containers 等安全运行时可以显著增强对主机系统的保护。主要安全优势如下:
- 增强隔离:gVisor 和 Kata Containers在容器和主机系统之间提供了额外的隔离层。这种隔离有助于防止容器逃逸攻击并限制容器内安全漏洞的影响。
- 内核级保护:gVisor 和 Kata Containers都可以防御内核级漏洞。gVisor 自身已实现类内核接口,可拦截来自容器的系统调用并执行安全策略。Kata Containers采用硬件虚拟化技术可在具有内核实例的独立虚拟机中运行容器,将这些独立虚拟机与主机内核隔离。
- 纵深防御:通过将这些运行时的安全机制与其他最佳安全实践(例如强大的访问控制、网络分段和图像扫描等)相结合,可提升容器部署的安全性。
- 兼容性和互操作性:gVisor 和 Kata Containers都可与 Kubernetes 等容器编排平台配合使用,用户借助这些技术的安全优势,无需对现有容器化应用程序或部署流程进行重大更改。
请注意,虽然 gVisor 和 Kata Constianers提升了安全性,但由于额外的隔离层,它们可能会带来一些性能开销。因此,实际部署时仍需要对特定用例和性能要求进行评估,以确定所提供的安全优势是否超过任何潜在的性能影响。
在安全容器运行时中运行微服务
微服务架构通常涉及在同一基础设施上运行的多个独立服务。通过在安全容器运行时中运行每个微服务,可以确保它们彼此隔离。
这有助于防止容器逃逸、权限升级和内核级漏洞。一旦发生安全漏洞或故障,安全容器运行时还可帮助限制安全危机的扩散,防止事态进一步升级。
用户还可通过容器运行时为每个微服务分配特定的资源(例如CPU、内存和存储),确保资源的公平分配。这可以防止资源争用问题,否则恶意行为者可能会利用资源争用问题来降低其他微服务的性能或稳定性。
要在安全容器运行时中运行微服务,需要执行以下步骤:
- 选择安全容器运行时 评估不同的安全容器运行时(例如 gVisor 和 Kata Containers),并选择最能满足需求的一种。考虑安全功能、性能影响、与现有基础设施的兼容性以及社区支持等因素。
- 安全构建容器镜像 使用受信任的基础镜像,定期更新依赖项并扫描镜像是否存在漏洞。实施安全的镜像注册并强制执行镜像签名以验证镜像的真实性。
- 安全配置 以适当的安全设置配置容器运行时,可能包括启用隔离功能、应用资源限制、设置容器网络策略和控制对主机系统资源的访问等。遵循容器运行时文档提供的安全准则。
- 实施严格的访问控制 为容器化微服务实施强大的访问控制。这包括限制容器权限、对容器编排平台采用基于角色的访问控制 (RBAC) 以及保护容器运行时 API。
- 持续监控和记录 实施监控和日志记录解决方案来跟踪容器化微服务的行为。监控可疑活动、异常行为和潜在的安全事件。集中式日志记录和分析有助于高效地检测和响应安全事件。
- 定期更新和打补丁 通过应用安全补丁和更新来使容器运行时保持最新,确保拥有最新的安全改进和错误修复。
- 运行安全测试 对容器化微服务定期进行安全评估和渗透测试,这有助于识别容器运行时配置和应用程序代码中的漏洞和潜在弱点。
gVisor的架构
gVisor 主要包括两大组件:Sentry 和 Gofer。
Sentry
Sentry(不要与同名的 Sentry监控平台混淆)代表容器化应用程序负责拦截和服务系统调用。它充当类似内核的接口,但不会将调用直接转发到主机内核。
相反,Sentry 在其自己的隔离环境中为这些请求提供服务,可在正在运行的微服务和主机之间提供一层隔离。Sentry 会进行自己的有限系统调用,这些调用与安全执行的 seccomp规则密切相关。
Gofer
Gofer 是 gVisor 的组件,负责协调文件系统操作。当容器化应用程序需要访问主机文件系统时,Sentry 会将这些请求转发给 Gofer。
然后,Gofer 使用主机,代表应用程序执行必要的文件系统操作,通过防止从容器内直接访问主机文件系统,引入了额外的隔离层。
gVisor 使用名为 runsc(runsc 沙箱)的进程来代替 runc 作为底层容器运行时。runsc 专为 gVisor 设计,充当容器运行时和 gVisor 组件(Sentry 和 Gofer)之间的接口。
它处理容器生命周期管理、进程隔离和其他底层容器操作。 runsc 与 Sentry 和 Gofer 交互,为 gVisor 中的容器化应用程序提供安全的执行环境。
Kata Containers的架构
Kata Containers 通过将每个容器或 Pod 封装在自己的专用 VM 中的方法提供了额外的保护层,因为每个 VM 都有自己的内核,该内核仅包含容器工作负载所需的服务,从而减少了潜在的攻击面。
除增强安全性之外,Kata Containers 还优先考虑性能和资源效率。Kata Containers提倡最小程度占用空间,这对于那些在安全需求与高效资源利用率之间寻求平衡的组织来说颇具吸引力。
Kata Containers旨在与现有的容器化应用程序和部署基础设施兼容,使组织能够采用安全的运行时功能,而无需进行重大修改。
用户通过将 Kata Containers 视为集群的安全运行时,可从其出色的隔离性、轻量资源占用和增强的安全性中受益,使其成为部署敏感或不可信工作负载的绝佳选择。
配置 gVisor 以实现容器安全
以下是用于创建 RuntimeClass对象和利用 gVisor 作为容器运行时的 pod 清单文件的代码示例。
在此示例中,创建了一个名为gvisor的RuntimeClass,指定容器运行时处理程序为“runsc”,该命令用于与 gVisor 交互。
在gvisor-pod.yaml文件中,定义了一个名为gvisor-pod的Pod。runtimeClassName字段指定该 Pod 使用“gvisor”RuntimeClass,对应于 gVisor 容器运行时。
容器部分可定义容器配置,包括要使用的容器名称和镜像(将“your-image”替换为实际镜像名称)。
准备好 gvisor.yaml 和gvisor-pod.yaml 文件后,可以使用以下命令创建 RuntimeClass 并部署 pod。
这些命令将为 gVisor 创建RuntimeClass 对象,并使用 gVisor 作为容器运行时来部署 pod。
请注意,务必确保在 Kubernetes 集群上正确安装和配置 gVisor,以使这些配置正常工作。
以下是用于创建RuntimeClass对象和利用 Kata Containers 作为容器运行时的 pod 清单文件的代码演示。
在上面的示例中,定义了一个名为kata的RuntimeClass,指定容器运行时处理程序为“kata-runtime”,对应于 Kata Containers 运行时。
kata-pod.yaml文件中定义了一个名为kata-pod的 Pod。runtimeClassName字段指定该 Pod 应使用kata RuntimeClass,对应于 Kata Containers 运行时。
容器部分可定义容器配置,包括容器名称和要使用的容器镜像(将“your-image”替换为实际镜像名称)。
准备好 kata.yaml 和kata-pod.yaml 文件后,可以使用以下命令创建 RuntimeClass 并部署 pod:
这些命令将为 Kata Containers 创建RuntimeClass 对象并部署 Pod,使用 Kata Containers 作为容器运行时。
请确保在 Kubernetes 集群上正确安装和配置 Kata Containers,以使这些配置按预期工作。
Kubernetes RuntimeClass 功能的优势
Kubernetes 中的 RuntimeClass 功能高度灵活,用户可根据特定需求和安全策略选择不同的容器运行时,为集群中的不同工作负载定义和选择合适的运行时。
以下是使用 RuntimeClass 的主要优势和用例:
- 工作负载隔离:不同的工作负载可能有不同的安全需求,通过RuntimeClass,您可选择最合适的运行时,为每个工作负载提供所需的隔离和安全级别。 例如,对于安全敏感型工作负载,可使用 gVisor 或 Kata Containers 等更轻量级、更高效的容器运行时;对于其他工作负载则使用标准运行时,如 Docker 或containerd。
- 自定义运行时:RuntimeClass能够在 Kubernetes 环境中集成和使用自定义容器运行时。如果您已经开发或采用定制的运行时来满足需求,可为其定义RuntimeClass 并用于特定工作负载。
- 性能优化:不同容器运行时的性能各有不同,通过使用RuntimeClass,您可为每个工作负载选择最合适的运行时以优化性能。例如,对于需要更高资源利用率和更快启动时间的工作负载,可选择 gVisor 或 Kata Containers 等轻量级运行时。
- 合规和安全策略:组织通常有特定的安全策略或合规性要求,规定用于某些工作负载的运行时。RuntimeClass让您可通过为需要遵守特定安全准则的工作负载配置适当的运行时来执行这些策略。
- 动态运行时切换:RuntimeClass还支持为正在运行的工作负载动态切换运行时。这种灵活性使您可按需切换运行时,从而适应不断变化的工作负载需求或高效应对安全事件。
部署安全运行时的最佳实践
了解何时以及如何使用安全容器运行时对于规划安全的 Kubernetes 环境至关重要。以下是根据特定需求部署安全运行时的一些选项和注意事项。
在集群中的每个 Pod 上使用安全运行时。一种方法是使用安全容器运行时(例如 gVisor 或 Kata Containers)作为集群中所有 Pod 的默认运行时,这可确保集群中运行的所有工作负载的一致且强大的隔离,无论其信任级别如何。
通过默认使用安全运行时,可为整个环境提供额外的保护层。
在安全容器中运行不受信任的或第三方应用程序。在运行不受信任的或第三方应用程序时,安全运行时尤为重要。通过使用 gVisor 或 Kata Containers 的运行时在安全容器中部署这些应用程序,可以降低潜在风险并将其与底层主机系统隔离。
此方法有助于保护主机和其他工作负载免受来自不可信代码可的潜在漏洞或恶意活动的影响。
在默认的 runC 运行时中部署自研应用程序。如果企业或组织内部开发了值得信赖的应用程序并且经过了严格的安全审查,可以选择在默认的 runC 运行时中运行。这种方法承认可信应用程序可能不需要安全运行时提供的额外隔离。
然而,为这些应用程序实施适当的安全实践(例如容器加固和漏洞扫描)至关重要。
考虑具体需求和环境。是否部署安全容器运行时应基于具体需求、安全要求和风险评估。评估数据敏感性、监管合规性、威胁形势和环境整体安全态势等因素。
此外,还应考虑使用安全运行时产生的性能开销和对资源使用的影响,因为与标准运行时相比,它们可能会带来一些额外的开销。
本文翻译自英文文章:
https://thenewstack.io/container-security-and-the-importance-of-secure-runtimes/