LabHub
배우기 러닝패스 코스

KCSA — Kubernetes Security Associate

Applying Pod Security Admission With Labels

LabHub 에서 이어서 보기

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

목표

네임스페이스 라벨만으로 파드 보안 정책을 걸어 보고, restricted 위반 파드가 실제로 거부되는 것과 그 거부 메시지를 확인하고, 마지막으로 등급을 올려도 이미 돌고 있는 파드는 쫓겨나지 않는다는 PSA 의 핵심 성질을 직접 관찰합니다.

왜 중요한가

PSP 가 실패한 이유는 기능이 부족해서가 아니라 디버깅이 불가능했기 때문입니다. 여러 PSP 가 매칭될 때 어느 것이 적용됐는지 알기 어려웠고, mutating 이라 내가 쓴 스펙과 실행되는 스펙이 달랐습니다. 쓰이지 않는 보안 기능은 보안이 아닙니다.

PSA 는 그 교훈으로 만들어졌습니다. 라벨 하나면 정책이고, 거부 메시지에 위반 필드가 다 나옵니다. 표현력을 잃은 대신 운영 가능성을 얻은 교환이며, 이런 교환이 왜 옳았는지 이해하는 것이 KCSA 에서 자주 묻는 지점입니다.

7 단계가 이 실습의 하이라이트입니다. PSA 는 어드미션 시점에만 동작하므로 enforce 를 restricted 로 올려도 이미 도는 파드는 그대로 삽니다. 아무 일도 안 일어난 것처럼 보이다가 다음 롤아웃이나 노드 교체 때 파드가 안 뜨면서 터집니다. 정책 변경과 사고 사이에 시차가 있다는 것을 손으로 확인해 두면 마이그레이션을 설계할 때 달라집니다.

단계

  1. 네임스페이스 kcsa-psa-baseline 을 만들고 라벨 네 개를 붙입니다 — pod-security.kubernetes.io/enforce=baseline, pod-security.kubernetes.io/enforce-version=v1.30, pod-security.kubernetes.io/audit=restricted, pod-security.kubernetes.io/warn=restricted.
  2. 네임스페이스 kcsa-psa-restricted 를 만들고 라벨 네 개를 붙입니다 — pod-security.kubernetes.io/enforce=restricted, pod-security.kubernetes.io/enforce-version=v1.30, pod-security.kubernetes.io/audit=restricted, pod-security.kubernetes.io/warn=restricted.
  3. kcsa-psa-restricted 에 privileged 컨테이너를 가진 파드 bad 를 만들려고 시도합니다. 거부되어야 하며, 그 거부 메시지 전체를 /root/kcsa-psa/reject.txt 에 저장합니다. 파드 bad 는 끝까지 만들어지지 않은 상태여야 합니다.
  4. kcsa-psa-baseline 에 파드 app-baseline 을 만듭니다 — 이미지 nginx:1.27-alpine, securityContext 는 아무것도 지정하지 않습니다. Running 까지 갑니다.
  5. kcsa-psa-restricted 에 파드 app-restricted 를 만듭니다 — 이미지 nginx:1.27-alpine, 파드 레벨에 securityContext.runAsNonRoot: truesecurityContext.seccompProfile.type: RuntimeDefault, 컨테이너 레벨에 allowPrivilegeEscalation: false, capabilities.drop: ["ALL"], runAsUser: 1000.
  6. /root/kcsa-psa/exempt.yaml 에 AdmissionConfiguration 을 작성합니다 — plugins[0].namePodSecurity, configuration.kindPodSecurityConfiguration, defaults.enforcebaseline, exemptions.namespaces 의 첫 항목은 kube-system. 그리고 /root/kcsa-psa/exempt-note.txt 에 PSA 면제의 세 축을 한 줄씩 정확히 usernames, runtimeClasses, namespaces 로 적습니다.
  7. kcsa-psa-baselineenforce 라벨만 restricted 로 바꿉니다. 그다음 (a) 기존 파드 app-baseline 의 현재 상태를 확인해 /root/kcsa-psa/upgrade.txtexisting-pod=<상태> 로 적고, (b) 4단계와 같은 스펙의 파드 app-baseline2 를 만들어 보고 거부되는 것을 확인한 뒤 같은 파일에 new-pod=rejected 줄을 추가합니다. 파드 app-baseline2 는 만들어지지 않은 상태여야 합니다.

참고

baseline 네임스페이스 만들기

네임스페이스 kcsa-psa-baseline 을 만들고 라벨 네 개를 붙입니다 — pod-security.kubernetes.io/enforce=baseline, pod-security.kubernetes.io/enforce-version=v1.30, pod-security.kubernetes.io/audit=restricted, pod-security.kubernetes.io/warn=restricted.

PSA 는 네임스페이스 라벨로만 동작합니다. 모드는 셋(enforce/audit/warn)이고 여기에 버전 고정 라벨이 하나 더 붙습니다. enforce 는 실제로 차단하는 등급, audit/warn 은 '올리면 무엇이 깨지는지' 미리 보는 등급이라는 점을 생각하며 값을 정하세요.

restricted 네임스페이스 만들기

네임스페이스 kcsa-psa-restricted 를 만들고 라벨 네 개를 붙입니다 — pod-security.kubernetes.io/enforce=restricted, pod-security.kubernetes.io/enforce-version=v1.30, pod-security.kubernetes.io/audit=restricted, pod-security.kubernetes.io/warn=restricted.

앞 단계와 같은 라벨 네 개인데 값이 전부 가장 엄격한 등급입니다. 버전 라벨을 빠뜨리면 클러스터를 업그레이드할 때 정책 내용이 바뀔 수 있습니다.

restricted 위반 파드가 거부되는지 확인

kcsa-psa-restricted 에 privileged 컨테이너를 가진 파드 bad 를 만들려고 시도합니다. 거부되어야 하며, 그 거부 메시지 전체를 /root/kcsa-psa/reject.txt 에 저장합니다. 파드 bad 는 끝까지 만들어지지 않은 상태여야 합니다.

이 단계는 실패를 만드는 것이 목표입니다. 명령이 실패하면 메시지는 표준 에러로 나오니 리다이렉션에 주의하세요. 거부 메시지 안에 어떤 프로파일과 어떤 필드가 문제인지 다 적혀 있습니다.

baseline 만 통과하는 평범한 파드

kcsa-psa-baseline 에 파드 app-baseline 을 만듭니다 — 이미지 nginx:1.27-alpine, securityContext 는 아무것도 지정하지 않습니다. Running 까지 갑니다.

특별한 보안 설정을 하나도 넣지 않은 평범한 파드입니다. baseline 은 통과하지만 restricted 요건은 하나도 만족하지 않는 상태여야 합니다. 다음 단계와 비교하기 위한 대조군이니 굳이 강화하지 마세요.

restricted 를 통과하는 파드

kcsa-psa-restricted 에 파드 app-restricted 를 만듭니다 — 이미지 nginx:1.27-alpine, 파드 레벨에 securityContext.runAsNonRoot: truesecurityContext.seccompProfile.type: RuntimeDefault, 컨테이너 레벨에 allowPrivilegeEscalation: false, capabilities.drop: ["ALL"], runAsUser: 1000.

네 가지 요건이 필요하고, 그중 둘은 파드 레벨 securityContext 에 둘은 컨테이너 레벨에 들어갑니다. 어느 것이 어디에 가는지 헷갈리면 거부 메시지가 알려 줍니다. runAsUser 도 명시해야 합니다.

면제 설정과 면제 축 정리

/root/kcsa-psa/exempt.yaml 에 AdmissionConfiguration 을 작성합니다 — plugins[0].namePodSecurity, configuration.kindPodSecurityConfiguration, defaults.enforcebaseline, exemptions.namespaces 의 첫 항목은 kube-system. 그리고 /root/kcsa-psa/exempt-note.txt 에 PSA 면제의 세 축을 한 줄씩 정확히 usernames, runtimeClasses, namespaces 로 적습니다.

면제는 네임스페이스 라벨로 표현할 수 없어 apiserver 의 어드미션 설정 파일에 씁니다. 이 실습에서는 그 파일을 작성만 합니다. 면제 축은 셋이고, 세 축이 각각 무엇을 기준으로 검사를 건너뛰는지 생각해 보세요.

라벨만 올렸을 때 기존 파드는 어떻게 되나

kcsa-psa-baselineenforce 라벨만 restricted 로 바꿉니다. 그다음 (a) 기존 파드 app-baseline 의 현재 상태를 확인해 /root/kcsa-psa/upgrade.txtexisting-pod=<상태> 로 적고, (b) 4단계와 같은 스펙의 파드 app-baseline2 를 만들어 보고 거부되는 것을 확인한 뒤 같은 파일에 new-pod=rejected 줄을 추가합니다. 파드 app-baseline2 는 만들어지지 않은 상태여야 합니다.

PSA 는 어드미션 시점에만 판단합니다. 이 사실이 등급을 올렸을 때의 결과를 결정합니다. 기존 파드의 상태를 직접 확인해 그대로 적고, 같은 스펙의 새 파드를 만들어 보면 대비가 선명해집니다.