## K8s安全分析 声明,本篇文章的内容主要来自[Kubernetes修炼手册 @Nigel Poulton](https://book.douban.com/subject/35486781/)。 ### 1. 安全模型 本文采用STRIDE模型来对K8s进行安全分析,该模型定义了6种潜在威胁: - 伪装 - 篡改 - 抵赖 - 信息泄露 - 拒绝服务 - 提升权限 下文将对这些威胁进行详细分析。 ### 2. 伪装 在信安领域,伪装指攻击者为了获得更多的系统权限而冒充另一个人或主体。 #### 2.1 与API Server的安全通信 K8s是一个由多个组件构成的系统,它们包括: - API Server(Pod) - Controller manager(Pod) - Scheduler(Pod) - Store(etcd) - kube-proxy(Pod) - kubelet(journald) 以上这些组件基本上都是与API Server进行通信的,K8s在组件通信之间采用了Mutual TLS(mTLS)认证。 这要求通信双方都要提供自己的证书给对方进行身份验证,这种方式区别于传统的单向证书认证(仅限于客户端验证服务端)。 K8s内部通过自旋证书来简化了mTLS的实现。简单来说,K8s部署时自动生成了一个自签名的CA,这个CA将用来为集群内的所有组件颁发证书。 所以mTLS的安全性依赖于CA的可靠性,CA私钥的泄露将导致整个集群组件之间的通信彻底陷入危险。 除此之外,还需要注意: - CA证书仅在集群内使用 - 使用CA批准证书签名请求(CSR)时保持严谨 - 确保CA不会被系统外的任何组件设置为可信CA 此外,API Server还可能与集群外的组件进行通信(比如Webhook)。这时候推荐使用两套不同的可信秘钥——分别用于认证内部组件和外部组件。 要实现这一点,K8s需要将一个集群外的CA添加为可信CA,然后对于集群外的组件,应当使用外部CA颁发的证书来与API Server进行通信。 #### 2.2 Pod间的安全通信 Pod间通信的认证方式可以通过Service Account(SA)来实现。每个Pod启动时默认都会自动分配一个ServiceAccount作为Secret挂载到Pod中。 并且该SA是被允许访问API Server的,只是权限受限,但我们要知道,大多数Pod并不需要访问API Server。 所以,对于不需要与API Server通信的Pod,我们建议将Pod清单中的`automountServiceAccountToken`属性设置为`false`。 如果需要挂载SA,那么有些非默认的配置需要了解一下: - expireSeconds:设置token有效期 - audience:限制token的受众 这些属性的具体示例,你可以在官方找到。 ### 3. 篡改 篡改通常基于以下目的: - 拒绝服务:篡改资源使其不可用 - 提高权限:篡改资源获取额外权限 #### 3.1 对K8s组件的篡改 K8s系统内可以篡改的资源有以下几种: - etcd - 配置文件:API Server、ControllerManager、Scheduler、Kube-proxy和Kubelet - 容器运行时的二进制 - 容器镜像 - K8s的二进制 篡改行为通常发生在(网络)传输和保存过程中。TLS可以确保数据的完整性,下面的建议有助于防范对保存在集群中数据的篡改攻击: - 严格限制对运行由K8s组件的服务器的访问,尤其是部署了控制层组件的节点 - 严格限制对保存有K8s配置文件的库的访问 - 仅在最初部署K8s时进行ssh访问节点 - 对下载的二进制文件进行哈希验证 - 严格限制镜像仓库及相关库的访问 此外,建议在生产环境中对关键组件的二进制文件的审计和监控,相关的工具有`auditctl`等。 #### 3.2 对于运行在K8s中的应用的篡改 推荐将Pod中容器的文件系统设置为只读是一种推荐的办法。我们可以在Pod的清单中设置`securityContext.readOnlyRootFilesystem` 属性为`true`, 也可以通过部署PodSecurityPolicy对象来全局性的限制所有Pod的文件系统的读写权限。 > PodSecurityPolicy特性从v1.25开始被删除,转而使用[Pod 安全性标准][Pod 安全性标准]进行代替。 ### 4. 抵赖 抵赖就是制造对某件事的不确定性。不可抵赖就是提供证据(以证实某事),具体来说应该能够证明以下信息: - 发生了什么 - 什么时间发生的 - 谁操作的 - 在哪里发生的 - 为什么发生的 - 如何发生的 对于后两个信息,通常需要一段时间内的多个相关事件的信息。 K8s提供针对API Server的审计(Audit)功能来完成回答以上问题,该功能需要手动开启,请参考官方文档[审计][审计]部分。 然而,除了API Server,K8s还提供了对其他组件的审计,比如容器运行时、Kubelet等各应用的审计日志。如果要收集多个组件的日志, 那么就需要一个中心化的日志后端来实现对事件的保存和分析。一种常见的做法是在每个节点上部署DaemonSet类型的日志代理来收集日志, 然后发送至中心日志数据库,同时要确保这个中心化的日志库是安全的。 ### 5. 信息泄露 主要是指敏感数据的泄露。 #### 5.1 保护集群数据 K8s中的所有集群配置都是保存在集群存储中的(目前是etcd),包含网络和存储配置以及Secret形式保存的密码登敏感数据。 这就使得集群存储会成为被攻击的首要目标。 我们必须对运行由集群存储的节点进行访问限制和审计。 #### 5.2 保护Pod中的数据 前面提到,K8s提供了Secret对象来保存密码等敏感数据。但请注意,Secret是以未加密的形式存储在集群存储中的, 我们可以为其使用静态加密,请参考官方文档[Secrets良好实践][Secrets良好实践]部分。 ### 6. 拒绝服务 这种方式的攻击目的在于使服务不可用。在K8s中,拒绝服务的最大可能攻击对象就是API Server。 #### 6.1 保护集群资源免于DoS攻击 首先,我们应该对主节点进行高可用(HA)部署。 进一步,我们还可以考虑将主节点部署在多个可用域中, 这样可以避免某个可用域的网络遭受故障导致整个集群不可用。这个防范原则也适用于工作节点。 此外,我们还应当为以下资源配置限额(参考`ResourceQuota`对象): - 内存 - CPU - 存储 - K8s对象(ReplicaSet、Pod、Service、Configmap、Secret等) 添加配额限制有助于避免重要系统资源被消耗殆尽,从而提高抵御DoS的能力。 #### 6.2 保护API Server防范DoS攻击 参考以下方法: - 高可用部署主节点 - 对到达API Server的请求进行合理监控和预警 - 不将API Server暴露在互联网上(借助防火墙/安全组规则) 即使K8s拥有良好的鉴权机制,也需要注意妥善保管具有较高权限的账号。 #### 6.3 保护集群存储防范DoS攻击 集群存储(etcd)的重要性已经不需要过多强调,参考一下方法加固etcd: - 将etcd部署为3个或5个节点的集群 - 对etcd收到的请求进行监控和预警 - 在网络层面对etcd进行隔离,只允许控制平面组件与其交互 默认情况下,K8s会将etcd安装到与控制平面组件相同的节点上,对于非生产环境这是OK的。但是生产环境就不太合适了, 应该考虑为K8s单独部署一个etcd集群,有助于提高系统的性能和弹性。 在性能方面,etcd可能是大型K8s集群的瓶颈所在。所以在集群部署阶段应当进行适当的性能测试,以确保整体架构能够在较大规模下维持较高性能。 性能不足的etcd集群的表现基本等同于正在遭受DoS攻击的etcd集群。 #### 6.4 保护应用组件防范DoS攻击 多数Pod会将其服务暴露于互联网之上,如果没有采取合理的控制,任何人都可以访问Pod并对其实施DoS攻击。 好在,K8s支持对Pod进行资源请求限制。推荐下面的安全措施: - 对Pod之间和Pod与外部的通信配置网络安全策略以控制流量的出站/入站(NetworkPolicy) - 利用mTLS和基于Token的API认证来提供(Pod层面)应用级的认证 ### 7. 提升权限 #### 7.1 保护API Server K8s提供以下鉴权方式来保证API Server的安全性: - RBAC(基于角色的访问控制) - webhook - 节点认证 建议同时开启多个鉴权机制。例如,一种常见的方式是同时启用RBAC和节点两种鉴权机制。 RBAC模式能够指限制哪个用户(组)能够对哪些资源执行哪些操作。Webhook模式则是将鉴权工作交给外部的基于REST接口的策略引擎来完成。 不过需要额外搭建和维护一套外部引擎,且这个引擎也可能会带来API Server的单点故障隐患,因为API Server会持续调用外部引擎来完成每一次鉴权。 所以一旦外部引擎宕机,则API Server无法继续完成请求。鉴于此,应该严格谨慎地对待webhook鉴权服务的设计与实现。 节点认证模式是指对来自kubelet的请求进行鉴权。对节点请求的鉴权工作由节点鉴权器(node authorizer)完成。 #### 7.2 保护Pod 下面提供一些手段来显著降低以Pod和容器为目标的"提升权限"攻击的风险。 - 避免进程以root身份运行(通过PodSpec中的`spec.securityContext.runAsUser`,此属性还可以在容器层面进行配置) - 若Pod由不同容器组成,建议为不同容器配置不同用户,以避免容器之间互相干扰 - 限制capabilities - 在linux内部,root用户的权限是由不同的capabilities的权限组合而成的,比如,名为`SYS_TIME`的capability允许用户设置系统时钟。 - `NET_ADMIN`允许用户执行网关相关操作(如修改本地路由表、配置本地接口) - root用户拥有所有的capability - 我们可以在配置非root用户的同时,为其添加capability来满足容器需求 - 过滤系统调用 - Seccomp是linux自 2.6.12 以来一直是Linux 内核的一个特性。它可以用来沙箱化进程的权限,限制进程从用户态到内核态的调用。 - 避免权限提升 - linux中允许子进程申请比父进程更多的权限。我们可以通过`spec.securityContext.allowPrivilegeEscalation`属性来禁止权限提升 - 配置selinux 这些功能配置基本都是通过`spec.securityContext` 属性来完成的。具体配置参考官方文档的[为 Pod 或容器配置安全上下文][为 Pod 或容器配置安全上下文]。 **Capabilities** 如今[linux-capabilities][linux-capabilities]总共有30个,我们可以在`spec.securityContext.capabilities` 中配置容器所拥有的capabilities。 ### 8. K8s安全展望 2019年CNCF委托了一个第三方机构对K8s进行了安全审计。通过安全威胁建模、人工代码审查、动态渗透测试和加密审查等方面的审计方法发现了一些安全隐患。 所有的隐患都给出了难度和严重程度级别。这次审查非常细致,本着负责人的态度,所有严重级别的隐患都在对外发布前被修复了。 不过,仍然有许多问题等待社区来解决。 ### 9. 一些实践建议汇总 **CI/CD流水线**: - 使用私有镜像仓库(或公共仓库如DockerHub中的私有repository) - 并且使用分离的repository(如开发、测试、生产) - 使用已验证的基础镜像 - 控制镜像库的访问权限(push/pull) - 生产repo限关键用户拥有push权限 - 限制用户进行push/pull镜像 - 限制某些客户端节点能访问镜像库 - 整合漏洞扫描 - 在扫描到镜像漏洞后自动令构建流水线失败 - 审查配置文件(Dockerfile、K8s YAML文件) - 使用镜像签名,确保镜像完整性 - push时添加签名,pull时验证签名 **基础设施隔离**: - 使用K8s命名空间隔离工作负载 - 对单一命名空间进行资源限额 - 创建单独的用户管理特点的命名空间 - 节点隔离 - 对于需要特殊权限(如root)运行的应用Pod,应将其部署到特定的节点(通过亲合度/污点/标签) - 运行时隔离 - 容器和虚拟机的隔离性是不同的 - 多个容器会共享一个CPU内核,隔离性由内核指令提供。是一种软性隔离,不如虚拟机。 - 虚拟机的隔离性由硬件虚拟化技术提供,每台虚拟机独享一个硬件CPU内核 - 可以使用不同容器运行时(如gVisor和Kata)来提供更高级别的Pod隔离性 - 首先为特定节点安装配置特定的容器运行时 - 然后使用Runtime Class(K8s v1.20 stable)和nodeSelector将Pod调度到这类节点 **网络隔离**: - K8s中的Pod网络通过CNI(Container Network Interface)实现,主流实现分为Overlay和BGP两类 - 使用K8s的网络策略(NetworkPolicy)来限制Pod之间的通信,支持跨命名空间 **身份认证和访问控制管理**: - 充分使用RBAC系统 - 严格限制哪些人员能够ssh到控制平面节点的权限 - 部署审计和监控系统,应对未来某个时候被攻破的情况 - 安全配置 - 在PodSpec中限制容器以非root身份运行 - 信息安全中心(CIS)发布了一套针对K8s安全性的行业标准要求 - Aqua Security公司编写了一个易于使用的K8s安全评估工具kube-bench来执行对节点的CIS要求的测试 **审计和安全监控**: - 保留容器和Pod的生命周期事件 - 通过采集每个节点上的容器运行时日志发送到外部存储(如ES),以便在未来调查问题时使用 - 采集每个节点上的Pod日志 - 记录每个节点上的命令执行,发送到日志中心(可以采用跳板机) ### 参考 - [Kubernetes修炼手册 @Nigel Poulton](https://book.douban.com/subject/35486781/) [Pod 安全性标准]: https://kubernetes.io/zh-cn/docs/concepts/security/pod-security-standards/ [审计]: https://kubernetes.io/zh-cn/docs/tasks/debug/debug-cluster/audit/ [Secrets良好实践]: https://kubernetes.io/zh-cn/docs/concepts/security/secrets-good-practices/ [linux-capabilities]: https://linux-audit.com/linux-capabilities-hardening-linux-binaries-by-removing-setuid/ [为 Pod 或容器配置安全上下文]: https://kubernetes.io/zh-cn/docs/tasks/configure-pod-container/security-context/ |