Kubernetes를 “프로덕션 플랫폼”으로 쓰기 시작하면, 결국 보안은 다음 질문으로 수렴합니다.
- 누가(Who) 클러스터에 접근할 수 있는가? (Authentication)
- 무엇을(What) 할 수 있는가? (Authorization/RBAC)
- 그 통신은 도청/변조로부터 안전한가? (TLS/mTLS)
- 인증서/키는 어떻게 발급·회전·폐기하는가? (CSR API, 만료 점검)
- 여러 클러스터/환경을 어떻게 안전하게 다루는가? (kubeconfig/context)
이 글은 위 흐름대로 정리합니다.
1. “클러스터 보안”의 출발점: 호스트(노드)부터
클러스터가 안전하려면 “Kubernetes 안”만 신경 쓰면 안 됩니다. 노드(호스트)가 뚫리면, 그 위의 모든 것이 무너집니다.
운영 환경의 기본 수칙:
- 루트 로그인 금지(또는 최소화), 패스워드 로그인 비활성화
- SSH 키 기반 로그인만 허용
- OS 패치/취약점 점검, 방화벽/보안그룹 최소화
- 노드 접근 권한(누가 SSH 가능한지)을 조직 정책으로 관리
이 기반 위에서 Kubernetes 영역 보안을 설계합니다.
2. Kubernetes 보안의 “첫 번째 방어선”: API Server
Kubernetes에서 거의 모든 관리 작업은 kube-apiserver를 통해 일어납니다.
kubectl로 하든,- REST API로 직접 호출하든,
- 다른 컨트롤 플레인 컴포넌트가 내부적으로 호출하든,
결국 요청은 API Server를 거칩니다.
그래서 API Server 앞단에서 두 가지가 결정됩니다.
- 인증(Authentication): “너 누구야?”
- 인가(Authorization): “너 이거 해도 돼?”
3. 인증(Authentication): Kubernetes는 “일반 사용자 계정”을 내부적으로 관리하지 않는다
여기서 가장 많이 헷갈리는 지점이 하나 있습니다.
3.1 “User”와 “ServiceAccount”는 다릅니다
- User(일반 사용자/사람)
Kubernetes가 자체적으로 생성/목록/비밀번호 관리를 하지 않습니다.
보통 외부(인증서, OIDC/SSO, LDAP 등)에서 “이미 존재하는 신원”을 가져와 씁니다. - ServiceAccount(서비스 계정/클러스터 내부 프로세스용)
Kubernetes가 리소스로 관리합니다.파드가 API Server와 통신할 때 “아 내가 누구인지”를 증명하는 정체성으로 쓰입니다. kubectl get sa -A kubectl create sa myapp
즉, ServiceAccount가 User 같은 역할을 하긴 하지만, 목적과 생명주기(관리 방식)가 다릅니다.
3.2 인증 방식들(대표)
- 정적 토큰/정적 패스워드 파일: 학습용으로만 (구버전에서 deprecated)
- X.509 클라이언트 인증서 기반: 온프렘/관리자 접근에서 흔함
- 외부 인증(예: OIDC/SSO, LDAP, Kerberos): 조직형 환경에서 흔함
- ServiceAccount 토큰: 파드/컨트롤러 등 “클러스터 내부 주체”용
참고: “정적 패스워드/토큰 파일” 방식은 실무 권장 아님(평문 저장/관리 난이도/버전 제한).
4. 인가(Authorization): RBAC가 사실상 표준
인증이 끝났으면, 그 다음은 “권한”입니다.
- RBAC(Role/ClusterRole + Binding)
- Node authorizer, Webhook 등도 있지만, 일반적으로 RBAC가 기본
여기서 중요한 건:
“인증서(신원)만 발급해주면 끝”이 아니라, RBAC로 무엇을 할 수 있는지 반드시 제한해야 한다는 점입니다.
5. TLS/인증서가 필요한 이유: “도청” + “가짜 서버(MITM)” 문제
5.1 대칭키(Symmetric Key) 암호화의 핵심과 한계
대칭키 방식은 “같은 키로 암호화/복호화”합니다.
- 장점: 빠르고 효율적(대량 데이터에 적합)
- 문제: 키를 어떻게 안전하게 공유하나?
키를 네트워크로 보내는 순간, 공격자가 키를 가로채면 끝입니다.
즉, 대칭키 암호화의 약점은 키 교환(key exchange) 입니다.
5.2 비대칭키(Asymmetric Key)로 “키 교환” 문제를 해결
비대칭키는 공개키/개인키 쌍을 씁니다.
- 공개키: 공개해도 됨
- 개인키: 절대 유출되면 안 됨
대표 사용 패턴 2가지:
- 암호화(Confidentiality)
- 공개키로 암호화 → 개인키로 복호화
- 서명(Signature, 무결성/신원확인)
- 개인키로 서명 → 공개키로 검증
TLS/HTTPS에서 핵심은 대개:
- 비대칭키로 “안전하게 대칭키를 합의/교환”
- 실제 데이터 전송은 빠른 대칭키로 처리
5.3 그런데 비대칭키만으로도 “가짜 서버” 문제는 남는다
클라이언트가 어떤 서버에 접속했는데,
그 서버가 진짜인지 가짜인지 어떻게 보장할까요?
공격자는 자기 서버를 세우고, 자기 공개키를 “서버 공개키”라고 내밀 수 있습니다.
그러면 통신은 암호화되지만, 가짜 서버와 안전하게 통신하는 상황이 됩니다(MITM).
그래서 CA(인증기관)가 필요합니다.
6. CA(인증기관)와 인증서: “이 서버는 진짜다”를 증명하는 체계
6.1 인증서의 역할
서버는 단순히 공개키만 보내지 않고, 인증서(공개키 + 신원 정보 + CA 서명)를 보냅니다.
클라이언트는 다음을 확인합니다.
- 이 인증서가 신뢰하는 CA에 의해 서명되었는가?
- 인증서의 이름(CN/SAN) 이 내가 접속한 도메인과 맞는가?
6.2 가짜 서버가 CA 서명을 못 받는 이유(현실적으로)
CA는 “이 도메인의 소유자”임을 검증합니다.
공격자가 my-bank.com의 도메인 소유자가 아니라면, 일반적인 검증(HTTP/DNS/이메일 등)에서 통과하기 어렵습니다.
그리고 더 중요한 포인트:
가짜 서버가 “진짜 인증서 파일”을 복사해도 서버 개인키가 없으면 TLS 핸드셰이크를 통과할 수 없습니다.
(인증서는 공개 정보지만, 그 인증서에 대응하는 개인키는 서버만 가지고 있어야 합니다.)
7. SSH는 왜 CA 없이도 동작하나? (known_hosts 모델)
SSH는 웹처럼 “전 세계 공용 PKI(브라우저 내장 CA)”에 기대지 않는 경우가 많습니다.
대신 SSH는 보통 다음 모델을 씁니다.
- 서버는 호스트키(host key) 를 가짐(서버의 고유 개인키/공개키)
- 클라이언트는 최초 접속 시 서버 호스트 공개키를
~/.ssh/known_hosts에 저장 - 다음 접속 때 서버가 제시한 공개키가 known_hosts와 다르면 경고(중간자 공격 가능)
즉 SSH는:
- 첫 연결을 신뢰(TOFU: Trust On First Use) 하고
- 이후 변경을 감지하는 방식이 기본입니다.
(조직에서 SSH CA를 도입하는 방식도 있지만, 기본 모델은 위와 같습니다.)
8. Kubernetes 클러스터의 TLS 구조: “누가 서버고 누가 클라이언트인가”
Kubernetes 내부는 크게 두 종류 통신이 있습니다.
- 외부/내부 주체 → API Server(HTTPS)
- 컴포넌트끼리 → mTLS(TLS + 클라이언트 인증서)
kubeadm 기준으로 주요 인증서 흐름을 정리하면:
8.1 서버 인증서(Serving Cert)가 필요한 컴포넌트
- kube-apiserver (가장 중요)
- etcd
- kubelet(각 노드에서 HTTPS 엔드포인트 제공)
8.2 클라이언트 인증서(Client Cert)가 필요한 주체
- 관리자(kubectl의 admin)
- kube-scheduler (API Server에 붙는 클라이언트)
- kube-controller-manager
- kube-proxy
- kube-apiserver가 etcd에 붙을 때의 클라이언트 인증서(예: apiserver-etcd-client)
- kube-apiserver가 kubelet에 붙을 때의 클라이언트 인증서(예: apiserver-kubelet-client)
8.3 CA가 2개일 수 있다: pki/ca.crt vs pki/etcd/ca.crt
kubeadm 클러스터에서 흔히:
/etc/kubernetes/pki/ca.crt: “클러스터 전반” CA/etc/kubernetes/pki/etcd/ca.crt: “etcd 전용” CA
etcd는 클러스터 상태의 핵심 저장소라 신뢰 도메인을 분리하는 구성이 흔합니다.
9. kubeadm에서 control plane 인증서는 어디 있나?
대부분 여기입니다.
/etc/kubernetes/pki//etc/kubernetes/pki/etcd/
그리고 “어떤 컴포넌트가 어떤 인증서를 쓰는지”는 아래에서 확정합니다.
/etc/kubernetes/manifests/kube-apiserver.yaml/etc/kubernetes/manifests/etcd.yaml/etc/kubernetes/manifests/kube-scheduler.yaml/etc/kubernetes/manifests/kube-controller-manager.yaml
10. 기존 클러스터에서 인증서 점검(만료/발급자/SAN)
10.1 openssl로 인증서 내용 확인
openssl x509 -in /etc/kubernetes/pki/apiserver.crt -noout -text
openssl x509 -in /etc/kubernetes/pki/apiserver.crt -noout -subject -issuer -dates
중점 체크:
- Subject(CN/O/OU)
- Issuer(누가 서명했나)
- Validity(Not After 만료일)
- SAN(DNS/IP) — 특히 apiserver
10.2 kubeadm 빠른 만료 체크
sudo kubeadm certs check-expiration
10.3 장애 시 로그 확인(상황별)
- 가능하면:
kubectl -n kube-system logs <pod>- API Server가 죽어 kubectl이 안 되면:
sudo journalctl -u kubelet -n 200 --no-pager sudo crictl ps -a sudo crictl logs <container-id>
11. Certificate API(CSR): 인증서 발급을 “클러스터 오브젝트”로 관리하기
수동으로 ca.key 들고 서명하는 방식은 팀이 커지면 운영이 힘듭니다.
그래서 Kubernetes는 CSR API를 제공합니다.
흐름:
- 사용자가 로컬에서
user.key+user.csr생성 - 관리자가 CSR을 Kubernetes
CertificateSigningRequest오브젝트로 등록 - 관리자가 검토 후 approve/deny
- controller-manager가 CA 키로 서명 →
.status.certificate에 발급 결과 저장 - 관리자가 인증서를 추출해 사용자에게 전달
11.1 CSR 오브젝트 생성에서 가장 흔한 실수: base64 한 줄
CSR은 .spec.request에 base64(한 줄)로 넣어야 합니다.
cat akshay.csr | base64 -w 0
11.2 승인/거절/삭제 기본 명령
kubectl get csr
kubectl describe csr akshay
kubectl certificate approve akshay
kubectl certificate deny agent-smith
kubectl delete csr agent-smith
11.3 보안 관점: “요청 그룹(system:masters)”는 특히 주의
CSR을 YAML로 보면 spec.groups 같은 정보가 보일 수 있습니다.system:masters는 사실상 관리자급 권한으로 연결되기 쉬운 그룹이라, 요청이 의심스러우면 deny 후 삭제가 맞습니다.
12. kubeconfig: kubectl이 인증서/서버 주소를 매번 치지 않게 하는 파일
kubeconfig는 3요소로 구성됩니다.
- clusters: API 서버 주소 + CA
- users: 내 인증서/키(또는 토큰)
- contexts: 어떤 유저로 어떤 클러스터에 들어갈지 + 기본 namespace
그리고 current-context가 “기본으로 사용할 접속 프로필”입니다.
12.1 자주 쓰는 kubectl config 명령
kubectl config view
kubectl config get-contexts
kubectl config current-context
kubectl config use-context <context>
12.2 --kubeconfig를 썼는데 current-context가 안 바뀐 것처럼 보이는 이유
use-context를 특정 kubeconfig 파일에 적용했는데, 다음 조회를 기본 파일(~/.kube/config)로 해버리면 그렇게 보입니다.
정확히 같은 파일로 조회해야 합니다.
kubectl --kubeconfig=/root/my-kube-config config current-context
12.3 파일을 바꾸지 않고 “기본 참조”를 바꾸는 법: KUBECONFIG
export KUBECONFIG=/root/my-kube-config
kubectl config current-context
13. 실무 체크리스트(요약)
- API Server 인증/인가(RBAC) 분리해서 설계
- CA 개인키(
ca.key) 접근 통제(최상위 비밀) - 인증서 만료 점검 자동화(
kubeadm certs check-expiration) - CSR 요청 승인 프로세스(검토/승인자/기록) 정착
- etcd CA 분리 여부 이해(환경별)
- kubeconfig/context 실수 방지(특히 prod) —
current-context습관화 - 네트워크 정책(NetworkPolicy)로 파드 간 통신 제한
- Secret은 “base64는 암호화가 아님” + at-rest 암호화/접근통제 병행
'CKA' 카테고리의 다른 글
| Security - Authorization(RBAC) (0) | 2026.01.01 |
|---|---|
| Security - API Groups (0) | 2026.01.01 |
| Security - Kubeconfig (0) | 2026.01.01 |
| Security - CSR API (0) | 2026.01.01 |
| Security - 인증서 상태 점검 방법 (1) | 2026.01.01 |