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:
- Habilitamos el servidor interno sftp (con la máscara de usuario intentando que sea 0002).
- Le indicamos que emplee PAM como mecanismo de validación de credenciales.
- Establecemos condiciones especiales para las conexiones del grupo www-data:
- Enjaulamos la conexión al directorio
/chroot
. - Impedimos reenvíos varios de X11 y TCP
- Forzamos a emplear sólo el ftp seguro interno.
- Enjaulamos la conexión al directorio
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
- OpenSSH/Cookbook/File Transfer with SFTP
- bad ownership or modes for chroot directory component
- chroot access to www folder
- How do I do ‘mount –bind’ in /etc/fstab?
- Setting a umask for chrooted sftp users
- Unix Permissions Calculator