
Kyverno 是來自 Nirmata 的開源項目,后來捐贈給了 CNCF。Kyverno 是一個具有驗證和變異能力的 Kubernetes 策略引擎,但是它還有生成資源的功能,還加入了 API 對象查詢的能力。Kyverno 原本就是為 Kubernetes 編寫的,除了對象生成功能之外,還無需專用語言即可編寫策略。
同樣 Kyverno 在 Kubernetes 集群中也是作為動態準入控制器運行的。Kyverno 從 kube-apiserver 接收驗證和修改準入 webhook HTTP 回調,并應用匹配策略返回執行準入策略或拒絕請求的結果。Kyverno 策略可以使用資源 Kind、name 和標簽選擇器匹配資源,而且名稱中支持通配符。
策略執行是通過 Kubernetes events 來捕獲的,Kyverno 還報告現有資源的策略違規行為。下圖顯示了 Kyverno 的整體架構:

Kyverno架構
Kyverno 的高可用安裝可以通過運行多個副本來完成,并且 Kyverno 的每個副本將具有多個執行不同功能的控制器。Webhook 處理來自 Kubernetes APIServer 的 AdmissionReview 請求,其 Monitor 組件創建和管理所需的配置。PolicyController watch 策略資源并根據配置的掃描間隔啟動后臺掃描,GenerateController 管理生成資源的生命周期。
安裝
首先需要保證你的 Kubernetes 集群版本必須高于 v1.14,要安裝的版本也和 Kubernetes 版本有關系。

兼容版本
我們這里已經是 v1.26.x 版本了,所以選擇安裝最新的 1.9.2 版本即可。
你可以選擇直接從最新版本的資源清單安裝 Kyverno,直接執行下面的命令即可:
? kubectl create -f https://github.com/kyverno/kyverno/releases/download/v1.9.2/install.yaml
此外同樣可以使用 Helm 來進行一鍵安裝:
? helm repo add kyverno https://kyverno.github.io/kyverno/
? helm repo update
# Install the Kyverno Helm chart into a new namespace called "kube-kyverno"
? helm upgrade --install kyverno kyverno/kyverno -n kube-kyverno --create-namespace
Release "kyverno" does not exist. Installing it now.
NAME: kyverno
LAST DEPLOYED: Tue Apr 11 15:51:30 2023
NAMESPACE: kube-kyverno
STATUS: deployed
REVISION: 1
NOTES:
Chart version: 2.7.2
Kyverno version: v1.9.2
Thank you for installing kyverno! Your release is named kyverno.
?? WARNING: Setting replicas count below 3 means Kyverno is not running in high availability mode.
?? Note: There is a trade-off when deciding which approach to take regarding Namespace exclusions. Please see the documentation at https://kyverno.io/docs/installation/#security-vs-operability to understand the risks.
安裝完成會創建一個 kube-kyverno 命名空間,同樣也包含一些相關的 CRD:
? kubectl get pods -n kube-kyverno
NAME READY STATUS RESTARTS AGE
kyverno-8657b8cfcf-mgtsr 1/1 Running 0 2m25s
kyverno-cleanup-controller-5c964d77dc-5s5zp 1/1 Running 0 2m25s
? kubectl get validatingwebhookconfiguration
NAME WEBHOOKS AGE
kyverno-cleanup-validating-webhook-cfg 1 44m
kyverno-exception-validating-webhook-cfg 1 16m
kyverno-policy-validating-webhook-cfg 1 16m
kyverno-resource-validating-webhook-cfg 0 16m
? kubectl get mutatingwebhookconfigurations
NAME WEBHOOKS AGE
kyverno-policy-mutating-webhook-cfg 1 17m
kyverno-resource-mutating-webhook-cfg 0 17m
kyverno-verify-mutating-webhook-cfg 1 17m
? kubectl get crd |grep kyverno
admissionreports.kyverno.io 2023-04-11T07:51:33Z
backgroundscanreports.kyverno.io 2023-04-11T07:51:33Z
cleanuppolicies.kyverno.io 2023-04-11T07:51:33Z
clusteradmissionreports.kyverno.io 2023-04-11T07:51:33Z
clusterbackgroundscanreports.kyverno.io 2023-04-11T07:51:33Z
clustercleanuppolicies.kyverno.io 2023-04-11T07:51:33Z
clusterpolicies.kyverno.io 2023-04-11T07:51:34Z
generaterequests.kyverno.io 2023-04-11T07:51:33Z
policies.kyverno.io 2023-04-11T07:51:34Z
policyexceptions.kyverno.io 2023-04-11T07:51:33Z
updaterequests.kyverno.io 2023-04-11T07:51:33Z
可以看出安裝完成后創建了幾個 validatingwebhookconfiguration 與 mutatingwebhookconfigurations 對象。
策略與規則
使用 Kyverno 其實就是對策略和規則的應用,Kyverno 策略是規則的集合,每個規則都包含一個 match 聲明、一個可選的 exclude 聲明以及 validate、mutate、generate 或 verifyImages 聲明之一組成,每個規則只能包含一個 validate、mutate、generate 或 verifyImages 子聲明。

Kyverno策略
策略可以定義為集群范圍的資源(ClusterPolicy)或命名空間級別資源(Policy)。
- Policy 將僅適用于定義它們的 namespace 內的資源。
- ClusterPolicy 應用于匹配跨所有 namespace 的資源。
策略定義
編寫策略其實就是定義 Policy 或者 ClusterPolicy 對象。
驗證資源
驗證規則基本上是我們使用最常見和最實用的規則類型,當用戶或進程創建新資源時,Kyverno 將根據驗證規則檢查該資源的屬性,如果驗證通過,則允許創建資源。如果驗證失敗,則創建被阻止。比如現在我們添加一個策略,要求所有的 pod 都包含一個 kyverno 的標簽:
# kyverno-require-label.yaml
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-label-policy
spec:
validationFailureAction: Enforce
rules:
- name: check-for-labels
match:
resources:
kinds:
- Pod
validate:
message: "label 'kyverno' is required"
pattern:
metadata:
labels:
kyverno: "?*"
上面策略文件中添加了一個 validatinotallow=[Audit, Enforce] 屬性:
- 當處于 Audit 模式下,每當創建違反規則集的一個或多個規則的資源時,會允許 admission review 請求,并將結果添加到報告中。
- 當處于 Enforce 模式下,資源在創建時立即被阻止,報告中不會有。
然后就是下面使用 rules 屬性定義的規則集合,match 用于表示匹配的資源資源,validate 表示驗證方式,這里我們定義 kyverno: "?*" 這樣的標簽表示必須有這樣的一個標簽 key。
直接應用上面的策略對象即可:
? kubectl apply -f kyverno-require-label.yaml
clusterpolicy.kyverno.io/require-label-policy created
? kubectl get clusterpolicy
NAME BACKGROUND VALIDATE ACTION READY AGE
require-label-policy true Enforce true 4m23s
現在我們添加一個不帶標簽 kyverno 的 Pod:
? kubectl run busybox --image=busybox:1.28.3 --restart=Never -- sleep 1000000
Error from server: admission webhook "validate.kyverno.svc-fail" denied the request:
policy Pod/default/busybox for resource violation:
require-label-policy:
check-for-labels: 'validation error: label ''kyverno'' is required. rule check-for-labels
failed at path /metadata/labels/kyverno/'
可以看到提示,需要一個 kyverno 標簽,同樣我們也可以通過查看 Events 事件來了解策略應用情況:
? kubectl get events -A -w
......
for-labels fail: validation error: label 'kyverno' is required. rule autogen-check-for-labels failed at path /spec/template/metadata/labels/kyverno/
qdrant-system 51s Warning PolicyViolation pod/qdrant-0 policy require-label-policy/check-for-labels fail: validation error: label 'kyverno' is required. rule check-for-labels failed at path /metadata/labels/kyverno/
qdrant-system 50s Warning PolicyViolation statefulset/qdrant policy require-label-policy/autogen-check-for-labels fail: validation error: label 'kyverno' is required. rule autogen-check-for-labels failed at path /spec/template/metadata/labels/kyverno/
如果創建的 Pod 帶有 kyverno 標簽則可以正常創建:
? kubectl run busybox --image=busybox:1.28.3 --labels kyverno=demo --restart=Never -- sleep 1000000
pod/busybox created
如果將 validationFailureAction 的值更改為 Audit,則即使我們創建的 Pod 不帶有 kyverno 標簽,也可以創建成功,但是我們可以在 PolicyReport 對象中看到對應的違規報告:
? kubectl get policyreports
NAME PASS FAIL WARN ERROR SKIP AGE
cpol-require-label-policy 0 1 0 0 0 4m42s
? kubectl describe policyreports |grep "Result: \+fail" -B10
UID: def28081-aa68-4e96-bb43-fdc73274df00
Results:
Message: validation error: label 'kyverno' is required. rule check-for-labels failed at path /metadata/labels/kyverno/
Policy: require-label-policy
Resources:
API Version: v1
Kind: Pod
Name: busybox
Namespace: default
UID: 9667e83d-62a3-4844-b5d7-da127e9cee2c
Result: fail
從上面的報告資源中可以看到違反策略的資源對象。
變更規則
變更規則可以用于修改匹配到規則的資源(比如規則設置了 metadata 字段可以和資源的 metadata 進行合并),就是根據我們設置的規則來修改對應的資源。
比如現在我們添加如下所示一個策略,給所有包含 nginx 鏡像的 pod 都加上一個標簽(kyverno=nginx):
# kyverno-mutate-label.yaml
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: nginx-label-policy
spec:
rules:
- name: nginx-label
match:
resources:
kinds:
- Pod
mutate:
patchStrategicMerge:
metadata:
labels:
kyverno: nginx
spec:
(containers):
- (image): "*nginx*" # 容器鏡像包含 nginx 即可
直接應用上面這個策略對象即可:
? kubectl apply -f kyverno-mutate-label.yaml
clusterpolicy.kyverno.io/nginx-label-policy created
? kubectl get clusterpolicy
NAME BACKGROUND VALIDATE ACTION READY AGE
nginx-label-policy true Audit true 6s
現在我們使用 nginx 鏡像直接創建一個 Pod:
? kubectl run --image=nginx:1.7.9 nginx
pod/nginx created
? kubectl get pod nginx --show-labels
NAME READY STATUS RESTARTS AGE LABELS
nginx 1/1 Running 0 11s kyverno=nginx,run=nginx
可以看到 Pod 創建成功后包含了一個 kyverno=nginx 標簽,由于有 kyverno 標簽,所以上面的驗證策略也是通過的,可以正常創建。
生成資源
生成規則可用于在創建新資源或更新源時創建其他資源,例如為命名空間創建新 RoleBindings 或 Secret 等。
比如現在我們一個需求是將某個 Secret 同步到其他命名空間中去(比如 TLS 密鑰、鏡像倉庫認證信息),手動復制這些 Secret 比較麻煩,則我們可以使用 Kyverno 來創建一個策略幫助我們同步這些 Secret。比如在 default 命名空間中有一個名為 regcred 的 Secret 對象,需要復制到另外的命名空間,如果源 Secret 發生更改,它還將向復制的 Secret 同步更新。
# kyverno-generate-secret.yaml
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: sync-secrets-policy
spec:
rules:
- name: sync-image-pull-secret
match:
resources:
kinds:
- Namespace
generate: # 生成的資源對象
kind: Secret
name: regcred
namespace: "{{request.object.metadata.name}}" # 獲取目標命名空間
synchronize: true
clone:
namespace: default
name: regcred
先在 default 命名空間中準備我們的 Secret 對象:
? kubectl create secret docker-registry regcred --docker-server=DOCKER_REGISTRY_SERVER --docker-username=DOCKER_USER --docker-password=DOCKER_PASSWORD --docker-email=DOCKER_EMAIL
secret/regcred created
然后應用上面的同步 Secret 策略:
? kubectl apply -f kyverno-generate-secret.yaml
clusterpolicy.kyverno.io/sync-secrets-policy created
? kubectl get clusterpolicy
NAME BACKGROUND ACTION READY
sync-secrets-policy true Audit true 9s
現在我們創建一個新的命名空間:
? kubectl create ns test
namespace/test created
? kubectl get secret -n test
NAME TYPE DATA AGE
regcred kubernetes.io/dockerconfigjson 1 6s
可以看到在新建的命名空間中多了一個 regcred 的 Secret 對象。
更多的 Kyverno 策略可以直接查看官方網站:https://kyverno.io/policies,可以在該網站上面根據策略類型、分類、主題等進行篩選。Kyverno 在靈活、強大和易用之間取得了一個很好的平衡,不需要太多學習時間,就能夠提供相當方便的功能,官網提供了大量的針對各種場景的樣例,非常值得使用。
比如我們可以創建一個如下所示的策略限制 NGINX Ingress 的路徑值(CVE-2021-25745 安全問題,在 NGINX Ingress v1.2.0 中修復):
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: restrict-ingress-paths
annotations:
policies.kyverno.io/title: Restrict NGINX Ingress path values
policies.kyverno.io/category: Security, NGINX Ingress
policies.kyverno.io/severity: high
policies.kyverno.io/subject: Ingress
policies.kyverno.io/minversion: "1.6.0"
kyverno.io/kyverno-version: "1.6.0"
kyverno.io/kubernetes-version: "1.23"
policies.kyverno.io/description: >-
This policy mitigates CVE-2021-25745 by restricting `spec.rules[].http.paths[].path` to safe values.
Additional paths can be added as required. This issue has been fixed in NGINX Ingress v1.2.0.
Please refer to the CVE for details.
spec:
validationFailureAction: enforce
rules:
- name: check-paths
match:
any:
- resources:
kinds:
- networking.k8s.io/v1/Ingress
validate:
message: "spec.rules[].http.paths[].path value is not allowed"
deny:
conditions:
any:
- key: "{{ request.object.spec.rules[].http.paths[].path.contains(@,'/etc') }}"
operator: AnyIn
value: [true]
- key: "{{ request.object.spec.rules[].http.paths[].path.contains(@,'/var/run/secrets') }}"
operator: AnyIn
value: [true]
- key: "{{ request.object.spec.rules[].http.paths[].path.contains(@,'/root') }}"
operator: AnyIn
value: [true]
- key: "{{ request.object.spec.rules[].http.paths[].path.contains(@,'/var/run/kubernetes/serviceaccount') }}"
operator: AnyIn
value: [true]
- key: "{{ request.object.spec.rules[].http.paths[].path.contains(@,'/etc/kubernetes/admin.conf') }}"
operator: AnyIn
value: [true]