Kubernetes 认证、授权与准入:一次 API 请求如何获得许可
同样执行 kubectl get pods,有人成功,有人收到 Unauthorized,还有人收到 Forbidden。即使能够读取 Pod,创建它时仍可能被配额或安全策略拒绝。这些结果分别来自身份认证、权限检查和准入控制,排障时需要先分清失败发生在哪一步。
本文从账户和凭据开始,使用 ServiceAccount 与 RBAC 配置访问范围,再检查 kubeconfig 如何选择身份,最后通过日志读取、用户证书与资源配额实验串起完整流程。阅读前需要了解 Pod、Namespace 和 YAML;创建账户、绑定权限和签发凭据的操作需要相应管理权限,只应在获准的测试环境执行。
一、认证、授权与准入分别回答什么
| 阶段 | 检查内容 | 常见结果 |
|---|---|---|
| 认证(Authentication) | 凭据对应哪个用户名和组 | 无效凭据通常得到 401;匿名访问行为取决于配置 |
| 授权(Authorization) | 该身份能否对目标资源执行动作 | 无允许规则时通常得到 403 |
| 准入(Admission) | 创建、更新、删除等请求是否满足对象与策略要求 | 可能被配额、字段约束或安全策略拒绝 |
成功认证不会自动赋予任意资源操作权限;集群也可能通过默认绑定赋予已认证用户少量发现权限。读取资源主要经过认证和授权,创建或修改资源还要经过准入。下面先确定请求使用什么身份。
二、区分人的身份与工作负载身份
Kubernetes 使用用户名和组表达身份,但没有用于创建普通人的内置 User、Group 资源。用户通常来自客户端证书、OIDC 或外部认证系统;ServiceAccount 则是 API 中真实存在、属于命名空间的资源。两类身份都可作为 RBAC 绑定主体,并不按“只能在集群外”或“只能在集群内”严格区分。
UserAccount:由认证系统提供用户与组
kubectl 根据选中的 kubeconfig 用户项获取凭据。kubeadm 环境常见 kubernetes-admin,它来自管理员 kubeconfig,而不是 kubectl 在服务端创建出的 User 对象。kubeadm 通常生成 /etc/kubernetes/admin.conf,用户还需要按安装步骤将其放到自己的 kubeconfig 路径,不能假定所有用户家目录会自动出现管理员配置。
ServiceAccount:为程序分配可管理的身份
ServiceAccount 通常供应用、控制器或自动化任务访问 API。它的身份字符串是 system:serviceaccount:<namespace>:<name>,还会属于 system:serviceaccounts、system:serviceaccounts:<namespace> 和 system:authenticated 等组。命名空间决定账户的归属,实际能访问哪些资源仍由绑定决定,因此也可以通过授权访问别的命名空间。
ServiceAccount token 也能用于外部自动化或某些 Dashboard 登录,但人的日常访问更适合带个人身份、可撤销且可审计的企业认证。下面用 SA 生成 kubeconfig 解释凭据与授权的关系,不能把共享机器人 token 当成每个人的长期登录方案。
短期 TokenRequest 与绑定 Pod 的投射 token 机制在 1.22 稳定;从 1.24 起,默认不再为每个 SA 自动生成旧式 token Secret。前者有有效期,Pod 中的投射 token 由 kubelet 轮换;手动保存到 kubeconfig 的 token 则需要调用方自行续期。
为外部测试客户端签发短期 token
可以把实验账户放在专用的 kube-users 命名空间,便于盘点。这里先在 default 中创建 hangx,后续示例会演示跨命名空间绑定;账户命名空间与授权目标命名空间不要混淆。
使用 TokenRequest 获取限时凭据
# 先创建sa
kubectl create sa hangx
# 命令行创建token,指定有效期
kubectl create token hangx --duration=1h--duration 是请求的有效期,服务端可以按配置调整;不要用极长 duration 代替凭据轮换。创建 token 本身不增加 RBAC 权限。
兼容旧客户端的长期 Secret token
只有在必须兼容无法获取短期 token 的客户端时,才考虑手动创建此类 Secret。token 由控制平面的 token 控制器填入,通常没有显式到期时间,但会因 Secret/SA 删除、签名密钥变化等失效,不能称为永久有效。其泄露窗口更大,应单独控制读取权限、轮换与撤销。
apiVersion: v1
kind: Secret
type: kubernetes.io/service-account-token
metadata:
name: hangx-token-secret
annotations:
kubernetes.io/service-account.name: hangx将上面的清单保存并应用后,可检查 Secret 是否已填充;普通 get 不会直接显示明文 token。
kubectl get secret hangx-token-secret
kubectl get secret hangx-token-secret -o jsonpath='{.data.token}' | base64 --decode将集群地址、CA 与 token 组合成 kubeconfig
下面优先使用短期 token 构建独立 kubeconfig。需要从已有可信 kubeconfig 获取集群地址和 CA,并保护生成的文件;不要把包含 token 的配置提交到仓库。
-
获取 api server 地址
方法 1:
serverAddr=$(kubectl config view --minify -o jsonpath='{.clusters[0].cluster.server}') echo "$serverAddr"方法 2:
kubectl cluster-info # 直接看 # https://192.168.40.180:6443 -
获取 SA 的短期 token 与可信 CA;需要兼容旧 Secret 时才使用注释中的替代方式
serviceaccountName="hangx" ca=$(kubectl config view --raw --minify --flatten -o jsonpath='{.clusters[0].cluster.certificate-authority-data}') token=$(kubectl create token "$serviceaccountName" --duration=1h) # 兼容旧Secret时:先确认该Secret确属这个SA且已填充 # secretName="hangx-token-secret" # ca=$(kubectl get secret/"$secretName" -o jsonpath='{.data.ca\.crt}') # token=$(kubectl get secret/"$secretName" -o jsonpath='{.data.token}' | base64 --decode) -
生成 kubeconfig
umask 077 cat <<EOF > ${serviceaccountName}-kubeconfig.yaml apiVersion: v1 kind: Config clusters: - name: default-cluster cluster: server: ${serverAddr} certificate-authority-data: ${ca} users: - name: ${serviceaccountName} user: token: ${token} contexts: - name: ${serviceaccountName}-context context: cluster: default-cluster user: ${serviceaccountName} namespace: default current-context: ${serviceaccountName}-context EOF多集群可以继续添加独立的 clusters、users 和 contexts 项,名称应互不混淆。context 引用的是这些本地配置名称,它不会创建服务端用户或授予权限。
-
生成之后就可以使用新的 kubeconfig 操作集群:
kubectl get po --kubeconfig hangx-kubeconfig.yaml # 如果没有权限,可以临时做一个授权 kubectl create rolebinding hangx-view --clusterrole=view --serviceaccount=default:hangx
把 ServiceAccount 分配给 Pod
Pod 通过 spec.serviceAccountName 选择一个同命名空间的 SA。默认启用自动挂载时,应用可从投射卷读取 token、CA 与 namespace。客户端库应在轮换后重新读取 token,不要把启动时读取的一份字符串永久缓存。
每个命名空间通常都有名为 default 的 SA,Pod 未指定时使用它。SA 必须与 Pod 同命名空间,但它获得的权限可以通过绑定延伸到其他命名空间。应用不需要访问 API 时,可在 Pod 或 SA 上设置 automountServiceAccountToken: false。
给某个命名空间的 default ServiceAccount 增加权限,会影响该命名空间内所有使用它的 Pod,包括未显式指定 serviceAccountName 的 Pod。
先创建 SA,再创建使用该身份的测试 Pod。镜像需要同时包含 shell 与 curl,普通 BusyBox 镜像不能直接运行后面的 curl 命令。
#创建sa
kubectl create sa sa-test
# 创建Pod
cat > pod-busybox.yaml <<'EOF'
apiVersion: v1
kind: Pod
metadata:
name: pod-busybox
spec:
serviceAccountName: sa-test
containers:
- name: busybox
image: curlimages/curl:8.16.0
command:
- "/bin/sh"
- "-c"
- "sleep 36000"
EOF
kubectl apply -f pod-busybox.yaml
# 进入Pod
kubectl exec -it pod-busybox -- /bin/sh
cd /var/run/secrets/kubernetes.io/serviceaccount/
# 手动请求apiserver接口,报403,是因为这个role还没有任何权限
curl --cacert ./ca.crt -H "Authorization: Bearer $(cat ./token)" https://kubernetes.default.svc/api/v1/namespaces/kube-system
# 退出容器,回到本机管理员终端
exit
# 仅授予default命名空间的读取权限
kubectl create rolebinding sa-test-view --clusterrole=view --serviceaccount=default:sa-test -n default
# 再进入Pod,读取default内Pod;跨空间访问仍然应被拒绝
kubectl exec -it pod-busybox -- /bin/sh
cd /var/run/secrets/kubernetes.io/serviceaccount/
curl --cacert ./ca.crt -H "Authorization: Bearer $(cat ./token)" https://kubernetes.default.svc/api/v1/namespaces/default/pods
exit # 返回本机终端注意
默认投射卷包含 ca.crt、namespace 和 token。client-go 的 InClusterConfig 会读取这些文件;kubectl 也支持集群内凭据,但容器内已有 kubeconfig 或环境配置可能改变实际选择,应核实当前身份与目标集群。
向命名空间 SA 组授权会影响现有和未来账户
组授权能覆盖当前与以后创建的所有匹配 SA。只有确实需要共同基线时才采用;常规应用应优先绑定单独 SA。下面 RoleBinding 的有效资源范围仍是 my-namespace。
kubectl create rolebinding sa-view --clusterrole=view --group=system:serviceaccounts:my-namespace --namespace=my-namespace集群 SA 组授权的影响范围
下面是组匹配语法示例,会把所有命名空间中的所有 SA 统一授予全局 view 读取权限。view 不是可以随意赋予所有工作负载的“低风险基线”,它仍能读取大量配置和业务元数据。生产环境一般不使用这种全局绑定,应缩小到专用 SA 与 RoleBinding。
kubectl create clusterrolebinding sa-view --clusterrole=view --group=system:serviceaccounts
#system:serviceaccounts指的是集群中所有SA这个组三、用 RBAC 表达动作与资源范围
身份确定以后,授权器检查这次请求的动词、目标 API 组、资源、子资源、命名空间和名称。常见授权器包括 Node(节点授权)、ABAC(属性授权)、RBAC(角色授权)和 Webhook,具体启用组合由 API Server 配置决定。
RBAC 的规则定义在 Role 或 ClusterRole 中,再通过绑定赋给 User、Group 或 SA。RoleBinding 只在其所在命名空间授权,ClusterRoleBinding 才能把相应规则授予整个集群。ClusterRole 表示规则定义可以被全局引用,不代表它天然拥有所有资源权限。
从实际操作清单开始写规则
规划 RBAC 规则 时,先列出实际需要的操作,再组合成角色。同一身份的多个允许规则会叠加,新增一个更窄的绑定不会收回旧绑定的权限。
RBAC 没有显式 deny。如果要减少权限,必须找到并调整提供该权限的已有绑定或角色,不能额外写一个“拒绝角色”。
例如,开发人员可能只需要查看日志与执行排障命令;运维人员可能还需要删除故障 Pod、修改工作负载或查看命名空间。先把这些操作分开,再按环境组合授权,比直接绑定管理员角色更容易检查。
Role 与 ClusterRole:规则定义放在哪里
Role
Role 定义在指定命名空间中,只能授权该范围内的资源。下面定义 rbac 命名空间中的 Pod 读取规则,使用前需先创建该命名空间。rules 的 apiGroups 填组名,版本号不写在这里。
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: pod-read
namespace: rbac
rules:
- apiGroups: [""] # 填API组而非版本:核心组为空字符串,Deployment使用apps
resources: ["pods"] # 支持的资源对象列表,例如pods、deployments、jobs。也支持子资源比如["pods/log"]
resourceNames: [] # 进一步细化,指定某种resource的名称
verbs: ["get","watch","list"] # 对资源对象的操作方法列表。ClusterRole
ClusterRole 本身不属于命名空间,可定义 Node 等集群级资源、/livez 等非资源 URL,以及可复用于各命名空间的 Pod 规则。它是否跨命名空间生效,由引用它的绑定类型决定;创建绑定的操作者也需要相应授权。
下面定义读取 Secret 的规则。Secret 读取可能直接获得凭据,应只授予确有需要的主体,再用目标命名空间的 RoleBinding 或受控的 ClusterRoleBinding 指定范围。
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: secrets-clusterrole
rules:
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get","watch","list"]将常见权限写成 rules 片段
下面每个代码块都是 Role/ClusterRole 中的 rules 片段,不是完整资源。按业务需求选用,并补齐 apiVersion、kind、metadata 与绑定。读写资源时通常要包含 get/list/watch,读取日志与 exec 则是独立子资源。
-
允许读取核心 API 组的 Pod 资源
rules: - apiGroups: [""] resources: ["pods"] verbs: ["get","list","watch"] -
允许读写 apps API 组中的 deployment 资源
rules: - apiGroups: ["apps"] resources: ["deployments"] verbs: ["get","list","watch","create","update","patch","delete"] -
允许读取 Pod 以及读写 job 信息
rules: - apiGroups: [""] resources: ["pods"] verbs: ["get","list","watch"] - apiGroups: ["batch"] resources: ["jobs"] verbs: ["get","list","watch","create","update","patch","delete"] -
允许读取一个名为 my-configmap 的 ConfigMap(必须绑定到一个 RoleBinding 来限制到一个 Namespace 下的 ConfigMap)
rules: - apiGroups: [""] resources: ["configmaps"] resourceNames: ["my-configmap"] verbs: ["get"] -
读取核心组的 Node 资源(Node 属于集群级的资源,所以必须存在于 ClusterRole 中,并使用 ClusterRoleBinding 进行绑定)
rules: - apiGroups: [""] resources: ["nodes"] verbs: ["get","list","watch"] -
允许对非资源端点”/healthz”及其所有子路径进行 GET 和 POST 操作(必须使用 ClusterRole 和 ClusterRoleBinding):
rules: - nonResourceURLs: ["/healthz","/healthz/*"] verbs: ["get","post"]说明
healthz、livez和readyz)来表明 API 服务器的当前状态。healthz端点已被弃用(自 Kubernetes v1.16 起),你应该使用更为明确的livez和readyz端点。Kubernetes API 服务器提供 3 个 API 端点(
使用内置角色前先检查权限边界
内置面向用户的角色便于起步,但其权限可能包含聚合规则。使用前可以读取实际 ClusterRole,再结合业务需求决定是否改用自定义规则。
| 内置 ClusterRole | 常见用途与边界 |
|---|---|
cluster-admin | 全集群管理能力,保留给严格控制的管理或恢复身份 |
admin | 通过 RoleBinding 管理命名空间,包含许多权限管理能力 |
edit | 编辑多数业务对象,不管理 Role/Binding;可读 Secret 并运行使用其他 SA 的 Pod,需要评估提权边界 |
view | 读取多数业务对象,默认排除 Secret 与 RBAC 对象 |
RoleBinding 和 ClusterRoleBinding
RoleBinding 与 ClusterRoleBinding 把规则授予 subjects。主体可以是 User、Group 或 ServiceAccount,前两者的名称必须与认证器返回的值一致;SA 则需要同时指定 name 和 namespace。
RoleBinding
RoleBinding 引用本命名空间 Role,或引用一个 ClusterRole,只为绑定所在命名空间提供该项权限。其他已有绑定仍可能赋予更多权限,所以“新建一个 RoleBinding”不能单独证明用户只有这一范围。roleRef 是单个对象,创建后不可修改。
# 将在rbac命名空间中把pod-read角色授予用户es
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: pod-read-bind
namespace: rbac
subjects: # 被绑定对象,可以指定多个
- kind: User # User、Group、ServiceAccount
name: es
apiGroup: rbac.authorization.k8s.io
roleRef: # 单个对象,不是列表;创建后不可修改
kind: Role
name: pod-read
apiGroup: rbac.authorization.k8s.ioClusterRoleBinding
将 ClusterRole 绑定到用户、组或 ServiceAccount,不用指定 ns,绑定后用户具备集群范围内的权限。
# es用户获得全局view读取权限,仍不包括Secret和任意管理动作
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: es-view
subjects:
- kind: User
name: es
apiGroup: rbac.authorization.k8s.io
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: viewkubectl 管理 RBAC
# 创建一个可以查询Pod的Role:
kubectl create role pod-reader --verb=get --verb=list --verb=watch --resource=pods
# 创建一个对rs有权限的role。指定非核心组,需要写上组名apps
kubectl create role rs-role --verb=get,list,watch --resource=replicasets.apps
# 创建一个可以查询Pod的ClusterRole:
kubectl create clusterrole pod-reader --verb=get,list,watch --resource=pods
# 创建一个RoleBinding,把pod-reader 绑定至default 空间下的用户:
kubectl create rolebinding hangx-pod-reader --clusterrole=pod-reader --serviceaccount=default:hangx
# 下面仍是RoleBinding,只把admin规则授予default内资源;仅用于管理身份演示:
kubectl create rolebinding hangx-admin-binding --clusterrole=admin --serviceaccount=default:hangx
# 验证某个用户是否具有某个权限:
kubectl auth can-i get configmaps -n default --as=system:serviceaccount:default:hangx
kubectl auth can-i get configmaps --as=system:serviceaccount:default:hangx -n kube-system--as 使用的是身份模拟,操作者需要相应 impersonate 权限。测试依赖组的规则时,还要通过 --as-group 传入需要模拟的组,或直接用目标身份的真实凭据验证;只填用户名不代表复现了全部组成员关系。
从三个主体示例理解身份匹配
User、Group 与 ServiceAccount 的 subjects 写法
-
用户名 alice
subjects: - kind: User name: alice apiGroup: rbac.authorization.k8s.io -
组名 alice
subjects: - kind: Group name: alice apiGroup: rbac.authorization.k8s.io -
Service Account
subjects: - kind: ServiceAccount # kube-system命名空间中默认sa name: default namespace: kube-system - kind: Group name: system:serviceaccounts:qa # 对qa命名空间中的所有sa apiGroup: rbac.authorization.k8s.io
RoleBinding 引用同命名空间 Role
这张图把规则定义与绑定放在同一个命名空间,用户获得其中明确列出的动作。

RoleBinding 复用 ClusterRole
如果 6 个命名空间需要同一套权限,可以定义一个 ClusterRole,再在每个命名空间创建自己的 RoleBinding,复用规则而保留范围隔离。ClusterRole 应只写所需动作,不能因为要复用就授予所有权限;每个绑定引用的主体也可不同。

ClusterRoleBinding 授权到全局
ClusterRoleBinding 不指定 namespace,会将 ClusterRole 中的相应规则应用到全局。用于 pods 规则时可覆盖全部命名空间,用于 nodes 等资源时则提供集群级访问;仍只允许规则列出的动作。

四、把常用运维动作拆成可组合角色
下面按查看命名空间、指标、日志、exec、删除与编辑拆分角色。先创建规则,再按环境绑定;每份 YAML 是独立示例,实际需要先保存为文件并 apply。读取日志与执行命令都可能接触凭据或业务数据,生产授权应结合审计和职责范围评估。
命名空间列表与 Pod 指标权限
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: namespace-readonly
rules:
- apiGroups:
- ""
resources:
- namespaces
verbs:
- get
- list
- watch
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: pod-metrics-reader
rules:
- apiGroups:
- metrics.k8s.io
resources:
- pods
verbs:
- get
- list
- watch命名空间列表是集群级读取;Pod 指标则可以只通过目标命名空间的 RoleBinding 授权。这里拆成两个角色,避免查看 Namespace 时顺带获得所有业务的指标。
Pod 删除权限
删除权限会影响可用性,通常与读取权限分开绑定。下面只在 Pod 资源上保留 delete,读取状态不需要对子资源授予删除动作。
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: pod-delete
rules:
- apiGroups:
- ""
resources:
- pods
verbs:
- get
- list
- delete执行命令权限
pods/exec 允许在业务容器内执行命令,可能读取容器中的密钥与数据。示例授予常见 POST 路径使用的 create;如客户端使用不同的连接方法,应核实所需动词,不能用全资源通配符处理连接失败。
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: pod-exec
rules:
- apiGroups:
- ""
resources:
- pods
- pods/status
verbs:
- get
- list
- apiGroups:
- ""
resources:
- pods/exec
verbs:
- create查看日志权限
日志读取需要 pods/log。下面将 pods 的发现权限与日志读取放在同一角色,方便先列出 Pod 再选择目标。
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: pod-log
rules:
- apiGroups:
- ""
resources:
- pods
- pods/log
- pods/status
verbs:
- get
- list
- watch资源编辑权限
这个角色允许创建和修改 ConfigMap 与 Deployment,不包含删除。修改 Deployment 仍可能改变容器运行身份,绑定前需要检查该命名空间允许使用的 SA 与准入约束。
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: configmap-deployment-manager
rules:
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["get", "list", "watch", "create", "update", "patch"]
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "list", "watch", "create", "update", "patch"]创建实验身份与命名空间
本节继续用 SA 模拟开发与运维身份,便于执行 auth can-i;真实人员访问应接入个人身份系统。下面的组绑定会影响 kube-users 内未来新增的账户,必须将该命名空间作为受控身份范围。
# 创建一个专用于存储用户的Namespace
kubectl create ns kube-users
# 因为在这个场景下,所有用户都是用过sa来作为用户登录的。
# 授权kube-users空间下的所有sa都有查看Namespace的权限
kubectl create clusterrolebinding namespace-readonly \
--clusterrole=namespace-readonly \
--group=system:serviceaccounts:kube-users
# 创建多个用户模拟不同的场景:
kubectl create sa project-a-develop -n kube-users
kubectl create sa project-a-ops -n kube-users
# 创建Namespace 模拟不同的环境:
kubectl create ns project-a-dev
kubectl create ns project-a-test
kubectl create ns project-a-prod非生产环境管理
在非生产环境,可以针对开发和测试人员开放查看日志和执行命令的权限,方便排查问题等。
授权 project-a-develop 用户对 project-a-dev 和 project-a-test 两个空间可以有查看日志和执行命令的权限:
kubectl create rolebinding develop-pod-log \
--clusterrole=pod-log \
--serviceaccount=kube-users:project-a-develop \
-n project-a-dev
kubectl create rolebinding develop-pod-exec \
--clusterrole=pod-exec \
--serviceaccount=kube-users:project-a-develop \
-n project-a-dev
kubectl create rolebinding develop-pod-log \
--clusterrole=pod-log \
--serviceaccount=kube-users:project-a-develop \
-n project-a-test
kubectl create rolebinding develop-pod-exec \
--clusterrole=pod-exec \
--serviceaccount=kube-users:project-a-develop \
-n project-a-test
# 创建token测试
kubectl create token project-a-develop -n kube-users
# 之后可以登录到dashboard测试权限或者生成kubeconfig进行测试生产环境管理
生产环境可以从只读日志开始授权,确有排障需要时再开放更高权限。日志本身可能包含敏感数据,也应按项目范围控制。
授权开发人员只能查看生产环境的日志权限:
kubectl create rolebinding develop-pod-log \
--clusterrole=pod-log \
--serviceaccount=kube-users:project-a-develop \
-n project-a-prod开发人员可以修改资源
ConfigMap 与 Deployment 的修改可用于测试配置,但修改 Deployment 还可能改变镜像、挂载与 ServiceAccount。这是工作负载执行能力,不能仅当成“改配置”的低权限。下面只向开发环境开放该组合角色。
kubectl create rolebinding develop-configmap-deployment-manager \
--clusterrole=configmap-deployment-manager \
--serviceaccount=kube-users:project-a-develop \
-n project-a-dev多租户具备受限管理员权限
运维人员若确实需要管理指定命名空间的多数业务对象,可以评估 edit 或 admin。它们不代表所有动作都被允许,且 edit 的工作负载与 Secret 权限可能使用户获得该命名空间其他 SA 的能力。强多租户环境还需准入策略等额外约束。
例如,分别向 project-a-dev、project-a-test、project-a-prod 绑定 edit,提供多数业务对象的编辑能力:
kubectl create rolebinding ops-edit \
--clusterrole=edit \
--serviceaccount=kube-users:project-a-ops \
-n project-a-dev
kubectl create rolebinding ops-edit \
--clusterrole=edit \
--serviceaccount=kube-users:project-a-ops \
-n project-a-test
kubectl create rolebinding ops-edit \
--clusterrole=edit \
--serviceaccount=kube-users:project-a-ops \
-n project-a-prod此时使用 project-a-ops 用户登录集群,即可操作上述空间的大部分资源。
应用程序访问集群资源
应用与人的访问一样,需要明确读取哪些资源。下面为应用建立独立 app-view SA,再在本命名空间绑定 view;如果只需读取 Pod 状态,还可以继续缩小为自定义 Role。
kubectl create sa app-view -n project-a-dev
# 给这个sa授权
kubectl create rolebinding app-view \
--clusterrole=view \
--serviceaccount=project-a-dev:app-view \
-n project-a-dev创建 Pod 时,在 spec.serviceAccountName 指定 app-view。身份被赋予 Pod 后,客户端库仍需要正确读取凭据并调用 API。
说明
给 default SA 增加权限,会影响所有使用它的 Pod。为不同权限需求建立独立 SA,才能分别检查和撤销授权。
五、从 kubeconfig 找到请求真正使用的凭据
客户端认证
kubectl 先处理显式的 --kubeconfig 参数;没有时使用 KUBECONFIG 指定的文件列表,否则读取 $HOME/.kube/config。路径随当前用户变化,不固定为 /root/.kube/config,也不要求把环境变量指向管理员配置。
kubeconfig 决定访问的集群、CA 和用户凭据。使用客户端证书时,TLS 同时验证服务端证书与客户端证书;使用 token 或 exec 插件时,则不必存在客户端证书。下面的图用于说明证书认证的链路。

Bearer Token 如何发送身份凭据
Bearer token 放在 HTTP Authorization: Bearer ... 头中。持有有效 token 的请求方就能使用相应身份,因此必须通过 TLS 传输并保护存储。它不是 API Server 用非对称加密发送给 kubectl 的“密码”;JWT 等 token 可能使用签名验证,签名与内容加密是不同机制。

kubeconfig 文件
多集群 kubeconfig 合并、context/namespace 切换及 kubectx/kubens 防误操作实践,见 ../k8s-installation-management/k8s多集群kubeconfig管理.md。
读取 kubeconfig 时,按 current-context 找到上下文,再查它引用的 cluster 和 user。官方 kubeconfig 组织方式 描述了这些本地配置之间的关系。下面先查看不含敏感值的结构:
kubectl config viewapiVersion: v1
clusters:
- cluster:
certificate-authority-data: DATA+OMITTED
server: https://192.168.40.4:6443 #apiserver的地址
name: kubernetes #集群的名字
contexts:
- context:
cluster: kubernetes
user: kubernetes-admin
name: kubernetes-admin@kubernetes #上下文的名字
current-context: kubernetes-admin@kubernetes #当前上下文的名字
kind: Config
preferences: {}
users:
- name: kubernetes-admin
user:
client-certificate-data: REDACTED
client-key-data: REDACTED这里的当前上下文是 kubernetes-admin@kubernetes,它引用名为 kubernetes 的集群和名为 kubernetes-admin 的用户。集群项给出 API 地址与 CA,用户项给出客户端证书和密钥。配置中的名字只是索引,真正的用户名仍由服务端对凭据的认证结果决定。
ServiceAccount
Pod 中的 ServiceAccount 也使用 Bearer token 认证,并没有独立于 token 的另一种传输协议。默认投射卷同时提供 token、CA 和 namespace:token 证明身份,CA 用于验证服务端,namespace 表示当前 Pod 的命名空间。它们用途不同,不能用 namespace 文件决定实际权限。

六、授权通过以后,准入还会检查对象
哪些请求需要准入检查
准入在认证与授权之后处理会变更对象的请求,例如 CREATE、UPDATE、DELETE,也可处理部分 CONNECT 操作。它可以修改对象(mutating)或拒绝不符合要求的对象(validating),但不会拦截普通 GET、LIST、WATCH 读取。
例如,一个身份可以被允许创建 Pod,但 Pod 的资源声明不满足 LimitRange 时仍会被拒绝。ServiceAccount、LimitRanger、ResourceQuota 等内置插件也参与这一过程;外部 Webhook 是扩展方式之一,并不是所有准入插件都需要网络回调。
查看实际启用的插件与版本默认值
LimitRanger、ResourceQuota 和 ServiceAccount 是常见的默认插件。实际启用项取决于版本、默认列表及显式 enable/disable 参数,应查当前集群的配置。
--enable-admission-plugins 会在默认集合之外增加指定插件,--disable-admission-plugins 用于禁用项。因此 manifest 中只看到 NodeRestriction,不表示其他默认插件没启用。修改启动参数会重启相应控制平面实例,读取与变更应分开处理。
-
用 kubeadm 安装的集群,如何查看 apiserver 开启的准入控制器?
#kubeadm安装的集群,登录到master节点 cat /etc/kubernetes/manifests/kube-apiserver.yaml apiVersion: v1 kind: Pod metadata: annotations: kubeadm.kubernetes.io/kube-apiserver.advertise-address.endpoint: 192.168.40.4:6443 creationTimestamp: null labels: component: kube-apiserver tier: control-plane name: kube-apiserver namespace: kube-system spec: containers: - command: ... - --enable-admission-plugins=NodeRestriction ...#用command查看 kube-apiserver -h | grep enable-admission-plugins #在kubernetes 1.32中,默认开启的admission controller plugin如下: CertificateApproval, CertificateSigning, CertificateSubjectRestriction, DefaultIngressClass, DefaultStorageClass, DefaultTolerationSeconds, LimitRanger, MutatingAdmissionWebhook, NamespaceLifecycle, PersistentVolumeClaimResize, PodSecurity, Priority, ResourceQuota, RuntimeClass, ServiceAccount, StorageObjectInUseProtection, TaintNodesByCondition, ValidatingAdmissionPolicy, ValidatingAdmissionWebhook
上面的列表标明了 1.32 这个版本范围。查询其他版本时,以对应版本的 准入控制器说明 和实际二进制帮助为准。
七、用日志读取实验理解资源与子资源
API URL 中的资源名使用复数,例如 /api/v1/namespaces/{namespace}/pods/{podname}。日志则位于对象的 /log 子路径,在 RBAC 中写为 pods/log。HTTP URL、API 资源名和 YAML kind 不是相同字符串:Pod 的 kind 为 Pod,资源名却是 pods。
创建只读日志身份并在 Pod 内验证
先创建 rbac 命名空间:kubectl create namespace rbac。下面按 Role、SA、RoleBinding、测试 Pod 的顺序操作;Role 的 rules 是授权规则,不会自动生成身份。日志接口使用 pods/log 子资源,单有 pods 的权限不一定能读取日志。
第一步,定义读取 Pod 与日志的 Role:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: logs-reader
namespace: rbac
rules:
- apiGroups: [""]
resources: ["pods","pods/log"]
verbs: ["get","list","watch"]第二步,创建独立 SA:
kubectl create sa sa-test -n rbac第三步,用 RoleBinding 把规则授予该 SA:
kubectl create rolebinding rb-sa-test-logs-reader -n rbac --role=logs-reader --serviceaccount=rbac:sa-test第四步,创建使用该 SA 的 Pod。下面 Nginx 镜像需包含 bash 与 curl,若所选标签缺少工具,应改用带工具的实验镜像或临时调试容器。
apiVersion: v1
kind: Pod
metadata:
name: pod-sa-test
namespace: rbac
labels:
app: sa
spec:
serviceAccountName: sa-test
containers:
- name: sa-nginx
ports:
- containerPort: 80
image: nginx
imagePullPolicy: IfNotPresent最后进入容器,通过投射 token 读取 Pod 与日志。请求成功说明这一资源与动词组合获得了授权,不表示 SA 有管理整个集群的权限。
kubectl exec -it pod-sa-test -n rbac -- /bin/bash
#这里面保存着sa挂进来的token
cd /var/run/secrets/kubernetes.io/serviceaccount
#看rbac名称空间下的pod
curl --cacert ./ca.crt -H "Authorization: Bearer $(cat ./token)" https://kubernetes.default/api/v1/namespaces/rbac/pods/
#看某个pod的log
curl --cacert ./ca.crt -H "Authorization: Bearer $(cat ./token)" https://kubernetes.default/api/v1/namespaces/rbac/pods/pod-sa-test/log
exit # 退出容器,后续证书操作在管理员终端执行八、通过客户端证书演示命名空间范围
下面只用于自建测试集群中理解证书 CN 与 RBAC 的映射,需要已获授权的签发环境。ca.key 是集群根私钥,不得复制给用户;托管集群或企业日常访问应使用平台提供的身份签发流程。证书有效期也应按组织策略控制,此处使用 30 天演示,不能把长期签发替代轮换。
为新身份授予指定命名空间的读取权限
-
SSL 认证
#生成私钥 cd /etc/kubernetes/pki/ (umask 077; openssl genrsa -out hangx.key 2048) #生成证书请求 openssl req -new -key hangx.key -out hangx.csr -subj "/CN=hangx" #生成证书,表明这个用户被api server信任 openssl x509 -req -in hangx.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out hangx.crt -days 30 -
配置 k8s 用户
#把hangx用户添加到集群中,可以用来认证apiserver的连接 kubectl config set-credentials hangx --client-certificate=./hangx.crt --client-key=./hangx.key --embed-certs=true #在kubeconfig中新增hangx账号 kubectl config set-context hangx@kubernetes --cluster=kubernetes --user=hangx #切换到hangx用户 kubectl config use-context hangx@kubernetes #切回cluster-admin kubectl config use-context kubernetes-admin@kubernetes #给hangx用户rolebinding授权 kubectl create ns hangx-test kubectl create rolebinding hangx -n hangx-test --clusterrole=view --user=hangx #切换用户,测试权限 kubectl config use-context hangx@kubernetes kubectl get pods -n hangx-test exit # 退出su会话,回到管理员终端 -
配置 linux 客户端的用户,让他能用 hangx 的 config 文件访问集群
useradd hangx # 只导出目标上下文,避免复制管理员证书与其他身份 umask 077 kubectl --context=hangx@kubernetes config view --minify --flatten --raw > /tmp/hangx-only.yaml install -d -m 700 -o hangx -g hangx /home/hangx/.kube install -m 600 -o hangx -g hangx /tmp/hangx-only.yaml /home/hangx/.kube/config # 核对导出的users只有目标身份,并按凭据管理流程清理临时副本 #切换用户 su - hangx #验证权限 kubectl get pods -n hangx-test exit # 退出su会话,回到管理员终端
为另一个身份授予全部命名空间的 Pod 读取权限
-
SSL 认证
#生成私钥 cd /etc/kubernetes/pki/ (umask 077; openssl genrsa -out hangx1.key 2048) #生成证书请求 openssl req -new -key hangx1.key -out hangx1.csr -subj "/CN=hangx1" #生成证书,表明这个用户被api server信任 openssl x509 -req -in hangx1.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out hangx1.crt -days 30 -
配置 k8s 用户
#把hangx用户添加到集群中,可以用来认证apiserver的连接 kubectl config set-credentials hangx1 --client-certificate=./hangx1.crt --client-key=./hangx1.key --embed-certs=true #在kubeconfig中新增hangx账号 kubectl config set-context hangx1@kubernetes --cluster=kubernetes --user=hangx1 #切换到hangx用户 kubectl config use-context hangx1@kubernetes #切回cluster-admin kubectl config use-context kubernetes-admin@kubernetes -
创建 role 和 rolebinding
# 创建ClusterRole清单 cat > cr-pod-reader.yaml <<'EOF' apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: cr-pod-reader rules: - apiGroups: [""] resources: ["pods"] verbs: ["get", "list", "watch"] EOF kubectl apply -f cr-pod-reader.yaml #给hangx用户clusterrolebinding授权 # hangx-test已在上一实验创建;这里只添加集群范围绑定 kubectl create clusterrolebinding rb-hangx-cr --clusterrole=cr-pod-reader --user=hangx1 #切换用户,测试权限 kubectl config use-context hangx1@kubernetes kubectl get pods --all-namespaces
将 hangx1 的独立配置分发给对应 Linux 用户:
useradd hangx1
# 只导出目标上下文,避免复制管理员证书与其他身份
umask 077
kubectl --context=hangx1@kubernetes config view --minify --flatten --raw > /tmp/hangx1-only.yaml
install -d -m 700 -o hangx1 -g hangx1 /home/hangx1/.kube
install -m 600 -o hangx1 -g hangx1 /tmp/hangx1-only.yaml /home/hangx1/.kube/config
# 核对导出的users只有目标身份,并按凭据管理流程清理临时副本
#切换用户
su - hangx1
#验证权限
kubectl get pods --all-namespaces
exit # 退出su会话,回到管理员终端九、用 ResourceQuota 限制命名空间总量
RBAC 只控制身份能否创建对象,不负责给团队计算资源预算。ResourceQuota 在命名空间层面检查声明总量与对象数量;下面先对 CPU、内存、Pod、Deployment 和 PVC 数量设置配额。需要先回到有权限的管理员上下文,执行 kubectl create namespace quota,再应用清单。自建示例可用 kubectl config use-context kubernetes-admin@kubernetes 切回;其他集群应使用实际管理上下文。
超出限制时,新的对象可能被准入拒绝,但不会为满足新配额而自动终止已运行对象。若 CPU/内存配额要求容器有对应 requests/limits,可通过下一节的 LimitRange 补默认值。
限制 CPU/memory/deploy 等资源量
#对ns做限制
apiVersion: v1
kind: ResourceQuota
metadata:
name: quota-test
namespace: quota
spec:
hard:
pods: "6"
count/deployments.apps: "6"
persistentvolumeclaims: "6"
requests.cpu: "2"
requests.memory: 2Gi
limits.cpu: "4"
limits.memory: 10Gikubectl get quota -n quota
kubectl get resourcequota -n quota限制存储空间大小
apiVersion: v1
kind: ResourceQuota
metadata:
name: quota-storage-test
namespace: quota
spec:
hard:
persistentvolumeclaims: "5"
requests.storage: "5Gi" # 所有PVC声明的存储请求总和不超过5Gi
requests.ephemeral-storage: "1Gi" #限制本地临时存储
limits.ephemeral-storage: "2Gi"十、用 LimitRange 约束单个对象
ResourceQuota 限制总量,LimitRange 则约束单个容器、Pod 或 PVC 的资源声明。它可以为未设置的 requests/limits 补默认值,也可以检查最小值、最大值和 limit/request 比例。检查的是准入时声明,实际 CPU 与内存使用由运行时和节点资源机制约束。
下面使用 type: Container,表示逐容器检查。default 是 limit 默认值,defaultRequest 是 request 默认值,maxLimitRequestRatio 约束两者比例。已有字段不会被默认值覆盖,但补齐后的配置仍需同时满足 LimitRange 与 ResourceQuota。
apiVersion: v1
kind: LimitRange
metadata:
name: lr-cpu-memory
namespace: quota
spec:
limits:
- default: #如果创建pod不指定limit,就自动用这里default指定的值
cpu: 1000m
memory: 1000Mi
defaultRequest: #如果创建pod不指定request,就自动用这里default指定的值
cpu: 500m
memory: 500Mi
min: #pod的yaml里面如果自己定义request,不能少于500m cpu,500mimemory
cpu: 500m
memory: 500Mi
max: #pod的yaml里面如果自己定义了limit,不能多于2000m cpu和2000mimemory
cpu: 2000m
memory: 2000Mi
maxLimitRequestRatio: #定义limit/request的比值<=4。自己定义poid的limit/request比值不能高于4
cpu: 4
memory: 4
type: Container应用这些规则后,可以按请求路径验证:先用 kubectl auth can-i 检查身份是否允许创建,再创建满足或故意超出约束的测试 Pod,查看 API 错误、ReplicaSet 事件和实际 Pod 字段。这样能区分“没有创建权限”与“有权限但对象未通过准入”,也能避免为了修复配额错误而扩大用户权限。