LabHub
배우기 러닝패스 코스

KCA — Kyverno 인증 어소시에이트 · 진짜 Kyverno 에서 확인하기 · 실습

Deployment 는 만들어졌는데 파드가 하나도 없다

LabHub 에서 이어서 보기

목표

Kyverno 정책의 작성 도구(preconditions, apiCall 컨텍스트, 변수 기본값, autogen, JSON 패치, foreach, CEL, 백그라운드 스캔)가
실제 요청에서 무엇을 거절하고 무엇을 바꾸는지 진짜 Kyverno 1.19.1 에서 확인합니다.

왜 중요한가

정책의 문법이 맞다는 것과 의도대로 판정한다는 것은 다릅니다. match 는 리소스 종류와 위치로만 고르므로, 요청 내용에 따라 규칙을 켜고 끄려면
preconditions 가, 요청에 없는 정보가 필요하면 컨텍스트가 있어야 합니다. 변수가 해석되지 않으면 규칙은 판정 대신 오류를 내고, Enforce 에서는
그 오류가 거절로 보여서 원인을 엉뚱한 곳에서 찾게 됩니다. Pod 규칙은 기본적으로 컨트롤러용 규칙으로 자동 생성되는데, 이것을 끄면 Deployment 는
성공한 것처럼 보이고 파드만 조용히 생기지 않습니다. 변형 규칙은 요청마다 다른 값을 넣거나 목록의 일부만 고칠 수 있어야 하고,
이미 클러스터에 있던 리소스는 어드미션을 다시 거치지 않으므로 백그라운드 스캔으로만 드러납니다.

단계

1. 네임스페이스 kca-pre 를 만들고 /root/kca-write/01-limits.yaml 에 ClusterPolicy limits-for-prod(background false)를 작성해 적용하세요. 규칙 memory-limitkca-pre 의 Pod 를 match 하고, preconditions 로 라벨 tierprod 인 요청에만 적용되며(라벨이 없으면 빈 문자열로 취급), 모든 컨테이너에 resources.limits.memory 를 요구합니다(Enforce). match 에 라벨 selector 를 쓰지 않습니다.
2. 네임스페이스 kca-prod(라벨 env=prod)와 kca-dev(라벨 env=dev)를 만들고, /root/kca-write/02-team.yaml 에 ClusterPolicy team-in-prod-ns(background false)를 적용하세요. 규칙 team-when-prod 는 두 네임스페이스의 Pod 에 대해 컨텍스트 nsenvapiCall/api/v1/namespaces/{{request.namespace}} 에서 읽어(라벨 env, 없으면 none) env 가 prod 이고 team 라벨이 없을 때 거부합니다(Enforce). 그다음 kca-dev 의 env 를 잠시 prod 로 바꿔 라벨 없는 파드를 서버 dry-run 으로 보내 본 출력 전체를 /root/kca-write/flip.txt 에 저장하고, env 를 dev 로 되돌리세요.
3. 네임스페이스 kca-var 에 ClusterPolicy no-platform-team(background false, 규칙 not-platform)을 적용하세요. team 라벨이 platform 이면 거부하는 deny 조건인데, 처음에는 key 를 기본값 없이 {{ request.object.metadata.labels.team }} 로 씁니다. 라벨 없는 파드를 서버 dry-run 으로 보내 거절 출력 전체를 /root/kca-write/missing.txt 에 저장하세요. 그다음 /root/kca-write/03-no-platform.yaml 의 key 에 기본값(빈 문자열)을 더해 다시 적용해, 라벨 없는 파드는 받아들이고 team=platform 은 거절하게 하세요.
4. 네임스페이스 kca-autokca-noauto 를 만들고, 각 네임스페이스의 Pod 에 readinessProbe 를 요구하는(Enforce, background false, 규칙 need-probe) ClusterPolicy probes-autoprobes-noauto/root/kca-write/04-probes.yaml 에 작성해 적용하세요. probes-noauto 에만 어노테이션 pod-policies.kyverno.io/autogen-controllers: none 을 붙입니다. 그다음 두 네임스페이스에서 kubectl create deployment web --image=nginx:1.27-alpine 을 실행하고, kca-auto 쪽 출력 전체를 /root/kca-write/auto.txt 에 저장하세요.
5. 네임스페이스 kca-mut 를 만들고 /root/kca-write/05-requested-by.yaml 에 ClusterPolicy requested-by(규칙 annotate-user)를 작성하세요. kca-mut 의 Pod 에 patchesJson6902 의 add 연산으로 어노테이션 kca.io/requested-by 를 넣고 값은 {{request.userInfo.username}} 입니다. 먼저 spec.background 를 적지 않고 적용해 거절되는 출력 전체를 /root/kca-write/bg-denied.txt 에 저장한 뒤, background 를 false 로 두어 적용하세요. 기존 어노테이션은 보존되어야 합니다.
6. /root/kca-write/06-pull-policy.yaml 에 ClusterPolicy pull-policy(규칙 latest-always)를 작성해 적용하세요. kca-mut 의 Pod 에 대해 foreach 로 컨테이너마다 이미지가 :latest 로 끝나는지 원소 단위 preconditions 로 보고, 그런 컨테이너만 patchStrategicMerge 로 imagePullPolicy: Always 로 바꿉니다. 태그를 고정한 컨테이너의 imagePullPolicy 는 그대로여야 합니다.
7. 네임스페이스 kca-cel 을 만들고 /root/kca-write/07-no-priv-esc.yamlpolicies.kyverno.io/v1 ValidatingPolicy no-priv-esc 를 적용하세요. validationActions 는 [Deny], matchConstraints 는 core v1 pods 의 CREATE·UPDATE 와 namespaceSelector kubernetes.io/metadata.name: kca-cel 입니다. CEL 변수 escalating 에 securityContext.allowPrivilegeEscalation 이 명시적으로 false 가 아닌 컨테이너 이름 목록을 담고, 그 목록이 비어 있어야 통과하며 거부 메시지(messageExpression)에 그 이름들을 쉼표로 이어 보여 줍니다.
8. 네임스페이스 kca-bg 에 라벨 없는 Pod old-nolabel 과 라벨 team=a 인 Pod old-label먼저 만든 뒤, /root/kca-write/08-audit-team.yaml 에 ClusterPolicy audit-team(background true, Audit, 규칙 team, kca-bg 의 Pod 에 team 라벨 요구)을 적용하세요. PolicyReport 에 두 파드의 결과가 나타날 때까지 기다린 뒤 /root/kca-write/report.jsonfail·pass 키로 각 결과의 파드 이름 목록을 적습니다.

참고

단계 8개

  1. prod 파드만 limit 을 요구한다
  2. 네임스페이스 라벨 하나로 규칙이 켜진다
  3. 라벨이 없는 파드가 엉뚱한 이유로 거절됐다
  4. Deployment 는 만들어졌는데 파드가 하나도 없다
  5. 요청한 사람의 이름을 파드에 새긴다
  6. latest 태그 컨테이너만 매번 새로 받게 한다
  7. CEL 한 줄이 Deployment 까지 막았다
  8. 정책보다 먼저 있던 파드도 보고서에 올랐다