FTP desde PHP entre instancias de EC2

Recientemente he tenido que configurar la conexión FTP entre 2 instancias de EC2, que puede ser bastante complicada si no tenemos en cuenta varios puntos.

Lo principal, es comprender que la conexión entre 2 instancias de EC2 es una conexión que se realiza entre 2 servidores protegidos por un cortafuegos, que además seguramente funcionen con una IP interna, y usen una IP pública asociada a su VPC.

Los problemas de FTP

La mayor parte de los problemas de FTP son fallos de diseño del propio protocolo (debido a su antiguedad), y no pueden ser solucionados de forma sencilla.

Cuando trabajamos con FTP en este tipo de entornos tenemos que tener en cuenta dos aspectos importantes:

El modo pasivo

Para conectar entre máquinas protegidas por cortafuegos, se usa el modo pasivo, donde se indica de forma directa los puertos de la conexión.

En la instancia que contiene el servidor FTP, debemos crear un Grupo de Seguridad que permita la conexión a un rango de puertos alto (por ejemplo, del 10.000 al 15.000).

Además, en el servidor FTP que estemos usando, debemos configurar el mismo rango de puertos como puertos a usar para las conexiones pasivas, de forma que todas las conexiones pasivas se realicen contra los puertos que hemos abierto.

En el lado del cliente, debemos indicar que la conexión es pasiva, y dependiendo de como nos estemos conectando, es posible que tengamos que enviar un comando PORT indicando la IP pública.

Las IPs de conexión

Cuando se realiza una conexión pasiva, es el cliente el que inicia la conexión de datos con el servidor.

Cuando el cliente se va a conectar, y dependiendo de la forma de hacerlo, y del tipo de conexión o VPC que estemos usando, es posible que envíe para la conexión la IP privada en lugar de la pública, con lo que la comunicación con el servidor no va a ser posible.

Esto se puede solucionar forzando la dirección IP pública con un comando PORT, justo después de iniciar el modo pasivo.

Conectando desde PHP

Si bien la conexión desde PHP es en principio exactamente igual a cualquier otro tipo de conexión, en PHP podemos hacerlo de una forma más sencilla con las funciones que el propio lenguaje incorpora.

En el siguiente ejemplo podéis ver un script completamente funcional de conexión en PHP:

<?php
ini_set('display_errors', 1);
ini_set('track_errors', 1);

$conn_id = ftp_connect('54.54.54.54');
ftp_login($conn_id, 'username', 'password');

ftp_set_option($conn_id, FTP_USEPASVADDRESS, false);
ftp_pasv($conn_id,true);

$l = ftp_nlist($conn_id, ".");
if( ! $l ) {
        print_r(error_get_last());
}
var_dump($l);

Las dos instrucciones importantes en el son las que usan las funciones ftp_set_option y ftp_pasv, que es donde indicamos la forma en que vamos a hacer la conexión.

La opción FTP_USEPASVADDRESS lo que hace es ignorar la IP que nos indica el servidor (que puede ser incorrecta) y usar la IP de la conexión directamente (que es la correcta).

La opción ftp_pasv inicia una conexión pasiva normal.

Con estas dos opciones, es posible conectar sin problemas aunque estemos conectando entre instancias separadas por un cortafuegos y que usan NAT para conectar.