AppArmor : confinement Linux par profil

AppArmor : confinement Linux par profil

Limitez ce que vos services peuvent faire sur Linux avec AppArmor. Système de contrôle d'accès obligatoire (MAC) basé sur des profils par binaire. Plus simple à apprivoiser que SELinux, déjà installé sur Ubuntu et Debian.

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 : read
  • w : write
  • m : memory map (executable)
  • k : lock
  • x : execute
  • ix : 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)

  1. Créez le profil en complain mode :
sudo aa-complain /opt/downloader/app.py
  1. Faites tourner l'app en conditions normales pendant quelques jours.

  2. Récupérez les violations loggées :

sudo aa-logprof

L'outil propose d'ajouter chaque permission manquante au profil. Validez.

  1. 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/hosts
  • python : librairies Python standard
  • openssl : crypto + lecture certs
  • nginx : tout ce dont Nginx a besoin
  • apache2-common : Apache
  • ssl_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

Join our Discord community server

For any questions, suggestions, or just to chat with the community, join us on Discord!

900+Members