Aller au contenu
English
Conteneurs & Orchestration high

Gatekeeper, OPA pour Kubernetes (complément CKS)

50 min de lecture

Logo Kubernetes

Gatekeeper est le projet Kubernetes construit autour d'OPA (Open Policy Agent), moteur de policy-as-code CNCF Graduated. Il utilise le langage Rego pour définir des policies complexes via un modèle ConstraintTemplate + Constraint. C'est un outil mature et largement adopté par les équipes déjà familières avec l'écosystème OPA.

Une précision sur la place de cet outil dans l'examen. Le programme CKS insiste sur les Pod Security Standards, les registres autorisés, la validation d'artefacts et le durcissement ; il ne cite pas Gatekeeper comme compétence obligatoire. Ce guide sert donc à relier le programme à une implémentation réelle, pas à réviser une question d'examen. Pour les policies d'admission natives, voir ValidatingAdmissionPolicy.

Ce guide a été rejoué sur Gatekeeper v3.23.1, chart Helm 3.23.1, sur un cluster Kubernetes 1.37. Comptez un peu plus de deux minutes d'installation, contre une quarantaine de secondes pour Kyverno : le chart déploie un contrôleur en trois répliques plus un contrôleur d'audit, ce qui donne une idée de l'empreinte.

Les sections qui suivent mentionnent aussi Gatekeeper v3.22.x sur Kubernetes 1.34, la version de référence du CKS 2026.

  • Quand choisir Gatekeeper vs VAP vs Kyverno
  • Comprendre le modèle ConstraintTemplate + Constraint
  • Écrire des policies Rego orientées sécurité (privileged, hostPath, runAsNonRoot)
  • Utiliser gator pour tester localement
  • Diagnostiquer un rejet d'admission (Gatekeeper vs PSA vs RBAC)

Gatekeeper n'est pas toujours le bon choix. Apprendre Rego représente un investissement réel, et il faut savoir ce qu'on achète avec. Ce tableau compare les trois approches principales sur des critères concrets.

CritèreVAPKyvernoGatekeeper
LangageCEL (natif K8s)YAMLRego
Composant externeNonOui (webhook)Oui (webhook)
Courbe d'apprentissageFaibleFaibleÉlevée
Logique complexeLimitéeMoyenneTrès riche
Audit ressources existantesNonPolicyReportsIntégré
MutationBeta (1.34)
Génération de ressourcesNonNon
Écosystème OPA existantNonNonOui
Maturité CNCFNatif K8sIncubatingGraduated (OPA)

Résumé en une phrase : Choisissez VAP si vous voulez rester natif, Kyverno pour sa simplicité YAML, ou Gatekeeper si vous avez déjà un écosystème OPA/Rego.

Voici les scénarios où Gatekeeper a un avantage clair :

  • Vous utilisez déjà OPA ailleurs (Terraform, CI/CD, APIs)
  • Votre équipe maîtrise Rego
  • Vous avez besoin de logique complexe (références croisées, calculs, ensembles)
  • Vous voulez un audit intégré des ressources existantes
  • Vous avez une librairie de policies Rego à réutiliser

À l'inverse, ces situations suggèrent un autre outil :

  • Vous débutez en policy-as-code → VAP ou Kyverno
  • Vous voulez éviter un composant externe → VAP
  • Vous avez besoin de générer des ressources → Kyverno
  • L'équipe préfère rester en YAML pur → Kyverno

Gatekeeper génère des VAP, mais pas depuis n'importe quel template

Section intitulée « Gatekeeper génère des VAP, mais pas depuis n'importe quel template »

Gatekeeper sait traduire ses contraintes en ValidatingAdmissionPolicy natives, et c'est activé par défaut. Deux drapeaux le gouvernent, tous deux marqués bêta et relevés directement sur le binaire :

Sortie de --help
-default-create-vap-for-templates
(beta) Create VAP resource for template containing VAP-style CEL source.
(default true)
-default-create-vap-binding-for-constraints
(beta) Create VAPBinding resource for constraint of the template
containing VAP-style CEL source. (default true)

La condition est dans la formulation, et elle est décisive : « containing VAP-style CEL source ». Un ConstraintTemplate écrit en Rego ne produit aucune VAP, ce qui a été vérifié : la contrainte fonctionne, elle bloque bien, mais par le webhook. Seul un template déclarant un bloc code avec engine: K8sNativeValidation déclenche la génération.

La conséquence pratique est importante, parce qu'elle concerne l'existant : la quasi-totalité des bibliothèques de policies publiques sont écrites en Rego. Elles continueront donc de passer par le webhook, et migrer vers le chemin natif suppose de les réécrire en CEL.

Sur un template CEL, la génération est mesurable et elle se fait en deux temps :

Ressource généréeDélai après création de la contrainte
ValidatingAdmissionPolicy~2 secondes
ValidatingAdmissionPolicyBinding~30 secondes

Ce n'est pas une lenteur, c'est un réglage : --default-wait-for-vapb-generation vaut 30 secondes par défaut. Pendant cet intervalle, la VAP existe sans son binding, et une VAP sans binding n'applique rien. C'est donc le webhook Gatekeeper qui refuse pendant ces trente secondes, avant que le chemin natif ne prenne le relais.

Le message d'erreur le dit sans ambiguïté, et c'est le meilleur moyen de savoir quel chemin a tranché. Par le webhook :

Sortie
Error from server (Forbidden): admission webhook "validation.gatekeeper.sh"
denied the request: [exiger-equipe] le label equipe est obligatoire

Par la policy native, une fois le binding en place :

Sortie
The pods "sans-label" is invalid: : ValidatingAdmissionPolicy
'gatekeeper-k8slabelcel' with binding 'gatekeeper-k8slabelcel-exiger-equipe-cel'
denied request: un label exige manque

La frontière entre « outil externe » et « mécanisme natif » s'estompe donc réellement, mais seulement pour les policies écrites en CEL : Gatekeeper cohabite avec VAP au lieu de le remplacer.

Le schéma ci-dessous montre le point qui distingue Gatekeeper des autres outils d'admission : le modèle à deux niveaux. Un ConstraintTemplate définit la logique en Rego et crée au passage un nouveau type de ressource (une CRD) dans le cluster. Une Constraint est une instance de ce type : elle applique le template à un périmètre précis, avec des paramètres. Regardez le sens des flèches : un même template peut être instancié par plusieurs Constraints, ce qui permet d'écrire la règle une fois et de la décliner par namespace ou par équipe. Tout passe par l'admission webhook, sur le chemin critique de l'API server.

Architecture Gatekeeper : ConstraintTemplate instancie Constraint

Fenêtre de terminal
kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper/v3.22.0/deploy/gatekeeper.yaml

Vérifier l'installation :

Fenêtre de terminal
kubectl get pods -n gatekeeper-system
NAME READY STATUS RESTARTS AGE
gatekeeper-audit-5c4b5d897d-x7bmt 1/1 Running 0 42s
gatekeeper-controller-manager-6958bc4b76-6bhq2 1/1 Running 0 42s
gatekeeper-controller-manager-6958bc4b76-cjtk2 1/1 Running 0 42s
gatekeeper-controller-manager-6958bc4b76-sjhdf 1/1 Running 0 42s

Après l'installation, vous verrez deux types de pods. Comprendre leur rôle est essentiel pour le dépannage.

ComposantRôleRéplicas
controller-managerWebhook d'admission, intercepte et valide les requêtes3 (HA)
auditScan périodique des ressources existantes1

Pourquoi 3 réplicas pour le controller-manager ? C'est un composant critique sur le chemin de l'API Server. Si un seul pod est down, les requêtes d'admission pourraient échouer. Avec 3 réplicas, vous avez de la haute disponibilité.

Le pod audit scanne les ressources existantes pour détecter les violations. Même si une ressource a été créée avant l'installation de Gatekeeper, l'audit la trouvera.

Cet exemple est le plus rentable pour la CKS : refuser les conteneurs en mode privileged est un contrôle fondamental.

apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
name: k8sdenyprivileged
spec:
crd:
spec:
names:
kind: K8sDenyPrivileged
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package k8sdenyprivileged
violation[{"msg": msg}] {
container := input.review.object.spec.containers[_]
container.securityContext.privileged == true
msg := sprintf("Conteneur '%v' en mode privileged interdit", [container.name])
}
violation[{"msg": msg}] {
container := input.review.object.spec.initContainers[_]
container.securityContext.privileged == true
msg := sprintf("InitContainer '%v' en mode privileged interdit", [container.name])
}

Exemple 2 : securityContext complet (runAsNonRoot + readOnlyRootFilesystem)

Section intitulée « Exemple 2 : securityContext complet (runAsNonRoot + readOnlyRootFilesystem) »

Pour la CKS, imposer un securityContext sécurisé est encore plus pertinent :

apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
name: k8ssecuritycontextcomplete
spec:
crd:
spec:
names:
kind: K8sSecurityContextComplete
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package k8ssecuritycontextcomplete
# Vérifie runAsNonRoot
violation[{"msg": msg}] {
container := input.review.object.spec.containers[_]
not container.securityContext.runAsNonRoot == true
msg := sprintf("Conteneur '%v' doit avoir runAsNonRoot: true", [container.name])
}
# Vérifie allowPrivilegeEscalation
violation[{"msg": msg}] {
container := input.review.object.spec.containers[_]
not container.securityContext.allowPrivilegeEscalation == false
msg := sprintf("Conteneur '%v' doit avoir allowPrivilegeEscalation: false", [container.name])
}
# Vérifie readOnlyRootFilesystem
violation[{"msg": msg}] {
container := input.review.object.spec.containers[_]
not container.securityContext.readOnlyRootFilesystem == true
msg := sprintf("Conteneur '%v' doit avoir readOnlyRootFilesystem: true", [container.name])
}

Exemple 3 : refuser hostPath (isolation filesystem)

Section intitulée « Exemple 3 : refuser hostPath (isolation filesystem) »

Un volume hostPath monte un répertoire du nœud directement dans le conteneur. C'est une porte de sortie classique de l'isolation : monter / ou /var/run/docker.sock donne au pod un accès au système hôte, souvent équivalent à une élévation de privilèges sur le nœud. Le template ci-dessous refuse tout pod qui déclare un volume hostPath, sans exception. La règle Rego est volontairement simple : elle itère sur spec.volumes et rejette dès qu'un volume porte le champ hostPath, quelle que soit la valeur du chemin.

apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
name: k8sdenyhostpath
spec:
crd:
spec:
names:
kind: K8sDenyHostPath
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package k8sdenyhostpath
violation[{"msg": msg}] {
volume := input.review.object.spec.volumes[_]
volume.hostPath
msg := sprintf("Volume hostPath '%v' interdit", [volume.name])
}

Cet exemple relève directement du domaine Supply Chain Security de la CKS, détaillé dans le guide Supply Chain Security.

apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
name: k8sallowedregistries
spec:
crd:
spec:
names:
kind: K8sAllowedRegistries
validation:
openAPIV3Schema:
type: object
properties:
registries:
type: array
items:
type: string
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package k8sallowedregistries
violation[{"msg": msg}] {
container := input.review.object.spec.containers[_]
not registry_allowed(container.image)
msg := sprintf("Image '%v' provient d'un registry non autorisé", [container.image])
}
registry_allowed(image) {
registry := input.parameters.registries[_]
startswith(image, registry)
}

Constraint associé :

apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sAllowedRegistries
metadata:
name: only-trusted-registries
spec:
enforcementAction: deny
match:
kinds:
- apiGroups: [""]
kinds: ["Pod"]
parameters:
registries:
- "gcr.io/my-project/"
- "registry.internal.company.com/"

La librairie officielle Gatekeeper a une section dédiée aux Pod Security Standards. Pour la CKS, c'est l'angle le plus fort de Gatekeeper.

Fenêtre de terminal
# Installer les templates Pod Security Standards
kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/library/pod-security-policy/privileged-containers/template.yaml
kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/library/pod-security-policy/host-network-ports/template.yaml
kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/master/library/pod-security-policy/allowed-users/template.yaml
Template PSSDescriptionÉquivalent PSA
K8sPSPPrivilegedContainerInterdit privilegedBaseline/Restricted
K8sPSPHostNetworkingPortsContrôle hostNetwork/hostPortBaseline/Restricted
K8sPSPAllowedUsersRestreint UID/GIDRestricted
K8sPSPCapabilitiesContrôle les capabilitiesRestricted
K8sPSPHostFilesystemRestreint hostPathBaseline

Ces gabarits rendent les mêmes contrôles que PSA, mais avec plus de souplesse. Vous pouvez par exemple autoriser hostPath sur certains namespaces seulement, ce que PSA ne sait pas faire : son niveau s'applique au namespace entier.

Gatekeeper peut ainsi compléter PSA pour des règles spécifiques non couvertes, ou l'implémenter complètement si vous avez besoin de plus de flexibilité.

Déployer une policy directement en mode bloquant est risqué. Gatekeeper offre trois modes d'enforcement pour une adoption progressive et sécurisée.

ValeurComportementPhase
dryrunAccepte, enregistre dans statusDécouverte
warnAccepte, affiche warningSensibilisation
denyBloque la requêteProduction

Comment utiliser ces modes ?

  1. Démarrez avec dryrun : Les violations sont enregistrées dans le champ status de la Constraint, mais aucune requête n'est bloquée. Lancez l'audit pour voir les ressources existantes non conformes.
  2. Passez à warn : Les développeurs voient des warnings lors de leurs déploiements, mais peuvent continuer. Cela les sensibilise avant le blocage.
  3. Activez deny : Une fois tous les workloads corrects, passez au blocage effectif.
spec:
enforcementAction: dryrun # Étape 1 : découverte
# enforcementAction: warn # Étape 2 : alerter
# enforcementAction: deny # Étape 3 : enforcement

Une Constraint posée aujourd'hui ne s'applique qu'aux ressources créées ou modifiées ensuite : l'admission se déclenche sur une requête à l'API server, et ce qui tourne déjà n'en émet aucune. Sans autre mécanisme, vous auriez donc un cluster conforme sur les nouveaux déploiements et aveugle sur l'existant, qui est précisément là où se trouvent les dérives accumulées.

L'audit répond à ce trou. Gatekeeper réévalue périodiquement toutes les ressources du cluster contre chaque Constraint, et inscrit le résultat dans le status de la Constraint elle-même. Rien n'est bloqué ni modifié : l'audit constate. C'est ce qui en fait le bon point de départ avant de passer une règle en deny, et c'est aussi le champ à lire pour mesurer une dette de conformité.

Fenêtre de terminal
# Voir les violations détectées par l'audit
kubectl get k8sdenyprivileged deny-privileged-containers -o yaml
status:
auditTimestamp: "2026-03-24T10:30:00Z"
totalViolations: 2
violations:
- enforcementAction: deny
kind: Pod
message: "Conteneur 'debug' en mode privileged interdit"
name: debug-pod
namespace: production

Pour éprouver vos templates et contraintes en local, avant tout déploiement, utilisez gator plutôt que opa eval : c'est la CLI de Gatekeeper, et elle comprend nativement son format. Tester au plus tôt évite précisément le scénario que l'encadré suivant décrit.

Fenêtre de terminal
# Installer gator
brew install gator
# Tester un template + constraint contre une ressource
gator test \
--filename template.yaml \
--filename constraint.yaml \
--filename pod-to-test.yaml
# Vérifier la syntaxe Rego d'un template
gator verify --filename template.yaml

Depuis v3.22+, gator policy ajoute même une gestion "brew-like" de la policy library :

Fenêtre de terminal
# Installer des policies depuis la librairie
gator policy install privileged-containers

Un Pod refusé peut l'être par plusieurs mécanismes. Avant de supposer que Gatekeeper est la cause, vérifiez systématiquement ces sources possibles. Le message d'erreur vous indique généralement la source.

Message / IndiceSource probableCommande de diagnostic
admission webhook "validation.gatekeeper.sh" deniedGatekeeperkubectl get constraints
violates PodSecurityPSAkubectl get ns -o yaml (labels)
exceeded quotaResourceQuotakubectl describe quota
forbidden: User cannotRBACkubectl auth can-i
admission webhook "..." denied (autre)Autre webhookkubectl get validatingwebhookconfigurations

Ces commandes se lisent du général au particulier. Commencez par lister les webhooks et les Constraints actifs pour vérifier ce qui est réellement en place, puis descendez au kubectl describe d'une Constraint précise pour voir ses violations dans le champ status. Les deux dernières commandes ciblent les logs : celui du controller-manager trace les rejets d'admission en temps réel, celui du pod audit remonte les violations sur les ressources déjà déployées. Distinguer ces deux flux évite de chercher un rejet d'admission dans les logs d'audit, où il n'apparaîtra pas.

Fenêtre de terminal
# Lister tous les webhooks
kubectl get validatingwebhookconfigurations
# Lister les ConstraintTemplates
kubectl get constrainttemplates
# Lister tous les Constraints actifs
kubectl get constraints
# Détails d'un Constraint (violations, status)
kubectl describe k8sdenyprivileged deny-privileged-containers
# Logs du controller (rejets récents)
kubectl logs -l control-plane=controller-manager -n gatekeeper-system --tail=50
# Logs d'audit (violations ressources existantes)
kubectl logs -l control-plane=audit-controller -n gatekeeper-system --tail=50

Une Constraint trop large peut bloquer un déploiement qu'il faut passer tout de suite. Les deux sorties de secours ci-dessous n'ont pas la même portée, et c'est ce qui doit guider le choix sous pression.

Passer la Constraint en dryrun ne désarme qu'elle : le reste des règles continue de protéger le cluster, et l'audit continue de compter les violations. C'est l'option à privilégier. Supprimer la configuration de webhook désarme toutes les policies d'un coup, y compris celles qui n'ont rien à voir avec l'incident, et laisse le cluster sans admission jusqu'à sa recréation. Ne l'employez que si Gatekeeper lui-même est en panne.

Fenêtre de terminal
# Option 1 : passer le Constraint en dryrun
kubectl patch k8sdenyprivileged deny-privileged-containers \
--type='merge' -p '{"spec":{"enforcementAction":"dryrun"}}'
# Option 2 : désactiver le webhook temporairement
kubectl delete validatingwebhookconfiguration gatekeeper-validating-webhook-configuration
# ATTENTION : penser a le reactiver apres resolution

Gatekeeper supporte la mutation (stable depuis v3.10+). Ce guide se concentre sur la validation, mais voici un exemple minimal :

apiVersion: mutations.gatekeeper.sh/v1
kind: Assign
metadata:
name: add-default-security-context
spec:
applyTo:
- groups: [""]
kinds: ["Pod"]
versions: ["v1"]
match:
scope: Namespaced
location: "spec.containers[name:*].securityContext.runAsNonRoot"
parameters:
assign:
value: true

Pour un guide complet sur la mutation Gatekeeper, voir la documentation officielle.

Les recommandations qui suivent se lisent en deux temps. Les premières évitent qu'une policy ne casse le cluster qu'elle est censée protéger, un risque réel dès lors que Gatekeeper s'insère sur le chemin de l'API server. Les secondes visent la durée : une Constraint écrite en Rego et non documentée devient ingérable dès que son auteur change d'équipe.

Ces trois règles répondent à un risque propre à Gatekeeper : puisqu'il s'insère sur le chemin de l'API server, une Constraint trop large peut bloquer le cluster lui-même, y compris les pods système qui le font tourner. La première règle est donc la plus importante : exclure kube-system et gatekeeper-system du périmètre de chaque Constraint. La deuxième, démarrer en dryrun, vous laisse mesurer l'impact avant tout blocage.

  1. Excluez les namespaces système (kube-system, gatekeeper-system) pour éviter de bloquer le cluster
  2. Commencez en dryrun avant deny, découvrez les violations sans impact
  3. Limitez le scope avec match.namespaces pour les performances

Au quotidien, l'enjeu est de garder des policies maintenables dans la durée. Le premier réflexe fait gagner le plus de temps : partir de la librairie officielle de policies plutôt que d'écrire du Rego from scratch, car les cas courants (privileged, hostPath, capabilities) y sont déjà couverts et testés. Le dernier point n'est pas cosmétique : le Rego est illisible pour qui ne le pratique pas, une Constraint non documentée devient un point de blocage dès que son auteur quitte l'équipe.

  1. Réutilisez la librairie avant de créer des templates custom
  2. Testez avec gator localement avant de déployer
  3. Surveillez l'audit pour la conformité continue
  4. Documentez vos Constraints, le Rego n'est pas lisible par tous

Une contrainte large coûte cher : chaque requête à l'API server déclenche son évaluation, et une contrainte qui balaie toutes les ressources du cluster se paie en latence d'admission. Limitez systématiquement la portée avec match.namespaces et match.kinds, même quand la règle pourrait s'appliquer partout.

  1. Gatekeeper = OPA pour Kubernetes, basé sur le moteur CNCF Graduated OPA
  2. Modèle à 2 niveaux : ConstraintTemplate (logique Rego) + Constraint (application)
  3. Rego : puissant pour la logique complexe, mais courbe d'apprentissage
  4. Audit intégré pour scanner les ressources existantes, avantage unique
  5. gator pour tester localement (pas opa eval)
  6. Librairie PSS pour implémenter les Pod Security Standards
  7. Admission webhook : composant critique, tester hors production
  8. v3.22+ intègre VAP : Gatekeeper peut cohabiter avec les policies natives

Ce quiz vérifie les points qui reviennent le plus en situation réelle et à l'examen CKS : le modèle ConstraintTemplate contre Constraint, le choix entre Gatekeeper, VAP et Kyverno, et le diagnostic d'un rejet d'admission.

Contrôle de connaissances

Validez vos connaissances avec ce quiz interactif

6 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

  • Supply Chain Security : Les contraintes de provenance d'image qu'une policy d'admission peut faire respecter.
  • Audit Logs : La trace des requêtes rejetées par les webhooks d'admission.

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