系统学习 学习路径总入口:先选领域,再建立模型,最后用故障演练验证

从证书盘点、签发新链、分批下发到验证与回滚,完成一次集群或内部 CA 的证书轮换

适用场景

本流程默认先做“新旧 CA 并存”的过渡,再移除旧 CA。不要在未验证的情况下直接覆盖唯一的 CA 文件;CA 私钥损坏且没有备份,往往意味着集群重建。

前置检查

  1. 盘点证书和信任关系。列出每张证书的 subject、SAN、签发者、有效期、密钥算法、指纹、使用方、分发路径和负责人。控制面证书用 kubeadm 检查,kubelet、etcd、Ingress、Webhook 和内部服务用 openssl 逐个核对:
sudo kubeadm certs check-expiration
sudo find /etc/kubernetes/pki /var/lib/kubelet/pki -type f \
  \( -name '*.crt' -o -name '*.pem' \) -print
sudo openssl x509 -in <certificate> -noout \
  -subject -issuer -dates -fingerprint -sha256 -text

同时检查 kubeconfig、Webhook 的 caBundle、Ingress Secret、服务网格、客户端信任库和可能固定证书指纹的系统。只盘点服务端证书,漏掉客户端和信任库,是轮换后大面积失败的主要原因。

  1. 判断轮换类型。CA 仍可信、只是叶子证书到期时,优先用原 CA 重签;根 CA 到期、私钥泄露或算法不合规时,必须做完整 CA 轮换。完整轮换要假设旧链不再可信,准备新旧并存的信任包,并确认所有使用方都能接受多根证书。

  2. 核对证书属性。新证书的 SAN、EKU、KeyUsage、链长度、有效期和密钥用途必须覆盖原场景;服务端证书要有正确的 DNS/IP SAN,客户端证书要有 clientAuth,etcd peer 证书要能被成员间互相验证。不要只复制 subject,忽略扩展字段。

  3. 备份 CA 和配置。备份 /etc/kubernetes/pki、*.conf、/var/lib/kubelet/pki、Webhook caBundle、Ingress Secret 和内部 CA 的离线密钥;目录权限收紧到最小范围,备份完成后做一次恢复演练。CA 私钥不要分发到普通节点,也不要在临时目录留下副本。

  4. 准备灰度与回滚。选择测试集群、一个非关键节点、一个命名空间或一个 Ingress 作为首批;明确批次大小、观察时间、回滚负责人和停机窗口。轮换期间暂停证书自动续期和自动签发,避免自动化与手工操作竞争。

  5. 检查时间和监控。用 timedatectl 确认时间同步;证书的 notBefore/notAfter 对时钟敏感。准备好 TLS 握手错误、证书剩余天数、API /readyz、节点 Ready、etcd 健康和业务错误率看板。证书已经过期时,先按应急流程恢复,不要在故障中直接做 CA 轮换。

操作步骤

1. 固定基线

kubectl get nodes -o wide > /backup/nodes-before.txt
kubectl get --raw='/readyz?verbose' > /backup/readyz-before.txt
sudo kubeadm certs check-expiration > /backup/certs-before.txt

记录当前 CA 指纹、叶子证书指纹、各组件信任的 CA 和所有待替换的 Secret。后续每一步都用同一组指标对比。

2. 签发新链

只轮换叶子证书时,生成新私钥和 CSR,交给现有 CA 按原模板签发。CSR 必须保留 SAN 和扩展字段:

openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:3072 -out leaf-new.key
openssl req -new -key leaf-new.key -out leaf-new.csr \
  -subj "/CN=api.internal" \
  -addext "subjectAltName=DNS:api.internal,DNS:api.internal.svc"
openssl req -in leaf-new.csr -noout -text

完整 CA 轮换时,新建根 CA 或“新根 + 新中间 CA”,把旧根和新根一起放进信任包。签发和分发都要保留旧链,直到所有叶子证书都换完:

openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:4096 -out ca-new.key
openssl req -x509 -new -key ca-new.key -sha256 -days 3650 \
  -subj "/CN=Internal Root CA" -out ca-new.crt \
  -addext "basicConstraints=critical,CA:TRUE" \
  -addext "keyUsage=critical,keyCertSign,cRLSign"
cat ca-old.crt ca-new.crt > ca-bundle.crt
openssl verify -CAfile ca-bundle.crt leaf-old.crt

用 openssl x509 -req 签发叶子时,务必通过 -extfile 或 CA 模板带上 SAN、EKU 和 KeyUsage;默认签发可能丢掉这些扩展,导致 TLS 校验失败。有中间 CA 时,信任包放根证书,叶子下发时带上中间证书,验证命令用 -untrusted intermediate.crt 提供中间链。更稳妥的做法是让 cert-manager、Vault、step-ca 或现有内部 CA 服务按模板签发,再把证书和链导出。

3. 先分发信任包

完整 CA 轮换的第一步不是换叶子证书,而是让所有使用方同时信任新旧 CA。把 ca-bundle.crt 分发到控制面、节点、kubeconfig、Webhook caBundle、Ingress、服务网格和外部客户端,然后验证旧证书仍然可用:

openssl verify -CAfile ca-bundle.crt leaf-old.crt
openssl x509 -in ca-new.crt -noout -subject -dates -fingerprint -sha256

Kubernetes 中要区分“信任包”和“签发者证书”。ca.crt 可以作为多根信任包使用,但签发新证书时必须使用新 CA 的私钥和对应证书;不要把合并包直接当作签发证书。etcd、front-proxy 和 kubelet 各自有独立的信任位置,要逐个确认,不能只改 apiserver。

4. 分批替换叶子证书

顺序建议是:一个测试节点或命名空间 → 一个控制面节点 → 其余控制面 → 节点池 → Ingress、Webhook 和内部服务 → 外部客户端。每批只改一个层次,观察后再继续。

控制面叶子证书用原 CA 续期时,可以执行:

sudo cp -a /etc/kubernetes/pki /etc/kubernetes/pki.bak.$(date +%Y%m%d%H%M)
sudo kubeadm certs renew all
sudo kubeadm certs check-expiration

kubeadm certs renew all 只更新磁盘文件,不会让运行中的静态 Pod 自动加载;要逐台重启 kube-apiserver、controller-manager、scheduler 和 etcd,并确认 quorum 与 /readyz 后再处理下一台。它只负责用当前 CA 重签叶子,不会生成、分发或切换 CA;完整 CA 轮换还要替换 CA、分发信任包并重签所有客户端证书。如果磁盘上仍是旧 CA,renew 仍会签出旧链。

kubelet 客户端证书启用自动轮换时,重启 kubelet 后会生成 CSR,需要及时批准:

sudo systemctl restart kubelet
kubectl get csr
kubectl certificate approve <csr-name>

如果证书已经过期,kubelet 无法用旧证书认证,也就无法提交 CSR。此时要从安全备份恢复一份未过期的 kubelet.conf,或用集群 CA 重新签发客户端证书:CN 使用 system:node:<node>,组使用 system:nodes,替换后先用 kubectl --kubeconfig 验证身份和权限,再重启 kubelet。服务端证书启用了 serverTLSBootstrap 时,对应 CSR 通常需要人工批准。

etcd 成员要逐台替换证书并重启,确认 etcdctl endpoint health --cluster、leader 和成员列表正常后再继续。Webhook 要同时更新 caBundle 和证书 Secret,确认 failurePolicy、超时和调用链;Ingress 和内部服务要处理连接复用和客户端缓存,避免旧连接继续使用旧证书。

5. 收敛旧 CA

所有叶子证书都换成新链,并且经过完整业务高峰后,才能从信任包中移除旧 CA。移除前搜索日志中的 unknown authority、certificate expired 和 TLS 握手失败,确认没有客户端仍在使用旧链。移除后再次滚动重启或重载相关组件,保留旧 CA 的离线备份和回滚材料。

验证

失败与回滚

新链不被信任。 最常见的原因是只替换了叶子证书,没有先分发合并信任包,或某个使用方仍只信任旧 CA。立即恢复旧+新合并包,确认旧 CA 仍在信任列表中;不要删除旧 CA 私钥和证书。

控制面重启后不可用。 停止后续批次,保留日志和证书文件。用备份恢复 /etc/kubernetes/pki 和 *.conf,逐台重启静态 Pod;如果 etcd 成员异常,先恢复 etcd 证书和 quorum,再处理 apiserver。不要在所有控制面节点同时替换证书。

kubelet 报 unknown authority 或 NotReady。 把合并信任包恢复到节点的 /etc/kubernetes/pki/ca.crt 和 kubelet 配置引用的位置,重启 kubelet;如果客户端证书已过期,重新签发或恢复备份。不要删除 /var/lib/kubelet/pki,否则会失去回滚材料。

Webhook 或 Ingress 中断。 恢复旧 caBundle/Secret 和旧证书,确认调用恢复后再排查新链;临时把 failurePolicy 改为 Ignore 只能作为受控应急手段,必须尽快恢复并记录审批。

证书已经过期。 回滚到过期证书不会恢复服务,只能向前修复:恢复未过期备份、用 CA 重签,或先恢复旧信任链再换新链。此时应转入证书过期应急流程,避免在服务不可用时扩大变更范围。

部分批次失败。 保持新旧 CA 并存,按相反顺序回滚已替换的叶子证书;在确认所有使用方恢复前,不要移除新 CA。记录失败的批次、指纹和日志,修复后在测试环境复现。

相关命令

参考链接

读完自测

合上页面,先回答,再展开对照。

  1. 完整 CA 轮换为什么必须先分发新旧合并信任包,而不是直接替换旧 CA?
  2. kubeadm certs renew all 能完成 CA 轮换吗?
  3. 证书已经过期时,为什么不能只重启 kubelet 等它自动轮换?
答案
  1. 过渡期旧叶子证书还在使用,使用方必须同时信任旧 CA 和新 CA;直接替换会让旧证书立刻变成 unknown authority,导致 apiserver、kubelet、etcd 或 Webhook 认证失败。合并信任包让新旧链并存,等所有叶子换完再移除旧 CA。
  2. 不能。它只负责用当前 CA 重签叶子证书,不会生成、分发或切换根 CA;完整 CA 轮换要新建 CA、分发信任包、用新 CA 重签所有叶子证书,并逐批验证。
  3. 过期证书无法通过 apiserver 认证,kubelet 无法提交 CSR,自动轮换链条已经断了。需要恢复未过期的 kubelet 凭据或用 CA 重签,再重启 kubelet,并参考证书过期应急流程。
微信小程序码:扫码打开命令速查小程序

小程序版

微信扫码,随时查命令