Chargement en cours

SELinux #02 | LAMP et booléens

SELinux #02 | LAMP et booléens

Cas concret avec une stack LAMP~

Suite de la « mini-série » sur SELinux ! Aujourd’hui on va donc setuper une stack classique LAMP, pour Linux, Apache, MySQL, PHP. Bref, on va installer un simple petit site wordpress sur une RHEL pour voir et comprendre concrètement comment bien utiliser SELinux 😎

I) Installation de la stack

Rien de très spécifique ici, pour rappel voici la version de l’OS utilisé:

On installe donc Apache, Mariadb, et PHP. Comme vous le savez sûrement, sur RHEL et ses forks, apache est nommé httpd :

sudo dnf update && sudo dnf install httpd mariadb-server php php-mysqlnd php-fpm php-json php-cli php-common php-gd php-xml php-mbstring php-intl php-zip php-curl

Une fois fait, on active httpd et mariadb, + activation au démarrage:

sudo systemctl enable --now httpd mariadb

Bien penser à autoriser l’http/https niveau Firewalld, qui est installé et activé par défaut. On peut vérifier l’état du démon et lister les services déjà autorisés:

Et on autorise les flux HTTP/HTTPs :

# On autorise
sudo firewall-cmd --permanent --add-service=http & sudo firewall-cmd --permanent --add-service=https
# On reload le tout
sudo firewall-cmd --reload

On est tout bon !

II) Création de la BDD et installation WordPress

Là encore, rien d’insurmontable, on créer une petite DB pour notre site, puis on télécharge et extrait WordPress dans un folder sous /var/www/wordpress :

Niquel ! Maintenant avant d’aller plus loin, on va déjà vérifier si notre Apache tourne correctement:

Bien ! Maintenant on va vite attribuer les bons droits, et créer un vHost pour WordPress puis vérifier tout ça:

sudo chown -R apache:apache /var/www/wordpress & sudo chmod -R 755 /var/www/wordpress/
# Contenu du vHost
<VirtualHost *:80>
    ServerName wordpress.lab.local
    DocumentRoot /var/www/wordpress

    <Directory /var/www/wordpress>
        AllowOverride All
        Require all granted
    </Directory>

    ErrorLog /var/log/httpd/wordpress_error.log
    CustomLog /var/log/httpd/wordpress_access.log combined
</VirtualHost>

On redémarre le service, et on vérifie !

Et oui… les « ennuis » commencent… 🥲

Pourtant au niveau des droits, tout est bien OK :

III) Qu’est-ce qui bloque réellement…

Commençons par vérifier les logs, disponibles sous /var/log/audit/audit.log. On fait un petit tail -f pour voir le contenu en temps réel, et si on refresh la page côté navigateur, on voit cette erreur:

Alors c’est beaucoup de charabia maaais on peut notamment voir un avc: denied. Preuve que c’est bien SELinux qui bloque ! A noter qu’on peut aussi directement utiliser ausearch, qui est un peu plus propre :

sudo ausearch -m avc -ts today

Si on s’attarde un peu, on voit ceci:

avc: denied { read } for pid=97364 comm= »php-fpm » name= »index.php »

On voit donc que le process php-fpm n’a pas les droits en lecture pour le fichier index.php.

Mais là vous allez me dire, « nan mais si pour la moindre erreur on doit se coltiner des logs imbuvables comme ça, c’est juste trop !« 

‘pour cela qu’il existe audit2why, et qui est disponible nativement ! Un petit coup de :

 sudo cat /var/log/audit/audit.log | sudo audit2why

Et c’est déjà bien plus « human-readable » ! On peut voir la cause, une petite description, et il nous donne même la solution !

Mais avant de se lancer tête baissée… identifions correctement le souci:

  • type= AVC, pour Access Vector Cache, c’est le mécanisme de SELinux lui-même. Pour ne pas devoir recalculer à chaque fois les droits, il fait du caching ;
  • avc: denied { read }, assez clair comme message, l’action de lecture est refusée ;
  • comm= »php-fpm », le nom du processus en question ;
  • scontext=system_u:system_r:httpd_t:s0, le contexte source, ici on voit d’ailleurs que son type, (appelé aussi domaine quand on parle de processus), est httpd_t ;
  • tcontext=unconfined_u:object_r:user_home_t:s0, le contexte cible, ici user_home_t ;

On peut donc dire que le process PHP-FPM, qui est dans le même domaine que httpd, essaie d’accéder en lecture au fichier index.php, mais que ce dernier n’a pas le bon type, il est en user_home_t.

Ici, audit2why nous propose de modifier un booléen, de passer httpd_read_user_content à 1 au lieu de 0.

On va vite parler des booléens, puis expliquer pourquoi ce n’est pas une si bonne idée de procéder ainsi !

IV) Les booléens

Avec SELinux, on a la possibilité d’activer des sortes de « switchs« , appelés booléens. Pour rappel, un booléen c’est un type de donnée qui ne peut avoir que deux valeurs, True ou False, 1 ou 0 par exemple.

Et ça, il y en a des centaines de disponibles ! Par exemple:

  • httpd_read_user_content, s’il est à 1, les processus dans le domaine httpd peuvent accéder aux fichiers qui ont le type user_home_t ;
  • httpd_can_network_connect, s’il est à 1, permet aux processus dans le domaine httpd d’initier des connexions réseaux vers l’extérieur ;

Pour faire simple, vous avez la politique globale SELinux, et vous pouvez en quelques commandes modifier son comportement général. Cela permet de ne pas devoir chipoter à la mano’ dans la conf « pure et dure« , et de par exemple n’activer un booléen que durant un temps donné, ou bien de le rendre persistant, c’est-à-dire qu’il sera activé même après un reboot.

Plus haut, SELinux nous dit que pour solutionner notre erreur, il suffit d’activer le booléen httpd_read_user_content, et de cette manière PHP-FPM pourra lire les fichiers qui ont le type user_home_t. Alors dans les faits, c’est tout à fait vrai !

Le léger souci… c’est que de cette manière on ouvre la porte à un peu tout et n’importe quoi :

  • Ce n’est pas juste PHP-FPM qui va pouvoir accéder aux fichiers de ce type en lecture, mais tous les processus qui sont dans le domaine httpd ;
  • Ce ne sont pas juste les fichiers WordPress en type user_home_t qui vont être accessibles en lecture seule… mais tous les autres sur le système, si ces derniers ont les bons droits UNIX ;

Donc attention tout de même ! Mais vu que maintenant on a bien compris le souci, est-ce qu’on ne le corrigerait pas plus proprement ?

V) Correction via contexte

Vérifions donc les contextes SELinux, comme on a déjà vu dans le précédent article:

Et oui ! Il nous suffirait de changer le type de ces fichiers, et ça devrait rouler ! On teste ça:

# On change le contexte en lecture seule (sauf wp-content)
sudo semanage fcontext -a -t httpd_sys_content_t "/var/www/wordpress(/.*)?"
sudo restorecon -Rv /var/www/wordpress

# On change le contexte en lecture/écriture pour wp-content
sudo semanage fcontext -a -t httpd_sys_rw_content_t "/var/www/wordpress/wp-content(/.*)?"
sudo restorecon -Rv /var/www/wordpress/wp-content

Pour plus d’infos:

  • semanage fcontext, permet de changer le type de fichier, mais pas encore de l’appliquer ;
  • -a, ajoute une nouvelle règle ;
  • – t httpd_sys_content_t, le nouveau type à utiliser ;
  • « /var/www/wordpress(/.*)? », petite regex pour dire de l’appliquer à tous notre dossier en mode récursif ;
  • restorecon, « restaurer le contexte », dans le sens appliquer les changements faits plus haut via semanage ;
  • -R, en mode récursif, pas besoin de regex ici ;
  • -v, mode verbeux ;

Et si désormais on se rend à nouveau sur notre navigateur:

It works ! 😎

Finalisons tout de même l’installation:

On créer rapidement le fichier wp-config.php, et on peut d’ailleurs voir que le contexte SELinux est bien appliqué par défaut :

VI) Conclusion

Déjà félicitations à vous ! Vous venez d’installer une petite stack LAMP sur une RHEL avec SELinux en mode enforcing ! 🤗

On aura donc parlé ici :

  • Des commandes de débug, /var/log/audit/audit.log, audit2why… ;
  • Des booléens, même si on ira vraiment plus loin avec dans le prochain article, où on utilisera des commandes comme setsebool, setsebool -P, etc ;
  • Des contextes ;

Déjà rien qu’à partir de là, vous devriez y voir bien plus clair ! Vous avez fait le plus dur.

Pour le prochain, on refera sûrement un petit labo, en essayant d’aller un peu plus loin et de découvrir les dernières notions à apprendre 🙂

Sur ce, j’espère vous avoir appris quelques bricoles, et vous souhaite une bonne journée/soirée ! Ciao !

Laisser un commentaire