쿠버네티스 클러스터에 외부에서 안전하게 접속하기: TLS 검증 문제 해결
게시:
쿠버네티스 클러스터를 GCP, AWS, 온프레미스 또는 베어메탈 환경에 수동으로 구축한 후, 외부에서 kubectl
로 안전하게 접근하기 위해서는 TLS 인증서의 SAN(Subject Alternative Name) 관련 문제를 해결해야 합니다.
본 문서에서는 API 서버 노출 설정, 클라이언트 인증서 발급, kubeconfig 구성 과정을 통해, 일반적으로 발생하는 TLS 검증 오류를 인증서 재생성 없이 또는 kubeadm을 활용하여 효과적으로 해결하는 두 가지 방법을 상세히 설명합니다.
배경
쿠버네티스 API 서버는 기본적으로 TLS를 사용하여 클라이언트를 인증합니다. 초기 클러스터 구성 시 생성되는 인증서에는 일반적으로 내부 클러스터 IP(예: 10.96.0.1
), 마스터 노드 내부 IP(예: 10.128.0.7
) 등만 SAN에 포함됩니다. 외부 IP나 도메인이 SAN에 없으면 TLS 검증 과정에서 실패하게 됩니다.
문제 정의
외부 네트워크에서 kubectl get nodes
와 같은 명령을 실행할 때 다음과 같은 오류가 발생할 수 있습니다.
tls: failed to verify certificate: x509: certificate is valid for 10.96.0.1, 10.128.0.7, not 11.22.33.44
이 오류는 kubectl
이 접속하려는 호스트명 또는 IP 주소가 인증서의 SAN 목록에 포함되어 있는지 확인하는 과정에서 발생합니다. 외부 IP로 접속하려고 할 때, 해당 IP가 인증서의 SAN에 포함되어 있지 않으면 연결이 거부됩니다.
1. API 서버 노출 및 방화벽 설정
1.1 kube-apiserver 매니페스트 수정
마스터 노드에서 /etc/kubernetes/manifests/kube-apiserver.yaml
파일을 수정하여 API 서버가 외부 IP로 노출되도록 설정합니다.
spec:
containers:
- name: kube-apiserver
command:
- --advertise-address=11.22.33.44 # 외부 IP
- --bind-address=0.0.0.0 # 모든 인터페이스 바인딩
# ...기존 옵션...
--advertise-address
: 클러스터 멤버(노드)에게 알릴 마스터 IP를 지정합니다.--bind-address
: API 서버가 수신 대기할 인터페이스를 지정합니다.
1.2 방화벽 규칙 설정
방화벽에서 TCP 6443 포트를 인바운드로 허용합니다.
- GCP 환경 기준:
- VPC 네트워크 → 방화벽 → 방화벽 정책 생성
- 우선순위: 1000 (또는 적절한 값)
- 트래픽 방향: 인그레스
- 프로토콜 및 포트: TCP:6443
2. 클라이언트 인증서 발급
쿠버네티스 API 서버는 클라이언트 TLS 인증서를 통해 사용자 인증을 수행합니다. kubeadm
으로 구성된 클러스터에서는 /etc/kubernetes/pki/ca.crt
와 ca.key
가 기본 CA 역할을 합니다.
2.1 개인키 및 CSR 생성
# 개인키 생성
openssl genrsa -out admin.key 2048
# CSR(인증서 서명 요청) 생성
openssl req -new -key admin.key \
-subj "/CN=admin/O=system:masters" \
-out admin.csr
2.2 CA로 인증서 서명
openssl x509 -req \
-in admin.csr \
-CA /etc/kubernetes/pki/ca.crt \
-CAkey /etc/kubernetes/pki/ca.key \
-CAcreateserial \
-out admin.crt \
-days 365
2.3 클라이언트로 인증서 전송
scp admin.crt admin.key /etc/kubernetes/pki/ca.crt user@client:/path/to/credentials/
3. kubeconfig 구성
외부 클라이언트에서 ~/.kube/config
파일을 설정하여 클러스터에 안전하게 접속할 수 있도록 합니다.
# 클러스터 설정
kubectl config set-cluster my-cluster \
--server=https://11.22.33.44:6443 \
--certificate-authority=/path/to/credentials/ca.crt \
--embed-certs=true
# 인증 정보 설정
kubectl config set-credentials admin-user \
--client-certificate=/path/to/credentials/admin.crt \
--client-key=/path/to/credentials/admin.key \
--embed-certs=true
# 컨텍스트 설정
kubectl config set-context my-context \
--cluster=my-cluster \
--user=admin-user
# 현재 컨텍스트로 설정
kubectl config use-context my-context
4. TLS 검증 에러와 해결 방법
외부 IP 주소가 API 서버 인증서의 SAN에 포함되어 있지 않으면 다음과 같은 오류가 발생합니다.
couldn't get current server API group list: Get "https://11.22.33.44:6443/api?timeout=32s": tls: failed to verify certificate: x509: certificate is valid for 10.96.0.1, 10.128.0.7, not 11.22.33.44
이 문제를 해결하기 위한 두 가지 접근 방법이 있습니다.
4.1 방법 A: TLS 서버 이름 오버라이드
인증서를 재생성하지 않고 kubectl
설정에서 TLS 검증 호스트명을 SAN에 포함된 내부 IP로 오버라이드할 수 있습니다.
kubectl config set-cluster my-cluster \
--server=https://11.22.33.44:6443 \
--certificate-authority=/path/to/credentials/ca.crt \
--embed-certs=true \
--tls-server-name=10.128.0.7 # 인증서에 포함된 내부 IP
kubectl config use-context my-context
kubectl get nodes # 정상적으로 작동
- 장점:
- 인증서 재발급 없이 즉시 적용 가능
- 클러스터 구성 변경 불필요
- 단점:
- 최초 설정 시에만 유효 (클러스터 IP 변경 시 재설정 필요)
- 실제 접속 IP와 검증 IP의 불일치로 인한 논리적 모호성
4.2 방법 B: kubeadm을 이용한 SAN 추가
kubeadm
을 사용하여 API 서버 인증서에 외부 IP를 SAN으로 추가하고 인증서를 재발급합니다.
# API 서버 인증서 재생성 (외부 IP 추가)
sudo kubeadm init phase certs apiserver \
--apiserver-cert-extra-sans=11.22.33.44
# kubelet 재시작으로 변경사항 적용
sudo systemctl restart kubelet
# (선택) admin.conf 업데이트
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
# 확인
kubectl get nodes
- 장점:
- 보안 표준에 맞는 정상적인 TLS 검증 가능
- 외부 IP를 인증서에 명시하여 투명한 접근성 보장
- HA 클러스터에도 적용 가능
- 단점:
- API 서버 인증서 재발급 필요
- 모든 컨트롤 플레인에 동일한 작업 수행 필요 (HA 구성 시)
추가 팁
SSH 터널링을 통한 안전한 접속
API 서버를 외부에 직접 노출하지 않고 SSH 터널을 통해 안전하게 접속할 수 있습니다.
# 로컬 포트 16443을 원격 API 서버의 6443으로 포워딩
ssh -N -L 16443:127.0.0.1:6443 user@11.22.33.44
# 로컬 포트를 통해 클러스터에 접속
kubectl --server=https://127.0.0.1:16443 \
--tls-server-name=10.128.0.7 \
get nodes
도메인 이름 활용
고정 IP 대신 api.example.com
과 같은 DNS 레코드를 생성하고 SAN에 추가하면 IP 변경에도 유연하게 대응할 수 있습니다.
sudo kubeadm init phase certs apiserver \
--apiserver-cert-extra-sans=api.example.com,11.22.33.44
인증서 자동 갱신 구성
인증서의 수명을 1년 이하로 설정하고, cert-manager로 갱신을 자동화할 수 있습니다.
결론
외부에서 쿠버네티스 클러스터에 안전하게 접속하기 위해 TLS 인증서 검증 문제를 해결하는 두 가지 방법을 살펴보았습니다.
- 방법 A(TLS 서버 이름 오버라이드)는 인증서 변경 없이 즉시 적용 가능한 간편한 해결책입니다.
- 방법 B(SAN 추가)는 표준을 준수하는 보안성과 정합성을 보장하는 권장 방식입니다.
환경 특성과 운영 정책에 맞게 적절한 방법을 선택하여 외부에서도 안전하게 클러스터를 관리할 수 있습니다.
참고문서
- Kubernetes: Controlling Access to the API
- Certificate Verification Error in HTTPS Connection
- Kubernetes API Server: bind-address vs advertise-address
- OWASP Kubernetes Security Cheat Sheet
- Kubectl Config Set-Cluster Documentation
- Kubeconfig API Reference
- Using kubectl via an SSH Tunnel
- Kubeadm Init Reference
- Adding Alternative IP Address to Kubernetes API Server
- Adding a Name to Kubernetes API Server Certificate
- Securing Kubernetes Clusters with VPNs and Firewalls
댓글남기기