7.1.3. SSH como servidor FTP

Nota

Para la instalación y configuración de un servidor SSH consulte el apartado referente la administración remota.

Bajo el epígrafe trataremos de afinar la configuración de un servidor SSH para que cumpla las veces de un servidor FTP. Esto supone la posibilidad de implementar los siguientes aspectos, aparte de la obvia transmisión de ficheros:

  • Cortar la conexión tras un tiempo de inactividad.

  • Limitar el número de conexiones simultáneas.

  • Habilitar la transferencia anónima.

  • Enjaular usuarios.

  • Usuarios virtuales.

De todas ellos, sólo el último no es posible, al menos no si pretendemos realizar el acceso con usuarios realmente virtuales. Restringir el acceso a otros servicios, en cambio, sí es posible.

Antes de entrar en harina, definiremos un grupo ftpusers al que pertenecerán todos aquellos usuarios de los que queremos que su relación con el servidor se limite a la transferencia de ficheros:

# addgroup --system ftpusers

Además, nos proponemos implementar dos versiones distintas:

  1. Un servidor FTP puro, que sólo permita la conexión mediante un cliente, a la manera de como lo hacen los servidores FTP tradicionales.

  2. Un servidor FTP con esteroides, que habilite también el uso de scp e incluso de un script que permita el cambio de contraseña al usuario.

Advertencia

Es preciso notar, antes de empezar que, aunque hablemos de servidor FTP, lo que implementamos es un servidor sFTP, que no es lo mismo, aunque sirva para lo mismo: no habrá dos canales independientes ni podrán usarse clientes FTP (aunque muchos como Filezilla también implementan el protocolo sFTP)

7.1.3.1. FTP puro

Lograr esto es relativamente sencillo gracias a que el servidor de openssh dispone de un servidor sFTP interno, que propicia que se puedan hacer enjaulamientos sin necesidad de preparar jaulas.

7.1.3.1.1. Configuración básica

Basta añadir a /etc/ssh/sshd_config las siguientes líneas:

Match Group ftpusers
   ChrootDirectory         %h
   ForceCommand            internal-sftp
   X11Forwarding           no
   AllowTcpForwarding      no
   ClientAliveInterval     120
   ClientAliveCountMax     0

Es decir, creamos una configuración particular para los usuarios pertenecientes a grupo ftpusers para que sólo puedan usar el servidor como servidor FTP, no puedan hacer túneles TCP, ni arrancar aplicaciones gráficas, se desconecten automáticamente tras 2 minutos de inactividad y estén enjaulados dentro su propio directorio personal (%h).

Advertencia

Para que el enjaulamiento funcione es necesario que todos los directorios que constituyen la ruta de la jaula pertenezcan al administrador[1].

Nota

El resto de aspectos no básicos que incluiremos en este FTP puro, son tambien aplicables al FTP con esteroides.

7.1.3.1.2. Acceso anónimo

Definitivamente, no es buena idea permitir un acceso anónimo ya que este suele incluirse para permitir la descarga de ficheros y, para esto, es mejor usar un servidor web, por ejemplo. Si, a pesar de todo, deseamos permitirlo, debemos primero crear un usuario adecuado:

# adduser ftp --ingroup ftpusers --no-create-home --home /srv/ftp/anonymous --shell /bin/false --disabled-password --gecos "Anonimo"
# useradd anonymous -g $(getent group ftpusers | cut -d: -f3) -M -d /srv/ftp/anonymous/ -s /bin/false -c "Anonimo" -o -u $(id -u ftp)
# mkdir -p /srv/ftp/anonymous

Lo que hacemos es crear un usuario, ftp, y otro usuario llamado anonymous, que en realidad es el mismo porque comparte uid con él.

Además debemos usar el módulo pam_ftp en la autenticación, por lo que deberemos añadir en /etc/pam.d/sshd justamente antes de que se incluya la configuración de autenticación la línea que carga ese módulo:

auth    sufficient      pam_ftp.so      ignore
# Standard Un*x authentication.
@include common-auth

Advertencia

Hacer esto es una pésima idea. Incluso la página de manual del módulo advierte de que «this module is not safe and easily spoofable».

7.1.3.1.3. Limitación de conexiones

El servidor SSH no permite directamente la limitación en el número de conexiones, ni totales ni por usuario; pero puede lograrse su implementación actuando sobre el proceso de autenticación.

Como las sessiones de FTP, no abren para el servidor consolas interactivas, es absolutamente inútil usar pam_limit, ya que maxlogins y maxsyslogins sólo son aplicables cuando se abren shells en el servidor. Por tanto, añadir al fichero de configuración una línea de este tipo:

@ftpusers   -     maxlogins   2
%ftpusers   -     maxlogins   4

que, teóricamente, limitaría el número máximo de sesiones parsa cada usuario de ftpusers a 2 y el total de sesiones para todos los integrantes del grupo a 4 es absolutamente inútil[2].

Como esta solución es inútil, hemos de aplicar otra basada en la creación de un script que ejecute pam_exec. Es éste.

Para aplicar el script basta con añadir al final de /etc/pam.d/sshd la siguiente línea (suponemos alojado el script en /usr/local/bin):

session  required    pam_exec.so  quiet /usr/local/bin/sftp_limit.sh max_per_user=2

Advertencia

El script es muy simple y, aunque sólo se aplica a los grupos de usuarios que le indiquemos (ftpusers en nuestro caso), en el caso del maxclients y max_per_ip cuenta cualquier conexión SSH, la haga quien la haga. Como consecuencia, todos los usuarios que acceden por SSH contribuyen a alcanzar el máximo, aunque no les afecte.

7.1.3.2. Cuota

Ver también

Consulte cómo crear cuotas de disco.

7.1.3.3. FTP con esteroides

En este caso, la dificultad estriba en que para que podamos enjaular usuarios, necesitamos construir una jaula con todo lo necesario. Recordemos, además, que nuestro propósito es permitir al usuario:

  • El acceso sFTP.

  • El uso de scp.

  • El cambio de contraseña.

Nota

Si sólo nos interesaran las dos primeras acciones, podríamos usar rssh.

Para resolver la papeleta tiraremos —cómo no— de script. La idea es crear uno que dependiendo de qué cliente usemos (si ssh, si scp o si sftp) indique al servidor cómo debe comportarse (pedir contraseña, permitir la copia remota o ejecutar el servidor sFTP, respectivamente)[3]. Además el propio script se encargará de realizar el enjaulado.

Necesitamos cinco ingredientes:

  1. el propio script que colocaremos en /usr/local/bin.

  2. La utilidad fakechroot que permite enjaular con un usuario distinto del administrador:

    # apt-get install fakechroot
    
  3. El directorio donde haremos el enjaulamiento (/srv/ftp):

    # mkdir -p /srv/ftp
    
  4. Asegurarnos de que /etc/ssh/sshd_config contiene descomentada la línea[4][5]:

    Subsystem   sftp  /usr/lib/openssh/sftp-server -d %d
    
  5. Un grupo al que pertenezcan los usuarios del FTP y un usuario de pruebas[6]:

    # addgroup --system ftpusers
    # adduser paco --ingroup ftpusers --no-create-home --home /home/paco --gecos "PacoFTP" --shell /bin/sh
    # mkdir -p /home/paco/.ssh
    # chown -R paco:ftpusers /home/paco
    

Hecho esto, tantearemos dos soluciones: una en que todos los usuarios enjaulados comparten una jaula común; y otra en la que cada usuario se enjaula en una distinta.

Creación de jaulas

Antes, sin embargo, es preciso aclarar cómo crear jaulas apropiadas. En principio, una jaula es un subárbol del sistema de ficheros en que se encierra la acción de un usuario o programa. Esto implica, que dicho subárbol debe contener todos los componentes (programas y librerias) necesarios para que se puedan desarrollar dentro de él las acciones para la que ha sido concebida. Por ejemplo, si queremos que un usuario enjaulado pueda copiar ficheros es obvio que dentro de la jaula debe encontrarse el ejecutable cp.

Por tanto, para crear una jaula primero debemos determinar cuáles son los ficheros que necesitaremos dentro de ella. En nuestro caso, son exclusivamente estos[7][8]:

/etc/nsswitch.conf
/etc/ld.so.conf
/etc/ld.so.cache
/etc/ld.so.conf.d/*
/lib/x86_64-linux-gnu/libnss_compat.so.2
/lib/x86_64-linux-gnu/libnss_files.so.2
/lib/x86_64-linux-gnu/libnsl.so.1
/usr/bin/scp
/usr/lib/openssh/sftp-server

O sea, los ficheros de configuración y las librerías necesarios para resolver usuarios, y los dos programas que se usarán dentro de la jaula[9]. Por supuesto, a esta lista es necesario añadir:

  • Las librerías que usan los dos programas[10].

  • Algunos dispositivos (/dev/null y /dev/zero).

Para evitarnos la tediosa tarea hacer a mano la jaula, hemos escrito… otro script, que admite por la entrada estándar la lista de ficheros y exige como argumento la dirección que ocupará la jaula. Así, si almacenamos la lista anterior en req.txt y queremos crear la jaula en /srv/ftp, podemos hacer lo siguiente:

# ./enjaular.sh /srv/ftp < req.txt

Advertencia

Recuerde que los nuevos usuarios que cree no se añadirán automáticamente al /etc/passwd de la jaula.

7.1.3.3.1. Jaula única

Advertencia

Es necesario que lea (y haga) los preliminares de FTP con esteroides.

Nuestra primera intención es enjaular a todos los usuarios del FTP, dentro de /srv/ftp. Para ello debemos añadir a /etc/ssh/sshd_config, lo siguiente:

Match Group ftpusers
   X11Forwarding        no
   AllowTcpForwarding   no
   ForceCommand         /usr/local/bin/rssh.sh /srv/ftp

A continuación debemos copiar la lista de ficheros requeridos en un fichero (p.e. /srv/ftp) y crear la jaula:

# ./enjaular.sh /srv/ftp`< req.txt

y, finalmente, copiar el script en la ruta apropiada:

# cp /path/donde/este/rssh.sh /usr/local/bin

Además, /home debe aparecer dentro de la jaula, así que:

# ln -s /home /srv/ftp/home

Nota

Recuérdese que se fijó el directorio personal del usuario como el habitual /home/usuario, pero, como estos usuarios siempre actúan enjaulados, su directorio real es /srv/ftp/home/usuario,

Nota

Con esta solución, al usar scp, el directorio remoto de trabajo es la raíz de la jaula no el directorio personal, por lo que algo como:

$ scp fichero.txt paco@servidor:

intentaría copiar fichero.txt en /srv/ftp y no en /srv/ftp/home/paco[11].

Advertencia

Obsérvese que la shell del usuario es sh, porque así lo requiere el servidor SSH para ejecutar nuestro script. Esta circunstancia provoca que el usuario puede autenticarse sin restricciones en cualquier otro servicio. Por ejemplo, a través de login, si alcanza físicamente el servidor. Para evitar este inconveniente podemos añadir al final de /etc/pam.d/common-session, las líneas:

session [success=2 default=ignore] pam_succeed_if.so service = sshd
session [success=1 default=ignore] pam_succeed_if.so user notingroup ftpusers
session required                   pam_deny.so
session required                   pam_permit.so

las cuales harán que el acceso falle cuando el servicio no sea sshd y el usuario pertenezca al grupo ftpusers.

Nota

El usuario puede, incluso, usar autenticación con claves si quiere, aunque no podrá usar ssh-copy-id para subirla, ni utilizar más de una pareja de claves:

$ scp .ssh/id_ecdsa.pub paco@servidor:~/.ssh/authorized_keys

7.1.3.3.2. Jaula personal

Advertencia

Es necesario que lea (y haga) los preliminares de FTP con esteroides.

Si queremos una jaula personal distinta para cada usuario es obvio que debemos construirla, pero crear físicamente una distinta para cada uno es un desperdicio de espacio en disco. Podríamos usar enlaces duros, pero sigue siendo una solución bastante guarra. Una estrategia más elegante es la siguiente:

  • Se construye la base de la jaula que contiene todos los ficheros de configuracion, ejecutables y librerías necesarios en una ubicación, p.e. /var/lib/sftp_jail.

  • Cuando un usuario de FTP accede, sin tener abierta otra sesión previa, se crea la vuelo la jaula fusionando con overlay el directorio anterior con el directorio personal del usuario. Cuando el usuario abandona el FTP sin dejar otras sesiones abiertas, se destruye la jaula.

  • Al construir al vuelo la jaula, se crea también un fichero /etc/passwd con solamente una línea: la referente al usuario. Eso impedirá que dentro de la jaula haya información sobre el resto de usuarios.

Empecemos por indicar cuál es la configuración a añadir dentro de /etc/ssh/sshd_config:

Match Group ftpusers
   X11Forwarding        no
   AllowTcpForwarding   no
   ForceCommand         /usr/local/bin/rssh.sh %x/ftp

o sea, la misma que anteriormente, excepto por el hecho de que ahora el directorio de enjaulamiento es %x/ftp. %x representa el directorio XDG_RUNTIME_DIR que crea systemd para cada usuario[12].

Como en el caso anterior hemos de crear la base de jaula:

# ./enjaular.sh /var/lib/sftp_jail

Finalmente, crear al vuelo la jaula durante la autenticación, requiere manipular pam. El script sftp_jail.sh para pam_exec hace esto suponiendo, de modo predeterminado, que la jaula se montará en %x/ftp, que la base de la jaula está en /var/lib/sftp_jail y que la parte de la jaula que contiene los datos de usuario está en /srv/ftp/nombre_usuario [#]_.

Para que pam ejecute el script se ha preparado un fichero de automatización para pam-auth-update que, además, soluciona el problema de que el usuario pueda acceder al sistema con otros servicios:

# mv /path/a/sftp_jail /usr/share/pam-configs
# pam-auth-update

Por último, necesitamos que el directorio personal de usuario aparezca debajo en uno de los componentes que constituirá el sistema de ficheros fusionado, de modo que para cada usuario habrá que hacer lo siguiente:

# mkdir -p /srv/ftp/paco/home
# ln -s /home/paco /srv/ftp/paco/home

Notas al pie