在 Kubernetes 集群中,kubelet 并不是天生就“被信任”的

当我们在新节点上执行:

kubeadm join ... --token xxx --discovery-token-ca-cert-hash sha256:...

表面上看只是“把节点加进集群”,但在系统内部,其实发生了一整套 严格、可审计、可轮换的身份建立流程

本质上,这是一次 基于 PKI + RBAC + 控制器的“节点入职流程”

本文将完整拆解 kubelet 从“路人节点”到“合法 Node”的 6 个关键阶段,解释每一步背后的安全设计逻辑,而不只是“做了什么”。


kubelet 成为合法 Node 的 6 个阶段

  1. kubeadm 写入 bootstrap kubeconfig(临时身份证)
  2. kubelet 用 bootstrap token 与 APIServer 建立 TLS 连接
  3. kubelet 提交 CSR(证书签名请求)
  4. kube-controller-manager 自动审批并签发证书
  5. kubelet 切换为 证书认证身份
  6. Node 对象注册 + 证书自动轮换

接下来我们逐个展开。


一、kubeadm join:给 kubelet 发一张“临时身份证”

在新节点执行 kubeadm join 时,kubeadm 并不是直接把 kubelet变成 Node,而是先做两件非常关键的事情:

1️⃣ 下发集群 CA(用于验证 API Server)

/etc/kubernetes/pki/ca.crt

这一步解决的是一个根本问题:

“我连的这个 API Server,真的是我的集群吗?”

后续所有 TLS 校验,都基于这张 CA。


2️⃣ 写入 bootstrap kubeconfig(只含 token)

/etc/kubernetes/bootstrap-kubelet.conf

其核心内容如下(简化):

clusters:
- cluster:
    server: https://<ip>:<port>
    certificate-authority-data: <ca.crt>
users:
- name: kubelet-bootstrap
  user:
    token: 0vxwfj.pieyd6s6jvmbg93y

关键点:

  • kubelet 此时没有证书
  • 只能用一个 短期有效的 bootstrap token
  • token 本质上是一个 Secret:bootstrap-token-xxxxx

认证后,APIServer 会将 kubelet 识别为:

username: system:bootstrap:<token-id>
groups:
- system:bootstrappers
- system:bootstrappers:kubeadm:default-node-token

⚠️ 这是一个权限极小的临时身份,只能做极少数事(例如提交 CSR)。


二、kubelet 用 bootstrap token 连接 APIServer

kubelet 启动后,会先使用:

--bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf

来建立连接。

这一阶段发生了什么?

  1. kubelet 使用 CA 验证 APIServer 的服务端证书
  2. TLS 连接建立成功
  3. HTTP 请求中携带:
Authorization: Bearer <bootstrap-token>
  1. APIServer 通过 Bootstrap Token Authenticator 校验:

    • token 是否存在
    • 是否过期
    • 是否属于合法 bootstrap token 类型

校验通过后,相当于 kubelet 进了门,但还没工牌


三、kubelet 提交 CSR:申请“正式工牌”

一旦 kubelet 能与 APIServer 通信,它做的第一件事不是注册 Node,而是申请证书

kubelet 在本地做了什么?

等价逻辑如下:

openssl genrsa -out kubelet.key 2048
openssl req -new \
  -key kubelet.key \
  -subj "/CN=system:node:<nodeName>/O=system:nodes" \
  -out kubelet.csr

⚠️ 私钥 永远只存在于 kubelet 本地,不会上传。


kubelet 提交的 CSR 对象

apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
spec:
  request: <base64 CSR>
  signerName: kubernetes.io/kube-apiserver-client-kubelet
  usages:
  - client auth

其中最重要的是 CSR 里的 Subject

  • CN=system:node:<nodeName>
  • O=system:nodes

这一步实际上是在声明:“我请求以 Node 身份加入集群。”

注:Kubernetes 的 CertificateSigningRequest 对象本身并不包含 subject 字段。kubelet 的身份信息(如 CN=system:node:<nodeName>、O=system:nodes)实际上编码在 spec.request 中的 PKCS#10 CSR 里,kube-controller-manager 在审批时会解析该 CSR 内容来校验节点身份。


四、kube-controller-manager:谁决定你是不是 Node?

CSR 提交后,是否通过不是 kubelet 说了算,而是控制平面说了算

参与角色

kube-controller-manager 中,有两类核心控制器:

  • csrapproving
  • csrsigning

它们会综合判断:

  • CSR 是谁提交的(bootstrap 身份)
  • signerName 是否正确
  • CN / O 是否符合 Node 规范
  • NodeName 是否冲突
  • 是否符合 NodeRestriction 规则

只有全部满足,才会:

  1. Approve
  2. 使用集群 CA 签发证书
  3. kube-controller-manager 在签名完成后,会构造一个 API 更新请求,同时将证书写入:
apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
status:
  certificate: <base64 encoded x509 cert>

这一步的本质动作是:UPDATE CSR.status

也就是:PUT /apis/certificates.k8s.io/v1/certificatesigningrequests/<name>/status

由于 所有 API 对象最终都会进 etcd,完整链路为:

kube-controller-manager
  ↓(REST API)
kube-apiserver
  ↓(storage layer)
etcd

也就是说,status.certificate 最终是作为 CSR 资源的一部分,被序列化后存储在 etcd 中。

所以,在 kubectl 里看到的,其实是 etcd 中的数据。当我们执行:

kubectl get csr
# Approved, Issued

实际上是 API Server 从 etcd 读出来,再返回给 kubectl


五、kubelet 切换为“证书身份”

kubelet 会持续监听自己的 CSR 状态(watch / poll 自己提交的 CSR)。

一旦发现证书已签发:

status:
  conditions:
  - type: Approved
  certificate: <base64>
  1. 通过 API Server 读取 status.certificate。kubelet 从 API Server 读取到 status.certificate 后,并不会直接写入本地使用,而是首先解析该证书并校验其中的公钥是否与本地已生成的私钥匹配。只有在验证通过后,kubelet 才会将证书与私钥一起保存,并切换为基于客户端证书的 mTLS 认证方式。
  2. 将证书写到自己本地:
/var/lib/kubelet/pki/kubelet-client-current.pem
  1. 生成正式 kubeconfig:
/etc/kubernetes/kubelet.conf

核心变化是:

users:
- name: system:node:<nodeName>
  user:
    client-certificate: kubelet-client-current.pem
    client-key: kubelet-client-current.pem

从这一刻起:

kubelet 与 APIServer 的通信,正式切换为 mTLS 客户端证书认证


APIServer 如何识别它?

通过证书 Subject:

CN = system:node:<nodeName>
O  = system:nodes

映射为:

username: system:node:<nodeName>
groups:
- system:nodes
- system:authenticated

再由:

  • NodeAuthorizer
  • NodeRestriction
  • RBAC

严格限制 kubelet 只能操作“与自己 Node 相关”的资源


六、Node 对象注册 & 证书自动轮换

有了正式身份后,kubelet 才会:

  1. 创建 / 更新 Node 对象
  2. 上报心跳、容量、条件
  3. 接收调度的 Pod
  4. 成为真正意义上的 集群成员

证书不会一劳永逸

kubelet 默认开启 证书自动轮换

  • 在证书即将过期前
  • 自动重新发起 CSR
  • 走一遍 相同的审批流程
  • 无需人工介入

这保证了长期安全性、节点可控性以及身份的可撤销。


总结

kubelet 并不是被“加入”集群,而是:

用一次性 token 证明“我有资格申请” →用 CSR 申请正式证书 →通过控制平面审核 →最终以证书身份被授予 Node 权限。

这套机制解决的核心问题是:

  • 节点身份可信
  • 权限最小化
  • 证书可轮换、可撤销
  • 不依赖长期共享密钥

也正是这套设计,使得 Kubernetes 能在 大规模、动态、不可信环境 中安全运行。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐