Détection d'Intrusions Runtime avec Falco et Falcosidekick
Détection d'Intrusions Runtime avec Falco et Falcosidekick
Défense en profondeur : Filtrage de périmètre vs Détection runtime
La sécurité du système est organisée en couches étanches : 1. Filtrage de périmètre (L7) : Traefik et CrowdSec analysent les requêtes HTTP/HTTPS entrantes et bloquent les scanners et attaques Web. 2. Surveillance d'exécution (Runtime) : Falco inspecte directement les appels système (syscalls) émis vers le noyau Linux par l'intermédiaire de sondes eBPF et de tracepoints du kernel.
Si un attaquant parvient à compromettre une application (par exemple via une vulnérabilité RCE ou une injection de commande dans un conteneur), CrowdSec ne voit plus rien car le trafic initial a déjà été acquitté. Falco intervient alors en détection d'exécution (runtime) en détectant les comportements anormaux au sein du conteneur (spawn d'un shell, lecture de /etc/shadow, exécution de binaire téléchargé, reverse-shell).
1. Architecture du Pipeline de Détection
graph TD
subgraph Host_Kernel["Noyau Linux - Espace Kernel"]
Trace["Sondes eBPF / Kernel Tracepoints"]
Syscalls["Appels Système (syscalls)"]
Syscalls --> Trace
end
Trace -->|"Flux d'événements"| Falco["Conteneur Falco (Mode Privilégié)"]
subgraph Falco_Engine["Moteur de Règles Falco"]
DefaultRules["Règles Officielles Falco"]
CustomRules["Règles Locales Personnalisées"]
Engine["Moteur de Corrélation Heuristique"]
DefaultRules --> Engine
CustomRules --> Engine
end
Falco --> Falco_Engine
Falco_Engine -->|"Flux HTTP JSON"| Sidekick["Falcosidekick"]
Sidekick -->|"Notification Discord"| Discord["Salon Discord d'alertes"]
Sidekick -->|"SMTP Local"| Mailrise["Mailrise (Notifications Sysadmin)"]
2. Déploiement Docker Compose (docker-compose.yaml)
Falco s'exécute dans un conteneur privilégié pour lui permettre de communiquer directement avec le sous-système de traçage du noyau de la machine hôte :
services:
falco:
stdin_open: true
tty: true
image: falcosecurity/falco:latest
container_name: falco
privileged: true
restart: always
environment:
- TZ=${TIME_ZONE}
volumes:
# Accès aux points de traçage du noyau
- /sys/kernel/tracing:/sys/kernel/tracing:ro
# Corrélation des conteneurs via le socket Docker
- /var/run/docker.sock:/host/var/run/docker.sock
# Espaces de nommage des processus et arborescence système hôte
- /proc:/host/proc:ro
- /etc:/host/etc:ro
# Règles locales personnalisées
- $INFRASTRUCTURE_FOLDER_LOCATION/variable-falco_rules.local.yaml:/etc/falco/falco_rules.local.yaml
command:
- /usr/bin/falco
- -o
- json_output=true
- -o
- json_include_output_property=true
- -o
- http_output.enabled=true
- -o
- http_output.url=http://falcosidekick:2801/
- -o
- plugins[0].init_config.engine_timeout=60
networks:
internal-net:
ipv4_address: 10.2.1.44
depends_on:
- falcosidekick
falcosidekick:
container_name: falcosidekick
image: falcosecurity/falcosidekick:latest
restart: always
environment:
- DISCORD_WEBHOOKURL=${FALCOSIDEKICK_DISCORD_WEBHOOKURL}
- DISCORD_MINIMUMPRIORITY=notice
networks:
internal-net:
ipv4_address: 10.2.1.45
Timeout Engine et Métadonnées Docker
Le paramètre engine_timeout=60 évite les faux positifs et les alertes intempestives lors du redémarrage d'autres conteneurs : il laisse le temps à Falco de collecter les métadonnées Docker (nom du conteneur, image) avant de statuer sur la légitimité d'un événement.
3. Personnalisation des Règles : variable-falco_rules.local.yaml
Les règles standard de Falco sont conçues pour des clusters Kubernetes stricts. Dans d'autre contexte multi-services avec Docker Compose, certains comportements légitimes déclenchent naturellement des alertes s'ils ne sont pas calibrés :
* L'exécution de shells légitimes dans les conteneurs administratifs (cronjob, open-terminal, scripts de build MkDocs).
* Les healthchecks réseau internes (ping google.fr dans les conteneurs WireGuard/Transmission).
* Les sockets réseau bruts créés par Home Assistant pour la découverte IoT.
* Les pilotes NVIDIA Container Runtime (nvidia-ctk) créant des exécutables anonymes en mémoire (memfd).
Le fichier de règles locales adapte Falco sans abaisser le niveau de vigilance :
# 1. Autoriser les tests de connectivité (healthchecks) de Transmission et Traefik
- macro: transmission_healthcheck
condition: ((container.name = "transmission" or container.name endswith "_transmission") and proc.name = "ping")
- macro: traefik_healthcheck
condition: (container.image.repository = "traefik" and proc.name = "ping")
- macro: user_known_stand_streams_redirect_activities
override:
condition: append
condition: or (transmission_healthcheck or traefik_healthcheck)
# 2. Whitelisting des binaires administratifs du conteneur de maintenance (cronjob)
- macro: cronjob_safe_bins
condition: ((container.name = "cronjob" or container.image.repository = "cytopia/ansible" or proc.aname in (cron-wrapper, supercronic)) and proc.name in (curl, rsync, xargs, supercronic, find, jq, docker))
- macro: known_drop_and_execute_activities
override:
condition: append
condition: or (cronjob_safe_bins or open_terminal_drop_and_execute)
# 3. Autorisation des shells interactifs attendus (Open WebUI Terminal & Cronjob)
- macro: cronjob_shell
condition: ((container.name = "cronjob" or container.image.repository = "cytopia/ansible") and proc.name in (bash, sh))
- macro: open_terminal_shell
condition: (container.name = "open-terminal" or container.image.repository = "ghcr.io/open-webui/open-terminal")
- macro: user_expected_terminal_shell_in_container_conditions
override:
condition: append
condition: or (cronjob_shell or open_terminal_shell)
# 4. Autoriser les bibliothèques GPU NVIDIA CTK
- macro: nvidia_ctk_memfd_ldconfig
condition: (proc.pname = "nvidia-ctk" and proc.exepath startswith "memfd:runc_cloned:" and proc.exepath endswith "ldconfig")
- macro: known_memfd_execution_processes
override:
condition: append
condition: or (nvidia_ctk_memfd_ldconfig)
# 5. Autoriser les paquets bruts pour la découverte réseau domotique (Home Assistant)
- macro: homeassistant_packet_socket
condition: ((container.image.repository = "ghcr.io/home-assistant/home-assistant" or proc.cmdline contains "homeassistant") and proc.name = "python3")
- rule: Packet socket created in container
override:
condition: append
condition: and not homeassistant_packet_socket
4. Exemple Concret d'Alerte de Sécurité
Lorsqu'une commande hostile non autorisée est lancée (par exemple, un attaquant ouvrant un shell interactif dans le conteneur Nextcloud) :
1. Le noyau Linux transmet l'appel système execve("/bin/bash") à la sonde eBPF de Falco.
2. Falco évalue la règle Terminal shell in container.
3. Le conteneur n'étant pas dans la liste d'exceptions (open-terminal, cronjob), Falco émet un événement JSON de priorité Notice ou Warning.
4. falcosidekick reçoit le payload et poste instantanément dans Discord #alerte :
{
"output": "10:14:22.183742419: Notice A shell was spawned in a container with an attached terminal (user=www-data user_loginuid=-1 k8s.ns=<NA> k8s.pod=<NA> container=nextcloud image=nextcloud:latest connection=<NA> shell=bash parent=apache2 cmdline=bash)",
"priority": "Notice",
"rule": "Terminal shell in container",
"time": "2026-09-26T10:14:22.183742419Z"
}
- Un webhook ou agent de supervision intercepte le message et notifie immédiatement les administrateurs pour investigation.
5. Maintenance Automatisée
Chaque matin à 06h00, un script de vérification peut contrôler l'état opérationnel des conteneurs :
for container in falco falcosidekick; do
if ! docker ps --format '{{.Names}}' | grep -q "^${container}$"; then
echo "Conteneur ${container} inactif ! Redémarrage..."
docker start "$container"
fi
done
6. Playbook Ansible Prêt à l'Emploi (playbook-falco.yaml)
Ce playbook autonome installe et orchestre la stack de détection runtime : création du dossier /opt/falco, déploiement des règles locales personnalisées, génération du docker-compose.yaml, lancement des conteneurs et mise en place de la surveillance quotidienne :
---
- name: Déploiement de la Détection Runtime Falco et Falcosidekick
hosts: all
become: true
vars:
# Répertoire d'installation de la stack
falco_dir: "/opt/falco"
# Webhook Discord pour la réception des alertes (remplacer par votre URL)
discord_webhook_url: "https://discord.com/api/webhooks/VOTRE_WEBHOOK_ICI"
# Priorité minimale d'alerte (notice, warning, error, critical)
discord_min_priority: "notice"
tasks:
# -------------------------------------------------------------------------
# 1. CONTRÔLE DES PRÉREQUIS DOCKER
# -------------------------------------------------------------------------
- name: Vérifier la présence de Docker
ansible.builtin.command: which docker
register: docker_check
changed_when: false
failed_when: false
- name: Interrompre si Docker n'est pas installé
ansible.builtin.meta: end_host
when: docker_check.rc != 0
- name: Créer le répertoire de configuration Falco
ansible.builtin.file:
path: "{{ falco_dir }}"
state: directory
mode: '0755'
owner: root
group: root
# -------------------------------------------------------------------------
# 2. DÉPLOIEMENT DES RÈGLES LOCALES PERSONNALISÉES
# -------------------------------------------------------------------------
- name: Déployer le fichier de règles locales /opt/falco/falco_rules.local.yaml
ansible.builtin.copy:
dest: "{{ falco_dir }}/falco_rules.local.yaml"
mode: '0644'
owner: root
group: root
content: |
# 1. Autoriser les tests de connectivité (healthchecks) de conteneurs
- macro: app_healthcheck
condition: (proc.name = "ping")
- macro: user_known_stand_streams_redirect_activities
override:
condition: append
condition: or (app_healthcheck)
# 2. Whitelisting des binaires de maintenance administrative
- macro: admin_safe_bins
condition: (proc.name in (curl, rsync, xargs, find, jq, docker))
- macro: known_drop_and_execute_activities
override:
condition: append
condition: or (admin_safe_bins)
# 3. Autoriser les shells interactifs pour les conteneurs d'administration
- macro: admin_shell
condition: (container.name in ("open-terminal", "cronjob") and proc.name in (bash, sh))
- macro: user_expected_terminal_shell_in_container_conditions
override:
condition: append
condition: or (admin_shell)
# 4. Autoriser les paquets bruts pour la domotique (Home Assistant)
- macro: homeassistant_packet_socket
condition: ((container.image.repository = "ghcr.io/home-assistant/home-assistant" or proc.cmdline contains "homeassistant") and proc.name = "python3")
- rule: Packet socket created in container
override:
condition: append
condition: and not homeassistant_packet_socket
# -------------------------------------------------------------------------
# 3. DÉPLOIEMENT DU FICHIER DOCKER COMPOSE
# -------------------------------------------------------------------------
- name: Déployer le fichier docker-compose.yaml pour Falco & Falcosidekick
ansible.builtin.copy:
dest: "{{ falco_dir }}/docker-compose.yaml"
mode: '0644'
owner: root
group: root
content: |
services:
falco:
stdin_open: true
tty: true
image: falcosecurity/falco:latest
container_name: falco
privileged: true
restart: always
volumes:
- /sys/kernel/tracing:/sys/kernel/tracing:ro
- /var/run/docker.sock:/host/var/run/docker.sock
- /proc:/host/proc:ro
- /etc:/host/etc:ro
- ./falco_rules.local.yaml:/etc/falco/falco_rules.local.yaml:ro
command:
- /usr/bin/falco
- -o
- json_output=true
- -o
- json_include_output_property=true
- -o
- http_output.enabled=true
- -o
- http_output.url=http://falcosidekick:2801/
- -o
- plugins[0].init_config.engine_timeout=60
depends_on:
- falcosidekick
falcosidekick:
container_name: falcosidekick
image: falcosecurity/falcosidekick:latest
restart: always
environment:
- DISCORD_WEBHOOKURL={{ discord_webhook_url }}
- DISCORD_MINIMUMPRIORITY={{ discord_min_priority }}
# -------------------------------------------------------------------------
# 4. DÉMARRAGE DE LA STACK AVEC DOCKER COMPOSE
# -------------------------------------------------------------------------
- name: Lancer la stack Falco avec Docker Compose
ansible.builtin.shell: |
docker compose -f {{ falco_dir }}/docker-compose.yaml up -d
register: compose_up
changed_when: "'Started' in compose_up.stdout or 'Running' in compose_up.stdout or 'Created' in compose_up.stdout"
# -------------------------------------------------------------------------
# 5. DÉPLOIEMENT DU SCRIPT DE SURVEILLANCE AUTOMATISÉE
# -------------------------------------------------------------------------
- name: Déployer le script de maintenance /usr/local/bin/check_falco.sh
ansible.builtin.copy:
dest: /usr/local/bin/check_falco.sh
mode: '0755'
owner: root
group: root
content: |
#!/bin/bash
# Surveillance et relance automatique des conteneurs
for container in falco falcosidekick; do
if ! docker ps --format '{{ "{{" }}.Names{{ "}}" }}' | grep -q "^$container$"; then
echo "[$(date)] Conteneur $container inactif, relance..."
docker compose -f {{ falco_dir }}/docker-compose.yaml up -d "$container"
fi
done
- name: Mettre en place la tâche cron quotidienne de surveillance à 06h00
ansible.builtin.cron:
name: "Supervision quotidienne Falco et Falcosidekick"
minute: "0"
hour: "6"
job: "/usr/local/bin/check_falco.sh > /dev/null 2>&1"
user: root
# -------------------------------------------------------------------------
# 6. VALIDATION DE L'ÉTAT OPÉRATIONNEL
# -------------------------------------------------------------------------
- name: Vérifier que les conteneurs Falco sont actifs
ansible.builtin.shell: |
docker ps --filter "name=falco" --format '{{ "{{" }}.Names{{ "}}" }}: {{ "{{" }}.Status{{ "}}" }}'
register: falco_status
changed_when: false
- name: Afficher le statut des conteneurs
ansible.builtin.debug:
msg: "{{ falco_status.stdout_lines }}"