콘텐츠로 이동

Kubernetes kubeadm 기반 취약점 점검 및 보완 방법

1. 목적 및 대상

Kubernetes 클러스터에서 운영자가 직접 점검하고 보완할 수 있는 설정 및 인증서 관련 취약점 항목을 정리합니다.

대상은 다음과 같습니다.

  • Kubernetes: kubeadm 기반 클러스터
  • 적용 버전: v1.30, v1.33 계열 등 kubeadm 인증서 체계를 사용하는 버전
  • OS: Red Hat 계열 Linux, Ubuntu 계열 Linux
  • 구성: 단일 Control Plane 또는 3-master HA 구성
  • API Server 접속: 노드 IP, VIP 또는 FQDN 사용
  • 환경: 폐쇄망 또는 온라인 설치 환경

2. HA 구성 작업 주의사항

3-master HA 구성에서는 점검은 전체 노드에서 수행하되, 보완 작업은 한 번에 한 대씩 진행합니다.

  1. 조치 전 kubectl get nodeskubectl get pods -n kube-system을 확인합니다.
  2. 작업 대상 마스터 1대를 선정합니다.
  3. 해당 마스터에서 manifest 백업, 설정 변경, 인증서 갱신 등 보완 작업을 수행합니다.
  4. kubelet 또는 control plane 정적 Pod를 재시작합니다.
  5. API Server /readyz, Node Ready, kube-system Pod 상태를 확인합니다.
  6. 정상 확인 후 다음 마스터로 진행합니다.

동시에 3대 마스터의 kubelet, containerd, API Server, etcd를 재시작하지 않습니다.

3. 점검 항목 요약

구분 점검 내용 보완 기준
API Server 설정 익명 접근, 권한 모드, Admission Plugin, 감사 로그 설정 확인 보안 기준에 맞게 설정
etcd 설정 client/peer TLS, 인증서 사용 여부 확인 TLS 및 인증서 기반 통신 적용
인증서 만료 Kubernetes 인증서 잔여 기간 확인 만료 전 갱신
API Server SAN 인증서에 VIP, FQDN 또는 접속 IP 포함 여부 확인 실제 접속 엔드포인트 포함
인증서 공유 Kubernetes 인증서와 타 서비스 인증서 공유 여부 확인 서비스별 인증서/개인키 분리
클러스터 상태 Node 및 Control Plane Pod 상태 확인 Ready, Running, /readyz 응답 ok

4. Kubernetes 설정 취약점 점검 및 보완

4.1 API Server 설정 점검

마스터 노드에서 API Server manifest를 확인합니다.

sudo grep -nE 'anonymous-auth|authorization-mode|enable-admission-plugins|disable-admission-plugins|audit-log' /etc/kubernetes/manifests/kube-apiserver.yaml

출력에 없는 옵션은 kube-apiserver 바이너리 기본값으로 동작합니다. 예를 들어 --anonymous-auth는 명시하지 않으면 기본값 false로 동작합니다. 다만 보안 점검자가 manifest만 보고 확인할 수 있도록 운영 환경에서는 --anonymous-auth=false처럼 명시하는 것을 권장합니다.

옵션별 요약은 다음과 같습니다.

옵션 한줄 요약 권장 기준
--anonymous-auth 인증서나 토큰이 없는 익명 요청 허용 여부 기본값 false, 명시 권장
--authorization-mode API 요청 권한 검증 방식 Node,RBAC 포함
--enable-admission-plugins API 요청 저장 전 추가 검증 플러그인 NodeRestriction 포함
--disable-admission-plugins 기본 활성 Admission Plugin 비활성화 보안 필수 플러그인 비활성화 금지
--audit-log-path 감사 로그 저장 경로 운영 기준에 따라 설정
--audit-policy-file 감사 로그 기록 정책 파일 운영 기준에 따라 설정

확인 기준은 다음과 같습니다.

항목 판정 기준
--anonymous-auth=false 옵션이 없어도 기본값은 false. 점검 대응을 위해 명시 권장
--authorization-mode=Node,RBAC 포함되어 있으면 양호
--enable-admission-plugins=NodeRestriction 포함되어 있으면 양호
감사 로그 점검 기준에서 요구하는 경우 audit-log-path, audit-policy-file 확인

4.2 API Server 설정 보완

/etc/kubernetes/manifests/kube-apiserver.yaml을 백업합니다.

sudo cp -a /etc/kubernetes/manifests/kube-apiserver.yaml /etc/kubernetes/manifests/kube-apiserver.yaml.bak.$(date +%Y%m%d%H%M%S)

필요한 설정을 추가 또는 수정합니다.

- --anonymous-auth=false
- --authorization-mode=Node,RBAC
- --enable-admission-plugins=NodeRestriction

기존 --enable-admission-plugins 값이 이미 있으면 덮어쓰지 말고 NodeRestriction을 기존 목록에 추가합니다.

정적 Pod는 manifest 변경 후 kubelet이 자동으로 재기동합니다.

sudo crictl ps | grep kube-apiserver
kubectl get pods -n kube-system -o wide | grep kube-apiserver

4.3 etcd TLS 설정 점검

sudo grep -nE 'cert-file|key-file|trusted-ca-file|peer-cert-file|peer-key-file|peer-trusted-ca-file|client-cert-auth|peer-client-cert-auth' /etc/kubernetes/manifests/etcd.yaml

옵션별 요약은 다음과 같습니다.

옵션 한줄 요약 권장 기준
--cert-file etcd client 접속용 서버 인증서 etcd 전용 인증서 지정
--key-file etcd client 접속용 서버 개인키 etcd 전용 개인키 지정
--trusted-ca-file etcd client 인증서 검증용 CA etcd CA 지정
--client-cert-auth client 인증서 인증 강제 여부 true
--peer-cert-file etcd peer 통신용 인증서 etcd peer 인증서 지정
--peer-key-file etcd peer 통신용 개인키 etcd peer 개인키 지정
--peer-trusted-ca-file etcd peer 인증서 검증용 CA etcd CA 지정
--peer-client-cert-auth peer 인증서 인증 강제 여부 true

확인 기준은 다음과 같습니다.

항목 판정 기준
client 인증서 --cert-file, --key-file, --trusted-ca-file, --client-cert-auth=true 확인
peer 인증서 --peer-cert-file, --peer-key-file, --peer-trusted-ca-file, --peer-client-cert-auth=true 확인
인증서/키 /etc/kubernetes/pki/etcd/ 하위의 etcd 전용 인증서와 개인키 사용

4.4 etcd 설정 보완

kubeadm 기본 stacked etcd 구성은 일반적으로 etcd TLS가 이미 적용되어 있습니다. 점검 결과에 위 옵션들이 모두 보이면 별도 보완 작업은 필요하지 않습니다.

누락된 경우에도 운영자가 인증서를 직접 새로 만들어 넣는 절차가 우선은 아닙니다. 먼저 현재 클러스터가 kubeadm stacked etcd인지 확인하고, 기존 백업 또는 kubeadm이 관리하는 etcd 인증서를 기준으로 복구합니다.

sudo cp -a /etc/kubernetes/manifests/etcd.yaml /etc/kubernetes/manifests/etcd.yaml.bak.$(date +%Y%m%d%H%M%S)
sudo ls -l /etc/kubernetes/pki/etcd/
sudo kubeadm certs check-expiration

etcd 인증서가 만료되었거나 재발급이 필요한 경우 kubeadm으로 갱신합니다.

sudo kubeadm certs renew etcd-server
sudo kubeadm certs renew etcd-peer
sudo kubeadm certs renew etcd-healthcheck-client
sudo kubeadm certs renew apiserver-etcd-client
sudo systemctl restart kubelet

etcd manifest의 TLS 옵션 자체가 누락된 경우에는 인증서를 새로 만드는 것이 아니라, 백업 또는 kubeadm 기본 manifest 형식에 맞춰 /etc/kubernetes/manifests/etcd.yaml의 인증서 경로를 복구합니다.

수정 후 상태를 확인합니다.

sudo crictl ps | grep etcd
kubectl get pods -n kube-system -o wide | grep etcd
sudo kubeadm certs check-expiration

외부 etcd를 사용하는 구성은 인증서 경로와 갱신 절차가 다르므로 별도 운영 절차를 따릅니다.

5. 인증서 및 PKI 취약점 점검 및 보완

5.1 인증서 만료 점검

마스터 노드에서 실행합니다.

sudo kubeadm certs check-expiration

파일 기준으로도 확인합니다.

sudo openssl x509 -in /etc/kubernetes/pki/apiserver.crt -noout -subject -issuer -dates
sudo openssl x509 -in /etc/kubernetes/pki/front-proxy-client.crt -noout -subject -issuer -dates
sudo openssl x509 -in /etc/kubernetes/pki/etcd/server.crt -noout -subject -issuer -dates
sudo openssl x509 -in /etc/kubernetes/pki/etcd/peer.crt -noout -subject -issuer -dates
sudo openssl x509 -in /etc/kubernetes/pki/etcd/healthcheck-client.crt -noout -subject -issuer -dates

5.2 인증서 만료 보완

사전 백업을 수행합니다.

BACKUP_DIR=/root/k8s-cert-backup-$(date +%Y%m%d-%H%M%S)
sudo mkdir -p ${BACKUP_DIR}
sudo cp -a /etc/kubernetes/pki ${BACKUP_DIR}/
sudo cp -a /etc/kubernetes/*.conf ${BACKUP_DIR}/

kubeadm 기반 클러스터에서 인증서를 갱신합니다.

sudo kubeadm certs renew all
sudo systemctl restart kubelet

kubeadm certs renew alladmin.conf, controller-manager.conf, scheduler.conf, super-admin.conf에 포함된 client certificate도 함께 갱신합니다. 별도로 kubeconfig 재생성이 필요한 경우에만 백업 후 다음 명령을 사용합니다. 기존 파일이 있으면 kubeadm이 덮어쓰지 않을 수 있으므로 출력 메시지를 확인합니다.

sudo kubeadm init phase kubeconfig all

갱신 후 확인합니다.

sudo kubeadm certs check-expiration
kubectl get nodes -o wide
kubectl get pods -n kube-system -o wide

5.3 API Server 인증서 SAN 점검

API Server에 VIP, FQDN 또는 특정 IP로 접속하는 구성에서는 apiserver.crt의 SAN에 실제 접속 엔드포인트가 포함되어야 합니다.

sudo openssl x509 \
  -in /etc/kubernetes/pki/apiserver.crt \
  -noout -text | grep -A1 "Subject Alternative Name"

확인 대상 예시는 다음과 같습니다.

IP Address:<K8S_API_VIP>
IP Address:<MASTER_NODE_IP>
DNS:<K8S_API_DNS>
DNS:kubernetes
DNS:kubernetes.default
DNS:kubernetes.default.svc
DNS:kubernetes.default.svc.cluster.local

5.4 API Server 인증서 SAN 보완

운영 중인 kubeadm 설정을 확인합니다.

kubectl -n kube-system get configmap kubeadm-config -o yaml

설정 파일에 SAN 값을 포함합니다.

apiServer:
  certSANs:
    - <K8S_API_VIP>
    - <K8S_API_DNS>
    - <MASTER1_IP>
    - <MASTER2_IP>
    - <MASTER3_IP>

기존 API Server 인증서를 백업하고 재생성합니다.

sudo cp -a /etc/kubernetes/pki/apiserver.crt /etc/kubernetes/pki/apiserver.crt.bak.$(date +%Y%m%d%H%M%S)
sudo cp -a /etc/kubernetes/pki/apiserver.key /etc/kubernetes/pki/apiserver.key.bak.$(date +%Y%m%d%H%M%S)
sudo rm -f /etc/kubernetes/pki/apiserver.crt /etc/kubernetes/pki/apiserver.key
sudo kubeadm init phase certs apiserver --config /path/to/kubeadm-config.yaml
sudo systemctl restart kubelet

SAN 반영을 확인합니다.

sudo openssl x509 \
  -in /etc/kubernetes/pki/apiserver.crt \
  -noout -text | grep -A1 "Subject Alternative Name"

5.5 인증서 공유 여부 점검

Kubernetes 인증서와 개인키를 MariaDB, Harbor, Jenkins, Ingress 등 다른 서비스에서 공유하지 않아야 합니다.

sudo find /etc/kubernetes/pki -maxdepth 3 -type f -print
sudo grep -RInE "/etc/kubernetes/pki|apiserver\.crt|apiserver\.key|front-proxy|etcd/server" \
  /etc /opt /app 2>/dev/null

다른 서비스에서 다음과 같이 Kubernetes 인증서를 직접 참조하면 안 됩니다.

ssl-ca=/etc/kubernetes/pki/ca.crt
ssl-cert=/etc/kubernetes/pki/apiserver.crt
ssl-key=/etc/kubernetes/pki/apiserver.key

5.6 인증서 공유 보완

서비스별 인증서를 별도로 발급합니다.

서비스 인증서 기준
Kubernetes Kubernetes PKI 전용
MariaDB MariaDB 서버 인증서 별도 발급
Harbor Harbor 또는 Ingress 인증서 별도 발급
Jenkins Jenkins 또는 Ingress 인증서 별도 발급

동일한 내부 CA를 사용할 수는 있습니다.

단, 서버 인증서와 개인키는 서비스별로 분리합니다.

6. API Server 및 클러스터 상태 확인

6.1 API Server 상태 확인

실제 API Server 접속 엔드포인트 기준으로 상태를 확인합니다.

curl -k https://<K8S_API_ENDPOINT>:6443/readyz
curl --cacert /etc/kubernetes/pki/ca.crt https://<K8S_API_ENDPOINT>:6443/readyz

정상 결과는 다음과 같습니다.

ok

6.2 클러스터 상태 확인

kubectl get nodes -o wide
kubectl get pods -n kube-system -o wide
kubectl get componentstatuses 2>/dev/null || true

Control Plane 정적 Pod 상태를 확인합니다.

sudo crictl ps | grep -E "kube-apiserver|kube-controller-manager|kube-scheduler|etcd"

kubelet 상태를 확인합니다.

sudo systemctl status kubelet --no-pager
sudo journalctl -u kubelet -n 100 --no-pager

7. 최종 확인 명령

sudo kubeadm certs check-expiration
kubectl get nodes -o wide
kubectl get pods -n kube-system -o wide
curl --cacert /etc/kubernetes/pki/ca.crt https://<K8S_API_ENDPOINT>:6443/readyz
sudo openssl x509 -in /etc/kubernetes/pki/apiserver.crt -noout -text | grep -A1 "Subject Alternative Name"

8. 최종 판정 기준

항목 기준
API Server 설정 익명 접근, 권한 모드, Admission Plugin 등 운영 보안 기준 충족
etcd 설정 TLS 및 인증서 기반 통신 설정 확인
API Server SAN 실제 접속 VIP, FQDN 또는 IP 포함
API Server 상태 /readyz 응답 ok
Node 상태 Ready
Control Plane Pod Running
인증서 만료 운영 기준 기간 이상 남음
인증서 공유 Kubernetes 인증서와 개인키를 타 서비스에서 미사용