SELinux sin Miedo: Politicas Personalizadas para Servicios Criticos
De la auditoria con audit2allow a modulos de policy versionados y mantenidos en produccion, sin caer en el permissive eterno.
Cada vez que un servicio se rompe en RHEL o Rocky, el reflejo del equipo de guardia es el mismo: setenforce 0, problema resuelto, ticket cerrado. Seis meses despues el cluster entero corre en permissive, nadie recuerda por que, y el informe de compliance se convierte en ciencia ficcion. El equipo Basilisk OffSec paso los ultimos dos anos derribando ambientes asi en red teams autorizados, y la conclusion es directa: SELinux apagado acorta el camino de un RCE en PHP-FPM hasta root en menos de 90 segundos. Este articulo no vende magia, muestra el flujo que usamos para entregar modulos .pp versionados en produccion sin romper deploys.
Antes de escribir cualquier policy, necesitas leer lo que ya existe. El comando seinfo -t lista alrededor de 5.000 tipos en RHEL 9 estandar, y sesearch --allow -s httpd_t muestra exactamente lo que Apache puede tocar. Iniciamos toda investigacion con ausearch -m AVC -ts recent corriendo en paralelo con el servicio en modo permissive TEMPORARIO, jamas el sistema entero. Usa semanage permissive -a httpd_t para aislar solo el dominio bajo investigacion. Ese patron salvo auditorias completas que describimos en Hardening de Linux Server: CIS Benchmark Aplicado sin Romper Produccion, donde CIS exige enforcing global pero permite excepciones documentadas por dominio.
audit2allow es un arma de doble filo. Ejecutar ausearch -m AVC | audit2allow -M mimodulo genera un .te que compila y funciona, pero frecuentemente concede permisos absurdos tipo allow httpd_t shadow_t:file read. Nuestro checklist interno exige que cada .te pase por revision manual antes del semodule -i. Busca reglas que toquen shadow_t, etc_t, kernel_t o self:capability sys_admin, esas son banderas rojas. En una investigacion reciente, descrita parcialmente en DFIR en Linux: Triaje en Vivo con UAC y Velociraptor, un modulo generado a ciegas habia abierto acceso a /etc/sudoers y nadie lo noto por ocho meses.
Para servicios nuevos, preferimos escribir policy desde cero con el lenguaje macro del refpolicy. Un modulo tipico tiene tres archivos: miservicio.te con las reglas, miservicio.fc con file contexts y miservicio.if con interfaces para otros dominios. El make -f /usr/share/selinux/devel/Makefile genera el .pp que instalas con semodule -i. Versionamos esos tres archivos en git junto con Ansible, y cada PR corre checkmodule -M -m y sediff contra la baseline. Ese proceso se conecta con lo que mostramos en Supply Chain Security: Firma con Sigstore y SBOM Real en CI/CD, donde el .pp termina firmado con Sigstore antes de llegar a los nodos.
Los servicios que abren sockets en puertos no estandar son el caso mas comun de ruptura silenciosa. Postgres en 5433 por ejemplo necesita semanage port -a -t postgresql_port_t -p tcp 5433, y no una nueva policy. Nginx sirviendo archivos fuera de /var/www quiere semanage fcontext -a -t httpd_sys_content_t "/srv/app(/.*)?" seguido de restorecon -Rv /srv/app. El 80 por ciento de los casos que vemos son file context y port labeling, no policy nueva. El equipo que entiende esa diferencia ahorra horas, y el tema reaparece cuando discutimos pivoting interno en Nmap Avanzado: Scripts NSE para Recon Interno en Lab Corporativo Simulado.
El mantenimiento en produccion exige observabilidad. Configuramos setroubleshoot-server en modo silent enviando AVCs al SIEM via journald, con reglas Sigma especificas para denials no esperados. Cuando un deploy rompe, la alerta llega antes que el usuario se queje. Tambien corremos sealert -a /var/log/audit/audit.log semanalmente en staging para detectar policy drift. Ese ciclo de feedback continuo es exactamente lo que defendemos en Purple Team en la Practica: Construyendo Ciclo de Feedback Red vs Blue: red team encuentra el camino, blue team escribe la policy, y la policy entra al pipeline.
Takeaway practico: nunca corras setenforce 0 en produccion por mas de 15 minutos. Usa semanage permissive -a dominio_t para aislar el problema, captura AVCs con ausearch, revisa audit2allow manualmente, commitea el .te en git, firma el .pp y despliega via Ansible. Si no puedes justificar cada allow rule en revision de codigo, la policy no esta lista. SELinux no es obstaculo, es el ultimo perimetro despues que el atacante ya esta adentro.