I set up an FTP server on AWS EC2 (Ubuntu16.04) with passive mode (PASV
), but it doesn't work. However, it works with EPSV
, don't know why. I searched around but find no answers, any body can help me with this?
1. vsftpd config
anonymous_enable=NO
local_enable=YES
write_enable=YES
chroot_local_user=YES
pasv_enable=YES
pasv_min_port=13000
pasv_max_port=13100
port_enable=YES
pasv_address=[public ip address of AWS EC2 instance]
allow_writeable_chroot=YES
seccomp_sandbox=NO
2. AWS EC2 Firewall
3. Test through FireFTP
With PASV
mode, I cannot connect to FTP server, the log is:
220 (vsFTPd 3.0.3)
USER sensor
331 Please specify the password.
PASS (password not shown)
230 Login successful.
CWD /
250 Directory successfully changed.
TYPE A
200 Switching to ASCII mode.
PASV
QUIT
However, with it works with EPSV
(with IPV6 checkbox selected), the log as below:
220 (vsFTPd 3.0.3)
USER sensor
331 Please specify the password.
PASS (password not shown)
230 Login successful.
PWD
257 "/" is the current directory
TYPE A
200 Switching to ASCII mode.
EPSV
229 Entering Extended Passive Mode (|||13082|)
LIST
150 Here comes the directory listing.
226 Directory send OK.
4. Test through Python ftplib
from ftplib import FTP
contents = []
ftp = FTP(host=xxx, timeout=3000)
ftp.login(user=xxx, passwd=xxx)
ftp.set_debuglevel(2)
ftp.retrlines("NLST", contents.append)
ftp.quit()
The log as below:
*cmd* 'TYPE A'
*put* 'TYPE A\r\n'
*get* '200 Switching to ASCII mode.\n'
*resp* '200 Switching to ASCII mode.'
*cmd* 'PASV'
*put* 'PASV\r\n'
*get* '227 Entering Passive Mode (0,0,0,0,50,245).\n'
*resp* '227 Entering Passive Mode (0,0,0,0,50,245).'
ConnectionRefusedError: [Errno 111] Connection refused
The PASV command tells the server to enter a passive FTP session rather than Active. This allows users behind routers/firewalls to connect over FTP when they might not be able to connect over an Active (PORT) FTP session. PASV mode has the server tell the client where to connect the data port on the server.
Passive mode can sometimes resolve certain clients ability to connect to the FTP server which may have been blocked by firewalls. If you are having issues connecting remotely, would like you to try to to enable passive connections in you will need to edit vsftpd.
It looks like a bug in vsftpd to me.
From the code it looks like, it will always send the 0,0,0,0
, if the public pasv_address
is set, but the server has a (local) IPv6 address.
To fix this, make sure the server does not listen on IPv6 address (what is the default behavior, which you are overriding by setting listen_ipv6=YES
):
listen_ipv6=NO
listen=YES
The only other solution is removing a private IPv6 address, if it is possible in EC2.
Or use another FTP server, e.g. ProFTPD.
Or make ftplib ignore the IP address returned by the server.
See Cannot list FTP directory using ftplib – but FTP client works
To prove that this is indeed a bug, check this code of the latest vsftpd release (3.0.3):
handle_pasv
in postlogin.c
:
int is_ipv6 = vsf_sysutil_sockaddr_is_ipv6(p_sess->p_local_addr);
...
if (tunable_pasv_address != 0)
{
vsf_sysutil_sockaddr_alloc_ipv4(&s_p_sockaddr);
/* Report passive address as specified in configuration */
if (vsf_sysutil_inet_aton(tunable_pasv_address, s_p_sockaddr) == 0)
{
die("invalid pasv_address");
}
}
else
{
vsf_sysutil_sockaddr_clone(&s_p_sockaddr, p_sess->p_local_addr);
}
str_alloc_text(&s_pasv_res_str, "Entering Passive Mode (");
if (!is_ipv6)
{
str_append_text(&s_pasv_res_str, vsf_sysutil_inet_ntop(s_p_sockaddr));
}
else
{
const void* p_v4addr = vsf_sysutil_sockaddr_ipv6_v4(s_p_sockaddr);
if (p_v4addr)
{
str_append_text(&s_pasv_res_str, vsf_sysutil_inet_ntoa(p_v4addr));
}
else
{
str_append_text(&s_pasv_res_str, "0,0,0,0");
}
}
where the vsf_sysutil_sockaddr_ipv6_v4
returns 0, if the s_p_sockaddr
is not IPv6, what it never is, when the pasv_address
is set.
sysutil.c
:
const void*
vsf_sysutil_sockaddr_ipv6_v4(const struct vsf_sysutil_sockaddr* p_addr)
{
static unsigned char pattern[12] =
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF };
const unsigned char* p_addr_start;
if (p_addr->u.u_sockaddr.sa_family != AF_INET6)
{
return 0;
}
if (vsf_sysutil_memcmp(pattern, &p_addr->u.u_sockaddr_in6.sin6_addr, 12))
{
return 0;
}
p_addr_start = (const unsigned char*)&p_addr->u.u_sockaddr_in6.sin6_addr;
return &p_addr_start[12];
}
Imho, the code is wrong. It works (and makes sense), when the IP address is "autodetected" from p_sess->p_local_addr
, but fails, when the pasv_address
address is used.
Consider reporting this to the author of vsftpd.
Keeping an original explanation of the PASV
vs. EPSV
:
Just to explain the difference between the PASV
and the EPSV
: The PASV
returns an IP address in the response. That information is in 99.9% redundant. And it commonly causes problems, when the server is not aware of its external IP address.
The EPSV
was introduced later than the PASV
, when it was clear that the IP address presence in the response is problematic. So with the EPSV
, only a port number is included. And the client connects to the FTP server IP address implicitly.
If the server really returns 0,0,0,0
in a response to the PASV
command, it's clear why the client cannot connect to the server, when the PASV
is used.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With