¿SELinux? Nah, dejalo en Disabled
En el mundo real muchos sysadmins y developers en sistemas operativos de la familia Red Hat deshabilitan SELinux. "¡Es muy difícil!" dicen. En Ubuntu ni en Debian existe esa cuestión ya que en su lugar se usa AppArmor. Este post no te dará servido en bandeja como solucionar un problema, sino algo mucho más valioso, que es entender el fundamento del comportamiento de SELinux y por qué funciona la solución que vayas a aplicar.
Usamos un caso práctico: tenemos una instalación basada en Laravel, php-fpm y con nginx como web server.
Tenemos una estructura de directorio así:
> tree -L 1 /home/sergio/laravel-voyager/ /home/sergio/laravel-voyager/ ├── app ├── artisan ├── bootstrap ├── composer.json ├── composer.lock ├── config ├── database ├── lang ├── package.json ├── phpunit.xml ├── public ├── README.md ├── resources ├── routes ├── storage ├── tests ├── vendor └── vite.config.js 11 directories, 7 files
Veamos la información de SELinux para algunos archivos y directorios:
[sergio@masterk laravel-voyager]$ sudo ls -Zd public/storage /etc/nginx /home/sergio/.bashrc /root/.bashrc /usr/share/nginx/ bootstrap/cache/ [sudo] password for sergio: unconfined_u:object_r:user_home_t:s0 bootstrap/cache/ unconfined_u:object_r:user_home_t:s0 public/storage system_u:object_r:httpd_config_t:s0 /etc/nginx system_u:object_r:admin_home_t:s0 /root/.bashrc system_u:object_r:user_home_t:s0 /home/sergio/.bashrc system_u:object_r:usr_t:s0 /usr/share/nginx/
Si bien la salida es demasiado críptica, la podemos descifrar:
Archivo/Directorio | Usuario (SELinux) | Rol (SELinux) | Asignación basada en tipos (SELinux) | Rango (SELinux) | Descripción y Observaciones |
---|---|---|---|---|---|
bootstrap/cache/ | unconfined_u |
object_r |
user_home_t |
s0 |
Considerado como parte del directorio personal de un usuario. Debe ser httpd_sys_rw_content_t para Laravel. |
public/storage | unconfined_u |
object_r |
user_home_t |
s0 |
Similar a bootstrap/cache . Debe ser httpd_sys_content_t para que Laravel sirva archivos estáticos. |
/etc/nginx | system_u |
object_r |
httpd_config_t |
s0 |
Archivo de configuración de Nginx. Correctamente etiquetado como httpd_config_t . No requiere cambios. |
/home/sergio/.bashrc | system_u |
object_r |
user_home_t |
s0 |
Archivo personal de configuración de un usuario. Etiquetado como user_home_t , lo cual es correcto. |
/root/.bashrc | system_u |
object_r |
admin_home_t |
s0 |
Archivo de configuración del usuario root . Etiquetado como admin_home_t , lo cual es correcto. |
/usr/share/nginx/ | system_u |
object_r |
usr_t |
s0 |
Etiquetado como usr_t , que es genérico. Debe ser httpd_sys_content_t si Nginx sirve contenido estático aquí. |
Descripción de los campos
Concepto | Descripción | Ejemplo |
---|---|---|
Usuario (SELinux) | Define el usuario de SELinux asociado al recurso. |
system_u : Archivo gestionado por el sistema. unconfined_u : Archivo de un usuario sin restricciones. |
Rol (SELinux) | Define el rol asignado al recurso. | Usualmente es object_r para archivos regulares y directorios. |
Asignación basada en tipos | Determina qué procesos o aplicaciones pueden interactuar con un recurso. |
httpd_sys_rw_content_t : Permite lectura/escritura por parte del servidor web. httpd_sys_content_t : Permite solo lectura por parte del servidor web. user_home_t : Para directorios o archivos en /home . |
Rango (SELinux) | Se usa para configuraciones de seguridad MLS o TE (usualmente s0 en configuraciones estándar). |
Normalmente no requiere ajustes en configuraciones básicas. |
Ahora bien, hay varios tipos de políticas en cuanto a funcionalidades que se pueden aplicar en SELinux, la que se usa en general es targeted. El tipo de política targeted significa que determinados servicios se ejecutan de manera confinada, es decir limitada para realizar solamente determinadas acciones más allá de los permisos ordinarios.
Y es importante enfatizar que cuando usamos targeted el campo que más nos tiene que interesar de un contexto es el "Type Enforcement". En general no prestaremos demasiada atención al resto de los campos.
Lo más importante para entender es que las reglas de SELinux tienen sujetos y objetos. El contexto o etiqueta del objeto podría no permitir que el sujeto realice una determinada acción al sujeto, y es allí cuando se produce un error. Dicho error tiene por objetivo proteger al sistema.
En este ejemplo los sujetos son nginx y php-fpm y los objetos son los directorios bootstrap/cache
y public/storage
(y sus respectivos contenidos).
Por ejemplo, nginx sería el sujeto y con el siguiente comando vemos el contexto del proceso:
> ps -C nginx,php-fpm -Z LABEL PID TTY TIME CMD system_u:system_r:httpd_t:s0 898 ? 00:00:00 php-fpm system_u:system_r:httpd_t:s0 1118 ? 00:00:00 php-fpm system_u:system_r:httpd_t:s0 1119 ? 00:00:00 php-fpm system_u:system_r:httpd_t:s0 1120 ? 00:00:00 php-fpm system_u:system_r:httpd_t:s0 1121 ? 00:00:00 php-fpm system_u:system_r:httpd_t:s0 1122 ? 00:00:00 php-fpm system_u:system_r:httpd_t:s0 1451 ? 00:00:00 nginx system_u:system_r:httpd_t:s0 1452 ? 00:00:00 nginx system_u:system_r:httpd_t:s0 1453 ? 00:00:00 nginx
Pero habrá problemas...
La cuestión es que tanto nginx como php-fpm se ejecutan con la asignación de tipo (TE) httpd_t y la TE que tienen directorior como bootstrap/cache y public/storage es user_home_t.
Con el siguiente comando podemos verifucar qué cosas puede hacer el dominio httpd_t
en el contexto del objeto que tiene el tipo de asignación user_home_t
:
> sesearch -s httpd_t -A -t user_home_t allow daemon user_home_t:file { append getattr }; allow domain file_type:blk_file map; [ domain_can_mmap_files ]:True allow domain file_type:chr_file map; [ domain_can_mmap_files ]:True allow domain file_type:file map; [ domain_can_mmap_files ]:True allow domain file_type:lnk_file map; [ domain_can_mmap_files ]:True allow httpd_t file_type:dir { getattr open search }; allow httpd_t file_type:filesystem getattr; allow httpd_t user_home_t:file map; [ httpd_read_user_content ]:True allow httpd_t user_home_type:dir { getattr ioctl lock open read search }; [ httpd_read_user_content ]:True allow httpd_t user_home_type:dir { getattr open search }; [ httpd_enable_homedirs ]:True allow httpd_t user_home_type:dir { getattr open search }; [ httpd_read_user_content ]:True allow httpd_t user_home_type:dir { getattr open search }; [ httpd_read_user_content ]:True allow httpd_t user_home_type:file { getattr ioctl lock open read }; [ httpd_read_user_content ]:True allow httpd_t user_home_type:lnk_file { getattr read }; [ httpd_enable_homedirs ]:True
Cada regla tiene:
- El tipo de regla ( allow )
- El sujeto de la regla ( httpd_t )
- El contexto y tipo del objeto ( user_home_t:file )
- Los permisos (append y getattr)
¡No hay ninguna regla que permita la modificación total del contenido del archivo ni la creación de archivos en la carpeta (por ejemplo, el permiso write)!. Esto significa que, de manera predeterminada, servicios como apache o nginx no tienen permiso para escribir en los directorios home de los usuarios. Y por ese motivo hay cosas que fallarán, sin importar los permisos ni el usuario...
Por lo tanto los directorios public/storage
y bootstrap/cache
y su contenido necesitan otro contexto para que Laravel funcione correctamente cuando está ubicado en un subdirectorio de /home. Mirando la documentación podemos ver que httpd_sys_content_t y httpd_sys_rw_content son respectivamente los apropiados para esos directorios y su contenido:
httpd_sys_content_t - Set files with the httpd_sys_content_t type, if you want to treat the files as httpd sys content. Paths: /srv/([^/]*/)?www(/.*)?, /var/www(/.*)?, /etc/htdig(/.*)?, /srv/gallery2(/.*)?, /var/lib/trac(/.*)?, /var/lib/htdig(/.*)?, /var/www/icons(/.*)?, /usr/share/glpi(/.*)?, /usr/share/htdig(/.*)?, /usr/share/drupal.*, /usr/share/z-push(/.*)?, /var/www/svn/conf(/.*)?, /usr/share/icecast(/.*)?, /var/lib/cacti/rra(/.*)?, /usr/share/ntop/html(/.*)?, /usr/share/nginx/html(/.*)?, /usr/share/doc/ghc/html(/.*)?, /usr/share/openca/htdocs(/.*)?, /usr/share/selinux-pol‐ icy[^/]*/html(/.*)?
y
httpd_sys_rw_content_t - Set files with the httpd_sys_rw_content_t type, if you want to treat the files as httpd sys read/write content. Paths: /etc/rt(/.*)?, /etc/glpi(/.*)?, /etc/horde(/.*)?, /etc/drupal.*, /etc/z-push(/.*)?, /var/lib/svn(/.*)?, /var/www/svn(/.*)?, /etc/owncloud(/.*)?, /var/www/html(/.*)?/uploads(/.*)?, /var/www/html(/.*)?/wp-content(/.*)?, /var/www/html(/.*)?/wp_back‐ ups(/.*)?, /var/www/html(/.*)?/sites/default/files(/.*)?, /var/www/html(/.*)?/sites/default/settings.php, /etc/mock/koji(/.*)?, /etc/nextcloud(/.*)?, /var/lib/drupal.*, /etc/zabbix/web(/.*)?, /var/lib/moodle(/.*)?, /var/log/z-push(/.*)?, /var/spool/gosa(/.*)?, /etc/WebCalendar(/.*)?, /usr/share/joomla(/.*)?, /var/lib/dokuwiki(/.*)?, /var/lib/owncloud(/.*)?, /var/spool/viewvc(/.*)?, /var/lib/nextcloud(/.*)?, /var/lib/pootle/po(/.*)?, /var/lib/phpMyAdmin(/.*)?, /var/www/moodledata(/.*)?, /srv/gallery2/smarty(/.*)?, /var/www/moodle/data(/.*)?, /var/lib/graphite-web(/.*)?, /var/log/shibboleth-www(/.*)?, /var/www/gallery/albums(/.*)?, /var/www/html/owncloud/data(/.*)?, /var/www/html/nextcloud/data(/.*)?, /usr/share/wordpress-mu/wp- content(/.*)?, /usr/share/wordpress/wp-content/upgrade(/.*)?, /usr/share/wordpress/wp-content/uploads(/.*)?, /var/www/html/config‐ uration.php
De modo que esos son los contextos que tendrías que aplicar a esos directorios y sus contenidos. El resto, si sos administrador o desarrollador de Linux con algo de experiencia es anecdótico.
Fuentes y más recursos
- Página del manual de httpd_selinux
- Ventajas de usar SELinux
- Desactivación de SELinux
- Página del manual de booleans (concepto importante que complementan a las reglas de las políticas de SELinux)