- SELinux añade control de acceso obligatorio al kernel Linux mediante etiquetas y políticas que van más allá de los permisos tradicionales DAC.
- Los contextos de seguridad (usuario, rol, tipo, nivel) y las políticas basadas en tipos permiten un control de acceso muy granular sobre procesos, archivos y puertos.
- Herramientas como getenforce, chcon, semanage, semodule y los booleanos facilitan la gestión práctica de SELinux en producción.
- Bien configurado, SELinux mitiga exploits de día cero y errores de configuración al limitar de forma estricta qué puede hacer cada servicio o aplicación.

SELinux puede sonar a tecnología reservada para frikis del kernel, pero en realidad es una de las piezas de seguridad más potentes que tenemos hoy en Linux. Si administras servidores, la seguridad en contenedores Docker, infraestructuras en la nube o incluso equipos de escritorio un poco sensibles, entender cómo funciona SELinux marca la diferencia entre un sistema simplemente «bien configurado» y un sistema difícil de tumbar incluso con vulnerabilidades de día cero.
Más allá de la fama de “complicado” que arrastra, SELinux ofrece un modelo muy lógico: define qué puede hacer cada proceso y con qué objetos puede hablar, y si algo se sale de ese guion, el kernel lo corta en seco. En vez de confiar ciegamente en que root se comportará o en que tus daemons no tendrán bugs, SELinux aplica un control de acceso obligatorio que se impone incluso al superusuario, usando etiquetas y políticas muy detalladas.
Qué es SELinux y qué problema resuelve
Security-Enhanced Linux (SELinux) es un módulo de seguridad del kernel Linux basado en LSM (Linux Security Modules). Fue desarrollado originalmente por la NSA en colaboración con Red Hat y otros actores, y desde la rama 2.6 del kernel forma parte oficial del núcleo. No es una aplicación aparte, sino una extensión del propio kernel que añade un sistema de control de acceso obligatorio (MAC) y control de acceso basado en roles (RBAC).
A diferencia del clásico control de acceso discrecional (DAC) de Unix —el de propietario, grupo, otros y permisos rwx—, en SELinux las decisiones de acceso no se basan en lo que decide el dueño del fichero, sino en una política global establecida por el administrador de seguridad. DAC sigue estando ahí y se aplica primero: si DAC niega, SELinux ni entra en juego; pero aunque DAC permita, SELinux puede seguir bloqueando la operación según su política.
La arquitectura SELinux separa claramente las piezas: por un lado, el código del kernel que toma las decisiones de seguridad, y por otro, los módulos de política que describen qué está permitido y qué no. Esta separación permite ajustar las reglas sin recompilar el kernel y racionaliza el número de componentes que pueden afectar a la seguridad del sistema.
SELinux ha sido adoptado de forma predeterminada en distribuciones como Fedora, Red Hat Enterprise Linux, CentOS, Scientific Linux, y también está muy integrado en sistemas como Android, donde se usa para confinar procesos del sistema y apps a dominios muy específicos. En el mundo BSD y GNU/Linux existen alternativas como AppArmor, TOMOYO o TrustedBSD (en macOS/FreeBSD), pero SELinux destaca por el nivel de granularidad que propone sobre todos los objetos del sistema.
De DAC a MAC: por qué el modelo clásico ya no basta
En un sistema Unix tradicional, el modelo Dominio-Objeto se gestiona mediante DAC: cada fichero o recurso tiene un propietario y sus permisos, y cualquier proceso que corra como ese usuario puede hacer lo que quiera con esos recursos. Esto significa que si un daemon corre como root, cualquier bug explotable puede abrir la puerta a controlar medio sistema con su contexto de root.
Ejemplos típicos: bases de datos cuyos ficheros de datos solo deberían manipularse a través del DBMS, pero que en realidad son legibles y modificables por procesos con UID root; o demonios críticos que se ejecutan con privilegios excesivos. Un fallo de programación, un desbordamiento de búfer o una mala validación de entrada puede convertir el servicio en una autopista hacia el sistema completo.
SELinux añade una capa de Mandatory Access Control (MAC) por encima de DAC. «Mandatory» significa que el control de acceso está definido centralmente por el administrador a través de políticas, y ni los usuarios ni los procesos pueden relajar esas reglas por su cuenta. El sistema operativo aplica esas políticas de forma obligatoria, evaluando cada operación relevante del kernel antes de permitirla.
El kernel, mediante los hooks de LSM, consulta a SELinux en cada llamada al sistema sensible (abrir ficheros, crear sockets, montar sistemas de ficheros, comunicarse a través de IPC, etc.). En cada punto de decisión, SELinux evalúa la operación basándose en la política cargada y en el contexto de seguridad del sujeto y del objeto. Si la política no otorga de forma explícita el permiso, la acción se deniega, independientemente de que el proceso sea root o no.
Modos de funcionamiento: enforcing, permissive y disabled
SELinux puede funcionar en tres estados operativos claramente diferenciados, que conviene conocer bien para no volverse loco en producción:
- Enforcing: SELinux está habilitado y aplica la política plenamente. Todas las acciones que no estén permitidas por las reglas se bloquean y se registran.
- Permissive: SELinux está activo, carga la política y etiqueta el sistema de ficheros, pero no bloquea las operaciones, solo las registra como si las hubiera denegado. Es ideal para depurar y ajustar políticas.
- Disabled: SELinux está desactivado. No se aplican políticas ni se realiza etiquetado. El sistema queda confiado al modelo DAC clásico.
Para cambios rápidos y temporales entre enforcing y permissive se utiliza el comando setenforce, donde el modo se indica como 0 (permissive) o 1 (enforcing). Es importante saber que este cambio es volátil: tras el siguiente reinicio, el modo volverá a lo definido en la configuración.
Si lo que necesitas es un cambio permanente, hay que editar el fichero /etc/selinux/config (o /etc/sysconfig/selinux en algunas distribuciones) y ajustar el valor de la directiva SELINUX=disabled|permissive|enforcing. Los cambios se aplicarán en el siguiente arranque y, en muchos casos, implicarán reetiquetar el sistema de ficheros.
Para comprobar el modo activo se pueden usar comandos como getenforce o sestatus. El primero devuelve simplemente Enforcing, Permissive o Disabled; el segundo ofrece un resumen más completo del estado de SELinux, políticas cargadas y módulos activos.
Contextos de seguridad y sistema de etiquetado
El corazón de SELinux es su sistema de etiquetas o contextos de seguridad. Cada archivo, proceso, puerto de red, socket, dispositivo, etc., tiene asociado un contexto que describe cómo puede ser usado. Ese contexto se compone de varios campos que juntos forman la vista que SELinux tiene de ese objeto.
El formato general de un contexto es usuario_u:rol_r:tipo_t:nivel, con algunos matices según la política (sobre todo si se usa MLS/MCS). Cada campo tiene un propósito concreto: el usuario SELinux, el rol, el tipo (también llamado dominio cuando se refiere a procesos) y el nivel de sensibilidad o categoría.
En la práctica real, el elemento más crítico es el tipo (el tercer campo), ya que la inmensa mayoría de las reglas de la política se formulan como relaciones entre tipos. Por ejemplo, permitir que procesos de tipo httpd_t accedan a ficheros etiquetados como httpd_sys_content_t, o que un dominio concreto se pueda comunicar con sockets etiquetados como http_port_t.
Estos contextos se almacenan como atributos extendidos del sistema de ficheros, por lo que es imprescindible usar filesystems que soporten xattrs (como ext4, XFS, etc.). En el caso de procesos, el contexto actual y otros contextos relacionados se exponen a través del pseudo-sistema de ficheros /proc/<pid>/attr/ en ficheros como current, exec, fscreate, prev, sockcreate o keycreate.
Ejemplos típicos de contextos en un sistema con política targeted serían:
- system_u:object_r:httpd_sys_content_t:s0 para contenido web servido por Apache o Nginx.
- system_u:object_r:home_user_t:s0 para directorios home de usuarios.
- system_u:system_r:httpd_t:s0 para el dominio de ejecución del propio servidor web.
Desgranando los componentes del contexto SELinux
Cuando te encuentras un contexto del estilo unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023, puede parecer un jeroglífico, pero cada pieza responde a un concepto concreto dentro de SELinux y su política de referencia.
El usuario SELinux (por convención con sufijo _u) no es lo mismo que el usuario Unix de /etc/passwd. SELinux mantiene su propia base de datos de usuarios y los mapea contra usuarios de Linux. Un usuario SELinux puede agrupar varios usuarios Unix, porque la idea es que la capa MAC sea independiente de DAC.
El rol SELinux (con sufijo _r) define qué tipos puede adoptar un usuario o dominio. Si se usa de forma estricta, hablamos de RBAC completo. En la mayoría de los objetos de fichero se usa el rol object_r, mientras que roles como system_r, user_r, staff_r o sysadm_r se aplican a procesos según el contexto de seguridad que deben asumir.
El tipo o dominio SELinux (sufijo _t) es, en la práctica, el elemento crucial. El tipo describe la clase de objeto o el dominio de ejecución de un proceso. La política especifica qué interacciones están permitidas entre tipos: qué dominios pueden leer, escribir, ejecutar o comunicarse con qué otros tipos de objetos.
El nivel y las categorías se utilizan cuando se habilitan políticas multinivel (MLS) o multicategoría (MCS). Las sensibilidades son jerárquicas (por ejemplo, s0, s1, etc.), mientras que las categorías no lo son (c0, c1, c2, …). En entornos muy críticos —por ejemplo, ciertas organizaciones gubernamentales— se usan para garantizar que solo se pueda leer hacia abajo y escribir hacia el mismo o hacia arriba, y para aislar datos según compartimentos muy concretos.
Políticas SELinux: targeted, strict, MLS/MCS y modularidad
La seguridad que aplica SELinux viene determinada por la política cargada. Una política es simplemente un conjunto enorme de reglas que describen qué dominios pueden hacer qué sobre qué tipos, además de otros elementos (transiciones, reglas de etiquetado, etc.). Las distribuciones suelen empaquetar políticas estándar para no obligarte a escribirlo todo desde cero.
La política más habitual es la llamada targeted. En este modo, solo algunos procesos concretos —principalmente servicios de red y demonios de alto riesgo como apache, nginx, dns, proxies, snmp, syslog, etc.— se ejecutan bajo dominios confinados. El resto de procesos de usuario corren en dominios «unconfined», donde se aplica esencialmente la seguridad estándar de Linux más un registro de ciertas acciones.
También existe la política strict, en la que prácticamente todos los procesos están confinados bajo alguna política. Es mucho más segura, pero también puede ser bastante más dolorosa de mantener si no se domina bien el modelo, porque cualquier desajuste de etiquetado o regla puede romper flujos de trabajo habituales.
Para entornos de máxima seguridad hay políticas MLS/MCS (Multi-Level / Multi-Category Security), que aprovechan sensibilidades y categorías para imponer controles aún más finos. Son las típicas de entornos militares o administrativos muy rígidos, y apenas se usan fuera de contextos específicos, ya que su complejidad operativa es alta.
Las políticas modernas se distribuyen de forma modular. En lugar de un único archivo de política monolítico, se usan módulos independientes para servicios concretos, lo que facilita muchísimo la gestión y la actualización. Los módulos se administran con herramientas como semodule y semanage module, que permiten instalar, eliminar, habilitar o deshabilitar partes de la política sin recompilarlo todo cada vez.
Cómo decide SELinux: sujetos, objetos, clases y permisos
Cuando un proceso (el sujeto) intenta acceder a un recurso (el objeto), SELinux formula conceptualmente la pregunta: «¿Puede un dominio de tipo X realizar la operación Y sobre un objeto de tipo Z perteneciente a la clase C?». La respuesta se busca en la política mediante las reglas definidas.
En las políticas basadas en Type Enforcement (TE), que son las que usa la mayoría de distribuciones y Android, cada objeto pertenece a una clase (file, dir, fifo_file, tcp_socket, process, etc.) y la política define qué permisos son posibles para cada clase: read, write, execute, bind, connect, getattr, open, y un largo etcétera.
Las reglas TE se expresan de forma muy directa. Un ejemplo básico podría ser:
allow httpd_t http_port_t:tcp_socket name_bind;
Con esta regla, la política indica que los procesos en el dominio httpd_t pueden realizar la operación name_bind sobre sockets TCP etiquetados como http_port_t. El enfoque se centra en los tipos y las clases de objetos, no en rutas concretas, lo que evita sorpresas cuando se mueven ficheros o se cambian estructuras de directorios.
En Android, por ejemplo, se utilizan atributos SELinux para agrupar tipos bajo etiquetas más generales como appdomain, de modo que una sola regla pueda aplicar a varios dominios (untrusted_app, isolated_app, etc.) sin repetir definiciones. También se usan macros como rw_file_perms para agrupar varios permisos de fichero habituales y reducir errores por despistes.
Estados internos, AVC y registro de denegaciones
Cuando se produce una solicitud de acceso, SELinux primero consulta la Access Vector Cache (AVC), una caché en la que se guardan decisiones recientes de acceso para acelerar el proceso. Si la decisión ya está en la caché, se usa directamente; si no, se pregunta al servidor de seguridad (la parte interna de SELinux en el kernel), que evalúa la operación en función de la política y del contexto de sujeto y objeto.
Si no hay una regla que permita explícitamente la acción pedida, la decisión por defecto es denegar. Esta filosofía de «denegación por defecto» es uno de los pilares de SELinux y lo que le da su robustez frente a errores de configuración permisivos.
Cuando se produce una denegación en modo enforcing, el kernel registra un mensaje de tipo avc: denied en los logs del sistema. Dependiendo de la distribución, puede aparecer en /var/log/audit/audit.log, en /var/log/messages o ser capturado por el daemon auditd. Esos mensajes incluyen el contexto del proceso (scontext), el contexto del objeto (tcontext), la clase, la operación pedida y otros datos muy útiles para depurar.
En modo permissive, la operación se permite pero igualmente se genera el evento avc: denied, marcado con permissive=1. Esto es oro puro para construir y ajustar políticas, porque permite ver qué rompería el sistema si se pusiera enforcing sin dejar de operar normalmente.
Integración de SELinux en distribuciones y Android
En el ecosistema Linux de servidor y escritorio, SELinux se encuentra habilitado por defecto en Fedora, RHEL, CentOS y derivados. Debian y Ubuntu proporcionan soporte completo en sus kernels y paquetes, aunque la activación suele ser opcional y requiere instalar paquetes como selinux-basics, selinux-policy-default y auditd, seguido de un reetiquetado global con fixfiles relabel.
Android incorporó SELinux inicialmente en la versión 4.3 en modo permissive, pasó a usarlo parcialmente en 4.4 (solo para dominios críticos como installd, netd, vold, zygote) y desde Android 5.0 está completamente integrado. La política de Android se centra en aislar apps, servicios del sistema y procesos sensibles usando tipos y atributos, con el objetivo de restringir al máximo el impacto de un compromiso en cualquiera de esos componentes.
En Android se usan conceptos como el tipo untrusted_app para procesos de aplicaciones comunes, atributos appdomain para agrupar dominios de apps, y categorías MLS/MCS para aislar datos entre apps y entre usuarios físicos. Todo esto se combina para que una aplicación comprometida no pueda salir de su caja de arena aunque obtenga permisos de usuario muy amplios.
Importante: Android simplifica el modelo de SELinux ignorando usuarios, roles y sensibilidades avanzadas. Solo hay un usuario SELinux (u), dos roles básicos (r para sujetos y object_r para objetos) y la sensibilidad siempre es s0. Las categorías son las que marcan la diferencia para el aislamiento de datos.
Comparación con AppArmor y otros LSM
En muchas discusiones aparece inevitablemente la comparación entre SELinux y AppArmor, ya que ambos son módulos de seguridad de Linux y ofrecen MAC. Sin embargo, el enfoque que siguen es bastante distinto y compensa entenderlo antes de elegir uno u otro para tu entorno.
SELinux define una política centrada en los objetos y sus tipos: todo objeto del sistema (ficheros, procesos, sockets, puertos, dispositivos, IPC, etc.) recibe una etiqueta. Las decisiones de acceso se toman basándose en los contextos de sujeto y objeto, sin depender de la ruta exacta de los ficheros. Esto hace que el sistema sea más estable frente a cambios de estructura de directorios o a vistas alternativas del sistema de ficheros (chroot, contenedores, montajes bind, etc.).
AppArmor, por su parte, aplica una política centrada en la tarea y basada en rutas. Define perfiles para cada programa indicando a qué rutas de ficheros, puertos, etc., puede acceder y con qué permisos. Es más intuitivo de configurar y suele ser más amigable para administradores que no quieren hacerse expertos en SELinux, pero su control es algo menos fino y más dependiente de la estructura del sistema de ficheros.
Ambos comparten el principio de denegación por defecto, pero lo aplican de forma diferente: AppArmor lo hace sobre las tareas que cubre con perfiles, mientras que SELinux, cuando está en modos estricto, lo extiende a todo el sistema y a todos los objetos etiquetados. La consecuencia es que SELinux suele ofrecer un nivel de confinamiento más profundo, al precio de una política más extensa y compleja.
Herramientas prácticas para gestionar SELinux
Trabajar con SELinux se apoya en una serie de herramientas de línea de comandos que simplifican la gestión del modo, el etiquetado, los booleanos y los módulos de política. Aunque parezca un arsenal abrumador al principio, unas pocas utilidades cubren la mayoría de tareas del día a día.
Para ver el contexto de seguridad de archivos y procesos se puede usar la opción -Z en comandos habituales como ls, ps o id. Por ejemplo, ls -Z mostrará, además de permisos DAC, el contexto SELinux de cada fichero, permitiendo comprobar rápidamente si el etiquetado es el esperado.
El comando chcon («change context») permite modificar manualmente el contexto completo de un fichero o solo partes concretas (rol, tipo, rango) usando opciones como -r, -t o -l. Es útil para correcciones puntuales, pero conviene recordar que sus cambios pueden perderse si se vuelve a aplicar el etiquetado según la política con herramientas como restorecon o fixfiles.
La herramienta semanage es la navaja suiza de la administración de política en tiempo de ejecución. Con distintos subcomandos se pueden gestionar contextos de fichero persistentes (fcontext), mapeos de login entre usuarios Linux y usuarios SELinux (login), usuarios SELinux y sus roles (user), puertos etiquetados (port), booleanos, e incluso módulos de política. Todo ello sin necesidad de recompilar desde los fuentes de la política completa.
Para revisar y cambiar el estado de los booleanos —pequeños interruptores que habilitan o deshabilitan bloques de reglas dentro de la política— se utilizan getsebool y setsebool, además del propio semanage boolean. Un booleano típico es httpd_enable_homedirs, que cuando está activo permite que el servidor web acceda a directorios home de usuarios (útil para ~usuario/public_html/).
Finalmente, comandos como fixfiles permiten forzar un reetiquetado completo del sistema de ficheros según las reglas definidas, y semodule se ocupa de instalar, listar, habilitar o deshabilitar módulos de política (.pp) empaquetados y distribuidos por la política de referencia o por el administrador.
Creación y ajuste de políticas personalizadas
Cuando una aplicación no dispone de módulo SELinux propio o necesitas un confinamiento más específico, toca meterse en harina y crear políticas personalizadas. Puede sonar duro, pero el flujo de trabajo está bastante bien definido si se siguen ciertos pasos con cabeza.
Lo primero es asegurarse de que los objetos relevantes (ejecutables, directorios de datos, sockets, etc.) están etiquetados con tipos adecuados. Para ello se pueden definir reglas de contexto de fichero con semanage fcontext y aplicarlas con restorecon, usando expresiones regulares para abarcar árboles de directorios completos.
Después se suele poner el sistema en modo permissive para esa máquina (o, en algunos casos, para un dominio concreto) y dejar que la aplicación funcione de forma normal mientras SELinux registra todas las denegaciones avc: denied que se habrían producido. Esos logs, habitualmente analizados con herramientas como audit2allow, sirven para extraer reglas candidatas.
Las políticas de referencia se estructuran habitualmente en tres ficheros por aplicación: un archivo .te con las reglas TE (allow, type, domain_type, etc.), un .fc con las reglas de contexto de ficheros y un .if con interfaces públicas que otros módulos pueden reutilizar. Todo ello se compila en módulos .pp mediante make y se carga con semodule.
Es fundamental no fiarse ciegamente de todo lo que propone audit2allow: tiende a ser más permisivo de lo estrictamente necesario. Lo ideal es revisar a mano las reglas sugeridas, crear nuevos tipos donde convenga para separar datos sensibles de datos irrelevantes y, cuando una operación denegada no sea crítica, plantearse usar reglas dontaudit para dejar de registrar ruido sin conceder permisos.
En entornos como SUSE Linux Micro o Android se proporcionan herramientas adicionales (por ejemplo, Udica para generar políticas de contenedores a partir de descripciones JSON) que automatizan parte del proceso adaptando rápidamente la política a contenedores concretos sin tener que aprender todo el lenguaje de políticas desde cero.
Todo este ecosistema de contextos, políticas modulares, booleanos y herramientas de gestión hace que SELinux sea una plataforma de seguridad extremadamente robusta y flexible. Con algo de inversión inicial en aprendizaje, permite limitar de forma muy precisa qué puede hacer cada servicio o aplicación, endureciendo drásticamente la superficie de ataque del sistema frente a exploits, malware y errores humanos.
Tabla de Contenidos
- Qué es SELinux y qué problema resuelve
- De DAC a MAC: por qué el modelo clásico ya no basta
- Modos de funcionamiento: enforcing, permissive y disabled
- Contextos de seguridad y sistema de etiquetado
- Desgranando los componentes del contexto SELinux
- Políticas SELinux: targeted, strict, MLS/MCS y modularidad
- Cómo decide SELinux: sujetos, objetos, clases y permisos
- Estados internos, AVC y registro de denegaciones
- Integración de SELinux en distribuciones y Android
- Comparación con AppArmor y otros LSM
- Herramientas prácticas para gestionar SELinux
- Creación y ajuste de políticas personalizadas