LabHub
배우기 러닝패스 코스

CKS — Kubernetes Security Specialist

Watch What the Security Settings Actually Block

LabHub 에서 이어서 보기

한국어 원문으로 표시합니다.

이 실습은 보안이 실제로 강제되는 클러스터에서 돕니다

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 Admissionrestricted 로 걸고, 특권 파드가 생성 시점에 거부되는 것을 /root/cks/psa.txt 에 담으세요.
  5. lockedapp-sa 서비스어카운트와 app-role 을 만들어 configmap 읽기만 허용하세요. 결과를 /root/cks/rbac.txt 에 담습니다.
  6. app-secret 을 만들어 파드에 마운트하고, 파드 안에서 그 파일이 어떻게 생겼는지 확인해 /root/cks/secret.txt 에 담으세요.
  7. **/root/cks/audit-policy.yaml**에 감사 정책을 쓰세요. 첫 규칙에서 core secrets·configmaps·serviceaccounts/token을 사용자·동사·네임스페이스 제한 없이 Metadata로 보호하세요. 다음은 /healthz, /readyz, /livez와 각 /* 하위 경로만 None, 마지막은 조건 없는 Metadata입니다. omitStagesRequestReceived만 생략할 수 있습니다. 총 3규칙 또는 기본값 앞 RBAC 쓰기 상세 규칙을 더한 4규칙을 사용합니다. 선택적 상세 규칙은 rbac.authorization.k8s.io의 roles·rolebindings·clusterroles·clusterrolebindings에 create·update·patch·delete·deletecollection만 RequestResponse로 기록합니다. **/root/cks/audit.txt**에 수준·첫 일치·본문 보호·로그 보존 이유를 설명하세요. 이 단계는 정책 작성이며 API 적용 증명은 아닙니다.
  8. /root/cks/report.mdnonroot_reject_reason=, psa_level=, rbac_denied= 세 줄과 함께 언제 어디서 막히는지를 정리하세요.

참고

70분 실습이므로 만료 전에 +시간으로 연장하세요(최대 180분). 세션 종료 시 작업물이 사라집니다.

root 로 돌면 아예 안 뜬다

runAsNonRoot: true 를 준 파드로 root 로 도는 이미지를 띄워 보세요. 거부 사유를 /root/cks/nonroot.txt 에 담고, 올바른 파드(good-nonroot)도 함께 띄우세요. 위반 파드 이름은 bad-root 입니다.

runAsNonRoot: true 는 kubelet 이 컨테이너를 만들기 직전에 이미지의 USER 를 보고 판단합니다. 어드미션이 아니라 kubelet 이 막는 것이 요점입니다.

루트를 읽기 전용으로 만든다

hardened 파드에 readOnlyRootFilesystem: true 를 주고 쓰기가 실제로 막히는 것을 확인해 /root/cks/readonly.txt 에 담으세요. 임시 파일을 쓸 곳도 함께 주어야 합니다.

readOnlyRootFilesystem: true 만 주면 임시 파일을 쓰는 프로그램이 죽습니다. emptyDir 을 함께 붙이세요.

capability 를 전부 내려놓는다

같은 파드에서 capability 를 ALL 로 내려놓고 allowPrivilegeEscalation: false 를 준 뒤, 그 효과를 /root/cks/caps.txt 에 담으세요.

drop: ["ALL"] 뒤에 정말 필요한 것만 add 로 다시 올립니다. 대부분의 애플리케이션은 아무것도 필요 없습니다.

생성 시점에 막는다

locked 네임스페이스에 Pod Security Admissionrestricted 로 걸고, 특권 파드가 생성 시점에 거부되는 것을 /root/cks/psa.txt 에 담으세요.

네임스페이스에 pod-security.kubernetes.io/enforce=restricted 라벨을 붙입니다. 이것은 어드미션이라 kubectl apply 자체가 거부됩니다.

필요한 것만 준다

lockedapp-sa 서비스어카운트와 app-role 을 만들어 configmap 읽기만 허용하세요. 결과를 /root/cks/rbac.txt 에 담습니다.

kubectl auth can-i --as=system:serviceaccount:<ns>:<sa> 로 허용과 거부를 둘 다 확인하세요. 열린 것만 보면 절반입니다.

Secret 은 파드 안에서 어떻게 보이나

app-secret 을 만들어 파드에 마운트하고, 파드 안에서 그 파일이 어떻게 생겼는지 확인해 /root/cks/secret.txt 에 담으세요.

볼륨으로 마운트한 뒤 ls -la 로 들여다보세요. ..data 심링크 구조와 저장 매체가 요점입니다.

무엇을 남길지 고른다

**/root/cks/audit-policy.yaml**에 감사 정책을 쓰세요. 첫 규칙에서 core secrets·configmaps·serviceaccounts/token을 사용자·동사·네임스페이스 제한 없이 Metadata로 보호하세요. 다음은 /healthz, /readyz, /livez와 각 /* 하위 경로만 None, 마지막은 조건 없는 Metadata입니다. omitStagesRequestReceived만 생략할 수 있습니다. 총 3규칙 또는 기본값 앞 RBAC 쓰기 상세 규칙을 더한 4규칙을 사용합니다. 선택적 상세 규칙은 rbac.authorization.k8s.io의 roles·rolebindings·clusterroles·clusterrolebindings에 create·update·patch·delete·deletecollection만 RequestResponse로 기록합니다. **/root/cks/audit.txt**에 수준·첫 일치·본문 보호·로그 보존 이유를 설명하세요. 이 단계는 정책 작성이며 API 적용 증명은 아닙니다.

보호 규칙이 먼저 일치해야 합니다. omitStages는 본문 제거가 아닙니다. 정책 작성과 실제 완료 이벤트 수집을 구분하세요.

언제 어디서 막히는가

/root/cks/report.mdnonroot_reject_reason=, psa_level=, rbac_denied= 세 줄과 함께 언제 어디서 막히는지를 정리하세요.

nonroot_reject_reason=, psa_level=, rbac_denied= 세 줄과 함께, 어드미션·kubelet·런타임 세 층을 정리하세요.