Skip to content

Confiner les services systemd avec un bac à sable

Énumérer, désactiver, noter et confiner les unités systemd via des drop-ins pour réduire la surface d'attaque de chaque service Linux.

Publié le Mis à jour le 3 min de lecture

Chaque service activé est une parcelle de surface d'attaque : un processus joignable, exploitable, ou détournable pour une élévation de privilèges. systemd offre deux leviers puissants pour réduire cette surface sans toucher au code applicatif — couper ce dont vous n'avez pas besoin, et confiner le reste avec un bac à sable appliqué par le noyau. Ce guide détaille l'énumération des unités, l'évaluation de leur exposition, et l'application d'un durcissement par drop-in qui survit aux mises à jour.

Énumérer et réduire ce qui tourne

Avant tout durcissement, déterminez ce qui est réellement activé et en cours d'exécution. Une installation par défaut embarque des dizaines d'unités que vous n'utiliserez jamais.

# Unités configurées pour démarrer au boot
systemctl list-unit-files --state=enabled

# Services actifs dans ce démarrage
systemctl list-units --type=service --state=running

Pour chaque service, demandez-vous : cet hôte en a-t-il vraiment besoin ? Un serveur web n'a guère besoin de Bluetooth, ModemManager ou Avahi. Désactivez l'inutile pour qu'il ne redémarre plus, et masquez ce que vous voulez rendre impossible à démarrer (même comme dépendance) :

# Arrêter maintenant et empêcher tout démarrage futur
sudo systemctl disable --now bluetooth.service avahi-daemon.service

# Masquer : lier vers /dev/null pour interdire tout démarrage
sudo systemctl mask cups.service

Le masquage est l'interrupteur le plus fort — une unité masquée ne peut être activée manuellement ni tirée par une autre unité. Réservez-le aux services dont vous êtes certain qu'ils ne doivent jamais tourner sur cet hôte.

Noter l'exposition existante

systemd embarque un analyseur intégré qui évalue le degré d'exposition d'une unité. Utilisez-le comme référence de départ et comme tableau de bord.

# Classement global de tous les services par exposition
systemd-analyze security

# Détail pour une unité précise
systemd-analyze security nginx.service

La sortie par unité liste chaque prédicat de confinement, indique s'il est satisfait, et donne un niveau d'exposition global allant de OK (bien confiné) à MEDIUM puis UNSAFE. Le tableau détaillé vous dit exactement quelles directives manquent : c'est votre liste de tâches de durcissement.

Confiner une unité avec un drop-in

Ne modifiez jamais les fichiers unité du fournisseur dans /usr/lib/systemd/system ; ils sont écrasés à la mise à jour. Créez plutôt un drop-in :

sudo systemctl edit nginx.service

Cela ouvre un override sous /etc/systemd/system/nginx.service.d/override.conf. Ajoutez un bloc [Service] :

[Service]
NoNewPrivileges=yes
ProtectSystem=strict
ProtectHome=yes
PrivateTmp=yes
PrivateDevices=yes
ProtectKernelTunables=yes
ProtectKernelModules=yes
ProtectControlGroups=yes
RestrictAddressFamilies=AF_INET AF_INET6
CapabilityBoundingSet=
AmbientCapabilities=
SystemCallFilter=@system-service
MemoryDenyWriteExecute=yes
LockPersonality=yes
RestrictNamespaces=yes
ReadWritePaths=/var/lib/nginx /var/log/nginx

Le rôle de ces directives, en bref :

  • NoNewPrivileges bloque toute escalade setuid/setgid pour le processus et ses enfants.
  • ProtectSystem=strict monte tout le système de fichiers en lecture seule ; ReadWritePaths ouvre les rares répertoires où le service écrit légitimement.
  • ProtectHome=yes masque entièrement /home, /root et /run/user.
  • PrivateTmp et PrivateDevices fournissent un /tmp isolé et un /dev minimal.
  • ProtectKernelTunables, ProtectKernelModules, ProtectControlGroups interdisent l'écriture dans /proc//sys, le chargement de modules et les évasions de cgroup — directement liés au durcissement du noyau plus large.
  • RestrictAddressFamilies limite les sockets à IPv4/IPv6, en complément de votre politique de pare-feu réseau.
  • Vider CapabilityBoundingSet et AmbientCapabilities retire toutes les capacités Linux ; ne réajoutez que celles nécessaires au démon (par exemple CAP_NET_BIND_SERVICE).
  • SystemCallFilter=@system-service autorise le jeu d'appels système standard d'un service et bloque le reste.

Pour une défense en profondeur, associez ces confinements à un profil de contrôle d'accès obligatoire afin qu'une seule erreur de configuration ne soit pas le dernier rempart entre un attaquant et l'hôte.

Appliquer et revérifier

Rechargez le gestionnaire, redémarrez le service, puis renotez-le :

sudo systemctl daemon-reload
sudo systemctl restart nginx.service
systemd-analyze security nginx.service

Le niveau d'exposition devrait descendre d'un ou plusieurs paliers. Le confinement trop strict est le principal mode d'échec — un appel système refusé ou une entrée ReadWritePaths manquante fera planter le service au démarrage. Vérifiez toujours sa santé et lisez le journal :

systemctl status nginx.service
journalctl -u nginx.service -b --no-pager | tail -n 40

Cherchez Operation not permitted, Read-only file system ou des messages de seccomp : chacun pointe une directive trop stricte pour cette charge. Assouplissez le seul réglage fautif plutôt que d'abandonner tout le profil. Alimentez ces événements dans votre chaîne de journalisation et d'audit pour détecter automatiquement les régressions futures. Itérez une unité à la fois jusqu'à confiner vos démons les plus exposés.