认证和授权是两层
一句话总结
进入 apiserver 的请求依次经过认证(你是谁)→ 授权(这个人能否这样做)→ 准入(内容是否符合规则)。RBAC 只是第二层,第一层属于证书与 token。混淆两者会让诊断纠缠不清。
为什么需要它
Kubernetes 没有 user 对象,也没有 kubectl get users,因为 apiserver 不是身份签发者,而是验证者。身份来自外部。
- client certificate——CN 成为用户名,O 成为组。
kubeadm生成的 admin.conf 就采用这种方式。 - ServiceAccount token——Pod 内 workload 的身份。其名称为
system:serviceaccount:<네임스페이스>:<이름>,并属于system:serviceaccounts组。 - OIDC、webhook、bootstrap token 等
所以“请删除那个用户”并不是一个成立的请求。能做的只有吊销证书或删除 binding。
授权侧只有四种对象。
| namespace scope | cluster scope | |
|---|---|---|
| 定义权限 | Role | ClusterRole |
| 授予权限 | RoleBinding | ClusterRoleBinding |
唯一容易混淆的组合是:允许 RoleBinding 引用 ClusterRole,此时 ClusterRole 的规则只在该 RoleBinding 所在 namespace 内生效。这是按 namespace 复用 view、edit 等内置 ClusterRole 的标准方法。反之,使用 ClusterRoleBinding 会扩散到整个 cluster,试图守住 namespace 边界时经常在此出错。
RBAC 只会累加,没有 deny 规则。任一 binding 一旦允许,结果就是允许。因此追查“这个账号为什么能做这件事”时,必须检查全部 binding,而 kubectl auth can-i --as= 是捷径。它只是创建 SubjectAccessReview 向 apiserver 询问,并不会真的发送目标请求,所以可以无副作用地安全确认。
它如何运作
Aggregated ClusterRole 是不直接写规则的 ClusterRole。在 aggregationRule 中写 label selector 后,控制器会寻找带该 label 的其他 ClusterRole,并填充 rules。通过 CRD 添加新资源时,只需一个 label 就能纳入现有 view/edit role。手动在其中写 rules 会被控制器覆盖。
automountServiceAccountToken 决定是否向 Pod 注入 API token,默认会注入。这样即使完全不使用 API 的 nginx Pod 也会得到 token,container 被攻破后,该 token 就是 cluster 访问权。在 ServiceAccount 或 Pod spec 中设为 false,kube-api-access-* projected volume 本身就不会注入。
在实际工作中会遇到的情况
**案例 1——把 CA private key 暴露窗口缩短到 2 小时。**家庭实验室从 3 节点扩展到 7 节点时,worker join 与 control plane join 的差异完整展示了认证结构。worker 只需要一个 token。
kubeadm token create --print-join-command
control plane 不同。新节点也必须成为签发证书的主体,因此需要 CA private key。kubeadm 不让人通过 scp 传输敏感文件,而用 kubeadm init phase upload-certs --upload-certs 把 CA key bundle 加密后上传为集群内 Secret。join command 中的 64 位 certificate-key 是解密该密文的 symmetric key。
而且这个 Secret 会在 2 小时后自动删除。即使已经加密,里面仍包含 cluster root of trust,因此设计目标是最小化暴露窗口。过期后重新执行 upload-certs 即可,不影响现有集群。认证的核心正是**“让信任根暴露得多短”**。
**案例 2——证书 SAN 就是访问权限。**同一集群即使把 control plane 扩到三台,第一台死亡后仍无人能连接,因为 apiserver 证书 SAN 中没有另外两台的 IP。RBAC 再完善,无法通过 TLS 验证就根本到不了授权层。反过来利用这一性质,把死亡节点的 IP 加到存活节点 interface 上,无需重新签发证书或修改 kubeconfig,所有 client 都会重新连接。client 验证的不是节点本身,而是连接地址是否位于 SAN 中。
**案例 3——有时问题不是权限,而是配置。**安装 GPU Operator 时,所有 Pod 都卡在 Init。kubectl get runtimeclass 中正常存在 nvidia,节点 containerd 配置里却完全没有 nvidia。名称存在于 Kubernetes 对象中,实体必须存在于节点,而后者缺失。对象存在与实际可用是两个不同命题。诊断授权问题也需要相同习惯:can-i 返回 yes 后,应继续按证书、网络、admission webhook 的顺序向下检查。
后续实验要做什么
创建 ServiceAccount,用 Role/RoleBinding 只授予读取权限,并用 kubectl auth can-i --as= 同时确认允许和拒绝的行为。随后通过 ClusterRole/ClusterRoleBinding 开放 cluster scope 资源,配置 Aggregated ClusterRole,最后创建一个完全不注入 token 的 Pod。