├── .gitignore ├── LICENSE ├── README.md ├── other-zh_CN.md ├── other.md └── update-kubeadm-cert.sh /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .vscode 3 | /tmp -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 yuyicai 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # update-kube-cert 2 | 3 | A tool to update and extend Kubernetes certificate expiration dates in kubeadm-initiated clusters. 4 | 5 | ## Overview 6 | 7 | This script helps you manage Kubernetes certificates by: 8 | 9 | - Extending certificate validity to 10 years for existing Kubernetes clusters. (includes both cluster with certificate expiration issues and normal cluster) 10 | - Generating long-lived CA certificates (100 years) before initializing new clusters 11 | 12 | ## Usage 13 | 14 | ### Get the Script 15 | 16 | ```bash 17 | git clone https://github.com/yuyicai/update-kube-cert.git 18 | cd update-kube-cert 19 | ``` 20 | 21 | ### For Existing Clusters 22 | 23 | Renew certificates to 10 years: 24 | Run on all control plane nodes (master0, master1, master2...) 25 | 26 | ```bash 27 | bash update-kubeadm-cert.sh --cri containerd 28 | ``` 29 | 30 |
31 | 32 | Terminal Output 33 | 34 | ```bash 35 | root@master0:~/update-kube-cert# bash update-kubeadm-cert.sh --cri containerd 36 | [2025-04-01T00:09:48.47+0800] [INFO] checking if all certificate files are existed... 37 | [2025-04-01T00:09:48.47+0800] [INFO] backup /etc/kubernetes to /etc/kubernetes.old-2025-04-01_00-09-48 38 | [2025-04-01T00:09:48.48+0800] [INFO] checking certificate expiration before update... 39 | |-----------------------------------|----------------------------| 40 | | CERTIFICATE | EXPIRES | 41 | | ca.crt | Mar 29 16:09:05 2035 GMT | 42 | | apiserver.crt | Mar 31 16:09:05 2026 GMT | 43 | | apiserver-kubelet-client.crt | Mar 31 16:09:05 2026 GMT | 44 | | front-proxy-ca.crt | Mar 29 16:09:05 2035 GMT | 45 | | front-proxy-client.crt | Mar 31 16:09:05 2026 GMT | 46 | |-----------------------------------|----------------------------| 47 | | controller-manager.conf | Mar 31 16:09:05 2026 GMT | 48 | | scheduler.conf | Mar 31 16:09:05 2026 GMT | 49 | | admin.conf | Mar 31 16:09:05 2026 GMT | 50 | | super-admin.conf | Mar 31 16:09:05 2026 GMT | 51 | |-----------------------------------|----------------------------| 52 | | etcd/ca.crt | Mar 29 16:09:05 2035 GMT | 53 | | etcd/server.crt | Mar 31 16:09:05 2026 GMT | 54 | | etcd/peer.crt | Mar 31 16:09:05 2026 GMT | 55 | | etcd/healthcheck-client.crt | Mar 31 16:09:05 2026 GMT | 56 | | apiserver-etcd-client.crt | Mar 31 16:09:05 2026 GMT | 57 | |-----------------------------------|----------------------------| 58 | [2025-04-01T00:09:48.52+0800] [INFO] updating certificates with 3650 days expiration... 59 | [2025-04-01T00:09:48.53+0800] [INFO] updated /etc/kubernetes/pki/etcd/server.crt 60 | [2025-04-01T00:09:48.55+0800] [INFO] updated /etc/kubernetes/pki/etcd/peer.crt 61 | [2025-04-01T00:09:48.56+0800] [INFO] updated /etc/kubernetes/pki/etcd/healthcheck-client.crt 62 | [2025-04-01T00:09:48.57+0800] [INFO] updated /etc/kubernetes/pki/apiserver-etcd-client.crt 63 | [2025-04-01T00:09:48.59+0800] [INFO] restarted etcd 64 | [2025-04-01T00:09:48.61+0800] [INFO] updated /etc/kubernetes/pki/apiserver.crt 65 | [2025-04-01T00:09:48.62+0800] [INFO] updated /etc/kubernetes/pki/apiserver-kubelet-client.crt 66 | [2025-04-01T00:09:48.63+0800] [INFO] updated /etc/kubernetes/controller-manager.conf 67 | [2025-04-01T00:09:48.65+0800] [INFO] updated /etc/kubernetes/scheduler.conf 68 | [2025-04-01T00:09:48.66+0800] [INFO] updated /etc/kubernetes/admin.conf 69 | [2025-04-01T00:09:48.68+0800] [INFO] updated /etc/kubernetes/super-admin.conf 70 | [2025-04-01T00:09:48.69+0800] [INFO] updated /etc/kubernetes/pki/front-proxy-client.crt 71 | [2025-04-01T00:09:48.71+0800] [INFO] restarted control-plane pod: apiserver 72 | [2025-04-01T00:09:48.73+0800] [INFO] restarted control-plane pod: controller-manager 73 | [2025-04-01T00:09:48.76+0800] [INFO] restarted control-plane pod: scheduler 74 | [2025-04-01T00:09:48.83+0800] [INFO] restarted kubelet 75 | [2025-04-01T00:09:48.83+0800] [INFO] checking certificate expiration after update... 76 | |-----------------------------------|----------------------------| 77 | | CERTIFICATE | EXPIRES | 78 | | ca.crt | Mar 29 16:09:05 2035 GMT | 79 | | apiserver.crt | Mar 29 16:09:48 2035 GMT | 80 | | apiserver-kubelet-client.crt | Mar 29 16:09:48 2035 GMT | 81 | | front-proxy-ca.crt | Mar 29 16:09:05 2035 GMT | 82 | | front-proxy-client.crt | Mar 29 16:09:48 2035 GMT | 83 | |-----------------------------------|----------------------------| 84 | | controller-manager.conf | Mar 29 16:09:48 2035 GMT | 85 | | scheduler.conf | Mar 29 16:09:48 2035 GMT | 86 | | admin.conf | Mar 29 16:09:48 2035 GMT | 87 | | super-admin.conf | Mar 29 16:09:48 2035 GMT | 88 | |-----------------------------------|----------------------------| 89 | | etcd/ca.crt | Mar 29 16:09:05 2035 GMT | 90 | | etcd/server.crt | Mar 29 16:09:48 2035 GMT | 91 | | etcd/peer.crt | Mar 29 16:09:48 2035 GMT | 92 | | etcd/healthcheck-client.crt | Mar 29 16:09:48 2035 GMT | 93 | | apiserver-etcd-client.crt | Mar 29 16:09:48 2035 GMT | 94 | |-----------------------------------|----------------------------| 95 | [2025-04-01T00:09:48.89+0800] [INFO] DONE!!!enjoy it 96 | 97 | please copy admin.conf to /root/.kube/config manually. 98 | # back old config 99 | cp /root/.kube/config /root/.kube/config_backup 100 | # copy new admin.conf to /root/.kube/config for kubectl manually 101 | cp -i /opt/kube/tmp/kubernetes/admin.conf /root/.kube/config 102 | 103 | 104 | root@master0:~/update-kube-cert# kubectl get po -A 105 | NAMESPACE NAME READY STATUS RESTARTS AGE 106 | kube-system coredns-668d6bf9bc-7kwkk 0/1 Pending 0 37m 107 | kube-system coredns-668d6bf9bc-b68dx 0/1 Pending 0 37m 108 | kube-system etcd-master0 1/1 Running 4 (4m21s ago) 37m 109 | kube-system kube-apiserver-master0 1/1 Running 2 (60s ago) 37m 110 | kube-system kube-controller-manager-master0 1/1 Running 4 (49s ago) 37m 111 | kube-system kube-proxy-5mf68 1/1 Running 0 37m 112 | kube-system kube-scheduler-master0 1/1 Running 3 (48s ago) 37m 113 | root@master0:~/update-kube-cert# 114 | root@master0:~/update-kube-cert# crictl --runtime-endpoint unix:///run/containerd/containerd.sock pods 115 | POD ID CREATED STATE NAME NAMESPACE ATTEMPT RUNTIME 116 | 59935ee07550b About a minute ago Ready kube-apiserver-master0 kube-system 2 (default) 117 | 37b73945aee1f About a minute ago NotReady kube-apiserver-master0 kube-system 1 (default) 118 | 5f05c3a5abfac 4 minutes ago Ready etcd-master0 kube-system 4 (default) 119 | 40c2c1480cbc8 5 minutes ago Ready kube-controller-manager-master0 kube-system 1 (default) 120 | 781806f0cc91d 6 minutes ago NotReady etcd-master0 kube-system 3 (default) 121 | 75b68162b9476 37 minutes ago Ready kube-proxy-5mf68 kube-system 0 (default) 122 | dc3da94fda7f9 37 minutes ago Ready kube-scheduler-master0 kube-system 0 (default) 123 | ``` 124 | 125 |
126 | 127 | ### For New Clusters 128 | 129 | Generate 100 year CA certificates before running `kubeadm init` 130 | Just generate CA on the first control plane node (master0, which will run `kubeadm init`) 131 | 132 | ```bash 133 | # master0 134 | # 1. Generate 100 years CA certificates 135 | bash update-kubeadm-cert.sh --action gen-ca 136 | 137 | # 2. Initialize your cluster with kubeadm 138 | # kubeadm will use the existing CA certificates generated by the script 139 | kubeadm init --upload-certs [options] 140 | 141 | # 3. Update all certificates to 100 years use extended expiration 142 | bash update-kubeadm-cert.sh --cri containerd --days 36500 143 | 144 | # 4. Join master1, master2 to the cluster and just run 'bash update-kubeadm-cert.sh --cri containerd --days 36500' on them 145 | ``` 146 | 147 |
148 | 149 | Key Kubeadm init Output 150 | kubeadm uses the existing CA certificates generated by the script 151 | 152 | ```bash 153 | ... 154 | [certs] Using existing ca certificate authority 155 | ... 156 | [certs] Using existing front-proxy-ca certificate authority 157 | ... 158 | [certs] Using existing etcd/ca certificate authority 159 | ... 160 | ``` 161 | 162 |
163 | 164 | 165 |
166 | 167 | Full terminal Output 168 | 169 | ```bash 170 | root@master0:~/update-kube-cert# bash update-kubeadm-cert.sh --action gen-ca 171 | [2025-04-01T00:14:35.89+0800] [INFO] generating CA with 36500 days expiration... 172 | [2025-04-01T00:14:35.90+0800] [INFO] generating k8s CA... 173 | [2025-04-01T00:14:36.06+0800] [INFO] generated /etc/kubernetes/pki/ca.crt 174 | [2025-04-01T00:14:36.06+0800] [INFO] generating front-proxy CA... 175 | [2025-04-01T00:14:36.11+0800] [INFO] generated /etc/kubernetes/pki/front-proxy-ca.crt 176 | [2025-04-01T00:14:36.11+0800] [INFO] generating etcd CA... 177 | [2025-04-01T00:14:36.14+0800] [INFO] generated /etc/kubernetes/pki/etcd/ca.crt 178 | |-----------------------------------|----------------------------| 179 | | CERTIFICATE | EXPIRES | 180 | | ca.crt | Mar 7 16:14:36 2125 GMT | 181 | | apiserver.crt | | 182 | | apiserver-kubelet-client.crt | | 183 | | front-proxy-ca.crt | Mar 7 16:14:36 2125 GMT | 184 | | front-proxy-client.crt | | 185 | |-----------------------------------|----------------------------| 186 | | controller-manager.conf | | 187 | | scheduler.conf | | 188 | | admin.conf | | 189 | |-----------------------------------|----------------------------| 190 | | etcd/ca.crt | Mar 7 16:14:36 2125 GMT | 191 | | etcd/server.crt | | 192 | | etcd/peer.crt | | 193 | | etcd/healthcheck-client.crt | | 194 | | apiserver-etcd-client.crt | | 195 | |-----------------------------------|----------------------------| 196 | [2025-04-01T00:14:36.18+0800] [INFO] DONE!!! generated CA for new cluster. 197 | # create new cluster after generating CA, you can use the following command: 198 | kubeadm init [options] 199 | # after running kubeadm init, update certificates for 100 yeas 200 | bash update-kubeadm-cert.sh --cri containerd --days 36500 201 | root@master0:~/update-kube-cert# 202 | root@master0:~/update-kube-cert# 203 | root@master0:~/update-kube-cert# kubeadm init 204 | [init] Using Kubernetes version: v1.32.3 205 | [preflight] Running pre-flight checks 206 | [WARNING SystemVerification]: cgroups v1 support is in maintenance mode, please migrate to cgroups v2 207 | [preflight] Pulling images required for setting up a Kubernetes cluster 208 | [preflight] This might take a minute or two, depending on the speed of your internet connection 209 | [preflight] You can also perform this action beforehand using 'kubeadm config images pull' 210 | W0401 00:15:10.683957 13914 checks.go:846] detected that the sandbox image "" of the container runtime is inconsistent with that used by kubeadm.It is recommended to use "registry.k8s.io/pause:3.10" as the CRI sandbox image. 211 | [certs] Using certificateDir folder "/etc/kubernetes/pki" 212 | [certs] Using existing ca certificate authority 213 | [certs] Generating "apiserver" certificate and key 214 | [certs] apiserver serving cert is signed for DNS names [kubernetes kubernetes.default kubernetes.default.svc kubernetes.default.svc.cluster.local master0] and IPs [10.96.0.1 10.0.0.186] 215 | [certs] Generating "apiserver-kubelet-client" certificate and key 216 | [certs] Using existing front-proxy-ca certificate authority 217 | [certs] Generating "front-proxy-client" certificate and key 218 | [certs] Using existing etcd/ca certificate authority 219 | [certs] Generating "etcd/server" certificate and key 220 | [certs] etcd/server serving cert is signed for DNS names [localhost master0] and IPs [10.0.0.186 127.0.0.1 ::1] 221 | [certs] Generating "etcd/peer" certificate and key 222 | [certs] etcd/peer serving cert is signed for DNS names [localhost master0] and IPs [10.0.0.186 127.0.0.1 ::1] 223 | [certs] Generating "etcd/healthcheck-client" certificate and key 224 | [certs] Generating "apiserver-etcd-client" certificate and key 225 | [certs] Generating "sa" key and public key 226 | [kubeconfig] Using kubeconfig folder "/etc/kubernetes" 227 | [kubeconfig] Writing "admin.conf" kubeconfig file 228 | [kubeconfig] Writing "super-admin.conf" kubeconfig file 229 | [kubeconfig] Writing "kubelet.conf" kubeconfig file 230 | [kubeconfig] Writing "controller-manager.conf" kubeconfig file 231 | [kubeconfig] Writing "scheduler.conf" kubeconfig file 232 | [etcd] Creating static Pod manifest for local etcd in "/etc/kubernetes/manifests" 233 | [control-plane] Using manifest folder "/etc/kubernetes/manifests" 234 | [control-plane] Creating static Pod manifest for "kube-apiserver" 235 | [control-plane] Creating static Pod manifest for "kube-controller-manager" 236 | [control-plane] Creating static Pod manifest for "kube-scheduler" 237 | [kubelet-start] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env" 238 | [kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml" 239 | [kubelet-start] Starting the kubelet 240 | [wait-control-plane] Waiting for the kubelet to boot up the control plane as static Pods from directory "/etc/kubernetes/manifests" 241 | [kubelet-check] Waiting for a healthy kubelet at http://127.0.0.1:10248/healthz. This can take up to 4m0s 242 | [kubelet-check] The kubelet is healthy after 501.312263ms 243 | [api-check] Waiting for a healthy API server. This can take up to 4m0s 244 | [api-check] The API server is healthy after 3.501053598s 245 | [upload-config] Storing the configuration used in ConfigMap "kubeadm-config" in the "kube-system" Namespace 246 | [kubelet] Creating a ConfigMap "kubelet-config" in namespace kube-system with the configuration for the kubelets in the cluster 247 | [upload-certs] Skipping phase. Please see --upload-certs 248 | [mark-control-plane] Marking the node master0 as control-plane by adding the labels: [node-role.kubernetes.io/control-plane node.kubernetes.io/exclude-from-external-load-balancers] 249 | [mark-control-plane] Marking the node master0 as control-plane by adding the taints [node-role.kubernetes.io/control-plane:NoSchedule] 250 | [bootstrap-token] Using token: pwjq3f.6vdgdbfy8mk3gq0s 251 | [bootstrap-token] Configuring bootstrap tokens, cluster-info ConfigMap, RBAC Roles 252 | [bootstrap-token] Configured RBAC rules to allow Node Bootstrap tokens to get nodes 253 | [bootstrap-token] Configured RBAC rules to allow Node Bootstrap tokens to post CSRs in order for nodes to get long term certificate credentials 254 | [bootstrap-token] Configured RBAC rules to allow the csrapprover controller automatically approve CSRs from a Node Bootstrap Token 255 | [bootstrap-token] Configured RBAC rules to allow certificate rotation for all node client certificates in the cluster 256 | [bootstrap-token] Creating the "cluster-info" ConfigMap in the "kube-public" namespace 257 | [kubelet-finalize] Updating "/etc/kubernetes/kubelet.conf" to point to a rotatable kubelet client certificate and key 258 | [addons] Applied essential addon: CoreDNS 259 | [addons] Applied essential addon: kube-proxy 260 | 261 | Your Kubernetes control-plane has initialized successfully! 262 | 263 | To start using your cluster, you need to run the following as a regular user: 264 | 265 | mkdir -p $HOME/.kube 266 | sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config 267 | sudo chown $(id -u):$(id -g) $HOME/.kube/config 268 | 269 | Alternatively, if you are the root user, you can run: 270 | 271 | export KUBECONFIG=/etc/kubernetes/admin.conf 272 | 273 | You should now deploy a pod network to the cluster. 274 | Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at: 275 | https://kubernetes.io/docs/concepts/cluster-administration/addons/ 276 | 277 | Then you can join any number of worker nodes by running the following on each as root: 278 | 279 | kubeadm join 10.0.0.186:6443 --token pwjq3f.6vdgdbfy8mk3gq0s \ 280 | --discovery-token-ca-cert-hash sha256:6cef6024c7b0a25a2b81c31907248a2dc124eada0fd7abd565bbe60d6ad775a1 281 | root@master0:~/update-kube-cert# 282 | root@master0:~/update-kube-cert# 283 | root@master0:~/update-kube-cert# bash update-kubeadm-cert.sh --cri containerd --days 36500 284 | [2025-04-01T00:16:12.09+0800] [INFO] checking if all certificate files are existed... 285 | [2025-04-01T00:16:12.09+0800] [INFO] backup /etc/kubernetes to /etc/kubernetes.old-2025-04-01_00-16-12 286 | [2025-04-01T00:16:12.09+0800] [INFO] checking certificate expiration before update... 287 | |-----------------------------------|----------------------------| 288 | | CERTIFICATE | EXPIRES | 289 | | ca.crt | Mar 7 16:14:36 2125 GMT | 290 | | apiserver.crt | Mar 31 16:15:10 2026 GMT | 291 | | apiserver-kubelet-client.crt | Mar 31 16:15:10 2026 GMT | 292 | | front-proxy-ca.crt | Mar 7 16:14:36 2125 GMT | 293 | | front-proxy-client.crt | Mar 31 16:15:10 2026 GMT | 294 | |-----------------------------------|----------------------------| 295 | | controller-manager.conf | Mar 31 16:15:10 2026 GMT | 296 | | scheduler.conf | Mar 31 16:15:10 2026 GMT | 297 | | admin.conf | Mar 31 16:15:10 2026 GMT | 298 | | super-admin.conf | Mar 31 16:15:10 2026 GMT | 299 | |-----------------------------------|----------------------------| 300 | | etcd/ca.crt | Mar 7 16:14:36 2125 GMT | 301 | | etcd/server.crt | Mar 31 16:15:10 2026 GMT | 302 | | etcd/peer.crt | Mar 31 16:15:10 2026 GMT | 303 | | etcd/healthcheck-client.crt | Mar 31 16:15:10 2026 GMT | 304 | | apiserver-etcd-client.crt | Mar 31 16:15:10 2026 GMT | 305 | |-----------------------------------|----------------------------| 306 | [2025-04-01T00:16:12.14+0800] [INFO] updating certificates with 36500 days expiration... 307 | [2025-04-01T00:16:12.15+0800] [INFO] updated /etc/kubernetes/pki/etcd/server.crt 308 | [2025-04-01T00:16:12.16+0800] [INFO] updated /etc/kubernetes/pki/etcd/peer.crt 309 | [2025-04-01T00:16:12.17+0800] [INFO] updated /etc/kubernetes/pki/etcd/healthcheck-client.crt 310 | [2025-04-01T00:16:12.18+0800] [INFO] updated /etc/kubernetes/pki/apiserver-etcd-client.crt 311 | [2025-04-01T00:16:12.21+0800] [INFO] restarted etcd 312 | [2025-04-01T00:16:12.22+0800] [INFO] updated /etc/kubernetes/pki/apiserver.crt 313 | [2025-04-01T00:16:12.23+0800] [INFO] updated /etc/kubernetes/pki/apiserver-kubelet-client.crt 314 | [2025-04-01T00:16:12.25+0800] [INFO] updated /etc/kubernetes/controller-manager.conf 315 | [2025-04-01T00:16:12.26+0800] [INFO] updated /etc/kubernetes/scheduler.conf 316 | [2025-04-01T00:16:12.28+0800] [INFO] updated /etc/kubernetes/admin.conf 317 | [2025-04-01T00:16:12.29+0800] [INFO] updated /etc/kubernetes/super-admin.conf 318 | [2025-04-01T00:16:12.30+0800] [INFO] updated /etc/kubernetes/pki/front-proxy-client.crt 319 | [2025-04-01T00:16:12.33+0800] [INFO] restarted control-plane pod: apiserver 320 | [2025-04-01T00:16:12.35+0800] [INFO] restarted control-plane pod: controller-manager 321 | [2025-04-01T00:16:12.37+0800] [INFO] restarted control-plane pod: scheduler 322 | [2025-04-01T00:16:12.42+0800] [INFO] restarted kubelet 323 | [2025-04-01T00:16:12.42+0800] [INFO] checking certificate expiration after update... 324 | |-----------------------------------|----------------------------| 325 | | CERTIFICATE | EXPIRES | 326 | | ca.crt | Mar 7 16:14:36 2125 GMT | 327 | | apiserver.crt | Mar 7 16:16:12 2125 GMT | 328 | | apiserver-kubelet-client.crt | Mar 7 16:16:12 2125 GMT | 329 | | front-proxy-ca.crt | Mar 7 16:14:36 2125 GMT | 330 | | front-proxy-client.crt | Mar 7 16:16:12 2125 GMT | 331 | |-----------------------------------|----------------------------| 332 | | controller-manager.conf | Mar 7 16:16:12 2125 GMT | 333 | | scheduler.conf | Mar 7 16:16:12 2125 GMT | 334 | | admin.conf | Mar 7 16:16:12 2125 GMT | 335 | | super-admin.conf | Mar 7 16:16:12 2125 GMT | 336 | |-----------------------------------|----------------------------| 337 | | etcd/ca.crt | Mar 7 16:14:36 2125 GMT | 338 | | etcd/server.crt | Mar 7 16:16:12 2125 GMT | 339 | | etcd/peer.crt | Mar 7 16:16:12 2125 GMT | 340 | | etcd/healthcheck-client.crt | Mar 7 16:16:12 2125 GMT | 341 | | apiserver-etcd-client.crt | Mar 7 16:16:12 2125 GMT | 342 | |-----------------------------------|----------------------------| 343 | [2025-04-01T00:16:12.48+0800] [INFO] DONE!!!enjoy it 344 | 345 | please copy admin.conf to /root/.kube/config manually. 346 | # back old config 347 | cp /root/.kube/config /root/.kube/config_backup 348 | # copy new admin.conf to /root/.kube/config for kubectl manually 349 | cp -i /opt/kube/tmp/kubernetes/admin.conf /root/.kube/config 350 | root@master0:/etc/kubernetes# 351 | root@master0:/etc/kubernetes# cp /opt/kube/tmp/kubernetes/admin.conf /root/.kube/config 352 | root@master0:/etc/kubernetes# 353 | root@master0:/etc/kubernetes# kubeadm certs check-expiration 354 | [check-expiration] Reading configuration from the "kubeadm-config" ConfigMap in namespace "kube-system"... 355 | [check-expiration] Use 'kubeadm init phase upload-config --config your-config.yaml' to re-upload it. 356 | 357 | CERTIFICATE EXPIRES RESIDUAL TIME CERTIFICATE AUTHORITY EXTERNALLY MANAGED 358 | admin.conf Mar 07, 2125 16:16 UTC 99y ca no 359 | apiserver Mar 07, 2125 16:16 UTC 99y ca no 360 | apiserver-etcd-client Mar 07, 2125 16:16 UTC 99y etcd-ca no 361 | apiserver-kubelet-client Mar 07, 2125 16:16 UTC 99y ca no 362 | controller-manager.conf Mar 07, 2125 16:16 UTC 99y ca no 363 | etcd-healthcheck-client Mar 07, 2125 16:16 UTC 99y etcd-ca no 364 | etcd-peer Mar 07, 2125 16:16 UTC 99y etcd-ca no 365 | etcd-server Mar 07, 2125 16:16 UTC 99y etcd-ca no 366 | front-proxy-client Mar 07, 2125 16:16 UTC 99y front-proxy-ca no 367 | scheduler.conf Mar 07, 2125 16:16 UTC 99y ca no 368 | super-admin.conf Mar 07, 2125 16:16 UTC 99y ca no 369 | 370 | CERTIFICATE AUTHORITY EXPIRES RESIDUAL TIME EXTERNALLY MANAGED 371 | ca Mar 07, 2125 16:14 UTC 99y no 372 | etcd-ca Mar 07, 2125 16:14 UTC 99y no 373 | front-proxy-ca Mar 07, 2125 16:14 UTC 99y no 374 | ``` 375 | 376 |
377 | 378 | ## After Running 379 | Copy the admin configuration to your kubectl config directory 380 | 381 | ```bash 382 | # Backup existing config 383 | cp $HOME/.kube/config $HOME/.kube/config_backup 384 | 385 | # Copy new config 386 | cp -i /etc/kubernetes/admin.conf $HOME/.kube/config 387 | ``` 388 | 389 | ## Options 390 | ``` bash 391 | -c, --cri (default: containerd) 392 | Set the cri type, in order to restart control-plane and etcd service by different command, 'docker' or 'crictl'. 393 | -a, --action (default: update) 394 | update: Update certificates 10 years for existing clusters 395 | check: Only check the expiration of the certificates without updating them. 396 | gen-ca: Generate 100 years CA before kubeadm init cluster. (only used for new clusters, not for existing clusters) 397 | --days Set the number of days for certificate expiration. (default: 3650) 398 | -h, --help Show this help message and exit. 399 | ``` 400 | 401 | ## Certificate Files Updated 402 | certificates files: 403 | ``` 404 | /etc/kubernetes/pki/apiserver.crt 405 | /etc/kubernetes/pki/apiserver-kubelet-client.crt 406 | /etc/kubernetes/pki/front-proxy-client.crt 407 | /etc/kubernetes/pki/apiserver-etcd-client.crt 408 | /etc/kubernetes/pki/etcd/server.crt 409 | /etc/kubernetes/pki/etcd/peer.crt 410 | /etc/kubernetes/pki/etcd/healthcheck-client.crt 411 | ``` 412 | kubeconfig files: 413 | ``` 414 | /etc/kubernetes/admin.conf 415 | /etc/kubernetes/controller-manager.conf 416 | /etc/kubernetes/scheduler.conf 417 | /etc/kubernetes/super-admin.conf (after Kubernetes v1.29.0, this script will check automatically) 418 | /etc/kubernetes/kubelet.conf (before Kubernetes v1.17.0, this script will check automatically) 419 | ``` 420 | 421 | ## FAQ 422 | 423 | - **Can I generate CA for 100 years on an existing cluster by this script?** 424 | No, this script only updates the certificates on existing clusters, not including CA. 425 | 426 | - **How can I Change CA for an existing cluster?** 427 | See: https://kubernetes.io/docs/tasks/tls/manual-rotation-of-ca-certificates/ 428 | 429 | - **If I have a multi-master cluster, do I need to run this on all master?** 430 | Yes, you should run this script on all control plane nodes, by not on worker nodes. 431 | 432 | - **How to force restart control-plane pods manually?** 433 | If any control plane components couldn't be automatically restarted, you should manually restart them. 434 | 435 | ```bash 436 | # Make sure kubelet is running 437 | systemctl restart kubelet 438 | 439 | # Move manifests to trigger kubelet to recreate the pods 440 | mv /etc/kubernetes/manifests /etc/kubernetes/manifests_backup 441 | 442 | # Wait for kubelet to remove the old pods 443 | sleep 120 444 | 445 | # Restore manifests, kubelet will recreate the pods 446 | mv /etc/kubernetes/manifests_backup /etc/kubernetes/manifests 447 | 448 | # Check the status of control-plane pods 449 | kubectl get pods -n kube-system -o wide 450 | ``` 451 | 452 | - **What happens if the script fails?** 453 | The script performs backup of critical files before making changes. If it fails, you can find backups in `/etc/kubernetes.old-$(date +%Y-%m-%d_%H-%M-%S)`. 454 | 455 | - **Can I run this on worker nodes?** 456 | No, this script should only be run on control plane nodes. 457 | 458 | - **Will this cause downtime?** 459 | There might be a brief disruption while control plane components restart with new certificates. 460 | But on multi-master clusters, the disruption should be minimal. 461 | 462 | - **How can I skip etcd certificate update?** 463 | You can use the env `KUBE_SKIP_ETCD_CERTS` to skip etcd certificate update. 464 | ```bash 465 | # just update kubernetes master certificates, not etcd 466 | export KUBE_SKIP_ETCD_CERTS=true 467 | bash update-kubeadm-cert.sh --cri containerd 468 | ``` 469 | 470 | 471 | ## License 472 | MIT License 473 | -------------------------------------------------------------------------------- /other-zh_CN.md: -------------------------------------------------------------------------------- 1 | # 使用脚本处理后证书是延续 10 年吗? 2 | 3 | 准确来说并不是 4 | 5 | kubeadm 签发的 CA 默认有效期是 10 年 (从 init 集群那一刻开始算),当 CA 到期后,整套证书体系都失效了 6 | 7 | 也就是说,10 年有效期是从 init 集群那一刻开始算的,不是从执行脚本更新证书那一刻开始算 8 | 9 | # kubeadm 证书相关命令发展 10 | 11 | - `v1.8` 版开始提供了证书生成命令 `kubeadm alpha phase certs ` 12 | - `v1.13` 版开始证书生成命令改为 `kubeadm init phase certs ` 13 | - `v1.15` 版增加了证书更新命令 `kubeadm alpha certs renew `(这个命令与上面两个区别是:上面两个是生成证书,这个是更新证书),`v1.15` 版之后可使用 `kubeadm alpha certs renew ` 来更新证书 14 | 15 | # kubeadm 命令更新证书手动处理 16 | 17 | 使用该脚本更新证书,不涉及下面这个 bug,无需手动处理 18 | 19 | bug 见 https://github.com/kubernetes/kubeadm/issues/1753 ,这个 bug 在 `1.17` 版修复 20 | 21 | 针对小于 `1.17版本` ,使用 `kubeadm alpha certs renew ` 来更新证书 22 | 23 | `kubeadm alpha certs renew` 并不会更新 kubelet 证书(kubelet.conf 文件里面写的客户端证书),因为 kubelet 证书是默认开启自动轮回更新的,但是在执行 `kubeadm init` 的 master 节点的 kubelet.conf 文件里面的证书是以 base64 编码写死的 (类似 controller-manager.conf 里面的证书) 24 | 25 | 在用 `kubeadm` 命令更新 master 证书时需要手动将 kubelet.conf 文件的 `client-certificate-data` 和 `client-key-data` 改为: 26 | 27 | ```yaml 28 | client-certificate: /var/lib/kubelet/pki/kubelet-client-current.pem 29 | client-key: /var/lib/kubelet/pki/kubelet-client-current.pem 30 | ``` 31 | -------------------------------------------------------------------------------- /other.md: -------------------------------------------------------------------------------- 1 | # Is the certificate valid for 10 years after executing the script? 2 | 3 | No, technically. 4 | 5 | The default CA that issued by kubeadm is valid for 10 years (from the moment you init the cluster). And the whole certificate system will expire when the CA expires. 6 | 7 | In other words, the 10-year validity period starts from the moment the cluster is initiated, instead of from the moment the script is executed to renew the certificate. 8 | 9 | # The history of kubeadm certificate related commands 10 | 11 | - Since `v1.8`, it provides the certificate generation command `kubeadm alpha phase certs `. 12 | - The command changed to `kubeadm init phase certs ` in `v1.13` 13 | - The certificate renewal command `kubeadm alpha certs renew ` comes since `v1.15`. (the difference between this command and the above two is: The above two are to generate certificates. But this one is to renew certificates) So after `v1.15`, you can simply use `kubeadm alpha certs renew ` to renew certificates. name>` to renew the certificate 14 | 15 | # handle kubeadm command bug manually 16 | 17 | If use this script to update the certificate, this bug won't appear. And there is no need to handle it. 18 | 19 | See https://github.com/kubernetes/kubeadm/issues/1753 for the detail of the bug, which was fixed in `1.17` version. 20 | 21 | For versions less than `1.17`, use `kubeadm alpha certs renew ` to renew the certificate. 22 | 23 | `kubeadm alpha certs renew` does not renew the kubelet certificate (the client certificate written in the kubelet.conf file) because the kubelet certificate is automatically renewed by default. But in the kubelet.conf file of the master node where `kubeadm init` is executed, the certificate is hard coded in base64 encoding format. (like the controller-manager.conf certificate) 24 | 25 | When updating the master certificate with the `kubeadm` command, you need to manually change the `client-certificate-data` and `client-key-data` in the kubelet.conf file to the following contents: 26 | 27 | ```yaml 28 | client-certificate: /var/lib/kubelet/pki/kubelet-client-current.pem 29 | client-key: /var/lib/kubelet/pki/kubelet-client-current.pem 30 | ``` 31 | -------------------------------------------------------------------------------- /update-kubeadm-cert.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # MIT License 4 | # 5 | # The full license can be found at: 6 | # https://github.com/yuyicai/update-kube-cert/blob/master/LICENSE 7 | 8 | # more information about the kubernetes certificates can be found at: 9 | # https://kubernetes.io/docs/setup/best-practices/certificates/ 10 | 11 | # renew certificates to 10 years for existing cluster 12 | # bash update-kubeadm-cert.sh --cri containerd 13 | # generate 100 years CA before kubeadm init cluster (only used for new clusters) 14 | # bash update-kubeadm-cert.sh --action gen-ca 15 | # check the expiration of the certificates without updating them 16 | # bash update-kubeadm-cert.sh --action check 17 | 18 | # GitHub: https://github.com/yuyicai/update-kube-cert 19 | 20 | # version of the script 21 | VERSION="v2.1.0" 22 | 23 | set -o errexit 24 | set -o pipefail 25 | # set -o xtrace 26 | 27 | # loglevel: debug, info 28 | LOG_LEVEL=${LOG_LEVEL:-"info"} 29 | 30 | # set cri: docker, containerd 31 | # cri is used to determine the command used to restart the control-plane pod 32 | # when cri is docker, use `docker restart` to restart the control-plane pod 33 | # when cri is containerd, use `crictl stopp` to restart the control-plane pod (kill the pod, and kubelet will recreate the pod) 34 | KUBE_CRI=${KUBE_CRI:-"containerd"} 35 | 36 | # set default certificate expiration days 37 | KUBE_CERT_DAYS=${KUBE_CERT_DAYS:-3650} 38 | 39 | # set default CA expiration days 40 | KUBE_CA_DAYS=${KUBE_CA_DAYS:-36500} 41 | 42 | # skip update etcd certs 43 | KUBE_SKIP_ETCD_CERTS=${KUBE_SKIP_ETCD_CERTS:-"false"} 44 | 45 | # ----------------------------- Certificates Path Begin ----------------------------- 46 | # set default kubernetes path 47 | KUBE_PATH=${KUBE_PATH:-"/etc/kubernetes"} 48 | KUBE_PKI_PATH=${KUBE_PATH}/pki 49 | 50 | # master certificates path 51 | # api-server 52 | KUBE_CERT_CA=${KUBE_PKI_PATH}/ca 53 | KUBE_CERT_APISERVER=${KUBE_PKI_PATH}/apiserver 54 | KUBE_CERT_APISERVER_KUBELET_CLIENT=${KUBE_PKI_PATH}/apiserver-kubelet-client 55 | # kubeconfig 56 | KUBE_CONF_CONTROLLER_MANAGER=${KUBE_PATH}/controller-manager 57 | KUBE_CONF_SCHEDULER=${KUBE_PATH}/scheduler 58 | KUBE_CONF_ADMIN=${KUBE_PATH}/admin 59 | # super-admin.conf, add on v1.29.0 60 | # https://github.com/kubernetes/kubernetes/blob/master/CHANGELOG/CHANGELOG-1.29.md#no-really-you-must-read-this-before-you-upgrade 61 | KUBE_CONF_SUPER_ADMIN=${KUBE_PATH}/super-admin 62 | KUBE_CONF_KUBELET=${KUBE_PATH}/kubelet 63 | # front-proxy 64 | KUBE_FRONT_PROXY_CA=${KUBE_PKI_PATH}/front-proxy-ca 65 | KUBE_FRONT_PROXY_CLIENT=${KUBE_PKI_PATH}/front-proxy-client 66 | 67 | # etcd certificates path 68 | KUBE_ETCD_CERT_CA=${KUBE_PKI_PATH}/etcd/ca 69 | KUBE_ETCD_CERT_SERVER=${KUBE_PKI_PATH}/etcd/server 70 | KUBE_ETCD_CERT_PEER=${KUBE_PKI_PATH}/etcd/peer 71 | KUBE_ETCD_CERT_HEALTHCHECK_CLIENT=${KUBE_PKI_PATH}/etcd/healthcheck-client 72 | KUBE_ETCD_CERT_APISERVER_ETCD_CLIENT=${KUBE_PKI_PATH}/apiserver-etcd-client 73 | 74 | KUBE_ETCD_CERT_LIST=("${KUBE_ETCD_CERT_CA}" "${KUBE_ETCD_CERT_SERVER}" "${KUBE_ETCD_CERT_PEER}" "${KUBE_ETCD_CERT_HEALTHCHECK_CLIENT}" "${KUBE_ETCD_CERT_APISERVER_ETCD_CLIENT}") 75 | 76 | KUBE_MASTER_CERT_LIST=("${KUBE_CERT_CA}" "${KUBE_CERT_APISERVER}" "${KUBE_CERT_APISERVER_KUBELET_CLIENT}" "${KUBE_FRONT_PROXY_CA}" "${KUBE_FRONT_PROXY_CLIENT}") 77 | 78 | KUBE_MASTER_CONF_LIST=("${KUBE_CONF_CONTROLLER_MANAGER}" "${KUBE_CONF_SCHEDULER}" "${KUBE_CONF_ADMIN}") 79 | # if super-admin.conf is existed, add it to the list 80 | if [[ -f "${KUBE_CONF_SUPER_ADMIN}.conf" ]]; then 81 | KUBE_MASTER_CONF_LIST+=("${KUBE_CONF_SUPER_ADMIN}") 82 | fi 83 | # add kubelet.conf to the list if needed 84 | # kubelet.conf does not need to update for K8s v1.17+ 85 | # https://github.com/kubernetes/kubeadm/issues/1753 86 | 87 | # if the kubelet.conf contains kubelet-client-current.pem, it does not need to update 88 | IS_KUBELET_NEED_RESTART="false" 89 | if [[ -f "${KUBE_CONF_KUBELET}.conf" ]]; then 90 | grep -q kubelet-client-current.pem "${KUBE_CONF_KUBELET}.conf" 2>/dev/null || KUBE_MASTER_CONF_LIST+=("${KUBE_CONF_KUBELET}") && IS_KUBELET_NEED_RESTART="true" 91 | fi 92 | 93 | # ----------------------------- Certificates Path End ----------------------------- 94 | 95 | # Determines if the kubelet, apiserver, controller-manager, scheduler, etcd should be restarted after update certificates 96 | # Set to false if you want to restart manually 97 | KUBE_RESTART_SERVICES=true 98 | 99 | # is need restart control-plane manually 100 | IS_NEED_RESTART_CONTROL_PLANE_MANUALLY=false 101 | 102 | # set output color 103 | COLOR_NC='\033[0m' 104 | COLOR_RED='\033[31m' 105 | COLOR_GREEN='\033[32m' 106 | COLOR_YELLOW='\033[33m' 107 | COLOR_BLUE='\033[34m' 108 | COLOR_PURPLE='\033[35m' 109 | 110 | # loglevel color 111 | LOG_INFO_COLOR="${COLOR_GREEN}" 112 | LOG_WARNING_COLOR="${COLOR_YELLOW}" 113 | LOG_ERROR_COLOR="${COLOR_RED}" 114 | LOG_DEBUG_COLOR="${COLOR_PURPLE}" 115 | 116 | log_err() { 117 | printf "[$(date +'%Y-%m-%dT%H:%M:%S.%2N%z')] ${LOG_ERROR_COLOR}[ERROR]${COLOR_NC} %b\n" "$@" 118 | } 119 | 120 | log_info() { 121 | printf "[$(date +'%Y-%m-%dT%H:%M:%S.%2N%z')] ${LOG_INFO_COLOR}[INFO]${COLOR_NC} %b\n" "$@" 122 | } 123 | 124 | log_warning() { 125 | printf "[$(date +'%Y-%m-%dT%H:%M:%S.%2N%z')] ${LOG_WARNING_COLOR}[WARNING]${COLOR_NC} %b\n" "$@" 126 | } 127 | 128 | log_debug() { 129 | if [[ "${LOG_LEVEL}" == "debug" ]]; then 130 | printf "[$(date +'%Y-%m-%dT%H:%M:%S.%2N%z')] ${LOG_DEBUG_COLOR}[DEBUG]${COLOR_NC} %b\n" "$@" 131 | fi 132 | } 133 | 134 | # get x509v3 subject alternative name from the old certificate 135 | # like: DNS:kubernetes, DNS:kubernetes.default, DNS:kubernetes.default.svc, DNS:kubernetes.default.svc.cluster.local, DNS:some.domain.com, IP:x.x.x.x 136 | cert_get_subject_alt_name() { 137 | local cert_file_path=${1}.crt 138 | local alt_name 139 | alt_name=$(openssl x509 -text -noout -in "${cert_file_path}" | grep -A1 'Alternative' | tail -n1 | sed 's/[[:space:]]*Address//g' | sed 's/^[[:space:]]*//') 140 | printf "%s\n" "${alt_name}" 141 | 142 | } 143 | 144 | # get subject from the old certificate 145 | # like: /CN=kube-apiserver 146 | cert_get_subj() { 147 | local cert_file_path=${1}.crt 148 | local subj 149 | subj=$(openssl x509 -text -noout -in "${cert_file_path}" | grep "Subject:" | sed 's/Subject:/\//g;s/\,/\//;s/[[:space:]]//g') 150 | printf "%s\n" "${subj}" 151 | } 152 | 153 | # generate certificate whit client, server or peer 154 | # Args: 155 | # $1 (the path of certificate, without suffix. 156 | # example: /etc/kubernetes/pki/apiserver) 157 | # $2 (the type of certificate, must be one of 'client', 'server', 'peer') 158 | # $3 (the subject of certificate) 159 | # $4 (the validity of certificate) (days) 160 | # $5 (the path of ca, without suffix. 161 | # example: /etc/kubernetes/pki/ca) 162 | # $6 (the x509v3 subject alternative name of certificate. 163 | # This option is required when the type of certificate is 'server' or 'peer'. 164 | # example: "DNS:kubernetes, DNS:kubernetes.default, DNS:kubernetes.default.svc, DNS:kubernetes.default.svc.cluster.local, DNS:some.domain.com, IP:10.96.0.1, IP:10.0.0.185") 165 | cert_gen_cert() { 166 | local cert_file_path=${1}.crt 167 | local key_file_path=${1}.key 168 | local csr_file_path=${1}.csr 169 | local cert_type=${2} 170 | local subj=${3} 171 | local cert_days=${4} 172 | local ca_cert_file_path=${5}.crt 173 | local ca_key_file_path=${5}.key 174 | local alt_name=${6} 175 | local cert_name=${cert_file_path##*/} 176 | local common_csr_conf='distinguished_name = dn\n[dn]\n[v3_ext]\nkeyUsage = critical, digitalSignature, keyEncipherment\nbasicConstraints = critical, CA:FALSE\n' 177 | # check x509v3 subject alternative name when the type of certificate is 'server' or 'peer' 178 | if [[ "${cert_type}" == "server" || "${cert_type}" == "peer" ]]; then 179 | if [[ -z "${alt_name}" ]]; then 180 | log_err "x509v3 subject alternative name is required when the type of certificate is 'server' or 'peer'" 181 | exit 1 182 | fi 183 | log_debug "[${cert_name}] x509v3 subject alternative name: ${alt_name}" 184 | fi 185 | log_debug "[${cert_name}] subject: ${subj}" 186 | 187 | # set the extended key usage for the certificate with different types 188 | case "${cert_type}" in 189 | client) 190 | csr_conf=$(printf "%bextendedKeyUsage = clientAuth\n" "${common_csr_conf}") 191 | ;; 192 | server) 193 | csr_conf=$(printf "%bextendedKeyUsage = serverAuth\nsubjectAltName = %b\n" "${common_csr_conf}" "${alt_name}") 194 | ;; 195 | peer) 196 | csr_conf=$(printf "%bextendedKeyUsage = serverAuth, clientAuth\nsubjectAltName = %b\n" "${common_csr_conf}" "${alt_name}") 197 | ;; 198 | *) 199 | log_err "unknown, unsupported certs type: ${COLOR_YELLOW}${cert_type}${COLOR_NC}, supported type: client, server, peer" 200 | exit 1 201 | ;; 202 | esac 203 | 204 | # gen csr 205 | log_debug "[${cert_name}] generate csr" 206 | if ! openssl req -new -key "${key_file_path}" -subj "${subj}" -reqexts v3_ext \ 207 | -config <(printf "%b" "${csr_conf}") \ 208 | -out "${csr_file_path}" >/dev/null 2>&1; then 209 | log_err "Failed to generate CSR: ${csr_file_path}" 210 | exit 1 211 | fi 212 | # gen cert 213 | log_debug "[${cert_name}] generate cert" 214 | if ! openssl x509 -in "${csr_file_path}" -req \ 215 | -CA "${ca_cert_file_path}" -CAkey "${ca_key_file_path}" -CAcreateserial -extensions v3_ext \ 216 | -extfile <(printf "%b" "${csr_conf}") \ 217 | -days "${cert_days}" -out "${cert_file_path}" >/dev/null 2>&1; then 218 | log_err "Failed to generate certificate: ${cert_file_path}" 219 | exit 1 220 | fi 221 | log_debug "[${cert_name}] remove csr" 222 | # remove csr 223 | rm -f "${csr_file_path}" 224 | } 225 | 226 | cert_update_kubeconf() { 227 | local cert_path_without_suffix=${1} 228 | local kubeconf_file_path=${cert_path_without_suffix}.conf 229 | local cert_file_path=${cert_path_without_suffix}.crt 230 | local key_file_path=${cert_path_without_suffix}.key 231 | local subj 232 | local cert_base64 233 | 234 | # get the key file from the old kubeconf 235 | grep "client-key-data" "${kubeconf_file_path}" | awk '{print$2}' | base64 -d >"${key_file_path}" 236 | # get the old certificate file from the old kubeconf 237 | grep "client-certificate-data" "${kubeconf_file_path}" | awk '{print$2}' | base64 -d >"${cert_file_path}" 238 | # get subject from the old certificate 239 | subj=$(cert_get_subj "${cert_path_without_suffix}") 240 | # generate new certificate 241 | cert_gen_cert "${cert_path_without_suffix}" "client" "${subj}" "${KUBE_CERT_DAYS}" "${KUBE_CERT_CA}" 242 | # convert the new certificate to base64 code 243 | cert_base64=$(base64 -w 0 "${cert_file_path}") 244 | 245 | # set new certificate base64 code to kubeconf 246 | sed -i 's/client-certificate-data:.*/client-certificate-data: '"${cert_base64}"'/g' "${kubeconf_file_path}" 247 | 248 | # remove certificate, key file after set kubeconf 249 | rm -f "${cert_file_path}" 250 | rm -f "${key_file_path}" 251 | } 252 | 253 | cert_update_etcd_cert() { 254 | local subj 255 | local subject_alt_name 256 | local cert 257 | 258 | # generate new etcd server, peer certificate (extendedKeyUsage = serverAuth, clientAuth) 259 | # /etc/kubernetes/pki/etcd/server.crt 260 | # /etc/kubernetes/pki/etcd/peer.crt 261 | for cert in ${KUBE_ETCD_CERT_SERVER} ${KUBE_ETCD_CERT_PEER}; do 262 | log_debug "updating ${cert}.crt" 263 | subj=$(cert_get_subj "${cert}") 264 | subject_alt_name=$(cert_get_subject_alt_name "${cert}") 265 | cert_gen_cert "${cert}" "peer" "${subj}" "${KUBE_CERT_DAYS}" "${KUBE_ETCD_CERT_CA}" "${subject_alt_name}" 266 | log_info "updated ${COLOR_BLUE}${cert}.crt${COLOR_NC}" 267 | done 268 | 269 | # generate new etcd healthcheck-client, apiserver-etcd-client certificate (extendedKeyUsage = clientAuth) 270 | # /etc/kubernetes/pki/etcd/healthcheck-client.crt 271 | # /etc/kubernetes/pki/apiserver-etcd-client.crt 272 | for cert in ${KUBE_ETCD_CERT_HEALTHCHECK_CLIENT} ${KUBE_ETCD_CERT_APISERVER_ETCD_CLIENT}; do 273 | log_debug "updating ${cert}.crt" 274 | subj=$(cert_get_subj "${cert}") 275 | cert_gen_cert "${cert}" "client" "${subj}" "${KUBE_CERT_DAYS}" "${KUBE_ETCD_CERT_CA}" 276 | log_info "updated ${COLOR_BLUE}${cert}.crt${COLOR_NC}" 277 | done 278 | 279 | # restart etcd pod if needed 280 | # This will restart etcd if KUBE_RESTART_SERVICES is set to true 281 | restart_etcd 282 | } 283 | 284 | restart_etcd() { 285 | # restart etcd if needed 286 | if [[ "${KUBE_RESTART_SERVICES}" == "true" ]]; then 287 | log_debug "restarting etcd" 288 | set +e 289 | case ${KUBE_CRI} in 290 | "docker") 291 | docker ps 2>/dev/null | grep 'k8s_etcd' | awk '{print$1}' | xargs -r -I '{}' docker restart {} >/dev/null 2>&1 292 | ;; 293 | "containerd") 294 | crictl --runtime-endpoint unix:///run/containerd/containerd.sock pods | grep 'etcd-' | awk '{print$1}' | xargs -r -I '{}' crictl --runtime-endpoint unix:///run/containerd/containerd.sock stopp {} >/dev/null 2>&1 295 | ;; 296 | esac 297 | is_etcd_restarted=$? 298 | set -e 299 | if [[ "${is_etcd_restarted}" != "0" ]]; then 300 | IS_NEED_RESTART_CONTROL_PLANE_MANUALLY=true 301 | log_warning "failed to restart etcd, please restart etcd manually" 302 | else 303 | log_info "restarted etcd" 304 | fi 305 | else 306 | IS_NEED_RESTART_CONTROL_PLANE_MANUALLY=true 307 | log_info "please restart etcd manually, KUBE_RESTART_SERVICES is set to false" 308 | fi 309 | } 310 | 311 | cert_update_master_cert() { 312 | local subj 313 | local subject_alt_name 314 | local conf 315 | 316 | # generate new apiserver server certificate (extendedKeyUsage = serverAuth) 317 | # /etc/kubernetes/pki/apiserver.crt 318 | subj=$(cert_get_subj "${KUBE_CERT_APISERVER}") 319 | log_debug "updating ${KUBE_CERT_APISERVER}.crt" 320 | subject_alt_name=$(cert_get_subject_alt_name "${KUBE_CERT_APISERVER}") 321 | cert_gen_cert "${KUBE_CERT_APISERVER}" "server" "${subj}" "${KUBE_CERT_DAYS}" "${KUBE_CERT_CA}" "${subject_alt_name}" 322 | log_info "updated ${COLOR_BLUE}${KUBE_CERT_APISERVER}.crt${COLOR_NC}" 323 | 324 | # generate new apiserver-kubelet-client certificate (extendedKeyUsage = clientAuth) 325 | # /etc/kubernetes/pki/apiserver-kubelet-client.crt 326 | log_debug "updating ${KUBE_CERT_APISERVER_KUBELET_CLIENT}.crt" 327 | subj=$(cert_get_subj "${KUBE_CERT_APISERVER_KUBELET_CLIENT}") 328 | cert_gen_cert "${KUBE_CERT_APISERVER_KUBELET_CLIENT}" "client" "${subj}" "${KUBE_CERT_DAYS}" "${KUBE_CERT_CA}" 329 | log_info "updated ${COLOR_BLUE}${KUBE_CERT_APISERVER_KUBELET_CLIENT}.crt${COLOR_NC}" 330 | 331 | # generate new kubeconf for controller-manager,scheduler and kubelet (extendedKeyUsage = clientAuth) 332 | # /etc/kubernetes/controller-manager.conf,scheduler.conf,admin.conf,kubelet.conf,super-admin.conf 333 | # Note: kubelet.conf does not need to update for K8s v1.17+ unless it contains kubelet-client-current.pem, 334 | # it will be skipped in the KUBE_MASTER_CONF_LIST if it does not contain the kubelet-client-current.pem 335 | # Note: super-admin.conf was added in v1.29.0, it will be included in the KUBE_MASTER_CONF_LIST if it exists 336 | for conf in "${KUBE_MASTER_CONF_LIST[@]}"; do 337 | # update kubeconf 338 | log_debug "updating ${conf}.conf" 339 | cert_update_kubeconf "${conf}" 340 | log_info "updated ${COLOR_BLUE}${conf}.conf${COLOR_NC}" 341 | done 342 | 343 | # generate new front-proxy-client certificate (extendedKeyUsage = clientAuth) 344 | # /etc/kubernetes/pki/front-proxy-client 345 | log_debug "updating ${KUBE_FRONT_PROXY_CLIENT}.crt" 346 | subj=$(cert_get_subj "${KUBE_FRONT_PROXY_CLIENT}") 347 | cert_gen_cert "${KUBE_FRONT_PROXY_CLIENT}" "client" "${subj}" "${KUBE_CERT_DAYS}" "${KUBE_FRONT_PROXY_CA}" 348 | log_info "updated ${COLOR_BLUE}${KUBE_FRONT_PROXY_CLIENT}.crt${COLOR_NC}" 349 | 350 | # restart apiserver, controller-manager, scheduler and kubelet if needed 351 | restart_control_plane 352 | } 353 | 354 | restart_control_plane() { 355 | # restart control-plane pods if needed 356 | if [[ "${KUBE_RESTART_SERVICES}" == "true" ]]; then 357 | for item in "apiserver" "controller-manager" "scheduler"; do 358 | log_debug "restarting control-plane pod: ${item}" 359 | set +e 360 | case ${KUBE_CRI} in 361 | "docker") 362 | docker ps 2>/dev/null | awk "k8s_kube-${item}" | awk '{print$1}' | xargs -r -I '{}' docker restart {} >/dev/null 2>&1 363 | ;; 364 | "containerd") 365 | crictl --runtime-endpoint unix:///run/containerd/containerd.sock pods | grep "kube-${item}-" | awk '{print $1}' | xargs -r -I '{}' crictl --runtime-endpoint unix:///run/containerd/containerd.sock stopp {} >/dev/null 2>&1 366 | ;; 367 | esac 368 | is_control_plane_restarted=$? 369 | set -e 370 | if [[ "${is_control_plane_restarted}" != "0" ]]; then 371 | IS_NEED_RESTART_CONTROL_PLANE_MANUALLY=true 372 | log_warning "failed to restart ${item}, please restart ${item} manually" 373 | else 374 | log_info "restarted control-plane pod: ${item}" 375 | fi 376 | done 377 | 378 | if [[ "${IS_KUBELET_NEED_RESTART}" == "true" ]]; then 379 | set +e 380 | systemctl restart kubelet 381 | is_kubelet_restarted=$? 382 | set -e 383 | if [[ "${is_kubelet_restarted}" != "0" ]]; then 384 | log_warning "failed to restart kubelet, please restart kubelet manually 385 | systemctl restart kubelet" 386 | else 387 | log_info "restarted kubelet" 388 | fi 389 | fi 390 | else 391 | IS_NEED_RESTART_CONTROL_PLANE_MANUALLY=true 392 | log_info "please restart control-plane pods manually, KUBE_RESTART_SERVICES is set to false" 393 | fi 394 | } 395 | 396 | # get certificate expires date 397 | cert_get_cert_expires_date() { 398 | local cert_file_path=${1}.crt 399 | local cert_expires 400 | 401 | cert_expires=$(openssl x509 -text -noout -in "${cert_file_path}" 2>/dev/null | awk -F ": " '/Not After/{print$2}') 402 | printf "%s\n" "${cert_expires}" 403 | } 404 | 405 | # get kubeconfig expires date 406 | cert_get_kubeconfig_expires_date() { 407 | local config_file_path=${1}.conf 408 | local cert_content 409 | local cert_expires 410 | 411 | cert_content=$(grep "client-certificate-data" "${config_file_path}" 2>/dev/null | awk '{print$2}' | base64 -d) 412 | cert_expires=$(openssl x509 -text -noout -in <(printf "%s" "${cert_content}") 2>/dev/null | awk -F ": " '/Not After/{print$2}') 413 | printf "%s\n" "${cert_expires}" 414 | } 415 | 416 | # check etcd certificates expires information 417 | cert_check_etcd_certs_expires() { 418 | local cert 419 | local name 420 | 421 | for cert in "${KUBE_ETCD_CERT_LIST[@]}"; do 422 | name=${cert##*/} 423 | [[ "${name}" == "apiserver-etcd-client" ]] || name="etcd/${name}" 424 | printf "| %-33s | %-27s|\n" "${name}.crt" "$(cert_get_cert_expires_date "${cert}")" 425 | done 426 | } 427 | 428 | split_line() { 429 | printf "|%b|%b|\n" "-----------------------------------" "----------------------------" 430 | } 431 | 432 | # check master certificates expires information 433 | cert_check_master_certs_expires() { 434 | local cert 435 | local conf 436 | local name 437 | 438 | for cert in "${KUBE_MASTER_CERT_LIST[@]}"; do 439 | name=${cert##*/} 440 | printf "| %-33s | %-27s|\n" "${name}.crt" "$(cert_get_cert_expires_date "${cert}")" 441 | done 442 | 443 | split_line 444 | 445 | for conf in "${KUBE_MASTER_CONF_LIST[@]}"; do 446 | name=${conf##*/} 447 | printf "| %-33s | %-27s|\n" "${name}.conf" "$(cert_get_kubeconfig_expires_date "${conf}")" 448 | done 449 | } 450 | 451 | # check all certificates expires 452 | cert_check_expires() { 453 | local cert 454 | local conf 455 | local name 456 | 457 | split_line 458 | printf "| %-33s | %-27s|\n" "CERTIFICATE" "EXPIRES" 459 | 460 | for cert in "${KUBE_MASTER_CERT_LIST[@]}"; do 461 | name=${cert##*/} 462 | printf "| %-33s | %-27s|\n" "${name}.crt" "$(cert_get_cert_expires_date "${cert}")" 463 | done 464 | 465 | split_line 466 | 467 | for conf in "${KUBE_MASTER_CONF_LIST[@]}"; do 468 | name=${conf##*/} 469 | printf "| %-33s | %-27s|\n" "${name}.conf" "$(cert_get_kubeconfig_expires_date "${conf}")" 470 | done 471 | 472 | split_line 473 | 474 | for cert in "${KUBE_ETCD_CERT_LIST[@]}"; do 475 | name=${cert##*/} 476 | [[ "${name}" == "apiserver-etcd-client" ]] || name="etcd/${name}" 477 | printf "| %-33s | %-27s|\n" "${name}.crt" "$(cert_get_cert_expires_date "${cert}")" 478 | done 479 | 480 | split_line 481 | } 482 | 483 | # backup kubernetes files, copy $KUBE_PATH to $KUBE_PATH.old-$(date +%Y-%m-%d_%H-%M-%S) 484 | cert_backup_kube_file() { 485 | local file=${KUBE_PATH} 486 | local time_date 487 | time_date=$(date +'%Y-%m-%d_%H-%M-%S') 488 | log_info "backup ${file} to ${file}.old-${time_date}" 489 | cp -rp "${file}" "${file}.old-${time_date}" 490 | } 491 | 492 | check_file() { 493 | local file=${1} 494 | if [[ ! -f ${file} ]]; then 495 | log_err "file not found: ${file}" 496 | exit 1 497 | elif [[ ! -r ${file} || ! -w ${file} ]]; then 498 | log_err "insufficient permissions for ${file}" 499 | exit 1 500 | fi 501 | log_info "found file: ${file}" 502 | } 503 | 504 | # make sure the etcd certificates are existed 505 | cert_check_etcd_files_existed() { 506 | local cert 507 | local conf 508 | 509 | # Check all certificate files 510 | for cert in "${KUBE_ETCD_CERT_LIST[@]}"; do 511 | check_file "${cert}.crt" 512 | check_file "${cert}.key" 513 | done 514 | 515 | # Check all kubeconfig files 516 | for conf in "${KUBE_MASTER_CONF_LIST[@]}"; do 517 | check_file "${conf}.conf" 518 | done 519 | } 520 | 521 | # make sure the master certificates are existed 522 | cert_check_master_files_existed() { 523 | local cert 524 | local conf 525 | 526 | # Check all certificate files 527 | for cert in "${KUBE_MASTER_CERT_LIST[@]}"; do 528 | check_file "${cert}.crt" 529 | check_file "${cert}.key" 530 | done 531 | 532 | # Check all kubeconfig files 533 | for conf in "${KUBE_MASTER_CONF_LIST[@]}"; do 534 | check_file "${conf}.conf" 535 | done 536 | } 537 | 538 | cert_gen_ca() { 539 | # make sure the directory exists 540 | if [[ ! -d "${KUBE_PKI_PATH}" ]]; then 541 | mkdir -p "${KUBE_PKI_PATH}" 542 | log_debug "created directory: ${KUBE_PKI_PATH}" 543 | fi 544 | # etcd 545 | if [[ ! -d "${KUBE_PKI_PATH}/etcd" ]]; then 546 | mkdir -p "${KUBE_PKI_PATH}/etcd" 547 | log_debug "created directory: ${KUBE_PKI_PATH}/etcd" 548 | fi 549 | 550 | local ca_list=("${KUBE_PKI_PATH}/ca" "${KUBE_PKI_PATH}/front-proxy-ca" "${KUBE_PKI_PATH}/etcd/ca") 551 | # Check if CA keys already exist 552 | for ca in "${ca_list[@]}"; do 553 | if [[ -f "${ca}.key" ]]; then 554 | log_err "${ca}.key already exists, make sure you want to regenerate the CA. please backup the existing ca certs, keys and remove them before regenerating the CA." 555 | exit 1 556 | fi 557 | if [[ -f "${ca}.crt" ]]; then 558 | log_err "${ca}.crt already exists, make sure you want to regenerate the CA. please backup the existing ca certs, keys and remove them before regenerating the CA." 559 | exit 1 560 | fi 561 | done 562 | 563 | csr_conf='distinguished_name = dn\n[dn]\n[ v3_ca_ext ]\nkeyUsage = critical, digitalSignature, keyEncipherment, keyCertSign\nbasicConstraints = critical, CA:true\n' 564 | 565 | # generate ca.crt 566 | log_info "generating k8s CA..." 567 | openssl genrsa -out "${KUBE_PKI_PATH}"/ca.key 2048 >/dev/null 2>&1 568 | log_debug "generated ${KUBE_PKI_PATH}/ca.key" 569 | openssl req -x509 -new -nodes -key "${KUBE_PKI_PATH}"/ca.key \ 570 | -subj "/CN=kubernetes" \ 571 | -config <(printf "%bsubjectAltName = DNS:kubernetes" "${csr_conf}") \ 572 | -extensions v3_ca_ext \ 573 | -days "${KUBE_CA_DAYS}" \ 574 | -out "${KUBE_PKI_PATH}"/ca.crt >/dev/null 2>&1 575 | log_info "generated ${COLOR_BLUE}${KUBE_PKI_PATH}/ca.crt${COLOR_NC}" 576 | 577 | # generate front-proxy-ca.crt 578 | log_info "generating front-proxy CA..." 579 | openssl genrsa -out "${KUBE_PKI_PATH}"/front-proxy-ca.key 2048 >/dev/null 2>&1 580 | log_debug "generated ${KUBE_PKI_PATH}/front-proxy-ca.key" 581 | openssl req -x509 -new -nodes -key "${KUBE_PKI_PATH}"/front-proxy-ca.key \ 582 | -subj "/CN=front-proxy-ca" \ 583 | -config <(printf "%bsubjectAltName = DNS:front-proxy-ca" "${csr_conf}") \ 584 | -extensions v3_ca_ext \ 585 | -days "${KUBE_CA_DAYS}" \ 586 | -out "${KUBE_PKI_PATH}"/front-proxy-ca.crt >/dev/null 2>&1 587 | log_info "generated ${COLOR_BLUE}${KUBE_PKI_PATH}/front-proxy-ca.crt${COLOR_NC}" 588 | 589 | # generate etcd/ca.crt 590 | log_info "generating etcd CA..." 591 | openssl genrsa -out "${KUBE_PKI_PATH}"/etcd/ca.key 2048 >/dev/null 2>&1 592 | log_debug "generated ${KUBE_PKI_PATH}/etcd/ca.key" 593 | openssl req -x509 -new -nodes -key "${KUBE_PKI_PATH}"/etcd/ca.key \ 594 | -subj "/CN=etcd-ca" \ 595 | -config <(printf "%bsubjectAltName = DNS:etcd-ca" "${csr_conf}") \ 596 | -extensions v3_ca_ext \ 597 | -days "${KUBE_CA_DAYS}" \ 598 | -out "${KUBE_PKI_PATH}"/etcd/ca.crt >/dev/null 2>&1 599 | log_info "generated ${COLOR_BLUE}${KUBE_PKI_PATH}/etcd/ca.crt${COLOR_NC}" 600 | } 601 | 602 | help() { 603 | printf "%b\n" " 604 | Usage: bash update-kubeadm-cert.sh [OPTIONS] 605 | Version: ${VERSION} 606 | Example: 607 | # renew certificates to 10 years for existing cluster 608 | bash update-kubeadm-cert.sh --cri containerd 609 | # generate 100 years CA before kubeadm init cluster (only used for new clusters) 610 | bash update-kubeadm-cert.sh --action gen-ca 611 | # check the expiration of the certificates without updating them 612 | bash update-kubeadm-cert.sh --action check 613 | Options: 614 | -c, --cri (default: containerd) 615 | Set the cri type, in order to restart control-plane and etcd service by different command, 'docker' or 'crictl'. 616 | -a, --action (default: update) 617 | update: Update certificates 10 years for existing clusters 618 | check: Only check the expiration of the certificates without updating them. 619 | gen-ca: Generate 100 years CA before kubeadm init cluster. (only used for new clusters, not for existing clusters) 620 | --days Set the number of days for certificate expiration. (default: 3650) 621 | -h, --help Show this help message and exit. 622 | 623 | more info: https://github.com/yuyicai/update-kube-cert 624 | " 625 | } 626 | 627 | main() { 628 | local action="update" # default action 629 | 630 | # read the options 631 | ARGS=$(getopt -n update-kubeadm-cert.sh -a -o a:c:h --long action:,cri:,days:,help -- "$@") 632 | eval set -- "$ARGS" 633 | # extract options and their arguments into variables. 634 | while true; do 635 | case "$1" in 636 | -h | --help) 637 | help 638 | exit 0 639 | ;; 640 | -a | --action) 641 | # Set the action (update, check, gen-ca) 642 | case "$2" in 643 | "update" | "check" | "gen-ca") 644 | action=$2 645 | shift 2 646 | ;; 647 | *) 648 | echo 'Unsupported action '"$2"'. Valid options are "update", "check", "gen-ca".' 649 | exit 1 650 | ;; 651 | esac 652 | ;; 653 | -c | --cri) 654 | # Set the container runtime interface (KUBE_CRI) to use. 655 | case "$2" in 656 | "docker" | "containerd") 657 | KUBE_CRI=$2 658 | shift 2 659 | ;; 660 | *) 661 | echo 'Unsupported cri '"$2"'. Valid options are "docker", "containerd".' 662 | exit 1 663 | ;; 664 | esac 665 | ;; 666 | --days) 667 | # This option is deprecated, use KUBE_CERT_DAYS and KUBE_CA_DAYS instead 668 | # Set the number of days for certificate expiration 669 | if [[ -n "$2" && "$2" =~ ^[0-9]+$ ]]; then 670 | KUBE_CERT_DAYS=$2 671 | shift 2 672 | else 673 | echo "Invalid value for --days. It should be a positive integer." 674 | exit 1 675 | fi 676 | ;; 677 | --) 678 | shift 679 | break 680 | ;; 681 | *) 682 | echo "Invalid arguments '$1'" 683 | help 684 | exit 1 685 | ;; 686 | esac 687 | done 688 | 689 | # Only check the expiration of the certificates without updating them 690 | if [[ "${action}" == "check" ]]; then 691 | log_info "checking certificate expiration only..." 692 | cert_check_expires 693 | log_info "${COLOR_GREEN}DONE!!!${COLOR_NC}" 694 | exit 0 695 | fi 696 | 697 | # Generate 100 years CA before kubeadm init cluster (only used for new clusters) 698 | if [[ "${action}" == "gen-ca" ]]; then 699 | log_info "generating CA with ${KUBE_CA_DAYS} days expiration..." 700 | cert_gen_ca 701 | cert_check_expires 702 | log_info "${COLOR_GREEN}DONE!!!${COLOR_NC} generated CA for new cluster. 703 | # create new cluster after generating CA, you can use the following command: 704 | kubeadm init [options] 705 | # after running kubeadm init, update certificates for 100 yeas 706 | bash update-kubeadm-cert.sh --cri containerd --days 36500 707 | " 708 | exit 0 709 | fi 710 | 711 | # make sure the certificates are existed 712 | log_info "checking if all certificate files are existed..." 713 | if [[ "${KUBE_SKIP_ETCD_CERTS}" != "true" ]]; then 714 | cert_check_etcd_files_existed 715 | else 716 | log_info "skipping etcd certificates check" 717 | fi 718 | cert_check_master_files_existed 719 | log_info "all certificate files are existed" 720 | 721 | # backup kubernetes files 722 | cert_backup_kube_file 723 | # check expires before updating the certificates 724 | log_info "checking certificate expiration before update..." 725 | cert_check_expires 726 | 727 | # update certificates 10 years for existing clusters 728 | log_info "updating certificates with ${KUBE_CERT_DAYS} days expiration..." 729 | 730 | # etcd 731 | if [[ "${KUBE_SKIP_ETCD_CERTS}" != "true" ]]; then 732 | # update etcd certificates 733 | cert_update_etcd_cert 734 | else 735 | log_info "skipping etcd certificates update" 736 | fi 737 | 738 | # master 739 | # update master certificates and kubeconf 740 | cert_update_master_cert 741 | 742 | # check expires after updating the certificates 743 | log_info "checking certificate expiration after update..." 744 | cert_check_expires 745 | 746 | log_info "${COLOR_GREEN}DONE!!!${COLOR_NC}enjoy it" 747 | 748 | # printf cofy admin.conf manually info 749 | printf "\n%b\n\n\n" "please copy admin.conf to ${HOME}/.kube/config manually. 750 | # back old config 751 | cp $HOME/.kube/config $HOME/.kube/config_backup 752 | # copy new admin.conf to ${HOME}/.kube/config for kubectl manually 753 | ${LOG_WARNING_COLOR}cp -i ${KUBE_PATH}/admin.conf ${HOME}/.kube/config${COLOR_NC}" 754 | 755 | if [[ "${IS_NEED_RESTART_CONTROL_PLANE_MANUALLY}" == "true" ]]; then 756 | log_warning "please restart control-plane pods manually" 757 | printf "\n%b\n" "${LOG_WARNING_COLOR}you can use the following command to restart control-plane pods:${COLOR_NC} 758 | # make sure kubelet is running 759 | systemctl restart kubelet 760 | # move manifests to trigger kubelet to recreate the pods 761 | mv /etc/kubernetes/manifests /etc/kubernetes/manifests_backup 762 | # wait for 2 minutes, let kubelet remove the old pods 763 | sleep 120 764 | # restore manifests, kubelet will recreate the pods 765 | mv /etc/kubernetes/manifests_backup /etc/kubernetes/manifests 766 | # check the status of control-plane pods 767 | kubectl get pods -n kube-system -o wide" 768 | fi 769 | } 770 | 771 | # call the main function with all command-line arguments 772 | main "$@" 773 | --------------------------------------------------------------------------------