Aller au contenu
English
Compte : non connecté, synchronisation possible Mon apprentissage
Selectionner le thème
English

Gatekeeper, OPA pour Kubernetes (complément CKS)

Par mis à jour le

Améliorer cette page

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 kind en Kubernetes 1.37, le 5 octobre 2026, sorties comprises. L'installation par Helm, attendue avec --wait, prend 36 secondes : le chart déploie un contrôleur en trois répliques plus un contrôleur d'audit, soit quatre Pods, ce qui donne une idée de l'empreinte. L'examen CKS, lui, est aligné sur Kubernetes 1.35 ; rien de ce qui suit n'en dépend.

  • 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é
MutationGA en 1.36 (MutatingAdmissionPolicy)OuiOui
Génération de ressourcesNonOuiNon
Écosystème OPA existantNonNonOui
Maturité CNCFNatif K8sGraduated (mars 2026)Via OPA (Graduated)

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 status de la Constraint le dit sans détour pour un template Rego : le point d'application vap.k8s.io y porte message: K8sNativeValidation engine is missing et state: error, pendant que le point webhook est marqué enforced. C'est là qu'il faut lire par quel chemin une contrainte s'applique réellement.

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.

Modèle à deux niveaux de Gatekeeper : le ConstraintTemplate définit la logique, écrite en Rego, et s'instancie en Constraint, qui applique la policy avec ses paramètres (labels, registries).instancieConstraintTemplatedéfinit la logiqueTemplate Regologique de policyConstraintapplique la policyParamètreslabels, registries
Le Template définit la règle, la Constraint l'applique. Source · SVG
Échap pour fermer, + et − pour zoomer, défilement pour se déplacer. 100 %
Fenêtre de terminal
kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper/v3.23.1/deploy/gatekeeper.yaml

Dans les deux cas la version est épinglée : le manifeste par son tag, le chart par --version. Un helm install sans version prend la dernière publiée, et deux clusters installés à une semaine d'écart ne tournent plus le même Gatekeeper. Le --wait rend la commande au moment où les quatre Pods répondent, 36 secondes mesurées.

Vérifier l'installation :

Fenêtre de terminal
kubectl get pods -n gatekeeper-system
Sortie
NAME READY STATUS RESTARTS AGE
gatekeeper-audit-74f586bd57-frrpr 1/1 Running 0 27s
gatekeeper-controller-manager-75d49687d6-bv4pt 1/1 Running 0 27s
gatekeeper-controller-manager-75d49687d6-k26qr 1/1 Running 0 27s
gatekeeper-controller-manager-75d49687d6-n7f8s 1/1 Running 0 27s

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])
}

Le ConstraintTemplate se crée en premier, et il faut quelques secondes pour que la CRD K8sDenyPrivileged qu'il déclare soit établie ; appliquer la Constraint avant rend no matches for kind "K8sDenyPrivileged". Une fois les deux en place, un Pod privileged: true est refusé par le webhook, et le message cite le nom de la Constraint entre crochets.

debug-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: debug
spec:
containers:
- name: debug
image: nginx:1.30@sha256:9bf97bd7714f5e24c1ccd545ecb9eb5435cb6d109c97cebb15e7e455e0239edb
securityContext:
privileged: true
Fenêtre de terminal
kubectl wait --for=condition=established crd/k8sdenyprivileged.constraints.gatekeeper.sh
kubectl apply -f constraint.yaml
kubectl apply -f debug-pod.yaml
Sortie
Error from server (Forbidden): error when creating "debug-pod.yaml": admission webhook
"validation.gatekeeper.sh" denied the request: [deny-privileged-containers] Conteneur 'debug'
en mode privileged interdit

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. Les trois règles Rego sont indépendantes, et c'est ce qui rend le message utile : un Pod sans aucun securityContext reçoit les trois violations d'un coup, là où une VAP en mode Deny ne renvoie que la première. Mesuré avec une Constraint K8sSecurityContextComplete instanciée comme à l'exemple 1 :

Sortie
admission webhook "validation.gatekeeper.sh" denied the request:
[require-secure-context] Conteneur 'nginx' doit avoir allowPrivilegeEscalation: false
[require-secure-context] Conteneur 'nginx' doit avoir readOnlyRootFilesystem: true
[require-secure-context] Conteneur 'nginx' doit avoir runAsNonRoot: true
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])
}

Un Pod qui monte /etc de l'hôte dans un volume host-etc est refusé avec [deny-hostpath] Volume hostPath 'host-etc' interdit. Notez ce que Rego fait ici sans rien demander : un Pod sans volume n'itère sur rien, la règle ne produit aucune violation, et le Pod passe. En CEL, le même contrôle exige un has(object.spec.volumes) explicite, sans quoi le champ absent est une erreur d'évaluation.

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/"

Le startswith compare des chaînes, sans normaliser la référence d'image : nginx:1.30@sha256:…, qui vient pourtant de Docker Hub, ne commence par aucun des préfixes et est refusé avec [only-trusted-registries] Image 'nginx:1.30@sha256:…' provient d'un registry non autorisé. Pour autoriser Docker Hub, la liste doit porter docker.io/library/ et les manifestes écrire le registre en toutes lettres, exactement comme avec une VAP.

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. Le dépôt gatekeeper-library ne publie aucune release ni aucun tag : la seule référence stable est un SHA de commit, ici celui de master au 5 octobre 2026, et c'est lui qui figure dans les URL. Un raw.githubusercontent.com/.../master/... changerait de contenu sans prévenir.

Fenêtre de terminal
# Installer les cinq templates Pod Security Standards (commit épinglé)
LIB=https://raw.githubusercontent.com/open-policy-agent/gatekeeper-library/d2b86f87b0c98922d0765f6829bdabd75b3348e5/library/pod-security-policy
kubectl apply -f "$LIB/privileged-containers/template.yaml"
kubectl apply -f "$LIB/host-network-ports/template.yaml"
kubectl apply -f "$LIB/users/template.yaml"
kubectl apply -f "$LIB/capabilities/template.yaml"
kubectl apply -f "$LIB/host-filesystem/template.yaml"
Template PSSDescriptionÉquivalent PSA
K8sPSPPrivilegedContainerInterdit privilegedBaseline/Restricted
K8sPSPHostNetworkingPortsContrôle hostNetwork/hostPortBaseline/Restricted
K8sPSPAllowedUsersRestreint UID/GIDRestricted
K8sPSPCapabilitiesContrôle les capabilitiesRestricted
K8sPSPHostFilesystemRestreint hostPathBaseline

Un template installé ne refuse encore rien : il faut l'instancier par une Constraint, exactement comme pour un template écrit soi-même. Celle-ci applique K8sPSPPrivilegedContainer à tous les Pods hors namespaces système :

apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sPSPPrivilegedContainer
metadata:
name: psp-privileged-container
spec:
enforcementAction: deny
match:
kinds:
- apiGroups: [""]
kinds: ["Pod"]
excludedNamespaces:
- kube-system
- gatekeeper-system

Le même debug-pod.yaml que plus haut est alors refusé, avec le message de la librairie cette fois, qui nomme le conteneur et le champ fautif :

Sortie
Error from server (Forbidden): error when creating "debug-pod.yaml": admission webhook
"validation.gatekeeper.sh" denied the request: [psp-privileged-container] Privileged
container is not allowed: debug, securityContext.privileged: true

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

Mesuré en passant la Constraint de l'exemple 1 en warn : le Pod privileged est créé, et kubectl affiche la violation en avertissement, avec le même texte qu'un refus. Le passage se fait par un kubectl patch sur la Constraint, le template ne bouge pas.

Sortie (enforcementAction: warn)
Warning: [deny-privileged-containers] Conteneur 'debug' en mode privileged interdit
pod/debug created

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

Rejoué le 5 octobre 2026 : un Pod privileged créé dans audit-demo avant la Constraint tourne toujours, et le premier cycle d'audit, dans la minute qui suit, l'inscrit dans le status. L'intervalle vaut 60 secondes par défaut (--audit-interval), ce qui explique qu'une violation fraîche mette jusqu'à une minute à apparaître.

Fenêtre de terminal
# Voir les violations détectées par l'audit
kubectl get k8sdenyprivileged deny-privileged-containers -o yaml
Extrait du status
status:
auditTimestamp: "2026-10-05T19:10:13Z"
totalViolations: 1
violations:
- enforcementAction: deny
group: ""
kind: Pod
message: Conteneur 'debug' en mode privileged interdit
name: debug
namespace: audit-demo
version: v1

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 de la section « En cas d'urgence » plus bas : une Constraint fausse découverte en production.

Fenêtre de terminal
# Installer gator : binaire de la release, somme de contrôle vérifiée
curl -fsSLO https://github.com/open-policy-agent/gatekeeper/releases/download/v3.23.1/gator-v3.23.1-linux-amd64.tar.gz
curl -fsSLO https://github.com/open-policy-agent/gatekeeper/releases/download/v3.23.1/sha256sums.txt
grep gator-v3.23.1-linux-amd64.tar.gz sha256sums.txt | sha256sum -c -
tar xzf gator-v3.23.1-linux-amd64.tar.gz && sudo install gator /usr/local/bin/gator
# Évaluer une ressource contre un template et sa constraint
gator test \
--filename template.yaml \
--filename constraint.yaml \
--filename debug-pod.yaml

Sur macOS, brew install gator installe le même binaire. La commande gator test sort en code 1 dès qu'une violation est trouvée et l'affiche, en code 0 sans rien dire quand la ressource passe, ce qui en fait un gardien de pipeline immédiat :

Sortie (gator test, v3.23.1)
v1/Pod debug: ["deny-privileged-containers"] Message: "Conteneur 'debug' en mode privileged interdit"

gator verify n'est pas un contrôle de syntaxe Rego. Il exécute des suites de tests : un fichier Suite déclare, pour chaque template et sa Constraint, des cas qui disent si un objet donné doit produire une violation ou non. C'est l'équivalent d'un test unitaire pour une policy, et c'est lui qu'on branche en intégration continue.

suite.yaml
kind: Suite
apiVersion: test.gatekeeper.sh/v1alpha1
tests:
- name: deny-privileged
template: template.yaml
constraint: constraint.yaml
cases:
- name: pod-privileged-refuse
object: debug-pod.yaml
assertions:
- violations: yes
- name: pod-plain-passe
object: pod-plain.yaml
assertions:
- violations: no
Sortie (gator verify suite.yaml)
ok suite.yaml 0.006s
PASS

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 (action et violations d'audit en colonnes)
kubectl get constraints
# NAME ENFORCEMENT-ACTION TOTAL-VIOLATIONS
# k8spspprivilegedcontainer.../psp-privileged-container deny 1
# 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 à le réactiver après résolution

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, ce que VAP ne fait pas
  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

Ce projet vous est utile ?

Les guides, formations, quiz et labs restent gratuits. Si vous les utilisez régulièrement, vous pouvez contribuer à leur maintenance et à leur développement.

Devenir membre de soutien (nouvel onglet) Soutenir une fois (nouvel onglet) En savoir plus

Suivez aussi le projet sur LinkedIn