K8S安全框架深度解析:从认证到RBAC实战完全指南
导读 :Kubernetes 的安全体系是生产环境的基石。一个配置不当的集群,可能因为权限滥用、匿名访问或认证绕过导致严重的安全事故。本文从 API Server 的角度发,逐步拆解 认证 → 鉴权 → 准入控制 三层防线,并通过 静态令牌认证、X509 证书签发、RBAC 角色绑定、ServiceAccount 授权 四个递进式实战,帮你彻底掌握 K8S 安全框架。
一、API Server 访问控制全貌
Kubernetes API Server 是集群的唯一入口 ,所有对集群资源的操作(无论是 kubectl 命令、客户端 SDK 还是 Dashboard)最终都是向 API Server 发送 HTTPS 请求。因此,API Server 内置了一套插件化的访问控制机制,分为三层:
客户端请求 ──→认证(Authentication) ──→鉴权(Authorization) ──→ 准入控制(Admission) ──→ etcd
身份是谁? 能做什么操作? 操作内容合规吗?
| 层级 | 作用 | 逻辑 | 说明 |
|---|---|---|---|
| 认证 | 核验请求者身份 | 或逻辑(任一插件成功即通过) | 回答"你是谁" |
| 鉴权 | 核验操作是否被许可 | 或逻辑(任一插件授权即通过) | 回答"你能做什么" |
| 准入控制 | 检查操作内容合规性 | 与逻辑(所有插件都必须通过) | 回答"操作内容合规吗",仅对写请求有效 |
1.1 认证(Authentication)
认证过程回答的是 "你是谁" 的问题。K8S 支持多种认证方式,任何一个认证插件成功后即停止后续验证(或逻辑 )。如果所有插件都失败,请求将以匿名用户 system:anonymous 身份继续。
支持的主要认证策略:
| 认证方式 | 适用场景 | 说明 |
|---|---|---|
| X.509 客户端证书 | 各组件间通信、管理员访问 | 双向 TLS,CN=用户名,O=用户组 |
| 静态令牌文件 | 测试、简单场景 | --token-auth-file 加载 CSV 文件 |
| Bootstrap 令牌 | 节点加入集群 | kubeadm token 管理 |
| ServiceAccount 令牌 | Pod 内进程访问 API Server | 自动挂载,内置启用 |
| OIDC 令牌 | 企业统一认证 | 类似 OAuth2(微信/支付宝登录) |
| Webhook 令牌 | 外部认证集成 | 支持 LDAP、钉钉等 |
| 匿名请求 | 开发测试 | 生产环境建议禁用 |
1.2 鉴权(Authorization)
认证通过后,鉴权层判断该用户是否有权限执行请求的操作。K8S 同样采用或逻辑------任一授权插件允许即通过。
主要鉴权模式:
| 模式 | 说明 |
|---|---|
| RBAC(推荐) | 基于角色的访问控制,1.8+ 默认启用,本文重点 |
| ABAC | 基于属性的访问控制,维护成本高 |
| Webhook | 通过外部 HTTP 服务进行授权回调 |
| Node | 专门为 kubelet 设计的授权模式 |
1.3 准入控制(Admission Control)
准入控制在数据写入 etcd 之前做最后的检查,仅对写请求生效,分为两类:
- Validating(校验):检查字段类型、值是否合法
- Mutating(变更):自动补全或修正默认字段(如自动注入 Sidecar)
准入控制遵循与逻辑,所有插件都必须通过。
二、Kubernetes 用户体系
理解 K8S 的用户体系是掌握安全框架的前提。K8S 中的"用户"分为三类:
┌─────────────────────────────────────────────────┐
│ Kubernetes 用户体系 │
├──────────────────┬──────────────────────────────┤
│ Service Account │ Pod 内进程访问 API Server │
│ (服务账号) │ 引用格式: system:serviceaccount│
│ │ :<NS>:<SA_NAME> │
├──────────────────┼──────────────────────────────┤
│ User Account │ 人(管理员/开发者)访问集群 │
│ (用户账号) │ 信息保存在 kubeconfig 文件中 │
│ │ CN=用户名, O=用户组 │
├──────────────────┼──────────────────────────────┤
│ Anonymous │ 未提供认证信息的请求者 │
│ (匿名账号) │ system:anonymous │
│ │ 生产环境建议禁用 │
└──────────────────┴──────────────────────────────┘
关键区别 :ServiceAccount 是 K8S 内置的资源类型,可以通过
kubectl创建和管理;而 UserAccount 不是资源对象,其信息存储在外部文件(kubeconfig)或外部认证系统中。
三、实战一:静态令牌文件认证
静态令牌认证是最直观的认证方式,适合理解 K8S 认证流程。我们将创建一个 CSV 格式的令牌文件,配置 API Server 加载它,并通过令牌访问集群。
3.1 生成 Token
bash
# 方式一:openssl 随机生成
echo "$(openssl rand -hex 3).$(openssl rand -hex 8)"
# 输出示例:01b202.d5c4210389cbff08
# 方式二:kubeadm 生成
kubeadm token generate
# 输出示例:jvt496.ls43vufojf45q73i
3.2 创建 Token CSV 文件
bash
cat > /etc/kubernetes/pki/token.csv <<EOF
01b202.d5c4210389cbff08,test01,10001,k8s
497804.9fc391f505052952,test02,10002,k8s
8fd32c.0868709b9e5786a8,test03,10003,k3s
EOF
CSV 格式说明 :
token,用户名,用户ID,用户组,共四个字段,用户组可选。
3.3 修改 API Server 加载 Token 文件
编辑 /etc/kubernetes/manifests/kube-apiserver.yaml,添加启动参数和卷挂载:
yaml
spec:
containers:
- command:
- kube-apiserver
- --token-auth-file=/etc/kubernetes/pki/token.csv # 添加此行
...
volumeMounts:
- mountPath: /etc/kubernetes/pki/token.csv # 添加挂载
name: static-token-file
readOnly: true
volumes:
- hostPath:
path: /etc/kubernetes/pki/token.csv
type: File
name: static-token-file
通过移动 YAML 文件触发 Pod 重建来重启 API Server:
bash
mv /etc/kubernetes/manifests/kube-apiserver.yaml /opt/
mv /opt/kube-apiserver.yaml /etc/kubernetes/manifests/
# 等待 30s+,确认 API Server 就绪
kubectl get pods -n kube-system -l component=kube-apiserver -o wide
3.4 使用 Token 认证访问集群
bash
# 场景1:使用有效 Token 认证(已通过认证,但未授权 → 403 Forbidden)
kubectl --server=https://10.0.0.231:6443 \
--certificate-authority=/etc/kubernetes/pki/ca.crt \
--token=01b202.d5c4210389cbff08 get nodes
# Error from server (Forbidden): User "test01" cannot list resource "nodes"
# 场景2:使用无效 Token(未通过认证 → 401 Unauthorized)
kubectl --server=https://10.0.0.231:6443 \
--certificate-authority=/etc/kubernetes/pki/ca.crt \
--token=invalid.token get nodes
# error: You must be logged in to the server (Unauthorized)
# 场景3:不提供任何认证信息(匿名用户 → 403 Forbidden)
kubectl --server=https://10.0.0.231:6443 \
--certificate-authority=/etc/kubernetes/pki/ca.crt get nodes
# Error from server (Forbidden): User "system:anonymous" cannot list resource "nodes"
关键理解:这三个场景完美演示了认证与鉴权的区别。场景1认证通过(用户身份已识别为 test01),但鉴权失败(无权限);场景2认证未通过(Token 不存在);场景3被识别为匿名用户。
3.5 curl 基于 Token 的认证测试
bash
# 有效 Token(认证通过,但无权限)
curl -k -H "Authorization: Bearer 01b202.d5c4210389cbff08" \
https://10.0.0.231:6443/api/v1/pods
# 返回 403 Forbidden
# 无效 Token
curl -k -H "Authorization: Bearer invalid.token" \
https://10.0.0.231:6443/api/v1/pods
# 返回 401 Unauthorized
# 无认证信息
curl -k https://10.0.0.231:6443
# 返回 403 Forbidden (system:anonymous)
四、实战二:X509 证书认证
X509 证书认证是 K8S 生产环境最常用的认证方式。通过自签名证书,将证书中的 CN 字段映射为用户名、O 字段映射为用户组。
4.1 认证流程
客户端 Master 节点
│ │
├─ 1.生成私钥 (openssl genrsa) │
├─ 2.生成证书签发请求 CSR │
│ (CN=用户名, O=用户组) │
├─ 3.CSR 进行 base64 编码 ───────→ 4.创建 CSR 资源
│ 5.管理员批准 CSR
│ ←──── 6.导出签发的证书 ───── 7.base64 解码证书
│ │
├─ 8.配置 kubeconfig 使用证书 │
├─ 9.kubectl 访问集群 │
4.2 客户端创建证书签发请求
bash
# 1. 生成私钥
openssl genrsa -out test.key 2048
# 2. 生成 CSR(CN=用户名,O=用户组)
openssl req -new -key test.key -out test.csr \
-subj "/CN=test/O=group"
# 3. 对 CSR 进行 base64 编码
cat test.csr | base64 | tr -d '\n'
4.3 Master 节点签发证书
yaml
# csr-test.yaml
apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
metadata:
name: test-csr
spec:
# 将 base64 编码的 CSR 内容粘贴到这里
request: LS0tLS1CRUdJTi...(base64编码的CSR内容)
# 签名者类型
signerName: kubernetes.io/kube-apiserver-client
# 证书有效期(单位:秒,86400 = 24小时)
expirationSeconds: 86400
# 密钥用途
usages:
- client auth
bash
# 创建 CSR 资源
kubectl apply -f csr-test.yaml
kubectl get csr
# NAME AGE SIGNERNAME REQUESTOR CONDITION
# test-csr 4s kubernetes.io/kube-apiserver-client kubernetes-admin Pending
# 管理员批准 CSR
kubectl certificate approve test-csr
kubectl get csr
# test-csr 47s kubernetes.io/kube-apiserver-client Approved,Issued
# 导出签发的证书
kubectl get csr test-csr -o jsonpath='{.status.certificate}' \
| base64 -d > test.crt
4.4 CSR 签名者类型说明
K8S 支持三种 CSR 签名者类型:
| signerName | 用途 | 自动批准 |
|---|---|---|
kubernetes.io/kube-apiserver-client |
客户端证书(如管理员) | 不自动批准 |
kubernetes.io/kube-apiserver-client-kubelet |
Kubelet 客户端证书 | 可自动批准 |
kubernetes.io/kubelet-serving |
Kubelet 服务端证书 | 不自动批准 |
4.5 配置 kubeconfig 使用证书
将签发的证书和私钥配置到 kubeconfig 中:
bash
# 设置集群信息
kubectl config set-cluster my-cluster \
--server=https://10.0.0.231:6443 \
--certificate-authority=/etc/kubernetes/pki/ca.crt \
--kubeconfig=/root/test-kubeconfig \
--embed-certs
# 设置用户凭证(使用签发的证书和私钥)
kubectl config set-credentials test \
--client-certificate=/root/test.crt \
--client-key=/root/test.key \
--kubeconfig=/root/test-kubeconfig \
--embed-certs
# 设置上下文
kubectl config set-context test-context \
--cluster=my-cluster \
--user=test \
--kubeconfig=/root/test-kubeconfig
# 切换当前上下文
kubectl config use-context test-context \
--kubeconfig=/root/test-kubeconfig
测试访问:
bash
# 认证成功(CN 被识别为用户名 "test",O 被识别为用户组 "group")
kubectl --kubeconfig=/root/test-kubeconfig get nodes
# Error from server (Forbidden): User "test" cannot list resource "nodes"
# 注意:此时虽然认证通过,但鉴权尚未配置,所以返回 403
要点:证书中的 CN 字段会被识别为用户名,O 字段会被识别为用户组。这个映射关系是 RBAC 鉴权的基础。
五、RBAC 授权机制详解
RBAC(Role-Based Access Control)是 K8S 1.8 之后默认启用且推荐的授权模式。它的核心思想是:不直接给用户授权,而是将权限绑定到角色上,再将角色绑定给用户。
5.1 RBAC 四大资源
┌──────────────────────────────────────────────────┐
│ RBAC 资源模型 │
│ │
│ Role ──────┐ │
│ ├──→ RoleBinding ──→ (User/Group/SA)│
│ ClusterRole┼ │
│ ├──→ ClusterRoleBinding → (User/...) │
│ │ │
│ └──→ RoleBinding ──→ (User/Group/SA) │
│ (ClusterRole 降级使用) │
└──────────────────────────────────────────────────┘
| 资源 | 作用范围 | 说明 |
|---|---|---|
| Role | 命名空间级别 | 定义在某个命名空间内的权限 |
| ClusterRole | 集群级别 | 定义集群范围的权限,也可用于命名空间 |
| RoleBinding | 命名空间级别 | 将 Role 或 ClusterRole 绑定到用户/组/SA |
| ClusterRoleBinding | 集群级别 | 将 ClusterRole 绑定到用户/组/SA |
关键区别 :Role + RoleBinding = 命名空间级权限;ClusterRole + ClusterRoleBinding = 集群级权限。但 ClusterRole 也可以通过 RoleBinding 绑定,此时 ClusterRole 的权限会被限制在 RoleBinding 所在的命名空间内。
5.2 K8S 内置的 ClusterRole
K8S 自带了几个常用的系统级 ClusterRole:
| ClusterRole | 权限范围 | 典型用途 |
|---|---|---|
| cluster-admin | 超级管理员,所有权限 | 集群管理员 |
| admin | 命名空间管理员(不含资源配额) | 命名空间负责人 |
| edit | 命名空间内读写权限(不含角色/绑定) | 开发者 |
| view | 命名空间内只读权限 | 只读用户 |
六、实战三:RBAC 角色绑定
6.1 创建只读角色(Role + RoleBinding)
给用户 test 授予 kube-public 命名空间的只读权限:
yaml
# reader-role.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: reader
namespace: kube-public
rules:
- apiGroups: ["", "apps"]
resources: ["pods", "deployments", "services"]
verbs: ["get", "list", "watch"]
yaml
# reader-binding.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: reader-bind
namespace: kube-public
subjects:
- kind: User
name: test
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: reader
apiGroup: rbac.authorization.k8s.io
bash
kubectl apply -f reader-role.yaml
kubectl apply -f reader-binding.yaml
测试验证:
bash
kubectl --kubeconfig=/root/test-kubeconfig get pods -n kube-public
# 可以查看 Pod 列表(只读权限生效)
kubectl --kubeconfig=/root/test-kubeconfig delete pod <pod-name> -n kube-public
# Error from server (Forbidden): 用户无删除权限(verbs 中没有 "delete")
6.2 使用 ClusterRole(集群级权限)
创建集群级别的管理员角色:
yaml
# admin-clusterrole.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: admin
rules:
- apiGroups: ["", "apps", "extensions"]
resources: ["*"]
verbs: ["*"]
yaml
# admin-clusterrolebinding.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: admin-bind
subjects:
- kind: User
name: test
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: admin
apiGroup: rbac.authorization.k8s.io
生产提示 :
resources: ["*"]+verbs: ["*"]表示对所有资源拥有所有操作权限,在生产环境中应严格控制 ,遵循最小权限原则。
6.3 快捷绑定内置 ClusterRole
如果只是需要标准权限,可以直接绑定内置的 ClusterRole,无需自定义:
bash
# 将 view 角色绑定给 test 用户(所有命名空间只读)
kubectl create clusterrolebinding test-view-binding \
--clusterrole=view \
--user=test
# 将 admin 角色绑定给 test 用户(所有命名空间管理)
kubectl create clusterrolebinding test-admin-binding \
--clusterrole=admin \
--user=test
# 将 edit 角色绑定给 ServiceAccount
kubectl create rolebinding sa-edit-binding \
--clusterrole=edit \
--serviceaccount=kube-public:test01 \
--namespace=kube-public
七、实战四:ServiceAccount + RBAC
ServiceAccount 是 K8S 中为 Pod 内进程设计的身份。Pod 默认会自动挂载所在命名空间的 default ServiceAccount。
7.1 创建 ServiceAccount 并绑定权限
yaml
# sa.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: test01
namespace: kube-public
yaml
# sa-rolebinding.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: reader-bind
subjects:
- kind: ServiceAccount
name: test01
namespace: kube-public
roleRef:
kind: ClusterRole
name: reader
apiGroup: rbac.authorization.k8s.io
bash
kubectl apply -f sa.yaml
kubectl apply -f sa-rolebinding.yaml
7.2 在 Pod 中使用 ServiceAccount
yaml
# pod-with-sa.yaml
apiVersion: v1
kind: Pod
metadata:
name: pods-sa
namespace: kube-public
spec:
serviceAccountName: test01 # 指定使用自定义 SA
containers:
- name: test-container
image: nginx:1.20
command: ["/bin/sh", "-c", "sleep 3600"]
Pod 启动后,K8S 会自动将 SA 的 Token 以文件形式挂载到容器内:
/var/run/secrets/kubernetes.io/serviceaccount/
├── ca.crt # API Server 的 CA 证书
├── namespace # 当前命名空间
└── token # SA 的 Bearer Token
7.3 在 Pod 内通过 Python SDK 访问 API Server
以下示例展示 Pod 内的进程如何使用自动挂载的 SA Token 访问 API Server:
python
# view-k8s-resources.py
from kubernetes import client, config
# Pod 内自动使用挂载的 SA Token
config.load_incluster_config()
apps_api = client.AppsV1Api()
core_api = client.CoreV1Api()
try:
print("###### Deployment列表 ######")
for dp in apps_api.list_namespaced_deployment("kube-public").items:
print(dp.metadata.name)
except:
print("没有权限访问Deployment资源!")
try:
print("###### Pod列表 ######")
for po in core_api.list_namespaced_pod("kube-public").items:
print(po.metadata.name)
except:
print("没有权限访问Pod资源!")
将此脚本放入 Pod 中执行:
bash
# 进入 Pod 并执行
kubectl -n kube-public exec -it <pod-name> -- python3 view-k8s-resources.py
# 输出示例(有权限时):
# ###### Deployment列表 ######
# xiuxian
# ###### Pod列表 ######
# pods-sa
# xiuxian-6ffc4f5fd7-m9tf2
7.4 验证权限回收
删除 RoleBinding 后,Pod 内的访问将被拒绝:
bash
# 删除绑定
kubectl delete clusterrolebinding reader-boy-bind
# 再次在 Pod 内执行脚本
kubectl -n kube-public exec -it <pod-name> -- python3 view-k8s-resources.py
# ###### Deployment列表 ######
# 没有权限访问Deployment资源!
# ###### Pod列表 ######
# 没有权限访问Pod资源!
权限即时生效:RBAC 的权限变更在 API Server 层面即时生效,无需重启 Pod。删除 RoleBinding 后,使用该 SA 的 Pod 立即失去相应权限。
八、生产环境最佳实践
8.1 安全配置检查清单
| 检查项 | 推荐做法 | 命令 |
|---|---|---|
| 禁用匿名访问 | --anonymous-auth=false |
检查 API Server 启动参数 |
| 启用 RBAC | --authorization-mode=RBAC |
K8S 1.8+ 默认启用 |
| 启用准入控制 | --enable-admission-plugins=NodeRestriction,... |
按需启用 |
| 审计日志 | --audit-log-path=/var/log/audit.log |
记录所有 API 请求 |
| 最小权限原则 | 按需分配 Role,避免 resources: ["*"] |
定期审查 RoleBinding |
| SA 自动挂载 | 对不需要访问 API Server 的 Pod 设为 false | automountServiceAccountToken: false |
8.2 RBAC 权限设计原则
- 最小权限原则:只授予完成工作所需的最小权限集
- 命名空间隔离:不同环境的资源分命名空间管理,通过 Role 控制边界
- 使用内置角色优先 :
view/edit/admin覆盖大多数场景 - 定期审计:检查是否存在过度授权的 ClusterRoleBinding
- 命名规范:Role 和 RoleBinding 的命名应能体现其用途
8.3 禁用 Pod 默认 SA 的 API 访问
对于不需要访问 API Server 的 Pod(如纯计算任务),应禁用 SA Token 自动挂载:
yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: no-api-access
namespace: production
automountServiceAccountToken: false # 禁用自动挂载
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
namespace: production
spec:
template:
spec:
serviceAccountName: no-api-access
automountServiceAccountToken: false # Pod 级别也可设置
8.4 多集群管理方案
在生产环境中,通常需要管理多套 K8S 集群。kubectl 天然支持通过 kubeconfig 管理多集群:
bash
# 配置多集群的 kubeconfig
kubectl config set-cluster cluster-a --server=https://10.0.0.231:6443 ...
kubectl config set-cluster cluster-b --server=https://10.0.0.240:8443 ...
# 创建上下文
kubectl config set-context ctx-a --cluster=cluster-a --user=user-a
kubectl config set-context ctx-b --cluster=cluster-b --user=user-b
# 切换集群
kubectl config use-context ctx-a
kubectl get nodes
kubectl config use-context ctx-b
kubectl get nodes
常用多集群管理工具:
| 工具 | 特点 |
|---|---|
| kubectl | 官方 CLI,通过 kubeconfig 管理多集群 |
| Kuboard | 免费 Web UI,最多 3 个集群 |
| KubeSphere | 功能全面的国产管理平台 |
| Rancher | 企业级多集群管理 |
| 云平台 | ACK/TKE/CCE 等托管服务 |
十、总结
K8S 安全框架是一个分层防御体系:
- 认证层确认"你是谁"(X509/Token/SA)
- 鉴权层判断"你能做什么"(RBAC:Role → RoleBinding → Subject)
- 准入控制层检查"操作合规吗"(Validating/Mutating)
生产环境中,建议按照最小权限原则设计 RBAC 策略,禁用匿名访问,定期审计权限配置,并使用专用 ServiceAccount 替代默认 SA。掌握这些内容,你就能构建一个安全、可控的 K8S 集群。