Security - 정리(1)

2026. 1. 1. 20:30·CKA

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 앞단에서 두 가지가 결정됩니다.

  1. 인증(Authentication): “너 누구야?”
  2. 인가(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가지:

  1. 암호화(Confidentiality)
    • 공개키로 암호화 → 개인키로 복호화
  2. 서명(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 내부는 크게 두 종류 통신이 있습니다.

  1. 외부/내부 주체 → API Server(HTTPS)
  2. 컴포넌트끼리 → 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를 제공합니다.

흐름:

  1. 사용자가 로컬에서 user.key + user.csr 생성
  2. 관리자가 CSR을 Kubernetes CertificateSigningRequest 오브젝트로 등록
  3. 관리자가 검토 후 approve/deny
  4. controller-manager가 CA 키로 서명 → .status.certificate에 발급 결과 저장
  5. 관리자가 인증서를 추출해 사용자에게 전달

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
'CKA' 카테고리의 다른 글
  • Security - Authorization(RBAC)
  • Security - API Groups
  • Security - Kubeconfig
  • Security - CSR API
5jyan5
5jyan5
  • 5jyan5
    jyan
    5jyan5
  • 전체
    오늘
    어제
    • 분류 전체보기 (243)
      • 김영한의 스프링 핵심 원리(기본편) (8)
      • 김영한의 스프링 핵심 원리 - 고급편 (11)
      • 김영한의 스프링 MVC 1편 (1)
      • 김영한의 스프링 DB 1편 (3)
      • 김영한의 스프링 MVC 2편 (3)
      • 김영한의 ORM 표준 JPA 프로그래밍(기본편) (9)
      • 김영한의 스프링 부트와 JPA 활용2 (2)
      • 김영한의 실전 자바 - 중급 1편 (1)
      • 김영한의 실전 자바 - 고급 1편 (9)
      • 김영한의 실전 자바 - 고급 2편 (9)
      • Readable Code: 읽기 좋은 코드를 작성.. (2)
      • 김영한의 실전 자바 - 고급 3편 (9)
      • CKA (119)
      • 개발 (37)
      • 경제 (4)
      • 리뷰 (1)
      • 정보 (2)
  • 블로그 메뉴

    • 링크

    • 공지사항

    • 인기 글

    • 태그

      JPQL
      Thread
      reentarantlock
      조회 성능 최적화
      락
      양방향 맵핑
      자바
      hibernate5module
      고급
      cglib
      @discriminatorvalue
      log trace
      jdk 동적 프록시
      gesingleresult
      김영한
      프록시
      requset scope
      @within
      스레드
      빈 후처리기
      버퍼
      typequery
      WAS
      페치 조인
      jpq
      Target
      프록시 팩토리
      @discriminatorcolumn
      @args
      단방향 맵핑
    • 최근 댓글

    • 최근 글

    • hELLO· Designed By정상우.v4.10.2
    5jyan5
    Security - 정리(1)
    상단으로

    티스토리툴바