Sections du handbook

Durcir les services systemd

Revu le

Chaque démon en cours d'exécution est une surface d'attaque accessible. Éteignez les unités inutiles et confinez celles que vous gardez, puis mesurez l'exposition de chacune.

La menace : démons oubliés et démons trop puissants

Une installation Linux standard démarre des dizaines d'unités que vous n'avez jamais demandées : une pile d'impression (cups), un résolveur de noms en cache, le Bluetooth, la découverte Avahi/mDNS, un shell de débogage, la collecte de core dumps. Chacune est du code qui s'exécute en processus privilégié, écoute souvent sur un socket, et peut être atteinte avant qu'un seul humain ne se connecte. La chaîne RCE cups-browsed de 2024 en est l'exemple type : un service dont presque personne n'avait besoin sur un serveur, à l'écoute du réseau, transformé en exécution de code à distance.

Un service oublié en marche est pire qu'un service choisi : il est hors de votre modèle de menace, non patché, non surveillé, et souvent activé par socket, donc il s'éveille à la demande même lorsqu'il paraît inactif. Et un démon qui doit tourner s'exécute avec bien plus de portée qu'il n'en a besoin (visibilité totale sur le système de fichiers, accès en écriture à /home, capacité à acquérir de nouveaux privilèges), si bien qu'une seule faille compromet l'hôte entier.

Pourquoi le durcir

Le service le plus sûr est celui qui ne tourne pas, et le deuxième le plus sûr est celui qui ne peut pas dépasser sa fonction. Désactiver les unités inutiles réduit la surface d'attaque : moins de ports ouverts, moins de processus privilégiés, moins de CVE qui vous concernent. Pour les démons gardés, le confinement plafonne le rayon d'impact : si le processus est compromis, le noyau lui refuse encore le système de fichiers, les répertoires personnels, les capacités supplémentaires et les appels système dangereux dont il ne se sert jamais légitimement.

Mesurer l'exposition d'abord : systemd-analyze security

Ne devinez pas quels services sont exposés, mesurez-les. systemd-analyze security liste chaque service avec un score d'exposition de 0.0 (sûr) à 10.0 (dangereux) et un verdict UNSAFE/EXPOSED/MEDIUM/OK ; systemd-analyze security <unité> détaille chaque directive de sandboxing et ce qu'elle apporterait. C'est la baseline avant et après durcissement, et le moyen le plus rapide de trouver le pire cas.

systemd-analyze security              # classe tous les services par exposition
systemd-analyze security nginx.service  # détail par directive + correctifs suggeres

Confiner les services que vous gardez

Appliquez les directives dans un drop-in, jamais en éditant l'unité du paquet : systemctl edit <unité> écrit /etc/systemd/system/<unité>.d/override.conf, puis systemctl daemon-reload && systemctl restart <unité>. Les directives à forte valeur :

Directive Effet
ProtectSystem=strict tout le système de fichiers en lecture seule sauf les chemins d'API (à coupler avec ReadWritePaths=)
ProtectHome=yes /home, /root, /run/user rendus inaccessibles
PrivateTmp=yes un /tmp privé, isolé de l'hôte
PrivateDevices=yes un /dev minimal, pas de matériel brut
NoNewPrivileges=yes bloque l'escalade setuid/setgid/capacités via execve
ProtectKernelTunables/Modules/Logs=yes /proc+/sys en lecture seule, pas de chargement de module, pas de kmsg
ProtectControlGroups=yes, ProtectClock=yes pas d'altération des cgroups ni de l'horloge
RestrictAddressFamilies= seulement les familles de sockets nécessaires
RestrictNamespaces=, RestrictSUIDSGID=yes, RestrictRealtime=yes refuse la création de namespaces, les fichiers setuid, l'ordonnancement temps réel
MemoryDenyWriteExecute=yes, LockPersonality=yes pas de mémoire W+X, architecture figée
SystemCallFilter=@system-service n'autorise que les appels système légitimes du service
CapabilityBoundingSet= retire toute capacité Linux inutile au service

Ce que Pavois audite : l'état systemd vivant

Le facteur différenciant : Pavois lit l'état systemd vivant, jamais un fichier de configuration. Pour chaque unité, il interroge le statut résolu is-enabled / is-active (la ressource service d'InSpec via systemctl), et voit donc ce que la machine fera vraiment au prochain démarrage, en tenant compte de l'activation par socket : un cups.service qui semble inactif peut être relancé par cups.socket, ce qu'un scan de /etc/cups/cupsd.conf ne décèlerait jamais.

Les règles de Pavois vérifient que les unités inutiles sont désactivées et arrêtées (cups, avahi-daemon, bluetooth, cockpit, nfs, rpcbind, snmpd, slapd, named, dnsmasq, autofs, smb, vsftpd, tftp, telnet, xinetd, rsyncd, dovecot, squid, apport, kdump, le debug-shell noyau et systemd-coredump) et que les unités liées à la sécurité sont activées et en cours d'exécution (auditd, systemd-journald, sssd, usbguard, fapolicyd, rngd, cron/crond). Les résultats sont mappés sur CIS, NIST CM-7 (fonctionnalité minimale) et ANSSI BP-028 R67, et le plan de durcissement peut désactiver/arrêter ou activer/démarrer chaque unité.

Pièges

  • ProtectSystem=strict sans ReadWritePaths= rend en lecture seule les répertoires d'état/log/cache du service, qui ne démarre plus. Ajoutez les chemins où il doit écrire (/var/lib/<svc>, /var/log/<svc>).
  • Une directive trop agressive laisse le service actif mais muet : il démarre, puis ne peut silencieusement plus faire son travail (par ex. un appel système refusé). Surveillez systemctl status et le code de sortie.
  • Les codes de sortie du sandbox sont des indices : 226/NAMESPACE, 227, 228/SECCOMP, 233/NETWORK pointent directement la directive trop stricte.
  • mask contre disable : systemctl disable empêche le démarrage automatique ; systemctl mask rend l'unité totalement indémarrable (garantie plus forte pour un service banni).

FAQ

Comment savoir quels services sont trop exposés ? systemd-analyze security les classe de 0 à 10 ; commencez par les UNSAFE.

Pourquoi Pavois lit-il systemctl et non la config ? Parce que l'activation par socket et l'état résolu enabled/active sont la vérité ; une unité peut être relancée par socket même quand son fichier de config paraît inerte.

Mon service ne démarre plus après le sandboxing. Pourquoi ? Le plus souvent ProtectSystem=strict sans ReadWritePaths=, ou un SystemCallFilter qui a retiré un appel nécessaire. Les codes de sortie 226-233 nomment le coupable ; relâchez la seule directive en cause, pas tout le sandbox.