目前从各方面来看,Kubernetes 都在容器编排市场中占据了主导地位。近日发布的《Kubernetes 和容器安全状况报告》更是指出,87% 的组织正在使用 Kubernetes 管理容器工作负载。
另外,该报告中有一项调查显示,在过去的 12 个月里,有 94% 的组织在其容器环境中遇到过安全问题,其中 69% 的组织检测到错误配置,27% 的组织在运行时遇到安全事件,还有 24% 的组织发现了严重的安全漏洞。
粗略地说,这些安全问题分别对应着容器生命周期的各个阶段。我们应该在构建阶段修复已知的漏洞;在构建、部署阶段修复错误的配置;在运行阶段对威胁进行快速响应。
本文将深入探讨使用 Kubernetes 时可能遇到的一些安全风险和挑战。
K8s 安全风险和挑战
因容器大量部署导致忽视问题
尽管容器具有快速、可移植性等特点,但是它们也会出现安全盲点,从而增加攻击面。随着容器的大量部署,保持云基础架构组件的可见性会变得越来越困难。容器化应用程序的分布式性质会让我们难以快速发现哪些容器存在漏洞或错误配置。
滥用镜像和镜像注册中心会带来安全问题
组织必须要有镜像和可信镜像注册中心的有效治理策略。我们需要确保使用定期扫描、安全、已批准的基础镜像来构建容器镜像,并仅使用白名单镜像注册中心中的镜像来启动 Kubernetes 环境中的容器。
容器彼此之间以及与其他端点之间的通信
在部署过程中,容器和 Pod 需要相互通信,并与其他内、外部端点也进行通信才能正常运行。如果某个容器被破坏,攻击者可影响的环境范围与该容器的通信范围直接相关,这意味着与该容器通信的其他容器以及 Pod 可能会遭受攻击。在庞大的容器环境中,手动配置非常复杂,因此实施网络分段会非常困难。
K8s 有丰富的配置选项,但默认值通常并不安全
根据 DevOps 原则,Kubernetes 旨在加快应用程序部署并简化管理和操作,同时提供了丰富的控件集,用于有效保护集群及其应用程序的安全。
例如 Kubernetes 网络策略,它类似于防火墙规则,控制了 Pod 之间以及与其他端点的通信方式。当网络策略与 Pod 关联时,该 Pod 只能与该网络策略中定义的目标进行通信。但默认情况下,Kubernetes 不会将网络策略应用于 Pod,这意味着在 Kubernetes 环境中每个 Pod 都可以与其他 Pod 通信。
另一个配置风险与 secret 管理有关:如何存储和访问敏感数据(例如凭证和密钥)。我们需要确保不会将 secret 信息当做环境变量传递,要将其放入容器的只读卷中。
容器和 Kubernetes 构成合规性挑战
云原生环境在遵守安全最佳实践、行业标准和基准以及内部组织策略方面也带来了挑战。除了保持合规性之外,组织还必须显示该合规性的证明。我们要调整策略以确保 Kubernetes 环境符合最初为传统应用程序体系架构编写的规范。同时,容器化应用程序的分布式和动态特性意味着在实现合规性监控和审核自动化时,才能大规模运行。
容器运行带来了常见和新的安全性挑战
容器和 Kubernetes 的一个安全性优势是可以将它们视为不可变的基础设施,因为应用在运行时不应该被修补或更改,要在更新时从通用模板中删除并重新创建。
容器的其他属性带来了独特的挑战,例如它们可以快速创建、删除。当我们在正在运行的容器中检测到潜在威胁时,不仅要停止该容器并重新启动未被破坏的版本,还必须确保修复信息能够应用到新的容器镜像中,以安全地重新配置应用。
其他的安全风险还包括运行带有恶意进程的受损容器。对于那些破坏容器环境的攻击者而言,加密挖矿是常用手段,但他们还可以通过受损容器执行其他恶意进程,例如通过网络端口扫描以查找漏洞,再进行其他破坏。
要解决上面列出的这些 Kubernetes 安全挑战,开发者需要将安全集成到容器生命周期的每个阶段:构建、部署和运行。我们必须构建安全的镜像,按照最佳安全实践部署配置,并在运行时保护工作负载免受威胁。
最后,我们还需要保护 Kubernetes 基础架构及其组件,包括 Kubernetes API Server、etcd 等,这些组件自身都可能受到攻击,总体攻击面也会由此增加。
构建阶段
保护容器和 Kubernetes 的安全要从构建阶段开始,此时花费的时间将在将来获得回报,如果开始就没做好安全实践,后面将付出极大的修复成本。现在要做的两件事就是构建安全镜像,然后扫描这些镜像查找已知漏洞。
1)使用最小的基础镜像
避免将镜像与 OS 软件包管理器或 Shell 一起使用,因为它们可能会有未知漏洞。如果必须要使用 OS 软件包,请在后面的步骤中删除软件包管理器。
2)不要添加不必要的组件
确保从生产中的容器中删除 debug 工具。镜像中不要有对攻击者有用的通用工具(例如 Curl)。
3)使用最新镜像
确保镜像以及任何第三方工具都是最新的,并使用其最新版本的组件。
4)使用镜像扫描识别已知漏洞
镜像扫描能够识别镜像中的漏洞,并提示漏洞是否可修复。另外,它能够扫描 OS 软件包和第三方运行库中的漏洞,以查找容器化应用程序中使用的程序语言漏洞。
5)将安全性集成到 CI/CD 管道中
让镜像扫描和其他安全检查成为 CI/CD 管道的一部分,这样在扫描程序检测到严重的可修复漏洞时,可以自动执行安全保护并使 CI 构建失败同时生成警报。
6)标记无需修复的漏洞
如果没有已知漏洞的修复程序,或者该漏洞不是关键漏洞,在这种不用立即修复的情况下,将它们添加到白名单或在扫描中过滤,这样就不会被不必要的警报中断工作流程。
7)实施纵深防御
在容器镜像或使用该镜像运行的部署中发现安全问题时,确保准备好策略检查和修复工作流程来检测和更新这些镜像。
部署阶段
在部署工作负载之前,应该配置好 Kubernetes 基础设施。从安全角度来看,我们首先要了解正在部署的内容以及部署的方式,然后识别并应对违反安全策略的情况,至少应该知道:
- 正在部署的内容——包括使用镜像的有关信息,例如组件、漏洞以及将要部署的 Pod
- 将在哪里部署——哪些集群、命名空间和节点
- 部署方式——是否以特权方式运行,可以与其他哪些部署进行通信
- 可以访问的内容——包括 secret、卷和其他基础结构组件,例如主机或 orchestrator API
- 是否符合要求——是否符合策略和安全要求
有了这些信息,我们就可以开始针对需要修复和加固的区域,并进行适当的分段。
8)使用命名空间隔离敏感的工作负载
命名空间是 Kubernetes 资源的关键隔离方式。它们为网络策略、访问控制和其他重要的安全控制提供了参考。将工作负载分到不同的命名空间可以遏制攻击,并限制授权用户的错误或破坏性操作的影响。
9)使用 K8s 网络策略来控制 Pod 和集群之间的流量
默认情况下,Kubernetes 允许每个 Pod 与其他 Pod 通信。网络分段策略是一项安全控制措施,可以防止攻击者闯入后跨容器横向移动。
10)防止过度访问 secret 信息
确保部署时仅安装其实际需要的 secret,以防止不必要的信息泄露。
11)评估容器使用的特权
赋予容器的功能、角色绑定和权限集会极大影响安全风险。我们最好遵守最小特权原则,只提供容器执行其预期功能的最小特权和功能。
Pod 安全策略是一种控制 Pod 与安全相关属性的方法,包括容器特权级别,可以使操作人员指定以下内容:
- 不要以超级用户身份运行应用程序进程
- 不允许特权升级
- 使用只读的根文件系统
- 使用默认的 masked/proc 文件系统挂载
- 不要使用主机网络或进程空间
- 删除不使用和不必要的 Linux 功能
- 使用 SELinux 获得更细粒度的过程控制
- 为每个应用程序分配自己的 Kubernetes 服务帐户
- 如果不需要访问 Kubernetes API,就不要在容器中保存服务帐户凭据
12)评估镜像来源
不要通过未知来源的镜像部署代码,仅使用已知或列入白名单的注册中心镜像。
13)将镜像扫描扩展到部署阶段
扩展镜像扫描到部署阶段,并根据扫描结果执行策略。有种执行方式是使用 Kubernetes 的 Validation Admission Controller 功能,当没有扫描结果、镜像有严重漏洞或者是 90 天前构建的,Kubernetes 会拒绝进行部署,因为近期未扫描的镜像可能会包含上次扫描披露的新漏洞。
14)适当使用标签和注释
使用负责应用程序团队的名称、电子邮件别名或 Slack Channel 为部署添加标签或注释,这将提醒负责团队更加注意安全性问题。
15)启用 Kubernetes 基于角色的访问控制(RBAC)
RBAC 提供了一种方法,用于控制集群中用户和服务帐户访问集群的 Kubernetes API 服务授权。
接下来是在运行时阶段保护 Kubernetes 工作负载的建议。
运行阶段
运行阶段的容器化应用程序又会面临许多新的安全挑战。我们既要获得运行环境的可见性,又要在威胁出现时对其进行安全检测和快速响应。在构建和部署阶段主动保护容器和 Kubernetes 部署可以大大减少运行时发生安全事件的可能性以及响应这些事件而进行的后续工作。
首先,我们必须监视与安全性最相关的容器活动,包括:
- 进程活动情况
- 容器服务之间的网络通信
- 容器化服务与外部客户端和服务器之间的网络通信
由于容器和 Kubernetes 具有声明性,因此在容器中观察容器行为来检测异常通常比在虚拟机中更容易。
16)利用 Kubernetes 中的上下文信息
使用 Kubernetes 中构建和部署的时间信息来评估运行时观察到的活动与预期活动,以检测可疑活动。
17)将漏洞扫描扩展到正在运行的部署
除了扫描容器镜像中存在的漏洞之外,还需要监控正在运行的部署中是否有新发现的漏洞。
18)使用 Kubernetes 内置控件以加强安全性
配置 Pod 的安全上下文以限制其功能。这些控件可以消除依赖特权访问的整个攻击类别。例如,只读根文件系统可以防止任何依赖于安装软件或写入文件系统的攻击。
19)监视网络流量,限制不必要或不安全的通信
观察应用网络流量并将该流量与基于 Kubernetes 网络策略所允许的流量进行比较。容器化的应用程序通常会大量使用集群网络,因此观察应用网络流量是了解应用程序交互并识别意外通信的好方法。同时,将应用流量与允许的流量进行比较,可以提供一些有价值的信息。通过这些信息,我们可以进一步收紧网络策略,以消除多余的网络连接并减少攻击面。
20)利用进程白名单
进程白名单是一种用于识别意外运行进程的有效做法。首先,观察应用程序一段时间,将应用程序正常过程中执行的所有进程加入列表,然后将该列表用作针对将来应用程序行为的白名单。
21)比较和分析相同部署的 Pod 不同运行时的状态
出于高可用性、容错性或规模等原因,容器化的应用程序常被用于复制。复制的应用应该大致相同,所以与其他副本有明显差异的副本要进一步调查。
22)如果被破坏,将可疑 Pod 数量减少至零
通过 Kubernetes 控制器将可疑 Pod 数量减少至零或者杀死,然后重新启动被破坏的应用程序实例。
基础设施安全
上文专注于以构建、部署和运行工作负载时的安全实践,但是安全性还需要扩展到镜像和工作负载之外,直到整个环境,包括集群基础结构。我们同时要保证集群、节点和容器引擎的安全。
23)将 Kubernetes 更新到最新版本
Kubernetes 仅支持最近的三个版本,因此如果在 Kubernetes 中发现了一个严重漏洞,并且落后四个版本,那么我们将不会收到补丁。
24)安全地配置 Kubernetes API Server
确保已经禁用未经身份验证的匿名访问,并使用 TLS 加密对 kubelet 和 API Server 之间的连接。
25)etcd 的安全
etcd 是 Kubernetes 用于数据访问的键值存储。etcd 被认为是 Kubernetes 的信任来源,我们可以根据需要从中读取数据或将数据写入其中,另外要确保仅通过 TLS 提供客户端连接。
26)kubelet 的安全
作为在每个节点上运行的主节点代理,kubelet 的错误配置会使攻击者可以通过 kubelet 进行后门访问。通过使用 --anonymous-auth=false
启动 kubelet,并利用 NodeRestriction 准入控制器限制 kubelet 可以访问的内容,确保已禁用对 kubelet 的匿名访问。
Kubernetes 还有很多组件,包括 kube-scheduler、kube-controller-manager、主节点和工作节点上的配置文件等,都可以帮助安全配置。
实施 K8s 的安全
容器和 Kubernetes 的出现并没有改变安全需求,我们的目标仍是使攻击者难以入侵应用程序及其基础设施,如果被入侵,就要尽快阻止,但是,这些工具和方法必须适应 DevOps 实践和云原生原则的需求。
27)将安全更早地嵌入到容器的生命周期中
我们必须尽早将安全集成到容器的整个生命周期中,并确保安全性和 DevOps 团队保持一致。这样开发人员和 DevOps 团队才能放心地构建和部署具有生产级规模、稳定性和安全性的应用程序。
28)使用 Kubernetes 原生安全控制来降低运营风险
尽可能利用 Kubernetes 内置控件来实施安全策略,这样安全控件就不会与编排工具发生冲突。例如,不要使用第三方代理或 shim 来进行网络分段,而应使用 Kubernetes 网络策略来确保安全的网络通信。
29)按优先级进行补救工作
在庞大的 Kubernetes 环境中,手动筛选安全事件非常耗时。如果具有高风险漏洞或是包含特权容器并且对互联网开放的部署,那应该上移修复优先级;如果是在测试环境,并且是支持非关键应用程序的,那就将其修复优先级下移。