认证与鉴权概览
本教程共 65 篇 · 第 43 篇 · 更新于 2026-08-14 · 约 13 分钟阅读
本节目标:搞懂你敲下
kubectl get pods之后,这个请求在 API 服务器里到底过了几道安检,以及每道安检各管什么。
前面几十章我们一直在”造东西”——建 Pod、发 Service、挂存储。这些操作能成功,是因为你手里那份 ~/.kube/config 权限很大。但生产集群不是这样的:开发只能看自己命名空间,CI 只能改 Deployment,监控组件只能读不能写。
这套”谁能干什么”的规则,就是本章要讲的访问控制。
43-1 一个请求要过四道关
先记住一张流程图。任何访问 Kubernetes API 的请求,都要依次经过:
- 传输安全——TLS 加密通道,先确认你连的是真的 API 服务器。
- 认证(Authentication)——你是谁?拿不出身份就 401。
- 鉴权(Authorization)——你有权做这件事吗?没权限就 403。
- 准入控制(Admission Control)——请求内容本身合规吗?不合规直接打回。
四关全过,对象才会被校验并写进 etcd。
用一个生活比方:进公司大楼。刷门禁卡证明”我是员工”,这是认证;卡的权限决定你能不能刷开机房门,这是鉴权;进机房还要签保密协议、换防静电鞋,这是准入控制。三件事完全不同,别混着记。
Note顺序不能颠倒。鉴权一定发生在认证之后——系统得先知道你是谁,才能判断你能干什么。准入控制又在鉴权之后,只有被允许的请求才会走到那一步。
43-2 传输安全:先建起加密通道
API 服务器默认在第一个非 localhost 网络接口的 6443 端口监听,全程 TLS 保护。典型生产集群会对外暴露 443 端口。
服务端出示证书,证书可以由集群内的私有 CA 签发,也可以来自公认的 CA。启动参数 --tls-cert-file 和 --tls-private-key-file 指定证书和私钥,--secure-port 改端口,--bind-address 改监听地址。
如果集群用的是私有 CA(kubeadm 装出来的集群就是这样),你的 ~/.kube/config 里必须带一份该 CA 证书的副本。否则 kubectl 无法验证服务端身份,会直接报证书不受信任。
集群内部组件之间——apiserver、controller-manager、scheduler、kubelet、etcd——走的是双向 TLS(mTLS)。双方都要出示证书,不是只验证服务端。这套证书体系依赖那个自签名 CA,所以 CA 私钥泄露等于整个集群通信全线失守。
WarningCA 证书只在集群内部使用,别把它设置成系统级可信 CA。如果 API 服务器还要和集群外组件通信(比如外部 Webhook),推荐用两套独立的信任根:内部组件一套,外部组件一套。
43-3 认证:证明你是谁
TLS 建好以后,请求进入认证阶段。
认证组件的输入是整个 HTTP 请求,但实际上通常只看请求头和客户端证书。Kubernetes 支持的认证方式主要有这几类:
- 客户端证书——最常见,kubeadm 给管理员发的就是这个。
- Bearer Token——放在
Authorization: Bearer <token>请求头里。 - ServiceAccount 的 JWT 令牌——工作负载(Pod)访问 API 用的就是它,详见第 45 章。
- 引导令牌(Bootstrap Token)——节点加入集群时临时用。
- 静态密码文件——已不推荐,只在很老的集群里见得到。
可以同时配置多个认证组件。API 服务器会一个个试,直到有一个成功。全都失败就返回 HTTP 401。
认证成功后,你会被识别为一个具体的 username,部分认证器还会同时给出你所属的组(Group)。这个用户名和组名会传给后面的鉴权阶段用。
这里有个反直觉的点,很多人第一次学会懵:
TipKubernetes 里没有
User这种资源对象。 你不能kubectl create user zhangsan。用户名只是认证阶段产出的一个字符串,Kubernetes 既不存储用户,也不管用户从哪来。真正被 Kubernetes 当作对象管理的身份只有ServiceAccount。
也就是说,人类用户的身份由外部系统提供——证书、OIDC 提供商、LDAP 网关等等。Kubernetes 只负责认这个结果。
43-4 鉴权:判断你能干什么
请求被确认来自某个用户后,进入鉴权。
鉴权的第一原则是默认拒绝。请求的每一部分都必须被某个鉴权机制放行,才能继续往下走。
鉴权看哪些属性
Kubernetes 只审查这些请求属性,不多也不少:
| 属性 | 含义 |
|---|---|
| 用户(user) | 认证阶段得到的用户名 |
| 组(group) | 用户所属的组名列表 |
| 额外信息(extra) | 认证层附加的键值对 |
| API 组 | 被访问的 API 组,空字符串代表核心组 |
| 资源(resource) | 资源类型或名称,如 pods |
| 子资源(subresource) | 如 status、scale、log |
| 名字空间(namespace) | 仅命名空间级资源有 |
| 请求动词(verb) | get/list/create/update 等 |
| 请求路径 | 非资源端点,如 /api、/healthz |
注意”动词”不是 HTTP 方法本身。Kubernetes 会做一层映射:
| HTTP 方法 | 请求动词 |
|---|---|
POST | create |
GET、HEAD | get(单个)、list(集合)、watch(监听) |
PUT | update |
PATCH | patch |
DELETE | delete(单个)、deletecollection(集合) |
Warning
get、list、watch都能拿到资源的完整内容,就返回数据来说它们是等价的。对secrets授予list权限,等于把所有 Secret 的data字段都给出去了。这个坑我见过不少人踩——以为”只是列个清单,应该没事”。
六种鉴权模式
API 服务器通过 --authorization-mode 参数配置启用哪些模式:
RBAC——基于角色的访问控制。用rbac.authorization.k8s.ioAPI 组驱动决策,可以通过 API 动态配置策略。这是现在的绝对主流,第 44 章专门讲。Node——专用模式,根据 kubelet 上实际调度了哪些 Pod,来决定放行它的哪些请求。生产集群一般和 RBAC 一起开。Webhook——把鉴权决策同步 HTTP 外调给远端服务,阻塞等待响应。灵活但有单点故障风险,外部引擎挂了 API 服务器就没法完成鉴权。ABAC——基于属性的访问控制,策略写在文件里,改了要重启 API 服务器。基本被 RBAC 取代了。AlwaysAllow——放行一切。AlwaysDeny——拒绝一切,只用于测试。
Warning绝对不要在 API 服务器能从公网访问的集群上启用
AlwaysAllow。而且它有传染性:--authorization-mode=AlwaysAllow,RBAC的效果等同于只写AlwaysAllow,因为 RBAC 不提供”拒绝”规则,只会返回”批准”或”无意见”,而AlwaysAllow会把所有”无意见”变成放行。
多个模块怎么协同
配置了多个鉴权模块时,按顺序逐个检查:
- 任何一个模块批准或拒绝,立即返回该结论,不再问后面的模块。
- 所有模块都无意见,请求被拒绝,返回 HTTP 403 Forbidden。
生产集群最常见的组合是 Node,RBAC。
43-5 准入控制:请求还得再过一遍筛子
鉴权通过了,不代表请求内容没问题。
准入控制器能做两件鉴权做不到的事:它能看到正在创建或修改的对象的完整内容,而且它能改这个对象。
举几个实际例子:
- 给没写
resources的容器自动补上默认的 CPU/内存限额(LimitRanger)。 - 拒绝以 root 身份运行的 Pod(Pod 安全准入,第 46 章)。
- 给每个新命名空间自动注入 Sidecar 或标签(自定义 Webhook,第 57 章)。
几个要点:
- 准入控制器只对
create、update、delete、connect(代理)这类请求起作用,纯读取请求不经过它。 - 配置了多个控制器时按顺序调用。
- 和认证、鉴权不同:任何一个准入控制器拒绝,请求立即失败,不存在”某一个放行就通过”。
请求过完所有准入控制器,才会走对象校验例程,然后写入 etcd。
43-6 审计:留下证据
前面四关管的是”拦住不该做的事”。审计(Audit)管的是”记下已经做过的事”。
Kubernetes 审计功能会按时间顺序记录集群里的操作序列——谁、什么时候、对什么对象、做了什么、结果如何。用户操作、通过 API 访问的应用、控制平面自身的活动,都在记录范围内。
这个功能默认不开,需要手动配置审计策略文件和后端。真出了安全事件,没有审计日志基本就是两眼一抹黑。
除了 API 服务器,容器运行时、kubelet 也各有自己的日志。要串起来分析,通常的做法是每个节点跑一个 DaemonSet 日志代理,统一送到中心化的日志库。
43-7 自查权限:kubectl auth can-i
理论讲完,给你一个每天都能用上的命令。
# 我能在 default 命名空间创建 Pod 吗?
kubectl auth can-i create pods --namespace default
# 我能删除节点吗?
kubectl auth can-i delete nodes
# 列出我在当前命名空间的全部权限
kubectl auth can-i --list
输出就是 yes 或 no,干脆利落。
管理员还能用 --as 模拟别人来查——排查”为什么他报 Forbidden”时特别好用:
# 以 dev 命名空间的 ci-bot 服务账号的身份检查
kubectl auth can-i update deployments \
--namespace dev \
--as system:serviceaccount:dev:ci-bot
注意那个身份格式:system:serviceaccount:<命名空间>:<名称>。ServiceAccount 在认证层就是这么一个用户名,前缀 system: 是 Kubernetes 内部保留的。
想看当前 kubeconfig 用的是谁,可以查:
kubectl config view --minify -o jsonpath='{.contexts[0].context.user}'
43-8 新手常踩的三个坑
第一,把 401 和 403 搞混。 401 是认证失败——证书过期、Token 无效,系统不知道你是谁。403 是鉴权失败——知道你是谁,但你没这个权限。看到 401 去查凭据,看到 403 去查 RBAC 规则,方向完全不同。
第二,以为 cluster-admin 是”临时方便一下”。 遇到 403 就把 cluster-admin 绑上去,问题当然立刻消失,然后就再也没人回来收紧。正确做法是用 kubectl auth can-i 定位到缺的那一条动词,只补那一条。
第三,忘了 Pod 也是 API 的客户端。 集群里每个 Pod 默认都挂载着 ServiceAccount 令牌,能访问 API 服务器。虽然默认权限很小,但大多数应用根本不需要访问 API。对这类 Pod,把 automountServiceAccountToken 设为 false 是白捡的安全收益:
apiVersion: v1
kind: Pod
metadata:
name: no-api-access
spec:
automountServiceAccountToken: false
containers:
- name: app
image: nginx:1.27
小结
这一章没有太多要动手配的东西,主要是把地图铺开:
- 传输安全解决”连的对不对”,认证解决”你是谁”,鉴权解决”你能干什么”,准入控制解决”这个请求内容行不行”。
- 认证失败 401,鉴权失败 403,两者排查方向不同。
- 鉴权默认拒绝,多模块中任一批准即通过;准入控制反过来,任一拒绝即失败。
- Kubernetes 不存储人类用户,只管理
ServiceAccount。
接下来第 44 章进入 RBAC——把”谁能干什么”真正落成 YAML。