[ << ]
[ < ]
[ Sommaire ]
[ > ]
[ >> ]
1. Présentation de SELinux
Table des matières :
1.a. Les types SELinux
Un type SELinux est un attribut de sécurité assigné à un objet (un fichier, un
port réseau, etc.). Le type d'un processus est communément appelé son domaine.
Une politique de sécurité SELinux est principalement constituée de règles pour
respecter les types, qui décrivent de quelles manières les domaines sont
autorisés à interagir avec les objets et avec les autres domaines. Un type est
généralement suffixé par « _t », par exemple : sysadm_t.
Le type est l'attribut le plus important pour un processus ou un objet, car la
plupart des décisions politiques se basent sur les types sources et cibles.
1.b. Les rôles SELinux
La sécurité fournie par SELinux tourne autour des types. Les rôles SELinux sont
donc différents de ce que l'on peut trouver dans un système de contrôle d'accès
basé sur les rôles (RBAC). Les différentes autorisations ne sont pas attribuées
aux rôles. Un rôle décrit l'ensemble des types dont un utilisateur peut se
servir. Par exemple, un administrateur qui utilise un système pour exécuter des
tâches d'utilisateur classique devrait être dans le rôle staff_r. S'il
veut procéder à une tâche d'administration système, il doit alors explicitement
basculer dans le rôle sysadm_r. En termes SELinux, la liste des domaines
dans lesquels un utilisateur peut se trouver dépend de son rôle. Si un rôle
n'est pas autorisé à avoir un certain domaine, une transition vers ce domaine
sera rejetée, même si la règle du type permet la transition de domaine. Un rôle
est généralement suffixé par un « _r », par exemple :
system_r.
1.c. Les identités SELinux
Qu'est-ce qu'une identité SELinux ?
Une identité SELinux est similaire à un nom d'utilisateur Linux. Le changement
d'identité devrait être limité à certains cas spécifiques, car le contrôle
d'accès basé sur les rôles se repose sur l'identité SELinux. Donc, en général,
l'identité SELinux d'un utilisateur ne changera pas tout au long de sa session.
L'identifiant utilisateur de Linux peut être changé par set(e)uid, ce n'est
donc pas un identifiant approprié pour une identité SELinux. Si on attribue une
identité SELinux à un utilisateur, celle-ci doit correspondre au nom
d'utilisateur Linux. À chaque identité SELinux on attribue un ensemble de
rôles.
Configurer la correspondance des identités SELinux
Une politique SELinux contient plusieurs identités SELinux génériques qui
devraient convenir pour tous les utilisateurs. Cette correspondance n'a besoin
d'être configurée que lors d'une utilisation de la politique stricte. Vous
n'avez pas besoin de la configurer avec la politique ciblée, car l'identité par
défaut (user_u) est suffisante dans tous les cas.
Lorsqu'un utilisateur se connecte, l'identité SELinux est déterminée selon
cette correspondance.
| Identité SELinux |
Rôles |
Description |
| system_u |
system_r |
Pour les processus systèmes (non-interactifs). Ne devrait pas être
attribuée à un utilisateur.
|
| user_u |
user_r |
Pour les utilisateurs sans privilège. La correspondance par défaut. |
| staff_u |
staff_r, sysadm_r |
Pour les administrateurs systèmes qui se connectent également pour exécuter
des tâches d'utilisateur normal.
|
| sysadm_u |
sysadm_r |
Pour les administrateurs systèmes qui ne font que se connecter pour
exécuter des tâches administratives. Il est suggéré de ne pas utiliser
cette identité.
|
| root |
staff_r, sysadm_r |
Identité spéciale pour root. Les autres utilisateurs devraient plutôt
utiliser staff_u à la place.
|
Pour savoir comment utiliser semanage afin de configurer vos
correspondances, lisez notre Guide pratique
SELinux.
1.d. Les contextes SELinux
L'utilisation des trois modèles de sécurité précédents ensemble s'appelle un
contexte SELinux et se note sous la forme :
identité:rôle:type. Le contexte SELinux est la valeur la
plus importante dans la décision d'un accès.
Le contexte objet
Un ls -Z typique peut renvoyer un résultat semblable à celui-ci :
Exemple de code 4.1 : Exemple de ls -Z |
drwxr-xr-x root root system_u:object_r:bin_t bin
drwxr-xr-x root root system_u:object_r:boot_t boot
drwxr-xr-x root root system_u:object_r:device_t dev
drwxr-xr-x root root system_u:object_r:etc_t etc
|
Les trois premières colonnes décrivent les permissions, le nom d'utilisateur et
le nom de groupe, valeurs typiques de Linux. La quatrième colonne est le
contexte du fichier ou du répertoire. On donne le rôle générique
object_r aux objets. Des deux autres champs du contexte, on en déduit
que les fichiers ont l'identité système et qu'ils sont de types différents,
bin_t, boot_t, device_t, et etc_t.
Le contexte processus
Un ps ax -Z typique peut renvoyer ce genre de résultat :
Exemple de code 4.2 : Exemple de ps ax -Z |
PID CONTEXT COMMAND
1 system_u:system_r:init_t [init]
2 system_u:system_r:kernel_t [keventd]
3 system_u:system_r:kernel_t [ksoftirqd_CPU0]
4 system_u:system_r:kernel_t [kswapd]
5 system_u:system_r:kernel_t [bdflush]
6 system_u:system_r:kernel_t [kupdated]
706 system_u:system_r:syslogd_t [syslog-ng]
712 system_u:system_r:httpd_t [apache]
791 system_u:system_r:sshd_t [sshd]
814 system_u:system_r:crond_t [cron]
826 system_u:system_r:getty_t [agetty]
827 system_u:system_r:getty_t [agetty]
828 system_u:system_r:getty_t [agetty]
829 system_u:system_r:getty_t [agetty]
830 system_u:system_r:getty_t [agetty]
831 system_u:system_r:httpd_t [apache]
832 system_u:system_r:httpd_t [apache]
833 system_u:system_r:httpd_t [apache]
23093 system_u:system_r:sshd_t [sshd]
23095 user_u:user_r:user_t [bash]
23124 system_u:system_r:sshd_t [sshd]
23126 user_u:user_r:user_t [bash]
23198 system_u:system_r:sshd_t [sshd]
23204 user_u:user_r:user_t [bash]
23274 system_u:system_r:sshd_t [sshd]
23275 pebenito:staff_r:staff_t [bash]
23290 pebenito:staff_r:staff_t ps ax -Z
|
Dans cet exemple, le contexte de chaque processus est affiché en plus des
informations typiques. Si on examine cette liste, on remarque que tous les
processus du noyau et les démons tournent sous l'identité system_u et
avec le rôle system_r. Les domaines individuels dépendent du programme.
On note plusieurs utilisateurs connectés en SSH, sous l'identité générique
user_u. Enfin, un utilisateur, portant l'identité pebenito, est
connecté avec le rôle staff_r et se trouve dans le domaine
staff_t.
1.e. Les fichiers de politiques de SELinux
Les fichiers sources des politiques de SELinux ne sont plus installés sur le
système. Dans les répertoires /usr/share/selinux/{strict,targeted}, vous
trouverez une série de paquets de politiques et d'en-têtes afin de construire
des modules locaux. Les fichiers de politiques sont traités par m4, puis le
compilateur de politique checkmodule vérifie qu'il n'y ait pas d'erreur
de syntaxe avant de créer un module de politique. Ensuite, un paquet de
politique est créé avec la commande semodule_package en utilisant le
module de politique et les contextes de fichiers du module. La politique, une
fois empaquetée, peut alors être chargée dans un noyau SELinux en l'insérant
dans le magasin de modules.
*.pp
Les paquets de politiques pour cette politique. Ils doivent être insérés dans
le magasin de modules afin d'être chargés dans la politique. Dans le paquet, se
trouve un module de politique chargeable et éventuellement un fichier de
contexte de fichier.
include/
Les en-têtes de politiques pour cette politique.
1.f. Les versions de politiques binaires
Lors d'une compilation de politique, le fichier binaire résultant contient un
numéro de version. La première version qui a été introduite dans le noyau Linux
a été la version 15. Le numéro de version n'est en général incrémenté que
lorsqu'une nouvelle fonctionnalité ajoutée nécessite des changements de
structure de la politique compilée binaire. Par exemple, dans Linux 2.6.5, des
extensions concernant les politiques conditionnelles ont été ajoutées. Le
numéro de version des politiques résultantes a été incrémenté à 16.
Quelle est la version de politique de mon noyau ?
La version de politique d'un noyau peut se déterminer à l'aide des outils
sestatus ou policyvers. Les noyaux actuels peuvent charger une
politique d'une version antécédente, par exemple un noyau en version 17 peut
aussi charger les politiques en version 16. Cependant, cette compatibilité
pourra être supprimée à l'avenir.
Note :
L'infrastructure de gestion des politiques (libsemanage) créera et utilisera
automatiquement la bonne version de la politique. Aucune étape supplémentaire
n'est requise.
|
Versions de politiques
La table suivante indique l'historique des versions de politiques selon la
version de Linux 2.6.
| Version |
Description |
Versions de Linux |
| 12 |
SELinux « Vieille API » (obsolète) |
| 15 |
SELinux « nouvelle API » intégrée dans 2.6 |
2.6.0 - 2.6.4 |
| 16 |
Ajout d'extensions permettant les politiques conditionnelles |
2.6.5 |
| 17 |
Ajout du support IPv6 |
2.6.6 - 2.6.7 |
| 18 |
Amélioration du support des sockets Netlink |
2.6.8 - 2.6.11 |
| 19 |
Amélioration de la sécurité multi-niveaux |
2.6.12 - 2.6.13 |
| 20 |
Optimisation de la taille des tables de vecteurs d'accès |
2.6.14 - 2.6.18 |
| 21 |
Classes d'objets |
2.6.19 - 2.6.24 |
| 22 |
Fonctionnalités des politiques |
Depuis 2.6.25 |
1.g. Les extensions de politique conditionnelle
Les extensions de politique conditionnelle permettent l'activation et la
désactivation de règles à la volée, sans besoin de recharger une politique
modifiée. En utilisant les booléens et les expressions de politique, les règles
peuvent être appliquées conditionnellement.
Afficher les valeurs des booléens
L'état des booléens de la politique courante peut être déterminé de deux
façons. Premièrement, en utilisant sestatus :
Exemple de code 7.1 : Exemple de résultat de sestatus |
# sestatus
SELinux status: enabled
SELinuxfs mount: /selinux
Current mode: enforcing
Policy version: 17
Policy booleans:
user_ping inactive
|
La deuxième méthode est l'utilisation de getsebool, un outil simple qui
affiche l'état courant et l'état en attente (pending) des booléens.
Exemple de code 7.2 : Exemple de résultat de getsebool |
# getsebool -a
user_ping --> active: 0 pending: 0
|
Modifier un booléen
La valeur d'un booléen peut être inversée en utilisant la commande
togglesebool. On peut spécifier plusieurs booléens sur la ligne de
commande. La nouvelle valeur des booléens sera affichée en retour.
Exemple de code 7.3 : Exemple d'utilisation de togglesebool |
# togglesebool user_ping
user_ping: active
|
La valeur d'un booléen peut également être définie explicitement avec
setsebool.
Exemple de code 7.4 : Exemple d'utilisation de setsebool |
# setsebool user_ping 0
|
Pour définir la valeur d'un booléen et en faire sa valeur par défaut, utilisez
l'option -P.
Exemple de code 7.5 : Changer la valeur par défaut |
# setsebool -P user_ping 1
|
1.h. Les messages du noyau concernant la politique
Lorsque le système tourne, un programme ou un utilisateur peut tenter de faire
quelque chose qui viole la politique de sécurité. Si le système respecte la
politique (mode enforcing), l'accès sera refusé et un message sera
envoyé dans le journal du noyau. Si le système est en mode permissive,
l'accès sera autorisé mais il y aura tout de même un message envoyé au journal
du noyau.
Les messages de l'AVC
La plupart des messages du noyau provenant de SELinux viennent du cache de
vecteurs d'accès (AVC pour Access Vector Cache). Comprendre les refus d'accès
est important pour déterminer si une attaque est en cours ou bien si le
programme demande un accès non prévu. Voici un exemple de refus d'accès :
Exemple de code 8.1 : Exemple de message de l'AVC |
avc: denied { read write } for pid=3392 exe=/bin/mount dev=03:03 ino=65554
scontext=pebenito:sysadm_r:mount_t tcontext=system_u:object_r:tmp_t tclass=file
|
Bien que la plupart des messages de l'AVC soient des refus d'accès, il peut
aussi de temps en temps y avoir des messages d'audit à propos d'accès
autorisés :
Exemple de code 8.2 : Un autre exemple de message de l'AVC |
avc: granted { load_policy } for pid=3385 exe=/usr/sbin/load_policy
scontext=pebenito:sysadm_r:load_policy_t tcontext=system_u:object_r:security_t tclass=security
|
Dans ce cas-ci, la demande de charger la politique a été autorisée. C'est un
évènement de sécurité critique et est donc toujours audité. Un autre évènement
toujours audité est le passage du mode enforcing vers permissive
et inversement.
SELinux n'enregistrera pas les refus d'accès dans le journal s'il y en a trop à
la fois. Cela ne veut pas forcément dire qu'une attaque est en cours. Un
programme peut par exemple être en train d'exécuter un stat() sur les fichiers
présents dans /dev/. SELinux limite le nombre de messages afin de
minimiser les risques de saturation du journal :
Exemple de code 8.3 : Un dernier exemple de message de l'AVC |
AVC: 12 messages suppressed.
|
Si le comportement du programme est normal et que ces accès doivent bien être
refusés, la politique doit alors être modifiée afin de ne pas auditer ces
accès.
Autres messages du noyau
Exemple de code 8.4 : inode_doinit_with_dentry |
inode_doinit_with_dentry: context_to_sid(system_u:object_r:bar_t) returned 22 for dev=hda3 ino=517610
|
Cela signifie que le fichier se trouvant sur /dev/hda3 et ayant le numéro
d'inode 517610 a le contexte system_u:object_r:bar_t, qui est invalide. Les
objets qui ont un contexte invalide sont traités comme s'ils avaient le
contexte system_u:object_r:unlabeled_t.
1.i. Autopsie d'un refus d'accès
Les refus d'accès contiennent une quantité variable d'information, selon le
type d'accès.
Exemple de code 9.1 : Exemples de refus d'accès |
avc: denied { lock } for pid=28341 exe=/sbin/agetty path=/var/log/wtmp dev=03:03 ino=475406
scontext=system_u:system_r:getty_t tcontext=system_u:object_r:var_log_t tclass=file
avc: denied { create } for pid=20909 exe=/bin/ls scontext=pebenito:sysadm_r:mkinitrd_t
tcontext=pebenito:sysadm_r:mkinitrd_t tclass=unix_stream_socket
avc: denied { setuid } for pid=3170 exe=/usr/bin/ntpd capability=7
scontext=system_u:system_r:ntpd_t tcontext=system_u:system_r:ntpd_t tclass=capability
|
Les refus d'accès concernent généralement l'accès à un fichier. Pour mieux
comprendre, décortiquons ici le premier message :
| Extrait |
Explication |
| avc: denied |
SELinux a refusé l'accès. |
| { lock } |
La tentative d'accès est de type « lock » (verrou). |
| pid=28341 |
L'identifiant du processus est 28341. |
| exec=/sbin/agetty |
Le chemin complet du processus exécutable est /sbin/agetty. |
| path=/var/log/wtmp |
Le chemin complet de l'objet cible est /var/log/wtmp. Notez que le chemin
complet n'est pas toujours disponible.
|
| dev=03:03 |
L'objet cible réside sur le périphérique 03:03 (numéros majeur:mineur). Le
noyau 2.6 peut procéder à une résolution de nom, hda3 ici.
|
| ino=475406 |
Le numéro d'inode de la cible est 475406. |
| scontext=system_u:system_r:getty_t |
Le contexte du programme est system_u:system_r:getty_t. |
| tcontext=system_u:object_r:var_log_t |
Le contexte de l'objet cible est system_u:object_r:var_log_t. |
| tclass=file |
L'objet cible est un fichier normal. |
Tous les messages de l'AVC n'ont pas forcément tous ces champs, comme notamment
les deux exemples suivants. Les champs présents varient en fonction de la
classe de l'objet cible. Cependant, les champs les plus importants (le type
d'accès, les contextes source et cible et l'objet cible) seront toujours
affichés dans un message de l'AVC.
Comprendre le refus d'accès
Les messages de refus d'accès sont parfois trompeurs car ils peuvent être
envoyés pour plusieurs raisons. Pour comprendre ce qu'il se passe, il faut
connaitre le comportement du programme et savoir correctement interpréter les
messages de refus d'accès. La cible n'est pas forcément un fichier, mais peut
aussi être une socket réseau, une communication interprocessus, etc.
Dans l'exemple précédent, agetty se voit refuser le droit de poser un
verrou sur un fichier. Le type du fichier est var_log_t, ce qui implique que le
fichier cible se trouve dans /var/log/. Grâce aux informations
supplémentaires fournies par le champ path= du message de refus d'accès, on
confirme que le fichier est /var/log/wtmp. Si l'information du
chemin complet n'avait pas été disponible, on aurait pu trouver le fichier avec
le numéro d'inode. wtmp est un fichier qui contient des
informations à propos des utilisateurs actuellement connectés au système et
agetty gère justement les connexions depuis un tty. On peut donc en
conclure que c'est un comportement normal de la part d'agetty de vouloir
mettre à jour wtmp. Cependant, pourquoi cet accès est-il
refusé ? Y aurait-il une anomalie dans la politique qui empêcherait
agetty de mettre à jour wtmp ? En réalité, c'est
wtmp qui a un mauvais contexte. Cela devrait être
system_u:object_r:wtmp_t et non pas system_u:object_r:var_log_t.
Si cette demande d'accès n'avait pas été bien comprise, l'administrateur aurait
pu faire l'erreur d'autoriser les accès en lecture/écriture de getty_t vers les
fichiers var_log_t, ce qui aurait été incorrect, puisqu'agetty n'a
besoin que de modifier /var/log/wtmp. Cet exemple souligne à quel
point il est important de garder les contextes des fichiers dans un état
cohérent.
1.j. Références
Le site de SELinux et le fichier
README de SELinux.
[ << ]
[ < ]
[ Sommaire ]
[ > ]
[ >> ]
Ce document est protégé par la licence Creative
Commons : Paternité - Partage des Conditions Initiales à
l'Identique 2.5.
|