Configurar un servidor FTP en modo pasivo para Internet y red local

Redes más profesionales traen problemas más profesionales. Aquí una solución que no vi documentada en otra parte para solucionar un problema que podría llegar a ser típico en redes domésticas de profesionales.

Desde hace algún tiempo que tengo un espacio en mi servidor que comparto con amigos como un punto central para distribuirnos información. Este servicio funciona por FTP y ha pasado por varias evoluciones (y varios equipos), pero en la última implementación el servicio nunca quedó completamente bien, pero con algunos ajustes (y un cron que reiniciaba el servicio todos los días) funcionó durante un año, hasta que dejó de funcionar completamente cuando venció el certificado SSL que tenía. Luego de eso intenté realizar varios ajustes a la configuración sin éxito. Y después de investigar durante algunas horas se hizo la luz.

¿Y qué hiciste?

En muy resumidas cuentas cambié vsftpd por proftpd. En el camino tuve que hacer algunos ajustes a la configuración para que se ajustara a las necesidades específicas que tenía, hasta que quedó funcionando bien, salvo por un pequeño gran detalle: El servicio funcionaba solo hacia Internet o solo en la red local, pero no en ambas simultáneamente.

Investigando

El problema surgía en cómo funciona FTP en modo pasivo. Existe mucha documentación al respecto sobre el funcionamiento del modo pasivo como una forma de ordenar las conexiones de transferencias de datos que no sé si valga la pena explicar acá, pero hay documentos en Internet que lo explican bastante bien.

Y en mi caso particular era que el modo pasivo de FTP requiere que, en el caso que la conexión a internet esté tras un firewall y/o NAT, se especifique en la configuración del servidor cuál es la dirección a la que el cliente debe apuntar para establecer las conexiones secundarias de transferencias de datos. En simple y mayoría de los casos, que el servidor FTP anuncie por el modo pasivo la IP pública del servidor FTP. Cuando este anuncio falla, se establece la conexión pero fallará el acceso a los datos, arrojando errores como ECONNREFUSED – Connection refused by server, Server returned unroutable private IP address in PASV reply o 425 Can't open data connection for transfer of. Y esto sucede por la forma en que tiene que ir configurado. Entonces, si configuraba para que el comando PASV devolviera mi IP pública, no podía acceder desde mi red local, y si configuraba para que me deolviese la IP local, no funcionaba desde Internet.

Nota al margen: En los casos donde existe reenvío de puertos en algunas marcas de routers domésticos, es el propio router el encargado de devolver localmente la solicitud que se va a la dirección de internet que corresponde a sí mismo, en cuyo caso esta configuración no es necesaria. Este caso es más usual donde hay routers de grado profesional, o ante la presencia de firewalls con configuraciones estrictas.

Afortunadamente existe una configuración especial ProFTPD que permite que las directivas de configuración sean dinámicas, condicionadas por situaciones específicas. Esto se hace gracias al uso de clases y al módulo ifsession.

La configuración

Gracias a ProFTPD puedo indicar en la configuración que la IP que se anuncia como respuesta a PASV (que en el caso de ProFTPD es el parámetro MasqueradeAddress) cambie según la IP de origen de la conexión. Con esto, el objetivo es que la IP que se anuncie sea la pública si la conexión viene desde Internet, y la local de la red cuando la conexión sea local. Esto se logra definiendo las clases y luego la configuración condicional.

Definición de clases

Dentro del archivo de configuración de ProFTPD (por defecto en /etc/proftpd/proftpd.conf) crearemos dos clases de nombre wan y lan. Estas serán contrarias entre sí, de forma que se aplique la clase lan cuando la IP de origen esté dentro de nuestro segmento local y se aplique la clase wan en cualquier otro caso. Quedaría más o menos así, asumiendo nuestra red local como 192.168.1.0/24:

<Class lan>
  From 192.168.1.0/24
</Class>

<Class wan>
  From !192.168.1.0/24
</Class>

Nótese el signo de exclamación en la clase wan, que significa negación, es decir, que es válida cuando no se cumple esa condición. Esto es recomendable colocarlo al inicio del archivo de configuración.

Definición condicional del anuncio

Ahora que están las clases definidas, dentro del mismo archivo condicionaremos el parámetro MasqueradeAddress a estas clases. Aquí de ejemplo definiremos como IP local la 192.168.1.2 y a la IP pública en base a una dirección de servicio DDNS.

<IfClass lan>
  MasqueradeAddress 192.168.1.2
</IfClass>

<IfClass wan>
  MasqueradeAddress ejemplo.ddns.net
</IfClass>

Esto es recomendable hacerlo donde se explica el comando MasqueradeAddress, aproximadamente en la línea 65 del archivo.

Hecho este cambio podemos proceder a reiniciar el servicio proftpd. Y eso sería todo.

Algunas consideraciones

Para que esta configuración funcione se asume que los puertos del modo pasivo se encuentren correctamente configurados y definidos en la solución que tengan para la conexión a Internet. En mi caso tengo un router Mikrotik donde las configuraciones ya estaban hechas y probadas con anterioridad. Antiguamente no necesitaba hacer esta configuración y me bastaba con la IP pública, pero dado que ahora tengo reglas de firewall mucho más estrictas, debía hacer esta configuración para asegurarme que el servicio funcionase tanto dentro de mi LAN como para mis amigos desde Internet.