LabHub
배우기 러닝패스 코스

KCSA — 쿠버네티스 보안 어소시에이트 · 플랫폼 보안 — 서비스 메시, PKI, 보안 관측성 · 이론

메시의 신원, 클러스터의 인증서, 두 종류의 눈

LabHub 에서 이어서 보기

한 줄 요약

서비스 메시의 mTLS 는 워크로드마다 서비스 어카운트 기반 신원을 주고 선 위를 암호화하지만 인가는 하지 않으며, 사이드카가 가로채지 않는 트래픽은 보호하지 못합니다. 클러스터 PKI 는 세 개의 CA(kubernetes-ca·etcd-ca·front-proxy-ca) 아래 서버·클라이언트 인증서가 달려 있고 kubeadm 이 만든 클라이언트 인증서는 1년 뒤 만료됩니다. 감사 로그는 "누가 API 에 무엇을 요청했는가" 를, Falco 같은 런타임 탐지는 "노드와 컨테이너 안에서 실제로 무엇이 일어났는가" 를 알려 주며 둘은 서로의 사각을 메웁니다.

왜 이게 필요했나

KCSA 의 플랫폼 보안 도메인은 클러스터 자체가 아니라 그 위에 얹는 층을 묻습니다. 메시를 깔면 안전해진다는 말, 인증서는 kubeadm 이 알아서 한다는 말, 감사 로그를 켰으니 다 보인다는 말 — 셋 다 절반만 맞습니다. 어디까지가 맞고 어디부터 틀리는지를 모르면 보안 통제가 있다고 믿는 자리에 구멍이 남습니다. 앞 모듈에서 본 [API 서버 우회 위험](https://kubernetes.io/docs/concepts/security/api-server-bypass-risks/) 문서가 kubelet API 와 etcd 에 대한 직접 접근이 감사 로그에 남지 않는다고 적은 것이 그 구멍의 한 예입니다.

어떻게 동작하나

서비스 메시 — 대신하는 것과 못 하는 것

[Istio 보안 개념](https://istio.io/latest/docs/concepts/security/) 문서에 따르면 메시는 서비스 간 통신을 클라이언트 쪽과 서버 쪽 Envoy 프록시로 터널링합니다. 클라이언트 Envoy 가 서버 Envoy 와 mTLS 핸드셰이크를 하면서 secure naming 검사로 서버 인증서의 서비스 어카운트가 그 서비스를 운영할 권한이 있는지 확인하고, 연결이 서면 서버 Envoy 가 요청을 인가합니다. 그래서 메시가 대신하는 것은 분명합니다 — 애플리케이션마다 TLS 인증서를 발급·배포·교체하던 일, 그리고 "이 요청이 어느 워크로드에서 왔는가" 를 IP 가 아니라 서비스 어카운트 신원으로 확인하는 일입니다.

[Istio 보안 모범 사례](https://istio.io/latest/docs/ops/best-practices/security/) 문서는 못 하는 것을 나열합니다. 첫째, 기본 PERMISSIVE 모드는 평문도 받으므로 STRICT 로 옮기기 전까지는 암호화가 보장되지 않습니다. 둘째, mTLS 는 인증만 제공하므로 유효한 인증서를 가진 누구나 서비스에 닿을 수 있고, 잠그려면 AuthorizationPolicy 를 default-deny 패턴(예: 전부 거부하는 allow-nothing 정책 뒤에 조건부 허용)으로 두어야 합니다. 정책이 하나도 없는 워크로드에 대해 Istio 는 모든 요청을 허용합니다. 셋째, 사이드카는 TCP 만 가로채고 UDP·ICMP 는 지나가며, 22번을 비롯한 몇몇 포트는 inbound 캡처에서 빠지고, 애플리케이션과 사이드카가 같은 네트워크·프로세스 네임스페이스에 있어 애플리케이션이 리다이렉션 규칙을 지워 사이드카를 우회할 수 있습니다. 문서의 결론은 "모든 트래픽이 무조건 캡처된다고 믿는 것은 안전하지 않다" 이고, 그래서 NetworkPolicy 를 겹쳐 두는 심층 방어를 권합니다. [AuthorizationPolicy](https://istio.io/latest/docs/reference/config/security/authorization-policy/) 는 selector 나 targetRefs 로 적용 대상을 고르며, 루트 네임스페이스에 두면 메시 전체에 걸립니다.

| 메시가 주는 것 | 메시가 주지 않는 것 |
| --- | --- |
| 워크로드 신원(서비스 어카운트 인증서) | 인가 — AuthorizationPolicy 가 따로 필요 |
| 사이드카 사이의 암호화 | 애플리케이션과 사이드카 사이의 보호(같은 파드 안 평문) |
| STRICT 에서 평문 거부 | UDP·ICMP·제외 포트·우회된 트래픽의 보호 |
| L7 속성 기반 정책 | L3/L4 경계 — NetworkPolicy 로 보강 |

PKI — CA 세 개와 만료 시계

[PKI 인증서와 요구사항](https://kubernetes.io/docs/setup/best-practices/certificates/) 문서는 클러스터에 필요한 인증서를 두 무리로 나눕니다. 서버 인증서는 API 서버 엔드포인트, etcd 서버, 노드마다의 kubelet, 선택적으로 front-proxy 에 있습니다. 클라이언트 인증서는 kubelet 이 API 서버에 인증할 때, API 서버가 etcd 에 인증할 때, 컨트롤러 매니저·스케줄러·kube-proxy 가 API 서버에 인증할 때, 그리고 관리자에게 있습니다. 이것들을 서명하는 CA 는 셋입니다.

| CA | 파일 | 용도 |
| --- | --- | --- |
| kubernetes-ca | /etc/kubernetes/pki/ca.crt | 쿠버네티스 일반 CA |
| etcd-ca | /etc/kubernetes/pki/etcd/ca.crt | etcd 관련 모든 인증서 |
| kubernetes-front-proxy-ca | /etc/kubernetes/pki/front-proxy-ca.crt | front-end proxy 용 |

여기에 서비스 어카운트 토큰 서명용 키 쌍 sa.key·sa.pub 가 더 있습니다. CA 를 계층으로 두려면 관리자가 통제하는 루트 CA 하나에서 중간 CA 들을 만들어 나머지 발급을 쿠버네티스에 맡길 수 있습니다. 위협 모델에서 중요한 것은 각 CA 의 무게입니다 — etcd-ca 가 서명한 클라이언트 인증서는 etcd 의 모든 데이터에 대한 접근이고(우회 위험 문서: "etcd 가 신뢰하는 CA 가 발급한 어떤 인증서든 etcd 안 데이터에 대한 완전한 접근을 허용"), kubernetes-ca 로 O=system:masters 인 인증서를 만들면 슈퍼유저입니다. 문서는 kube-apiserver 의 kubelet 클라이언트 인증서에 system:masters 대신 덜 특권적인 그룹을 쓸 수 있으며 kubeadm 은 kubeadm:cluster-admins 그룹을 쓴다고 적습니다.

SAN 도 보안 항목입니다. 인증서의 hosts(SAN)에 없는 이름으로 접속하면 TLS 검증이 실패하므로, kube-apiserver 인증서에는 호스트명·호스트 IP·advertise IP 에 더해 로드밸런서의 주소와 kubernetes, kubernetes.default, kubernetes.default.svc, kubernetes.default.svc.cluster, kubernetes.default.svc.cluster.local 이 들어갑니다. 나중에 로드밸런서 주소를 바꾸면 SAN 이 없어 접속이 깨지는 것이 전형적인 사고입니다.

[kubeadm 인증서 관리](https://kubernetes.io/docs/tasks/administer-cluster/kubeadm/kubeadm-certs/) 문서의 첫 문장이 시계입니다 — kubeadm 이 만든 클라이언트 인증서는 1년 뒤 만료됩니다. kubeadm certs check-expiration/etc/kubernetes/pki 의 인증서와 admin.conf·controller-manager.conf·scheduler.conf 에 박힌 클라이언트 인증서의 만료를 보여 주고, 문서의 예시 출력에서 CA 는 잔여 9년으로 나옵니다. 갱신은 두 길입니다. kubeadm 은 컨트롤 플레인 업그레이드 때 모든 인증서를 갱신하므로 1년 안에 한 번씩 업그레이드하면 따로 할 일이 없고(끄려면 --certificate-renewal=false), 수동으로는 kubeadm certs renew 를 쓰되 복제된 컨트롤 플레인이면 모든 노드에서 실행해야 하고 실행 뒤 컨트롤 플레인 파드를 재시작해야 합니다(동적 재적재가 되지 않습니다). kubelet 의 serving 인증서는 기본이 자체 서명이라 외부 서비스가 TLS 로 검증할 수 없다는 점도 같은 문서에 있습니다.

보안 관측성 — 감사 로그와 런타임 탐지

[감사](https://kubernetes.io/docs/tasks/debug/debug-cluster/audit/) 문서에 따르면 감사는 클러스터의 행위를 시간순으로 기록해 무엇이·언제·누가·무엇에·어디서 일어났는지 답하게 합니다. 기록은 kube-apiserver 안에서 시작합니다. 요청의 단계마다(RequestReceived, ResponseStarted, ResponseComplete, Panic) 이벤트가 생기고, 정책이 남길지와 수준을 정하며, 백엔드(로그 파일 또는 webhook)가 저장합니다. 정책은 규칙을 순서대로 비교해 첫 번째로 맞는 규칙이 수준을 정하고, 수준은 None·Metadata·Request·RequestResponse 네 가지입니다. --audit-policy-file 을 주지 않으면 아무것도 기록되지 않고, 규칙이 0개인 정책은 불법입니다. 감사는 API 서버의 메모리 사용을 늘립니다.

감사 로그의 사각은 우회 위험 문서가 정확히 적어 두었습니다 — kubelet API 직접 접근, etcd 직접 접근, 런타임 소켓은 어드미션도 감사 로그도 거치지 않습니다. 감사 로그는 API 서버가 본 것만 보여 줍니다.

그 사각을 보는 눈이 런타임 탐지입니다. [Falco 문서](https://falco.org/docs/) 에 따르면 Falco 는 호스트·컨테이너·쿠버네티스·클라우드 환경에서 런타임 보안을 제공하는 CNCF 졸업 프로젝트로, 커널의 시스템 콜을 실시간으로 파싱해 규칙 엔진에 대조하고 규칙이 위반되면 경고를 냅니다. 컨테이너 런타임과 쿠버네티스의 메타데이터를 이벤트에 붙여 "어느 네임스페이스의 어느 파드에서" 를 알려 주고, 플러그인으로 시스템 콜 밖의 이벤트 소스도 받습니다. 기본 규칙이 잡는 것으로 문서는 특권 컨테이너를 이용한 권한 상승, setns 같은 도구를 쓴 네임스페이스 변경, 잘 알려진 디렉터리에 대한 읽기·쓰기를 듭니다. 경고는 SIEM 이나 데이터 레이크로 보내 조사에 씁니다.

두 눈은 같은 사건을 다른 자리에서 봅니다. 누군가 파드에 exec 로 들어가면 감사 로그에는 pods/exec 하위 리소스에 대한 요청과 요청자가 남고, Falco 에는 그 컨테이너 안에서 셸이 생겼다는 시스템 콜이 남습니다. 감사 로그에는 없는데 Falco 에만 셸이 보인다면 API 서버를 거치지 않은 길(kubelet API, 런타임 소켓, static Pod)을 의심할 수 있고, 감사 로그에는 있는데 Falco 에 아무것도 없다면 요청이 거부됐거나 아직 실행되지 않은 것입니다. 하나만 켜면 이 대조가 불가능합니다.

현장에서 만나는 모습

1년 뒤 어느 날 kubectl 이 x509 오류를 낸다. kubeadm 클러스터를 세우고 업그레이드 없이 1년을 넘긴 경우입니다. admin.conf 의 인증서와 컨트롤 플레인 구성요소의 인증서가 같은 날 만료되므로 API 서버 자체는 돌아도 아무도 말을 걸 수 없습니다. check-expiration 을 정기 점검에 넣어 두면 몇 달 전에 잔여 시간이 보입니다.

감사 로그에 아무 흔적이 없는 침해. 노드에 SSH 로 들어간 공격자가 런타임 소켓으로 컨테이너를 띄웠다면 API 서버는 아무것도 보지 못합니다. 이 경우 유일한 기록은 노드의 시스템 콜을 보는 런타임 탐지와 노드의 인증 로그이고, 우회 위험 문서가 노드 접근 자체를 제한하라고 하는 이유입니다.

다음 퀴즈에서 확인할 것

퀴즈에서는 mTLS 가 주지 않는 것, 사이드카가 가로채지 못하는 트래픽, 클러스터 CA 세 개의 역할, kubeadm 인증서의 만료와 갱신, 감사 정책의 규칙 평가 방식, 그리고 감사 로그와 런타임 탐지가 각각 보는 것을 묻습니다.