Kubernetes Kyverno 政策引擎

Kubernetes Kyverno Policy Engine

Kyverno 概述與架構

Kyverno 是一個專為 Kubernetes 設計的政策引擎,名稱源自希臘語「治理」之意。它作為 Kubernetes 的動態准入控制器(Dynamic Admission Controller),能夠驗證、變更和生成 Kubernetes 資源配置,確保叢集符合組織的安全性與合規性要求。

為什麼選擇 Kyverno?

  • 原生 Kubernetes 體驗:使用 YAML 定義政策,無需學習新的程式語言
  • 無需外部依賴:不像 OPA Gatekeeper 需要學習 Rego 語言
  • 豐富的功能:支援驗證、變更、生成資源等多種操作
  • 易於維護:政策以 Kubernetes CRD 形式存在,可使用 kubectl 管理
  • 完整的報告機制:提供政策執行結果的詳細報告

Kyverno 架構

Kyverno 主要由以下元件組成:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
┌─────────────────────────────────────────────────────────────┐
│                    Kubernetes API Server                     │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│                   Admission Webhook                          │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────────────┐  │
│  │  Validate   │  │   Mutate    │  │      Generate       │  │
│  │  Handler    │  │   Handler   │  │      Handler        │  │
│  └─────────────┘  └─────────────┘  └─────────────────────┘  │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│              Policy Engine (ClusterPolicy / Policy)          │
└─────────────────────────────────────────────────────────────┘
  • Webhook Server:接收來自 API Server 的 Admission Review 請求
  • Policy Controller:監控 Policy 和 ClusterPolicy 資源的變更
  • Generate Controller:處理資源生成政策
  • Report Controller:生成政策執行報告

安裝與設定

使用 Helm 安裝

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# 新增 Kyverno Helm Repository
helm repo add kyverno https://kyverno.github.io/kyverno/
helm repo update

# 建立命名空間
kubectl create namespace kyverno

# 安裝 Kyverno
helm install kyverno kyverno/kyverno -n kyverno

# 安裝 Kyverno Policies(選擇性)
helm install kyverno-policies kyverno/kyverno-policies -n kyverno

使用 kubectl 安裝

1
2
# 安裝最新版本
kubectl create -f https://github.com/kyverno/kyverno/releases/latest/download/install.yaml

驗證安裝

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# 檢查 Kyverno Pod 狀態
kubectl get pods -n kyverno

# 預期輸出
NAME                                             READY   STATUS    RESTARTS   AGE
kyverno-admission-controller-xxxxx-xxxxx         1/1     Running   0          2m
kyverno-background-controller-xxxxx-xxxxx        1/1     Running   0          2m
kyverno-cleanup-controller-xxxxx-xxxxx           1/1     Running   0          2m
kyverno-reports-controller-xxxxx-xxxxx           1/1     Running   0          2m

# 檢查 CRD 是否已安裝
kubectl get crd | grep kyverno

Helm 進階設定

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
# kyverno-values.yaml
admissionController:
  replicas: 3
  resources:
    limits:
      memory: 384Mi
    requests:
      cpu: 100m
      memory: 128Mi

backgroundController:
  replicas: 2

config:
  webhooks:
    - namespaceSelector:
        matchExpressions:
          - key: kubernetes.io/metadata.name
            operator: NotIn
            values:
              - kube-system
              - kyverno
1
helm install kyverno kyverno/kyverno -n kyverno -f kyverno-values.yaml

Validation 驗證政策

驗證政策用於檢查資源配置是否符合規範,不符合的請求將被拒絕。

基本驗證政策範例

要求所有 Pod 必須有資源限制

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: require-resource-limits
spec:
  validationFailureAction: Enforce
  background: true
  rules:
    - name: validate-resources
      match:
        any:
          - resources:
              kinds:
                - Pod
      validate:
        message: "CPU and memory resource limits are required."
        pattern:
          spec:
            containers:
              - resources:
                  limits:
                    memory: "?*"
                    cpu: "?*"

禁止使用 latest 標籤

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: disallow-latest-tag
spec:
  validationFailureAction: Enforce
  background: true
  rules:
    - name: validate-image-tag
      match:
        any:
          - resources:
              kinds:
                - Pod
      validate:
        message: "Using 'latest' tag is not allowed. Please specify a specific version."
        pattern:
          spec:
            containers:
              - image: "!*:latest"

要求特定標籤

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: require-labels
spec:
  validationFailureAction: Enforce
  background: true
  rules:
    - name: check-for-labels
      match:
        any:
          - resources:
              kinds:
                - Pod
                - Deployment
                - Service
      validate:
        message: "The labels 'app' and 'env' are required."
        pattern:
          metadata:
            labels:
              app: "?*"
              env: "?*"

使用 deny 規則

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: deny-privileged-containers
spec:
  validationFailureAction: Enforce
  background: true
  rules:
    - name: deny-privileged
      match:
        any:
          - resources:
              kinds:
                - Pod
      validate:
        message: "Privileged containers are not allowed."
        deny:
          conditions:
            any:
              - key: "{{ request.object.spec.containers[*].securityContext.privileged }}"
                operator: AnyIn
                value: [true]

validationFailureAction 設定

  • Enforce:拒絕不符合政策的資源建立/更新請求
  • Audit:允許請求但記錄違規事件,適合初期測試
1
2
3
4
5
6
7
8
9
spec:
  validationFailureAction: Audit  # 或 Enforce
  validationFailureActionOverrides:
    - action: Enforce
      namespaces:
        - production
    - action: Audit
      namespaces:
        - development

Mutation 變更政策

變更政策用於自動修改資源配置,確保資源符合組織標準。

自動新增標籤

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: add-default-labels
spec:
  rules:
    - name: add-labels
      match:
        any:
          - resources:
              kinds:
                - Pod
                - Deployment
      mutate:
        patchStrategicMerge:
          metadata:
            labels:
              managed-by: kyverno
              environment: "{{ request.namespace }}"

自動設定資源限制

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: add-default-resources
spec:
  rules:
    - name: add-resources
      match:
        any:
          - resources:
              kinds:
                - Pod
      mutate:
        patchStrategicMerge:
          spec:
            containers:
              - (name): "*"
                resources:
                  limits:
                    memory: "256Mi"
                    cpu: "200m"
                  requests:
                    memory: "128Mi"
                    cpu: "100m"

自動新增 Sidecar 容器

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: inject-sidecar
spec:
  rules:
    - name: inject-logging-sidecar
      match:
        any:
          - resources:
              kinds:
                - Deployment
      preconditions:
        all:
          - key: "{{ request.object.metadata.labels.inject-sidecar || '' }}"
            operator: Equals
            value: "true"
      mutate:
        patchStrategicMerge:
          spec:
            template:
              spec:
                containers:
                  - name: logging-sidecar
                    image: fluent/fluent-bit:latest
                    resources:
                      limits:
                        memory: "64Mi"
                        cpu: "50m"

使用 JSON Patch

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: add-tolerations
spec:
  rules:
    - name: add-toleration
      match:
        any:
          - resources:
              kinds:
                - Pod
      mutate:
        patchesJson6902: |-
          - op: add
            path: /spec/tolerations/-
            value:
              key: "dedicated"
              operator: "Equal"
              value: "app"
              effect: "NoSchedule"          

Generation 生成政策

生成政策用於自動建立相關資源,例如自動為新命名空間建立 NetworkPolicy 或 ResourceQuota。

自動生成 NetworkPolicy

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: generate-default-networkpolicy
spec:
  rules:
    - name: generate-networkpolicy
      match:
        any:
          - resources:
              kinds:
                - Namespace
      exclude:
        any:
          - resources:
              namespaces:
                - kube-system
                - kyverno
      generate:
        synchronize: true
        apiVersion: networking.k8s.io/v1
        kind: NetworkPolicy
        name: default-deny-ingress
        namespace: "{{ request.object.metadata.name }}"
        data:
          spec:
            podSelector: {}
            policyTypes:
              - Ingress

自動生成 ResourceQuota

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: generate-resourcequota
spec:
  rules:
    - name: generate-quota
      match:
        any:
          - resources:
              kinds:
                - Namespace
      exclude:
        any:
          - resources:
              namespaces:
                - kube-system
                - kyverno
      generate:
        synchronize: true
        apiVersion: v1
        kind: ResourceQuota
        name: default-quota
        namespace: "{{ request.object.metadata.name }}"
        data:
          spec:
            hard:
              requests.cpu: "4"
              requests.memory: "8Gi"
              limits.cpu: "8"
              limits.memory: "16Gi"
              pods: "20"

自動複製 Secret

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: sync-registry-secret
spec:
  rules:
    - name: sync-secret
      match:
        any:
          - resources:
              kinds:
                - Namespace
      generate:
        synchronize: true
        apiVersion: v1
        kind: Secret
        name: registry-credentials
        namespace: "{{ request.object.metadata.name }}"
        clone:
          namespace: default
          name: registry-credentials

生成政策的 synchronize 選項

  • true:來源資源變更時,生成的資源也會同步更新
  • false:僅在初始建立時生成,後續不再同步

ClusterPolicy 與 Policy

Kyverno 提供兩種政策類型:

ClusterPolicy

適用於整個叢集的政策,可以套用至所有命名空間。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: global-policy
spec:
  rules:
    - name: rule-1
      match:
        any:
          - resources:
              kinds:
                - Pod
      # 政策規則...

Policy

僅適用於特定命名空間的政策。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
apiVersion: kyverno.io/v1
kind: Policy
metadata:
  name: namespace-policy
  namespace: production
spec:
  rules:
    - name: rule-1
      match:
        any:
          - resources:
              kinds:
                - Pod
      # 政策規則...

選擇使用時機

類型適用場景
ClusterPolicy全叢集安全基準、合規要求、標準化配置
Policy特定命名空間的額外限制、專案特定需求

使用 namespaceSelector 限制範圍

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: require-labels-production
spec:
  rules:
    - name: require-labels
      match:
        any:
          - resources:
              kinds:
                - Pod
              namespaceSelector:
                matchLabels:
                  env: production
      validate:
        message: "Production pods must have owner label."
        pattern:
          metadata:
            labels:
              owner: "?*"

政策例外處理

有時需要為特定資源或命名空間豁免政策檢查。

使用 exclude 排除

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: disallow-privileged
spec:
  validationFailureAction: Enforce
  rules:
    - name: deny-privileged
      match:
        any:
          - resources:
              kinds:
                - Pod
      exclude:
        any:
          - resources:
              namespaces:
                - kube-system
          - resources:
              selector:
                matchLabels:
                  app: special-app
          - subjects:
              - kind: ServiceAccount
                name: admin-sa
                namespace: admin
      validate:
        message: "Privileged containers are not allowed."
        pattern:
          spec:
            containers:
              - securityContext:
                  privileged: "!true"

PolicyException 資源

從 Kyverno 1.9 開始,可以使用 PolicyException 資源定義例外。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
apiVersion: kyverno.io/v2beta1
kind: PolicyException
metadata:
  name: allow-privileged-monitoring
  namespace: monitoring
spec:
  exceptions:
    - policyName: disallow-privileged
      ruleNames:
        - deny-privileged
  match:
    any:
      - resources:
          kinds:
            - Pod
          namespaces:
            - monitoring
          names:
            - prometheus-*
            - node-exporter-*

啟用 PolicyException 功能

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# Helm values
config:
  enablePolicyException: true

# 或透過 ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: kyverno
  namespace: kyverno
data:
  enablePolicyException: "true"

使用 Annotations 豁免

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
apiVersion: v1
kind: Pod
metadata:
  name: special-pod
  annotations:
    policies.kyverno.io/last-applied-patches: |
      add-default-resources.add-resources.kyverno.io: skipped      
spec:
  containers:
    - name: app
      image: nginx

與 OPA Gatekeeper 比較

Kyverno 和 OPA Gatekeeper 都是 Kubernetes 政策引擎,以下是兩者的比較:

政策語言

面向KyvernoOPA Gatekeeper
政策語言YAML(原生 Kubernetes)Rego(專用語言)
學習曲線
靈活性中等
可讀性需要學習

Kyverno 範例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: require-labels
spec:
  rules:
    - name: check-labels
      match:
        any:
          - resources:
              kinds:
                - Pod
      validate:
        message: "Label 'app' is required."
        pattern:
          metadata:
            labels:
              app: "?*"

OPA Gatekeeper 範例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# ConstraintTemplate
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
  name: k8srequiredlabels
spec:
  crd:
    spec:
      names:
        kind: K8sRequiredLabels
      validation:
        openAPIV3Schema:
          type: object
          properties:
            labels:
              type: array
              items:
                type: string
  targets:
    - target: admission.k8s.gatekeeper.sh
      rego: |
        package k8srequiredlabels

        violation[{"msg": msg}] {
          provided := {label | input.review.object.metadata.labels[label]}
          required := {label | label := input.parameters.labels[_]}
          missing := required - provided
          count(missing) > 0
          msg := sprintf("Missing required labels: %v", [missing])
        }        
---
# Constraint
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sRequiredLabels
metadata:
  name: require-app-label
spec:
  match:
    kinds:
      - apiGroups: [""]
        kinds: ["Pod"]
  parameters:
    labels:
      - "app"

功能比較

功能KyvernoOPA Gatekeeper
驗證政策支援支援
變更政策支援需要額外設定
生成政策支援不支援
映像驗證支援(Sigstore/Cosign)需要整合
報告機制內建 PolicyReport需要額外元件
API 呼叫支援不支援
社群支援CNCF IncubatingCNCF Graduated

選擇建議

選擇 Kyverno 當:

  • 團隊熟悉 YAML,希望快速上手
  • 需要變更(Mutation)和生成(Generation)功能
  • 需要映像簽章驗證
  • 希望減少學習成本

選擇 OPA Gatekeeper 當:

  • 已有 OPA/Rego 經驗
  • 需要非常複雜的政策邏輯
  • 希望在 Kubernetes 以外的系統使用相同的政策語言
  • 需要更成熟的生態系統(CNCF Graduated)

進階功能

映像簽章驗證

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: verify-image-signature
spec:
  validationFailureAction: Enforce
  webhookTimeoutSeconds: 30
  rules:
    - name: verify-signature
      match:
        any:
          - resources:
              kinds:
                - Pod
      verifyImages:
        - imageReferences:
            - "ghcr.io/myorg/*"
          attestors:
            - entries:
                - keys:
                    publicKeys: |-
                      -----BEGIN PUBLIC KEY-----
                      MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE...
                      -----END PUBLIC KEY-----                      

外部資料來源

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: check-external-registry
spec:
  rules:
    - name: check-registry
      match:
        any:
          - resources:
              kinds:
                - Pod
      context:
        - name: allowedRegistries
          configMap:
            name: allowed-registries
            namespace: kyverno
      validate:
        message: "Image must be from an allowed registry."
        deny:
          conditions:
            all:
              - key: "{{ request.object.spec.containers[0].image | split('@')[0] | split(':')[0] }}"
                operator: AnyNotIn
                value: "{{ allowedRegistries.data.registries | split(',') }}"

政策報告

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# 查看政策報告
kubectl get policyreport -A
kubectl get clusterpolicyreport

# 查看報告詳情
kubectl describe policyreport -n default

# 輸出範例
Name:         polr-ns-default
Namespace:    default
Results:
  Category:  Pod Security Standards
  Message:   validation rule 'deny-privileged' passed.
  Policy:    disallow-privileged
  Result:    pass
  Rule:      deny-privileged
  Source:    kyverno

最佳實踐

1. 循序漸進導入

1
2
3
4
5
6
7
# 先使用 Audit 模式測試
spec:
  validationFailureAction: Audit

# 確認無誤後改為 Enforce
spec:
  validationFailureAction: Enforce

2. 使用有意義的訊息

1
2
3
4
5
validate:
  message: |
    Container '{{ request.object.spec.containers[0].name }}' in Pod '{{ request.object.metadata.name }}'
    is using image '{{ request.object.spec.containers[0].image }}' which is not from an allowed registry.
    Allowed registries: gcr.io, docker.io/library    

3. 定期檢視政策報告

1
2
# 建立監控腳本
kubectl get policyreport -A -o json | jq '.items[].results[] | select(.result=="fail")'

4. 版本控制政策

1
2
3
4
# 使用 GitOps 管理政策
git add policies/
git commit -m "Add new security policies"
git push

常用指令

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# 查看所有 ClusterPolicy
kubectl get clusterpolicy

# 查看政策詳情
kubectl describe clusterpolicy <policy-name>

# 測試政策(dry-run)
kubectl apply -f pod.yaml --dry-run=server

# 查看政策報告
kubectl get policyreport -A

# 查看 Kyverno 日誌
kubectl logs -n kyverno -l app.kubernetes.io/component=admission-controller

# 刪除政策
kubectl delete clusterpolicy <policy-name>

參考資料

comments powered by Disqus
Built with Hugo
Theme Stack designed by Jimmy