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
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).
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:
r
). Si se habilita sobre un archivo, permite leer su contenido. Sobre un directorio, permite listar el contenido.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.x
). Si se habilita sobre un archivo, permite ejecutarlo. Sobre un directorio, permite acceder a el.Estos permisos pueden establecerse respectivamente para:
u
)g
)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.
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:
/var/www/elsitioweb/
tendrá como propietario a elusuario y pertenecerá al grupo www-data (usualmente el grupo al que pertenecen los servicios web).
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
).
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:
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.
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
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
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:
-R
(también puede usarse --recursive
) significa que las ACL se establecerán de manera recursiva.-P
(también puede usarse --physical
) significa que no se seguirá ningún enlace simbólico.-m
significa que se modificarán las ACL (también puede usarse --modify
).o::---
(también puede usarse other::---
) significa que para los otros usuarios no se permitirá lectura, escritura ni ejecución.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.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.
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 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. ;)