——使用U盘是一种丑陋且蠢笨的文件传输手段
自从闪存介质和带有文件传输功能的社交媒体发明以来,人们将日常工作中的文件传输方法寄托于二者,然而必须要指出的是:作为一名IT从业者和开发人员,这样的文件传输与管理策略是丑陋且愚钝的。FTP协议是一个广泛应用但鲜有人拥有完善了解的协议——即使是那些为了计算机网络原理期末考试通宵达旦的可怜本科生们也在面对FTP报文时表现出相当的无措和局促。然而无论是从事互联网WEB开发还是嵌入式Linux开发,全面而详细的了解FTP都是一件必要的准备工作。
1.FTP简介与专用名词及缩写说明
首先简述FTP协议的一切众所周知的内容,以便让这个过程的开端看起来不那么具有挑战性。FTP的全称是File Transfer Protocol也就是文件传协议,这种协议的设计目标是在不同的文件系统与物理/虚拟主机之间通过一个统一的互联网接口传输文件,其协议的详细定义可以在文档RFC959之中找到。我们可以认为FTP的整个协议由以下七大部分构成:
- 工作模式:FTP可以分为主动或者被动模式,主动模式(Active Mode)即使用一个服务器指定的固定端口(当然端口的具体选择是随机的)传输数据;而被动模式(Passive Mode)就真的使用一个服务器临时指定的随机端口传输数据。
- 控制/数据:一切网络协议都以传递信息建立连接为纲要,FTP协议采取双通道连接,其中第一类通道叫做控制连接,用于传递控制信息。而另一类通道叫做数据连接,用于传递真正的文件操作数据信息。
- 命令/响应:FTP的指令集。
- 文件操作:将物理主机对于存储介质和文件系统的操作通过互联网映射出去。
- 传输模式:将文件区分为文本(ASCII)和二进制文件传输。
- 身份验证:当你涉及到操作系统和文件系统,你不可避免地需要进行用户认证以确认权限和留下文件操作的痕迹——一份来源或者修改人不明的文件并不令人开心。
- 安全保障:原始的FTP可并不安全,那个时代人们并不对用户名、密码、文件内容这些敏感信息做传输过程之中的加密,如果真的要求极高的安全性,还是看看远处的FTPS和SFTP吧。二者虽然看起来仅仅是缩写顺序有所区别,但是前者属于FTP协议的改良,后者基于SSH协议构建。
FTP协议的历史沿革完全可以从RFC的故纸堆里面乱翻,当我查阅RFC-959时我也果断地跳过了这一部分,显然笔者和读者都是开发者而不是赛博历史学家,故而至今进入到名词解释的部分:
- ASCII:不懂这个专业名词的建议关闭本文章。
- access control:访问控制,主要定义通过FTP系统”登录“到服务器的用户的文件操作权限,主要用于阻止未授权或者意外的文件操作。然而需要指出的是,尽管我们可以将FTP的访问控制与其他的什么访问控制体系混合使用,但是最终生效的仍然是是服务器上的Daemon进程。
- byte size:在FTP协议中字长显然有两种不同的差异——真实文件内容的字长以及FTP使用报文在传输过程之中的字长。传输过程的字长永远,也必须是8 bits,然而这并不代表隐藏在FTP接口后的真实文件系统也必须限定为这一标准,实际操作中的手段可以灵活而多变。
- control connecton:控制连接,用于沟通服务端协议报文转译器与客户端协议报文转译器,用于传输指令类别的信息,不应当传递具体文件内容的数据信息。
- data connection:数据连接,在控制连接之外真正的用于传输文件数据的信道,这个连接在一次FTP会话之中可以存在复数个,并且其方向可以是双向的。一个数据连接既可以承载多个文件或者目录的内容,一个文件或者目录的内容也可以切割到多个数据连接之中传递。
- data port:用于监听数据连接的端口号
- DTP:data transfer process的缩写,用于建立并且维持数据连接,可以是主动的也可以是被动的
- EOF/EOR:结束符,对于文件为EOF,对于记录则为EOR。所谓的记录就是一个具有明确序列性和连续性的文件。
- error recovery:这是一种FTP的容灾机制,允许用户从某种错误恢复原有进程,例如主机文件系统或者传输过程之中引起的误码导致的文件系统结构或者文件内容错误。这种机制表现为从给定的检查点(checkpoint)重新启动文件传输,也就是断点重传
- NVT:network virtual terminal的缩写,遵循telnet协议定义
- NVFS:network virtual file-system的缩写,在FTP传输过程之中可以认为将主机文件系统上的信息通过网络进行逻辑映射以便拥有更好的操作逻辑,但是需要指出这与NFS是不一样的。
- page:FTP协议一个文件或者一个信息流被切分为许多不同但具有某种独立性的数据碎片,这种数据碎片就是page,FTP支持将一个不连续的文件拆分为多个page进行传输
- PI:protocol interpreter,FTP报文转译器,也就是协议解析程序。
- reply:响应,一种特殊的报文信息,只包含肯定性或者否定性的确认,只存在于控制连接中
2.FTP指令与工作模式详解
基于以上介绍,不难理解下图所展示的FTP基本工作模式。在下图展示的FTP工作模型之中,不难梳理出FTP的工作流程:首先由用户端PI开启控制连接,标志着一次会话的开启;通过控制连接,客户端与服务器将会协商关于接下来要用到的数据连接的若干细节,例如传输模式、传输端口、数据长度、数据内容等等;完成所有数据连接的传输后,由客户端PI发出关闭会话信号。在以上的叙述中,关于数据连接要指出:
- FTP的整个协议工作在TCP/IP层,使用TCP作为基础,所以它的数据传输是可靠传输
- FTP的控制连接与数据连接必不能使用同一端口
- FTP的数据连接不具有缓冲区,至少这种数据连接在DTP与文件系统的层面不具有缓冲性,也就是说数据的传输在协议层面是实时的
然而,了解了基本工作模式之后,我们可以发现PI和DTP并不是绑定的,控制连接与数据连接明显也是可分离的,那么略微发挥想象力,我们不难想象如下这样的工作场景:
然而有过计算机网络原理基础或者开发经验的朋友们则会立即想到一些问题,这些问题主要集中在数据连接层面,这很好理解,控制连接就是经典的Telnet协议的另一种实现而已,但是数据连接相关的问题可能主要出在以下几个方面:
- 数据连接的方向当然可以通过控制连接讨论,但是具体的端口使用应当如何规定?
- 如果建立数据连接的双方其中有一方或者双方都必须工作在很严格的防火墙策略之下,并且二者无法通过真实或者虚拟的子网跳过防火墙堡垒机连接该怎么办?
针对这一问题,FTP采用了灵活的连接策略,也就是所谓的”主动传输“和”被动传输“,需要着重指出的是这两种传输方式都是对于服务端DTP而言的。这两种传输模式可以通过如下不严谨但相当生动的叙述非常容易地理解:
- 主动传输:服务器拥有一个固定的端口X(一般来说是20),客户端使用对应命令对服务器说:我将会开放Y端口,这一端口能够安全的通过防火墙,你来主动连接我吧
- 被动传输:客户端表示它没有能够突破堡垒机的入站端口,于是对服务器说:你给我一个端口号我去连接你,你等着我连接不用考虑我的防火墙。而后服务器在响应信息之中下发给客户端一个可用的能够通过服务端防火墙的协议号用于连接。
在一台安装了基础的FTP工具的Linux主机上,我们可以通过这样的命令看到可以进行的FTP操作,这也就对应了FTP协议之中的整个指令集,对于其中的两个负号需要做特殊说明,他们并不是FTP协议原生支持的部分,而是ftp这个命令行工具包装的用户操作接口:
- ! 后跟的指令将会在主机中执行而不会进入PI,如果只有一个!表示退出FTP命令行
- $ 表示新建一个宏定义,例如我们在一次会话之中也许会多次用到某个很长或者很难输入的字符串,这个时候如果你不是使用自动脚本而是进行手动操控显然令人恼火。
ftp
help
在这个ftp命令行工具之中,使用help+命令即可获取命令的功能信息,使用usage+命令即可查看命令的详细用法。FTP协议通信报文的请求代码和响应代码如下所示:
请求码 | 对应功能解释 |
ABOR | 终止正在执行的FTP命令 |
ACCT | 指定登录FTP远程主机的账户 |
ALLO | 分配指定长度的的服务器存储空间用于存储文件 |
APPE | 在指定文件后追加数据 |
CDUP | 返回上一级目录 |
CWD | 更改工作目录 |
DELE | 删除指定文件 |
EPSV | 在IPv6下进入扩展被动模式 |
HELP | 查询FTP远程服务器的帮助信息 |
LIST | 列出当前目录或者指定目录下的文件和子文件夹列表 |
MKD | 创建新目录 |
MODE | 设置文件传输模式 |
FEAT | 获取服务器支持的FTP功能列表 |
MLSD | 列出指定目录下的文件和子文件夹列表 |
MLST | 显示指定文件或者目录的属性 |
NLST | 列出指定目录下的文件名 |
NOOP | 什么也不做,用于连接保活 |
PASS | 输入用户登录密码 |
PASV | 进入被动模式 |
PORT | 进入主动模式,并且设置端口 |
PWD | 显示当前工作目录 |
QUIT | 结束FTP会话 |
REIN | 重新初始化登录状态 |
REST | 设置数据传输的起点 |
RETR | 下载指定文件 |
RMD | 删除指定目录 |
RNFR | 设置重命名文件的旧文件名 |
RNTO | 设置重命名文件的新文件名 |
SITE | 发送关于远程服务器的特殊命令 |
SIZE | 查询指定文件的大小 |
SMNT | 挂载指定的文件系统 |
STAT | 显示FTP服务器的状态 |
STOR | 上传文件 |
STOU | 存储文件到服务器上并且指定文件名 |
STRU | 设置文件结构 |
SYST | 查看服务器的操作系统 |
TYPE | 设置数据连接的类型,可以是二进制或者ASCII |
USER | 指定FTP登录的用户名 |
EPRT | 在IPv6下使用主动模式并且指定端口 |
响应码 | 对应功能解释 |
110 | 服务器等待客户端发送REST进行重传 |
120 | 服务器尚未就绪,等待 |
125 | 数据连接已打开,即将进行传输 |
150 | 文件状态正常,即将打开数据连接 |
200 | 命令已完成 |
202 | 命令未执行 |
211 | 服务器系统状态 |
212 | 服务器目录状态 |
213 | 服务器文件状态 |
214 | 服务器帮助信息 |
215 | 服务器系统类型 |
220 | 服务已就绪 |
221 | 控制连接正在关闭 |
225 | 数据连接已打开,当前处于空闲模式 |
226 | 数据连接已关闭 |
227 | IPv4被动模式已就绪 |
229 | IPv6被动模式已就绪 |
230 | 用户已登录 |
250 | 文件操作成功完成 |
257 | 创建目录/文件成功 |
331 | 需要验证密码 |
332 | 需要验证账户名 |
350 | 命令包含的文件操作需要更加详细指定 |
421 | 服务不可用 |
425 | 数据连接无法打开 |
426 | 数据连接已关闭 |
450 | 文件不可用 |
451 | 操作中止 |
452 | 分配的存储空间不足以处理该命令 |
500 | 命令语法错误,或者服务器不支持该命令 |
501 | 命令参数语法错误 |
502 | 命令未执行 |
503 | 服务拒绝访问 |
504 | 命令未能描述如何操作,需要详细命令 |
530 | 操作需要身份验证 |
532 | 存储文件需要进行完整性校验 |
550 | 操作无法执行,文件拒绝访问 |
551 | 操作无法执行,未进行存档/备份 |
552 | 操作无法执行,存储空间不足 |
553 | 操作无法执行,非法文件名 |
3.VSFTPD在Ubuntu系统中的安装与使用
VSFTPD,全程very secure FTP daemon,是一种提高安全性并且将服务托管到daemon进程的FTP服务器,它完全开源并且可以运行在Linux、BSD、Solaris等诸多系统上。振奋代码针对于安全性和快速性都进行了大量的优化,速度快,稳定性高,易用性好,安全性可靠。并且除了传统的IPv4支持之外,VSFTPD完全支持IPv6的特性,也支持SSL/TLS加密传输。在安全认证方面,VSFTPD支持本地用户、PAM用户认证、LDAP用户认证等多种安全认证和权限管理方法。并且VSFTPD也支持虚拟用户和虚拟目录,便于管理员进行逻辑映射。安装VSTPD主要有以下步骤:
- 通过apt包管理工具安装vsftpd
- 配置vsftpd配置文件,默认地址/etc/vsftpd.conf
- 创建FTP用户,改写访问权限
- 放行防火墙对应端口
- 重启FTP服务并设置开机启动托管
#首先更新apt
sudo apt update
#其次安装vsftpd
sudo apt install vsftpd
#以下是VSFTPD配置文件解释
#开启服务控制连接端口监听
listen=YES
#开启主动模式
port_enable=YES
#配置传输连接从20端口开始,设置此选项将会强制主动模式
connect_from_port_20=YES
#设置主动模式端口
ftp_data_port=20
#开启被动模式
pasv_enable=YES
#设置被动模式起止端口
pasv_min_port=10000
pasv_max_port=10100
#是否允许匿名用户登录和上传文件新建目录
anonymous_enable=NO
anon_upload_enable=NO
anon_mkdir_write_enable=NO
#是否开启FTP可写入权限
write_enable=YES
#是否允许任何服务器上的本地用户通过FTP登录访问文件,并且设置本地用户访问掩码
local_enable=YES
local_umask=077
#是否允许通过FTP远程连接显示目录信息
dirmessage_enable=YES
#是否不适用GMT时间而使用服务器系统的本地时间
use_localtime=YES
#对于上传和下载动作是否进行日志记录
xferlog_enbale=YES
#设置日志记录文件的存放地点
xferlog_file=/var/log/vsftpd.log
#日志记录文件是否符合标准格式
xferlog_std_format=YES
#配置可匿名上传的用户拥有权限,注意这个名字不能是root
chown_uploads=NO
chown_username=<YOUR_EXPECTED_NAME>
#设置连接无动作超时时间和传输连接无响应超时时间,单位为s
idle_session_timeout=600
data_connection_timeout=120
#设置SSL/TLS认证文件以及该功能是否开启
ssl_enable=NO
rsa_cert_file=/etc/ssl/certs/ssl-certs-snakeoil.pem
rsa_private_key_file=/etc/ssl/private/ssl_cert_snakeoil.key
#设置VSFTPD用到的PAM认证服务名称
pam_service_name=vsftpd
#设置安全根目录,这个目录应当是一个空目录,以下是默认值
secure_chroot_dir=/var/run/vsftpd/empty
#是否允许递归遍历目录显示信息,这可能会大量占用服务器资源
ls_recurse_enable=NO
#此选项用于设置VSFTPD服务默认的非特权用户,应当是宿主系统中存在的一个用户并且没有sudo权限,可以不设置
nopriv_user=ftpsecure
#是否允许异步中断FTP操作,基于安全性考虑不应当这样设置,但是一些较古老的驱动PI可能出现识别错误
async_abor_enable=NO
#是否打开以ASCII模式上传下载文件,这将导致性能的些许下降
ascii_upload_enable=NO
ascii_download_enable=NO
#是否拒绝含有任何未经审定的电子邮件地址的文件存在
deny_email_enable=NO
banned_email_file=/etc/vsftpd.banned_emails
#是否开启全部本地用户对于其home目录的控制
chroot_local_user=NO
#限制在home目录的用户的列表
chroot_list_enable=YES
chroot_list_file=/etc/vsftpd.chroot_list
#开启非home目录的写入权限
allow_writeable_chroot=YES
#开启可登录用户列表
userlist_enable=YES
userlist_file=/etc/vsftpd.user_list
#设置是否是黑名单,YES表示黑名单NO表示白名单
userlist_deny=NO
#推荐创建一个系统用户专门为FTP使用,并且在创造时指定其home目录和禁止其登录shell
sudo mkdir /home/ftpdir
sudo useradd -d /home/ftpdir -s /sbin/nologin ftpuser
sudo chown ftpuser:ftpuser /home/ftpdir
sudo passwd ftpuser <YOUR_FTP_USER_PASSWORD>
#创建用户后将其写入配置中的对应文件例如上文/etc/vsftpd.chroot_list
#如果由多个用户,那么应当保证一行一个
#如果防火墙建议工具未开启需要先开启
sudo ufw enable
#打开防火墙21端口(控制连接端口)
sudo ufw allow 21
#打开防火墙20端口(主动数据端口)
sudo ufw allow 20
#打开防火墙10000-10100端口(被动数据端口)
sudo ufw allow 10000:10100/tcp
#重启防火墙
sudo ufw reload
#重启VSFTPD服务并且载入开机启动
systemctl restart vsftpd
systemctl enable vsftpd
#查看服务状态,若显示为active(running)即可
systemctl status vsftpd
在以上配置过程中,可能遇到诸多具有歧义性的问题,根据笔者的经验以下列出的问题最常见,最令人费解或者难以找到现成解决方案:
- 如果要使用新建本地用户登录的方式必须开启local_enable=YES
- 有关chroot部分的设置只是限定用户通过FTP所更改的文件范围能否超越其home目录
- 如果要使用/sbin/nologin选项禁止ftp用户通过shell登录,那么必须这样做:
- 找到PAM文件,默认来说是/etc/pam.d/vsftpd
- 注释掉PAM文件之中的auth required pam_shells.so
- 不可以同时打开listen选项和listen_ipv6选项
- 务必保证配置文件的第一个有效选项是listen=YES整个配置文件才生效