Product SiteDocumentation Site

14.5. Introducción a SELinux

14.5.1. Principios

SELinux (Linux con seguridad mejorada: «Security Enhanced Linux») es un sistema de control obligatorio de acceso («Mandatory Access Control») basado en la interfaz LSM (módulos de seguridad de Linux: «Linux Security Modules»). En la práctica, el núcleo pregunta a SELinux antes de cada llamada al sistema para saber si un proceso está autorizado a realizar dicha operación.
SELinux utiliza una serie de reglas — conocidas en conjunto como una política («policy») — para autorizar o denegar operaciones. Estas reglas son difíciles de crear. Afortunadamente se proporcionan dos políticas estándar (targeted, dirigida, y strict, estricta) para evitar gran parte del trabajo de configuración.
Con SELinux, la gestión de permisos es completamente distinta a la de los sistemas Unix tradicionales. Los permisos de un proceso dependen de su contexto de seguridad. El contexto está definido por la identidad del usuario que lanza el proceso y el rol y el dominio que el usuario tenía en ese momento. Los permisos realmente dependen del dominio, pero los roles controlan la transición entre dominios. Por último, las transiciones posibles entre roles dependen de la identidad.
Contextos de seguridad y usuarios Unix

Figura 14.3. Contextos de seguridad y usuarios Unix

En la práctica, a un usuario se le asigna un contexto de seguridad predeterminado al iniciar sesión (dependiendo de los roles que pueda adoptar). Esto define el dominio actual y, por lo tanto, el dominio de todos los procesos hijos que lance. Si desea cambiar el rol actual y su dominio asociado, debe ejecutar newrole -r rol_r -t dominio_t (habitualmente se permite un único dominio para un rol determinado por lo que puede omitir el parámetro -t). Este programa lo autenticará pidiéndole que ingrese su contraseña. Esta característica impide que los programas cambien de rol de forma automática. Estos cambios sólo pueden ocurrir si se permiten explícitamente en la política de seguridad de SELinux.
Obviamente los permisos no se aplican a todos los objetos (archivos, directorios, zócalos, dispositivos, etc.). Pueden variar de objeto a objeto. Para conseguir esto, cada objeto está asociado a un tipo (esta operación se conoce como etiquetado). Por ello se expresan los permisos de los dominios como conjuntos de operaciones permitidas o denegadas sobre estos tipos (e indirectamente sobre todos los objetos que estan etiquetados con dicho tipo).
De forma predeterminada, los programas heredan el dominio del usuario que los ejecuta, pero las políticas estándar de SELinux esperan que muchos programas importantes se ejecuten en dominios dedicados. Para conseguir esto, se etiquetan dichos ejecutables con un tipo dedicado (por ejemplo, se etiqueta ssh con ssh_exec_t y, cuando inicia el programa, automáticamente cambia al dominio ssh_t). Este mecanismo de transición automática de dominios permite otorgar exclusivamente los permisos que requiere cada programa. Es un principio fundamental de SELinux.
Transiciones automáticas entre dominios

Figura 14.4. Transiciones automáticas entre dominios

14.5.2. Configuración de SELinux

Todos los núcleos estándar que Debian proporciona incluyen compatibilidad con SELinux. Todas las herramientas básicas Unix son compatibles con SELinux sin ninguna modificación. Por lo tanto, es relativamente sencillo habilitar SELinux.
La orden apt install selinux-basics selinux-policy-default instalará automáticamente todos los paquetes necesarios para configurar un sistema SELinux.
El paquete selinux-policy-default contiene un conjunto de reglas estándar. De forma predeterminada, esta política sólo restringe el acceso a algunos servicios expuestos ampliamente. Las sesiones de usuario no están restringidas y, por lo tanto, es improbable que SELinux bloquee una operación legítima de un usuario. Sin embargo, mejora la seguridad de los servicios del sistema que estén ejecutando en la máquina. Para establecer una política equivalente a las reglas «estrictas» antiguas debe deshabilitar el módulo unconfined (detallamos la gestión de módulos más adelante en esta sección).
Después de instalar una política, debe etiquetar todos los archivos disponibles (lo que quiere decir asignarles un tipo). Debe iniciar esta operación manualmente con fixfiles relabel.
Ahora el sistema SELinux está listo. Para habilitarlo debe añadir el parámetro selinux=1 security=selinux al núcleo Linux. El parámetro audit=1 habilita los registros de SELinux que graban todas las operaciones denegadas. Por último, el parámetro enforcing=1 hace que se apliquen las reglas: sin él, SELinux trabaja en el modo predeterminado permissive (permisivo) en el que las acciones prohibidas son registradas pero son ejecutadas de todas formas. Por lo tanto, debe modificar el archivo de configuración del gestor de arranque GRUB para añadir los parámetros que desee. Una forma sencilla de hacerlo es modificar la variable GRUB_CMDLINE_LINUX en el archivo /etc/default/grub y ejecutar update-grub. SELinux estará activo al reiniciar.
Es importante saber que el script selinux-activate automatiza todas estas operaciones y fuerza el etiquetado de archivos en el siguiente reinicio (lo que evita que se creen nuevos archivos sin etiquetar cuando SELinux aún no esta activo mientras se realiza el etiquetado).

14.5.3. Gestión de un sistema SELinux

La política SELinux consiste en un conjunto de reglas modular, y su instalación detecta y habilita automáticamente todos los módulos necesarios en función de los servicios que se encuentren instalados. El sistema, por lo tanto, se encuentra operativo de forma inmediata. Sin embargo, cuando instale un servicio después de haber instalado la política SELinux deberá habilitar el módulo correspondiente manualmente. Para ello existe el programa semodule. Lo que es más, debería tener la capacidad de definir los roles que cada usuario puede adoptar, lo que puede realizar con el programa semanage.
Puede utilizar estos dos programas para modificar la configuración actual de SELinux, almacenada en /etc/selinux/default/. A diferencia de otros archivos de configuración que puede encontrar en /etc/, no debe modificar estos archivos manualmente. Debe utilizar los programas diseñados para este propósito.

14.5.3.1. Gestión de módulos SELinux

Los módulos SELinux disponibles se almacenan en el directorio /usr/share/selinux/default/. Para habilitar uno de estos módulos en la configuración actual debe ejecutar semodule -i módulo.pp.bz2. La extensión pp.bz2 significa paquete de política («policy package») comprimido mediante bzip2.
Puede eliminar un módulo de la configuración actual con semodule -r módulo. Por último, semodule -l enumera los módulos instalados actualmente. También imprime los números de versión correspondientes. Los módulos puden ser activados selectivamente con semodule -e y desactivados mediante semodule -d.
# semodule -i /usr/share/selinux/default/abrt.pp.bz2
# semodule -l
abrt    1.5.0   Disabled
accountsd       1.1.0   
acct    1.6.0   
[...]
# semodule -e abrt
# semodule -d accountsd
# semodule -l
abrt    1.5.0
accountsd       1.1.0   Disabled
acct    1.6.0   
[...]
# semodule -r abrt
# semodule -l
accountsd       1.1.0   Disabled
acct    1.6.0   
[...]
semodule carga inmediatamente la nueva configuración a menos que utilice la opción -n. De forma predeterminada, el programa actúa sobre la configuración actual (indicada por la variable SELINUXTYPE en el archivo /etc/selinux/config), pero también puede modificar una distinta especificándola con la opción -s.

14.5.3.2. Gestión de identidades

Cada vez que un usuario inicia sesión, se le asigna una identidad SELinux. Esta identidad determina los roles que puede adoptar. Puede configurar estas correspondencias (entre el usuario y la identidad y entre la identidad y los roles) con el programa semanage.
Es muy recomenable que lea la página de manual semanage(8), incluso cuando la sintaxis del programa tienda a ser similar para todos los conceptos que gestiona. Encontrará muchas opciones comunes a todas las subórdenes: -a para agregar, -d para borrar, -m para modificar, -l para enumerar y -t para indicar un tipo (o dominio).
semanage login -l enumera las correspondencias actuales entre identificadores de usuarios y entidades SELinux. Los usuarios que no aparecen explícitamente poseen la identidad predeterminada, que corresponde al elemento __default__. Si ejecuta semanage login -a -s user_u usuario, asociará la identidad user_u con el usuario dado. Por último, semanage login -d usuario elimina la asociación asignada al usuario.
# semanage login -a -s user_u rhertzog
# semanage login -l

Login Name           SELinux User         MLS/MCS Range        Service

__default__          unconfined_u         SystemLow-SystemHigh *
rhertzog             user_u               SystemLow            *
root                 unconfined_u         SystemLow-SystemHigh *
system_u             system_u             SystemLow-SystemHigh *
# semanage login -d rhertzog
semanage user -l enumera las asociaciones entre las identidades de usuario de SELinux y los roles permitidos. Agregar una nueva identidad requiere definir tanto sus roles correspondientes como un prefijo de etiquetado que se utiliza para asignar un tipo a los archivos personales (/home/usuario/*). Debe elegir el prefijo entre user, staff y sysadm. El prefijo «staff» hace que los archivos sean del tipo «staff_home_dir_t». Para crear una nueva identidad de usuario SELinux, ejecute semanage user -a -R roles -P prefijo identidad. Puede eliminar una identidad de usuario SELinux ejecutando semanage user -d identidad.
# semanage user -a -R 'staff_r user_r' -P staff test_u
# semanage user -l

                Labeling   MLS/       MLS/                          
SELinux User    Prefix     MCS Level  MCS Range             SELinux Roles

root            sysadm     SystemLow  SystemLow-SystemHigh  staff_r sysadm_r system_r
staff_u         staff      SystemLow  SystemLow-SystemHigh  staff_r sysadm_r
sysadm_u        sysadm     SystemLow  SystemLow-SystemHigh  sysadm_r
system_u        user       SystemLow  SystemLow-SystemHigh  system_r
test_u          staff      SystemLow  SystemLow             staff_r user_r
unconfined_u    unconfined SystemLow  SystemLow-SystemHigh  system_r unconfined_r
user_u          user       SystemLow  SystemLow             user_r
# semanage user -d test_u

14.5.3.3. Gestión de contextos de archivos, puertos y valores booleanos

Cada módulo de SELinux proporciona un conjunto de reglas de etiquetado de archivos, pero también es posible crear reglas de etiquetado personalizadas para adaptarse a algún caso específico. Por ejemplo, si desea que el servidor web sea capaz de leer archivos en el directorio /srv/www/, podría ejecutar semanage fcontext -a -t httpd_sys_content_t "/srv/www(/.*)?" seguido de restorecon -R /srv/www/. La primera ejecución registra las nuevas reglas de etiquetado, mientras que la segunda hace que se reinicialicen los tipos de archivo según las reglas de etiquetado actuales.
De forma similar, se etiquetan los puertos TCP/UDP de forma que asegure que únicamente los demonios correspondientes puedan escuchar en ellos. Por ejemplo, si desea que el servidor web pueda escuchar en el puerto 8080, deberá ejecutar semanage port -m -t http_port_t -p tcp 8080.
Algunos módulos de SELinux exportan opciones booleanas que puede ajustar para alterar el comportamiento de las reglas predeterminadas. Puede utilizar la herramienta getsebool para inspeccionar estas opciones (getsebool opcion_booleana muestra una opción concreta, mientras que getsebool -a muestra todas). La orden setsebool opción_booleana valor cambia el valor de una opción booleana. La opción -P hace que el cambio sea permanente, es decir que el nuevo valor se convierte en el predeterminado y se mantiene después de reiniciar el equipo. El ejemplo a continuación permite a los servidores web acceso a los directorios personales (esto es útil cuando los usuarios tienen sitios web personales en ~/public_html/).
# getsebool httpd_enable_homedirs
httpd_enable_homedirs --> off
# setsebool -P httpd_enable_homedirs on
# getsebool httpd_enable_homedirs 
httpd_enable_homedirs --> on

14.5.4. Adaptación de las reglas

Puesto que la política SELinux es modular, puede ser interesante desarrollar nuevos módulos para aplicaciones (posiblemente propias) que carezcan de uno. Estos nuevos módulos completarán la política de referencia.
Para crear nuevos módulos, necesitará los paquetes selinux-policy-dev y selinux-policy-doc. Este último contiene la documentación de las reglas estándar (/usr/share/doc/selinux-policy-doc/html/) y los archivos de ejemplo que puede utilizar como plantillas para crear nuevo módulos. Instale estos módulos y estúdielos detenidamente:
$ cp /usr/share/doc/selinux-policy-doc/Makefile.example Makefile
$ cp /usr/share/doc/selinux-policy-doc/example.fc ./
$ cp /usr/share/doc/selinux-policy-doc/example.if ./
$ cp /usr/share/doc/selinux-policy-doc/example.te ./
El archivo .te es el más importante. Define las reglas. El archivo .fc define los «contextos de archivo», es decir los tipos asignados a los archivos relacionados con este módulo. Los datos del archivo .fc se utilizan durante el paso de etiquetado de archivos. Por último, el archivo .if define la interfaz del módulo: es una serie de «funciones públicas» que otros módulos pueden utilizar para interactuar con el módulo que se está creando.

14.5.4.1. Creación de un archivo .fc

Leer el ejemplo a continuación debería ser suficiente para entender la estructura de este tipo de archivos. Puede utilizar expresiones regulares para asignar el mismo contexto de seguridad a múltiples archivos, o incluso a un árbol de directorios completo.

Ejemplo 14.2. Archivo example.fc

# El ejecutable myapp tendrá:
# etiqueta: system_u:object_r:myapp_exec_t
# Sensibilidad MLS: s0
# Categorías MCS: <none>

/usr/sbin/myapp         --      gen_context(system_u:object_r:myapp_exec_t,s0)

14.5.4.2. Creación de un archivo .if

En el ejemplo a continuación, la primera interfaz («myapp_domtrans») controla quién puede utilizar la aplicación. La segunda («myapp_read_log») otorga permisos de escritura a los archivos de registro de la aplicación.
Cada interfaz debe generar un conjunto de reglas válido que pueda ser integrado en un archivo .te. Por lo tanto, debe declarar todos los tipos que utilizará (con el macro gen_require) y utilizar directivas estándar para otorgar permisos. Sepa que puede utilizar interfaces proporcionadas por otros módulos. La siguiente sección dará más explicaciones sobre cómo expresar estos permisos.

Ejemplo 14.3. Archivo ejemplo.if

## <summary>Política de ejemplo de Myapp</summary>
## <desc>
##      <p>
##              Texto más descriptivo de myapp. La etiqueta <desc>
##              también puede utilizar etiquetas HTML <p>,
##              <ul>, and <ol> para dar formato.
##      </p>
##      <p>
##              Esta política es compatible con las siguientes 
##              funcionalidades de myapp:
##              <ul>
##              <li>Funcionalidad A</li>
##              <li>Funcionalidad B</li>
##              <li>Funcionalidad C</li>
##              </ul>
##      </p>
## </desc>
#

########################################
## <summary>
##      Ejecutar una transición de dominio para ejecutar myapp.
## </summary>
## <param name="domain">
##      Dominio permitido para la transición
## </param>
#
interface(`myapp_domtrans',`
        gen_require(`
                type myapp_t, myapp_exec_t;
        ')

        domtrans_pattern($1,myapp_exec_t,myapp_t)
')

########################################
## <summary>
##      Leer archivos de registro de myapp.
## </summary>
## <param name="domain">
##      Dominio al que se le permite leer archivos de registro.
## </param>
#
interface(`myapp_read_log',`
        gen_require(`
                type myapp_log_t;
        ')

        logging_search_logs($1)
        allow $1 myapp_log_t:file r_file_perms;
')

14.5.4.3. Escritura de un archivo .te

Revise el archivo example.te:
policy_module(myapp,1.0.0) 1

########################################
#
# Declaraciones
#

type myapp_t; 2
type myapp_exec_t;
domain_type(myapp_t)
domain_entry_file(myapp_t, myapp_exec_t) 3

type myapp_log_t;
logging_log_file(myapp_log_t) 4

type myapp_tmp_t;
files_tmp_file(myapp_tmp_t)

########################################
#
# Política local de Myapp
#

allow myapp_t myapp_log_t:file { read_file_perms append_file_perms }; 5

allow myapp_t myapp_tmp_t:file manage_file_perms;
files_tmp_filetrans(myapp_t,myapp_tmp_t,file)

1

El módulo debe ser identificado por su nombre y número de versión. Esta directiva es obligatoria.

2

Si el módulo introduce tipos nuevos, debe declararlos con directivas como las siguientes. No dude en crear tantos tipos como necesite en lugar de otorgar demasiados permisos inútiles.

3

Dichas interfaces definen el tipo myapp_t como un dominio de proceso que cualquier ejecutable con la etiqueta myapp_exec_t debería utilizar. Implícitamente, esto agrega un atributo exec_type en estos objetos, lo que a su vez permite a otros módulos otorgar permisos para ejecutar dichos programas: por ejemplo, el módulo userdomain permite que los ejecuten los proceso con dominios user_t, staff_t y sysadm_t. Los dominios de otras aplicaciones confinadas no tendrán los permisos para ejecutarlos a menos que las reglas les otorguen permisos similares (este es el caso, por ejemplo, de dpkg con su dominio dpkg_t).

4

logging_log_file es una interfaz provista por la política de referencia. Indica que los archivos etiquetados con el tipo dado son archivos de registro que deben gozar de los beneficios de las reglas asociadas (por ejemplo, otorgando permisos a logrotate para que los pueda manipular).

5

La directiva allow es la directiva base para autorizar una operación. El primer parámetro es el dominio de proceso al que se le permite ejecutar la operación. El segundo define el objeto que puede manipular un proceso del dominio anterior. Este parámetro debe estar en el formato «tipo:clase», en el que tipo es el tipo SELinux y clase describe la naturaleza del objeto (archivo, directorio, zócalo, tubería, etc.). Finalmente, el último parámetro describe los permisos (las operaciones permitidas).
Los permisos están definidos como el conjunto de operaciones permitidas y siguen la siguiente plantilla: { operación1 operación2 }. Sin embargo, también puede utilizar macros que representan los permisos más útiles. El archivo /usr/share/selinux/devel/include/support/obj_perm_sets.spt los enumera.
La siguiente página web provee una lista relativamente exhaustiva de las clases de objetos y los permisos que puede otorgar.
Ahora sólo debe encontrar el conjunto mínimo de reglas necesario para asegurar que la aplicación o servicio objetivo funcione correctamente. Para lograrlo, debería tener buen conocimiento de cómo funciona la aplicación y qué tipo de datos genera o administra.
Sin embargo, es posible un enfoque empírico. Una vez que se etiquetaron correctamente los objetos relevantes, puede utilizar la aplicación en modo permisivo: las operaciones que hubiesen estado bloqueadas son registradas pero ejecutarán correctamente. Si analiza los registros, ahora puede identificar las operaciones a permitir. A continuación encontrará un ejemplo de elemento en dicho registro:
avc:  denied  { read write } for  pid=1876 comm="syslogd" name="xconsole" dev=tmpfs ino=5510 scontext=system_u:system_r:syslogd_t:s0 tcontext=system_u:object_r:device_t:s0 tclass=fifo_file permissive=1
Para entender mejor este mensaje, estudiémoslo parte por parte.

Tabla 14.1. Análisis de una traza SELinux

MensajeDescripción
avc: deniedSe denegó una operación.
{ read write }Esta operación necesita los permisos read y write.
pid=1876El proceso con PID 1876 ejecutó la operación (o intentó hacerlo).
comm="syslogd"Este proceso era una instancia del programa syslogd.
name="xconsole"El objeto de destino se llamaba xconsole. En ciertos casos también se puede tener una variable «path» con una ruta completa.
dev=tmpfsEl dispositivo que alberga el objeto destino es un tmpfs (sistema de archivos en memoria). Para un disco real, podría ver la partición que alberga el objeto (por ejemplo: «sda3»).
ino=5510El objeto está identificado por el número de inodo 5510.
scontext=system_u:system_r:syslogd_t:s0Este es el contexto de seguridad del proceso que ejecutó la operación.
tcontext=system_u:object_r:device_t:s0Este es el contexto de seguridad del objeto destino.
tclass=fifo_fileEl objeto destino es un archivo FIFO.
Observando esta entrada de registro, es posible crear una regla que permitiría esta operación. Por ejemplo: allow syslogd_t device_t:fifo_file { read write }. Se puede automatizar este proceso, que es exactamente lo que ofrece el paquete audit2allow (del paquete policycoreutils. Este enfoque sólo es útil si ya están etiquetados correctamente los muchos objetos que deben ser confinados. En cualquier caso, debe revisar cuidadosamente las reglas generadas y validarlas según su conocimiento de la aplicación. En efecto, este enfoque tiende a otorgar más permisos de los que son realmente necesarios. La solución apropiada generalmente es crear nuevos tipos y otorgar los permisos sólo sobre dichos tipos. También puede suceder que denegar una operación no es fatal para la aplicación, en cuyo caso podría ser mejor simplemente agregar una regla «dontaudit» para evitar que sea registrada a pesar de que sea denegada.

14.5.4.4. Compilación de los archivos

Una vez que los 3 archivos (ejemplo.if, ejemplo.fc y ejemplo.te) está a la altura de sus expectativas de las nuevas reglas, simplemente ejecute make NAME=devel para generar un módulo en el archivo ejemplo.pp (puede cargarlo inmediatamente con semodule -i ejemplo.pp). Si define varios módulos, make creará todos los archivos .pp correspondientes.