Aller au contenu
Conteneurs & Orchestration high

Kyverno, Policy Engine Kubernetes en YAML et CEL

23 min de lecture

Logo Kubernetes

Kyverno est un policy engine Kubernetes qui permet de valider, muter, générer des ressources et vérifier les signatures d'images. Projet CNCF Incubating, Kyverno utilise des policies YAML et supporte CEL. Ce guide est utile en complément d'une préparation CKS pour traduire les exigences sécurité en policies concrètes.

Un mot sur le positionnement par rapport à la CKS : le curriculum de la certification ne cite pas Kyverno comme compétence requise. Il mentionne Pod Security Standards, registries autorisées, signature et validation d'artefacts et analyse statique. Kyverno est un outil pratique pour implémenter ces exigences sur un cluster réel, mais ce n'est pas un sujet d'examen en soi.

  • Quand utiliser Kyverno (et quand ne pas l'utiliser)
  • Installer Kyverno et comprendre sa complexité opérationnelle
  • Créer des policies de validation orientées sécurité
  • Vérifier les signatures d'images (Supply Chain Security)
  • Diagnostiquer les violations avec les PolicyReports

Kyverno suit un cycle de versions aligné avec Kubernetes. Chaque version majeure de Kyverno supporte plusieurs versions de Kubernetes, mais les fonctionnalités varient. Voici les versions actuelles.

KyvernoKubernetes officiellement supportéNotes
1.18.x1.33 – 1.35Branche courante, publiée le 29 avril 2026
1.17.xEn fin de support depuis la sortie de 1.18CRD legacy kyverno.io/v1 marquées deprecated

Le support communautaire dure environ trois mois par branche mineure : la sortie de la version n+1 met fin aux correctifs de la version n. Les versions de Kubernetes hors de la plage annoncée peuvent fonctionner, mais elles ne sont pas testées par le projet et aucune garantie n'est donnée.

Chaque outil a son domaine d'excellence. Ce tableau vous aide à choisir en fonction de votre besoin réel, pas de la popularité de l'outil.

BesoinOutil recommandéPourquoi
Validation simple, pas de dépendanceVAPNatif, GA depuis K8s 1.30
Profils sécurité standard (runAsNonRoot...)Pod Security Admission3 profils prêts à l'emploi
Mutation avancée (sidecars, defaults)KyvernoMutation mature et flexible
Génération automatique (NetworkPolicies)KyvernoFonctionnalité unique
Vérification signatures imagesKyvernoIntégration Cosign/Sigstore
Culture OPA/Rego existanteGatekeeperÉcosystème Rego

Règle de décision simple : Si vous avez besoin de mutation ou de génération, Kyverno est très probablement le bon choix. Sinon, commencez par les solutions natives (PSA, VAP).

Trois de ces quatre capacités n'ont aucun équivalent natif dans Kubernetes : l'API server sait refuser une ressource avec une ValidatingAdmissionPolicy, mais il ne sait ni la modifier, ni en créer une autre en réaction, ni interroger un registry pour vérifier une signature. C'est ce qui justifie d'ajouter une dépendance au control plane.

  • Mutation : injecter sidecars, ajouter labels/annotations, defaults
  • Génération : créer automatiquement NetworkPolicies, Secrets, ResourceQuotas
  • Vérification d'images : Cosign, Sigstore, attestations SLSA
  • PolicyReports : audit de conformité natif

Le point commun de ces quatre situations est le rapport bénéfice / dette d'exploitation. Chaque webhook ajouté au cluster est un composant à maintenir, à mettre à jour au rythme de Kubernetes et à surveiller. Si le besoin se couvre avec un mécanisme déjà présent dans l'API server, l'ajout de Kyverno coûte plus qu'il ne rapporte.

  • Validation simple sans mutation → préférez VAP (natif, pas de dépendance)
  • Profils sécurité standards → préférez Pod Security Admission (plus simple)
  • Cluster sans HA → Kyverno est un webhook d'admission, composant sensible
  • Équipe déjà formée à Rego → préférez Gatekeeper

Installer Kyverno revient à insérer un composant entre l'API server et toutes les écritures de ressources du cluster. Cette position explique l'essentiel de ses contraintes d'exploitation : sa disponibilité conditionne celle des déploiements, ses certificats TLS ont une durée de vie limitée, et ses droits RBAC couvrent une large partie du cluster. Avant d'écrire une seule policy, il faut donc décider comment le cluster se comporte quand Kyverno est indisponible.

En production, déployez avec HA :

Fenêtre de terminal
helm install kyverno kyverno/kyverno \
--set admissionController.replicas=3 \
--set backgroundController.replicas=2 \
-n kyverno --create-namespace

Si Kyverno bloque le cluster :

Fenêtre de terminal
# Supprimer les webhooks en urgence
kubectl delete validatingwebhookconfiguration kyverno-resource-validating-webhook-cfg
kubectl delete mutatingwebhookconfiguration kyverno-resource-mutating-webhook-cfg

Deux méthodes coexistent, et le choix n'est pas cosmétique. Le chart Helm expose les réglages de production (nombre de réplicas par contrôleur, ressources, exclusions de namespaces) et permet une mise à jour maîtrisée. Le manifeste install.yaml déploie une configuration figée, pratique pour un cluster jetable mais difficile à personnaliser ensuite. Dans les deux cas, Kyverno doit vivre dans un namespace dédié : le projet interdit explicitement de le cohabiter avec d'autres applications, y compris kube-system.

Fenêtre de terminal
# Ajouter le repo Helm
helm repo add kyverno https://kyverno.github.io/kyverno/
helm repo update
# Installer Kyverno avec HA
helm install kyverno kyverno/kyverno \
-n kyverno --create-namespace \
--set admissionController.replicas=2 \
--wait --timeout 5m

Vérifier l'installation :

Fenêtre de terminal
kubectl get pods -n kyverno
NAME READY STATUS RESTARTS AGE
kyverno-admission-controller-b469db77f-57xcn 1/1 Running 0 39s
kyverno-admission-controller-b469db77f-8k2lp 1/1 Running 0 39s
kyverno-background-controller-6674dc69f5-spbvp 1/1 Running 0 39s
kyverno-cleanup-controller-5bb56f66f4-psp8f 1/1 Running 0 39s
kyverno-reports-controller-647dd56678-56kmg 1/1 Running 0 39s

Cet exemple est directement aligné avec l'objectif CKS « Use appropriate pod security standards ». Les deux onglets produisent le même refus, mais par des chemins très différents : la version CEL évalue une expression booléenne sur l'objet soumis, alors que la version historique compare la ressource à un motif YAML que Kyverno applique récursivement. La version CEL gagne sur la lisibilité des conditions complexes, notamment l'exclusion des namespaces système, qui tient ici en une seule ligne matchConditions.

apiVersion: policies.kyverno.io/v1
kind: ValidatingPolicy
metadata:
name: require-run-as-non-root
spec:
validationActions: [Deny]
matchConstraints:
resourceRules:
- apiGroups: [""]
apiVersions: ["v1"]
operations: ["CREATE", "UPDATE"]
resources: ["pods"]
matchConditions:
- name: exclude-system-ns
expression: >-
!(['kube-system','kyverno'].exists(ns, object.metadata.namespace == ns))
validations:
- expression: >-
object.spec.containers.all(c,
has(c.securityContext) &&
has(c.securityContext.runAsNonRoot) &&
c.securityContext.runAsNonRoot == true
)
message: "Tous les conteneurs doivent avoir runAsNonRoot: true."

Ces trois règles ferment les portes qui donnent au conteneur un accès direct au nœud : un volume hostPath monte un répertoire de l'hôte dans le pod, hostNetwork place le pod dans l'espace réseau du nœud, hostPID et hostIPC lui donnent la visibilité sur les processus et la mémoire partagée de l'hôte. Chaque règle est indépendante et échoue séparément, ce qui rend le diagnostic lisible : Kyverno indique le chemin exact qui a déclenché le refus, par exemple rule deny-hostpath failed at path /spec/volumes/0/hostPath/.

Notez l'ancre =(volumes) : elle rend le contrôle conditionnel. Un pod sans aucun volume passe la règle au lieu d'échouer sur un champ absent.

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: deny-host-access
spec:
background: true
rules:
- name: deny-hostpath
match:
any:
- resources:
kinds:
- Pod
validate:
failureAction: Enforce
message: "Les volumes hostPath sont interdits"
pattern:
spec:
=(volumes):
- X(hostPath): "null"
- name: deny-host-network
match:
any:
- resources:
kinds:
- Pod
validate:
failureAction: Enforce
message: "hostNetwork n'est pas autorisé"
pattern:
spec:
=(hostNetwork): false
- name: deny-host-pid-ipc
match:
any:
- resources:
kinds:
- Pod
validate:
failureAction: Enforce
message: "hostPID et hostIPC sont interdits"
pattern:
spec:
=(hostPID): false
=(hostIPC): false

Ce contrôle relie Kyverno au domaine Supply Chain Security de la CKS. La boucle foreach parcourt chaque conteneur du pod, ce qui évite l'angle mort classique d'une règle qui ne vérifierait que le premier. Attention au piège de l'image sans registry explicite : un manifeste qui demande nginx:1.29.2 ne correspond à aucun des motifs autorisés et sera refusé, alors que l'image proviendrait bien de Docker Hub. Le développeur doit écrire docker.io/library/nginx:1.29.2 pour passer la règle, ce qui est précisément l'effet recherché, mais il faut l'expliquer dans le message d'erreur.

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: restrict-image-registries
spec:
background: true
rules:
- name: validate-registries
match:
any:
- resources:
kinds:
- Pod
validate:
failureAction: Enforce
message: "Images autorisées uniquement depuis gcr.io ou docker.io/library"
foreach:
- list: "request.object.spec.containers"
deny:
conditions:
all:
- key: "{{ element.image }}"
operator: AnyNotIn
value:
- "gcr.io/*"
- "docker.io/library/*"

Vérification des signatures d'images (Supply Chain)

Section intitulée « Vérification des signatures d'images (Supply Chain) »

Vérifier une signature à l'admission déplace le contrôle au dernier moment utile : peu importe ce qui s'est passé dans la chaîne de build, une image non signée par la clé attendue n'entre pas dans le cluster. Kyverno résout la référence d'image auprès du registry, récupère la signature Cosign associée et la valide contre la clé publique déclarée dans la policy. Conséquence directe à anticiper : le contrôleur d'admission a besoin d'un accès réseau sortant vers le registry, et un registry privé impose de lui fournir des identifiants de lecture.

ImageValidatingPolicy sépare deux préoccupations que la syntaxe historique mélangeait. Le bloc attestors déclare qui a le droit de signer, ici via une clé publique stockée dans un Secret Kubernetes ; le bloc validations décrit ce qu'on exige de l'image une fois la signature vérifiée. L'expression "true" de l'exemple signifie donc « la signature suffit » : c'est là qu'on ajouterait une condition sur une attestation SLSA ou sur le contenu d'un SBOM.

apiVersion: policies.kyverno.io/v1
kind: ImageValidatingPolicy
metadata:
name: verify-image-signature
spec:
validationActions: [Deny]
matchConstraints:
resourceRules:
- apiGroups: [""]
apiVersions: ["v1"]
operations: ["CREATE", "UPDATE"]
resources: ["pods"]
matchImageReferences:
- glob: "gcr.io/my-project/*"
attestors:
- name: my-cosign-key
cosign:
key:
secretRef:
name: cosign-public-key
namespace: kyverno
validations:
- expression: "true"
message: "Image must be signed with our Cosign key."

La règle verifyImages inscrit la clé publique en clair dans la policy, ce qui reste acceptable puisqu'une clé publique n'est pas un secret, mais complique la rotation : changer de clé oblige à modifier chaque policy concernée. Le background: false n'est pas un détail : vérifier des signatures suppose des appels réseau vers le registry, opération trop coûteuse pour être rejouée en tâche de fond sur toutes les ressources existantes.

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: verify-image-cosign
spec:
background: false
rules:
- name: verify-signature
match:
any:
- resources:
kinds:
- Pod
verifyImages:
- imageReferences:
- "gcr.io/my-project/*"
attestors:
- entries:
- keys:
publicKeys: |
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE...
-----END PUBLIC KEY-----

La mutation permet de modifier automatiquement les ressources. C'est une fonctionnalité mature de Kyverno :

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: add-security-defaults
spec:
background: false # Mutation = pas de background
rules:
- name: add-security-context
match:
any:
- resources:
kinds:
- Pod
mutate:
patchStrategicMerge:
spec:
containers:
- (name): "*"
securityContext:
+(runAsNonRoot): true
+(allowPrivilegeEscalation): false
+(readOnlyRootFilesystem): true

La syntaxe de mutation Kyverno utilise des opérateurs spéciaux pour contrôler le comportement. Comprendre ces opérateurs est essentiel pour écrire des mutations efficaces.

OpérateurEffetUsage
(key)Ancre conditionnelleCibler des conteneurs spécifiques
+(key)Ajouter si absentDefaults non intrusifs
-(key)SupprimerRetirer des champs
=(key)Remplacer si existeForcer une valeur

Exemple concret : Si vous voulez ajouter runAsNonRoot: true uniquement aux pods qui n'ont pas déjà cette valeur, utilisez +(runAsNonRoot): true. Cela évite de surcharger une configuration explicite du développeur.

La génération répond à un problème récurrent : une ressource de sécurité doit exister dans chaque namespace, mais personne ne pense à la créer. Ici, la création d'un namespace déclenche la création d'une NetworkPolicy qui bloque tout le trafic entrant et sortant par défaut. Le champ synchronize: true change le contrat : Kyverno ne se contente pas de créer la ressource, il la remet en état si quelqu'un la modifie ou la supprime. C'est ce qui distingue la génération d'un simple script d'initialisation.

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: generate-default-deny
spec:
rules:
- name: create-network-policy
match:
any:
- resources:
kinds:
- Namespace
exclude:
any:
- resources:
namespaces:
- kube-system
- kyverno
generate:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
name: default-deny
namespace: "{{request.object.metadata.name}}"
synchronize: true
data:
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress

Effet : Chaque nouveau namespace reçoit automatiquement une NetworkPolicy default-deny.

Déployer une policy directement en mode bloquant (Enforce) est risqué. Kyverno permet un déploiement progressif avec deux modes et des overrides par namespace.

ValeurEffetPhase
EnforceBloque la requêteProduction
AuditLog dans PolicyReportsDécouverte

Stratégie recommandée :

  1. Démarrez avec Audit : Les violations sont enregistrées dans les PolicyReports. Regardez-les pour comprendre l'impact.
  2. Ajoutez des overrides : Passez à Enforce sur les namespaces critiques (production) tout en restant en Audit ailleurs.
  3. Généralisez Enforce : Une fois tous les workloads conformes, passez globalement en mode bloquant.

Stratégie progressive avec overrides :

spec:
rules:
- name: require-labels
match:
any:
- resources:
kinds:
- Pod
validate:
failureAction: Audit # Permissif par défaut
failureActionOverrides:
- action: Enforce
namespaces:
- production # Strict en production
message: "Le label 'app' est obligatoire"
pattern:
metadata:
labels:
app: "?*"

La CLI permet de tester les policies localement avant déploiement, sans cluster, ce qui la rend utilisable dans un job de pipeline. Deux commandes couvrent l'essentiel : kyverno apply évalue une policy contre un ou plusieurs manifestes et affiche le compte pass / fail / warn / error / skip, tandis que kyverno test exécute une suite de cas décrite dans un fichier kyverno-test.yaml. La commande apply sert aussi de contrôle syntaxique : une policy mal formée remonte en error avant même d'atteindre le cluster.

Fenêtre de terminal
# Installer via Krew
kubectl krew install kyverno
# Tester une policy sur un manifeste
kyverno apply ./policy.yaml --resource ./deployment.yaml
# Générer un fichier de test à partir d'une policy et d'une ressource
kyverno create test -p policy.yaml -r deployment.yaml \
--pass require-run-as-non-root,run-as-non-root,api-paiement,production,Pod
# Exécuter une suite de tests
kyverno test ./policy-tests/

La sortie de kyverno apply sur un pod qui viole la policy de l'exemple 1 ressemble à ceci :

Applying 1 policy rule(s) to 1 resource(s)...
policy require-run-as-non-root -> resource equipe-web/Pod/nginx-sans-securite failed:
1 - Tous les conteneurs doivent avoir runAsNonRoot: true.
pass: 0, fail: 1, warn: 0, error: 0, skip: 0

Un réflexe fréquent consiste à chercher une commande kyverno validate pour contrôler la syntaxe : elle n'existe pas. La CLI 1.18 expose exactement apply, create, docs, jp, json, migrate, test et version. Pour détecter une policy invalide, passez par kyverno apply.

Le diagnostic suit deux pistes distinctes qu'il ne faut pas confondre. La première concerne la policy elle-même : est-elle acceptée par Kyverno ? La réponse est dans kubectl describe, au niveau du champ status.conditions. La seconde concerne les ressources évaluées : quelles violations ont été relevées ? Elles vivent dans les PolicyReports, une ressource par namespace, alimentée seulement si la policy est en mode Audit ou si background: true est actif.

Fenêtre de terminal
# Lister les policies
kubectl get clusterpolicies,policies -A
# Détails d'une policy (erreurs, conditions)
kubectl describe clusterpolicy <name>
# PolicyReports (violations par namespace)
kubectl get policyreport -A
# Détail des violations
kubectl describe policyreport -n <namespace>
# Résumé rapide
kubectl get policyreport -A -o jsonpath='{range .items[*]}{.metadata.namespace}{"\t"}{.summary}{"\n"}{end}'

Voici les problèmes les plus fréquents et comment les résoudre rapidement.

SymptômeCause probableSolution
Policy "Ready: False"Erreur de syntaxekubectl describe clusterpolicy
Pas de violation en Auditbackground: falseAjouter background: true
Mutation non appliquéePod déjà existantRecréer le pod
Webhook timeoutKyverno surchargéAugmenter replicas

Astuce : Le champ status.conditions d'une ClusterPolicy contient les messages d'erreur détaillés. Regardez-le en premier.

  1. Les CRD ClusterPolicy et CleanupPolicy sont deprecated depuis la 1.17, leur suppression est planifiée pour la v1.20 (estimée octobre 2026)
  2. validationFailureAction est deprecated → utilisez validate.failureAction au niveau règle
  3. Composant sensible, déployez avec HA, surveillez les webhooks
  4. Meilleur pont CKS : vérification d'images (Supply Chain Security)
  5. Si validation simple → préférez VAP (natif, pas de dépendance)
  6. Fonctionnalités uniques : génération et mutation mature
  7. kyverno validate n'existe pas : la CLI se contrôle avec kyverno apply et kyverno test

Ces questions portent uniquement sur ce guide : le positionnement de Kyverno face aux solutions natives, la syntaxe des policies de validation et de mutation, et la lecture des PolicyReports.

Contrôle de connaissances

Validez vos connaissances avec ce quiz interactif

10 questions
8 min.
70% requis

Informations

  • Le chronomètre démarre au clic sur Démarrer
  • Questions à choix multiples, vrai/faux et réponses courtes
  • Vous pouvez naviguer entre les questions
  • Les résultats détaillés sont affichés à la fin

Lance le quiz et démarre le chronomètre

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracking. Un soutien, même symbolique, m'aide à couvrir l'hébergement et à garder ces ressources gratuites. Merci pour votre appui.

Le formulaire ne s'affiche pas ? Ouvrir Ko-fi dans un onglet.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn