Aller au contenu
Réussir mon BTS SIO

Tape une notion, une commande ou le nom d'une épreuve.

Par exemple

Chapitre 1611 minIntermédiaire

Sécuriser un serveur Linux : AppArmor et durcissement

Par Josselyn C., prof de BTS SIO. Mis à jour le .

À la fin de ce chapitre, tu sauras

  • appliquer une liste de contrôle de durcissement à un serveur Debian ;
  • expliquer ce qu'apporte un contrôle d'accès obligatoire comme AppArmor ;
  • lire un refus d'AppArmor et le corriger sans désactiver la protection ;
  • tracer les actions sensibles et vérifier le durcissement.

3 exercices corrigés

Sécuriser (durcir) un serveur Linux, c'est réduire ce qu'un attaquant pourrait exploiter : ne garder que les services utiles, appliquer les correctifs, limiter les comptes et leurs droits, verrouiller l'accès à distance, confiner les programmes et tracer les actions sensibles. Ce chapitre rassemble ces mesures en une liste de contrôle pour Debian 13, détaille AppArmor, le contrôle d'accès obligatoire activé par défaut, et montre comment vérifier le résultat avec auditd et Lynis.

La démarche

Le durcissement suit la méthode présentée dans le cours Cybersécurité des infrastructures : inventorier le rôle du serveur, réduire ce qui ne sert pas, mettre à jour, restreindre, activer les protections, vérifier et documenter. Pour Linux, deux référentiels donnent des listes précises : les Recommandations de configuration d'un système GNU/Linux de l'ANSSI, classées par niveau (minimal, intermédiaire, renforcé, élevé), et le CIS Benchmark de Debian. On ne les applique pas aveuglément : chaque mesure se teste, et chaque écart se justifie.

La liste de contrôle d'un serveur Debian 13

DomaineMesureCommande ou fichierChapitre
InstallationPas d'environnement graphique ; seulement « serveur SSH » et les utilitairesSélection des logiciels2
ServicesLister ce qui écoute ; désactiver ou désinstaller le restesudo ss -tulpn, systemctl disable --now, apt purge9, 11
CorrectifsMises à jour de sécurité automatiques ; redémarrage planifiéunattended-upgrades, needrestart8
ComptesComptes nominatifs ; root verrouillé ; comptes temporaires qui expirentadduser, chage -E, passwd -S5
Privilègessudo par groupe ; règles restreintes, sans éditeur ni joker/etc/sudoers.d/, visudo5
FichiersDroits minimaux, umask 027 ; pas de fichier accessible en écriture à tous ; liste des setuidfind / -xdev -perm -0002 -type f, find / -xdev -perm -40006
Accès distantSSH par clé, sans root, AllowGroups, sources filtrées/etc/ssh/sshd_config.d/12
RéseauPare-feu local nftables : SSH depuis l'administration seulement, le reste fermé/etc/nftables.confcours Cybersécurité
Montagesnodev,nosuid,noexec sur les partitions de données et /var/log ; /tmp est déjà en nosuid,nodev sous Debian 13/etc/fstab13
ConfinementAppArmor actif, profils en mode enforce ; services durcis par systemdaa-status, systemd-analyze securityci-dessous, 9
TracesJournal persistant, envoyé vers le serveur central ; heure synchronisée ; auditd pour les fichiers sensiblesjournald, rsyslog, timedatectl, auditd10
VérificationAudit régulier, écarts documentéslynis audit systemci-dessous

L'heure n'est pas un détail : des journaux horodatés de travers sont inutilisables pour reconstituer un incident, et les certificats TLS sont refusés si l'horloge dérive. timedatectl doit afficher System clock synchronized: yes. Chez Sorbier, les serveurs se synchronisent sur les contrôleurs de domaine, qui servent aussi de source de temps (ligne NTP=192.168.10.177 192.168.10.181 sous la section [Time] de /etc/systemd/timesyncd.conf.d/sorbier.conf, puis sudo systemctl restart systemd-timesyncd). Sur une machine où chrony est installé, comme srv-journaux, c'est lui qui fait ce travail à la place de systemd-timesyncd : ligne server 192.168.10.177 iburst (et la même pour .181) dans /etc/chrony/sources.d/sorbier.sources, puis sudo chronyc reload sources.

AppArmor, le contrôle d'accès obligatoire

Les droits rwx et les ACL sont un contrôle d'accès discrétionnaire (DAC) : c'est le propriétaire d'un fichier qui décide qui y accède, et un programme a tous les droits du compte qui le lance. Si nginx est détourné par une faille, il peut lire tout ce que www-data peut lire.

Un contrôle d'accès obligatoire (MAC) ajoute des règles fixées par l'administrateur, que même le propriétaire ou root ne contournent pas : chaque programme confiné n'a accès qu'aux fichiers, aux capacités et au réseau listés dans son profil. Debian et Ubuntu utilisent AppArmor, actif par défaut depuis Debian 10 ; la famille Red Hat utilise SELinux, qui suit le même principe avec des étiquettes sur chaque fichier.

Un profil fonctionne dans l'un de deux modes :

ModeEffetUsage
enforceLes accès hors profil sont bloqués et journalisésProduction
complainLes accès hors profil sont autorisés mais journalisésMise au point d'un profil, diagnostic
bash
sudo aa-status                 # module chargé ? combien de profils, dans quel mode, quels processus confinés
sudo apt install apparmor-utils apparmor-profiles apparmor-profiles-extra
sudo aa-complain /etc/apparmor.d/usr.sbin.<programme>   # passer un profil en mode complain
sudo aa-enforce  /etc/apparmor.d/usr.sbin.<programme>   # le repasser en enforce
sudo systemctl reload apparmor                           # recharger tous les profils

aa-status commence par apparmor module is loaded., puis donne le nombre de profils chargés, ceux en mode enforce et complain, et la liste des processus effectivement confinés. Un service sans profil tourne non confiné : c'est le cas de nginx sur Debian, qui n'en fournit pas par défaut. Les profils sont dans /etc/apparmor.d/, nommés d'après le chemin du programme (usr.sbin.chronyd pour /usr/sbin/chronyd). Pour ajouter une autorisation à un profil fourni par un paquet, on écrit dans /etc/apparmor.d/local/<nom-du-profil>, que le profil inclut et qu'aucune mise à jour n'écrase.

Lire un refus. Quand AppArmor bloque un accès, le noyau l'écrit dans le journal :

text
audit: type=1400 audit(1762246812.413:58): apparmor="DENIED" operation="open" class="file" profile="/usr/sbin/chronyd" name="/srv/journaux/chrony.log" pid=811 comm="chronyd" requested_mask="wc" denied_mask="wc" fsuid=110 ouid=0

Il se lit : le programme chronyd, confiné par le profil /usr/sbin/chronyd, a voulu ouvrir en écriture et création (wc) le fichier /srv/journaux/chrony.log, et AppArmor l'a refusé. sudo journalctl -k -g 'apparmor="DENIED"' liste les refus. Face à un service qui échoue avec « Permission denied » alors que les droits du fichier sont bons, pense à ce journal.

Écrire un profil complet dépasse le niveau du BTS ; l'outil aa-genprof en génère un en observant le programme, et aa-logprof propose d'ajouter aux profils les accès refusés. La bonne réponse à un refus n'est jamais de désactiver AppArmor (aa-disable, ou apparmor=0 au démarrage) : c'est d'ajouter au profil l'accès précis dont le service a besoin, ou de ranger le fichier là où le profil l'autorise.

Tracer les actions sensibles avec auditd

auditd enregistre, au niveau du noyau, les accès à des fichiers choisis et certains appels système, avec le compte d'origine même après un sudo. On surveille par exemple les modifications des comptes et des droits :

bash
sudo apt install auditd
sudo nano /etc/audit/rules.d/sorbier.rules
text
# Modifications des comptes, des groupes et des droits sudo
-w /etc/passwd -p wa -k comptes
-w /etc/shadow -p wa -k comptes
-w /etc/group -p wa -k comptes
-w /etc/sudoers -p wa -k sudoers
-w /etc/sudoers.d/ -p wa -k sudoers
# Configuration SSH
-w /etc/ssh/sshd_config.d/ -p wa -k ssh

-w désigne le fichier ou le dossier surveillé, -p wa les opérations (écriture, changement d'attributs), -k une étiquette de recherche. sudo augenrules --load charge les règles, et sudo ausearch -k sudoers -i retrouve les événements, avec le compte d'origine (auid) en clair.

Vérifier avec Lynis

Lynis audite la configuration d'un système Linux et donne un indice de durcissement, avec la liste des avertissements et des suggestions :

bash
sudo apt install lynis
sudo lynis audit system

Le rapport détaillé est enregistré dans /var/log/lynis-report.dat. Lancé avant et après le durcissement, il mesure le progrès ; relancé chaque mois, il repère les dérives (un service ajouté pour un test, une règle oubliée). Pour un serveur exposé, on complète par un scanner de vulnérabilités lancé avec l'accord du responsable, et par la surveillance continue d'un SIEM comme Wazuh, qui contrôle aussi l'intégrité des fichiers sensibles.

Exercices

Exercice 1 sur 3 Comprendre un refus AppArmor Réussi À revoir

Après avoir déplacé ses fichiers dans /srv/dns, un service confiné par AppArmor ne démarre plus. Le journal contient :

text
apparmor="DENIED" operation="open" class="file" profile="/usr/sbin/named" name="/srv/dns/zones/db.sorbier.example" comm="named" requested_mask="r" denied_mask="r" fsuid=107 ouid=107
  1. Que s'est-il passé ? Les droits Unix du fichier sont-ils en cause ?
  2. Quelles sont les deux corrections possibles, et laquelle est à exclure ?
Voir la réponseMasquer la réponse de l'exercice 1
  1. Le programme named (serveur DNS Bind9), confiné par son profil AppArmor, a voulu lire (r) son fichier de zone dans /srv/dns/zones/. Le fichier lui appartient (fsuid et ouid valent tous deux 107, le compte du service) : les droits Unix ne bloquent pas. C'est AppArmor qui refuse, parce que le profil n'autorise la lecture des zones que dans les dossiers prévus par le paquet (sous /etc/bind/ et /var/cache/bind/).
  2. Soit remettre les fichiers à l'emplacement autorisé par le profil ; soit ajouter l'accès dans /etc/apparmor.d/local/usr.sbin.named (une ligne /srv/dns/zones/** r,), puis sudo systemctl reload apparmor et redémarrer le service. À exclure : désactiver le profil ou AppArmor, qui retirerait le confinement d'un service exposé au réseau.

Tu avais trouvé ?

Exercice 2 sur 3 Auditer un serveur (type E7) Réussi À revoir

L'audit d'un serveur Debian de la DMZ relève : SSH accepte les mots de passe et la connexion root ; ss -tulpn montre MariaDB à l'écoute sur 0.0.0.0:3306 ; le paquet unattended-upgrades n'est pas installé ; /etc/sudoers.d/support contient %support ALL=(ALL) NOPASSWD: ALL ; aucun journal n'est envoyé hors du serveur ; aa-status indique que le module AppArmor n'est pas chargé. Présente, pour chaque constat, le risque et la mesure corrective, dans un tableau.

Voir la réponseMasquer la réponse de l'exercice 2
ConstatRisqueMesure corrective
SSH : mots de passe et root acceptésEssais de mots de passe, prise de contrôle totale sans trace nominativeClés uniquement, PermitRootLogin no, AllowGroups, sources limitées au réseau d'administration, Fail2ban
MariaDB sur 0.0.0.0:3306Base joignable depuis le réseau, essais d'identifiants, exploitation de faillesbind-address = 127.0.0.1, pare-feu local qui ferme 3306
Pas d'unattended-upgradesFailles connues non corrigées sur un serveur exposéInstaller et activer les mises à jour de sécurité automatiques, needrestart, supervision des mises à jour en attente
%support ALL=(ALL) NOPASSWD: ALLTout membre du groupe, ou quiconque vole sa session, devient root sans mot de passeRègle limitée aux commandes nécessaires, sans NOPASSWD, éditée avec visudo
Journaux locaux uniquementUn attaquant efface ses traces ; aucune alerte centraliséeEnvoi chiffré vers le serveur de journaux ou un agent SIEM, journal persistant, heure synchronisée
AppArmor non chargéAucun confinement : un service détourné a tous les droits de son compteRéactiver AppArmor (vérifier qu'il n'est pas désactivé au démarrage par apparmor=0), profils en mode enforce

Tu avais trouvé ?

Exercice 3 sur 3 Restreindre les privilèges d'un service Réussi À revoir

L'API suivi-api (chapitre 9) tourne sous le compte suivi. systemd-analyze security suivi-api donne une exposition de 6,8 sur 10. Propose quatre directives à ajouter à son unité pour réduire ce score, en disant ce que chacune empêche.

Voir la réponseMasquer la réponse de l'exercice 3

Par exemple, dans la section [Service] :

  • ProtectSystem=strict : tout le système de fichiers devient en lecture seule pour le service, sauf les dossiers listés par ReadWritePaths=/var/lib/suivi ; un service détourné ne peut modifier ni programmes ni configuration.
  • PrivateDevices=yes : aucun accès aux périphériques (disques, /dev/mem).
  • RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX : pas d'autre type de socket réseau.
  • CapabilityBoundingSet= (vide) : aucune capacité privilégiée du noyau, même si le programme en demandait.

D'autres conviennent aussi : ProtectKernelTunables=yes, ProtectControlGroups=yes, SystemCallFilter=@system-service, MemoryDenyWriteExecute=yes. Après chaque ajout : daemon-reload, redémarrage, test de l'application, et nouveau calcul du score.

Tu avais trouvé ?

Chapitre terminé !

Fais les 3 exercices avant de passer à la suite : c'est en t'entraînant que tout se fixe.

chapitres
–
de série
–
du cours
–
Chapitre suivant : Dépanner un serveur Linux : méthode et cas typesRevoir le sommaire du cours