Aller au contenu

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

  1. Hypothèse sur un contrôle : « si Kyverno est injoignable, aucune image non conforme n'est admise »
  2. Conditions d'arrêt et rayon d'impact : staging d'abord, un namespace ciblé
  3. Injection de la défaillance ou du comportement hostile
  4. Observation sur quatre axes : sécurité, disponibilité, détection, réponse
  5. Apprentissage : l'écart à l'hypothèse devient une action corrective

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