4 분 소요

게시:

쿠버네티스 클러스터를 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.crtca.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 추가)는 표준을 준수하는 보안성과 정합성을 보장하는 권장 방식입니다.

환경 특성과 운영 정책에 맞게 적절한 방법을 선택하여 외부에서도 안전하게 클러스터를 관리할 수 있습니다.

참고문서

댓글남기기