Seguridad

Seguridad en Linux: cómo configurar SELinux en modo enforcing paso a paso

SELinux en modo enforcing es la configuración más segura que un Linux puede tener, y la que más sysadins evitan. El miedo es real: activar enforcing sin preparación rompe servicios sin explicación clara, los logs son crípticos, y el camino de vuelta atrás no siempre es obvio. Esta guía documenta la transición gradual que recomiendo: de disabled a permissive (todo se loguea, nada se bloquea) durante 2 semanas, luego enforcing por servicio uno a uno. También cubre los comandos clave: `audit2why`, `audit2allow`, `semanage`, y el truco del booleans.

Si quieres el oro en seguridad Linux, este es el camino.

TL;DR — Lo que vas a tener al final

  • Tiempo de setup: 2-3 horas para el switch a permissive, 1-2 horas por servicio al pasar a enforcing.
  • Requisitos: RHEL/CentOS/Fedora o Debian con SELinux habilitado, root.
  • Resultado: SELinux en enforcing con servicios críticos funcionando, política documentada.
  • Troubleshooting típico: AVC denials en /var/log/audit/audit.log, contexto mal asignado. Solución: `audit2allow -M mypolicy && semodule -i mypolicy.pp`.

Por qué importa activar SELinux en enforcing

Si administras un servidor Linux basado en RHEL, Fedora, Rocky, AlmaLinux o CentOS Stream, SELinux viene activado de fábrica en enforcing. La mayoría de los sysadmins lo descubren cuando algo deja de funcionar y lo primero que hacen es desactivarlo. Es el peor consejo que se repite en foros: SELinux en disabled deja al servidor sin una capa de control de acceso obligatoria (MAC) que contiene procesos comprometidos dentro de su dominio.

La diferencia práctica entre un servidor con disabled y uno con enforcing es que en el segundo, aunque un atacante explote una vulnerabilidad de Nginx o Apache y obtenga httpd_t, el kernel le niega acciones fuera del contexto esperado: no puede leer /etc/shadow, no puede escribir en directorios no etiquetados como httpd_sys_content_t, no puede abrir procesos fuera del dominio permitido. En el primero, todo eso queda a merced de los permisos DAC tradicionales.

Esta guía está orientada al sysadmin que quiere dejar SELinux activado de verdad, no solo “para salir del paso”. Vamos del modo al troubleshooting real con AVC denials.

Los tres modos de SELinux y qué significa cada uno

SELinux puede estar en uno de tres estados operativos. La confusión típica es mezclar “permisivo” con “desactivado”: no son lo mismo.

Modo Carga política Aplica reglas Loguea AVC Uso legítimo
enforcing Sí Sí, bloquea Sí Producción
permissive Sí No, solo avisa Sí Diagnóstico temporal
disabled No No No Ninguno recomendado

El modo permissivo no es un fallback permanente: es una herramienta de diagnóstico. Lo activas temporalmente, reproduces el problema, recoges las denegaciones en el log de auditoría, creas una política correcta, y vuelves a enforcing. Dejar el servidor en permissivo “porque algo fallaba” significa que SELinux no te está protegiendo aunque diga estar activo.

Verificar el modo actual antes de tocar nada

Antes de cambiar configuración, confirma el estado real con tres comandos. Cada uno reporta un aspecto distinto: el modo efectivo del kernel, el modo declarado en el archivo de config y el modo en que bootea el próximo reinicio.

# Modo en runtime (lo que el kernel está aplicando ahora)
$ getenforce
Enforcing
# Detalle completo del estado
$ sestatus
SELinux status:                 enabled
SELinuxfs mount:                /sys/fs/selinux
SELinux root directory:         /etc/selinux
Loaded policy name:             targeted
Current mode:                   enforcing
Mode from config file:          enforcing
Policy MLS status:              disabled
Policy deny_unknown status:     denied
Max kernel policy version:      33

Si Current mode y Mode from config file están alineados, todo bien. Si difieren, el sistema va a revertir al modo del config en el siguiente reinicio y te conviene alinearlos antes de continuar.

Cambiar a enforcing de forma persistente

Hay dos ajustes: uno temporal (válido hasta el próximo reboot) y uno persistente (válido hasta que vuelvas a cambiar el archivo). Para un cambio real de política de seguridad, necesitas el persistente.

# Cambia el modo en runtime (no sobrevive reboot)
$ sudo setenforce 1
# Verifica el cambio inmediato
$ getenforce
Enforcing
# Cambia el modo persistente editando /etc/selinux/config
$ sudo vi /etc/selinux/config

El archivo /etc/selinux/config controla el modo en que arranca el sistema. La línea clave es SELINUX=:

# This file controls the state of SELinux on the system on boot.
# SELINUX can take one of these three values:
#       enforcing - SELinux security policy is enforced.
#       permissive - SELinux prints warnings instead of enforcing.
#       disabled - No SELinux policy is loaded.
SELINUX=enforcing
# SELINUXTYPE can take one of these values:
#       targeted - Solo procesos específicos corren confinados (default en RHEL).
#       mls      - Multi-Level Security, uso en agencias de inteligencia y entornos clasificados.
SELINUXTYPE=targeted

El cambio de disabled a enforcing (o de disabled a permissive en el primer boot) requiere un relabel del sistema de archivos. En RHEL y derivados esto sucede automáticamente: el sistema detecta el cambio en el config y agenda un autorelabel antes de montar todo. Esto retrasa el primer arranque unos minutos, pero es un evento único.

Troubleshooting: el sistema no termina de arrancar

Si después de cambiar disabled a enforcing el arranque se queda colgado, lo más probable es que algún servicio estuviera funcionando fuera de su contexto bajo disabled y ahora el kernel niegue acceso. Arranca en permissivo, identifica, y vuelve a enforcing:

# En GRUB, editar la línea del kernel y agregar enforcing=0
linux16 ... enforcing=0
# O temporalmente permissive via kernel parameter
linux16 ... selinux=1 enforcing=0

Esto no es “desactivarlo otra vez”: es darte margen para diagnosticar sin quedarte afuera del servidor.

Booleans: el 90% de los problemas del día a día

Los booleans son interruptores de política que ajustan el comportamiento de SELinux sin tocar el código de la política base. Son la razón por la que “SELinux rompe mi servicio” casi siempre se resuelve con un comando en vez de con una política custom.

# Ver todos los booleans disponibles y su estado
$ getsebool -a | less
# Ver el valor actual de un boolean específico
$ getsebool httpd_can_network_connect
httpd_can_network_connect --> off
# Activarlo solo para esta corrida (se pierde al reiniciar)
$ sudo setsebool httpd_can_network_connect on
# Activarlo de forma persistente (sobrevive reboot, opción -P)
$ sudo setsebool -P httpd_can_network_connect on

Booleans comunes que vas a usar tarde o temprano:

Boolean Qué permite Cuándo activarlo
httpd_can_network_connect httpd inicia conexiones salientes Apache/Nginx como reverse proxy
httpd_read_user_content httpd lee archivos en /home Sirviendo sitios desde home de usuarios
httpd_enable_homedirs httpd puede listar /home Carpetas públicas por usuario
samba_enable_home_dirs Samba exporta /home Recursos compartidos por usuario
nis_enabled Acceso a mapas NIS Entornos con servidor NIS
ftpd_full_access vsftpd acceso total al sistema FTP sirviendo fuera de /var/ftp
mysql_connect_any MySQL conecta a cualquier puerto Replicación o acceso a servicios remotos

El truco está en que cada boolean tiene un propósito específico. Si tu servidor web necesita hacer conexiones salientes a un backend, el boolean correcto es httpd_can_network_connect; si activo ftpd_full_access “para que funcione algo” estoy abriendo más superficie de ataque de la necesaria.

Troubleshooting: el cambio no se mantiene

Si haces setsebool sin -P, el cambio se pierde al reiniciar. Para cambios persistentes, siempre usa -P. La razón es que setsebool sin -P modifica el estado en memoria del kernel pero no toca la base de políticas en disco.

Contextos de archivo: por qué tu web no carga

El caso clásico: mueves el document root de Nginx a /srv/web, reinicias, y el servidor devuelve 403. Los permisos DAC están bien, pero SELinux está bloqueando el acceso. La razón es que el contexto del directorio no es httpd_sys_content_t.

# Ver el contexto actual de un archivo/directorio
$ ls -Z /srv/web/
drwxr-xr-x. root root unconfined_u:object_r:var_t:s0   html
# Restaurar el contexto default según la política
$ sudo restorecon -Rv /srv/web/
Relabeled /srv/web/hello from unconfined_u:object_r:var_t:s0 to unconfined_u:object_r:httpd_sys_content_t:s0
# Verifica que el contexto quedó correcto
$ ls -Z /srv/web/
drwxr-xr-x. root root unconfined_u:object_r:httpd_sys_content_t:s0   html

Si la ruta no está en la base de contextos default (porque no es una ubicación estándar), necesitas declararla con semanage fcontext antes de aplicar restorecon:

# Declarar el contexto default para una ruta nueva
$ sudo semanage fcontext -a -t httpd_sys_content_t "/srv/web(/.*)?"
# Aplicar el contexto declarado
$ sudo restorecon -Rv /srv/web/
# Verificar la regla quedó registrada
$ sudo semanage fcontext -l | grep '/srv/web'
/srv/web(/.*)?    all files  system_u:object_r:httpd_sys_content_t:s0

Sin el semanage fcontext, cada vez que hagas restorecon o que el sistema haga un autorelabel, el directorio vuelve a var_t (porque esa es la política default para /srv).

Troubleshooting: restorecon no cambia el contexto

Si restorecon reporta que no hizo nada y el contexto sigue siendo incorrecto, lo más probable es que hayas aplicado chcon manualmente antes (que escribe el contexto en el archivo extendido de atributos sin tocar la base). Soluciónalo con restorecon -F para forzar la restauración:

# Forzar la restauración ignorando el contexto actual
$ sudo restorecon -F -v /srv/web/html/index.html

Como regla general, evita chcon en producción. Es tentador porque “funciona ahora”, pero la próxima vez que el sistema haga un relabel (upgrade de política, primer boot desde disabled), pierdes el cambio. semanage fcontext + restorecon es la combinación durable.

Diagnosticar denegaciones reales con audit2why y audit2allow

Cuando algo no funciona en enforcing, el kernel escribe un evento AVC (Access Vector Cache) en el log de auditoría. Lo que nunca debes hacer es desactivar SELinux. Lo que sí hay es un flujo sistemático de diagnóstico.

# Buscar denegaciones recientes
$ sudo ausearch -m AVC -ts recent
# Interpretar la denegación con audit2why (explica el POR QUÉ)
$ sudo ausearch -m AVC -ts recent | audit2why
# Si la denegación es legítima y necesitas permitirla, generar política custom
$ sudo ausearch -m AVC -ts recent | audit2allow -M mi_app_local
# Esto crea dos archivos:
# - mi_app_local.pp: el módulo compilado, listo para cargar
# - mi_app_local.te: el código fuente, para revisar antes de aplicar
# Activar el módulo (revisar el .te antes; audit2allow puede generar reglas más amplias de lo necesario)
$ sudo semodule -i mi_app_local.pp

El paso crítico es revisar el archivo .te antes de cargar el módulo. audit2allow ve una denegación específica y genera la regla mínima para permitirla, pero si hay varias denegaciones del mismo proceso, todas terminan en el mismo módulo. Si solo querías permitir la acción específica y no abrir un compomiso con todo el dominio, ajusta el .te a mano.

Troubleshooting: ausearch no devuelve nada

Si ausearch dice “no matches”, lo más probable es que el demonio de auditoría no esté corriendo o que las denegaciones estén en otro log. Verifica:

# ¿auditd está corriendo?
$ systemctl status auditd
# ¿Hay AVC denials en syslog como fallback?
$ sudo grep -i 'avc:.*denied' /var/log/messages
# Si nada aparece, ¿la política permite auditar?
$ getsebool auditadm_exec_content

Si auditd no corre, las denegaciones AVC igual aparecen en /var/log/messages pero se roturan antes de que tengas tiempo de leerlas en sistemas con mucha rotación.

Diagnóstico interactivo con sealert

Para servidores donde da pereza parsear AVC crudos, setroubleshootd (parte de setroubleshoot-server) genera alertas en el journal con explicación detallada y sugerencias accionables:

# Instalar setroubleshoot (si no está)
$ sudo dnf install setroubleshoot-server
# Activar e iniciar
$ sudo systemctl enable --now setroubleshootd
# Buscar alertas de SELinux con explicación humana
$ sudo sealert -a /var/log/audit/audit.log | less
# O filtrar solo las de hoy
$ sudo sealert -a /var/log/audit/audit.log | grep -A 20 "today"

Cada alerta de sealert viene con: la denegación cruda, el POR QUÉ SELinux la bloqueó (qué política/domain), y al menos una sugerencia (que puede ser “restorecon”, “activar boolean X”, o “crear módulo custom con audit2allow”). Para sysadmins que vienen de un entorno sin SELinux, sealert baja mucho la curva de aprendizaje.

Cuándo NO usar enforcing directo

Hay escenarios legítimos donde empezar en permissive tiene sentido, no como estado permanente, sino como fase de transición:

  • Migración desde un servidor que venía con SELinux disabled: arranca en permissivo, deja que el sistema funcione unas horas/días, revisa los logs, y solo entonces pasa a enforcing. Si saltas directo, la primera aplicación que intente hacer algo fuera de su contexto te deja sin servicio.
  • Aplicaciones legadas que requieren permisos amplios y no tienen sponsor para una política custom: un ERP interno escrito hace 15 años que toca 20 rutas. La opción aquí no es enforcing, sino evaluar si vale la pena mantener esa aplicación en este servidor o moverla a uno sin SELinux.
  • Contenedores Docker/Podman en RHEL: por defecto Docker monta sus volúmenes con el label correcto (container_file_t), pero aplicaciones que hacen cosas raras con --privileged o que montan sockets del host van a tener fricciones. Empieza con --security-opt label=disable en esos casos específicos, no en todo el daemon.

El patrón correcto es: permissive como herramienta de transición, no como destino final. Si después de una auditoría completa hay denegaciones que solo se resuelven con módulos custom innecesariamente permisivos, está bien mantener el dominio específico en permissivo (semanage permissive -a httpd_t) y dejar enforcing activo para el resto. Es un compromiso consciente, no desactivar SELinux.

Resumen del flujo de adopción

El orden recomendado para un servidor que viene con disabled o permissive:

  1. Confirma el estado actual con sestatus y getenforce.
  2. Edita /etc/selinux/config y pon SELINUX=enforcing.
  3. Reinicia. El sistema hace autorelabel si venías de disabled; eso retrasa el primer boot unos minutos.
  4. Verifica que los servicios críticos arrancaron: systemctl status nginx postfix sshd.
  5. Recoge denegaciones durante una semana: sudo ausearch -m AVC -ts week | audit2why > /tmp/selinux_review.txt.
  6. Resuelve cada denegación con la herramienta correcta: restorecon, setsebool -P, semanage fcontext, o módulo local con audit2allow.
  7. Documenta los boolean activos y los módulos locales en un runbook del equipo.

La parte de documentar es la que más se salta. Si no dejás registro de qué boolean está activo y por qué, el próximo sysadmin que herede el servidor va a desactivar SELinux porque “no entiende por qué está tan raro”. Una lista de boolean activos con justificación lleva cinco minutos y ahorra meses de confusión.

Preguntas frecuentes

¿Por qué Seguridad en Linux: cómo configurar SELinux en modo enforcing paso a paso es relevante para sysadmins Linux?

Forma parte del flujo diario de administración. Los conceptos explicados se aplican directamente a servidores en producción y resuelven problemas reales que enfrentan sysadmins hispanohablantes.

¿Qué necesito para empezar con Seguridad en Linux: cómo configurar?

Distribución Linux actualizada (Debian, Ubuntu, RHEL, Fedora o Arch), acceso root, y conexión a internet. Para producción: entorno de staging separado antes de aplicar cambios.

¿Cuáles son los errores más comunes con Seguridad en Linux: cómo configurar?

Los tres más frecuentes: (1) saltar la lectura de logs, (2) aplicar cambios directos en producción sin probar, (3) olvidar permisos y ownership. La documentación y un plan de rollback siempre.

¿Cuándo usar Seguridad en Linux: cómo configurar y cuándo una alternativa?

Esta solución es ideal para el caso descrito. Alternativas como Docker, Ansible o configuración manual pueden ser mejores según contexto: portabilidad, escala y reproducibilidad son los factores clave.

¿Cómo migro desde una configuración previa a Seguridad en Linux: cómo configurar?

Documentar el estado actual, probar la nueva configuración en staging, validar funcionalmente, y aplicar en producción con rollback plan listo. Los posts de SoyLinux incluyen troubleshooting para los problemas más comunes.

Lee también

Alfonso Moreno | Sysadmin Senior

Soy Alfonso, sysadmin senior con RHCE y más de 10 años administrando sistemas Linux para empresas y proyectos personales. Me especializo en infraestructura, redes, virtualización, monitoring, automation y self-hosting. Sobre SoyLinux.com SoyLinux.com es un blog técnico en español sobre Linux, software libre y self-hosting. Lo mantengo en mi tiempo libre. Áreas de expertise - Linux server administration: RHEL, CentOS, Rocky, Alma, Fedora, Debian, Ubuntu - Network services: Postfix, BIND, Samba, NFS, Squid - Virtualización y containers: KVM, Proxmox, Docker, Podman - Monitoring: Prometheus, Grafana, Zabbix - Automation: Ansible, Bash scripting - Self-hosting: Vaultwarden, Nextcloud, Jellyfin, Immich, Homelab Contacto - Email: [email protected]

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *