Debian 13 : AppArmor bloque votre service — autorisez ce dont vous avez besoin sans désactiver la sécurité

Cet article vous a aidé ?

Votre service démarre correctement en staging. Puis Debian 13 en production le transforme en brique : « Permission denied », des fichiers manquants qui existent pourtant, des sockets qui refusent de se lier,
des échecs mystérieux qui disparaissent dès que vous arrêtez AppArmor. Félicitations, vous rencontrez un contrôle de sécurité qui fait son travail — juste pas encore le vôtre.

Voici la méthode que j’utilise quand AppArmor enferme un démon : identifier le refus exact, associer ce refus au profil qui l’a appliqué, ajouter la règle la plus restrictive possible, et prouver la correction.
Désactiver AppArmor n’est pas « temporaire ». C’est la manière dont le temporaire devient permanent.

Ce qu’est AppArmor (et pourquoi Debian 13 le rend plus strict)

AppArmor est un Linux Security Module qui confine les programmes à l’aide de profils. Un profil indique ce qu’un programme peut lire, écrire, exécuter, et quelles capacités il peut utiliser.
Si le programme tente quelque chose qui n’est pas autorisé, le noyau le refuse et enregistre le refus. C’est tout. Pas de magie, pas d’« IA de sécurité », juste une politique et son application.

Si vous venez de l’école « nous exécutons tout en root dans un conteneur », AppArmor est un électrochoc. Les conteneurs ne sont pas des permissions. Root dans un conteneur a toujours besoin
que le noyau de l’hôte dise « oui ». AppArmor fait partie de ce « oui/non ».

Sur Debian 13 vous rencontrerez typiquement AppArmor quand :

  • Vous déployez un service personnalisé qui accède à des fichiers hors des emplacements standards.
  • Vous ajoutez un nouveau plugin, une cible de sauvegarde ou un chemin de clé TLS sous /srv ou /mnt.
  • Vous passez un démon de TCP à sockets Unix (ou inversement).
  • Vous déplacez des logs dans un nouveau répertoire parce que « nous faisons du rangement ». (Dernières paroles célèbres.)
  • Vous durcissez systemd et changez par accident l’environnement du processus d’une manière que le profil n’anticipait pas.

Modèle mental : AppArmor ne « bloque pas votre service ». Votre service demande quelque chose qui ne lui a jamais été accordé. Votre travail est d’accorder le minimum nécessaire et de vérifier
que vous n’élargissez pas la surface d’impact.

Une citation à garder sur votre mur — parce que c’est de l’exploitation en une phrase :
L’espoir n’est pas une stratégie. (attribution : idée paraphrasée souvent associée au général Gordon R. Sullivan ; utilisée largement en ingénierie et opérations)

Playbook de diagnostic rapide

Quand un service échoue et que vous suspectez AppArmor, n’« essayez pas des choses au hasard ». Faites ces trois vérifications dans l’ordre. C’est plus rapide, et ça laisse des preuves.

1) Confirmer qu’AppArmor est actif et en enforcement

Si AppArmor est désactivé globalement, vous poursuivez un autre problème. S’il est activé mais que le profil est en complain mode, vous aurez des logs mais pas d’application — c’est aussi un problème différent.

2) Identifier l’événement de refus et le nom du profil

La ligne de log de refus vous indique le profile= et le chemin cible, l’opération et les permissions demandées. Cette ligne est votre demande de changement. Pas de ligne de refus, pas de changement de politique.

3) Décider : corriger le profil, changer le comportement du service, ou modifier l’emplacement des fichiers

Si le service tente de lire un secret depuis un répertoire lisible par tous, ce n’est pas « un bug AppArmor ». C’est un problème de disposition. Si le service a besoin de l’accès, ajoutez une règle étroite.
S’il ne devrait pas y accéder, corrigez le service.

Règle empirique rapide : un refus signifie généralement une règle manquante. Dix refus sur dix chemins différents signifie souvent que le binaire exécuté n’est pas celui attendu, ou que le profil est incorrect.

Tâches pratiques : commandes, sorties, décisions (12+)

Tout ce qui suit est exécutable sur un hôte Debian avec AppArmor installé. Remplacez les noms de service et les chemins, mais conservez la méthode. La méthode est l’essentiel.

Task 1: Check if AppArmor is enabled in the kernel

cr0x@server:~$ cat /sys/module/apparmor/parameters/enabled
Y

Ce que cela signifie : Y signifie que l’LSM AppArmor est activé dans le noyau.
Décision : Si c’est N ou si le fichier n’existe pas, arrêtez. Ce n’est pas AppArmor qui vous bloque sur ce démarrage.

Task 2: Verify AppArmor service health

cr0x@server:~$ systemctl status apparmor
● apparmor.service - Load AppArmor profiles
     Loaded: loaded (/lib/systemd/system/apparmor.service; enabled; preset: enabled)
     Active: active (exited) since Tue 2025-12-28 08:11:29 UTC; 2h 4min ago
       Docs: man:apparmor(7)
    Process: 512 ExecStart=/lib/apparmor/apparmor.systemd reload (code=exited, status=0/SUCCESS)

Ce que cela signifie : Les profils ont été chargés avec succès.
Décision : Si c’est en échec, corrigez cela d’abord ; sinon toute modification de profil ne sera pas appliquée.

Task 3: List loaded profiles and their mode

cr0x@server:~$ sudo aa-status
apparmor module is loaded.
46 profiles are loaded.
44 profiles are in enforce mode.
2 profiles are in complain mode.
0 profiles are in kill mode.

Ce que cela signifie : Vous avez une vraie application des règles.
Décision : Si le profil pertinent est en complain mode, votre service peut encore fonctionner tout en journalisant des refus ; décidez si vous voulez passer en enforce après correction.

Task 4: Confirm whether a specific service’s process is confined

cr0x@server:~$ systemctl show -p MainPID myservice.service
MainPID=1842
cr0x@server:~$ sudo cat /proc/1842/attr/current
/usr/bin/myservice (enforce)

Ce que cela signifie : Le processus est confiné par le profil /usr/bin/myservice en mode enforce.
Décision : Éditez le profil qui correspond à ce que vous voyez dans attr/current, pas ce que vous pensez qu’il devrait être.

Task 5: Pull recent AppArmor denials from journald

cr0x@server:~$ sudo journalctl -k --since "30 min ago" | grep -i apparmor | tail -n 5
Dec 28 10:01:22 server kernel: audit: type=1400 audit(1766916082.123:311): apparmor="DENIED" operation="open" class="file" profile="/usr/bin/myservice" name="/etc/myservice/secret.key" pid=1842 comm="myservice" requested_mask="r" denied_mask="r" fsuid=998 ouid=0

Ce que cela signifie : Votre service a tenté d’ouvrir un fichier en lecture et s’est vu refuser l’accès.
Décision : Vous autorisez la lecture de ce fichier précis (ou d’un motif de répertoire), ou vous déplacez le secret dans un chemin déjà autorisé par la politique. Ne devinez pas — utilisez la ligne de refus.

Task 6: Use the audit log if journald is noisy or rotated

cr0x@server:~$ sudo grep -i "apparmor=\"DENIED\"" /var/log/audit/audit.log | tail -n 3
type=AVC msg=audit(1766916082.123:311): apparmor="DENIED" operation="open" class="file" profile="/usr/bin/myservice" name="/etc/myservice/secret.key" pid=1842 comm="myservice" requested_mask="r" denied_mask="r"

Ce que cela signifie : Même refus, provenant du sous-système audit.
Décision : Si auditd n’est pas installé, envisagez-le pour les serveurs où vous avez besoin d’un journal de sécurité durable ; journald seul peut suffire, mais il n’est pas toujours conservé.

Task 7: Locate the profile file on disk

cr0x@server:~$ sudo grep -R "profile /usr/bin/myservice" -n /etc/apparmor.d | head -n 2
/etc/apparmor.d/usr.bin.myservice:12:profile /usr/bin/myservice flags=(attach_disconnected) {

Ce que cela signifie : Le profil se trouve dans /etc/apparmor.d/usr.bin.myservice.
Décision : Éditez ce fichier (ou un include local) plutôt que d’inventer un nouveau nom de profil.

Task 8: Generate rules interactively with aa-logprof

cr0x@server:~$ sudo aa-logprof
Reading log entries from /var/log/audit/audit.log.
Updating AppArmor profiles in /etc/apparmor.d.
Profile:  /usr/bin/myservice
Execute:  /usr/bin/myservice
Severity: unknown

[(A)llow]/(D)eny/(I)gnore/(N)ew/(G)lob/(Q)uit

Ce que cela signifie : AppArmor a analysé votre refus et propose un changement de politique.
Décision : Préférez Allow uniquement lorsque l’accès est légitime et attendu. Si vous n’êtes pas sûr, choisissez Ignore, reproduisez le problème avec plus de journalisation, et revenez-y.

Task 9: Reload a single profile after editing

cr0x@server:~$ sudo apparmor_parser -r /etc/apparmor.d/usr.bin.myservice

Ce que cela signifie : L’absence de sortie est normale en cas de succès.
Décision : Si vous voyez des erreurs d’analyse, corrigez la syntaxe avant de redémarrer le service. Un profil cassé peut vous laisser avec une ancienne politique encore chargée.

Task 10: Confirm the new rule is loaded (and not just edited)

cr0x@server:~$ sudo aa-status | grep -n "/usr/bin/myservice"
21:   /usr/bin/myservice

Ce que cela signifie : Le profil est chargé. Cela ne prouve pas que votre nouvelle règle est en vigueur, mais prouve que le profil existe et est actif.
Décision : Redémarrez maintenant le service et vérifiez que le refus s’arrête.

Task 11: Restart the service and watch logs live

cr0x@server:~$ sudo systemctl restart myservice.service
cr0x@server:~$ sudo journalctl -u myservice.service -n 50 --no-pager
Dec 28 10:06:41 server myservice[2011]: Loaded TLS key from /etc/myservice/secret.key
Dec 28 10:06:41 server myservice[2011]: Listening on /run/myservice.sock
Dec 28 10:06:41 server systemd[1]: Started myservice.service - My Service.

Ce que cela signifie : Le service lit maintenant la clé et lie son socket.
Décision : Conservez la règle. Si cela échoue encore, retournez à la ligne de refus du noyau/audit — le log du service n’est pas une source autoritaire pour AppArmor.

Task 12: Prove you didn’t introduce a flood of new denials

cr0x@server:~$ sudo journalctl -k --since "5 min ago" | grep -i "apparmor=\"DENIED\"" | tail -n 10

Ce que cela signifie : Idéalement : aucune sortie.
Décision : Si de nouveaux refus apparaissent, vous avez corrigé un chemin mais le service a d’autres besoins. Itérez avec la même discipline : un refus, une règle minimale.

Task 13: Temporarily switch a profile to complain mode (for diagnosis)

cr0x@server:~$ sudo aa-complain /usr/bin/myservice
Setting /usr/bin/myservice to complain mode.

Ce que cela signifie : Le profil journalise mais n’applique pas. Cela peut débloquer un incident pendant que vous récoltez des preuves.
Décision : Utilisez le complain mode comme étape de diagnostic, pas comme mode de vie. Mettez un rappel pour revenir en enforce après mise à jour du profil.

Task 14: Put it back into enforce mode

cr0x@server:~$ sudo aa-enforce /usr/bin/myservice
Setting /usr/bin/myservice to enforce mode.

Ce que cela signifie : L’application est de retour.
Décision : Si le service casse encore, vous avez manqué une règle allow. C’est attendu — récoltez les nouveaux refus et resserrez.

Task 15: Find “which file is missing” problems that are actually confinement

cr0x@server:~$ sudo strace -f -e trace=file -p 1842 2>&1 | head -n 8
openat(AT_FDCWD, "/etc/myservice/secret.key", O_RDONLY|O_CLOEXEC) = -1 EACCES (Permission denied)
openat(AT_FDCWD, "/etc/ssl/certs/ca-certificates.crt", O_RDONLY|O_CLOEXEC) = 3

Ce que cela signifie : Le noyau refuse l’accès au fichier. Cela correspond aux refus AppArmor.
Décision : Quand les développeurs insistent « le fichier existe », montrez-leur l’appel système retournant EACCES et la ligne d’audit. Ça clôt rapidement le débat.

Task 16: Validate the unit’s execution path is what the profile expects

cr0x@server:~$ systemctl cat myservice.service
# /etc/systemd/system/myservice.service
[Service]
ExecStart=/usr/local/bin/myservice --config /etc/myservice/config.yaml

Ce que cela signifie : Le binaire est dans /usr/local/bin, pas /usr/bin.
Décision : Si le profil chargé concerne /usr/bin/myservice mais que le service exécute /usr/local/bin/myservice, vous éditez le mauvais profil. Corrigez la correspondance du profil ou le chemin.

Comment lire les refus comme un adulte

Une ligne de refus AppArmor paraît intimidante jusqu’à ce que vous sachiez quels champs regardez. Voici ce qui importe :

  • profile= la politique qui a pris la décision. C’est le fichier que vous éditerez.
  • operation= ce que le processus a tenté : open, create, unlink, connect, bind, ptrace, mount, etc.
  • name= le chemin cible (pour les opérations sur fichiers) ou le nom de l’objet.
  • requested_mask= ce que le processus a demandé, comme r (read), w (write), k (lock), m (memory map), x (execute).
  • denied_mask= ce qui a été refusé (généralement identique à requested_mask pour un refus propre).
  • comm= nom de la commande ; utile quand des wrappers ou scripts shell sont impliqués.
  • pid= vous permet de relier au systemd, strace et à l’arbre des processus.

Le refus n’est pas une suggestion. C’est la preuve exacte de ce qui a été bloqué.
Votre processus peut échouer pour de nombreuses raisons ; ne changez AppArmor que quand le noyau indique qu’AppArmor a refusé l’action.

Blague n°1 : si vous « corrigez » AppArmor en le désactivant, vous n’avez pas corrigé AppArmor — vous avez réglé votre conscience en le supprimant.

Types de refus que vous verrez le plus souvent

Lectures de fichiers : configs, certificats, identifiants, fichiers de plugin. Généralement le plus simple à corriger avec une règle r ciblée.

Écritures de fichiers : logs, pidfiles, caches, bases de données. C’est là que les gens deviennent négligents. N’autorisez pas l’écriture sur des répertoires larges « juste pour que ça marche ».

Sockets Unix : liaison sous /run, connexion aux sockets Docker/containerd, communication avec des services système. AppArmor peut restreindre à la fois la création et l’opération de connexion.

Signaux et ptrace : vérifications de santé, watchdogs, débogueurs. Ceux-ci apparaissent souvent pendant la réponse à un incident quand quelqu’un attache des outils à un processus confiné.

Autoriser ce dont vous avez besoin : éditer les profils sans tout ouvrir

Le bon changement de profil est ennuyeux. Il accorde une capacité, un chemin, une opération — et rien d’autre. Le mauvais changement est « /** rw », ce qui revient à
acheter un coffre-fort et le laisser ouvert parce que le clavier est pénible.

Privilégiez des règles fichier étroites plutôt que des jokers de répertoire

Si le service a besoin d’une clé secrète précise :

cr0x@server:~$ sudo sed -n '1,80p' /etc/apparmor.d/usr.bin.myservice
#include <tunables/global>

profile /usr/bin/myservice flags=(attach_disconnected) {
  #include <abstractions/base>
  /usr/bin/myservice mr,

  /etc/myservice/secret.key r,
}

Ce que cela signifie : Le profil autorise la lecture d’un seul fichier. C’est facile à raisonner lors d’un audit et facile à revenir en arrière.
Décision : Choisissez le chemin le plus petit qui correspond. N’utilisez les jokers de répertoire que si le service a réellement besoin de plusieurs fichiers tournants.

Rendez les écritures intentionnelles : logs, état, cache

Un démon typique a besoin de :

  • Config en lecture seule sous /etc
  • État inscriptible sous /var/lib
  • Logs inscriptibles sous /var/log (ou stdout vers journald)
  • Socket runtime ou pid sous /run

Alignez la disposition du système de fichiers sur ces attentes. Si vous insistez pour écrire l’état dans /etc ou /opt, vous vous battrez contre AppArmor indéfiniment.
Et vous le mériterez.

Utilisez les abstractions, mais ne vous en cachez pas

Les abstractions AppArmor (comme abstractions/base) peuvent être utiles. Elles facilitent aussi l’erreur consistant à autoriser plus que nécessaire.
Ma règle : inclure les abstractions lorsqu’elles correspondent clairement au rôle du service, puis ajouter des règles explicites pour les parties uniques.

Ne confondez pas « ça marche » et « c’est sûr »

Vous pouvez toujours faire fonctionner le service en ajoutant des règles larges. Vous êtes ici parce que vous gérez des systèmes de production, et la production a des ennemis : erreurs, attaquants, et le vous du futur.
AppArmor aide contre les trois, mais seulement si vous ne le sabotez pas.

Blague n°2 : « J’ai temporairement mis en complain mode » est la version sécurité de « Je vais juste tenir ce fil sous tension une seconde ».

Systemd, services, et quel profil s’applique réellement

La plupart des incidents AppArmor dans les environnements Debian ne sont pas « AppArmor est trop strict ». Ce sont des « nous avons édité le mauvais profil » ou « systemd exécute un binaire différent de ce que nous croyons ».
Systemd ajoute des wrappers : fichiers d’environnement, scripts pré-démarrage, utilisateurs dynamiques, répertoires runtime.

Connaissez la chaîne d’exécution

AppArmor s’attache généralement par chemin exécutable. Si votre unité exécute un wrapper shell qui fait ensuite exec du vrai binaire, vous pouvez confiner le shell, pas le démon. Ou ne rien confiner du tout.
La preuve est toujours dans /proc/<pid>/attr/current.

Méfiez-vous de /usr/local vs /usr/bin

Le packaging Debian tend à placer les binaires gérés dans /usr/bin. Les déploiements artisanaux aiment /usr/local/bin.
Si votre profil est nommé pour l’un mais que vous exécutez l’autre, vous courrez après des fantômes.

Le durcissement systemd peut changer le symptôme

Des options comme ProtectSystem=, ReadOnlyPaths=, et PrivateTmp= peuvent aussi provoquer des « permission denied », mais ce ne sont pas des refus AppArmor.
C’est pourquoi le diagnostic rapide commence par la ligne de refus du noyau. Vous avez besoin du coupable exact avant d’agir.

Trois mini-histoires d’entreprise issues du terrain

Incident #1: The wrong assumption (“It’s just a filesystem permission problem”)

Une entreprise de taille moyenne a déployé une mise à jour Debian sur une flotte exécutant une passerelle API interne. Une région a commencé à renvoyer des 503 sporadiques.
L’astreignant a fait ce que la plupart d’entre nous font sous pression : chercher l’évidence.

Les logs de la passerelle indiquaient qu’elle ne pouvait pas lire sa clé TLS. Le fichier existait, la propriété semblait correcte, et un rapide sudo -u gateway cat /path/to/key fonctionnait.
Quelqu’un en a conclu qu’il s’agissait d’une condition de course et a relancé le job de gestion de configuration.
Le problème a persisté. Naturellement, ils ont essayé d’ajouter plus de retries.

Finalement un ingénieur plus calme a vérifié le journal du noyau et a trouvé des refus AppArmor pour operation="open" sur ce chemin de clé.
La clé avait été déplacée de l’ancien emplacement vers un nouveau répertoire qui correspondait à leurs conventions de nommage soignées — mais pas au profil AppArmor.

Le moment « aha » a été gênant : toutes les permissions Unix étaient correctes. AppArmor était la couche qui refusait l’accès, et il se moquait du fait qu’un cat manuel fonctionnait
parce que le test manuel était effectué hors du contexte confiné.

La correction fut une seule règle autorisant la lecture du nouveau chemin de la clé. La correction durable fut culturelle : ne jamais accepter « permission denied » comme un diagnostic uniquement lié au système de fichiers
sur un système avec un contrôle d’accès obligatoire activé. Vérifiez d’abord la ligne de refus.

Incident #2: The optimization that backfired (log path consolidation)

Une autre équipe voulait « optimiser » l’envoi des logs. Au lieu de journald, ils ont redirigé plusieurs services pour écrire des logs JSON sous un répertoire partagé sur un disque rapide,
puis les surveillaient avec un agent. Ça paraissait propre : un emplacement pour tous les logs, rotation cohérente, tableaux de bord heureux.

Sur Debian 13, plusieurs services ont commencé à échouer au démarrage avec des erreurs IO génériques. Les ingénieurs ont chassé les problèmes de disque, puis SELinux (ils ne faisaient pas tourner SELinux),
puis le sandboxing systemd. Les échecs étaient inconsistants parce que seules certaines voies de code écrivaient des logs tôt au démarrage.

La cause racine était des profils AppArmor qui autorisaient l’écriture vers le chemin de log standard de chaque service, mais pas vers le nouveau répertoire partagé.
L’« optimisation » avait silencieusement changé la cible d’écriture pour plusieurs démons. AppArmor a fait exactement ce qu’il devait : empêcher un service confiné d’écrire où il veut.

La correction naïve aurait été d’ajouter un accès d’écriture large au répertoire partagé pour chaque service. Cela aurait rendu la consolidation des logs fonctionnelle — et aurait aussi créé une surface de falsification inter-services.
Un service compromis aurait pu écraser les logs d’un autre, ce qui est un cadeau délicieux pour un attaquant qui aime effacer les traces.

La correction appropriée fut d’éviter les répertoires partagés inscriptibles entre démons non liés, ou d’utiliser des sous-répertoires par service avec des règles allow étroites et un contrôle des propriétaires.
L’équipe a fini par utiliser journald pour la plupart des services et n’externaliser que ce qui nécessitait du JSON local pour des outils spécifiques.
Moins joli sur le disque. Beaucoup plus propre en posture de risque.

Incident #3: The boring practice that saved the day (pre-change deny harvesting)

Une équipe fintech exécutait un processeur de paiements avec un processus de changement strict. Leur approche était douloureusement peu glamour : avant d’activer l’enforcement AppArmor pour un service,
ils l’exécutaient en complain mode en production pendant une journée, récoltaient les refus, les revoyaient, puis passaient en enforce.

Lors d’une mise à jour de dépendance routinière, le service a commencé à accéder à un nouveau chemin de bundle CA à cause de changements dans la pile TLS.
En complain mode, les refus ont été journalisés immédiatement. Rien n’a cassé, et il n’y a eu aucun impact client.

Ils ont ajouté une règle de lecture étroite pour cet emplacement du bundle CA, rechargé le profil, et relancé les tests de fumée.
Ce n’est qu’ensuite qu’ils ont activé le mode enforce. Quand le changement a atteint la flotte complète, c’était un non-événement.

La pratique n’était pas intelligente. Elle ne nécessitait pas d’exploits. Elle demandait de la discipline : traiter la politique comme un artefact de production, la tester comme du code, et la déployer comme du code.
Le résultat fut de la prévisibilité, ce que vous voulez réellement à 3h du matin.

Erreurs courantes : symptôme → cause racine → correction

1) Le service dit « fichier introuvable », mais le fichier existe

Symptôme : L’application logge ENOENT ou « fichier manquant », mais vous voyez le fichier sur le disque.

Cause racine : AppArmor refuse l’ouverture, et l’application mappe mal l’erreur (ou vous regardez le mauvais chemin à cause de liens symboliques/chroot).

Correction : Confirmez avec journalctl -k ou /var/log/audit/audit.log ; autorisez le chemin exact ou corrigez le service pour qu’il lise depuis un emplacement autorisé.

2) Vous avez édité un profil et rien n’a changé

Symptôme : Vous ajoutez des règles, redémarrez le service, toujours refusé.

Cause racine : Le fichier édité n’est pas le profil chargé, ou le profil n’a pas été rechargé, ou le processus exécute un chemin exécutable différent.

Correction : Vérifiez /proc/<pid>/attr/current. Rechargez avec apparmor_parser -r. Validez le chemin binaire dans ExecStart de systemd.

3) La « correction » a été de désactiver AppArmor et l’incident a cessé (pour l’instant)

Symptôme : Désactiver AppArmor fait que tout fonctionne.

Cause racine : La politique manque d’accès légitimes. Désactiver l’application supprime complètement les garde-fous.

Correction : Utilisez brièvement le complain mode si nécessaire, collectez les refus, mettez à jour le profil, et revenez en enforce.

4) Le service ne peut pas bind son socket Unix sous /run

Symptôme : Le démarrage échoue lors de la création ou de la liaison de /run/myservice.sock.

Cause racine : Le profil n’autorise pas la création de ce chemin de socket, ou le RuntimeDirectory systemd diffère de ce qui est attendu.

Correction : Ajoutez des règles pour /run/myservice.sock avec les permissions correctes ; assurez-vous que RuntimeDirectory= correspond au chemin que vous autorisez.

5) Après avoir ajouté un wildcard, le service fonctionne mais la revue sécurité bloque la mise en production

Symptôme : Un réviseur rejette le profil à cause de règles larges comme /etc/** r ou /** rw.

Cause racine : Politique trop large écrite sous pression.

Correction : Remplacez par des règles de fichiers explicites, des règles par répertoire pour des chemins propriétaires, et des capacités minimales. Reproduisez les refus pour trouver les besoins spécifiques.

6) DNS, HTTP sortant ou connexions DB échouent mais aucun refus fichier n’apparaît

Symptôme : Les appels réseau expirent ou sont refusés ; les logs de l’application sont vagues.

Cause racine : AppArmor peut restreindre les opérations réseau selon le profil et les fonctionnalités ; ou le problème est le durcissement systemd/firewall.

Correction : Recherchez des refus avec operation="connect" ou network. Si aucun refus AppArmor, inspectez le durcissement du unit systemd et les règles de pare-feu.

7) Vous voyez des refus pour un nom de processus que vous ne reconnaissez pas

Symptôme : Les refus référencent comm="(quelque chose)" pas votre service.

Cause racine : Votre service exécute des binaires auxiliaires (curl, openssl, scripts shell), et le profil restreint l’exécution.

Correction : Décidez si le service doit du tout exécuter des helpers. Si oui, autorisez des binaires spécifiques avec les bons modes d’exécution ; sinon, retirez ce comportement du service.

Checklists / plan étape par étape

Checklist A: When production is down and you need signal fast

  1. Vérifiez la confinement : /proc/<pid>/attr/current. S’il indique unconfined, arrêtez de blâmer AppArmor.
  2. Récupérez les refus noyau : journalctl -k filtré pour apparmor="DENIED".
  3. Corrélez l’horodatage avec la panne du service dans journalctl -u.
  4. Décidez la plus petite règle allow légitime. Si vous ne pouvez pas la justifier, ne l’ajoutez pas.
  5. Rechargez le profil, redémarrez le service, confirmez que les refus s’arrêtent.
  6. Programmez un minuteur pour revenir sur tout complain mode utilisé pendant l’incident.

Checklist B: Safe policy development loop (the one that keeps you employed)

  1. Mettez le profil du service en complain mode sur un hôte canari.
  2. Exécutez le service : démarrage, état stable, sauvegardes, rotations, mises à jour, basculement.
  3. Exécutez aa-logprof et révisez chaque règle comme s’il s’agissait d’un changement de firewall.
  4. Privilégiez les chemins de fichiers explicites ; évitez les répertoires partagés inscriptibles.
  5. Rechargez le profil, passez en enforce sur le canari, relancez les tests.
  6. Déployez sur une petite tranche, surveillez les refus, puis élargissez.
  7. Conservez les modifications de profil dans votre système de changement habituel (Git, CI, revues). Traitez-les comme du code.

Checklist C: A sane layout that reduces AppArmor friction

  • Configs : /etc/myservice/ (lecture seule à l’exécution sauf cas délibérés)
  • Secrets : /etc/myservice/ ou /var/lib/myservice/ (lecture seule, propriétaire strict)
  • État : /var/lib/myservice/ (inscriptible)
  • Cache : /var/cache/myservice/ (inscriptible, jetable)
  • Logs : journald préféré ; sinon /var/log/myservice/ par service
  • Sockets/pids : /run/myservice/ créé par systemd RuntimeDirectory=

Faits intéressants & contexte historique (court et concret)

  • AppArmor est né comme produit commercial d’Immunix ; il est ensuite entré dans l’écosystème Linux principal via l’implication de Novell.
  • Contrairement au modèle par labels de SELinux, AppArmor est basé sur les chemins : les politiques se réfèrent à des chemins de fichiers, ce qui les rend lisibles mais aussi vulnérables aux particularités de chemin si vous êtes négligent.
  • Les profils AppArmor supportent le « complain mode », qui journalise les violations sans appliquer — utile pour construire des politiques à partir du comportement observé.
  • Le framework Linux Security Modules (LSM) permet à AppArmor, SELinux et autres de s’accrocher aux contrôles d’accès du noyau.
  • Les politiques AppArmor peuvent utiliser des « hats » (sous-profils) pour changer temporairement le confinement dans des parties d’un programme, bien que ce soit moins courant dans les déploiements de services modernes.
  • Les patterns d’adoption de Debian ont historiquement différé de ceux d’Ubuntu ; Ubuntu a fait d’AppArmor un pilier par défaut tôt, ce qui a influencé les outils et les pratiques communautaires.
  • L’essor de systemd a changé le flux de dépannage : journald est devenu un lieu principal pour voir les refus, pas seulement les fichiers syslog classiques.
  • AppArmor peut restreindre plus que les fichiers : signaux, interactions dbus et certaines fonctionnalités noyau apparaissent comme des refus dans des incidents réels.

FAQ

1) Comment savoir si c’est AppArmor et non les permissions Unix ?

Recherchez une ligne de refus noyau/audit contenant apparmor="DENIED". Si elle existe en même temps que la défaillance, c’est AppArmor.
Si elle n’existe pas, vous êtes probablement face à des permissions système de fichiers, au sandboxing systemd, ou à des bugs applicatifs.

2) Dois-je utiliser le complain mode en production ?

Oui, brièvement et délibérément : le complain mode est utile pour collecter des refus sans casser le trafic.
Mais traitez-le comme un outil de fenêtre de changement. Une fois les règles correctes, revenez en enforce.

3) Pourquoi sudo -u myuser cat /path fonctionne, mais le service ne peut pas lire le fichier ?

Parce que le service est confiné et que votre test manuel ne l’est généralement pas. Les décisions AppArmor dépendent du contexte du processus confiné, pas seulement de l’UID/GID.
Vérifiez avec /proc/<pid>/attr/current.

4) Quelle est la façon la plus sûre d’ajouter l’accès aux secrets ?

Autorisez la lecture du fichier secret exact (pas tout le répertoire), et gardez la propriété et les modes serrés.
Si vous avez besoin de rotation, autorisez le glob minimal qui correspond au schéma de rotation (par exemple, un motif de nom de base spécifique).

5) Puis-je simplement autoriser /etc/** r pour arrêter ces problèmes ?

Vous le pouvez, et cela « fonctionnera », mais autoriser largement permettra aussi la lecture de configurations et secrets non liés.
Si un service est compromis, un large accès en lecture facilite le vol de données. Soyez précis.

6) Mon service s’exécute depuis /usr/local/bin. Pourquoi le profil packagé ne s’applique-t-il pas ?

La plupart des profils s’attachent à un chemin exécutable. Si le profil cible /usr/bin/myservice mais que vous exécutez /usr/local/bin/myservice,
vous n’utilisez pas le profil que vous pensez. Ajustez l’unité au chemin attendu ou créez/modifiez le profil correct pour le binaire réel.

7) J’ai corrigé un refus, et maintenant j’en vois un autre. Est-ce normal ?

Oui. Les services échouent souvent sur la première opération bloquée. Une fois que vous débloquez cela, le besoin suivant apparaît.
Itérez jusqu’à ce que le démarrage et l’état stable fonctionnent sans refus en mode enforce.

8) Comment éviter de répéter ça à chaque mise à jour ?

Traitez les profils comme faisant partie du service, pas comme une réflexion après coup. Conservez les changements en contrôle de version, testez-les en CI quand possible,
et exécutez des canaris en complain mode après les mises à jour pour récolter le nouveau comportement avant d’appliquer l’enforcement à l’échelle.

9) AppArmor est-il « plus faible » que SELinux ?

Modèle différent, compromis différent. L’approche basée sur les chemins d’AppArmor est souvent plus accessible et plus rapide à adopter pour le confinement au niveau service.
« Plus faible » signifie généralement « moins uniformément déployé et moins strictement maintenu », pas intrinsèquement incapable.

Conclusion : étapes suivantes réalisables aujourd’hui

Si Debian 13 bloque votre service, ne désactivez pas AppArmor. Utilisez-le comme outil de débogage et comme ceinture de sécurité.
Trouvez le refus. Identifiez le profil qui l’applique. Ajoutez la règle minimale qui correspond au comportement légitime. Rechargez. Prouvez-le avec des logs.

Étapes pratiques :

  1. Sélectionnez un service en échec et capturez ses lignes de refus depuis journalctl -k ou /var/log/audit/audit.log.
  2. Vérifiez le confinement via /proc/<pid>/attr/current, puis localisez le fichier correspondant sous /etc/apparmor.d/.
  3. Exécutez aa-logprof, acceptez seulement les règles que vous pouvez expliquer, rechargez le profil et retestez.
  4. Une fois stable, assurez-vous que le profil est en mode enforce et surveillez les nouveaux refus après les changements.

L’objectif n’est pas de faire taire AppArmor. L’objectif est de faire fonctionner votre service de façon prévisible sous confinement — parce que la prévisibilité est ce qui maintient les incidents limités.

← Précédent
SERVFAIL DNS en amont : prouver que votre fournisseur est en cause
Suivant →
Snapshots ZFS : la superpuissance qui peut aussi remplir votre pool

Laisser un commentaire