随着云计算、微服务架构、服务网格等分布式应用架构技术的普及,以 Docker、Kubernetes 为代表的容器化技术,逐步被业务所认可使用。当前 Kubernetes 已逐渐成为生产环境中所必备的基础架构系统,安全问题也随着日渐凸显。本文围绕着 Kubernetes RBAC 权限控制,介绍了在 Kubernetes 可导致权限提升的权限配置以及提供相应的解决方案。
1. 风险现状
在以往使用以 OpenStack 为代表的虚拟化技术中,单个服务收到安全问题影响的范围往往是可控的,通常情况下一台机器只会影响到一个业务,但当在 Kuberntes 中则完全不同,通常一台 Kubernetes Node 节点会负载多个业务的多个服务,其中一个受到攻击,且同时存在异常权限配置则可能会影响到这个 Node 节点上或这个集群上的所有服务。
通过调研角色绑定中管理者字段,我们发现目前 Kubernetes 中创建角色及绑定角色来源主要分为几类:
-
直接调用 Kubernetes Api 创建或使用 kubectl 创建(一般为业务使用)
-
Kubernetes 管控平台创建(如 Dashboard)
-
其他 Kubernetes 组件创建(如 存储插件、网络插件、日志采集插件 等)
而其中存在的安全风险主要如下:
-
在部署某些 Kubernetes 组件时,由于未正确配置导致权限配置过大,或者组件本身权限设计不合理导致的权限配置过大
-
业务不合理需求导致权限配置过大
-
业务临时排错使用高权限但后续未及时进行回收
通过调研的市场上的部分容器安全产品发现目前对于 Kubernetes 权限这一块只是采集了 API 的数据进行简单的罗列,未能以一种直观的形势展示出集群中的权限安全风险。
因此我们尝试利用图技术,将容器集群中的账户、资源、网络等信息以权限进行连接,从而实现直观的方式展示 Kubernetes 集群中的权限风险。
2. Kubernetes 高风险权限梳理
先从一个简单的例子了解一下怎么在 Kubernetes 使用 RBAC 赋予一个 ServiceAccount 权限:
apiVersion: v1
kind: ServiceAccount
metadata:
name: create-pod-account
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: create-pod-clusterrole
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["create"]
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: create-pod-rolebinding
namespace: default
subjects:
- kind: ServiceAccount
name: create-pod-account
namespace: default
roleRef:
kind: ClusterRole
name: create-pod-clusterrole
apiGroup: rbac.authorization.k8s.io
该配置文件以 “—” 符号进行分割为三个部分:
-
第一部分作用为在 default 命名空间下创建一个名为 create-pod-account 的 Service Account
-
第二部分作用为创建一个名为 create-pod-clusterrole 的 ClusterRole,这个角色具有创建 Pod 的权限。
-
第三部分作用为将第一部分创建的 create-pod-account 绑定到 create-pod-clusterrole 这个角色上,这也就意味着 create-pod-account 具有了 create-pod-clusterrole 中所定义的权限,并且用于是使用的 ClusterRoleBinding,则这个权限的范围是在整个集群上的。
从上面这个例子中我们可以了解到 Kubernetes 中的权限主要由以下三部分组成:
-
账户主体:账户主体包含 User、ServiceAccount、Group
-
角色主体:ClusterRole(集群范围)、Role(命名空间范围)
-
角色权限:角色所绑定的权限,包括(verb、resource/subResource、resourceName)
下面我们将介绍一些 Kubernetes 集群中的一些高权限配置:
2.1 操纵授权/认证类权限
2.1.1 impersonate verb 权限
用户可以通过伪装头部字段来以另一个用户的身份执行操作,具体参考文档:https://kubernetes.io/zh-cn/docs/reference/access-authn-authz/authentication/#user-impersonation
以下是一个权限配置样例:
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: impersonator
rules:
- apiGroups: [""]
resources: ["users"]
verbs: ["impersonate"]
resourceNames: ["app-fe-user"]
在上述例子中 [email protected] 用户可以通过如下命令模拟 app-fe-user 用户的权限
kubectl --as-user app-fe-user ...
这也意味着 [email protected] 用户通过 impersonate 获得了 app-fe-user 用户的权限。
2.1.2 escalate verb 权限
参考 Kubernetes 官方文档 https://kubernetes.io/zh-cn/docs/reference/access-authn-authz/rbac/#restrictions-on-role-creation-or-update中的介绍,通常情况下 RBAC 会阻止用户创建比他所拥有的更多权限的角色。
而只有在符合以下条件的情况下,才能赋予角色新的授权:
-
根据需要赋予他们一个角色,允许他们根据需要创建/更新 Role 或 ClusterRole 对象。
-
授予他们在所创建/更新角色中包含特殊权限的权限:
-
隐式的为他们授权(如果他们试图创建或更改 Role 或 ClusterRole 的权限,但自身没有授予相应的权限,API 请求将被禁止)
-
通过允许他们在 Role 或 ClusterRole 资源上执行 escalate 动词显式完成授权
以下是一个权限配置的样例:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: role-escalate
rules:
- apiGroups: ["rbac.authorization.k8s.io"]
resources: ["roles"]
verbs: ["create", "escalate"]
拥有该权限的角色可以通过 create 及 escalate verb 权限创建一个高于当前自身权限的角色
2.1.3 bind verb 权限
bind 与 escalate 权限类似,在默认情况下用户创建 RoleBinding 或 ClusterRolebinding 时,无法绑定一个高于当前已有权限的角色,如果用户同时存在 bind verb 权限,则可以绑定任意权限的角色。
以下是一个权限配置的样例:
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: bind-clusterrole
rules:
- apiGroups: ["rbac.authorization.k8s.io"]
resources: ["rolebindings"]
verbs: ["create"]
- apiGroups: ["rbac.authorization.k8s.io"]
resources: ["clusterroles"]
verbs: ["bind"]
拥有上述权限的角色可以通过创建 RoleBinding 使当前用户扮演默认高权限角色 admin
2.1.4 approve signers 权限
approve signers 权限通常需要与 create certificatesigningrequests 权限组合使用才能达成提权目的。
以下是一个权限配置的样例:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: csr-approver
rules:
- apiGroups:
- certificates.k8s.io
resources:
- certificatesigningrequests
verbs:
- create
- get
- apiGroups:
- certificates.k8s.io
resources:
- signers
verbs:
- approve
拥有该权限的角色,可以通过 certificatesigningrequests 创建 API Server 客户端证书。之后使用 kubectl 使用申请到的客户端证书以获取管理权限
kubectl --client-certificate xxx.crt --client-key xxxx.key --certificate-authority xxxx.crt --server https://xxxxx:6443 get pods -A
2.1.5 control mutating webhooks 权限
如果用户被赋予了 control mutating webhooks 则用户可以完成以下操作:
-
创建 MutatingWebhookConfiguration 资源对象,注册新的 mutating webhooks
-
配置 webhook 可以拦截哪些资源对象(如 Pod、Deployment 等)
-
定义 webhook 可以对资源对象执行哪些操作,如添加字段、使用高权限 ServiceAccount,注入 sidecar 容器等
以下是一个权限配置样例:
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: bind-clusterrole
rules:
- apiGroups: ["admissionregistration.k8s.io"]
resources: ["mutatingwebhookconfigurations"]
verbs: ["create","update","patch"]
2.2 窃取 Token 类权限
2.2.1 get secrets 权限
get secrets 权限只能通过指定的 secrets 名称获取其敏感数据,无法列出所有的 secrets 列表,但 Kubernetes secrets 的名称是可穷举的。
Kubernetes secrets 名称由 <service-account-name>-token-<随机值> 这三部分组成,查看 Kubernetes secrets 生成源码可以发现,随机值部分是从 bcdfghjklmnpqrstvwxz2456789 中随机生成 5 位字符,共有 14348907 种可能性,这意味这可以通过穷举的方式获取到集群中默认高权限 serviceaccount 的 token 信息,以实现提权。
2.2.2 create serviceaccounts/token 权限
当用户存在 create serviceaccount/token 权限的时候可以使用以下命令创建 token
kubectl create token my-token --serviceaccount=my-sa
2.2.3 create pods 权限
具有 create:pods 权限的账户可以执行以下高风险操作:
-
在容器集群上创建挖矿 Pod 恶意占用集群资源
-
创建 hostPath 高风险目录(例如 /root、/etc 等)实现容器逃逸,获取到宿主机 Node 节点的管理员权限
-
绑定集群内高权限的 ServiceAccount,通过高权限 ServiceAccount Token 接管整个容器集群
-
挂载 configmap 信息,窃取服务配置以进一步执行渗透
以下是绑定了宿主机 /etc 目录以使用 crontab 逃逸到宿主机 Node 节点的 Pod 资源配置样例:
apiVersion: v1
kind: Pod
metadata:
name: mount-etc
namespace: default
spec:
containers:
- name: nginx
imagePullPolicy: Always
image: nginx
volumeMounts:
- mountPath: /data
name: etc-mount
volumes:
- name: etc-mount
hostPath:
path: /etc
2.2.4 control pod controllers 权限
Pod Controllers 是 Kubernetes 中的一类 controller,它负责 Pod 的管理和调度。比如 Deployment、StatefulSet、DaemonSet、Job 等都是常见的 Pod Controllers。
如果一个用户被赋予了 control pod controllers 权限,则利用方式如 create pods 权限相同,均可通过挂载高权限 ServiceAccount 或者 hostPath的方式获取到 Node 节点权限甚至集群管理员权限。
2.3 RCE 类权限
2.3.1 create pods/exec 权限
pods/exec 是 Kubernetes 中的一种资源,用于在 Pod 内的 Shell 中运行命令。此权限适用于想要访问容器并运行命令的管理员。这就像为容器创建 SSH 会话一样。
如果我们拥有这个特权,实际上就有能力控制所有 Pod。为此,我们需要使用以下命令:
kubectl exec -it <POD_NAME> -n <NAMESPACE> -- sh
2.3.2 create nodes/proxy 权限
用户拥有 create nodes/proxy 权限,则可以通过调用 kubelet API 实现在 Pod 中执行命令,如果该 Pod 挂载了拥有集群管理权限的 ServiceAccount 则可以通过该 ServiceAccount token 接管整个集群。
权限配置样例:
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: node-reader
rules:
- apiGroups: [""]
resources: ["nodes/proxy"]
verbs: ["create"]
在 nginx pod 中执行命令:
TOKEN=$(kubectl get secret read-node-token-vv7vf -o jsonpath={.data.token} | base64 -d)
curl -ik -H "Authorization: Bearer $TOKEN" -XPOST https://10.0.0.81:10250/run/default/nginx/nginx -d "cmd=whoami"
3. Kubernetes 权限风险识别系统设计
通过采集 User、Group、Role 等信息作为顶点,边则代表着顶点与顶点之间的权限流动或者顶点与顶点之间的属性关系。
我们在图中定义了以下顶点和边,以用来描述 Kubernetes 中的权限关系。
3.1 顶点 – User
属性信息:
clusterName |
User 所属集群的名称 |
name |
User 名称 |
namespace |
User 所属的 Namespace 名称 |
kind |
类型,分为 User、ServiceAccount |
automountToken |
是否允许自动挂载 ServiceAccount Token |
firstManager |
首次创建的 Manager 信息 |
firstManagerTime |
首次创建的时间 |
lastManager |
最近一次修改的 Manager 信息 |
lastManagerTime |
最近一次修改的时间 |
在 Kubernetes 中 User 可以分为两类,一类是 ServiceAccount 由 Kubernets 管理维护且记录到 Kubernetes Etcd 中,另一部分由身份提供商管理,不在 Kubernetes Etcd 中记录,仅记录其权限信息,由证书标识用户。因此在描述 User 时将 User 和 ServiceAccount 都采用一个 User 顶点,并使用 Kind 属性进行区分。
3.2 顶点 – Group
属性信息:
clusterName |
Group 所属集群的名称 |
name |
Group 名称 |
3.3 顶点 – Role
属性信息:
clusterName |
Role 所属集群的名称 |
name |
Role 名称 |
namespace |
Role 所属的 Namespace 名称 |
roleType |
角色类型,分为 ClusterRole、Role |
criticalPermFlag |
高权限风险标记 |
isProjection |
是否为投影角色 |
firstManager |
首次创建的 Manager 信息 |
firstManagerTime |
首次创建的时间 |
lastManager |
最近一次修改的 Manager 信息 |
lastManagerTime |
最近一次修改的时间 |
在 Kubernetes 中 ClusterRole 也可以通过 RoleBinding 绑定到命名空间范围,在这种情况下角色的权限范围发生了变化,因此为了便于更好的区分,我们会在 RoleBinding 中定义的命名空间中生成一个投影角色,投影角色和原本的ClusterRole是相同的属性配置,只是权限范围发生了变化。
3.4 顶点 – Authority
属性信息:
clusterName |
Authority 所属集群的名称 |
name |
Authority 名称 |
apiGroup |
Role 规则配置中的 apiGroup 字段,用来描述权限所属的 api 组 |
resource |
资源类型 |
subResource |
资源子类型,如 pods/exec ,则资源子类型为 exec |
verb |
动作,如 get、create、update、patch、watch 等 |
nonResourceURL |
非资源 url 信息,如 /api 等 |
resourceName |
资源名称 |
3.5 顶点 – Node
属性信息:
clusterName |
Node 所属集群的名称 |
name |
Node 名称 |
firstManager |
首次创建的 Manager 信息 |
firstManagerTime |
首次创建的时间 |
lastManager |
最近一次修改的 Manager 信息 |
lastManagerTime |
最近一次修改的时间 |
3.6 顶点 – Namespace
属性信息:
clusterName |
Namespace 所属集群的名称 |
name |
Namespace 名称 |
3.7 顶点 – Cluster
属性信息:
name |
集群名称 |
在定义如上顶点信息之后,我们需要定义顶点与顶点之间的边关系,在本系统中边关系代表着一种权限的跃迁或是顶点拥有的属性。
3.8 边关系 – ActTo
ActTo 关系主要用来描述用户实体(User、ServiceAccount、Group)与角色实体(Role、ClusterRole)之间的一种关系,用来描述该用户/组可以扮演指定的角色。
3.8 边关系 – MemberOf
MemberOf 边关系主要用来描述 User/ServiceAccount 与 Group 之间的关系,代表着用户属于某用户组,这也意味这用户可以继承用户组的权限。
3.9 边关系 – HoldAuthority
HoldAuthority 边关系主要用来描述 Role 与 Authority 之间的关系,代表着角色实际拥有哪些权限
3.10 边关系 – BindTo
BindTo 边关系主要用来描述角色与角色之间的关系,它主要描述了拥有该角色权限的账户具有可以通过 RoleBinding/ClusterRoleBinding 绑定其他角色的权限。
具体场景参考:2.1 操纵授权/认证类权限 – bind verb 权限
3.11 边关系 – ManagerMutatingWebhook
ManagerMutatingWebhook 边关系主要用来描述角色与集群之间的关系,它主要描述了拥有该角色权限的账户可以通过配置 MutatingWebhookConfigration 以实现对集群的完全控制。
具体场景参考: 2.1 操纵授权/认证类权限 – control mutating webhooks 权限
3.12 边关系 – ManagerMutatingWebhook
ManagerMutatingWebhook 边关系主要用来描述角色与集群之间的关系,它主要描述了拥有该角色权限的账户可以通过配置 MutatingWebhookConfigration 以实现对集群的完全控制。
具体场景参考: 2.1 操纵授权/认证类权限 – control mutating webhooks 权限
3.13 边关系 – GetSecrets
GetSecrets 边关系主要描述角色与 ServiceAccount 之间的关系,它主要描述了该角色是否有权限读取指定 ServiceAccount 的 Token 信息。
具体场景参考:2.2 窃取 Token 类权限 – get secrets 权限
3.14 边关系 – ExecPod
ExecPod 边关系主要描述了角色与 Pod 之间的关系,它主要描述了该角色是否有权限在指定 Pod 中执行系统命令。
具体场景参考:2.3 RCE 类权限 – create pods/exec 权限
3.15 边关系 -ControlPod
ControlPod 边关系主要描述了角色与 Pod 之间的关系,它主要描述了角色是否有权限 create、update、patch Pod。
具体场景参考:2.2 窃取 Token 类权限 – create pods 权限
3.16 边关系 – ControlPodController
ControlPodController 边关系主要描述了角色与 Deployment、StatefulSet、DaemonSet 等 Pod Controller 之间的关系,它主要描述了角色是否有权限 create、update、patch pod controller。
具体场景参考:2.2 窃取 Token 类权限 – control pod controllers
3.17 边关系 – CreateToken
CreateToken 边关系主要描述了角色与 ServiceAccount 之间的关系,它主要描述了角色是否有权限创建指定 SeviceAccount 的 Token 信息。
具体场景参考: 2.2 窃取 Token 类权限 – create serviceaccounts/token 权限
3.18 边关系 – CreateNodeProxy
CreateNodeProxy 边关系主要描述了角色与 Node 节点之间的关系,它主要描述了角色是否有 create nodes/proxy 权限,以影响指定 Node 上的 Pod。
具体场景参考:2.3 RCE 类权限 – create nodes/proxy 权限
3.19 边关系 – GenerateRead
GenerateRead 边关系主要描述了角色与 Cluster、Namespace 之间的关系,它主要描述了角色是否有针对于全局的 get verb 权限。
一个权限配置样例:
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: generate-read
rules:
- apiGroups: ["*"]
resources: ["*"]
verbs: ["get"]
拥有上述权限配置的角色则存在 GenerateRead 权限,如果该角色是 ClusterRole 且由 ClusterRoleBinding 绑定则与 Cluster 之间存在 GenerateRead 关系,否则与 Namespace 之间存在 GenerateRead 关系。
3.20 边关系 – GenerateWrite
GenerateWrite 边关系主要描述了角色与Cluster、Namespace 之间的关系,它主要描述了角色是否有针对于全局的 update、patch、create verb 权限。
一个权限配置样例:
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: generate-write
rules:
- apiGroups: ["*"]
resources: ["*"]
verbs: ["create","update","patch"] # 只需由任意一个 verb, 即存在 GenerateWrite 关系
拥有上述权限配置的角色则存在 GenerateWrite 权限,如果该角色是 ClusterRole 且由 ClusterRoleBinding 绑定则与 Cluster 之间存在 GenerateWrite 关系,否则与 Namespace 之间存在 GenerateWrite 关系。
3.21 边关系 – GenerateAll
GenerateAll 边关系主要描述了角色与Cluster、Namespace 之间的关系,它主要描述了角色是否有针对于全局的所有权限。
一个权限配置样例:
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: generate-read
rules:
- apiGroups: ["*"]
resources: ["*"]
verbs: ["*"]
拥有上述权限配置的角色则存在 GenerateAll 权限,如果该角色是 ClusterRole 且由 ClusterRoleBinding 绑定则与 Cluster 之间存在 GenerateAll 关系,否则与 Namespace 之间存在 GenerateAll 关系。
3.22 边关系 – RunOn
RunOn 边关系主要描述了 Node 与 Pod 之间的关系,通过读取 Pod 中的属性值,判断当前状态下 Pod 运行在哪一个 Node 节点上,利用该关系可以帮助识别 create nodes/proxy 影响范围以及如果 Pod 存在逃逸配置其进一步的影响范围。
4. 部分使用场景举例
4.1 快速分析从某一用户提升到集群管理员权限的最短路径
match p=shortestpath((:User)-[*1..]->(:Cluster))
return p
4.2 分析某一角色由哪些用户扮演
match p=(n)-[:ActTo]->(role:Role {name:"system:controller:bootstrap-signer"})
return p
4.3 分析集群中哪些角色存在可提权权限
match (role:Role)-[:HoldAuthority]->(auth:Authority)
where (auth.verb in ['bind'] and auth.resource in ['clusterroles', 'roles']) or (auth.verb='create' and auth.resource in ['clusterrolebindings', 'rolebindings'])
return role.name,role.roleType,auth.apiGroup,auth.resource,auth.verb
5. 结语
通过利用识别到的权限数据可以对接到 SOAR 上结合 ApiServer 的审计日志进行进一步告警优化,帮助安全运营了解到集群内的高权限账户的活动行为,以及在推动容器集群安全治理专项上发挥作用。
由于作者本身能力有限,文章中不可避免会有疏漏错误之处,希望得到大家的指正,如有任何问题可通过邮件或微信与我联系:h7hac9#gmail.com,微信号: h7hac9
原文始发于微信公众号(贝壳安全应急响应中心):技术分享|基于图实现 Kubernetes 异常权限检测