Saltar al contenido

SELinux y AppArmor: control de acceso obligatorio sin misterio

    SELinux y AppArmor:

    control de acceso obligatorio sin misterio

    Linux avanzado  |  unfantasmaenelsistema.com

    Casi todo el que administra Linux se ha topado alguna vez con un «Permission denied» imposible: el usuario es el correcto, los permisos del fichero son 644, el propietario es el que toca, y aun así el servicio se niega a arrancar. Cuando `ls -l` e `id` dicen que todo está bien pero el sistema sigue diciendo que no, la explicación casi siempre es la misma: hay una segunda capa de control de acceso por encima de la clásica, y esa capa es SELinux o AppArmor.

    Estas dos tecnologías implementan lo que se llama control de acceso obligatorio (MAC). En este artículo vamos a quitarles el aura de «cosa que se desactiva en el primer paso de cualquier tutorial»: qué problema resuelven, en qué se diferencian del modelo de permisos de toda la vida, cómo se leen sus decisiones y cómo se depura un bloqueo sin recurrir al `setenforce 0` que tantos disgustos ha dado.

    Qué aprenderás en este artículo

    La diferencia real entre control de acceso discrecional (DAC) y obligatorio (MAC) · Cómo funciona SELinux: contextos, tipos, type enforcement, booleans y modos · Cómo funciona AppArmor: perfiles basados en rutas, modos complain y enforce · Qué distribuciones usan cada uno y por qué · Cómo diagnosticar un «permission denied» que no es de permisos Unix

    1. DAC frente a MAC: por qué hace falta una segunda capa

    El modelo de permisos clásico de Unix —usuario, grupo, otros; lectura, escritura, ejecución— es control de acceso discrecional (DAC, *Discretionary Access Control*). Se llama discrecional porque el dueño de un recurso decide a su discreción quién accede a él: si tú creas un fichero, puedes hacer `chmod 777` sobre él y abrirlo al mundo. El sistema confía en el criterio de cada propietario.

    Ese modelo tiene dos agujeros estructurales. El primero es root: el superusuario ignora por completo el DAC, así que cualquier proceso que se ejecute como root —y son muchos: servidores web, gestores de paquetes, demonios de sistema— tiene acceso total. El segundo es que el DAC razona sobre *identidades*, no sobre *comportamientos*: no hay forma de expresar «el servidor web puede leer sus páginas pero jamás debe tocar `/etc/shadow` ni abrir un puerto saliente».

    El control de acceso obligatorio (MAC, *Mandatory Access Control*) cambia esa lógica. Una política central, definida por el administrador del sistema y no por los dueños de los ficheros, declara exactamente qué puede hacer cada programa. Esa política es de obligado cumplimiento: aunque un proceso corra como root, aunque un fichero tenga permisos 777, si la política no autoriza esa operación concreta, el kernel la deniega.

    La idea clave en una frase

    El DAC responde a «quién eres tú»; el MAC responde a «qué es correcto que este programa haga», y se aplica además del DAC, nunca en su lugar — una operación tiene que pasar los dos controles

    El caso de uso canónico es la contención de daños. Si un atacante consigue ejecutar código a través de una vulnerabilidad en tu servidor web, con solo DAC hereda todo lo que puede hacer el usuario del servicio. Con MAC bien configurado queda encerrado en lo que la política permite a ese servicio: leer el *docroot*, escribir en su directorio de logs y poco más. El confinamiento no evita la vulnerabilidad, pero limita drásticamente lo que se puede hacer con ella.

    2. SELinux: etiquetas por todas partes

    SELinux (*Security-Enhanced Linux*) nació en la NSA y hoy lo mantiene una comunidad amplia. Es el MAC por defecto en la familia Red Hat: RHEL, Rocky, AlmaLinux, Fedora y CentOS Stream, y también en Android. Su modelo es potente y granular, a cambio de una curva de aprendizaje empinada.

    2.1 Todo tiene un contexto

    SELinux asigna una etiqueta —el contexto de seguridad— a cada proceso, fichero, puerto, socket y prácticamente cualquier objeto del sistema. Puedes verlo con la opción `-Z`, que casi todas las herramientas base entienden.

    # Contexto de los ficheros de un directorio
    ls -Z /var/www/html
    # -> unconfined_u:object_r:httpd_sys_content_t:s0  index.html
     
    # Contexto de los procesos en ejecucion
    ps -eZ | grep httpd
    # -> system_u:system_r:httpd_t:s0  1234 ?  00:00:01 httpd
     
    # Contexto de tu propia sesion
    id -Z
    # -> unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023

    Un contexto tiene la forma `usuario:rol:tipo:nivel`. En la práctica diaria el campo que importa es el tercero, el tipo (todos acaban en `_t`). El usuario y el rol de SELinux no son el usuario y el grupo de Unix: son categorías internas de la política. El nivel solo entra en juego con MLS/MCS, que la mayoría de sistemas no usan de forma explícita.

    2.2 Type enforcement: la regla de oro

    El corazón de SELinux es el type enforcement. La política es esencialmente una lista enorme de reglas del tipo «un proceso con el tipo `httpd_t` puede leer ficheros con el tipo `httpd_sys_content_t`». Si el tipo del proceso y el tipo del objeto no tienen una regla que los conecte para esa acción concreta, la operación se deniega y se registra.

    De ahí viene el «permission denied» clásico: mueves tu web a `/srv/misitio`, los ficheros conservan un tipo genérico como `var_t` o `default_t`, y `httpd_t` no tiene permiso para leer ese tipo. Los permisos Unix son perfectos; el que dice que no es SELinux. La solución no es desactivar nada, es corregir la etiqueta.

    # Anadir una regla persistente: este directorio es contenido web
    semanage fcontext -a -t httpd_sys_content_t '/srv/misitio(/.*)?'
     
    # Aplicar la etiqueta a lo que ya existe en disco
    restorecon -Rv /srv/misitio
     
    # Ver que tipos de puerto tiene permitido usar el servicio web
    semanage port -l | grep http

    2.3 Booleans: interruptores sin reescribir la política

    Muchos comportamientos opcionales se controlan con booleans: variables on/off que activan o desactivan bloques de reglas ya escritos. Por ejemplo, por defecto el servidor web no puede hacer conexiones de red salientes (para frenar exfiltración y SSRF); si tu aplicación legítimamente necesita eso, activas el boolean correspondiente en lugar de tocar la política.

    # Listar booleans y su estado (aqui, los del servidor web)
    getsebool -a | grep httpd
     
    # Permitir que el servicio web haga conexiones de red salientes
    # -P lo hace persistente tras reinicios
    setsebool -P httpd_can_network_connect on

    2.4 Modos: enforcing, permissive y disabled

    ModoQué haceCuándo usarlo
    enforcingAplica la política y bloquea lo no permitidoProducción, siempre que sea viable
    permissiveNo bloquea nada, pero registra todo lo que habría bloqueadoDiagnóstico y ajuste de política
    disabledSELinux apagado, deja de etiquetarCasi nunca; reactivarlo exige reetiquetar todo el disco

    La herramienta de diagnóstico es el modo permissive, no el apagado. En permissive el sistema funciona con normalidad y a la vez te deja un rastro completo de cada denegación que se produciría en enforcing, para que puedas construir la política correcta antes de volver a activarlo.

    # Estado actual
    getenforce
    sestatus
     
    # Cambiar a permissive solo hasta el proximo reinicio (para depurar)
    setenforce 0
     
    # Cambio permanente: editar SELINUX= en el fichero de config
    sudoedit /etc/selinux/config

    El reetiquetado tras un disabled

    Si SELinux ha estado en ‘disabled’, los ficheros creados en ese periodo no tienen contexto válido · Al reactivarlo hay que forzar un reetiquetado completo del sistema de ficheros, creando el fichero ‘/.autorelabel’ y reiniciando · En discos grandes ese primer arranque puede tardar bastante — planifícalo, no lo hagas en caliente sobre un servidor crítico

    3. AppArmor: perfiles atados a rutas

    AppArmor es el MAC por defecto en Debian, Ubuntu, openSUSE y SUSE Linux Enterprise. Persigue el mismo objetivo que SELinux —confinar programas— pero con un modelo deliberadamente más sencillo de leer y de escribir.

    3.1 Perfiles en lugar de etiquetas

    AppArmor no etiqueta el sistema de ficheros. En su lugar, cada programa confinado tiene un perfil: un fichero de texto en `/etc/apparmor.d/` que enumera, mediante rutas, a qué puede acceder el proceso y con qué permisos. Es la diferencia conceptual grande frente a SELinux: AppArmor razona sobre *dónde está* un fichero, no sobre *qué etiqueta lleva*.

    # Fragmento simplificado de un perfil de AppArmor
    /usr/sbin/miservicio {
      #include <abstractions/base>
     
      /etc/miservicio/**       r,      # leer su configuracion
      /var/lib/miservicio/**   rw,     # leer y escribir sus datos
      /var/log/miservicio.log  w,      # escribir su log
      network inet stream,             # sockets TCP
     
      # todo lo que no aparece aqui, queda denegado
    }

    Esa legibilidad tiene un coste en granularidad: como el modelo se apoya en rutas, un enlace duro o un `bind mount` pueden dar acceso a un fichero por un camino que el perfil no contemplaba. SELinux, al viajar la etiqueta con el inodo, no tiene ese problema. Es el compromiso clásico entre simplicidad y hermetismo.

    3.2 Modos: complain y enforce

    Aquí AppArmor brilla en ergonomía. Cada perfil, de forma individual, puede estar en modo *enforce* (aplica) o *complain* (solo registra lo que bloquearía). No es un interruptor global como el enforcing/permissive de SELinux: puedes tener el 90 % de los perfiles aplicando y dejar en complain solo el que estás ajustando.

    # Ver el estado de todos los perfiles
    aa-status
     
    # Poner un perfil concreto en modo aprendizaje
    aa-complain /usr/sbin/miservicio
     
    # Volver a aplicarlo cuando este afinado
    aa-enforce /usr/sbin/miservicio

    3.3 Generar y afinar perfiles

    AppArmor incluye dos asistentes que hacen el trabajo pesado. `aa-genprof` arranca un perfil nuevo y te pide que ejercites el programa en otra terminal (que use sus ficheros, abra sus puertos) para observar qué necesita. `aa-logprof` lee los registros de denegaciones acumulados y te propone, entrada por entrada, añadir la regla, generalizarla con un patrón o rechazarla.

    # Crear un perfil nuevo de forma interactiva
    sudo aa-genprof /usr/sbin/miservicio
     
    # Revisar denegaciones registradas y actualizar perfiles existentes
    sudo aa-logprof
     
    # Recargar un perfil tras editarlo a mano
    sudo apparmor_parser -r /etc/apparmor.d/usr.sbin.miservicio

    4. SELinux o AppArmor: comparativa

    AspectoSELinuxAppArmor
    ModeloEtiquetas (contextos) sobre cada objetoPerfiles por programa basados en rutas
    DistribucionesRHEL, Fedora, Rocky, Alma, CentOS, AndroidDebian, Ubuntu, openSUSE, SLES
    Granularidad de modoGlobal (enforcing / permissive / disabled)Por perfil (enforce / complain)
    Curva de aprendizajeEmpinada; muchos conceptos nuevosSuave; los perfiles se leen casi como prosa
    Robustez ante trucos de rutasAlta: la etiqueta viaja con el inodoMenor: enlaces y bind mounts pueden esquivar reglas
    Herramientas de ajusteaudit2allow, semanage, sealert, restoreconaa-genprof, aa-logprof, aa-complain, aa-enforce
    Cobertura por defectoTodo el sistema (targeted policy)Solo los programas con perfil instalado

    No se eligen: vienen con la distribución

    En la práctica no instalas uno u otro según preferencia: usas el que trae tu distribución, porque toda su política base y sus paquetes ya vienen preparados para él · Cambiar de MAC en una distro que no es la suya nativa es posible pero raro y con poco soporte · Lo útil es saber manejar el que te toca, y entender el otro para cuando administres ambos mundos

    5. Depurar un «permission denied» que no es de permisos

    El síntoma típico: un servicio recién instalado o recién reconfigurado falla al arrancar, al abrir un fichero o al escuchar en un puerto, y los permisos Unix están impecables. Este es el procedimiento ordenado.

    5.1 Confirmar que el MAC es el culpable

    • Comprueba si está activo: `getenforce` (SELinux) o `aa-status` (AppArmor)
    • Ponlo temporalmente en modo no bloqueante — `setenforce 0` o `aa-complain` sobre el perfil — y reproduce la operación
    • Si con eso funciona, ya sabes que la causa es la política MAC; vuelve a activarlo y pasa a construir la regla correcta. No lo dejes desactivado como «solución»

    5.2 SELinux: leer las denegaciones y proponer política

    # Denegaciones recientes, en lenguaje humano
    sudo sealert -a /var/log/audit/audit.log
     
    # Buscar las entradas AVC (denegaciones) en el log de auditoria
    sudo ausearch -m AVC -ts recent
     
    # Generar una regla que permita EXACTAMENTE lo denegado
    sudo ausearch -m AVC -ts recent | audit2allow -M mi_modulo_local
    # revisa mi_modulo_local.te ANTES de cargarlo:
    sudo semodule -i mi_modulo_local.pp

    audit2allow no es copiar y pegar a ciegas

    audit2allow te da la regla que hace desaparecer el error, no necesariamente la regla correcta: si el bloqueo lo estaba causando un intento de acceso ilegítimo, permitirlo abre justo el agujero que SELinux acababa de tapar · Lee siempre el fichero .te que genera y entiende qué tipo y qué acción estás autorizando · Cuando exista un boolean para ese caso (por ejemplo httpd_can_network_connect), usarlo es mejor que cargar un módulo local a medida

    5.3 AppArmor: revisar el perfil que bloquea

    # Denegaciones de AppArmor en el diario del sistema
    sudo journalctl -k | grep -i apparmor | grep -i denied
    # -> apparmor="DENIED" operation="open" profile="/usr/sbin/miservicio"
    #    name="/srv/datos/x" requested_mask="r" ...
     
    # Procesar esas denegaciones y actualizar el perfil de forma guiada
    sudo aa-logprof

    El log de AppArmor es directo: te dice el perfil implicado, la operación (open, exec, connect…) y la ruta o recurso exactos. Con eso, `aa-logprof` te propone la línea que falta en el perfil; tú decides si la añades tal cual, la generalizas con un patrón o la rechazas.

    5.4 La regla que nunca falla

    Ante un bloqueo MAC, el orden correcto de preferencia es: (1) corregir la etiqueta o la ruta si el recurso simplemente está en un sitio no estándar; (2) activar el boolean o la abstracción prevista para ese caso de uso; (3) como último recurso, añadir una regla local mínima y revisada. Desactivar el MAC entero no está en la lista: es cambiar un problema acotado y con solución conocida por una superficie de ataque mayor en todo el sistema.

    Conclusión

    SELinux y AppArmor no son obstáculos que los tutoriales te enseñan a apagar en el primer paso: son la capa que convierte una vulnerabilidad en un servicio en un incidente contenido en lugar de un compromiso total. El DAC dice quién eres; el MAC dice qué es razonable que ese programa haga, y lo hace cumplir aunque el proceso sea root.

    SELinux etiqueta cada objeto del sistema y razona sobre tipos; AppArmor escribe perfiles legibles atados a rutas. Uno es más hermético, el otro más fácil de vivir. No se elige entre ellos: se usa el que trae la distribución y se aprende a diagnosticar sus bloqueos con permissive/complain, los logs de auditoría y las herramientas de ajuste.

    La próxima vez que veas un «permission denied» que no cuadra con `ls -l`, ya sabes dónde mirar — y ya sabes que `setenforce 0` es para depurar cinco minutos, no para dejarlo así.

    Referencias

    El proyecto SELinux (github.com/SELinuxProject) y la guía de SELinux de Red Hat · Wiki oficial de AppArmor (gitlab.com/apparmor/apparmor/-/wikis/home) · Páginas de manual: selinux(8), semanage(8), audit2allow(1), apparmor.d(5), aa-logprof(8) · ‘The SELinux Coloring Book’ — introducción visual a contextos y type enforcement

    unfantasmaenelsistema.com | Seguridad, sistemas y todo lo que no debería funcionar (pero funciona)

    Resumen de privacidad

    Esta web utiliza cookies para que podamos ofrecerte la mejor experiencia de usuario posible. La información de las cookies se almacena en tu navegador y realiza funciones tales como reconocerte cuando vuelves a nuestra web o ayudar a nuestro equipo a comprender qué secciones de la web encuentras más interesantes y útiles.