从 TLS 到 Kubernetes PKI:一条证书链如何支撑整个集群安全(问题合集)

问题一

"kubelet 和 APIServer 之间的双向 TLS(mTLS)到底是怎么跑起来的?"

本文按从底层原理到 Kubernetes 视角,分两条线讲:

  1. 纯 TLS 协议视角:双方如何握手、校验证书
  2. Kubernetes 视角:APIServer 怎么把证书身份变成 system:node:<nodeName> 这个"用户",再做鉴权

一、先对齐:什么是「双向 TLS / mTLS」

  • 单向 TLS(普通 HTTPS)
    客户端校验服务器证书(我连的是不是正版网站?)
    服务器不校验客户端是谁(除非用额外认证,比如 token、cookie)。
  • 双向 TLS(mTLS)
    ✅ 服务器校验客户端证书(你是谁?)
    ✅ 客户端也校验服务器证书(你是不是正版?)

在 Kubernetes 里:kubelet = TLS 客户端、APIServer = TLS 服务器、两边都带证书 → mTLS


二、从「网络协议」角度看 mTLS 握手流程

以 kubelet 正常工作时调用 APIServer 为例(比如上报心跳、获取 PodSpec):

0️⃣ 建立 TCP 连接

kubelet 先拨号:

text 复制代码
kubelet → TCP → apiserver:6443

成功后,开始 TLS 握手。


1️⃣ ClientHello(kubelet 发起)

kubelet 发:

  • 支持的 TLS 版本
  • 支持的密码套件(cipher suites)
  • 随机数等参数

此时 还没有证书,也还没认证任何身份


2️⃣ ServerHello + ServerCertificate(APIServer 证明自己的身份)

APIServer 回:

  1. ServerHello:选定 TLS 版本 + 密码套件
  2. Certificate :发自己的服务器证书链(--tls-cert-file 对应)
  3. 可选:一些扩展,如 ALPN 等
  4. ServerHelloDone

kubelet 做的事:

  • 拿到 APIServer 的证书链
  • 用本地的 CA 证书校验:
    • 这个证书是不是被我信任的 CA 签发的?
    • 证书没过期吗?
    • 证书里的 SAN / CN 是否匹配我要访问的主机名或 IP?

这里 kubelet用的是:

bash 复制代码
/etc/kubernetes/pki/ca.crt

或者通过 kubeconfig 里的 certificate-authority-data

如果校验失败:

kubelet 直接认为 "API Server 不可信",握手失败,连接断开。


3️⃣ CertificateRequest(APIServer 要客户端也出示证书)

因为 APIServer 配置了:

bash 复制代码
--client-ca-file=/etc/kubernetes/pki/ca.crt

表示:

"我要求客户端也用 CA 签发的证书来证明自己的身份。"

所以在握手过程中,APIServer 会发一个 CertificateRequest 消息,意思是:

"请你也发一个客户端证书给我,我要认证你。"


4️⃣ kubelet 发送 ClientCertificate + 证明自己确实拥有私钥

kubelet 现在要出示自己"办好的工牌":

  • 从 kubeconfig 配置中读到:
yaml 复制代码
client-certificate: /var/lib/kubelet/pki/kubelet-client-current.pem
client-key: /var/lib/kubelet/pki/kubelet-client-current.pem

(这个文件里既有证书,也有私钥)

然后在握手中发送:

  1. Client Certificate

    • 里面的 Subject:
      • CN = system:node:<nodeName>
      • O = system:nodes
    • Issuer = 集群 CA
  2. ClientKeyExchange + CertificateVerify

    • 用私钥对部分握手数据签名,证明:

      "这个证书真的是我自己的,不是偷来的。"

APIServer 收到后,会:

  • --client-ca-file 提供的 CA,校验证书:
    • 签发者是否是这个 CA
    • 是否未过期
  • 用证书中的公钥校验签名,确认"对方确实拥有私钥"

如果都 OK,则 客户端认证成功


5️⃣ 双方生成会话密钥,握手完成

接下来就是标准 TLS 流程:

  • 双方根据前面交换的信息,生成对称会话密钥
  • 后续 HTTP 请求和响应,全部用这个对称密钥加密传输

至此,从传输层视角 已经建立了一个:

"既机密、又认证了双方身份"的双向 TLS 通道。


三、从「Kubernetes」视角看:APIServer 如何把证书 → 用户身份

TLS 层做完之后,接下来就是:

"把 X.509 证书里的信息映射成 Kubernetes 里的 user / group 身份。"

APIServer 在配置了 --client-ca-file 后,会启用 x509 client cert authenticator,流程大致是:

1️⃣ 从证书中抽取 Subject

证书 subject 一般长这样:

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

Kubernetes 约定:

  • CN(Common Name) → username
  • O(Organization) → group(可以多个)

于是 APIServer 把 kubelet 视为:

text 复制代码
username = system:node:<nodeName>
groups   = ["system:nodes", "system:authenticated"]

这就是我们平时在日志 or kubectl auth can-i 中看到的身份。


2️⃣ 然后交给「授权(Authorization)」阶段

认证只是告诉 APIServer:

"对方是谁。"

接下来要问:

"这个身份 做什么?"

APIServer 内部的授权模块(如 RBAC + NodeAuthorizer)会:

  • 根据请求内容:
    verb(get/list/watch/create/update/delete)
    resource(pods/nodes/secrets/...)
    namespace(default/kube-system/...)
    name(具体对象)
  • 结合请求发起者的:
    • username = system:node:<nodeName>
    • groups = system:nodes

查一遍 RBAC 规则 和 NodeAuthorizer 规则:

  • 节点可以:
    • 获取/更新自己的 Node 对象
    • 获取/更新和自己有关的 PodStatus
    • 获取需要运行的 PodSpec 等
  • 但不能:
    • 随便看其他 Node 的敏感信息
    • 读取任意 Secret
    • 改其他 Namespace 的资源(有严格限制)

如果授权通过:

请求被执行(例如 kubelet 上报心跳 / 获取 Pod)。

如果授权失败:

kubelet 收到 403 Forbidden,日志里能看到对应错误。


四、把这两条线合并成一个"完整故事"

当 kubelet 和 APIServer 通信时,每一次 HTTP 请求,都可以拆成两部分理解:

传输安全层(TLS)

  1. kubelet 拨 API Server 端口
  2. TLS 握手完成 mTLS:
    • APIServer 身份通过服务器证书被 kubelet 验证
    • kubelet 身份通过客户端证书被 APIServer 验证
  3. 建立加密通道

Kubernetes 认证 + 授权

  1. APIServer 读取客户端证书 Subject:
    • 映射为 user = system:node:<nodeName> group = system:nodes
  2. 交给 RBAC + NodeAuthorizer:
    • 看这个"用户"是否有权限干这件事
  3. 如果允许 → 正常返回
    如果不允许 → 返回 403

整个过程对应用层来说是透明的,kubelet 只管:

  • 配好 kubeconfig(证书+私钥+CA)

  • 发 HTTP 请求

其余的都由 TLS + APIServer 完成。


五、和 bootstrap token 阶段对比一下,更好理解

之前的 bootstrap 认证是:

kubelet 用 token 来当临时凭证,只能做少数事情(例如发 CSR)。

现在 mTLS 阶段是:

kubelet 用 证书+私钥 来证明自己是 system:node:<nodeName>,有完整的 Node 角色能力(但仍受限于 RBAC&NodeAuthorizer)。

我们可以这么理解:

  • token=一次性访客码
  • 证书 + 私钥=正式工牌 + 指纹

六、如果想"现场观察"这个过程,可以做几件有趣的实验

如果是在真实集群里玩,可以尝试:

  1. 在 kubelet 节点上查看证书信息:

    bash 复制代码
    openssl x509 -in /var/lib/kubelet/pki/kubelet-client-current.pem -noout -text
  2. 在 APIServer 节点上看启动参数(kube-apiserver 静态 Pod 的 manifest):

    bash 复制代码
    cat /etc/kubernetes/manifests/kube-apiserver.yaml | grep client-ca-file
  3. 在 master 上用:

    bash 复制代码
    kubectl auth can-i get pods --as=system:node:<nodeName> --as-group=system:nodes

    看看这个 Node 身份能做什么,不能做什么。


七、总结

kubelet ↔ APIServer 双向 TLS 的本质是:用集群 CA 签发的客户端证书,把 kubelet 认证为 system:node:<nodeName>,然后再通过 RBAC + NodeAuthorizer 控制它能访问哪些资源,所有请求都跑在加密的 mTLS 通道之上。
Server(API Server) Client(kubelet) Server(API Server) Client(kubelet) 前提(mTLS,按 TLS 1.2 语义简化): 1) C 本地信任 CA 证书 ca.crt(用于验证 S 的证书) 2) S 本地信任 CA 证书 ca.crt(用于验证 C 的证书) 3) S 持有服务端证书+私钥(ServerCert/ServerKey) 4) C 持有客户端证书+私钥(ClientCert/ClientKey) Client 侧校验 Server: - 用本地 ca.crt 验证证书链签名 - 校验有效期 / KeyUsage / EKU - 校验 SAN 中主机名/IP 匹配(仅 Client 侧) 失败则中断 Server 侧校验 Client: - 用本地 ca.crt 验证证书链签名 - 校验有效期 / KeyUsage / EKU(ClientAuth) - 不做"主机名/IP 匹配"(这是 Server 证书校验语义) 私钥持有证明: - 用 ClientCert 中公钥验证签名 Verify(pubKey, handshake_hash, signature) 成功 ⇒ 证明 C 持有对应私钥 至此 TLS 通道建立: 1) 双方完成证书链校验(mTLS 身份真实性) 2) 双方确认对方"持有私钥" 3) 协商出对称会话密钥,后续数据加密传输 TLS 成功后进入 Kubernetes 认证/鉴权(非 TLS 协议本身): - X.509 Authenticator:从 ClientCert 取 Subject CN → username(如 system:node:<nodeName>) O → groups(如 system:nodes) - Authorization:RBAC + NodeAuthorizer 决定可执行操作 ClientHello (TLS 版本、cipher suites、随机数...) 1 ServerHello (选择版本 & cipher) 2 ServerCertificate (服务端证书链:ServerCert + intermediates) 3 CertificateRequest (请求客户端证书,用于 mTLS) 4 ServerHelloDone 5 ClientCertificate (客户端证书链:ClientCert + intermediates) 6 ClientKeyExchange (密钥协商相关数据) 7 CertificateVerify (ClientKey 对握手摘要签名) 8 ChangeCipherSpec 9 Finished(已加密) 10 ChangeCipherSpec 11 Finished(已加密) 12 HTTPS API Request(加密通道内) 13 HTTPS API Response(加密通道内) 14


问题二

客户端用本地 CA 公钥验证"证书是否由受信 CA 签发",这个证书指的是谁的?怎么一步步验证?

答案分两层:

✔️ 这里验证的证书是 服务器的证书(在 mTLS 下,后来也会验证客户端证书)

✔️ 验证的核心动作是:用"CA 公钥"去验证"证书里的签名" ,如果能验证通过 → 说明:这个证书确实是那个 CA 签发的,没被篡改。


一、谁的证书在被客户端验证?

在 TLS 握手早期:

复制代码
ClientHello →
← ServerHello + ServerCertificate

服务器把 自己的证书 发给客户端,证书里面写着:

  • Server 公钥
  • 服务器身份(域名 / IP)
  • 颁发者:某个 CA
  • 有效时间
  • 证书用途
  • 以及 CA 对它做的数字签名

所以,当我们说 "客户端用 CA 公钥验证证书" ,指的就是:客户端在检查服务器证书是否真的由那个 CA 签发。


二、CA 给证书做了什么(签名原理)

当服务器向 CA 申请证书时:

1️⃣ 服务器生成公钥/私钥

2️⃣ 把「公钥 + 身份信息」打包成 CSR

3️⃣ 发送给 CA

4️⃣ CA 审核通过后,做两件事:

复制代码
A = (服务器公钥 + 身份 + 有效期 + 用途 + 颁发 CA 信息 + ...)
B = hash(A)
signature = Sign( B , CA_private_key )

然后把 signatureA 一起打包成证书(X.509)。

📌 这意味着:只有 拥有 CA 私钥 的一方才能生成合法 signature。


三、客户端验证的过程(关键步骤)

客户端拿到证书之后,会做这样的事:

第一步:提取内容和签名

从证书中分出两部分:

复制代码
证书内容(A)
签名(signature)

第二步:重新计算 hash

复制代码
B' = hash(A)

第三步:用 CA 公钥做"验证"

复制代码
Verify( CA_public_key , B' , signature )

如果验证成功:

说明是 用这个 CA 的私钥签发的(可信) 且证书内容未被修改(完整性 OK)

如果失败:

这个证书要么被篡改,要么不是受信 CA 签的 → 直接拒绝。


四、如何判断"是否是受信 CA"?

客户端的系统 / 程序内置一组:

  • 信任 CA 列表

例如:

  • Linux:/etc/ssl/certs/
  • 浏览器内置 CA store
  • Kubernetes/kubelet:ca.crt

TLS 规则:

只有在 信任列表中的 CA 才可以给证书背书。

如果证书的签发者不在列表中:

复制代码
UNTRUSTED CERTIFICATE

连接会被拒绝。


五、那"服务器身份"又是怎么验证的?

除了检查是不是受信 CA 签发外,客户端还会:

✔ 读取证书中的:

复制代码
Subject Alternative Name (SAN)

里面列着:

  • 域名 / IP 白名单

然后:

复制代码
如果请求的域名/IP 不在证书允许的列表中 → 失败

比如:

证书签发给 api.cluster.local 但你访问的是 1.2.3.4 → 会报 证书不匹配


六、回到最初的问题

问:客户端用本地 CA 公钥验证证书是否由授信 CA 签发,这个证书是服务器的吗?

✔ 是的------在 TLS 握手初期,是服务器证书。(在 mTLS 中服务器也会验证客户端证书,但原理相同。)

问:具体是怎么验证的?(几个步骤浓缩一下)

复制代码
服务器 → 发证书(含公钥 & CA 签名)

客户端:
  1)取出证书内容 & 签名
  2)重新计算 hash
  3)用 CA 公钥验证签名
  4)确认证书没改过,且证书确实由受信 CA 签发
  5)检查域名/IP 是否匹配
  6)检查是否过期

完全通过后:客户端确认 ------ "对方是真的服务器。"


七、实验一下

下载任意 HTTPS 站点证书:

bash 复制代码
echo | openssl s_client -connect www.google.com:443 -showcerts

然后验证:

bash 复制代码
openssl verify -CAfile <系统CA路径> server-cert.pem

就会看到:

复制代码
server-cert.pem: OK

这就是我们刚刚讲的验证过程。


八、总结

客户端验证的是 服务器证书 ;验证方法是:用受信 CA 的公钥,检查证书中的数字签名是否正确,以此确认证书真的来自受信 CA,而且没有被篡改。


问题三

从证书中分出两部分:证书内容(A)签名(signature)这两部分分别是什么?

我们把证书拆开来看一下。

部分 含义 是否可读 谁生成
证书内容(A = TBSCertificate) 证书真正携带的信息(公钥、身份、有效期等) 可以解析并查看 由证书持有人/CA 构造
签名(signature) CA 用自己的私钥对 A 的摘要 做的数字签名 机器可验证 由 CA 生成

🔹 A 是"要声明的内容"

🔹 signature 是"CA 的背书证明"


一、先看「证书内容(A)」到底包含什么

在 X.509 规范里,它的正式名字是:

TBSCertificate(To Be Signed Certificate ------ "待签名部分")

我们可以用 openssl 查看:

bash 复制代码
openssl x509 -in cert.pem -noout -text

我们看到的所有可读内容,基本都属于 A


🧾 A(TBSCertificate)包含内容(简化)

版本号 (v1/v3)

序列号(唯一 ID)

签名算法标识(未来要用哪种算法签名)

Issuer(颁发者)

Validity(有效期)

  • Not Before

  • Not After

Subject(证书拥有者身份)

  • CN / O / OU / C 等

Subject Public Key Info(主体公钥信息)

  • 公钥算法(RSA/ECDSA...)
  • 公钥值

扩展字段(Extensions)

  • Key Usage(密钥用途)
  • Extended Key Usage(client auth / server auth)
  • Subject Alternative Name(域名/IP)
  • Basic Constraints(是否为 CA)
  • CRL / OCSP 信息
  • ...

👉 总结:A = 证书自身所有"声明的内容",并且 A 是可读且透明的(并非加密的),任何人可以解析看到里面写了什么。


二、再看「签名(signature)」是什么

我们把 A 计算一次摘要:

复制代码
digest = HASH(A)

HASH 取决于证书里记录的算法,比如:SHA256、SHA384 等等。

然后:

复制代码
signature = SIGN( digest , CA_private_key )

也就是:

用 CA 的 私钥 对 A 的 hash 进行数字签名。

签名结果就是一个大块二进制数据,放在证书的 签名值字段中。


三、完整证书结构(抽象结构)

证书的逻辑结构可以写成:

复制代码
Certificate {
    tbsCertificate       = A
    signatureAlgorithm   = ...
    signatureValue       = signature
}

我们可以理解为:

「内容(A)」 + 「签名算法」 + 「签名值」。


四、验证时两部分如何配合?

客户端验证时:

① 重新计算 A 的 hash

复制代码
digest' = HASH(A)

② 使用 CA 公钥验证 signature

复制代码
Verify( CA_public_key , digest' , signature )

如果成功说明:

  • 这张证书确实由 该 CA 的私钥签过
  • A 的内容没有被篡改

👉 要是有人篡改了 A:

复制代码
digest' != 原来的 digest

验证立刻失败。


五、可以用 openssl 看到两部分

🔍 只打印 "待签名部分 A"

复制代码
openssl x509 -in cert.pem -noout -text -certopt no_signature

🔍 查看签名本身

复制代码
openssl x509 -in cert.pem -noout -signature -text

我们会看到一个大段 Base64 的签名值。


六、再用一句"生活比喻"巩固

A = 合同正文(写了双方身份、条款等)
signature = 公证处盖章(确保内容真实、未被改动)

合同可以阅读;章不是加密,而是 验证用

七、总结

证书内容(A = TBSCertificate)

是"要被签名的全部信息":身份、公钥、用途、有效期、扩展等。

签名(signature)

是 CA 用私钥对 hash(A) 做的数字签名,用来证明:"这个证书确实是我签过的,而且没被改过。"


问题四

客户端证书和服务器证书,一开始是从哪儿来的?谁颁发?怎么拿到?

答案其实是:

都来自同一个"证书体系(PKI)",由一个受信任的 CA(证书颁发机构)签发。

不过在不同系统里(例如 Kubernetes、浏览器 HTTPS、企业内部系统)获取方式有所不同

下面,我们分三层讲:

1️⃣ 先讲通用原理:证书是怎么"被签出来"的

2️⃣ 然后讲 Kubernetes 里的 server/client 证书

3️⃣ 最后讲互联网 HTTPS 的证书来源

一、通用原理:证书的来源(PKI 工作流)

任何一张合法证书(客户端或服务器)都会经历这 4 步:

① 生成密钥对(本地完成)

无论是客户端/服务器,都要先创建:

复制代码
公钥(public key)
私钥(private key)

示例命令:

bash 复制代码
openssl genrsa -out server.key 2048

👉 私钥 永远不离开本机

👉 公钥用于"公开 + 放进证书中"。

② 生成 CSR(证书申请)

接下来生成:

CSR(Certificate Signing Request)= 证书签名请求

里面包含:

  • 公钥
  • 希望被写进证书的身份信息(域名、组织、用途等)

例如:

bash 复制代码
openssl req -new -key server.key -out server.csr

③ 发送给 CA 审核 & 签名

把 CSR 交给 证书颁发机构 CA

  • 可能是公司内部 CA(自建 PKI)
  • 或互联网 CA(比如 DigiCert、Let's Encrypt)
  • Kubernetes 集群自己的 CA(内置)

CA 做两件事:

✔ 验证"你是谁"

✔ 用 CA 私钥 对 CSR 内容签名(生成证书)

得到证书:server.crt

④ 安装证书 & 配置系统

  • 服务器 → 配置 server.crt + server.key
  • 客户端可能拿到 client.crt + client.key

然后双方都要:

预先信任 CA 的 公钥(根证书)


二、在 Kubernetes 里:证书来源

K8S 自己有一个 内置 CA,默认由 kubeadm 初始化:

📁 放在控制节点:

复制代码
/etc/kubernetes/pki/ca.crt      # CA 公钥
/etc/kubernetes/pki/ca.key      # CA 私钥(绝密)

1️⃣ 服务器证书(API Server)

初始化时:

复制代码
kubeadm init

会自动:

  1. 生成 APIServer 公钥/私钥
  2. 生成 CSR
  3. 用 CA 私钥签发 server 证书

得到:

复制代码
/etc/kubernetes/pki/apiserver.crt
/etc/kubernetes/pki/apiserver.key

👉 这个就是:

API Server 的 HTTPS 服务器证书

2️⃣ 客户端证书(kubelet / controller / admin)

客户端证书有三类常见来源:

✔ kubelet(节点)
  1. kubelet 生成密钥对
  2. 提 CSR 给 APIServer
  3. kube-controller-manager 自动审批
  4. CA 签发客户端证书

存放:

复制代码
/var/lib/kubelet/pki/kubelet-client-current.pem
✔ 管理员 kubeconfig

kubeadm 也会创建 admin 的客户端证书:

复制代码
/etc/kubernetes/admin.conf

里面包含:

  • client-certificate
  • client-key
  • cluster CA

我们 kubectl 的时候就是用它认证。

✔ 各控制组件(scheduler / controller)

类似:

复制代码
/etc/kubernetes/scheduler.conf
/etc/kubernetes/controller-manager.conf

都包含:

  • 组件客户端证书
  • 组件私钥
  • 集群 CA

全部由 CA 签发。

关键点

**在 Kubernetes 里,绝大多数 server/client 证书都由"集群内置 CA" 自动签发,**管理员很少手动发证书(除非特别场景)。

三、在互联网上(HTTPS 网站)

当我们访问:

复制代码
https://www.google.com

服务器证书的来源是:

✔ 网站向 Public CA (DigiCert / Let'sEncrypt / GlobalSign...)申请

✔ CA 验证域名所有权

✔ 签发 server.crt

浏览器信任的 CA 列表,存放在Chrome / Firefox 内置 或 OS 提供

客户端证书(互联网少见)

少量企业系统会要求客户端也必须提供证书(mTLS)

客户端证书来源:

  • 通常由 企业内部 CA 系统 签发
  • 发到员工电脑/手机
  • 安装在浏览器 / 证书存储里

总结

1️⃣ 证书本身不是"凭空生成",而是 CA 根据 CSR 签出来的。

2️⃣ 客户端 & 服务器证书都可以由同一个 CA 签发(Kubernetes 就是这样)。

3️⃣ 只要双方都信任同一个 CA,就能够通过 TLS 建立安全身份信任。


问题五

Kubernetes API Server 的核心安全文件

复制代码
/etc/kubernetes/pki/apiserver.crt
/etc/kubernetes/pki/apiserver.key

它们分别是什么?有什么用?什么时候参与 TLS?

文件 含义 作用
apiserver.crt API Server 的 服务器证书(X.509 证书) 发给客户端(kubelet / kubectl / controller 等)用来验证"你连接的是正版 APIServer"
apiserver.key API Server 的 私钥 用于 TLS 握手签名证明"证书确实属于我",绝不能泄漏

👉 **这两者一起构成 API Server 的 HTTPS 身份,**就像网站的 server.crt + server.key


一、apiserver.crt ------ API Server 的"身份证"

这是一个 公开证书,内容包括:

  • API Server 的 公钥
  • 主机身份(域名/IP):
    • kubernetes
    • kubernetes.default
    • 10.x.x.x(Service IP)
    • 节点 IP
  • 有效期
  • 颁发者(CN=kubernetes 这种 CA)
  • 证书用途:
    • TLS Web Server Authentication
    • TLS Web Client Authentication(部分环境)

我们可以查看:

bash 复制代码
openssl x509 -in /etc/kubernetes/pki/apiserver.crt -noout -text

客户端(比如 kubelet、kubectl、controller-manager)在连接 API Server 时:

1️⃣ 接收这个证书

2️⃣ 使用 CA (/etc/kubernetes/pki/ca.crt) 验证:

  • 证书是否由受信 CA 签发
  • 证书是否未过期
  • 域名/IP 是否匹配目标 API 地址

验证成功:

客户端确认 ------ "我连的就是集群里真正的 API Server。"


二、apiserver.key ------ API Server 的私钥(极其敏感)

这个文件包含与 apiserver.crt 公钥成对的 私钥

它的作用是:

在 TLS 握手过程中,证明:"我确实拥有和证书匹配的私钥。"

流程是:

1️⃣ 客户端发起握手

2️⃣ API Server 发送 apiserver.crt

3️⃣ TLS 协议要求服务器用 私钥 对某些握手数据签名

4️⃣ 客户端用证书中的公钥验证这份签名

如果验证成功,客户端确认 ------ "不仅证书是真的,而且服务器真的掌握对应私钥,没有被冒充。"

⚠️ 一旦 apiserver.key 泄漏

  • 攻击者可以伪装成 API Server
  • 截获 kubelet/kubectl/token/证书
  • 甚至接管整个集群

👉 所以,apiserver.key 必须只在控制节点、root 可读。


三、这两者是在什么时候生成的?

在我们执行 kubeadm init 时生成的:

  • Kubeadm 自动创建 server 证书

  • 用集群 CA(ca.key)签发:

    /etc/kubernetes/pki/ca.crt
    /etc/kubernetes/pki/ca.key

并把它配置到:

复制代码
/etc/kubernetes/manifests/kube-apiserver.yaml

我们可以在里面看到:

yaml 复制代码
- --tls-cert-file=/etc/kubernetes/pki/apiserver.crt
- --tls-private-key-file=/etc/kubernetes/pki/apiserver.key
- --client-ca-file=/etc/kubernetes/pki/ca.crt

四、谁会用到 apiserver.crt?

✔ kubelet

✔ scheduler

✔ controller-manager

✔ admin (kubectl)

✔ metrics-server 等外部组件

每个客户端都要 通过 APIServer 证书确认"服务端身份"。


五、总结

apiserver.crt = API Server 的服务器证书(公开,可分发)
apiserver.key = 对应的私钥(只能服务器自己使用)

两者组合 → 支撑 API Server 的 HTTPS / mTLS 通信。


问题六

/var/lib/kubelet/pki/kubelet-client-current.pem 里既有 CERTIFICATE 又有 PRIVATE KEY

可是这个 CERTIFICATE 和 /etc/kubernetes/pki/ca.crt 里显示的 证书内容不一样

为什么?难道有问题吗?

👉 答案:完全正常,而且这是 TLS 工作的正常设计。

kubelet-client-current.pem 里的是:kubelet 自己的客户端证书

ca.crt 里的是:集群 CA(证书颁发机构)的证书(根证书)

🔹 两者 本来就不一样

🔹 但 kubelet 的证书是由 ca.crt 对应的 CA 签发的

所以,不是要内容相同 ,而是 kubelet 证书的 签名 可被 ca.crt 成功验证 ------ 这才是"可信"的关键。


一、对比一下这两类证书是谁的

1️⃣ kubelet-client-current.pem(客户端证书)

用途:

kubelet → 连接 API Server → mTLS 客户端身份认证

内容包括:

字段 说明
Subject CN=system:node:<node-name>
Group O=system:nodes
公钥 kubelet 生成
用途 Client Authentication
签名 由 CA 私钥签名

查看:

bash 复制代码
openssl x509 -in /var/lib/kubelet/pki/kubelet-client-current.pem -noout -text

2️⃣ ca.crt(CA 根证书)

用途:

作为"证书颁发机构"的身份,用来验证别人(包括 kubelet)的证书是否可信。

特点:

✔ Subject = Issuer(自签名)

✔ 通常标为:

复制代码
CA: TRUE
keyCertSign: TRUE

查看:

bash 复制代码
openssl x509 -in /etc/kubernetes/pki/ca.crt -noout -text

二、为什么看起来"不一样"?

因为 它们的角色完全不同。

文件 角色 谁的"身份证"
kubelet-client-current.pem 客户端证书 kubelet(节点)
ca.crt 根证书(颁发机构) CA 自己

就像:

  • 你的身份证(个人证件)
  • 公安局的公章证书(认证机构)

当然不会一样。


三、但它们之间一定有"数学关系"

虽然内容不一样,但有一个地方是会相互对应的:

🔍 kubelet 证书里的 Issuer 字段

查看 kubelet 证书:

复制代码
Issuer: CN = kubernetes

查看 CA 证书:

复制代码
Subject: CN = kubernetes

👉 说明:

kubelet 证书是 由这个 CA(ca.crt)签发的


🔍 进一步:校验签名看看

bash 复制代码
openssl verify -CAfile /etc/kubernetes/pki/ca.crt \
  /var/lib/kubelet/pki/kubelet-client-current.pem

如果输出:

复制代码
...: OK

→ 这就是完整证明:

虽然内容不同,但 kubelet 的证书 是由集群 CA 签名认证的,可信。


四、为什么把 CERTIFICATE & PRIVATE KEY 放在同一个 pem?

便于 kubelet:

  • 读取一个文件即可
  • 同时获取:
    • 客户端证书
    • 匹配的私钥

🔹 证书 = 公开信息 (可以给 API Server 看)

🔹 私钥 = 绝密,仅用于签名(永远不发送)


五、如果有一天kubelet-client-current.pem与ca.crt真的"完全一样",反而是灾难

如果:

复制代码
kubelet-client-current.pem == ca.crt

意味着:

kubelet 拿着 CA 证书当作自己证书

那后果是:

  • kubelet 伪装成 CA
  • 可以伪造无限证书
  • 集群安全 = 直接崩溃

👉 所以 不一样才是对的

六、总结

/var/lib/kubelet/pki/kubelet-client-current.pem = kubelet 本机的 客户端证书 + 私钥

/etc/kubernetes/pki/ca.crt = 集群根 CA(签发别人的证书)

两者不一样是正常的。正确关系是:kubelet 证书由 ca.crt 对应 CA 签名。


问题七

kubeconfig 的核心结构/etc/kubernetes/admin.conf 里的这些字段展开介绍:

复制代码
clusters:
- cluster:
    certificate-authority-data:  ???(看起来不像 ca.crt)

users:
- name: kubernetes-admin
  user:
    client-certificate-data:  ???
    client-key-data:          ???
字段 含义 实际内容 为什么和 ca.crt/其他证书不一样
certificate-authority-data 集群 CA 证书(根证书) ca.crt 的 Base64 内嵌版本 因为是 Base64 编码;不是逐字一致,但解码后内容相同
client-certificate-data admin 的客户端证书 admin.crt 它是"管理员自己的证书",当然不会等同 CA 证书
client-key-data admin 的私钥 admin.key 只给本地使用,永远不与别人相同

👉 admin.conf 内嵌了三样东西:

✔ 信任"谁签的证书"(CA)

✔ 证明"我是管理员"(客户端证书)

✔ 证明"证书真的属于我"(私钥)


1️⃣ certificate-authority-data ------ 集群 CA 证书(Base64)

问:

certificate-authority-data 为什么跟 /etc/kubernetes/pki/ca.crt 不一样?

原因是:

它其实就是 ca.crt,但被 Base64 编码后直接放进 kubeconfig 了。

我们可以验证:

bash 复制代码
echo "<certificate-authority-data 内容>" | base64 -d

我们会看到熟悉的:

复制代码
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----

📌 作用:kubectl / API 客户端通过它验证 API Server 的证书是否合法

也就是:

  • "这个 API Server 的证书是不是这个 CA 签的?"
  • "这个证书有没有被篡改?"

2️⃣ client-certificate-data ------ kubernetes-admin 的客户端证书

这是 管理员(kubernetes-admin)的 X.509 客户端证书

它通常对应:

复制代码
/etc/kubernetes/pki/admin.crt

内容里:

  • CN = kubernetes-admin
  • O = system:masters

👉 通过 RBAC,这个 group 允许管理员拥有集群所有权限。

我们可以解码查看:

bash 复制代码
echo "<client-certificate-data>" | base64 -d | openssl x509 -text

📌 关键点:

它是 用 CA 签发出来的 ,但 不是 CA 证书,所以内容当然与CA证书不一样。

就像:

  • 公安局有"公章"
  • 你有"身份证"

两者不是一张证件,但身份证是公安局盖章的。


3️⃣ client-key-data ------ kubernetes-admin 的私钥

这是管理员证书的 私钥

对应:

复制代码
/etc/kubernetes/pki/admin.key

Base64 解码:

bash 复制代码
echo "<client-key-data>" | base64 -d

会看到:

复制代码
-----BEGIN PRIVATE KEY-----
...
-----END PRIVATE KEY-----

📌 作用:

当 kubectl 连接 API Server 时,通过 TLS 签名证明:"我确实拥有 kubernetes-admin 这张证书。"

⚠️ 极其重要:

  • 不能泄漏
  • 泄漏 = 别人可以假装超级管理员

🔍 再整体看一眼 admin.conf 的工作方式

当我们运行:

复制代码
kubectl get pods

流程是:

1️⃣ kubectl 读取 admin.conf

2️⃣ 用 client-certificate-data + client-key-data

→ 作为客户端证书(管理员身份)

3️⃣ 用 certificate-authority-data

→ 验证 API Server 证书

4️⃣ mTLS 成功 → API Server 识别为:

复制代码
username = kubernetes-admin
groups   = system:masters

然后 RBAC 允许几乎所有操作。

总结

复制代码
certificate-authority-data   = 集群 CA(信任谁)【Base64】
client-certificate-data      = 管理员证书(我是谁)
client-key-data              = 管理员私钥(证明证书是我的)

👉 看起来不一样,是因为:

  • 一个是 CA(根证书)
  • 一个是 admin 的个人证书
  • 而且都经过 Base64 编码

不是问题,是设计如此。


问题八

/etc/kubernetes/pki/ca.crt 难道本身不是 Base64 吗?
那为什么 kubeconfig 里还要再 Base64 一层?

答案是:

ca.crt 既是,也不是"Base64"。
它是 PEM 格式,而 PEM 里面确实包含了一段 Base64。

接下来,我们逐步介绍。

文件 实际格式 里面是否有 Base64 备注
/etc/kubernetes/pki/ca.crt PEM(文本) ✔ 包含 Base64 数据 已经外层带有 BEGIN/END CERTIFICATE
certificate-authority-data 纯 Base64 字符串 ✔ 对 PEM 再 Base64 一次 方便直接嵌入 YAML

📌 关键区别不是 是否 Base64,而是:

PEM = Base64 + 头尾 + 换行 + 元信息
kubeconfig 里的 data = 原封不动再包一层 Base64


1️⃣ /etc/kubernetes/pki/ca.crt 的真实结构

打开看:

复制代码
-----BEGIN CERTIFICATE-----
MIIC2DCC...
...
u8Q==
-----END CERTIFICATE-----

这就是 PEM(Privacy Enhanced Mail)格式

PEM 由三部分组成:

1️⃣ 头部

复制代码
-----BEGIN CERTIFICATE-----

2️⃣ Base64 编码的 DER(二进制证书)

3️⃣ 尾部

复制代码
-----END CERTIFICATE-----

👉 所以我们看到的正文:

复制代码
MIIC2DCC...

确实是 Base64 ------ 但它是带包装的。


2️⃣ kubeconfig 为什么还需要 Base64?

在 kubeconfig 中:

复制代码
certificate-authority-data: <长串...>

这里存储的是 整个 PEM 文件再次 Base64 编码后的字符串

为什么要再包一层?

✔ 1. YAML / JSON 需要安全传输

PEM 包含:

复制代码
-----BEGIN CERTIFICATE-----
换行符
等号 = 
斜杠 /

直接嵌入 YAML / JSON:

  • 容易破坏格式
  • 解析器不稳定
  • 缩进 & 空格容易导致错误

✔ 2. 方便跨平台 & API 传输

Base64 是 通用"安全字符串"

  • 可嵌入环境变量
  • REST API
  • ConfigMap
  • 任意格式文件

所以 kubeconfig 采用:

复制代码
PEM → Base64 → 放入 YAML

3️⃣ 我们可以验证两者是不是同一个证书

🔍 解码 kubeconfig

bash 复制代码
echo "<certificate-authority-data>" | base64 -d > decoded-ca.crt

然后对比:

bash 复制代码
diff decoded-ca.crt /etc/kubernetes/pki/ca.crt

大概率:

复制代码
完全一致(可能仅换行不同)

4️⃣ 对比

名称 长什么样 含义
PEM 文件 -----BEGIN CERTIFICATE----- Base64 + 头尾
Base64 纯字符串 MIIC2DCC... 只是编码后的一串文本
kubeconfig data TUlJQzJEQ0... 对 PEM 整体再 Base64

总结

/etc/kubernetes/pki/ca.crt = PEM 文件 内含 Base64 数据,但格式可读。

certificate-authority-data = 把 PEM 整个 Base64 再编码一遍后的字符串。

两者解码后内容其实是 同一个 CA 证书


问题九

复制代码
openssl x509 -in /var/lib/kubelet/pki/kubelet-client-current.pem -noout -text

是什么意思?在做什么?

**这条命令是:用 openssl 读取一个 X.509 证书文件,并"人类可读地打印出来"。**而且它只会显示证书内容,不会输出原始 PEM(因为用了 -noout)。


🔍 我们逐个拆解参数

1️⃣ openssl

openssl 是一个 通用的加密工具箱,包含:

  • 证书解析
  • 编解码
  • TLS 测试
  • 签名/加密
  • 生成证书等

2️⃣ x509

告诉 openssl:"我要操作的是 X.509 证书。"

X.509 是 TLS/HTTPS/Kubernetes 常用的证书标准格式。


3️⃣ -in /var/lib/kubelet/pki/kubelet-client-current.pem

指定 要读取哪个证书文件

这个文件是:

kubelet 的 客户端证书(附带私钥)


4️⃣ -noout

默认 openssl 会输出:

  • 原始 PEM
  • 再加解析输出

-noout 的作用:

不输出原始内容,只显示解析后的信息。


5️⃣ -text

表示:

以"人类可读"的格式打印证书结构

例如:

  • 版本
  • 序列号
  • 签发者(Issuer)
  • 持有者(Subject)
  • 有效期
  • 公钥信息
  • 证书用途
  • 扩展字段
  • 证书签名算法
  • 证书签名本身

实际输出示例(关键部分)

我们会看到类似:

复制代码
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:  12345
    Signature Algorithm: sha256WithRSAEncryption
        Issuer: CN = kubernetes
        Validity
            Not Before: Jan  1 00:00:00 2026 GMT
            Not After : Jan  1 00:00:00 2027 GMT
        Subject: CN = system:node:<hostname>, O = system:nodes
        ...
        X509v3 Key Usage: 
            Digital Signature, Key Encipherment
        X509v3 Extended Key Usage:
            TLS Web Client Authentication

关键字段解释:

字段 含义
Issuer CA(谁签发的)
Subject 证书属于谁(system:node:xxx)
Validity 有效期
Key Usage 允许的用途(数字签名、认证等)
Ext Key Usage client auth / server auth
Public Key 公钥

为什么我们常用这条命令?

因为它能回答很多关键问题:

✔ 证书是谁签发的?

✔ 证书属于哪个节点?

✔ 是否为客户端证书?

✔ 是否快过期?

✔ 支持哪些用途?

✔ 是否由正确的 CA 签发?

在 Kubernetes 故障排查中必备。

总结

复制代码
openssl x509 -in FILE -noout -text

把证书解析成人类可读形式,用来检查证书内容。

对于 kubelet:

复制代码
/var/lib/kubelet/pki/kubelet-client-current.pem

👉 就是检查 kubelet 的客户端证书是否正确。


问题十

Kubernetes 证书过期了怎么办?会影响什么?怎么修?

我们分三层回答(从概念 → 判断 → 修复):

1️⃣ 哪些证书会过期、过期会发生什么

2️⃣ 如何判断是谁过期了

3️⃣ 最常见的 修复(续期)方案

1. 先弄清:哪些证书会过期(影响不同)

K8S 里常见证书:

证书 作用 过期后后果
API Server (apiserver.crt) API Server HTTPS 整个集群几乎瘫痪
Controller / Scheduler 控制平面组件访问 API 控制平面异常
Admin (admin.conf) kubectl 管理员 你登录不了,但集群还能跑
kubelet-client kubelet 访问 API Node 变 NotReady
kubelet-server kubelet 对外 TLS 少量功能异常

👉 最危险的是:

复制代码
apiserver.crt / controller / scheduler

2. 先确认:谁真的过期了?

在目标节点查看:

bash 复制代码
openssl x509 -in <证书路径> -noout -dates

例如:

bash 复制代码
openssl x509 -in /etc/kubernetes/pki/apiserver.crt -noout -dates

输出类似:

复制代码
notBefore=...
notAfter=Jan 28 12:00:00 2026 GMT   ← 过期时间

如果已经过去 → 就是 expired

3. 修复(按场景分)

场景 A:kubeadm 部署(最常见)

👉 kubeadm 提供了 一键续期

① 查看证书过期情况

bash 复制代码
kubeadm certs check-expiration

② 续期(控制平面证书)

bash 复制代码
kubeadm certs renew all

它会自动:

✔ 生成新证书

✔ 用当前 CA 签名

✔ 覆盖旧文件

③ 重启控制平面 Pod

证书更新后,重启 kube-apiserver / scheduler / controller:

bash 复制代码
docker ps | grep kube-apiserver   # containerd 的话用 crictl

通常只需要:

bash 复制代码
systemctl restart kubelet

因为控制平面是 static pod,kubelet 会重新加载。

场景 B:kubelet 证书过期

kubelet 证书默认 会自动轮换(只要 API 可用)

但如果已经失效导致无法轮换:

解决方法:

1️⃣ 停止 kubelet

bash 复制代码
systemctl stop kubelet

2️⃣ 删除 kubelet 证书(会自动重新申请 CSR)

bash 复制代码
rm -f /var/lib/kubelet/pki/kubelet-client-current.pem
rm -f /var/lib/kubelet/pki/kubelet-client-*.pem

3️⃣ 重启 kubelet

bash 复制代码
systemctl start kubelet

然后:

kube-controller-manager 会自动 approve CSR,kubelet 获得新证书。

场景 C:CA 证书过期(最复杂)

复制代码
/etc/kubernetes/pki/ca.crt

如果 CA 本身过期

所有证书都必须重新签发,集群需要"重建 trust 链"

这是 重大变更,通常建议:

✔ 维护窗口

✔ 官方文档步骤

✔ 甚至重新创建集群 + 迁移 workloads(视情况而定)

4. 特别常见的问题

❓ 管理员 kubectl 不能用:是致命吗?

不是。

复制代码
~/.kube/config 或 admin.conf 证书过期

👉 只影响你的 CLI

👉 集群本身照样运行

解决:

bash 复制代码
cp /etc/kubernetes/admin.conf ~/.kube/config

(如果 admin.conf 续了)


❓ 我改证书了,但服务还在用旧的?

→ 重启 kubelet(static pod reload)

bash 复制代码
systemctl restart kubelet

kubeadm certs renew all 会重建 CA 吗?

❌ **不会。**它只续签"依赖 CA 的组件证书"。

5、总结

复制代码
发现报错 / 连接失败
↓
openssl x509 -in FILE -noout -dates   ← 看谁过期
↓
kubeadm 环境?
  ↓ 是
    kubeadm certs check-expiration
    kubeadm certs renew all
    systemctl restart kubelet
  ↓ 否
    手工重新签发(需要 PKI 流程)

问题十一

/etc/kubernetes/pki/ 下的这些文件都是什么?

bash 复制代码
apiserver.crt
apiserver-etcd-client.crt
apiserver-etcd-client.key
apiserver.key
apiserver-kubelet-client.crt
apiserver-kubelet-client.key
ca.crt
ca.key
etcd
front-proxy-ca.crt
front-proxy-ca.key
front-proxy-client.crt
front-proxy-client.key
sa.key
sa.pub

总览:它们大致分 4 大类

类别 说明
API Server 相关 用于 HTTPS & 访问 etcd / kubelet
集群 CA(根证书) 签发其他证书
Front-proxy 证书 aggregator API 代理使用
ServiceAccount 密钥 Pod ServiceAccount Token 签名
etcd 证书 etcd 自己的 TLS(在 etcd 目录里)

接下来逐个说明:

1️⃣ API Server 本身的证书(对外 HTTPS)

apiserver.crt

👉 API Server 的服务器证书

  • 包含 API Server 的公钥
  • 写了允许访问的域名 / IP(kubernetesclusterIP、节点IP等)
  • 发给客户端(kubectl、kubelet、controller 等)用于验证服务端身份

作用:让别人确认:你连的是"正版" API Server


apiserver.key

👉 API Server 的私钥(极度敏感)

  • apiserver.crt 成对
  • 在 TLS 握手中证明:"这张证书确实属于我"

⚠️ 一旦泄漏 → 可以伪装 API Server → 集群几乎失控

2️⃣ API Server 用来访问 etcd 的证书

apiserver-etcd-client.crt

👉 API Server 访问 etcd 时使用的客户端证书

apiserver-etcd-client.key

👉 对应的私钥

etcd 启用了 TLS 双向认证(mTLS):

  • etcd 校验:你是不是合法客户端

  • API Server 用这个证书访问 etcd

⚠️ 过期 → API Server 读不到 etcd → 集群崩溃

3️⃣ API Server 访问 kubelet 的证书

apiserver-kubelet-client.crt

👉 API Server 调 kubelet API 时的客户端证书

apiserver-kubelet-client.key

👉 对应私钥

API Server 需要查看 Pod 日志 / Exec / 端口转发 等时,会通过 kubelet 访问节点。

⚠️ 过期后:

  • kubectl logs
  • kubectl exec
  • 端口转发

等功能 会失败

4️⃣ 集群 CA(根证书)

ca.crt

👉 集群的根 CA 证书(公开)

  • 所有组件的证书都由它签发
  • 客户端通过它验证 APIServer 证书是否可信

ca.key

👉 CA 私钥(绝密)

  • 用来 签发所有控制平面证书

⚠️ 一旦泄漏:

任何人都能伪造合法证书 → 彻底失控

⚠️ 一旦过期:

所有证书必须重新签发,这是最复杂、最危险情况

5️⃣ front-proxy(聚合层 API 代理证书)

K8S 支持 API Aggregation(聚合 API),比如 metrics-server、apiserver-extension。

这时 API Server 需要 代理请求 给别的 API 服务。

front-proxy-ca.crt

👉 代理系统专用的 CA 证书

front-proxy-ca.key

👉 代理系统 CA 私钥

front-proxy-client.crt

👉 API Server 作为"代理客户端"时使用

front-proxy-client.key

👉 对应私钥

⚠️ 过期影响:metrics-server、扩展 API 可能变红、监控失败

6️⃣ ServiceAccount token 签名密钥(Pod 身份)

sa.key(私钥)

sa.pub(公钥)

作用:用来 签名和验证 ServiceAccount JWT Token,Pod 访问 API Server 时,就是拿这个 token 认证的。

⚠️ 如果删除/替换:

  • 旧 Pod 的 token 失效
  • 需要重新创建 Pod

7️⃣ etcd 目录(etcd 自己的 TLS)

复制代码
/etc/kubernetes/pki/etcd/

里面通常有:

复制代码
server.crt / server.key
peer.crt / peer.key
etcd/ca.crt

👉 用于:

  • etcd 集群节点间 mTLS
  • API Server 访问 etcd

⚠️ 任何 etcd 证书过期 → 集群立即出现严重问题。

总结

文件 作用 过期影响
apiserver.crt/key API Server HTTPS 集群几乎瘫痪
apiserver-etcd-client.* API ↔ etcd mTLS 控制面挂掉
apiserver-kubelet-client.* API ↔ kubelet logs/exec 失效
ca.crt 根证书 校验证书
ca.key 根私钥 泄漏则毁灭级
front-proxy-* 聚合API metrics / extensions 异常
sa.key/sa.pub Pod Token 旧Pod失效
etcd/* etcd TLS 直接崩溃

问题十二

apiserver-kubelet-client.key(私钥)在 "API Server 访问 kubelet" 这件事里到底起什么作用?

apiserver-kubelet-client.key

= API Server 的 "客户端私钥"

= 用来在 mTLS 握手中 证明:「我确实是合法的 API Server」

kubelet 通过验证它,才允许:kubectl logskubectl exec、端口转发 ...

没有它,API Server 无法建立受信任连接到 kubelet。


场景复盘:谁访问谁?

当我们执行:

复制代码
kubectl logs pod

流程是:

复制代码
kubectl  →  API Server  →  kubelet(对应节点)

📌 关键点:

访问 kubelet 的 不是 kubectl ,是 API Server 作为客户端

所以:

  • kubelet = 服务器(Server)
  • API Server = 客户端(Client)

👉 这是 一个新的 TLS 连接(和 kubectl→API Server 不同)。


连接 kubelet 是 "双向 TLS(mTLS)"

Kubelet 配置了:

复制代码
--client-ca-file=/etc/kubernetes/pki/ca.crt

意思是:"我只接受:由这个 CA 签发的客户端证书。"

于是 TLS 流程变为:

1️⃣ API Server 发起连接(客户端角色)

复制代码
API Server → kubelet

2️⃣ kubelet 发送自己的服务器证书

(供 API Server 验证)

3️⃣ kubelet 要求:

"你也必须提供客户端证书。"

于是 API Server 发送:

  • apiserver-kubelet-client.crt(客户端证书)
  • 并使用 apiserver-kubelet-client.key 完成握手签名

那私钥(apiserver-kubelet-client.key)在 TLS 里具体做了什么?

它主要完成 两件关键事情

作用 1:证明证书真的属于 API Server(数字签名)

TLS 会要求:"请用你的客户端私钥,对这次握手的摘要签名。"

过程:

复制代码
signature = Sign(privateKey, handshake_hash)

然后 kubelet 用证书里的 公钥 去验:

复制代码
Verify(publicKey, handshake_hash, signature)

验证成功:

kubelet 确认:

✔ 证书不是偷来的

✔ API Server 真的是合法客户端

✔ 而且证书内容未被篡改


作用 2:参与密钥协商(建立加密通道)

在 TLS 握手中:

  • 一些数据会使用 客户端私钥参与签名
  • 以确保中间人无法伪造连接

最终,双方协商出:

一个新的对称加密会话密钥

之后传输的:

  • Pod 日志数据
  • exec 指令数据
    全部走 加密 TLS 通道

如果这个私钥丢失 / 损坏 / 权限错了

我们会看到报错类似:

复制代码
Error from server: error dialing backend: x509: certificate signed by unknown authority

或:

复制代码
unable to upgrade connection: unauthorized

具体表现:

kubectl logs 失败

kubectl exec 失败

❌ 端口转发失败

但:

✔ API Server 还能正常工作(只是不能访问 kubelet)


如果这个私钥泄漏(最危险)

攻击者可以假装 API Server 直接与 kubelet 通信

意味着:

⚠️ 可以在节点上执行命令

⚠️ 读取 Pod 日志

⚠️ 操作容器

严重安全风险

👉 所以:

复制代码
-rw------- root root

权限设计是对的。


与其他证书的对比

文件 用途 API Server 是谁
apiserver.crt/key 提供 HTTPS 给客户端 服务器
apiserver-etcd-client.* 访问 etcd 客户端
apiserver-kubelet-client.* 访问 kubelet 客户端

📌 记住一句:

API Server 既是"服务器",也是"多个下游服务的客户端"。

总结

apiserver-kubelet-client.key 是 API Server 访问各 kubelet 节点时,在 mTLS 握手中证明身份的 客户端私钥

✔ 证明"我是合法 API Server"

✔ 确保 kubelet 信任并建立加密通道

❌ 永远不能泄漏

❌ 删除/损坏 → logs/exec 等失效


问题十三

"握手摘要(handshake hash)到底是什么?"

TLS 为什么要让客户端用私钥签它?

握手摘要 = 把"到目前为止双方交换过的所有握手消息" 做一次(或多次)哈希得到的结果。

然后客户端用自己的 私钥 对这个哈希签名,服务器用客户端 证书里的公钥 验证。

👉 这样能证明:

✔ "发送这些握手消息的人,就是证书的拥有者"

✔ "消息没有被篡改"

✔ "中间人不能插入自己内容"


1. 握手过程中,双方都在记录"聊天记录"

假设在一个 TLS 连接里,已发生的消息:

复制代码
ClientHello
ServerHello
ServerCertificate
ServerKeyExchange
CertificateRequest
ServerHelloDone
ClientCertificate
ClientKeyExchange
...

TLS 规范要求:

客户端 & 服务器都要把这些消息(原始二进制)拼在一起保存。

称为:

复制代码
handshake_messages

2. 计算"握手摘要(handshake hash)"

当需要客户端证明身份时:

复制代码
handshake_hash = HASH(handshake_messages)

HASH 通常是:

  • SHA256
  • SHA384
    (取决于 cipher suite)

👉 得到的是一个固定长度的摘要值。


3. 客户端用私钥签名握手摘要

客户端执行:

复制代码
signature = SIGN(private_key, handshake_hash)

并把 signature 作为 CertificateVerify 消息发给服务器。


4. 服务器如何验证?

服务器已经有:

  • handshake_messages(同样记录过)
  • 客户端证书里的 公钥
  • 客户端的签名 signature

于是它做:

复制代码
handshake_hash' = HASH(handshake_messages)
is_valid = VERIFY(public_key, handshake_hash', signature)

如果验证成功:

✔ 证明客户端确实拥有私钥

✔ 而且握手过程没有被篡改

这就是 客户端身份认证(Client Authentication)

5. 为什么要签"整个握手历史"?

关键安全目标:

❌ 防止中间人攻击(MITM)

想象一个攻击者:

复制代码
Client <---> Attacker <---> Server

如果只签一个随机值:

攻击者可能替换掉握手消息,再骗两边同时握手

但签的是:

整个握手历史(包括 Server 证书、密钥交换参数、随机数等)

那么:

  • 任何改动 → 将导致 hash 不一致 → 签名验证失败。

❌ 防止重放攻击

旧签名 不能在新连接中复用,因为:

  • 握手里包含了双方的 随机数(nonces)
  • 新链路 → 新随机数
    → 新 hash
    → 旧签名再也不匹配

6. TLS1.2 vs TLS1.3

TLS 1.2

明确有 CertificateVerify = Sign( handshake_hash )

TLS 1.3

仍然签握手哈希,但计算方式更复杂,引入更多绑定(如 Finished keys)。

但概念不变:依然签的是"握手历史摘要"。

7. 总结

握手摘要 = 到当前为止包含所有 TLS 握手消息的数据 → 做 hash。

客户端用私钥签它 → 证明自己身份

服务器用公钥验证 → 确保证书真的属于你,并且握手没被篡改。


问题十四

  • apiserver-kubelet-client.key:API Server 访问 kubelet 时的私钥
    这次是:
  • apiserver-etcd-client.key:API Server 访问 etcd 时的私钥

本质一样:API Server 作为"客户端",跟下游组件做 mTLS。但因为 etcd 是整个集群的"数据库",这对证书 & 私钥更关键。

apiserver-etcd-client.key 是:
API Server 连接 etcd 时的"客户端私钥"。

它在 TLS 中用来:

  1. 证明「我真的是受信的 API Server 客户端」

  2. 配合 apiserver-etcd-client.crt 完成 mTLS 握手

  3. 建立 API Server ↔ etcd 之间的加密、可信通道

❌ 泄漏 = 别人可以伪装 API Server 直接读写 etcd

❌ 过期/损坏 = API Server 不能访问 etcd → 集群直接半挂甚至瘫痪


1️⃣ 场景复盘:API Server 是 etcd 的"客户端"

Kubernetes 控制面是这样连 etcd 的:

kube-apiserver.yaml 中一般能看到类似配置:

yaml 复制代码
- --etcd-servers=https://127.0.0.1:2379
- --etcd-cafile=/etc/kubernetes/pki/etcd/ca.crt
- --etcd-certfile=/etc/kubernetes/pki/apiserver-etcd-client.crt
- --etcd-keyfile=/etc/kubernetes/pki/apiserver-etcd-client.key

而 etcd 这边(通常在 /etc/etcd/etcd.conf 或 systemd 参数里)会有:

bash 复制代码
--client-cert-auth=true
--trusted-ca-file=/etc/kubernetes/pki/etcd/ca.crt

这说明:

  • etcd:要求客户端必须带合法证书(开启 client auth)
  • API Server:用 apiserver-etcd-client.crt/.key 作为客户端证书

👉 所以:在这个链路上:

  • etcd = 「服务器」
  • API Server = 「客户端」

2️⃣ TLS 连接 etcd 时,私钥在干嘛?

完整链路:

text 复制代码
API Server (client)
   ↓  TLS 握手 + mTLS
etcd (server)

握手关键步骤里:

  1. etcd 发送自己的服务器证书(给 API Server 校验)
  2. etcd 要求:客户端也必须提供证书(因为 client-cert-auth=true
  3. API Server 发送:
    • apiserver-etcd-client.crt(客户端证书)
    • apiserver-etcd-client.key 对握手摘要签名
  4. etcd 用客户端证书里的"公钥"验证签名

这里 apiserver-etcd-client.key 具体做的两件事:

作用 1:证明"证书确实属于 API Server"
  • etcd 随机产生/或者协议内定义握手数据(包含双边随机数等)

  • API Server 使用 私钥 生成签名:

    text 复制代码
    signature = Sign(privateKey, handshake_hash)
  • etcd 使用客户端证书里的 public key 验证:

    text 复制代码
    Verify(publicKey, handshake_hash, signature)

验证成功 → etcd 知道:

"这个连接的对方,确实拥有证书对应的私钥,不是偷来的证书。"

作用 2:确保握手过程不可伪造,协商出加密通道

在这个过程中,握手 hash + 签名参与了最终会话密钥的协商,

防止中间人修改握手参数。

最后,API Server ↔ etcd 之间

➜ 有一个 对称加密 的 TLS 通道

kubectl get/describe/apply 等所有 API 操作,都经由 API Server → etcd 写入/读取。


3️⃣ 为什么必须用"客户端证书 + 私钥",而不是 token?

因为对 etcd 来说:

  • 它不是 Kubernetes 内的一个"普通工作负载",
  • 它是 直接存集群状态的分布式数据库
  • 不懂 kubeconfig / RBAC / ServiceAccount。

etcd 支持的身份认证方式很简单、传统:

  • TLS 客户端证书(最典型、最常用)
  • 或用户名密码 / token(一般不用在 kubeadm 这类部署里)

所以,最稳妥方式就是:mTLS + 客户端证书 也就是 apiserver-etcd-client.crt/.key 这对组合。


4️⃣ 如果这个私钥出问题,会发生什么?

1. 被删除 / 损坏 / 权限不对

API Server 启动或运行时连接 etcd 会失败:

  • 启动失败

  • 日志类似:

    text 复制代码
    failed to create client for etcd: open /etc/kubernetes/pki/apiserver-etcd-client.key: no such file or directory
  • 整个控制面挂掉(kubectl get pods 等都报错)

因为 API Server 没法连 etcd,就无法读/写任何集群状态。


2. 证书或私钥不匹配

比如:

  • 某次误操作替换了证书/私钥
  • 或 etcd CA 换了但客户端不中断升级

握手会失败,报:

text 复制代码
remote error: tls: bad certificate
tls: failed to verify client's certificate

3. 私钥泄漏(最危险情况)

如果:

text 复制代码
apiserver-etcd-client.key
apiserver-etcd-client.crt
etcd/ca.crt

全部泄漏,对攻击者意味着:

可以假装"API Server 客户端"直接连 etcd

做到什么程度?

  • 直接读/写 etcd 所有 key
    (包含所有 Secret 的原始加密/明文视环境而定)
  • 伪造 Node / Pod / Role / Binding / Token
  • 基本等于"数据库级别拿下整个集群"

⚠️ 这类文件要视为高敏感资产,建议:

  • 仅 root 可读
  • 内部备份需加密
  • 变更需走严格流程

5️⃣ 和 apiserver-kubelet-client.key 的对比

私钥文件 下游服务 API Server角色 主要影响
apiserver-kubelet-client.key kubelet 客户端 logs/exec/端口转发 等调试能力
apiserver-etcd-client.key etcd 客户端 控制面读写状态能力(全局)

👉 可以这么记:

kubelet那条链断了 → 调试不方便

etcd那条链断了 → 集群脑子没了


6️⃣ 实操建议

如果想进一步"摸清楚"自己的环境,可以:

  1. 看一下证书内容:

    bash 复制代码
    openssl x509 -in /etc/kubernetes/pki/apiserver-etcd-client.crt -noout -text

    关注:

    • Issuer(签发者,应是 etcd CA)
    • Subject(通常类似 CN=kube-apiserver-etcd-client 之类)
    • 有效期(Not Before / Not After)
  2. 看 kube-apiserver 启动参数里是否正确引用:

    bash 复制代码
    grep -E "etcd-servers|etcd-certfile|etcd-keyfile" /etc/kubernetes/manifests/kube-apiserver.yaml
  3. 如果要续期,一般用:

    bash 复制代码
    kubeadm certs renew apiserver-etcd-client
    systemctl restart kubelet   # 让 apiserver static pod 重启加载新证书

总结

apiserver-etcd-client.key
API Server 访问 etcd 的客户端私钥

它配合 apiserver-etcd-client.crt

  • 在 mTLS 中证明"我是合法 API Server 客户端"

  • 与 etcd 建立安全、加密、可认证的连接

❌ 损坏/过期 → API Server 无法连 etcd,控制面瘫痪

❌ 泄漏 → 攻击者可以伪装成 API Server 操控整个 etcd(极高风险)


问题十五

kubectl → API Server → kubelet(对应节点) kubelet起什么作用?

kubelet 就是每个节点上的 "Node Agent / 小管家"

专门负责:

  • 跟 API Server 打交道
  • 把 "Pod 规范" 变成真正跑起来的容器
  • 上报节点和 Pod 的真实状态
  • 提供日志、exec 等接口给 API Server 用

我们可以把它理解成:"Kubernetes 控制面的远程执行器 + 节点状态汇报员 + Pod 生命周期经理"


1️⃣ 在这条链路里:kubectl → API Server → kubelet

以刚才那条调用链为例:

text 复制代码
kubectl  →  API Server  →  kubelet(对应节点)

常见操作都是这样走的:

  • kubectl get pods
    • kubectl 只请求 API Server
    • API Server 从 etcd 读 "期望状态 + 当前状态"
    • 不直接找 kubelet(除非需要实时拉状态)
  • kubectl logs / kubectl exec / 端口转发
    • kubectl 请求 API Server
    • API Server 再通过 HTTPS 去调用 对应 Node 上的 kubelet API
    • kubelet 去和容器 runtime 通信(比如 containerd),拿日志/开交互/转发数据

这条链上的分工:

组件 角色
kubectl 用户 CLI 客户端
API Server 控制面入口 / 网关
kubelet 每台节点上的 "执行代理"

所以可以记住一句:

凡是"跟节点上容器打交道"的具体动作,都是 kubelet 在干。


2️⃣ kubelet 的核心职责拆解

① 接收 "期望状态":PodSpec → 真正容器

控制平面通过 API Server 下发:

  • "在 Node X 上应该跑哪些 Pod(以及怎么跑)"

kubelet 在本节点做的事:

  1. watch API Server 上和自己这个 node 相关的 Pod 对象
  2. 对比:
    • 当前节点上"实际运行"的容器情况
    • 与 Pod 对象里"期望状态"是否一致
  3. 如果发现:
    • 少了一个 Pod → 创建容器
    • 多了一个 Pod → 停掉
    • Pod 配置变化 → 重建、更新

真正调用 container runtime(CRI)的,就是 kubelet

  • containerd / dockershim / CRI-O 等
  • 通过 CRI gRPC 协议拉镜像、创建 sandbox、启动容器等

控制面只说:"我要 3 个 nginx 副本",具体 "在哪个节点启动哪个容器、怎么启动" 是 kubelet 与 runtime 一起完成的。


② 管理 Pod 生命周期与健康检测

kubelet 负责:

  • 创建 / 启动 / 停止 容器
  • 挂载 Volume(本地目录、NFS、CSI 存储等)
  • 设置环境变量、secret、configMap 注入
  • 处理 probe:
    • livenessProbe
    • readinessProbe
    • startupProbe

探针也是 kubelet 执行的

  • HTTP GET /exec / TCP check
  • 失败次数超阈值 → 杀容器、重启
  • 结果上报给 API Server(Pod 是否 Ready/Running)

③ 上报 Node 和 Pod 状态(NodeStatus / PodStatus)

kubelet 周期性地:

  • 向 API Server 上报 Node 状态:
    • 当前 CPU / 内存 容量与使用情况
    • Conditions(Ready / NotReady / DiskPressure / MemoryPressure 等)
    • 节点标签、污点变化等
  • 上报 PodStatus:
    • 容器状态(Running / Waiting / Terminated)
    • 重启次数
    • ExitCode
    • 最后一次退出原因 / 消息

调度器(kube-scheduler)和控制器都是基于这些状态做决策的。

没有 kubelet 的上报,控制面就 看不到真实世界里发生了什么


④ 作为 "节点 API" 被 API Server 调用

我们看到的:

  • kubectl logs
  • kubectl exec
  • kubectl port-forward

背后都是:

  1. kubectl 请求 API Server
  2. API Server 建立到 对应 Node 上 kubelet 的连接
  3. kubelet 再去跟容器 runtime 通信,执行相应操作
    • 读容器日志
    • 建立 tty 通道
    • 建立端口转发隧道

这条链路里:

  • kubelet 暴露一个 HTTPS 服务(kubelet API)
  • API Server 用 apiserver-kubelet-client.crt/key 以客户端证书身份访问
  • kubelet 的 --client-ca-file 校验"对方是否是合法 API Server"

这里 kubelet 相当于"节点上的本地代理控制器",外面所有对容器的操作必须通过它。


⑤ 集成 CNI / CSI / 其他插件

kubelet 是网络和存储插件的落地方:

  • 网络(CNI)
    • Pod 创建时,kubelet 调 CNI 插件(如 calico、flannel、cilium)
    • 为 Pod 设置网络命名空间与 IP
    • Pod 删除时调用 CNI cleanup
  • 存储(CSI)
    • 按照 PVC / PV 绑定结果
    • 调 CSI driver 完成卷的 attach / mount / unmount / detach

它自己不会直接"配置网卡 / 存储",而是负责调度相应的插件。


⑥ TLS 引导、证书轮换、安全接入

前面已经深挖过:

  • kubelet 在加入集群时:
    • 用 bootstrap token 加入
    • 提交 CSR
    • 拿到 kubelet-client 证书
  • 后面运行期:
    • 自动轮换客户端证书
    • 使用 /var/lib/kubelet/pki/kubelet-client-current.pem 与 API Server mTLS 通信

所有这些 安全接入、持续认证 都是 kubelet 在节点上完成的。


3️⃣ 整体架构视角:kubelet 在 Kubernetes 里的位置

可以画成这样:

text 复制代码
              +---------------------------+
              |        Control Plane      |
              |                           |
User → kubectl → kube-apiserver → etcd   |
              |      ↑                    |
              +------+--------------------+
                     |
                     | (watch Pod/Node, report status)
                     v
         +------------------------+
         |        Node            |
         |                        |
         |  +-----------------+   |
         |  |     kubelet     |<-----------------+
         |  +--------+--------+                  |
         |           |                           |
         |           | CRI                       | CNI / CSI / plugins
         |   +-------v--------+                  |
         |   |  containerd    |                  |
         |   +-------+--------+                  |
         |           |                           |
         |      [containers / pods]             |
         +--------------------------------------+
  • 上连:API Server(控制面)
  • 下管:容器 runtime、网络、存储
  • 对外:对 API Server 与运维工具提供日志/exec/etc 能力

4️⃣ 如果 kubelet 出问题,会发生什么?

常见现象:

  • Node 变成 NotReady
  • Pod 无法调度到该 Node
  • 已有 Pod 状态不更新 / 重启不生效
  • kubectl logs/exec 失败:
    • Error from server: error dialing backend
  • 控制面仍然可以工作(其它节点正常)

排查通常从以下命令开始:

bash 复制代码
systemctl status kubelet
journalctl -u kubelet -f

5️⃣ 总结

kubelet = 每个节点上跑的 Kubernetes "智能管家"

  • 接收 API Server 下发的 Pod 期望状态
  • 调用 container runtime / CNI / CSI 让 Pod 真正跑起来
  • 周期性上报节点和 Pod 状态
  • 提供日志、exec、端口转发等能力(给 API Server 用)
  • 通过 mTLS 与 API Server 建立安全信任关系

没有 kubelet:

  • 控制面就只剩 "期望的 YAML",
  • 和真实的机器、容器世界完全脱节

问题十六

kubelet "watch API Server 上和自己这个 node 相关的 Pod 对象" 到底是什么意思?

我们把它拆成 非常直观的三步

先想象一个简单的问题:

API Server 怎么告诉某个具体节点:"你要运行这个 Pod"?

👉 它 不会主动去推送

👉 它也 不会一台一台机子发命令

Kubernetes 设计的思路是:

API Server 只维护"状态",
kubelet 主动来"订阅/监听"。

第 1 步:API Server 里存的,是"全局 Pod 列表"

例如系统期望运行这些 Pod:

Pod name 被调度到哪个 Node
nginx-1 nodeA
nginx-2 nodeB
mysql-0 nodeB
busybox nodeC

这些对象都存在 etcd 中,通过 API Server 暴露出来:

复制代码
/api/v1/pods

它只是数据库,并不执行任何命令。

第 2 步:调度器给 Pod 贴上 nodeName

当 scheduler 选定一个 node:

例如:

复制代码
busybox → 决定放到 nodeB

就会把 Pod 对象改成这样:

yaml 复制代码
spec:
  nodeName: nodeB

于是 API Server 中有这么一条记录:

"busybox 这个 Pod,期望运行在 nodeB 上"

📌 注意:只是写入记录,还没人真的去创建容器。

第 3 步:kubelet 只盯"属于自己的 Pod"

每台 Node 上的 kubelet,会向 API Server 发送一个 Watch 请求

text 复制代码
给我所有  nodeName = <当前节点> 的 Pod
一旦有变化,请实时通知我。

等价于(简化理解):

bash 复制代码
GET /api/v1/pods?fieldSelector=spec.nodeName=<my-node>&watch=true

👉 这就是:

watch API Server 上与自己这个 Node 相关的 Pod 对象。

举个非常直观的例子

假设当前节点叫:nodeB

kubelet 会持续监听:

复制代码
所有被调度到 nodeB 的 Pod

API Server 里当记录发生这些变化:

事件 Pod 状态 kubelet 行为
新建一个 Pod,并调度到 nodeB ADDED kubelet:创建容器
修改 Pod 规范(镜像、环境变量等) MODIFIED kubelet:必要时重建
Pod 被删除 DELETED kubelet:停止并清理容器

📌 关键思想:不是 API Server 命令 kubelet 做事,而是 kubelet"自己看到变化后就去做"。


为什么要这么设计?

不是"命令执行"模型(那叫中控)

比如传统系统:

复制代码
中控系统 → SSH 到节点 → 执行命令

问题:

  • 强耦合
  • 中控单点失败
  • 网络抖动 = 整体失控

Kubernetes 采用"期望状态 + 自己驱动"

  • API Server 只存"我想让系统成为的样子"
  • kubelet 读取并执行,让现实逐渐向"期望"靠近

这是所谓 声明式(Declarative)模型


总结

kubelet 会一直向 API Server 订阅:"所有被调度到我这台机器的 Pod。"

只要这些 Pod 对象发生:

  • 新增
  • 修改
  • 删除

kubelet 就会:

  • 创建

  • 更新

  • 删除

节点上的真实容器。

API Server 不发命令;kubelet 主动 watch 变化,然后执行。


问题十七

"双方都要预先信任 CA 公钥(根证书)是什么意思?
客户端和服务端真的都要提前配置吗?
这个公钥又是从哪里来的?"

这正是 PKI/证书体系的核心

1️⃣ 什么叫"预先信任 CA 的公钥(根证书)"?

先记一句:

TLS 不是"相信对方",而是"相信签发对方证书的 CA"。

而 CA 的"身份证"就是:

复制代码
根证书(Root CA Certificate)

根证书里面包含:

  • CA 的 公钥
  • CA 的身份信息
  • CA 自己对自己的 自签名

⚠️ 关键点:

只要你信任某个 CA 的根证书,你自然就会信任它签发的一切服务器 / 客户端证书。

所以:

  • 浏览器信任一些"公共 CA 根证书"
  • Kubernetes 集群内部,组件信任 自己的集群 CA

这就叫:

预先信任 CA 公钥。

2️⃣ 那是不是"客户端 & 服务端都要事先配置"?

👉 是的(在需要双向认证 mTLS 时,双方都要配置)。

我们拆一下:


场景 A:普通 HTTPS(单向 TLS)

例如访问:

复制代码
https://www.google.com
  • 客户端(浏览器)
    ✔ 预装"公共 CA 根证书集合" → 用来验证网站证书。
  • 服务器(Google)
    ❌ 不需要知道客户端是谁
    ✔ 只需要配置自己的 server 证书和私钥。

只有 客户端预信任 CA 即可。


场景 B:mTLS(双向认证,例如 Kubernetes 组件内部)

例如:

复制代码
API Server  ↔  kubelet
API Server  ↔  etcd

这时:

角色 必须预先信任 目的
客户端(API Server) 服务端证书对应的 CA 验证:我连的是真 kubelet / etcd
服务端(kubelet/etcd) 客户端证书对应的 CA 验证:对方真的是合法 API Server

结论:双方都必须配置各自信任的 CA 根证书。

3️⃣ 最关键:CA 的"公钥(根证书)"是从哪里来的?

在 Kubernetes 内部(kubeadm 集群)

根 CA 存在 master 节点:

复制代码
/etc/kubernetes/pki/ca.crt      ← 公开(公钥)
/etc/kubernetes/pki/ca.key      ← 私钥(绝密)

流程:

1️⃣ kubeadm 创建 CA

2️⃣ 用 ca.key 签发:

  • apiserver.crt
  • kubelet 证书
  • controller/scheduler 证书
  • admin.crt
  • apiserver-etcd-client.crt
  • 等等...

3️⃣ kubeadm 同时把 ca.crt(公钥)复制给需要信任它的组件:

  • 放进 kubeconfig (certificate-authority-data)
  • 或显式配置:
yaml 复制代码
--client-ca-file=/etc/kubernetes/pki/ca.crt

👉 所以 Kubernetes 组件确实是:

提前拿到同一个 ca.crt 并保存下来。


在互联网 HTTPS 场景

浏览器的 CA 列表来自:

  • 操作系统(Windows / macOS / Linux)
  • 浏览器内置(Chrome / Firefox)

这些 CA 根证书:

✔ 由厂商事先审核

✔ 安装到你的系统

✔ 每次升级还会更新

所以当你访问:

复制代码
https://example.com

流程是:

  1. 网站提供 server 证书 + 中间证书链
  2. 浏览器沿着证书链一路追溯,直到某个 根证书
  3. 发现这个根证书在"已信任列表"中
    → OK,这个站可信。

为什么必须"预先分发 CA 根证书"?

因为你不可能在连接时,再去问对方:"请告诉我应该信任谁。"

那会产生死循环:

  • 想下载 CA,需要先信任连接
  • 连接需要验证 CA

所以:

✔ CA 必须通过 安全渠道提前部署

✔ 以后所有连接,只需要:

"验证证书是否由这个 CA 签发就行了。"

总结

TLS 信任 = 信 CA,而不是信服务器本身。

因此,客户端要预装 CA 根证书,在 mTLS 中,服务端也必须预装。

CA 根证书来自:

  • Kubernetes:/etc/kubernetes/pki/ca.crt

  • 互联网:操作系统 / 浏览器的 CA 目录

问题十八

浏览器"沿着证书链一路追溯到根证书",是不是"不断用 CA 公钥去解密"?

浏览器验证证书链时:

不会去解密证书

✔️ 而是用上一级 CA 的公钥验证下一级证书的"签名"

证书链验证的是:"是不是由一条受信 CA 链一路签下来" ,不是 "用公钥把什么东西解开"

1️⃣ 证书里没有"加密内容",只有"签名"

假设网站证书是:

复制代码
www.example.com 证书

里面包含:

  • 网站的 公钥
  • 主题(Subject) = example.com
  • 有效期
  • 用途(Server Auth)
  • 签发者 Issuer = 中间 CA
  • CA 的签名

📌 关键点:证书本身不是加密文件,是 "公开信息 + 数字签名"

2️⃣ 那签名验证到底是做什么?

看一条证书链:

复制代码
Root CA  →  Intermediate CA  →  Server cert(网站)

浏览器做:


第一步:验证服务器证书

中间 CA 的公钥 去验证:

复制代码
Verify( intermediate_pubkey , server_cert_signature )

问的问题是:

"这个网站证书,

真的由中间 CA 用它的私钥签的吗?"


第二步:验证中间 CA 证书

根 CA 的公钥 验证:

复制代码
Verify( root_pubkey , intermediate_cert_signature )

问的问题是:

"中间 CA 自己是不是被根 CA 授权的?"


第三步(最后一步):根证书

根证书是自签名

复制代码
Verify( root_pubkey , root_signature )   ✔ 总能通过

问题变成:

"这个根 CA 是否在我的"受信列表"里?"

✔ 如果在 → 整条链可信

❌ 如果不在 → UNTRUSTED CERTIFICATE

浏览器只是不断"验章",不是不断"解密"。

就像:合同 → 县公章 → 市公章 → 省公章

最终看:省公章是不是国家承认的。

3️⃣ 那"解密"到底是谁干的?

阶段 用途 是否解密
证书链验证 认证对方身份 ❌ 不解密,只验签
TLS 握手 生成会话密钥 ✔️ 用到公钥算法(但不是解密证书)
真正通信 加密 HTTP 内容 ✔️ 对称加密(非常快)

证书本身 永远不会被"解密"

用一个直观比喻

证书链验证 = 核对"每一级公证是否由上一级授权"(数字签名验证)
TLS 加密 = 用钥匙锁箱子,双方都有钥匙(对称加密)

不同概念,不要混淆。

4️⃣ 实验验证

查看某站证书链:

bash 复制代码
echo | openssl s_client -connect www.google.com:443 -showcerts

然后逐个验证:

bash 复制代码
openssl verify -CAfile root.pem intermediate.pem server.pem

我们会发现:

每一步都是 verify(验证签名) ,而不是 decrypt(解密)

总结

1️⃣ 证书链验证不是解密,是逐级签名验证

2️⃣ 浏览器只信"在系统信任库里的根 CA"

3️⃣ 解密只发生在 TLS 握手和后续会话中(与证书验证分开)


问题十九

如果浏览器(或客户端)没有中间 CA 的公钥,那还怎么验证服务器证书?

答案是:

服务器会把中间 CA 证书"顺带一起发过来"。

浏览器通常 不需要提前保存中间 CA 的公钥

下面详细说明:

1️⃣ 证书链一般长这样

复制代码
Root CA   →   Intermediate CA   →   Server Cert

浏览器通常只预装 Root CA

✔ Root CA(可信)

❌ 不存储 Intermediate CA(数量太多)

2️⃣ 那浏览器怎么拿到"中间 CA 证书"?

服务器在 TLS 握手时,会把"中间证书"一起发回来。

这个阶段叫:

复制代码
Certificate message

服务器发送:

复制代码
[ server certificate,
  intermediate certificate,
  (可能还有更多)]

注意:这些中间证书不是秘密,是公开的。

3️⃣ 验证过程变成这样

浏览器拿到这些证书后:

① 验证:Server Cert 是否由 Intermediate 签发

复制代码
Verify( intermediate_pubkey , server_signature )

中间证书就在上一步握手消息里。

② 验证:Intermediate 是否由 Root CA 签发

浏览器看:

复制代码
Issuer = Root CA

然后在本地"受信根列表"中查:

✔ 找到 Root CA

✔ 取出它的 公钥

用来验证:

复制代码
Verify( root_pubkey , intermediate_signature )

③ 确认 Root CA 是系统信任的终点

如果 Root CA 存在于信任库:整条链"闭合" → 证书可信。

那如果服务器没发中间证书怎么办?

这是现实中经常出现的问题:

  • 有的运维忘了配置完整证书链
  • 服务器只发了"Server Cert",没发 Intermediate

结果:

✔ Root CA 仍然被系统信任

❌ 但中间证书缺失

浏览器会报错:

复制代码
certificate chain incomplete
unable to get local issuer certificate

Chrome/Wget/cURL 等工具的报错常见:

复制代码
SSL certificate problem: unable to get local issuer certificate

👉 解决方法:在服务器上正确配置"证书链(bundle)",包含:Server Cert + Intermediate Cert。

Kubernetes 里对应的做法

Kubernetes 内部组件中:

  • 一般 没有中间 CA

  • 大多是:

    Root CA(ca.crt,自签) → 各组件证书

如果引入企业 CA / 多级 CA 架构:

  • kube-apiserver/kubelet 等组件必须配置 完整证书链文件

例如:

复制代码
--tls-cert-file=/etc/kubernetes/pki/apiserver.crt   (可能包含中间证书)
--tls-private-key-file=/etc/kubernetes/pki/apiserver.key

再强调一句避免误解:

证书链不是"一级一级解密"

✔️ 是一张"一级一级验签的授权关系图"

缺少中间证书不是加密失败,而是 缺少签署证明

总结

问:

如果没有"中间 CA 的公钥",还能验证吗?

答:

✔️ 服务器会把中间证书一起发给你

❌ 你不需要预装它

✔️ 浏览器用服务器发来的 中间证书 + 本地 Root CA

✓ 完成整条链的验证


问题二十

"中间证书就在上一步握手消息里"到底是什么意思?它在握手的哪一步?谁发的?怎么发的?

整体 TLS 握手流程(简化版)

(不区分 1.2/1.3 的细节)

复制代码
ClientHello  →  
              ←  ServerHello  
              ←  Certificate
              ←  (CertificateRequest, 可选)
              ←  ServerHelloDone
ClientCertificate (可选)  →
CertificateVerify (可选) →
Finished →
              ← Finished

中间证书在哪里?

👉 就在上面的「Certificate」消息中。

服务器发送:

复制代码
Certificate message

内容其实是一个 证书列表(chain)

复制代码
[
  Server certificate,
  Intermediate CA certificate,
  (可能还有更多中间 CA),
]

📌 这一整串由"服务器发给客户端"

展开来看 ------ Server 在做什么?

当客户端说:

复制代码
我要 HTTPS

服务器这样回应:

✉️ Certificate(证书链)

服务器打包:

复制代码
server_cert.pem
intermediate_CA.pem
(optional another intermediate)

然后一次性发给浏览器。

所以,"中间证书在握手消息里" 就是说:它是随着 TLS 握手数据包一起,从服务器传回来的。

验证

运行:

bash 复制代码
echo | openssl s_client -connect www.google.com:443 -showcerts

我们会看到类似输出:

复制代码
Certificate chain
 0 s: CN=www.google.com
   i: CN=GTS CA 1O1
-----BEGIN CERTIFICATE-----
(服务器证书)
-----END CERTIFICATE-----

 1 s: CN=GTS CA 1O1
   i: CN=GTS Root R1
-----BEGIN CERTIFICATE-----
(中间 CA)
-----END CERTIFICATE-----

解释:

  • 0 = Server cert
  • 1 = Intermediate cert

👉 它们就是 服务器在握手时一次性发给你的。

为什么服务器必须带上"中间证书"?

因为客户端通常:

  • 只内置 Root CA
  • 没有中间 CA

如果服务器不发,浏览器就没法验证"谁签了服务器证书"。

它会直接报错:

复制代码
unable to get local issuer certificate

总结

当浏览器与网站握手时,服务器在 Certificate 握手消息 中主动把自己的证书、中间 CA 证书(甚至多级中间 CA)一起打包发送给浏览器。

所以我们说:中间证书"就在握手消息里"。

相关推荐
运维开发故事3 天前
基于 Arthas 的多集群在线诊断系统设计与实现
kubernetes
Patrick_Wilson5 天前
从「改个端口」到 502:Next.js on k8s 的容器端口、Service 映射与 env 覆盖
docker·kubernetes·next.js
探索云原生5 天前
K8s 1.36 这个 GA 特性,把 initContainer 拉模型的 hack 干掉了
ai·云原生·kubernetes
云恒要逆袭5 天前
运行你的第一个Docker容器
后端·docker·容器
Java之美6 天前
一次k8s升级引发的DevicePlugin注册失败
云原生·kubernetes
程序员老赵7 天前
10 分钟部署 OpenCode:Docker 一键安装,浏览器打开就能用 AI 写代码(附完整命令与排错)
docker·容器·ai编程
武子康10 天前
调查研究-183 Apple container:Mac 上用轻量 VM 跑 Linux 容器,Swift 会改写本地容器体验吗?
docker·容器·apple
2601_9618752413 天前
决战申论100题2026|最新|范文
linux·容器·centos·debian·ssh·fabric·vagrant
java_cj13 天前
深入kube-apiserver认证机制:从Bearer Token到mTLS的完整认证链解析
linux·运维·服务器·云原生·容器·kubernetes