Aller au contenu
Sécurité medium

fapolicyd : bloquer l'exécution non autorisée sur RHEL

14 min de lecture

fapolicyd est un démon de RHEL, basé sur fanotify, qui autorise ou bloque l'exécution des binaires et le chargement des bibliothèques selon une politique de confiance. Il complète SELinux : là où SELinux confine ce qu'un processus a le droit de faire, fapolicyd décide ce qui a le droit de s'exécuter. C'est le whitelisting applicatif natif de RHEL, un différenciant fort en durcissement enterprise. Ce guide s'adresse aux sysadmins RHEL et à la préparation RHCSA/RHCE qui veulent aller au-delà du programme officiel.

fanotify est le mécanisme du noyau Linux qui permet à un démon d'être notifié, et de statuer, sur les accès aux fichiers. fapolicyd s'en sert pour intercepter chaque tentative d'exécution et la comparer à sa base de confiance.

  • Situer fapolicyd face à SELinux et firewalld, trois couches aux rôles distincts.
  • Construire et interroger la trust database (base de confiance).
  • Écrire des règles allow/deny dans rules.d/ et les compiler.
  • Tester en mode permissive avant d'enforcer, et diagnostiquer un refus.
  • Éviter le piège classique qui brique des démons légitimes au démarrage.
  • Un système RHEL 8.4 ou 9 (ou un clone : Rocky Linux, AlmaLinux). Le répertoire de règles modulaire rules.d/ est disponible depuis RHEL 8.4.
  • Un accès root (les commandes ci-dessous s'exécutent en root ou via sudo).

fapolicyd, SELinux et firewalld : trois couches distinctes

Section intitulée « fapolicyd, SELinux et firewalld : trois couches distinctes »

Ces trois mécanismes sont souvent confondus alors qu'ils répondent à des questions différentes et se complètent. Les empiler, c'est faire de la défense en profondeur.

CoucheCe qu'elle contrôleLa question
firewalldle trafic réseau (ports, zones)qui a le droit de se connecter
SELinuxce qu'un processus peut faire (MAC par étiquettes)ce qu'un processus a le droit de faire
fapolicydce qui peut s'exécuter (whitelisting)ce qui a le droit de s'exécuter

Le MAC (Mandatory Access Control, contrôle d'accès obligatoire) de SELinux et le whitelisting de fapolicyd ne se remplacent pas. Un binaire malveillant déposé par un attaquant peut être correctement étiqueté par SELinux et pourtant illégitime : fapolicyd le bloque parce qu'il n'est pas dans la base de confiance. C'est la parade directe aux binaires détournés, cousine côté Linux du principe vu pour Windows avec LOLBAS.

Le paquet est disponible dans les dépôts de base.

Fenêtre de terminal
# Installer le démon
sudo dnf install fapolicyd

Tout repose sur la trust database (base de confiance) : la liste des fichiers autorisés à s'exécuter, avec leur taille et leur empreinte SHA256. fapolicyd fait confiance par défaut à tout ce qui est installé par RPM (la base RPM sert de source de confiance), ce qui couvre l'essentiel d'un système sain.

La commande centrale reconstruit et rafraîchit cette base :

Fenêtre de terminal
# Reconstruire la base de confiance depuis la base RPM
sudo fapolicyd-cli --update

Cette commande notifie le démon de recharger sa base de confiance. À lancer après chaque installation ou mise à jour de paquets, sinon les nouveaux binaires seront inconnus de fapolicyd, donc refusés. Sur un système sain, la base compte des dizaines de milliers d'entrées : par exemple 50624 sur l'AlmaLinux 8.10 (fapolicyd 1.3.2) qui a servi à valider ce guide.

Pour vérifier ce que fapolicyd considère comme fiable, videz la base :

Fenêtre de terminal
# Lister les entrées de confiance (fichiers RPM + fichiers ajoutés à la main)
sudo fapolicyd-cli --dump-db | head

Chaque ligne suit le format <source> <chemin> <taille> <empreinte SHA256>, par exemple rpmdb /usr/bin/bash <taille> <sha256>. Le préfixe rpmdb indique que la confiance vient de la base RPM. Une longue liste de chemins connus (/usr/bin/..., /opt/...) confirme que la base est bien peuplée. Les fichiers de confiance ajoutés manuellement vivent dans /etc/fapolicyd/fapolicyd.trust et dans le répertoire /etc/fapolicyd/trust.d/.

Les règles décident, pour chaque accès, s'il faut autoriser (allow) ou refuser (deny). Elles sont modulaires, réparties dans des fichiers du répertoire /etc/fapolicyd/rules.d/, traités dans l'ordre des noms de fichiers (d'où le préfixe numérique, 10-, 20-, etc.).

L'ordre compte : fapolicyd applique la première règle qui correspond. Une règle deny placée avant un allow gagne.

Fenêtre de terminal
# Lister les règles compilées, numérotées (utile pour lire les logs)
sudo fapolicyd-cli --list

La politique par défaut d'AlmaLinux/RHEL se termine par un catch-all qui refuse tout ce qui n'a pas été explicitement autorisé (extrait réel) :

2. allow perm=any uid=0 trust=1 : all
9. allow perm=execute all : trust=1
12. allow perm=any all : ftype=text/x-shellscript
13. deny_audit perm=execute all : all

Deux règles portent toute la logique. La règle 9 n'autorise l'exécution que si le fichier est de confiance (trust=1) ; la règle 13, le catch-all final, refuse et journalise (deny_audit) toute exécution non couverte. Ce couple est ce qui bloque un binaire inconnu.

Après toute modification d'un fichier de rules.d/, il faut recompiler. L'outil fagenrules assemble tous les fichiers de rules.d/ en un fichier unique compiled.rules :

Fenêtre de terminal
# Recompiler les règles puis recharger le démon
sudo fagenrules --load

Permissive puis enforcing : ne jamais brûler l'étape

Section intitulée « Permissive puis enforcing : ne jamais brûler l'étape »

Le mode de fonctionnement se règle dans le fichier de configuration principal /etc/fapolicyd/fapolicyd.conf, avec l'option permissive.

  • permissive = 1 : fapolicyd journalise ce qu'il aurait bloqué, sans rien empêcher. C'est le mode d'apprentissage.
  • permissive = 0 : fapolicyd enforce, il bloque réellement.

La méthode sûre consiste à démarrer en permissive, observer les refus qui seraient survenus, corriger la base de confiance et les règles, puis seulement basculer en enforcing.

Les refus se lisent dans le journal d'accès :

Fenêtre de terminal
# Suivre les décisions de fapolicyd (refus inclus)
sudo tail -f /var/log/fapolicyd-access.log

Un refus vous donne le chemin du fichier et le numéro de règle appliquée (corrélé avec fapolicyd-cli --list), de quoi diagnostiquer précisément ce qui manque à la base de confiance.

C'est l'erreur qui coûte une soirée, et que presque aucune doc ne mentionne clairement. Une base de confiance corrompue, vide ou périmée fait bloquer par fapolicyd tout ce qu'elle ne connaît pas. En lab, une base lmdb corrompue était tombée à 14 entrées ; la reconstruire de zéro l'a ramenée à 50624.

Fenêtre de terminal
# Reconstruire une base de confiance corrompue, de zéro
sudo systemctl stop fapolicyd
sudo rm -f /var/lib/fapolicyd/*.mdb
sudo fapolicyd-cli --update
sudo systemctl start fapolicyd

La règle à graver : reconstruire la base de confiance avant d'enforcer, et tester en permissive d'abord. Un service bloqué par une base corrompue ressemble à une panne de permissions, jamais à fapolicyd, d'où la perte de temps.

Un binaire ELF installé hors RPM (une application maison dans /opt, un binaire Go compilé sur place) n'est pas dans la base RPM : il est inconnu, donc refusé par le catch-all. Un script, lui, passe déjà (voir le piège des scripts plus haut). Ajoutez le binaire à la base de confiance :

Fenêtre de terminal
# Ajouter un binaire (ou tout un répertoire) à la base de confiance
sudo fapolicyd-cli --file add /opt/pavois-hello

La commande écrit l'entrée de confiance et notifie le démon ; sa sortie le confirme :

Fapolicyd was notified

Le binaire est aussitôt exécutable, sans --update (validé en lab). Si le chemin est un répertoire, fapolicyd parcourt l'arborescence et ajoute chaque fichier régulier avec son empreinte SHA256. À refaire (ou via fapolicyd-cli --file update) après chaque recompilation de votre application, sinon l'empreinte ne correspond plus et l'exécution est refusée.

Le déroulé fiable, du paquet à l'enforcement, tient en peu d'étapes.

  1. Installer sans activer.

    Fenêtre de terminal
    sudo dnf install fapolicyd
  2. Reconstruire la base de confiance depuis la base RPM.

    Fenêtre de terminal
    sudo fapolicyd-cli --update
  3. Ajouter vos binaires ELF hors RPM, s'il y en a (l'ajout notifie le démon, pas besoin de --update).

    Fenêtre de terminal
    sudo fapolicyd-cli --file add /opt/pavois-hello
  4. Tester en permissive : mettre permissive = 1 dans /etc/fapolicyd/fapolicyd.conf, activer le service, puis observer.

    Fenêtre de terminal
    sudo systemctl enable --now fapolicyd
    sudo tail -f /var/log/fapolicyd-access.log
  5. Basculer en enforcing une fois les faux refus corrigés : permissive = 0, puis redémarrer le service.

    Fenêtre de terminal
    sudo systemctl restart fapolicyd

Face à un blocage, ce tableau relie le symptôme à sa cause et à la correction.

SymptômeCause probableCorrection
Un service échoue avec « Permission denied »Souvent des permissions de fichiers ou un refus SELinux (AVC), parfois une base de confiance corrompueVérifier les modes et les AVC SELinux, puis seulement la base (rebuild, voir le piège)
Un binaire ELF maison est refuséAbsent de la base (installé hors RPM)fapolicyd-cli --file add <chemin> (notifie le démon)
Refus après recompilation d'une appliEmpreinte SHA256 changéefapolicyd-cli --file update puis --update
Les règles éditées n'ont aucun effetrules.d/ non recompiléfagenrules --load
Le démon ne démarre pasErreur de configurationfapolicyd-cli --check-config
Incohérence base de confiance / disqueFichiers modifiés hors trustfapolicyd-cli --check-trustdb
  1. fapolicyd contrôle ce qui a le droit de s'exécuter, en complément de SELinux (ce qu'un processus peut faire) et de firewalld (le réseau).

  2. Tout repose sur la base de confiance : les fichiers RPM sont fiables par défaut, fapolicyd-cli --update la reconstruit.

  3. Toujours reconstruire la base avant d'enforcer, et tester en permissive : c'est la règle qui évite de briquer des services légitimes.

  4. Les règles vivent dans rules.d/, se compilent avec fagenrules --load, et l'ordre des fichiers décide de la priorité allow/deny.

  5. Un binaire hors RPM doit être ajouté à la main (fapolicyd-cli --file add) et réajouté après chaque recompilation.

  6. Les refus se diagnostiquent dans /var/log/fapolicyd-access.log, corrélés au numéro de règle via fapolicyd-cli --list.

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracking. Un soutien, même symbolique, m'aide à couvrir l'hébergement et à garder ces ressources gratuites. Merci pour votre appui.

Le formulaire ne s'affiche pas ? Ouvrir Ko-fi dans un onglet.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn