--- title: "Configurar SELinux en Rocky Linux 9: modos y contextos" description: "Aprende a consultar y cambiar los modos de SELinux en Rocky Linux 9, corregir contextos de archivos, puertos y booleanos, y diagnosticar denegaciones AVC." category: "Seguridad del Servidor" language: es source: https://cubepath.com/es/docs/seguridad-del-servidor/configuracion-basica-de-selinux-y-modos updated: 2026-09-25 --- # Configurar SELinux en Rocky Linux 9: modos y contextos SELinux (Security-Enhanced Linux) es el sistema de control de acceso obligatorio (MAC) que viene activado por defecto en Rocky Linux, AlmaLinux y Red Hat Enterprise Linux. Además de los permisos clásicos de usuario y grupo, el kernel comprueba que cada proceso solo acceda a los archivos, puertos y recursos que su política permite. En este tutorial aprenderás a consultar y cambiar el modo de SELinux en Rocky Linux 9 y a resolver los tres problemas más habituales al publicar un servicio: contextos de archivo incorrectos, puertos no permitidos y booleanos desactivados. Usarás Nginx como ejemplo práctico. ## Requisitos previos - Un servidor con Rocky Linux 9 (o AlmaLinux 9 / RHEL 9), por ejemplo un VPS de CubePath. - Un usuario no root con privilegios `sudo`. - Acceso a la consola del servidor por si necesitas reiniciar con un cambio de modo. > Nota: Ubuntu y Debian usan AppArmor en lugar de SELinux. Si trabajas con Ubuntu 24.04, consulta la guía de configuración de perfiles AppArmor. ## Conceptos básicos Antes de tocar nada conviene tener claros tres conceptos: - **Contexto**: cada proceso, archivo y puerto tiene una etiqueta con el formato `usuario:rol:tipo:nivel`. En la política `targeted` que usa Rocky Linux, lo que importa casi siempre es el **tipo**, por ejemplo `httpd_t` para el proceso de Nginx o `httpd_sys_content_t` para los archivos web. - **Política**: reglas que dicen qué tipo de proceso puede hacer qué sobre qué tipo de objeto. Todo lo que no está permitido explícitamente se deniega. - **Modo**: determina si las denegaciones se aplican o solo se registran. | Modo | Qué hace | Cuándo usarlo | |------|----------|---------------| | `enforcing` | Aplica la política y registra las denegaciones | Producción, siempre que sea posible | | `permissive` | No bloquea nada, pero registra lo que habría bloqueado | Diagnóstico temporal | | `disabled` | No carga ninguna política | No recomendado | ## Paso 1: Comprobar el estado de SELinux Instala primero las herramientas de administración. `policycoreutils-python-utils` incluye `semanage` y `audit2allow`, y `setroubleshoot-server` añade `sealert`, que traduce las denegaciones a explicaciones legibles: ```bash sudo dnf install policycoreutils-python-utils setroubleshoot-server ``` Consulta el modo actual con `getenforce`: ```bash getenforce ``` ```output Enforcing ``` Para ver el detalle completo usa `sestatus`: ```bash sestatus ``` ```output 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: enabled Policy deny_unknown status: allowed Memory protection checking: actual (secure) Max kernel policy version: 33 ``` `Current mode` es el modo en ejecución y `Mode from config file` el que se aplicará en el siguiente arranque. Si no coinciden, alguien ha cambiado el modo en caliente. También puedes ver el contexto de tu usuario, de los procesos y de los archivos con la opción `-Z`, que aceptan muchos comandos: ```bash id -Z ls -Z /etc/passwd ps -eZ | grep sshd ``` ## Paso 2: Cambiar entre enforcing y permissive El modo se puede cambiar en caliente, sin reiniciar, con `setenforce`. Este cambio se pierde en el siguiente arranque: ```bash sudo setenforce 0 # permissive getenforce sudo setenforce 1 # enforcing getenforce ``` Para que el cambio sea permanente, edita el archivo de configuración: ```bash sudo nano /etc/selinux/config ``` Las dos directivas relevantes son estas: ```ini SELINUX=enforcing SELINUXTYPE=targeted ``` `SELINUX` acepta `enforcing`, `permissive` o `disabled`, y `SELINUXTYPE` debe quedarse en `targeted` salvo que tengas un motivo muy concreto. En lugar de poner todo el sistema en permissive para diagnosticar un servicio, puedes poner solo su dominio en modo permisivo y dejar el resto protegido: ```bash sudo semanage permissive -a httpd_t ``` Cuando termines, vuelve a activarlo: ```bash sudo semanage permissive -d httpd_t ``` ### Volver a activar SELinux si estaba desactivado Si heredas un servidor con SELinux desactivado, no pases directamente a `enforcing`: los archivos creados mientras estaba desactivado no tienen etiqueta y muchos servicios fallarían al arrancar. Sigue este orden. En Rocky Linux 9 la forma soportada de desactivar SELinux es el parámetro de kernel `selinux=0`, así que elimínalo por si existe: ```bash sudo grubby --update-kernel ALL --remove-args selinux ``` Pon `SELINUX=permissive` en `/etc/selinux/config`, marca el sistema para reetiquetar todos los archivos en el próximo arranque y reinicia: ```bash sudo touch /.autorelabel sudo reboot ``` El reetiquetado puede tardar varios minutos en discos con muchos archivos. Después, revisa las denegaciones registradas (paso 6) y, cuando no aparezcan nuevas, cambia a `SELINUX=enforcing` y ejecuta `sudo setenforce 1`. ## Paso 3: Corregir contextos de archivos El caso más frecuente: sirves una web desde un directorio que no es el predeterminado y Nginx devuelve `403 Forbidden` aunque los permisos Unix son correctos. Vamos a reproducirlo. Instala Nginx, crea un directorio web en `/srv/web` y una página de prueba: ```bash sudo dnf install nginx sudo mkdir -p /srv/web echo "Hola desde /srv/web" | sudo tee /srv/web/index.html ``` Crea un bloque de servidor que use ese directorio en el puerto 80: ```bash sudo nano /etc/nginx/conf.d/web.conf ``` ```nginx server { listen 80; server_name your_domain; root /srv/web; } ``` Sustituye `your_domain` por tu dominio o por la IP del servidor. Comprueba la configuración, arranca Nginx y abre el puerto HTTP en firewalld: ```bash sudo nginx -t sudo systemctl enable --now nginx sudo firewall-cmd --permanent --add-service=http sudo firewall-cmd --reload ``` Haz una petición local indicando el nombre del servidor: ```bash curl -i -H "Host: your_domain" http://127.0.0.1/ ``` ```output HTTP/1.1 403 Forbidden ``` Mira el contexto de los archivos y compáralo con el del directorio web por defecto: ```bash ls -Zd /srv/web /srv/web/index.html /usr/share/nginx/html ``` ```output unconfined_u:object_r:var_t:s0 /srv/web unconfined_u:object_r:var_t:s0 /srv/web/index.html system_u:object_r:httpd_sys_content_t:s0 /usr/share/nginx/html ``` El proceso `httpd_t` puede leer `httpd_sys_content_t`, pero no `var_t`. La solución correcta es registrar una regla de contexto permanente con `semanage fcontext` y aplicarla con `restorecon`: ```bash sudo semanage fcontext -a -t httpd_sys_content_t "/srv/web(/.*)?" sudo restorecon -Rv /srv/web ``` ```output Relabeled /srv/web from unconfined_u:object_r:var_t:s0 to unconfined_u:object_r:httpd_sys_content_t:s0 Relabeled /srv/web/index.html from unconfined_u:object_r:var_t:s0 to unconfined_u:object_r:httpd_sys_content_t:s0 ``` Repite la petición: ```bash curl -H "Host: your_domain" http://127.0.0.1/ ``` ```output Hola desde /srv/web ``` Si la aplicación necesita escribir en un subdirectorio (subidas, caché), usa el tipo `httpd_sys_rw_content_t` solo para ese subdirectorio, nunca para todo el sitio. > Importante: `chcon` también cambia contextos, pero el cambio se pierde en el siguiente `restorecon` o reetiquetado completo. Úsalo solo para pruebas. Ten en cuenta además que `mv` conserva el contexto de origen, mientras que `cp` aplica el del directorio de destino: si mueves archivos desde tu home, ejecuta `restorecon` después. Puedes listar las reglas personalizadas que has añadido con: ```bash sudo semanage fcontext -l -C ``` ## Paso 4: Permitir un puerto no estándar SELinux también etiqueta los puertos. Nginx (`httpd_t`) solo puede escuchar en los puertos del tipo `http_port_t` y relacionados. Consulta cuáles son: ```bash sudo semanage port -l | grep -w http_port_t ``` ```output http_port_t tcp 80, 81, 443, 488, 8008, 8009, 8443, 9000 ``` Cambia `listen 80;` por `listen 8090;` en `/etc/nginx/conf.d/web.conf` y reinicia Nginx: ```bash sudo systemctl restart nginx ``` El reinicio falla. En el journal verás el motivo: ```bash sudo journalctl -u nginx -n 20 --no-pager ``` ```output nginx: [emerg] bind() to 0.0.0.0:8090 failed (13: Permission denied) ``` Añade el puerto al tipo `http_port_t`: ```bash sudo semanage port -a -t http_port_t -p tcp 8090 ``` Si `semanage` responde que el puerto ya está definido para otro tipo, usa `-m` (modificar) en lugar de `-a`. Reinicia Nginx y abre el puerto en el firewall: ```bash sudo systemctl restart nginx sudo firewall-cmd --permanent --add-port=8090/tcp sudo firewall-cmd --reload curl -H "Host: your_domain" http://127.0.0.1:8090/ ``` ```output Hola desde /srv/web ``` ## Paso 5: Gestionar booleanos Los booleanos activan o desactivan partes de la política sin escribir reglas. Por ejemplo, por defecto Nginx no puede abrir conexiones de red salientes, así que un `proxy_pass` hacia una aplicación en `127.0.0.1:3000` devuelve `502 Bad Gateway` y en el log de Nginx aparece `connect() ... failed (13: Permission denied)`. Lista los booleanos relacionados con el servidor web: ```bash getsebool -a | grep httpd ``` ```output httpd_can_network_connect --> off httpd_can_network_connect_db --> off httpd_can_sendmail --> off ... ``` `semanage boolean -l` muestra además una descripción de cada uno: ```bash sudo semanage boolean -l | grep httpd_can_network_connect ``` Activa el booleano de forma permanente con `-P` (sin esa opción se pierde al reiniciar): ```bash sudo setsebool -P httpd_can_network_connect on getsebool httpd_can_network_connect ``` ```output httpd_can_network_connect --> on ``` Si Nginx solo tiene que conectar con una base de datos, `httpd_can_network_connect_db` es más restrictivo y preferible. Activa únicamente los booleanos que tu aplicación necesita. ## Paso 6: Diagnosticar denegaciones Cada denegación queda registrada como un mensaje AVC en `/var/log/audit/audit.log`. Para ver las recientes: ```bash sudo ausearch -m AVC -ts recent ``` ```output type=AVC msg=audit(1727251200.123:456): avc: denied { name_bind } for pid=2345 comm="nginx" src=8090 scontext=system_u:system_r:httpd_t:s0 tcontext=system_u:object_r:unreserved_port_t:s0 tclass=tcp_socket permissive=0 ``` Los campos clave son el permiso denegado (`name_bind`), el proceso (`comm`), el tipo del proceso (`scontext`) y el tipo del objeto (`tcontext`). `sealert` analiza el log y propone soluciones concretas, normalmente un `semanage` o un `setsebool`: ```bash sudo sealert -a /var/log/audit/audit.log ``` `audit2why` explica por qué se produjo cada denegación: ```bash sudo ausearch -m AVC -ts recent | audit2why ``` Algunas denegaciones no se registran porque la política las marca como `dontaudit`. Si no ves nada y sospechas de SELinux, desactiva esas reglas temporalmente, reproduce el problema y vuelve a activarlas: ```bash sudo semodule -DB sudo semodule -B ``` ## Paso 7: Crear un módulo de política propio (último recurso) Si ni un contexto, ni un puerto, ni un booleano resuelven el problema, puedes generar un módulo local a partir de las denegaciones con `audit2allow`. Revisa siempre lo que genera antes de cargarlo, porque permite exactamente lo que se denegó, incluidos accesos que quizá no deberían permitirse: ```bash sudo ausearch -m AVC -c nginx --raw | audit2allow -M mi_nginx cat mi_nginx.te ``` Si las reglas del archivo `.te` son razonables, instala el módulo compilado: ```bash sudo semodule -i mi_nginx.pp sudo semodule -l | grep mi_nginx ``` Para eliminarlo más adelante: ```bash sudo semodule -r mi_nginx ``` ## Solución de problemas - **Un servicio falla solo en enforcing**: ejecuta `sudo ausearch -m AVC -ts recent -c nombre_del_proceso`. Si hay denegaciones, sigue el orden: contexto de archivo, puerto, booleano y, en último caso, módulo propio. - **Los contextos vuelven a estar mal tras un reetiquetado**: usaste `chcon` en lugar de `semanage fcontext`. Registra la regla con `semanage` y ejecuta `restorecon`. - **El sistema no arranca tras activar enforcing**: en el menú de GRUB, edita la entrada y añade `enforcing=0` a la línea del kernel para arrancar en permissive. Después ejecuta `sudo touch /.autorelabel`, reinicia y revisa las denegaciones. - **Un contenedor de Podman no puede leer un volumen**: monta el volumen con la opción `:Z` (privado) o `:z` (compartido) para que Podman lo reetiquete. ## Conclusión Ya sabes consultar y cambiar el modo de SELinux, corregir contextos de archivo con `semanage fcontext` y `restorecon`, permitir puertos no estándar, activar booleanos y diagnosticar denegaciones con `ausearch` y `sealert`. Con estas herramientas no hace falta desactivar SELinux para que un servicio funcione. Como siguientes pasos, puedes revisar `semanage fcontext -l -C` y `semanage port -l -C` para documentar tus cambios locales, configurar firewalld con zonas específicas y aplicar el mismo método a PHP-FPM o a tu base de datos.