RBAC 权限控制
管理员可以通过 Kubernetes API 动态配置策略来启用 RBAC,需要在 kube-apiserver 中添加参数 authorization-mode=RBAC,如果使用的 kubeadm 安装的集群那么是默认开启了 RBAC 的,可以通过查看 Master
节点上 apiserver 的静态 Pod 定义文件:
ubuntu@ubuntu:~/example/config_map$ sudo cat /etc/kubernetes/manifests/kube-apiserver.yaml
[sudo] password for ubuntu:
apiVersion: v1
kind: Pod
...
- --authorization-mode=Node,RBAC
...
API对象
在 Kubernetes 集群中,一切可持久化的内容都表现为 对象(Object),例如 Pod、Service、Secret、Deployment 等。这些对象最终会被 存储在 etcd 中,并代表整个集群的期望状态。
当我们编写 YAML 文件并使用 kubectl apply -f 提交时,本质上是通过 声明式 API(Declarative API) 与 Kubernetes 的 API Server 进行通信,将 YAML 描述转换为 Kubernetes 的对象。
Kubernetes API 是一个基于 HTTP + JSON 的服务(内部部分通信使用 Protobuf)。
可以通过命令查看URI
ubuntu@ubuntu:~/example/config_map$ kubectl get --raw /
{"paths": ["/.well-known/openid-configuration","/api","/api/v1","/apis","/apis/","/apis/acme.cert-manager.io","/apis/acme.cert-manager.io/v1","/apis/admissionregistration.k8s.io","/apis/admissionregistration.k8s.io/v1","/apis/apiextensions.k8s.io","/apis/apiextensions.k8s.io/v1","/apis/apiregistration.k8s.io","/apis/apiregistration.k8s.io/v1","/apis/apps","/apis/apps/v1","/apis/auditlog.cattle.io","/apis/auditlog.cattle.io/v1","/apis/authentication.k8s.io","/apis/authentication.k8s.io/v1","/apis/authorization.k8s.io",
...
YAML 文件只是客户端书写的格式,kubectl 会:
- 将 YAML 解析为 JSON
- 通过 REST API 提交给 API Server
- API Server 根据 G/V/R(Group/Version/Resource)识别对象类型
- 验证、存储到 etcd
通知控制器(Controller)去实现所需状态
这就是声明式 API 模式:
用户只声明想要的状态,控制器负责让集群达到该状态。
Kubernetes 为了可扩展性和兼容性,提供了不同的 API 路径:
- /api/v1
- /apis/apps/v1
- /apis/batch/v1
- ……
同一类资源在不同路径下有不同的版本,这些版本具有不同的成熟度。
| 级别 | 例子 | 含义 | 特点 |
|---|---|---|---|
| Alpha | v1alpha1 |
实验性,要手动启用 | 随时会变动或删除,强烈不建议生产用 |
| Beta | v2beta1 |
默认启用,基本稳定 | 语义可能改变,不兼容可能性较小 |
| Stable(稳定版) | v1 |
完全稳定 | 未来版本会长期支持 |
API 对象在 etcd 中的路径组成
每个 Kubernetes 对象在 etcd 中的位置由 3 部分组成:
- Group(API 组)
如:
- core(无前缀)
- apps
- batch
- rbac.authorization.k8s.io
- Version(版本)
如:
- v1
- v1beta1
- v1alpha1
- Resource(资源类型)
全部是复数形式,例如:
- pods
- deployments
- secrets
- configmaps
因此 etcd 内的对象路径结构:
/apis/<group>/<version>/<resource>/<name> 或对于 core 组 /api/v1/<resource>/<name>
树状结构理解
如果把 Kubernetes 所有资源类比为一个树,可以这样理解:
api└── v1├── pods├── services└── configmapsapis├── apps│ └── v1│ ├── deployments│ └── daemonsets├── batch│ └── v1│ └── jobs└── rbac.authorization.k8s.io└── v1└── roles
总结
- YAML 是声明式配置 → 实际上传输给 API Server 的是 JSON
- Kubernetes 对象最终存储在 etcd 中,代表集群状态
- 一个 API 由 Group/Version/Resource(G/V/R) 唯一标识
- API 的成熟度分为 Alpha → Beta → Stable(v1)
通过这种结构,Kubernetes 实现了高度扩展性和向后兼容性
GVK
我们知道要表达一个资源对象需要指定 group/kind 和 version,这个在 Kubernetes 的 API Server 中简称为GVK,GVK 是定位一种类型的方式,例如 daemonsets 就是 Kubernetes 中的一种资源。
daemonsets 的资源清单文件:
apiVersion: apps/v1
kind: DaemonSet
metadata:name: node-exporternamespace: kube-system
spec:
# ......
这里声明的 apiVersion 是 apps/v1,其实就是隐含了 Group 是 apps,Version 是 v1,Kind 就是定义的DaemonSet,而 kubectl 接收到这个清单之后,就可以根据这个声明去调用 API Server 对应的 URL 去获取信息,
例如这里就是 /api/apps/v1/daemonset。所以我们说 Kubernetes 组织资源的方式是以 REST 的 URI 的形式来的。
/apis/apps/v1/namespaces/$NAMESPACE/daemonset
/apis/${Group}/${Version}/namespaces/$NAMESPACE/${Resource}
我们这里不是说是 G(roup)V(ersion)K(ind) 吗,怎么现在又变成了 G(roup)V(ersion)R(esource) ?这就是 API Server 中的第二个概念:GVR。
GVR
GVR 里面 Kind 与 Resource 的关系,和面向对象编程里面的类和对象的概念是一样的:
| Kubernetes | OOP |
|---|---|
| Kind | Class |
| Resource | Object |
Kind 其实就是一个类,用于描述对象的;而 Resource 就是具体的 Kind,可以理解成类已经实例化成对象了。Resource 只是 API 中的一个 Kind 的使用方式,通常情况下,Kind 和 Resource 之间有一个一对一的映射。
例如,pods 资源对应于 Pod 种类,但是有时,同一类型可能由多个资源返回。例如 Scale Kind 是由所有 scale 子资源返回的,如 deployments/scale 或 replicasets/scale,这就是允许 KubernetesHorizontalPodAutoscaler(HPA) 与不同资源交互的原因。
RESTMapper
当你执行:
kubectl apply -f ds.yaml
kubectl 会:
-
解析 YAML 中的两个字段:
apiVersion: apps/v1 kind: DaemonSet -
kubectl 拿到 GVK 后会向 RESTMapper 查询:
apps/v1 里的 DaemonSet 对应哪个 REST 资源路径(GVR)?
-
RESTMapper 查表得到:
GVK: apps/v1, DaemonSet → GVR: apps/v1, daemonsets
API Server 最终就是访问这个地址:
/apis/apps/v1/namespaces/<ns>/daemonsets
RESTMapper 本质上就是一个 GVK → GVR 翻译器。
现在我们知道了 API Server 可以通过 URI 的形式访问到资源,我们可以通过 kubectl proxy 来代理一个本地的API Server 端口,这样就可以绕过认证了,直接可以以 http 的形式进行:
# 开启临时代理
ubuntu@ubuntu:~/example/config_map$ kubectl proxy
Starting to serve on 127.0.0.1:8001
# 另一个终端访问
ubuntu@ubuntu:~$ curl http://127.0.0.1:8001/apis/apps/v1/daemonsets
{"kind": "DaemonSetList","apiVersion": "apps/v1","metadata": {"resourceVersion": "709279"},"items": [{"metadata": {"name": "kube-flannel-ds","namespace": "kube-flannel","uid": "4db1c00e-793b-4ab8-8c9e-2e3364126cc2","resourceVersion": "133537","generation": 1,"creationTimestamp": "2025-09-03T10:40:50Z","labels": {"app": "flannel","k8s-app": "flannel","tier": "node"},
# ...
# 如果想看系统支持哪些 GVK,那么可以通过 kubectl 的命令查看:
ubuntu@ubuntu:~$ kubectl api-resources
NAME SHORTNAMES APIVERSION NAMESPACED KIND
bindings v1 true Binding
componentstatuses cs v1 false ComponentStatus
configmaps cm v1 true ConfigMap
endpoints ep v1 true Endpoints
events ev v1 true Event
limitranges limits v1 true LimitRange
namespaces ns v1 false Namespace
nodes no v1 false Node
persistentvolumeclaims pvc v1 true PersistentVolumeClaim
# ...
RBAC
上面我们介绍了 Kubernetes 所有资源对象都是模型化的 API 对象,允许执行 CRUD(Create、Read、Update、Delete) 等操作(也就是我们常说的增、删、改、查操作),比如下面的这些资源:
Pods
ConfigMaps
Deployments
Nodes
Secrets
Namespaces
对于上面这些资源对象的可能存在的操作有:
create
get
delete
list
update
edit
watch
exec
patch
在更上层,这些资源和 API Group 进行关联,比如 Pods 属于 Core API Group,而 Deployements 属于 appsAPI Group,现在我们要在 Kubernetes 中通过 RBAC 来对资源进行权限管理。
除了上面的这些资源和操作以外,我们还需要了解另外几个概念:
- Rule:规则,规则是一组属于不同 API Group 资源上的一组操作的集合
- Role 和 ClusterRole:角色和集群角色,这两个对象都包含上面的 Rules 元素,二者的区别在于,在 Role中,定义的规则只适用于单个命名空间,也就是和 namespace 关联的,而 ClusterRole 是集群范围内的,因此定义的规则不受命名空间的约束。另外 Role 和 ClusterRole 在 Kubernetes 中都被定义为集群内部的 API 资源,和我们前面学习过的 Pod、Deployment 这些对象类似,都是我们集群的资源对象,所以同样的可以使用 YAML文件来描述,用 kubectl 工具来管理
- Subject:主题,对应集群中尝试操作的对象,集群中定义了 3 种类型的主题资源:
- User Account:用户,这是有外部独立服务进行管理的,管理员进行私钥的分配,用户可以使用 KeyStone 或者 Goolge 帐号,甚至一个用户名和密码的文件列表也可以。对于用户的管理集群内部没有一个关联的资源对象,所以用户不能通过集群内部的 API 来进行管理
- Group:组,这是用来关联多个账户的,集群中有一些默认创建的组,比如 cluster-admin。
- Service Account:服务帐号,通过 Kubernetes API 来管理的一些用户帐号,和 namespace 进行关联的,适用于集群内部运行的应用程序,需要通过 API 来完成权限认证,所以在集群内部进行权限操作,我们都需要使用到 ServiceAccount。
- RoleBinding 和 ClusterRoleBinding:角色绑定和集群角色绑定,简单来说就是把声明的 Subject 和我们的Role 进行绑定的过程(给某个用户绑定上操作的权限),二者的区别也是作用范围的区别:RoleBinding 只会影响到当前 namespace 下面的资源操作权限,而 ClusterRoleBinding 会影响到所有的 namespace。
只能访问某个 namespace 的普通用户
我们想要创建一个 User Account,只能访问 kube-system 这个命名空间,对应的用户信息如下所示:
username: demo
group: example
``` 创建证书签名请求(CSR)### 创建用户凭证利用管理员分配给你的一个私钥就可以创建了,这个我们可以参考官方文档中的方法,这里我们来使用 OpenSSL 证书来创建一个 User,当然我们也可以使用更简单的 cfssl 工具来创建:```shell
# 1. 生成私钥(User Private Key)
ubuntu@ubuntu:~/ssl$ openssl genrsa -out demo.key 2048
# 2. 创建证书签名请求(CSR)
ubuntu@ubuntu:~/ssl$ openssl req -new -key demo.key -out demo.csr -subj "/CN=demo/O=example"
# 3. 使用集群 CA 签发证书
# kubeadm 集群的 CA 证书位于:
# /etc/kubernetes/pki/ca.crt
# /etc/kubernetes/pki/ca.key
ubuntu@ubuntu:~/ssl$ sudo openssl x509 -req \-in demo.csr \-CA /etc/kubernetes/pki/ca.crt \-CAkey /etc/kubernetes/pki/ca.key \-CAcreateserial \-out demo.crt \-days 500
[sudo] password for ubuntu:
Certificate request self-signature ok
subject=CN = demo, O = example
# 查看文件
ubuntu@ubuntu:~/ssl$ ls
demo.crt demo.csr demo.key
# 4. 在 kubeconfig 中注册用户(Credentials)
ubuntu@ubuntu:~/ssl$ sudo kubectl config set-credentials demo \--client-certificate=demo.crt \--client-key=demo.key
User "demo" set.
# 5. 创建 Context(指定用户、集群、namespace)
ubuntu@ubuntu:~/ssl$ kubectl config get-clusters
NAME
kubernetes
# 根据name 指定集群
ubuntu@ubuntu:~/ssl$ sudo kubectl config set-context demo-context \--cluster=kubernetes \--namespace=kube-system \--user=demo
Context "demo-context" created.
# 6. 切换 Context 并测试权限
ubuntu@ubuntu:~/ssl$ kubectl --context=demo-context get pods
Error in configuration: context was not found for specified context: demo-context
# 原因是:我们还没有给用户授予 RBAC 权限。
KinD 集群可能遇到的证书验证错误
KinD 的 kube-apiserver 可能会因为证书签名算法(SHA1)导致认证失败:
解决方法:在 apiserver 中设置 GODEBUG 环境变量
创建角色并授权
Role 用于命名空间级授权,本例授予用户对 kube-system 命名空间中:
- Deployments
- ReplicaSets
- Pods
# demo-role.yaml 角色定义yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:name: demo-rolenamespace: kube-system
rules:
- apiGroups: ["", "apps"]resources: ["deployments", "replicasets", "pods"]verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]# verbs 也可简写为 ["*"]
# demo-rolebinding.yaml 角色用户绑定关系
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:name: demo-rolebindingnamespace: kube-system
subjects:
- kind: Username: demoapiGroup: ""
roleRef:kind: Rolename: demo-roleapiGroup: rbac.authorization.k8s.io
测试:
# 创建
ubuntu@ubuntu:~/example/rbac$ kubectl apply -f ./demo-role.yaml
role.rbac.authorization.k8s.io/demo-role created
ubuntu@ubuntu:~/example/rbac$ kubectl apply -f ./demo-rolebinding.yaml
rolebinding.rbac.authorization.k8s.io/demo-rolebinding created
# 测试
ubuntu@ubuntu:~/example/rbac$ kubectl --context=demo-context get pods
NAME READY STATUS RESTARTS AGE
coredns-674b8bbfcf-6rjbh 1/1 Running 11 (6h24m ago) 74d
coredns-674b8bbfcf-s85b7 1/1 Running 11 (6h24m ago) 74d
etcd-master 1/1 Running 14 (6h24m ago) 74d
kube-apiserver-master 1/1 Running 13 (6h24m ago) 74d
kube-controller-manager-master 1/1 Running 13 (6h24m ago) 74d
kube-proxy-6bgf7 1/1 Running 11 (6h24m ago) 74d
kube-proxy-ddms4 1/1 Running 12 (6h24m ago) 74d
kube-proxy-hrqpb 1/1 Running 11 (6h24m ago) 74d
kube-scheduler-master 1/1 Running 14 (6h24m ago) 74d
metrics-server-56fb9549f4-sgljk 1/1 Running 2 (6h24m ago) 66d
vpa-admission-controller-84998b6899-7g2bh 1/1 Running 2 (6h24m ago) 65d
vpa-recommender-6d7fbcb5f6-vfncg 1/1 Running 2 (6h24m ago) 65d
vpa-updater-864ff5f65f-wj5dz 1/1 Running 2 (6h24m ago) 65d
ubuntu@ubuntu:~/example/rbac$ kubectl --context=demo-context get rs,deploy
NAME DESIRED CURRENT READY AGE
replicaset.apps/coredns-674b8bbfcf 2 2 2 74d
replicaset.apps/metrics-server-56fb9549f4 1 1 1 66d
replicaset.apps/vpa-admission-controller-84998b6899 1 1 1 65d
replicaset.apps/vpa-recommender-6d7fbcb5f6 1 1 1 65d
replicaset.apps/vpa-updater-864ff5f65f 1 1 1 65dNAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/coredns 2/2 2 2 74d
deployment.apps/metrics-server 1/1 1 1 66d
deployment.apps/vpa-admission-controller 1/1 1 1 65d
deployment.apps/vpa-recommender 1/1 1 1 65d
deployment.apps/vpa-updater 1/1 1 1 65d
# 验证未授权
ubuntu@ubuntu:~/example/rbac$ kubectl --context=demo-context get svc
Error from server (Forbidden): services is forbidden: User "demo" cannot list resource "services" in API group "" in the namespace "kube-system"
只能访问某个 namespace 的 ServiceAccount
- ServiceAccount(SA):为 Pod 中运行的进程提供身份(Identity),Pod 可以使用该身份向 Kubernetes API Server 进行认证。
- 每个 Pod 都会关联一个 ServiceAccount,Pod 中的应用可以使用其 token 访问 Kubernetes 资源。
- 默认行为
- 如果 Pod 没有指定 spec.serviceAccountName,则使用 default ServiceAccount。
- 与 Pod 关联的 token 会被自动投射到 Pod 的文件系统:
/run/secrets/kubernetes.io/serviceaccount/- 包含 token、ca.crt 等文件
- Pod 内进程可以读取 token 来认证 API Server
Pod 中指定 ServiceAccount
apiVersion: v1
kind: Pod
metadata:name: mypod
spec:serviceAccountName: my-sa # 指定使用自定义 SAcontainers:- name: nginximage: nginx
权限控制
- ServiceAccount 本身没有权限,需要通过 RBAC 授权:
- Pod 中的应用拿到 token,就能执行对应权限操作
Token 使用差异(Kubernetes 版本)
- v1.20 及之前:
- 默认会创建 长期有效的 token secret,自动挂载到 Pod
- token 永久有效,权限变动后需要重新创建 Pod 才能刷新
- v1.21+:
- 引入 Bound ServiceAccount Token(Kubernetes TokenRequest API)
- Pod 默认挂载 短期 token,token 有 TTL(一般 1h)
- 更安全,权限调整后可自动刷新
总结:
- ServiceAccount 是 Pod 的身份认证凭证
- Token 文件由 Kubernetes 自动投射
- 权限需要通过 RBAC 绑定
- 不同 K8s 版本对 token 管理方式不同:长期有效 vs 短期绑定 token
- 自定义 ServiceAccount 可以实现更细粒度权限控制
利用用户创建Token
# demo-sa.yaml
apiVersion: v1
kind: ServiceAccount
metadata:name: demo-sanamespace: kube-system
---
# demo-sa-role.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:name: demo-sa-rolenamespace: kube-system
rules:
- apiGroups: ["", "apps"]resources: ["pods", "deployments", "replicasets"]verbs: ["get", "list", "watch", "create", "update", "delete"]
---
# demo-sa-rolebinding.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:name: demo-sa-rolebindingnamespace: kube-system
subjects:
- kind: ServiceAccountname: demo-sanamespace: kube-system
roleRef:kind: Rolename: demo-sa-roleapiGroup: rbac.authorization.k8s.io
生成token:
ubuntu@ubuntu:~/example/rbac$ kubectl create token demo-sa -n kube-system
eyJhbGciOiJSUzI1NiIsImtpZCI6ImVuV3duUlNNMmxZd00tUlA4Z3V6TdemodHNVSFRPRXBiQjhZTk5qdjZlN00ifQ.eyJhdWQiOlsiaHR0cHM6Ly9rdWJlcm5ldGVzLmRlZmF1bHQuc3ZjLmNsdXN0ZXIubG9jYWwiXSwiZXhwIjoxNzYzMzczNDk5LCJpYXQiOjE3NjMzNjk4OTksImlzcyI6Imh0dHBzOi8va3ViZXJuZXRlcy5kZWZhdWx0LnN2Yy5jbHVzdGVyLmxvY2FsIiwianRpIjoiZmM1NmUyMzAtZDhhZi00NWUyLWFjYjQtNjdkOWUxYTIzZjM5Iiwia3ViZXJuZXRlcy5pbyI6eyJuYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsInNlcnZpY2VhY2NvdW50Ijp7Im5hbWUiOiJoemwtc2EiLCJ1aWQiOiI5MWVmYzhjOS0yZjA1LTQ4YTAtOGMyOC00ZWIxNWQ3ODc3ZDAifX0sIm5iZiI6MTc2MzM2OTg5OSwic3ViIjoic3lzdGVtOnNlcnZpY2VhY2NvdW50Omt1YmUtc3lzdGVtOmh6bC1zYSJ9.M6iCyd3zMhxNojoKvd37eW13Hf6pknRCAJMrCjp3VWudi1PXO35Wn3_r5dcwiOJEp2Y-qvzuqf1alYwGYM6IakmoRGWFZeRB6ix5dAFQFNXrAUlpBdWknsI-Utebkw7Uv4hepCtFBkxFKrrOHXmyS7rSUtjjnE52RvztUu7VBCvcBU-QsxNIb-tp2WrZfuphGZFrg6HvLoJB2Ah0mmyPDtw7MeYwt_jBu_kQB369aU1Mm6srtTj7YbfK8ZYI9K6UR3QwTUdasdNk1n8d3eIwRSHOBU-uEeqlTYnsxWWG4KR6eDaDpNW3z0vSEHEeoOUazRXi4SUiGJekppcXbxYCVA