eBPF : La Révolution de l'Observabilité et de la Sécurité sous Linux

eBPF révolutionne l'observabilité et la sécurité Linux. Cilium, Tetragon, Falco, bpftrace : programmez le kernel sans le modifier. Guide complet avec exemples.

Introduction

Le noyau Linux est au cœur de l'infrastructure informatique mondiale. Des serveurs de production aux clusters Kubernetes, en passant par les smartphones Android, il orchestre des milliards d'opérations chaque seconde. Pourtant, pendant des décennies, observer ce qui se passait à l'intérieur du kernel ou y ajouter de la logique personnalisée nécessitait soit de modifier le code source du noyau, soit de charger des modules kernel — deux opérations risquées, complexes et lentes à déployer.

eBPF (extended Berkeley Packet Filter) a changé la donne. Cette technologie révolutionnaire permet d'exécuter des programmes sandboxés directement dans le noyau Linux, sans modifier son code source ni charger de modules. Elle a ouvert la voie à une nouvelle génération d'outils d'observabilité, de networking et de sécurité qui redéfinissent ce qui est possible sous Linux.

Architecture en un coup d'œil

Diagramme - eBPF : La Révolution de l'Observabilité et de la Sécurité sous Linux

En 2025-2026, eBPF est devenu un pilier incontournable de l'infrastructure cloud native. Des géants comme Meta, Google, Netflix, Cloudflare, Microsoft et Apple l'utilisent massivement en production. Des projets comme Cilium, Falco, Tetragon et Pixie ont construit des solutions complètes sur cette fondation.

Dans cet article, nous allons explorer en profondeur ce qu'est eBPF, comment il fonctionne, ses cas d'usage majeurs, les outils qui en tirent parti, et comment vous pouvez commencer à l'utiliser dès aujourd'hui.

Qu'est-ce qu'eBPF ? Un peu d'histoire

Du BPF classique à eBPF

L'histoire commence en 1992 avec le Berkeley Packet Filter (BPF), conçu par Steven McCanne et Van Jacobson au Lawrence Berkeley National Laboratory. Le BPF classique était une machine virtuelle minimaliste dans le noyau, conçue pour un seul objectif : filtrer efficacement les paquets réseau. C'est la technologie sous-jacente de tcpdump — quand vous écrivez tcpdump port 80, cette expression est compilée en bytecode BPF et exécutée dans le kernel pour filtrer les paquets directement à la source.

Le BPF classique était limité : registres 32 bits, jeu d'instructions restreint, pas de possibilité de stocker des données entre les exécutions, et uniquement orienté réseau. Mais l'idée fondamentale — exécuter du code utilisateur de manière sûre dans le kernel — était brillante.

En 2014, Alexei Starovoitov, ingénieur chez PLUMgrid (puis Facebook/Meta), a proposé une refonte majeure du BPF dans le noyau Linux. Cette version étendue — eBPF — a été progressivement intégrée à partir du kernel 3.15 puis massivement enrichie dans les versions suivantes :

  • Kernel 3.15 (2014) : introduction de la nouvelle machine virtuelle eBPF avec des registres 64 bits
  • Kernel 4.1 (2015) : programmes eBPF attachables aux kprobes (traçage de fonctions kernel)
  • Kernel 4.7 (2016) : support des programmes attachés au traffic control (networking)
  • Kernel 4.10 (2017) : XDP (eXpress Data Path) pour le traitement réseau ultra-rapide
  • Kernel 5.3 (2019) : BTF (BPF Type Format) pour la portabilité des programmes
  • Kernel 5.8 (2020) : support des ring buffers, améliorations significatives
  • Kernel 6.x (2022-2025) : maturité, nouveaux types de programmes, améliorations continues

Aujourd'hui, le terme « eBPF » est souvent simplifié en « BPF » dans la communauté. La Fondation eBPF, hébergée par la Linux Foundation et soutenue par Meta, Google, Microsoft, Netflix et Isovalent, coordonne le développement de l'écosystème.

Pourquoi eBPF est-il révolutionnaire ?

Pour comprendre l'impact d'eBPF, il faut comprendre le problème qu'il résout. Traditionnellement, pour ajouter une fonctionnalité au noyau Linux, vous aviez trois options :

  1. Modifier le code source du noyau : processus long (proposer un patch, review, acceptation, attendre la prochaine release, distribution par les distros). Délai : mois voire années.
  2. Charger un module kernel : plus rapide, mais risqué (un bug dans un module = kernel panic), pas de sandboxing, difficile à maintenir entre versions du kernel.
  3. Utiliser les interfaces existantes : limitées à ce que le kernel expose (procfs, sysfs, netfilter, etc.).

eBPF introduit une quatrième voie : programmer le kernel de manière sûre et dynamique. Les programmes eBPF sont vérifiés avant exécution, exécutés dans un sandbox, et peuvent être chargés et déchargés à chaud sans redémarrage. C'est comme si JavaScript avait rendu le web programmable — eBPF rend le kernel programmable.

Comment fonctionne eBPF en profondeur

Architecture de la machine virtuelle eBPF

eBPF est fondamentalement une machine virtuelle in-kernel avec les caractéristiques suivantes :

  • 11 registres 64 bits (r0-r10) : r0 pour la valeur de retour, r1-r5 pour les arguments de fonctions, r6-r9 préservés entre appels, r10 pour le pointeur de pile (lecture seule)
  • Jeu d'instructions RISC : environ 100 instructions couvrant l'arithmétique, les sauts, les accès mémoire et les appels de fonctions
  • Pile de 512 octets : limitation volontaire pour la sécurité
  • Pas de boucles infinies garanties : le vérifieur s'assure que chaque programme termine

Le vérifieur (verifier) : la clé de la sécurité

Le composant le plus critique d'eBPF est son vérifieur statique. Avant qu'un programme eBPF ne soit autorisé à s'exécuter dans le kernel, il passe par une analyse exhaustive :

  • Analyse de flux de contrôle : vérifie que le programme est un DAG (Directed Acyclic Graph) — pas de boucles infinies possibles (les boucles bornées sont autorisées depuis le kernel 5.3)
  • Vérification des accès mémoire : chaque lecture/écriture est validée pour s'assurer qu'elle reste dans les bornes autorisées
  • Suivi des types : chaque registre est typé et les opérations invalides sont rejetées
  • Vérification de terminaison : le programme doit terminer en un nombre borné d'instructions (limite actuelle : 1 million d'instructions vérifiées)
  • Vérification des helper functions : seules les fonctions d'aide autorisées pour le type de programme peuvent être appelées

Si le vérifieur rejette un programme, il fournit un message d'erreur détaillé expliquant pourquoi. Ce mécanisme garantit qu'un programme eBPF ne peut pas crasher le kernel, ne peut pas accéder à de la mémoire arbitraire, et terminera toujours.

Compilation JIT (Just-In-Time)

Une fois vérifié, le bytecode eBPF est compilé en code machine natif par le compilateur JIT du kernel. Cela signifie que les programmes eBPF s'exécutent à des performances proches du code natif, sans overhead d'interprétation. Le JIT est disponible pour toutes les architectures majeures : x86_64, ARM64, RISC-V, s390x, etc.

Les maps eBPF : le stockage partagé

Les maps sont des structures de données clé-valeur qui permettent de stocker des données entre les exécutions de programmes eBPF et de partager des données entre le kernel et l'espace utilisateur. Les types de maps incluent :

  • Hash maps : tables de hachage classiques
  • Array maps : tableaux indexés
  • Ring buffers : pour le streaming d'événements vers l'espace utilisateur
  • LRU maps : avec éviction automatique des entrées les moins récemment utilisées
  • Per-CPU maps : une instance par CPU pour éviter le verrouillage
  • Stack trace maps : pour capturer des traces d'appels
  • Map-in-map : maps imbriquées pour des structures complexes

Points d'attache (hook points)

Les programmes eBPF peuvent être attachés à de nombreux points du kernel :

  • kprobes / kretprobes : entrée et sortie de n'importe quelle fonction du kernel
  • tracepoints : points de traçage statiques définis dans le kernel
  • uprobes / uretprobes : entrée et sortie de fonctions en espace utilisateur
  • XDP (eXpress Data Path) : traitement des paquets réseau avant même la pile réseau
  • TC (Traffic Control) : filtrage et manipulation du trafic réseau
  • Socket filters : filtrage au niveau des sockets
  • cgroup hooks : contrôle des opérations par cgroup (réseau, device, etc.)
  • LSM (Linux Security Modules) : hooks de sécurité pour le contrôle d'accès
  • Perf events : événements de performance matériels et logiciels
  • fentry / fexit : variantes modernes des kprobes avec accès aux arguments typés

Cas d'usage majeurs d'eBPF

1. Observabilité et monitoring

L'observabilité est le cas d'usage originel et le plus mature d'eBPF. Au lieu de s'appuyer sur des mécanismes coûteux comme strace (qui utilise ptrace et a un overhead de 100x), eBPF permet de tracer n'importe quelle fonction du kernel ou de l'espace utilisateur avec un overhead quasi nul (typiquement moins de 1 %).

Voici ce que vous pouvez observer avec eBPF :

  • Latence de chaque opération I/O disque (quel processus, quel fichier, combien de temps)
  • Chaque connexion TCP ouverte/fermée avec sa durée de vie et ses métriques
  • Chaque appel système avec ses arguments et sa valeur de retour
  • Chaque ouverture de fichier, chaque allocation mémoire
  • Les stack traces de n'importe quel événement
  • Le profiling CPU continu avec un overhead minimal

2. Networking haute performance

eBPF a transformé le networking sous Linux. Avec XDP, les paquets peuvent être traités avant même d'entrer dans la pile réseau du kernel, permettant des performances de l'ordre de millions de paquets par seconde par cœur CPU. Les cas d'usage incluent :

  • Load balancing : Facebook/Meta utilise eBPF (Katran) pour son load balancer L4, traitant des milliards de requêtes par jour
  • DDoS mitigation : Cloudflare utilise XDP pour filtrer le trafic malveillant au plus tôt
  • Service mesh : Cilium remplace les proxies sidecar avec du networking eBPF kernel-level
  • Network policies : remplacement d'iptables par des programmes eBPF plus performants et plus expressifs

3. Sécurité runtime

eBPF permet une visibilité sans précédent sur le comportement des applications au runtime, ce qui en fait un outil puissant pour la détection d'intrusions et l'application de politiques de sécurité :

  • Détection de comportements anormaux : exécution de binaires inattendus, ouverture de fichiers sensibles, connexions réseau suspectes
  • Runtime enforcement : bloquer en temps réel des opérations dangereuses (pas seulement détecter, mais empêcher)
  • Audit : tracer toutes les opérations privilégiées, les changements de capabilities, les appels système sensibles
  • Forensics : capturer le contexte complet d'un incident (processus, fichiers, réseau, stack traces)

4. Profiling et optimisation des performances

eBPF est devenu l'outil de choix pour le profiling en production :

  • Profiling CPU continu : identifier les fonctions les plus consommatrices sans overhead significatif
  • Analyse de latence : mesurer précisément la latence de chaque couche (application, système de fichiers, bloc device, driver)
  • Détection de contention : identifier les locks, les attentes I/O, les cache misses
  • Analyse off-CPU : comprendre pourquoi un processus ne s'exécute pas (attentes I/O, locks, scheduling)

Les outils BCC : la boîte à outils du sysadmin moderne

BCC (BPF Compiler Collection) est une collection d'outils eBPF prêts à l'emploi, développée par Brendan Gregg (Netflix) et la communauté. Ces outils couvrent un large spectre de l'observabilité système. Voici les plus essentiels :

execsnoop : tracer chaque exécution de programme

execsnoop trace chaque appel à execve(), c'est-à-dire chaque fois qu'un nouveau programme est lancé sur le système. C'est inestimable pour comprendre ce qui se passe réellement sur un serveur :

# Installer BCC tools sur Ubuntu/Debian
sudo apt-get install bpfcc-tools linux-headers-$(uname -r)

# Sur Fedora/RHEL
sudo dnf install bcc-tools

# Tracer toutes les exécutions de programmes
sudo execsnoop-bpfcc

# Exemple de sortie :
# PCOMM   PID    PPID   RET ARGS
# bash    18478  18477    0 /bin/bash
# curl    18479  18478    0 /usr/bin/curl -s https://api.example.com
# grep    18480  18478    0 /usr/bin/grep status
# node    18481  18476    0 /usr/bin/node /app/server.js

# Filtrer par nom de processus
sudo execsnoop-bpfcc -n curl

# Inclure les timestamps
sudo execsnoop-bpfcc -T

biosnoop : tracer les I/O disque

biosnoop trace chaque opération d'entrée/sortie au niveau du block device, avec la latence précise :

# Tracer toutes les I/O disque
sudo biosnoop-bpfcc

# Exemple de sortie :
# TIME(s)  COMM         PID    DISK  T SECTOR     BYTES  LAT(ms)
# 0.000    postgres     1234   sda   R 12345678   8192   0.42
# 0.001    postgres     1234   sda   R 12345686   4096   0.38
# 0.015    jbd2/sda1    456    sda   W 98765432   16384  1.23

# Filtrer par PID
sudo biosnoop-bpfcc -p 1234

# Filtrer par disque
sudo biosnoop-bpfcc -d sda

tcplife : suivre le cycle de vie des connexions TCP

tcplife trace chaque connexion TCP de son ouverture à sa fermeture, avec la durée, les octets envoyés et reçus :

# Tracer toutes les connexions TCP
sudo tcplife-bpfcc

# Exemple de sortie :
# PID   COMM       LADDR           LPORT RADDR           RPORT TX_KB RX_KB MS
# 1234  curl       10.0.0.5        54312 93.184.216.34   443   1     12    250
# 5678  nginx      10.0.0.5        80    10.0.0.100      39482 145   2     30042
# 9012  postgres   10.0.0.5        5432  10.0.0.20       48210 512   128   5023

# Filtrer par port local
sudo tcplife-bpfcc -L 443

# Filtrer les connexions de plus d'1 seconde
sudo tcplife-bpfcc -D 1000

opensnoop : tracer les ouvertures de fichiers

opensnoop trace chaque appel à open() et openat(), révélant quels fichiers chaque processus tente d'ouvrir :

# Tracer toutes les ouvertures de fichiers
sudo opensnoop-bpfcc

# Exemple de sortie :
# PID    COMM         FD  ERR PATH
# 1234   python3      5   0   /app/config.yaml
# 1234   python3      6   0   /app/data/users.db
# 5678   nginx        12  0   /var/log/nginx/access.log
# 9012   bash         -1  2   /etc/shadow  # ENOENT ou permission denied

# Filtrer par PID
sudo opensnoop-bpfcc -p 1234

# Afficher seulement les erreurs (fichiers non trouvés, permissions refusées)
sudo opensnoop-bpfcc -x

bpftrace : le langage de traçage eBPF

bpftrace est un langage de traçage haut niveau pour eBPF, inspiré d'AWK et de DTrace. Il permet d'écrire des programmes de traçage en une ligne ou en quelques lignes, sans avoir à écrire du C ou à utiliser les APIs eBPF bas niveau. C'est l'outil de prédilection pour l'exploration et le debugging en temps réel.

Installation de bpftrace

# Ubuntu/Debian (22.04+)
sudo apt-get install bpftrace

# Fedora
sudo dnf install bpftrace

# Arch Linux
sudo pacman -S bpftrace

# Vérifier l'installation
bpftrace --version
# bpftrace v0.21.0

# Lister les tracepoints disponibles
sudo bpftrace -l 'tracepoint:syscalls:*'

# Lister les kprobes disponibles
sudo bpftrace -l 'kprobe:tcp_*'

Exemples pratiques bpftrace

# 1. Compter les appels système par nom
sudo bpftrace -e '
tracepoint:raw_syscalls:sys_enter {
    @syscalls[comm] = count();
}
'

# 2. Histogramme de la latence des lectures disque
sudo bpftrace -e '
kprobe:blk_account_io_start { @start[arg0] = nsecs; }
kprobe:blk_account_io_done /@start[arg0]/ {
    @usecs = hist((nsecs - @start[arg0]) / 1000);
    delete(@start[arg0]);
}
'

# 3. Tracer les ouvertures de fichiers avec le chemin complet
sudo bpftrace -e '
tracepoint:syscalls:sys_enter_openat {
    printf("%-6d %-16s %s\n", pid, comm, str(args.filename));
}
'

# 4. Top 10 des processus par appels système (sur 5 secondes)
sudo bpftrace -e '
tracepoint:raw_syscalls:sys_enter {
    @[comm, pid] = count();
}
interval:s:5 { print(@, 10); clear(@); }
'

# 5. Tracer les connexions TCP sortantes
sudo bpftrace -e '
kprobe:tcp_connect {
    $sk = (struct sock *)arg0;
    $inet_family = $sk->__sk_common.skc_family;
    if ($inet_family == 2) { // AF_INET
        $daddr = ntop($sk->__sk_common.skc_daddr);
        $dport = $sk->__sk_common.skc_dport;
        printf("%-6d %-16s -> %s:%d\n", pid, comm, $daddr, $dport);
    }
}
'

# 6. Mesurer la latence des requêtes DNS
sudo bpftrace -e '
uprobe:/lib/x86_64-linux-gnu/libc.so.6:getaddrinfo {
    @start[tid] = nsecs;
    @name[tid] = arg0;
}
uretprobe:/lib/x86_64-linux-gnu/libc.so.6:getaddrinfo /@start[tid]/ {
    $latency_ms = (nsecs - @start[tid]) / 1000000;
    printf("%-6d %-16s DNS lookup: %-30s %d ms\n",
        pid, comm, str(@name[tid]), $latency_ms);
    delete(@start[tid]);
    delete(@name[tid]);
}
'

# 7. Script bpftrace complet : monitoring des processus qui consomment le plus de CPU
sudo bpftrace -e '
profile:hz:99 {
    @cpu[comm, pid] = count();
}
interval:s:10 {
    printf("\n--- Top CPU consumers (last 10s) ---\n");
    print(@cpu, 10);
    clear(@cpu);
}
'

Script bpftrace avancé : analyse de latence complète

#!/usr/bin/env bpftrace
/*
 * iolatency.bt - Analyse détaillée de la latence I/O
 * Affiche un histogramme par type d'opération (R/W) et par disque
 */

BEGIN
{
    printf("Tracing block I/O latency... Hit Ctrl-C to end.\n");
}

tracepoint:block:block_io_start
{
    @start[args.sector] = nsecs;
    @type[args.sector] = args.rwbs;
}

tracepoint:block:block_io_done
/@start[args.sector]/
{
    $duration_us = (nsecs - @start[args.sector]) / 1000;
    $type = @type[args.sector];

    @latency_us[$type] = hist($duration_us);
    @total_io[$type] = count();
    @avg_us[$type] = avg($duration_us);

    delete(@start[args.sector]);
    delete(@type[args.sector]);
}

END
{
    printf("\n=== I/O Latency Distribution (microseconds) ===\n");
    print(@latency_us);
    printf("\n=== Total I/O Count by Type ===\n");
    print(@total_io);
    printf("\n=== Average Latency (us) by Type ===\n");
    print(@avg_us);
    clear(@start);
    clear(@type);
}

Cilium : le networking eBPF pour Kubernetes

Cilium est le projet phare de l'écosystème eBPF pour Kubernetes. Développé par Isovalent (acquis par Cisco en 2024), Cilium est un CNI (Container Network Interface) qui remplace les composants réseau traditionnels de Kubernetes par des programmes eBPF hautement performants. Il est devenu le CNI par défaut de Google Kubernetes Engine (GKE) et est largement adopté par l'industrie.

Pourquoi Cilium remplace kube-proxy

Le composant kube-proxy de Kubernetes utilise traditionnellement iptables pour implémenter les Services Kubernetes. Or, iptables pose des problèmes fondamentaux à l'échelle :

  • Complexité O(n) : chaque paquet doit traverser toutes les règles iptables linéairement. Avec 10 000 Services, cela signifie potentiellement 50 000+ règles
  • Mises à jour coûteuses : chaque modification nécessite de réécrire l'ensemble des règles (opération non-atomique)
  • Pas de visibilité : iptables ne fournit aucune métrique sur le trafic qui traverse les règles
  • Pas de policies L7 : iptables opère aux couches 3/4 uniquement

Cilium remplace tout cela par des hash maps eBPF avec un lookup O(1), des mises à jour atomiques, une visibilité complète via Hubble, et des policies jusqu'à la couche 7 (HTTP, gRPC, Kafka, DNS).

Installation de Cilium

# Installer le CLI Cilium
CILIUM_CLI_VERSION=$(curl -s https://raw.githubusercontent.com/cilium/cilium-cli/main/stable.txt)
CLI_ARCH=amd64
curl -L --fail --remote-name-all \
  https://github.com/cilium/cilium-cli/releases/download/${CILIUM_CLI_VERSION}/cilium-linux-${CLI_ARCH}.tar.gz
sudo tar xzvfC cilium-linux-${CLI_ARCH}.tar.gz /usr/local/bin
rm cilium-linux-${CLI_ARCH}.tar.gz

# Installer Cilium sur un cluster Kubernetes (remplace kube-proxy)
cilium install --version 1.16.5 \
  --set kubeProxyReplacement=true \
  --set hubble.enabled=true \
  --set hubble.relay.enabled=true \
  --set hubble.ui.enabled=true

# Vérifier le statut
cilium status --wait

# Valider la connectivité
cilium connectivity test

Configuration Cilium avec Helm

# values-cilium.yaml - Configuration Cilium complète pour production
# Installer avec : helm install cilium cilium/cilium -n kube-system -f values-cilium.yaml

# === Remplacement de kube-proxy ===
kubeProxyReplacement: true
k8sServiceHost: "api.k8s.example.com"
k8sServicePort: "6443"

# === Tunnel mode ===
# "vxlan" pour la compatibilité, "disabled" pour le mode natif (meilleure perf)
tunnel: "disabled"
ipv4NativeRoutingCIDR: "10.0.0.0/8"
autoDirectNodeRoutes: true

# === IPAM ===
ipam:
  mode: "kubernetes"
  operator:
    clusterPoolIPv4PodCIDRList:
      - "10.244.0.0/16"
    clusterPoolIPv4MaskSize: 24

# === eBPF ===
bpf:
  masquerade: true
  hostLegacyRouting: false
  # Taille de la map pour le connection tracking
  ctTcpMax: 524288
  ctAnyMax: 262144
  # Monitoring de la bande passante
  monitorAggregation: medium
  monitorInterval: "5s"

# === Load Balancing ===
loadBalancer:
  algorithm: "maglev"  # Consistent hashing pour un meilleur balancing
  mode: "dsr"          # Direct Server Return pour réduire la latence
  acceleration: "native"  # Utilise XDP pour le LB (best perf)

# === Hubble - Observabilité réseau ===
hubble:
  enabled: true
  metrics:
    enabled:
      - dns:query;ignoreAAAA
      - drop
      - tcp
      - flow
      - icmp
      - httpV2:exemplars=true;labelsContext=source_ip,source_namespace,source_workload,destination_ip,destination_namespace,destination_workload,traffic_direction
    serviceMonitor:
      enabled: true  # Pour Prometheus
  relay:
    enabled: true
    resources:
      limits:
        cpu: "500m"
        memory: "512Mi"
      requests:
        cpu: "100m"
        memory: "128Mi"
  ui:
    enabled: true
    ingress:
      enabled: true
      hosts:
        - hubble.k8s.example.com
      tls:
        - secretName: hubble-tls
          hosts:
            - hubble.k8s.example.com

# === Network Policies ===
policyEnforcementMode: "default"  # "default", "always", "never"
hostFirewall:
  enabled: true  # Policies sur les nœuds eux-mêmes

# === Bandwidth Manager ===
bandwidthManager:
  enabled: true
  bbr: true  # Algorithme BBR de Google pour le TCP

# === Encryption ===
encryption:
  enabled: true
  type: "wireguard"
  wireguard:
    userspaceFallback: false

# === Operator ===
operator:
  replicas: 2
  resources:
    limits:
      cpu: "1000m"
      memory: "1Gi"
    requests:
      cpu: "100m"
      memory: "128Mi"

# === Agent ===
resources:
  limits:
    cpu: "2000m"
    memory: "2Gi"
  requests:
    cpu: "200m"
    memory: "256Mi"

# === Monitoring ===
prometheus:
  enabled: true
  serviceMonitor:
    enabled: true
dashboards:
  enabled: true
  namespace: "monitoring"

Network Policies L7 avec Cilium

L'un des avantages majeurs de Cilium est la possibilité de créer des network policies au niveau applicatif (L7). Voici un exemple :

# CiliumNetworkPolicy - Politique L7 HTTP
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
  name: "api-access-policy"
  namespace: production
spec:
  description: "Contrôle d'accès L7 pour l'API"
  endpointSelector:
    matchLabels:
      app: api-server
  ingress:
    # Le frontend peut faire des GET/POST sur /api/v1/*
    - fromEndpoints:
        - matchLabels:
            app: frontend
      toPorts:
        - ports:
            - port: "8080"
              protocol: TCP
          rules:
            http:
              - method: "GET"
                path: "/api/v1/.*"
              - method: "POST"
                path: "/api/v1/orders"
                headers:
                  - 'Content-Type: application/json'
    # Le service de monitoring peut seulement lire /health et /metrics
    - fromEndpoints:
        - matchLabels:
            app: monitoring
      toPorts:
        - ports:
            - port: "8080"
              protocol: TCP
          rules:
            http:
              - method: "GET"
                path: "/health"
              - method: "GET"
                path: "/metrics"
  egress:
    # L'API peut accéder à la base de données PostgreSQL
    - toEndpoints:
        - matchLabels:
            app: postgres
      toPorts:
        - ports:
            - port: "5432"
              protocol: TCP
    # Et faire des requêtes DNS
    - toEndpoints:
        - matchLabels:
            "k8s:io.kubernetes.pod.namespace": kube-system
            k8s-app: kube-dns
      toPorts:
        - ports:
            - port: "53"
              protocol: ANY
          rules:
            dns:
              - matchPattern: "*.production.svc.cluster.local"

Hubble : l'observabilité réseau

# Installer le CLI Hubble
export HUBBLE_VERSION=$(curl -s https://raw.githubusercontent.com/cilium/hubble/master/stable.txt)
curl -L --fail --remote-name-all \
  https://github.com/cilium/hubble/releases/download/$HUBBLE_VERSION/hubble-linux-amd64.tar.gz
sudo tar xzvfC hubble-linux-amd64.tar.gz /usr/local/bin
rm hubble-linux-amd64.tar.gz

# Port-forward le relay Hubble
cilium hubble port-forward &

# Observer les flux réseau en temps réel
hubble observe --namespace production

# Filtrer par verdict (DROPPED, FORWARDED, etc.)
hubble observe --verdict DROPPED --namespace production

# Filtrer par protocole HTTP et code de réponse
hubble observe --protocol http --http-status 500 --namespace production

# Voir les flux DNS
hubble observe --protocol dns --namespace production

# Exporter les métriques
hubble observe --output json | jq '.flow.source.namespace'

Sécurité runtime avec Tetragon

Tetragon est un projet de sécurité runtime basé sur eBPF, également développé par Isovalent/Cisco. Contrairement à Falco qui détecte principalement les comportements suspects, Tetragon peut bloquer en temps réel les opérations dangereuses grâce à des programmes eBPF attachés aux hooks LSM et aux kprobes.

Caractéristiques de Tetragon

  • Security observability : visibilité complète sur les processus, fichiers, réseau et appels système
  • Runtime enforcement : blocage en temps réel des opérations non autorisées, directement dans le kernel
  • Kubernetes-aware : enrichissement automatique avec les métadonnées Kubernetes (pod, namespace, labels)
  • Overhead minimal : filtrage dans le kernel avant même que l'événement ne remonte en espace utilisateur
  • Politiques déclaratives : TracingPolicy CRD pour définir ce qu'on observe et ce qu'on bloque

Installation de Tetragon

# Installation via Helm sur Kubernetes
helm repo add cilium https://helm.cilium.io
helm repo update

helm install tetragon cilium/tetragon \
  -n kube-system \
  --set tetragon.grpc.enabled=true \
  --set tetragon.enableProcessCred=true \
  --set tetragon.enableProcessNs=true

# Installer le CLI tetra
GOOS=$(go env GOOS)
GOARCH=$(go env GOARCH)
curl -L --remote-name-all \
  https://github.com/cilium/tetragon/releases/latest/download/tetra-${GOOS}-${GOARCH}.tar.gz
sudo tar -C /usr/local/bin -xzvf tetra-${GOOS}-${GOARCH}.tar.gz
rm tetra-${GOOS}-${GOARCH}.tar.gz

# Observer les événements en temps réel
kubectl logs -n kube-system -l app.kubernetes.io/name=tetragon -c export-stdout -f | tetra getevents

TracingPolicy : politiques de sécurité déclaratives

# Politique Tetragon : détecter et bloquer l'écriture dans /etc/passwd
apiVersion: cilium.io/v1alpha1
kind: TracingPolicy
metadata:
  name: "block-etc-passwd-write"
spec:
  kprobes:
    - call: "security_file_permission"
      syscall: false
      args:
        - index: 0
          type: "file"
        - index: 1
          type: "int"
      selectors:
        - matchArgs:
            - index: 0
              operator: "Equal"
              values:
                - "/etc/passwd"
                - "/etc/shadow"
                - "/etc/sudoers"
            - index: 1
              operator: "Equal"
              values:
                - "2"  # MAY_WRITE
          matchActions:
            - action: Sigkill  # Tuer le processus qui tente l'écriture
---
# Politique Tetragon : détecter l'exécution de binaires suspects
apiVersion: cilium.io/v1alpha1
kind: TracingPolicy
metadata:
  name: "detect-suspicious-binaries"
spec:
  tracepoints:
    - subsystem: "raw_syscalls"
      event: "sys_enter"
      args:
        - index: 4
          type: "syscall64"
      selectors:
        - matchArgs:
            - index: 4
              operator: "Equal"
              values:
                - "59"  # SYS_execve
          matchActions:
            - action: Post
              rateLimit: "1m"
              rateLimitScope: process
---
# Politique Tetragon : empêcher les conteneurs de lancer un shell
apiVersion: cilium.io/v1alpha1
kind: TracingPolicy
metadata:
  name: "block-shell-in-containers"
spec:
  kprobes:
    - call: "security_bprm_check"
      syscall: false
      args:
        - index: 0
          type: "linux_binprm"
      selectors:
        - matchArgs:
            - index: 0
              operator: "Equal"
              values:
                - "/bin/sh"
                - "/bin/bash"
                - "/bin/zsh"
                - "/usr/bin/sh"
                - "/usr/bin/bash"
          matchNamespaces:
            - namespace: Pid
              operator: NotEqual
              values:
                - "host_ns"  # Seulement dans les conteneurs
          matchActions:
            - action: Sigkill
              message: "Shell execution blocked in container"

Falco : la détection d'intrusions cloud native

Falco, projet de la CNCF, est un moteur de détection de menaces runtime qui utilise eBPF (ou un module kernel) pour surveiller les appels système et les événements Kubernetes. Il est complémentaire à Tetragon : là où Tetragon excelle dans l'enforcement, Falco brille dans la détection basée sur des règles riches et communautaires.

# Installation de Falco avec eBPF driver
helm repo add falcosecurity https://falcosecurity.github.io/charts
helm repo update

helm install falco falcosecurity/falco \
  --namespace falco --create-namespace \
  --set driver.kind=ebpf \
  --set falcosidekick.enabled=true \
  --set falcosidekick.config.slack.webhookurl="https://hooks.slack.com/services/XXX"

# Vérifier que Falco fonctionne
kubectl logs -n falco -l app.kubernetes.io/name=falco
# Règle Falco personnalisée
# falco-custom-rules.yaml
customRules:
  custom-rules.yaml: |-
    - rule: Detect Cryptocurrency Mining
      desc: Detect attempts to mine cryptocurrency in containers
      condition: >
        spawned_process and
        container and
        (proc.name in (xmrig, minerd, minergate, cpuminer) or
         proc.cmdline contains "stratum+tcp" or
         proc.cmdline contains "stratum+ssl" or
         proc.cmdline contains "coin" or
         proc.cmdline contains "pool.minergate")
      output: >
        Cryptocurrency mining detected
        (user=%user.name command=%proc.cmdline container=%container.name
         image=%container.image.repository namespace=%k8s.ns.name
         pod=%k8s.pod.name)
      priority: CRITICAL
      tags: [cryptomining, mitre_execution]

    - rule: Sensitive File Access in Container
      desc: Detect access to sensitive files within containers
      condition: >
        open_read and
        container and
        (fd.name startswith /etc/shadow or
         fd.name startswith /etc/gshadow or
         fd.name startswith /root/.ssh or
         fd.name startswith /root/.bash_history)
      output: >
        Sensitive file accessed in container
        (file=%fd.name user=%user.name command=%proc.cmdline
         container=%container.name pod=%k8s.pod.name)
      priority: WARNING
      tags: [filesystem, mitre_credential_access]

Pixie : l'observabilité auto-instrumentée pour Kubernetes

Pixie (projet CNCF, développé par New Relic) utilise eBPF pour fournir une observabilité automatique sans instrumentation des applications Kubernetes. Sans modifier une seule ligne de code, Pixie capture automatiquement :

  • Les requêtes HTTP/gRPC/MySQL/PostgreSQL/Redis/Kafka avec leur contenu
  • Les métriques CPU, mémoire, réseau par pod
  • Les flamegraphs CPU en continu
  • Les traces distribuées reconstruites automatiquement
# Installer Pixie
bash -c "$(curl -fsSL https://withpixie.ai/install.sh)"

# Déployer sur un cluster Kubernetes
px deploy

# Lancer une requête PxL (le langage de requête de Pixie)
px run px/http_data -- --start_time=-5m

# Voir les requêtes HTTP les plus lentes
px run px/http_data_filtered -- \
  --start_time=-5m \
  --namespace=production \
  --min_latency=100

# Voir l'utilisation des ressources par pod
px run px/pod -- --namespace=production

Performances : eBPF vs iptables

L'un des arguments les plus convaincants pour eBPF dans le networking est la différence de performance par rapport à iptables. Voici les résultats de benchmarks réalisés par Isovalent et la communauté :

Métrique iptables (kube-proxy) Cilium eBPF Amélioration
Latence du premier paquet (1k Services) ~2.5 ms ~0.3 ms ~8x plus rapide
Latence du premier paquet (10k Services) ~15 ms ~0.3 ms ~50x plus rapide
Temps de mise à jour (ajout d'un Service) ~11 secondes (10k rules) ~0.02 ms ~500 000x plus rapide
Utilisation mémoire (10k Services) ~250 MB ~50 MB 5x moins
Débit réseau (pps, XDP mode) ~2M pps ~14M pps 7x plus rapide
CPU overhead par paquet Élevé (linéaire) Constant (O(1)) Scalabilité supérieure

La différence est particulièrement frappante à l'échelle. Un cluster avec 10 000 Services et 100 000 endpoints verra iptables s'écrouler sous le poids de centaines de milliers de règles, tandis qu'eBPF maintiendra des performances constantes grâce à ses hash maps O(1).

Adoption par l'industrie

eBPF n'est plus une technologie expérimentale. En 2025-2026, les plus grandes entreprises technologiques l'utilisent massivement en production :

Meta (Facebook)

Meta est l'un des contributeurs majeurs d'eBPF. L'entreprise utilise eBPF pour :

  • Katran : load balancer L4 basé sur XDP, traitant des milliards de requêtes par jour
  • Networking : remplacement quasi complet d'iptables dans leur infrastructure
  • Observabilité : profiling CPU continu sur l'ensemble de leur flotte de serveurs

Google

Google utilise eBPF dans GKE (Google Kubernetes Engine) via Cilium comme CNI par défaut, et pour le profiling continu dans ses datacenters. Le projet gVisor intègre également des composants eBPF.

Netflix

Netflix, à travers le travail de Brendan Gregg, a été un pionnier de l'utilisation d'eBPF pour l'observabilité. Ils utilisent BCC et bpftrace pour le debugging de performance sur leur infrastructure de streaming.

Cloudflare

Cloudflare utilise eBPF/XDP pour :

  • DDoS mitigation : filtrage des attaques directement dans le driver réseau avec XDP
  • Load balancing : distribution du trafic à la périphérie
  • Flowtrackd : suivi avancé des connexions TCP

Microsoft

Microsoft a porté eBPF sur Windows avec le projet eBPF for Windows, et l'utilise dans Azure pour le networking et la sécurité. C'est un signal fort de l'universalité d'eBPF au-delà de Linux.

Autres adoptions notables

  • Datadog : utilise eBPF pour le monitoring réseau et la sécurité (Cloud Workload Security)
  • Grafana : Beyla, un outil d'auto-instrumentation basé sur eBPF
  • Alibaba, Tencent, ByteDance : utilisation massive dans leurs infrastructures cloud
  • Red Hat / RHEL : intégration d'eBPF comme technologie de première classe

Limites et prérequis d'eBPF

Malgré ses avantages considérables, eBPF a des limites qu'il est important de connaître :

Prérequis kernel

  • Version minimale du kernel : Linux 4.9+ pour les fonctionnalités de base, mais 5.10+ est recommandé pour les fonctionnalités modernes (BTF, boucles bornées, CO-RE). Pour Cilium, le minimum est 4.19.57 mais 5.10+ est fortement recommandé
  • CONFIG_BPF=y : le kernel doit être compilé avec le support BPF
  • CONFIG_BPF_SYSCALL=y : pour charger des programmes depuis l'espace utilisateur
  • CONFIG_BPF_JIT=y : pour la compilation JIT (performances)
  • CONFIG_DEBUG_INFO_BTF=y : pour la portabilité CO-RE (Compile Once, Run Everywhere)

Limitations techniques

  • Taille de la pile : limitée à 512 octets, ce qui contraint les structures de données locales
  • Complexité du vérifieur : le vérifieur peut rejeter des programmes valides s'ils sont trop complexes ou si les preuves de sûreté ne sont pas évidentes. Le debugging des rejets du vérifieur est parfois frustrant
  • Pas de boucles arbitraires : les boucles doivent être bornées (le compilateur doit pouvoir prouver qu'elles terminent)
  • Pas d'allocation dynamique : pas de malloc dans les programmes eBPF
  • Appels de fonctions limités : seules les helper functions exposées par le kernel sont disponibles (bien que les BPF subprograms permettent des appels entre programmes eBPF)
  • Privilèges : charger des programmes eBPF nécessite généralement CAP_BPF ou root (les programmes non-privilégiés sont possibles mais limités)

Courbe d'apprentissage

  • Écrire des programmes eBPF en C bas niveau est complexe (bien que libbpf et libbpf-rs simplifient considérablement)
  • Comprendre les interactions avec le kernel requiert une connaissance approfondie de Linux
  • Le debugging est plus difficile que pour du code userspace classique
  • Les outils haut niveau (bpftrace, Cilium, Falco) abstraient cette complexité pour la majorité des cas d'usage

Sécurité du mécanisme lui-même

  • Des vulnérabilités dans le vérifieur eBPF ont été découvertes par le passé (CVE-2021-3490, CVE-2021-31440). Le vérifieur est un composant critique qui fait l'objet d'audits et de fuzzing intensifs
  • Les programmes eBPF s'exécutent avec les privilèges du kernel : un programme malveillant autorisé pourrait théoriquement être dangereux. D'où l'importance des prérequis de privilèges pour charger des programmes

Développer avec eBPF : l'écosystème de développement

libbpf et CO-RE (Compile Once, Run Everywhere)

L'approche moderne pour développer des programmes eBPF repose sur libbpf et CO-RE. CO-RE permet de compiler un programme eBPF une seule fois et de l'exécuter sur différentes versions du kernel, grâce à BTF (BPF Type Format) qui fournit les informations de type du kernel.

# Vérifier le support BTF de votre kernel
ls -la /sys/kernel/btf/vmlinux

# Installer les outils de développement eBPF
sudo apt-get install libbpf-dev clang llvm libelf-dev

# Compiler un programme eBPF avec CO-RE
clang -target bpf -D__TARGET_ARCH_x86 -O2 -g \
  -c my_program.bpf.c -o my_program.bpf.o

# Générer le skeleton C
bpftool gen skeleton my_program.bpf.o > my_program.skel.h

Langages et frameworks

  • C + libbpf : l'approche standard, la plus performante et portable
  • Rust + Aya : framework Rust pour eBPF, de plus en plus populaire en 2025
  • Go + cilium/ebpf : bibliothèque Go pour charger et interagir avec des programmes eBPF
  • Python + BCC : pour le prototypage rapide et les scripts d'observabilité
  • bpftrace : langage de scripting dédié au traçage

Exemple en Python avec BCC

#!/usr/bin/env python3
"""
Tracer les appels à execve() et afficher les commandes exécutées.
Utilise BCC (BPF Compiler Collection) pour Python.
"""

from bcc import BPF
from bcc.utils import printb
import ctypes

# Programme eBPF en C, compilé et chargé par BCC
bpf_program = """
#include 
#include 
#include 

#define ARGSIZE 256
#define MAXARG  20

struct event_t {
    u32 pid;
    u32 ppid;
    u32 uid;
    char comm[TASK_COMM_LEN];
    char filename[ARGSIZE];
    int retval;
};

BPF_PERF_OUTPUT(events);

TRACEPOINT_PROBE(syscalls, sys_enter_execve) {
    struct event_t event = {};
    struct task_struct *task;

    event.pid = bpf_get_current_pid_tgid() >> 32;
    event.uid = bpf_get_current_uid_gid() & 0xFFFFFFFF;

    task = (struct task_struct *)bpf_get_current_task();
    event.ppid = task->real_parent->tgid;

    bpf_get_current_comm(&event.comm, sizeof(event.comm));
    bpf_probe_read_user_str(&event.filename, sizeof(event.filename),
                            args->filename);

    events.perf_submit(args, &event, sizeof(event));
    return 0;
}
"""

# Charger le programme eBPF
b = BPF(text=bpf_program)

# Callback pour les événements
def print_event(cpu, data, size):
    event = b["events"].event(data)
    printb(b"%-6d %-6d %-6d %-16s %s" % (
        event.pid,
        event.ppid,
        event.uid,
        event.comm,
        event.filename,
    ))

# Header
print("%-6s %-6s %-6s %-16s %s" % ("PID", "PPID", "UID", "COMM", "FILENAME"))

# Ouvrir le buffer d'événements
b["events"].open_perf_buffer(print_event)

# Boucle principale
while True:
    try:
        b.perf_buffer_poll()
    except KeyboardInterrupt:
        print("\nDetaching...")
        exit()

Bonnes pratiques pour adopter eBPF

Par où commencer

  1. Utilisez les outils existants : ne réinventez pas la roue. bpftrace, BCC tools, Cilium, Falco, Tetragon couvrent 95 % des cas d'usage
  2. Commencez par l'observabilité : c'est le cas d'usage le moins risqué et le plus immédiatement utile. Installez bpftrace et explorez votre système
  3. Migrez vers Cilium progressivement : si vous utilisez Kubernetes, migrez votre CNI vers Cilium pour bénéficier du networking eBPF
  4. Ajoutez la sécurité : une fois familiarisé, déployez Falco pour la détection et Tetragon pour l'enforcement

Vérifier la compatibilité de votre système

# Vérifier la version du kernel
uname -r

# Vérifier le support eBPF
sudo bpftool feature probe kernel

# Vérifier la disponibilité de BTF
ls -la /sys/kernel/btf/vmlinux

# Tester avec un programme simple
sudo bpftrace -e 'BEGIN { printf("eBPF works!\n"); exit(); }'

# Vérifier les capabilities nécessaires
capsh --print | grep bpf

Considérations en production

  • Monitoring de l'overhead : même si eBPF est performant, surveillez l'impact de vos programmes sur le CPU et la mémoire
  • Gestion des versions kernel : testez vos programmes eBPF sur toutes les versions de kernel de votre flotte
  • Utilisation de CO-RE : préférez CO-RE pour la portabilité entre versions de kernel
  • Alerting sur les erreurs du vérifieur : loguez les échecs de chargement de programmes eBPF
  • Dimensionnement des maps : les maps eBPF consomment de la mémoire kernel. Dimensionnez-les en fonction de vos besoins réels

L'avenir d'eBPF

L'écosystème eBPF continue d'évoluer rapidement en 2025-2026 :

  • eBPF for Windows : Microsoft porte activement eBPF sur Windows, ouvrant la voie à une portabilité multi-OS
  • BPF tokens : nouveau mécanisme pour déléguer les privilèges eBPF de manière granulaire (kernel 6.9+)
  • Arènes BPF : mémoire partagée entre programmes eBPF et espace utilisateur pour des performances accrues
  • eBPF dans le hardware : des NIC (cartes réseau) intelligentes commencent à exécuter des programmes eBPF directement dans le matériel
  • Standardisation : la Fondation eBPF travaille sur la standardisation de l'ABI et des formats
  • Service mesh sans sidecar : Cilium Service Mesh élimine le besoin de proxies sidecar, réduisant la consommation de ressources de 50 % ou plus

Conclusion

eBPF représente une transformation fondamentale de la façon dont nous interagissons avec le noyau Linux. En rendant le kernel programmable de manière sûre et performante, il a ouvert la voie à une nouvelle génération d'outils qui redéfinissent l'observabilité, le networking et la sécurité des systèmes modernes.

L'analogie avec JavaScript et le web est pertinente : de même que JavaScript a transformé les pages web statiques en applications dynamiques, eBPF transforme le noyau Linux d'un bloc monolithique en une plateforme programmable. Et comme pour le web, ce sont les frameworks et les outils construits sur cette fondation — Cilium, Falco, Tetragon, bpftrace — qui rendent cette puissance accessible à tous.

Si vous gérez une infrastructure Linux, que ce soit un serveur unique ou des milliers de nœuds Kubernetes, eBPF n'est plus une technologie optionnelle. C'est un avantage compétitif en termes de performance, de sécurité et d'observabilité. Commencez par installer bpftrace sur un serveur de développement, explorez les BCC tools, et vous comprendrez rapidement pourquoi l'industrie entière se tourne vers eBPF.

Pour aller plus loin : le site ebpf.io est le point d'entrée officiel de l'écosystème eBPF. Le livre "BPF Performance Tools" de Brendan Gregg (Addison-Wesley) est la référence pour l'observabilité. Et le repository Cilium sur GitHub est un excellent exemple de projet eBPF de production.
Vous vous êtes abonné avec succès à CodeClan
Parfait ! Ensuite, complétez le paiement pour obtenir un accès complet à tout le contenu premium.
Erreur ! Impossible de s'inscrire. Lien invalide.
Bienvenue ! Vous vous êtes connecté avec succès.
Erreur ! Impossible de se connecter. Veuillez réessayer.
Succès ! Votre compte est entièrement activé, vous avez maintenant accès à tout le contenu.
Erreur ! Le paiement Stripe a échoué.
Succès ! Vos informations de facturation sont mises à jour.
Erreur ! La mise à jour des informations de facturation a échoué.