kubeadm创建的kubernetes集群证书续期

网友投稿 919 2022-05-29

kubeadm创建的Kubernetes集群证书续期

Kubernetes cluster certificate created by kubeadm expires renewal

以1.9版本为例

编译kubeadm

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

# wget https://dl.google.com/go/go1.11.5.linux-amd64.tar.gz

# tar -C /usr/local -xzf go1.11.5.linux-amd64.tar.gz

# export PATH=$PATH:/usr/local/go/bin

# go get -d k8s.io/kubernetes  #下载kubernetes源码,500多M

# cd go/src/k8s.io/kubernetes/ #进入kubernetes源码目录

# git branch                 #查看当前分支

* master

# git tag -l                 #查看所有分支

# git checkout v1.9.4        #切换到指定分支

HEAD is now at bee2d15... Merge pull request #61045 from liggitt/subpath-1.9

# git branch

* (detached from v1.9.4)

master

# vim vendor/k8s.io/client-go/util/cert/cert.go  #修改107行、152行

107                 NotAfter:     time.Now().Add(duration365d * 10).UTC(),

152                 NotAfter:  time.Now().Add(time.Hour * 24 * 3650),

如上,kubeadm生成的证书时间由一年变更为10年,其他版本编译过程差不多

源码在这里

https://github.com/kubernetes/kubernetes/blob/release-1.9/staging/src/k8s.io/client-go/util/cert/cert.go#L107

如果修改错误,编译完成的话,先清理,再次编译即可

1

2

3

4

5

6

7

# ./build/make-clean.sh

# make all WHAT=cmd/kubeadm GOFLAGS=-v

# ls _output/local/bin/linux/amd64/

conversion-gen  deepcopy-gen  defaulter-gen  go-bindata  kubeadm  openapi-gen  teststale

# _output/local/bin/linux/amd64/kubeadm version

kubeadm version: &version.Info{Major:"1", Minor:"9+", GitVersion:"v1.9.4-dirty", GitCommit:"bee2d1505c4fe820744d26d41ecd3fdd4a3d6546", GitTreeState:"dirty", BuildDate:"2019-01-29T08:45:42Z", GoVersion:"go1.11.5", Compiler:"gc", Platform:"linux/amd64"}

如果是新的集群,直接用此kubeadm创建集群好了,以下忽略。如果是正在运行的集群快要到期,用下面方式.

以下步骤已在我的生产环境验证

当前配置

以2台主机举例,1 master节点,1 node节点

1

2

3

4

5

[root@master ~]# cat /etc/hosts

127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4

::1         localhost localhost.localdomain localhost6 localhost6.localdomain6

192.168.12.40 master

192.168.12.18 node

当前状态

1

2

3

4

[root@master ~]# kubectl get no

NAME      STATUS    ROLES     AGE       VERSION

master    Ready     master    20d       v1.9.4

node      Ready         20d       v1.9.4

查询现在证书时间

1

2

3

4

5

6

7

8

9

10

11

[root@master ~]# openssl x509 -in /etc/kubernetes/pki/apiserver-kubelet-client.crt -text -noout

Certificate:

Data:

Version: 3 (0x2)

Serial Number: 1200214740602462488 (0x10a804f293cc2118)

Signature Algorithm: sha256WithRSAEncryption

Issuer: CN=kubernetes

Validity

Not Before: Jan 29 06:34:48 2019 GMT

Not After : Jan 29 06:34:49 2020 GMT

Subject: O=system:masters, CN=kube-apiserver-kubelet-client

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

[root@master ~]# cat /etc/kubernetes/config.yaml

apiVersion: kubeadm.k8s.io/v1alpha1

kind: MasterConfiguration

etcd:

endpoints:

- https://192.168.12.40:2379    #three master node

- https://192.168.12.18:2379

caFile: /etc/etcd/ssl/ca.pem  #same as etcd ca

certFile: /etc/etcd/ssl/etcd.pem

keyFile: /etc/etcd/ssl/etcd-key.pem

dataDir: /var/lib/etcd

networking:

podSubnet: 20.244.0.0/21

kubernetesVersion: 1.9.4

api:

advertiseAddress: "192.168.12.40"   #kube-api VIP

token: "f6a0d5.ecf8a4377dc9dbae"

tokenTTL: "0s"

apiServerCertSANs:

- master

- 192.168.12.40

- 192.168.12.18

- 127.0.0.1

- kubernetes

- kubernetes.default

- kubernetes.default.svc

- kubernetes.default.svc.cluster

- kubernetes.default.svc.cluster.local

- 10.96.0.1

- 20.244.0.1

featureGates:

CoreDNS: true

更换kubeadm

替换编译后的kubeadm

1

2

3

[root@master ~]# mv /usr/bin/kubeadm{,.bak}

[root@master ~]# cp _output/local/bin/linux/amd64/kubeadm /usr/bin/kubeadm

[root@master ~]# scp kubeadm node:/usr/bin/

更改配置

kubelet配置和kube-controller-manager配置更改

kubelet配置

增加feature-gates

1

--feature-gates=RotateKubeletClientCertificate=true,RotateKubeletServerCertificate=true

1

2

3

# vim /etc/systemd/system/kubelet.service.d/10-kubeadm.conf

[Service]

Environment="KUBELET_KUBECONFIG_ARGS=--bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --kubeconfig=/etc/kubernetes/kubelet.conf --feature-gates=RotateKubeletClientCertificate=true,RotateKubeletServerCertificate=true"

kube-controller-manager配置

增加feature-gates和experimental-cluster-signing-duration

1

2

--feature-gates=RotateKubeletServerCertificate=true

--experimental-cluster-signing-duration=87600h0m0s

1

2

3

4

5

# vim /etc/kubernetes/manifests/kube-controller-manager.yaml

- --node-cidr-mask-size=24

- --feature-gates=RotateKubeletServerCertificate=true

- --experimental-cluster-signing-duration=87600h0m0s

image: gcr.io/google_containers/kube-controller-manager-amd64:v1.9.4

添加ClusterRoleBinding绑定

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

# cat >> update.yaml <

apiVersion: rbac.authorization.k8s.io/v1

kind: ClusterRole

metadata:

annotations:

rbac.authorization.kubernetes.io/autoupdate: "true"

labels:

kubernetes.io/bootstrapping: rbac-defaults

name: system:certificates.k8s.io:certificatesigningrequests:selfnodeserver

rules:

- apiGroups:

- certificates.k8s.io

resources:

- certificatesigningrequests/selfnodeserver

verbs:

- create

---

apiVersion: rbac.authorization.k8s.io/v1

kind: ClusterRoleBinding

metadata:

name: kubeadm:node-autoapprove-certificate-server

roleRef:

apiGroup: rbac.authorization.k8s.io

kind: ClusterRole

name: system:certificates.k8s.io:certificatesigningrequests:selfnodeserver

subjects:

kubeadm创建的kubernetes集群证书续期

- apiGroup: rbac.authorization.k8s.io

kind: Group

name: system:nodes

EOF

[root@master ~]# kubectl apply -f update.yaml

clusterrole "system:certificates.k8s.io:certificatesigningrequests:selfnodeserver" created clusterrolebinding "kubeadm:node-autoapprove-certificate-server" created

查看当前系统配置

1

2

3

4

5

6

7

8

9

[root@master ~]# ls /etc/kubernetes/

admin.conf               kubelet.conf             scheduler.conf

config.yaml              manifests/               ssl/

controller-manager.conf  pki/

[root@master ~]# ls /etc/kubernetes/pki/

apiserver.crt                 ca.crt              front-proxy-client.crt

apiserver.key                 ca.key              front-proxy-client.key

apiserver-kubelet-client.crt  front-proxy-ca.crt  sa.key

apiserver-kubelet-client.key  front-proxy-ca.key  sa.pub

更新证书

重新初始化kubernetes集群

备份,并使用更改时间的kubeadm重新初始化

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

[root@master ~]# cp -r /etc/kubernetes/ /tmp/

[root@master ~]# rm -f /etc/kubernetes/pki/apiserver* /etc/kubernetes/pki/front-proxy-client.*

[root@master ~]# kubeadm alpha phase certs apiserver --config /etc/kubernetes/config.yaml

[certificates] Generated apiserver certificate and key.

[certificates] apiserver serving cert is signed for DNS names [master kubernetes kubernetes.default kubernetes.default.svc kubernetes.default.svc.cluster.local master kubernetes kubernetes.default kubernetes.default.svc kubernetes.default.svc.cluster kubernetes.default.svc.cluster.local] and IPs [10.96.0.1 192.168.12.40 192.168.12.40 192.168.12.18 127.0.0.1 10.96.0.1 20.244.0.1]

[root@master ~]# kubeadm alpha phase certs apiserver-kubelet-client --config /etc/kubernetes/config.yaml

[certificates] Generated apiserver-kubelet-client certificate and key.

[root@master ~]# kubeadm alpha phase certs front-proxy-client --config /etc/kubernetes/config.yaml

[certificates] Generated front-proxy-client certificate and key.

[root@master ~]# kubeadm alpha phase kubeconfig all --config /etc/kubernetes/config.yaml

[kubeconfig] Using existing up-to-date KubeConfig file: "admin.conf"

[kubeconfig] Using existing up-to-date KubeConfig file: "kubelet.conf"

[kubeconfig] Using existing up-to-date KubeConfig file: "controller-manager.conf"

[kubeconfig] Using existing up-to-date KubeConfig file: "scheduler.conf"

[root@master ~]# reboot

[root@master ~]# /bin/cp /etc/kubernetes/admin.conf .kube/config

1

2

3

4

5

6

7

8

9

10

11

[root@master ~]# openssl x509 -in /etc/kubernetes/pki/apiserver-kubelet-client.crt -text -noout

Certificate:

Data:

Version: 3 (0x2)

Serial Number: 296656465301295716 (0x41def7bc29b5264)

Signature Algorithm: sha256WithRSAEncryption

Issuer: CN=kubernetes

Validity

Not Before: Jan 29 06:34:48 2019 GMT

Not After : Feb 16 02:48:51 2029 GMT

Subject: O=system:masters, CN=kube-apiserver-kubelet-client

node节点操作

更改kubelet配置后重新加入集群

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

[root@node ~]# vim /etc/systemd/system/kubelet.service.d/10-kubeadm.conf

[root@node ~]# cp -r /etc/kubernetes/ /tmp/

[root@node ~]# rm -f /etc/kubernetes/pki/ca.crt /etc/kubernetes/kubelet.conf

[root@node ~]# systemctl daemon-reload

[root@node ~]# systemctl stop kubelet

[root@node ~]# kubeadm join --token f6a0d5.ecf8a4377dc9dbae 192.168.12.40:6443 --discovery-token-ca-cert-hash sha256:51a0d767b6be6db863a7087fa1c872c45dcd7eeed3cf0eeeb88ca69df474fc45

[preflight] Running pre-flight checks.

[WARNING FileExisting-crictl]: crictl not found in system path

[preflight] Starting the kubelet service

[discovery] Trying to connect to API Server "192.168.12.40:6443"

[discovery] Created cluster-info discovery client, requesting info from "https://192.168.12.40:6443"

[discovery] Requesting info from "https://192.168.12.40:6443" again to validate TLS against the pinned public key

[discovery] Cluster info signature and contents are valid and TLS certificate validates against pinned roots, will use API Server "192.168.12.40:6443"

[discovery] Successfully established connection with API Server "192.168.12.40:6443"

This node has joined the cluster:

* Certificate signing request was sent to master and a response

was received.

* The Kubelet was informed of the new secure connection details.

Run 'kubectl get nodes' on the master to see this node join the cluster.

[root@node ~]# tail -f /var/log/messages

discovery-token 不记得?

1

[root@node ~]# grep token\: /etc/kubernetes/bootstrap-kubelet.conf

discovery-token-ca-cert-hash 忘了的话

1

[root@master ~]# openssl x509 -pubkey -in /etc/kubernetes/pki/ca.crt | openssl rsa -pubin -outform der 2>/dev/null | openssl dgst -sha256 -hex | sed 's/^.* //'

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

[root@master ~]# kubectl get no

NAME      STATUS    ROLES     AGE       VERSION

master    Ready     master    20d       v1.9.4

node      Ready         20d       v1.9.4

[root@master ~]# kubectl get csr

NAME                                                   AGE       REQUESTOR                 CONDITION

csr-bd9q4                                              2m        system:node:master        Approved,Issued

csr-fvjpv                                              38s       system:node:node          Approved,Issued

node-csr-wiBwE2ZfO6brsCjPrgEqid4oSiUBGIeAyv0g3IZkur8   20d       system:bootstrap:f6a0d5   Approved,Issued

[root@node ~]# ll /var/lib/kubelet/pki/

total 20

-rw-r--r-- 1 root root  883 Feb 19 10:52 kubelet-client.crt

-rw------- 1 root root  227 Jan 29 14:44 kubelet-client.key

-rw-r--r-- 1 root root 1090 Jan 29 14:44 kubelet.crt

-rw------- 1 root root 1675 Jan 29 14:44 kubelet.key

-rw------- 1 root root 1220 Feb 19 10:52 kubelet-server-2019-02-19-10-52-26.pem

lrwxrwxrwx 1 root root   59 Feb 19 10:52 kubelet-server-current.pem -> /var/lib/kubelet/pki/kubelet-server-2019-02-19-10-52-26.pem

至此,证书更新完成

设计理论

kubelet服务证书引导和自动更新过程说明

当kubelet首次启动时,它会生成一个自签名证书/密钥对,用于接受传入的TLS连接。 此过程涵盖了在本地生成密钥,然后向集群API服务器发出证书签名请求以获取由集群证书颁发机构签名的关联证书的过程等。此外,当证书接近过期时,将使用相同的机制来请求更新的证书。

1,查看本机现有的证书/密钥对,如果存在就使用

2,如果不存在,在kubelet当前生成密钥和证书的位置,生成一个密钥,创建证书签名请求,从API服务器请求签名证书,等待回应,将新证书/密钥对存储在–cert-dir指定的目录中

自动更新Kubelet服务器证书过程:通过生成新的私钥,向API服务器发出新的证书签名请求(CSR/Certificate Signing Request),安全地更新磁盘上的证书/密钥对,开始使用新的证书/密钥对

1,将新的证书/密钥对存储在–cert-dir指定的目录中,如果未指定,则将其存储在默认值中。 需允许kubelet有一个位置,用于存储在任何给定时刻可能具有的多个证书/密钥对(因为正在进行轮换)

2,将有一个特定功能来启用证书引导和轮转,RotateKubeletServerCertificate

3,将kubelet代码中的证书访问集中到CertificateManager。 CertificateManager将负责:

※ 提供用于建立TLS连接的正确证书。

※ 当前证书即将到期时生成新私钥并请求新证书。

※ 由于证书可以随时轮换,因此每次使用证书时,kubelet的所有其他部分都应向CertificateManager询问正确的证书。 除CertificateManager外没有证书缓存。

※ 在证书轮转正在进行中发生的kubelet崩溃或重新启动恢复(请求已发出但尚未签名等)

4,将更新节点的RBAC角色,以允许节点请求新证书。

5,让CertificateManager在证书过期时重复CSR过程。

※ 超过配置的持续时间阈值时,将请求新证书。

※ 确保正确安全的文件结构,包含下面5点

仅存在于内存中的私钥,如果中断就被放弃

当接收到相应的签名证书时,证书/密钥对将被写入单个文件,例如kubelet-server-.pem

将kubelet-server-updated.pem文件软连接到新的证书/秘钥对

删除kubelet-server-current.pem

将kubelet-server-updated.pem移动到kubelet-server-current.pem

6,证书请求签名(Certificate Request Signing)API被硬编码为颁发证书1年

实际过程

kubelet 进程接收 –rotate-certificates 参数,该参数决定 kubelet 在当前使用的证书即将到期时, 是否会自动申请新的证书。 由于证书轮换是 beta 特性,必须通过参数 –feature-gates=RotateKubeletClientCertificate=true 进行启用。

kube-controller-manager 进程接收 –experimental-cluster-signing-duration 参数,该参数控制证书签发的有效期限。

当 kubelet 启动时,如被配置为自举(使用–bootstrap-kubeconfig 参数),kubelet 会使用其初始证书连接到 ,并发送证书签名的请求。 可以通过以下方式查看证书签名请求的状态:

1

kubectl get csr

最初,来自节点上 kubelet 的证书签名请求处于 Pending 状态。 如果证书签名请求满足特定条件, 控制管理器会自动批准,此时请求会处于 Approved 状态。 接下来,控制器管理器会签署证书, 证书的有效期限由 –experimental-cluster-signing-duration 参数指定,签署的证书会被附加到证书签名请求中。

Kubelet 会从 Kubernetes API 取回签署的证书,并将其写入磁盘,存储位置通过 –cert-dir 参数指定。 然后 kubelet 会使用新的证书连接到 Kubernetes API。

当签署的证书即将到期时,kubelet 会使用 Kubernetes API,发起新的证书签名请求。 同样地,控制管理器会自动批准证书请求,并将签署的证书附加到证书签名请求中。 Kubelet 会从 Kubernetes API 取回签署的证书,并将其写入磁盘。 然后它会更新与 Kubernetes API 的连接,使用新的证书重新连接到 Kubernetes API。

详细过程

当调用kubeadm init时,kubelet配置将在/var/lib/kubelet/config.yaml中写入到磁盘,并上载到群集中的ConfigMap(kubectl describe configmap -n kube-system kubeadm-config 查看)。ConfigMap名为kubelet-config-1.X,其中.X是您正在初始化的Kubernetes版本的次要版本。还将kubelet配置文件写入/etc/kubernetes/kubelet.conf,并为群集中的所有kubelet提供基线群集(baseline cluster-wide)范围配置。此配置文件指向允许kubelet与API服务器通信的客户端证书。这解决了将群集级配置传播到每个kubelet的需要。

初始化集群的时候已经指定了token(

kubeadm token generate) ,node节点 join master节点也写明了此token。kubelet要向 kube-apiserver 请求客户端证书,kubelet 首先需要一个包含 bootstrap 身份验证 token 的 kubeconfig 文件路径。启动 kubelet 时,如果 –kubeconfig 指定的文件不存在,则使用 bootstrap kubeconfig 向 API server 请求客户端证书。在批准 kubelet 的证书请求和回执时,将包含了生成的密钥和证书的 kubeconfig 文件写入由 -kubeconfig 指定的路径。证书和密钥文件将被放置在由 –cert-dir 指定的目录中。

API服务器中的Bootstrap Authenticator会读取由token信息组成的Secrets。node节点执行 kubeadm join 时,kubeadm使用Bootstrap Token凭据执行TLS引导程序,该引导程序获取下载kubelet-config-1.X ConfigMap所需的凭据并将其写入/var/lib/kubelet/config.yaml。动态环境文件的生成方式与kubeadm init完全相同。在kubelet加载新配置之后,kubeadm会写入/etc/kubernetes/bootstrap-kubelet.conf KubeConfig文件,该文件包含CA证书和Bootstrap Token。这些由kubelet用于执行TLS Bootstrap并获得唯一的凭证,该凭证存储在/etc/kubernetes/kubelet.conf中。写入此文件后,kubelet已完成执行TLS Bootstrap

kubelet 使用的 bootstrap.kubeconfig 配置文件中包含apiserver的ca证书和此token,与 apiserver 建立 TLS 通讯,使用 bootstrap.kubeconfig 中的用户 Token等信息组成的secret 来向 apiserver 声明自己的 RBAC 授权身份

在默认情况下,kubelet 通过 bootstrap.kubeconfig 中的预设用户 Token 声明了自己的身份,然后创建 CSR 请求,1.9版本中默认是允许的。用于TLS Bootstrap的KubeConfig文件是/etc/kubernetes/bootstrap-kubelet.conf,但仅在/etc/kubernetes/kubelet.conf不存在时使用。

kube-controller-manager为了批准CSR,您需要告诉控制器管理员批准它们是可以接受的。这是通过向正确的组授予RBAC权限来完成的。有两组不同的权限:

※ nodeclient:如果节点正在为节点创建新证书,则它还没有证书。它使用上面列出的一个令牌进行身份验证,因此是该群组的一部分system:bootstrappers。

※ selfnodeclient:如果某个节点正在续订其证书,那么它已经拥有一个证书(根据定义),它会连续使用该证书作为该组的一部分进行身份验证system:nodes。

要使kubelet能够请求和接收新证书,请创建一个ClusterRoleBinding将引导节点所属的组绑定system:bootstrappers到ClusterRole授予其权限的组,即system:certificates.k8s.io:certificatesigningrequests:nodeclient。即上面配置的update.yaml

kube-controller-manager 默认签发证书时间是1年,所以要更改时间,对 kubelet 发起的特定 CSR 请求自动批准即可,TLS bootstrapping 时的证书实际是由 kube-controller-manager 组件来签署的

1. kubelet 读取 bootstrap.kubeconfig,使用其 CA 与 Token 向 apiserver 发起第一次 CSR 请求(nodeclient)

2. apiserver 根据 RBAC 规则自动批准首次 CSR 请求(approve-node-client-csr),并下发证书(kubelet-client.crt)

3. kubelet 使用刚刚签发的证书(O=system:nodes, CN=system:node:NODE_NAME)与 apiserver 通讯,并发起申请kubelet(10250端口) 所使用证书的 CSR 请求

4. apiserver 根据 RBAC 规则自动批准 kubelet 为其 10250 端口申请的证书(kubelet-server-current.crt)

证书即将到期时,kubelet 自动向 apiserver 发起用于与 apiserver 通讯所用证书的 renew CSR 请求和 renew 本身 kubelet(10250端口) 端口所用证书的 CSR 请求

5. apiserver 根据 RBAC 规则自动批准两个证书

6. kubelet 拿到新证书后关闭所有连接,reload 新证书,以后便一直如此

参考

https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet-tls-bootstrapping/

https://k8smeetup.github.io/docs/tasks/tls/certificate-rotation/

https://kubernetes.io/docs/reference/setup-tools/kubeadm/kubeadm-init-phase/

https://sealyun.com/post/kubeadm-dev/

https://github.com/kubernetes/kubeadm/issues/581

https://k8smeetup.github.io/docs/tasks/administer-cluster/kubeadm-upgrade-ha/

https://k8smeetup.github.io/docs/admin/kubelet-tls-bootstrapping/

https://kubernetes.io/zh/docs/reference/setup-tools/kubeadm/kubeadm-init/

1.13的版本准备拿来用,把1.9改为1.13版本

支持外部自签名的证书(避免更换一次证书)

支持自定义镜像仓库(避免从谷歌拉取不到镜像)

华为云APP Kubernetes

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:典型游戏分析(上)
下一篇:一看就懂,一写就懵?搞懂回溯算法,一口气刷了20多道题
相关文章