Introduction
Par défaut sur Linux, un process tourne avec les droits de l'user qui l'a lancé : Nginx en www-data peut lire tout /etc, écrire dans /tmp, ouvrir n'importe quel port. Si Nginx est compromis, l'attaquant a accès à tout ça.
AppArmor restreint chaque process à ce dont il a vraiment besoin via des profils qui listent fichiers et capabilities autorisés. Si Nginx est compromis, l'attaquant ne peut accéder qu'à /var/www, lire ses certs et écouter sur 80/443. Rien d'autre.
Avantages vs SELinux : syntaxe lisible, basé sur des chemins, fourni avec Ubuntu/Debian, profils prêts à l'emploi pour les services courants.
Prérequis
- Ubuntu / Debian (AppArmor préinstallé)
- Accès root
- Bases CLI Linux
Étape 1 : Vérifier qu'AppArmor est actif
sudo systemctl status apparmor
sudo aa-status
Sortie type :
apparmor module is loaded.
35 profiles are loaded.
30 profiles are in enforce mode.
5 profiles are in complain mode.
12 processes have profiles defined.
Si pas installé :
sudo apt install -y apparmor apparmor-utils apparmor-profiles
sudo systemctl enable --now apparmor
Étape 2 : Modes d'AppArmor
3 modes par profil :
- enforce : bloque + log (mode prod)
- complain : log uniquement, ne bloque pas (mode test)
- disabled : profil chargé mais inactif
Voir le mode d'un profil :
sudo aa-status | grep nginx
Changer le mode :
sudo aa-complain /etc/apparmor.d/usr.sbin.nginx # passer en complain
sudo aa-enforce /etc/apparmor.d/usr.sbin.nginx # passer en enforce
sudo aa-disable /etc/apparmor.d/usr.sbin.nginx # désactiver
Étape 3 : Lister les profils existants
ls /etc/apparmor.d/
Profils prêts à l'emploi : Nginx, MySQL, PHP, Apache, Tor, Bind9, etc.
Pour les activer :
sudo apt install -y apparmor-profiles apparmor-profiles-extra
sudo aa-enforce /etc/apparmor.d/*
Étape 4 : Anatomie d'un profil
sudo cat /etc/apparmor.d/usr.sbin.nginx
Structure type :
#include <tunables/global>
/usr/sbin/nginx {
#include <abstractions/base>
#include <abstractions/nis>
capability dac_override,
capability dac_read_search,
capability net_bind_service,
capability setgid,
capability setuid,
/etc/nginx/** r,
/etc/ssl/certs/** r,
/var/log/nginx/*.log w,
/var/www/** r,
/run/nginx.pid w,
network inet stream,
network inet6 stream,
}
Légende :
r: readw: writem: memory map (executable)k: lockx: executeix: inherit (sous-process hérite du profil)Px: exécuter un autre binaire avec son propre profil
Étape 5 : Créer un profil pour un nouveau service
Imaginons un script Python qui télécharge des fichiers dans /srv/downloads.
sudo nano /opt/downloader/app.py
#!/usr/bin/env python3
import urllib.request
urllib.request.urlretrieve("https://example.com/file.txt", "/srv/downloads/file.txt")
sudo chmod +x /opt/downloader/app.py
Méthode 1 : aa-genprof (interactif)
sudo aa-genprof /opt/downloader/app.py
L'outil démarre. Dans un autre terminal, lancez le script :
/opt/downloader/app.py
Retour dans le premier terminal : AppArmor liste chaque accès, vous validez ou refusez.
Méthode 2 : profil manuel
sudo nano /etc/apparmor.d/opt.downloader.app
#include <tunables/global>
/opt/downloader/app.py {
#include <abstractions/base>
#include <abstractions/python>
#include <abstractions/openssl>
/usr/bin/python3 ix,
/opt/downloader/** r,
/srv/downloads/** rw,
network inet stream,
network inet6 stream,
}
Chargez :
sudo apparmor_parser -r /etc/apparmor.d/opt.downloader.app
sudo aa-enforce /etc/apparmor.d/opt.downloader.app
Étape 6 : Workflow recommandé (complain → enforce)
- Créez le profil en complain mode :
sudo aa-complain /opt/downloader/app.py
Faites tourner l'app en conditions normales pendant quelques jours.
Récupérez les violations loggées :
sudo aa-logprof
L'outil propose d'ajouter chaque permission manquante au profil. Validez.
- Une fois stable, passez en enforce :
sudo aa-enforce /opt/downloader/app.py
Étape 7 : Voir les violations loggées
sudo journalctl -k | grep apparmor
sudo dmesg | grep DENIED
sudo cat /var/log/audit/audit.log | grep apparmor
Exemple typique :
apparmor="DENIED" operation="open" profile="/opt/downloader/app.py" name="/etc/shadow" pid=12345
→ Le script a tenté de lire /etc/shadow. AppArmor a bloqué. Si c'est légitime, ajoutez /etc/shadow r, au profil. Sinon, c'est probablement une tentative malveillante.
Étape 8 : Profils pour Docker
Docker tourne avec un profil AppArmor par défaut (docker-default). Vous pouvez le surcharger :
sudo nano /etc/apparmor.d/docker-nginx
#include <tunables/global>
profile docker-nginx flags=(attach_disconnected,mediate_deleted) {
#include <abstractions/base>
network inet tcp,
network inet udp,
/etc/nginx/** r,
/var/log/nginx/** w,
/var/cache/nginx/** rw,
/var/www/** r,
deny /proc/sysrq-trigger w,
deny /sys/[^f]*/** wkmr,
deny /sys/f[^s]*/** wkmr,
}
sudo apparmor_parser -r /etc/apparmor.d/docker-nginx
Au lancement du container :
docker run --security-opt apparmor=docker-nginx nginx
Étape 9 : Cas concret — protéger un FTP
Imaginons vsftpd qui doit servir /srv/ftp uniquement.
sudo nano /etc/apparmor.d/usr.sbin.vsftpd
#include <tunables/global>
/usr/sbin/vsftpd {
#include <abstractions/base>
#include <abstractions/nameservice>
capability net_bind_service,
capability setuid,
capability setgid,
capability chown,
/etc/vsftpd.conf r,
/etc/ftpusers r,
/srv/ftp/** rw,
/var/log/vsftpd.log w,
network inet stream,
network inet6 stream,
deny /etc/shadow r,
deny /etc/passwd w,
deny /root/** rwx,
deny /home/** rwx,
}
sudo apparmor_parser -r /etc/apparmor.d/usr.sbin.vsftpd
sudo aa-enforce /etc/apparmor.d/usr.sbin.vsftpd
sudo systemctl restart vsftpd
Même si vsftpd est compromis, l'attaquant ne pourra pas lire /home, /root, ou /etc/shadow.
Étape 10 : Abstractions courantes
/etc/apparmor.d/abstractions/ contient des blocs réutilisables :
base: essentiel pour tout binaire (libc, lookup DNS basique)nameservice: résolution DNS, /etc/hostspython: librairies Python standardopenssl: crypto + lecture certsnginx: tout ce dont Nginx a besoinapache2-common: Apachessl_certs: lecture/etc/ssl/certs
Incluez-les avec #include <abstractions/nom>.
Étape 11 : Recharger après modification
sudo apparmor_parser -r /etc/apparmor.d/usr.sbin.nginx
Si plusieurs profils modifiés, rechargez tout :
sudo systemctl reload apparmor
Étape 12 : Désactiver AppArmor (à éviter)
Si vraiment besoin (debug) :
sudo systemctl stop apparmor
sudo systemctl disable apparmor
⚠️ Ne le laissez jamais désactivé en prod sur des services exposés.
Dépannage
Un service ne démarre plus après l'enforce
Vérifiez les denials :
sudo dmesg | grep DENIED | tail -20
Soit ajoutez les permissions manquantes au profil, soit repassez en complain :
sudo aa-complain /etc/apparmor.d/usr.sbin.servicename
"Failed to apply profile"
Syntaxe invalide. Vérifiez :
sudo apparmor_parser -Q /etc/apparmor.d/usr.sbin.servicename
Profil ne s'applique pas
Vérifiez le chemin du binaire dans le profil correspond au binaire réel :
which nginx # /usr/sbin/nginx
# Profil doit lister /usr/sbin/nginx (pas /usr/bin/nginx)
Les denials remplissent les logs
C'est attendu en complain. Une fois en enforce avec un profil bien réglé, les denials légitimes diminuent.
Commandes utiles
# Status général
sudo aa-status
# Profils chargés
sudo apparmor_status
# Tester un profil sans l'enforcer
sudo aa-complain /etc/apparmor.d/usr.sbin.nginx
# Analyse les logs pour suggérer des règles
sudo aa-logprof
# Voir si un process est confiné
ps auxZ | grep nginx
# Lister les capabilities autorisées
sudo aa-unconfined
# Compiler un profil
sudo apparmor_parser -r /etc/apparmor.d/profile
# Convertir DENIED en règles à ajouter
sudo aa-mergeprof
Conclusion
AppArmor ajoute une couche de défense majeure à votre Linux :
- Confinement par binaire (granularité chemin de fichier)
- Mode complain pour tester sans casser
- Profils prêts pour les services courants
- Combinable avec Docker, systemd, conteneurs
Pour aller plus loin :
- Combinez avec seccomp pour filtrer aussi les syscalls
- Utilisez bpftrace pour observer les accès en temps réel
- Migrez vers SELinux si vous gérez un parc RedHat / CentOS / Rocky
Ressources
- Documentation officielle Ubuntu : https://ubuntu.com/server/docs/security-apparmor
- Wiki AppArmor : https://gitlab.com/apparmor/apparmor/-/wikis/Documentation
- Profils communautaires : https://gitlab.com/apparmor/apparmor-profiles
- Reference de syntaxe : https://manpages.ubuntu.com/manpages/jammy/man5/apparmor.d.5.html


















