CKS — 쿠버네티스 보안 전문가 · 진짜 클러스터에서 확인하기 · 실습
보안 설정이 실제로 막는 것을 본다
이 실습은 보안이 실제로 강제되는 클러스터에서 돕니다
VM 안에 진짜 k3s 가 떠 있습니다. kubelet 과 containerd 가 실제로 돌기
때문에, securityContext 를 위반한 파드는 정말로 거부되고, 읽기 전용
루트는 정말로 쓰기를 막습니다.
CKS 과정의 다른 실습은 kwok 위에서 돕니다. 거기서는 위반한 파드도 그냥Running 이 되어, 설정을 쓰는 연습은 되지만 **그 설정이 지켜지는 것을
확인하는 연습**은 되지 않습니다.
처음 뜨는 데 2분쯤 걸립니다.
목표
보안 통제가 언제 어디서 막는지를 구분합니다. 어드미션에서 막히는 것과
런타임에서 막히는 것은 증상도 고치는 곳도 다릅니다.
왜 중요한가
실무에서 사고가 나는 자리는 "설정을 틀리게 쓴 것" 이 아니라 **"설정을 다 써
놓고 그것이 적용되지 않은 것"** 입니다. 후자는 조용하기 때문입니다.
그리고 같은 "파드가 안 뜬다" 도 원인이 여러 층에 흩어져 있습니다.
| 어디서 막히나 | 증상 | 고칠 곳 |
|---|---|---|
| 어드미션 (생성 시점) | kubectl apply 자체가 거부됨 | 네임스페이스 라벨, 정책 |
| kubelet (컨테이너 만들 때) | 파드는 만들어지고 CreateContainerConfigError | securityContext |
| 런타임 (프로세스 안에서) | 파드는 Running, 애플리케이션만 실패 | 볼륨, capability |
이 셋을 구분하지 못하면 장애를 볼 때마다 처음부터 훑게 됩니다.
단계
1. runAsNonRoot: true 를 준 파드로 root 로 도는 이미지를 띄워 보세요. 거부 사유를 /root/cks/nonroot.txt 에 담고, 올바른 파드(good-nonroot)도 함께 띄우세요. 위반 파드 이름은 bad-root 입니다.
2. hardened 파드에 readOnlyRootFilesystem: true 를 주고 쓰기가 실제로 막히는 것을 확인해 /root/cks/readonly.txt 에 담으세요. 임시 파일을 쓸 곳도 함께 주어야 합니다.
3. 같은 파드에서 capability 를 ALL 로 내려놓고 allowPrivilegeEscalation: false 를 준 뒤, 그 효과를 /root/cks/caps.txt 에 담으세요.
4. locked 네임스페이스에 Pod Security Admission 을 restricted 로 걸고, 특권 파드가 생성 시점에 거부되는 것을 /root/cks/psa.txt 에 담으세요.
5. locked 에 app-sa 서비스어카운트와 app-role 을 만들어 configmap 읽기만 허용하세요. 결과를 /root/cks/rbac.txt 에 담습니다.
6. app-secret 을 만들어 파드에 마운트하고, 파드 안에서 그 파일이 어떻게 생겼는지 확인해 /root/cks/secret.txt 에 담으세요.
7. /root/cks/audit-policy.yaml 로 감사 정책을 쓰세요. Secret 접근은 RequestResponse, 잡음은 None, 나머지는 Metadata 입니다. 설명을 /root/cks/audit.txt 에 담습니다.
8. /root/cks/report.md 에 nonroot_reject_reason=, psa_level=, rbac_denied= 세 줄과 함께 언제 어디서 막히는지를 정리하세요.
참고
- 위반 파드의 상태는
kubectl get pod bad-root -o jsonpath='{.status.containerStatuses[0].state}'로 봅니다. - PSA 라벨은
pod-security.kubernetes.io/enforce=restricted입니다.audit과warn도 따로 걸 수 있고, 실무에서는 warn 부터 걸어 무엇이 걸리는지 본 뒤 enforce 로 올립니다. - 권한 확인은
kubectl auth can-i <동사> <자원> --as=system:serviceaccount:<ns>:<sa> -n <ns>입니다. - 읽기 전용 루트를 쓰면
/tmp같은 곳에emptyDir을 붙여야 합니다. 안 그러면 임시 파일을 쓰는 대부분의 프로그램이 죽습니다. - 흔한 실수 1:
runAsNonRoot: true만 주고runAsUser를 안 주는 것. 이미지가 root 로 돌면 거부되는데, 그 사실이 이미지를 봐야만 알 수 있어서 헷갈립니다. - 흔한 실수 2: PSA 를
enforce로 바로 거는 것. 이미 도는 워크로드가 다음 재배포에서 전부 막힙니다.warn으로 먼저 재 보세요.
단계 8개
- root 로 돌면 아예 안 뜬다
- 루트를 읽기 전용으로 만든다
- capability 를 전부 내려놓는다
- 생성 시점에 막는다
- 필요한 것만 준다
- Secret 은 파드 안에서 어떻게 보이나
- 무엇을 남길지 고른다
- 언제 어디서 막히는가