[Kubernetes] Cluster: 내 손으로 클러스터 구성하기 - 4.2. Provisioning a CA and Generating TLS Certificates
서종호(가시다)님의 On-Premise K8s Hands-on Study 1주차 학습 내용을 기반으로 합니다.
TL;DR
이번 글의 목표는 OpenSSL 설정 파일(ca.conf)의 구조 이해다. Kubernetes the Hard Way 튜토리얼의 Provisioning a CA and Generating TLS Certificates 단계에서 사용하는 ca.conf 파일을 분석한다.
- ca.conf 파일의 전체 구조와 섹션별 역할 분석
- Root CA, Admin, Worker Node, API Server 등 각 인증서 설정 이해
- Subject 필드(CN, O)와 Kubernetes RBAC의 연동 방식
- SAN(Subject Alternative Name) 설정의 중요성
생성할 인증서 목록
이번 실습에서 생성할 인증서 구조는 다음과 같다. 이전 글에서 살펴본 것처럼 mTLS 기반 인증 구조에서 각 컴포넌트가 독립적인 신원을 가져야 하므로, 컴포넌트마다 별도의 인증서가 필요하다.
CA (Certificate Authority)
├── kube-apiserver 인증서
│ └── 용도: API 서버 식별
├── kubelet 인증서 (각 노드마다)
│ └── 용도: 워커 노드 식별
├── kube-controller-manager 인증서
├── kube-scheduler 인증서
├── kube-proxy 인증서
├── service-accounts 인증서
└── admin 인증서
└── 용도: kubectl로 클러스터 관리
| 용도 | 개인키 | CSR | 인증서 | Subject 정보 |
|---|---|---|---|---|
| Root CA | ca.key | - | ca.crt | CN=CA |
| admin | admin.key | admin.csr | admin.crt | CN=admin, O=system:masters |
| node-0 | node-0.key | node-0.csr | node-0.crt | CN=system:node:node-0, O=system:nodes |
| node-1 | node-1.key | node-1.csr | node-1.crt | CN=system:node:node-1, O=system:nodes |
| kube-proxy | kube-proxy.key | kube-proxy.csr | kube-proxy.crt | CN=system:kube-proxy, O=system:node-proxier |
| kube-scheduler | kube-scheduler.key | kube-scheduler.csr | kube-scheduler.crt | CN=system:kube-scheduler, O=system:kube-scheduler |
| kube-controller-manager | kube-controller-manager.key | kube-controller-manager.csr | kube-controller-manager.crt | CN=system:kube-controller-manager, O=system:kube-controller-manager |
| kube-api-server | kube-api-server.key | kube-api-server.csr | kube-api-server.crt | CN=kubernetes, SAN 포함 |
| service-accounts | service-accounts.key | service-accounts.csr | service-accounts.crt | CN=service-accounts |
ca.conf
실습에서는 위의 모든 인증서를 생성하기 위한 설정 파일을 제공해 주고 있다. 실습 환경의 편의를 위해 미리 정의된 파일을 사용하지만, 그 본질은 클러스터 내 모든 통신의 신원을 보장하는 정책을 한 데 모아 정의하는 데에 있다(출처: 박용연님 스터디 정리 글)
ca.conf는 쿠버네티스 클러스터 내 모든 컴포넌트의 TLS 인증서를 생성하기 위한 OpenSSL 설정 파일 형식을 따른다. 하나의 설정 파일에 여러 인증서 설정을 섹션별로 정의해 두고, OpenSSL 명령어 실행 시 -section 옵션으로 해당 섹션을 참조하여 각각의 인증서를 생성한다.
단, Root CA 생성 시에는 -section 옵션 없이 기본 [req] 섹션을 사용하고, 컴포넌트 인증서들은 각자의 섹션(admin, node-0 등)을 명시적으로 지정한다.
섹션별 항목 설명
박용연님의 1주차 스터디 정리 글을 참고하였습니다.
섹션 공통 구조
각 섹션 별로 아래와 같은 구조를 따른다.
distinguished_name: 인증서의 주체(Subject) 정보를 어디서 가져올지 지정XXX_req_extensions: CSR을 만들 때, 추가할 확장(extension) 필드들이 어디에 있는지 지정
req_extensions
basicConstraints: CA 인증서인지 확인extendedKeyUSage: 인증서 사용 용도 지정keyUsage: 키의 사용 방식 지정subjectAltName: 해당 인증서로 접근할 수 있는 DNS 이름과 IP 주소 목록(SAN, Subject Alternative Name). 항목이 많을 경우@를 사용해 별도 섹션으로 분리 가능
distinguished_name
인증서의 Subject 정보를 정의하는 섹션이다. CN, O 등의 필드는 X.500 표준에서 유래한 Distinguished Name(DN) 구조를 따른다.
DN 필드의 유래와 의미는 이전 글(4.1)의 “Distinguished Name과 X.500 표준” 섹션을 참고하자.
CN(Common Name): 사용자 이름으로 매핑.system:node:<nodeName>,system:kube-scheduler등O(Organization): 그룹 이름으로 매핑.system:nodes,system:masters등
이 필드들은 RBAC 권한 부여 시 사용되므로, 올바른 값 설정이 중요하다.
파일 구성 확인
ca.conf 파일은 다음 8가지 부분으로 구성된다.
- Root CA: CA 자체의 인증서 설정
- Admin: kubectl 사용자 인증서
- Service Accounts: ServiceAccount 토큰 서명용 인증서
- Worker Nodes: kubelet 인증서 (node-0, node-1)
- Kube Proxy: kube-proxy 인증서
- Controller Manager: kube-controller-manager 인증서
- Scheduler: kube-scheduler 인증서
- API Server: kube-apiserver 인증서 (SAN 포함)
- 공통 섹션: 여러 인증서에서 공통으로 참조하는 설정
Root CA
CA 자체의 인증서를 생성하기 위한 부분으로, [req], [ca_x509_extensions], [req_distinguished_name] 섹션으로 구성된다.
[req]
distinguished_name = req_distinguished_name
prompt = no # CSR 생성 시 대화형 입력 없음
x509_extensions = ca_x509_extensions # CA 인증서 생성 시 사용할 확장 필드: ca_x509_extensions
# x_509_extensions에 의해 바로 아래 해당 블록을 참조함
[ca_x509_extensions] # CA 인증서 설정 (Root of Trust)
basicConstraints = CA:TRUE # 이 인증서가 CA임을 명시
keyUsage = cRLSign, keyCertSign # CA의 핵심 권한
# - keyCertSign: 다른 인증서에 서명
# - cRLSign: 인증서 폐기 목록(CRL) 서명
[req_distinguished_name]
C = US # Country: 국가 코드
ST = Washington # State: 주/도
L = Seattle # Locality: 지역/도시
CN = CA # Common Name: 이름. 이 경우, CA로 클러스터 CA
basicConstraints = CA:TRUE가 설정이 핵심으로, 이 설정이 있어야 다른 인증서에 서명할 수 있는 CA 인증서가 된다[req_distinguished_name]항목에 따라, 인증서가 생성될 때 Subject 필드에Subject: C=US, ST=Washington, L=Seattle, CN=CA값이 들어간다
Root CA는 지리적 정보(C, ST, L)를 포함하지만, 다른 컴포넌트 인증서들은 주로 CN(사용자)과 O(그룹)만 중요하다.
Admin
Admin 사용자를 위한 설정이다. [admin], [admin_distinguished_name] 섹션으로 구성된다.
클러스터 관리자 권한을 가진 사용자(CN=admin)를 위한 인증서이다. 이 실습에서는 jumpbox에서 kubectl을 실행할 때 이 인증서를 사용하여 클러스터를 관리한다.
실제 kubectl 설정 방법은 10. Configuring kubectl for Remote Access에서 다룬다.
[admin] # Admin 사용자 (kubectl)
distinguished_name = admin_distinguished_name
prompt = no
req_extensions = default_req_extensions
[admin_distinguished_name]
CN = admin # 쿠버네티스 사용자 이름
O = system:masters # 쿠버네티스 슈퍼유저 그룹, 모든 RBAC 인가 우회
O = system:masters는 쿠버네티스의 built-in 슈퍼유저 그룹이다. 이 그룹에 속한 사용자는 모든 리소스에 대한 전체 권한을 가진다.- kubectl은 여러 사용자 인증서를 전환하며 사용할 수 있지만, 이 실습에서는 단순화를 위해 admin 인증서만 사용한다.
Service Accounts
[service-accounts], [service-accounts_distinguished_name] 섹션으로 구성된다.
ServiceAccount 토큰의 생성(서명)과 검증에 사용되는 키 쌍이다. Pod 내부에서 실행되는 애플리케이션이 API Server와 통신할 때 인증에 사용하는 토큰을 발급하고 검증하는 데 필요하다.
이후 실습 진행 과정의 kube-apiserver, kube-controller-manager 설정 과정에서 사용될 것이다.
- kube-controller-manager:
--service-account-private-key-file에 개인키(.key)를 지정하여 ServiceAccount 토큰 생성 및 서명 - kube-apiserver:
--service-account-key-file에 공개키(.crt)를 지정하여 Pod가 보낸 토큰 검증--service-account-signing-key-file에 개인키(.key)를 지정하여 API Server도 토큰 발급 가능 (v1.20+)
실제 설정은 8.1. Bootstrapping the Kubernetes Control Plane에서 확인할 수 있다.
[service-accounts] # Service Account 서명자
distinguished_name = service-accounts_distinguished_name
prompt = no
req_extensions = default_req_extensions
[service-accounts_distinguished_name]
CN = service-accounts # ServiceAccount 토큰 서명용 인증서
ServiceAccount 인증서는 API Server와 통신하는 데에 사용되지 않고, 단지 Service Account 토큰을 서명하고 검증하는 데에만 사용된다. 따라서 RBAC 매핑이 필요 없어 O 필드가 필요하지 않다.
Worker Nodes
[node-0], [node-1] 및 각각의 하위 섹션들로 구성된다.
kubelet이 API 서버와 통신하기 위한 인증서이다.
- kubelet 인증서는 Node Authorizer의 요구사항을 충족해야 한다. CN은 반드시
system:node:<nodeName>형식이어야 하고, O는system:nodes여야 한다. extendedKeyUsage에clientAuth와serverAuth가 모두 포함된 이유는, kubelet이 API 서버의 클라이언트이면서 동시에 10250 포트로 HTTPS 서버를 운영하기 때문이다.
node-0과 node-1이 동일한 구조로 정의된다.
[node-0] # Worker Node 인증서 (kubelet)
distinguished_name = node-0_distinguished_name # node-0_distinguished_name 참조
prompt = no
req_extensions = node-0_req_extensions
[node-0_req_extensions]
basicConstraints = CA:FALSE
extendedKeyUsage = clientAuth, serverAuth # 클라이언트 및 서버 인증 모두 사용
keyUsage = critical, digitalSignature, keyEncipherment
nsCertType = client
nsComment = "Node-0 Certificate"
subjectAltName = DNS:node-0, IP:127.0.0.1
subjectKeyIdentifier = hash
[node-0_distinguished_name]
CN = system:node:node-0 # CN = system:node:<nodeName>
O = system:nodes # O = system:nodes
C = US
ST = Washington
L = Seattle
# node-1 생략
Kube Proxy
[kube-proxy] 및 하위 섹션들로 구성된다. 각 노드의 kube-proxy가 API 서버와 통신하기 위한 인증서이다.
O = system:node-proxier는 kube-proxy에 필요한 권한을 부여하는 built-in ClusterRoleBinding과 연결된다.
[kube-proxy] # kube-proxy
distinguished_name = kube-proxy_distinguished_name
prompt = no
req_extensions = kube-proxy_req_extensions
[kube-proxy_req_extensions]
basicConstraints = CA:FALSE
extendedKeyUsage = clientAuth, serverAuth
keyUsage = critical, digitalSignature, keyEncipherment
nsCertType = client
nsComment = "Kube Proxy Certificate"
subjectAltName = DNS:kube-proxy, IP:127.0.0.1
subjectKeyIdentifier = hash
[kube-proxy_distinguished_name]
CN = system:kube-proxy
O = system:node-proxier # 서비스 네트워크 제어 권한
C = US
ST = Washington
L = Seattle
Controller Manager, Scheduler
Control plane 컴포넌트인 kube-controller-manager와 kube-scheduler의 인증서이다.
# Controller Manager
[kube-controller-manager]
distinguished_name = kube-controller-manager_distinguished_name
prompt = no
req_extensions = kube-controller-manager_req_extensions
[kube-controller-manager_req_extensions]
basicConstraints = CA:FALSE
extendedKeyUsage = clientAuth, serverAuth
keyUsage = critical, digitalSignature, keyEncipherment
nsCertType = client
nsComment = "Kube Controller Manager Certificate"
subjectAltName = DNS:kube-controller-manager, IP:127.0.0.1
subjectKeyIdentifier = hash
[kube-controller-manager_distinguished_name]
CN = system:kube-controller-manager
O = system:kube-controller-manager # 클러스터 상태 관리 권한
C = US
ST = Washington
L = Seattle
# Scheduler
[kube-scheduler]
distinguished_name = kube-scheduler_distinguished_name
prompt = no
req_extensions = kube-scheduler_req_extensions
[kube-scheduler_req_extensions]
basicConstraints = CA:FALSE
extendedKeyUsage = clientAuth, serverAuth
keyUsage = critical, digitalSignature, keyEncipherment
nsCertType = client
nsComment = "Kube Scheduler Certificate"
subjectAltName = DNS:kube-scheduler, IP:127.0.0.1
subjectKeyIdentifier = hash
[kube-scheduler_distinguished_name]
CN = system:kube-scheduler
O = system:kube-scheduler # Pod 스케줄링 권한
C = US
ST = Washington
L = Seattle
API Server
[kube-api-server] 및 하위 섹션들로 구성된다. 가장 복잡한 SAN(Subject Alternative Names) 설정을 가진다.
API 서버는 여러 경로로 접근 가능해야 하므로, 인증서에 모든 접근 주소를 SAN으로 포함해야 한다.
| 접근 경로 | 예시 |
|---|---|
| localhost | 127.0.0.1 |
| Service ClusterIP | 10.32.0.1 (ServiceCIDR의 첫 번째 IP) |
| 내부 DNS | kubernetes, kubernetes.default, kubernetes.default.svc 등 |
| 외부 접근용 | server.kubernetes.local, api-server.kubernetes.local |
10.32.0.1은 ServiceCIDR(10.32.0.0/24)의 첫 번째 IP이다. 쿠버네티스는 이 IP를kubernetesService의 ClusterIP로 자동 할당한다. Pod 내부에서 API 서버에 접근할 때 이 IP를 사용한다.- 로드밸런서나 외부 접근용 도메인을 추가해야 한다면,
[kube-api-server_alt_names]섹션에 해당 IP나 DNS를 추가해야 한다.
[kube-api-server] # API Server 인증서
distinguished_name = kube-api-server_distinguished_name
prompt = no
req_extensions = kube-api-server_req_extensions
[kube-api-server_req_extensions]
basicConstraints = CA:FALSE
extendedKeyUsage = clientAuth, serverAuth
keyUsage = critical, digitalSignature, keyEncipherment
nsCertType = client, server
nsComment = "Kube API Server Certificate"
subjectAltName = @kube-api-server_alt_names
subjectKeyIdentifier = hash
[kube-api-server_alt_names] # SAN: 모든 내부/외부 접근 주소
IP.0 = 127.0.0.1
IP.1 = 10.32.0.1 # ServiceCIDR의 첫 번째 IP
DNS.0 = kubernetes
DNS.1 = kubernetes.default
DNS.2 = kubernetes.default.svc
DNS.3 = kubernetes.default.svc.cluster
DNS.4 = kubernetes.svc.cluster.local
DNS.5 = server.kubernetes.local
DNS.6 = api-server.kubernetes.local
[kube-api-server_distinguished_name]
CN = kubernetes
C = US
ST = Washington
L = Seattle
공통 섹션
[default_req_extensions]는 여러 클라이언트 인증서에서 공통으로 참조하는 설정이다.
- admin, service-accounts 인증서가 이 공통 섹션을 참조한다.
- kubelet과 apiserver는 서버 역할도 해야 하므로 별도의
req_extensions를 정의한다.
[default_req_extensions] # 공통 CSR 확장
basicConstraints = CA:FALSE
extendedKeyUsage = clientAuth # 클라이언트 인증 전용
keyUsage = critical, digitalSignature, keyEncipherment
nsCertType = client
nsComment = "Admin Client Certificate"
subjectKeyIdentifier = hash
참조 동작 방식
ca.conf 파일은 섹션 간 참조를 통해 설정을 재사용한다.
단순 참조
distinguished_name 지시어로 Subject 정보가 담긴 섹션을 참조한다.
[섹션명]
distinguished_name = [다른섹션명]_distinguished_name
└─────────────┘
│
▼
[다른섹션명_distinguished_name]
CN = ...
O = ...
체인 참조
req_extensions가 다시 subjectAltName을 통해 다른 섹션을 참조하는 구조이다. API Server 인증서에서 사용된다.
[섹션명]
req_extensions = [섹션명]_req_extensions
│
▼
[섹션명_req_extensions]
subjectAltName = @[섹션명]_alt_names
└──────────┘
│
▼
[섹션명_alt_names]
IP.0 = ...
DNS.0 = ...
공통 참조
여러 섹션이 동일한 공통 섹션을 참조하는 구조이다.
[admin] [service-accounts]
req_extensions = default req_extensions = default
│ │
└────────┬───────┘
▼
[default_req_extensions]
(공통 클라이언트 설정)
실제 동작

OpenSSL 명령어 실행 시 -section 옵션으로 섹션을 지정하면, 해당 섹션에서 시작하여 참조를 따라가며 설정을 읽는다.
# admin 인증서 CSR 생성 시
openssl req -new -key admin.key \
-config ca.conf \
-section admin \ # ← [admin] 섹션 참조
-out admin.csr
# 내부적으로:
# 1. [admin] 섹션 읽기
# 2. distinguished_name = admin_distinguished_name 발견
# 3. [admin_distinguished_name] 섹션으로 이동 → CN, O 값 읽기
# 4. req_extensions = default_req_extensions 발견
# 5. [default_req_extensions] 섹션으로 이동 → 확장 정보 읽기
결과
이 단계를 완료하면 다음과 같은 결과를 얻을 수 있다:
- ca.conf 파일 구조 이해: 하나의 파일에 여러 인증서 설정을 섹션별로 정의하고,
-section옵션으로 선택 - Subject 필드와 RBAC 연동: CN은 사용자 이름, O는 그룹 이름으로 매핑되어 Kubernetes 인증/인가에 사용
- SAN(Subject Alternative Name) 설정: API Server 인증서에 모든 접근 경로(IP, DNS)를 포함해야 함
이번 실습에서는 Kubernetes the Hard Way 튜토리얼에서 제공하는 ca.conf 파일을 분석했다. 실무에서 인증서 갱신, 컴포넌트 추가, 커스텀 네트워크 환경 구성 시 이 글에서 분석한 각 섹션의 의미와 필드 설정 방법을 참고하면 된다.
다음 단계에서는 이 설정 파일을 사용하여 실제로 인증서를 생성하고 확인한다.
댓글남기기