KCSA — 쿠버네티스 보안 어소시에이트 · 플랫폼 보안 · 실습
파드를 만들 권한은 있는데, 특권 파드는 막아야 한다
목표
ValidatingAdmissionPolicy(VAP)로 "특권 컨테이너 금지" 와 "hostPath 볼륨 금지" 정책을 CEL 로 직접 쓰고,
바인딩으로 한 네임스페이스에만 켠 뒤, API 서버가 돌려주는 거절 메시지와 정상 파드의 승인을 양쪽 다 관찰합니다.
왜 중요한가
쿠버네티스의 모든 쓰기 요청은 인증 → 인가(RBAC) → 어드미션 을 거쳐야 etcd 에 저장됩니다. RBAC 은
"이 사용자가 파드를 만들어도 되는가" 까지만 답하고, "그 파드의 내용이 안전한가" 는 묻지 않습니다.
파드를 만들 권한만 있으면 privileged: true 나 노드의 / 를 hostPath 로 마운트한 파드로 노드를 통째로
장악할 수 있습니다. 그 구멍을 막는 층이 어드미션 제어입니다.
Pod Security Admission(PSA)은 네임스페이스 라벨 하나로 정해진 기준(privileged/baseline/restricted)을 켭니다.
편하지만 조직만의 규칙(허용 레지스트리, 특정 라벨 필수 등)은 표현할 수 없습니다. VAP 는 API 서버 안에서
CEL 식을 직접 평가하므로 웹훅 서버 없이도 원하는 규칙을 쓸 수 있고, 정책(무엇을 검사하나) 과
바인딩(어디에, Deny/Warn/Audit 중 무엇으로) 이 분리돼 있어 같은 정책을 네임스페이스마다 다르게 켤 수 있습니다.
거절 메시지에는 어떤 정책과 어떤 바인딩이 막았는지가 이름으로 찍힙니다. 사고 대응 때 "왜 배포가 안 되나" 를
가장 빨리 푸는 단서가 바로 이 한 줄입니다. 또 어드미션은 생성 요청 시점에만 일어나므로, 정책을 켜기 전에
이미 만들어진 오브젝트는 그대로 남는다는 점도 기억해 두세요.
단계
1. 네임스페이스 kcsa-adm 을 만들고 자동 라벨을 확인합니다.
2. 특권 컨테이너를 거절하는 정책 deny-privileged 를 CEL 로 씁니다.
3. 바인딩 deny-privileged-binding 으로 kcsa-adm 에만 Deny 로 켭니다.
4. 특권 파드를 만들어 보고 거절 메시지를 /root/kcsa-adm/denied-privileged.txt 에 저장합니다.
5. 평범한 파드 good 이 승인되는 것을 확인합니다.
6. hostPath 볼륨을 거절하는 정책 deny-hostpath 와 바인딩을 만듭니다.
7. hostPath 파드를 만들어 보고 거절 메시지를 /root/kcsa-adm/denied-hostpath.txt 에 저장합니다.
8. 무엇이 막히고 무엇이 통과했는지 /root/kcsa-adm/report.txt 에 남깁니다.
참고
- 정책만 만들고 바인딩을 빠뜨리면 아무것도 막히지 않습니다.
kubectl get validatingadmissionpolicybinding으로 둘 다 있는지 먼저 보세요. validationActions를Warn으로 두면 요청은 통과하고 경고만 뜹니다. 막으려면Deny여야 합니다.- 이 실습의 파드는 컨테이너 없이 흉내 내지만, 어드미션은 API 요청 단계에서 일어나므로 거절·승인은 진짜입니다.
- 공식 문서: [Validating Admission Policy](https://kubernetes.io/docs/reference/access-authn-authz/validating-admission-policy/) ·
[Pod Security Standards](https://kubernetes.io/docs/concepts/security/pod-security-standards/).
단계 8개
- 정책을 걸 격리 구역 만들기
- 특권 컨테이너를 막는 규칙을 CEL 로 쓰다
- 규칙을 kcsa-adm 에 묶어 실제로 켜기
- 특권 파드가 문 앞에서 돌려보내지다
- 평범한 파드는 막힘 없이 들어간다
- 노드 디스크로 통하는 hostPath 도 막기
- 노드의 /etc 를 마운트하려던 파드가 거절되다
- 무엇이 막히고 무엇이 통과했는지 장부로 남기기