À la fin de cette page, vous aurez vérifié que votre noyau prend eBPF en charge, chargé un vrai programme dans le noyau et vu défiler, en direct, chaque commande lancée sur votre machine. On commence par la voie sans code (bpftrace, une ligne suffit), puis, pour ceux qui lisent du C, on écrit un vrai programme avec BCC et on regarde ce qui se passe sous le capot : compilation, vérificateur, chargement, accrochage. Public : administrateur à l'aise avec le terminal Linux. Aucune connaissance préalable d'eBPF n'est requise, mais lire d'abord eBPF, c'est quoi aide à situer les termes.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Vérifier que le noyau de votre machine prend eBPF en charge
- Installer la chaîne d'outils eBPF sur une distribution récente
- Tracer les exécutions de programmes avec une seule ligne de bpftrace
- Identifier chaque étape : compilation en bytecode, vérification, chargement, accrochage
- Déchiffrer un refus du vérificateur et comprendre pourquoi il protège la machine
Prérequis
Section intitulée « Prérequis »- Une VM Linux avec un noyau récent (5.x ou 6.x). Ici Ubuntu 24.04, noyau
6.8.0. - Un accès root ou
sudo: charger un programme eBPF est une opération privilégiée. - De quoi provoquer un peu d'activité (lancer des commandes) pour voir passer des événements.
- Pour la voie 2 uniquement : savoir lire du C (une fonction, un pointeur, un
if) et un peu de Python. La voie 1 n'en demande aucun.
Votre noyau prend-il eBPF en charge ?
Section intitulée « Votre noyau prend-il eBPF en charge ? »Un noyau prend eBPF en charge quand il est compilé avec CONFIG_BPF et CONFIG_BPF_SYSCALL, et il autorise la portabilité CO-RE quand il expose son BTF. Les distributions récentes (Ubuntu 24.04, Debian 13, RHEL 10) l'activent toutes, mais une image minimale ou un noyau personnalisé peut s'en passer. Mieux vaut le vérifier avant d'installer quoi que ce soit.
Premier test, le plus parlant : le noyau expose-t-il la description de ses types (le BTF, expliqué dans la leçon CO-RE et BTF) ? Si ce fichier existe, le noyau a été compilé avec CONFIG_DEBUG_INFO_BTF.
ls -l /sys/kernel/btf/vmlinuxLe fichier doit apparaître, en lecture seule, avec une taille de plusieurs mégaoctets. Un No such file or directory signifie que les outils fondés sur CO-RE ne fonctionneront pas ; bpftrace et BCC, eux, continuent de marcher tant que les options BPF sont présentes.
Deuxième test, les options de compilation du noyau, que les distributions publient dans /boot :
grep -E '^CONFIG_(BPF|BPF_SYSCALL|DEBUG_INFO_BTF)=' /boot/config-$(uname -r)Vous attendez trois lignes qui se terminent par =y : CONFIG_BPF=y, CONFIG_BPF_SYSCALL=y et CONFIG_DEBUG_INFO_BTF=y. Il manque une option ? Le fichier /boot/config-* peut aussi être absent sur certaines images (conteneur, noyau d'un fournisseur de cloud) ; dans ce cas, /proc/config.gz joue le même rôle quand il existe.
Troisième test, une fois bpftool installé (étape suivante) : il interroge le noyau lui-même plutôt que sa configuration.
sudo bpftool feature probe kernel | head -20La commande liste les types de programmes, de maps et de helpers que ce noyau accepte réellement. C'est la source la plus fiable : une option compilée ne garantit pas que le type de programme dont vous avez besoin soit disponible. Sortie réelle sur AlmaLinux 10.2 (noyau 6.12, bpftool 7.7.0) :
Scanning system configuration...bpf() syscall restricted to privileged users (admin can change)JIT compiler is enabledJIT compiler hardening is enabled for unprivileged usersJIT compiler kallsyms exports are enabled for rootGlobal memory limit for JIT compiler for unprivileged users is 528482304 bytesCONFIG_BPF is set to yCONFIG_BPF_SYSCALL is set to yCONFIG_HAVE_EBPF_JIT is set to yCONFIG_BPF_JIT is set to yCONFIG_BPF_JIT_ALWAYS_ON is set to yCONFIG_DEBUG_INFO_BTF is set to yCONFIG_DEBUG_INFO_BTF_MODULES is set to yCONFIG_CGROUPS is set to yCONFIG_CGROUP_BPF is set to yCONFIG_CGROUP_NET_CLASSID is set to yCONFIG_SOCK_CGROUP_DATA is set to yCONFIG_BPF_EVENTS is set to yCONFIG_KPROBE_EVENTS is set to yCONFIG_UPROBE_EVENTS is set to ySur Ubuntu 24.04 (noyau 6.8, bpftool 7.4.0), les lignes sont les mêmes, sauf JIT compiler hardening is disabled à la place de JIT compiler hardening is enabled for unprivileged users. Ces vingt premières lignes ne couvrent que la configuration : les types de programmes (eBPF program_type kprobe is available, par exemple) et de helpers viennent plus bas dans la sortie complète.
Installer la chaîne d'outils
Section intitulée « Installer la chaîne d'outils »Trois briques suffisent pour commencer : bpftrace (traçage en une ligne), BCC (BPF Compiler Collection, pour écrire des programmes en C pilotés depuis Python) et bpftool (l'outil d'inspection officiel du noyau). On ajoute clang et les en-têtes du noyau, car BCC compile le programme C directement sur la machine cible.
sudo apt updatesudo apt install -y bpftrace bpfcc-tools python3-bpfcc \ linux-headers-$(uname -r) linux-tools-$(uname -r) \ clang llvm libbpf-devsudo dnf install -y bpftrace bcc-tools python3-bcc \ kernel-devel-$(uname -r) bpftool clang llvm libbpf-develVérifiez que les versions répondent. Sur la VM de test :
bpftrace --version # bpftrace v0.20.2clang --version # Ubuntu clang version 18.1.3sudo bpftool version # bpftool v7.4.0Voie 1 : tracer sans écrire une ligne de C
Section intitulée « Voie 1 : tracer sans écrire une ligne de C »Le moyen le plus rapide de voir eBPF à l'œuvre est bpftrace. Vous décrivez ce que vous voulez observer dans un langage court, et bpftrace se charge de produire le programme eBPF, de le vérifier, de le charger et d'afficher le résultat. Aucune compilation manuelle.
L'exemple canonique : afficher chaque fois qu'un programme en démarre un autre. En Linux, lancer un programme passe par l'appel système execve (c'est lui qui remplace le contenu d'un processus par un nouveau programme). On s'accroche au tracepoint correspondant, un point d'observation stable prévu par le noyau :
sudo bpftrace -e 'tracepoint:syscalls:sys_enter_execve { printf("%-16s -> %s\n", comm, str(args->filename)); }'Décortiquons la ligne :
tracepoint:syscalls:sys_enter_execve: le point de déclenchement. « À l'entrée de l'appel systèmeexecve».comm: une variable fournie par bpftrace, le nom du processus courant (comm = command, sur 16 caractères).args->filename: l'argument de l'appel, ici le chemin du programme lancé.str(...): convertit le pointeur en chaîne lisible.
Pendant que cette commande tourne, lancez quelques commandes dans un autre terminal. La sortie réelle, capturée sur la VM :
bash -> /usr/bin/unamebash -> /usr/bin/idbash -> /usr/bin/sleepbash -> /usr/bin/unamebash -> /usr/bin/idbash -> /usr/bin/sleepVous venez d'observer, depuis le noyau, chaque programme lancé sur la machine, sans modifier ni bash ni les commandes tracées. C'est toute la promesse d'eBPF, en une ligne. La leçon bpftrace explore ce langage en détail.
Voie 2 (facultative) : écrire un vrai programme en C
Section intitulée « Voie 2 (facultative) : écrire un vrai programme en C »Cette voie est facultative pour un administrateur. Écrire et charger soi-même un programme en C est un geste de développeur d'outils, comme la leçon CO-RE : elle demande de lire du C et de Python, et vous pouvez la sauter sans perdre le fil du module. Elle reste utile pour comprendre ce que bpftrace cache, et pour reconnaître le cycle compilation, vérification, chargement quand il apparaît dans les journaux de vos outils.
Pour la suivre, écrivons le même genre de sonde « à la main » avec BCC. Le principe de BCC : le programme eBPF est écrit en C, embarqué dans une chaîne de caractères, et un script Python pilote sa compilation, son chargement et la lecture des résultats.
Créez le fichier hello.py :
#!/usr/bin/env python3from bcc import BPF
# Le programme eBPF, en C, embarque dans une chaine Python.programme = r"""int au_demarrage_execve(void *ctx) { char nom[16]; bpf_get_current_comm(&nom, sizeof(nom)); bpf_trace_printk("execve appele par %s\n", nom); return 0;}"""
b = BPF(text=programme) # compile + verifie + chargeb.attach_kprobe(event=b.get_syscall_fnname("execve"), # accroche a l'entree de execve fn_name="au_demarrage_execve")
print("En ecoute. Chaque commande lancee sur la machine apparait ci-dessous.\n")for _ in range(5): # on capture 5 evenements puis on sort (task, pid, cpu, flags, ts, msg) = b.trace_fields() print(f"{ts:14.6f} PID {pid:<6d} {msg.decode()}")Chaque terme du programme C mérite une explication :
int au_demarrage_execve(void *ctx): la fonction eBPF. Son argumentctx(context) est le contexte fourni par le noyau au point d'accrochage ; ici on ne s'en sert pas.bpf_get_current_comm: une fonction d'assistance (helper), une fonction que le noyau met à disposition des programmes eBPF. Celle-ci copie le nom du processus courant dans une variable.bpf_trace_printk: un autre helper, qui écrit une ligne de texte dans un canal de traçage du noyau nommétrace_pipe. C'est le « printf » du monde eBPF, pratique pour débuter, à ne pas utiliser en production (voir la section sécurité).return 0: un programme eBPF renvoie toujours une valeur ; ici elle n'a pas d'effet.
Côté Python :
b.attach_kprobe(...)accroche notre fonction à une kprobe surexecve. Une kprobe (kernel probe) est un point d'accrochage dynamique sur n'importe quelle fonction du noyau, à son entrée.get_syscall_fnname("execve")récupère le nom exact de la fonction noyau derrièreexecve, qui varie selon l'architecture.b.trace_fields()lit une ligne depuistrace_pipe.
Lancez-le (root obligatoire) en provoquant un peu d'activité à côté :
sudo python3 hello.pySortie réelle capturée sur la VM :
En ecoute. Chaque commande lancee sur la machine apparait ci-dessous.
422.733187 PID 22427 execve appele par bash 422.733882 PID 22428 execve appele par bash 422.734436 PID 22429 execve appele par bash 422.935559 PID 22430 execve appele par bash 422.936251 PID 22431 execve appele par bashLe premier nombre est l'horodatage noyau (secondes depuis le démarrage), suivi du PID (Process IDentifier, le numéro du processus) et de notre message. Vous avez écrit, compilé, chargé et exécuté un programme noyau, et chaque ligne confirme qu'il a bien été accroché.
Que se passe-t-il quand on charge un programme ?
Section intitulée « Que se passe-t-il quand on charge un programme ? »L'appel BPF(text=programme) a enchaîné, en une fraction de seconde, tout le cycle de vie décrit dans eBPF, c'est quoi. bpftrace suit exactement les mêmes étapes, sans que vous les voyiez.
-
Compilation : clang traduit le C en bytecode eBPF, un jeu d'instructions propre à la machine virtuelle du noyau (pas du code machine classique).
-
Vérification : le noyau confie le bytecode au vérificateur, qui prouve que le programme est sûr avant de l'accepter. S'il échoue, rien n'est chargé.
-
Compilation JIT : une fois accepté, le bytecode est traduit en code machine natif par le compilateur JIT (Just-In-Time) du noyau, pour tourner à pleine vitesse.
-
Accrochage :
attach_kproberelie le programme à la kprobe surexecve. À partir de là, chaqueexecvedéclenche notre code.
Pendant qu'un programme BCC ou bpftrace tourne, vous pouvez lister les programmes eBPF réellement chargés avec bpftool, l'outil d'inspection livré avec le noyau :
sudo bpftool prog showExtrait réel (le système en charge déjà plusieurs, ici ceux de systemd) :
147: cgroup_device name sd_devices tag fbee7646fdd03110 gpl loaded_at 2026-07-14T11:41:50+0000 uid 0 xlated 184B jited 103B memlock 4096B152: cgroup_skb name sd_fw_egress tag 6deef7357e7b4530 gpl loaded_at 2026-07-14T11:41:50+0000 uid 0 xlated 64B jited 56B memlock 4096BDeux champs éclairent ce qu'on vient de voir : xlated est la taille du bytecode vérifié (translated), jited celle du code machine produit par le JIT. La colonne de gauche est le type de programme (cgroup_skb, tracing, etc.), qui détermine où il peut s'accrocher.
Que fait le vérificateur quand il refuse un programme ?
Section intitulée « Que fait le vérificateur quand il refuse un programme ? »Le point qui inquiète les débutants, c'est le vérificateur. Voyons-le dire « non », pour constater qu'il protège sans rien casser. La règle la plus fondamentale, documentée par le noyau : un programme eBPF doit pouvoir être prouvé fini. La documentation du vérificateur le formule ainsi : « First step does DAG check to disallow loops ». Une boucle sans condition de sortie est donc rejetée d'office.
Écrivons volontairement une boucle infinie :
programme = r"""int boucle_sans_fin(void *ctx) { #pragma clang loop unroll(disable) for (;;) { bpf_ktime_get_ns(); // appel noyau : empeche la suppression du code } return 0;}"""Au chargement, le noyau refuse, et il explique précisément pourquoi. Journal réel du vérificateur :
0: (85) call bpf_ktime_get_ns#51: (05) goto pc-2...infinite loop detected at insn 1processed 10 insns (limit 1000000)
Failed to load BPF program 'boucle_sans_fin': Invalid argumentTrois choses à retenir de cette sortie. D'abord, infinite loop detected : le vérificateur a repéré le retour en arrière (goto pc-2) et refusé. Ensuite, limit 1000000 : le noyau borne l'analyse à un million d'instructions, c'est sa garantie de terminer son propre travail. Enfin, la machine n'a pas bronché : le programme n'a jamais été chargé, le noyau est intact, on récupère juste une erreur Invalid argument. Le vérificateur est un videur, pas un piège.
Ce n'est qu'une de ses règles. La leçon le vérificateur eBPF expliqué détaille les autres (accès mémoire bornés, pile initialisée, valeurs de map testées contre NULL) et comment lire ses messages.
Sécurité et points de vigilance
Section intitulée « Sécurité et points de vigilance »Charger un programme eBPF est une opération privilégiée, et pour de bonnes raisons : un programme mal intentionné peut observer des données sensibles. Quelques réflexes dès le premier jour.
- Les outils de cette page exigent
root. Mesuré en lab (Ubuntu 24.04 et AlmaLinux 10.2),bpftracerefuse de démarrer pour un compte non root, même doté deCAP_BPFetCAP_PERFMON, et les outils BCC échouent faute d'accès à/sys/kernel/tracing. Depuis le noyau 5.8, la capacitéCAP_BPF(associée àCAP_PERFMONpour le traçage) permet à un agent conçu pour cela de charger des programmes sans tous les pouvoirs du super-utilisateur, ce qui limite la casse s'il est compromis. Cette voie n'a pas été rejouée ici ; la leçon durcir eBPF en donne les limites. bpf_trace_printkest réservé au débogage. Il écrit dans un canal unique et partagé (trace_pipe) : pratique pour apprendre, inadapté dès qu'il y a du volume ou plusieurs programmes. En vrai, on transmet les données via des maps, décrites dans maps : le pont noyau / espace utilisateur.- BCC compile sur la cible. C'est simple pour apprendre, mais cela impose clang et les en-têtes du noyau sur chaque machine. La voie de production, libbpf + CO-RE, compile une fois et tourne partout ; voir BCC et libbpf.
Dépannage
Section intitulée « Dépannage »Quand le premier programme ne démarre pas, le message d'erreur désigne presque toujours l'une de ces causes, et la dernière colonne donne le geste qui la corrige.
| Symptôme | Cause probable | Solution |
|---|---|---|
Failed to compile BPF module | En-têtes du noyau absents | Installer linux-headers-$(uname -r) (ou kernel-devel) |
Permission denied au chargement | Programme lancé sans privilège | Lancer avec sudo (les capacités seules ne suffisent pas pour bpftrace ni BCC, mesuré en lab) |
infinite loop detected / back-edge | Boucle non bornée | Borner la boucle (compteur fixe) ou la supprimer |
/sys/kernel/btf/vmlinux introuvable | Noyau sans CONFIG_DEBUG_INFO_BTF | Utiliser bpftrace ou BCC, qui n'en ont pas besoin ; éviter les outils CO-RE |
| Aucune ligne ne s'affiche | Aucun événement ne se produit | Provoquer l'activité tracée (lancer des commandes) |
bpftrace: command not found | Paquet non installé | Installer bpftrace (voir plus haut) |
À retenir
Section intitulée « À retenir »- Avant tout, vérifier le noyau :
/sys/kernel/btf/vmlinux,CONFIG_BPF,CONFIG_BPF_SYSCALL,CONFIG_DEBUG_INFO_BTFetbpftool feature probe. - bpftrace trace en une ligne : idéal pour voir eBPF fonctionner sans écrire de C.
- BCC laisse écrire le programme en C et le piloter en Python ; c'est la voie facultative, celle d'un développeur d'outils.
- Charger un programme enchaîne compilation en bytecode, vérification, JIT, accrochage ; c'est instantané mais bien réel.
- Une kprobe s'accroche à une fonction du noyau, un tracepoint à un point d'observation stable.
- Le vérificateur refuse proprement tout programme qu'il ne peut prouver sûr, sans jamais mettre la machine en danger.
- Charger de l'eBPF est privilégié :
bpftraceet BCC exigentroot, etCAP_BPFn'est une voie que pour les agents conçus pour elle.
Contrôler vos acquis
Section intitulée « Contrôler vos acquis »Ce quiz porte sur le geste : vérifier un noyau, choisir entre bpftrace et BCC, et interpréter ce que le chargeur et le vérificateur répondent.
Contrôle de connaissances
Validez vos connaissances avec ce quiz interactif
Informations
- Le chronomètre démarre au clic sur Démarrer
- Questions à choix multiples, vrai/faux et réponses courtes
- Vous pouvez naviguer entre les questions
- Les résultats détaillés sont affichés à la fin
Lance le quiz et démarre le chronomètre
Vérification
(0/0)Score par domaine
Un domaine regroupe plusieurs guides et reçoit plusieurs questions : son score est plus fiable qu'un score par thème, qui porte souvent sur une seule question.
Bilan
Points forts
Aucun sujet réussi à 80 % sur au moins deux questions pour l’instant.
Points faibles
Aucun sujet sous le seuil de réussite.
À faire ensuite
Des indices pour retenter votre chance ?
Continuer le cours : Les maps, entre noyau et espace utilisateur
Nouveau quiz complet avec des questions aléatoires
Retravailler uniquement les questions ratées
Retour à la liste des certifications