SELinux expliqué aux administrateurs frileux

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 Red Hat, CentOS et Fedora. 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’exemples pratiques.

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 kernel 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 – EA) 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’oeil 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.

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
# systemctl 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 RHEL, CentOS ou Fedora, SELinux est activé en mode strict (Enforcing) par défaut.
  • Sur un serveur dédié sous CentOS chez Online, SELinux est désactivé (Disabled) par défaut.

Exemple 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 virtuelle c7-server.microlinux.lan avec un serveur Apache. 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 -R /usr/share/httpd/noindex/* .
# chown -R microlinux:microlinux *
# find . -type d -exec chmod 0755 {} \;
# find . -type f -exec chmod 0644 {} \;

J’édite la page index.html pour la différencier de la page fournie par défaut, je démarre Apache et j’obtiens ceci.

SELinux exemple

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.

Le contexte de sécurité SELinux

Le contenu de mon répertoire /var/www/html ressemble à ceci.

# cd /var/www/html/
# ls -l
total 16
drwxr-xr-x. 3 microlinux microlinux 4096 6 sept. 08:46 css
drwxr-xr-x. 2 microlinux microlinux 4096 6 sept. 08:46 images
-rw-r--r--. 1 microlinux microlinux 4902 6 sept. 08:47 index.html

L’option -Z me permet d’afficher le contexte de sécurité de ces fichiers.

# ls -Z
... unconfined_u:object_r:httpd_sys_content_t:s0 css
... unconfined_u:object_r:httpd_sys_content_t:s0 images
... 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  1032  ?  Ss 0:00  /usr/sbin/httpd 
system_u:system_r:httpd_t:s0  1033  ?  S  0:00  /usr/sbin/httpd 
system_u:system_r:httpd_t:s0  1034  ?  S  0:00  /usr/sbin/httpd 
system_u:system_r:httpd_t:s0  1035  ?  S  0:00  /usr/sbin/httpd 
... 

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.

Exemple pratique n° 2

Dans ce deuxième exemple, je vais copier les fichiers servis par Apache vers un répertoire en-dessous de /tmp, effacer le contenu original, puis déplacer les fichiers copiés vers l’emplacement original, en redéfinissant des permissions appropriées.

# cd /var/www/html/
# mkdir /tmp/backup
# cp -R * /tmp/backup/
# rm -rf *
# mv /tmp/backup/* .
# chown -R microlinux:microlinux *
# find . -type d -exec chmod 0755 {} \;
# find . -type f -exec chmod 0644 {} \;

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 exemple

Jetons un oeil dans les logs d’Apache pour voir ce qui se passe.

# cat /var/log/httpd/error_log 
...
[Wed Sep 06 10:47:15.855245 2017] [core:error] [pid 1103] 
(13)Permission denied: [client 192.168.2.2:54742] AH00132: 
file permissions deny server access: /var/www/html/index.html

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 oeil sur le contexte SELinux des fichiers.

# cd /var/www/html/
# ls -Z
... unconfined_u:object_r:user_tmp_t:s0 css
... unconfined_u:object_r:user_tmp_t:s0 images
... unconfined_u:object_r:user_tmp_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
found 1 alerts in /var/log/audit/audit.log
------------------------------------------------------------------
SELinux is preventing /usr/sbin/httpd from open access on the file 
/var/www/html/index.html.
*****  Plugin restorecon (92.2 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.
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 -R -v /var/www/html/

Je recharge la page web, et je constate qu’elle s’affiche à nouveau correctement.

Modifier le contexte SELinux

Les fichiers que nous avons créés dans /var/www/html étaient tous étiquetés httpd_sys_content_t, alors que leurs copies correspondantes dans /tmp/backup étaient de type user_tmp_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.

Exemple 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: création du répertoire « /srv/web »
mkdir: création du répertoire « /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 -R -v /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.

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.

Exemple 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, éditer /etc/httpd/conf.d/userdir.conf comme ceci.

<IfModule mod_userdir.c>
  # UserDir disabled
  UserDir public_html
</IfModule>

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.

# su - microlinux
$ mkdir public_html
$ cp -R /usr/share/httpd/noindex/* public_html/
$ vim public_html/index.html (modifier le titre de la page)
$ chmod 0755 public_html
$ chmod 0711 ~
$ exit

À partir de là, le site s’affiche à l’adresse http://c7-server.microlinux.lan/~microlinux.

Apache UserDir

Repassons SELinux en mode strict.

# setenforce 1

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

SELinux UserDir

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 oeil 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 (24.7 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. CentOS 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
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.

L’ensemble des booléens personnalisés apparaît dans le fichier /etc/selinux/targeted/active/booleans.local. Comme le suggère l’avertissement en tête de ce fichier, cela ne sert à rien de l’éditer directement, étant donné qu’il est automatiquement regénéré à chaque invocation de setsebool.

# This file is auto-generated by libsemanage
# Do not edit directly.
httpd_enable_homedirs=1

Documentation

Ce contenu a été publié dans CentOS, Documentation Microlinux, avec comme mot(s)-clé(s) , , . Vous pouvez le mettre en favoris avec ce permalien.

5 réponses à SELinux expliqué aux administrateurs frileux

  1. Très bon article qui permet de mieux comprendre le mécanisme SELinux. Il est vrai que la tentation est grande de désactiver cet outil lorsque l’on rencontre un problème alors qu’il apporte une sécurité importante.
    Merci Nicolas.

  2. Ping : SELinux expliqué aux administrateurs frileux - My Tiny Tools

  3. chdorb dit :

    Très bon article, la prochaine fois si j’ai un peu de temps je le ressortirai pour laissé en Enforcing plutôt que de passer direct en Disabled.
    Merci

  4. zwindler dit :

    Super article, très bien expliqué et très détaillé.

    J’utilise rarement SELinux (je confesse le passer parfois en Permissive et ne plus regarder…) et je ne connaissais vraiment les notions d’étiquetage des processus et des fichiers.

    Je me contentais de lire les logs et de passer les policy sans trop me poser de questions… J’avais même créé un tuto détaillant comment créer un policypackage avec semodule pour autoriser certaines actions pour l’agent de Nagios NRPE sans savoir dans le détail comment ça marchait 😉

    Heureusement, maintenant, c’est plus clair !

  5. jadjay dit :

    Merci pour cet article !

    C’est le premier qui soit lisible, assez complet et surtout dans le bon ordre !

    Je dit assez complet car on aurait aimé je pense, lire la fabrication du petite règle “hello world”, juste pour le fun.

    Sinon c’est vraiment le guide qu’il faut pour comprendre ce #@[!$ de SELinux qui me fit perdre tant d’heures de débogage venant plutôt du monde Debian.

    Encore merci,

Laisser un commentaire

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