Argo CD excelle pour synchroniser vos applications depuis Git. Mais il ne vérifie pas que ce qu’il déploie respecte vos politiques de sécurité. C’est le rôle d’un admission controller comme Kyverno ou Gatekeeper. Ce guide montre comment les déployer ensemble, dans une architecture GitOps cohérente.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Déployer Kyverno via Argo CD en pattern App-of-Apps
- Utiliser les sync-waves pour ordonner CRDs → opérateur → politiques
- Configurer ServerSideApply, ServerSideDiff et IncludeMutationWebhook pour éviter les faux OutOfSync
- Vérifier les images signées (Cosign) et la provenance (SLSA) à l’admission
- Passer progressivement de Audit à Enforce
Dans quel contexte ?
Section intitulée « Dans quel contexte ? »Vous avez mis en place Argo CD et vos applications sont déployées en GitOps. Mais plusieurs situations courantes restent ouvertes :
- Un développeur pousse une image Docker non signée — elle finit en production
- Un Deployment tourne avec
runAsNonRoot: false— personne ne le bloque - Un Service expose un
externalIP— porte ouverte à un MITM (CVE-2020-8554) - Vous signez vos images avec Cosign, mais rien ne vérifie la signature au moment du déploiement
Un admission controller comble ces lacunes. Combiné à Argo CD, il forme le dernier maillon d’une chaîne de supply chain complète.
Architecture cible
Section intitulée « Architecture cible »config-repo/ infra-services/ # ← app racine Argo CD (sync-wave 0) kyverno.yaml # Application: déploie Kyverno (wave 1) kyverno-policies.yaml # Application: déploie les policies (wave 2) kyverno/ Chart.yaml # wrapper Helm → kyverno 3.7.1 values.yaml kyverno-policies/ Chart.yaml # wrapper Helm → kyverno-policies values.yaml templates/ # vos policies custom (ValidatingPolicy ou ClusterPolicy legacy) restrict-external-ips.yaml require-image-signature.yamlLe pattern App-of-Apps garantit que tout est piloté depuis Git. Les sync-waves garantissent l’ordre.
Étape 1 : déployer Kyverno via Argo CD
Section intitulée « Étape 1 : déployer Kyverno via Argo CD »-
Créer le wrapper Helm
kyverno/Chart.yaml apiVersion: v2name: kyvernodescription: Wrapper Helm pour Kyvernotype: applicationversion: 1.0.0dependencies:- name: kyvernoversion: "3.7.1"repository: https://kyverno.github.io/kyverno/kyverno/values.yaml kyverno:config:preserve: false # évite les OutOfSync liés au hook Helm de préservationcleanupController:rbac:clusterRole:extraResources:- apiGroups: [""]resources: [pods]verbs: [get, list, watch, delete] -
Créer l’Application Argo CD avec les bonnes options
infra-services/kyverno.yaml apiVersion: argoproj.io/v1alpha1kind: Applicationmetadata:name: kyvernonamespace: argocdannotations:argocd.argoproj.io/sync-wave: "1"argocd.argoproj.io/compare-options: >-ServerSideDiff=true,IncludeMutationWebhook=truespec:project: infrasource:repoURL: https://github.com/votre-org/config-repotargetRevision: mainpath: kyvernohelm:valueFiles:- values.yamldestination:server: https://kubernetes.default.svcnamespace: kyvernosyncPolicy:automated:prune: trueselfHeal: truesyncOptions:- CreateNamespace=true- ServerSideApply=trueTrois points critiques :
Option Pourquoi ServerSideApply=trueLes CRDs Kyverno dépassent 262 Ko — le client-side apply échoue ServerSideDiff=trueKyverno mute des ressources — sans ça, Argo CD voit du drift permanent. Stable depuis Argo CD v3.1.0, ce mode fait participer les admission controllers au calcul du diff. IncludeMutationWebhook=trueIntègre les mutations de Kyverno dans le calcul des diffs Si vous utilisez largement les mutations Kyverno, envisagez d’activer
ServerSideDiffglobalement dans la configuration Argo CD plutôt que de le répéter Application par Application. Kyverno le recommande explicitement dans ses notes plateforme. -
Créer l’Application pour les politiques (wave 2)
infra-services/kyverno-policies.yaml apiVersion: argoproj.io/v1alpha1kind: Applicationmetadata:name: kyverno-policiesnamespace: argocdannotations:argocd.argoproj.io/sync-wave: "2"spec:project: infrasource:repoURL: https://github.com/votre-org/config-repotargetRevision: mainpath: kyverno-policieshelm:valueFiles:- values.yamldestination:server: https://kubernetes.default.svcnamespace: kyvernosyncPolicy:automated:prune: trueselfHeal: truesyncOptions:- ServerSideApply=trueLe
sync-wave: "2"garantit que Kyverno est installé et Healthy avant que les politiques ne soient créées.
Étape 2 : premières politiques
Section intitulée « Étape 2 : premières politiques »Interdire les externalIPs (CVE-2020-8554)
Section intitulée « Interdire les externalIPs (CVE-2020-8554) »CVE-2020-8554 est une vulnérabilité de conception dans Kubernetes qui permet
un MITM via les services externalIPs. Il n’y a pas de correctif natif — la
mitigation passe par une politique d’admission.
apiVersion: policies.kyverno.io/v1kind: ValidatingPolicymetadata: name: restrict-external-ipsspec: validationActions: [Audit] # passer en [Deny] après validation matchConstraints: resourceRules: - apiGroups: [""] apiVersions: ["v1"] operations: ["CREATE", "UPDATE"] resources: ["services"] validations: - expression: "!has(object.spec.externalIPs) || size(object.spec.externalIPs) == 0" message: "externalIPs are not allowed (CVE-2020-8554)."Exiger des images signées avec Cosign
Section intitulée « Exiger des images signées avec Cosign »La capacité VerifyImages de Kyverno vérifie les signatures Cosign et les attestations de provenance SLSA avant admission.
apiVersion: policies.kyverno.io/v1kind: ImageValidatingPolicymetadata: name: require-signed-imagesspec: validationActions: [Audit] # passer en [Deny] après validation matchConstraints: resourceRules: - apiGroups: [""] apiVersions: ["v1"] operations: ["CREATE", "UPDATE"] resources: ["pods"] matchImageReferences: - glob: "registry.example.com/*" attestors: - name: cosign-keyless cosign: keyless: identities: - subjectRegExp: "^https://github.com/votre-org/" issuer: "https://token.actions.githubusercontent.com" validations: - expression: "true" message: "Image must be signed with a valid Cosign keyless signature."Interdire les conteneurs root
Section intitulée « Interdire les conteneurs root »apiVersion: policies.kyverno.io/v1kind: ValidatingPolicymetadata: name: require-run-as-nonrootspec: validationActions: [Audit] # passer en [Deny] après validation 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: "Les conteneurs doivent tourner en non-root (runAsNonRoot: true)."Étape 3 : vérifier le fonctionnement
Section intitulée « Étape 3 : vérifier le fonctionnement »Une fois les politiques déployées, vérifiez que Kyverno fonctionne :
# Lister les policieskubectl get validatingpolicies.policies.kyverno.iokubectl get imagevalidatingpolicies.policies.kyverno.io
# Vérifier les PolicyReport (violations en mode Audit)kubectl get policyreport -A
# Tester une violationkubectl run test-root --image=nginx --restart=Never \ --overrides='{"spec":{"containers":[{"name":"test","image":"nginx","securityContext":{"runAsNonRoot":false}}]}}'
# Voir la violationkubectl get policyreport -A -o wideEn mode [Audit], le pod est créé mais la violation est enregistrée. En mode
[Deny], la création est bloquée.
Étape 4 : passage Audit → Enforce
Section intitulée « Étape 4 : passage Audit → Enforce »Le rollout progressif se fait entièrement via Git :
-
Mesurer : consultez les
PolicyReportpour identifier les workloads non conformesFenêtre de terminal kubectl get policyreport -A --no-headers | wc -l -
Corriger : adaptez les Deployments et StatefulSets concernés (ajoutez
securityContext, retirez lesexternalIPs…) -
Basculer : modifiez
validationActions: [Deny]dans les fichiers YAML, commitez, poussez — Argo CD réconcilie -
Surveiller : vérifiez que les applications restent Healthy dans l’interface Argo CD
Kyverno vs Gatekeeper : quel admission controller avec Argo CD ?
Section intitulée « Kyverno vs Gatekeeper : quel admission controller avec Argo CD ? »| Critère | Kyverno | Gatekeeper (OPA) |
|---|---|---|
| Langage des politiques | YAML Kubernetes natif + CEL | Rego + CRDs Gatekeeper |
| Courbe d’apprentissage | Plus faible — même syntaxe que vos manifestes | Plus forte — Rego est un langage déclaratif différent |
| Vérification d’images | VerifyImages natif (Cosign, Notary) | Pas natif — nécessite un composant externe |
| Mutate | Oui, natif | Oui (stable depuis v3.10+ : Assign, AssignMetadata, ModifySet, AssignImage) |
| Generate | Oui, natif | Non |
| Cleanup | Oui, natif | Non |
| Maturité CNCF | Graduated (mars 2026) | Graduated |
| Intégration Argo CD | Nécessite ServerSideDiff + IncludeMutationWebhook | Nécessite ServerSideDiff |
Pour un usage supply chain (vérification d’images, provenance, mutations), Kyverno est le choix le plus naturel. Pour un comparatif détaillé, voir VAP vs Kyverno vs Gatekeeper.
Dépannage
Section intitulée « Dépannage »| Symptôme | Cause | Solution |
|---|---|---|
no matches for kind "ValidatingPolicy" | CRDs pas encore installés | Vérifier le sync-wave — les policies doivent être en wave > Kyverno |
| OutOfSync permanent sur Kyverno | Mutations non prises en compte dans le diff | Ajouter ServerSideDiff=true,IncludeMutationWebhook=true |
Erreur request entity too large | CRDs > 262 Ko | Activer ServerSideApply=true dans syncOptions |
| Pods bloqués après passage en Deny | Policy trop restrictive | Repasser en [Audit], corriger les manifestes, puis [Deny] |
| Argo CD lui-même bloqué par Kyverno | Policy trop large (match *) | Exclure le namespace argocd dans la policy |
| OutOfSync sur Kyverno après upgrade | Hook Helm de préservation actif | Définir config.preserve: false dans les values Kyverno |
Conflit label app.kubernetes.io/instance | Argo CD et Kyverno utilisent le même label | Passer en tracking annotation ou ajuster webhookLabels |
À retenir
Section intitulée « À retenir »- Kyverno + Argo CD = policy-as-code GitOps — vos politiques sont versionnées, revues et déployées comme du code applicatif
- Les sync-waves sont indispensables : CRDs (wave 1) → policies (wave 2)
- ServerSideApply est obligatoire pour les CRDs volumineux de Kyverno
- ServerSideDiff + IncludeMutationWebhook évitent les faux OutOfSync
- VerifyImages permet de vérifier les signatures Cosign et la provenance SLSA directement à l’admission Kubernetes
- Commencez en Audit, mesurez, corrigez, puis passez en Deny
- Excluez toujours les namespaces
argocdetkyvernodes policies Enforce