RBAC 就是 Role-based access control,基于角色的访问控制
k8s 中的 RBAC 的核心就两个资源 Role 和 Binding:
Role 定义权限,Binding 将 Role 绑定到身份账户(username、usergroup、sa)。

Role 定义具体资源的具体权限:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: default
name: pod-reader
rules:
- apiGroups: [""] # "" indicates the core API group
resources: ["pods"]
verbs: ["get", "watch", "list"]
然后用 RoleBinding 绑定到用户或者 sa:
apiVersion: rbac.authorization.k8s.io/v1
# This role binding allows "jane" to read pods in the "default" namespace.
# You need to already have a Role named "pod-reader" in that namespace.
kind: RoleBinding
metadata:
name: read-pods
namespace: default
subjects:
# You can specify more than one "subject"
- kind: User
name: jane # "name" is case sensitive
apiGroup: rbac.authorization.k8s.io
roleRef:
# "roleRef" specifies the binding to a Role / ClusterRole
kind: Role #this must be Role or ClusterRole
name: pod-reader # this must match the name of the Role or ClusterRole you wish to bind to
apiGroup: rbac.authorization.k8s.io
资源(pods 等)通过和 sa 关联来获取权限。
Role 和 RoleBinding 都仅限于某个 namespace,如果要跨 namespace 或者绑定一些集群的非资源(如接口),则需要使用 ClusterRole,ClusterRoleBinding。
看上去除了一些 clusterRole 之类的新概念外并不难。
问题在于,用户在哪?从哪来?
k8s 的用户体系是一个很有意思的东西,很多人用了很多年可能都没认真想过
虽然 RBAC 的认证对象是用户,但是 k8s 并没有用户体系😂
k8s 的用户体系比较奇特,它内部不存储任何用户信息,一切的身份校验都是围绕以 kube-ca 这个根 CA 为核心的证书签发来实现的。
先来简单回顾下 k8s 的 CA
主要的几个 CA:

k8s 里所谓的用户,实际上就是用核心根证书 kubernetes-ca 签发的一个客户端证书
创建一个用户:
# 生成用户私钥
openssl genrsa -out hello.key 2048
# 创建用户的 CSR,指定用户名和 group
openssl req -new -key hello.key -out hello.csr -subj "/CN=hello/O=app1/O=app2"
# 用 kube-ca 这个集群根 CA 签发用户证书
openssl x509 -req -in hello.csr -CA /etc/kubernetes/pki/ca.crt -CAkey /etc/kubernetes/pki/ca.key -CAcreateserial -out hello.crt -days 500
# 为 kubectl 设置用户
kubectl config set-credentials hello --client-certificate=hello.crt --client-key=hello.key
kubectl config set-context employee-context --cluster=minikube --namespace=office --user=employee
上述 CSR 就是申请创建一个名为 hello,用户组为 app1 和 app2 的用户。
前面提到过,RBAC 需要用过 RoleBiding 将 Role 绑定到 username/group。
k8s 本身不存储用户,当请求到达 apiserver 时,从证书或 token 中获取 username,然后根据 username 查询相关联的 ClusterRole 和 Role,再根据这些 roles 获取用户的全部权限。
可以通过 kubectl 查询当前用户是否有某个权限 kubectl auth can-i xxx。
没有存储用户信息带来一个恶果就是没法维护用户状态,比如你没法吊销用户身份。
一个曲线的方法是吊销某个用户的 RoleBinding 来解除其权限(虽然能 authenticated 但是无法 authorizated)。这样做的前提是你的 RoleBing 都是绑定到用户而不是绑定到用户组。
要想支持吊销等用户操作,就不能使用默认方案,而要使用外部的 OpenID Connect/webhook 方案。
OpenID Connect/webhook 方案的原理实际上很简单,因为 k8s 不存储用户,所以 k8s 其实只需要知道“用户名”就够了。
如 oidc 方案中,用户通过 oauth2 登陆后,通过 jwt token 告知 k8s 用户的关键信息(username/group)即可。
至于权限,仍然通过 Role/RoleBinding 的形式设置和绑定。

术语:

可以看到 Authentication 排在最前面,请求到达 apiserver 后,首先需要验明身份。
然后才是一系列准入控制器(admission controllers),可以将其理解为一系列的中间件 MiddleWares。
最后才会触达数据层,也就是 ETCD。
当到达 apiserver 的请求已经通过身份认证和 ACL 后,还会经过一系列的准入控制器(中间件)。
这些准入控制器是编译在 apiserver 的二进制文件中,系统管理员可配置其开启。

默认开启的准入控制器有:CertificateApproval, CertificateSigning, CertificateSubjectRestriction, DefaultIngressClass, DefaultStorageClass, DefaultTolerationSeconds, LimitRanger, MutatingAdmissionWebhook, NamespaceLifecycle, PersistentVolumeClaimResize, Priority, ResourceQuota, RuntimeClass, ServiceAccount, StorageObjectInUseProtection, TaintNodesByCondition, ValidatingAdmissionWebhook。
其中有两个特殊的控制器:
MutatingAdmissionWebhook:是第一个执行的控制器,可以修改请求ValidatingAdmissionWebhook:是最末执行的控制器上述两个 webhook 控制器可以动态调用用户配置的远端第三方 webhook server 实现动态的准入控制。