1.FTP协议简介
为了便于后面的讨论,我们首先简要地讨论一下FTP协议(如果对FTP协议已经有了比较深入的了解,你可以略过这一节)。大多数的TCP服务是使用单个的连接,一般是客户向服务器的一个周知端口发起连接,然后使用这个连接进行通讯。但是,FTP协议却有所不同,它使用双向的多个连接,而且使用的端口很难预计。一般,FTP连接包括:
一个控制连接(control connection)
这个连接用于传递客户端的命令和服务器端对命令的响应。它使用服务器的21端口,生存期是整个FTP会话时间。
几个数据连接(data connection)
这些连接用于传输文件和其它数据,例如:目录列表等。这种连接在需要数据传输时建立,而一旦数据传输完毕就关闭,每次使用的端口也不一定相同。而且,数据连接既可能是客户端发起的,也可能是服务器端发起的。
下面,我们通过一个FTP客户程序看一下控制连接。这里,我们需要使用debug模式(ftp -d)才能显示客户发出的FTP协议命令。在客户程序的输出信息中,这些协议命令是以--->开头的,例如:
---> USER nixe0n
在命令发出之后,服务器会发出响应,响应信息以数字开头,例如:
530 Login incorrect.
下面,我们和FTP服务器建立一个连接,使用用户名nixe0n登录,在会话过程中发出两次目录切换名,一次成功一次失败,其中黑体是我们的输入:
ftp -d ftp.linuxaid.com.cn
Connected to ftp.linuxaid.com.cn.
220 ftp.linuxaid.com.cn FTP server ready.
Name (ftp.linuxaid.com.cn:nixe0n): nixe0n
---> USER nixe0n
331 Password required for nixe0n.
Password:
---> PASS XXXX
230 User nixe0n logged in.
---> SYST
215 UNIX Type: L8
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> cd one
---> CWD one
250 CWD command successful.
ftp> cd tmp
---> CWD tmp
550 tmp: No such file or directory.
ftp> bye
---> QUIT
221-You have transferred 0 bytes in 0 files.
221-Total traffic for this session was 398 bytes in 0 transfers.
221 Thank you for using the FTP service on ftp.linuxaid.com.cn.
在FTP协议中,控制连接使用周知端口21,因此使用SSH的标准端口转发就可以这种连接进行很好的安全保护。相反,数据传输连接的目的端口通常实现无法知道,因此处理这样的端口转发非常困难。FTP协议使用一个标准的端口21作为ftp-data端口,但是这个端口只用于连接的源地址是服务器端的情况,在这个端口上根本就没有监听进程。FTP的数据连接和控制连接的方向一般是相反的,也就是说,是服务器向客户端发起一个用于数据传输的连接。连接的端口是由服务器端和客户端协商确定的。FTP协议的这个特征对SSH转发以及防火墙和NAT的配置增加了很多困难。
除此之外,还有另外一种FTP模式,叫做被动模式(passive mod)。在这种模式下,数据连接是由客户程序发起的,和刚才讨论过的模式(我们可以叫做主动模式)相反。是否采取被动模式取决于客户程序,在ftp命令行中使用passive命令就可以关闭/打开被动模式。
在了解了使用SSH转发FTP连接的一些难点之后,我们将开始讨论如何解决这些问题。
2.转发控制连接
FTP的控制连接的一端是一个周知端口21,因此很容易通过SSH实现端口的转发。通常,需要保护的FTP服务器上需要运行SSH服务,而且你需要在服务器上有一个合法帐户以便通过SSH访问FTP服务。
假设你已经登录到一台主机名为client的客户主机,然后想通过安全的连接登录到FTP服务器ftp.linuxaid.com.cn。要转发FTP控制连接,首先要在client上运行一个SSH端口转发命令:
[nixe0n@client nixe0n]ssh -L 2001:ftp.linuxaid.com.cn:21 ftp.linuxaid.com.cn
nixe0n@ftp.linuxaid.com.cn's password:
接着,就可以使用被转发的端口登录到ftp.linuxaid.com.cn:
[nixe0n@clinet nixe0n]ftp localhost 2001
Connected to localhost
220 ftp.linuxaid.com.cn FTP server ready.
Name:foo
Password:
230 User foo logged in.
ftp>
这里,我们需要注意两个非常重要的问题:
在本地进行转发,可能出现一些错误。
在确定转发的目标时,建议不要使用localhost作为目标,因为有时使用这种地址可能出现一些莫名其妙的问题。假如在你的主机(client)上,有其它的网络接口(例如:eth0),其地址为192.168.0.1,如果你想在本机上进行SSH进行FTP端口转发:
[nixe0n@localhost nixe0n] $ssh -L 2001:localhost:21 localhost
nixe0n@localhost's password:
然后,使用ftp命令登录到FTP服务器就可能出现一些错误:
[nixe0n@localhost nixe0n]ftp localhost 2001
Connected to localhost
220 localhost FTP server ready.
Name[localhost:nixe0n]:nixe0n
331 Password required for nixe0n
Password:
230 User nixe0n logged in
ftp>ls
200 PORT command successful.
425 Can't build data connection:Cannot assign requested address.
ftp>
出现这个问题是因为FTP服务器会试图通过回环地址(lo:127.0.0.1)向client(eth0:192.168.0.1)发起连接造成的。本机的回环接口只能和本机的其它回环接口进行通讯,如果和其它的网络接口(例如:eth0)通讯,就会返回"address not available"的错误。
客户程序需要使用被动模式,被动模式对于解决NAT/防火墙造成的一些问题很有帮助。Linux系统的ftp命令在默认情况下使用这种模式。
3.FTP、防火墙和被动模式
前面我们讲过,FTP协议的数据传输存在两种模式:主动模式和被动模式。这两种模式发起连接的方向截然相反,主动模式是从服务器端向客户端发起;被动模式是客户端向服务器端发起连接。但是如果服务器和客户之间存在防火墙,主动模式经常会引起一些麻烦。设想,客户位于防火墙之后,防火墙允许所有内部向外部的连接通过,但是对于外部向内部发起的连接却存在很多限制。在这种情况下,客户可以正常地和服务器建立控制连接,而如果使用主动模式,ls、put和get等数据传输命令就很难成功运行,因为防火墙会阻塞从服务器向客户发起的数据传输连接。简单包过滤防火墙把控制连接和数据传输连接完全分离开了,因此很难通过配置防火墙允许主动模式的FTP数据传输连接通过。如果防火墙允许ICMP或者TCP RST报文通过,客户程序就会马上返回connection refused错误信息;而如果防火墙只是做简单的丢弃处理,会造成客户程序挂起一段时间。
被动模式一般可以解决此类问题,因为在被动模式下,连接是由客户端发起的饿。不过,这要看FTP服务器和客户程序是否支持被动模式。命令行FTP客户程序一般使用passive命令关/开被动模式。例如:
ftp>passive
Passive mode off
ftp>passive
Passive mode on
如果客户程序不支持被动模式,它就会返回?Invaild command;如果客户程序支持被动模式,而服务器不支持,就会返回"PASV:command not understood",PASV是一个FTP协议命令,使服务器进入到被动模式。
4.FTP和网络地址转换(Network Address Translation)
除了简单包过滤防火墙之外,被动模式也可以解决使用网络地址转换(NAT)给FTP造成的一些问题。在转发报文之前,进行网络地址转换的网关首先会改变报文的源地址和目的地址。网络地址转换能够提高网络的安全性,有助于解决IP地址资源不足问题。
4.1.客户端网络地址转换问题
假设你的FTP客户主机位于局域网内,通过一个网络地址转换(NAT)网关连入互联网。在这种情况下,客户程序可以毫无困难地和外部的FTP服务器建立控制连接,但是,如果使用主动模式建立数据传输连接,还是会出现问题。在主动模式下,客户程序会忽略NAT网关,直接使用FTP协议的PORT命令告诉服务器与一个包含客户主机私有地址的套接字建立连接,因此服务器无法向客户主机发起连接。被动模式也可以很好地解决这个问题。
到此为止,我们罗列了三种需要使用FTP被动模式的情况:控制连接的转发;客户位于简单包过滤防火墙之后;和客户主机位于NAT之后。在这三种情况下,主动模式可能无法正常使用,因此我们建议尽量使用FTP被动模式。
4.2.服务器端网络地址转换问题
上面我们讨论了客户端NAT问题。如果FTP服务器位于NAT网关之后,并且你正在通过SSH转发FTP控制连接会怎么样呢?很显然,这种情况下麻烦更大。
首先,假设在没有SSH转发的情况下,服务器位于NAT网关之后会出现什么问题。这与前面讨论的情况正好相反,在主动模式下,如果客户主机位于NAT网关之后,客户程序会使用FTP的PORT命令告诉服务器自己的私有IP地址,造成服务器无法建立连接;而如果服务器位于NAT网关之后,它就会通过FTP命令PASV告诉客户程序自己未经转换的地址,使客户程序无法建立连接。在这种情况下,我们会想到可以使用主动模式来解决。但是,这种解决方式实际上没有多大帮助。在网络上,客户主机位于NAT网关之后是非常普遍的,绝大多数FTP客户程序的默认数据传输方式都是被动模式的。这样,会对客户造成很多不便,对提高服务器的访问量非常不利。
因此,需要有其它的方法解决这个问题。现在的一些FTP服务器可以人工设置PASV命令的地址。wu-ftp,/etc/ftpaccess文件中使用如下命令来设置PASV的地址(具体用法可以参考man ftpaccess):
passive address <extenalip> <cidr>
例如:
passive address 10.0.0.32 10.0.0.0/8
passive address 192.168.1.6 0.0.0.0/0
除此之外,还可以使用智能化的NAT网关解决这个问题。网关需要能够识别位于应用层的FTP协议,能够自动修改FTP协议的FTP控制报文。但是,如果使用SSH转发FTP控制连接,因为控制连接被嵌入到了SSH会话通道中,使网关不能自动修改PASV的地址。这种情况下,你需要使用第一种方法。
5.使用默认数据传输端口
在FTP协议中,除了被动模式和主动模式之外,还有另外一种数据传输模式。如果客户程序既不向服务器发出PASV命令也不发送PORT命令,FTP服务器就会使用FTP协议的数据传输端口(20)和客户端的控制连接源端口建立一个数据传输连接。这就需要客户程序在这个端口上监听。在客户程序上使用sendport命令可以关闭FTP协议的PORT控制指令,然后需要使用passive命令关闭被动模式。整个过程如下:
客户程序从本地端口N初始化一个FTP控制连接。
用户使用sendport命令和passive命令(某些客户程序在默认情况下,被动模式是打开的)关闭主动模式和被动模式,然后使用数据传输指令,例如:ls、get等。这样客户程序就会在本地端口N上监听FTP服务缉发起的数据传输连接。
服务器通过TCP例程(例如:getpeername())确定客户端的端口N。然后从FTP数据传输端口(20)发起一个连接。
不过,这种方式有一个最大的缺点就是无法在很短的时间之内连续输入数据传输命令,用户经常会遇到"bind:Address Already in use等错误。这是TCP协议造成的。因此,这种模式并不常用。但是,它对于解决使用SSH转发FTP数据连接的问题却很有帮助。
6.转发数据连接
前面我们提到了很多使用SSH转发FTP数据连接会遇到的一些困难。现在,我们将讨论如何使用SSH转发FTP数据连接,这里我们将使用上一节介绍的数据传输模式。。注意:下面的讨论是针对OpenSSH的。具体实现步骤如下:
在客户段启动SSH命令转发FTP控制连接。使FTP客户程序和转发的端口建立连接。这里需要关闭被动模式。
client $ssh -f -n -L2001:localhost:21 server sleep 10000&
client $ ftp localhost 2001
Connected to localhost
220 server FTP server ready.
Password:
230 User res logged in.
ftp> sendport
Use of PORT cmds off.
ftp> passive
Passive mode off.
下面,我们还需要确定FTP客户的真正和代理数据端口。在客户端,可以使用netstat命令:
client $netstat -t|grep 2001
tcp 0 0 client:2001 client:3310 ESTABLISHED
tcp 0 0 client:3310 client:2001 ESTABLISHED
可以看出FTP客户程序是通过3310端口和SSH连接。
下面我们需要知道服务器端使用的端口,假设现在服务器只为你一个人服务,使用netstat,我们获得以下输出:
server $netstat|grep ftp
tcp 0 0 server:8250 server:ftp ESTABLISHED
tcp 0 0 server:ftp server:8250 ESTABLISHED
这样,我们知道了3310端口被转发到了服务器端的8250端口。因此,FTP服务器就会认为这是在这种模式下,客户程序使用的数据传输端口(和控制连接共用)。下面,我们只要把这个端口转发到客户端就可以了。
在客户端使用如下命令转发远程服务器的8250端口:
clent $ssh -f -n -R8250:localhost:3310 server sleep 10000&
接着,最好在服务器端使用如下命令:
server $ssh -f -n -L8250:localhost:3310 client sleep 10000&
最后,你可以使用FTP数据传输命令了。
7.结论
本文其实主要讨论了使用SSH进行FTP转发的一些困难,最后讨论的转发方法其实几乎没有多大实用价值:)。'