Aller au contenu

Sécurité runtime & réponse automatisée avec Falco

Objectifs pédagogiques

  • Comprendre ce que les contrôles préventifs ne peuvent pas voir
  • Déployer Falco sur Kubernetes et écrire des règles propres à son application
  • Router les événements vers l'alerting, les journaux et un moteur de réponse
  • Confiner automatiquement un pod suspect sans détruire les preuves
  • Prouver la couverture de détection avec des tests rejouables et la matrice MITRE ATT&CK

Ce que la prévention ne voit pas


La défense en profondeur

  • Avant le déploiement : SAST, analyse des dépendances, scan d'image, signature
  • À l'admission : Pod Security Admission, politiques Kyverno, vérification de signature
  • À l'exécution : détection des comportements anormaux
  • Une application compromise par une faille inconnue tourne dans une configuration parfaitement conforme
  • Seule l'observation de ce qu'elle fait réellement révèle l'attaque : un shell, une lecture de jeton, une connexion inattendue

MITRE ATT&CK pour les conteneurs

Tactique Exemple sur Kubernetes
Exécution shell ouvert dans un conteneur, kubectl exec
Persistance écriture d'un binaire, CronJob malveillant
Élévation de privilèges conteneur privilégié, montage du système de fichiers de l'hôte
Accès aux identifiants lecture du jeton de ServiceAccount
Découverte requêtes vers l'API Kubernetes depuis un pod
Impact minage de cryptomonnaie, exfiltration de données
  • Chaque règle de détection se rattache à une technique : on sait ce qui est couvert, et ce qui ne l'est pas

Falco


Falco en bref

  • Projet diplômé de la CNCF, standard de fait de la détection runtime sur Kubernetes
  • Capture les appels système dans le noyau, par une sonde eBPF moderne (CO-RE) quand le noyau le permet
  • Enrichit chaque événement : processus, fichier, réseau, conteneur, pod, namespace
  • Évalue les événements contre des règles déclaratives et émet une alerte quand une condition est vraie
  • Des plugins ajoutent d'autres sources : métadonnées des conteneurs, audit Kubernetes, journaux cloud
  • Il détecte : le blocage relève d'autres outils (Tetragon, KubeArmor) ou d'une réponse automatisée

Architecture sur Kubernetes

  • Un pod Falco par nœud (DaemonSet), installé par le chart Helm falcosecurity/falco
  • Le plugin de conteneurs interroge le runtime du nœud : le socket de k3s (/run/k3s/containerd/containerd.sock) est déjà prévu par le chart
  • Sans composant supplémentaire, les métadonnées Kubernetes disponibles sont le nom, le namespace et les labels du pod
  • Le collecteur k8s-metacollector ajoute, si besoin, les métadonnées complètes (Deployment, Service…)
  • Les alertes sortent sur la sortie standard, en JSON, ou vers Falcosidekick

Nœuds k3d ou kind : un noyau partagé

Les nœuds sont des conteneurs qui partagent le noyau de la VM

  • Chaque Falco voit les appels système de tous les nœuds
  • Mais il ne sait enrichir que les conteneurs de son nœud : son plugin n'interroge que le runtime local
  • Constat en test : un shell dans un pod du worker n'est signalé que par le Falco du worker
  • Conséquences :
  • garder Falco sur tous les nœuds ;
  • conditionner les règles aux métadonnées Kubernetes (k8s.ns.name), ce qui évite aussi les doublons

Anatomie d'une règle

- rule: SecurePay - Shell dans un conteneur applicatif
  desc: Un shell est lancé dans un conteneur de l'API SecurePay
  condition: >
    spawned_process and container and proc.name in (shell_binaries)
    and k8s.ns.name in (securepay_namespaces)
  output: >
    Shell dans l'API SecurePay (user=%user.name cmd=%proc.cmdline
    pod=%k8s.pod.name ns=%k8s.ns.name image=%container.image.repository)
  priority: CRITICAL
  tags: [securepay, mitre_execution, T1059]
  • condition : expression sur les champs de l'événement
  • output : message enrichi avec les champs utiles à l'enquête
  • priority : de DEBUG à EMERGENCY, sert au routage et à la réponse

Macros et listes

  • Les règles par défaut fournissent des briques réutilisables :
  • spawned_process : un nouveau processus est exécuté
  • container : l'événement vient d'un conteneur, pas de l'hôte
  • open_read, open_write : ouverture réussie d'un fichier en lecture ou en écriture
  • outbound : connexion réseau sortante
  • listes shell_binaries, rfc_1918_addresses
  • On déclare ses propres listes :
- list: securepay_namespaces
  items: [securepay-staging, securepay-prod]

Deux autres règles maison

- rule: SecurePay - Lecture du jeton de ServiceAccount
  desc: Un processus lit le jeton de ServiceAccount monté dans le conteneur
  condition: >
    open_read and container and k8s.ns.name in (securepay_namespaces)
    and fd.name startswith /var/run/secrets/kubernetes.io/serviceaccount
  output: Lecture du jeton (proc=%proc.name file=%fd.name pod=%k8s.pod.name)
  priority: WARNING
  tags: [securepay, mitre_credential_access, T1528]

- rule: SecurePay - Connexion sortante hors réseau privé
  desc: Une connexion sortante vise une adresse publique
  condition: >
    outbound and container and k8s.ns.name in (securepay_namespaces)
    and not fd.snet in (rfc_1918_addresses)
  output: Connexion sortante (proc=%proc.name connection=%fd.name pod=%k8s.pod.name)
  priority: ERROR
  tags: [securepay, mitre_exfiltration, T1041]

Ajuster sans réécrire

# Exclure un namespace d'outillage d'une règle par défaut
- rule: Terminal shell in container
  condition: and not k8s.ns.name = "debug-tools"
  override:
    condition: append
  • Une règle par défaut s'ajuste par override, sans copier ni modifier le fichier d'origine
  • Chaque exception est justifiée et versionnée : une exception trop large est une zone aveugle
  • Les règles sont validées au démarrage : une erreur de syntaxe empêche Falco de démarrer

Router & répondre


Falcosidekick

  • Reçoit les événements de Falco et les diffuse vers plusieurs destinations
  • Plus de 50 sorties : Alertmanager, Loki, messageries, webhooks, files de messages, moteurs de réponse
  • Un seuil de priorité par sortie : tout vers Loki, seulement le critique vers l'astreinte
  • La détection rejoint ainsi la chaîne d'alerting et d'automatisation existante
falcosidekick:
  enabled: true
  config:
    alertmanager:
      hostport: http://kube-prometheus-stack-alertmanager.monitoring:9093
      minimumpriority: error
    talon:
      address: http://falco-talon:2803
      minimumpriority: critical

Quelle réponse automatique ?

Réponse Avantage Limite
Notifier aucun risque l'attaquant garde la main
Isoler le pod stoppe la propagation, conserve les preuves à préparer à l'avance
Restreindre la sortie réseau bloque l'exfiltration peut toucher toutes les répliques
Tuer le pod arrêt immédiat efface les preuves, le pod est recréé avec la même faille
Capturer (logs, trafic) matière pour l'enquête ne stoppe rien
  • La réponse automatique est proportionnée à la priorité de la règle
  • Isoler et conserver d'abord, analyser ensuite

Falco Talon

  • Moteur de réponse du projet Falco, en statut incubating
  • Reçoit les événements de Falcosidekick et déclenche des actions selon des règles
  • Actionneurs Kubernetes : label, networkpolicy, terminate, log, tcpdump, exec…
  • Déduplication des événements sur une courte fenêtre de temps
  • Par défaut, son chart lui accorde des droits larges (y compris la suppression de namespaces) : restreignez son ClusterRole aux actionneurs utilisés

Une quarantaine par les labels

- action: Quarantaine du pod
  actionner: kubernetes:label
  parameters:
    labels:
      quarantine: "true"
      app.kubernetes.io/name: ""   # valeur vide : le label est retiré

- rule: Quarantaine SecurePay
  match:
    rules:
      - SecurePay - Shell dans un conteneur applicatif
  actions:
    - action: Quarantaine du pod
  • Retirer le label du sélecteur sort le pod de son ReplicaSet, qui recrée un remplaçant
  • Le pod n'est plus derrière le Service, mais il reste en vie pour l'analyse

Isoler le pod en quarantaine

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: quarantine-deny-all
  namespace: securepay-prod
spec:
  podSelector:
    matchLabels:
      quarantine: "true"
  policyTypes: [Ingress, Egress]
  • Créée à l'avance, versionnée, synchronisée par ArgoCD
  • Testé : le pod isolé n'est plus joignable et ne peut plus sortir, ses voisins sains fonctionnent normalement

Le piège de l'actionneur networkpolicy

  • Il construit sa NetworkPolicy à partir du sélecteur du propriétaire du pod (Deployment, StatefulSet…)
  • Elle s'applique donc à toutes les répliques, pas seulement au pod compromis
  • Pour un service en production, l'effet ressemble à une panne provoquée par la défense
  • À réserver aux cas où bloquer toute la sortie du workload est le comportement voulu
  • Une réponse automatique se teste comme une expérience de chaos, rayon d'impact compris

Prouver la détection


Des tests de détection rejouables

# security-tests/runtime-detection.sh (extrait)
POD=$(kubectl -n securepay-staging get pod -l app.kubernetes.io/name=securepay-api -o name | head -1)
kubectl -n securepay-staging exec "$POD" -- sh -c 'id'
kubectl -n securepay-staging exec "$POD" -- cat /var/run/secrets/kubernetes.io/serviceaccount/token
kubectl -n securepay-staging exec "$POD" -- wget -q -T 3 -O /dev/null http://1.1.1.1
  • Chaque règle maison a son test, qui simule le comportement surveillé
  • Le script vérifie ensuite que l'alerte est bien arrivée (journaux Falco ou Loki)
  • Il est joué sur staging, sur un pod jetable, jamais sur la production

La matrice de couverture

Règle Technique ATT&CK Test Réponse Preuve
Shell dans l'API T1059 Exécution exec sh -c id quarantaine docs/preuves/...
Lecture du jeton T1528 Identifiants exec cat token alerte docs/preuves/...
Connexion sortante T1041 Exfiltration exec wget 1.1.1.1 alerte docs/preuves/...
  • Les lignes vides de la matrice sont aussi importantes que les pleines : ce sont les angles morts connus

Le runbook de réponse à incident

  1. Confirmer : lire l'alerte, son contexte et les événements voisins
  2. Contenir : vérifier que la quarantaine est effective, l'étendre si nécessaire
  3. Préserver : journaux, état du pod, processus et fichiers du conteneur isolé
  4. Éradiquer : corriger la faille et redéployer une image saine par la chaîne normale
  5. Rétablir : vérifier le retour au SLO, supprimer le pod isolé une fois l'analyse terminée
  6. Apprendre : postmortem blameless, nouvelle règle ou nouveau test si un angle mort est apparu

Anti-patterns à proscrire

  • Activer toutes les règles sans réglage : la fatigue d'alerte arrive en une journée
  • Tuer automatiquement les pods suspects et perdre toute trace de l'attaque
  • Des exceptions qui excluent un namespace entier « pour avoir la paix »
  • Un moteur de réponse avec les droits cluster-admin
  • Des règles jamais testées, dont on découvre l'inefficacité pendant un incident

À retenir

  • La détection runtime voit ce que la prévention ne peut pas voir : le comportement réel d'une application compromise
  • Falco capture les appels système par eBPF et les évalue contre des règles, enrichies des métadonnées Kubernetes
  • Sur des nœuds qui partagent un noyau, Falco tourne sur tous les nœuds et les règles s'appuient sur k8s.ns.name
  • Falcosidekick route, Talon répond : isoler et conserver plutôt que tuer
  • Chaque règle a son test, sa technique ATT&CK et sa preuve

Pour aller plus loin