能创建角色,就能成为管理员吗
目标
用低权限的 ServiceAccount 尝试 RBAC 权限提升,观察 apiserver 的权限提升防护(escalate/bind)是否真的挡住了这种尝试。并确认只有合法的管理员才能把权限交出去,模拟身份(impersonation)同样是受到单独保护的权限,把这些以报告的形式留下来。
为什么重要
RBAC 里最危险的失误,是把“能创建 Role 的权限”交出去,实际上就等于交出了“能成为任何东西的权限”。如果只要有创建 Role 的权限,就能给自己创建并绑定一个包含 Secret 权限的 Role,那么这个 ServiceAccount 转眼就成了集群管理员。
Kubernetes 在 apiserver 层面堵住了这一点——不能创建或绑定包含自己当前并不拥有的权限的 Role(权限提升防护)。所以即使给了创建 Role 的权限,也不会直接导致特权扩大。亲眼看到这条边界不在代码里,而在 apiserver 的批准阶段,以及为什么像 impersonate 这样的动词被单独当作危险动词来对待,是这个实验的核心。
步骤
- 创建命名空间
kcsa-priv和 ServiceAccountlowpriv。 - 给 lowpriv 绑定只允许创建 Role、RoleBinding 的 Role
rolemaker。 - 以管理员身份创建作为目标的 ClusterRole
secret-admin(Secret 的 get/list)。 - 确认以 lowpriv 的身份尝试创建带 Secret 权限的 Role 时被拒绝,并保存下来。
- 确认以 lowpriv 的身份尝试把 secret-admin 绑定给自己时被拒绝,并保存下来。
- 由管理员正式把 secret-admin 绑定给 lowpriv,让它现在能读取 Secret。
- 确认没有模拟身份权限的 ServiceAccount
auditor2无法冒充别人。 - 把什么被拦住、什么通过了,记录到
report.txt。
参考
- 用
kubectl auth can-i <동사> <리소스> --as=<주체>(占位符依次为动词、资源与主体),以管理员权限确认其他主体的权限。 - 自我提升的尝试,失败才是正常的,所以在命令后面用
2>&1把标准错误也存入文件。 - 拒绝消息里的 'is attempting to grant RBAC permissions not currently held',就是权限提升防护的证据。
- 官方文档:RBAC 授权 · RBAC 良好实践.
准备低权限主体
创建命名空间 kcsa-priv 以及其中的 ServiceAccount lowpriv。暂时不给任何权限。
ServiceAccount 是命名空间里的主体(subject)。把 create 用 --dry-run=client 生成后再 apply,运行多次也安全。这一步不给 lowpriv 绑定任何 Role。
只把创建 Role 的权限交给它
在命名空间 kcsa-priv 中创建 Role rolemaker——apiGroups 为 rbac.authorization.k8s.io,resources 为 roles、rolebindings,verbs 为 create、get、list。然后用 RoleBinding(rolemaker-binding)把这个 Role 绑定到 ServiceAccount lowpriv。lowpriv 可以创建 Role 和 RoleBinding,但不能有 Secret 权限。
RBAC 分为 Role(权限的集合)和 RoleBinding(连接到主体)。对 kubectl create role、create rolebinding 使用 --verb、--resource、--serviceaccount。请记住,lowpriv 在这一步之后获得了创建 Role 的权限,但对 Secret 仍然没有权限——这是后面几步的关键。
诱人的目标——读取 Secret 的权限
以管理员权限创建 ClusterRole secret-admin——对核心组 secrets 的 get、list 权限。这是后面的步骤里,lowpriv 试图拿到手的目标权限。
ClusterRole 是不绑定命名空间的权限集合。对 kubectl create clusterrole 使用 --verb、--resource。这一步直接用默认的 kubeconfig(cluster-admin)创建即可。
想把自己没有的权限给自己,却被挡住
现在以 lowpriv 的身份(--as=system:serviceaccount:kcsa-priv:lowpriv),试着在命名空间 kcsa-priv 中创建包含 Secret get 权限的 Role sneaky。lowpriv 没有 Secret 权限,所以 apiserver 必须拒绝这次创建。把拒绝消息原样保存到 /root/kcsa-priv/escalate-denied.txt。
RBAC 有权限提升防护规则——不能创建包含自己没有的权限的 Role。即使有创建 Role 的权限(第 2 步),这条规则也会另外起作用。命令失败才是正常的,所以要把标准错误也一起存入文件(2>&1)。消息里会看到 'forbidden' 和 'not currently held'。
想把目标权限绑定给自己,却被挡住
以 lowpriv 的身份(--as=system:serviceaccount:kcsa-priv:lowpriv),试着在命名空间 kcsa-priv 中创建把 ClusterRole secret-admin 绑定给自己(lowpriv)的 RoleBinding grab。这同样是把 lowpriv 没有的权限交出去,所以必须被拒绝。把拒绝消息保存到 /root/kcsa-priv/bind-denied.txt。
lowpriv 可以创建 RoleBinding(第 2 步)。但是,要绑定包含自己没有的权限的 Role,要么自己已经有这个权限,要么对目标 Role 有 bind 权限。两者都没有,所以 apiserver 会拦住。失败才是正常的,所以用 2>&1 把错误也存下来。
管理员正式交出权限
这次用管理员权限(默认 kubeconfig),在命名空间 kcsa-priv 中创建把 ClusterRole secret-admin 绑定到 ServiceAccount lowpriv 的 RoleBinding granted。现在 lowpriv 应该能够在 kcsa-priv 中 get Secret。
边界不在于 lowpriv 的“创建 RoleBinding 的能力”,而在于 apiserver 的权限提升检查。已经拥有这个权限的管理员创建绑定,就能通过。用 --clusterrole 和 --serviceaccount 创建绑定,并确认 auth can-i get secrets --as=<lowpriv> 是否变成了 yes。
冒充别人的权限也受到单独保护
在命名空间 kcsa-priv 中创建 ServiceAccount auditor2(不给它模拟身份的权限)。确认 auditor2 能不能冒充其他用户(impersonate users),看到没有权限的主体无法模拟身份。
模拟身份(--as)本身就是另外的 RBAC 权限(对 users/groups/serviceaccounts 资源的 impersonate 动词)。没有被授予权限的 ServiceAccount,auth can-i impersonate users --as=<그 SA>(占位符为该 SA)必须是 no。请与管理员(默认 kubeconfig)是 yes 这一点做个对比。
把什么被拦住、什么通过了,以账簿的形式留下
把观察结果恰好写成四行,保存到 /root/kcsa-priv/report.txt——self-escalate-role=blocked、self-bind-clusterrole=blocked、admin-grant=allowed、impersonate-without-verb=blocked。值必须与集群的实际行为一致。
评分器会在集群里重新确认这四行——以 lowpriv 重新尝试自我提升是否失败,管理员绑定之后 lowpriv 能否 get Secret,auditor2 能否模拟身份。不要靠猜测来写,要把前面步骤里看到的结果原样抄过来。