vsftpd在响应PASV时返回0,0,0,0

11

我在AWS EC2(Ubuntu16.04)上建立了一个FTP服务器,并开启了被动模式(PASV),但是它不起作用。但是如果使用EPSV,就可以运行,不知道原因。我搜索了一下但没有找到答案,有人能帮帮我吗?

1. vsftpd配置

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 防火墙

安全组(入站)设置

3. 通过 FireFTP 进行测试

未选中 IPV6

使用 PASV 模式,我无法连接到 FTP 服务器,日志如下:

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

然而,当它与EPSV一起工作(选择了IPV6复选框)时,日志如下:

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. 通过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()

以下是日志:

*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
1个回答

20

在我看来,这似乎是vsftpd的一个错误。

从代码中看,如果公共pasv_address已设置,但服务器有(本地)IPv6地址,则它似乎总是发送0,0,0,0

要解决此问题,请确保服务器不侦听IPv6地址(默认行为,您通过设置listen_ipv6=YES覆盖了该行为):

listen_ipv6=NO
listen=YES

唯一的解决方案是在EC2上删除私有IPv6地址(如果可能的话)。

或者使用另一个FTP服务器,例如ProFTPD

或让ftplib忽略服务器返回的IP地址。
请参见Cannot list FTP directory using ftplib – but FTP client works


要证明这确实是一个 bug,请检查最新 vsftpd版本(3.0.3)中的以下代码:

postlogin.c 中的 handle_pasv

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");
  }
}

s_p_sockaddr不是IPv6时,vsf_sysutil_sockaddr_ipv6_v4返回0,而当设置了pasv_address时,它永远不是IPv6。

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];
}

在我看来,代码是错的。当IP地址从p_sess->p_local_addr“自动检测”时,它有效(并且有意义),但当使用pasv_address地址时失败了。

考虑向vsftpd的作者报告此问题。


保留关于PASVEPSV的原始解释:

仅仅为了解释PASVEPSV之间的区别: PASV在响应中返回一个IP地址。这个信息在99.9%的情况下都是多余的。当服务器不知道其外部IP地址时,它通常会引起问题。

EPSVPASV晚引入,当响应中包含IP地址存在问题时,这一点已经很明显了。因此,在EPSV中只包括端口号。客户端隐式地连接到FTP服务器IP地址。

如果服务器真的在响应PASV命令时返回0,0,0,0,那么当使用PASV时,客户端无法连接到服务器就很清楚了。


非常感谢您的详细解释!对于 EC2 的私有 IPv6 地址,我无能为力,现在只能更换并尝试另一个 FTP 服务器了。 - codefluxer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接