[K8S] 인증서 갱신하기

Kaite.Kang·2025년 2월 9일

쿠버네티스 인증서의 만료 기한은 kubeadm certs check-expiration 명령으로 확인할 수 있다.
현재 테스트 환경에서는 인증서들의 만료 기한은 1년으로 설정되어 있다.
이번 포스팅에서는 인증서의 구성 요소와 재발급 방법에 대해 살펴보자.

클러스터 구성 환경

  • OS: Ubuntu 20.04.01 LTS
  • kubespray 2.26.0 를 통해 구성
  • master node(3개)/ worker node(2개)
  • kubernetes v1.31
  • docker 26.1.2
  • etcd 3.5 -> systemd 기반 동작

CA와 인증서의 관계

쿠버네티스는 클러스터 구성소요 간에 안전한 통신을 위해 Public Key 기반 구조(PKI)를 사용한다. CA(Certificate Authority, 인증 기관)는 클러스터 내 모든 인증서를 발급하고 서명하는 역할을 한다. CA가 서명한 인증서는 클러스터 내에서 신뢰할 수 있는 것으로 간주된다.
예를 들어, 클러스터 내에서 apiserver, kubelet, scheduler 등이 서로를 인증할 때, 각 구성 요소의 인증서는 CA가 서명했기 때문에 서로를 신뢰할 수 있다.

테스트 클러스터 환경에서는 apiserver, kubelet, scheduler 인증서는 같은 CA를 사용하여 발급된 것을 확인할 수 있다.

root@kjh-m1:~# 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'
W0209 23:45:46.495110 1159802 utils.go:69] The recommended value for "clusterDNS" in "KubeletConfiguration" is: [10.233.0.10]; the provided value is: [169.254.25.10]

CERTIFICATE                EXPIRES                  RESIDUAL TIME   CERTIFICATE AUTHORITY   EXTERNALLY MANAGED
admin.conf                 Feb 07, 2026 00:26 UTC   362d            ca                      no
apiserver                  Feb 07, 2026 00:26 UTC   362d            ca                      no
apiserver-kubelet-client   Feb 07, 2026 00:26 UTC   362d            ca                      no
controller-manager.conf    Feb 07, 2026 00:26 UTC   362d            ca                      no
front-proxy-client         Feb 07, 2026 00:26 UTC   362d            front-proxy-ca          no
scheduler.conf             Feb 07, 2026 00:26 UTC   362d            ca                      no
super-admin.conf           Feb 07, 2026 00:26 UTC   362d            ca                      no

CERTIFICATE AUTHORITY   EXPIRES                  RESIDUAL TIME   EXTERNALLY MANAGED
ca                      Dec 03, 2034 05:08 UTC   9y              no
front-proxy-ca          Dec 03, 2034 05:08 UTC   9y              no

etcd는 자체 CA를 사용하여 독립적으로 인증서를 발급되었는데, 그 이유는 kubespray 의 스크립트에 의해서 Etcd만 독립적으로 인증서를 발급한 것을 알 수 있다.

root@kjh-m1:~/kubespray/roles/etcd# pwd
/root/kubespray/roles/etcd
root@kjh-m1:~/kubespray/roles/etcd# grep -r ssl |head -1
tasks/main.yml:  command: "openssl x509 -in {{ etcd_cert_dir }}/node-{{ inventory_hostname }}.pem -noout -serial"

인증서 재발급하기

1. etcd 스냅샷 생성하기

etcd 데이터 스냅샷은 현재 etcd 클러스터에 저장된 모든 키-값 데이터를 백업하는 파일이다. 즉, Kubernetes 클러스터의 상태를 저장하는 데이터베이스의 백업본이라고 보면 된다.
etcd는 모든 노드가 동일한 데이터를 가지므로, 한 개의 노드에서만 스냅샷을 생성하면 된다. 가능하면 리더 노드에서 생성하는 것이 가장 안전하다.

  • 작업 대상: etcd 리더가 올라간 마스터 노드
# etcd 리더 확인 -> master node1 번에 있는 Etcd가 리더이다.
root@kjh-m1:~# ETCDCTL_API=3 etcdctl endpoint status \
   --write-out=table \
   --endpoints=https://192.168.122.21:2379,https://192.168.122.22:2379,https://192.168.122.23:2379 \
   --cacert=/etc/ssl/etcd/ssl/ca.pem \
   --cert=/etc/ssl/etcd/ssl/admin-kjh-m1.pem \
   --key=/etc/ssl/etcd/ssl/admin-kjh-m1-key.pem
+-----------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+
|          ENDPOINT           |        ID        | VERSION | DB SIZE | IS LEADER | IS LEARNER | RAFT TERM | RAFT INDEX | RAFT APPLIED INDEX | ERRORS |
+-----------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+
| https://192.168.122.21:2379 | e99611c964d08e01 |  3.5.16 |   11 MB |      true |      false |       172 |   16432203 |           16432203 |        |
| https://192.168.122.22:2379 | aac23c77cb18b0cc |  3.5.16 |   11 MB |     false |      false |       172 |   16432202 |           16432202 |        |
| https://192.168.122.23:2379 | 83b998bb779c8db9 |  3.5.16 |   11 MB |     false |      false |       172 |   16432204 |           16432203 |        |
+-----------------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+

# 스냅샷 생성
ETCDCTL_API=3 etcdctl --endpoints=https://127.0.0.1:2379 \
--cacert=/etc/ssl/etcd/ssl/ca.pem --cert=/etc/ssl/etcd/ssl/admin-kjh-m1.pem \
--key=/etc/ssl/etcd/ssl/admin-kjh-m1-key.pem \
snapshot save ~/etcd_snapshot.db

## 스냅샷 생성 결과 확인
ll ~/etcd_snapshot.db
ETCDCTL_API=3 etcdutl snapshot status ~/etcd_snapshot.db --write-out=table

2. 인증서 만료 기한 확인

k8s 각 구성요소들은 인증서들은 만료 기한이 1년 CA는 10년이다.

  • k8s 구성 요소(apiserver, kubelet, scheduler)의 인증서 저장 위치와 만료 기한 확인
  • 작업 대상: 모든 마스터 노드
# 저장 위치
root@kjh-m1:~# ls -al /etc/kubernetes/pki
lrwxrwxrwx 1 root root 19 12월  5 13:45 /etc/kubernetes/pki -> /etc/kubernetes/ssl
root@kjh-m1:~# ls -al /etc/kubernetes/pki/
total 80
drwxr-xr-x 2 root root 4096 12월  5 14:46 .
drwxr-xr-x 5 kube root 4096  2월  7 04:18 ..
-rw-r--r-- 1 root root 1452  2월  7 09:26 apiserver.crt
-rw-r--r-- 1 root root 1452 12월  5 14:08 apiserver.crt.old
-rw------- 1 root root 1679  2월  7 09:26 apiserver.key
-rw------- 1 root root 1675 12월  5 14:08 apiserver.key.old
-rw-r--r-- 1 root root 1176  2월  7 09:26 apiserver-kubelet-client.crt
-rw-r--r-- 1 root root 1176 12월  5 14:08 apiserver-kubelet-client.crt.old
-rw------- 1 root root 1675  2월  7 09:26 apiserver-kubelet-client.key
-rw------- 1 root root 1675 12월  5 14:08 apiserver-kubelet-client.key.old
-rw-r--r-- 1 root root 1107 12월  5 14:08 ca.crt
-rw------- 1 root root 1679 12월  5 14:08 ca.key
-rw-r--r-- 1 root root 1123 12월  5 14:08 front-proxy-ca.crt
-rw------- 1 root root 1675 12월  5 14:08 front-proxy-ca.key
-rw-r--r-- 1 root root 1119  2월  7 09:26 front-proxy-client.crt
-rw-r--r-- 1 root root 1119 12월  5 14:08 front-proxy-client.crt.old
-rw------- 1 root root 1679  2월  7 09:26 front-proxy-client.key
-rw------- 1 root root 1675 12월  5 14:08 front-proxy-client.key.old
-rw------- 1 root root 1679 12월  5 14:08 sa.key
-rw------- 1 root root  451 12월  5 14:08 sa.pub

# 만료 기한 확인
root@kjh-m1:~# 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'
W0210 00:14:58.418948 1182414 utils.go:69] The recommended value for "clusterDNS" in "KubeletConfiguration" is: [10.233.0.10]; the provided value is: [169.254.25.10]

CERTIFICATE                EXPIRES                  RESIDUAL TIME   CERTIFICATE AUTHORITY   EXTERNALLY MANAGED
admin.conf                 Feb 07, 2026 00:26 UTC   362d            ca                      no
apiserver                  Feb 07, 2026 00:26 UTC   362d            ca                      no
apiserver-kubelet-client   Feb 07, 2026 00:26 UTC   362d            ca                      no
controller-manager.conf    Feb 07, 2026 00:26 UTC   362d            ca                      no
front-proxy-client         Feb 07, 2026 00:26 UTC   362d            front-proxy-ca          no
scheduler.conf             Feb 07, 2026 00:26 UTC   362d            ca                      no
super-admin.conf           Feb 07, 2026 00:26 UTC   362d            ca                      no

CERTIFICATE AUTHORITY   EXPIRES                  RESIDUAL TIME   EXTERNALLY MANAGED
ca                      Dec 03, 2034 05:08 UTC   9y              no
front-proxy-ca          Dec 03, 2034 05:08 UTC   9y              no

etcd는 만료 기한이 100년이다.

  • etcd 인증서 저장 위치와 만료 기한 확인
  • 작업 대상: 모든 마스터 노드
# 저장 위치
root@kjh-m1:~# ls -al /etc/ssl/etcd/ssl
total 88
drwx------ 2 etcd root 4096 12월  5 15:26 .
drwx------ 4 etcd root 4096  2월  7 04:18 ..
-rwx------ 1 etcd root 1675 12월  5 15:26 admin-kjh-m1-key.pem
-rwx------ 1 etcd root 1415 12월  5 15:26 admin-kjh-m1.pem
-rwx------ 1 etcd root 1675 12월  5 15:26 admin-kjh-m2-key.pem
-rwx------ 1 etcd root 1415 12월  5 15:26 admin-kjh-m2.pem
-rwx------ 1 etcd root 1675 12월  5 15:26 admin-kjh-m3-key.pem
-rwx------ 1 etcd root 1415 12월  5 15:26 admin-kjh-m3.pem
-rwx------ 1 etcd root 1675 12월  5 14:06 ca-key.pem
-rwx------ 1 etcd root 1111 12월  5 14:06 ca.pem
-rwx------ 1 etcd root 1679 12월  5 15:26 member-kjh-m1-key.pem
-rwx------ 1 etcd root 1415 12월  5 15:26 member-kjh-m1.pem
-rwx------ 1 etcd root 1675 12월  5 15:26 member-kjh-m2-key.pem
-rwx------ 1 etcd root 1415 12월  5 15:26 member-kjh-m2.pem
-rwx------ 1 etcd root 1679 12월  5 15:26 member-kjh-m3-key.pem
-rwx------ 1 etcd root 1415 12월  5 15:26 member-kjh-m3.pem
-rwx------ 1 etcd root 1675 12월  5 15:26 node-kjh-m1-key.pem
-rwx------ 1 etcd root 1415 12월  5 15:26 node-kjh-m1.pem
-rwx------ 1 etcd root 1679 12월  5 15:26 node-kjh-m2-key.pem
-rwx------ 1 etcd root 1415 12월  5 15:26 node-kjh-m2.pem
-rwx------ 1 etcd root 1679 12월  5 15:26 node-kjh-m3-key.pem
-rwx------ 1 etcd root 1415 12월  5 15:26 node-kjh-m3.pem

# 만료 기한 확인
root@kjh-m1:~# openssl x509 -in /etc/ssl/etcd/ssl/ca.pem -noout -dates
notBefore=Dec  5 05:06:00 2024 GMT
notAfter=Nov 11 05:06:00 2124 GMT
root@kjh-m1:~# openssl x509 -in /etc/ssl/etcd/ssl/admin-kjh-m1.pem -noout -dates
notBefore=Dec  5 06:26:53 2024 GMT
notAfter=Nov 11 06:26:53 2124 GMT
root@kjh-m1:~# openssl x509 -in /etc/ssl/etcd/ssl/member-kjh-m1.pem -noout -dates
notBefore=Dec  5 06:26:53 2024 GMT
notAfter=Nov 11 06:26:53 2124 GMT

3. PKI 인증서 백업

인증서를 재발급하기 전에 기존 인증서를 백업한다.

  • 작업 대상: 모든 마스터 노드
    kubeadm certs renew 로 인증서를 갱신하면 기본값으로 /etc/kubernetes/pki 아래에 저장된 인증서가 갱신된다. (--cert-dir string Default: "/etc/kubernetes/pki")

따라서 /etc/kubernetes/pki 경로 아래에 있는 인증서를 백업하면 된다.

## PKI 인증서 백업
cp -rp /etc/kubernetes/ssl /etc/kubernetes/ssl_bkup_$(date +%y%m%d)
ls -al /etc/kubernetes/ssl_bkup_$(date +%y%m%d)/

4. 인증서 재발급

  • 작업 대상: 모든 마스터 노드
kubeadm certs renew all

5. 인증서 재발급 확인

  • 작업 대상: 모든 마스터 노드
# PKI 인증서 확인 -> 인증서만 재발급됨. ca는 재발급 안됨.
kubeadm certs check-expiration

# etcd 인증서 확인 -> 재발급 안됨.
openssl x509 -in /etc/ssl/etcd/ssl/ca.pem -noout -dates
openssl x509 -in /etc/ssl/etcd/ssl/admin-kjh-m1.pem -noout -dates
openssl x509 -in /etc/ssl/etcd/ssl/member-kjh-m1.pem -noout -dates

6. kube-apiserver, kube-control-manager, kube-scheduler Pod 재기동

kube-apiserver, kube-controller-manager, kube-scheduler가 갱신된 인증서를 사용하기 위해 재시작이 필요하다. 파드가 재기동하는 동안 서비스 연속성을 위해 Rolling 방식으로 마스터 노드 하나씩 작업하는 것이 좋다.

  • 작업 대상: 모든 마스터 노드
# static pod 재기동을 위해 설정 파일 이동
ls -al /etc/kubernetes/manifests/kube-*
mv /etc/kubernetes/manifests/kube-* /root/
ls -al /root/kube-*

# 상태 확인
kubectl get pods -n kube-system |egrep ^kube-
kubectl get nodes

# 복구
mv /root/kube-* /etc/kubernetes/manifests/

# 상태 확인
kubectl get pods -n kube-system |egrep ^kube-
kubectl get nodes

7. kubeconfig 변경 및 kubelet 재기동

admin.conf(/etc/kubernetes/admin.conf)는 kubeadm certs renew all 실행 시 자동으로 갱신된다. 반면 /root/.kube/config는 자동으로 업데이트되지 않으므로 수동으로 변경해야 한다. 갱신된 인증서를 포함한 admin.conf를 ~/.kube/config로 복사하면 된다.

  • 작업 대상: 모든 마스터 노드
# 기존 kubeconfig 백업
mv /root/.kube/config /root/.kube/config-$(date +%y%m%d)
ls -al /root/.kube/config-$(date +%y%m%d)

# 갱신된 admin.conf를 사용하여 새로운 kubeconfig 적용
cp /etc/kubernetes/admin.conf ~/.kube/config
ls -al ~/.kube/config

# kubelet 재시작하여 적용
systemctl restart kubelet.service
systemctl status kubelet.service

참고

[1] https://kubernetes.io/docs/setup/best-practices/certificates/#certificate-paths
[2] https://sickrov.github.io/
[3] https://danykde0til.tistory.com/165

0개의 댓글