「引言」
Kubernetes 是一个开源的容器编排平台,用于自动化部署、扩展和管理容器化应用程序。它提供了丰富的功能,如服务发现、负载均衡、自动缩放等。随着 Kubernetes 在云原生领域的广泛应用,「有效管理谁可以对 Kubernetes 集群执行何种操作变得至关重要」。本文将简要介绍 Kubernetes的认证与授权体系以及RBAC授权原理。通过实际案例展示RBAC管理不当可能导致的安全风险,然后向大家分享RBAC安全研发与运维的最佳实践,以及我们在字节跳动内部的安全防护和治理经验。
背景知识
❝
如果您对相关背景知识比较了解,可直接跳转到“RBAC 安全风险剖析”、“RBAC 安全研发与运维最佳实践” 章节阅读。
❞
本章节将对 Kubernetes 的认证和授权体系进行概述,了解这些机制的原理有助于理解不同场景下集群权限的安全风险。特别是那些能够被轻易利用的未授权访问漏洞,以及那些容易被忽视的权限提升与横向移动攻击风险。
Kubernetes 认证与授权体系
Kubernetes 的认证与授权体系主要用于满足对关键服务 API(API Server、Kubelet Server)的访问控制。在经过多年的发展后,Kubernetes 已经实现了一套比较完善的认证与授权机制,可以满足用户大多数场景的使用需求。
「API Server」
Kubernetes 是一个以容器技术为基础,以声明式 API Server 为核心的分布式容器编排系统。Kubernetes 几乎所有的功能都通过 API Server 对外暴露。而 API Server 支持了多种认证机制,内置了多种授权模式和准入控制器,允许用户根据需要灵活配置和使用。
简单来说,当一个用户访问 Kubernetes 的 API Server 时,API Server 会使用启用的认证器依次对请求进行身份认证,API Server 使用第一个成功认证的身份来标识请求者;然后再使用启用的授权器依次对请求进行授权策略的检查,当有任意一个授权器显式地允许、拒绝一个请求时,则立刻返回当前授权结果(如果没有授权器显式地授权,那么请求也将被拒绝)。除此之外,在 API Server 真正处理请求前,它还会使用启用的准入控制器对请求进一步变异和验证。只有所有的准入控制器都验证通过后,请求才会被真正处理。
❝
注意:API Server 不保证认证器和准入控制器的执行顺序,但会按照授权模式的配置顺序进行鉴权。
❞
「Kubelet Server」
Kubernetes 中还有一个非常重要的组件,那就是 Kubelet。它充当了分布式系统中的 Agent 角色,并使用节点专属的用户证书访问 API Server,管理节点上的资源。但 Kubelet 自身也会作为服务端,对外提供服务。从而实现在容器内执行命令、获取指标信息、容器日志、宿主机日志等功能。
Kubernetes 也为 Kubelet Server 提供了多种认证和授权模式。值得一提的是其中的 webhook 认证和 webhook 授权,它们本质上是向 API Server 发送 TokenReview 和 SubjectAccessReview 请求,对客户端的身份进行认证与授权。
「小结」
Kubernetes 为 API Server 和 Kubelet Server 支持了多种认证机制、授权机制、准入控制器,以及灵活的自定义接口。这些机制虽然能够满足各种用户需求,但也给用户带来了困扰。因为如果不了解这些机制的原理和负面影响,就很容易为集群引入安全风险和入侵检测盲点。特别是那些能够被轻易利用的未授权访问漏洞,以及那些容易被忽视的提权与横向移动攻击风险。
请参见附录和参考文献,了解更多 API Server 和 Kubelet Server 的认证、授权、准入控制的技术细节。
Kubernetes RBAC 授权原理
RBAC 是 Kubernetes 默认启用的授权机制,也是 Kubernetes 核心组件所使用的授权机制。用户在使用集群时,往往需要使用 RBAC 授权机制来为其用户账号授权,以便部署、运维工作负载及所需的各种资源。各类云原生应用的 Operator、Controller 往往也需要利用 RBAC 授权机制来为其服务账户授权,以确保它们能够访问必要的资源,从而实现其功能。
下面的示意图展示了用户账号和服务账号访问 API Server 时的认证、授权、准入控制过程。
在 Kubernetes 的 RBAC 授权体系中,引入了以下几种概念:
「Subject」
- 在 Kubernetes 环境中有三类 Subject 可以被授予 RBAC 角色权限。
「Rule」
- 用于在
Role
,ClusterRole
内部定义具体权限,每一个 rule 都可以通过 apiGroups, resources, resourcesName, verbs, nonResourceURLs 来定义允许对什么资源(API 组,资源类型,资源名称 )执行什么操作(动词)。 - 注意:rule中的apiGroups, resources, resourcesNames, verbs, nonResourceURLs 支持使用通配符
「Role & ClusterRole」
Role
用来定义当前命名空间范围内资源的角色,它通过Rules
显式地定义权限。ClusterRole
用来定义集群范围内资源的角色,它通过Rules
显式地定义权限。
「Role & ClusterRoleBinding」
RoleBinding
将某个ClusterRole
或当前命名空间中的某个Role
绑定到 subjects,使 subjects 获得当前命名空间中的ClusterRole
、Role
所定义的角色权限,例如可以在命名空间 A 中创建 RoleBinding,将命名空间 A 中的 Role 与命名空间 B 中的 ServiceAccount 绑定。那么命名空间 B 中的 ServiceAccount 将获得命名空间 A 中的 Role 定义的权限。ClusterRoleBinding
将某个ClusterRole
绑定到 subjects,使 subjects 获得ClusterRole
所定义的角色权限。
由以上可知,Role
和 ClusterRole
内的 rules
代表一系列显式授予的权限,遵从 Deny-by-Default 安全模型。由于不支持 "deny" 规则,因而不支持显式的排除某些权限。
这一特点使得某些应用场景无法利用 RBAC 授权机制实现:在授予所有已知、未知 CRD 资源操作权限的同时,显式地排除某些敏感权限。但我们可以借助 ABAC、Webhook 授权模式,结合准入控制器来为此类场景的服务账号进行权限管理,从而缓解这类问题。
下面是一个通过 RBAC 授权机制为 ServiceAccount 绑定权限的示例:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: example-clusterrole
namespace: example-ns
rules:
- apiGroups:
- apps
resources:
- daemonsets
- deployments
- replicasets
- statefulsets
resourcesNames:
- test
verbs:
- '*'
- nonResourceURLs:
- /healthz
- /healthz/*
verbs:
- get
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: example-rolebinding
namespace: example-ns
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: example-clusterrole
subjects:
- kind: ServiceAccount
name: example-sa
namespace: example-ns
RBAC 安全风险剖析
Kubernetes 是一个分布式的容器编排系统。除了要确保 Kubernetes 基础组件的配置安全(例如 API Server、Kubelet Server 基本的认证授权配置等,对应 CIS Kubernetes Benchmark 中的第一至第四章中的要求)外,我们还需要对其 RBAC 授权配置进行精细化管理。
正确的授予主体 RBAC 权限能够避免为集群引入不必要的稳定性 & 安全性风险,而不恰当的权限设置可能导致敏感数据泄露、资源滥用、权限提升,甚至威胁整个集群的安全。接下来我们将借助文献和案例来进一步说明其安全风险。
概述
在 Kubernetes 中,可以通过对资源的操作来实现信息窃取、权限提升、横向移动等攻击。例如可以利用 pods/exec 资源的 create 权限通过 API Server 在指定容器内执行任意命令,也可以利用 nodes/proxy 资源的 create 权限直接访问 Kubelet Server 在指定容器内执行任意命令,还可以利用 pods 的 create 权限创建具有安全风险的容器、利用 pods 的 patch 权限在指定 Pod 的容器内执行代码......「随着 Kubernetes 的广泛使用,此类风险在云厂商、PaaS平台、云原生应用、SaaS产品中愈演愈烈,轻则被用于后渗透入侵,重则会给产品引入安全漏洞。」
Palo Alto Networks 的安全研究员深入分析了 Kubernetes 中的所有敏感权限,并根据其危害类型将其分类和分级[2](严重等级请参考开源项目 rbac-police 的风险权限扫描策略集[3])。如下图所示,在这些敏感权限中,有许多都可以被攻击者用于信息泄漏、权限提升、横向移动等攻击,最终实现整个集群的接管。
Palo Alto Networks 的研究结果表明,在针对主流公有云、CNI 厂商的分析中,有将近 50% 的厂商存在容器逃逸后轻易导致集群沦陷的安全问题。另外有 25% 的厂商存在容器逃逸后在一定条件下导致集群沦陷的安全风险[2]。
公开案例
- RBAC Buster:来自 Aqua Sec 的研究者通过蜜罐首次捕获到利用 Kubernetes 的 RBAC 配置漏洞进行攻击的行为,黑客通过创建后门访问集群,导致未授权访问和数据泄露的风险。详见 First-Ever Attack Leveraging Kubernetes RBAC to Backdoor Clusters[5]
- Sys:All:研究团队 The Orca Research Pod 扫描了 250,000 个 GKE 集群(约总数的 2%),发现其中 1300 个集群存在错误配置的角色绑定,其中有 108 个集群允许攻击者使用任何有效的谷歌帐号接管集群。详见:How A Simple Loophole in Google Kubernetes Engine Puts Clusters at Risk of Compromise[6] & GCP-2024-003 security-bulletins[7]
- 在 OWASP Kubernetes Top 10 安全风险中,RBAC 配置错误导致的“权限过多”问题排名第三,可能引发未授权操作和权限提升。详见 OWASP Kubernetes Top 10[8]
风险示例
下面的示例演示了攻击者可以利用任意 secrets 的 create 权限,来获取了包含敏感权限的 ServiceAccount(这里以窃取 prometheus-agent SA 的 token 为例)的 token。对此,我们建议使用专用命名空间中的 Role 来定义所需权限,从而与 kube-system 等敏感命名空间隔离。
下面的示例演示了攻击者可以利用任意 secrets 的 get 权限,来爆破获取保存 SA token 的 secrets。虽然爆破 SA token 需要较长时间(爆破一个拥有 5 个随机字符串的 SA token 最多需要 27^5 次),但此权限也可能被用于窃取其他已知名称的 secrets 资源。对此,我们建议使用 Role 定义角色,或者通过 resourceNames 对 secrets 的权限范围进行约束,而非授予全部命名空间中任意 secrets 的 get 权限。
以上数据和案例表明,Kubernetes RBAC 权限管理已成为一个必须认真对待并及时采取有效防御措施的安全问题。
RBAC 安全研发与运维最佳实践
基于我们在字节跳动内部的安全实践,我们为 RBAC 授权配置总结了如下原则,以指引大家进行 Kubernetes RBAC 权限管理,从而降低由此为集群引入的安全风险。
遵循最小权限原则
在 RBAC 角色中分配权限时,请遵循最小权限原则授予执行任务所需的最低权限。例如:
- 优先使用 Role, RoleBinding 授予一个、多个特定命名空间中的权限。
- 定义 rule 时,使用明确的 apiGroups, resources, verbs 以及 resourceNames 来限定权限范围。
❝
注意:
如果设置了 resourceNames 字段,那么请求权限不能是 list、watch、create、deletecollection,否则请求将不会被允许(「当使用 resourceNames 限制 list、watch 权限范围时,客户端必须在请求参数中指定 fieldSelector=metadata.name%3D{RESOURCENAME} 用于通过授权」)。但当 resourceNames 字段中包含 "" 时,将允许 list 请求。
虽然 RBAC 授权模式不支持通过 resourceNames 来约束 create、deletecollection 权限,但仍然建议通过 resourceNames 来约束 update、patch、get 等权限。
❞
RBAC 权限最小化不应被视作“非黑即白”,哪怕组件的某些敏感权限无法收敛,最小化权限仍然对降低风险、增加入侵检测的机率有重要作用。
避免使用默认角色/用户/用户组
一般情况下,Kubernetes 和基于 Kubernetes 的 PaaS 平台会自动将一些默认角色绑定到默认用户和用户组,以保证系统的正常运行。如需查看 Kubernetes 创建的默认角色和绑定的完整列表,请参阅 Default roles and role bindings。
- 常见的默认角色有:cluster-admin, system:node, system:controller:daemon-set-controller ...
- 常见的系统用户有:system:anonymous, system:kube-controller-manager, system:kube-scheduler, system:kube-proxy, system:serviceaccounts:NAMESPACE:default ...
- 常见的系统用户组:system:unauthenticated, system:authenticated, system:serviceaccounts, system:nodes, system:monitoring, system:masters ...
大部分默认角色(例如 cluster-admin, edit, system:node 等)都会被授予较广泛的权限。因此,我们「不建议」将默认角色绑定到服务账号,除非您知道并接受由此带来的安全风险。用户可以根据实际需要将其绑定到用户账号上。
除此之外,我们「应当避免」为系统用户(例如 system:anonymous, system:serviceaccounts:NAMESPACE:default 等)、系统用户组(例如 system:authenticated, system:serviceaccounts 等)绑定额外的角色,这会导致权限的非预期扩散,引入严重的安全风险。
避免为 default 服务账号授予权限
在附录 1 的“准入控制机制”一节中,我们提到了默认启用的 ServiceAccount 准入控制器。创建 Pod 时如果未指定 ServiceAccount,那么 ServiceAccount 准入控制器会将命名空间内名为 default 的 ServiceAccount 分配给 Pod。
因此,我们「应当避免」为 default 服务账号授予权限,这会导致非预期的权限泄露。
尽量避免使用通配符
*
字符是一个适用于所有内容的通配符,「应尽量避免」在规则中使用通配符。这容易造成授权范围过大,除非您明确知晓并接受此行为可能引入的安全风险。建议您在 RBAC 规则中明确指定 API 组(apiGroups)、资源(resources)、动词(verbs),甚至是资源名称(resourceNames)。
例如,在 verbs
字段中指定 *
将授予 get
、list
、watch
、patch
、update
、deletecollection
和 delete
等权限。下表举例说明了如何避免在规则中使用通配符。
尽量避免使用敏感权限
设计角色前,请先仔细评估存在权限提升、命令执行、信息泄漏等安全风险的权限。例如 secrets 的操作权限、证书签发权限、pods/exec 访问权限等,更多请参考 Kubernetes RBAC - privilege escalation risks[9] 和 风险权限扫描策略集[3]。
为应用服务、控制组件授予敏感权限会给整个集群引入安全风险。在系统设计和开发时,「应尽量避免」使用它们,并配合其他手段进行安全编排、安全加固和入侵检测。
尽量使用单独规则对特定资源授予权限
规划规则时,建议您尝试以下简要步骤,在每个角色中采用更高效、可读、易于维护的规则设计[4]:
- 为主体需要访问的每项资源上的动词草拟单独的 RBAC 规则。
- 草拟规则后,分析规则,以检查多条规则是否具有相同的
verbs
列表。将这些规则合并为一条规则。 - 请将其余的所有规则彼此分散。
这种方法可实现更有条理的规则设计,将对多个资源授予相同动词的规则组合起来,将为资源授予不同动词的规则彼此分散[4]。
例如,如果您的工作负载需要获取 deployments
资源的权限,但需要 daemonsets
资源的 list
和 watch
权限,则您应该在创建角色是使用单独规则。当您将 RBAC 角色绑定到工作负载时,该角色将无法对 deployments
资源进行 watch
操作[4]。
再举一例,如果您的工作负载需要 pods
资源和 daemonsets
资源的 get
和 watch
权限,您可以将它们组合成一条规则,因为工作负载需要在这两个资源上使用相同的动词[4]。
在下表中,这两种规则设计均有效,但拆分规则会根据需要更精细地限制资源访问权限[4]。
安全编排与其它
有些场景下,业务需求可能与安全要求产生冲突。例如一些应用必需某些敏感权限才可以正常运行或提供必要功能。对此,我们建议您考虑采取以下安全编排、纵深防御策略来尽量降低风险。
❝
注意:如果您的组件是 DaemonSet 类型且必需某些敏感权限,我们强烈建议您对其进行重构或缓解(例如通过webhook准入控制器进行校验等)。否则当出现节点沦陷的事件时,整个集群都将遭受威胁。
❞
「使用专用命名空间」
- 如果应用仅需命名空间范围内的权限,那么我们「强烈建议」将其部署在专用命名空间中,而非 kube-system 命名空间、default 命名空间、业务负载所在的命名空间,从而避免不必要的权限扩散。
- 例如某控制组件的 SA 会被授予所在命名空间 secrets 的 list & watch 权限用于维护 ssl 密钥对。我们可以将其部署在专用命名空间(而非 kube-system 命名空间、业务负载所在的命名空间)来降低 SA token 泄漏后的能够带来的安全风险。
- 如果应用需要所有全局范围内的敏感权限,那么「不建议」将其部署在 default 命名空间、业务负载所在的命名空间,从而避免潜在的权限扩散。
「制定特殊调度策略」
- 如果某些组件需要全局范围内的敏感权限,那么「建议」制定合适的调度策略(通过节点污点、准入控制策略等确保调度策略不会被绕过),将此类组件强制调度到专用节点池或使用弹性容器部署组件,从而实现将包含敏感权限的控制组件与业务负载分离。避免业务负载所在节点沦陷后,敏感权限 SA token 泄漏带来的安全风险。
「单独部署敏感组件」
- 您也可以将需要敏感权限的组件部署到独立的控制面,从而解决此类安全风险。
「建设纵深防御」
- 在实际业务场景中,不是所有的敏感权限都能够被消除。尤其是那些风险等级较高,但业务又强依赖的权限。因此,除权限最小化、安全编排外,我们强烈建议您引入主机、Kubernetes 层面的威胁管理与入侵检测能力。从而及时发现并管理风险,告警并响应潜在的入侵行为。
RBAC 安全防护与治理实践
在许多企业中,往往会因为安全意识不足、云原生安全建设开展较晚、使用开源云原生应用等原因,已经为系统引入了大量 RBAC 权限风险。但由于涉及基础设施,并且缺乏相应的知识和手段,针对这类风险的防护和治理往往充满挑战。接下来笔者将向大家介绍我们在字节跳动内部的一些经验和实践,抛砖引玉供大家参考。
整体思路
通过公开案例和红蓝演练等方式,向研发团队展示 K8s RBAC 错误配置对生产环境安全性和稳定性造成的危害。与 DevOps 团队在风险认知上达成一致,从而自上而下对齐治理目标。在开展治理工作前,应根据企业的实际情况制定合理的计划。同时,安全团队应提供治理所需的知识库、工具和系统,与 Ops 团队构建合适的治理流程,以确保治理工作顺利推进。此外,安全团队还应持续加强反入侵能力建设,为 K8s RBAC 等安全风险提供兜底保障。
制定计划
制定切实可行的计划,以及提供必要的工具与系统,是收敛 K8s RBAC 安全风险的重要前提和保障。
「数据驱动」
- 我们建议以数据驱动的方式开展防护和治理工作。通过持续的安全扫描来识别风险、评估风险严重性、明确优先级。通过定位风险引入的原因,来定位责任人和卡点,从而对症下药。
❝
需要指出的是,虽然“RBAC 安全风险剖析”一章已经指出 Kubernetes 中有大量权限存在安全风险,但面对各种场景和现实因素,我们很可能无法要求业务避免使用所有的敏感权限。这需要我们在安全防护与业务需求之间取得平衡。
❞
「明确优先级」
- 我们结合 RBAC 风险权限的可利用性、严重等级,以及影响范围等,将其划分成五个优先级(风险权限及其安全扫描策略请参考 风险权限扫描策略集[3]),以此来推进 K8s RBAC 的权限评估和治理。在完成风险评估和治理前后,我们还需借助入侵检测等机制来进行持续监控与兜底。
「增量管控 & 存量整改」
- 通过在企业的 PaaS 平台、K8s 集群内集成准入控制机制,以此来实现增量管控。建议提供必要的白名单机制,为无法立刻整改的应用进行临时豁免。然后根据定期扫描结果来推动责任人进行存量组件的整改、灰度测试、全量更新。
防护与治理框架
在字节内部,我们构建了如下图所示的安全防护和治理框架,并推进了权限治理工作。
在开发与集成阶段,我们借助最佳安全实践来指导研发部门进行安全的 RBAC 权限设计和开发。并在部分 CI/CD 流水线中集成了安全扫描,对存在危险 RBAC 配置的 chart 产物进行拦截、告警和记录。
在部署阶段,我们通过与 PaaS 平台集成的准入控制机制、K8s 准入控制器来对非法的应用和资源进行增量管控。并指导关键业务通过安全编排等手段来降低具有敏感权限的控制组件的安全风险。这里我们基于开源项目 Kyverno 的策略引擎,实现了 Policy as Code。从而在流水线安全扫描、准入控制中实现策略兼容,降低了安全策略的维护成本。
在运行阶段,我们通过定期扫描(基于开源项目 rbac-police)来持续识别风险。此外,我们还设计实现了针对服务账号和用户账号的行为建模能力。此能力基于账户行为来生成最小权限的角色定义,为组件的权限收敛提供参考。由于不是所有的敏感权限都能得到整改,因此,在实践中我们会基于 K8s 的审计日志进行入侵检测,从而发现潜在的攻击行为。
通过以上机制,我们构建了针对 K8s RBAC 安全风险的防护和治理框架,为字节内部大规模生产集群的 RBAC 安全治理和防护提供了必要能力。
总结
RBAC 是 Kubernetes 中的一项重要的授权机制,正确地配置 RBAC 对于保障基于 Kubernetes 的系统安全至关重要。在设计中,我们应遵循最小权限原则进行权限设计,并理解敏感权限的安全风险,为其引入必要的防护能力。在开发中,我们要注意避免过度授权、权限混乱等问题。在安全防护和运营中,我们还要平衡安全要求和业务需求,持续收敛安全风险,建立纵深防御体系。
希望本文能让大家更好地理解 Kubernetes 的权限体系,了解 RBAC 授权模式的安全风险和最佳安全实践,从而指导系统的安全设计、开发和防护,最终构建更加安全可靠的系统。
参考文献
- https://kubernetes.io/docs/reference/access-authn-authz
- https://www.paloaltonetworks.com/apps/pan/public/downloadResource?pagePath=/content/pan/en_US/resources/whitepapers/kubernetes-privilege-escalation-excessive-permissions-in-popular-platforms
- https://github.com/PaloAltoNetworks/rbac-police/tree/main/lib
- https://cloud.google.com/kubernetes-engine/docs/best-practices/rbac
- https://www.aquasec.com/blog/leveraging-kubernetes-rbac-to-backdoor-clusters
- https://orca.security/resources/blog/sys-all-google-kubernetes-engine-risk
- https://cloud.google.com/kubernetes-engine/security-bulletins#gcp-2024-003
- https://owasp.org/www-project-kubernetes-top-ten
- https://kubernetes.io/docs/concepts/security/rbac-good-practices/#privilege-escalation-risks