Aller au contenu
CI/CD & Automatisation medium

Argo CD + Admission Controllers : Kyverno et policy-as-code GitOps

12 min de lecture

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.

  • 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

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.

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.yaml

Le pattern App-of-Apps garantit que tout est piloté depuis Git. Les sync-waves garantissent l’ordre.

  1. Créer le wrapper Helm

    kyverno/Chart.yaml
    apiVersion: v2
    name: kyverno
    description: Wrapper Helm pour Kyverno
    type: application
    version: 1.0.0
    dependencies:
    - name: kyverno
    version: "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éservation
    cleanupController:
    rbac:
    clusterRole:
    extraResources:
    - apiGroups: [""]
    resources: [pods]
    verbs: [get, list, watch, delete]
  2. Créer l’Application Argo CD avec les bonnes options

    infra-services/kyverno.yaml
    apiVersion: argoproj.io/v1alpha1
    kind: Application
    metadata:
    name: kyverno
    namespace: argocd
    annotations:
    argocd.argoproj.io/sync-wave: "1"
    argocd.argoproj.io/compare-options: >-
    ServerSideDiff=true,IncludeMutationWebhook=true
    spec:
    project: infra
    source:
    repoURL: https://github.com/votre-org/config-repo
    targetRevision: main
    path: kyverno
    helm:
    valueFiles:
    - values.yaml
    destination:
    server: https://kubernetes.default.svc
    namespace: kyverno
    syncPolicy:
    automated:
    prune: true
    selfHeal: true
    syncOptions:
    - CreateNamespace=true
    - ServerSideApply=true

    Trois points critiques :

    OptionPourquoi
    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 ServerSideDiff globalement dans la configuration Argo CD plutôt que de le répéter Application par Application. Kyverno le recommande explicitement dans ses notes plateforme.

  3. Créer l’Application pour les politiques (wave 2)

    infra-services/kyverno-policies.yaml
    apiVersion: argoproj.io/v1alpha1
    kind: Application
    metadata:
    name: kyverno-policies
    namespace: argocd
    annotations:
    argocd.argoproj.io/sync-wave: "2"
    spec:
    project: infra
    source:
    repoURL: https://github.com/votre-org/config-repo
    targetRevision: main
    path: kyverno-policies
    helm:
    valueFiles:
    - values.yaml
    destination:
    server: https://kubernetes.default.svc
    namespace: kyverno
    syncPolicy:
    automated:
    prune: true
    selfHeal: true
    syncOptions:
    - ServerSideApply=true

    Le sync-wave: "2" garantit que Kyverno est installé et Healthy avant que les politiques ne soient créées.

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.

kyverno-policies/templates/restrict-external-ips.yaml
apiVersion: policies.kyverno.io/v1
kind: ValidatingPolicy
metadata:
name: restrict-external-ips
spec:
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)."

La capacité VerifyImages de Kyverno vérifie les signatures Cosign et les attestations de provenance SLSA avant admission.

kyverno-policies/templates/require-image-signature.yaml
apiVersion: policies.kyverno.io/v1
kind: ImageValidatingPolicy
metadata:
name: require-signed-images
spec:
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."
kyverno-policies/templates/require-run-as-nonroot.yaml
apiVersion: policies.kyverno.io/v1
kind: ValidatingPolicy
metadata:
name: require-run-as-nonroot
spec:
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)."

Une fois les politiques déployées, vérifiez que Kyverno fonctionne :

Fenêtre de terminal
# Lister les policies
kubectl get validatingpolicies.policies.kyverno.io
kubectl get imagevalidatingpolicies.policies.kyverno.io
# Vérifier les PolicyReport (violations en mode Audit)
kubectl get policyreport -A
# Tester une violation
kubectl run test-root --image=nginx --restart=Never \
--overrides='{"spec":{"containers":[{"name":"test","image":"nginx","securityContext":{"runAsNonRoot":false}}]}}'
# Voir la violation
kubectl get policyreport -A -o wide

En mode [Audit], le pod est créé mais la violation est enregistrée. En mode [Deny], la création est bloquée.

Le rollout progressif se fait entièrement via Git :

  1. Mesurer : consultez les PolicyReport pour identifier les workloads non conformes

    Fenêtre de terminal
    kubectl get policyreport -A --no-headers | wc -l
  2. Corriger : adaptez les Deployments et StatefulSets concernés (ajoutez securityContext, retirez les externalIPs…)

  3. Basculer : modifiez validationActions: [Deny] dans les fichiers YAML, commitez, poussez — Argo CD réconcilie

  4. 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èreKyvernoGatekeeper (OPA)
Langage des politiquesYAML Kubernetes natif + CELRego + CRDs Gatekeeper
Courbe d’apprentissagePlus faible — même syntaxe que vos manifestesPlus forte — Rego est un langage déclaratif différent
Vérification d’imagesVerifyImages natif (Cosign, Notary)Pas natif — nécessite un composant externe
MutateOui, natifOui (stable depuis v3.10+ : Assign, AssignMetadata, ModifySet, AssignImage)
GenerateOui, natifNon
CleanupOui, natifNon
Maturité CNCFGraduated (mars 2026)Graduated
Intégration Argo CDNécessite ServerSideDiff + IncludeMutationWebhookNé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.

SymptômeCauseSolution
no matches for kind "ValidatingPolicy"CRDs pas encore installésVérifier le sync-wave — les policies doivent être en wave > Kyverno
OutOfSync permanent sur KyvernoMutations non prises en compte dans le diffAjouter ServerSideDiff=true,IncludeMutationWebhook=true
Erreur request entity too largeCRDs > 262 KoActiver ServerSideApply=true dans syncOptions
Pods bloqués après passage en DenyPolicy trop restrictiveRepasser en [Audit], corriger les manifestes, puis [Deny]
Argo CD lui-même bloqué par KyvernoPolicy trop large (match *)Exclure le namespace argocd dans la policy
OutOfSync sur Kyverno après upgradeHook Helm de préservation actifDéfinir config.preserve: false dans les values Kyverno
Conflit label app.kubernetes.io/instanceArgo CD et Kyverno utilisent le même labelPasser en tracking annotation ou ajuster webhookLabels
  • 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 argocd et kyverno des policies Enforce

Ce site vous est utile ?

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

Je maintiens +700 guides gratuits, sans pub ni tracing. Aujourd'hui, ce site ne couvre même pas mes frais d'hébergement, d'électricité, de matériel, de logiciels, mais surtout de cafés.

Un soutien régulier, même symbolique, m'aide à garder ces ressources gratuites et à continuer de produire des guides de qualité. Merci pour votre appui.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn