Kubernetes集群的安全体系完全建立在PKI证书之上,apiserver、etcd、kubelet、controller-manager等组件之间的双向认证都依赖证书完成。平时用kubeadm部署时证书会自动生成,一年一换,看似省心,但当你需要延长证书有效期、修改 apiserver 的SAN列表、多集群复用同一CA,或者排障时发现证书对不上,就必须理解证书的生成原理并掌握手动签发方法。本文用cfssl工具完整演示从零创建CA到签发各组件证书的全过程,并附上openssl备选方案和踩坑经验。

一、先弄清楚Kubernetes到底需要哪些证书
Kubernetes的证书体系比很多人想象的复杂,一个生产级集群至少需要十来张证书,分别服务于不同的组件和通信路径。动手之前必须先梳理清楚谁持有证书、谁验证证书,否则签出来的证书放错位置,排错会非常痛苦。
首先是集群的根证书,也就是CA证书及其私钥。CA是整个信任链的起点,所有组件证书都由它签发,apiserver、kubelet、etcd各角色默认会复用同一个CA,也可以为etcd单独建一个CA实现隔离。CA私钥是集群中最高机密,泄露意味着任何人都能签发合法证书,务必妥善保管。
其次是apiserver的服务端证书。这张证书最关键,它的SAN字段必须包含apiserver对外提供服务的所有可能被访问到的地址,包括服务IP(默认10.96.0.1)、Kubernetes短域名、完整域名、Master节点IP、Master主机名、负载均衡地址等。任何一个访问地址没写进SAN,客户端校验就会报证书主体不匹配的错误。除了服务端证书,apiserver还需要一张客户端证书用于访问etcd,一张用于访问kubelet的客户端证书。etcd如果是独立部署,还需要自己的服务端证书和对等证书。kubelet侧则包含apiserver访问kubelet用的客户端证书,以及kubelet自己的服务端证书。
另外controller-manager和scheduler各需要一张客户端证书连接apiserver,集群管理员用的kubectl以及ServiceAccount自动挂载的证书也都属于这套体系。理清这些关系后你会发现,手动生成证书本质上就是围绕一个CA,按角色签发对应用途的证书。
二、用cfssl创建CA并签发组件证书
cfssl是CloudFlare开源的证书工具,相比openssl命令行,它的JSON配置更易维护、可重复执行,是社区里手动管理Kubernetes证书的主流选择。先下载安装cfssl、cfssljson两个工具,然后将它们移动到PATH目录下即可。
第一步创建CA。编写ca-config.json定义签名策略,编写ca-csr.json定义CA自身信息:
cat > ca-config.json <<EOF
{
"signing": {
"default": {
"expiry": "87600h"
},
"profiles": {
"server": {
"expiry": "87600h",
"usages": ["signing", "key encipherment", "server auth"]
},
"client": {
"expiry": "87600h",
"usages": ["signing", "key encipherment", "client auth"]
},
"peer": {
"expiry": "87600h",
"usages": ["signing", "key encipherment", "server auth", "client auth"]
}
}
}
}
EOF
cat > ca-csr.json <<EOF
{
"CN": "kubernetes",
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "CN",
"ST": "Beijing",
"L": "Beijing",
"O": "k8s",
"OU": "system"
}
]
}
EOF
cfssl gencert -initca ca-csr.json | cfssljson -bare ca执行后得到ca.pem和ca-key.pem,分别对应CA证书和私钥。这里的87600h是十年有效期,CA有效期建议设置得比组件证书长很多,避免CA先过期导致整个信任链失效。
第二步签发apiserver证书,这是最容易出错的一步,核心在于hosts字段要写全。编写kubernetes-csr.json:
cat > kubernetes-csr.json <<EOF
{
"CN": "kubernetes",
"hosts": [
"127.0.0.1",
"10.96.0.1",
"169.169.0.1",
"kubernetes",
"kubernetes.default",
"kubernetes.default.svc",
"kubernetes.default.svc.cluster",
"kubernetes.default.svc.cluster.local",
"192.168.10.10",
"master01",
"master01.k8s.local",
"192.168.10.100"
],
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "CN",
"ST": "Beijing",
"L": "Beijing",
"O": "k8s",
"OU": "system"
}
]
}
EOF
cfssl gencert -ca=ca.pem -ca-key=ca-key.pem \
-config=ca-config.json -profile=server \
kubernetes-csr.json | cfssljson -bare kuberneteshosts列表中10.96.0.1是service网段的第一个IP,apiserver的ClusterIP固定用它;127.0.0.1覆盖本机访问;后面依次是节点IP、主机名和负载均衡VIP。如果后续扩容Master或者更换LB地址,记得重新签发证书补充SAN,否则kubectl会报证书校验失败。
第三步签发客户端和节点证书。admin用户的客户端证书有一个特殊细节:O字段必须写成system:masters,apiserver通过O字段做RBAC用户组识别。kubelet的证书CN要采用system:node:节点名 格式,这样它才能被内建的Node授权器认可:
cat > admin-csr.json <<EOF
{
"CN": "admin",
"hosts": [],
"key": { "algo": "rsa", "size": 2048 },
"names": [
{ "C": "CN", "ST": "Beijing", "L": "Beijing", "O": "system:masters", "OU": "system" }
]
}
EOF
cfssl gencert -ca=ca.pem -ca-key=ca-key.pem \
-config=ca-config.json -profile=client \
admin-csr.json | cfssljson -bare admin
cat > kubelet-csr.json <<EOF
{
"CN": "system:node:node01",
"hosts": [],
"key": { "algo": "rsa", "size": 2048 },
"names": [
{ "C": "CN", "ST": "Beijing", "L": "Beijing", "O": "system:nodes", "OU": "system" }
]
}
EOFetcd集群的证书建议用peer profile签发,因为etcd节点之间既要作为服务端也要作为客户端,双向认证场景下一张证书同时承担两种角色。每张证书签发完成后,可以用openssl x509 -in kubernetes.pem -text -noout查看SAN和有效期,确认无误再分发到各节点。
三、openssl备选方案与关键参数
如果不方便安装cfssl,openssl同样可以完成全部工作,只是命令较长且容易出错。核心命令是生成私钥、构造CSR、用CA签发三步:
# 生成apiserver私钥 openssl genrsa -out apiserver-key.pem 2048 # 生成CSR,重点在extfile中定义SAN openssl req -new -key apiserver-key.pem \ -subj "/CN=kubernetes/O=k8s" \ -out apiserver.csr cat > apiserver-ext.cnf <<EOF basicConstraints=CA:FALSE keyUsage = digitalSignature, keyEncipherment extendedKeyUsage = serverAuth subjectAltName = IP.1=127.0.0.1,IP.2=10.96.0.1,IP.3=192.168.10.10,DNS.1=kubernetes,DNS.2=kubernetes.default,DNS.3=master01 EOF # 用CA签发 openssl x509 -req -in apiserver.csr \ -CA ca.pem -CAkey ca-key.pem -CAcreateserial \ -out apiserver.pem -days 3650 \ -extfile apiserver-ext.cnf
openssl方案最大的坑是忘写-extfile参数,导致签出来的证书没有SAN字段。现代客户端普遍强制校验SAN而忽略CN,所以用openssl签发Kubernetes证书时,subjectAltName绝对不能省,签发后务必检查一遍。
四、常见坑点与避坑建议
第一类坑是SAN不全。表现为kubectl偶发性报证书错误,直接IP访问没问题但通过LB访问失败,或者Pod内访问ClusterIP失败。原因都是证书没有覆盖对应的访问路径。对策是在签发前把所有可能的地址列全,宁可多写不要漏写,并预留未来扩容的地址。
第二类坑是证书有效期。kubeadm默认一年,到期后集群所有组件同时失联,恢复起来相当狼狈。建议自签证书时把有效期设为三到十年,并建立监控,在到期前三十天告警。检查证书有效期的命令是kubeadm certs check-expiration,或者用openssl逐张查看。
第三类坑是CN和O字段不符合Kubernetes约定。apiserver内置的授权器对证书里的组织信息有硬编码要求,kubelet证书CN必须是system:node:加上节点名且与kubelet配置中的节点名完全一致,admin证书的O必须是system:masters。这些字段写错,证书本身校验能通过,但请求会被授权层拒绝,报权限不足的问题,排查方向容易跑偏。
第四类是文件权限和分发问题。CA私钥只能保存在Master节点,权限建议600;各组件私钥权限也要收紧,放在kubelet可读的目录并防止普通用户读取。证书更新后需要重启对应组件才能加载新证书,apiserver可以借助--tls-cert-file等参数指定路径,kubelet则可以开启轮转机制实现自动续期。最后建议把所有CSR的JSON文件纳入版本管理,下次换证时直接复用,既保证SAN信息不丢,也大幅降低操作失误的概率。
Kubernetes证书手动生成证书cfssl修改时间:2026-09-02 13:32:55