查看源文件

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 的配置提交到仓库。

  1. 获取 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
  2. 获取 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)
  3. 生成 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 引用的是这些本地配置名称,它不会创建服务端用户或授予权限。

  4. 生成之后就可以使用新的 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.io

ClusterRoleBinding

将 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: view

kubectl 管理 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:在一个命名空间授予规则

RoleBinding 复用 ClusterRole

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

RoleBinding:复用 ClusterRole,保留范围隔离

ClusterRoleBinding 授权到全局

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

ClusterRoleBinding:将规则应用到全局

四、把常用运维动作拆成可组合角色

下面按查看命名空间、指标、日志、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 插件时,则不必存在客户端证书。下面的图用于说明证书认证的链路。

客户端证书:kubectl 与 API Server 的信任

Bearer Token 如何发送身份凭据

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

Bearer Token:身份凭据随请求发送

kubeconfig 文件

多集群 kubeconfig 合并、context/namespace 切换及 kubectx/kubens 防误操作实践,见 ../k8s-installation-management/k8s多集群kubeconfig管理.md。

读取 kubeconfig 时,按 current-context 找到上下文,再查它引用的 cluster 和 user。官方 kubeconfig 组织方式 描述了这些本地配置之间的关系。下面先查看不含敏感值的结构:

kubectl config view
apiVersion: 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 文件决定实际权限。

ServiceAccount:Pod 内凭据各司其职

六、授权通过以后,准入还会检查对象

哪些请求需要准入检查

准入在认证与授权之后处理会变更对象的请求,例如 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: 10Gi
kubectl 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 字段。这样能区分“没有创建权限”与“有权限但对象未通过准入”,也能避免为了修复配额错误而扩大用户权限。