
1. 问题场景与报错拆解1.1 一条几乎让所有FTP新手都头疼的报错如果你在地铁上、工位上或者深更半夜还盯着屏幕折腾“阿里云搭建 ftp 服务器”大概率已经见过下面这段对话Connected to 49.xxx.xxx.xxx. 220 (vsFTPd 3.0.3) Name (49.xxx.xxx.xxx:root): test 331 Please specify the password. Password: 230 Login successful. Remote system type is UNIX. Using binary mode to transfer files. ftp ls 200 PORT command successful. Consider using PASV. 425 Failed to establish connection. ftp登录成功、密码验证通过、服务端也响应了但一执行ls就立刻翻车。200 PORT command successful. Consider using PASV.后面跟着425 Failed to establish connection.然后整个会话就像被掐住脖子一样什么都做不了。我第一次遇到这个报错时第一反应是“防火墙没放行端口”于是把 21 端口在安全组和系统防火墙里放了一遍又一遍甚至一度怀疑是阿里云的入方向规则和本地网络冲突。折腾了大半天才搞清楚这不是简单的端口没放开而是FTP协议本身的工作模式在云服务器环境下“水土不服”。这篇文章会把这个问题从头到尾拆开从协议原理讲到云服务器环境的特殊坑点再给出一套可以直接照抄的完整配置流程帮你在二十分钟内把这事搞定。1.2 主动模式与被动模式的底账要理解 425 报错必须先看清 FTP 的两种数据传输模式。FTP 使用两条通道一条叫控制连接通常走 21 端口负责传递指令和响应另一条叫数据连接负责实际传输目录列表或文件内容。问题是这条数据连接的建立方式有两种完全不同的玩法。主动模式Active ModePORT客户端在控制连接上发送PORT 192,168,1,100,10,50这样的命令意思是“我在某个 IP 的某个端口上等着你服务器主动连接过来”。这里有个关键点服务器是主动方它从自己的 20 端口发起连接去连客户端指定的那个高位端口。被动模式Passive ModePASV客户端发送PASV命令后服务器回应227 Entering Passive Mode (49,xxx,xxx,xxx,200,10)告诉客户端“我在这个 IP 的 20010 端口上等你来连接”。这回客户端变成了主动方由它去连服务器开出来的数据端口。回到报错本身当你看到200 PORT command successful.意味着你手头的FTP客户端默认在发PORT命令也就是要求服务器反过来连你。这与云服务器默认的安全策略天然冲突接下来的内容会详细解释这个“冲突”到底是怎么发生的。1.3 425报错的真实含义服务器回应425 Failed to establish connection.翻译成人话就是服务器收到了你的PORT命令也愿意配合你的主动模式去连你指定的那个地址和端口但是——连不上。为什么会连不上因为服务器尝试用它的 20 端口去连接客户端的 IP:Port 时这个连接请求被拦掉了。最常见的拦截点有三个你所在本机的防火墙/杀毒软件拦住了来自外网的主动连接你家或者公司的路由器/NAT 设备没有做端口转发服务器根本找不到“你”云服务器侧因为net.ipv4.ip_forward、iptables状态跟踪模块缺失放行了第一个数据包却放行不了后续的包。这里面最坑的一点是425 Failed to establish connection.往往并不会告诉你到底是哪一环被拦了。客户端日志里只有一句话服务端日志/var/log/vsftpd.log里可能也只写着CONNECT: Client 49.xxx.xxx.xxx, Connection refused信息量少得可怜。所以接下去要做的是把这条链路从头到尾捋一遍找到那个真正卡住你的环节。2. 云环境下FTP失败的根因链条2.1 默认配置下vsftpd在做什么很多教程会教你装 vsftpd 之后直接systemctl start vsftpd就算完成。这个操作本身没错但在云服务器上默认配置几乎必踩坑。因为 vsftpd 默认会同时支持主动和被动模式而主动模式在云环境下就是一个“陷阱”。看一段典型的 vsftpd 默认配置文件/etc/vsftpd/vsftpd.confanonymous_enableNO local_enableYES write_enableYES local_umask022 dirmessage_enableYES xferlog_enableYES connect_from_port_20YES xferlog_std_formatYES listen_ipv6NO pam_service_namevsftpd userlist_enableYES tcp_wrappersYES看到connect_from_port_20YES没有这一项表示在主动模式下vsftpd 使用 20 端口作为数据连接源端口。问题是云服务器安全组如果没有放行 20 端口或者本地客户端所在网络拒收外网主动连接这条数据通道就立不起来。更麻烦的是默认情况下 vsftpd 没有显式声明被动端口范围。如果客户端改用PASV模式这也是报错信息里那句Consider using PASV.在提醒你的事vsftpd 会动态分配一个高位端口——一般是 1024 到 65535 之间的任意端口。这就让安全组规则变得非常难配难道要把所有高位端口全部放行那样安全风险太大了。所以云服务器上跑 FTP 必须把被动端口范围固定下来比如pasv_min_port30000和pasv_max_port30010然后只放行这一段端口。这可能是整个配置过程中最重要的一步没有之一。2.2 云服务器的三层拦截先说本地虚拟机或公司内网服务器。在那里跑 FTP通常只需要关心服务器本机的防火墙。但在云服务器无论哪家云厂商上流量要过三层关卡第一层云平台安全组。阿里云 ECS 的控制台里有一份入方向规则列表。默认情况下只放行 22、3389 等少数端口21 端口需要手动添加规则。很多人配了 21 端口结果数据连接走的是 30000 的高位端口安全组里没有对应规则数据通道直接中断。第二层操作系统防火墙。云服务器镜像默认开启 firewalld 或 ufw它们的规则是独立于云平台安全组的。即使安全组放行了 21 和 30000-30010本机防火墙不放行数据连接依然进不来。第三层内核连接跟踪模块。这一层最容易忽略。FTP 的主动模式太特立独行——数据连接的建立方向和控制命令是反着来的。Linux 内核的conntrack连接跟踪机制如果不知道“这是 FTP 的数据连接”就会把数据包当成无关联的新连接。若防火墙规则里没有显式放行这些端口数据包就会被丢弃。这也能解释为什么很多人在本地测试时一切正常一放到云服务器就 425。本地如果没开防火墙或者防火墙比较宽松三层拦截只碰到一层甚至一层都碰不到在云服务器上三层拦截是叠加的任何一层出问题都会导致最终连接失败。2.3 为什么本地测试“一切正常”这个现象很迷惑人同样的配置在本地用ftp 192.168.1.100测ls正常上传下载也正常一换成阿里云的公网 IP就 425。原因在于192.168.1.100在同一个局域网内服务器去连你的 IP:Port 时中间没有 NAT 设备也没有安全组拦截。数据包从服务器网卡出来一路直达你的电脑畅通无阻。但公网环境下你的电脑在某个局域网里比如公司网络服务器看到的你的 IP 是路由器做了 NAT 之后的公网出口地址而你的PORT命令里填的可能还是你本机的局域网 IP比如192.168.1.100。服务器试着去连接192.168.1.100:port结果这个地址在公网根本路由不到。于是425 Failed to establish connection.就如期而至了。这也是为什么PORT模式在公网环境里几乎不可用——它对客户端的网络环境要求极为苛刻客户端必须拥有公网 IP且中途没有任何 NAT 和防火墙。相比之下PASV模式把所有连接压力都放在服务器侧只要服务器按被动端口范围开放端口客户端不管在什么网络环境里都能连上。3. 完整实操从安装到跑通FTP服务3.1 系统环境与前提检查下面这套流程以 CentOS 7.9 vsftpd 为例。我个人经常在阿里云 ECS 上这么干其他发行版Ubuntu / Debian差异不大只是防火墙命令略有区别。先确认系统版本和是否已安装 vsftpdcat /etc/redhat-release rpm -qa | grep vsftpd如果没有安装直接装yum install -y vsftpd同时建议装一个 lftp 或 ftp 客户端用于本机验证。云服务器上默认可能没有 FTP 客户端也可以用yum install -y ftp装上。3.2 固定被动端口范围打开/etc/vsftpd/vsftpd.conf直接修改或追加以下内容。我个人习惯把跟被动模式相关的配置集中在文件末尾方便日后排查# 允许本地用户登录 local_enableYES write_enableYES local_umask022 # 主动模式允许但从 20 端口发起 connect_from_port_20YES # 被动模式端口范围固定为 30000-30010 pasv_enableYES pasv_min_port30000 pasv_max_port30010 pasv_address你的云服务器公网IP # 指定被动模式下返回给客户端的IP pasv_addr_resolveYES我把pasv_address叫做“公网IP开关”因为它直接影响227 Entering Passive Mode (49,xxx,xxx,xxx,xxx,xxx)这行响应里返回的 IP 地址。如果不填vsftpd 会拿网卡上的私网 IP 来回应客户端。在阿里云这种典型的环境里ECS 内网 IP 是 172.16.x.x 或 192.168.x.x客户端拿到这个地址根本连不上。注意如果服务器做了公网 IP 映射比如经典网络里的公网 IP 映射到私网 IPpasv_address必须填公网 IP。如果是同一台 ECS 上绑定了弹性公网 IP也填公网 IP。另外如果你的 IP 是动态的极少数情况可以把pasv_addr_resolveYES打开然后pasv_address填一个域名。这样每次解析域名就能拿到最新 IP但在阿里云固定公网 IP 的场景下直接填 IP 更省事。3.3 创建FTP用户并限制目录如果直接用 root 登录 FTPvsftpd 会拒绝默认配置里root在/etc/vsftpd/user_list或/etc/vsftpd/ftpusers里。为了安全我通常会单独建一个系统用户比如叫ftpuseruseradd -d /data/ftp -s /sbin/nologin ftpuser mkdir -p /data/ftp chown -R ftpuser:ftpuser /data/ftp passwd ftpuser-s /sbin/nologin表示这个用户不能登录 shell只能用于 FTP 访问兼顾了安全性。之后你还可以在 vsftpd.conf 里加上这几行把用户限制在自己的家目录里避免他跑到系统其他目录去chroot_local_userYES allow_writeable_chrootYES如果不加allow_writeable_chrootYES有些版本会因为你把/data/ftp设置为用户家目录且目录可写而直接拒绝登录并报500 OOPS: vsftpd: refusing to run with writable root inside chroot()。这个坑在 CentOS 7 上很常见很多新手会跑来问为什么登录直接被拒其实就是少了这一行。3.4 安全组规则配置到阿里云控制台找到你的 ECS 实例进入“安全组”添加入方向规则端口范围协议授权对象用途21TCP0.0.0.0/0FTP 控制连接20TCP0.0.0.0/0主动模式数据连接可选30000-30010TCP0.0.0.0/0被动模式数据连接我见过有人只放行 21 和一个被动端口结果文件传不上去。FTP 目录列表和文件传输都会使用数据连接如果被动端口只有一个多个传输任务同时进行时第二个任务会因为没有可用端口而失败。所以固定 10 个左右的端口范围是最稳妥的对日常使用完全够用。授权对象0.0.0.0/0表示允许所有 IP 访问。如果要求更安全可以填你本地的出口公网 IP。但要注意出口 IP 会动态变化一旦变了就会把自己挡在外面。3.5 系统防火墙放行CentOS 7 默认用 firewalld操作如下firewall-cmd --permanent --add-port21/tcp firewall-cmd --permanent --add-port20/tcp firewall-cmd --permanent --add-port30000-30010/tcp firewall-cmd --reload如果用的是 iptables 管理核心概念是一样的只是命令不同iptables -I INPUT -p tcp --dport 21 -j ACCEPT iptables -I INPUT -p tcp --dport 20 -j ACCEPT iptables -I INPUT -p tcp --dport 30000:30010 -j ACCEPT别忘了保存规则。否则服务器重启后这些 iptables 规则就没了。这里有个细节如果你既开了 firewalld 又装了 iptables-services两者管理的是同一套规则表其实不是firewalld 底层虽然也用 netfilter但它有自己的一套规则管理方式。建议只留一个防火墙服务两个都开容易互相打架排查时非常痛苦。3.6 加载FTP连接跟踪模块这一步是针对主动模式和被动模式都适用的一层保障。Linux 内核的nf_conntrack_ftp模块能够让 conntrack 正确识别 FTP 数据连接。modprobe nf_conntrack_ftp想让它在系统重启后自动加载可以写到/etc/modules-load.d/ftp.conf里内容就一行nf_conntrack_ftp。为什么需要这步因为防火墙在处理状态化连接时会跟踪每个连接的状态。FTP 的数据连接新建方向和命令通道不一致conntrack 如果不认识这个协议数据包建立的新连接就会被防火墙规则拦下。加载这个模块后防火墙能识别出这是某个 FTP 会话的数据连接自动放行。实测效果很明显只配了安全组和 firewalld但不加载nf_conntrack_ftp主动模式照样可能 425。加载之后主动模式在某些受限场景下也能正常工作前提是你本地允许外网主动连接。3.7 启动服务并验证配置完成后执行systemctl start vsftpd systemctl enable vsftpd然后看服务状态systemctl status vsftpd ss -tlnp | grep -E 21|3000看到 21 端口在监听30000-30010 端口在监听vsftpd 会在收到 PASV 请求时动态监听其中某个端口所以这里可能只看到 21 端口一直存在基本就绪。用客户端连一次注意这次要切换到被动模式。命令行ftp工具默认可能是主动模式你可以在ftp提示符下手动输入passive或pwd部分系统里passive命令不可用可以输pass切换不同版本有差异。更省事的办法是直接用lftplftp ftpuser49.xxx.xxx.xxx -e set ftp:passive-mode yes; ls; quit如果目录列表正常输出说明被动模式和端口放行都已经生效。你也可以顺手试一下主动模式lftp ftpuser49.xxx.xxx.xxx -e set ftp:passive-mode no; ls; quit在云服务器场景下后面的主动模式大概率仍然失败但没关系我们跑通被动模式就达到目的了。强烈建议日常使用全部被动模式。4. 常见问题速查与避坑实录4.1 PASV地址返回内网IP客户端死活连不上现象登录没问题ls也能触发但响应是227 Entering Passive Mode (172,16,0,10,117,44)紧接着425 Failed to establish connection.原因客户端拿到的是 ECS 内网 IP比如 172.16.0.10。这个地址在公网路由不到客户端去连接自然失败。解决在 vsftpd.conf 里设置pasv_address为 ECS 公网 IP。设置完重启systemctl restart vsftpd再试。注意不要在配置里同时设置pasv_address和pasv_addr_resolveYES却没提供域名那样 vsftpd 会尝试解析一个空字符串反而出错。4.2 “Consider using PASV”提示很刺眼但无害现象客户端输出200 PORT command successful. Consider using PASV.然后就没有然后了有些客户端会卡住。原因这句提示只是服务器在“好心”提醒你主动模式可能不通试试被动模式。如果你用的是 FileZilla默认就是被动模式通常不会出现这句。出现这句说明你的客户端配置成了主动模式或自动退回到了主动模式。排查顺序先在客户端把传输模式强制设为“被动”。FileZilla 在“设置 - 传输 - FTP”里的“被动模式”勾上命令行客户端切换方式见上文。如果切了被动模式还是 425再看227响应里返回的 IP 和端口是否可访问——用telnet 公网IP 30000测一下端口通不通。4.3 端口范围开得特别大但依然失败现象安全组放行了 1024-65535防火墙也放行了被动模式还是连不上。原因端口放行范围太广不会导致失败但可能有另一个坑——云厂商安全组规则有上限或者你放行规则写错了协议类型比如填成了 UDP 而不是 TCP。另外ECS 实例如果绑定了多个网卡vsftpd 可能默认监听在错误的网卡上导致公网流量进不来。排查思路先用ss -tlnp | grep vsftpd确认监听地址是 0.0.0.0 还是网卡地址再用控制台的“安全组规则”页面核对协议和端口范围最后在另一台机器上telnet 公网IP 30000验证端口连通性。4.4 FileZilla连上了但列目录卡死现象输入账号密码后状态栏显示Listing /...然后一直转圈最后超时。原因目录列表也是一个数据连接过程。FileZilla 默认采用被动模式但如果你之前在某处把模式切成了主动也会遇到这个问题。另一个常见因素是本地或服务器侧的代理干预了 FTP 流量。处理打开“站点管理器”选中你的站点“传输设置”里的“被动模式”确保选中。同时确认 vsftpd 配置里pasv_address是公网 IPpasv_min_port/pasv_max_port与实际放行端口一致。4.5 明明重启vsftpd了却还是旧配置现象改了配置、重启了服务行为还是老样子。原因配置文件写错位置。有些教程会让人在/etc/vsftpd/vsftpd_user_conf或/etc/vsftpd/conf.d/下加配置片段如果你的版本没有加载这个目录配置就不会生效。验证执行vsftpd -olistenYES这种参数只是在本次启动时生效不够直观。更推荐用grep -v ^# /etc/vsftpd/vsftpd.conf | grep -v ^$把所有有效配置项列出来检查pasv_min_port等是否真的在里面。如果配置被文件内的重复项覆盖比如前面写了一个值后面又写了一个最终生效的是靠后的那个值。4.6 上传大文件时中途断开现象传小文件没问题一传几百 MB 的包就断。原因可能是运营商或云平台的空闲连接超时把连接断了也可能是被动端口只有一个传输过程中另一个连接请求抢占端口。更常见的原因是客户端和服务器之间的 NAT 会话超时比 FTP 数据连接的静默期更短。建议把被动端口范围设成 10 个以上上传时给 FileZilla 启用“保持连接活跃”或者用lftp的set net:timeout 30提高容忍度。4.7 多用户场景下的隔离与权限如果你给多个同事各开一个 FTP 账号建议每个用户单独设置目录useradd -d /data/ftp/user1 -s /sbin/nologin user1 useradd -d /data/ftp/user2 -s /sbin/nologin user2 mkdir -p /data/ftp/user1 /data/ftp/user2 chown -R user1:user1 /data/ftp/user1 chown -R user2:user2 /data/ftp/user2然后保持chroot_local_userYES每个用户就只能在/data/ftp/user1、/data/ftp/user2里活动了。4.8 安全加固就算跑通了也别裸奔我见过不少教程讲完systemctl start vsftpd就结束了。但 FTP 本身就是明文协议账号密码在网络上裸奔所以至少在云服务器层面做几件小事限制可以登录 FTP 的用户在/etc/vsftpd/user_list里把不想开放的用户名写进去设置max_clients10限制并发连接数用vsftpd自带的deny_email_enable或tcp_wrappers做访问源限制如果条件允许优先考虑 SFTPSSH 自带的子系统就能做文件传输不用额外开 FTP 端口安全性高很多。这次遇到 425 报错回头看其实是PORT主动模式在云环境里走不通服务端友好地提醒你换PASV而你——包括当年的我——根本没看懂这句提醒的含义。配好被动模式、固定端口范围、放行安全组和系统防火墙再给pasv_address填上公网 IP这个问题基本就消失了。我在实际配置里最后踩的一次坑是在安全组里漏放了 30000-30010 段的某个小尾巴当时227返回的端口是 30005我只放行了 30000-30004日志里反复出现Failed to establish connection。所以建议忘掉单端口思维——FTP 天然是多端口协作的协议把端口段当做一个整体来管理思路会清晰很多。