Este artículo explica cómo configurar un acceso SFTP restringido para un usuario no privilegiado. Restringido en el sentido de que este usuario sólo pueda moverse dentro del directorio al que se le da acceso, y sea incapaz de siquiera ver qué archivos existen fuera del mismo. Es el ejemplo clásico en el que se debe limitar el acceso a un desarrollador o cliente al directorio de trabajo de su sitio Web, sin que pueda "salir" fuera del mismo (chroot).
A modo de ejemplo, se desea crear el usuario "linuxito", el cual sólo podrá acceder a archivos dentro del directorio /var/www/linuxito
. Más aún, se desea que para la vista de este usuario no existan en el servidor otros archivos que no sean los que posee este directorio. Desde su perspectiva, el directorio /var/www/linuxito
será su directorio raíz (/
). De esto se trata un chroot.
El primer paso consiste en crear el usuario "linuxito" negando el acceso a una shell (/usr/sbin/nologin
) y sin crear un directorio $HOME
(opción -M
):
root@cloud:~# useradd -c "Acceso SFTP Linuxito" -M -s /usr/sbin/nologin linuxito
Notar que la nueva entrada en el archivo /etc/passwd
indica que el $HOME
del usuario es /home/linuxito
, si embargo éste no ha sido creado:
root@cloud:~# tail -n 1 /etc/passwd linuxito:x:1000:1000:Acceso SFTP Linuxito:/home/linuxito:/usr/sbin/nologin root@cloud:~# ll /home/ total 8 drwxr-xr-x 2 root root 4096 Feb 16 11:46 . drwxr-xr-x 24 root root 4096 Mar 7 11:22 ..
A continuación, asignar una contraseña al usuario "linuxito":
root@cloud:~# passwd linuxito Enter new UNIX password: Retype new UNIX password: passwd: password updated successfully
Si el mecanismo de autenticación utilizado será mediante clave pública, se deberá generar un par de claves DSA.
El directorio /var/www/linuxito
es actualmente propiedad del usuario con el que corre Apache (www-data
) y posee dos subdirectorios:
root@cloud:~# ll -d /var/www/linuxito drwxrwx--- 4 www-data www-data 4096 Mar 21 11:59 /var/www/linuxito root@cloud:~# ll /var/www/linuxito total 16 drwxr-xr-x 4 root root 4096 Mar 21 11:59 . drwxr-xr-x 15 root root 4096 Mar 21 11:50 .. drwxrwx--- 10 www-data www-data 4096 May 2 2012 httpdocs drwxrwxr-- 11 www-data www-data 4096 Mar 18 13:54 private
Lo que se desea implementar es un chroot a /var/www/linuxito
cada vez que el usuario "linuxito" se conecte al servidor mediante SFPT. Afortunadamente, el servidor SSH lo implementa a través de la directiva ChrootDirectory
.
De acuerdo al manual del servidor SSH (man sshd_config
), cuando se implementa un chroot, todos los componentes de la ruta deben ser propiedad de root y no deben tener permisos de escritura para otros usuarios o grupos. Por ello, el siguiente paso consiste en ajustar los permisos de todos los directorios en la ruta.
Ajustar los permisos del directorio /var/www
(originalmente propiedad de www-data
):
root@cloud:~# chown root:root /var/www root@cloud:~# chmod 755 /var/www root@cloud:~# ll -d /var/www drwxr-xr-x 15 root root 4096 Mar 21 11:50 /var/www
Cabe destacar que cambiar el owner del directorio /var/www
puede afectar el funcionamiento de otros sitios. Aunque, por seguridad, /var/www
no debería ser capaz de escribir en el mismo.
Luego, hacer lo mismo con el directorio /var/www/linuxito
:
root@cloud:~# chown root:root /var/www/linuxito root@cloud:~# chmod 755 /var/www/linuxito root@cloud:~# ll -d /var/www/linuxito drwxr-xr-x 4 root root 4096 Mar 21 11:59 /var/www/linuxito
De manera opcional, es posible cambiar el directorio $HOME
del usuario "linuxito". Luego del chroot (una vez que el usuario ha sido autenticado exitosamente), el servidor SSH cambia el directorio de trabajo de la sesión al directorio home del usuario.
De esta forma, si se cambia el home de "linuxito" de /home/linuxito
a /httpdocs
, cada vez que acceda mediante SFTP será posicionado automáticamente en el directorio /var/www/linuxito/httpdocs
(aunque lógicamente desde su punto de vista se tratará del directorio /httpdocs
):
root@cloud:~# usermod -d /httpdocs linuxito root@cloud:~# grep "linuxito:" /etc/passwd linuxito:x:1000:1000:Acceso SFTP Linuxito:/httpdocs:/usr/sbin/nologin
Es importante notar que para que esto funcione, es un requisito que éste directorio sea propiedad del usuario. A su vez es necesario modificar los permisos sobre todos los archivos y subdirectorios dentro del sitio en cuestión, para que el nuevo usuario tenga total control sobre ellos:
root@cloud:~# cd /var/www/linuxito root@cloud:/var/www/linuxito# chown -R linuxito:www-data ./* root@cloud:/var/www/linuxito# chmod -R 775 ./* root@cloud:/var/www/linuxito# ll total 16 drwxr-xr-x 4 root root 4096 Mar 21 11:59 . drwxr-xr-x 15 root root 4096 Mar 21 11:50 .. drwxrwxr-x 9 linuxito www-data 4096 Mar 21 13:16 httpdocs drwxrwxr-x 11 linuxito www-data 4096 Mar 18 13:54 private
Finalmente resta configurar el servidor SSH para implementar el chroot, sólo para el usuario "linuxito":
root@cloud:~# nano /etc/ssh/sshd_config
Agregar las siguientes líneas:
# Acceso SFTP Linuxito Match User linuxito ForceCommand internal-sftp PasswordAuthentication yes ChrootDirectory /var/www/linuxito/ AllowTcpForwarding no MaxSessions 5
Si se va a utilizar autenticación mediante clave pública, cambiar la línea PasswordAuthentication yes
por PubkeyAuthentication yes
.
Y reiniciar el servicio:
root@cloud:~# service ssh restart [ ok ] Restarting OpenBSD Secure Shell server: sshd.
Si se necesitan varios usuarios SFTP para un mismo sitio, es posible crear un grupo "linuxito_sftp":
root@cloud:~# groupadd linuxito_sftp root@cloud:~# groups linuxito linuxito : linuxito root@cloud:~# usermod -G linuxito_sftp linuxito root@cloud:~# groups linuxito linuxito : linuxito linuxito_sftp
Y luego modificar la configuración del servidor SSH para que el chroot aplique al grupo:
Match Group linuxito_sftp
Habiendo finalizado la configuración, es posible verificar el acceso remoto:
emi@hal9000:~ % sftp -P 2222Esta dirección de correo electrónico está siendo protegida contra los robots de spam. Necesita tener JavaScript habilitado para poder verlo. Esta dirección de correo electrónico está siendo protegida contra los robots de spam. Necesita tener JavaScript habilitado para poder verlo. 's password: Connected to cloud.linuxito.com. sftp> pwd Remote working directory: /httpdocs sftp> ls _private _public config.php downloads home.html images index.html index.php pdf stats swf sftp> cd .. sftp> pwd Remote working directory: / sftp> ls httpdocs private sftp> quit emi@hal9000:~ %
En caso de errores al intentar conectar, verificar el log de accesos de SSH en el servidor (/var/log/auth.log
).
Notar que la sesión SFTP ve al directorio /var/www/linuxito
como si se tratase del directorio raíz. No es posible salir fuera de /var/www/linuxito
en la sesión SFTP, por lo que se ha logrado restringir el acceso al usuario "linuxito" de manera exitosa.
Por otro lado, tanto el no contar con una shell válida (/usr/bin/nologin
) en la configuración del usuario dentro del archivo /etc/passwd
, como el forzar el uso de SFTP para el usuario (ForceCommand internal-sftp
) en la configuración del servidor SSH, hacen que sea imposible para "linuxito" abrir una shell en el servidor.
Ahora bien, todo funciona perfecto y seguro pero aún queda resolver un inconveniente. Tal como está configurado hasta el momento, la única forma de que Apache pueda modificar un nuevo archivo subido por "linuxito" mediante SFTP, es que este le asigne permisos de escritura para todo el mundo (xx7
). Por supuesto, Apache debe tener permisos de escritura en la menor cantidad posible de archivos/directorios, pero a veces es necesario, por ejemplo para aquellos directorios donde los usuarios suben contenido (archivos). Desde el punto de vista de la seguridad, es un riesgo otorgar permisos de escritura para todos sobre cualquier archivo. Sin embargo, es posible asignar permisos automáticamente al grupo Apache (para aquellos archivos subidos por el usuario "linuxito" mediante SFTP) mediante una ACL por defecto.
Claro que antes se debe verificar que el sistema de archivos soporte ACL's (mount -a
).
Como se observa, el directorio /var/www/linuxito/httpdocs
no posee ACL (sólo permisos Unix):
root@cloud:/var/www/linuxito# ll -d httpdocs/ drwxrwxr-x+ 9 linuxito www-data 4096 Mar 21 13:43 httpdocs/ root@cloud:/var/www/linuxito# getfacl httpdocs/ # file: httpdocs/ # owner: linuxito # group: www-data user::rwx group::rwx other::r-x
A modo de ejemplo, supongamos que es necesario que todos los archivos subidos por SFTP a el directorio /var/www/linuxito/httpdocs
tengan permisos de escritura para Apache (www-data
). Para ello es posible utilizar la siguiente ACL por defecto:
root@cloud:/var/www/linuxito# setfacl -R -m default:user:linuxito:rwx httpdocs/ root@cloud:/var/www/linuxito# setfacl -R -m default:group:www-data:rwx httpdocs/ root@cloud:/var/www/linuxito# getfacl httpdocs/ # file: httpdocs/ # owner: linuxito # group: www-data user::rwx group::rwx other::r-x default:user::rwx default:user:linuxito:rwx default:group::rwx default:group:www-data:rwx default:mask::rwx default:other::r-x
Ahora veamos que sucede al subir un archivo por SFTP desde un cliente. Primero, crear un archivo:
emi@hal9000:~ % echo Hola > prueba.txt emi@hal9000:~ % ll prueba.txt -rw-r--r-- 1 emi wheel 5 Mar 21 10:38 prueba.txt
Luego, subirlo por SFTP. El puerto de SSH se indica con P mayúscula, a diferencia del comando ssh
:
emi@hal9000:~ % sftp -P 2222Esta dirección de correo electrónico está siendo protegida contra los robots de spam. Necesita tener JavaScript habilitado para poder verlo. Esta dirección de correo electrónico está siendo protegida contra los robots de spam. Necesita tener JavaScript habilitado para poder verlo. 's password: Connected to cloud.linuxito.com. sftp> pwd Remote working directory: /httpdocs sftp> put prueba.txt Uploading prueba.txt to /httpdocs/prueba.txt prueba.txt 100% 5 0.0KB/s 00:00 sftp> ls -l prueba.txt -rw-r--r-- 0 1000 1000 5 Mar 21 10:37 prueba.txt
Se observa que el archivo se sube con los mismos permisos que poseía en el cliente, pero con el nombre/grupo del usuario SFTP, en este caso "linuxito":
root@cloud:/var/www/linuxito# id linuxito uid=1000(linuxito) gid=1000(linuxito) groups=1000(linuxito)
Luego, al consultar los permisos extendidos por ACL, se observa que, a pesar de haber otorgado permisos de escritura para el grupo "www-data", éstos no se aplican a causa de la máscara (mask):
root@cloud:/var/www/linuxito# ll httpdocs/prueba.txt -rw-r--r--+ 1 linuxito linuxito 5 Mar 21 13:37 httpdocs/prueba.txt root@cloud:/var/www/linuxito# getfacl httpdocs/prueba.txt # file: httpdocs/prueba.txt # owner: linuxito # group: linuxito user::rw- user:linuxito:rwx #effective:r-- group::rwx #effective:r-- group:www-data:rwx #effective:r-- mask::r-- other::r--
Al subir un archivo por SFTP, la máscara ACL que se genera coincide con el permiso Unix para el grupo, el cual es tomado directamente desde los permisos Unix del archivo origen en el cliente:
emi@hal9000:~ % ll prueba.txt -rw-r--r-- 1 emi wheel 5 Mar 21 10:38 prueba.txt
Notar que el permiso Unix para el grupo en el archivo origen es 5
(r--
), al igual que la máscara ACL (mask::r--
).
La máscara ACL afecta a (limita) los permisos efectivos sobre todos los grupos y usuarios dentro de la ACL.
El problema se debe entonces a los permisos en el archivo origen en el cliente y la máscara de creación de archivos del servicio SFTP en el sistema de archivos del servidor. Una alternativa para resolver este inconveniente sería cambiar la máscara (umask
) para el servidor SSH a nivel sistema operativo, pero esto no impide que el cliente SFTP fuerce el uso de los permisos del archivo origen, lo cual crearía una máscara ACL que limite los permisos efectivos.
Luego de muchas horas investigando este inconveniente entre las ACLs, la máscara y los permisos seteados por el cliente, no logré una solución. Al menos no logré evitar que se cree esta máscara ACL, a partir de los permisos para el grupo, que limita efectivamente los permisos sobre los grupos (principalmente sobre el grupo "www-data" definido a través de la ACL por defecto en el directorio padre). Finalmente se me ocurrió que lo mejor sería configurar a "www-data" como grupo primario para el nuevo usuario. Por otro lado ésto no implica un riesgo alguno en cuanto a la seguridad del servidor, pues el acceso se encuentra restringido a un directorio específico mediante un chroot.
root@cloud:/var/www/linuxito/httpdocs# groups linuxito linuxito : linuxito remote_sftp root@cloud:/var/www/linuxito/httpdocs# usermod -g www-data -G linuxito,remote_sftp linuxito root@cloud:/var/www/linuxito/httpdocs# groups linuxito linuxito : www-data linuxito remote_sftp
Al implementar esta solución no tiene sentido seguir utilizando ACLs, por lo que simplemente es posible borrarlas y modificar el grupo para todos los archivos:
root@cloud:/var/www/linuxito/httpdocs# cd .. root@cloud:/var/www/linuxito# setfacl -R -b * root@cloud:/var/www/linuxito# chown -R linuxito:www-data * root@cloud:/var/www/linuxito# ll total 16 drwxr-xr-x 4 root root 4096 Mar 21 11:59 . drwxr-xr-x 15 root root 4096 Mar 21 11:50 .. drwxrwxr-x 9 linuxito www-data 4096 Mar 22 15:41 httpdocs drwxrwxr-x 11 linuxito www-data 4096 Mar 18 13:54 private
Referencias
man chroot man sshd_config man umask man setfacl man usermod man chmod
Fuente: linuxito