====== Los permisos de un sistema de archivos en GNU/Linux ====== En este artículo, profundizaremos un poco en los permisos utilizados en los sistemas de archivos de GNU/Linux. La idea no es rescribir los manuales existentes, sino mas bien ofrecer en un solo artículo una breve explicación de los principios y las operaciones más utilizadas, con algunos ejemplos y alguna que otra observación. En ocasiones utilizaremos caracteres entre paréntesis para indicar la forma en que el sistema operativo suele representar los diferentes objetos o propiedades. Estos caracteres suelen provenir de las iniciales o parte del nombre de dichos objetos en inglés, o de representaciones numéricas. Los comandos que se mencionen, en GNU/Linux normalmente se distribuyen junto con su página de manual, y para acceder a esta, solo es necesario ejecutar en una terminal un comando como este: man elnombredelcomando ===== Directorios, archivos, usuarios y grupos ===== La mayoría de los sistemas de archivos para GNU/Linux reconocen dos objetos principales: los directorios (''d'') y los archivos (''f''). Sobre ambos pueden aplicarse diferentes permisos, en dependencia de la funcionalidad que se busque. Pero antes de adentrarnos en los permisos, es necesario referirse brevemente a los usuarios. En GNU/Linux, cada usuario pertenece a uno o más grupos, aunque por seguridad usualmente pertenecen a un solo grupo creado con el mismo nombre del usuario, para evitar que otros usuarios del mismo grupo tengan acceso a directorios o archivos privados. Para garantizar justamente esta privacidad, todos los directorios y archivos tienen un propietario y un grupo. No obstante, el propietario y el grupo al que pertenezca un archivo o directorio puede modificarse mediante el comando ''chown''. Nótese que este comando solamente permite cambiar el propietario y/o el grupo de uno o varios archivos o directorios, pero no así establecer los propios permisos (aunque en ocasiones cambiar el propietaro o grupo de un archivo o directorio puede ser una forma indirecta, pero útil de modificar los permisos). ===== Los permisos ===== Pues bien, existen tres permisos principales, cuya función varía ligeramente en dependencia de si se aplican a un archivo o a un directorio: * Lectura (''r''). Si se habilita sobre un archivo, permite leer su contenido. Sobre un directorio, permite listar el contenido. * Escritura (''w''). Si se habilita sobre un archivo, permite modificar o eliminar el archivo. Sobre un directorio, permite crear, modificar, mover y eliminar archivos del mismo. * Ejecución (''x''). Si se habilita sobre un archivo, permite ejecutarlo. Sobre un directorio, permite acceder a el. Estos permisos pueden establecerse respectivamente para: * El propietario (''u'') * El grupo (''g'') * Los demás usuarios y grupos (''o'') A nivel informático, cada permiso es un bit de propiedad que se representa con un número octal: ^ Permiso ^ Octal ^ | Ejecución | 1 | | Escritura | 2 | | Lectura | 4 | Entonces, la suma de varios permisos define las propiedades finales de un archivo. Así, el número ''7'' ofrece todos los permisos, el ''6'' permite lectura y escritura (pero no ejecución), el ''5'' permite lectura y ejecución (pero no escritura), el ''4'' solo lectura, y así sucesivamente. Para modificar los permisos se utiliza el comando ''chmod''. Por ejemplo, para agregar a un archivo que hemos creado el bit de ejecución (útil para scripts, pues en GNU/Linux los archivos no son ejecutables por defecto), podemos hacer lo siguiente: chmod u+x elarchivo Para especificar que a ese archivo tengan acceso de escritura los usuarios del grupo, podemos hacer esto: chmod g=w elarchivo Y para especificar que el resto de los usuarios tenga acceso de solo lectura, podemos hacer esto: chmod o=r Estos tres permisos podrían combinarse de la siguiente manera: chmod u=rwx,g=rw,o=r elarchivo Una manera de referirse a los propietarios, el grupo y los demás es como "todos" (''a''). De modo que también podría haberse hecho esto: chmod a=r,ug=w,u+x elarchivo Otra forma de lograr lo mismo pero invirtiendo la forma de asignación: chmod a=rwx,go-x,o-w elarchivo Como vemos, establecer varios permisos de una vez puede resultar un poco engorroso de teclear, y aqui es donde tiene utilidad práctica el modo octal, que nos permite establecer todos los permisos anteriores haciendo solo esto: chmod 764 elarchivo Nótese que en este comando el primer dígito establece los permisos para el propietario del archivo, el segundo dígito para el grupo del archivo, y el tercer dígito para el resto de los usuarios y grupos. ===== La recursividad ===== Ahora bien, el comando ''chmod'' también permite el argumento ''%%-R%%'' que aplica los permisos de manera recursiva en un directorio y todos los subdirectorios que contenga, pero con esto debemos tener cuidado, especialmente al asignar permisos de ejecución. Si por algun motivo necesitásemos asignar privilegios muy permisivos para todos los usuarios recursivamente sobre todos los subdirectorios y archivos dentro de un directorio, podría hacerse así: chmod -R a=rwX eldirectorio Nótese que la X está en mayúscula; si se coloca en minúscula se asignará el permiso de ejecución no solo a los directorios, sino también a los archivos, algo que desde el punto de vista de la seguridad no es recomendable. Por ejemplo, algunos principiantes en GNU/Linux suelen experimentar dificultades con los permisos al crear un sitio web digamos en ''/var/www/elsitioweb/'', y para garantizar que el sitio funcione, intentan solucionarlo de la siguiente manera: chmod -R 777 /var/www/elsitioweb Obviamente, ahora el sitio web funciona y todos tienen acceso a el, pero como los permisos se aplicaron de manera recursiva, ahora cada directorio del sitio tiene permiso de escritura para todos los usuarios del equipo y cada archivo tiene permisos de escritura y ejecución, por lo que se ha creado una vulnerabilidad innecesaria que algún usuario malintencionado podría aprovechar para alterar el sitio web, intentar escalar privilegios para leer o modificar información privada de otros usuarios, instalar o distribuir software malicioso, usar el equipo para atacar otros equipos de la red, etc. Una manera de arreglar esto y garantizar un buen compromiso entre funcionalidad y seguridad es la siguiente: chown -R elusuario:www-data /var/www/elsitioweb chmod 755 /var/www/elsitioweb find /var/www/elsitioweb -type d -print0 | xargs -0 chmod 755 find /var/www/elsitioweb -type f -print0 | xargs -0 chmod 644 Esto envía al sistema las siguientes instrucciones: * El directorio ''/var/www/elsitioweb/'' tendrá como propietario a **elusuario** y pertenecerá al grupo **www-data** (usualmente el grupo al que pertenecen los servicios web). * El directorio del sitio web tendrá permisos de acceso y listado para todos los usuarios y grupos, pero solo el propietario tendrá permisos de escritura. * Entonces, encontrar recursivamente todos los subdirectorios del sitio y asignar sobre estos los permisos de acceso, listado y escritura para el propietario, y solamente acceso y listado para el resto. * Luego, encontrar recursivamente todos los archivos del sitio y asignar permisos de lectura y escritura para el propietario y solamente lectura para el resto. Este esquema de permisos no impide que posteriormente se asignen permisos puntuales sobre algún archivo o directorio, según sea necesario; por ejemplo, de haber algún archivo con información de autentificación, se le pueden asignar permisos ''640'', etc. A propósito de los dos últimos comandos: como observarán, si en el comando find se especifica que busque solo directorios (''%%-type d%%'') nunca devolverá archivos, y si se especifica que busque archivos (''%%-type f%%'') nunca devolverá directorios. De esta manera pueden asignarse permisos recursivos precisos. Notarán que se utiliza además la combinación del comando ''find'' con ''xargs'', devolviendo los nombres de archivos o directorios encontrados como cadenas de texto delimitadas por el carácter nulo (''%%-print0%%'') y esto se le pasa al comando ''xargs'' habilitando el soporte para cadenas delimitadas en nulo (''%%-0%%''), para que entonces sobre dichos archivos o directorios se ejecute el comando ''chmod''. Puede parecer innecesariamente complejo, pero esto permite cambiar recursivamente permisos sobre archivos o directorios cuyos nombres pudieran contener espacios y saltos de línea. Aunque el comando ''find'' también incluye parámetros para la ejecución de comandos, lo consigue lanzando un intérprete de comandos para cada archivo encontrado, lo cual consume recursos, mientras que la variante de ''xargs'' es más eficiente (aunque tiene sus propios límites impuestos por el sistema operativo, que pueden averiguarse con el comando ''%%xargs --show-limits%%''). ===== La "pegajosidad" o permanencia ===== Además de estos bits de propiedades, existe otro menos conocido pero muy útil (sobre todo para directorios compartidos) llamado "bit pegajoso" (en inglés //sticky bit//) que cuando se antepone a los otros, permite que solo el superusuario (root) o el propietario del archivo pueda mover o eliminar un objeto (sin este permiso, cualquier usuario con permisos de escritura podría hacerlo). Entonces, si un usuario ejecuta los siguientes comandos: mkdir ~/upload chmod 1730 ~/upload El conjunto de instrucciones que se está enviando al sistema operativo es este: Crea en mi directorio inicial un subdirectorio llamado ''upload'', y cambia el modo de permisos de dicho subdirectorio estableciendo lo siguiente: * Para el propietario (o sea, yo mismo), la posibilidad de acceder al directorio y listar sus contenidos, renombrarlo, moverlo o eliminarlo, y también de crear, modificar, mover o eliminar archivos en dicho subdirectorio. * Para los usuarios en el mismo grupo, la posibilidad de acceder al directorio y crear y modificar archivos (pero no de listar el contenido del directorio), y la posibilidad de eliminar o mover archivos, pero solo los archivos que que hayan creado ellos. * Para el resto de los usuarios, denegar cualquier tipo de permiso. ===== La personificación ===== Además de la propiedad de permanencia (''1''), el dígito octal que se antepone a los otros tres puede representar dos propiedades adicionales, conocidas como ''setgid'' (''2'') y ''setuid'' (''4''), que pueden sumarse al valor de la permanencia para establecer varios permisos simultáneamente. Estas dos propiedades pueden ser útiles, pero también pueden constituir una de las mayores vulnerabilidades de un sistema, por lo que deben utilizarse con mucha precaución, pero de todas formas es conveniente conocerlas. Supongamos que el superusuario crea en un directorio compartido un script al cual asigna los permisos ''5750''. Es decir, el archivo tiene propiedades de lectura, escritura y ejecución para el propietario, lectura y ejecución para el grupo, y el resto de los usuarios y grupos no tiene acceso. Adicionalmente, ningún usuario que no sea el creador puede eliminar el script, y aquí viene la novedad: el usuario que tenga permisos para ejecutar el script lo hace tomando temporalmente la identidad del creador. La propiedad ''sgid'' hace algo similar, pero en este caso tomando la identidad del grupo al que pertenece el archivo. ===== La inmutabilidad y otras hierbas ===== Otro bit muy útil y tampoco muy conocido, es el "bit de inmutabilidad" (''i''), que como su nombre lo indica, permite establecer la inmutabilidad sobre el archivo al que se aplique. En otras palabras, nadie podrá modificar, mover o eliminar el archivo (ni siquiera el superusuario) mientras el archivo tenga el bit inmutable habilitado. Por ejemplo, supongamos que deseamos que nadie pueda modificar el nombre del equipo, podemos lograrlo con el siguiente comando: chattr +i /etc/hostname El comando ''chattr'' permite establecer o quitar más atributos; otro de los útiles es el de la adición (''a''). Si deseamos que pueda escribirse a un archivo, pero no eliminar el contenido que ya existe en este, sino solamente añadir contenido nuevo, podemos utilizar el siguiente comando: chattr +a elarchivo ===== La máscara de creación ===== Puede que a estas alturas esté pensando: bueno, todo eso está muy bien, pero es bastante engorroso estar asignando constantemente permisos, ¿como sabe el sistema que permisos asignar por defecto a los diferentes directorios y archivos, y cómo podría uno cambiarlos? Para esto existe el comando ''umask'', que permite establecer una variable de entorno que automáticamente quita los permisos seleccionados sobre los archivos y directorios que se creen (asumiendo como base ''777'' para directorios y ''666'' para archivos). Usualmente no se utiliza el comando directamente porque la mayoría de las distribuciones establecen su valor por defecto a ''022'', en los archivos ''/etc/bashrc'' o ''/etc/profile''. Esto hace que por defecto los directorios se crean con los permisos ''755'' y los archivos con los permisos ''644''. Nótese que por seguridad cualquier archivo recién creado tiene deshabilitado el bit de ejecución, ya que ''umask'' no permite habilitarlo, hay que hacelo manualmente. Si uno quisiera por ejemplo que en su directorio de usuario todos los directorios por defecto se creen con permisos ''750'' y los archivos con permisos ''640'', podría colocar en el archivo ''%%~/.bashrc%%'' o en ''%%~/.profile%%'' el siguiente comando: umask 027 Para conocer la máscara que está activa, podemos ejecutar los comandos ''%%umask -p%%'' o también ''%%umask -pS%%'' ===== Las listas de control de acceso ===== Existe una manera adicional de establecer permisos a un nivel más detallado mediante listas de control de acceso, que pueden instalarse mediante el paquete **acl**. Este paquete tiene dos comandos muy útiles: ''getfacl'' y ''setfacl''. El comando ''getfacl'' permite consultar los valores de control de acceso de un directorio o archivo cualquiera (con el argumento ''%%-R%%'', la consulta será recursiva). Por ejemplo, sobre el directorio de configuración del sistema: getfacl /etc/ Debería aparecer un resultado como este: # file: etc # owner: root # group: root user::rwx group::r-x other::r-x Obsérvese que los permisos para el directorio ''/etc/'' deberían coincidir con los que aparecen al ejecutar ''%%ls -l /%%'': drwxr-xr-x 90 root root 4096 May 12 22:33 etc Para establecer las listas de control de acceso sobre un archivo o directorio, se utiliza el comando ''setfacl''. Por ejemplo, para prohibir cualquier tipo de permisos sobre un directorio y todo su contenido a todos los otros usuarios (es decir, diferentes al propietario o el grupo), podría ejecutarse este comando: setfacl -R -P -m o::---,d:o::--- ruta Expliquemos brevemente este comando: * El argumento ''%%-R%%'' (también puede usarse ''%%--recursive%%'') significa que las ACL se establecerán de manera recursiva. * El argumento ''%%-P%%'' (también puede usarse ''%%--physical%%'') significa que no se seguirá ningún enlace simbólico. * El argumento ''%%-m%%'' significa que se modificarán las ACL (también puede usarse ''%%--modify%%''). * La opción ''%%o::---%%'' (también puede usarse ''%%other::---%%'') significa que para los otros usuarios no se permitirá lectura, escritura ni ejecución. * La coma permite establecer múltiples ACL, y a continuación de esta se repiten los permisos ya establecidos, pero anteponiendo el prefijo ''%%d:%%'' (también puede usarse ''%%default:%%''), lo cual cuando se aplica a un directorio, tiene el efecto de establecer dichos permisos de manera predeterminada para cualquier archivo que se cree en dicho directorio. * Finalmente, el último argumento es la ruta del directorio o archivo. Para asignar permisos específicos, a veces es necesario combinar varias ACL. Para ilustrarlo mejor, pongamos un ejemplo. Supongamos que en una estación de trabajo tenemos bajo el directorio ''/var/www/'' varios subdirectorios con aplicaciones web hospedadas mediante //virtualhosts// de **Apache**, y que deseamos dar solamente a un usuario identificado como **mengano** la posibilidad de listar, crear, eliminar y modificar archivos de una aplicación en el subdirectorio ''/var/www/aplicacion/'', pero prohibirle el acceso a los otros subdirectorios de aplicaciones. Asumiremos además que el subdirectorio ''/var/www/aplicacion/'' tendrá como propietario al usuario **fulano**, y pertenecerá al grupo **www-data**. Lo primero es cerciorarnos de que el usuario **mengano** tenga acceso de entrada (pero no de otro tipo) a los directorios adecuados para poder recorrer la ruta hasta el directorio de la aplicación: setfacl -m u:mengano:--x /var setfacl -m u:mengano:--x /var/www Lo segundo es evitar que el usuario tenga acceso a las aplicaciones hospedadas: setfacl -R -m u:mengano:---,d:u:mengano:--- /var/www/* Entonces, nos cercioramos de que el usuario tenga acceso a la aplicación deseada: setfacl -R -m u:mengano:rwX,d:u:mengano:rwX /var/www/aplicacion Nótese que en el comando anterior la X está en mayúscula, el principio es el mismo que en el comando ''chmod''. Luego, nos cercioramos de que por defecto los nuevos directorios y archivos de la aplicación sigan siendo propiedad del grupo: chmod -R g+s /var/www/aplicacion Una vez hecho esto, nos cercioramos de que el propietario (en este caso, **fulano**) siga teniendo por defecto acceso a cualquier directorio o archivo, aunque los cree **mengano**: setfacl -R -m d:u:fulano:rwX /var/www/aplicacion Finalmente, el usuario **mengano** puede crear un enlace simbólico en su directorio inicial para un acceso rápido a los archivos de la aplicación: ln -s /var/www/aplicacion ~/aplicacion Si por algún motivo deseásemos eliminar todas las ACLs que recién creamos, podríamos ejecutar los siguientes comandos: setfacl -R -b -k /var/www/aplicacion setfacl -b -k /var/www setfacl -b -k /var El comando ''setfacl'' tiene otras utilidades como establecer la máscara de creación por defecto, asignar permisos tomándolos de un archivo o de la entrada estándar, etc. Recomendamos consultar el manual para más detalles. ===== Los puntos de montaje ===== Existe además otra forma de establecer ciertos permisos, y es mediante las opciones de montaje. Por ejemplo, supongamos que tenemos un servidor en producción que deseamos asegurar contra posibles ejecuciones de código malicioso en el espacio de almacenamiento temporal. En las versiones recientes de GNU/Linux, se utiliza un área de memoria compartida proveniente de la memoria virtual del sistema (es decir, RAM + Swap), que usualmente se monta en ''/dev/shm/''. En el archivo ''/etc/fstab'' usualmente debería haber una línea como esta: none /dev/shm tmpfs defaults 0 0 Podemos modificarla para que quede así: none /dev/shm tmpfs defaults,nodev,noexec,nosuid,noatime,nodiratime 0 0 Luego ejecutamos el siguiente comando para remontar con las nuevas opciones: mount -a Con las opciones ''noexec'', ''nosuid'' y ''nodev'', estaríamos protegiéndonos contra cualquier archivo temporal que intente la ejecución, personificación, y el acceso directo a los dispositivos de bloque especiales del sistema (las unidades de almacenamiento). Las opciones ''noatime'' y ''nodiratime'' es para prevenir que el sistema actualice constantemente los tiempos de acceso a los archivos y directorios en ese sistema de archivos, lo cual puede implicar una penalización en rendimiento. Y a propósito de las optimizaciones, si se tiene suficiente cantidad de RAM, una manera de aumentar el rendimiento (especialmente en servidores donde trabajan múltiples usuarios simultáneamente) es aumentando la capacidad de este espacio de memoria volátil compartida: mount -t tmpfs -o remount,size=512M /dev/shm Si nuestro sistema por algún motivo no tuviese en ''/etc/fstab'' esta entrada, es recomendable crear una partición dedicada para ''/tmp/'' para así poder personalizar las opciones de montaje de dicho directorio. No obstante, puede que ya tengamos nuestro sistema particionado y resulte inconveniente reparticionarlo, ¿qué hacer entonces? Muy sencillo, montar un archivo como una partición virtual. Por ejemplo, para crear una partición virtual de 1 GiB para ''/tmp/'' utilizando el espacio disponible en ''/var/'', podríamos ejecutar los siguientes comandos: mkdir -p /var/img dd if=/dev/zero of=/var/img/tmp.bin bs=1 count=0 seek=1G mkfs -t ext4 /var/img/tmp.bin mkdir /tmp Una vez realizado lo anterior, editamos el archivo ''/etc/fstab'' para agregar una entrada como esta (nótese el uso de la opción de montaje ''loop'' para especificar que el dispositivo es virtual): /var/img/tmp.bin /tmp ext4 loop,rw,nodiratime,noatime,noexec,nodev,nosuid 0 0 Entonces, remontamos y nos cercioramos de que el punto de montaje tenga los permisos adecuados: mount -a chmod 1777 /tmp Nótese que aunque al directorio ''/tmp/'' teóricamente se le han asignado los permisos de ejecución para todos los usuarios, ningún archivo de dicho directorio o sus subdirectorios podrá ejecutarse, debido a la opción de montaje ''noexec'' anteriormente establecida. Para el directorio ''/var/tmp/'' podría seguirse un acercamiento similar. Hay quienes recomiendan que este directorio sea solo un enlace simbólico a ''/tmp/'', pero esto tiene el inconveniente de que el directorio ''/var/tmp/'' pierde parte de su funcionalidad, que es garantizar que los datos que contenga persistan a un reinicio. Para conocer con qué opciones están montados los diferentes sistemas de archivo, basta con ejecutar ''mount'' sin argumentos. ===== Para los más paranoides ===== Para aquellos a quienes todo esto parezca poco, se alegrarán en conocer que existe una manera adicional de establecer permisos conocida como **SELinux** que se utiliza principalmente para definir roles y políticas de ejecución para los servicios, pero esto es otra historia, asi que por ahora damos por terminado este artículo y **SELinux** queda para estudio individual. ;) ===== Atribuciones ===== * **Autor:** Hugo Florentino