Kubernetes 集群 TLS 证书与密钥创建实战:基于 CFSSL 的 CA 与组件证书生成全指南
【免费下载链接】kubernetes-handbookKubernetes 架构与生态:从云原生到 AI 原生基础设施的构建指南项目地址: https://gitcode.com/gh_mirrors/ku/kubernetes-handbook
本指南完整讲解如何在 Kubernetes 集群安装的第一步——使用 CloudFlare 的 PKI 工具集 CFSSL 生成 Certificate Authority (CA) 及各组件(etcd、kube-apiserver、kubelet、kube-proxy、kubectl、kube-controller-manager)所需的 TLS 证书与私钥。读者将掌握 CA 配置(ca-config.json)、证书签名请求(CSR)编写、cfssl gencert签名流程、openssl/cfssl-certinfo校验方法,以及证书与RBAC认证授权模型的对应关系,为后续在 CentOS 上手工部署 Kubernetes 集群(详见 install-kubernetes-on-centos)打下坚实基础。
在动手执行之前,建议先通读以下三篇前置文档,理解 TLS 在整个集群安全体系中的位置:
- 管理集群中的 TLS:讲解集群根 CA 的信任模型、
certificates.k8s.io证书签名请求 API 与签发流程,以及集群管理员建议(--cluster-signing-cert-file/--cluster-signing-key-file); - kubelet 的认证授权:说明如何通过 X509 客户端证书认证访问 kubelet 的 HTTPS 端点;
- TLS bootstrap:介绍 kubelet 如何利用 bootstrap token 与
csrapproving控制器自动申请客户端证书。
注意事项(务必先读):这一步是安装配置 Kubernetes 的所有步骤中最容易出错、也最难排查问题的一步,而它恰恰又是第一步。万事开头难,不要因为这点困难就望而却步。如果您足够有信心,能够在完全不了解自己在做什么的情况下成功地完成这一步的配置,那么可以跳过上面的几篇文章直接进行下面的操作。
Kubernetes 系统的各组件需要使用 TLS 证书对通信进行加密,本文档使用 CloudFlare 的 PKI 工具集 cfssl 来生成 Certificate Authority (CA) 和各类证书。
待生成的证书清单与组件使用对照
生成的 CA 证书和私钥文件如下:
ca-key.pemca.pemkubernetes-key.pemkubernetes.pemkube-proxy.pemkube-proxy-key.pemadmin.pemadmin-key.pem
使用证书的组件如下:
| 组件 | 使用的证书文件 |
|---|---|
| etcd | ca.pem、kubernetes-key.pem、kubernetes.pem |
| kube-apiserver | ca.pem、kubernetes-key.pem、kubernetes.pem |
| kubelet | ca.pem |
| kube-proxy | ca.pem、kube-proxy-key.pem、kube-proxy.pem |
| kubectl | ca.pem、admin-key.pem、admin.pem |
| kube-controller-manager | ca-key.pem、ca.pem |
操作范围说明:以下所有操作都在 master 节点(即
172.20.0.113这台主机)上执行。证书只需要创建一次即可,以后在向集群中添加新节点时,只要将/etc/kubernetes/目录下的证书拷贝到新节点上即可。
这些证书在仓库的实际部署配置中均有印证,例如 etc/kubernetes/apiserver 中KUBE_API_ARGS显式引用了--tls-cert-file=/etc/kubernetes/ssl/kubernetes.pem --tls-private-key-file=/etc/kubernetes/ssl/kubernetes-key.pem --client-ca-file=/etc/kubernetes/ssl/ca.pem --etcd-cafile=/etc/kubernetes/ssl/ca.pem --etcd-certfile=/etc/kubernetes/ssl/kubernetes.pem --etcd-keyfile=/etc/kubernetes/ssl/kubernetes-key.pem,与上表完全对应。
安装 CFSSL
CFSSL 是 CloudFlare 开源的 PKI/TLS 工具包,提供了cfssl(签发)、cfssljson(将 JSON 输出转为 PEM 文件)、cfssl-certinfo(查看证书详情)等子命令。
方式一:直接使用二进制源码包安装
wget https://pkg.cfssl.org/R1.2/cfssl_linux-amd64 chmod +x cfssl_linux-amd64 mv cfssl_linux-amd64 /usr/local/bin/cfssl wget https://pkg.cfssl.org/R1.2/cfssljson_linux-amd64 chmod +x cfssljson_linux-amd64 mv cfssljson_linux-amd64 /usr/local/bin/cfssljson wget https://pkg.cfssl.org/R1.2/cfssl-certinfo_linux-amd64 chmod +x cfssl-certinfo_linux-amd64 mv cfssl-certinfo_linux-amd64 /usr/local/bin/cfssl-certinfo export PATH=/usr/local/bin:$PATH方式二:使用 go 命令安装
如果系统中已安装 Go(本文场景为 Go 1.7.5),使用以下命令安装更快捷:
$ go get -u github.com/cloudflare/cfssl/cmd/... $ echo $GOPATH /usr/local $ ls /usr/local/bin/cfssl* cfssl cfssl-bundle cfssl-certinfo cfssljson cfssl-newkey cfssl-scan在$GOPATH/bin目录下会得到以cfssl开头的几个命令。
注意:后续操作中,凡是
cat命令写入的文件,如果不存在需要手工创建。
创建 CA(Certificate Authority)
CA 是整个集群信任链的根,后续所有组件证书都由它签发。因此 CA 的私钥ca-key.pem需要妥善保管——从仓库配置看,kube-controller-manager需要它来为 kubelet 的 bootstrap 请求签发证书。
创建 CA 配置文件
mkdir /root/ssl cd /root/ssl cfssl print-defaults config > config.json cfssl print-defaults csr > csr.json # 根据config.json文件的格式创建如下的ca-config.json文件 # 过期时间设置成了 87600h cat > ca-config.json <<EOF { "signing": { "default": { "expiry": "87600h" }, "profiles": { "kubernetes": { "usages": [ "signing", "key encipherment", "server auth", "client auth" ], "expiry": "87600h" } } } } EOF字段说明
ca-config.json:可以定义多个 profiles(配置模板),分别指定不同的过期时间、使用场景等参数;后续在签名证书时使用某个 profile。本例定义了一个名为kubernetes的 profile,后续所有组件证书签名都复用该 profile;signing:表示该证书可用于签名其它证书,生成的ca.pem证书中CA=TRUE;server auth:表示 client 可以用该 CA 对 server 提供的证书进行验证(即验证服务端身份);client auth:表示 server 可以用该 CA 对 client 提供的证书进行验证(即验证客户端身份);expiry: 87600h:证书有效期,87600h即 10 年(365 × 24 × 10)。生产环境可根据安全策略适当缩短,但 CA 根证书的有效期应长于所有由它签发的子证书。
创建 CA 证书签名请求
创建ca-csr.json文件,内容如下:
{ "CN": "kubernetes", "key": { "algo": "rsa", "size": 2048 }, "names": [ { "C": "CN", "ST": "BeiJing", "L": "BeiJing", "O": "k8s", "OU": "System" } ], "ca": { "expiry": "87600h" } }字段说明
CN(Common Name):kube-apiserver 从证书中提取该字段作为请求的用户名(User Name);浏览器则使用该字段验证网站是否合法。这里 CA 的 CN 设为kubernetes;O(Organization):kube-apiserver 从证书中提取该字段作为请求用户所属的组(Group);key.algo / key.size:密钥算法与长度,本例为 RSA 2048 位,是当前兼容性与安全性平衡的默认选择;names:证书主体信息,包含国家(C)、省(ST)、市(L)、组织(O)、组织单元(OU)。
关于 CN 与 O 如何映射为 Kubernetes 用户与组,可进一步阅读 Kubernetes 中的用户与身份认证授权 中 "X509 Client Certs" 一节:API server 通过--client-ca-file=SOMEFILE启用客户端证书认证,客户端证书验证通过后,使用 subject 的 CN 作为请求的用户名,使用证书的 organization 字段指示用户的组成员身份。
生成 CA 证书和私钥
$ cfssl gencert -initca ca-csr.json | cfssljson -bare ca $ ls ca* ca-config.json ca.csr ca-csr.json ca-key.pem ca.pem-initca表示以该 CSR 初始化一个自签名的根 CA;cfssljson -bare ca将输出写入以ca为前缀的文件,得到ca.pem(证书)与ca-key.pem(私钥),同时保留ca.csr以便审计。
创建 kubernetes 证书
kubernetes.pem是本集群中用途最广的一张证书:它同时被kube-apiserver与etcd使用,既充当 HTTPS 服务端证书,又充当访问 etcd 的客户端证书。仓库配置中,etc/kubernetes/apiserver 的--tls-cert-file、--etcd-certfile都指向它,systemd/kube-apiserver.service 与其一致。
创建证书签名请求文件
创建kubernetes-csr.json:
{ "CN": "kubernetes", "hosts": [ "127.0.0.1", "172.20.0.112", "172.20.0.113", "172.20.0.114", "172.20.0.115", "10.254.0.1", "kubernetes", "kubernetes.default", "kubernetes.default.svc", "kubernetes.default.svc.cluster", "kubernetes.default.svc.cluster.local" ], "key": { "algo": "rsa", "size": 2048 }, "names": [ { "C": "CN", "ST": "BeiJing", "L": "BeiJing", "O": "k8s", "OU": "System" } ] }关键点:hosts字段
- 如果
hosts字段不为空,则需要指定授权使用该证书的IP 或域名列表。由于该证书后续被etcd集群和kubernetes master集群共同使用,所以这里分别指定了:etcd集群、kubernetes master集群的主机 IP(172.20.0.112~172.20.0.115,其中172.20.0.113为 master 节点,其余为集群其他节点);kubernetes服务的服务 IP,一般是kube-apiserver指定的service-cluster-ip-range网段的第一个 IP,如10.254.0.1(仓库 etc/kubernetes/apiserver 中--service-cluster-ip-range=10.254.0.0/16与该值对应);kubernetes、kubernetes.default、kubernetes.default.svc、kubernetes.default.svc.cluster、kubernetes.default.svc.cluster.local等内置 DNS 名称。
- 这是最小化安装的 Kubernetes 集群,包括一个私有镜像仓库、一个三节点的 Kubernetes 集群;以上物理节点的 IP 也可以更换为主机名。
hosts会写入证书的X509v3 Subject Alternative Name(SAN)扩展,客户端在 TLS 握手时会校验对方的主机名/IP 是否落在 SAN 列表中,因此漏写任何将被访问的地址都会导致握手失败——这是最常见也最难排查的问题之一。
生成 kubernetes 证书和私钥
$ cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes kubernetes-csr.json | cfssljson -bare kubernetes $ ls kubernetes* kubernetes.csr kubernetes-csr.json kubernetes-key.pem kubernetes.pem或者直接在命令行上指定相关参数(hosts 通过-hostname传入):
echo '{"CN":"kubernetes","hosts":[""],"key":{"algo":"rsa","size":2048}}' | cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes -hostname="127.0.0.1,172.20.0.112,172.20.0.113,172.20.0.114,172.20.0.115,kubernetes,kubernetes.default" - | cfssljson -bare kubernetes签名命令参数拆解
-ca=ca.pem -ca-key=ca-key.pem:指定签发者(我们的根 CA)及 CA 私钥;-config=ca-config.json:指定配置文件;-profile=kubernetes:选用ca-config.json中名为kubernetes的 profile,即证书用途为signing + key encipherment + server auth + client auth,有效期 87600h;-hostname=...:命令行方式直接指定 SAN 列表,适合动态拼接 IP/域名;cfssljson -bare kubernetes:输出kubernetes.pem与kubernetes-key.pem。
创建 admin 证书
admin证书用于管理员(kubectl)身份认证,其特殊之处在于 Organization(O)字段为system:masters,这与 Kubernetes 预置的 RBAC 绑定直接关联。
创建证书签名请求文件
创建admin-csr.json:
{ "CN": "admin", "hosts": [], "key": { "algo": "rsa", "size": 2048 }, "names": [ { "C": "CN", "ST": "BeiJing", "L": "BeiJing", "O": "system:masters", "OU": "System" } ] }为什么 admin 证书的 O 是system:masters?
- 后续
kube-apiserver使用RBAC对客户端(如kubelet、kube-proxy、Pod)请求进行授权; kube-apiserver预定义了一些RBAC使用的RoleBindings,如cluster-admin将 Groupsystem:masters与 Rolecluster-admin绑定,该 Role 授予了调用kube-apiserver的所有 API的权限;- O 指定该证书的 Group 为
system:masters。kubelet使用该证书访问kube-apiserver时,由于证书被 CA 签名,所以认证通过;同时由于证书用户组为经过预授权的system:masters,所以被授予访问所有 API 的权限; hosts为空数组表示该证书不绑定任何 SAN,因为它只用作客户端身份凭证,不用于服务端 TLS 握手。
注意:这个 admin 证书,是将来生成管理员用的 kubeconfig 配置文件用的。现在我们一般建议使用 RBAC 来对 Kubernetes 进行角色权限控制。Kubernetes 将证书中的 CN 字段作为 User、O 字段作为 Group(具体参考 Kubernetes 中的用户与身份认证授权 中 X509 Client Certs 一段)。
验证 cluster-admin 绑定
搭建完 Kubernetes 集群后,可以通过以下命令查看到clusterrolebinding cluster-admin的 subjects 的 kind 是 Group、name 是system:masters,roleRef对象是ClusterRole cluster-admin。意思是:凡是system:mastersGroup 的 user 或者 serviceAccount 都拥有cluster-admin的角色。因此我们使用 kubectl 命令时,才拥有整个集群的管理权限。
$ kubectl get clusterrolebinding cluster-admin -o yaml apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: annotations: rbac.authorization.kubernetes.io/autoupdate: "true" creationTimestamp: 2017-04-11T11:20:42Z labels: kubernetes.io/bootstrapping: rbac-defaults name: cluster-admin resourceVersion: "52" selfLink: /apis/rbac.authorization.k8s.io/v1/clusterrolebindings/cluster-admin uid: e61b97b2-1ea8-11e7-8cd7-f4e9d49f8ed0 roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: cluster-admin subjects: - apiGroup: rbac.authorization.k8s.io kind: Group name: system:masters生成 admin 证书和私钥
$ cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes admin-csr.json | cfssljson -bare admin $ ls admin* admin.csr admin-csr.json admin-key.pem admin.pem创建 kube-proxy 证书
kube-proxy以独立身份访问 kube-apiserver 的 Proxy 相关 API,因此其证书 CN 必须设置为 Kubernetes 预定义的system:kube-proxy,否则无法匹配预置的 RBAC 绑定。
创建证书签名请求文件
创建kube-proxy-csr.json:
{ "CN": "system:kube-proxy", "hosts": [], "key": { "algo": "rsa", "size": 2048 }, "names": [ { "C": "CN", "ST": "BeiJing", "L": "BeiJing", "O": "k8s", "OU": "System" } ] }字段说明
- CN 指定该证书的 User 为
system:kube-proxy; kube-apiserver预定义的 RoleBindingsystem:node-proxier将 Usersystem:kube-proxy与 Rolesystem:node-proxier绑定,该 Role 授予了调用kube-apiserverProxy 相关 API 的权限。
生成 kube-proxy 客户端证书和私钥
$ cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes kube-proxy-csr.json | cfssljson -bare kube-proxy $ ls kube-proxy* kube-proxy.csr kube-proxy-csr.json kube-proxy-key.pem kube-proxy.pem生成的kube-proxy.pem、kube-proxy-key.pem与仓库 etc/kubernetes/proxy 中KUBE_PROXY_ARGS="--kubeconfig=/etc/kubernetes/kube-proxy.kubeconfig"配合使用,由 kubeconfig 文件引用该证书对 apiserver 做客户端认证。
校验证书
签发完成后务必逐一校验证书内容,确认 Issuer、Subject、SAN、Key Usage 等字段与 CSR 及 profile 配置一致。下面以 Kubernetes 证书为例。
使用openssl命令
$ openssl x509 -noout -text -in kubernetes.pem ... Signature Algorithm: sha256WithRSAEncryption Issuer: C=CN, ST=BeiJing, L=BeiJing, O=k8s, OU=System, CN=Kubernetes Validity Not Before: Apr 5 05:36:00 2017 GMT Not After : Apr 5 05:36:00 2018 GMT Subject: C=CN, ST=BeiJing, L=BeiJing, O=k8s, OU=System, CN=kubernetes ... X509v3 extensions: X509v3 Key Usage: critical Digital Signature, Key Encipherment X509v3 Extended Key Usage: TLS Web Server Authentication, TLS Web Client Authentication X509v3 Basic Constraints: critical CA:FALSE X509v3 Subject Key Identifier: DD:52:04:43:10:13:A9:29:24:17:3A:0E:D7:14:DB:36:F8:6C:E0:E0 X509v3 Authority Key Identifier: keyid:44:04:3B:60:BD:69:78:14:68:AF:A0:41:13:F6:17:07:13:63:58:CD X509v3 Subject Alternative Name: DNS:kubernetes, DNS:kubernetes.default, DNS:kubernetes.default.svc, DNS:kubernetes.default.svc.cluster, DNS:kubernetes.default.svc.cluster.local, IP Address:127.0.0.1, IP Address:172.20.0.112, IP Address:172.20.0.113, IP Address:172.20.0.114, IP Address:172.20.0.115, IP Address:10.254.0.1 ...校验清单
- 确认
Issuer字段的内容和ca-csr.json一致(即 CA 的 CN 为Kubernetes); - 确认
Subject字段的内容和kubernetes-csr.json一致(CN 为kubernetes); - 确认
X509v3 Subject Alternative Name字段的内容和kubernetes-csr.json一致(所有 hosts 都进入了 SAN); - 确认
X509v3 Key Usage、Extended Key Usage字段的内容和ca-config.json中kubernetesprofile 一致(Digital Signature, Key Encipherment+TLS Web Server Authentication, TLS Web Client Authentication); X509v3 Basic Constraints: CA:FALSE确认该证书是叶子证书而非 CA。
使用cfssl-certinfo命令
$ cfssl-certinfo -cert kubernetes.pem ... { "subject": { "common_name": "kubernetes", "country": "CN", "organization": "k8s", "organizational_unit": "System", "locality": "BeiJing", "province": "BeiJing", "names": [ "CN", "BeiJing", "BeiJing", "k8s", "System", "kubernetes" ] }, "issuer": { "common_name": "Kubernetes", "country": "CN", "organization": "k8s", "organizational_unit": "System", "locality": "BeiJing", "province": "BeiJing", "names": [ "CN", "BeiJing", "BeiJing", "k8s", "System", "Kubernetes" ] }, "serial_number": "174360492872423263473151971632292895707129022309", "sans": [ "kubernetes", "kubernetes.default", "kubernetes.default.svc", "kubernetes.default.svc.cluster", "kubernetes.default.svc.cluster.local", "127.0.0.1", "10.64.3.7", "10.254.0.1" ], "not_before": "2017-04-05T05:36:00Z", "not_after": "2018-04-05T05:36:00Z", "sigalg": "SHA256WithRSA", ...cfssl-certinfo以结构化 JSON 输出同样的信息,便于脚本化校验;注意实际集群若添加了节点(如示例中的10.64.3.7),SAN 列表会相应增加。
分发证书
将生成的证书和私钥文件(后缀名为.pem)拷贝到所有机器的/etc/kubernetes/ssl目录下备用:
mkdir -p /etc/kubernetes/ssl cp *.pem /etc/kubernetes/ssl分发完成后,各组件通过配置引用这些证书。仓库中的实际部署配置可作为核对依据:
- kube-apiserver:etc/kubernetes/apiserver 中
KUBE_API_ARGS通过--tls-cert-file=/etc/kubernetes/ssl/kubernetes.pem、--tls-private-key-file=/etc/kubernetes/ssl/kubernetes-key.pem、--client-ca-file=/etc/kubernetes/ssl/ca.pem启用 TLS 与客户端证书认证,并通过--etcd-cafile、--etcd-certfile、--etcd-keyfile以kubernetes证书身份访问 etcd; - kube-controller-manager:etc/kubernetes/controller-manager 中通过
--cluster-signing-cert-file=/etc/kubernetes/ssl/ca.pem与--cluster-signing-key-file=/etc/kubernetes/ssl/ca-key.pem为 kubelet 的 TLS bootstrap 请求签发证书,并通过--root-ca-file向 API 服务器提供根 CA; - kubelet:etc/kubernetes/kubelet 中
--cert-dir=/etc/kubernetes/ssl指定证书目录,配合--experimental-bootstrap-kubeconfig实现 kubelet 证书引导(详见 TLS bootstrap); - kube-proxy:etc/kubernetes/proxy 中通过
--kubeconfig=/etc/kubernetes/kube-proxy.kubeconfig引用kube-proxy.pem与kube-proxy-key.pem; - etcd:etc/etcd/etcd.conf 中
ETCD_LISTEN_CLIENT_URLS/ETCD_LISTEN_PEER_URLS均为https://协议,对应[security]段可配置ETCD_CERT_FILE、ETCD_KEY_FILE、ETCD_TRUSTED_CA_FILE、ETCD_CLIENT_CERT_AUTH等,与上表"etcd 使用 ca.pem、kubernetes-key.pem、kubernetes.pem"的约定一致。
进阶:运行期证书签发与 CSR API
除了一次性手工签发,Kubernetes 还提供运行期的证书签名请求 API(certificates.k8s.io),供 Pod 内应用按需申请证书,详见 管理集群中的 TLS。其流程为:cfssl genkey生成私钥与 CSR → 将 base64 编码的 CSR 提交为CertificateSigningRequest对象 → 管理员用kubectl certificate approve批准 → 通过kubectl get csr ... -o jsonpath='{.status.certificate}' | base64 -d下载签发后的证书。生产环境建议为 Kubernetes 生成专用 CA,并妥善管理 CA 私钥的生命周期。
参考
- 管理集群中的 TLS:集群根 CA 信任模型与 CSR API 使用详解;
- kubelet 的认证授权:kubelet HTTPS 端点的认证与授权机制;
- TLS bootstrap:kubelet 客户端证书引导的完整配置;
- Kubernetes 中的用户与身份认证授权:X509 客户端证书中 CN/O 字段与 User/Group 的映射关系;
- 生成自签名证书(CoreOS);
- 客户端证书与服务器证书(Microsoft)。
【免费下载链接】kubernetes-handbookKubernetes 架构与生态:从云原生到 AI 原生基础设施的构建指南项目地址: https://gitcode.com/gh_mirrors/ku/kubernetes-handbook
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考