Technical architectural analysis, practical configuration manifests, and deployment instructions for Kubernetes TLS Certificates & kubeadm certs.
Automated script
-
โ When renewing after expiration
Attempt to bootstrap
We will summarize the recovery sequence assuming the worst case scenario where bootstrapping is not set and both the control plane and kubelet certificates have already expired.
In this case,
kubectlwill not work because the API server is dead. Therefore, you must save the control plane first and then sequentially revive the kubelets of the nodes.โ
### Step 1: Renew control plane (Master) certificate
First, the certificates used by the API server, scheduler, and controller manager must be renewed to revive the โbrainโ of the cluster.
1. Check current expiration statusBash
kubeadm certs check-expiration2. Renew all control plane certificatesBash
kubeadm certs renew all3. Restart Static Pods
The certificate file has been updated, butkube-apiserver, which is already in memory, contains the old certificate. Forces a restart of Bash to read the file again.# If you move the manifest file for a while and then import it again, the container will restart.
mv /etc/kubernetes/manifests/*.yaml /tmp/
sleep 10
mv /tmp/*.yaml /etc/kubernetes/manifests/4. Update the admin configuration file (admin.conf)Replace my local
.kube/configwith the new file so that I can usekubectl.Bashcp /etc/kubernetes/admin.conf $HOME/.kube/config
chown $(id -u):$(id -g) $HOME/.kube/configโ
### Step 2: Prepare for manual renewal of Kubelet certificate (if bootstrap is not set)
Without bootstrapping (
bootstrap-kubelet.conf), the kubelet has no way to obtain the certificate itself. In this case, you must use the function ofkubeadmto temporarily open an authentication path.1. Grant RBAC permission (run on master)
Temporarily grant permission for the kubelet to send a certificate signing request (CSR). (Can be omitted if already set) Bashkubectl create clusterrolebinding kubelet-bootstrap --clusterrole=system:node-bootstrapper --group=system:bootstrappers2. Generate a new bootstrap tokenBash
kubeadm token create --print-join-commandCopy the token generated here.
โ
### Step 3: Kubelet recovery on each node (Worker/Master)
Now go to each node and have it issue a new certificate, ignoring the expired
kubelet-client-current.pem.1. Stop Kubelet and remove existing certificateBash
systemctl stop kubelet
# Remove expired certificates and symbolic links
rm /var/lib/kubelet/pki/kubelet-client-*.pem
rm /var/lib/kubelet/pki/kubelet-client-current.pem2. Regenerate kubelet.conf (Important)
If bootstrapping is not performed, the certificate information in/etc/kubernetes/kubelet.confmay also have expired. Reconfigure this file usingkubeadm.Bash# Run on node (using token and address received from master)
kubeadm join:6443 --token --discovery-token-ca-cert-hash sha256: --control-plane (add only if master) If the node is already joined, you may need the
--forceoption, or you may need to back up and run the existing.conffiles in/etc/kubernetes/.3. Starting KubeletBash
systemctl start kubeletโ
### Step 4: CSR approval (run on master)
The Kubelet comes to life and sends a request to the master, saying โIโm new, please authenticate me.โ
1. Check request listBash
kubectl get csr2. ApproveBash
# Approval of all CSRs in Pending status
kubectl get csr | grep Pending | awk '{print $1}' | xargs kubectl certificate approveโ
### Summary order
1. Master:
kubeadm certs renew alland then restart the Static Pod.
2. Master: Updateadmin.confto revivekubectl.
3. Master: Secure a new path withkubeadm token create.
4. Node: Delete the existingkubelet-client-*.pem.
5. Node: Attempt re-authentication with thekubeadm joincommand (or update relevant settings).
6. Final approval with Master:kubectl certificate approve.After completing this procedure,
kubectl get nodeswill return all nodes to theReadystate. Version 1.23 is a stable version ofkubeadm‘s certificate management commands, so the above commands should work well.
Ignore bootstrapping
The key to manually recovering expired control planes and kubelets without using bootstrapping (Token-based auto-join) is \*\*โcreating the certificate files yourself and updating kubeconfig manuallyโ\*\*
This method does not require
kubeadm joinortoken, but only useskubeadm‘s certificate renewal function and file copy.โ
### Step 1: Manually renew the control plane (Master) certificate
First of all, you need to save the โCertification Authority (CA)โ and API server related certificates.
1. Control plane certificate bulk renewalBash
kubeadm certs renew all2. Restart Static Pods (Load Certificate)Bash
# Move the manifest for a while and copy it to induce a container restart
mv /etc/kubernetes/manifests/*.yaml /tmp/
sleep 5
mv /tmp/*.yaml /etc/kubernetes/manifests/3. Update kubeconfig for administratorBash
cp /etc/kubernetes/admin.conf $HOME/.kube/configโ
### Step 2: Manually update the configuration file (
kubelet.conf) for KubeletIf you do not use bootstrapping, you must manually update the client certificate data in the
/etc/kubernetes/kubelet.conffile.kubeadmprovides dedicated commands for this process.\[Performed on all nodes (including Master)\]
1. Renew certificate in kubelet configuration fileBash
kubeadm init phase kubeconfig kubelet --config /etc/kubernetes/kubeadm-config.yaml (or can be omitted if using default)This command creates a new file
/etc/kubernetes/kubelet.confand includes the new embedded certificate inside it.2. Clean up existing individual PEM filesBash
If you do not use bootstrapping, the data in
/etc/kubernetes/kubelet.confwill be referenced first, notkubelet-client-current.pemin/var/lib/kubelet/pki/. Leave existing files aside to avoid conflicts.rm -f /var/lib/kubelet/pki/kubelet-client-*.pem
rm -f /var/lib/kubelet/pki/kubelet-client-current.pemโ
### Step 3: Restart Kubelet services and check permissions
Now let the kubelet on each node read the new
kubelet.confand communicate with the API server.1. Restart KubeletBash
systemctl restart kubelet2. CSR manual approval (if necessary)Bash
In some environments, the kubelet may request additional serving certificates even after updating
kubelet.conf. The master confirms and approves.kubectl get csr
kubectl certificate approveโ
### Step 4: How do I get
kubelet-client-current.pemback?The
/var/lib/kubelet/pki/kubelet-client-current.pemfile that the user mentioned at the beginning of the question is actually a file created when Kubelet’s Rotation feature is turned on.If you want to keep this file again without bootstrapping, your kubelet configuration (
config.yaml) withkubelet.confupdated must include:โ
rotateCertificates: true
โserverTLSBootstrap: true(optional)### Organized command flow chart
Step Execution Location Command 1. CP Renewal Master kubeadm certs renew all2. Update settings all nodes kubeadm init phase kubeconfig kubelet3. Organize your files all nodes rm -f /var/lib/kubelet/pki/kubelet-client*4. Restart all nodes systemctl restart kubeletThis way, you can manually replace the certificate and operate normally through the static configuration file created by
kubeadmwithout going through the token-based bootstrapping (bootstrap-kubelet.conf) process.When running
kubeadm init phase, does an error occur saying that a specific configuration file cannot be found? Please let me know if you need akubeadm-configdump for that cluster.
Basis
The client certificate created with kubeadm expires after one year.
# kubectl ๋ช
๋ น์ด ์ฌ์ฉ์
Unable to connect to the server: x509: certificate has expired or is not yet valid.
Check certificate expiration time
sudo kubeadm certs check-expiration
#>>> ์ถ๋ ฅ
#[check-expiration] Reading configuration from the cluster...
#[check-expiration] FYI: You can look at this config file with 'kubectl -n kube-system get cm kubeadm-config -o yaml'
#W0818 13:43:59.620410 4001964 utils.go:69] The recommended value for "clusterDNS" in "KubeletConfiguration" is: [10.233.0.10]; the provided value is: [10.233.0.3]
#
#CERTIFICATE EXPIRES RESIDUAL TIME CERTIFICATE AUTHORITY EXTERNALLY MANAGED
#admin.conf Aug 17, 2024 01:21 UTC 364d ca no
#apiserver Aug 17, 2024 01:21 UTC 364d ca no
#apiserver-kubelet-client Aug 17, 2024 01:21 UTC 364d ca no
#controller-manager.conf Aug 17, 2024 01:21 UTC 364d ca no
#front-proxy-client Aug 17, 2024 01:21 UTC 364d front-proxy-ca no
#scheduler.conf Aug 17, 2024 01:21 UTC 364d ca no
#
#CERTIFICATE AUTHORITY EXPIRES RESIDUAL TIME EXTERNALLY MANAGED
#ca Aug 14, 2032 13:09 UTC 8y no
#front-proxy-ca Aug 14, 2032 13:09 UTC 8y no
#<<<
Perform
Tasks
# ๋ชจ๋ master ๋
ธ๋
kubeadm certs renew all #or ๋ค๋ฅธ ๋ฐฉ๋ฒ์ผ๋ก ๊ฐฑ์
# ๊ฐฑ์ ๋ kubeapiserver config CLI ์ธ์ฆ์ ์ฌ์ฉ
mv ~/.kube/config ~/.kube/config-backup
cp /etc/kubernetes/admin.conf ~/.kube/config
# !! kubeconfig๋ฅผ ์ปค์คํ
ํด์ ์ฌ์ฉ์ค์ด๋ผ๋ฉด ์ ์ฒด ๋ณต์ฌ/๋ถ์ฌ๋ฃ๊ธฐ๊ฐ ์๋๋ผ ์๋์ ๋ด์ฉ๋ง ๊ฐฑ์ ํ๋ฉด ๋ฉ๋๋ค.
# client-certificate-data: <crt-sting>
# client-key-data: <key-string>
Additional tasks
Among the logic below, kubeadm init phase kubeconfig may have been reflected in kubeadm certs renew all, so you need to check.
- Check that
admin.confis actually reflected - Confirm that
kubelet.confis not reflected. - Need to check
scheduler.conf,controller-manager.conf
There is a possibility of unauthorized occurrence because the static pod is not restarted.
- master nodes
Error Logs (kube-scheduler)
W0820 05:54:38.069851 1 reflector.go:324] vendor/k8s.io/client-go/informers/factory.go:134: failed to list *v1.PersistentVolumeClaim: Unauthorized
E0820 05:54:38.069907 1 reflector.go:138] vendor/k8s.io/client-go/informers/factory.go:134: Failed to watch *v1.PersistentVolumeClaim: failed to list *v1.PersistentVolumeClaim: Unauthorized
W0820 05:54:38.943924 1 reflector.go:324] vendor/k8s.io/client-go/informers/factory.go:134: failed to list *v1.StatefulSet: Unauthorized
E0820 05:54:38.943996 1 reflector.go:138] vendor/k8s.io/client-go/informers/factory.go:134: Failed to watch *v1.StatefulSet: failed to list *v1.StatefulSet: Unauthorized
W0820 05:54:39.911036 1 reflector.go:324] vendor/k8s.io/client-go/informers/factory.go:134: failed to list *v1.CSINode: Unauthorized
E0820 05:54:39.911123 1 reflector.go:138] vendor/k8s.io/client-go/informers/factory.go:134: Failed to watch *v1.CSINode: failed to list *v1.CSINode: Unauthorized
W0820 05:54:41.927021 1 reflector.go:324] vendor/k8s.io/client-go/informers/factory.go:134: failed to list *v1.Pod: Unauthorized
E0820 05:54:41.927132 1 reflector.go:138] vendor/k8s.io/client-go/informers/factory.go:134: Failed to watch *v1.Pod: failed to list *v1.Pod: Unauthorized
- How to reissue a certificate (highly unlikely to be needed)
# ์ธ์ฆ์ ๊ฐฑ์ ํ kube-scheduler, kube-controller-manager ์ธ์ฆ์๋ฌ ๋ฐ์
# ํด๋ผ์ด์ธํธ์ธ์ฆ์ ๋ฏธ์ฌ์ฉ์ผ๋ก ์ธํจ
# ๋ฐฑ์
ํ ์ญ์
mv /etc/kubernetes/scheduler.conf /etc/kubernetes/scheduler.conf.bak
mv /etc/kubernetes/controller-manager.conf /etc/kubernetes/controller-manager.conf.bak
mv /etc/kubernetes/kubelet.conf /etc/kubernetes/kubelet.conf.bak
# ์ญ์ ๋์ด์์ด์ผ ์ ๊ท๋ฐ๊ธ๋จ
kubeadm init phase kubeconfig scheduler
kubeadm init phase kubeconfig controller-manager
kubeadm init phase kubeconfig kubelet
- How to apply a reissued certificate
# kubelet ์ฌ์์
mkdir ~/kubelet-backup
mv /var/lib/kubelet/pki/* ~/kubelet-backup
systemctl restart kubelet
cd /etc/kubernetes/manifests
# ๊ธฐ์กด ํ๋ก์ธ์ค ์ข
๋ฃ
mv kube-controller-manager.yaml ..
mv kube-scheduler.yaml ..
# ์ค์ง ํ์ธ
kubectl -n kube-system get pods -l 'component in (kube-controller-manager,kube-scheduler)'
# ์ฌ์์
mv ../kube-controller-manager.yaml .
mv ../kube-scheduler.yaml .
- worker nodes
# ์๋์๊ฐ์ด ํ์ผ๋ก๋ช
์๋์ด์์ผ๋ฉด ์๋๊ฐฑ์ ๋๊ณ ์์ ๊ฐ๋ฅ์ฑ์ด ๋์. ๋ง์คํฐ๋
ธ๋๋ ํด๋น์ค์ ๋์ด์์ผ๋ฉด ์กฐ์นX
grep client-certificate /etc/kubernetes/kubelet.conf
client-certificate: /var/lib/kubelet/pki/kubelet-client-current.pem
Olds
old
##### Action\*\*\*\*: Run on each master node\*\*\*\*
1. Backup
# [[ master node ]]
## kube pki backup
sudo mkdir -p $HOME/fcik8s-old-certs/pki
sudo mkdir -p $HOME/fcik8s-old-certs/kubelet
sudo mkdir -p $HOME/fcik8s-old-certs/.kube
sudo cp -p /etc/kubernetes/pki/*.* $HOME/fcik8s-old-certs/pki
sudo cp -p /etc/kubernetes/*.conf $HOME/fcik8s-old-certs
sudo cp -p /var/lib/kubelet/pki/* $HOME/fcik8s-old-certs/kubelet
sudo cp -p ~/.kube/config $HOME/fcik8s-old-certs/.kube/.
2. Update
sudo kubeadm certs renew all --config=/etc/kubernetes/kubeadm-config.yaml
>>>
W0818 10:21:42.676045 3888005 utils.go:69] The recommended value for "clusterDNS" in "KubeletConfiguration" is: [10.233.0.10]; the provided value is: [10.233.0.3]
certificate embedded in the kubeconfig file for the admin to use and for kubeadm itself renewed
certificate for serving the Kubernetes API renewed
certificate for the API server to connect to kubelet renewed
certificate embedded in the kubeconfig file for the controller manager to use renewed
certificate for the front proxy client renewed
certificate embedded in the kubeconfig file for the scheduler manager to use renewed
Done renewing certificates. You must restart the kube-apiserver, kube-controller-manager, kube-scheduler and etcd, so that they can use the new certificates.
<<<
# ํ์ธ
sudo kubeadm certs check-expiration
# kublet ์ฌ์์
#systemctl daemon-reload&&systemctl restart kubelet
# control-plane components ์ข
๋ฃ
sudo cp -pr /etc/kubernetes/manifest $HOME/fcik8s-old-certs
sudo rm /etc/kubernetes/manifest/*
# componets container ํ๋ก์ธ์ค ํ์ธ
sudo nerdctl ps
# control-plane components ์ฌ์์
sudo cp -p $HOME/fcik8s-old-certs/manifest/* /etc/kubernetes/manifest
kubectl get pods -n kube-system -l component=kube-apiserver
kubectl get pods -n kube-system -l component=kube-controller-manager
kubectl get pods -n kube-system -l component=kube-scheduler
kubectl get pods -n kube-system -l component=etcd
kubectl get nodes
3. Issue tracking
# log ํ์ธ
journalctl -u kubelet -f
journalctl -u etcd -f
journalctl -u containerd -f
kubectl logs -n kube-system -l component=kube-apiserver
kubectl logs -n kube-system -l component=kube-controller-manager
kubectl logs -n kube-system -l component=kube-scheduler
kubectl logs -n kube-system -l component=etcd
#!! kubeadm 1.17 ์ด์ ์ ๋ฒ์ ์์ kubeadm init ์ผ๋ก ์์ฑ๋ Node ์๋ kubelet.conf ํ์ผ์ ์๋์ผ๋ก ์์ ํด์ผ ํ๋ ๋ฒ๊ทธ๊ฐ ์๋ค.
## ์ต๋ 5๋ถ๋์ ์ ์ฉ์ฌ๋ถ ๋ชจ๋ํฐ๋ง ํ์
kubectl get nodes #์ ์์กฐํ ๋ถ๊ฐํ๋ค๋ฉด kubelet ๊ตฌ์ฑ ํน์ kubeconfig ํ์ผ ํ์ธ ํ์
### error: You must be logged in to the server (Unauthorized) ๋ฐ์์
### -> [2] ์กฐ์น์ฌํญ ํ์ธ
### [1] kubelet.conf ๋ฏธ๊ฐฑ์ ์ ๊ฒฝ์ฐ
sudo diff $HOME/fcik8s-old-certs/kubelet.conf /etc/kubernetes/kubelet.conf #์ฐจ์ด๊ฐ ์์ผ๋ฉด ๋ฏธ๊ฐฑ์
## ์ฌ์ฉ ์ ๋ถ์ ํ์
#cd /etc/kubernetes
sudo kubeadm kubeconfig user --org system:nodes --client-name system:node:$(hostname) > kubelet.conf
# kubelet.conf์ ํ์ผ๋ก ๋ช
์ํ๋ค๋ฉด
### [1-2] v1.23.7์์ ํ์ธ์ ๊ฐฑ์ ํ์ง ์์๊ฒ์ด ์ ์์ผ๋ก ๋ณด์
sudo diff $HOME/fcik8s-old-certs/kubelet.conf /etc/kubernetes/kubelet.conf
sudo diff $HOME/fcik8s-old-certs/kubelet/kubelet-client-current.pem /var/lib/kubelet/pki/kubelet-client-current.pem
sudo diff $HOME/fcik8s-old-certs/kubelet/kubelet.key /var/lib/kubelet/pki/kubelet.key
sudo diff $HOME/fcik8s-old-certs/kubelet/kubelet.crt /var/lib/kubelet/pki/kubelet.crt
### [2] kubeconfig ๋ฏธ๊ฐฑ์ ์ ๊ฒฝ์ฐ
sudo diff ~/.kube/config $HOME/fcik8s-old-certs/.kube/config #์ฐจ์ด๊ฐ ์์ผ๋ฉด ๋ฏธ๊ฐฑ์
cat /etc/kubernetes/kubelet.conf # ๋ด์ฉ ์ฐธ์กฐํ์ฌ ์ฝ์
. ํ์ผ์ ๊ฒฝ์ฐ [์ฌ๊ธฐ](<https://www.notion.so/cmd-cm-base64-d7e4a9892c6a4eaf8abc0e9ec1a13684?pvs=21>) ์ฐธ์กฐ
client-certificate -> ~/.kube./config:client-certificate-data
client-key -> ~/.kube./config:client-key-data
systemctl daemon-reload&&systemctl restart kubelet
kubectl get nodes
4. Reference: Previous version update
[SKT Enterprise](https://www.sktenterprise.com/bizInsight/blogDetail/dev/2918)
# [[ master node ]]
### **1. ์ธ์ฆ์๊ฐ ์กด์ฌํ๋ pki ๋๋ ํ ๋ฆฌ๋ฅผ ๋ฐฑ์
ํฉ๋๋ค.**
cp -f /etc/kubernetes/pki /etc/kubernetes/pki-backup
### **2. ์ธ์ฆ์๋ฅผ ์ผ๊ด์ ์ผ๋ก ์
๋ฐ์ดํธ ํฉ๋๋ค.**
kubeadm certs renew all
### **3. ๊ฐฑ์ ์ฌ๋ถ๋ฅผ ๋ค์ ์ฒดํฌํฉ๋๋ค.**
[12:56:54][danawa@kube3 /etc/kubernetes/pki]
$ openssl x509 -in apiserver.crt -noout -text |grep ' Not '
Not Before: Mar 25 08:12:56 2020 GMT
Not After : Mar 28 02:26:40 2023 GMT
#์ ์์ ์ผ๋ก 1๋
์ฐ์ฅ ๋์์ต๋๋ค.
### **4. ์ฌ์ฉ์ ๊ตฌ์ฑ์ ์
๋ฐ์ดํธ ํ๊ธฐ ์ํด kubernetes ๋๋ ํ ๋ฆฌ๋ฅผ ๋ฐฑ์
ํฉ๋๋ค.**
cp -f /etc/kubernetes /etc/kubernetes-backup
### **5. ์ฌ์ฉ์ ๊ตฌ์ฑ์ ์
๋ฐ์ดํธํฉ๋๋ค.**
kubeadm kubeconfig user --client-name=admin --config=/etc/kubernetes/kubeadm-config.yaml
kubeadm kubeconfig user --org system:masters --client-name=kubernetes-admin --config=/etc/kubernetes/kubeadm-config.yaml > /etc/kubernetes/admin.conf
kubeadm kubeconfig user --client-name system:kube-controller-manager --config=/etc/kubernetes/kubeadm-config.yaml > /etc/kubernetes/controller-manager.conf
kubeadm kubeconfig user --org system:nodes --client-name system:node:$(hostname) --config=/etc/kubernetes/kubeadm-config.yaml > /etc/kubernetes/kubelet.conf
kubeadm kubeconfig user --client-name system:kube-scheduler --config=/etc/kubernetes/kubeadm-config.yaml > /etc/kubernetes/scheduler.conf
### **6. /root/.kube/config๋ฅผ ๊ต์ฒดํฉ๋๋ค.**
cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
chown $(id -u):$(id -g) $HOME/.kube/config
### **7. ์๋น์ค๋ฅผ ์ฌ์์ํฉ๋๋ค.**
systemctl daemon-reload&&systemctl restart kubelet
### csr approve
kubectl certificate approve $(kubectl get csr | grep Pending | awk '{print $1}' | sed -z 's/[\\n\\r]/ /g')
# [[ worker node ]]
## master์์ ๋ณต์ฌํ ํ ๋ถ์ฌ๋ฃ๊ธฐ
vi /etc/kubernetes/kubelet.conf
>>>
# ์๋ ip locahost๋ก ์์
server: <https://localhost:6443>
<<<
systemctl restart kubelet
# systemctl ์๋น์ค์์ ์ฌ์ฉํ ๊ฒฝ์ฐ
systemctl restart kube-apiserver
systemctl restart kube-scheduler
# ๋์ปค ์ปจํ
์ด๋๋ฅผ ์ฌ์ฉํ ๊ฒฝ์ฐ
docker restart [apiserver ์ปจํ
์ด๋]
docker restart [scheduler ์ปจํ
์ด๋]
docker restart [controller ์ปจํ
์ด๋]
##### Other methods
###### References
[Certificate Management with kubeadm](https://kubernetes.io/docs/tasks/administer-cluster/kubeadm/kubeadm-certs/#automatic-certificate-renewal)




Leave a Reply