LabHub
배우기 러닝패스 코스

KCSA — 쿠버네티스 보안 어소시에이트 · 구성요소 보안 — 스케줄러, kube-proxy, 런타임, CNI, 스토리지, 클라이언트 · 이론

CNI, 스토리지, 클라이언트 — 경계에 있는 구성요소의 위험

LabHub 에서 이어서 보기

한 줄 요약

NetworkPolicy 는 CNI 플러그인이 집행하므로 집행하는 플러그인이 없으면 정책은 아무 효과가 없습니다. hostPath 는 노드의 자격 증명과 런타임 소켓으로 가는 통로이고, CSI 드라이버는 스토리지 백엔드의 비밀을 쥡니다. 클라이언트 쪽에서는 kubeconfig 한 파일이 클러스터 전체의 열쇠이면서 동시에 임의 명령을 실행시키는 파일이 될 수 있습니다.

왜 이게 필요했나

앞 읽기의 세 구성요소가 컨트롤 플레인과 노드 안에 있었다면, 이 셋은 클러스터의 경계에 있습니다. CNI 는 파드와 네트워크 사이, 스토리지는 파드와 디스크 사이, 클라이언트는 사람과 API 서버 사이입니다. 경계는 양쪽의 신뢰 수준이 다른 곳이라 위협 모델에서 가장 먼저 그리는 선입니다. 그런데 세 곳 모두 "쿠버네티스가 알아서 지켜 주겠지" 라고 오해하기 쉬운 자리입니다. NetworkPolicy 를 만들었으니 막혔겠지, PVC 를 썼으니 격리됐겠지, kubeconfig 는 그냥 설정 파일이겠지 — 셋 다 틀립니다.

어떻게 동작하나

CNI — 정책은 플러그인이 집행한다

[네트워크 정책](https://kubernetes.io/docs/concepts/services-networking/network-policies/) 문서의 전제 조건 절은 분명합니다. 네트워크 정책은 네트워크 플러그인이 구현하며, NetworkPolicy 를 지원하는 네트워킹 솔루션을 써야 하고, 구현하는 컨트롤러 없이 NetworkPolicy 리소스를 만들면 아무 효과가 없습니다. API 서버는 오브젝트를 저장할 뿐 거부하지 않으므로, 정책이 있는데 트래픽이 흐르는 상황이 조용히 생깁니다.

정책의 기본 상태도 알아야 합니다. 네임스페이스에 정책이 하나도 없으면 그 네임스페이스 파드로 드나드는 모든 트래픽이 허용됩니다. 격리는 ingress 와 egress 가 따로 선언되고, 문서의 표현대로 "격리" 는 절대적이 아니라 "어떤 제한이 적용된다" 는 뜻입니다. 그래서 [보안 점검표](https://kubernetes.io/docs/concepts/security/security-checklist/) 는 네임스페이스마다 모든 파드를 고르는 기본 거부 정책을 두어 허용 목록 방식으로 가라고 권하고, 쓰는 CNI 가 정책을 지원하는지를 첫 항목으로 둡니다. CNI 플러그인 자체는 앞 읽기에서 본 대로 컨테이너 런타임이 적재하므로, 플러그인 바이너리와 설정 디렉터리에 쓸 수 있는 권한은 노드의 네트워크 정책 집행을 무력화할 수 있는 권한입니다. 전송 중 암호화는 모든 CNI 가 제공하지 않으며, 없으면 서비스 메시가 대안이라는 점도 점검표에 있습니다.

스토리지 — hostPath 의 통로와 CSI 의 비밀

[볼륨](https://kubernetes.io/docs/concepts/storage/volumes/) 문서의 hostPath 절은 경고로 시작합니다. hostPath 는 많은 보안 위험을 안고 있으니 피할 수 있으면 피하고, 대신 local PersistentVolume 을 쓰라는 것입니다. 이유가 구체적입니다 — 호스트 파일 시스템에 닿으면 kubelet 의 자격 증명 같은 특권 시스템 자격 증명이나 컨테이너 런타임 소켓 같은 특권 API 가 노출되어 컨테이너 탈출이나 클러스터의 다른 부분에 대한 공격에 쓰일 수 있습니다. 어드미션에서 특정 디렉터리만 허용하도록 제한하더라도, 그 마운트를 읽기 전용으로 강제해야만 제한이 유효합니다. 신뢰할 수 없는 파드에 어떤 호스트 경로든 읽기·쓰기로 허용하면 그 파드의 컨테이너가 마운트를 뒤집을 수 있기 때문입니다. [Pod Security Standards](https://kubernetes.io/docs/concepts/security/pod-security-standards/) 의 baseline 프로파일이 hostPath 를 막는 것이 이 위험에 대한 기본 방어입니다.

CSI 쪽의 비밀은 다른 종류입니다. [CSI 드라이버 문서의 Secrets and Credentials](https://kubernetes-csi.github.io/docs/secrets-and-credentials.html) 절에 따르면 드라이버가 백엔드에 접근하는 데 필요한 자격 증명은 두 층으로 나뉩니다. 드라이버 단위 비밀(백엔드의 서비스 계정 같은 것)은 배포 시 표준 Secret 배포 방식으로 드라이버 파드에 직접 주입하고, 작업 단위·볼륨 단위 비밀은 CSI 스펙이 CreateVolume 같은 각 작업 요청에 실어 보낼 수 있게 해 두었으며 관리자가 Secret 을 만들고 StorageClass 나 VolumeSnapshotClass 에 그 키를 적어 넘깁니다. 사이드카 컨테이너의 Secret 관련 RBAC 규칙은 권한을 줄이기 위해 기본으로 꺼져 있어 필요할 때만 켜고, 스펙은 민감 필드에 csi_secret 표식을 두어 로그에 남기지 않도록 합니다. 위협 모델로 옮기면 이렇습니다 — CSI 컨트롤러 파드의 서비스 어카운트나 StorageClass 가 참조하는 Secret 을 읽을 수 있는 주체는 스토리지 백엔드 전체에 대한 자격 증명을 얻습니다.

클라이언트 — kubeconfig 는 열쇠이자 실행 파일이다

[kubeconfig 로 클러스터 접근 구성하기](https://kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/) 문서는 kubectl 이 기본으로 $HOME/.kube/config 를 읽고 KUBECONFIG 환경 변수나 --kubeconfig 플래그로 다른 파일을 지정한다고 설명한 뒤, 경고를 붙입니다. 신뢰할 수 있는 출처의 kubeconfig 만 쓰라는 것이고, 특별히 만들어진 kubeconfig 는 악성 코드 실행이나 파일 노출로 이어질 수 있으니 신뢰할 수 없는 파일은 셸 스크립트를 보듯 먼저 검사하라고 합니다.

왜 설정 파일이 코드 실행이 되는지는 [인증](https://kubernetes.io/docs/reference/access-authn-authz/authentication/) 문서의 자격 증명 플러그인 절이 설명합니다. client-go 와 그것을 쓰는 kubectl·kubelet 은 사용자 자격 증명을 얻기 위해 외부 명령을 실행할 수 있습니다(1.22 에서 안정화). LDAP·Kerberos·OAuth2·SAML 처럼 client-go 가 직접 지원하지 않는 프로토콜을 위한 기능이고, 플러그인이 돌려준 토큰을 client-go 가 bearer 토큰으로 쓰며 서버 쪽은 webhook 토큰 인증기로 TokenReview 를 처리합니다. 그러니 kubeconfig 의 exec 항목에 적힌 명령은 kubectl 을 칠 때마다 사용자 권한으로 실행됩니다. 받은 kubeconfig 를 열어 exec 블록이 어떤 바이너리를 가리키는지 보는 것이 그래서 필요합니다.

kubeconfig 에 들어 있는 자격 증명의 성질도 위험을 정합니다. [인증 메커니즘 하드닝 안내](https://kubernetes.io/docs/concepts/security/hardening-guide/authentication-mechanisms/) 는 X.509 클라이언트 인증서의 제약을 나열합니다 — 개별 폐기가 불가능해 유출되면 만료까지 쓸 수 있고, 무효화하려면 CA 를 다시 발급해야 하며, 발급 기록이 클러스터에 남지 않고, 개인 키를 비밀번호로 보호할 수 없어 파일을 읽는 누구나 쓸 수 있으며, 그룹이 인증서의 O 값에 박혀 있어 수명 동안 바꿀 수 없습니다. 서비스 어카운트 토큰도 마찬가지로 [인증](https://kubernetes.io/docs/reference/access-authn-authz/authentication/) 문서가 "클러스터 밖에서 써도 완전히 유효하다" 고 적으니, 파이프라인 도구에 넣어 둔 토큰은 kubeconfig 와 같은 무게로 다뤄야 합니다. 대응은 클러스터 보안 문서가 말하는 그대로입니다 — 짧은 수명, 자동 교체, 발급 토큰의 수명을 통제할 수 있는 인증 제공자.

현장에서 만나는 모습

NetworkPolicy 를 배포했는데 다 뚫린다. kwok 이나 정책을 지원하지 않는 플러그인으로 만든 클러스터에서 흔합니다. kubectl get networkpolicy 에 오브젝트가 보이는 것과 트래픽이 막히는 것은 다른 일이며, 집행 여부는 실제 연결을 시도해 확인해야 합니다.

팀 채널에 공유된 kubeconfig. 파일 안의 client-certificate-data 는 폐기할 수 없으므로 유출이 확인되면 만료까지 유효합니다. 인증서 기반 사용자 자격 증명을 쓰고 있다면 수명을 짧게 두고, 사람 사용자는 OIDC 같은 외부 인증 제공자로 옮기는 것이 하드닝 안내가 가리키는 방향입니다.

다음 퀴즈에서 확인할 것

퀴즈에서는 스케줄러 인증 플래그를 비웠을 때의 동작, kube-proxy 메트릭 포트의 기본 바인딩, 런타임 소켓 접근이 뜻하는 것, 집행 컨트롤러 없는 NetworkPolicy 의 효과, hostPath 제한이 유효해지는 조건, 그리고 kubeconfig 가 코드 실행이 되는 이유를 묻습니다.