SELinux (Security Enhanced Linux) est un mécanisme de sécurité développé pour Linux par la NSA (National Security Agency) et utilisé principalement sur les systèmes Fedora et Red Hat Enterprise Linux ainsi que tous les clones binairement compatibles à RHEL. Il améliore de manière significative la sécurité des serveurs sur lesquels il est déployé, en apportant une couche supplémentaire aux traditionnels droits d’accès aux fichiers Unix. En contrepartie, sa complexité considérable conduit tôt ou tard à des erreurs de configuration, ce qui a pour conséquence que bon nombre d’administrateurs décident tôt ou tard de désactiver SELinux. L’objet de cet article, c’est de fournir une introduction simple à SELinux, en s’appuyant sur une série d’ateliers pratiques.
Note : Les exemples pratiques ont tous été mis en œuvre sur une installation minimale de Rocky Linux 8.
DAC et MAC
Traditionnellement, Linux utilise le contrôle d’accès discrétionnaire (Discretionary Access Control – DAC). Une application tourne avec les droits d’un certain utilisateur, ce qui définit les fichiers, les répertoires et les périphériques auxquels elle peut accéder. Les services réseau comme les serveurs de bases de données, les serveurs web etc. démarrent avec les droits du superutilisateur root
pour ensuite tourner avec les droits limités d’un utilisateur système. Ce système traditionnel des permissions Unix est assez simple à manipuler, mais ses possibilités de configuration restent limitées. Dès qu’un intrus réussit à prendre la main sur un service, il aura accès à une partie plus ou moins importante du système de fichiers, y compris des fichiers que le service en question n’utilise pas forcément.
C’est là où SELinux entre en jeu avec le principe du contrôle d’accès obligatoire (Mandatory Access Control – MAC). Pour simplifier, chaque processus est confiné à un domaine, et les fichiers sont étiquetés en conséquence. Un processus ne pourra accéder qu’aux fichier étiquetés pour le domaine auquel il est confiné.
SELinux, c’est quoi exactement ?
Plus concrètement, SELinux est une extension du noyau Linux qui permet de surveiller des processus en cours d’exécution, en garantissant que ceux-ci respectent certaines règles. SELinux repose essentiellement sur deux fondements :
- Les fichiers et les processus doivent être étiquetés avec un contexte de sécurité SELinux approprié. Nous verrons un peu plus loin ce que cela signifie concrètement.
- Les processus surveillés par SELinux doivent respecter un certain nombre de règles prédéfinies. Là aussi, une série d’exemples pratiques nous permettra de comprendre de quoi il s’agit.
L’efficacité de SELinux va donc de pair avec la qualité des règles. Notons ici que seul le distributeur Red Hat s’est donné la peine de définir une collection de règles qui tiennent la route pour une utilisation professionnelle.
Les points problématiques
Comme toutes les technologies un peu pointues, SELinux présente quelques problèmes, que nous pouvons mentionner ici :
- SELinux utilise les attributs étendus (Extended Attributes) des fichiers pour les étiquettes. D’une part, cela nécessite des systèmes de fichiers compatibles avec les attributs étendus. D’autre part, il faudra être particulièrement vigilant lors des opérations de sauvegarde, de restauration et de mise à jour.
- Sous le capot, SELinux est d’une complexité ahurissante. La protection des services les plus élémentaires d’un serveur requiert des milliers de règles. Pour l’administrateur Linux « commun mortel », SELinux peut paraître comme une « boîte noire » développée par les ingénieurs Red Hat, dont la complexité l’amènera tôt ou tard à des erreurs d’implémentation. Un coup d’œil sur les blogs techniques divers et variés montre que les administrateurs succombent bien trop souvent à la tentation de désactiver SELinux au premier souci de configuration.
- Notons au passage une critique entièrement infondée, mais qui revient régulièrement dans les discussions. SELinux est développé par la NSA, un service secret tristement célèbre pour ses opérations de surveillance à l’échelle planétaire depuis les révélations d’Edward Snowden. Partant de là, il est facile d’imaginer que la NSA en aurait profité pour glisser du code malveillant dans le kernel, qui lui permettrait d’étendre ses opérations de surveillance à toutes les machines tournant sous Linux. Avant de sombrer dans la paranoïa, notons donc que SELinux est un projet open source qui a été audité par un nombre important d’experts indépendants avant d’être officiellement inclus dans le kernel Linux.
SELinux dans tous ses états
SELinux propose trois modes différents :
- Dans le mode strict (
Enforcing
), les accès sont restreints en fonction des règles SELinux en vigueur sur la machine. - Le mode permissif (
Permissive
) peut être considéré comme un mode de débogage. Les règles SELinux sont interrogées, les erreurs d’accès sont enregistrées dans les logs, mais l’accès ne sera pas bloqué. - Lorsque SELinux est désactivé (
Disabled
), l’accès n’est pas restreint, et rien n’est enregistré dans les logs.
La commande getenforce
vous informe sur le mode en vigueur sur votre machine :
# getenforce Enforcing
La commande setenforce
permet de basculer temporairement – jusqu’au prochain redémarrage – entre les modes strict (Enforcing
) et permissif (Permissive
) :
# getenforce Enforcing # setenforce 0 # getenforce Permissive # setenforce 1 # getenforce Enforcing
Le mode SELinux par défaut est défini dans le fichier /etc/selinux/config
:
# /etc/selinux/config
SELINUX=enforcing
SELINUXTYPE=targeted
Ici, SELINUX
prendra une des trois valeurs enforcing
, permissive
ou disabled
. Quant à SELINUXTYPE
, on gardera la valeur par défaut targeted
, qui garantit la surveillance des principaux services réseau.
Lorsque SELinux est activé – autrement dit, lorsqu’on passe du mode disabled
à permissive
ou enforcing
, il faut impérativement songer à réétiqueter l’ensemble des fichiers du système. Pour ce faire, il suffit de créer un fichier vide .autorelabel
à la racine du système de fichiers avant de redémarrer :
# touch /.autorelabel # reboot
Le réétiquetage du système de fichiers peut prendre un certain temps, en fonction de la taille de votre installation. Pour une première utilisation, je vous conseille d’opter pour le mode permissif par défaut. Cela évite de se retrouver avec un système qui ne démarre plus en mode strict.
- Sur une installation fraîche de Rocky Linux ou Red Hat Enterprise Linux, SELinux est activé en mode strict (
Enforcing
) par défaut. - Sur un serveur dédié sous Rocky Linux chez Scaleway, SELinux est désactivé (
Disabled
) par défaut.
Atelier pratique n° 1
Maintenant que nous disposons du minimum syndical de bagage théorique, passons à la pratique. Pour ce premier exemple, je passe en mode strict :
# getenforce Enforcing
Je mets en place une machine « bac à sable » sandbox.microlinux.lan
avec un serveur Apache :
# firewall-cmd --permanent --add-service=http # firewall-cmd --reload # dnf install -y httpd # systemctl enable httpd --now
Dans sa configuration par défaut, Apache est censé servir les pages web rangées dans /var/www/html
. Je copie la page web par défaut à cet endroit :
# cd /var/www/html/ # cp -v /usr/share/testpage/index.html . '/usr/share/testpage/index.html' -> './index.html'
J’édite la page index.html
(aux alentours de la ligne 225) pour la différencier de la page fournie par défaut :
<body> <h1>SELinux <strong>Test Page</strong></h1>
J’ouvre la page locale http://sandbox.microlinux.lan
dans un navigateur web et j’obtiens ceci :
Jusqu’ici, tout va bien. J’ai réussi à configurer un hébergement web avec SELinux en mode strict, et je me sens un peu comme Monsieur Jourdain dans Le Bourgeois Gentilhomme, qui fait de la prose sans le savoir.
Notez ici que dans la configuration par défaut, Apache sert les pages web rangées dans /usr/share/httpd/noindex
en l’absence de page d’index. Cette fonctionnalité peut s’avérer quelque peu déroutante pour la suite de nos manipulations. Il vaut mieux la désactiver, ce qui peut se faire simplement en commentant la directive correspondante dans le fichier /etc/httpd/conf.d/welcome.conf
:
# <LocationMatch "^/+$">
# Options -Indexes
# ErrorDocument 403 /.noindex.html
# </LocationMatch>
N’oubliez pas de prendre en compte cette nouvelle configuration :
# systemctl reload httpd
Le contexte de sécurité SELinux
Le contenu de mon répertoire /var/www/html
ressemble à ceci :
# cd /var/www/html/ # ls -l total 8 -rw-r--r--. 1 root root 7616 Nov 30 09:40 index.html
L’option -Z
me permet d’afficher le contexte de sécurité :
# ls -Z unconfined_u:object_r:httpd_sys_content_t:s0 index.html
Cette même option -Z
peut s’utiliser avec la commande ps
pour afficher le contexte de sécurité d’un processus :
# ps -axZ | grep httpd
system_u:system_r:httpd_t:s0 3948 ? Ss 0:00 /usr/sbin/httpd -DFOREGROUND
system_u:system_r:httpd_t:s0 3949 ? S 0:00 /usr/sbin/httpd -DFOREGROUND
system_u:system_r:httpd_t:s0 3950 ? S 0:00 /usr/sbin/httpd -DFOREGROUND
system_u:system_r:httpd_t:s0 3951 ? S 0:00 /usr/sbin/httpd -DFOREGROUND
...
Un contexte SELinux est présenté sous la forme utilisateur:rôle:type:niveau
. Laissons de côté les utilisateurs SELinux, les rôles et les niveaux, et concentrons-nous sur le type, qui correspond au troisième champ dans le contexte SELinux :
unconfined_u:object_r:httpd_sys_content_t:s0
system_u:system_r:httpd_t:s0
Nous avons vu que sur le serveur web en cours d’exécution, le processus httpd
est étiqueté httpd_t
. Les fichiers servis par Apache semblent tous munis de l’étiquette httpd_sys_content_t
. Si nous affichons le contexte de sécurité des fichiers appartenant à Apache, nous constatons qu’ils semblent tous appartenir à une même famille en termes d’étiquettes :
- Les fichiers de configuration sont étiquetés
httpd_config_t
. - Les fichiers logs sont tous de type
httpd_log_t
.
Essayons maintenant de cerner le principe de fonctionnement de SELinux en partant de cet exemple :
- Il est probablement logique qu’un processus étiqueté
httpd_t
puisse interagir avec des fichiers étiquetéshttpd_sys_content_t
,httpd_config_t
,httpd_log_t
, etc. - En revanche, il n’est probablement pas logique qu’un processus étiqueté
httpd_t
puisse interagir avec un fichier comme/etc/shadow
, qui porte l’étiquetteshadow_t
.
Faisons une petite expérience pour mieux comprendre ce principe.
Atelier pratique n° 2
Dans ce deuxième exemple, je vais copier le fichier servi par Apache vers un répertoire en-dessous de /srv
, effacer le contenu original, puis déplacer les fichier copié vers l’emplacement original :
# cd /var/www/html/ # mkdir -v /srv/html mkdir: created directory '/srv/html' # cp -v index.html /srv/html/ 'index.html' -> '/srv/html/index.html' # rm -f index.html # mv -v /srv/html/index.html . renamed '/srv/html/index.html' -> './index.html'
Certains parmi vous auront le vague sentiment de tourner en rond pour revenir à la case départ. Ce n’est pas tout à fait le cas, comme nous allons le voir tout de suite. Rafraîchissez la page web qui s’affiche dans le navigateur, et voici ce que vous obtenez :
Jetons un œil dans les logs d’Apache pour voir ce qui se passe :
# cat /var/log/httpd/error_log ... [Wed Nov 30 10:12:31.982597 2022] ... (13)Permission denied: [client 192.168.2.2:40168] AH00035: access to /index.html denied (filesystem path '/var/www/html/index.html') because search permissions are missing on a component of the path
Et là, nous restons quelque peu perplexes, parce que nous avons vérifié à deux fois les droits d’accès de nos fichiers, et qu’ils ont l’air corrects. Mystère et boules de gomme.
Retournons maintenant dans /var/www/html
et jetons un œil sur le contexte SELinux :
# ls -Z unconfined_u:object_r:var_t:s0 index.html
L’administrateur perplexe souhaite sans doute en savoir un peu plus sur le pourquoi du comment de ce refus d’obtempérer. Les logs de SELinux ne sont pas ce qu’il y a de plus lisible, mais fort heureusement, il existe un outil pratique pour nous faciliter la tâche. Installez le paquet setroubleshoot-server
et invoquez la commande suivante :
# sealert -a /var/log/audit/audit.log | less
100% done
found 1 alerts in /var/log/audit/audit.log
--------------------------------------------------------
SELinux is preventing /usr/sbin/httpd from getattr access
on the file /var/www/html/index.html.
***** Plugin restorecon (94.8 confidence) suggests *****
If you want to fix the label.
/var/www/html/index.html default label should be httpd_sys_content_t.
Then you can run restorecon. The access attempt may have been stopped
due to insufficient permissions to access a parent directory in which
case try to change the following command accordingly. Do
# /sbin/restorecon -v /var/www/html/index.html
...
Notez au passage qu’ici, j’ai invoqué la commande sur un serveur configuré en anglais (LANG=en_US.utf8
). Sur un système configuré en français, on obtient un mélange de français et d’anglais assez folklorique.
Quoi qu’il en soit, retenons le contenu de ce message d’erreur :
- SELinux empêche Apache d’accéder au fichier
index.html
. - L’étiquette par défaut devrait être
httpd_sys_content_t
. - La commande
restorecon
permet de rétablir l’étiquette correcte.
Je vais suivre les recommandations sans toutefois les prendre au pied de la lettre, car je vais utiliser restorecon
pour réétiqueter correctement tout le contenu de mon répertoire /var/www/html
.
# restorecon -Rv /var/www/html/
Je recharge la page web, et je constate qu’elle s’affiche à nouveau correctement.
Modifier le contexte SELinux
Le fichier que nous avons créé dans /var/www/html
était étiqueté httpd_sys_content_t
, alors que sa copie correspondante dans /srv/html
était de type var_t
.
En mode strict aussi bien qu’en mode permissif, chaque fichier nouvellement créé à un certain endroit du système de fichiers sera étiqueté de manière appropriée par le système. La commande matchpathcon
pourra nous renseigner sur l’étiquette utilisée en fonction du répertoire :
$ matchpathcon /var/www/html/ /var/www/html system_u:object_r:httpd_sys_content_t:s0 $ matchpathcon /var/log/httpd/ /var/log/httpd system_u:object_r:httpd_log_t:s0 $ matchpathcon /etc/httpd/conf/ /etc/httpd/conf system_u:object_r:httpd_config_t:s0
La commande restorecon
que nous avons utilisée un peu plus haut s’est donc chargée de restaurer le contexte par défaut des fichiers rangés dans cette arborescence.
En revanche, si nous décidons de ranger des fichiers dans un endroit un peu moins orthodoxe du système, nous devrons nous charger de définir un contexte SELinux adapté pour l’arborescence en question. Le prochain exemple nous le montrera.
Atelier pratique n° 3
Admettons que je veuille ranger les fichiers de mon serveur web dans l’arborescence /srv/web/html
plutôt que dans /var/www/html
:
# mkdir -pv /srv/web/html mkdir: created directory ‘/srv/web’ mkdir: created directory ‘/srv/web/html’
Comme on peut s’y attendre, le contexte SELinux par défaut de ce répertoire n’est pas adapté à son utilisation :
# matchpathcon /srv/web/html/ /srv/web/html system_u:object_r:var_t:s0
Pour modifier le contexte par défaut de cette arborescence, je pourrais m’y prendre de la manière suivante :
# semanage fcontext -a -t httpd_sys_content_t '/srv/web(/.*)?' # restorecon -Rv /srv/web/
L’opération se fait donc en deux temps et mérite d’ailleurs quelques remarques :
- Comme on s’en doute, l’outil
semanage
avec l’optionfcontext
permet de gérer les contextes de sécurité SELinux. - L’option
-a
(--add
) permet d’ajouter une entrée. - L’option
-t
(--type
) spécifie le type, en l’occurrencehttpd_sys_content_t
. - L’utilisation de l’expression régulière
'/srv/web(/.*)?'
applique la directive récursivement sur toute l’arborescence. - Une fois que le contexte par défaut est défini, il faut l’appliquer avec
restorecon
.
Notons ici que la documentation de SELinux cite chcon
comme premier outil pour modifier le contexte d’un fichier ou d’un répertoire. Le problème avec chcon
, c’est qu’à la prochaine utilisation de restorecon
sur le fichier ou ses parents, le fichier sera réétiqueté avec le contexte de son plus proche parent pour lequel un contexte spécifique est défini. Il vaut donc mieux prendre l’habitude d’utiliser semanage fcontext
et restorecon
pour éviter de se tirer dans le pied par la suite.
Atelier pratique n° 4
Dans ce quatrième exemple, nous mettons la pratique avant la théorie pour expliquer une autre particularité de SELinux.
Pour commencer, passez en mode permissif :
# setenforce 0
Nous allons activer l’affichage du contenu de ~/public_html
pour les répertoires utilisateur. En partant de la configuration par défaut d’Apache, éditez /etc/httpd/conf.d/userdir.conf
comme ceci :
<IfModule mod_userdir.c>
# UserDir disabled
UserDir public_html
</IfModule>
Prenez en compte la nouvelle configuration :
# systemctl reload httpd
En tant que simple utilisateur, créez un répertoire ~/public_html
, copiez la page web par défaut dans ce répertoire en l’éditant un tant soit peu et définissez les droits d’accès qui vont bien :
$ mkdir -v ~/public_html
mkdir: created directory '/home/microlinux/public_html'
$ cp -v /usr/share/testpage/index.html public_html/
'/usr/share/testpage/index.html' -> 'public_html/index.html'
$ vim public_html/index.html (modifiez le titre de la page)
$ chmod 0755 public_html/
$ chmod 0711 ~
À partir de là, la page s’affiche à l’adresse http://sandbox.microlinux.lan/~microlinux
:
Repassons SELinux en mode strict :
# setenforce 1
Rafraîchissons la page, et nous nous retrouvons avec l’erreur suivante :
Les logs dans /var/log/httpd/error_log
confirment qu’il s’agit d’un problème de droits d’accès, mais ne nous en disent pas plus. Jetons un œil dans les logs de SELinux :
# sealert -a /var/log/audit/audit.log | less
La première alerte nous suggère de modifier le contexte du fichier /home/microlinux/public_html/index.html
. Continuons un peu, et regardons de près la deuxième alerte :
***** Plugin catchall_boolean (32.5 confidence) suggests ***** If you want to allow httpd to enable homedirs Then you must tell SELinux about this by enabling the 'httpd_enable_homedirs' boolean. Do setsebool -P httpd_enable_homedirs 1
Gérer les booléens
Les booléens permettent de modifier une politique SELinux sans avoir à se plonger dans les arcanes de la rédaction de politiques. Rocky Linux comporte quelques centaines de booléens, dont certains vont nous simplifier la vie.
Affichons les booléens qui concernent Apache :
# getsebool -a | grep httpd
httpd_anon_write --> off
httpd_builtin_scripting --> on
httpd_can_check_spam --> off
httpd_can_connect_ftp --> off
httpd_can_connect_ldap --> off
...
Nous pouvons en savoir un peu plus sur le rôle de chaque booléen :
# semanage boolean -l | grep httpd | sort | less httpd_anon_write (off, off) Allow httpd to anon write httpd_builtin_scripting (on, on) Allow httpd to builtin scripting httpd_can_check_spam (off, off) Allow httpd to can check spam httpd_can_connect_ftp (off, off) Allow httpd to can connect ftp httpd_can_connect_ldap (off, off) Allow httpd to can connect ldap
Reprenons le dernier exemple pratique et tentons de corriger la politique SELinux de manière à autoriser l’affichage du contenu de ~/public_html
:
# setsebool -P httpd_enable_homedirs on
Notez qu’ici l’option -P
rend la directive permanente, c’est-à-dire qu’elle est conservée après un redémarrage du système.
Et pour afficher l’ensemble des booléens personnalisés, vous pourrez utiliser la commande suivante :
# semanage boolean -lC SELinux boolean State Default Description httpd_enable_homedirs (on , on) Allow httpd to enable homedirs
Documentation
- Linux 2021 – Michael Kofler, pp. 1285 – 1292 (en allemand)
- La documentation francophone du projet Fedora
- Thomas Cameron – SELinux For Mere Mortals, vidéo + présentation (en anglais)
La rédaction de cette documentation demande du temps et des quantités significatives de café espresso. Vous appréciez ce blog ? Offrez un café au rédacteur en cliquant sur la tasse.
4 commentaires
xhark · 30 novembre 2022 à 23 h 16 min
Excellent article, bravo pour cette démystification !
Polux · 1 décembre 2022 à 3 h 18 min
Merci. Bravo. Sous le coude.
astroblix · 11 février 2024 à 17 h 51 min
Article au top, bravo pour la maîtrise technique !
kikinovak · 11 février 2024 à 18 h 37 min
Merci pour les fleurs !