KCA — Kyverno Certified Associate
The policy was rejected, and the culprit was permissions
한국어 원문으로 표시합니다.
목표
설치된 Kyverno 의 구성요소를 읽고, generate·cleanup 이 거절될 때 컨트롤러 권한을 집계 ClusterRole 로 더해 고치며, kyverno ConfigMap 의 resourceFilters 와 webhooks 설정이 요청을 어디서 걸러 내는지 실제 요청으로 구분합니다.
왜 중요한가
Kyverno 는 하나의 프로그램이 아니라 어드미션·백그라운드·정리·보고서 네 컨트롤러가 각자의 ServiceAccount 로 도는 시스템입니다. generate 규칙이 만드는 객체와 정리 정책이 지우는 객체는 요청한 사람이 아니라 그 컨트롤러의 권한으로 처리되므로, 권한이 없으면 정책을 쓰는 순간 어드미션에서 거절됩니다. 설치 매니페스트는 기본 권한을 최소로 두고 라벨로 집계되는 ClusterRole 을 더하게 설계되어 있어, 와일드카드 권한을 주는 대신 필요한 종류와 동사만 더하는 것이 운영의 기본입니다. 반대로 검사를 빼고 싶을 때는 두 자리가 있습니다. resourceFilters 는 API 서버가 요청을 보낸 뒤 Kyverno 가 무시하는 것이고, 웹훅 namespaceSelector 는 API 서버가 아예 보내지 않는 것입니다. 어느 쪽이든 기본 제외 항목을 지우면 Kyverno 가 자기 자신이나 시스템 네임스페이스를 막는 사고가 납니다.
단계
/root/kca-install/inventory.json에version(kyverno-admission-controller 이미지의 태그),controllers(kyverno 네임스페이스 Deployment 이름 → spec.replicas 숫자),crd_count(API 그룹이 kyverno.io 로 끝나는 CRD 개수),webhook_configs(이름에 kyverno 가 든 ValidatingWebhookConfiguration·MutatingWebhookConfiguration 이름을 정렬한 배열)를 적으세요./root/kca-install/02-gen-pdb.yaml에 ClusterPolicygen-pdb를 작성해 적용해 보세요. 규칙default-pdb는 라벨kca.io/pdb=true인 Namespace 가 생기면 그 안에 PodDisruptionBudgetdefault-pdb(minAvailable 1, selector app=web)를 synchronize true 로 생성합니다. 적용 명령의 출력 전체를/root/kca-install/gen-denied.txt에 저장하세요./root/kca-install/03-role-bg.yaml에 ClusterRolekca-bg-pdb를 만들어 라벨rbac.kyverno.io/aggregate-to-background-controller: "true"를 붙이고 policy 그룹 poddisruptionbudgets 에 get·list·watch·create·update·delete 만 허용하세요(와일드카드 금지). 그다음gen-pdb를 다시 적용하고, 네임스페이스kca-pdb-a를 라벨kca.io/pdb=true로 만들어 PDB 가 생기는 것을 확인합니다. 생긴 PDB 의 UID 를 적어 두고 PDB 를 지운 뒤 다시 생긴 PDB 의 UID 와 함께/root/kca-install/pdb.json에first_uid,second_uid로 적으세요./root/kca-install/04-clean-short.yaml에 ClusterCleanupPolicyclean-short를 작성해 적용해 보세요. 네임스페이스kca-clean에서 라벨ttl=short인 Pod 를 매분(*/1 * * * *) 지웁니다. 적용 명령의 출력 전체를/root/kca-install/cleanup-denied.txt에 저장하세요./root/kca-install/05-role-cleanup.yaml에 ClusterRolekca-cleanup-pods(라벨rbac.kyverno.io/aggregate-to-cleanup-controller: "true", core pods 에 get·list·watch·delete 만)를 적용하고clean-short를 다시 적용하세요. 네임스페이스kca-clean에 라벨ttl=short인 Podshort와ttl=long인 Podlong을 만들고, 다음 정각 분이 지나 short 만 사라진 것을 확인한 뒤/root/kca-install/cleanup.json에long_uid(남은 long 의 UID)와last_execution(clean-short 의 status.lastExecutionTime)을 적으세요.- 네임스페이스
kca-skip과kca-ctl을 만들고/root/kca-install/06-need-team.yaml에 ClusterPolicyneed-team(background false, 규칙team, 두 네임스페이스의 Pod 에 team 라벨 요구, Enforce)을 적용하세요. 그다음 kyverno 네임스페이스 ConfigMapkyverno의resourceFilters에 기존 항목을 모두 유지한 채[Pod,kca-skip,*]를 덧붙이세요.kca-skip의 라벨 없는 파드는 받아들여지고kca-ctl의 라벨 없는 파드는 계속 거절되어야 합니다. - 네임스페이스
kca-sel을 라벨kca.io/webhook=skip으로 만들고need-team의 대상 네임스페이스에kca-sel을 더해 다시 적용하세요. 그다음 ConfigMapkyverno의webhooks값의 namespaceSelector 에 기존 kube-system·kyverno 제외 조건을 유지한 채kca.io/webhookNotIn[skip]조건을 더하세요. Kyverno 가 갱신한kyverno-resource-validating-webhook-cfg에 그 조건이 들어가고,kca-sel의 라벨 없는 파드는 받아들여지며kca-ctl은 계속 거절되어야 합니다. resourceFilters 에는 kca-sel 을 넣지 않습니다. /root/kca-install/report.json에background_role(generate 권한을 더한 ClusterRole 이름),cleanup_role(정리 권한을 더한 ClusterRole 이름),pdb_regenerated(3단계 두 UID 가 다른지, 불리언),skip_by(kca-skip 을 거른 설정 키:resourceFilters또는webhooks),sel_by(kca-sel 을 거른 설정 키),default_exclusions(웹훅 selector 에 남아 있는 기본 제외 네임스페이스 이름을 정렬한 배열)를 적으세요.
참고
- VM 안에 k3s 와 Kyverno v1.19.1(설치 매니페스트, 컨트롤러 각 1개)이 있습니다.
- 계정 권한 확인:
kubectl auth can-i create poddisruptionbudgets.policy --as system:serviceaccount:kyverno:kyverno-background-controller -n <ns> - 저장하지 않고 판정 보기:
kubectl -n <ns> run t --image=nginx:1.27-alpine --dry-run=server - 흔한 실수: resourceFilters 나 webhooks 값을 통째로 새 값으로 바꾸는 것. 기본 제외 항목이 사라집니다.
- 흔한 실수: 집계 ClusterRole 에
*동사를 주는 것. 채점기는 과제의 동사만 있는지 봅니다. - Customizing Permissions · Cleanup · Generate Rules · Kubernetes RBAC 집계
설치된 Kyverno 가 무엇으로 이루어져 있나
/root/kca-install/inventory.json 에 version(kyverno-admission-controller 이미지의 태그), controllers(kyverno 네임스페이스 Deployment 이름 → spec.replicas 숫자), crd_count(API 그룹이 kyverno.io 로 끝나는 CRD 개수), webhook_configs(이름에 kyverno 가 든 ValidatingWebhookConfiguration·MutatingWebhookConfiguration 이름을 정렬한 배열)를 적으세요.
CRD 의 그룹은 spec.group 에 있습니다. kyverno.io, policies.kyverno.io, reports.kyverno.io 처럼 여러 그룹이 있습니다. 웹훅 등록은 정책을 만들 때 Kyverno 가 추가로 만들기도 하므로 지금 있는 것을 읽으세요.
정책은 맞는데 어드미션이 거절했다
/root/kca-install/02-gen-pdb.yaml 에 ClusterPolicy gen-pdb 를 작성해 적용해 보세요. 규칙 default-pdb 는 라벨 kca.io/pdb=true 인 Namespace 가 생기면 그 안에 PodDisruptionBudget default-pdb(minAvailable 1, selector app=web)를 synchronize true 로 생성합니다. 적용 명령의 출력 전체를 /root/kca-install/gen-denied.txt 에 저장하세요.
generate 규칙의 대상 종류는 요청한 사람이 아니라 background controller 의 ServiceAccount 가 만듭니다. Kyverno 는 정책을 받을 때 그 계정의 권한을 미리 확인합니다. 거절 메시지에서 누구에게 어떤 동사가 없는지 읽으세요.
라벨 하나로 컨트롤러에 권한을 더한다
/root/kca-install/03-role-bg.yaml 에 ClusterRole kca-bg-pdb 를 만들어 라벨 rbac.kyverno.io/aggregate-to-background-controller: "true" 를 붙이고 policy 그룹 poddisruptionbudgets 에 get·list·watch·create·update·delete 만 허용하세요(와일드카드 금지). 그다음 gen-pdb 를 다시 적용하고, 네임스페이스 kca-pdb-a 를 라벨 kca.io/pdb=true 로 만들어 PDB 가 생기는 것을 확인합니다. 생긴 PDB 의 UID 를 적어 두고 PDB 를 지운 뒤 다시 생긴 PDB 의 UID 와 함께 /root/kca-install/pdb.json 에 first_uid, second_uid 로 적으세요.
Kyverno 가 설치한 kyverno:background-controller ClusterRole 은 aggregationRule 을 가집니다. 새 ClusterRole 을 따로 바인딩할 필요가 없습니다. synchronize 가 켜진 생성물은 지워도 다시 만들어집니다.
지울 권한이 없는 정리 정책
/root/kca-install/04-clean-short.yaml 에 ClusterCleanupPolicy clean-short 를 작성해 적용해 보세요. 네임스페이스 kca-clean 에서 라벨 ttl=short 인 Pod 를 매분(*/1 * * * *) 지웁니다. 적용 명령의 출력 전체를 /root/kca-install/cleanup-denied.txt 에 저장하세요.
정리 정책을 실행하는 것은 cleanup controller 입니다. 기본 설치의 그 계정이 어떤 종류를 지울 수 있는지는 kyverno:cleanup-controller ClusterRole 에서 볼 수 있습니다.
짧은 수명 파드만 정각에 사라졌다
/root/kca-install/05-role-cleanup.yaml 에 ClusterRole kca-cleanup-pods(라벨 rbac.kyverno.io/aggregate-to-cleanup-controller: "true", core pods 에 get·list·watch·delete 만)를 적용하고 clean-short 를 다시 적용하세요. 네임스페이스 kca-clean 에 라벨 ttl=short 인 Pod short 와 ttl=long 인 Pod long 을 만들고, 다음 정각 분이 지나 short 만 사라진 것을 확인한 뒤 /root/kca-install/cleanup.json 에 long_uid(남은 long 의 UID)와 last_execution(clean-short 의 status.lastExecutionTime)을 적으세요.
cron 일정은 분 단위라 최대 1분을 기다립니다. selector 에 맞지 않는 파드는 그대로 남아야 합니다.
Kyverno 가 받고도 못 본 척한 요청
네임스페이스 kca-skip 과 kca-ctl 을 만들고 /root/kca-install/06-need-team.yaml 에 ClusterPolicy need-team(background false, 규칙 team, 두 네임스페이스의 Pod 에 team 라벨 요구, Enforce)을 적용하세요. 그다음 kyverno 네임스페이스 ConfigMap kyverno 의 resourceFilters 에 기존 항목을 모두 유지한 채 [Pod,kca-skip,*] 를 덧붙이세요. kca-skip 의 라벨 없는 파드는 받아들여지고 kca-ctl 의 라벨 없는 파드는 계속 거절되어야 합니다.
resourceFilters 는 [종류,네임스페이스,이름] 묶음을 공백으로 이은 문자열입니다. 기본값에는 kube-system·kyverno 네임스페이스와 Event 같은 항목이 들어 있어서 통째로 바꾸면 Kyverno 가 자기 자원까지 검사하게 됩니다. Kyverno 는 이 ConfigMap 을 재시작 없이 다시 읽습니다.
API 서버가 아예 묻지 않게 한다
네임스페이스 kca-sel 을 라벨 kca.io/webhook=skip 으로 만들고 need-team 의 대상 네임스페이스에 kca-sel 을 더해 다시 적용하세요. 그다음 ConfigMap kyverno 의 webhooks 값의 namespaceSelector 에 기존 kube-system·kyverno 제외 조건을 유지한 채 kca.io/webhook NotIn [skip] 조건을 더하세요. Kyverno 가 갱신한 kyverno-resource-validating-webhook-cfg 에 그 조건이 들어가고, kca-sel 의 라벨 없는 파드는 받아들여지며 kca-ctl 은 계속 거절되어야 합니다. resourceFilters 에는 kca-sel 을 넣지 않습니다.
webhooks 값은 JSON 문자열입니다. Kyverno 는 그 selector 를 자기가 관리하는 웹훅 등록에 복사하고, API 서버는 selector 에 맞지 않는 네임스페이스의 요청을 Kyverno 에 보내지도 않습니다. 등록 객체를 직접 고치면 Kyverno 가 되돌립니다.
권한과 필터를 어디에 두었는지 보고
/root/kca-install/report.json 에 background_role(generate 권한을 더한 ClusterRole 이름), cleanup_role(정리 권한을 더한 ClusterRole 이름), pdb_regenerated(3단계 두 UID 가 다른지, 불리언), skip_by(kca-skip 을 거른 설정 키: resourceFilters 또는 webhooks), sel_by(kca-sel 을 거른 설정 키), default_exclusions(웹훅 selector 에 남아 있는 기본 제외 네임스페이스 이름을 정렬한 배열)를 적으세요.
앞 단계에서 남긴 파일과 지금의 ConfigMap·웹훅 등록을 근거로 적습니다. 채점기는 같은 값을 클러스터에서 다시 계산합니다.