从 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)一起打包发送给浏览器。

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

相关推荐
Swift社区2 小时前
Docker Compose 一键部署前后端分离项目
运维·docker·容器
心理之旅13 小时前
高校文献检索系统
运维·服务器·容器
大佐不会说日语~14 小时前
使用Docker Compose 部署时网络冲突问题排查与解决
运维·网络·spring boot·docker·容器
曾几何时`17 小时前
Docker容器化部署编译运行模块
运维·docker·容器
直饮水观察哨19 小时前
商用净水器亲测对比,哪个更专业?
容器
塔克拉玛攻城狮19 小时前
最新!银河麒麟v11 kubeadm部署k8s v1.35.0高可用集群
kubernetes·银河麒麟
Suchadar20 小时前
Docker基础命令(二)——数据卷管理端口映射与容器互联
运维·docker·容器
firstacui20 小时前
Docker容器网络管理与容器数据卷管理
运维·docker·容器
王锋(oxwangfeng)20 小时前
Apache Flink 在 Kubernetes 上的高效部署与优化实践
flink·kubernetes·apache