sftp y chroot trabajando juntos

Ayer tuve que enfrentarme a uno de esos problemas técnicos que había aparcado tiempo atrás, un poco defraudado cuando lo encontré por primera vez, y que ya puedo decir que funciona y entiendo.

El problema consistía en lo siguiente: la empresa va a contratar la instalación de un programa web a una persona externa. Ésta necesita acceso libre a ciertas carpetas que, al mismo tiempo, tienen que ser accesibles por el servidor web.

El programa se llama prestashop y el entorno de trabajo una máquina Debian con Apache como servidor de páginas y programas.

Acceso seguro a una parte del árbol de archivos

Lo primero es crear una cuenta de usuario en el sistema lo más limitada posible.

$ sudo adduser --no-create-home --home /tmp --shell /bin/false --ingroup www-data prestashop
...
$ getent passwd prestashop
prestashop:x:1001:33:Instalador Prestashop,,,:/tmp:/bin/false
$ 

Después creamos un directorio con propiedad completa de root (toda la ruta) y donde anclaremos las conexiones entrantes vía sftp del grupo del usuario.

$ sudo install -d -o root -g root -m 0755 /chroot/prestashop

Y, por último, toca configurar el servidor sshd para que acepte conexiones sftp limitadas al grupo del usuario especial:

Subsystem sftp /usr/lib/openssh/sftp-server -u 0002

UsePAM yes
Match Group www-data
        ChrootDirectory /chroot
        AllowTCPForwarding no
        X11Forwarding no
        ForceCommand internal-sftp

Esto es:

  1. Habilitamos el servidor interno sftp (con la máscara de usuario intentando que sea 0002).
  2. Le indicamos que emplee PAM como mecanismo de validación de credenciales.
  3. Establecemos condiciones especiales para las conexiones del grupo www-data:
    1. Enjaulamos la conexión al directorio /chroot .
    2. Impedimos reenvíos varios de X11 y TCP
    3. Forzamos a emplear sólo el ftp seguro interno.

Esto funciona excepto por un detalle: la máscara de usuario no queda correctamente establecida. Para ello vamos a añadir una modificación extra en el sistema: Editamos el archivo /etc/pam.d/sshd y añadimos la siguiente claúsula:

session optional pam_umask.so umask=0002

La explicación es que el programa internal-sftp no lee ninguno de los archivos de inicio de sesión (profile,login,rc,…) dado que no ejecuta ningún shell. Pero como emplea PAM para validar las credenciales sí que hará uso de esta característica.

Afinando el directorio inicial

En la configuración que vemos hasta ahora el usuario se encuentra con un paso previo para acceder a su espacio: debe cambiar al directorio prestashop o usarlo como ruta relativa al transferir información. No es bonito y muestra más de lo que debiera.

Mirando un poco más la documentación que anoto al final del artículo veo que es posible enjaular la conexión y darle ese toque profesional; para ello es necesario indicar al servidor ssh que el directorio jaula es /chroot pero que debe iniciar un servidor interno en el directorio de trabajo del usuario.

Subsystem sftp /usr/lib/openssh/sftp-server -u 0002

Match Group www-data
        ChrootDirectory /chroot
        ...
        ForceCommand internal-sftp -d %u

Es necesario crear entonces el directorio /chroot/prestashop con los permisos adecuados para que el usuario pueda trabajar con comodidad:

$ sudo install -d -o prestashop -g www-data -m 6760 /chmod/prestashop

Integración con el servidor web

Por ahora tenemos un directorio /chroot/prestashop donde nuestro usuario externo puede escribir con libertad, y donde todo queda de manera predeterminada con los siguientes permisos:

root@catalogo:/chroot# tree -pug
.
└── [drwxrwsrwx root     www-data]  prestashop
    └── [drwxrwsr-x prestashop www-data]  Pruebas
        ├── [-rw-rw-r-- prestashop www-data]  116981263_70d591cee5.jpg
        └── [-rw-rw-r-- prestashop www-data]  16789084225_c284b59b59.jpg

2 directories, 2 files
root@catalogo:/chroot# 

El propietario es prestashop, el grupo es www-data y los permisos son de lectura, escritura y paso para propietario y grupo en directorios y lectura y escritura para usuario y grupo en archivos.

Es decir, el servidor web tiene acceso para servir el contenido e incluso para modificarlo llegado el presumible caso de tener que actualizar la propia aplicación.

Así que podemos situar el directorio como el raíz para el propio servidor web, empleando la directiva DocumentRoot, pero ¿ y si tenemos que integrarla en un árbol más complejo ?

Suponiendo que nuestra instalación contemple varios servidores virtuales en una raíz como la siguiente:

/var/www/wordpress
/var/www/html/server1.com
/var/www/html/server2.com
/var/www/html/prestashop

Nos encontramos con que no podemos hacer que toda la ruta previa sea propiedad exclusivamente de root, ni excluir a cualquier otro grupo del acceso a esa parte del árbol.

Una solución práctica consiste en añadir el directorio jaula a nuestro árbol con un montaje de tipo bind de manera que ninguna de las dos partes sospeche nada raro.

$ sudo mount -o bind /chroot/prestashop /var/www/html/prestashop

y después hacerlo fijo en el arranque del sistema añadiendo lo siguiente a la tabla de montajes (/etc/fstab ):

/chroot/prestashop /var/www/html/prestashop none defaults,bind 0 1

 

Enlaces y referencias