概述
作为非常古老的协议(1971年首次提出,1980年首次应用),FTP在目前依然占有一席之地,但是由于目前IT业发展的非常迅猛,它和现在的基础架构产生了一些水土不服,本文旨在通过日常常见的案例,来介绍如何在阿里云上搭建FTP Server和FTP Client。
本文结构
本文先从FTP的协议分析,重点阐述了其中控制通道的两种连接模式,进而说明在阿里云上搭建FTP服务器可能遇到的问题,如被动模式下服务器公网IP无法获取、主动模式下无法连接等情况。
FTP协议摘选
控制通道与数据通道
在FTP协议中,通常有两个通道:控制通道和数据通道,前者用来传输控制命令,如协议握手、获得当前目录等操作,而数据通道顾名思义,就是用来传输文件数据之用,另外ls命令的结果通常也会通过数据通道来传输。
被动模式与主动模式
而被动模式和主动模式是针对如何建立数据通道而言的,这里的主动、被动是站在服务器的角度上描述的,他们的差别简单来说就是数据通道的TCP连接建立是由谁发起的,主动模式下服务器主动向客户端建立数据通道,而被动模式下,服务器是等待客户端来连接的。
这两个模式大家听到比较多的应该是被动模式,通常网上的文章都会建议客户端使用被动模式来连接,下面简单来说明一下两者的差别。
主动模式
主动模式的详细操作可以参考我之前写的一篇文章,这里节选部分信息:
以下是一次完整的交互,客户端为47.x.x.224,服务端为112.x.x.7。
对他的分析可以参考之后的一张交互图,需要重点说明的是交互图中标红的一行,这一行可以理解为数据通道的实际体现(抓包中倒数第4个包,由于我只过滤了FTP协议的包,TCP的控制包没有显示出来,这个包实际上是建立了一个新的TCP流来传输的),在这之前PORT命令是由客户端发出的,这一条报文为
PORT 47,x.x,224,39,16
其中前四个数字为十进制的IP地址,在主动模式下为客户端的IP地址,最后两个数字为端口,由于端口需要两个字节表示,所以在这里的计算方式为39x256+16=10000,那么这一条报文的意思就是告诉服务器,“我计划使用10000端口作为监听的端口,我的IP地址是47.x.x.224,你可以来连接我了进行数据传输了”。
被动模式
而被动模式最显著地差别就是在于如何建立数据通道,主动模式下是服务端去连接客户端传输数据,而被动模式下,是客户端去发起连接,向服务器建立连接。
以下是一次被动模式下的交互,客户端为30.x.x.85,服务端为47.x.x.255。
可以看出最大的区别在于红框中的命令,这一次从PORT改为PASV,之后服务器回复一条被动模式的信息,其中同样是6个数字:IP地址+端口号(端口号的计算方法和主动模式中一样),只是这一次换做服务器告诉客户端,“你现在可以连接我的47.x.x.255的47185端口了”。
这里特别提示一下,从下面的抓包截图中可以看出服务器回复的PASV响应中IP地址并不是上面提到的47.x.x.255,而是一个私网地址172.x.x.241,原因我们会在# 专有网络ECS内部署FTP Server #这一节中提到。
被动模式出现的原因
为何会同时存在被动模式和主动模式呢?在互联网的初期,由于各个终端使用的都是标准的公网IP地址,彼此时间可以互相通信,互相可见,所以在发起TCP连接时,无所谓是哪一方先发起。但随着互联网的高速发展,人们发现IP地址已经不够用了,进而诞生了NAT技术,这就在通信的双方之间插入了一个中间人,在NAT设备之后的终端对于对方是不可见的,所以只能由它来发起连接。
所以为了迎合NAT技术,FTP协议里就出现了被动模式,而且由于目前大部分FTP客户端都是在NAT设备之后,所以绝大多数情况下只能使用被动模式(当然在一些特殊的架构里主动模式还是有一定的用武之处,本文最后一节将会提及)
专有网络ECS内部署FTP Server
阿里云ECS的网络
目前阿里云ECS的网络类型有两种:经典网络与专有网络。对于经典网络的ECS来说,系统内有两张网卡:eth0和eth1,eth0为内网网卡,eth1为外网网卡。在系统内内网和外网的IP地址是静态写在配置文件里的,系统内可以直接看到。而专有网络的ECS系统内只有一张网卡eth0,所有的流量都由这张网卡出入,系统内部无法看到ECS自身的公网IP/弹性公网IP。
这就直接导致了一个问题:由于目前的网络环境下大部分客户端都会使用被动模式来连接,但是被动模式下服务器需要告诉客户端连接的IP地址,而专有网络下的ECS无法直接看到外网IP,那么就导致FTP Server将自己的私网IP告诉了客户端,客户端收到了一个私网IP自然就无法进行连接。而list、文件传输都需要走FTP协议的数据通道,所以最终导致了专有网络下搭建的FTP Server无法让客户端正常的传输数据。
解决方案
解决办法也很简单:主动告知FTP Server程序系统的外网地址,由于市面上FTP Server众多,各家的设置方式不尽相同,以下说明几个常见的FTP服务器的设置方法:
- vsftpd
在vsftpd的配置文件中,加入以下两行,其中替换为ECS的弹性公网地址:
listen_ipv6=NO
pasv_address=<EIP>
- IIS
打开IIS,选择服务器(注意不要选择“网站”下的ftp站点),打开右侧的“防火墙支持”,在“防火墙的外部IP地址”其中填入ECS的弹性公网地址即可。
- Serv-U(最新版本)
打开管理控制台,依次选择“域”-》[对应的域]-》“域详细信息”-》“监听器”-》21端口的监听器,点击“编辑”,将ECS的弹性公网地址填入弹出的对话框中
One more thing
在撰写本文时,我发现一些客户端似乎已经提供了智能化的规避方案,如lftp提供以下参数,当出现类似的问题时会尝试使用控制通道的服务器IP地址进行数据通道的连接。
ftp:fix-pasv-address (boolean)
if true, lftp will try to correct address returned by server for PASV command in case when server
address is in public network and PASV returns an address from a private network. In this case lftp
would substitute server address instead of the one returned by PASV command, port number would not be
changed. Default is true.
另外,FileZilla也默认开启了修正被动模式下数据通道IP地址的功能:
FTP的端口与ECS的安全组
ECS的安全组
阿里云上的安全组相当于一个方向墙,类似iptables或Windows防火墙,可以根据协议、源/目的IP、源/目的端口来实现特定流量的放行、拦截。在# 被动模式和主动模式 #一节中我们已经提到了FTP除了21端口以外,根据不同的模式在数据通道下还会使用到特定的端口,所以需要在ECS的安全组中放行。
FTP端口的选择策略
关于FTP两种模式下的报文交互,前文已经做了阐述,但还未说明程序是如何选择端口的,下面简单说明一下:
- 被动模式
- | 控制通道 | 数据通道 |
---|---|---|
客户端 | 主动连接服务器,随机选择本地源端口 | 主动连接服务器,随机选择本地源端口 |
服务端 | 被动接受客户端连接,本地默认监听21端口 | 被动接受客户端连接,本地随机监听端口 |
注:被动模式下服务端数据通道监听的端口理论上可以通过FTP程序的配置文件修改
- 主动模式
- | 控制通道 | 数据通道 |
---|---|---|
客户端 | 主动连接服务器,随机选择本地源端口 | 等待服务器的链接,本地监听一个随机的端口(注) |
服务端 | 被动接受客户端连接,本地默认监听21端口 | 主动链接客户端,随机选择本地源端口 |
注:主动模式下客户端数据通道监听的端口理论上可以通过FTP程序的配置文件修改
那么可以得出在ECS上部署FTP Server,若对于安全组有严格的进出限制,那么需要放行:
- 控制通道监听的端口,用来接收客户端的控制命令,默认为21端口
- 被动模式下建议指定一个端口范围,以便在安全组中可以放行;主动模式下由于安全组出方向是默认放行的,所以理论上不需要额外的配置
如何指定被动模式下服务器数据通道监听的端口
这里依然以常见的FTP程序为例:
- vsftpd
在配置文件里添加以下信息,其中端口的起止范围根据实际需求修改,然后重启vsftpd
pasv_enable=YES
pasv_min_port=10000
pasv_max_port=10020
- IIS
同上文提到的类似,在“FTP防火墙支持”中填入数据通道的端口范围即可
- Serv-U
- 打开左侧“全局”菜单,选择“限制与设置”
- 选择右侧“设置”选项卡,页面拉到下方,可以看到“网络设置”中有一项“PASV端口范围”
- 根据实际情况填写即可
主动模式适用的场景
前文提到在一些特定的场景下,确实只能使用主动模式,比如说IDC和银行侧相连,银行出于安全考虑仅允许21端口入方向的流量,其余的端口都是封禁的,那么就无法使用被动模式,只能退而求其次使用主动模式。那么此时对于客户端的要求可以总结为下列几点:
- 客户端必须有自己的公网IP,如ECS 弹性公网IP,或者可以使用阿里云NAT网关的DNAT功能,将公网IP的部分端口映射到ECS内部的端口上
- 客户端上强制使用主动模式(目前大部分客户端会优先使用被动模式)
- 由于主动模式下数据通道是由服务器发起连接,需要在ECS安全组中放行对应的端口
- 客户端系统内若无法直接看到公网IP(如专有网络下的ECS、DNAT模式下,依然需要对FTP客户端进行配置,告知客户端可以连接的公网IP地址)。
关于第二、三、四点,其实和被动模式下PASV命令的响应类似,都需要在程序中作相应的配置才行,但由于这个场景下这个需求确实比较小众,我只收集了少量的客户端配置方法,其他的客户端配置方法请和程序的提供商寻求技术支持。
配置主动模式的端口和公网IP地址
- lftp
lftp程序非常友好,在man手册中就已经说明了配置方法,依次是被动模式开关、主动模式下的IP地址、主动模式下数据通道监听的端口范围:
ftp:passive-mode (boolean)
sets passive FTP mode. This can be useful if you are behind a firewall or a dumb masquerading router.
In passive mode lftp uses PASV command, not the PORT command which is used in active mode. In passive
mode lftp itself makes the data connection to the server; in active mode the server connects to lftp
for data transfer. Passive mode is the default.
ftp:port-ipv4 (ipv4 address)
specifies an IPv4 address to send with PORT command. Default is empty which means to send the address
of local end of control connection.
ftp:port-range (from-to)
allowed port range for active mode. Format is min-max, or `full' or `any' to indicate any port.
Default is `full'.
- FileZilla
- 主动模式相关配置可以直接在菜单栏中的“编辑”-》“设置”中配置
- 客户端的连接行为可以在每个FTP会话中单独配置