Kubernetes 是一个开源的容器编排平台,用于自动化部署、扩展和管理容器化应用程序。它提供了丰富的功能,如服务发现、负载均衡、自动缩放等。随着 Kubernetes 在云原生领域的广泛应用,「有效管理谁可以对 Kubernetes 集群执行何种操作变得至关重要」。本文将简要介绍 Kubernetes的认证与授权体系以及RBAC授权原理。通过实际案例展示RBAC管理不当可能导致的安全风险,然后向大家分享RBAC安全研发与运维的最佳实践,以及我们在字节跳动内部的安全防护和治理经验。
❝如果您对相关背景知识比较了解,可直接跳转到“RBAC 安全风险剖析”、“RBAC 安全研发与运维最佳实践” 章节阅读。
❞
本章节将对 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 的认证、授权、准入控制的技术细节。
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 组,资源类型,资源名称 )执行什么操作(动词)。「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
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]。
下面的示例演示了攻击者可以利用任意 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 授权配置总结了如下原则,以指引大家进行 Kubernetes RBAC 权限管理,从而降低由此为集群引入的安全风险。
在 RBAC 角色中分配权限时,请遵循最小权限原则授予执行任务所需的最低权限。例如:
❝注意:
如果设置了 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, edit, system:node 等)都会被授予较广泛的权限。因此,我们「不建议」将默认角色绑定到服务账号,除非您知道并接受由此带来的安全风险。用户可以根据实际需要将其绑定到用户账号上。
除此之外,我们「应当避免」为系统用户(例如 system:anonymous, system:serviceaccounts:NAMESPACE:default 等)、系统用户组(例如 system:authenticated, system:serviceaccounts 等)绑定额外的角色,这会导致权限的非预期扩散,引入严重的安全风险。
在附录 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]:
verbs
列表。将这些规则合并为一条规则。这种方法可实现更有条理的规则设计,将对多个资源授予相同动词的规则组合起来,将为资源授予不同动词的规则彼此分散[4]。
例如,如果您的工作负载需要获取 deployments
资源的权限,但需要 daemonsets
资源的 list
和 watch
权限,则您应该在创建角色是使用单独规则。当您将 RBAC 角色绑定到工作负载时,该角色将无法对 deployments
资源进行 watch
操作[4]。
再举一例,如果您的工作负载需要 pods
资源和 daemonsets
资源的 get
和 watch
权限,您可以将它们组合成一条规则,因为工作负载需要在这两个资源上使用相同的动词[4]。
在下表中,这两种规则设计均有效,但拆分规则会根据需要更精细地限制资源访问权限[4]。
有些场景下,业务需求可能与安全要求产生冲突。例如一些应用必需某些敏感权限才可以正常运行或提供必要功能。对此,我们建议您考虑采取以下安全编排、纵深防御策略来尽量降低风险。
❝注意:如果您的组件是 DaemonSet 类型且必需某些敏感权限,我们强烈建议您对其进行重构或缓解(例如通过webhook准入控制器进行校验等)。否则当出现节点沦陷的事件时,整个集群都将遭受威胁。
❞
「使用专用命名空间」
「制定特殊调度策略」
「单独部署敏感组件」
「建设纵深防御」
在许多企业中,往往会因为安全意识不足、云原生安全建设开展较晚、使用开源云原生应用等原因,已经为系统引入了大量 RBAC 权限风险。但由于涉及基础设施,并且缺乏相应的知识和手段,针对这类风险的防护和治理往往充满挑战。接下来笔者将向大家介绍我们在字节跳动内部的一些经验和实践,抛砖引玉供大家参考。
通过公开案例和红蓝演练等方式,向研发团队展示 K8s RBAC 错误配置对生产环境安全性和稳定性造成的危害。与 DevOps 团队在风险认知上达成一致,从而自上而下对齐治理目标。在开展治理工作前,应根据企业的实际情况制定合理的计划。同时,安全团队应提供治理所需的知识库、工具和系统,与 Ops 团队构建合适的治理流程,以确保治理工作顺利推进。此外,安全团队还应持续加强反入侵能力建设,为 K8s RBAC 等安全风险提供兜底保障。
制定切实可行的计划,以及提供必要的工具与系统,是收敛 K8s RBAC 安全风险的重要前提和保障。
「数据驱动」
❝需要指出的是,虽然“RBAC 安全风险剖析”一章已经指出 Kubernetes 中有大量权限存在安全风险,但面对各种场景和现实因素,我们很可能无法要求业务避免使用所有的敏感权限。这需要我们在安全防护与业务需求之间取得平衡。
❞
「明确优先级」
「增量管控 & 存量整改」
在字节内部,我们构建了如下图所示的安全防护和治理框架,并推进了权限治理工作。
在开发与集成阶段,我们借助最佳安全实践来指导研发部门进行安全的 RBAC 权限设计和开发。并在部分 CI/CD 流水线中集成了安全扫描,对存在危险 RBAC 配置的 chart 产物进行拦截、告警和记录。
在部署阶段,我们通过与 PaaS 平台集成的准入控制机制、K8s 准入控制器来对非法的应用和资源进行增量管控。并指导关键业务通过安全编排等手段来降低具有敏感权限的控制组件的安全风险。这里我们基于开源项目 Kyverno 的策略引擎,实现了 Policy as Code。从而在流水线安全扫描、准入控制中实现策略兼容,降低了安全策略的维护成本。
在运行阶段,我们通过定期扫描(基于开源项目 rbac-police)来持续识别风险。此外,我们还设计实现了针对服务账号和用户账号的行为建模能力。此能力基于账户行为来生成最小权限的角色定义,为组件的权限收敛提供参考。由于不是所有的敏感权限都能得到整改,因此,在实践中我们会基于 K8s 的审计日志进行入侵检测,从而发现潜在的攻击行为。
通过以上机制,我们构建了针对 K8s RBAC 安全风险的防护和治理框架,为字节内部大规模生产集群的 RBAC 安全治理和防护提供了必要能力。
RBAC 是 Kubernetes 中的一项重要的授权机制,正确地配置 RBAC 对于保障基于 Kubernetes 的系统安全至关重要。在设计中,我们应遵循最小权限原则进行权限设计,并理解敏感权限的安全风险,为其引入必要的防护能力。在开发中,我们要注意避免过度授权、权限混乱等问题。在安全防护和运营中,我们还要平衡安全要求和业务需求,持续收敛安全风险,建立纵深防御体系。
希望本文能让大家更好地理解 Kubernetes 的权限体系,了解 RBAC 授权模式的安全风险和最佳安全实践,从而指导系统的安全设计、开发和防护,最终构建更加安全可靠的系统。
「通过“容器安全防护平台”审计K8s RBAC错误配置」
火山引擎容器安全防护平台的“RBAC洞察”功能支持定期自动扫描 K8s RBAC 错误配置,帮助您高效收敛攻击面。
同时支持自定义审计规则,满足企业个性化风险治理诉求。
Kubernetes 是一个以容器技术为基础,以声明式 API Server 为核心的分布式容器编排系统。作为 Kubernetes 对外服务的唯一接口,API Server 具备多种认证、授权、准入控制机制。
认证器将对请求者进行身份识别,识别出对应的 Username, Groups, UID, Extra fields。所有认证通过的用户都会位于 system:authenticated 用户组中。
❝注意:当用户使用
❞AlwaysAllow
以外的授权器时,将默认开启匿名访问。认证失败的用户将位于 system:unauthenticated 用户组,其 Username 则为 system:anonymous。
Kubernetes 支持多种认证机制,可用于不同场景下的客户端身份认证。
「证书认证」
「Token 认证」
--token-auth-file
参数来设置一个保存用户信息和 token 的 csv 文件。由于 token 无法动态吊销,因此不鼓励用户使用。Node 授权模式:此授权模式仅用于对 Kubelet 的账号进行授权,它基于调度到所在节点的 pods 为 Kubelet 授予相关权限,从而确保 Pod 能够正常运行。所有 Kubelet 账号的用户名格式为system:node:[NODE_NAME]
,并且都位于 system:nodes
用户组。
RBAC 授权模式:此授权模式可基于用户角色对 Kubernetes 内资源进行细粒度访问控制。Kubernetes 的一大特点是将底层基础设施及其关系抽象为不同类型的资源对象,并通过声明式 API 暴露出来。而 RBAC 则是被广泛使用的授权模式,它允许用户通过管理 rbac.authorization.k8s.io API 组内资源对象的方式来进行灵活的权限管理。更多请参见“Kubernetes RBAC 授权原理”一章。
ABAC 授权模式:此授权模式提供了基于属性的访问控制 (ABAC) 机制,允许用户通过结合属性的策略向用户授予资源的访问权限。用户需要以 JSON 形式定义访问控制策略,并且每次更新策略都需要重启 API Server,因而不常使用。
Webhook 授权模式:此授权模式支持用户配置 Webhook Server 来扩展 Kubernetes 的授权能力。
AlwaysAllow 授权模式:此授权模式将允许所有的用户请求。
❝注意:当开启 API Server 的
❞--anonymous-auth=true
后,匿名用户也可以访问 Kubernetes 的所有资源。
AlwaysDeny 授权模式:此授权模式将拦截所有的用户请求。
Kubernetes 内置了大量准入控制器,它们可以在 API Server 处理 create, delete, modify 请求之前,对请求进行变异(Mutating)、验证(Validating),从而实现强制修改资源、访问控制等功能。Kubernetes 内置了如下准入控制器:
AlwaysAdmit, AlwaysDeny, AlwaysPullImages, CertificateApproval, CertificateSigning, CertificateSubjectRestriction, ClusterTrustBundleAttest, DefaultIngressClass, DefaultStorageClass, DefaultTolerationSeconds, DenyServiceExternalIPs, EventRateLimit, ExtendedResourceToleration, ImagePolicyWebhook, LimitPodHardAntiAffinityTopology, LimitRanger, MutatingAdmissionWebhook, NamespaceAutoProvision, NamespaceExists, NamespaceLifecycle, NodeRestriction, OwnerReferencesPermissionEnforcement, PersistentVolumeClaimResize, PersistentVolumeLabel, PodNodeSelector, PodSecurity, PodTolerationRestriction, Priority, ResourceQuota, RuntimeClass, ServiceAccount, StorageObjectInUseProtection, TaintNodesByCondition, ValidatingAdmissionPolicy, ValidatingAdmissionWebhook.
Kubernetes 默认启用的准入控制器如下所示:
NamespaceLifecycle, LimitRanger, ServiceAccount, TaintNodesByCondition, PodSecurity, Priority, DefaultTolerationSeconds, DefaultStorageClass, StorageObjectInUseProtection, PersistentVolumeClaimResize, RuntimeClass, CertificateApproval, CertificateSigning, ClusterTrustBundleAttest, CertificateSubjectRestriction, DefaultIngressClass, MutatingAdmissionWebhook, ValidatingAdmissionPolicy, ValidatingAdmissionWebhook, ResourceQuota
接下来简要介绍如下几个常用的准入控制器
Kubelet 作为 Kubernetes 的 Agent 运行在集群中的每个节点上,它主要通过监控集群资源、本地资源的变化来完成 pods/containers 的创建、secrets/configmap 同步等工作。除此之外,Kubelet 还会通过本地端口对外提供服务。其中 10250 端口提供了多个接口,对外提供容器内命名执行、容器端口转发、获取容器和节点日志、获取节点状态、获取指标信息等功能。
对此 Kubernetes 也为 Kubelet Server 提供了多种认证和授权机制。
下图以 kubectl exec 命令的背后逻辑为例,展示 Kubelet Server 对客户端进行认证和授权的整个过程。
执行 kubectl exec 后都发生了什么?