SecurityGuide

Durcissement du noyau Linux : checklist sysctl

Sur cette page
  1. Pourquoi sysctl est le durcissement le moins cher à déployer
  2. Comment appliquer la checklist (persistant + audit)
  3. Réseau, 9 clés
  4. Kernel, 8 clés
  5. Système de fichiers, 7 clés
  6. Processus, BPF et modules, 8 clés
  7. Vérifier, établir une référence et ré-auditer
  8. Pièges et conseils de rollback
  9. Sources et pour aller plus loin

Le durcissement du noyau Linux avec sysctl, c'est le meilleur rapport sécurité/minute que la plateforme ait à offrir, donc c'est par là que je commence à chaque fois. La plupart des valeurs sysctl sortent du carton avec des réglages sains. La plupart. Mais une poignée restent aux valeurs par défaut de la distrib, et celles-là offrent discrètement à un attaquant un point d'appui, ou une fuite d'info à laquelle il n'a rien à faire d'accéder. Voici donc la liste que j'utilise vraiment. 32 réglages que CIS Distribution Independent Linux, ANSSI BP-028, le mode Heavy de lynis et Bastille retiennent tous en 2026. Regroupés par sous-système, triés selon l'importance de chacun, pour voir d'un coup ce qui part en prod aujourd'hui.

The short answer

Pose les 32 réglages dans un seul /etc/sysctl.d/99-hardening.conf, recharge avec sysctl --system, et tu as basculé les paramètres kernel à l'exécution qui comptent : ASLR complet, ptrace restreint, pas de paquets source-routed, pas de pointeurs kernel qui fuitent, les contre-mesures contre le JIT-spray BPF et les garde-fous sur la traversée des liens et des FIFO. Regroupés par sous-système, triés par criticité, avec un one-liner qui attrape toute valeur qui dérive.

32 clésréglages sysctl qui comptent
10 minsans reboot
1 fichierdans git, diffé par la CI
Carte réponse : 32 réglages sysctl de durcissement regroupés en réseau, kernel, système de fichiers et processus plus BPF plus modules, triés par criticité.
Les 32 réglages, répartis par sous-système. Les critiques vont sur chaque machine ; le reste dépend de ce que fait réellement l'hôte. PNG

La plupart des valeurs sysctl sortent du carton avec des réglages sains. La plupart. Mais une poignée d'entre elles restent aux valeurs par défaut de la distrib, et celles-là offrent discrètement à un attaquant un point d'appui, ou une fuite d'info à laquelle il n'a rien à faire d'accéder. J'ai vu des machines tourner des années sans que personne n'y touche jamais. Bref. Voici la liste que j'utilise vraiment. 32 réglages que CIS Distribution Independent Linux, ANSSI BP-028, le mode Heavy de lynis et les réécritures de Bastille continuent tous de retenir en 2026. Regroupés par sous-système, triés selon l'importance de chacun, pour que tu puisses jeter un œil et voir d'un coup ce qui part en prod aujourd'hui contre ce qui attend la prochaine fenêtre de maintenance.

Pourquoi sysctl est le durcissement le moins cher à déployer

J'ai perdu des semaines entières sur SELinux et AppArmor, rien que pour les empêcher de bloquer quelque chose de légitime en prod. auditd et Falco, même histoire. Vraie protection, vrai gouffre à temps. sysctl, c'est l'inverse. Dix minutes. Les 32 réglages ici font basculer des paramètres kernel à l'exécution, que le kernel vérifie au moment des syscalls concernés : la force de l'ASLR, qui a le droit de faire un ptrace sur quoi, est-ce qu'on avale les paquets source-routed, est-ce que les pointeurs kernel fuitent, les contre-mesures contre le JIT-spray BPF, les garde-fous sur la traversée des FIFO et des hardlinks. Aucun ne demande de reboot. Aucun ne change quoi que ce soit qu'une charge de travail saine remarquera un jour. Si tu veux le meilleur rapport sécurité/minute que Linux ait à offrir, honnêtement, c'est par là que je commence à chaque fois.

Comment appliquer la checklist (persistant + audit)

Tout va dans un seul fichier sous /etc/sysctl.d/ pour que le kernel le réapplique à chaque boot. Ensuite je le recharge à chaud. Pas de redémarrage. Ce même fichier, c'est aussi ce contre quoi ma référence CI fait son diff, donc il vit dans git. S'il n'est pas sous gestion de versions, il n'existe pas.

sudo install -m 0644 /dev/null /etc/sysctl.d/99-hardening.conf
sudo tee /etc/sysctl.d/99-hardening.conf > /dev/null <<'EOF'
# === Network ===
net.ipv4.ip_forward = 0
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.all.log_martians = 1
net.ipv4.tcp_syncookies = 1
net.ipv4.icmp_echo_ignore_broadcasts = 1
net.ipv6.conf.all.accept_ra = 0

# === Kernel ===
kernel.randomize_va_space = 2
kernel.kptr_restrict = 2
kernel.dmesg_restrict = 1
kernel.unprivileged_bpf_disabled = 1
kernel.kexec_load_disabled = 1
kernel.yama.ptrace_scope = 2
kernel.sysrq = 0
kernel.perf_event_paranoid = 3

# === Filesystem ===
fs.protected_symlinks = 1
fs.protected_hardlinks = 1
fs.protected_fifos = 2
fs.protected_regular = 2
fs.suid_dumpable = 0
kernel.core_pattern = |/bin/false

# === User namespaces, BPF hardening ===
kernel.unprivileged_userns_clone = 0
user.max_user_namespaces = 0
vm.unprivileged_userfaultfd = 0
net.core.bpf_jit_harden = 2
EOF
sudo sysctl --system

--system rescanne chaque .conf sous /etc/sysctl.d/, /run/sysctl.d/ et /usr/lib/sysctl.d/, toujours dans le même ordre. Lance-le une fois après chaque modif. Pas de reboot. Et ne te contente pas de supposer que ça a pris. Relis la valeur avec sysctl -n <key> et confronte-la au fichier. Une faute de frappe dans un nom de clé va rester là à ne strictement rien faire, sans jamais broncher. Demande-moi comment je le sais.

Réseau, 9 clés

C'est le groupe que les attaquants sondent en premier. Donc c'est le groupe que je règle correctement en premier.

  • net.ipv4.ip_forward = 0, coupe le routage IP sur tout ce qui n'est pas un routeur. La plupart des distribs le livrent déjà à 0. Voilà le piège, par contre : Docker, kubeadm, libvirt et OpenVPN vont tous le rebasculer à 1 dans ton dos. Re-vérifie-le après chaque installation de conteneur ou de VPN, parce qu'il ne te préviendra sûrement pas qu'il a bougé.
  • net.ipv4.conf.all.rp_filter = 1, filtrage strict du chemin inverse. Jette les adresses source usurpées qui n'ont rien à faire à arriver sur l'interface où elles se sont pointées. Hôte multi-homed ? Alors celui-là n'est pas optionnel.
  • net.ipv4.conf.all.accept_redirects = 0 et send_redirects = 0, non aux redirections ICMP. N'importe qui assis sur le LAN peut s'en servir pour réécrire ta table de routage. Tu n'as vraiment pas envie de ça.
  • net.ipv4.conf.all.accept_source_route = 0, jette les paquets qui portent des options de source-routing loose ou strict. Je n'ai jamais vu une seule fois un usage légitime de ces trucs sur une machine exposée à Internet. Pas une seule fois. Tue-les.
  • net.ipv4.conf.all.log_martians = 1, journalise les paquets avec des adresses impossibles. Quasiment gratuit, et ça attrape discrètement aussi bien les routeurs cassés que les attaquants négligents.
  • net.ipv4.tcp_syncookies = 1, ta parade contre les SYN-flood. Les kernels modernes l'activent déjà. Je le mets quand même, pour que le fichier dise tout haut ce que je veux.
  • net.ipv4.icmp_echo_ignore_broadcasts = 1, ferme la porte au vieil amplificateur d'attaque smurf. Menace antédiluvienne, certes. Mais pourquoi la laisser ouverte.
  • net.ipv6.conf.all.accept_ra = 0, arrête d'avaler les Router Advertisements IPv6 sur les hôtes qui ne font pas d'autoconf. Sur un serveur posé derrière un fabric que tu contrôles, c'est un incontournable.

Kernel, 8 clés

C'est le seau pour l'ASLR, les pointeurs kernel qui fuitent, plus quelques interfaces héritées qui auraient dû être retirées il y a des années et qui, va savoir, ne l'ont pas été.

  • kernel.randomize_va_space = 2, ASLR complet, segment de données compris. Si tu te soucies un tant soit peu de la mitigation des exploits, c'est le plancher.
  • kernel.kptr_restrict = 2, masque les adresses des symboles kernel dans /proc/kallsyms à quiconque n'est pas root. La fuite de pointeur kernel la plus facile qui soit. Ça la ferme.
  • kernel.dmesg_restrict = 1, verrouille dmesg derrière CAP_SYS_ADMIN. Les messages de boot crachent des adresses et des versions de modules en permanence, et sans ça un attaquant lit tout ça gratuitement.
  • kernel.unprivileged_bpf_disabled = 1, empêche les utilisateurs non-root de charger des programmes BPF. Ce seul réglage retire toute une famille d'attaques par canal auxiliaire et de JIT-spray de l'équation.
  • kernel.kexec_load_disabled = 1, bascule-le à 1 et kexec disparaît pour de bon. Un attaquant déjà root ne peut pas booter discrètement son propre kernel sans un vrai reboot que tu remarquerais.
  • kernel.yama.ptrace_scope = 2, désormais seul root a le droit de faire un ptrace sur un processus en cours. Fini d'aller piocher des identifiants directement dans la mémoire d'un autre processus.
  • kernel.sysrq = 0, tue purement et simplement les combos Magic SysRq. De toute façon, pas de clavier console sur tes serveurs. Tu ne perds rien.
  • kernel.perf_event_paranoid = 3, refuse perf_event_open aux utilisateurs non privilégiés et ferme un canal auxiliaire connu de la classe Spectre. Tu ne le sentiras que si tu profiles en prod. Ce qui, allons, tu ne devrais pas faire.

Système de fichiers, 7 clés

Ici, ce sont les races sur les liens et les FIFO, plus les dumps de crash qui livrent discrètement de la mémoire que tu n'as jamais eu l'intention de partager.

  • fs.protected_symlinks = 1 et fs.protected_hardlinks = 1, ces deux-là coupent la race /tmp des manuels. Tu connais celle-là : un attaquant pointe /tmp/foo vers /etc/passwd une fraction de seconde avant qu'un processus root n'y écrive. Vieille combine. Marche encore sur les machines non durcies, et c'est ça le plus déprimant.
  • fs.protected_fifos = 2 et fs.protected_regular = 2, même idée, étendue pour couvrir aussi les pipes FIFO et les fichiers réguliers posés dans des répertoires sticky.
  • fs.suid_dumpable = 0, les binaires SUID arrêtent de pondre des fichiers core. Bien. Ces cores transportent de la mémoire privilégiée que tu n'as vraiment, vraiment pas envie de voir traîner sur le disque.
  • kernel.core_pattern = |/bin/false, redirige chaque core vers /bin/false au lieu de l'écrire quelque part. Couple ça à suid_dumpable=0 et il ne reste plus aucun dump de crash à exfiltrer, point. Souviens-toi juste que tu l'as fait. Ne le repointe vers un vrai handler que quand tu traques réellement un bug.

Processus, BPF et modules, 8 clés

Dernier groupe. Celui-ci ferme la porte aux user namespaces, et au chargement de modules kernel après coup.

  • kernel.modules_disabled = 1, mets-le après le boot, une fois que tous les modules dont tu as besoin sont déjà chargés. À partir de là, plus rien ne s'insère. Pas même root. C'est brutal pour quiconque essaie de persister un rootkit kernel, et c'est tout l'intérêt.
  • kernel.unprivileged_userns_clone = 0 et user.max_user_namespaces = 0, ces deux-là arrachent les user namespaces aux non-root. Tout ce qui en dépend (Docker rootless, Podman) casse bruyamment et instantanément. Donc sur un hôte qui existe pour faire tourner ces trucs, laisse ces deux-là désactivés.
  • vm.unprivileged_userfaultfd = 0, coupe le chemin de syscall sur lequel s'appuie une bonne partie des exploits use-after-free. Victoire pas chère, honnêtement.
  • net.core.bpf_jit_harden = 2, randomise les constantes dans le JIT BPF pour que le JIT-spraying n'ait plus rien de prévisible à viser.
  • Et tant que tu y es, ajoute vsyscall=none et slab_nomerge à GRUB_CMDLINE_LINUX. Pas des sysctls, non. Mais dans ma tête c'est la même passe de durcissement, donc ils voyagent dans le même changement.

Attention : kernel.modules_disabled = 1 ne va que dans un sens. Une fois activé, l'hôte ne chargera plus aucun module jusqu'au prochain reboot, donc si quelque chose dont tu as besoin n'était pas déjà chargé, eh bien, tu rebootes pour réparer. Vérifie d'abord que tes modules VPN, de système de fichiers chiffré et graphiques apparaissent tous dans lsmod, puis commits. J'ai appris ça à la dure, et j'aimerais autant t'épargner ça.

Vérifier, établir une référence et ré-auditer

Voilà pourquoi je fais plus confiance à un fichier qu'à ma propre mémoire. Je peux faire un diff du kernel en cours contre lui. Le one-liner ci-dessous attrape tout ce qui a dérivé : un paquet qui s'est mal comporté à l'installation, ou le petit sysctl -w d'un collègue que personne n'a pris la peine de noter.

awk -F '=' '/^[^#]/ { gsub(/ /, "", $1); k=$1; gsub(/ /, "", $2); v=$2;
  cmd="sysctl -n " k " 2>/dev/null"; cmd | getline live; close(cmd);
  if (live != v) printf "DRIFT  %-40s  file=%s  live=%s\n", k, v, live
}' /etc/sysctl.d/99-hardening.conf

Balance ça dans un cron nocturne et tu seras au courant d'une dérive en moins de 24 heures au lieu de la découvrir en plein incident, peu importe que ce soit une mise à jour du kernel ou un runtime de conteneurs qui a discrètement remis l'un de tes boutons à zéro. Tu veux un filet plus large que les seuls sysctls ? Lance lynis audit system --pentest contre l'hôte. Sa section kernel hardening s'aligne une-pour-une avec les clés ci-dessus.

Terminal : le one-liner de dérive signale une seule ligne DRIFT où la valeur kernel en cours de net.ipv4.ip_forward ne correspond plus au fichier de durcissement.
La vérification de dérive nocturne en action : une ligne par clé qui ne correspond plus au fichier, pour attraper un bouton basculé avant qu'il ne morde en plein incident. PNG

Pièges et conseils de rollback

  • Docker et Kubernetes détestent ip_forward = 0. Si l'un ou l'autre vit sur la machine, mets net.ipv4.ip_forward = 1 et laisse le firewall par-bridge du runtime de conteneurs faire le gardien. Lutter ici ne fait que déchiqueter ton réseau.
  • Podman rootless a besoin des user namespaces. Ne pose pas kernel.unprivileged_userns_clone = 0 sur un poste de dev qui fait tourner Podman rootless ou Docker rootless. Tu vas griller une après-midi à te demander pourquoi rien ne démarre.
  • Et bpf_jit_harden = 2 n'est pas gratuit. Les chemins chauds qui s'appuient sur XDP ou eBPF prennent un petit coût CPU avec ça. Sur un proxy en bordure ça s'additionne, donc mesure avant et après plutôt que de deviner. Peut-être que c'est négligeable pour ton trafic. Peut-être que non. Vérifie.
  • Les dumps de crash disparaissent, tout simplement. À la seconde où core_pattern pointe vers /bin/false, chaque segfault que tu voudrais réellement déboguer s'en va avec. Note comment réactiver temporairement un vrai handler de core. Garde ça sous contrôle de changement pour que ce soit un geste délibéré, pas une panique de 2h du matin.
  • Oublier sysctl --system. Édite le fichier, saute le rechargement, et le kernel en cours reste sur les anciennes valeurs jusqu'au prochain boot. Du coup tu te crois protégé. Tu ne l'es pas. Termine toujours par le rechargement, et intègre-le dans ta passe de config-management pour que personne ne puisse l'oublier.

Sources et pour aller plus loin

Questions fréquentes

Est-ce que l'un de ces réglages cassera un serveur Ubuntu / Debian / RHEL standard ?

Pour un rôle de serveur normal (web, base de données, mail, peu importe) non, et j'ai déployé ces réglages sur des tas de machines. Ce qui change vraiment, ce sont des cas marginaux : Magic SysRq, divulgation des symboles kernel, redirections ICMP. Aucun vrai service de prod ne s'appuie là-dessus. Les deux seuls que je signalerais sont ip_forward = 0 si Docker vit aussi sur la machine, et unprivileged_userns_clone = 0 si tu fais tourner des conteneurs rootless. Ces deux-là vont mordre. Le reste, non.

Pourquoi /etc/sysctl.d/99-hardening.conf et pas /etc/sysctl.conf ?

Trois raisons, et je me suis brûlé en ignorant les trois. Sur les distribs de la famille RHEL, /etc/sysctl.conf appartient à un paquet, donc une mise à jour peut écraser tes modifs en douce. Les fichiers sous /etc/sysctl.d/ sont lus par sysctl --system dans l'ordre numérique, donc le préfixe 99- fait que ton durcissement se charge en dernier et l'emporte sur tout conflit. Dernière raison : ton outil de config-management peut déposer un fichier entièrement séparé sans avoir à parser et fusionner ce qui était déjà posé là. Plus propre quel que soit l'angle.

Faut-il rebooter après avoir édité le fichier ?

Non. sysctl --system rescanne les répertoires et pousse chaque clé dans le kernel en cours sur-le-champ. La seule exception ici, c'est kernel.modules_disabled. Tu peux l'activer à chaud, certes, mais une fois à 1 il reste à 1 jusqu'au reboot. Pas moyen de revenir en arrière sans ça.

Quel est le rapport entre cette checklist et CIS, ANSSI ou DISA STIG ?

Vois-la comme le recouvrement entre ces sources, rogné aux clés que je suis à l'aise d'appliquer sans m'asseoir d'abord pour passer en revue une charge de travail spécifique. CIS Distribution Independent Linux v1.1 couvre les 32. ANSSI BP-028 en touche 28. DISA STIG fait les sous-ensembles kernel et système de fichiers, puis empile par-dessus la configuration auditd. Le vrai boulot en entreprise est plus gros que ça (profils SELinux ou AppArmor, règles auditd, tout le toutim) mais ces sysctls sont la partie que tu peux réellement déployer aujourd'hui.

Y a-t-il des clés que tu as volontairement laissées de côté ?

Plein, exprès. Tout ce qui n'est en fait que du tuning de performance reste dehors : les buffers net.ipv4.tcp_*, vm.swappiness, vm.dirty_ratio, les trucs spécifiques au stockage comme vm.vfs_cache_pressure. Ce sont des boutons de charge de travail, pas de sécurité, et les mélanger ne fait que rendre la référence plus difficile à raisonner. Je les parque dans un 10-perf.conf séparé pour que ce fichier de sécurité reste identique sur chaque hôte.