쿠버네티스 보안 은 “방화벽 하나 잘 치면 끝” 이 아니다. 공격 표면 이 층층 이 다 — API 서버, 노드 호스트, 시크릿, 네트워크, 이미지, 파드, 감사. 한 층 이 뚫려도 다음 층 이 막게 깊이(defense in depth) 로 쌓아야 한다. 6노드 온프레미스 K3s 를 굴리며 실제로 챙기는 순서 대로, 7개 층 을 정리 한다.

클라우드 매니지드(EKS/GKE) 는 컨트롤플레인 보안 을 벤더 가 상당 부분 대신 해준다. 온프레미스 는 그걸 내가 다 진다 — 그래서 오히려 각 층 이 선명 하게 드러난다.


1. API 서버 — 가장 먼저, 가장 세게

kube-apiserver 는 클러스터 의 유일한 정문 이다. 여기 가 뚫리면 전부 끝 이다.

  • 노출 최소화 — 6443 을 인터넷 에 열지 마라. 우리 는 API 를 홈 LAN(사설망) 안 에서만 접근 가능 하게 두고, 외부 에서는 SSH 터널 을 거쳐야만 닿게 한다(ssh -L 16443:api:6443). 공인 IP 에 6443 을 여는 순간 전 세계 스캐너 의 표적 이 된다.
  • kubeconfig = 루트 키 — 클라이언트 인증서·토큰 이 통째로 든 파일 이다. 이걸 흘리면 클러스터 를 통째로 내준 것. 공개 채널(메신저·이슈·PR) 에 붙여넣지 말 것. 권한 도 최소 로.
  • 익명 접근 차단 · 감사 로그 on--anonymous-auth=false, audit policy 로 “누가 무엇 을 했나” 를 남긴다.

2. RBAC — 최소 권한 이 기본값

인증(누구냐) 다음 은 인가(무엇 을 할 수 있냐) 다. default 는 항상 최소 권한 이어야 한다.

  • ServiceAccount 마다 필요한 동사·리소스 만. cluster-admin 을 남발 하지 마라 — 특히 파드 안 의 SA 에.
  • 파드 가 API 를 안 쓰면 automountServiceAccountToken: false. 토큰 이 마운트 안 되면 그 파드 가 뚫려도 API 로 못 넘어간다.
  • 네임스페이스 단위 로 Role 을 끊어, 한 팀/앱 의 사고 가 다른 곳 으로 안 번지게.

RBAC 의 핵심 질문 은 늘 같다 — “이 주체 가 이 권한 을 정말 써야 하나?” 아니면 뺀다.


3. 시크릿 — 평문 은 어디에도 두지 마라

쿠버네티스 기본 Secret 은 base64 인코딩 일 뿐 암호화 가 아니다. etcd 에 사실상 평문 으로 앉는다.

  • etcd 저장 시 암호화(EncryptionConfiguration) 를 켜서, etcd 백업 이 유출 돼도 시크릿 이 안 읽히게.
  • GitOps 에 시크릿 을 넣을 땐 반드시 암호화 — 우리 는 SOPS + age 로 값 을 암호화 해 git 에 커밋 한다. 복호화 키 는 클러스터/운영자 만 갖는다. 평문 YAML 을 git 에 올리는 건 영구 유출 이다(히스토리 에 남는다).
  • 평문 시크릿 안티패턴 을 경계 — 토큰 을 launchd plist·env·로그 에 평문 으로 박아두는 습관. 편하지만, 그 파일 하나 유출 = 그 자격증명 유출.
  • 로테이션 가능 하게 설계. 유출 됐을 때 바꿀 수 있는 구조 인지 가 진짜 회복력.

4. 네트워크 — 기본 은 “다 통함”, 그걸 닫아라

쿠버네티스 는 기본적 으로 모든 파드 가 서로 통신 한다. 한 파드 가 뚫리면 옆 으로 자유롭게 번진다(lateral movement).

  • NetworkPolicy 로 필요한 통신 만 허용 하고 나머지 는 default-deny. (CNI 가 지원 해야 함)
  • DB 를 클러스터 밖 으로 노출 하지 마라 — DB Service 는 ClusterIP 로만 두고, 관리 접근 은 kubectl port-forward그때 그때 터널. 우리 DB 들 은 NodePort/LoadBalancer 로 공개 돼 있지 않다. 접근 도 파드 내부 loopback(포트포워드) 은 trust, 외부 는 비밀번호(scram) 로 pg_hba 를 갈라 둔다.
  • 인그레스 는 TLS 강제, 내부 서비스 는 mTLS 고려.

5. 노드·호스트 — 클러스터 밑 의 리눅스

파드 가 아무리 격리 돼도, 노드 호스트 가 뚫리면 그 노드 의 모든 것 이 넘어간다. 온프레미스 는 이 층 도 내 책임 이다.

  • SSH 하드닝 — 키 인증 전용(비밀번호 로그인 off), 비표준 포트, fail2ban 으로 brute-force 차단. (실제로 노드 하나 가 SSH brute-force 를 맞은 적 이 있어 fail2ban 이 필수 였다.)
  • 방화벽 과 오버레이 의 충돌 주의 — 리눅스 방화벽(ufw 등) 을 켜면 K3s 의 VXLAN 오버레이(pod-to-pod) 를 막아버려 파드 간 통신 이 죽는 함정 이 있다. 노드 재부팅 후 방화벽 이 자동 활성 되며 클러스터 가 조용히 반쪽 나기도 한다. 오버레이 포트 를 열거나 노드 내부망 을 신뢰 경계 로 설계 해야 한다.
  • 커널·런타임 최신 유지, 불필요 데몬 제거.

6. 공급망 — 내가 안 만든 코드 가 90%

돌리는 이미지 의 대부분 은 남 이 만든 것 이다. 이미지 를 믿을 수 있나 가 공급망 보안.

  • 출처 고정 — 신뢰 하는 레지스트리(예: 자체 ghcr) 에서만. latest 태그 대신 다이제스트/SHA 로 고정 해 바꿔치기 를 막는다.
  • 취약점 스캔 — Trivy 등 으로 CI 에서 이미지 CVE 스캔, 심각 취약점 은 배포 차단.
  • 이미지 서명·검증(cosign) — 서명 안 된 이미지 는 admission 에서 거부.
  • 배포 경로 자체 를 통제 — ArgoCD 로 git = 진실 을 강제 하면, 클러스터 에 몰래 꽂힌 워크로드(드리프트) 를 자동 으로 되돌린다. GitOps 는 배포 편의 이자 무결성 장치 다.

7. 파드 보안 — 마지막 격리막

컨테이너 는 VM 이 아니다. 커널 을 공유 하므로, 파드 탈출(container escape) 이 나면 노드 로 넘어간다.

  • non-root 실행runAsNonRoot: true, 읽기전용 루트FS, 불필요 capability 제거(drop: ALL).
  • privileged 금지privileged: true, hostPath, hostNetwork, hostPID 는 꼭 필요할 때만, 최소 로.
  • Pod Security Admission(PSP 후속) 으로 네임스페이스 단위 baseline/restricted 강제.
  • 리소스 한도 — limits 로 한 파드 가 노드 를 고갈 시키는 자원 고갈(DoS) 을 막는다. (보안 이자 안정성.)

정리 — 한 층 이 아니라 사슬

  • API 서버: 노출 최소·kubeconfig 는 루트키·감사 on
  • RBAC: 최소 권한, 안 쓰는 SA 토큰 은 마운트 끄기
  • 시크릿: etcd 암호화 + GitOps 는 SOPS/age, 평문 금지
  • 네트워크: default-deny, DB 는 비노출
  • 노드: SSH 하드닝·방화벽×오버레이 함정
  • 공급망: 출처 고정·스캔·서명·GitOps 무결성
  • 파드: non-root·privileged 금지·PSA·리소스 한도

보안 은 어느 한 곳 을 완벽 히 하는 게 아니라, 각 층 이 다음 층 을 백업 하게 사슬 로 엮는 일 이다. 온프레미스 는 이 사슬 전체 를 내가 든다는 부담 이 있지만, 그만큼 어디가 약한 고리 인지 를 정직 하게 마주 하게 된다.


이 글 은 특정 클러스터 의 설정값·자격증명 을 담지 않는다. 실제 적용 은 사용하는 CNI·런타임·매니지드 여부 에 따라 다르니, 공식 문서 와 CIS Kubernetes Benchmark 로 검증 하는 게 정확 하다.