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.
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 :
NoNewPrivilegesbloque toute escalade setuid/setgid pour le processus et ses enfants.ProtectSystem=strictmonte tout le système de fichiers en lecture seule ;ReadWritePathsouvre les rares répertoires où le service écrit légitimement.ProtectHome=yesmasque entièrement/home,/rootet/run/user.PrivateTmpetPrivateDevicesfournissent un/tmpisolé et un/devminimal.ProtectKernelTunables,ProtectKernelModules,ProtectControlGroupsinterdisent l'écriture dans/proc//sys, le chargement de modules et les évasions de cgroup — directement liés au durcissement du noyau plus large.RestrictAddressFamilieslimite les sockets à IPv4/IPv6, en complément de votre politique de pare-feu réseau.- Vider
CapabilityBoundingSetetAmbientCapabilitiesretire toutes les capacités Linux ; ne réajoutez que celles nécessaires au démon (par exempleCAP_NET_BIND_SERVICE). SystemCallFilter=@system-serviceautorise 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.