Security Chaos Engineering
Objectifs pédagogiques
- Traiter les contrôles de sécurité comme des systèmes qui tombent en panne, et le vérifier par l'expérience
- Choisir et documenter un comportement fail-open ou fail-closed pour chaque contrôle
- Mesurer le comportement de la plateforme quand le contrôleur d'admission, le coffre ou les certificats font défaut
- Simuler un attaquant et une dérive de configuration pour éprouver la détection et la réponse
- Chronométrer une restauration après destruction de données
Principes
Pourquoi éprouver les contrôles de sécurité
- Un contrôle de sécurité est un composant comme un autre : il plante, sature, expire, se désynchronise
- Contrôleur d'admission injoignable, coffre scellé, certificat expiré, pipeline de journaux arrêté, fournisseur d'identité en panne
- Ces défaillances se découvrent souvent pendant un incident, au pire moment
- Un contrôle qu'on n'a jamais vu échouer, on ne sait pas comment il échoue
- Le Security Chaos Engineering provoque ces défaillances volontairement, dans un cadre maîtrisé
Ce que ce n'est pas
| Pentest / Red Team | Security Chaos Engineering |
| Question posée | « Peut-on entrer ? » | « Nos contrôles se comportent-ils comme prévu ? » |
| Fréquence | ponctuelle | continue, rejouable |
| Méthode | exploration offensive | hypothèse, injection, mesure |
| Cible | failles | défaillances des contrôles, détection, réponse |
| Résultat | rapport de vulnérabilités | écarts à l'hypothèse et actions correctives |
- Les deux sont complémentaires : l'un cherche des portes, l'autre vérifie les serrures et les alarmes
Anatomie d'une expérience
- Hypothèse sur un contrôle : « si Kyverno est injoignable, aucune image non conforme n'est admise »
- Conditions d'arrêt et rayon d'impact : staging d'abord, un namespace ciblé
- Injection de la défaillance ou du comportement hostile
- Observation sur quatre axes : sécurité, disponibilité, détection, réponse
-
Apprentissage : l'écart à l'hypothèse devient une action corrective
-
Une hypothèse invalidée est un résultat, pas un échec
Quatre familles d'expériences
| Famille | Exemples | Ce qu'on vérifie |
| Panne d'une brique de sécurité | Kyverno, OpenBao, cert-manager, collecte des journaux | comportement dégradé, alerte |
| Simulation d'attaquant | shell, vol de jeton, connexion sortante | détection, confinement |
| Dérive de configuration | NetworkPolicy supprimée, pod privilégié, RBAC ajouté | blocage, réconciliation, audit |
| Destruction de données | namespace supprimé, rançongiciel | restauration, RTO, RPO |
Quand une brique de sécurité tombe
Fail-open ou fail-closed
| Comportement | Si le contrôle tombe | Risque |
| Fail-closed | l'action est refusée | indisponibilité : plus aucun déploiement |
| Fail-open | l'action est autorisée sans contrôle | contournement silencieux |
- Aucun choix n'est bon dans l'absolu : c'est un arbitrage sécurité / disponibilité, contrôle par contrôle
- Le choix est documenté (ADR), justifié par le risque et assorti d'une alerte
- Un fail-open non surveillé est une porte ouverte que personne ne voit
Les webhooks d'admission
apiVersion: policies.kyverno.io/v1
kind: ValidatingPolicy
metadata:
name: disallow-latest-tag
spec:
validationActions: [Deny]
failurePolicy: Fail # ou Ignore
webhookConfiguration:
timeoutSeconds: 5
matchConstraints:
namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: securepay-prod
resourceRules:
- apiGroups: [""]
apiVersions: ["v1"]
operations: ["CREATE", "UPDATE"]
resources: ["pods"]
validations:
- message: "Tag latest interdit"
expression: "object.spec.containers.all(c, !c.image.endsWith(':latest'))"
failurePolicy se règle par politique : Kyverno crée un webhook dédié pour chacune
Ce que montre l'expérience
Mesures sur Kyverno 1.19 (politique ci-dessus)
| Scénario | Résultat |
Kyverno injoignable (partition), Fail | même un pod conforme est refusé : failed calling webhook |
Kyverno injoignable (partition), Ignore | une image latest est admise sans contrôle |
| Arrêt propre de Kyverno (0 réplica) | ses webhooks sont supprimés : tout est admis, quel que soit failurePolicy |
| 3 réplicas, un pod tué | aucun refus, contrôle maintenu |
failurePolicy ne joue que si le webhook existe mais ne répond plus
Les leçons
- Haute disponibilité : plusieurs réplicas et un PodDisruptionBudget, pour qu'un drain ou une mise à jour ne fasse jamais tomber Kyverno à zéro
- Alerter sur la disponibilité du contrôleur et sur la présence de ses webhooks
- Couche native pour les règles critiques : Pod Security Admission et
ValidatingAdmissionPolicy sont évalués par l'API Server lui-même, sans dépendance externe - Documenter le choix
Fail / Ignore de chaque politique et le comportement constaté - Rejouer l'expérience après chaque montée de version du contrôleur
Une règle native qui survit à Kyverno
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
name: disallow-latest-tag-native
spec:
failurePolicy: Fail
matchConstraints:
resourceRules:
- apiGroups: [""]
apiVersions: ["v1"]
operations: ["CREATE", "UPDATE"]
resources: ["pods"]
validations:
- expression: "object.spec.containers.all(c, !c.image.endsWith(':latest'))"
message: "Tag latest interdit"
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicyBinding
metadata: {name: disallow-latest-tag-native}
spec:
policyName: disallow-latest-tag-native
validationActions: [Deny]
matchResources:
namespaceSelector: {matchLabels: {kubernetes.io/metadata.name: securepay-prod}}
Le coffre et les certificats
- OpenBao indisponible : l'application continue avec le dernier secret synchronisé, mais aucune rotation n'est possible, et l'instance redémarre scellée
- Certificat perdu : cert-manager le réémet dès que le Secret disparaît (constaté en une seconde)
- Certificat proche de l'expiration : alerter bien avant l'échéance, car un renouvellement peut échouer silencieusement
# Expire dans moins de 7 jours
certmanager_certificate_expiration_timestamp_seconds - time() < 7 * 24 * 3600
# Certificat non prêt
certmanager_certificate_ready_status{condition="False"} == 1
Quand la détection devient aveugle
- Si la collecte des journaux s'arrête, plus aucune alerte ne se déclenche, et rien ne le signale
- L'absence de données doit elle-même déclencher une alerte
# Plus aucun événement d'audit Kubernetes depuis 10 minutes
absent_over_time({job="kube-audit"}[10m])
- Même logique pour Falco et Falcosidekick : alerter sur leur disponibilité, et rejouer régulièrement un test de détection
- Un pipeline de détection se teste comme une sonde de supervision : par un signal connu, émis exprès
Simuler l'attaquant & la dérive
Simuler un attaquant
- Un pod jetable en staging rejoue des actions offensives simples :
- shell interactif dans un conteneur applicatif ;
- lecture du jeton de ServiceAccount puis appel à l'API Kubernetes ;
- connexion sortante vers une adresse publique ;
- tentative de lecture d'un secret d'un autre environnement
- Hypothèse : chaque action est détectée, la plus grave est confinée automatiquement
- Mesures : temps de détection, temps de confinement, cause établie, preuves conservées
Injecter une dérive
| Injection | Contrôle attendu | Mesure |
| Suppression d'une NetworkPolicy | ArgoCD selfHeal la recrée | délai de réconciliation |
| Retrait du label PSA du namespace | ArgoCD le rétablit | délai, fenêtre d'exposition |
| Pod privilégié | refus à l'admission | message, traçabilité |
| ClusterRoleBinding ajouté à la main | alerte d'audit, retrait par GitOps | délai de détection |
- La fenêtre d'exposition entre la dérive et sa correction est une donnée de risque à mesurer
Destruction & reprise
Simuler un rançongiciel
- Hypothèse : après la suppression du namespace de production, le service est rétabli dans le RTO, avec une perte de données inférieure au RPO
- Le RPO découle de la fréquence des sauvegardes, le RTO se mesure au chronomètre
- Le RTO réel inclut tout : restauration, redescellement d'OpenBao, resynchronisation des secrets, vérification du SLO
- Les sauvegardes doivent survivre à l'attaquant : stockage objet séparé, chiffré, verrouillé
- Une sauvegarde jamais restaurée n'est qu'une hypothèse
Avec Velero
# Sauvegarde avant l'expérience (volumes inclus)
velero backup create avant-gameday \
--include-namespaces securepay-prod --default-volumes-to-fs-backup --wait
# Destruction
kubectl delete namespace securepay-prod
# Restauration chronométrée
time velero restore create --from-backup avant-gameday --wait
# En routine : une sauvegarde par heure, conservée 7 jours
velero schedule create securepay-prod-horaire --schedule="0 * * * *" \
--include-namespaces securepay-prod --ttl 168h
Industrialiser
Le GameDay sécurité
- Préparer : catalogue d'expériences, hypothèses, conditions d'arrêt, rôles (animateur, attaquant, défense, observateur)
- Prévenir : les équipes concernées savent qu'un exercice a lieu, pas forcément quand
- Dérouler : une expérience à la fois, chronomètre en main, preuves horodatées
- Restituer : écarts à l'hypothèse, causes, actions correctives avec responsables
- Rejouer : chaque correction est vérifiée par la même expérience
Automatiser ce qui peut l'être
- Chaos Mesh injecte les pannes de pods et de réseau sur les briques de sécurité (
PodChaos sur OpenBao ou Kyverno, NetworkChaos) - Un
Workflow Chaos Mesh rejoue la séquence en staging, à intervalle régulier - Les scripts d'attaque simulée et de dérive sont versionnés dans
security-tests/ - Chaque exécution produit ses preuves : état attendu, état constaté, durées
- Ce qui n'est pas automatisable (descellement humain, décision d'arbitrage) est chronométré lors des GameDays
Les indicateurs
| Indicateur | Ce qu'il mesure |
| Taux d'expériences conformes à l'hypothèse | maturité des contrôles |
| Temps de détection | efficacité de la surveillance |
| Temps de confinement | efficacité de la réponse |
| Fenêtre d'exposition des dérives | rapidité de la réconciliation |
| RTO et RPO constatés | capacité de reprise |
| Contrôles fail-open non surveillés | angles morts restants |
Anti-patterns à proscrire
- Supposer qu'un contrôle fonctionne parce qu'il a été déployé
- Choisir
Ignore par confort, sans alerte ni justification - Laisser un contrôleur d'admission critique à un seul réplica
- Lancer une expérience destructrice en production sans l'avoir jouée en staging
- Des sauvegardes jamais restaurées, ou stockées là où l'attaquant peut les supprimer
- Un GameDay sans restitution ni action corrective
À retenir
- Les contrôles de sécurité tombent en panne comme le reste : il faut savoir comment, avant l'incident
Fail protège mais bloque, Ignore laisse passer en silence ; et un arrêt propre de Kyverno retire ses webhooks - Haute disponibilité, alertes sur la présence des contrôles, et une couche native pour les règles critiques
- Une détection qui ne reçoit plus de données doit le signaler elle-même
- Le RTO réel se mesure en restaurant, descellement du coffre compris
Pour aller plus loin