Tech Artifacts/K8s Security

K8s Security - 05. pod-security-admission

"privileged: true"가 왜 기본으로 허용되는가 — Pod Security Admission과 PSP의 역사

한 팀이 보안 점검에서 지적받았다: 클러스터의 Pod 중 일부가 privileged: true로 돌고 있었다 — 컨테이너가 노드의 커널·장치에 직접 접근할 수 있는 위험 모드. "이걸 막는 기능이 있지 않았나?" 있었다 — PodSecurityPolicy(PSP). 그런데 Kubernetes 1.25에서 PSP는 제거됐다. 후속이 Pod Security Admission. 왜 바뀌었고, 새 모델은 어떻게 다른가?

이 글이 푸는 것은: Pod의 권한 상승을 통제하는 Pod Security Admission의 세 모드(privileged/baseline/restricted), 그리고 PSP가 왜 제거됐는가다.

Pod의 보안 문맥 — 위험한 설정들

Pod는 여러 "위험한" 설정을 가질 수 있다:

  • privileged: true: 컨테이너가 노드의 거의 모든 능력(장치·커널)에 접근.
  • hostNetwork/hostPID/hostPath: 노드의 네트워크/프로세스/파일시스템 직접 접근.
  • allowPrivilegeEscalation: 컨테이너 내부에서 더 높은 권한 획득.
  • runAsUser: 0: root로 실행.

이것들이 기본적으로 허용된다 — Kubernetes는 안전 기본값이 아니다. 그래서 통제가 필요하고, 그것이 Pod Security Admission이다.

Pod Security Admission — 세 프로파일

Pod Security Admission은 namespace에 라벨로 세 프로파일 중 하나를 적용:

프로파일 의미 전형 용도
privileged 제한 없음(위험 설정 허용) 시스템/DaemonSet(노드 접근 필요)
baseline 가장 위험한 권한 상승 차단, 일반 Pod 허용 일반 앱
restricted 엄격(hardening). 최신 보안 관행 강제 미션 크리티컬/규제 환경
# Kubernetes 1.36 — namespace에 restricted 적용
kubectl label namespace prod pod-security.kubernetes.io/enforce=restricted
kubectl label namespace prod pod-security.kubernetes.io/enforce-version=latest

이제 prod namespace에 privileged: true Pod를 만들려 하면 거부.

flowchart LR
    NS["namespace<br/>enforce=restricted"] -. 라벨 .-> PSA["Pod Security Admission<br/>(내장 어드미션)"]
    POD["Pod 생성 (privileged:true)"] --> PSA
    PSA -->|거부| DENY["enforce: 위반"]
    PSA -->|audit 경고| LOG["violation 로그"]

enforce / audit / warn — 세 모드

Pod Security Admission은 라벨로 세 가지 동작 모드를 따로 설정:

  • enforce: 위반 시 거부(하드).
  • audit: 거부 않고 감사 로그에 경고 기록(점진적 도입).
  • warn: 사용자에게 경고(생성은 됨).

이 세 모드를 조합해 "일단 audit/warn으로 관찰 → 충분히 정리되면 enforce"로 점진 전환하는 패턴이 권장.

PSP는 왜 제거됐나 (1.25)

PodSecurityPolicy(이전 모델)는 강력했지만 설계 문제가 있었다:

  • 거부 이유가 불투명 — 어느 필드가 위반인지 사용자가 알기 어려움.
  • 기본 허용 모델 — 명시적으로 허용한 것만. 빠뜨리면 전부 거부(오토스케일 등 부작용).
  • 복잡한 우선순위 — 여러 PSP의 매칭 규칙이 헷갈림.

그래서 1.21에서 deprecated, 1.25에서 제거. 단순한 라벨 기반 3단계 모델(Pod Security Admission)로 대체. 더 단순하고 예측 가능.

이 사실이 중요한 이유: 구형 자료(1.24 이전)가 여전히 "PSP로 막아라"라고 가르친다. 1.25+에선 PSP가 존재하지 않는다. 현재 진실은 Pod Security Admission. 1.36 기준 문서는 반드시 새 모델로.

restricted 프로파일이 강제하는 것들

restricted는 엄격:

  • privileged: true 금지.
  • hostNetwork/hostPID/hostIPC 금지.
  • allowPrivilegeEscalation: false 강제.
  • 컨테이너 runAsNonRoot: true 또는 runAsUser != 0.
  • seccompProfile 설정 강제.
  • capability 삭제(drop: [ALL]).

이것이 "현대 보안 hardening"의 기준. 단 모든 앱이 이 제약을 감당하는 건 아니다 — 레거시, 노드 접근 필요(DaemonSet)는 baseline이나 privileged가 필요.

exemptions — 예외

Pod Security Admission은 특정 Pod(예: kube-system 시스템 Pod)를 예외로 둘 수 있다(라벨/username 기반). 시스템 Pod는 restricted 위반(노드 접근 필요)이 흔하므로.

직접 확인하기

# Kubernetes 1.36 — restricted namespace에서 privileged Pod 거부
kubectl create ns secure
kubectl label ns secure pod-security.kubernetes.io/enforce=restricted
kubectl run x --image=nginx:1.27 -n secure --restart=Never \
  --overrides='{"spec":{"containers":[{"name":"x","image":"nginx:1.27","securityContext":{"privileged":true}}]}}'

확인할 것: privileged:true Pod가 거부. 에러 메시지가 어느 필드 위반인지 알려줌.

# audit 모드로 경고만 (생성은 됨)
kubectl label ns secure pod-security.kubernetes.io/enforce- # enforce 제거
kubectl label ns secure pod-security.kubernetes.io/audit=restricted
kubectl run y --image=nginx:1.27 -n secure ...   # 생성됨 + 경고

restricted가 정확히 강제하는 것들 — 항목별

restricted 프로파일이 강제하는 요구 사항을 항목별로 보면, "왜 이게 hardening인가"가 분명해진다:

요구 강제 내용 막는 공격
privileged: true 금지 컨테이너가 노드 권한 획득 불가 컨테이너 탈출 → 노드 장악
hostNetwork/PID/IPC 금지 노드의 네트워크/프로세스 공간 접근 차단 다른 Pod/노드 프로세스 간섭
allowPrivilegeEscalation: false 컨테이너 내부에서 setuid 등 권한 상승 차단 악의적 바이너리의 권한 획득
runAsNonRoot: true 또는 runAsUser != 0 root가 아닌 사용자로 실행 root 권한 남용
seccompProfile 설정 syscall 필터링(비정상 syscall 차단) 커널 취약점 악용
capabilities: drop: [ALL] Linux capability 전부 제거 후 필요한 것만 추가 불필요 권한(예: NET_ADMIN) 악용

각 항목이 특정 공격 유형을 막는다. "restricted가 빡빡하다"는 불만은 이해되지만, 각 항목이 방어하는 위협을 보면 타협의 결과다. 특히 privilegedhostPath는 컨테이너 탈출(컨테이너 → 노드)의 주요 경로 — restricted가 이것을 원천 차단.

audit → enforce 점진 전환의 실제 절차

새 보안 정책을 "바로 enforce"하면 기존 워크로드가 거부돼 장애. 그래서 표준 패턴:

flowchart LR
    S1["1. warn만 설정<br/>(생성은 되되 경고)"] --> OBS["관찰: 어느 Pod가 위반?"]
    OBS --> S2["2. audit 추가<br/>(감사 로그에 기록)"]
    S2 --> FIX["위반 Pod 수정"]
    FIX --> S3["3. enforce 전환<br/>(거부 시작)"]
  1. warn: 위반 Pod 생성 시 사용자에게 경고. 하지만 생성은 됨. "어떤 팀이 위반하나" 파악.
  2. audit: 감사 로그에 위반 기록. 위반 빈도/심각도 가시화.
  3. enforce: 충분히 정리한 뒤 실제 거부.

이 점진 전환을 안 하면 — enforce 켜는 순간 미처 수정 못 한 Pod가 거부돼 서비스 장애. 특히 시스템 Pod(kube-system 등)는 restricted 위반이 흔하므로 exemption으로 미리 제외해야.

exemption 설계 — 시스템 Pod는 규칙 밖으로

restricted를 모든 namespace에 강제하면 — kube-system의 시스템 Pod(DaemonSet, CNI 에이전트 등)이 거부된다. 이들은 노드 접근(hostPath, privileged)이 합법적으로 필요. 그래서 exemption으로 제외:

# exemption 예 (Pod Security Admission 구성)
exemptions:
- usernames: ["system:..."]
  namespaces: ["kube-system"]
  runtimeClasses: []

exemption 설계의 핵심: 최소. kube-system 전체를 예외로 두면 편하지만, kube-system의 비시스템 Pod(누군가 실수로 배포한)도 예외가 됨. 가능한 좁게(특정 SA/라벨만). 이 trade-off가 exemption 운영의 핵심.

세 라벨의 조합 매트릭스 — enforce/audit/warn을 어떻게 섞나

Pod Security Admission의 세 모드(enforce/audit/warn)를 다른 프로파일*로 조합할 수 있다. 예: "enforce는 baseline, audit/warn은 restricted" — baseline 위반은 *거부(강), restricted 위반은 경고만(유연). 점진적 강화 패턴.

enforce audit warn 효과
privileged 사실상 제한 없음
baseline 위험한 권한만 거부
baseline restricted restricted baseline은 강제, restricted는 경고(점진 강화)
restricted restricted restricted 완전 restricted 강제

이 매트릭스가 "지금은 baseline만 강제하되, restricted 위반을 관찰해 나중에 강제로 올리겠다"를 표현. enforce를 올리는 건 거부를 일으키므로 신중해야 — audit/warn으로 먼저 관찰하는 것이 안전한 강화 경로.

enforce-version 핀닝 — 버전 고정의 안전망

Pod Security Admission은 버전을 고정할 수 있다:

pod-security.kubernetes.io/enforce-version: v1.36
pod-security.kubernetes.io/enforce-version: latest

latest는 "항상 최신 Pod Security Standard"를 — Kubernetes 버전업 시 기준이 바뀔 수 있어, 갑자기 더 엄격해져 기존 Pod가 거부될 위험. v1.36 핀닝은 "이 버전의 기준으로 고정" — 의도된 업그레이드 시점까지 안정. 프로덕션에선 latest 대신 구체 버전 핀닝이 안전.

이것이 Kubernetes의 GA/beta/alpha 규칙(03-fine-grained-kubelet에서 본)의 또 다른 적용 — "최신"은 예측 불가, "구체 버전"은 안정. Pod Security Standard도 버전이 있고, 핀닝이 예측 가능성을 준다.

흔히 묻는 것, 흔히 틀리는 것

오해 정정
"PSP가 아직 있다" 1.25 제거. 현재는 Pod Security Admission
"Pod Security Admission은 별도 설치한다" 내장 어드미션. namespace 라벨로 켬
"enforce는 모든 위반을 거부한다" 맞음. 단 exemption 가능
"restricted는 모든 앱에 적합하다" 엄격. 노드 접근 필요/레거시엔 baseline/privileged
"audit은 거부다" 거부 아님. 감사 로그 기록만. 점진 도입용
"privileged Pod는 절대 쓰면 안 된다" 시스템 Pod(DaemonSet)는 합리적 사용. 일반 앱은 금지

요약 — 이 글의 결론

  • Pod의 위험 설정(privileged/hostNetwork 등)이 기본 허용. Kubernetes는 안전 기본값이 아님 — 통제가 필요.
  • Pod Security Admission(내장 어드미션)이 namespace 라벨로 세 프로파일(privileged/baseline/restricted) 적용.
  • 세 동작 모드(enforce/audit/warn)로 점진적 도입 — audit/warn으로 관찰 후 enforce.
  • restricted가 현대 hardening 기준(privileged 금지, non-root, seccomp 등). 단 모든 앱이 감당하는 건 아님.
  • PSP는 1.25 제거 — 거부 이유 불투명/기본 허용 모델/복잡 우선순위 문제. 단순 라벨 기반 모델로 대체.
  • exemption으로 시스템 Pod(kube-system 등)는 예외 처리.

생각해 볼 문제

  1. privileged: true Pod가 왜 위험한가? 구체적 공격 시나리오를 하나.
  2. PSP에서 Pod Security Admission으로 바뀐 이유 중 "기본 허용 모델"의 문제를 설명하라.
  3. enforce로 바로 켰더니 기존 Pod 배포가 막혔다. audit/warn으로 점진 전환하는 절차를 세라.
  4. restricted를 prod에, baseline을 dev에 두었다. 차이가 생기는 구체적 설정은?
  5. DaemonSet(노드 로그 수집)을 restricted namespace에 만들려 한다. 거부된다. 어떻게?
  6. Pod Security Admission과 NetworkPolicy가 함께 쓰일 때 "심층 방어"가 어떻게 구성되나?

참고