Skip to content

Split View: Kyverno 정책 엔진 분석: 검증, 변형, 생성 규칙 심층 분석

|

Kyverno 정책 엔진 분석: 검증, 변형, 생성 규칙 심층 분석


1. Validate 규칙 상세

1.1 패턴 매칭(Pattern Matching)

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: require-run-as-non-root
spec:
  validationFailureAction: Enforce
  rules:
    - name: check-security-context
      match:
        any:
          - resources:
              kinds:
                - Pod
      validate:
        message: 'Containers must run as non-root'
        pattern:
          spec:
            containers:
              - securityContext:
                  runAsNonRoot: true

패턴 매칭 연산자:

  • ?*: 비어있지 않은 값 (null이 아닌 모든 값)
  • *: 모든 값 (null 포함)
  • X|Y: X 또는 Y
  • !X: X가 아닌 값
  • >X, <X, >=X, <=X: 수치 비교

1.2 deny 규칙

조건 기반으로 리소스를 거부:

rules:
  - name: deny-latest-tag
    match:
      any:
        - resources:
            kinds:
              - Pod
    validate:
      message: "Using 'latest' tag is not allowed. Use a specific version tag."
      deny:
        conditions:
          any:
            - key: '{{ request.object.spec.containers[].image }}'
              operator: AnyIn
              value:
                - '*:latest'
                - '*:*'

1.3 CEL 표현식

Kubernetes 1.25+ CEL(Common Expression Language) 사용:

rules:
  - name: check-replica-count
    match:
      any:
        - resources:
            kinds:
              - Deployment
    validate:
      cel:
        expressions:
          - expression: 'object.spec.replicas >= 2'
            message: 'Deployment must have at least 2 replicas'
          - expression: 'object.spec.replicas <= 100'
            message: 'Deployment cannot exceed 100 replicas'

1.4 foreach

컬렉션의 각 요소에 대해 검증:

rules:
  - name: check-each-container
    match:
      any:
        - resources:
            kinds:
              - Pod
    validate:
      message: 'All containers must have resource limits'
      foreach:
        - list: 'request.object.spec.containers'
          deny:
            conditions:
              any:
                - key: '{{ element.resources.limits.memory }}'
                  operator: Equals
                  value: ''

2. Mutate 규칙 상세

2.1 patchStrategicMerge

Kubernetes Strategic Merge Patch 방식:

rules:
  - name: add-sidecar
    match:
      any:
        - resources:
            kinds:
              - Deployment
            selector:
              matchLabels:
                inject-sidecar: 'true'
    mutate:
      patchStrategicMerge:
        spec:
          template:
            spec:
              containers:
                - name: log-collector
                  image: fluentbit:latest
                  volumeMounts:
                    - name: shared-logs
                      mountPath: /var/log/app
              volumes:
                - name: shared-logs
                  emptyDir: {}

2.2 patchesJson6902

JSON Patch (RFC 6902) 방식:

rules:
  - name: add-annotation
    match:
      any:
        - resources:
            kinds:
              - Service
    mutate:
      patchesJson6902: |-
        - op: add
          path: /metadata/annotations/modified-by
          value: kyverno
        - op: replace
          path: /spec/type
          value: ClusterIP

2.3 foreach mutate

rules:
  - name: add-pull-secret-to-all-containers
    match:
      any:
        - resources:
            kinds:
              - Pod
    mutate:
      foreach:
        - list: 'request.object.spec.containers'
          patchStrategicMerge:
            spec:
              imagePullSecrets:
                - name: my-registry-secret

3. Generate 규칙 상세

3.1 data 기반 생성

정책에 정의된 데이터로 리소스 생성:

rules:
  - name: generate-default-limitrange
    match:
      any:
        - resources:
            kinds:
              - Namespace
    generate:
      apiVersion: v1
      kind: LimitRange
      name: default-limits
      namespace: '{{ request.object.metadata.name }}'
      synchronize: true
      data:
        spec:
          limits:
            - default:
                cpu: 500m
                memory: 512Mi
              defaultRequest:
                cpu: 100m
                memory: 128Mi
              type: Container

3.2 clone 기반 생성

기존 리소스를 복제:

rules:
  - name: clone-configmap
    match:
      any:
        - resources:
            kinds:
              - Namespace
    generate:
      apiVersion: v1
      kind: ConfigMap
      name: shared-config
      namespace: '{{ request.object.metadata.name }}'
      synchronize: true
      clone:
        namespace: default
        name: template-configmap

3.3 synchronize 옵션

synchronize: true일 때:

  • 소스 리소스 변경 시 생성된 리소스도 자동 업데이트
  • Background Controller가 동기화 담당
  • 생성된 리소스의 수동 수정은 자동으로 복원됨

4. 변수와 컨텍스트

4.1 JMESPath 변수

rules:
  - name: add-ns-label
    match:
      any:
        - resources:
            kinds:
              - Deployment
    mutate:
      patchStrategicMerge:
        metadata:
          labels:
            namespace: '{{ request.object.metadata.namespace }}'
            owner: '{{ request.userInfo.username }}'

4.2 API 호출 컨텍스트

rules:
  - name: check-namespace-labels
    match:
      any:
        - resources:
            kinds:
              - Pod
    context:
      - name: namespaceInfo
        apiCall:
          urlPath: '/api/v1/namespaces/{{ request.namespace }}'
          jmesPath: "metadata.labels.environment || 'unknown'"
    validate:
      message: 'Pods can only run in labeled namespaces'
      deny:
        conditions:
          any:
            - key: '{{ namespaceInfo }}'
              operator: Equals
              value: 'unknown'

4.3 ConfigMap 룩업

rules:
  - name: check-allowed-registries
    match:
      any:
        - resources:
            kinds:
              - Pod
    context:
      - name: allowedRegistries
        configMap:
          name: allowed-registries
          namespace: kyverno
    validate:
      message: 'Image must be from an allowed registry'
      foreach:
        - list: 'request.object.spec.containers'
          deny:
            conditions:
              all:
                - key: '{{ element.image }}'
                  operator: AnyNotIn
                  value: '{{ allowedRegistries.data.registries }}'

5. 고급 패턴

5.1 조건부 앵커(Conditional Anchors)

# () 앵커: 조건부 - 필드가 존재하면 검증
validate:
  pattern:
    spec:
      template:
        spec:
          containers:
            - (image): "*/nginx:*"  # nginx 이미지인 경우에만
              resources:
                limits:
                  memory: ">=256Mi"

# X() 부정 앵커: 필드가 존재하지 않아야 함
validate:
  pattern:
    spec:
      template:
        spec:
          containers:
            - name: "*"
              X(securityContext):
                X(privileged): true  # privileged가 true이면 안 됨

5.2 전역 앵커

# =() 동등 앵커: 값이 같아야 함
validate:
  pattern:
    spec:
      =(replicas): '>=3' # replicas가 설정되어 있으면 3 이상

6. 정리

Kyverno 정책 엔진의 핵심:

  1. validate: 패턴 매칭, deny 조건, CEL 표현식, foreach로 다양한 검증
  2. mutate: Strategic Merge Patch, JSON Patch로 리소스 자동 수정
  3. generate: data/clone 기반 리소스 자동 생성, synchronize로 동기화
  4. 변수 시스템: JMESPath, API 호출, ConfigMap 룩업으로 동적 정책
  5. 앵커 시스템: 조건부, 부정, 동등 앵커로 세밀한 패턴 매칭

다음 글에서는 Kyverno의 이미지 검증 기능과 공급망 보안을 다룹니다.

Kyverno Policy Engine Analysis: Validate, Mutate, Generate Rules Deep Dive


1. Validate Rules

1.1 Pattern Matching

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: require-run-as-non-root
spec:
  validationFailureAction: Enforce
  rules:
    - name: check-security-context
      match:
        any:
          - resources:
              kinds:
                - Pod
      validate:
        message: 'Containers must run as non-root'
        pattern:
          spec:
            containers:
              - securityContext:
                  runAsNonRoot: true

Operators: ?* (non-empty), * (any), X|Y (or), !X (not), >X, <X (numeric comparison).

1.2 deny Rules

rules:
  - name: deny-latest-tag
    validate:
      deny:
        conditions:
          any:
            - key: '{{ request.object.spec.containers[].image }}'
              operator: AnyIn
              value: ['*:latest']

1.3 CEL Expressions

rules:
  - name: check-replica-count
    validate:
      cel:
        expressions:
          - expression: 'object.spec.replicas >= 2'
            message: 'Deployment must have at least 2 replicas'

1.4 foreach

rules:
  - name: check-each-container
    validate:
      foreach:
        - list: 'request.object.spec.containers'
          deny:
            conditions:
              any:
                - key: '{{ element.resources.limits.memory }}'
                  operator: Equals
                  value: ''

2. Mutate Rules

2.1 patchStrategicMerge

rules:
  - name: add-sidecar
    mutate:
      patchStrategicMerge:
        spec:
          template:
            spec:
              containers:
                - name: log-collector
                  image: fluentbit:latest

2.2 patchesJson6902

rules:
  - name: add-annotation
    mutate:
      patchesJson6902: |-
        - op: add
          path: /metadata/annotations/modified-by
          value: kyverno

3. Generate Rules

3.1 data-based Generation

rules:
  - name: generate-default-limitrange
    match:
      any:
        - resources:
            kinds:
              - Namespace
    generate:
      apiVersion: v1
      kind: LimitRange
      name: default-limits
      namespace: '{{ request.object.metadata.name }}'
      synchronize: true
      data:
        spec:
          limits:
            - default:
                cpu: 500m
                memory: 512Mi
              type: Container

3.2 clone-based Generation

rules:
  - name: clone-configmap
    generate:
      apiVersion: v1
      kind: ConfigMap
      name: shared-config
      namespace: '{{ request.object.metadata.name }}'
      synchronize: true
      clone:
        namespace: default
        name: template-configmap

4. Variables and Context

4.1 JMESPath, API Calls, ConfigMap Lookups

rules:
  - name: check-namespace-labels
    context:
      - name: namespaceInfo
        apiCall:
          urlPath: '/api/v1/namespaces/{{ request.namespace }}'
      - name: allowedRegistries
        configMap:
          name: allowed-registries
          namespace: kyverno

5. Summary

  1. validate: Pattern matching, deny conditions, CEL expressions, foreach
  2. mutate: Strategic Merge Patch, JSON Patch for automatic resource modification
  3. generate: data/clone-based auto-generation with synchronize
  4. Variable system: JMESPath, API calls, ConfigMap lookups for dynamic policies
  5. Anchor system: Conditional, negation, equality anchors for fine-grained matching