본문 바로가기
클라우드/쿠버네티스

🔐 쿠버네티스(k8s) x509 인증서로 안전한 API 서버 접근하기 (feat. openssl & CSR)

by gasbugs 2025. 8. 2.

안녕하세요! 클라우드 네이티브의 세계를 탐험하는 IT 블로거입니다. 오늘은 쿠버네티스 클러스터의 보안을 한 단계 끌어올리는 핵심 주제, 바로 x.509 클라이언트 인증서에 대해 깊이 파고들어 보려고 합니다.

 

쿠버네티스는 API 서버를 통해 모든 작업을 수행합니다. 그렇기 때문에 누가, 어떻게 API 서버에 접근할 수 있는지 통제하는 것은 클러스터 보안의 가장 첫걸음입니다. 여러 인증 방법 중 x.509 인증서는 사용자와 서비스 어카운트를 인증하는 가장 보편적이고 강력한 방법 중 하나입니다.

 

오늘은 새로운 사용자 'minjun'을 위한 x.509 인증서를 직접 생성하고, 이 인증서를 통해 쿠버네티스 클러스터에 안전하게 접속하는 전체 과정을 단계별로 알아보겠습니다.

 

🤔 x509 인증서 인증, 왜 필요한가요?

쿠버네티스 클러스터에서 kubectl 명령어를 사용할 때, 우리는 kubeconfig 파일 안에 정의된 인증 정보를 통해 API 서버로부터 '나'를 인증받습니다. 이 파일 안에는 클러스터 정보, 사용자 이름, 그리고 해당 사용자를 증명하는 클라이언트 인증서개인 키가 포함되어 있습니다.

 

https://prajwalt.medium.com/k8s-security-bit-by-bit-4-authentication-277e63bbcb29

 

API 서버는 요청이 들어올 때마다 클라이언트가 제시한 인증서를 신뢰할 수 있는 CA(Certificate Authority)에 의해 서명되었는지 확인합니다. 쿠버네티스에서는 클러스터 자체에 내장된 CA가 이 역할을 수행합니다.

 

인증서의 CN(Common Name)은 사용자 이름으로, O(Organization)는 사용자 그룹으로 매핑되어, 이후 RBAC(Role-Based Access Control)을 통해 권한을 부여하는 데 사용됩니다. 즉, x.509 인증서는 클러스터에 접속하기 위한 '신분증'과 같은 역할을 하는 셈이죠.

 

🚀 x509 인증서 생성 및 등록: 단계별 가이드

이제 'minjun'이라는 새로운 개발자가 입사했다고 가정하고, 이 개발자에게 default 네임스페이스의 pods만 조회할 수 있는 최소한의 권한을 부여하는 과정을 따라가 보겠습니다.

 

1단계: 개인 키(Private Key) 생성

모든 인증서의 시작은 개인 키입니다. 이 키는 절대로 외부에 노출되어서는 안 됩니다. openssl 도구를 사용해 2048비트 RSA 개인 키를 생성합니다.

# minjun 사용자의 개인 키(private key) 생성
openssl genrsa -out minjun.key 2048

 

이 명령을 실행하면 minjun.key 파일이 생성됩니다. 이 파일이 바로 'minjun'의 비밀 열쇠입니다.

 

2단계: 인증서 서명 요청(CSR) 생성

다음은 개인 키를 기반으로 인증서 서명 요청(Certificate Signing Request, CSR) 파일을 생성할 차례입니다. CSR에는 인증서 소유자의 정보(이름, 소속 등)와 공개 키가 포함됩니다.

여기서 가장 중요한 부분은 -subj 옵션입니다.

  • CN: Common Name, 즉 사용자 이름입니다. 여기서는 'minjun'으로 지정합니다.
  • O: Organization, 즉 사용자가 속한 그룹입니다. RBAC에서 그룹 단위로 권한을 부여할 때 유용합니다. 'dev-team'으로 지정해 보겠습니다.
# CSR 생성 (사용자 이름: minjun, 그룹: dev-team)
openssl req -new -key minjun.key -out minjun.csr -subj "/CN=minjun/O=dev-team"

 

이제 minjun.csr 파일이 생성되었습니다. 이 파일은 "저는 dev-team 소속의 minjun입니다. 제 정보가 맞으니 인증서 발급을 보증해주세요."라고 쿠버네티스 CA에 요청하는 신청서와 같습니다.

 

3단계: 쿠버네티스에 CSR 제출하기

생성된 CSR을 쿠버네티스 클러스터에 제출하여 서명을 받아야 합니다. 이를 위해 CertificateSigningRequest라는 쿠버네티스 오브젝트를 사용합니다.

먼저, CSR 파일 내용을 base64로 인코딩해야 합니다.

# CSR 파일 내용을 base64로 인코딩 (macOS)
cat minjun.csr | base64

# CSR 파일 내용을 base64로 인코딩 (Linux)
cat minjun.csr | base64 -w 0

 

이제 위에서 얻은 base64 인코딩 값을 사용하여 CertificateSigningRequest 매니페스트 파일을 작성합니다.

csr.yaml

apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
metadata:
  name: minjun-csr
spec:
  request: <여기에-위에서-복사한-base64-인코딩-값을-붙여넣으세요>
  signerName: kubernetes.io/kube-apiserver-client
  usages:
  - client auth
  • request: base64로 인코딩된 CSR 내용입니다.
  • signerName: 어떤 서명자가 서명할지 지정합니다. 클라이언트 인증의 경우 kubernetes.io/kube-apiserver-client를 사용합니다.
  • usages: 이 인증서의 용도를 나타냅니다. 'client auth'는 API 서버 인증용 클라이언트임을 의미합니다.

 

이제 이 YAML 파일을 클러스터에 적용합니다.

kubectl apply -f csr.yaml

 

CSR이 제출되었는지 확인해 봅시다. PENDING 상태로 대기 중인 것을 볼 수 있습니다.

kubectl get csr
# NAME         AGE   SIGNERNAME                            REQUESTER      CONDITION
# minjun-csr   10s   kubernetes.io/kube-apiserver-client   kubernetes-admin   Pending

4단계: CSR 승인 및 인증서 발급

보안을 위해 CSR은 자동으로 승인되지 않습니다. 클러스터 관리자가 직접 검토하고 승인해야 합니다.

# CSR 승인
kubectl certificate approve minjun-csr

 

승인이 완료되면 CONDITION이 Approved,Issued로 변경된 것을 확인할 수 있습니다. 이제 서명된 인증서를 파일로 추출할 차례입니다.

# 발급된 인증서(crt)를 파일로 저장
kubectl get csr minjun-csr -o jsonpath='{.status.certificate}' | base64 --decode > minjun.crt

 

드디어 minjun.crt 라는 서명된 인증서 파일이 생성되었습니다! 이 파일이 바로 쿠버네티스 API 서버가 신뢰하는 'minjun'의 공인 신분증입니다.

 

⚙️ kubeconfig 설정 및 권한 부여

인증서 발급은 끝났지만, 아직 할 일이 남았습니다. kubectl이 이 인증서를 사용하여 클러스터와 통신하도록 설정하고, 'minjun' 사용자에게 적절한 권한을 부여해야 합니다.

1단계: kubeconfig에 새로운 사용자 등록

kubectl config 명령을 사용하여 minjun.key와 minjun.crt를 사용하는 새로운 사용자 'minjun'을 kubeconfig에 추가합니다.

# 1. 새로운 사용자 자격 증명 설정
kubectl config set-credentials minjun --client-key=minjun.key --client-certificate=minjun.crt --embed-certs=true

# 2. 새로운 컨텍스트 생성 (minjun 사용자와 현재 클러스터를 연결)
kubectl config set-context minjun-context --cluster=$(kubectl config current-context | cut -d/ -f2) --user=minjun

# 확인
kubectl config get-contexts

 

--embed-certs=true 옵션은 인증서 파일의 내용을 kubeconfig 파일 안에 직접 포함시켜, 설정 파일 하나만으로 인증 정보를 관리할 수 있게 해주는 유용한 기능입니다.

2단계: RBAC을 통한 권한 부여

현재 minjun 사용자는 인증은 가능하지만, 클러스터에서 아무것도 할 수 없는 상태입니다. 이제 RoleRoleBinding을 생성하여 default 네임스페이스의 pods를 조회(get, list)할 수 있는 권한을 부여해 보겠습니다.

pod-reader-role.yaml

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: default
  name: pod-reader
rules:
- apiGroups: [""] # ""는 코어 API 그룹을 의미합니다.
  resources: ["pods"]
  verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: read-pods-minjun
  namespace: default
subjects:
- kind: User
  name: minjun # CSR 생성 시 CN에 지정한 사용자 이름
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: Role
  name: pod-reader
  apiGroup: rbac.authorization.k8s.io

 

YAML 파일을 적용합니다.

kubectl apply -f pod-reader-role.yaml

 

3단계: 새로운 사용자로 통신 테스트

모든 설정이 끝났습니다. 이제 minjun-context로 전환하여 'minjun' 사용자의 권한으로 API 서버와 통신해 봅시다.

# minjun 사용자로 컨텍스트 전환
kubectl config use-context minjun-context

# 권한이 있는 작업 테스트 (성공해야 함)
kubectl get pods --namespace=default

# 권한이 없는 작업 테스트 (실패해야 함)
kubectl get deployments --namespace=default
# Error from server (Forbidden): deployments.apps is forbidden: User "minjun" cannot list resource "deployments" in API group "apps" in the namespace "default"

 

예상대로 pods 조회는 성공하고, deployments 조회는 권한 오류가 발생하는 것을 확인할 수 있습니다. 이로써 우리는 x.509 인증서를 통해 사용자를 성공적으로 인증하고, RBAC으로 세밀하게 권한을 제어하는 데 성공했습니다!

 

마무리하며

오늘은 openssl과 쿠버네티스의 CertificateSigningRequest를 이용해 x.509 클라이언트 인증서를 발급하고, 이를 통해 특정 사용자에게 필요한 최소한의 권한을 부여하는 전체 과정을 살펴보았습니다.

이러한 인증 및 인가(Authorization) 메커니즘은 멀티 테넌트 환경을 구축하거나, CI/CD 파이프라인에 사용될 서비스 어카운트에 특정 권한만 부여하는 등 클러스터를 안전하게 운영하기 위한 필수적인 기술입니다. 처음에는 과정이 다소 복잡해 보일 수 있지만, 각 단계의 의미를 이해하고 직접 따라 해보면 쿠버네티스 보안에 대한 자신감을 얻으실 수 있을 겁니다.