SELinuxSELinux (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.

Rocky LinuxNote : 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.

AstuceL’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.

ImportantLorsque 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.

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 :

SELinux

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és httpd_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’étiquette shadow_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 :

SELinux

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
...

AstuceNotez 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’option fcontext 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’occurrence httpd_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.

ImportantNotons 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 :

SELinux

Repassons SELinux en mode strict :

# setenforce 1

Rafraîchissons la page, et nous nous retrouvons avec l’erreur suivante :

SELinux

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

AstuceNotez 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


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 !

Laisser un commentaire

Emplacement de l’avatar

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *