谈谈kubernetes 证书认证那些事儿

Overview

kubernetes各个组件都是加密通信的, 那么都有哪些证书、各个证书怎么交互、这些证书什么时候过期,这个就变得至关重要; 本文引用了一些其他网络内容(均已附上原文链接),并适当补充完善,用于让新手完善熟悉kubernetes证书体系(如有侵权联系邮箱可以删除)。

一、数字证书原理

1.1 传统非对称加密

1message --> (公钥加密) --> || 传输 || --> (私钥解密) --> message

注意:
1.这里与数字证书认证相反,是公钥加密私钥解密
2.公钥私钥需要是一个秘钥对

1.2 哈希函数

1message --> H(message) --> Hash message

处理加入一个随机数,然后得出结果(加盐); 可以有效缓解在输入值是一个有效的集合,哈希值也是固定长度被别人‘试’出来的几率

1message --> H(R|message) --> Hash message

1.3 数字证书

1.3.1 数字签名

数字签名;把数据根据私钥/哈希进行加密,然后必须要对应的公钥来进行解密认证才能确保数据安全。前半句的加密过程就叫做 '数字签名'

1.3.2 数字证书认证过程

Alice 想要通过证书加密让 Bob 安全读到自己的信息流程如下:

  1. Alice 在本地生成 Private Key 和 CSR(Certificate Signing Request)。CSR 中包含了 Alice 的公钥和姓名,机构、地址等身份信息。
  2. Alice 使用该 CSR 向证书机构发起数字证书申请。
  3. 证书机构验证 Alice 的身份后,使用 CSR 中的信息生成数字证书,并使用自己的 CA 根证书对应的私钥对该证书签名。
  4. Alice 使用自己的 Private Key 对合同进行签名,然后将签名后的合同和自己的证书一起并发送给 Bob。
  5. Bob 使用操作系统中自带的证书机构根证书中的公钥来验证 Alice 证书中的签名,以确认 Alice 的身份和公钥。(使用内置根证书确认身份并获取Alice证书)
  6. Alice 的证书验证成功后,Bob 使用 Alice 证书中的公钥来验证合同中数字签名。(使用刚刚获取的Alice证书(公钥)解析Alice发送的内容)
  7. 合同数字签名通过验证,可以证明该合同为 Alice 本人发送,并且中间未被第三方篡改过。

注意
1.自签发证书:证书签发商和证书持有者是同一个人,缺点是没有人能告诉你"这个人就是这个人(我就是我)"; 所以需要上面的信任证书机构的介入,多一环进行认证证明
2.证书链:但是信任的证书机构的根证书是很机密的,一旦被盗取可能会导致很多人都无法证明"我就是我", 所以一般会引入一些中间证书商,多一层上面的解析循环;这样也保证了万一中间商证书泄露,不至于全部使用证书机构的人都陷于危险之中
3.证书签发一般都是有一定时期的, 过期了就如同废纸

参考链接:
https://zhaohuabing.com/post/2020-03-19-pki/

二、双向 TLS 认证原理

both-tls

双向 TLS 认证需要的场景是,服务端和客户端都需要确认,这样就是正反都走一遍上面的流程,来完成双向的数据加密,保证双向的数据安全

参考链接:
https://medium.com/sitewards/the-magic-of-tls-x509-and-mutual-authentication-explained-b2162dec4401/

三、Kubernetes 中的证书工作机制

cert-picture

3.1 Kubernetes 中使用到的主要证书

  • etcd 集群中各个节点之间相互通信使用的证书。由于一个 etctd 节点既为其他节点提供服务,又需要作为客户端访问其他节点,因此该证书同时用作服务器证书和客户端证书
  • etcd 集群向外提供服务使用的证书。该证书是服务器证书
  • kube-apiserver 作为客户端访问 etcd 使用的证书。该证书是客户端证书
  • kube-apiserver 对外提供服务使用的证书。该证书是服务器证书
  • kube-controller-manager 作为客户端访问 kube-apiserver 使用的证书,该证书是客户端证书
  • kube-scheduler 作为客户端访问 kube-apiserver 使用的证书,该证书是客户端证书
  • kube-proxy 作为客户端访问 kube-apiserver 使用的证书,该证书是客户端证书
  • kubelet 作为客户端访问 kube-apiserver 使用的证书,该证书是客户端证书
  • 管理员用户通过 kubectl 访问 kube-apiserver 使用的证书,该证书是客户端证书
  • kubelet 对外提供服务使用的证书。该证书是服务器证书
  • kube-apiserver 作为客户端访问 kubelet 采用的证书。该证书是客户端证书
  • kube-controller-manager 用于生成和验证 service-account token 的证书。该证书并不会像其他证书一样用于身份认证,而是将证书中的公钥/私钥对用于 service account token 的生成和验证。kube-controller-manager 会用该证书的私钥来生成 service account token,然后以 secret 的方式加载到 pod 中。pod 中的应用可以使用该 token 来访问 kube-apiserver, kube-apiserver 会使用该证书中的公钥来验证请求中的 token

注意:

  1. 只有当你运行 kube-proxy 并要支持 扩展 API 服务器 时,才需要 front-proxy 证书

3.2 etcd 证书

 1$ ssh 10.20.11.120
 2$ sudo systemctl status etcd
 3● etcd.service - etcd docker wrapper
 4   Loaded: loaded (/etc/systemd/system/etcd.service; enabled; vendor preset: enabled)
 5   Active: active (running) since Mon 2020-08-03 15:42:36 CST; 1 months 18 days ago
 6$ cat /etc/etcd.env | tail -13
 7
 8# TLS settings
 9ETCD_TRUSTED_CA_FILE=/etc/ssl/etcd/ssl/ca.pem # etcd验证访问 etcd 服务器的客户端证书的 CA 根证书, 服务端签名证书和服务端私钥
10ETCD_CERT_FILE=/etc/ssl/etcd/ssl/member-ip-10-20-11-120.pem
11ETCD_KEY_FILE=/etc/ssl/etcd/ssl/member-ip-10-20-11-120-key.pem
12ETCD_CLIENT_CERT_AUTH=true
13
14ETCD_PEER_TRUSTED_CA_FILE=/etc/ssl/etcd/ssl/ca.pem # etcd peer之间验证访问 etcd 服务器的客户端证书的 CA 根证书, 服务端签名证书和服务端私钥
15ETCD_PEER_CERT_FILE=/etc/ssl/etcd/ssl/member-ip-10-20-11-120.pem
16ETCD_PEER_KEY_FILE=/etc/ssl/etcd/ssl/member-ip-10-20-11-120-key.pem
17ETCD_PEER_CLIENT_CERT_AUTH=True
18
19# 验证etcd证书过期时间
20$ sudo openssl x509 -in /etc/ssl/etcd/ssl/ca.pem -noout -dates
21notBefore=Aug  6 05:07:43 2019 GMT
22notAfter=Jul 13 05:07:43 2119 GMT

3.3 kube-apiserver 证书

 1$ ssh 10.20.11.120
 2$ cd /etc/kubernetes
 3$ cat manifests/kube-apiserver.yaml
 4apiVersion: v1
 5kind: Pod
 6metadata:
 7  creationTimestamp: null
 8  labels:
 9    component: kube-apiserver
10    tier: control-plane
11  name: kube-apiserver
12  namespace: kube-system
13spec:
14  containers:
15  - command:
16    - kube-apiserver
17    - --advertise-address=10.20.11.120
18    - --etcd-cafile=/etc/ssl/etcd/ssl/ca.pem # 用于验证 etcd 客户端证书的 CA 根证书, 用于访问 etcd 的客户端证书和私钥
19    - --etcd-certfile=/etc/ssl/etcd/ssl/node-ip-10-20-11-120.pem
20    - --etcd-keyfile=/etc/ssl/etcd/ssl/node-ip-10-20-11-120-key.pem
21    - --kubelet-client-certificate=/etc/kubernetes/ssl/apiserver-kubelet-client.crt # 用于访问 kubelet 的客户端证书和私钥
22    - --kubelet-client-key=/etc/kubernetes/ssl/apiserver-kubelet-client.key
23    - --proxy-client-cert-file=/etc/kubernetes/ssl/front-proxy-client.crt # 只有当你运行 kube-proxy 并要支持 扩展 API 服务器 时,才需要 front-proxy 证书
24    - --proxy-client-key-file=/etc/kubernetes/ssl/front-proxy-client.key
25    - --client-ca-file=/etc/kubernetes/ssl/ca.crt # 用于验证访问 kube-apiserver 的客户端的证书的 CA 根证书
26    - --service-account-key-file=/etc/kubernetes/ssl/sa.pub # 用于验证 service account token 的公钥
27    - --tls-cert-file=/etc/kubernetes/ssl/apiserver.crt # 用于对外提供服务的服务器证书和私钥
28    - --tls-private-key-file=/etc/kubernetes/ssl/apiserver.key
29    ...

3.4 controller-manager, kubelet, scheduler 证书

上面三个 apiserver 的客户端组件的证书都写在了对应的 KUBECONFIG 中,名为 controller-manager.conf, kubelet.conf, scheduler.conf, 就不一一展示, 随便列举一个如下:

 1# 包含验证apiserver证书的ca证书, 还有请求时候的看客户端证书和私钥
 2$ cat scheduler.conf
 3apiVersion: v1
 4clusters:
 5- cluster:
 6    certificate-authority-data: ca证书
 7    server: https://10.20.11.120:6443
 8  name: kubernetes
 9contexts:
10- context:
11    cluster: kubernetes
12    user: system:kube-scheduler
13  name: system:kube-scheduler@kubernetes
14current-context: system:kube-scheduler@kubernetes
15kind: Config
16preferences: {}
17users:
18- name: system:kube-scheduler
19  user:
20    client-certificate-data: server证书
21    client-key-data: 秘钥

3.5 Service Account 秘钥对

Service account 主要被 pod 用于访问 kube-apiserver。 在为一个 pod 指定了 service account 后,kubernetes 会为该 service account 生成一个 JWT token,并使用 secret 将该 service account token 挂载到 pod 上。pod 中的应用可以使用 service account token 来访问 api server。service account 证书被用于生成和验证 service account token。

注意:

  1. Service account加密过程是文章开头说的"非对称加密", 也就是说只是controller-manager私钥加密数据, 然后kube-apiserver的公钥进行解密而已
  2. 由于是非对称加密, 也就是说没有"双向 tls 认证"
  3. istio的做法就是为每个 service account 生成一个证书, 之后就可以"双向 tls 认证"
1$ cat manifests/kube-controller-manager.yaml | grep service-account
2    - --service-account-private-key-file=/etc/kubernetes/ssl/sa.key
3    - --use-service-account-credentials=true
4$ cat manifests/kube-apiserver.yaml | grep service-account
5    - --service-account-key-file=/etc/kubernetes/ssl/sa.pub

四、监控kubernetes证书

比较好用的是开源的x509 exporter

开源链接:
https://github.com/enix/x509-certificate-exporter/

 1$ gc https://github.com/enix/x509-certificate-exporter.git
 2$ cd deploy/charts/x509-certificate-exporter
 3$ vim values.yaml
 4...
 5  daemonSets:
 6    master:
 7      nodeSelector:
 8        "node-role.kubernetes.io/controlplane": "true"
 9      tolerations:
10        - effect: NoSchedule
11          operator: Exists
12        - effect: NoExecute
13          operator: Exists
14      watchDirectories:
15        - /etc/kubernetes/ssl/
16    nodes:
17      tolerations:
18        - effect: NoSchedule
19          operator: Exists
20      watchKubeconfFiles:
21        - /etc/kubernetes/ssl/kubecfg-kube-node.yaml
22        - /etc/kubernetes/ssl/kubecfg-kube-proxy.yaml
23
24$ helm install x509-certificate-exporter .
25$ k get daemonset                                               
26NAME                               DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR                               AGE
27x509-certificate-exporter-master   3         3         3       3            3           node-role.kubernetes.io/controlplane=true   18d
28x509-certificate-exporter-nodes    202       202       197     202          197         <none>                                      18d
29...

在daemonSet配置里面支持将核心的控制节点证书路径,或者kubeconfig文件写进去,然后部署即可;开源社区提供配套dashboard:

k8scert

FAQ

1. 在安装 Kubernetes 时,我们需要为每一个工作节点上的 Kubelet 分别生成一个证书。由于工作节点可能很多,手动生成 Kubelet 证书的过程会比较繁琐怎么办?

  1. Kubernetes 提供了一个 certificates.k8s.io API,可以使用配置的 CA 根证书来签发用户证书
  2. Kubernetes 提供了 TLS bootstrapping 的方式来简化 Kubelet 证书的生成过程

过程如下:(需要 apiserver 启用--enable-bootstrap-token-auth)

  1. 调用 kube-apiserver 生成一个 bootstrap token
  2. 将该 bootstrap token 写入到一个 kubeconfig 文件中,作为 kubelet 调用 kube-apiserver 的客户端验证方式
  3. 通过 --bootstrap-kubeconfig 启动参数将 bootstrap token 传递给 kubelet 进程
  4. Kubelet 采用 bootstrap token 调用 kube-apiserver API,生成自己所需的服务器和客户端证书
  5. 证书生成后,Kubelet 采用生成的证书和 kube-apiserver 进行通信,并删除本地的 kubeconfig 文件,以避免 bootstrap token 泄漏风险

参考链接:
https://zhaohuabing.com/post/2020-05-19-k8s-certificate/
https://kubernetes.io/zh/docs/tasks/tls/managing-tls-in-a-cluster/

2. 如何升级 kubernetes 证书但是不让 serviceaccout 轮换?

我们都知道 serviceaccount 的 token 是依赖于 sa.key 和 sa.pub 的,也就是说如果这俩秘钥更换了,所有的 serviceaccout 就都失效了,会重新创建(kube-system 命名空间的会自动轮换,其他 ns 的需要手动); 但是如果只是更换证书的话,其实是不需要更换 sa 秘钥对的,因为 sa 秘钥对采用的是"非对称加密", 也就是说永不过期,除非删除秘钥对;
事实上我们只要手动更换其他证书即可:

 1$ cp -R /etc/kubernetes/ssl /etc/kubernetes/ssl.backup
 2$ cp /etc/kubernetes/admin.conf /etc/kubernetes/admin.conf.backup
 3$ cp /etc/kubernetes/controller-manager.conf /etc/kubernetes/controller-manager.conf.backup
 4$ cp /etc/kubernetes/kubelet.conf /etc/kubernetes/kubelet.conf.backup
 5$ cp /etc/kubernetes/scheduler.conf /etc/kubernetes/scheduler.conf.backup
 6
 7$ kubeadm alpha certs renew apiserver-kubelet-client
 8$ kubeadm alpha certs renew apiserver
 9$ kubeadm alpha certs renew front-proxy-client
10$ kubeadm alpha kubeconfig user --client-name system:kube-controller-manager > /etc/kubernetes/controller-manager.conf
11$ kubeadm alpha kubeconfig user --client-name system:kube-scheduler > /etc/kubernetes/scheduler.conf
12$ kubeadm alpha kubeconfig user --client-name system:node:{nodename} --org system:nodes > /etc/kubernetes/kubelet.conf
13
14$ kubeadm alpha kubeconfig user --client-name kubernetes-admin --org system:masters > /etc/kubernetes/admin.conf
15$ cp /etc/kubernetes/admin.conf ~/.kube/config

上述过程每个 master 都需要做(如果有多个 master), 最后重启基础组件即可(嫌麻烦可以直接 master 节点关机重启)

参考链接:
https://github.com/kubernetes-sigs/kubespray/issues/5464/

3. kubernetes kubelet 证书自动 renew

 1# kubelet配置
 2--feature-gates=RotateKubeletServerCertificate=true
 3--feature-gates=RotateKubeletClientCertificate=true
 4# 1.8版本以上包含1.8都支持证书更换自动重载,以下版本只能手动重启服务
 5--rotate-certificates
 6
 7
 8# kube-controller-manager配置
 9# 证书有效期为10年
10--experimental-cluster-signing-duration=87600h0m0s
11--feature-gates=RotateKubeletServerCertificate=true

创建自动批准相关 CSR 请求的 ClusterRole:

1kind: ClusterRole
2apiVersion: rbac.authorization.k8s.io/v1
3metadata:
4  name: system:certificates.k8s.io:certificatesigningrequests:selfnodeserver
5rules:
6- apiGroups: ["certificates.k8s.io"]
7  resources: ["certificatesigningrequests/selfnodeserver"]
8  verbs: ["create"]

自动批准 kubelet-bootstrap 用户 TLS bootstrapping 首次申请证书的 CSR 请求

1kubectl create clusterrolebinding node-client-auto-approve-csr --clusterrole=system:certificates.k8s.io:certificatesigningrequests:nodeclient --user=kubelet-bootstrap

自动批准 system:nodes 组用户更新 kubelet 自身与 apiserver 通讯证书的 CSR 请求

1kubectl create clusterrolebinding node-client-auto-renew-crt --clusterrole=system:certificates.k8s.io:certificatesigningrequests:selfnodeclient --group=system:nodes

自动批准 system:nodes 组用户更新 kubelet 10250 api 端口证书的 CSR 请求

1kubectl create clusterrolebinding node-server-auto-renew-crt --clusterrole=system:certificates.k8s.io:certificatesigningrequests:selfnodeserver --group=system:nodes

参考链接:
https://www.leiyawu.com/2020/10/11/Untitled/