LabHub
배우기 러닝패스 코스

CKS — Kubernetesセキュリティスペシャリスト

Pod Security AdmissionとsecurityContext

LabHub 에서 이어서 보기

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

목표

네임스페이스 라벨만으로 워크로드의 보안 하한선을 정하는 방법을 익히고, restricted 프로파일을 실제로 통과하는 파드 스펙을 손으로 완성합니다.

왜 중요한가

PodSecurityPolicy 는 1.25 에서 제거됐습니다. 사용하려면 ServiceAccount 에 "이 PSP 를 use 할 권한"을 줘야 했는데 그 구조 자체가 권한 상승 경로였고, 여러 PSP 중 무엇이 적용될지 예측하기 어려웠기 때문입니다. 대체재인 PSA 는 네임스페이스 라벨 몇 줄로 끝나고, enforce·audit·warn 세 모드를 따로 켤 수 있어 점진적 도입이 가능합니다. 기존 클러스터에 곧바로 enforce 를 걸면 워크로드가 대량으로 막히므로 warn 과 audit 를 먼저 켜서 관찰하는 것이 표준 절차입니다.

restricted 프로파일이 요구하는 네 가지(runAsNonRoot, allowPrivilegeEscalation false, capabilities drop ALL, seccompProfile)는 CKS 에서 가장 자주 나오는 조합입니다. 외우는 것보다 직접 하나씩 빼 보면서 어떤 오류가 나는지 확인하는 편이 오래 갑니다.

이 환경의 API 서버에는 PodSecurity 어드미션이 켜져 있으므로, 위반 파드는 실제로 거부됩니다.

단계

  1. 네임스페이스 cks-psa 를 만들고 라벨 pod-security.kubernetes.io/enforce=baseline 을 붙인다.
  2. 네임스페이스 cks-psa-strict 를 만들고 enforce·audit·warn 세 라벨을 모두 restricted 로, 그리고 enforce-version·audit-version·warn-version 세 라벨을 모두 latest 로 붙인다.
  3. cks-psa-strict 에 파드 hardened(컨테이너 이름 app, 이미지 nginx:1.27-alpine)를 만든다. restricted 를 통과해야 하므로 파드 레벨에 runAsNonRoot: trueseccompProfile.type: RuntimeDefault, 컨테이너 레벨에 allowPrivilegeEscalation: falsecapabilities.drop: [ALL] 이 필요하다.
  4. cks-psa-strictprivileged: true 컨테이너를 가진 파드 bad-pod 를 apply 해 본다. 그 출력(표준 오류 포함)을 /root/cks-securitycontext/denied.txt 에 저장한다. 파드는 생성되면 안 된다.
  5. cks-psa 에 파드 nonroot-app(컨테이너 이름 app)을 만든다. 파드 레벨 securityContext 에 runAsNonRoot: true, runAsUser: 10001, runAsGroup: 10001, fsGroup: 20001 을 넣는다.
  6. cks-psa-strict 에 파드 dropped(컨테이너 이름 app)를 만든다. 파드 레벨 seccompProfile.typeRuntimeDefault, runAsNonRoottrue 이고, 컨테이너 레벨에 allowPrivilegeEscalation: false, capabilities.drop: [ALL], readOnlyRootFilesystem: true 를 넣는다.
  7. RuntimeClass cks-sandbox 를 만든다. handlerrunsc, overhead.podFixed 는 메모리 160Mi 와 CPU 250m 이다.
  8. cks-psa-strict 에 Deployment payments 를 만든다. replicas 2, 셀렉터와 파드 라벨은 app=payments, 파드 스펙의 runtimeClassNamecks-sandbox 이고, 파드 템플릿은 restricted 를 통과하도록 3 단계와 같은 네 조건을 모두 갖춘다.

참고

baseline 프로파일 적용

네임스페이스 cks-psa 를 만들고 라벨 pod-security.kubernetes.io/enforce=baseline 을 붙인다.

PSA 는 네임스페이스 라벨로 동작합니다. 라벨 키는 pod-security.kubernetes.io/ 로 시작합니다.

세 모드와 버전 라벨 전부 걸기

네임스페이스 cks-psa-strict 를 만들고 enforce·audit·warn 세 라벨을 모두 restricted 로, 그리고 enforce-version·audit-version·warn-version 세 라벨을 모두 latest 로 붙인다.

enforce·audit·warn 각각에 대응하는 -version 라벨이 따로 있습니다. 버전을 못 박으면 클러스터 업그레이드가 곧 정책 강화로 이어지지 않습니다.

restricted 를 통과하는 파드

cks-psa-strict 에 파드 hardened(컨테이너 이름 app, 이미지 nginx:1.27-alpine)를 만든다. restricted 를 통과해야 하므로 파드 레벨에 runAsNonRoot: trueseccompProfile.type: RuntimeDefault, 컨테이너 레벨에 allowPrivilegeEscalation: falsecapabilities.drop: [ALL] 이 필요하다.

restricted 는 네 가지를 필수로 요구합니다. 하나라도 빠지면 거부되고, 오류 메시지가 어떤 필드가 걸렸는지 알려 줍니다.

한 가지 더 알아 두세요. runAsNonRoot: trueUID 를 정해 주지 않습니다. "root 로 돌지 마라" 고 말할 뿐이라, 이미지가 UID 를 지정하지 않으면 진짜 노드에서는 kubelet 이 파드를 띄우지 못하고 CreateContainerConfigError 를 냅니다. 실제 클러스터에서는 runAsUser 를 함께 박거나 비-root 로 만들어진 이미지를 쓰세요. 이 실습 환경은 파드를 실제로 실행하지 않아 그 차이가 드러나지 않습니다.

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

cks-psa-strictprivileged: true 컨테이너를 가진 파드 bad-pod 를 apply 해 본다. 그 출력(표준 오류 포함)을 /root/cks-securitycontext/denied.txt 에 저장한다. 파드는 생성되면 안 된다.

apply 의 표준 출력과 표준 오류를 함께 파일로 남겨야 합니다. 2>&1 | tee 를 쓰세요. 파드가 만들어졌다면 정책이 걸리지 않은 것입니다.

실행 사용자와 그룹 지정

cks-psa 에 파드 nonroot-app(컨테이너 이름 app)을 만든다. 파드 레벨 securityContext 에 runAsNonRoot: true, runAsUser: 10001, runAsGroup: 10001, fsGroup: 20001 을 넣는다.

runAsUser/runAsGroup/fsGroup 은 파드 레벨 securityContext 에, runAsNonRoot 는 양쪽 다 가능합니다. fsGroup 은 볼륨 파일의 그룹 소유권을 바꿉니다.

seccomp 와 capability 조합

cks-psa-strict 에 파드 dropped(컨테이너 이름 app)를 만든다. 파드 레벨 seccompProfile.typeRuntimeDefault, runAsNonRoottrue 이고, 컨테이너 레벨에 allowPrivilegeEscalation: false, capabilities.drop: [ALL], readOnlyRootFilesystem: true 를 넣는다.

restricted 네임스페이스이므로 이 파드도 네 가지 필수 조건을 모두 만족해야 생성됩니다.

RuntimeClass 오브젝트

RuntimeClass cks-sandbox 를 만든다. handlerrunsc, overhead.podFixed 는 메모리 160Mi 와 CPU 250m 이다.

RuntimeClass 는 클러스터 범위 리소스입니다. handler 값은 노드의 컨테이너 런타임 설정에 있는 런타임 이름과 문자 단위로 같아야 합니다.

종합: 샌드박스 런타임 배포

cks-psa-strict 에 Deployment payments 를 만든다. replicas 2, 셀렉터와 파드 라벨은 app=payments, 파드 스펙의 runtimeClassNamecks-sandbox 이고, 파드 템플릿은 restricted 를 통과하도록 3 단계와 같은 네 조건을 모두 갖춘다.

Deployment 의 파드 템플릿도 PSA 검사를 받습니다. 앞에서 만든 RuntimeClass 이름을 파드 스펙의 runtimeClassName 에 씁니다.