Charger un programme eBPF est un privilège puissant : le durcissement consiste à décider qui peut le faire, à limiter la casse en cas de compromission, et à surveiller ce qui tourne. eBPF est une force pour la sécurité, mais aussi une surface d'attaque à part entière. Cette page donne les réglages concrets pour la réduire sur un serveur, avec les valeurs réelles vérifiées sur une VM Ubuntu 24.04 (noyau 6.8) et ancrées dans la documentation du noyau. Public : administrateur qui exploite des serveurs Linux, avec ou sans outils eBPF déployés.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Interdire l'eBPF non privilégié, et comprendre ses trois niveaux
- Remplacer
rootpar la capacitéCAP_BPFpour vos outils - Durcir le compilateur JIT contre une classe d'attaques
- Auditer les programmes eBPF réellement chargés sur une machine
- Situer BPF LSM comme contrôle avancé
eBPF, un privilège à encadrer
Section intitulée « eBPF, un privilège à encadrer »Un programme eBPF s'exécute dans le noyau et peut observer, voire modifier, le comportement du système. Le charger est donc une opération sensible. Durcir eBPF ne revient pas à s'en priver : c'est appliquer le moindre privilège à ce sous-système, exactement comme on le ferait pour sudo ou un service exposé. Trois axes : qui peut charger, avec quels pouvoirs, et comment on le surveille.
Levier 1 : interdire l'eBPF non privilégié
Section intitulée « Levier 1 : interdire l'eBPF non privilégié »Le premier réglage, et le plus important, contrôle si un utilisateur non privilégié peut appeler bpf(). Il se pilote par le sysctl kernel.unprivileged_bpf_disabled. La documentation du noyau définit trois valeurs :
| Valeur | Signification (doc noyau) |
|---|---|
0 | Appels non privilégiés à bpf() autorisés |
1 | Appels non privilégiés désactivés, sans retour possible (verrou à sens unique) |
2 | Appels non privilégiés désactivés, mais réversible |
Sur la VM de test, valeur réelle :
sysctl kernel.unprivileged_bpf_disabledkernel.unprivileged_bpf_disabled = 2C'est le défaut des distributions récentes (Ubuntu, Debian) : l'eBPF non privilégié est désactivé. Le laisser ainsi ferme la porte à toute une catégorie d'exploits, car plusieurs failles historiques n'étaient exploitables que par un appelant non privilégié. Pour un serveur qui n'a aucun besoin d'eBPF non privilégié, la valeur 1 (verrou définitif) est encore plus stricte.
Levier 2 : CAP_BPF au lieu de root
Section intitulée « Levier 2 : CAP_BPF au lieu de root »Historiquement, charger un programme eBPF exigeait CAP_SYS_ADMIN, c'est-à-dire quasiment les pleins pouvoirs. Depuis le noyau 5.8, une capacité dédiée, CAP_BPF (souvent accompagnée de CAP_PERFMON pour le traçage), permet de charger sans tout le reste. Sur la VM, ces capacités existent bien :
capsh --print | grep -o 'cap_bpf\|cap_perfmon'cap_bpfcap_perfmonL'intérêt est direct : un outil d'observabilité (agent de profilage, collecteur) qui n'a besoin que de charger des sondes doit tourner avec CAP_BPF et CAP_PERFMON, pas en root. Si l'outil est compromis, l'attaquant n'hérite pas de CAP_SYS_ADMIN. C'est du moindre privilège appliqué à la lettre, à accorder par exemple via les capacités d'une unité systemd (AmbientCapabilities).
Levier 3 : durcir le compilateur JIT
Section intitulée « Levier 3 : durcir le compilateur JIT »Une fois vérifié, un programme est traduit en code machine par le JIT (compilateur à la volée). Cette étape a été la cible d'attaques dites de JIT spraying, où un attaquant influence le code généré pour y glisser des instructions utiles. Le noyau offre une parade, le sysctl net.core.bpf_jit_harden. Valeurs documentées :
| Valeur | Effet (doc noyau) |
|---|---|
0 | Durcissement du JIT désactivé (défaut) |
1 | Durcissement activé pour les utilisateurs non privilégiés |
2 | Durcissement activé pour tous les utilisateurs |
La documentation précise que ce durcissement peut atténuer le JIT spraying. Le prix est un léger surcoût de performance. Sur un serveur sensible où des programmes eBPF peuvent être chargés par des utilisateurs variés, passer à 1 (voire 2) est un bon compromis :
# activer le durcissement du JIT pour les non-privilégiéssudo sysctl -w net.core.bpf_jit_harden=1Pour rendre le réglage persistant, on l'ajoute dans un fichier de /etc/sysctl.d/.
Levier 4 : auditer ce qui est chargé
Section intitulée « Levier 4 : auditer ce qui est chargé »On ne durcit bien que ce qu'on observe. bpftool liste tous les programmes eBPF réellement présents dans le noyau, quel que soit l'outil qui les a chargés. C'est la base d'un audit : savoir ce qui tourne, et repérer l'anormal.
sudo bpftool prog show | grep -E '^[0-9]+:' | awk '{print $2}' | sort | uniq -c | sort -rnSortie réelle sur la VM (au repos, seulement le système) :
6 cgroup_skb 6 cgroup_device 1 tracingIci, tout s'explique : les cgroup_skb et cgroup_device sont posés par systemd pour le filtrage réseau et l'accès aux périphériques des services, le tracing est interne. Un programme que vous ne savez pas expliquer mérite une enquête. Sur une machine de production, comparez régulièrement cette liste à une référence connue ; un programme kprobe ou tracing inattendu peut trahir un outil non autorisé, voire un rootkit eBPF.
Levier 5 : BPF LSM, le contrôle fin
Section intitulée « Levier 5 : BPF LSM, le contrôle fin »Pour aller plus loin, le noyau expose BPF LSM (Linux Security Module écrit en eBPF) : des points de décision où l'on peut autoriser ou refuser des opérations, y compris le chargement de programmes eBPF eux-mêmes. On peut ainsi écrire une politique qui n'autorise le chargement que pour certains processus, ou journalise chaque tentative. C'est le degré ultime de contrôle sur le sous-système.
Premier réflexe : vérifier si BPF LSM est actif, car il ne l'est pas toujours par défaut. La liste des modules de sécurité chargés est lisible ici :
cat /sys/kernel/security/lsmSur la VM de test (Ubuntu 24.04), la sortie ne contient pas bpf :
lockdown,capability,landlock,yama,apparmorPour activer BPF LSM, on l'ajoute à la liste au démarrage du noyau, via le paramètre lsm= (par exemple dans la configuration GRUB), puis on redémarre :
lsm=lockdown,capability,landlock,yama,apparmor,bpfUne fois bpf présent dans la liste, on peut attacher des programmes aux hooks de sécurité pour appliquer une politique. C'est un mécanisme avancé, à réserver aux environnements qui ont déjà appliqué les leviers précédents, mais il offre un contrôle qu'aucun sysctl ne permet.
Récapitulatif des réglages
Section intitulée « Récapitulatif des réglages »| Réglage | Recommandation serveur | Effet |
|---|---|---|
kernel.unprivileged_bpf_disabled | 2 (défaut) ou 1 | Bloque l'eBPF non privilégié |
| Capacités des outils | CAP_BPF + CAP_PERFMON, pas root | Moindre privilège |
net.core.bpf_jit_harden | 1 ou 2 sur machine sensible | Atténue le JIT spraying |
Audit bpftool prog show | Régulier, comparé à une référence | Détecte l'anormal |
| Mises à jour du noyau | À jour | Corrige les failles du vérificateur |
Validation
Section intitulée « Validation »Après avoir appliqué ces réglages, vérifiez-les :
sysctl kernel.unprivileged_bpf_disabled net.core.bpf_jit_hardensudo bpftool prog show | wc -l # doit correspondre à ce que vous attendezÀ retenir
Section intitulée « À retenir »- Charger de l'eBPF est un privilège : on lui applique le moindre privilège, comme à tout pouvoir sensible.
unprivileged_bpf_disabled=2(défaut récent) bloque l'eBPF non privilégié ;1le verrouille définitivement.- Faire tourner les outils avec
CAP_BPF/CAP_PERFMON, jamais enrootcomplet. bpf_jit_hardenatténue le JIT spraying, à activer sur les machines sensibles.- Auditer régulièrement les programmes chargés avec
bpftool: l'inexpliqué est suspect. - Le vérificateur a eu des failles (ex. CVE-2021-3490) : le noyau à jour reste la meilleure défense.