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
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 a 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.
To prove that this is indeed a bug:
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.