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
- Confirmer : lire l'alerte, son contexte et les événements voisins
- Contenir : vérifier que la quarantaine est effective, l'étendre si nécessaire
- Préserver : journaux, état du pod, processus et fichiers du conteneur isolé
- Éradiquer : corriger la faille et redéployer une image saine par la chaîne normale
- Rétablir : vérifier le retour au SLO, supprimer le pod isolé une fois l'analyse terminée
- 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