本文已发布一年多,较旧的文章可能包含过时的内容。请检查页面信息自发布以来是否仍旧准确。
深入了解 NSA/CISA Kubernetes 加固指南
免责声明
本文中列出的开源工具仅作为示例,绝不代表 Kubernetes 社区或作者的直接推荐。背景
美国国家安全局 (NSA) 和网络安全与基础设施安全局 (CISA) 于 2021 年 8 月 3 日发布了 Kubernetes 加固指南。该指南详细描述了 Kubernetes 环境面临的威胁,并提供了安全配置建议以最小化风险。
本博客的以下部分与 NSA/CISA 指南中的相应部分相关联。任何缺失的部分都已跳过,因为现有内容中没有太多新内容可以补充。
注意:本博客文章不能替代阅读指南。建议在继续阅读之前阅读已发布的指南,因为以下内容是补充性质的。
更新,2023 年 11 月
美国国家安全局 (NSA) 和网络安全与基础设施安全局 (CISA) 于 2021 年 8 月发布了 Kubernetes 加固指南的 1.0 版本,并根据行业反馈于 2022 年 3 月进行了更新(版本 1.1)。
Kubernetes 加固指南的最新版本于 2022 年 8 月发布,包含更正和澄清。1.2 版本概述了关于加固 Kubernetes 集群的许多建议。
引言与威胁模型
请注意,NSA/CISA 认定为重要的威胁,或本指南的目标受众所关注的威胁,可能与 Kubernetes 的其他企业用户认为重要的威胁不同。本节对于关注数据、资源盗窃和服务不可用性的组织仍然有用。
该指南重点强调了以下三个妥协来源(或称:攻击来源):
- 供应链风险
- 恶意威胁行为者
- 内部威胁(管理员、用户或云服务提供商)
的威胁模型试图退一步,审查不仅存在于 Kubernetes 集群边界内的威胁,还包括 Kubernetes 不管理的底层基础设施和周围工作负载。
例如,当集群外部的工作负载共享相同的物理网络时,它就可以访问 kubelet 和控制平面组件:etcd、控制器管理器、调度器和 API 服务器。因此,指南建议进行网络层面的隔离,将 Kubernetes 集群与不需要连接到 Kubernetes 控制平面节点的其他工作负载隔离开来。具体来说,调度器、控制器管理器、etcd 只需对 API 服务器可访问。从集群外部与 Kubernetes 的任何交互都可以通过访问 API 服务器端口来实现。
这些组件的端口和协议列表定义在 Kubernetes 文档中的端口和协议部分。
特别注意:kube-scheduler 和 kube-controller-manager 使用的端口与指南中提到的端口不同
CNCF 云原生安全白皮书 + 地图中的威胁建模部分从云原生视角提供了另一种进行 Kubernetes 威胁建模的方法。
Kubernetes Pod 安全性
Kubernetes 默认情况下不保证在同一节点上运行的 Pod 之间有严格的工作负载隔离。但是,该指南提供了一些技术来增强现有隔离并在发生泄露时减少攻击面。
“非 Root”容器和“无 Root”容器引擎
几项与最小权限基础安全原则相关的最佳实践值得二次审视,即只提供所需的权限,不多不少。
该指南建议在构建时设置非 Root 用户,而不是依赖于在 Pod spec 运行时设置 runAsUser
。这是一个很好的实践,并提供了一定程度的纵深防御。例如,如果容器镜像使用用户 10001
构建,而 Pod spec 在其 Deployment
对象中遗漏了 runAsuser
字段。在这种情况下,有一些边缘情况值得探讨以提高意识:
- 如果构建时定义的用户与 Pod spec 中定义的用户不同,并且因此某些文件不可访问,Pod 可能会启动失败。
- Pods 可能会无意中共享 User ID。即使 User ID 是非零的,在容器可能逃逸到主机文件系统的情况下,这也会带来问题。一旦攻击者获得了主机文件系统的访问权限,他们就可以访问所有由其他不相关但共享相同 UID 的 Pods 所拥有的文件资源。
- Pods 可能会与 Kubernetes 不管理的节点级进程共享 User ID,例如用于审计、漏洞扫描、遥测的节点级守护进程。这种威胁类似于上述情况,即主机文件系统访问可以使攻击者完全访问这些节点级守护进程而无需成为节点上的 Root 用户。
然而,所有这些情况的影响都不如以 Root 用户身份运行的容器能够逃逸成为主机上的 Root 用户那么严重,后者可以使攻击者完全控制工作节点,并进一步横向移动到其他工作节点或控制平面节点。
Kubernetes 1.22 引入了一个Alpha 特性,它通过用户命名空间(user namespaces)专门减少了此类以 Root 用户身份运行的控制平面组件对非 Root 用户的影响。
对用户命名空间 / 无 Root 模式的(Alpha 阶段)支持在以下容器运行时中可用:
一些发行版支持以无 Root 模式运行,如下所示:
不可变容器文件系统
NSA/CISA Kubernetes 加固指南强调了一个经常被忽视的特性 readOnlyRootFileSystem
,并在附录 B 中提供了一个工作示例。这个示例限制了容器在运行时的执行和篡改。任何读/写活动随后都可以通过使用 tmpfs
卷挂载限制在少数目录中。
然而,一些在运行时修改容器文件系统的应用,例如在容器启动时解压 WAR 或 JAR 文件,在启用此特性时可能会遇到问题。为了避免此问题,请考虑在可能的情况下尽量减少运行时对文件系统的更改。
构建安全的容器镜像
Kubernetes 加固指南还建议在部署时将扫描器作为准入控制器运行,以防止有漏洞或配置错误的 Pod 在集群中运行。理论上,这听起来是个好方法,但在实际实施之前有几个注意事项需要考虑:
- 根据网络带宽、可用资源和扫描器的选择,扫描镜像的漏洞可能需要不确定的时间。这可能导致 Pod 启动时间变慢或不可预测,从而在应用服务于峰值负载时导致可用性出现波动。
- 如果允许或拒绝 Pod 启动的策略是使用不正确或不完整的数据制定的,则可能导致以下几种误报或漏报结果:
- 在容器镜像内部,
openssl
包被检测出存在漏洞。然而,该应用是用 Golang 编写的,并使用 Go 的crypto
包进行 TLS 连接。因此,此漏洞不在代码执行路径中,如果未修复,其影响微乎其微。 - Debian 基础镜像的
openssl
包中检测到一个漏洞。然而,上游 Debian 社区认为这是一个低影响漏洞,因此没有为此漏洞发布补丁修复。此镜像的所有者现在陷入困境,面临一个无法修复的漏洞,而集群由于预设策略不考虑漏洞修复是否可用而阻止该镜像运行。 - 一个 Golang 应用基于distroless 镜像构建,但它是使用一个使用了有漏洞标准库的 Golang 版本编译的。扫描器无法查看 Golang 版本,只能查看 OS 级别的包。因此,即使镜像包含基于有漏洞 Golang 构建的应用二进制文件,它也允许该 Pod 在集群中运行。
- 在容器镜像内部,
需要明确的是,依赖漏洞扫描器绝对是个好主意,但策略定义应该足够灵活,以允许:
- 通过打标签(labelling)为镜像或漏洞创建例外列表
- 根据漏洞的影响,使用风险评分覆盖其严重性
- 在构建时应用相同的策略,以在部署到 Kubernetes 集群之前捕获具有可修复漏洞的易受攻击的镜像
如果在隔离环境(air-gapped environment)中运行集群,且扫描器需要互联网访问才能更新漏洞数据库,则可能需要进行特殊考虑,例如离线获取漏洞数据库。
Pod 安全策略
自 Kubernetes v1.21 起,PodSecurityPolicy API 和相关功能已被弃用,但在集群操作员将其集群升级到较新版本的 Kubernetes 之前,本节中的某些指导在未来几年内仍然适用。
Kubernetes 项目正在开发 PodSecurityPolicy 的替代方案。Kubernetes v1.22 包含一个名为 Pod 安全准入 (Pod Security Admission) 的 Alpha 功能,旨在强制执行 Pod 之间的最低级别隔离。
Pod 安全准入的内置隔离级别源自Pod 安全标准 (Pod Security Standards),它是指南表 I 第 10 页中提到的所有组件的超集。
有关从 PodSecurityPolicy 迁移到 Pod 安全准入功能的信息,请参阅从 PodSecurityPolicy 迁移到内置 PodSecurity Admission 控制器。
指南中提到的一项重要行为,在 Pod Security Policy 及其替代方案之间保持不变,即强制执行它们都不会影响已经在运行的 Pod。无论是 PodSecurityPolicy 还是 Pod 安全准入,强制执行都发生在 Pod 创建阶段。
强化容器引擎
某些容器工作负载的可信度低于其他工作负载,但可能需要在同一集群中运行。在这种情况下,将它们运行在包含强化容器运行时的专用节点上,这些运行时提供更严格的 Pod 隔离边界,可以作为一种有用的安全控制措施。
Kubernetes 支持一个名为 RuntimeClass 的 API,自 Kubernetes v1.20 起,该 API 已处于稳定版 / GA(通用可用性)阶段(因此默认启用)。RuntimeClass 允许你确保需要强隔离的 Pod 被调度到能够提供此功能的节点上。
可与 RuntimeClass 结合使用的第三方项目包括
如本文和指南中讨论的那样,Kubernetes 及其周边存在许多可以增强 Pod 之间隔离边界的功能和工具。基于相关的威胁和风险态势,你应该选择并应用其中一部分建议,而不是试图应用所有建议。尽管如此,尽管本文和指南中提到了改进措施,集群级别隔离,即在专用集群中运行工作负载,仍然是最严格的工作负载隔离机制。
网络隔离和强化
Kubernetes 网络可能比较复杂,本节重点介绍如何保护和强化相关配置。指南将以下内容确定为关键要点:
- 使用 NetworkPolicies 在资源之间创建隔离,
- 保护控制平面安全
- 加密流量和敏感数据
网络策略
可以借助网络插件创建网络策略。为了方便用户创建和可视化,Cilium 支持一个 Web GUI 工具。该 Web GUI 允许你创建 Kubernetes NetworkPolicies(一个通用 API,但需要兼容的 CNI 插件)和/或 Cilium 网络策略(CiliumClusterwideNetworkPolicy 和 CiliumNetworkPolicy,仅适用于使用 Cilium CNI 插件的集群)。你可以使用这些 API 来限制 Pod 之间的网络流量,从而最小化攻击向量。
另一个值得探讨的场景是使用外部 IP。某些服务在配置错误时,可能会创建随机的外部 IP。攻击者可以利用此配置错误轻松拦截流量。此漏洞已在 CVE-2020-8554 中报告。使用 externalip-webhook 可以通过阻止服务使用随机外部 IP 来缓解此漏洞。externalip-webhook 只允许创建不需要外部 IP 或其外部 IP 在管理员指定范围内的服务。
CVE-2020-8554 - 所有版本的 Kubernetes API 服务器都允许能够创建 ClusterIP 服务并设置
spec.externalIPs
字段的攻击者拦截流向该 IP 地址的流量。此外,能够修补 LoadBalancer 服务的status
(这被认为是一种特权操作,通常不应授予普通用户)的攻击者,可以设置status.loadBalancer.ingress.ip
达到类似效果。
资源策略
除了配置 ResourceQuotas 和限制外,还可以考虑限制给定 Pod 可以使用的进程 ID (PID) 数量,并为节点级别使用保留一些 PID,以避免资源耗尽。应用这些限制的更多详细信息可在进程 ID 限制和保留中找到。
控制平面强化
下一节,指南介绍了控制平面强化。值得注意的是,自 Kubernetes 1.20 起,API 服务器的不安全端口已被移除。
Etcd
作为一项通用规则,etcd 服务器应配置为仅信任分配给 API 服务器的证书。这限制了攻击面,并阻止恶意攻击者获得集群访问权限。使用单独的 CA 用于 etcd 可能是有益的,因为它默认信任根 CA 颁发的所有证书。
Kubeconfig 文件
除了直接指定令牌和证书,.kubeconfig
还支持使用认证提供者插件动态检索临时令牌。警惕 kubeconfig
文件中可能存在的恶意 Shell 代码执行。一旦攻击者获得集群访问权限,他们就可以窃取 SSH 密钥/Secrets 或更多。
Secrets
Kubernetes Secrets 是将 Secrets 作为 Kubernetes API 对象进行管理的本机方式。然而,在某些场景下,例如希望所有应用 Secrets 拥有一个单一的可信源,无论它们是否运行在 Kubernetes 上,Secrets 可以与 Kubernetes 松散耦合进行管理,并通过 Sidecar 或 Init 容器被 Pod 消费,同时最少使用 Kubernetes Secrets API。
外部 Secrets 提供者和 csi-secrets-store 是 Kubernetes Secrets 的一些替代方案。
日志审计
NSA/CISA 指南强调基于日志的监控和告警。关键点包括主机级别、应用级别和云端的日志记录。在生产环境中运行 Kubernetes 时,了解各层日志记录的责任人和问责人非常重要。
Kubernetes API 审计
一个值得更多关注的领域是应该具体告警或记录什么。该文档在附录 L:审计策略中概述了一个示例策略,该策略记录所有 RequestResponse,包括元数据和请求/响应体。虽然对演示有用,但在生产环境中可能不实用。
每个组织都需要评估自己的威胁模型,并构建一个补充或帮助排查事件响应的审计策略。思考攻击者会如何攻击你的组织,以及哪些审计踪迹可以识别它。在官方审计日志文档中查阅调整审计日志的更多高级选项。调整审计日志,使其仅包含符合你威胁模型的事件,这一点至关重要。一个记录所有 metadata
级别事件的最小审计策略也可以是一个很好的起点。
也可以按照这些说明,使用 kind 测试审计日志配置。
流式日志和审计
日志对于威胁和异常检测非常重要。正如文档所述,扫描日志并尽可能接近实时地触发告警,并在发生泄露时保护日志免遭篡改,这是一项最佳实践。反思各级日志记录,并识别关键领域,例如 API 端点,这非常重要。
Kubernetes API 审计日志可以流式传输到 Webhook,附录 N:Webhook 配置中有一个示例。使用 Webhook 可以是将日志存储在集群外部和/或集中所有审计日志的方法。一旦日志集中管理,就可以基于关键事件启用告警。同时确保你了解正常活动的基线。
告警识别
虽然指南强调了通知的重要性,但并没有一个统一的事件列表来触发告警。告警要求会根据你自己的需求和威胁模型而异。示例包括以下事件:
- Pod 的
securityContext
发生变化 - 准入控制器配置更新
- 访问特定文件/URL
其他日志资源
- Seccomp Security Profiles and You: A Practical Guide - Duffie Cooley
- TGI Kubernetes 119: Gatekeeper and OPA
- Abusing The Lack of Kubernetes Auditing Policies
- 使用新的 v1.22 alpha 功能为所有工作负载启用 seccomp
- This Week in Cloud Native: Auditing / Pod Security
升级和应用安全实践
Kubernetes 每年发布三次,因此升级相关的繁琐工作对于运行生产集群的人员来说是一个常见问题。除此之外,操作员还必须定期升级底层节点的操作系统和运行中的应用。这是确保持续支持并降低 Bug 或漏洞可能性的最佳实践。
Kubernetes 支持最近三个稳定版本。虽然每个 Kubernetes 版本在发布前都会经过大量测试,但有些团队在经过一段时间后才愿意运行最新的稳定版本。无论你运行哪个版本,确保补丁升级频繁或自动进行。更多信息可在版本偏差策略页面找到。
在考虑如何管理节点操作系统升级时,请考虑临时节点。能够销毁和添加节点使你的团队能够更快地响应节点问题。此外,能够容忍节点不稳定的部署(以及鼓励频繁部署的文化)可以使集群升级更轻松。
此外,值得从指南中重申的是,可以对各种系统组件进行定期漏洞扫描和渗透测试,以主动查找不安全配置和漏洞。
查找发布和安全信息
要查找最新的 Kubernetes 支持版本,请参考 https://k8s.io/releases,其中包含次要版本信息。及时更新你的次要版本补丁是很好的做法。
如果你正在使用托管式 Kubernetes 服务,请查找他们的发布文档并找到他们的各种安全渠道。
订阅 Kubernetes Announce 邮件列表。Kubernetes Announce 邮件列表可以搜索诸如 "Security Advisories" 等术语。只要你知道要告警的关键词,你就可以设置告警和电子邮件通知。
结论
总而言之,很高兴看到安全从业者在公开场合分享如此详细的指南。这份指南进一步凸显了 Kubernetes 正成为主流,以及保护 Kubernetes 集群和在 Kubernetes 上运行的应用容器持续需要从业者的关注和重视。就在指南发布几周后,一个根据此指南验证集群的开源工具 kubescape 就发布了。
这个工具可以作为一个很好的起点,用来检查集群的当前状态,之后你可以利用这篇博客文章和指南中的信息来评估可以在哪些方面进行改进。
最后,值得重申的是,此指南中的所有控制措施并非都适用于所有从业者。了解哪些控制措施重要的最佳方法是依赖你自己的 Kubernetes 环境的威胁模型。
特别鸣谢 Rory McCune (@raesene) 为这篇博客文章提供的宝贵意见!