CKS — 쿠버네티스 보안 전문가 · 공급망 보안 · 실습
CEL 로 쓰는 어드미션 정책
목표
외부 웹훅 서버 없이 API 서버 안에서 CEL 로 동작하는 ValidatingAdmissionPolicy 를 직접 만들고,
바인딩으로 적용 범위를 좁힌 뒤, 전통적인 ValidatingWebhookConfiguration 오브젝트도 함께
작성해 두 방식의 차이를 손으로 확인합니다.
왜 중요한가
RBAC 은 "Pod 를 만들 수 있는가"까지만 답합니다. 그 Pod 가 privileged 인지, 허용된 레지스트리의
이미지인지는 RBAC 으로 표현할 수 없습니다. 이 간극을 메우는 것이 어드미션 정책입니다.
전통적인 방법은 ValidatingWebhookConfiguration 으로 외부 서버를 등록하는 것인데, 여기에는
가용성 함정이 있습니다. failurePolicy: Fail 로 등록된 웹훅의 서버가 죽으면 대상 리소스의
생성이 전부 거부되어 배포가 통째로 멈춥니다. 정책을 지키려다 클러스터를 세우는 것입니다.
ValidatingAdmissionPolicy 는 API 서버 안에서 CEL 표현식을 평가하므로 네트워크 홉과 외부
프로세스가 사라지고, 이 실패 모드 자체가 줄어듭니다.
정책과 바인딩이 분리된 것도 의도된 설계입니다. 정책은 "무엇이 위반인가"만 정의하고, 바인딩이
"어디에, 얼마나 세게 적용할 것인가"를 정합니다. 같은 정책을 스테이징에서는 Warn 으로,
프로덕션에서는 Deny 로 켤 수 있습니다.
단계
1. 네임스페이스 cks-admission 을 만들고 라벨 cks-policy=enforce 를 붙인다.
2. ValidatingAdmissionPolicy cks-no-latest-tag 를 만든다.spec.matchConstraints.resourceRules[0] 은 apiGroups apps, apiVersions v1,
operations CREATE 와 UPDATE, resources deployments 다.
(API 서버는 validations 도 auditAnnotations 도 없는 정책을 거부한다. 이 단계에서는expression: "true" 같은 자리표시자 검증식을 하나 넣어 두고, 3 단계에서 진짜 표현식으로
바꾼다.)
3. 같은 정책에 spec.validations[0] 을 넣는다. expression 은 Deployment 의 모든 컨테이너
이미지가 :latest 로 끝나지 않는지 검사하는 CEL 이고(object.spec.template.spec.containers
와 all 이 들어가야 한다), message 는 비어 있으면 안 된다.
4. 같은 정책의 spec.failurePolicy 를 Fail 로 설정한다.
5. ValidatingAdmissionPolicyBinding cks-no-latest-tag-binding 을 만든다. policyName 은cks-no-latest-tag, validationActions 는 Deny,matchResources.namespaceSelector.matchLabels 는 cks-policy: enforce 다.
6. ValidatingAdmissionPolicy cks-no-privileged 를 만든다. matchConstraints 는 코어 그룹
(apiGroups 는 빈 문자열), apiVersions v1, operations CREATE·UPDATE, resources pods
이고, validations[0].expression 에는 privileged 라는 단어와 object.spec.containers 가
들어가야 하며 message 도 있어야 한다.
7. ValidatingWebhookConfiguration cks-image-verify 를 만든다. 웹훅 하나(name 은verify.images.cks.local)를 두고 failurePolicy 는 Ignore, sideEffects 는 None,admissionReviewVersions 는 v1, clientConfig.service 는 네임스페이스 cks-admission
의 서비스 image-verifier, rules 는 코어 그룹 v1 의 pods 에 대한 CREATE 다.
8. cks-admission 에 Deployment checkout 을 만든다. replicas 2, 셀렉터와 파드 라벨은app=checkout, 파드 템플릿에 라벨 app.kubernetes.io/name=checkout 도 넣는다.
컨테이너 이름은 app, 이미지는 @sha256: 로 고정하고 :latest 를 쓰지 않으며,
컨테이너 securityContext 에 privileged: false 와 allowPrivilegeEscalation: false 를
명시한다.
참고
kubectl explain validatingadmissionpolicy.spec.matchConstraints.resourceRules로 필드를 확인하세요.- CEL 예시:
object.spec.template.spec.containers.all(c, !c.image.endsWith(':latest')) - 파드용 예시:
!object.spec.containers.exists(c, has(c.securityContext) && c.securityContext.privileged == true) - 흔한 실수 1: 정책만 만들고 바인딩을 만들지 않습니다. 바인딩이 없는 정책은 아무 요청도 검사하지 않습니다.
- 흔한 실수 2: 웹훅에
sideEffects나admissionReviewVersions를 빠뜨리면 API 서버가 오브젝트 생성 자체를 거부합니다. - 흔한 실수 3: 웹훅 서버가 없는 상태에서
failurePolicy: Fail을 쓰면 대상 리소스를 아예 만들 수 없게 됩니다.
단계 8개
- 정책을 적용할 네임스페이스
- ValidatingAdmissionPolicy 골격
- CEL 표현식으로 검증 규칙 쓰기
- failurePolicy 정하기
- 바인딩으로 정책 켜기
- privileged 금지 정책 추가
- ValidatingWebhookConfiguration 작성
- 종합: 정책을 통과하는 배포