Certified Kubernetes Administrator (CKA) #8 인증서 관리: PKI, kubeconfig, 인증서 갱신
#7 etcd 백업과 복구에서 클러스터 상태를 지키는 법을 다뤘다면, 이번에는 클러스터의 모든 통신을 떠받치는 TLS 인증서를 다룹니다. 쿠버네티스에서 컴포넌트끼리 주고받는 통신은 전부 TLS로 암호화되고 상호 인증됩니다. kubectl이 apiserver를 부를 때도, apiserver가 etcd를 부를 때도, kubelet이 apiserver에 보고할 때도 인증서가 신원을 증명합니다.
문제는 이 인증서에 유효 기간이 있다는 점입니다. kubeadm이 발급하는 인증서는 대부분 1년이 기본이고, 만료되면 컴포넌트끼리 서로를 믿지 못해 클러스터가 통째로 멈춥니다. kubectl이 갑자기 안 먹고, control plane이 올라오지 못합니다. CKA에서 인증서 관리는 Cluster Architecture도메인의 핵심이자 Troubleshooting에서 자주 나오는 함정이므로, 이번 글에서 PKI 구조와 갱신 절차를 손에 익히겠습니다.
클러스터는 거대한 PKI다 #
쿠버네티스 클러스터는 그 자체로 하나의 **PKI(Public Key Infrastructure)**입니다. 모든 컴포넌트가 인증서로 신원을 증명하고, 그 인증서를 발급하고 서명한 **CA(Certificate Authority)**를 공통으로 신뢰함으로써 서로를 믿습니다. CA는 곧 신뢰의 뿌리입니다. apiserver가 “이 요청을 보낸 kubelet이 진짜 kubelet인가"를 판단할 때, 그 kubelet의 인증서가 클러스터 CA로 서명되었는지를 확인합니다.
이 구조에서 인증서는 두 종류의 역할을 동시에 합니다.
- 서버 인증서. apiserver처럼 연결을 받는 쪽이 “내가 진짜 그 서버다"를 증명
- 클라이언트 인증서. kubelet이나 controller-manager처럼 연결을 거는 쪽이 “내가 누구다"를 증명
apiserver는 다른 모든 컴포넌트가 거쳐 가는 관문이므로 서버 인증서와 클라이언트 인증서를 모두 가집니다. apiserver는 클라이언트들에게는 서버이지만, etcd에게는 클라이언트이기 때문입니다.
PKI 디렉터리 구조 #
kubeadm으로 설치한 클러스터의 인증서는 모두 control plane 노드의 /etc/kubernetes/pki/에 모여 있습니다. 이 디렉터리를 한 번 외워두면 시험에서 헤매지 않습니다.
ls -1 /etc/kubernetes/pki/ca.crt # 클러스터 루트 CA 인증서 (신뢰의 뿌리)
ca.key # 루트 CA 개인키 (이것으로 다른 인증서를 서명)
apiserver.crt # apiserver 서버 인증서
apiserver.key
apiserver-kubelet-client.crt # apiserver가 kubelet을 부를 때 쓰는 클라이언트 인증서
apiserver-kubelet-client.key
front-proxy-ca.crt # aggregation layer용 CA
front-proxy-client.crt
sa.key # ServiceAccount 토큰 서명용 키 쌍
sa.pub
etcd/ # etcd 전용 인증서 디렉터리etcd/ 안에는 etcd 자체의 CA와 인증서가 따로 들어 있습니다. etcd는 클러스터에서 가장 민감한 데이터를 들고 있으므로 별도의 신뢰 체계를 씁니다.
ls -1 /etc/kubernetes/pki/etcd/ca.crt # etcd 전용 CA
ca.key
server.crt # etcd 서버 인증서
server.key
peer.crt # etcd 멤버끼리 통신용
peer.key
apiserver-etcd-client.crt # apiserver가 etcd에 접속할 때 쓰는 클라이언트 인증서누가 누구를 신뢰하는가 #
인증서 파일 이름만 봐도 신뢰 관계가 읽힙니다. 핵심은 누가 어떤 CA로 서명되었고, 상대가 그 CA를 신뢰하는가입니다.
| 통신 | 클라이언트 | 서버 | 서명 CA |
|---|---|---|---|
| kubectl → apiserver | admin 인증서 | apiserver.crt | ca.crt |
| kubelet → apiserver | kubelet 인증서 | apiserver.crt | ca.crt |
| apiserver → kubelet | apiserver-kubelet-client | kubelet 서버 인증서 | ca.crt |
| apiserver → etcd | apiserver-etcd-client | etcd/server.crt | etcd/ca.crt |
| etcd ↔ etcd | etcd/peer | etcd/peer | etcd/ca.crt |
여기서 중요한 점은 etcd가 별도의 CA를 쓴다는 사실입니다. apiserver가 etcd에 붙으려면 etcd CA로 서명된 클라이언트 인증서가 필요합니다. 그래서 apiserver-etcd-client.crt는 클러스터 CA가 아니라 etcd CA로 서명됩니다.
인증서 내용을 직접 들여다보기 #
openssl로 인증서 한 장을 까보면 위 관계가 눈에 들어옵니다. CKA에서는 이 명령으로 만료일과 신원(Subject)을 확인하는 일이 잦습니다.
openssl x509 -in /etc/kubernetes/pki/apiserver.crt -noout -text출력에서 봐야 할 세 곳입니다.
- Subject. 이 인증서가 누구를 증명하는가 (예:
CN=kube-apiserver) - Issuer. 누가 서명했는가 (
CN=kubernetes, 즉 클러스터 CA) - Validity.
Not Before와Not After. 이Not After가 만료 시각
클라이언트 인증서에서는 **Subject의 CN과 O(Organization)**가 특히 중요합니다. 쿠버네티스는 CN을 사용자 이름으로, O를 그룹으로 해석합니다. 예를 들어 CN=jane, O=dev로 서명된 인증서로 접속하면 apiserver는 그 요청을 jane 사용자, dev 그룹의 요청으로 인식합니다. 이 매핑이 #9 RBAC에서 권한 부여의 출발점이 됩니다.
kubeconfig: 인증서를 들고 다니는 지갑 #
인증서 파일이 클러스터의 신원 증명이라면, kubeconfig는 그 증명을 들고 어느 클러스터에 어떤 신분으로 접속할지를 묶어 둔 설정 파일입니다. kubectl도, controller-manager도, scheduler도 모두 kubeconfig를 통해 apiserver에 붙습니다.
kubeconfig는 세 가지 블록과 그것을 묶는 context로 구성됩니다.
- clusters. apiserver 주소(
server)와 그 서버를 검증할 CA(certificate-authority-data) - users. 접속할 신원. 클라이언트 인증서(
client-certificate-data+client-key-data)나 토큰 - contexts. 어떤 cluster에 어떤 user로, 어떤 namespace에서 접속할지를 묶은 조합
# 현재 kubeconfig 전체 보기 (민감 데이터는 가려서)
k config view
# 현재 활성 context
k config current-context
# context 목록
k config get-contextscontrol plane의 kubeconfig 파일들 #
kubeadm은 control plane 컴포넌트가 쓸 kubeconfig를 /etc/kubernetes/에 만들어 둡니다. 이 파일들도 안에 인증서를 품고 있어 함께 갱신 대상이 됩니다.
ls -1 /etc/kubernetes/*.confadmin.conf # 클러스터 관리자(kubectl)용. 보통 ~/.kube/config로 복사
controller-manager.conf # kube-controller-manager가 apiserver에 붙을 때
scheduler.conf # kube-scheduler가 apiserver에 붙을 때
kubelet.conf # 노드의 kubelet이 apiserver에 붙을 때
super-admin.conf # 비상용 관리자 kubeconfig (최신 kubeadm)admin.conf 안의 클라이언트 인증서는 CN=kubernetes-admin, O=system:masters로 서명되어 있습니다. system:masters 그룹은 RBAC을 우회하는 사실상의 최고 권한 그룹이라, 이 파일이 곧 클러스터 전체에 대한 마스터 키입니다. 그래서 admin.conf는 함부로 복사하거나 노출하면 안 됩니다.
# ~/.kube/config는 보통 admin.conf를 복사한 것
sudo cp /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config인증서 만료 확인 #
CKA에서 인증서 문제는 대개 “kubectl이 안 먹는다, 원인을 찾아 고쳐라"는 형태로 나옵니다. 첫 단계는 항상 만료 여부 확인입니다. kubeadm이 전용 명령을 제공합니다.
kubeadm certs check-expirationCERTIFICATE EXPIRES RESIDUAL TIME EXTERNALLY MANAGED
admin.conf Jun 06, 2027 09:00 UTC 364d no
apiserver Jun 06, 2027 09:00 UTC 364d no
apiserver-etcd-client Jun 06, 2027 09:00 UTC 364d no
apiserver-kubelet-client Jun 06, 2027 09:00 UTC 364d no
controller-manager.conf Jun 06, 2027 09:00 UTC 364d no
scheduler.conf Jun 06, 2027 09:00 UTC 364d no
etcd-server Jun 06, 2027 09:00 UTC 364d no
...
CERTIFICATE AUTHORITY EXPIRES RESIDUAL TIME
ca Jun 04, 2035 09:00 UTC 9y
etcd-ca Jun 04, 2035 09:00 UTC 9y여기서 두 가지를 읽습니다. 개별 인증서는 보통 1년(RESIDUAL TIME이 짧음)이고, CA는 10년으로 훨씬 깁니다. 즉 주기적으로 갱신해야 하는 것은 개별 인증서이고, CA는 클러스터 수명 동안 거의 건드리지 않습니다.
kubeadm 명령을 못 쓰는 상황(apiserver가 이미 죽은 경우 등)에서는 openssl로 직접 확인합니다.
# 만료일만 빠르게
openssl x509 -enddate -noout -in /etc/kubernetes/pki/apiserver.crt
# notAfter=Jun 6 09:00:00 2027 GMT
# 여러 인증서를 한 번에
for f in /etc/kubernetes/pki/*.crt; do
echo "$f"
openssl x509 -enddate -noout -in "$f"
done인증서 갱신 #
만료가 임박했거나 이미 지났다면 kubeadm certs renew로 갱신합니다. 가장 흔히 쓰는 형태는 모든 인증서를 한 번에 갱신하는 것입니다.
# 모든 kubeadm 관리 인증서를 한 번에 갱신
kubeadm certs renew allcertificate embedded in the kubeconfig file for the admin to use renewed
certificate for serving the Kubernetes API renewed
certificate the apiserver uses to access etcd renewed
certificate for the API server to connect to kubelet 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.마지막 줄을 놓치면 안 됩니다. 갱신만으로는 끝나지 않습니다. control plane 컴포넌트는 시작할 때 인증서를 메모리로 읽어 들이므로, 디스크의 인증서를 새것으로 바꿔도 재기동하기 전까지는 옛 인증서를 그대로 들고 있습니다.
control plane 재기동: 매니페스트를 잠깐 옮긴다 #
control plane 컴포넌트는 static Pod로 떠 있습니다(이 동작 원리는 #2에서 다뤘습니다). static Pod는 kubelet이 /etc/kubernetes/manifests/의 매니페스트를 감시하다가 파일이 생기면 띄우고 사라지면 죽이는 방식으로 동작합니다. 그래서 매니페스트를 잠깐 다른 곳으로 옮겼다가 되돌리면 컴포넌트가 깔끔하게 재기동됩니다.
# 매니페스트를 잠깐 밖으로 옮긴다 → kubelet이 static Pod를 내린다
mv /etc/kubernetes/manifests /etc/kubernetes/manifests.bak
# 컴포넌트가 모두 내려갈 때까지 잠깐 기다린 뒤 되돌린다
mv /etc/kubernetes/manifests.bak /etc/kubernetes/manifests매니페스트를 되돌리면 kubelet이 다시 static Pod를 띄우고, 이때 새 인증서를 읽어 들입니다. 단순히 kubelet 자체를 재시작하는 것으로는 static Pod가 다시 뜨지 않을 수 있으므로, 매니페스트를 옮기는 방식이 가장 확실합니다.
갱신 후에는 ~/.kube/config도 새 admin 인증서로 다시 복사해야 kubectl이 새 신원으로 접속합니다.
sudo cp /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
# 갱신이 반영됐는지 다시 확인
kubeadm certs check-expirationkubelet 인증서는 자동 갱신된다 #
control plane 인증서와 달리, kubelet의 클라이언트 인증서는 보통 자동으로 갱신됩니다. kubelet 설정의 rotateCertificates: true(기본값)가 켜져 있으면, kubelet이 만료가 다가올 때 스스로 새 인증서를 요청하고 교체합니다. 그래서 kubeadm certs renew의 대상에는 kubelet의 클라이언트 인증서가 들어가지 않습니다.
# kubelet 설정에서 자동 갱신 여부 확인
grep rotateCertificates /var/lib/kubelet/config.yaml
# rotateCertificates: true
# kubelet이 들고 있는 현재 인증서
ls -l /var/lib/kubelet/pki/이 자동 갱신 덕분에 노드가 여러 대여도 kubelet 인증서를 일일이 갱신할 필요가 없습니다. 다만 자동 갱신이 동작하려면 kubelet이 apiserver와 통신할 수 있어야 하므로, 노드가 오래 꺼져 있다가 인증서가 만료된 채로 돌아오면 갱신이 막힐 수 있습니다.
사용자 인증서 발급: CertificateSigningRequest #
새 관리자나 개발자에게 클러스터 접근을 주려면 그 사람 몫의 클라이언트 인증서를 발급해야 합니다. 직접 CA 키로 서명할 수도 있지만, 쿠버네티스는 이를 API로 처리하는 CertificateSigningRequest(CSR) 리소스를 제공합니다. 사용자가 CSR을 제출하면 관리자가 승인하고, 클러스터가 서명한 인증서를 돌려주는 흐름입니다.
# 1) 개인키와 CSR 생성 (사용자 측)
openssl genrsa -out jane.key 2048
openssl req -new -key jane.key -out jane.csr -subj "/CN=jane/O=dev"여기서 CN=jane은 사용자 이름, O=dev는 그룹이 됩니다. 이 신원이 그대로 RBAC의 주체가 됩니다.
# 2) CertificateSigningRequest 제출
apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
metadata:
name: jane-csr
spec:
request: <jane.csr를 base64 -w0로 인코딩한 값>
signerName: kubernetes.io/kube-apiserver-client
usages:
- client authrequest에는 cat jane.csr | base64 -w0의 결과를 넣습니다. signerName은 발급받을 인증서의 용도를 지정하며, 일반 사용자 클라이언트 인증서에는 kubernetes.io/kube-apiserver-client를 씁니다.
# 3) 제출된 CSR 확인 → 승인
k get csr
k certificate approve jane-csr
# 4) 서명된 인증서 꺼내기
k get csr jane-csr -o jsonpath='{.status.certificate}' | base64 -d > jane.crt승인 직후 CSR의 상태가 Approved,Issued로 바뀌고, .status.certificate에 서명된 인증서가 base64로 담깁니다. 이걸 꺼내 jane.crt로 저장하면 jane의 클라이언트 인증서가 완성됩니다.
# 5) jane의 kubeconfig 구성
k config set-credentials jane \
--client-certificate=jane.crt --client-key=jane.key --embed-certs=true
k config set-context jane-context --cluster=kubernetes --user=jane여기까지는 jane이 누구인지를 증명하는 단계까지입니다. jane이 실제로 무엇을 할 수 있는지는 RoleBinding으로 권한을 묶어 줘야 정해집니다. 인증(authentication)과 인가(authorization)는 별개이며, 인가 쪽 RBAC은 #9에서 자세히 다루겠습니다. 잘못 만든 CSR을 정리할 때는 k certificate deny로 거부하거나 k delete csr <이름>으로 삭제합니다.
자주 빠지는 함정 #
인증서 작업에서 점수를 흘리는 지점은 거의 정해져 있습니다.
- 만료된 인증서로는 kubectl이 안 먹는다. apiserver가 죽거나 클라이언트 인증서가 만료되면
kubectl이Unauthorized나 연결 거부로 막힙니다. 이때는 kubectl이 아니라openssl과kubeadm certs로 디스크의 인증서를 직접 확인하는 것이 출발점입니다. - 갱신 후 재기동 누락.
kubeadm certs renew all만 돌리고 control plane을 재기동하지 않으면 컴포넌트가 옛 인증서를 계속 씁니다. 매니페스트를 옮겨 static Pod를 반드시 다시 띄워야 합니다. - admin.conf 복사 누락. 갱신 후
~/.kube/config를 새 admin.conf로 다시 복사하지 않으면 kubectl이 여전히 옛 인증서로 붙으려다 실패합니다. - CSR 승인을 잊음. CSR을 제출만 하고
k certificate approve를 빠뜨리면 인증서가 발급되지 않습니다. 상태가Pending인지Approved,Issued인지 꼭 확인합니다. - CN/O 오타. 사용자 이름과 그룹은 인증서의 Subject에 박혀 서명되므로, 발급 후에는 바꿀 수 없습니다. CSR을 만들 때
-subj의 CN과 O를 정확히 적습니다.
시험 포인트 #
/etc/kubernetes/pki/의 구성을 외웁니다.ca.crt/ca.key가 신뢰의 뿌리이고, etcd는etcd/하위에 별도 CA를 둡니다.- kubeconfig는 clusters + users + contexts의 조합이며,
k config view로 구조를,k config current-context로 현재 신원을 확인합니다. - 만료 확인은
kubeadm certs check-expiration이 기본이고, apiserver가 죽었으면openssl x509 -enddate -noout -in <파일>로 직접 봅니다. - 갱신은
kubeadm certs renew all→ 매니페스트를 옮겨 control plane 재기동 →~/.kube/config재복사의 3단계로 외웁니다. - kubelet 인증서는
rotateCertificates로 자동 갱신되므로 별도 작업이 보통 필요 없습니다. - 사용자 인증서는 CSR을 제출하고
k certificate approve로 승인한 뒤,.status.certificate를 꺼내 씁니다. 신원(CN/O)은 RBAC의 주체가 됩니다.
정리 #
이번 글에서 잡은 것입니다.
- 클러스터는 거대한 PKI이고, 모든 통신은 CA로 서명된 인증서로 상호 인증하는 TLS입니다.
- PKI 디렉터리
/etc/kubernetes/pki/에 CA와 컴포넌트별 인증서가 모여 있고, etcd는 별도 CA를 씁니다. - kubeconfig는 clusters/users/contexts로 신원과 접속 대상을 묶으며, control plane은
*.conf파일로 apiserver에 붙습니다. - 만료 확인은
kubeadm certs check-expiration과openssl, 갱신은kubeadm certs renew all뒤 control plane 재기동이 짝입니다. - 사용자 인증서는 CSR 제출과 승인으로 발급하며, 인증과 인가는 별개입니다.
다음: RBAC #
인증서로 “누구인지"를 증명하는 법을 익혔습니다. 다음은 그 신원이 클러스터에서 “무엇을 할 수 있는지"를 정하는 차례입니다.
#9 RBAC에서는 Role과 ClusterRole로 권한 묶음을 정의하고, RoleBinding과 ClusterRoleBinding으로 사용자,그룹,ServiceAccount에 그 권한을 연결하는 법, 그리고 kubectl auth can-i로 권한을 검증하는 법까지 직접 만들어 보며 정리하겠습니다. 이번 글에서 만든 jane이 실제로 일할 수 있게 만드는 단계입니다.