一次 kubectl apply 失败可能返回
401,也可能返回 503。两者的原因完全不同:401 是
Authentication 轴的失败,503 通常是 Storage 轴或 Webhook
轴的失败。混淆的常见场景是:SRE 看到 503 后从 RBAC
入手,其实 etcd 已经超时;或者看到 401 后怀疑
webhook,其实是 SA token 过期。本篇钉 kube-apiserver 在
v1.30.3 里的认证链结构,以及各认证器的边界。
本篇在系列中的位置
篇目 核心内容 第 9 篇 · Webhook timeout、failurePolicy、可用性门 第 10 篇 · Authentication 认证链、SA bound token、OIDC flags 边界 第 11 篇 · Authorization 与 Audit RBAC、SAR、403 分列 系列目录 五轴、阅读路径
版本锚定:Kubernetes v1.30.3(tag
v1.30.3)。认证链实现见staging/src/k8s.io/apiserver/pkg/authentication/;SA token 见pkg/serviceaccount/。无真实集群则不粘贴伪造kubectl输出或 token decode 结果。
一、认证链架构
apiserver 启动时把所有启用的认证器组装成一条链,存放在
staging/src/k8s.io/apiserver/pkg/authentication/request/union/union.go
的
unionAuthRequestHandler。处理逻辑是依次调用,直到某个认证器返回成功,或全部失败:
flowchart LR
req["HTTP Request"] --> x509["X509 cert"]
x509 -- "no cert / fail" --> sa["SA token (JWT)"]
sa -- "no match" --> bearer["Static Bearer"]
bearer -- "no match" --> bootstrap["Bootstrap token"]
bootstrap -- "no match" --> oidc["OIDC JWT"]
oidc -- "no match" --> wh["Webhook token"]
wh -- "no match / disabled" --> anon["Anonymous"]
anon -- "disabled" --> 401["401 Unauthorized"]
x509 -- "ok" --> identity["UserInfo: username, groups, extra"]
sa -- "ok" --> identity
oidc -- "ok" --> identity
anon -- "ok" --> identity
认证成功的产出是
user.Info(staging/src/k8s.io/apiserver/pkg/authentication/user/user.go):包含
Username、Groups、UID、Extra,传入后续
AuthZ 链与 Audit
层。认证本身不决定是否允许操作,那是 Authorization
的职责。
二、各认证器边界
X509 客户端证书
启用条件:--client-ca-file 指向 CA
bundle。apiserver 在 TLS 握手阶段验证客户端证书;证书的
CN(Common Name)作为
Username,O(Organization)作为
Groups。
Kubernetes 控制面组件(scheduler、controller-manager、kubelet)默认使用 X509 mTLS 认证,CA 由集群 bootstrap 阶段或 kubeadm 生成。客户端证书无法在不更换 CA 或重新颁发的情况下在线吊销——这是与 OIDC/Token 认证的重要区别;v1.30 中仍无内置 CRL/OCSP 机制。
ServiceAccount Token(SA token)
SA token 分两代:
Legacy SA token(v1.22
前默认):Kubernetes secret 中的长期
JWT,无过期时间,由 apiserver
私钥签名。BoundServiceAccountTokenVolume
feature gate 在 v1.22 默认开启后,Legacy token
不再自动挂载到新 Pod,但 Secret 类型的 SA token
仍可手动创建。
Bound SA token(TokenRequest,v1.20
GA):通过 TokenRequest
API(/apis/authentication.k8s.io/v1/serviceaccounts/{name}/token)颁发,带
expirationSeconds(默认
3600s)、audiences(受众)、boundObjectRef(绑定
Pod/Secret lifecycle)。v1.30 中 kubelet 挂载的 Projected
Service Account Token 即此机制,token 过期后 kubelet
自动轮换。
SA token 验证器实现见
pkg/serviceaccount/jwt.go(LegacyTokenValidation)与
staging/src/k8s.io/apiserver/pkg/authentication/token/cache/(token
验证缓存)。签名密钥由
--service-account-key-file 指定(验证)与
--service-account-signing-key-file
指定(颁发)。
Static Bearer Token
--token-auth-file 指向一个 CSV
文件,每行格式
token,user,uid[,group...]。生产中不推荐:token
明文存在文件,修改须重启
apiserver,无过期机制。不在现代集群新建配置中出现。
Bootstrap Token
kubeadm 使用的引导 token(kube-system
命名空间下 bootstrap.kubernetes.io/ 前缀的
Secret)。Username 为
system:bootstrap:<token-id>,仅用于节点首次加入流程,与日常运维无关。
OIDC JWT
启用条件:--oidc-issuer-url、--oidc-client-id
两个必填 flag,以及以下可选 flag:
| Flag | 说明 |
|---|---|
--oidc-username-claim |
JWT claim 映射为 Username(默认 sub) |
--oidc-username-prefix |
防止与 K8s 内置用户名冲突(推荐设置,如
oidc:) |
--oidc-groups-claim |
JWT claim 映射为 Groups(如 groups) |
--oidc-groups-prefix |
防止 group 与内置冲突 |
--oidc-ca-file |
IdP TLS CA bundle |
--oidc-required-claim |
必须存在的 claim key=value(可多次) |
apiserver 在收到请求时本地验证 JWT 签名(通过 OIDC discovery endpoint 获取 JWKS,缓存后本地校验),不向 IdP 发起实时认证请求。这与 Webhook token 认证不同,也意味着 IdP 吊销 token 后,apiserver 在 JWKS 缓存未过期期间仍可能接受。
OIDC 配置仅是 apiserver 的 flags 边界;OAuth2/OIDC 流程全书(授权码流、PKCE、SAML federation、refresh token)见 iam/。本篇只关心 apiserver 收到 Bearer token 后如何验证。
Webhook Token Authenticator
--authentication-token-webhook-config-file
指向一个 kubeconfig 格式文件,apiserver 向配置的 URL 发送
TokenReview
对象(authentication.k8s.io/v1)。外部服务返回
status.authenticated 与用户信息。此路径与
Webhook Admission 类似,外部服务不可用 =
认证失败;无 failurePolicy 可选(始终 Fail)。
Anonymous 认证
--anonymous-auth(默认
true):未被任何认证器识别的请求被赋予
system:anonymous user 和
system:unauthenticated group,仍进入 AuthZ
链——默认 RBAC 中 system:unauthenticated
只有极少 ClusterRoleBinding(例如
discovery)。关闭匿名认证
(--anonymous-auth=false) 后,未认证请求直接
401。
三、401 与存储层故障分列
| 返回码 | 落格 | 典型原因 |
|---|---|---|
| 401 | Authentication 轴 | Token 过期、签名密钥不匹配、OIDC issuer 不符 |
| 403 | Authorization 轴 | RBAC 无规则,见第 11 篇 |
| 503 | Storage / Admission 轴 | etcd 不可达、webhook 故障 |
| 504 | Storage / APF 轴 | etcd 超时、APF 排队超时,见第 12 篇 |
etcd 不可达不会直接产生 401。apiserver
与 etcd 之间用
--etcd-cafile/--etcd-certfile/--etcd-keyfile
建立 mTLS,该层 TLS 故障表现为 apiserver
写/读失败(503/504),不影响 Authentication 链的 JWT
验证逻辑。
SA token
过期排障:kubectl get secret -n kube-system 查
bootstrap token;对 Bound SA token,kubelet 轮换日志可看到
token expired, refreshing;直接
kubectl create token <sa-name> 颁发临时
token 验证签名密钥是否一致。
四、谱系与开放问题
K8s 早期:X509 + static token(无在线吊销)
→ v1.20:TokenRequest Bound SA token GA(生命周期绑定 + expiry)
→ v1.22:BoundServiceAccountTokenVolume 默认开启
→ v1.30:Legacy SA token 不再自动生成;Projected token 为默认
→ 仍开放:X509 吊销(无内置 CRL/OCSP);OIDC token 吊销与 JWKS 缓存时间 trade-off
开放问题:apiserver OIDC JWKS
缓存刷新间隔在 v1.30 没有可配置 flag(由实现决定);IdP 吊销
token 后,缓存内的 JWKS 仍可验证未过期 JWT。SA token
轮换间隔(默认 expirationSeconds: 3600)与
token projection
更新延迟之间的窗口,在节点时钟漂移时的行为未有正式 SLA。
本篇不写什么:OIDC/OAuth2 全书(见 iam/);kubeadm
证书轮换全书;Dex/Keycloak 配置教程;伪造
jwt.io 解码截图。
参考资料
规范 / 源码(A)
- Kubernetes v1.30.3(tag
v1.30.3):staging/src/k8s.io/apiserver/pkg/authentication/request/union/union.gostaging/src/k8s.io/apiserver/pkg/authentication/token/pkg/serviceaccount/jwt.go
- K8s · Authenticating(v1.30)
- K8s · ServiceAccount token projection(v1.30)
论文 / 规范(A)
- OpenID Connect Core 1.0 —
iss、aud、expclaim 语义;JWKS endpoint 约定
站内对照
实验台账
- 无集群实测;无伪造 token decode 或
kubectl auth输出。
上一篇:Mutating / Validating Webhook
下一篇:Authorization 与 Audit:RBAC、SAR 与 403 分列
读完这篇,下一步读什么
优先读同系列或同问题的下一篇,把单篇消费变成主题集群。
【kube-apiserver】控制面全景:缺口、五轴坐标系与 16 篇路线
相对 etcd/13、distributed/50、k8s-network 补齐 kube-apiserver 生产内核缺口;以 Storage/Watch/Admission/Auth/APF 五轴为坐标系定义 16 篇阅读路线;版本锚定 Kubernetes v1.30.3。
【kube-apiserver】进程与请求路径:generic apiserver、HandlerChain 与 REST 路由
拆解 kube-apiserver 进程模型与 generic apiserver 框架;钉 HandlerChain 各插槽顺序与失败落点;说明 GVR 路由机制与请求在到达 storage 前可能被拦截的位置。版本锚定 Kubernetes v1.30.3。
【kube-apiserver】storage.Interface 与 etcd3:codec、prefix 与 CRUD 路径
拆解 kube-apiserver 的 storage.Interface 契约与 etcd3 实现:codec 序列化、pathPrefix/resourcePrefix、value.Transformer 加密边界、GuaranteedUpdate 乐观并发,以及 Watch 到 etcd3 的完整路径。版本锚定 Kubernetes v1.30.3 / etcd v3.5.33。
【kube-apiserver】resourceVersion 与 Revision 映射:mod revision、continue 与一致性读期望
钉 Kubernetes resourceVersion 字段与 etcd mod revision 的对应关系;分析 continue token 的分页语义与成本;说明不同 List 路径的一致性期望差异;以及 410 Gone 与 ErrCompacted 的分列。版本锚定 Kubernetes v1.30.3 / etcd v3.5.33。