Kubernetes证书体系全解析:从双向TLS到kubelet轮换与故障排查

发布时间:2026/10/13 11:10:03
Kubernetes证书体系全解析:从双向TLS到kubelet轮换与故障排查
1. 证书体系存在的意义先弄懂K8s为什么要这么折腾说句老实话Kubernetes这套证书体系几乎是所有运维初期最不想碰、后期最绕不开的部分。刚接触集群的时候很多人会觉得证书就是个安全验证过的钥匙配好了能跑就行。可真到了生产环境你会发现整个集群的每个组件之间都在互相验证身份任何一个证书失效、错位、过期都会让集群陷入一种看起来开了但哪里都不对的诡异状态。我一直有个比喻Kubernetes的证书体系就是整个集群的户籍系统和门禁系统。每个组件kube-apiserver、kubelet、etcd、scheduler、controller-manager都有自己独立的身份证服务证书它们彼此通信时既要确认对方的身份证是真的由受信任的CA签发又要确认对方有权限做这件事RBAC 证书中的用户/组信息。这套逻辑如果捋不顺你排查问题的时候就会被证书链不完整、x509: certificate signed by unknown authority这类报错反复折磨。所以这篇文章我打算把证书体系架构从头到尾拆一遍包括证书的种类、签发关系、存放位置、轮换机制和典型故障。不管你是正准备考CKA、还是已经在上千节点的集群里摸爬滚打把这条主线吃透你排查K8s安全类问题会快得多也稳得多。1.1 为什么不是简单的用户名密码而要用双向证书先说一个很多新手容易忽略的点Kubernetes组件之间的通信大多数是双向TLS。这意味着不光客户端要验证服务端的证书服务端也要验证客户端的证书。举个例子kubelet要访问kube-apiserver时它会出示自己持有的客户端证书kube-apiserver用配置好的CA公钥去验证这个证书是否合法。反过来kube-apiserver也要向kubelet出示自己的服务端证书让kubelet确认它连接的是真正的API Server而不是某个中间人。这种双向校验的设计核心目标就是防中间人攻击、防节点伪装。如果你的环境用的是HTTP明文端口比如kube-apiserver的8080旧端口或者某些测试环境里kubelet的10250端口被改成了HTTP那基本上就等同于把门禁拆了。生产环境必须保证所有组件间的通信都走TLS这也是等保和各类合规审计里很基础的一条。1.2 证书体系三个关键角色CA、证书、kubeconfig在K8s里证书体系可以拆成三个层次来理解CACertificate Authority整个信任链的根。集群里一般会有多个CA比如kubeadm部署的集群会有kube-apiserver-kubelet-client.crt、sa.pub等分别服务于不同的信任域。具体证书crt/key各组件运行时加载的私钥和证书用于对外提供身份凭证。kubeconfig一种多段式的客户端配置里面包含了集群地址、CA证书、客户端证书/私钥或tokenkubectl、kubelet、scheduler等通过它来访问API Server。这三个层次理解透了后面看任何证书报错都能在脑子里快速定位是CA不认是证书过期还是kubeconfig里的client-certificate和server的CA对不上2. 集群里的证书与kubeconfig清单谁在用谁的证书、放在哪里要理解Kubernetes证书体系架构第一件事就是打开集群把证书清单列出来。下面这张表是我基于kubeadm部署的v1.26集群这也是目前很常见的一个版本整理出来的核心证书和配置文件清单强烈建议你先存下来排查问题时对照着看。证书/配置名称默认路径/etc/kubernetes/下使用方作用方向说明CA证书pki/ca.crt、pki/ca.key全局信任根签发其他组件证书API Server、controller-manager等都用它做信任锚点API Server服务端证书pki/apiserver.crt、pki/apiserver.keykube-apiserver服务端提供给访问6443端口的客户端验证API Server访问etcd证书pki/apiserver-etcd-client.crtkube-apiserver客户端API Server访问etcd时出示的客户端证书kubelet客户端证书pki/apiserver-kubelet-client.crtkube-apiserver客户端API Server访问kubelet10250时使用kubelet服务端证书/var/lib/kubelet/pki/kubelet.crtkubelet服务端kubelet的健康检查和日志接口用可与本地自签轮换前端代理证书pki/front-proxy-client.crtkube-aggregator客户端扩展API Servermetrics-server等用etcd CA/etc/kubernetes/pki/etcd/ca.crtetcd集群信任根签发etcd证书独立CA防止横向信任蔓延etcd服务端证书/etc/kubernetes/pki/etcd/server.crtetcd服务端etcd节点对外提供服务的证书etcd对等证书/etc/kubernetes/pki/etcd/peer.crtetcd节点间通信用于etcd集群内部peer端口2380ServiceAccount签名密钥pki/sa.pub、pki/sa.keycontroller-manager签名/校验非x509而是RSA密钥对这些证书的位置在kubeadm部署下是高度一致的你进到一台master节点的/etc/kubernetes目录基本都能看到上述文件。如果是二进制方式手动部署的话路径和命名通常由启动参数决定但总体思路一样。2.1 为什么etcd要单独一套CA这是很多人容易混淆的地方。etcd集群虽然在K8s里是数据库的角色但它同时也是一个多节点的高可用组件节点之间的数据同步需要双向认证。如果etcd直接用K8s的主CA来签证书那么一旦某个K8s组件证书泄露攻击者可能顺着同一套信任链摸进etcd集群。生产环境里把etcd的CA独立出来相当于把数据库的钥匙单独锁在一个保险柜里与业务系统的门禁分开管理。实际部署中你会发现kubeadm创建的etcd证书全部落在/etc/kubernetes/pki/etcd/目录下包括etcd/ca.crt、etcd/server.crt、etcd/peer.crt。即使是同一个etcd节点server证书和peer证书也是分开的——server证书给客户端kube-apiserver来连peer证书给etcd集群内部节点互相连。这一个区分如果你没注意等到etcd节点扩容时很容易出现节点能启动但集群无法建立成员关系的怪问题。2.2 kubeconfig里装的是什么kubeconfig不是证书但它包裹着证书。以默认的admin.conf为例打开之后你会看到证书和私钥被base64编码后塞进了users[].client-certificate-data和users[].client-key-data字段。真正让请求被API Server接受的核心就是这一段客户端证书。除了客户端证书kubeconfig里还会写明cluster.certificate-authority-data也就是集群CA的公钥用于验证API Server服务端身份。如果集群做了证书轮换但你本地kubeconfig里还存着旧的CA数据就会报x509: certificate signed by unknown authority。很多人在集群证书更新之后发现kubectl突然不好使了十有八九是忘了同步最新CA数据到所有kubeconfig。2.3 服务账户ServiceAccount用的是另一套证书严格意义上说ServiceAccount使用的不是证书而是由SA签名密钥签发的JWT Token。但它在K8s里承担的身份验证职责和证书类似。controller-manager启动时会加载sa.key和sa.pub前者负责给ServiceAccount Token签名后者放在API Server那边用于验签。在K8s 1.26这个版本里default的ServiceAccount通常不再自动挂载长期有效Token而是使用投射卷ProjectedVolume里的短期Token。这个调整主要是为了安全考量——Token的泄露窗口越小越好。但如果你在排查Pod日志权限问题时遇到证书过期或Token not valid的报错记得去检查Pod里挂载的Token是否已经过期以及是否漏了serviceAccountName的指定。3. 证书的签发机制与时间线从集群初始化到证书轮换背后的设计证书体系不是部署完就一劳永逸的它有一套完整的时间线和签发机制。搞清楚这套机制你才能理解为什么kubeadm集群跑了一年之后会突然冒出各种证书过期的告警也才能理解手动部署的集群为什么要自己维护一套脚本。3.1 kubeadm默认的证书有效期重要kubeadm在v1.26里生成的默认证书有效期如下集群CA证书ca.crt有效期10年。API Server、etcd、kubelet等组件的服务端/客户端证书大部分是1年。kubeadm自身生成的kubeconfig里的客户端证书也是1年。这个一年其实是最容易被忽略的定时炸弹。很多测试集群是随便搞的建完就丢在那第二年一看API Server、kubelet全在报证书过期。特别是kubelet的证书如果你没开RotateKubeletServerCertificate那它的服务端证书在一年后就会失效节点状态直接变NotReady。所以我的建议是从集群创建那天起就在日历里加一个每年重复的提醒。如果你有许多存量集群先用下文第4章的检查方法把所有集群的证书到期时间全扫一遍心里先有个底。3.2 静态证书签发kubeadm init/mdify阶段kubeadm部署时证书是静态生成的。流程大概是kubeadm init会读取kubeadm-config.yaml如果发现CA不存在就先生成CA然后用CA去签发apiserver、etcd、kubelet等各个组件的证书生成SA签名密钥对最后把客户端证书打包成各份kubeconfig。这个过程看起来是一条龙但要注意一个细节证书中的SANSubject Alternative Name是在生成时固定的。如果集群初始化之后你给API Server加了新的域名或负载均衡IP老的apiserver证书里没有这个SAN客户端访问时就会报证书主机名不匹配。这时候除了更新证书还得检查负载均衡和所有客户端的kubeconfig是否同步。3.3 动态签发kubelet的CSR流程和静态证书相对的是动态签发即CertificateSigningRequest机制。kubelet启动后可以向API Server提交CSR请求来申请自己的客户端证书和服务端证书。处理这些CSR的有两类角色kube-controller-manager里的csr-signing-controller可以自动签发符合策略的CSR。人工审批kubectl certificate approve或者基于RBAC的自动审批策略。对于kubelet客户端证书如果配置了--kubelet-certificate-authorityAPI Server会使用该CA验证kubelet证书。kubelet还会定期轮换自己的客户端证书--rotate-certificatestrue默认开启但这只覆盖客户端证书服务端证书要额外通过RotateKubeletServerCertificate和CSR批准来轮换。这也是很多节点证书出问题的重灾区客户端证书更新了服务端证书还在用旧的外部访问Pod日志时就会TLS握手失败。3.4 手工签发二进制部署下逃不开的活如果你不是用kubeadm而是直接拿二进制文件拼集群那么证书体系就得完全自己维护。最原始的做法是写openssl命令自己吃CSR但手工维护全部证书非常痛苦。我的建议是至少用cfssl或者easy-rsa这类工具来统一管理再配一套脚本记录每次签发的有效时间和SAN方便后续批量轮换。我见过几位老运维的做法是把整个集群证书全部打成一年一次的轮换任务每个月用脚本巡检一次到期时间提前30天自动触发重新签发和滚动分发。这套逻辑做成以后证书就不再是运维事故而是日常流程。4. 证书过期之前常见的检查命令、轮换与紧急恢复手段接下来是全文最实用的部分。不管你的集群是kubeadm装的、二进制装的、还是云厂商托管但自管了证书下面这些操作都能帮你把证书状态摸清并在关键时刻止血。4.1 检查证书有效期的几条命令针对kubeadm集群最简单的是用kubeadm certs check-expirationkubeadm certs check-expiration输出会列出所有由kubeadm管理的证书路径、剩余有效期和到期时间。在v1.26上执行后你会看到类似[check-expiration] Reading configuration from the cluster... CERTIFICATE EXPIRES RESIDUAL TIME CERTIFICATE AUTHORITY EXTERNALLY MANAGED admin.conf Dec 11, 2024 14:32 UTC 364d no apiserver Dec 11, 2024 14:32 UTC 364d no apiserver-kubelet-client Dec 11, 2024 14:32 UTC 364d no ca Dec 08, 2033 14:30 UTC 9y no etcd/ca Dec 08, 2033 14:30 UTC 9y no ...如果集群里的证书不是kubeadm管的或者你想看kubelet证书可以直接用openssl手动解析echo | openssl s_client -connect 127.0.0.1:6443 2/dev/null | openssl x509 -noout -dates sudo openssl x509 -in /var/lib/kubelet/pki/kubelet.crt -noout -text | grep -A2 Validity还有一种更早期的兜底办法检查kubectl能否正常工作。如果API Server证书已过期通常你在执行kubectl get nodes时就会遇到证书校验失败或连接被重置的异常。4.2 kubeadm方式下的轮换最稳的还是整套续期在kubeadm管理的集群里轮换证书最直接的方法是kubeadm certs renew all这条命令会重新签署所有证书但CA不会变也不会重启任何服务。执行完必须重启静态Pod让kube-apiserver、etcd等重新加载新证书mv /etc/kubernetes/manifests/kube-apiserver.yaml /tmp/ sleep 5 mv /tmp/kube-apiserver.yaml /etc/kubernetes/manifests/静态Pod的移除和再放置是最经典的重启方式多个组件可以按依赖顺序逐个处理。这里有个坑很多生产集群在renew all之后忘记同步新证书到kubeconfig导致kubectl立刻不可用。记得重新生成admin.conf等客户端配置kubeadm init phase kubeconfig admin --config/etc/kubernetes/kubeadm-config.yaml如果你不想手动折腾也可以直接跑kubeadm upgrade apply附带证书更新但重新生成kubeconfig这件事仍然要亲自确认。另一种思路是让kubeadm在集群启动时自动续期即kubeadm upgrade node。我在测试环境里试过配合kubeadm certs renew确实可以做到无感轮换。但生产集群务必在维护窗口里做并且提前备份/etc/kubernetes。4.3 非kubeadm环境下的紧急恢复二进制部署的集群证书轮换就没有这么优雅了。我的经验是先明确哪些证书需要轮换再逐一操作。项目里最常见的恢复路径是停止kube-apiserver但要注意高可用下要一台一台来否则会影响集群写入用现有CA签发新证书写入对应路径如/etc/kubernetes/pki/apiserver.crt重启kube-apiserver并验证日志随后批量处理kubelet、etcd的证书通过ansible等工具滚动更新。如果你连CA都没了那问题就大了基本上只能从etcd备份和所有证书的备份来恢复。所以备份策略里一定要包含/etc/kubernetes整个目录特别是pki/下的所有文件。说句难听的这些私钥文件丢了比etcd数据损坏还要让人崩溃。4.4 通过ExternalCA实现外部管理的思路新版的kubeadm支持externalCA模式即CA由外部系统管理kubeadm只负责生成组件证书请求。这种模式下kubeadm certs renew不会自动执行签发需要你把生成的CSR交给外部CA去签签完再放回对应位置。如果你所在的企业已经有PKI体系这是一条非常推荐的方向。它把集群证书管理从K8s单品运维升级成了企业统一信任体系的一部分轮换、审计、吊销都更有章法。但对应的运维的自主性会下降任何证书的申请都要走企业PKI流程需要提前规划好周期。5. 证书故障的真实排查路径从症状到根因按这个顺序走证书类故障最可怕的地方在于表面症状千奇百怪明明节点显示Ready但Pod无法调度、kubectl可以访问但kubelet日志疯狂报错、apiserver正常但metrics-server时好时坏。很多人一开始根本想不到是证书的问题绕了一大圈才追到根因上。这里我梳理一条我在生产环境里验证过多次的排查路径你遇到疑似证书问题时可以直接照着走。5.1 症状描述与可能方向的快速对照先做一个粗略定位症状可能涉及的证书常见原因kubectl连接6443端口报Unknown Authorityadmin.conf或任意kubeconfig中的CA数据集群CA轮换后未同步kubeconfigkubelet报错x509 certificate signed by unknownapiserver-kubelet-client.crt或kubelet服务端证书kubelet证书与API Server信任CA不一致或kubelet证书过期Pod创建后一直Pending/ContainerCreatingkubelet服务端证书、Pod网络组件证书kubelet无法向API Server注册或CNI插件证书问题etcd节点反复重启集群成员冲突etcd/peer.crt、etcd/server.crt对等证书过期或扩容时用错证书API Server日志反复报401/403kubeconfig中的客户端证书证书中用户/组信息与RBAC不匹配metrics-server拉不到指标front-proxy-client.crt、kubelet客户端证书聚合层证书链断裂或kubelet证书无法被校验这个表不需要死记硬背但当你看到x509或certificate关键字时要第一时间把问题归类到证书体系而不是先去查网络或代码。5.2 一次完整的排查演示kubelet频繁报Unknown Authority我这里还原一个真实场景。某次线上集群部分节点突然NotReady查看kubelet日志发现E0503 09:12:46.198688 12041 kubelet.go:1401] Failed to connect to apiserver errUnavailable I0503 09:12:46.198912 12041 kubelet.go:1401] Failed to connect to apiserver errx509: certificate signed by unknown authority我的排查顺序是这样的第一步检查Kubelet使用的kubeconfig指向的CA是否和API Server一致。kubectl --kubeconfig/etc/kubernetes/kubelet.conf config view --raw -o jsonpath{.clusters[0].cluster.certificate-authority-data}对比API Server的--client-ca-file配置看是否是同一个CA数据。这里最容易漏掉的是如果集群做了CA轮换kubelet.conf里的CA数据还是旧的就会一直报Unknown Authority。第二步确认kubelet当前证书和私钥是否配套。sudo openssl x509 -in /var/lib/kubelet/pki/kubelet.crt -noout -text检查证书的Not After日期以及Subject Alternative Name里是否包含当前节点IP或主机名。如果SAN里没有任何当前节点的标识那API Server在验证身份时也会认为证书不匹配。第三步在API Server侧检查是不是客户端证书未被正确签发。kubectl get csr如果看到大量Pending状态的CSR说明kubelet的客户端证书还没被批准。在部分老集群里自动批准配置可能没有放行所有合法的kubelet请求这时你需要手动kubectl certificate approve。第四步确认是否有网络层TLS终止设备比如SLB透传或eslb干扰了证书链。有一次我排查了很久最后发现是SLB上配置了误导的443证书导致kubelet虽然连的是正确地址但握手的却是负载均衡的证书。这种情况在云环境里特别容易踩坑。5.3 修复动作与验证方法针对上述问题如果确认是kubelet证书过期或错误在kubeadm环境下可以直接为kubelet删除旧证书并重启让它重新走CSR申请流程sudo rm -f /var/lib/kubelet/pki/kubelet.crt /var/lib/kubelet/pki/kubelet.key sudo systemctl restart kubelet重启之后查看sudo journalctl -u kubelet -f sudo kubectl get csr | grep kubelet会自动出现一个新的CertificateSigningRequest。等它被批准后kubelet会拿到新的客户端证书节点状态会在一两分钟内恢复Ready。修复完成后不要急着收工额外做一次全量巡检kubectl get nodes确认所有节点Readykubectl get csr确认没有遗留Pending的异常请求检查kubelet日志里是否还有TLS相关的warning。5.4 四个最容易踩的坑第一个坑是混淆了服务端证书和客户端证书。kubelet的kubelet.crt和admin.conf里的客户端证书不是一回事前者是节点对外提供服务的身份后者是访问API Server时用的身份。两者过期时间不同、签发策略不同你检查一个不能代表全部。第二个坑是只看kubeadm管的那部分证书忽略了etcd。特别是自管etcd的集群etcd证书的维护是你自己的责任。etcd证书一旦过期集群表现为kube-apiserver访问etcd失败很多组件的leader选举全部异常但表面日志可能只是淡淡的etcdserver: request timed out。第三个坑是更新了证书但忘了更新RBAC。证书本身只是证明我是谁真正决定你能干什么的是RBAC绑定。如果你用旧CA签发了新客户端证书但证书里Common Name对应的用户没有被赋予角色那请求依然会变成403。改证书的时候尽量保持CN和用户映射一致否则就是自己给自己埋雷。第四个坑是备份里漏掉私钥。有些人习惯备份所有.crt文件却觉得.key文件太敏感不好保存。结果等到证书要轮换时发现CA私钥和组件私钥都没了所有证书只能重新签复杂度瞬间指数级上升。我的做法是把整个pki/目录打包加密放对象存储并写清楚是哪一天的哪个集群、对应什么版本。6. 我的实操经验关于证书巡检、容器化和未来方向最后再说一点我自己的体会和习惯算是经验层面的分享。6.1 巡检脚本到底要不要自动化我强烈建议至少写一个简单的巡检脚本把集群里所有证书的过期时间都捞出来。脚本不用特别复杂核心就是做以下几件事列出/etc/kubernetes/pki/下所有证书并用openssl解析到期时间用kubeadm certs check-expiration做一次kubeadm层面的检查如果有检查所有kubeconfig里的client-certificate-data和certificate-authority-data能否正常解码并校验日期输出过期前15天、7天、1天预警。生产环境里把这套脚本接入监控用Prometheus的blackbox或自写exporter上报证书剩余天数每天看一次心里就有底了。接入之后我把告警阈值设置为30天提前30天主动处理基本可以避免所有因为证书过期导致的重大事故。6.2 kubelet证书轮换的配置建议在集群里我一般建议打开两个开关RotateKubeletClientCertificate: true RotateKubeletServerCertificate: true但开了之后别以为就万事大吉。服务端证书的轮换依赖CSR被自动批准你需要给controller-manager配置好--feature-gatesRotateKubeletServerCertificatetrue并且在clusterRole里放行certificatesigningrequests的创建和批准权限。很多云厂商虽然默认开了这个能力但客户自己加了网络策略或者安全组限制导致kubelet提交CSR时被防火墙拦了出问题也毫无头绪。6.3 容器化部署下的证书路径差异现在很多团队会直接通过helm或operator来部署K8s组件比如用kubebuilder写的operator来管理证书。部分operator会内置证书生命周期管理甚至自动轮换。但注意它们生成的证书默认可能挂在某个Secret里而不是/etc/kubernetes/pki目录。你的巡检脚本如果还只盯着老路径就会漏掉这部分证书。所以我的建议是不管是用什么方式部署都以证书最终被哪个进程加载为准做巡检。只要你按进程维度梳理好了Ker证书依赖关系脚本怎么变都跑不偏。6.4 云厂商托管集群还需要关心证书吗这个问题经常有人问。如果是EKS、AKS、TKE这类完全托管的集群控制面证书一般由云厂商负责轮换你不太会遇到kube-apiserver证书过期的问题。但以下几个点你仍然要确认kubelet证书是否由云厂商自动轮换你是否使用了自定义CA签发的kubeconfig访问集群节点池里的节点镜像是否内置了会过期的证书比如etcd或者底层kubelet的bootstrap token一些自建Ingress、ServiceMesh组件它们维护的证书仍然是你自己的。要知道托管只是把部分责任拿走了边界之外的证书全是自己的。别因为有托管就完全不做证书巡检那会在某个意外的夜里给你一记闷棍。6.5 最后推荐的学习路径如果你是想系统掌握这套知识的人我的建议顺序是先用kubeadm在虚机里装一套多节点集群自己手动改证书观察改了kubelet.crt之后会发生什么把kube-apiserver、etcd、kubelet的启动参数逐一看一遍对照证书参数--client-ca-file、--tls-cert-file、--peer-cert-file等建一张清单自己模拟一次证书过期用第4章的恢复流程走一遍看集群在各个阶段是什么表现再上手写巡检脚本把结果接入监控。这套流程跑下来你对Kubernetes证书体系架构的理解会比单纯看书或背命令扎实得多。毕竟证书这东西只有亲手在集群上折腾过期、恢复、轮换你才真正记得住它长什么样。