Cours Ubuntu 26.04 / Examiner les profils AppArmor et les journaux de refus

AppArmor ajoute des restrictions par programme aux permissions ordinaires de Linux. Pour comprendre un accès refusé sur Ubuntu, rapprochez le compte qui exécute le programme, le chemin concerné, le profil effectivement appliqué et les journaux ; désactiver toute la protection ne constitue pas un diagnostic.

Distinguer droits Unix et contrôle par profil

Les permissions du propriétaire, du groupe et des autres utilisateurs constituent une première couche d’accès. Un contrôle d’accès obligatoire, Mandatory Access Control ou MAC, peut en ajouter une autre : AppArmor définit les opérations permises à un programme confiné, notamment sur les fichiers, les autres processus ou le réseau.

Un fichier lisible pour l’utilisateur du service peut encore être inaccessible à ce programme à cause de son profil. À l’inverse, une erreur peut venir d’un répertoire parent non traversable, d’un montage en lecture seule, du compte de service ou de l’application elle-même. L’absence de message AppArmor ne valide pas ces autres couches.

Consignez le chemin complet demandé, l’action qui échoue, le compte et l’heure avant de modifier les permissions. La présentation de la sécurité AppArmor dans Ubuntu décrit sa portée ; un service actif ne signifie pas que tous les programmes possèdent un profil adéquat.

Identifier les profils chargés et leur mode réel

sudo aa-status
systemctl status apparmor --no-pager

Le rapport aa-status présente les profils chargés et des informations sur les processus. Recherchez le profil du programme concerné, son mode et les processus associés ; les totaux globaux ne suffisent pas à établir la protection d’un service précis.

En mode enforce, la politique est appliquée. Le mode complain permet une observation plus permissive et peut réduire la protection ; ce n’est pas une autorisation sans conséquence pour réparer une application. Les règles deny restent appliquées même en complain. Un essai réussi dans ce mode ne démontre donc ni une politique correcte ni une absence de restrictions.

Ce parcours d’inspection conserve les modes existants. Si l’outil n’existe pas ou renvoie une erreur, identifiez la version et les paquets disponibles dans l’image utilisée. N’arrêtez pas le service et ne déchargez pas tous les profils pour obtenir une sortie différente.

Rapprocher un refus récent de l’action qui échoue

Reproduisez une seule fois une action sans risque pour les données dans votre laboratoire et notez l’heure exacte. Cherchez ensuite les refus récents du noyau pendant ce démarrage :

sudo journalctl -k -b --since '10 minutes ago' --no-pager | grep 'apparmor="DENIED"'

Comparez l’heure, profile, operation, name, pid et, lorsqu’ils sont présents, requested_mask et denied_mask. Un PID peut être réutilisé ; rapprochez aussi l’identité du processus et la demande réelle. Un refus d’un autre programme survenu au même moment n’explique pas automatiquement votre incident.

Un résultat vide peut venir de la fenêtre de temps, du démarrage choisi, de la collecte vers un autre journal ou du type de règle. Une règle deny explicite peut notamment ne pas produire la trace attendue sans réglage d’audit. Consultez aussi le journal de l’application ; l’absence de DENIED ne prouve pas que la politique autorise l’action.

La lecture des refus dans le guide AppArmor aide à relier l’événement au contexte. Avant de partager une trace, masquez les données internes qui ne sont pas nécessaires : chemins privés, identifiants et arguments applicatifs peuvent y apparaître.

Lire un exemple de refus sans le confondre avec un résultat réel

La ligne suivante est une illustration fictive des champs, pas une trace collectée sur un serveur. Elle ne demande pas de créer ces fichiers ni d’ajouter une permission pour ce chemin :

apparmor="DENIED" operation="open" profile="/usr/sbin/example-app" name="/srv/example-data/settings.ini" requested_mask="r" denied_mask="r"
Champ Lecture de cet exemple
profile Nom de la politique appliquée au programme ; ce n’est pas nécessairement le nom du fichier source.
operation Action tentée, ici l’ouverture d’un fichier.
name Chemin visé par l’opération.
requested_mask Accès demandé ; r représente ici la lecture.
denied_mask Partie refusée, ici la lecture.

Dans un incident réel, demandez-vous pourquoi l’application lit ce chemin. Une nouvelle localisation de données non prise en charge peut être la cause ; revenir à l’emplacement prévu est parfois préférable à élargir le profil. Un journal montre une demande, pas la légitimité de cette demande.

Retrouver la source et ses inclusions avant une modification

Pour une politique ordinaire administrée localement, retrouvez la définition du profil dans /etc/apparmor.d, puis les abstractions, variables et fichiers inclus qu’elle utilise. La structure des profils AppArmor distingue nom, attachement et contenu des règles. Ne déduisez pas le nom du fichier d’un simple remplacement des barres obliques du programme.

Une inclusion local est utile seulement si la définition du profil la référence réellement ; créer un fichier dans local ne lui donne pas automatiquement effet. Un fichier peut aussi définir plusieurs profils. Identifiez cette portée avant de considérer son rechargement comme limité à un seul programme.

Pour les politiques générées de Snap, inspectez les interfaces et les paramètres Snap plutôt que de modifier le résultat généré. Une régénération peut écraser une correction locale. D’autres outils de déploiement peuvent également posséder la source : préservez leur procédure.

Si une adaptation locale est justifiée, définissez l’opération nécessaire, le chemin exact et l’accès minimal, lecture ou écriture selon le besoin. Les règles proposées par un outil d’apprentissage demandent aussi une revue. Un joker sur tout /srv ou tout le disque peut autoriser bien plus que le fichier qui a motivé l’incident.

Compiler un profil vérifié sans remplacer la politique du noyau

Le bloc suivant vise un fichier source ordinaire déjà identifié sous /etc/apparmor.d. Il résout le chemin existant et refuse un chemin canonique situé ailleurs. Il exclut ainsi de ce parcours les fichiers de politique générés de Snap. Saisissez le vrai fichier à l’invite :

(
set -eu
read -r -p 'Chemin réel du profil ordinaire sous /etc/apparmor.d/ : ' profile
profile=$(sudo realpath -e -- "$profile")
case "$profile" in
    /etc/apparmor.d/*) ;;
    *) printf 'Choisissez la source vérifiée du profil ordinaire.\n' >&2; exit 1 ;;
esac
sudo test -f "$profile"
sudo apparmor_parser -Q -K "$profile"
)

Dans le manuel apparmor_parser 5.0, -Q omet le chargement dans le noyau et -K désactive la lecture et l’écriture de cache. Le bloc compile donc la source dans l’environnement présent sans remplacer les profils en cours. Un code d’échec ou une erreur d’inclusion doit être examiné avant d’aller plus loin.

Un succès confirme la compilation dans ce contexte, pas le comportement réel de tous les accès de l’application. La version des outils, les fonctionnalités du noyau et les inclusions utilisées comptent. Ce test ne demande ni changement de mode ni rechargement ; n’exécutez pas une commande de remplacement si vous souhaitez seulement inspecter.

Encadrer une correction étroite et son retour arrière

Si le diagnostic justifie une modification, sauvegardez d’abord les sources, inclusions locales et modes actuels. Pour conserver les fichiers ordinaires de cette arborescence dans un nouveau répertoire protégé :

(
set -eu
apparmor_backup=$(sudo mktemp -d /root/apparmor-backup.XXXXXX)
sudo cp -a /etc/apparmor.d "$apparmor_backup/"
printf 'Sauvegarde : %s\n' "$apparmor_backup"
)

Ne poursuivez pas après une erreur de copie. Notez le chemin et les seuls fichiers que vous allez éditer ; si une inclusion provient d’ailleurs, sa sauvegarde demande aussi une prise en compte explicite. Modifiez uniquement la règle nécessaire, puis répétez la compilation -Q -K sur la véritable source.

Après validation et revue de la portée, sudo apparmor_parser -r -K suivi du fichier source vérifié remplace les définitions qu’il contient dans le noyau. Cette opération applique réellement une politique : elle n’est pas équivalente au contrôle précédent. Ne rechargez pas toute l’arborescence lorsque seule une définition identifiée doit changer.

  1. Vérifiez avec aa-status que le profil attendu est chargé et que son mode de protection correspond à la politique prévue.
  2. Répétez l’opération légitime qui échouait et confirmez son résultat applicatif, puis cherchez les nouveaux refus éventuels.
  3. Testez dans le laboratoire une opération connue qui doit rester interdite, sans exposer de vraies données confidentielles ni effectuer d’écriture destructive.
  4. En cas d’échec ou d’élargissement indésirable, restaurez uniquement les fichiers modifiés, compilez-les, réappliquez les définitions concernées et vérifiez le mode initial et l’application.

Ne copiez pas toute la sauvegarde sur une politique qui a évolué et ne désactivez pas globalement AppArmor ou la protection des espaces de noms utilisateurs. Conservez cause, source, portée du changement, résultats des tests permis et refusés et méthode de restauration : ces éléments permettent de distinguer une correction utile d’une simple suppression de protection.