Policy-as-code : sécuriser Kubernetes et l'IaC avec OPA/Rego et Kyverno
Le policy as code OPA Rego Kubernetes permet de refuser un pod privilégié avant qu'il ne démarre. OPA/Rego vs Kyverno, exemples, tests, et preuve NIS2/DORA.
Un pod tourne en privileged: true en production. Personne ne l'a décidé explicitement : un manifeste copié d'un tutoriel, un Dockerfile qui n'a jamais été revu, une image tirée d'un registre public sans vérification de signature. Le cluster l'a accepté parce que rien, techniquement, ne l'en empêchait. C'est exactement le problème que résout le policy as code : transformer une règle écrite dans un document de sécurité en barrière exécutée par la plateforme elle-même, au moment où l'objet est créé, pas après coup dans un rapport d'audit.
L'admission control, concrètement
Kubernetes expose un point d'ancrage précis pour ce contrôle : l'admission webhook. Chaque requête de création ou de modification d'objet (un pod, un déploiement, une règle réseau) transite par une chaîne de validation avant d'être persistée dans etcd. Un moteur de policy branché à cet endroit peut refuser la requête avant même que le pod ne démarre.
Deux cas concrets, fréquents en audit :
- Un pod demande
securityContext.privileged: trueou monte le socket Docker de l'hôte : sans policy, il démarre normalement et dispose d'un accès quasi root sur le nœud. - Une image provient d'un registre non vérifié ou n'est pas signée (Cosign, Notary) : sans policy, Kubernetes ne fait aucune différence entre une image signée par la chaîne CI officielle et une image poussée manuellement par n'importe qui disposant des identifiants du registre.
Dans les deux cas, l'admission control transforme un incident potentiel en un refus silencieux et journalisé, avant impact.
OPA/Rego ou Kyverno : deux philosophies
Deux moteurs dominent ce terrain, avec des logiques différentes.
OPA (Open Policy Agent) et son langage Rego sont généralistes. Le même moteur peut valider un manifeste Kubernetes, un plan Terraform avant apply, une règle de routage Envoy, ou une étape de pipeline CI/CD. Rego est un langage de requête logique à part entière, avec sa propre courbe d'apprentissage : il faut penser en termes de faits et de règles, pas en YAML séquentiel. C'est la contrepartie de sa versatilité multi-stack.
Kyverno est natif Kubernetes et s'écrit en YAML pur, sans langage tiers à apprendre. Une équipe qui maîtrise déjà les manifestes Kubernetes écrit une policy Kyverno en quelques minutes. La limite est le périmètre : Kyverno ne sort pas de l'écosystème Kubernetes, il ne validera pas un plan Terraform ou une règle Envoy.
En pratique, l'approche hybride est la plus courante en environnement mature : Kyverno pour les policies purement Kubernetes à fort volume et faible complexité (labels obligatoires, restrictions de securityContext, quotas), OPA/Rego pour tout ce qui traverse plusieurs stacks (IaC, API gateway, pipelines) ou nécessite une logique de décision plus fine. Le mauvais réflexe est de choisir un moteur unique par dogme plutôt que par périmètre réel.
Anatomie d'une policy Rego
Une policy Rego s'organise en package (le namespace logique) et en règles. La règle deny est la convention la plus répandue pour l'admission control : chaque instance de deny qui s'évalue à vrai bloque la requête et renvoie le message associé.
package kubernetes.admission
deny[msg] {
input.request.kind.kind == "Pod"
container := input.request.object.spec.containers[_]
container.securityContext.privileged == true
msg := sprintf(
"Le conteneur '%v' du pod '%v' est privilegie : refuse par la policy.",
[container.name, input.request.object.metadata.name],
)
}La structure est lisible : package isole la policy, la règle deny décompose la condition (type d'objet, itération sur les conteneurs, vérification du champ), et msg construit le message renvoyé à l'utilisateur qui tente le déploiement. C'est ce message qui apparaît dans le retour kubectl apply, pas une erreur générique.
L'équivalent Kyverno pour la même règle tient en YAML déclaratif, sans logique de requête à écrire :
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: disallow-privileged-containers
spec:
validationFailureAction: Enforce
rules:
- name: block-privileged
match:
resources:
kinds: ["Pod"]
validate:
message: "Les conteneurs privilegies sont interdits."
pattern:
spec:
containers:
- =(securityContext):
=(privileged): "false"Même intention, deux syntaxes. Le choix dépend du périmètre à couvrir, pas de la préférence stylistique de l'équipe.
Tester avant de bloquer un déploiement critique
Une policy mal écrite est aussi dangereuse qu'une absence de policy : elle peut bloquer un déploiement légitime en pleine astreinte, ou au contraire laisser passer un cas qu'elle croyait couvrir. Deux outils évitent cette double peine.
opa test exécute des tests unitaires écrits en Rego contre la policy, avant tout déploiement en admission controller :
opa test policies/ -vpackage kubernetes.admission
test_privileged_pod_denied {
deny["Le conteneur 'app' du pod 'demo' est privilegie : refuse par la policy."] with input as {
"request": {
"kind": {"kind": "Pod"},
"object": {
"metadata": {"name": "demo"},
"spec": {"containers": [{"name": "app", "securityContext": {"privileged": true}}]},
},
},
}
}conftest applique le même principe directement sur des fichiers de manifestes ou des plans Terraform exportés en JSON, ce qui permet de tester la policy en pipeline CI/CD avant même qu'elle n'atteigne le cluster :
conftest test --policy policies/ deployment.yamlL'ordre de déploiement recommandé reste constant : opa test pour valider la logique de la règle elle-même, conftest test en CI pour valider un large corpus de manifestes réels sans régression, et seulement ensuite l'activation en mode enforcement dans le cluster. Sauter cette étape, c'est le scénario classique de l'astreinte réveillée à 2h du matin par un déploiement bloqué pour une raison qui n'a jamais été testée.
De la policy technique à la preuve de conformité
NIS2 et DORA exigent des contrôles techniques documentés et, de plus en plus, des preuves d'automatisation plutôt que des procédures manuelles déclaratives. Le policy as code fournit un mapping direct entre une exigence réglementaire et une règle exécutée :
| Exigence NIS2/DORA | Policy technique correspondante |
|---|---|
| Chiffrement des données au repos et en transit | Rego/Kyverno refusant un PVC sans chiffrement, ou un service sans TLS |
| Segmentation réseau | NetworkPolicy Kubernetes générée et validée par policy, empêchant le trafic pod-to-pod non autorisé |
| Gestion des accès (moindre privilège) | Règles RBAC auditées automatiquement, refus des ClusterRoleBinding trop larges |
| Intégrité de la chaîne logicielle | Refus d'admission des images non signées |
Ce mapping n'est pas cosmétique : dans un audit NIS2 ou DORA, prouver qu'un contrôle est appliqué automatiquement et systématiquement (et pas seulement documenté dans une procédure) est ce qui distingue une conformité auditée d'une conformité déclarative. Le journal de refus de l'admission controller devient une pièce de preuve directement exploitable.
En résumé
Le policy as code déplace le contrôle de sécurité du document vers l'exécution : une policy Rego ou Kyverno refuse la dérive avant qu'elle ne produise un incident, et non plusieurs semaines après dans un rapport d'audit. Le choix du moteur suit le périmètre (Kyverno pour du Kubernetes pur, OPA/Rego pour du multi-stack), et le passage en production ne se fait jamais sans tests unitaires préalables. Le mapping vers NIS2/DORA transforme ensuite chaque policy technique en élément de preuve de conformité automatisée.
C'est précisément ce que fournit le pack de policies ThreatClaw : des règles Rego et Kyverno pré-testées, prêtes pour l'admission control, avec le mapping de conformité déjà établi, pour éviter d'écrire et de valider chaque règle sous pression au moment de l'audit.
Articles liés
Google observe des clusters attaqués en 18 minutes et des évasions via pods privilégiés. Voici la détection Falco au runtime et les garde-fous OPA à l'admission.
Chaque abonnement feed ThreatClaw est désormais livré avec une carte de couverture par exigence NIS2 et ReCyF, un export OSCAL et un connecteur qui pré-remplit votre GRC. Voici exactement comment ça marche, et ce que ça ne prétend pas faire.
La Commission a saisi la CJUE contre la France le 8 juillet 2026 pour non-transposition de NIS2. Sanctions en vue, loi résilience repoussée : ce qu'il faut savoir.
OPA Rego permissions agent IA MCP : comment interposer une décision de politique-as-code avant chaque appel d'outil, selon le rôle, la sensibilité et l'heure.