ftp-logo.jpg

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 2222 Esta 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 2222 Esta 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

¿Quién está en línea?

Hay 5964 invitados y ningún miembro en línea