名字相同,权限却不同
目标
给一个 ServiceAccount reader 依次绑定 Role、RoleBinding、ClusterRole、ClusterRoleBinding,
用 kubectl auth can-i --as 亲自确认权限到哪里为止(命名空间 / 集群),
以及到什么为止(动词、资源)。同时学习如何关闭令牌的自动挂载。
为什么重要
进入 Kubernetes API 服务器的每个请求,都按“谁(主体)对什么(动词)在哪里(资源、命名空间)”来判定。
RBAC 只有允许列表,没有拒绝规则,所以权限只会随着绑定增多以并集的方式变大。
即使同样叫 reader,绑定了哪些范围上的哪些绑定,它能做的事就完全不同。
Role 只在一个命名空间内有效,像节点这样没有命名空间的资源, 只能通过 ClusterRole 和 ClusterRoleBinding 授权。此外,Pod 默认会收到 ServiceAccount 令牌, 所以对不使用 API 的 Pod 关闭这一点,是最小权限的第一步。
步骤
- 创建命名空间
kcna-rbac和 ServiceAccountreader。 - 创建 Role
pod-viewer(pods 的 get/list/watch)和 RoleBindingreader-pods。 - 确认同样的权限在
default命名空间中不起作用,并记录到scope.txt。 - 创建另一个 Role
cm-viewer(configmaps 的 get/list)和 RoleBindingreader-cms。 - 用 ClusterRole
node-viewer和 ClusterRoleBindingreader-nodes允许查看节点。 - 以 reader 身份运行 Pod
app,并关闭令牌自动挂载。 - 确认 reader 不能创建 Pod,也不能使用通配符权限,并记录到
write-check.txt。 - 把四个 can-i 结果以账簿形式记录到
/root/kcna-rbac/report.txt。
参考
- 以 ServiceAccount 的身份进行模拟时,主体名称的格式为
system:serviceaccount:<네임스페이스>:<이름>(占位符依次为命名空间与名称)。 - 用
kubectl auth can-i --list -n kcna-rbac --as=...可以查看该主体拥有的全部权限。 - 官方文档:使用 RBAC 鉴权 · RBAC 良好实践。
没有任何权限的新账号到了
创建命名空间 kcna-rbac,并在其中创建 ServiceAccount reader。
ServiceAccount 是非人类的工作负载向 API 服务器表明自己的身份。它是属于命名空间的对象,所以要先创建命名空间。用 --dry-run=client -o yaml 生成 create 的结果再 apply,重复运行也是安全的。刚创建出来的 reader 没有任何权限。
只让它能看 Pod,不能删除
在命名空间 kcna-rbac 中创建 Role pod-viewer。规则只能是对核心组("")中 pods 的 get、list、watch,仅此而已。然后用 RoleBinding reader-pods 把这个 Role 绑定到 ServiceAccount kcna-rbac:reader。
RBAC 只有允许,没有拒绝。Role 是“能做什么”的列表,RoleBinding 决定把这份列表授予“谁”。Pod 属于核心 API 组,所以 apiGroups 是空字符串。请看 kubectl create role 的 --verb/--resource 选项,以及 kubectl create rolebinding 的 --role/--serviceaccount(格式为 命名空间:名称)选项。确认方式是 kubectl auth can-i <동사> pods --as=system:serviceaccount:<ns>:<이름>(占位符依次为动词、命名空间与名称)。
在隔壁命名空间什么都看不到
不创建新对象。用 kubectl auth can-i 确认 reader 能否在 default 命名空间中 list Pod,并把结果用 list-pods-default=<yes|no> 的形式写成一行,记录到 /root/kcna-rbac/scope.txt。
Role 和 RoleBinding 属于命名空间,所以这份权限只在有绑定的命名空间内有效。只改 -n 的值,把同一个问题问两遍。不要凭猜测填写,请原样抄写 can-i 返回的值。
权限不是叠加,而是另外绑定
不要动现有的 pod-viewer,在命名空间 kcna-rbac 中创建另一个 Role cm-viewer。规则是对核心组中 configmaps 的 get、list。用 RoleBinding reader-cms 把它绑定到 reader。reader 必须能 list ConfigMap,但不能 delete。
一个主体上有多个绑定时,权限是它们的并集。所以把角色按用途拆细,以后要摘掉其中一个就很容易。如果把 configmaps 塞进 pod-viewer,评分器会抓出来。像第 2 步那样,各创建一个角色和一个绑定即可。
节点住在命名空间之外
创建 ClusterRole node-viewer(对核心组 nodes 的 get、list),并用 ClusterRoleBinding reader-nodes 绑定到 ServiceAccount kcna-rbac:reader。reader 必须能 list 节点。
节点、PV、StorageClass 这类集群范围的资源没有命名空间,所以无法用 Role 授权。把 ClusterRole 用 RoleBinding 绑定,范围仍然只会缩小到一个命名空间,所以要查看节点需要 ClusterRoleBinding。请看 kubectl create clusterrolebinding 的 --clusterrole 选项。针对节点的 can-i 不要加 -n。
不使用 API 的 Pod 不要交给它钥匙
在命名空间 kcna-rbac 中创建 Pod app(镜像 nginx:1.27-alpine)。这个 Pod 以 ServiceAccount reader 运行,并把 automountServiceAccountToken 设为 false,使 ServiceAccount 令牌不会被自动挂载。
Pod 默认会以 kube-api-access-* 卷的形式收到自己 ServiceAccount 的令牌。容器一旦被攻破,就能用这个令牌调用 API,所以不使用 API 的 Pod 最好不要收到令牌。请在 Pod spec 中同时写上 serviceAccountName 和 automountServiceAccountToken 两个字段。也请在 kubectl get pod app -o yaml 中确认 volumes 里没有 kube-api-access。
如果只读账号想创建 Pod
用 kubectl auth can-i 确认 reader 能否在命名空间 kcna-rbac 中 create Pod,以及能否对所有资源使用所有动词(通配符)。两者都必须是 no。把 create Pod 的结果用 create-pods=<yes|no> 的形式写成一行,记录到 /root/kcna-rbac/write-check.txt。
到目前为止授予的动词只有 get、list、watch。除非另外允许,否则 create 会被拒绝。通配符的问题可以在资源和动词的位置填入用引号括起来的星号来提问。如果这里得到 yes,说明前面的步骤把权限给得太宽了,请重新检查 Role 规则。
把 reader 的权限图记成账簿
把 reader 的权限恰好写成四行,记录到 /root/kcna-rbac/report.txt——list-pods-in-ns=yes、list-pods-in-default=no、list-nodes=yes、create-pods=no。值必须与实际的 kubectl auth can-i 结果一致。
评分器会把每一行与以 ServiceAccount reader 身份模拟得到的实际 can-i 结果比对。四个问题依次是:kcna-rbac 中 pods 的 list、default 中 pods 的 list、集群范围 nodes 的 list、kcna-rbac 中 pods 的 create。请抄写你亲自询问得到的答案。