[CKS] Minimize Microservice Vulnerabilities (3) - Pod Security
Pod 보안 정책 가이드. Pod Security Policies, Pod Security Admission, Pod Security Standards의 개념과 설정 방법을 통해 클러스터 보안을 강화하는 방법을 설명합니다.
개요
CKS 자격증을 위해 아래 내용을 정리했습니다.
Pod Security Policies
Pod Security Admission, Pod Security Standards 를 이해하기 위해 Pod Security Policies를 같이 정리했습니다.
Pod Definition
취약한 Ubuntu Pod가 있다고 가정해보겠습니다. 파드는 최대 권한을 받아 취약점을 가지게 됩니다.
privileged플래그가 설정되어True컨테이너 프로세스에 관리자 권한이 부여됩니다- 컨테이너는 루트 권한으로 실행됩니다(
runAsUser: 0) - 다음과 같은 특정 기능을 추가합니다
CAP_SYS_BOOT - 호스트 경로 볼륨이 사용되어 호스트 파일 시스템의 일부가 노출됩니다
apiVersion: v1
kind: Pod
metadata:
name: sample-pod
spec:
containers:
- name: ubuntu
image: ubuntu
command: ["sleep", "3600"]
securityContext:
privileged: True
runAsUser: 0
capabilities:
add: ["CAP_SYS_BOOT"]
volumes:
- name: data-volume
hostPath:
path: /data
type: Directory
클러스터를 보호하는 방법과 구성을 제한하는 정책을 알아봅니다.
Pod Security Policies
Pod Security Policies(이하 PSP)는 미리 구성된 보안 규칙으로 Pod 생성 요청을 검증합니다.
PSP Admission Controller가 활성화되면 Pod 생성 요청을 중간 처리하여 정책을 준수하지 않을 때 요청을 거부합니다. privileged 플래그가 설정된 파드는 정책에 의해 거절될 수 있습니다.
- 위반 사항 감지 → 생성 요청 거부 → 에러 메시지 반환
Enabling PSP on the API Server
PSP는 Admission Controller 역할을 합니다.
이를 위해 API Server의 admission plugins 항목에서 PodSecurityPolicy 플러그인을 추가합니다.
ExecStart=/usr/local/bin/kube-apiserver \
--advertise-address=${INTERNAL_IP} \
--allow-privileged=true \
--apiserver-count=3 \
--authorization-mode=Node,RBAC \
--bind-address=0.0.0.0 \
--enable-swagger-ui=true \
--etcd-servers=https://127.0.0.1:2379 \
--event-ttl=1h \
--runtime-config=api/all \
--service-cluster-ip-range=10.32.0.0/24 \
--service-node-port-range=30000-32767 \
--v=2 \
--enable-admission-plugins=PodSecurityPolicy # Admission Plugins 활성화
혹은 API Server Pod Definition에서 Admission Plugins을 활성화합니다
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
name: kube-apiserver
namespace: kube-system
spec:
containers:
- command:
- kube-apiserver
- --authorization-mode=Node,RBAC
- --advertise-address=172.17.0.107
- --allow-privileged=true
- --enable-bootstrap-token-auth=true
- --enable-admission-plugins=PodSecurityPolicy
image: k8s.gcr.io/kube-apiserver-amd64:v1.11.3
name: kube-apiserver
Admission controller를 통해 PSP 규칙에 따라 승인되어 배포됩니다.
Definition PSP
privileged 가 설정된 파드를 허용하지 않으려면 PodSecurityPolicy 를 배포합니다.
- 모든
privileged를 거부하는 PSP
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
name: example-psp
spec:
privileged: false
- SELinux 컨텍스트 아무거나 어떤 그룹이든 가능하지만 Root로 불가능
- CAP_SYS_BOOT 반드시 제거, CAP_SYS_TIME 기본으로 추가
- PVC 만 허용
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
name: example-psp
spec:
privileged: false
seLinux:
rule: RunAsAny
supplementalGroups:
rule: RunAsAny
runAsUser:
rule: MustRunAsNonRoot
requiredDropCapabilities:
- CAP_SYS_BOOT
defaultAddCapabilities: # 자동으로 추가됨
- CAP_SYS_TIME
volumes:
- persistentVolumeClaim
최신 변경사항으로 defaultAddCapabilites는 Pod Security Admission, Pod Security Standards에서 허락하지 않습니다.
Admission Controller 동작 방식
- 사용 가능한 모든 Pod 보안 정책(PSP) 객체 조회
- 정의된 규칙에 따라 Pod의 유효성 검사
- 설정된 정책과 충돌하는 요청 거부합니다.
PSP의 검토가 없이 활성화한다면 모든 Pod 생성 요청이 차단될 수 있습니다.
해당 시나리오는 올바르게 정의된 정책과 관련 역할 및 역할 바인딩 구성을 요구합니다.
Pod가 PSP에 엑세스하기 위해선 Service Account가 필요합니다.
PSP 허용 권한을 부여하고 Role과 Role binding을 구성합니다.
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: psp-example-role
rules:
- apiGroups: ["policy"]
resources: ["podsecuritypolicies"]
resourceNames: ["example-psp"]
verbs: ["use"]
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: psp-example-rolebinding
subjects:
- kind: ServiceAccount
name: default
namespace: default
roleRef:
kind: Role
name: psp-example-role
apiGroup: rbac.authorization.k8s.io
주의사항
PSP를 활성화하려면 API Server(Admission Controller 허용)과 클러스터 수준(PSP 및 RBAC) 모두 변경이 필요합니다.
Pod Security Admission and Pod Security Standards 알아보기
KEP-2579(k8s v1.25)에 정의된 Pod Security Admission(PSA) 및 Pod Security Standards에 살펴봅니다. PSA와 PSS는 단순성과 안전성을 최우선으로 고려합니다. 또한 컴포넌트들은 Kyverno 또는 OPA Gatekeeper같은 3rd-party 솔루션으로 관리할 수 있습니다
PSA는 Admission Controller 역할을 하며 Kubernetes 클러스터 상에서 기본 활성화 되어있습니다.
따라서 별도의 활성화 과정이 필요없습니다.
kubectl exec -n kube-system kube-apiserver-controlplane -it -- kube-apiserver -h | grep enable-admission-plugins
동시에 PSA는 특정 레이블을 사용하여 Namespace 수준에서 적용됩니다. PSP처럼 별도의 Policy 리소스 + RBAC 설정이 필요 없고 네임스페이스에 라벨만 달면 됩니다.
apiVersion: v1
kind: Namespace
metadata:
name: payroll
labels:
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/warn: restricted
pod-security.kubernetes.io/audit: baseline
apiVersion: apiserver.config.k8s.io/v1
kind: AdmissionConfiguration
plugins:
- name: PodSecurity
configuration:
apiVersion: pod-security.admission.config.k8s.io/v1
kind: PodSecurityConfiguration
defaults:
enforce: "baseline"
warn: "restricted"
Privileged (권한 있음)
- 거의 모든 권한 허용 / 권한 상승(privilege escalation) 가능
- 용도: 시스템 컴포넌트, 인프라 Pod
Baseline (기본)
- 알려진 권한 상승 차단 / 대부분의 컨테이너 앱 호환
- 용도: 일반적인 워크로드
Restricted (제한됨)
- 가장 엄격한 보안 / Pod 하드닝 모범 사례 적용(일부 앱 호환성 문제 가능)
- 용도: 보안이 중요한 워크로드
Privileged < Baseline < Restricted 순으로 엄격하며 [무엇을 검사할 것인지] 정의할 때 사용합니다.
반대로 모드는 [위반시 어떻게 대응할지]를 정의할 때 사용합니다.
- enforce: 위반 시 Pod 생성 거부
- audit: 위반 시 Pod 생성 허용, 감사 로그에 기록
- warn: 위반 시 Pod 생성 허용, 사용자에게 경고 표시
이를 표로 정리하면 다음과 같습니다.
| 라벨 | 의미 |
|---|---|
pod-security.kubernetes.io/enforce: restricted |
restricted 위반 시 → 거부 |
pod-security.kubernetes.io/warn: restricted |
restricted 위반 시 → 경고 |
pod-security.kubernetes.io/audit: baseline |
baseline 위반 시 → 로그 기록 |
정책은 미리 정의되어있습니다. 동시에 대부분의 컨테이너화 애플리케이션엔 baseline 프로필을 통해 무단으로 권한을 상승하는 것을 방지하고 있습니다. 반면 restricted 프로필은 최신 보안 모범 사례가 정의되어있습니다. 호환성 문제가 발생할 수 있지만 강력한 보안을 제공합니다.
각 Namespace 별로 Label을 지정하는 방법은 다음과 같습니다
kubectl label ns payroll pod-security.kubernetes.io/enforce=restricted
kubectl label ns hr pod-security.kubernetes.io/enforce=baseline
kubectl label ns dev pod-security.kubernetes.io/warn=restricted
baseline 의 경우 priviledged=true 를 허용하지 않기 때문에 오류가 발생합니다.
Warning: would violate PodSecurity "baseline:latest": privileged (container "baseline-pod" must not set securityContext.privileged=true)
Error from server (AlreadyExists): error when creating "baseline-pod.yaml": pods "baseline-pod" already exists
restricted & warn
Warning: would violate PodSecurity "restricted:latest": allowPrivilegeEscalation != false (container "multi-psa" must set securityContext.allowPrivilegeEscalation=false), unrestricted capabilities (container "multi-psa" must set securityContext.capabilities.drop=["ALL"]), runAsNonRoot != true (pod or container "multi-psa" must set securityContext.runAsNonRoot=true), runAsUser=0 (container "multi-psa" must not set runAsUser=0), seccompProfile (pod or container "multi-psa" must set securityContext.seccompProfile.type to "RuntimeDefault" or "Localhost")
pod/multi-psa created
더 자세한 내용은 PSA 공식 문서를 참고하세요.