FTP 服务(vsftpd)
本文合并自:03-FTP服务.md、10-FTP服务.md、1-11-FTP服务部署.md
适用环境:CentOS / RHEL 5、6、7(vsftpd 2.x / 3.x)。文中凡涉及版本差异之处均并列标注,请按自己的系统版本选用。
内容覆盖:FTP 协议与端口、主动/被动模式原理与选择、传输模式、常见 FTP 服务端软件对比、FTP/FTPS/SFTP 区别、vsftpd 安装与
vsftpd.conf全参数详解、匿名用户/本地用户/虚拟用户三种模式实战、chroot 监牢、用户黑白名单、目录消息与欢迎横幅、xferlog 与 vsftpd.log 日志分析、限速与并发限制、xinetd 托管、TCP Wrapper 与 firewalld/iptables 放行、FTPS 加密、客户端命令与排错清单,并附综合练习与参考答案。
一、FTP 概述与原理
1.1 FTP 的作用
FTP 是 File Transfer Protocol(文件传输协议)的英文简称,中文简称"文传协议",用于在 Internet 上控制文件的双向传输(上传与下载)。
FTP 的特点:
- 成本低:服务端软件多为开源免费(vsftpd、proftpd 等)。
- 跨平台:Windows、Linux、macOS、各类嵌入式设备均有成熟客户端。
- 可跨公网:只要放行相应端口,即可通过互联网传输文件。
- 协议成熟:RFC 959 定义,浏览器、下载工具、yum/apt 等均原生支持。
常用的 FTP 客户端工具:ftp、lftp、wget、curl、FileZilla、浏览器。
1.2 FTP 的端口
FTP 是一个"双通道"协议:控制连接负责传命令,数据连接负责传文件内容与目录列表。
| 端口 | 名称 | 用途 |
|---|---|---|
| TCP 21 | 命令端口 / 控制连接 | 客户端发送登录、LIST、RETR、STOR 等命令,服务器返回状态码 |
| TCP 20 | 数据端口(主动模式) | 主动模式下由服务器端用 20 端口主动发起数据连接 |
| 随机高端口 | 数据端口(被动模式) | 被动模式下服务器监听一个 >1023 的临时端口,由客户端来连 |
| 端口类型 | 默认值 | 说明 |
|---|---|---|
| 命令端口 | 21 | 控制连接,用于发送命令 |
| 数据传输端口 | 受模式影响 | 被动模式下两端都是随机端口;主动模式下服务器使用 20 端口,客户端使用随机端口 |
要点:FTP 服务在未传输数据时只监听 21 端口,只有真正传输数据(ls、get、put)时才会打开数据端口。因此用 netstat 观察时,平时看不到 20 端口或高端口。
1.3 主动模式与被动模式
FTP 有两种连接模式,都是针对数据传输而言,并且是从服务器的角度来命名的:服务器主动去连客户端叫主动模式,服务器被动等客户端来连叫被动模式。
| 模式 | 控制连接 | 数据连接 |
|---|---|---|
| 主动模式(PORT) | 客户端 → 服务器 21 | 服务器 20 → 客户端(>1024 随机端口) |
| 被动模式(PASV) | 客户端 → 服务器 21 | 客户端 → 服务器(>1024 随机端口) |
| 模式 | 控制通道端口 | 数据通道 | 连接发起方 | 数据端口说明 |
|---|---|---|---|---|
| 主动模式(Active) | 服务器 TCP 21 | 服务器通过 TCP 20 连接客户端指定端口 | 服务器主动连接客户端 | 服务器固定 20 端口,客户端随机端口 |
| 被动模式(Passive) | 服务器 TCP 21 | 服务器打开临时端口,客户端主动连接 | 客户端主动连接服务器 | 服务器在指定范围内随机端口,客户端随机端口 |
1.3.1 主动模式(PORT)完整交互过程
客户端提供一个端口用于接收数据,然后由服务器发起数据连接(服务器主动):
- FTP 客户端首先用自己的一个随机高端口与服务器的 TCP 21 端口建立连接,这条连接用于发送命令(控制连接)。
- 客户端需要接收数据时(例如执行
ls或get),在该控制通道上发送PORT命令,PORT命令中包含客户端用于接收数据的 IP 和端口号(一个 >1024 的随机端口)。 - 传送数据时,服务器端通过自己的 TCP 20 端口主动连接至客户端在
PORT命令中指定的那个端口,发送数据。 - 也就是说,FTP server 必须与客户端建立一个新的连接来传送数据,这个新连接的发起方是服务器。
图示说明(箭头方向代表 TCP 连接的发起方向):
主动模式(PORT)
客户端 服务器
随机端口 N (>1024) ──────────────────► 21 (控制连接,客户端发起)
◄──── 220/230 ────
发送 PORT N+1 命令 ──────────────────► 21
随机端口 N+1 ◄────────────────── 20 (数据连接,服务器发起!)
1.3.2 被动模式(PASV)完整交互过程
服务器提供一个端口用于传输数据,然后由客户端发起数据连接(服务器被动):
- 建立控制通道的过程与主动模式类似:客户端用随机高端口连接服务器 21 端口。但建立连接后客户端发送
PASV(Pasv)命令而不是PORT命令。 - 服务器收到
PASV命令后,在本机打开一个临时端口(端口号大于 1023 小于 65535),并通过控制连接把这个端口号通知客户端,表示"请到这个端口上来传数据"。 - 客户端主动连接 FTP 服务器的这个端口,FTP 服务器通过该端口传送数据。
图示说明:
被动模式(PASV)
客户端 服务器
随机端口 N (>1024) ──────────────────► 21 (控制连接,客户端发起)
发送 PASV 命令 ──────────────────► 21
◄─ 227 Entering Passive Mode (x,x,x,x,p1,p2)
随机端口 N+1 ──────────────────► 随机端口 P (数据连接,客户端发起)
其中 227 响应里的 p1,p2 换算出实际数据端口:端口 = p1 * 256 + p2。
1.3.3 默认端口总结
| 用途 | 默认端口 | 说明 |
|---|---|---|
| 命令端口 | 21 | 传输控制命令 |
| 数据端口(主动模式) | 20 | 服务器端固定使用 |
| 数据端口(被动模式) | 随机高端口 | 可通过 pasv_min_port/pasv_max_port 配置指定范围 |
1.4 两种模式在防火墙与 NAT 环境下的差异与选择
哪种模式更好?
- 主动 FTP 对服务器管理有利,对客户端管理不利:服务端只需放行 21 和 20 两个固定端口即可,非常好管;但服务端要主动连接客户端的高端口,而客户端的个人防火墙默认只拒绝入站包,因此这个入站的数据连接常常被客户端防火墙阻塞。客户端处于 NAT 之后时,
PORT命令里携带的还是内网私有 IP,服务器根本连不上,问题更严重。 - 被动 FTP 对客户端管理有利,对服务端管理不利:客户端只发起出站连接,任何个人防火墙和 NAT 都能顺利通过;但客户端要主动连接服务端的随机高端口,服务端防火墙如果没放行这一大段端口就会失败。
- 折衷方案(生产推荐):使用被动模式,并在 vsftpd 中指定一个较小的端口范围(如 30001–30010),只对该范围开放防火墙。这样既照顾了 NAT 后的客户端,也不需要在服务端大开端口。
其他实践结论:
- vsftpd 默认工作在被动模式。
- 被动模式在防火墙 / NAT 环境中更常用,需要固定并放行数据端口范围。
yum、wget、大多数图形客户端默认使用被动模式。因此如果服务端配置了pasv_enable=NO(只允许主动模式),yum 通过 FTP 源装软件会失败。- 服务端处于 NAT 之后(有公网 IP 映射)时,被动模式还需配置
pasv_address=公网IP,否则227响应里回给客户端的是内网地址。
1.5 传输模式:ASCII 与 Binary
FTP 的"传输模式"与"连接模式"是两个不同概念,传输模式指的是文件内容如何编码传输。
| 传输模式 | 客户端命令 | 说明 | 适用场景 |
|---|---|---|---|
| ASCII(文本) | ascii |
传输过程中会转换行结束符(Windows 的 CRLF 与 Unix 的 LF 互转) |
纯文本文件、脚本、配置文件 |
| Binary(二进制) | binary 或 bin |
原样逐字节传输,不做任何转换 | 压缩包、图片、可执行程序、数据库文件、RPM 包等 |
重要提示:传输二进制文件时若误用 ASCII 模式,文件会被破坏(体积改变、无法解压或运行)。实践中除纯文本外,一律先执行 binary。传输日志 xferlog 中第 6 个字段就记录了本次传输用的是 a(ASCII)还是 b(binary)。
1.6 常见 FTP 服务端软件对比
| 软件 | 特点 | 说明 |
|---|---|---|
| vsftpd | very secure FTP daemon,安全性优先,配置简单,性能好 | RHEL/CentOS 默认自带并推荐,本文主讲;支持匿名/本地/虚拟用户、chroot、限速、FTPS |
| proftpd | 配置风格类似 Apache(<Directory> 段),功能非常丰富,模块化 |
适合需要复杂目录级 ACL、虚拟主机的场合,配置相对复杂 |
| pure-ftpd | 轻量、安全,虚拟用户支持极佳(可对接 MySQL/LDAP/PostgreSQL) | 适合大量虚拟用户的托管环境,参数以命令行/配置目录形式给出 |
选型建议:RHEL 系发行版做常规文件分发与内网共享,直接用 vsftpd 即可,官方源支持、SELinux 策略齐备。
1.7 FTP、FTPS 与 SFTP 的区别
| 项目 | FTP | FTPS | SFTP |
|---|---|---|---|
| 全称 | File Transfer Protocol | FTP over SSL/TLS | SSH File Transfer Protocol |
| 加密 | 完全不加密,账号密码与数据都是明文 | 用 SSL/TLS 加密控制连接和数据连接 | 用 SSH 加密,整条通道加密 |
| 端口 | 21(控制)+ 20/随机(数据) | 显式 FTPS:21 + 数据端口;隐式 FTPS:990 | 22(复用 sshd 端口) |
| 底层服务 | vsftpd / proftpd 等 | 同 FTP 服务端开启 ssl_enable |
sshd,与 FTP 服务端毫无关系 |
| 是否双通道 | 是 | 是(防火墙仍需处理数据端口) | 否,单通道,防火墙友好 |
| 需要证书 | 不需要 | 需要服务端证书 | 使用 SSH 主机密钥 |
| 常见客户端 | ftp、lftp、FileZilla | lftp、FileZilla(选"显式 TLS") | sftp、scp、FileZilla(选 SFTP) |
结论:只要装了 sshd,SFTP 是零成本、最省事的加密文件传输方案;若必须保留 FTP 协议语义(例如老客户端、匿名下载),再用 FTPS。明文 FTP 只适合内网或纯匿名公开下载。
1.8 FTP 用户类型
vsftpd 支持三类登录用户,这也是后文实战的三条主线。
| 用户类型 | 说明 | 认证来源 | 默认登录目录 |
|---|---|---|---|
| 匿名用户(anonymous) | 无需真实账号,任何人可登录,用于公开下载 | 无(密码任意/为空) | /var/ftp(可用 anon_root 改) |
| 本地用户(local) | FTP 服务器上真实存在的系统用户 | /etc/passwd + /etc/shadow,走 PAM |
该用户的家目录(可用 local_root 改) |
| 虚拟用户(virtual) | 只用于 FTP 的"假"用户,系统里不存在同名账号 | 独立的用户库(Berkeley DB / MySQL),映射到一个系统宿主账号 | 由 local_root 或 user_config_dir 中的个性化配置指定 |
匿名用户的两个名字:
| 用户名 | 系统中是否存在 | 默认登录目录 |
|---|---|---|
ftp |
存在(/etc/passwd 中有 ftp 用户) |
ftp 用户的家目录 /var/ftp |
anonymous |
不存在(是 ftp 的别名,纯匿名账号) |
/var/ftp |
两者密码均可为任意值或空。
虚拟用户的优势:不在系统中创建真实账号,因此即使 FTP 账号泄露也无法登录系统(无 shell、无邮箱、无 SSH),安全性远高于直接开放本地用户;且可对每个虚拟用户单独设定目录与读写权限。
二、vsftpd 安装与配置文件详解
2.1 安装
# 服务端
yum install vsftpd -y
sudo yum install -y vsftpd
# 客户端
yum install ftp lftp -y
# 一次装齐(练习常用)
sudo yum install vsftpd ftp -y
虚拟用户环节还需要 db4-utils(CentOS 6)或 libdb-utils(CentOS 7),用于 db_load 命令:
# CentOS 6
yum install db4-utils -y
# CentOS 7
yum install libdb-utils -y
2.2 启动、停止与开机自启
CentOS 6(SysV init):
/etc/init.d/vsftpd start
/etc/init.d/vsftpd stop
/etc/init.d/vsftpd restart
/etc/init.d/vsftpd reload # 修改配置后重新加载,不断开已有连接
chkconfig vsftpd on # 开机自启
chkconfig --list vsftpd
也可使用 service vsftpd start 等价写法。
CentOS 7(systemd):
systemctl start vsftpd
systemctl stop vsftpd
systemctl restart vsftpd
systemctl reload vsftpd
systemctl enable vsftpd # 开机自启
systemctl status vsftpd
修改
vsftpd.conf后必须reload或restart才生效。CentOS 6 用/etc/init.d/vsftpd reload,CentOS 7 用systemctl reload vsftpd。
2.3 验证监听端口
netstat -ntl | grep :21
# tcp 0 0 0.0.0.0:21 0.0.0.0:* LISTEN
netstat -ntlup | grep :21 # 带进程名
ss -ntl | grep :21 # CentOS 7 推荐用 ss
lsof -i:21
# COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
# vsftpd 3321 root 3u IPv4 21400 0t0 TCP *:ftp (LISTEN)
被动模式在未传输数据时只监听 21 端口,数据传输时才打开高端口。
2.4 目录与文件结构
| 路径 | 作用 |
|---|---|
/etc/vsftpd/vsftpd.conf |
主配置文件,所有参数都写在这里 |
/etc/vsftpd/ftpusers |
PAM 级黑名单,此文件中的用户一律禁止登录 FTP(由 /etc/pam.d/vsftpd 中的 pam_listfile 模块读取),默认含 root 及各系统账号 |
/etc/vsftpd/user_list |
用户名单文件,配合 userlist_enable / userlist_deny 作黑名单或白名单,路径由 userlist_file 指定 |
/etc/vsftpd/chroot_list |
chroot 名单文件(默认不存在,需手动创建),路径由 chroot_list_file 指定 |
/etc/pam.d/vsftpd |
vsftpd 的 PAM 认证配置,虚拟用户改造的关键文件 |
/etc/logrotate.d/vsftpd |
vsftpd 日志轮转配置 |
/var/ftp |
匿名用户默认根目录(即系统 ftp 用户的家目录) |
/var/ftp/pub |
匿名用户公共子目录,通常在此赋写权限做上传区 |
/var/log/xferlog |
标准 xferlog 格式传输日志 |
/var/log/vsftpd.log |
vsftpd 自有格式日志 |
/usr/sbin/vsftpd |
主程序 |
/etc/xinetd.d/vsftpd |
由 xinetd 托管时的配置文件(CentOS 6 及之前,需手动创建) |
2.5 查看有效配置
配置文件里注释很多,用下面命令只看生效的行:
grep -v ^# /etc/vsftpd/vsftpd.conf | grep -v ^$
完整参数说明查 man 文档:
man 5 vsftpd.conf
2.6 vsftpd.conf 参数逐项详解(分类)
配置文件语法:参数=值,等号两边不能有空格,一行一条,# 开头为注释。布尔值写 YES/NO(大写)。
2.6.1 匿名用户相关
| 参数 | 默认值 | 说明 |
|---|---|---|
anonymous_enable=YES |
YES(CentOS 6)/ 视版本 | 是否允许匿名用户(ftp、anonymous)登录 |
anon_upload_enable=YES |
NO | 是否允许匿名用户上传文件(还需 write_enable=YES 且目录可写) |
anon_mkdir_write_enable=YES |
NO | 是否允许匿名用户创建目录 |
anon_other_write_enable=YES |
NO | 是否允许匿名用户执行删除、重命名、覆盖等其他写操作(delete、rename) |
anon_umask=022 |
077 | 匿名用户上传文件时使用的 umask。022 → 文件 644;0222 → 文件 444;0777 → 文件 000 |
anon_root=/var/ftp |
/var/ftp |
匿名用户登录后的初始(根)目录 |
anon_world_readable_only=YES |
YES | 匿名用户只能下载"所有人可读"的文件。设为 NO 可下载非 world-readable 文件 |
anon_max_rate=100000 |
0(不限) | 匿名用户最大传输速率,单位字节/秒 |
chown_uploads=YES |
NO | 是否修改匿名用户上传文件的所有者;开启后上传文件权限受 chown_upload_mode 影响 |
chown_username=nobody |
root | 与 chown_uploads 配合,指定匿名上传文件的新所有者用户名 |
chown_upload_mode=0644 |
0600 | 与 chown_uploads 配合,指定被改属主后文件的权限模式 |
no_anon_password=YES |
NO | 匿名登录时不询问密码,直接进入 |
deny_email_enable=YES |
NO | 启用后可拒绝以 banned_email_file 中的邮箱作为匿名口令的登录 |
banned_email_file=/etc/vsftpd/banned_emails |
— | 配合 deny_email_enable 的被禁邮箱列表 |
2.6.2 本地用户相关
| 参数 | 默认值 | 说明 |
|---|---|---|
local_enable=YES |
YES | 是否允许本地(系统)用户登录。本地用户即 FTP 服务器上的系统用户,密码就是系统登录密码 |
write_enable=YES |
NO(编译默认)/ 配置文件里给 YES | 写操作的总开关,上传、删除、创建目录、重命名等一切写操作都必须先开这个 |
download_enable=YES |
YES | 是否允许下载。设为 NO 则任何用户都不能 get |
local_umask=022 |
077 | 本地用户新建文件/目录的权限掩码。022 → 文件 644、目录 755;0066 → 文件 600 |
local_root=/ftpdata |
用户家目录 | 统一指定所有本地用户登录后的初始目录;不影响用户 SSH 登录系统时的家目录 |
local_max_rate=100000 |
0(不限) | 本地用户最大传输速率,单位字节/秒 |
guest_enable=YES |
NO | 启用虚拟用户(guest)模式,把所有非匿名登录都映射成 guest_username 指定的系统账号 |
guest_username=virtual |
ftp | 虚拟用户映射到的系统宿主账号 |
user_config_dir=/etc/vsftpd/vusers_dir |
— | 用户个性化配置目录:目录中以用户名为文件名,可为每个(虚拟)用户单独设定参数 |
virtual_use_local_privs=YES |
NO | 让虚拟用户拥有与本地用户相同的权限(受 write_enable 控制),而不是匿名用户级别的权限 |
local_root相比"逐个修改/etc/passwd中所有用户的家目录"更安全、副作用更小,推荐使用。
2.6.3 权限与限制(chroot 监牢)
| 参数 | 默认值 | 说明 |
|---|---|---|
chroot_local_user=YES |
NO | 把所有本地用户禁锢(chroot)在其初始目录内,不能 cd 到上层目录 |
chroot_list_enable=YES |
NO | 启用 chroot 名单功能 |
chroot_list_file=/etc/vsftpd/chroot_list |
/etc/vsftpd/chroot_list |
chroot 名单文件路径,一行一个用户名(文件默认不存在,需手动创建) |
allow_writeable_chroot=YES |
NO | vsftpd 2.3.5+/CentOS 7 必需:允许被 chroot 的根目录本身可写,否则报 500 OOPS: vsftpd: refusing to run with writable root inside chroot |
secure_chroot_dir=/usr/share/empty |
— | vsftpd 内部使用的空目录,必须存在且不可写,不要改 |
chroot_local_user 与 chroot_list_enable 的组合逻辑(重要):
chroot_local_user |
chroot_list_enable |
chroot_list 的含义 |
|---|---|---|
| NO | YES | 名单中的用户被 chroot(黑名单式:只锁这几个人) |
| YES | YES | 名单中的用户不被 chroot(例外名单:除这几个人外都锁) |
| YES | NO | 所有本地用户都被 chroot |
| NO | NO | 所有本地用户都不被 chroot(默认,可随意浏览整个文件系统) |
匿名用户默认就在 chroot 环境中,无需额外配置。
概念补充:chroot(change root)用于把用户活动范围锁定在登录目录内,登录目录成为该用户视角下的
/,增强安全性。注意在 shell 里直接chroot /mnt/会因目标目录缺少 bash 及其依赖库而报错,只能 chroot 一个包含完整运行环境的目录;而 vsftpd 的 chroot 不需要目标目录里有 shell,因为 FTP 传输不依赖目标目录内的可执行文件。
2.6.4 连接与性能
| 参数 | 默认值 | 说明 |
|---|---|---|
listen=YES |
YES | 是否以 standalone(独立守护进程)方式运行 IPv4 监听。由 xinetd 托管时必须改为 NO |
listen_ipv6=NO |
NO | 是否以 standalone 方式监听 IPv6。listen 与 listen_ipv6 不能同时为 YES,否则服务启动失败(CentOS 7 默认模板里 listen=NO、listen_ipv6=YES) |
listen_address=10.0.0.61 |
— | 只在指定 IP 上监听(多网卡服务器常用) |
listen_port=21 |
21 | 控制连接监听端口,可改为非标准端口 |
max_clients=300 |
0(不限) | 服务器允许的总并发连接数,仅 standalone 模式有效 |
max_per_ip=3 |
0(不限) | 单个客户端 IP 的最大连接数,仅 standalone 模式有效 |
idle_session_timeout=300 |
300 | 控制连接空闲多少秒后断开(单位秒) |
data_connection_timeout=300 |
300 | 数据连接建立/传输超时时间(单位秒) |
accept_timeout=60 |
60 | 被动模式下等待客户端来连接数据端口的超时 |
connect_timeout=60 |
60 | 主动模式下连接客户端数据端口的超时 |
connect_from_port_20=YES |
YES | 主动模式的数据连接是否从服务器 20 端口发出(即是否支持标准主动模式) |
pasv_enable=YES |
YES | 是否启用被动模式。设为 NO 则只允许主动模式 |
pasv_min_port=30001 |
0(随机) | 被动模式数据端口范围下限 |
pasv_max_port=30010 |
0(随机) | 被动模式数据端口范围上限 |
pasv_address=1.2.3.4 |
— | 服务器在 NAT 之后时,227 响应中回给客户端的公网地址 |
pam_service_name=vsftpd |
vsftpd | PAM 服务名,对应 /etc/pam.d/ 下的文件名。虚拟用户时改为自定义文件名 |
nopriv_user=nobody |
nobody | vsftpd 以非特权身份运行时使用的账号 |
session_support=NO |
NO | 是否记录 utmp/wtmp 会话(开启后 who 能看到 FTP 会话,但有性能开销) |
ls_recurse_enable=YES |
NO | 是否允许 ls -R 递归列目录(大目录下会消耗大量资源) |
ascii_upload_enable=YES |
NO | 是否允许 ASCII 模式上传 |
ascii_download_enable=YES |
NO | 是否允许 ASCII 模式下载 |
utf8_filesystem=YES |
NO | 文件系统使用 UTF-8 编码,用于解决中文文件名乱码 |
2.6.5 日志相关
| 参数 | 默认值 | 说明 |
|---|---|---|
xferlog_enable=YES |
YES | 是否打开传输日志功能(记录上传/下载) |
xferlog_file=/var/log/xferlog |
/var/log/xferlog |
标准 xferlog 格式日志文件路径 |
xferlog_std_format=YES |
YES | 使用 wu-ftpd 标准 xferlog 格式;改为 NO 则改用 vsftpd 自有格式并写入 vsftpd_log_file |
dual_log_enable=YES |
NO | 同时产生 xferlog 和 vsftpd.log 两份日志 |
vsftpd_log_file=/var/log/vsftpd.log |
/var/log/vsftpd.log |
vsftpd 自有格式日志文件路径 |
log_ftp_protocol=YES |
NO | 记录所有 FTP 协议交互命令与响应,调试排错利器(需 xferlog_std_format=NO) |
syslog_enable=YES |
NO | 把日志送到系统 syslog 而不是自有文件 |
日志组合速记:
- 只要 xferlog:
xferlog_enable=YES+xferlog_std_format=YES - 只要 vsftpd.log:
xferlog_enable=YES+xferlog_std_format=NO - 两份都要:
xferlog_enable=YES+dual_log_enable=YES
2.6.6 用户访问控制
| 参数 | 默认值 | 说明 |
|---|---|---|
userlist_enable=YES |
YES | 是否启用用户名单功能 |
userlist_deny=YES |
YES | 关键参数:YES 表示 user_list 是黑名单(名单内禁止登录);NO 表示是白名单(只有名单内允许登录) |
userlist_file=/etc/vsftpd/user_list |
/etc/vsftpd/user_list |
名单数据文件路径,一行一个用户名 |
tcp_wrappers=YES |
YES | 是否支持 TCP Wrappers(/etc/hosts.allow、/etc/hosts.deny)作 IP 级访问控制 |
cmds_allowed=ABOR,CWD,LIST,... |
— | 白名单式限制客户端可执行的 FTP 协议命令 |
cmds_denied=DELE,RMD |
— | 黑名单式禁止某些 FTP 协议命令 |
deny_file={*.mp3,*.exe} |
— | 拒绝访问匹配的文件名模式 |
hide_file={*.log,.private} |
— | 隐藏匹配的文件(仍可访问,只是 ls 不显示) |
userlist_enable决定"名单功能是否有效",userlist_deny决定"名单是允许还是拒绝",两者必须配合理解。
2.6.7 欢迎信息与目录消息
| 参数 | 默认值 | 说明 |
|---|---|---|
ftpd_banner=Welcome to my ftp server !! |
— | 直接以字符串形式设定登录欢迎横幅(220 响应) |
banner_file=/etc/vsftpd/banner |
— | 从文件读取欢迎横幅,可写多行内容甚至字符画。与 ftpd_banner 同时存在时 banner_file 优先 |
dirmessage_enable=YES |
YES | 是否启用目录消息:进入某目录时显示该目录下 message_file 的内容 |
message_file=.message |
.message |
目录消息文件名 |
类似的消息提示机制在多数服务中都有:系统登录用
/etc/motd,sshd 登录前用Banner /etc/ssh_banner。
2.7 常用配置指令速览(带注释的配置片段)
anonymous_enable=YES # 启用匿名登录
local_enable=YES # 启用本地用户登录(本地用户即 FTP 服务器的系统用户)
write_enable=YES # 写操作的总开关
download_enable=NO # 是否允许下载,默认 YES
local_umask=022 # 本地用户创建文件时使用的 umask
anon_umask=0222 # 匿名用户创建文件时使用的 umask
anon_upload_enable=YES # 是否允许匿名用户上传
anon_mkdir_write_enable=YES # 是否允许匿名用户创建目录
chown_uploads=YES # 是否修改匿名用户上传文件的所有者
# 修改后匿名用户上传文件的权限将受 chown_upload_mode 影响
# 此选项与 chown_username 配合
# chown_username=nobody # 与 chown_uploads 配合,指定新的所有者用户名
chroot_local_user=YES # 把本地用户限制在其初始目录里
local_root=/ftp # 定义本地用户的初始目录
anon_root=/ftp # 定义匿名用户的初始目录
userlist_enable=YES # 启用用户名单
userlist_deny=no # 定义用户名单类型(白名单/黑名单)
pasv_min_port=33301 # 被动模式的最小端口
pasv_max_port=33310 # 被动模式的最大端口
anon_max_rate=102400 # 限制匿名用户最大传输速率,单位为字节
local_max_rate=102400 # 限制本地用户最大传输速率,单位为字节
max_clients=1000 # 最多允许的并发连接数
max_per_ip=10 # 每个 IP 的最大连接数
三、三种用户模式实战
3.0 权限判断的"两道门原则"
两道门原则:任何服务的权限控制都要同时经过"服务本身的权限配置"和"操作系统的文件权限",两者都允许才真正有权限;有防火墙时还要加上"防火墙放行"这第三道门。SELinux 开启时则还有第四道门。
排查 FTP 权限问题时,请依次检查:vsftpd.conf 参数 → 文件/目录属主与权限 → SELinux 布尔值与文件上下文 → 防火墙。
3.1 匿名用户访问
3.1.1 禁止匿名登录
vim /etc/vsftpd/vsftpd.conf
anonymous_enable=NO
# CentOS 6
/etc/init.d/vsftpd reload
# CentOS 7
systemctl reload vsftpd
此时 ftp 和 anonymous 均无法登录。
3.1.2 匿名用户的下载
匿名用户默认登录到 /var/ftp(即 /etc/passwd 中 ftp 用户的家目录)。能否下载取决于两层权限:
- 服务配置(
anonymous_enable=YES); - 操作系统权限。
目录权限对匿名访问的影响(实验数据):
/var/ftp 权限 |
能否 ls |
能否 get |
说明 |
|---|---|---|---|
| 755(默认) | 能 | 能 | 有 rx 权限 |
| 750 | 不能 | 不能 | 其他用户无任何权限,不能进入目录 |
| 754 | 不能 | 不能 | 只有 r 没有 x,不能 cd 进入目录 |
| 751 | 不能 | 能 | 只有 x 没有 r,可 cd 但不能列目录;知道文件名仍可下载 |
对文件本身还需要 r 权限才能下载。
结论:匿名用户对登录目录需有 rx 权限、对目录内文件需有 r 权限,即可下载。
匿名下载的完整最小配置:
anonymous_enable=YES
anon_root=/var/ftp
download_enable=YES
# 放几个测试文件
cp /etc/fstab /etc/inittab /var/ftp/pub/
chmod 644 /var/ftp/pub/fstab /var/ftp/pub/inittab
chmod 755 /var/ftp /var/ftp/pub
测试:
ftp 10.0.0.61
# Name: ftp 密码:直接回车
ftp> cd pub
ftp> ls
ftp> get fstab
ftp> bye
3.1.3 匿名用户的上传
默认不允许匿名上传。直接对 /var/ftp 赋 777 会导致服务拒绝启动或登录失败:
500 OOPS: vsftpd: refusing to run with writable anonymous root
因为 vsftpd 出于安全考虑不允许匿名根目录本身可写。正确做法是在登录目录下再建一个子目录用于上传:
chmod 755 /var/ftp # 根目录保持不可写
mkdir -p /var/ftp/pub
chmod 777 /var/ftp/pub # 只让子目录可写
更规范的做法是把上传目录的属主给 ftp 用户,而不是给 777:
mkdir -p /var/ftp/upload
chown ftp:ftp /var/ftp/upload
chmod 755 /var/ftp/upload
同时在配置中开启服务端的写权限:
anonymous_enable=YES
write_enable=YES # 写操作总开关,必须开
anon_upload_enable=YES # 允许匿名上传文件
anon_mkdir_write_enable=YES # 允许匿名创建目录
# CentOS 6
/etc/init.d/vsftpd reload
# CentOS 7
systemctl reload vsftpd
结论:匿名用户只能对登录目录内的子目录上传,且该子目录需要写权限(或属主为 ftp),并需开启 write_enable、anon_upload_enable 与 anon_mkdir_write_enable。
3.1.4 匿名用户删除、重命名、覆盖
anon_other_write_enable=YES
开启后匿名用户可执行 delete、rename、mkdir 等操作。在 RHEL 6 下实测:家目录(/var/ftp)里的文件不能删(因为根目录本身不可写),但可写子目录里的文件可以删。
生产环境不建议开启此参数,包括允许匿名写,安全风险很大——任何人都能删改文件。
3.1.5 允许下载刚上传的文件(anon_umask 与 anon_world_readable_only)
匿名上传的文件默认权限为 600,而 anon_world_readable_only=YES(默认)规定匿名用户只能下载"所有人可读"的文件,所以 600 权限的文件上传后自己都下载不了。
-rw------- 1 14 50 845 fstab 不可以下载
-rw-r--r-- 1 0 0 1666 inittab 可以下载
(注意 14 50 即 uid=14、gid=50,正是 ftp 用户,说明是匿名上传的文件。)
方法一:修改上传文件的 umask
anon_umask=022
上传后文件权限变为 644,可下载。若希望上传文件权限为 444(只读,防覆盖):
anon_umask=0222
若希望上传文件权限为 000(谁都读不了,只上传不下载的"投递箱"):
anon_umask=0777
方法二:允许下载非所有人可读的文件
anon_world_readable_only=NO
方法三:改上传文件的属主
chown_uploads=YES
chown_username=nobody
chown_upload_mode=0644
3.1.6 SELinux 相关设置(重要)
CentOS 6/7 默认开启 SELinux,即使 Linux 权限完全正确,匿名上传/读取仍可能失败(表现为 553 Could not create file. 或看不到文件)。
查看 FTP 相关布尔值:
getsebool -a | grep ftp
# allow_ftpd_anon_write --> off
# allow_ftpd_full_access --> off
# ftp_home_dir --> off
# ftpd_anon_write --> off # CentOS 7 命名
# ftpd_full_access --> off # CentOS 7 命名
开启(-P 表示永久生效,重启不丢):
# 允许 FTP 匿名写
setsebool -P allow_ftpd_anon_write on # CentOS 6
setsebool -P ftpd_anon_write on # CentOS 7
# 允许 FTP 完全访问(读写整个可访问文件系统,最省事但最宽松)
setsebool -P allow_ftpd_full_access on # CentOS 6
setsebool -P ftpd_full_access on # CentOS 7
# 允许本地用户访问自己的家目录(本地用户模式必需)
setsebool -P ftp_home_dir on
还需保证目录的 SELinux 上下文正确:
ls -Zd /var/ftp/pub
# drwxrwxrwx. root root system_u:object_r:public_content_rw_t:s0 /var/ftp/pub
# 若上下文不对,设置为可读写的公共内容类型
semanage fcontext -a -t public_content_rw_t "/var/ftp/upload(/.*)?"
restorecon -Rv /var/ftp/upload
教学/实验环境也可以直接临时关闭 SELinux(生产不推荐):
setenforce 0 # 临时关闭,重启失效
getenforce # 查看当前状态
# 永久关闭:编辑 /etc/selinux/config
# SELINUX=disabled
3.2 本地用户访问
3.2.1 本地用户登录
普通系统用户默认即允许登录 FTP,登录后进入自己的家目录,密码就是系统登录密码。
useradd zhangsan
echo '123456' | passwd --stdin zhangsan
local_enable=YES
write_enable=YES
local_umask=022
测试:
ftp 10.0.0.61
# Name: zhangsan
# Password: 123456
# 230 Login successful.
ftp> pwd # 257 "/home/zhangsan"
3.2.2 禁止本地用户登录的三种方法
方法一:服务参数(一刀切)
local_enable=NO
这会禁止所有本地用户,无法针对特定用户控制。
方法二:修改用户 shell
/etc/passwd 最后一列指定用户 shell,不同 shell 的效果:
| shell | 登录系统 | 登录 FTP | 收邮件 |
|---|---|---|---|
/bin/bash |
可以 | 可以 | 可以 |
/sbin/nologin |
不可以 | 可以 | 可以 |
/bin/false |
不可以 | 不可以 | 可以 |
usermod -s /sbin/nologin ftpuser1 # 只能用 FTP,不能登录系统(推荐)
usermod -s /bin/false ftpuser2 # 连 FTP 也不能用
这是一个非常实用的技巧:给纯 FTP 账号设置
/sbin/nologin,既能传文件又不能 SSH 登录系统。
方法三:用户名单(黑白名单,见下节)
3.2.3 用户列表黑白名单
通过 user_list 文件和 userlist_deny 参数控制:
userlist_enable=YES
userlist_file=/etc/vsftpd/user_list
userlist_deny |
user_list 文件作用 |
|---|---|
YES(默认) |
黑名单:文件中的用户禁止登录 |
NO |
白名单:只有文件中的用户允许登录 |
vim /etc/vsftpd/user_list
# 一行一个用户名
启用白名单:
userlist_enable=YES
userlist_deny=NO
启用黑名单:
userlist_enable=YES
userlist_deny=YES
参数含义再强调一次:
userlist_enable:用户列表功能是否有效;userlist_deny:列表是"允许"还是"拒绝"。
白名单陷阱:使用白名单时,如果还要允许匿名登录,必须把
ftp和anonymous也写进user_list,否则匿名用户(尤其是 lftp、yum)会登录失败。
3.2.4 允许 root 用户登录 FTP
PAM 默认会禁用 root 和 UID 小于 500(CentOS 7 是 1000)的系统用户,名单在 /etc/vsftpd/ftpusers。要让 root 登录:
-
注释(或删除)
/etc/vsftpd/ftpusers文件中的root那一行:vim /etc/vsftpd/ftpusers # root ← 注释掉 -
处理
user_list:若启用了黑名单,要确保root不在黑名单中(默认user_list里有 root,需注释掉);若启用了白名单,要确保root在白名单中。 -
必要时修改
/etc/pam.d/vsftpd,把pam_listfile那行的sense=deny改为sense=allow(或直接注释该行):auth required pam_listfile.so item=user sense=deny file=/etc/vsftpd/ftpusers onerr=succeed
生产环境不建议开放 root 登录 FTP,密码明文传输一旦被嗅探,整台服务器沦陷。
3.2.5 本地用户的下载和上传
本地用户默认允许下载和上传。上传需要同时满足:
- 系统目录对该用户有写权限;
- 配置中
write_enable=YES。
3.2.6 统一本地用户登录目录
让所有本地用户登录后统一进入 /ftpdata/:
local_root=/ftpdata
此参数不影响用户登录系统(SSH/终端)时进入自己的家目录。
mkdir /ftpdata
chmod 757 /ftpdata # 其他人可读写执行
chmod o+t /ftpdata # 加粘滞位:用户只能删除自己的文件
ls -ld /ftpdata
# drwxr-xrwt 2 root root 4096 ... /ftpdata
粘滞位(sticky bit,
o+t)是实现"多用户共享上传目录,但互相不能删对方文件"的关键,与/tmp同理。
3.2.7 家目录 chroot 禁锢实操
对指定用户启用 chroot:
chroot_list_enable=YES
chroot_list_file=/etc/vsftpd/chroot_list
手动创建名单文件(默认不存在):
vim /etc/vsftpd/chroot_list
# 一行一个用户名,例如:
# zhangsan
# lisi
# CentOS 6
/etc/init.d/vsftpd reload
# CentOS 7
systemctl reload vsftpd
测试发现,被锁定的用户执行 cd .. 后仍停留在登录目录,pwd 永远显示 /,无法跳出。
对所有本地用户启用 chroot:
chroot_local_user=YES
匿名用户默认就是 chroot 环境。
3.2.8 解决 500 OOPS: vsftpd: refusing to run with writable root inside chroot
现象:在 CentOS 7(vsftpd 3.x)或 vsftpd 2.3.5 及以上版本中,开启 chroot_local_user=YES 后,本地用户登录报错:
500 OOPS: vsftpd: refusing to run with writable root inside chroot ()
原因:从 vsftpd 2.3.5 起,出于安全考虑,被 chroot 的根目录本身不允许有写权限(因为 chroot 根可写存在提权风险)。而用户家目录默认属主是自己且有 w 权限,因此触发拒绝。
解决方法一(推荐,vsftpd ≥ 3.0):在 vsftpd.conf 中显式允许可写的 chroot 根:
chroot_local_user=YES
allow_writeable_chroot=YES
systemctl restart vsftpd
解决方法二:去掉家目录的写权限,另建可写子目录用于上传(更安全,与匿名用户的思路一致):
chmod a-w /home/zhangsan # 家目录去掉写权限,作为只读 chroot 根
mkdir /home/zhangsan/upload # 建可写子目录
chown zhangsan:zhangsan /home/zhangsan/upload
chmod 755 /home/zhangsan/upload
解决方法三:不用家目录,改用 local_root 指向一个不可写的父目录:
chroot_local_user=YES
local_root=/ftpdata
chmod 555 /ftpdata # 根不可写
mkdir /ftpdata/upload && chmod 777 /ftpdata/upload
注意:CentOS 6 自带的 vsftpd 2.2.2 没有
allow_writeable_chroot参数,写进配置文件会导致服务启动失败并报unrecognised variable。CentOS 6 环境请使用方法二或方法三。
3.3 虚拟用户完整配置
虚拟用户在系统中不存在同名账号,全部映射到一个系统宿主账号上,安全性最高,是生产环境的首选方案。
3.3.1 第一步:建立虚拟用户文本库
mkdir -p /etc/vsftpd/vuser
vim /etc/vsftpd/vuser/vusers.txt
文件格式为奇数行用户名、偶数行密码,严格交替:
zhangsan
pass123
lisi
pass456
web01
web@2024
3.3.2 第二步:用 db_load 生成 db 数据库文件
# 确保安装了 db_load 工具
# CentOS 6: yum install db4-utils -y
# CentOS 7: yum install libdb-utils -y
cd /etc/vsftpd/vuser
db_load -T -t hash -f vusers.txt vusers.db
参数说明:
| 参数 | 说明 |
|---|---|
-T |
允许应用程序(非 Berkeley DB 工具)使用该数据库 |
-t hash |
指定数据库类型为 hash |
-f vusers.txt |
指定源文本文件 |
保护数据库文件权限(内含明文密码):
chmod 600 /etc/vsftpd/vuser/vusers.db
chmod 600 /etc/vsftpd/vuser/vusers.txt
每次修改
vusers.txt(增删用户或改密码)后,必须重新执行db_load才生效。
3.3.3 第三步:创建 PAM 认证文件
vim /etc/pam.d/vsftpd.vu
CentOS 6(32 位用 pam_userdb.so,64 位路径同名,模块由 PAM 自动查找):
auth required pam_userdb.so db=/etc/vsftpd/vuser/vusers
account required pam_userdb.so db=/etc/vsftpd/vuser/vusers
CentOS 7:
auth required pam_userdb.so db=/etc/vsftpd/vuser/vusers
account required pam_userdb.so db=/etc/vsftpd/vuser/vusers
关键点:
db=后面的路径不要写.db后缀,PAM 会自动补上。CentOS 7 部分版本
pam_userdb.so由pam包提供,若报找不到模块,检查/usr/lib64/security/pam_userdb.so是否存在。
3.3.4 第四步:创建系统宿主账号
所有虚拟用户都映射到这一个系统账号,由它的身份进行文件读写:
useradd -d /var/ftproot -s /sbin/nologin virtual
chmod 755 /var/ftproot # 注意:作为 chroot 根不要给写权限
mkdir -p /var/ftproot/upload
chown virtual:virtual /var/ftproot/upload
chmod 755 /var/ftproot/upload
3.3.5 第五步:修改 vsftpd.conf
# 基础
anonymous_enable=NO
local_enable=YES
write_enable=YES
local_umask=022
# 虚拟用户核心四项
guest_enable=YES # 启用虚拟用户模式
guest_username=virtual # 映射到的系统宿主账号
pam_service_name=vsftpd.vu # 指向 /etc/pam.d/vsftpd.vu
user_config_dir=/etc/vsftpd/vusers_dir # 每个虚拟用户的独立配置目录
# 让虚拟用户拥有本地用户级权限(否则只有匿名级权限,无法上传删除)
virtual_use_local_privs=YES
# 禁锢
chroot_local_user=YES
allow_writeable_chroot=YES # 仅 vsftpd >= 3.0 / CentOS 7
# 目录
local_root=/var/ftproot
# 其他
listen=YES
listen_ipv6=NO
pasv_enable=YES
pasv_min_port=30001
pasv_max_port=30010
xferlog_enable=YES
virtual_use_local_privs=YES时,虚拟用户的写权限由write_enable控制;若设为NO,则虚拟用户按匿名用户规则处理,需要用anon_upload_enable、anon_mkdir_write_enable、anon_other_write_enable来放权。两种方式都可用,但混用容易搞错。
3.3.6 第六步:为不同虚拟用户设置独立权限与目录
mkdir -p /etc/vsftpd/vusers_dir
给 zhangsan 只读权限,目录 /var/ftproot/zhangsan:
mkdir -p /var/ftproot/zhangsan
chown virtual:virtual /var/ftproot/zhangsan
vim /etc/vsftpd/vusers_dir/zhangsan
local_root=/var/ftproot/zhangsan
write_enable=NO
download_enable=YES
local_max_rate=102400
给 lisi 完整读写权限,目录 /var/ftproot/lisi:
mkdir -p /var/ftproot/lisi
chown virtual:virtual /var/ftproot/lisi
chmod 755 /var/ftproot/lisi
vim /etc/vsftpd/vusers_dir/lisi
local_root=/var/ftproot/lisi
write_enable=YES
anon_upload_enable=YES
anon_mkdir_write_enable=YES
anon_other_write_enable=YES
local_max_rate=512000
给 web01 独立站点目录,只允许上传不允许删除:
mkdir -p /var/www/html
chown virtual:virtual /var/www/html
vim /etc/vsftpd/vusers_dir/web01
local_root=/var/www/html
write_enable=YES
anon_upload_enable=YES
anon_mkdir_write_enable=YES
anon_other_write_enable=NO
要点:
user_config_dir中的文件名必须与虚拟用户名完全一致(大小写敏感),文件内可写任何vsftpd.conf支持的参数,会覆盖主配置文件中的同名参数。
3.3.7 第七步:重启与测试验证
# CentOS 6
/etc/init.d/vsftpd restart
# CentOS 7
systemctl restart vsftpd
# SELinux 放行
setsebool -P ftpd_full_access on
测试 zhangsan(应该只能下载,不能上传):
ftp 10.0.0.61
# Name: zhangsan
# Password: pass123
# 230 Login successful.
ftp> pwd # 257 "/",说明已被 chroot
ftp> ls
ftp> get somefile # 成功
ftp> put /etc/hosts # 550 Permission denied.(write_enable=NO 生效)
ftp> bye
测试 lisi(应该可以上传、建目录、删除):
ftp 10.0.0.61
# Name: lisi
# Password: pass456
ftp> put /etc/hosts # 226 Transfer complete.
ftp> mkdir testdir # 257 created
ftp> delete hosts # 250 Delete operation successful.
ftp> bye
验证系统中确实没有这些账号:
id zhangsan
# id: zhangsan: no such user ← 说明是虚拟用户
grep zhangsan /etc/passwd
# (无输出)
验证上传文件的实际属主是宿主账号:
ls -l /var/ftproot/lisi/
# -rw-r--r-- 1 virtual virtual 158 ... hosts ← 属主是 virtual
3.3.8 虚拟用户排错要点
| 现象 | 可能原因 |
|---|---|
530 Login incorrect. |
vusers.db 未生成或路径写错;pam_service_name 与 PAM 文件名不一致;vusers.txt 奇偶行错位;改密码后忘记重新 db_load |
| 登录成功但无法上传 | write_enable=NO;virtual_use_local_privs=NO 且未开 anon_upload_enable;目录属主不是 guest_username |
500 OOPS: refusing to run with writable root inside chroot |
加 allow_writeable_chroot=YES,或去掉 local_root 目录的写权限 |
| 个性化配置不生效 | user_config_dir 目录不存在;文件名与用户名不一致;文件里有语法错误(等号旁有空格) |
553 Could not create file |
SELinux 未放行,执行 setsebool -P ftpd_full_access on |
四、客户端使用与运维
4.1 主动模式验证实验
在服务器上同时作为客户端连接自己:
ftp 10.0.0.61
# Connected to 10.0.0.61.
# 220 (vsFTPd 2.0.5)
Name (10.0.0.61:root): ftp # 输入匿名用户 ftp
331 Please specify the password.
Password: # 密码任意或空
230 Login successful.
ftp> ? # 查看可用命令
ftp> help passive # 查看 passive 命令帮助
ftp> passive # 切换被动模式开关,此处关闭被动模式
Passive mode off.
ftp> ls # 触发数据连接
打开第二个终端观察连接:
netstat -nt | grep TIME_
# tcp 0 0 10.0.0.61:20 10.0.0.61:56227 TIME_WAIT
可以看到服务器使用 20 端口连接客户端大于 1024 的随机端口,这就是主动模式的数据连接特征。
被动模式下再观察一次(passive 切回 on 后执行 ls),会看到数据连接的服务器端端口落在 pasv_min_port–pasv_max_port 范围内。
4.2 被动模式端口范围验证
为便于配置防火墙,指定被动模式数据传输使用的端口范围:
pasv_enable=YES # 默认开启,可不写
pasv_min_port=3000
pasv_max_port=3005
FTP 登录后执行 ls 等触发数据传输的命令,用 netstat -nt 观察,数据连接的服务器端端口一定落在 3000–3005 范围内。
netstat -nt | grep 10.0.0.61
# tcp 0 0 10.0.0.61:3002 10.0.0.185:41235 ESTABLISHED
端口范围要留够:每个并发数据传输占用一个端口,范围大小应 ≥ 预期并发传输数。
4.3 命令行 ftp 客户端全部常用命令
ftp 192.168.1.130 # 连接并登录
ftp -i 192.168.1.130 # 关闭交互确认(等价于登录后执行 prompt)
ftp -n 192.168.1.130 # 不自动登录,需手动 user
| 命令 | 作用 | 示例 |
|---|---|---|
open |
在 ftp 交互环境内连接一台服务器 | open 192.168.1.130 |
user |
输入用户名与密码进行认证 | user zhangsan |
close |
断开与当前服务器的连接但不退出客户端 | close |
pwd |
查看远程当前目录 | pwd |
ls / dir |
列出远程当前目录内容 | ls、ls -l、ls upload |
cd |
进入远程目录,回上层为 cd .. |
cd upload |
lcd |
切换本地目录(不带参数则显示本地当前目录),回上层为 lcd .. |
lcd /tmp |
get |
下载单个文件 | get bash、get bash /tmp/bash2、get bash bash3 |
mget |
批量下载,支持通配符 | mget *.txt |
put |
上传单个文件 | put /etc/passwd、put /etc/passwd ./passwd2 |
mput |
批量上传,支持通配符 | mput *.log |
binary / bin |
切换为二进制传输类型 | binary |
ascii |
切换为 ASCII 文本传输类型 | ascii |
passive |
切换被动模式开关(开↔关),显示 Passive mode on/off |
passive |
prompt |
切换批量操作(mget/mput)的逐个确认提示,关掉后才能无人值守批量传 | prompt |
delete |
删除远程单个文件 | delete hosts |
mdelete |
批量删除远程文件 | mdelete *.tmp |
mkdir |
在远程创建目录 | mkdir testdir |
rmdir |
删除远程空目录 | rmdir testdir |
rename |
重命名远程文件 | rename a.txt b.txt |
size |
查看远程文件大小 | size bigfile |
hash |
开启/关闭传输进度 # 号显示 |
hash |
debug |
开启/关闭调试信息,debug 1 开、debug 0 关 |
debug 1 |
verbose |
切换详细输出模式 | verbose |
status |
显示当前连接与各开关状态 | status |
! |
在本地执行 shell 命令 | !ls -l /tmp/bash* |
? / help |
列出所有可用命令 | ?、help、help debug |
bye / quit / exit |
退出客户端(也可用 Ctrl+D) |
bye |
常用 FTP 子命令小结:get(下载)、put(上传)、ls、cd、mget、mput、delete、rename 等。
4.4 lftp 客户端
lftp 功能远强于 ftp,支持目录镜像、断点续传、后台任务、书签。
# 匿名登录
lftp 10.0.0.61
# 指定用户登录(命令行给出用户名和密码)
lftp -u zhangsan,pass123 10.0.0.61
lftp -u zhangsan,pass123 ftp://10.0.0.61
# 直接执行命令后退出
lftp -u a,password -e "ls; quit" ftp://10.0.0.2
lftp 交互环境内常用命令:
| 命令 | 说明 |
|---|---|
ls / cd / lcd / pwd |
与 ftp 同义 |
get / put |
单文件下载/上传 |
mget / mput |
批量下载/上传 |
mirror 远程目录 本地目录 |
递归镜像下载整个目录树 |
mirror -R 本地目录 远程目录 |
反向镜像,即递归上传 |
pget -n 5 文件 |
多线程分段下载 |
set ftp:passive-mode on/off |
切换被动模式 |
bookmark add name |
添加书签 |
quit / exit |
退出 |
定时镜像备份示例(每天 18:00 把服务器 /ftp 全部内容下载到本地 /backup):
crontab -e
# 0 18 * * * lftp -u a,password -e "mirror /ftp /backup; quit" ftp://10.0.0.2
lftp 支持在命令行中直接指定用户名和密码,详见
man lftp。生产环境建议把密码写入~/.netrc并chmod 600,避免出现在进程列表中。
~/.netrc 示例:
machine 10.0.0.2
login a
password password
4.5 wget 访问 FTP
# 匿名下载
wget ftp://10.1.1.1/pub/50m
# 带用户名密码下载
wget ftp://u9:abc@10.1.1.1/50m
wget --ftp-user=u9 --ftp-password=abc ftp://10.1.1.1/50m
# 递归下载整个目录
wget -r ftp://10.1.1.1/pub/
# 限速下载(用于对比服务端限速效果)
wget --limit-rate=50k ftp://10.1.1.1/pub/50m
# 断点续传
wget -c ftp://10.1.1.1/pub/50m
wget 默认使用被动模式,可用 --no-passive-ftp 强制主动模式。
4.6 curl 访问 FTP
# 列出目录
curl ftp://10.0.0.61/pub/
# 下载(-O 保留原文件名)
curl -O ftp://10.0.0.61/pub/fstab
curl -u zhangsan:pass123 -O ftp://10.0.0.61/data.tar.gz
# 上传
curl -T /etc/hosts -u lisi:pass456 ftp://10.0.0.61/upload/
# 强制主动模式(默认被动)
curl -P - ftp://10.0.0.61/pub/
# FTPS(显式 TLS)
curl --ssl-reqd -u zhangsan:pass123 ftp://10.0.0.61/pub/fstab -O
curl -k --ssl-reqd -u zhangsan:pass123 ftp://10.0.0.61/pub/ # 忽略自签名证书校验
4.7 Windows 下访问
FileZilla(图形客户端,推荐):
- 打开"站点管理器"→"新站点"。
- 协议选择:
- 明文 FTP:
FTP - 文件传输协议,加密选"只使用明文 FTP"。 - FTPS:协议选
FTP,加密选"要求显式的 FTP over TLS"。 - SFTP:协议选
SFTP - SSH File Transfer Protocol(端口 22)。
- 明文 FTP:
- 主机填服务器 IP,端口 21(SFTP 为 22)。
- 登录类型:匿名 / 正常(填用户名密码)。
- 在"传输设置"中选择"被动"(推荐)或"主动"。
Windows 资源管理器 / 浏览器:
ftp://192.168.1.130/ 浏览器地址栏,仅支持匿名下载(新版 Chrome/Edge 已移除 FTP 支持)
ftp://zhangsan@192.168.1.130/ 会弹出密码框
在"此电脑"地址栏输入 ftp://192.168.1.130/ 可以像本地文件夹一样浏览、拖拽上传下载(需在 IE 兼容设置中启用"被动 FTP")。
Windows 自带命令行 ftp:
C:\> ftp 192.168.1.130
ftp> binary
ftp> get file.zip
ftp> quit
注意:Windows 自带的
ftp.exe只支持主动模式,在 NAT 环境下常常无法列目录,建议改用 FileZilla 或 PowerShell。
4.8 用 FTP 搭建 yum 源
服务端:把光盘内容放到 FTP 目录下。
mount /dev/cdrom /mnt
mkdir -p /var/ftp/yum
cp -a /mnt/* /var/ftp/yum/
# 或直接把光盘挂到 FTP 目录
# mount /dev/cdrom /var/ftp/yum
createrepo /var/ftp/yum # 若无 repodata 需自己生成
客户端:修改 yum 仓库配置,只需把 baseurl 一行改为 FTP 格式。
vim /etc/yum.repos.d/ftp.repo
[ftp-base]
name=FTP Base Repo
baseurl=ftp://192.168.0.183/yum
enabled=1
gpgcheck=0
yum clean all
yum repolist
yum install gimp -y
重要陷阱:
yum默认使用被动模式。若服务端配置了pasv_enable=NO(只允许主动模式),yum 将无法使用 FTP 源。同理,若启用了白名单(userlist_deny=NO),必须把ftp和anonymous都加入user_list,否则 yum 与 lftp 匿名登录都会失败。
4.9 目录消息与欢迎横幅
4.9.1 目录消息
dirmessage_enable=YES
message_file=.message
在各目录下创建 .message 文件:
vim /home/a/.message
vim /var/ftp/pub/.message
====================================
欢迎进入公共下载区,请遵守使用规范
====================================
任何能登录或 cd 到该目录的用户都会看到该消息。
4.9.2 全局登录横幅
方法一:直接配置字符串
ftpd_banner=Welcome to my ftp server !!
方法二:从文件读取(支持多行)
banner_file=/etc/vsftpd/banner
vim /etc/vsftpd/banner
可写入多行字符画等内容,连接时作为 220 响应显示。
类似的消息提示机制在多数服务中都有:系统登录
/etc/motd,sshd 登录前Banner /etc/ssh_banner。安全建议:不要在 banner 中暴露 vsftpd 版本号等信息。
4.10 传输日志分析
4.10.1 xferlog 格式
xferlog_enable=YES
xferlog_std_format=YES
xferlog_file=/var/log/xferlog
# CentOS 6
/etc/init.d/vsftpd reload
# CentOS 7
systemctl reload vsftpd
日志示例:
Tue May 3 06:24:57 2011 1 10.0.0.185 660 /etc/fstab b _ o r a ftp 0 * c
Tue May 3 06:36:24 2011 1 10.0.0.132 660 /etc/fstab b _ i r a ftp 0 * i
字段解析(以第一条为例,共 14 个字段):
| 位置 | 示例值 | 含义 |
|---|---|---|
| 1 | Tue May 3 06:24:57 2011 |
访问时间 |
| 2 | 1 |
传输耗时(秒) |
| 3 | 10.0.0.185 |
远程主机名/IP |
| 4 | 660 |
传输字节数 |
| 5 | /etc/fstab |
操作文件名 |
| 6 | b |
传输类型:a=ASCII,b=binary |
| 7 | _ |
特殊处理标志:_=无,C=压缩,U=解压,T=tar |
| 8 | o |
方向:i=上传(incoming),o=下载(outgoing),d=删除 |
| 9 | r |
访问模式:a=匿名,g=guest(虚拟用户),r=real(本地真实用户) |
| 10 | a |
用户名 |
| 11 | ftp |
服务名 |
| 12 | 0 |
认证方式:0=none,1=RFC931 |
| 13 | * |
认证用户 ID(不可用时为 *) |
| 14 | c |
完成状态:c=完整(complete),i=不完整(incomplete) |
对照第二条日志:方向为 i(上传)、状态为 i(不完整),说明这是一次中断的上传。
日志分析常用命令:
# 统计下载量最大的文件
awk '$8=="o"{print $9}' /var/log/xferlog | sort | uniq -c | sort -rn | head
# 统计各客户端 IP 的传输次数
awk '{print $7}' /var/log/xferlog | sort | uniq -c | sort -rn
# 找出所有传输不完整的记录
awk '$NF=="i"' /var/log/xferlog
# 统计总传输字节数
awk '{s+=$8} END{print s/1024/1024 " MB"}' /var/log/xferlog
4.10.2 vsftpd 自有日志
dual_log_enable=YES # xferlog 和 vsftpd.log 都产生
xferlog_std_format=NO # 设为 NO 时只产生 vsftpd.log
vsftpd_log_file=/var/log/vsftpd.log
log_ftp_protocol=YES # 额外记录所有协议交互,排错用
# CentOS 6
/etc/init.d/vsftpd reload
# CentOS 7
systemctl reload vsftpd
ls -l /var/log/vsftpd.log
tail -f /var/log/vsftpd.log
vsftpd.log 会清晰记录登录成功/失败、上传下载成功/失败等信息,示例:
Mon Aug 18 10:22:31 2025 [pid 2] CONNECT: Client "10.0.0.185"
Mon Aug 18 10:22:35 2025 [pid 1] [zhangsan] OK LOGIN: Client "10.0.0.185"
Mon Aug 18 10:22:40 2025 [pid 3] [zhangsan] OK UPLOAD: Client "10.0.0.185", "/upload/hosts", 158 bytes, 12.34Kbyte/sec
Mon Aug 18 10:23:02 2025 [pid 3] [lisi] FAIL LOGIN: Client "10.0.0.185"
排查登录失败:
grep 'FAIL LOGIN' /var/log/vsftpd.log
grep 'OK LOGIN' /var/log/vsftpd.log | awk '{print $NF}' | sort | uniq -c
4.10.3 日志轮转
轮转配置在 /etc/logrotate.d/vsftpd:
vim /etc/logrotate.d/vsftpd
/var/log/vsftpd.log {
nocompress
missingok
monthly
rotate 2
}
各指令含义:nocompress 不压缩、missingok 文件不存在也不报错、monthly 每月轮转、rotate 2 最多保留 2 个历史副本。
logrotate -d /etc/logrotate.d/vsftpd # 调试模式,只演示不执行
logrotate -f /etc/logrotate.d/vsftpd # 强制立即轮转
版本差异:RHEL 5 下这两种日志使用的是格林威治时间(比北京时间晚 8 小时);RHEL 6 及之后已修正为本地时间。
4.11 限速与并发限制的验证方法
4.11.1 下载限速配置
anon_max_rate=100000 # 匿名用户最大传输率(字节/秒),约 100KB/s
local_max_rate=100000 # 本地用户最大传输率(字节/秒),约 100KB/s
# 另一组常用取值
anon_max_rate=102400 # 精确 100KB/s
local_max_rate=204800 # 精确 200KB/s
# CentOS 6
/etc/init.d/vsftpd reload
# CentOS 7
systemctl reload vsftpd
vsftpd 的限速是近似值,实际速率会在设定值的 80%~120% 之间浮动,属正常现象。限速是针对单个连接的,不是全局带宽。
4.11.2 限速验证
方法一:用 wget 直接看速度
# 准备一个大文件
dd if=/dev/zero of=/var/ftp/pub/50m bs=1M count=50
# 匿名下载测速
wget ftp://10.1.1.1/pub/50m
# 本地用户下载测速
wget ftp://u9:abc@10.1.1.1/50m
wget 输出的末尾会给出平均速率,与配置值对比即可。
方法二:用 watch 观察本地文件增长
使用本地用户登录并下载一个较大文件,同时在另一终端观察:
watch ls -lh # 默认每 2 秒刷新一次
watch -n 1 ls -lh /tmp # 每 1 秒刷新
注意:
watch不支持ll等 shell 别名,必须写完整命令ls -lh。
方法三:用 ftp 客户端的 hash 观察
ftp> hash
ftp> get 50m
4.11.3 连接数限制
max_clients=300 # 服务器允许的总并发连接数
max_per_ip=3 # 单个客户端 IP 的最大连接数
超过单 IP 连接数限制时会报错:
421 There are too many connections from your internet address.
超过总连接数限制时会报错:
421 There are too many connected users, please try later.
这两个参数只在 standalone 模式(
listen=YES)下有效,由 xinetd 托管时无效,需改用 xinetd 的instances与per_source参数。
4.11.4 并发限制验证
# 同时开 4 个 ftp 会话(max_per_ip=3),第 4 个应被拒绝
for i in 1 2 3 4; do
(echo -e "user zhangsan pass123\nls\nsleep 30\nbye" | ftp -n 10.0.0.61 &)
done
# 服务器端观察当前连接数
netstat -nt | grep ':21 ' | grep ESTABLISHED | wc -l
ss -nt state established '( dport = :21 or sport = :21 )'
# 观察 vsftpd 进程数
ps -ef | grep vsftpd | grep -v grep | wc -l
4.12 查看服务器上与客户端交互的 FTP 进程
排查权限或进程身份问题时,可按以下步骤定位服务器上对应的 FTP 进程:
# 1. 在客户端找出自己的 ftp 客户端进程号
ps -C ftp
# 示例输出进程号:10292
# 2. 根据进程号找出客户端使用的端口号
netstat -tpn | grep 10292
# 示例找到:3.3.3.7:34330
# 3. 在服务器上根据远程端口号和 IP 地址找出对应的服务端进程
netstat -tpn | grep 3.3.3.7:34330
# 示例找到进程号:8161
# 4. 查看该进程的有效用户 ID(euid),由此确认进程以谁的身份运行
ps -o pid,euid -p 8161
# 示例输出:nobody
这一手法可以验证:匿名会话的 euid 是 ftp/nobody;虚拟用户会话的 euid 是 guest_username 指定的账号;本地用户会话的 euid 是该用户本身。
4.13 命令速查表
| 操作 | 命令 |
|---|---|
| 安装 vsftpd | sudo yum install -y vsftpd |
| 安装客户端 | yum install ftp lftp -y |
| 启动服务(CentOS 6) | /etc/init.d/vsftpd start |
| 启动服务(CentOS 7) | systemctl start vsftpd |
| 重载配置(CentOS 6) | /etc/init.d/vsftpd reload |
| 重载配置(CentOS 7) | systemctl reload vsftpd |
| 开机自启(CentOS 6) | chkconfig vsftpd on |
| 开机自启(CentOS 7) | systemctl enable vsftpd |
| 查看有效配置 | grep -v ^# /etc/vsftpd/vsftpd.conf \| grep -v ^$ |
| 查看监听端口 | netstat -ntl \| grep :21 / lsof -i:21 / ss -ntl |
| 查看 vsftpd 是否支持 TCP Wrapper | ldd /usr/sbin/vsftpd \| grep wrap |
| 查看 vsftpd 配置 man 文档 | man 5 vsftpd.conf |
| 生成虚拟用户库 | db_load -T -t hash -f vusers.txt vusers.db |
| 测试匿名下载 | wget ftp://IP/pub/file |
| 测试用户认证下载 | wget ftp://user:pass@IP/file |
| 查找 FTP 进程 | ps -C ftp |
| 查看进程网络连接 | netstat -tpn \| grep PID |
| 查看进程有效用户 | ps -o pid,euid -p PID |
| 查看 SELinux FTP 布尔值 | getsebool -a \| grep ftp |
五、访问控制与防火墙
5.1 TCP Wrapper 访问控制
TCP Wrappers 是一个第三方访问控制工具,可对支持它的 TCP 服务进行 IP 级过滤。
5.1.1 判断服务是否支持
ldd /usr/sbin/sshd | grep wrap
# libwrap.so.0 => /lib/libwrap.so.0 (0x00d39000)
ldd /usr/sbin/vsftpd | grep wrap
# libwrap.so.0 => /lib64/libwrap.so.0 (0x00007fe2acf7c000)
链接了 libwrap.so 即表示支持(有输出即支持)。并非所有服务都支持,很多软件编译时需要加 --enable-libwrap。vsftpd 默认支持,并需在配置中开启:
tcp_wrappers=YES
版本差异:CentOS 7.x 后期版本及 CentOS 8 起,
tcp_wrappers已被弃用/移除(systemd 不再链接 libwrap),请改用 firewalld 或 iptables 做 IP 级控制。CentOS 6 与 CentOS 7 早期版本仍可正常使用。
5.1.2 配置文件与匹配顺序
| 文件 | 作用 |
|---|---|
/etc/hosts.allow |
允许规则 |
/etc/hosts.deny |
拒绝规则 |
系统按以下顺序查找两个文件,找到第一个匹配的规则后立即停止:
- 若在
/etc/hosts.allow中匹配,则允许。 - 若在
/etc/hosts.deny中匹配,则拒绝。 - 若两个文件均无匹配,则允许(默认放行)。
若文件不存在,视为空文件,即默认允许访问。
hosts.allow优先级高于hosts.deny,这是最常考也最常写错的一点。
5.1.3 配置语法
服务进程名: 客户端地址列表 [: 动作]
vsftpd: 10.1.1.15,10.1.1.96,10.1.1.0/255.255.255.0
- 左侧
vsftpd:服务的进程名(不是端口名,也不是服务单元名)。 - 右侧:客户端地址,可指定单个 IP、多个 IP(逗号或空格分隔)或网段。
参考 man 文档:
man 5 hosts_options
man 5 hosts_access
5.1.4 规则示例(以 sshd 为例,vsftpd 同理只需换服务名)
# /etc/hosts.deny
# 拒绝所有
sshd: all
# 只允许 10.0.0.104
sshd: all EXCEPT 10.0.0.104
# 允许多个 IP
sshd: all EXCEPT 10.0.0.104 10.0.0.105
# 拒绝 10.0.0.0/24 网段,除了 10.0.0.104
sshd: 10.0.0.0/255.255.255.0 EXCEPT 10.0.0.104
# 拒绝并记录日志
sshd: all : spawn echo "`date`" %c %d >> /var/log/tcpwrapperdeny.log
# 拒绝并向所有在线终端广播
sshd: all : spawn wall %c "login denied"
sshd: all EXCEPT 10.0.0.61 : spawn wall %c "login denied"
注意:子网掩码不支持
/24简写,必须写完整掩码255.255.255.0。%c表示客户端信息(IP/主机名/用户),%d表示服务名(daemon),%h表示客户端主机名,%a表示客户端地址。
5.1.5 vsftpd 使用 TCP Wrapper 的完整示例
只需将规则中的服务名改为 vsftpd:
# /etc/hosts.deny —— 拒绝所有 FTP 访问
vsftpd: all
示例一:只允许某网段,排除某 IP,并记录被拒绝的访问
/etc/hosts.allow:
vsftpd: 10.0.0.0/255.255.255.0 EXCEPT 10.0.0.10 : spawn echo "`date`" %c >> /var/log/ftp_denyip.log
示例二:allow 白名单 + deny 兜底并记录日志(更清晰的写法)
/etc/hosts.allow:
vsftpd: 10.1.1.0/255.255.255.0 EXCEPT 10.1.1.3
/etc/hosts.deny:
vsftpd: * :spawn echo "`date`" %c >> /var/log/ftp_deny.log
这样:10.1.1.0/24 网段(除 10.1.1.3)在 allow 中匹配被放行;其余所有来源在 deny 中匹配被拒绝并写入日志。
5.2 firewalld 放行(CentOS 7)
5.2.1 放行 FTP 服务(自动处理主动模式与 conntrack)
# 查看当前区域与已放行服务
firewall-cmd --get-default-zone
firewall-cmd --list-all
# 放行 ftp 服务(等价于放行 21 端口并加载 nf_conntrack_ftp 连接跟踪)
firewall-cmd --permanent --add-service=ftp
firewall-cmd --reload
firewall-cmd --list-services
5.2.2 放行被动模式端口范围(完整规则)
服务端配置:
pasv_enable=YES
pasv_min_port=30001
pasv_max_port=30010
防火墙规则:
# 放行控制端口 21
firewall-cmd --permanent --add-port=21/tcp
# 放行主动模式数据端口 20(如启用 connect_from_port_20)
firewall-cmd --permanent --add-port=20/tcp
# 放行被动模式端口范围
firewall-cmd --permanent --add-port=30001-30010/tcp
# 生效并验证
firewall-cmd --reload
firewall-cmd --list-ports
# 20/tcp 21/tcp 30001-30010/tcp
5.2.3 只允许特定来源访问 FTP(富规则)
# 只允许 10.0.0.0/24 访问 ftp 服务
firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="10.0.0.0/24" service name="ftp" accept'
# 拒绝某个 IP 并记录日志
firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="10.0.0.10" service name="ftp" log prefix="FTP-DENY" level="warning" drop'
firewall-cmd --reload
firewall-cmd --list-rich-rules
5.2.4 加载 FTP 连接跟踪模块
主动模式(以及部分被动模式场景)需要内核连接跟踪帮助模块,才能自动放行动态数据连接:
# 查看是否已加载
lsmod | grep nf_conntrack_ftp
# 手动加载
modprobe nf_conntrack_ftp
# 开机自动加载(CentOS 7)
echo 'nf_conntrack_ftp' > /etc/modules-load.d/ftp.conf
# CentOS 7 还需允许 helper 自动分配(部分版本默认关闭)
echo 'options nf_conntrack_helper=1' > /etc/modprobe.d/conntrack.conf
# 或临时开启
echo 1 > /proc/sys/net/netfilter/nf_conntrack_helper
5.3 iptables 放行(CentOS 6 / 手工 iptables)
5.3.1 通用规则形式
# 允许指定流量
iptables -A INPUT [OPTIONS]... -j ACCEPT
# 拒绝指定流量
iptables -A INPUT [OPTIONS]... -j REJECT
# 设置 INPUT 链默认策略为丢弃
iptables -P INPUT DROP
5.3.2 FTP 完整放行规则(被动模式 + 主动模式)
# 0. 加载 FTP 连接跟踪模块
modprobe ip_conntrack_ftp # CentOS 6
modprobe nf_conntrack_ftp # CentOS 7 / 较新内核
# 1. 放行已建立和相关的连接(连接跟踪,最关键的一条)
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
# 2. 放行控制端口 21
iptables -A INPUT -p tcp --dport 21 -m state --state NEW -j ACCEPT
# 3. 放行主动模式数据端口 20
iptables -A INPUT -p tcp --dport 20 -m state --state NEW -j ACCEPT
# 4. 放行被动模式端口范围(与 pasv_min_port/pasv_max_port 一致)
iptables -A INPUT -p tcp --dport 30001:30010 -m state --state NEW -j ACCEPT
# 5. 只允许特定网段访问 FTP
iptables -A INPUT -p tcp --dport 21 -s 10.0.0.0/24 -j ACCEPT
iptables -A INPUT -p tcp --dport 21 -s 10.0.0.10 -j REJECT
# 6. 默认策略
iptables -P INPUT DROP
# 7. 查看与保存
iptables -L -n --line-numbers
service iptables save # CentOS 6
# CentOS 7 需装 iptables-services:
# systemctl restart iptables && service iptables save
输出方向(若 OUTPUT 链默认 DROP,主动模式必需):
iptables -A OUTPUT -p tcp --sport 20 -m state --state NEW,ESTABLISHED -j ACCEPT
iptables -A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
注意:FTP 被动模式需要放行命令端口 21 以及
pasv_min_port至pasv_max_port范围内的数据端口。只放行 21 端口而忘记数据端口,是 FTP 部署最常见的故障——表现为能登录但ls卡住直到超时。
5.4 由 xinetd 托管(CentOS 6 及之前)
vsftpd 默认以 standalone 方式运行(listen=YES)。也可交由 xinetd 超级守护进程托管,好处是可以利用 xinetd 的访问时间控制、并发控制等特性。
版本差异:xinetd 是 CentOS 6 及之前的机制。CentOS 7 使用 systemd socket 激活(
vsftpd.socket)代替,且 CentOS 7 默认不安装 xinetd。
5.4.1 配置 xinetd 服务
yum install xinetd -y
vim /etc/xinetd.d/vsftpd
service ftp
{
socket_type = stream
protocol = tcp
wait = no
user = root
server = /usr/sbin/vsftpd
disable = no
}
5.4.2 切换运行方式
/etc/init.d/vsftpd stop
/etc/init.d/xinetd restart
验证 21 端口由 xinetd 监听:
netstat -ntlup | grep :21
# tcp 0 0 0.0.0.0:21 0.0.0.0:* LISTEN 3486/xinetd
此时直接 FTP 登录会失败:
421 Service not available, remote server has closed connection
需要将 vsftpd 配置中的 listen 改为 NO(因为 vsftpd 不能自己也去监听 21):
listen=NO
/etc/init.d/xinetd restart
再次 FTP 登录即可正常。
5.4.3 xinetd 常用扩展参数
service ftp
{
disable = no
socket_type = stream
protocol = tcp
wait = no
user = root
server = /usr/sbin/vsftpd
access_times = 9:30-20:30 # 只在每天 9:30-20:30 允许访问
only_from = 10.0.0.0/24 # 只允许该网段
no_access = 10.0.0.10 # 排除该 IP
instances = 300 # 最大并发实例数(对应 max_clients)
per_source = 3 # 单 IP 最大并发(对应 max_per_ip)
log_on_success = HOST PID DURATION
log_on_failure = HOST
}
access_times是实现"只能在某时段访问 FTP"这一需求的唯一简洁办法,vsftpd 自身没有时间控制参数。
5.5 FTPS 加密配置
FTPS 使用 SSL/TLS 加密 FTP 的控制连接与数据连接,解决明文传输密码的问题。
5.5.1 生成自签名证书
方法一:openssl req 一步生成(推荐)
mkdir -p /etc/vsftpd/ssl
cd /etc/vsftpd/ssl
openssl req -x509 -nodes -days 3650 -newkey rsa:2048 \
-keyout /etc/vsftpd/ssl/vsftpd.key \
-out /etc/vsftpd/ssl/vsftpd.crt \
-subj "/C=CN/ST=Beijing/L=Beijing/O=MyCompany/OU=IT/CN=ftp.example.com"
chmod 600 /etc/vsftpd/ssl/vsftpd.key
chmod 644 /etc/vsftpd/ssl/vsftpd.crt
参数说明:
| 参数 | 说明 |
|---|---|
-x509 |
生成自签名证书而不是证书请求 |
-nodes |
私钥不加密(否则每次启动服务要输密码) |
-days 3650 |
有效期 10 年 |
-newkey rsa:2048 |
同时生成 2048 位 RSA 私钥 |
-keyout / -out |
私钥与证书的输出路径 |
-subj |
非交互式指定证书主题信息 |
方法二:证书与私钥合并到一个 pem 文件
cat /etc/vsftpd/ssl/vsftpd.key /etc/vsftpd/ssl/vsftpd.crt > /etc/vsftpd/ssl/vsftpd.pem
chmod 600 /etc/vsftpd/ssl/vsftpd.pem
查看证书信息:
openssl x509 -in /etc/vsftpd/ssl/vsftpd.crt -noout -text
openssl x509 -in /etc/vsftpd/ssl/vsftpd.crt -noout -dates
5.5.2 ssl_enable 系列参数完整配置
# ===== SSL/TLS 总开关 =====
ssl_enable=YES # 启用 SSL/TLS 支持
# ===== 证书与密钥 =====
rsa_cert_file=/etc/vsftpd/ssl/vsftpd.crt # 证书文件
rsa_private_key_file=/etc/vsftpd/ssl/vsftpd.key # 私钥文件
# 若使用合并的 pem,只写一行即可:
# rsa_cert_file=/etc/vsftpd/ssl/vsftpd.pem
# ===== 强制加密(生产建议开启)=====
force_local_logins_ssl=YES # 强制本地用户登录使用 SSL 加密密码
force_local_data_ssl=YES # 强制本地用户数据传输使用 SSL
force_anon_logins_ssl=NO # 匿名登录是否强制 SSL(匿名下载一般不强制)
force_anon_data_ssl=NO # 匿名数据传输是否强制 SSL
# ===== 协议版本(禁用不安全的旧协议)=====
ssl_tlsv1=YES # 启用 TLSv1
ssl_sslv2=NO # 禁用 SSLv2(不安全)
ssl_sslv3=NO # 禁用 SSLv3(POODLE 漏洞)
# ===== 加密套件与其他 =====
ssl_ciphers=HIGH # 只使用高强度加密套件
require_ssl_reuse=NO # 不要求 SSL 会话复用(很多客户端不支持,开启会导致连接失败)
allow_anon_ssl=NO # 是否允许匿名用户使用 SSL
debug_ssl=YES # SSL 调试日志(排错时临时打开)
# ===== 隐式 FTPS(可选,端口 990)=====
# implicit_ssl=YES
# listen_port=990
重启服务:
# CentOS 6
/etc/init.d/vsftpd restart
# CentOS 7
systemctl restart vsftpd
防火墙放行(FTPS 仍需放行数据端口范围):
firewall-cmd --permanent --add-port=21/tcp
firewall-cmd --permanent --add-port=30001-30010/tcp
firewall-cmd --permanent --add-port=990/tcp # 仅隐式 FTPS 需要
firewall-cmd --reload
5.5.3 FTPS 客户端测试
# lftp(显式 TLS)
lftp -u zhangsan,pass123 10.0.0.61
lftp> set ssl:verify-certificate no # 自签名证书需关闭校验
lftp> set ftp:ssl-force true
lftp> set ftp:ssl-protect-data true
lftp> ls
# 一行命令形式
lftp -e "set ssl:verify-certificate no; set ftp:ssl-force true; ls; quit" -u zhangsan,pass123 10.0.0.61
# curl
curl -k --ssl-reqd -u zhangsan:pass123 ftp://10.0.0.61/
# openssl 直接握手测试
openssl s_client -connect 10.0.0.61:21 -starttls ftp
FileZilla:新建站点 → 协议 FTP → 加密选"要求显式的 FTP over TLS" → 首次连接会提示证书不受信任,勾选"始终信任该证书"即可。
注意:开启
ssl_enable=YES且force_local_logins_ssl=YES后,普通ftp命令行客户端无法登录(它不支持 TLS),会报530 Non-anonymous sessions must use encryption.。必须改用 lftp、curl 或 FileZilla。若要兼容老客户端,把force_local_logins_ssl设为NO(但这就失去了强制加密的意义)。
六、排错清单
6.1 排错通用流程
# 1. 服务是否在跑
systemctl status vsftpd # CentOS 7
/etc/init.d/vsftpd status # CentOS 6
# 2. 端口是否监听
ss -ntlp | grep :21
# 3. 配置文件语法是否有错(启动失败时看这里)
vsftpd /etc/vsftpd/vsftpd.conf # 前台运行,语法错误会直接打印
journalctl -xe -u vsftpd # CentOS 7 查看启动失败原因
# 4. 本机是否能连通(排除网络/防火墙因素)
ftp 127.0.0.1
# 5. 打开协议级日志
# vsftpd.conf: xferlog_std_format=NO + log_ftp_protocol=YES
tail -f /var/log/vsftpd.log
# 6. 检查防火墙与 SELinux
firewall-cmd --list-all
getenforce
tail -f /var/log/audit/audit.log | grep -i denied
6.2 连接超时 / 能登录但 ls 卡住(被动模式端口未放行)
现象:
ftp> ls
227 Entering Passive Mode (10,0,0,61,117,52).
(长时间无响应,最终提示 425 Failed to establish connection 或超时)
原因与解决:
| 原因 | 排查与解决 |
|---|---|
| 被动端口范围未在防火墙放行 | 配置 pasv_min_port/pasv_max_port,并 firewall-cmd --add-port=30001-30010/tcp --permanent && firewall-cmd --reload |
| 未加载 FTP 连接跟踪模块 | modprobe nf_conntrack_ftp,并设置 nf_conntrack_helper=1 |
| 服务器在 NAT 后,227 回的是内网地址 | 配置 pasv_address=公网IP 与 pasv_addr_resolve=NO |
| 客户端在 NAT 后却用主动模式 | 客户端执行 passive 切换为被动模式,或 wget 去掉 --no-passive-ftp |
| 云主机安全组未放行 | 除系统防火墙外,还需在云平台安全组放行 21 与被动端口范围 |
| Windows 自带 ftp.exe 只支持主动模式 | 改用 FileZilla / lftp |
快速验证被动端口是否通:
# 客户端
telnet 10.0.0.61 21
nc -zv 10.0.0.61 30001-30010
6.3 530 登录失败
530 Login incorrect.
530 Permission denied.
530 Non-anonymous sessions must use encryption.
排查顺序:
| 检查项 | 命令/做法 |
|---|---|
| 密码是否正确 | echo 'newpass' \| passwd --stdin username 重设 |
用户是否在 /etc/vsftpd/ftpusers 中 |
grep username /etc/vsftpd/ftpusers,在则注释掉 |
| 黑名单命中 | userlist_deny=YES 时,检查 user_list 里是否有该用户 |
| 白名单未包含 | userlist_deny=NO 时,必须把该用户(含 ftp、anonymous)加进 user_list |
用户 shell 是 /bin/false |
usermod -s /sbin/nologin username |
local_enable=NO |
改为 YES |
| 匿名登录被禁 | anonymous_enable=YES |
| PAM 配置错误 | 检查 pam_service_name 与 /etc/pam.d/ 下文件名是否一致 |
| 虚拟用户库问题 | 重新 db_load;检查 db= 路径不带 .db 后缀 |
| 账号被锁定或密码过期 | passwd -S username、chage -l username |
| SELinux 阻止读家目录 | setsebool -P ftp_home_dir on |
| 强制 SSL 而客户端不支持 | 报 must use encryption,改用 lftp/FileZilla 或关 force_local_logins_ssl |
查看日志确认:
grep 'FAIL LOGIN' /var/log/vsftpd.log
grep vsftpd /var/log/secure
6.4 553 无法上传(权限与 SELinux)
553 Could not create file.
550 Permission denied.
排查清单(按顺序逐项检查):
# 1. vsftpd 写开关是否打开
grep -E 'write_enable|anon_upload_enable|anon_mkdir_write_enable' /etc/vsftpd/vsftpd.conf
# 必须有 write_enable=YES;匿名上传还需 anon_upload_enable=YES
# 2. 目标目录的 Linux 权限与属主
ls -ld /var/ftp/pub
# 匿名上传:目录需对 ftp 用户可写(属主为 ftp,或 777)
chown ftp:ftp /var/ftp/upload
chmod 755 /var/ftp/upload
# 3. 虚拟用户:目录属主必须是 guest_username 指定的账号
ls -ld /var/ftproot/lisi
chown virtual:virtual /var/ftproot/lisi
# 4. SELinux 布尔值
getsebool -a | grep ftp
setsebool -P ftpd_full_access on # CentOS 7
setsebool -P allow_ftpd_full_access on # CentOS 6
setsebool -P ftpd_anon_write on # 匿名写
# 5. SELinux 文件上下文
ls -Zd /var/ftp/upload
semanage fcontext -a -t public_content_rw_t "/var/ftp/upload(/.*)?"
restorecon -Rv /var/ftp/upload
# 6. 磁盘空间与配额
df -h
quota -u username
# 7. 文件系统是否只读挂载
mount | grep ' / '
如果开了 SELinux 却查不出原因,直接看审计日志并让 audit2why 解释:
tail -20 /var/log/audit/audit.log | audit2why
ausearch -m avc -ts recent
6.5 chroot 报错
500 OOPS: vsftpd: refusing to run with writable root inside chroot ()
| 环境 | 解决方案 |
|---|---|
| CentOS 7 / vsftpd ≥ 3.0 | 加 allow_writeable_chroot=YES |
| CentOS 6 / vsftpd 2.2.x | 不支持该参数(写了会导致启动失败)。改为去掉 chroot 根目录的写权限:chmod a-w /home/user,另建可写子目录 |
| 任意版本 | 用 local_root 指向一个 555 权限的目录,在其下建 777 子目录 |
500 OOPS: vsftpd: refusing to run with writable anonymous root
匿名根目录(/var/ftp 或 anon_root)不能有写权限:
chmod 755 /var/ftp
mkdir /var/ftp/upload && chown ftp:ftp /var/ftp/upload
500 OOPS: unrecognised variable in config file: allow_writeable_chroot
当前 vsftpd 版本不支持该参数(CentOS 6),删掉这一行。
500 OOPS: could not bind listening IPv4 socket
listen 与 listen_ipv6 同时为 YES,或 21 端口已被其他进程占用:
ss -ntlp | grep :21
# 二者只能开一个
6.6 中文文件名乱码与 UTF-8
现象:ls 显示中文文件名为 ???? 或乱码,或下载后文件名变乱码。
原因:服务端文件系统编码与客户端会话编码不一致(Linux 通常 UTF-8,老 Windows 客户端为 GBK)。
解决方法:
-
服务端 vsftpd 配置中启用 UTF-8 文件系统声明:
utf8_filesystem=YES -
确认服务端系统 locale 为 UTF-8:
# 查看 locale # CentOS 7 设置 localectl set-locale LANG=zh_CN.UTF-8 # CentOS 6:编辑 /etc/sysconfig/i18n # LANG="zh_CN.UTF-8" -
确保 vsftpd 服务进程本身运行在 UTF-8 环境(CentOS 7 systemd 不继承登录 locale):
systemctl edit vsftpd[Service] Environment=LANG=zh_CN.UTF-8 Environment=LC_ALL=zh_CN.UTF-8systemctl daemon-reload systemctl restart vsftpd -
客户端侧设置(最常见的解决点):
-
FileZilla:站点管理器 → "字符集"标签 → 选"强制 UTF-8";若服务端是 GBK 则选"使用自定义字符集" → 填
GBK。 -
lftp:
lftp -u user,pass 10.0.0.61 lftp> set ftp:charset GBK # 服务端为 GBK 时 lftp> set file:charset UTF-8 # 本地字符集 lftp> ls -
Windows 命令行 ftp:先执行
chcp 65001切换控制台为 UTF-8。
-
-
终极建议:生产环境统一使用英文/数字/下划线命名文件,彻底规避编码问题。
6.7 其他常见错误码速查
| 错误码 | 含义 | 常见原因与处理 |
|---|---|---|
220 |
服务就绪 | 正常,显示 banner |
230 |
登录成功 | 正常 |
331 |
需要密码 | 正常 |
421 Service not available |
服务不可用 | listen=YES 却由 xinetd 托管;服务未启动 |
421 There are too many connections from your internet address. |
超过 max_per_ip |
调大 max_per_ip 或关闭多余会话 |
421 There are too many connected users, please try later. |
超过 max_clients |
调大 max_clients |
425 Failed to establish connection |
数据连接建立失败 | 被动端口未放行、conntrack 模块未加载 |
426 Failure writing network stream |
传输中断 | 网络抖动、data_connection_timeout 过短 |
500 OOPS: ... |
vsftpd 内部拒绝启动/运行 | 见 6.5 节 chroot 相关 |
530 Login incorrect |
认证失败 | 见 6.3 节 |
550 Permission denied |
权限不足 | 见 6.4 节 |
550 Failed to change directory |
目录不存在或无 x 权限 | 检查 local_root/anon_root 路径是否存在 |
553 Could not create file |
无法创建文件 | 见 6.4 节,重点查 SELinux 与目录属主 |
netout: Broken pipe |
连接被中途切断 | TCP Wrapper/防火墙拒绝、空闲超时 |
6.8 注意事项汇总
write_enable是写操作的总开关,必须开启才能上传、创建目录等;匿名的anon_upload_enable等参数只有在它开启后才起作用。chroot_local_user=YES可将本地用户禁锢在其主目录,提升安全性;vsftpd ≥ 3.0 需配合allow_writeable_chroot=YES。- 匿名用户上传通常需要目录具备
ftp用户或指定用户的写入权限;匿名根目录本身绝不能可写。 - 被动模式在防火墙 / NAT 环境中更常用,需要固定并放行数据端口范围。
- TCP Wrapper 的
hosts.allow优先级高于hosts.deny。 userlist_deny的值决定了user_list是黑名单(YES)还是白名单(NO)。- 启用白名单时,别忘了把
ftp、anonymous加进名单,否则匿名访问与 yum 源都会失效。 pasv_enable=NO(只允许主动模式)会让 yum、wget 等默认被动的客户端失败。max_clients与max_per_ip只在 standalone(listen=YES)模式下有效。- 修改虚拟用户密码后必须重新执行
db_load。 - 传输二进制文件(压缩包、图片、程序)前务必执行
binary。 - 明文 FTP 的账号密码在网络上是完全裸奔的,跨公网请务必用 FTPS 或直接改用 SFTP。
七、综合练习
练习一:安装并启动 vsftpd
-
安装服务器和客户端:
sudo yum install vsftpd ftp -y -
启动服务:
systemctl start vsftpd # CentOS 7 # /etc/init.d/vsftpd start # CentOS 6 -
使用 ftp 命令访问:
ftp 192.168.1.130
练习二:设置一个基本的 FTP 服务器
要求:
- 允许匿名用户和本地用户登录。
- 允许匿名用户上传文件,但不允许创建目录。
- 每个 IP 只允许一个连接,服务器总共允许 5 个连接。
- 限制匿名用户传输速度为 100KB/s,本地用户为 200KB/s。
- 把被动连接的端口范围控制在 30001 至 30010 之间。
参考答案:
# /etc/vsftpd/vsftpd.conf
anonymous_enable=YES
local_enable=YES
write_enable=YES
anon_upload_enable=YES
anon_mkdir_write_enable=NO # 不允许创建目录
anon_umask=022
max_per_ip=1
max_clients=5
anon_max_rate=102400 # 100KB/s
local_max_rate=204800 # 200KB/s
pasv_enable=YES
pasv_min_port=30001
pasv_max_port=30010
xferlog_enable=YES
# 准备匿名上传目录
mkdir -p /var/ftp/upload
chown ftp:ftp /var/ftp/upload
chmod 755 /var/ftp/upload
chmod 755 /var/ftp # 根目录不可写
# SELinux 与防火墙
setsebool -P ftpd_full_access on
firewall-cmd --permanent --add-port=21/tcp
firewall-cmd --permanent --add-port=30001-30010/tcp
firewall-cmd --reload
systemctl restart vsftpd
验证:
# 匿名上传应成功,mkdir 应失败
ftp 192.168.1.130
ftp> cd upload
ftp> put /etc/hosts # 226 成功
ftp> mkdir aaa # 550 Permission denied
# 限速验证
wget ftp://192.168.1.130/upload/hosts
# 并发验证:开第 2 个会话应报 421 too many connections from your internet address
练习三:搭建匿名专用 FTP 服务器
要求:
- 禁止本地用户登录,只允许匿名用户登录。
- 登录后的默认目录为
/ftp_share。 - 在默认目录下创建两个目录:
pub、upload。 - 只能上传到
upload目录,不能下载upload里面的文件。 - 最大传输速率为 100KB/s。
- 服务器总连接数最大为 2。
- 每个 IP 最大连接数为 1。
- 只允许
192.168.0.0/24网段登录,但排除192.168.0.227,并将拒绝信息保存到/var/log/ftp_deny.log。 - 用 FTP 做一个 yum 源,并利用该 yum 源安装
gimp。
参考做法:
-
关闭 SELinux(实验环境简化处理):
setenforce 0 -
准备目录:
sudo mkdir -m 755 /ftp_share/ sudo mkdir -m 755 /ftp_share/pub sudo mkdir -m 777 /ftp_share/upload sudo chown -R root:root /ftp_share -
设置匿名用户的家目录(两种做法任选其一):
# 做法 A:修改 ftp 用户家目录 sudo usermod -d /ftp_share ftp # 做法 B(推荐):用配置参数指定,不动系统账号 # 在 vsftpd.conf 中写 anon_root=/ftp_share -
配置服务器
/etc/vsftpd/vsftpd.conf:anonymous_enable=YES local_enable=NO # 禁止本地用户登录 write_enable=YES anon_upload_enable=YES anon_mkdir_write_enable=NO anon_umask=0777 # 上传后权限 000,谁都下载不了,实现"只能上传不能下载" anon_root=/ftp_share anon_world_readable_only=YES # 只能下载所有人可读的文件 anon_max_rate=102400 max_clients=2 max_per_ip=1 tcp_wrappers=YES xferlog_enable=YES要点解析:
anon_umask=0777使上传的文件权限为000,配合默认的anon_world_readable_only=YES,即可实现"能上传到 upload 但下载不了 upload 里的文件"。 -
限制客户端 IP(TCP Wrapper):
/etc/hosts.allow:vsftpd: 10.1.1.0/255.255.255.0 EXCEPT 10.1.1.3/etc/hosts.deny:vsftpd: * :spawn echo "`date`" %c >> /var/log/ftp_deny.log按题目要求的网段应写为:
# /etc/hosts.allow vsftpd: 192.168.0.0/255.255.255.0 EXCEPT 192.168.0.227 # /etc/hosts.deny vsftpd: * : spawn echo "`date`" %c >> /var/log/ftp_deny.log -
做 FTP yum 源:把光盘内容放到
/ftp_share/yum,客户端 yum 配置中只需把baseurl一行修改为 FTP 格式:baseurl=ftp://192.168.0.183/yumyum clean all yum install gimp -y
练习四:操练 FTP 客户端命令
要掌握的操作:登录、退出、列目录、进入某个远程目录、进入某个本地目录、上传、下载、使用二进制传输类型、使用被动/主动数据传输模式、列出可用命令、查看某个命令的帮助信息、显示调试信息。
按以下步骤操练(括号内为参考命令和说明):
- 用匿名用户登录 FTP 服务器
192.168.1.130(ftp 192.168.1.130,用户名ftp代表匿名用户,密码为空)。 - 查看远程当前目录(
pwd)。 - 查看本地当前目录(
lcd,不带参数即显示本地当前目录)。 - 列出远程机当前目录的内容(
ls)。 - 下载远程机当前目录下的文件
bash(get bash,文件将下载到本地当前目录,名字不变)。 - 下载
bash放到本地/tmp下,改名为bash2(get bash /tmp/bash2)。 - 进入本地目录
/tmp(lcd /tmp,回到父目录为lcd ..)。 - 下载
bash放到本地当前目录,改名为bash3(get bash bash3)。 - 运行本地 shell 命令查看刚下载的文件(
!ls -l /tmp/bash*)。 - 进入远程机目录
upload(cd upload,回到父目录为cd ..)。 - 设置数据传输模式为 passive(
passive,看到Passive mode on即为成功;该命令是开关式切换,若显示 off 就再运行一次)。 - 设置数据传输类型为二进制(
binary)。 - 上传本机文件
/etc/passwd到远程机当前目录,改名为passwd2(put /etc/passwd ./passwd2,若远程已存在同名文件则会出错)。 - 列出可用命令(
help或?)。 - 查找命令
debug的使用方法(help debug)。 - 开启调试信息(
debug 1)。 - 关闭调试信息(
debug 0)。 - 退出(
exit、bye、quit,或快捷键Ctrl+D)。
练习五:综合大题
题目:搭建 FTP 服务器,满足以下需求:
- 允许本地用户登录,但只允许
a、b、c三个用户登录; - 所有普通用户登录后统一进入
/ftp目录; - 允许匿名用户登录,登录到
/var/ftp; - 匿名用户可以下载,不能上传;
a、b、c用户只能上传和下载自己的文件,不同用户之间不能互相删除文件;- 只使用
vsftpd.log格式的日志,并实现每月轮转、最多保留 2 个副本; - 只使用主动模式的 FTP 连接方式;
- 三个用户只能在
/ftp目录下活动(chroot); - 匿名用户和普通用户最大下载传输率均为 100KB/s;
- 最大总连接数 300,单客户端最大 3 个连接;
- 只能在上午 9:30 到晚上 20:30 访问 FTP;
- 允许
10.0.0.0/24网段登录,但拒绝10.0.0.10,并将拒绝信息记录到/var/log/ftp_denyip.log; - 在
/var/ftp下保存光盘文件,另一台机器通过 FTP 方式配置 yum 源; - 另一台机器每天晚上 18:00 使用 lftp 下载 FTP 服务器
/ftp里的所有内容到自己的/backup目录。
参考答案:
1. 创建共享目录并设置粘滞位
mkdir /ftp
chmod 757 /ftp
chmod o+t /ftp # 粘滞位:用户只能删除自己的文件(满足需求 5)
ls -ld /ftp
# drwxr-xrwt
useradd a; useradd b; useradd c
echo '123456' | passwd --stdin a
echo '123456' | passwd --stdin b
echo '123456' | passwd --stdin c
2. 配置 vsftpd.conf
anonymous_enable=YES # 需求 3
local_enable=YES # 需求 1
write_enable=YES
userlist_enable=YES
userlist_deny=NO # 白名单模式(需求 1)
local_root=/ftp # 需求 2
local_umask=0066 # 需求 5:新建文件 600,其他用户无法读写
# 需求 4:匿名只下载不上传(不开 anon_upload_enable 即可)
anon_upload_enable=NO
anon_mkdir_write_enable=NO
anon_other_write_enable=NO
xferlog_enable=YES # 需求 6
xferlog_std_format=NO # 只使用 vsftpd.log
pasv_enable=NO # 需求 7:只使用主动模式
connect_from_port_20=YES
chroot_local_user=YES # 需求 8
# CentOS 7 若 /ftp 可写还需加:allow_writeable_chroot=YES
anon_max_rate=100000 # 需求 9
local_max_rate=100000
max_clients=300 # 需求 10
max_per_ip=3
tcp_wrappers=YES # 需求 12
listen=NO # 需求 11:交由 xinetd 托管以使用 access_times
注意:
pasv_enable=NO会导致 yum 客户端无法通过 FTP 源安装软件(需求 13),因为 yum 默认使用被动模式。实际环境需权衡:若需求 13 必须满足,则应保留被动模式并改用pasv_min_port/pasv_max_port限定范围。这是本题内部的一处需求冲突,作答时应指出。
3. 配置白名单
vim /etc/vsftpd/user_list
a
b
c
ftp
anonymous # 必须加,否则匿名用户用 lftp 登录不了,yum 也无法使用
4. 配置日志轮转(需求 6)
vim /etc/logrotate.d/vsftpd
/var/log/vsftpd.log {
nocompress
missingok
monthly
rotate 2
}
5. 配置 xinetd 及访问时间(需求 11)
vim /etc/xinetd.d/vsftpd
service ftp
{
disable = no
socket_type = stream
protocol = tcp
wait = no
user = root
server = /usr/sbin/vsftpd
access_times = 9:30-20:30
}
/etc/init.d/vsftpd stop
/etc/init.d/xinetd restart
netstat -ntlup | grep :21 # 应显示 xinetd
6. 配置 TCP Wrapper 网段控制(需求 12)
vim /etc/hosts.allow
vsftpd: 10.0.0.0/255.255.255.0 EXCEPT 10.0.0.10 : spawn echo "`date`" %c >> /var/log/ftp_denyip.log
原教材示例网段写为
10.1.1.0/255.255.255.0,实际应根据题目要求的10.0.0.0/24网段调整为上面的写法。注意掩码必须写完整形式,不能用/24。
7. 配置 FTP yum 源(需求 13)
服务端:
mount /dev/cdrom /mnt
mkdir -p /var/ftp/yum
cp -a /mnt/* /var/ftp/yum/
客户端:
vim /etc/yum.repos.d/ftp.repo
[ftp]
name=FTP Repo
baseurl=ftp://10.0.0.2/yum
enabled=1
gpgcheck=0
yum clean all && yum repolist
8. 定时下载任务(需求 14)
在客户端配置 lftp 脚本和 crontab。lftp 支持在命令行中指定用户名和密码(可参考 man lftp),每天 18:00 执行:
crontab -e
# 0 18 * * * lftp -u a,password -e "mirror /ftp /backup; quit" ftp://10.0.0.2
验证:
crontab -l
tail -f /var/log/cron
ls -l /backup
练习六:虚拟用户实战(进阶)
要求:
- 建立三个虚拟用户
dev、test、readonly,系统中不存在同名账号。 dev拥有/data/dev的完整读写权限,限速 500KB/s。test拥有/data/test的上传权限,但不能删除文件。readonly只能下载/data/pub,不能做任何写操作。- 所有虚拟用户均被 chroot 在各自目录内。
- 被动端口范围 40001–40010,并放行防火墙。
参考答案要点:
# 1. 用户库
mkdir -p /etc/vsftpd/vuser
cat > /etc/vsftpd/vuser/vusers.txt <<'EOF'
dev
dev@123
test
test@123
readonly
read@123
EOF
db_load -T -t hash -f /etc/vsftpd/vuser/vusers.txt /etc/vsftpd/vuser/vusers.db
chmod 600 /etc/vsftpd/vuser/vusers.*
# 2. PAM
cat > /etc/pam.d/vsftpd.vu <<'EOF'
auth required pam_userdb.so db=/etc/vsftpd/vuser/vusers
account required pam_userdb.so db=/etc/vsftpd/vuser/vusers
EOF
# 3. 宿主账号与目录
useradd -d /data -s /sbin/nologin virtual
mkdir -p /data/{dev,test,pub}
chown -R virtual:virtual /data
chmod 755 /data /data/dev /data/test /data/pub
主配置 /etc/vsftpd/vsftpd.conf 关键项:
anonymous_enable=NO
local_enable=YES
write_enable=YES
local_umask=022
guest_enable=YES
guest_username=virtual
pam_service_name=vsftpd.vu
user_config_dir=/etc/vsftpd/vusers_dir
virtual_use_local_privs=YES
chroot_local_user=YES
allow_writeable_chroot=YES
pasv_enable=YES
pasv_min_port=40001
pasv_max_port=40010
xferlog_enable=YES
xferlog_std_format=NO
log_ftp_protocol=YES
个性化配置:
mkdir -p /etc/vsftpd/vusers_dir
# dev:完整读写 + 限速 500KB/s
cat > /etc/vsftpd/vusers_dir/dev <<'EOF'
local_root=/data/dev
write_enable=YES
local_max_rate=512000
EOF
# test:可上传不可删除
cat > /etc/vsftpd/vusers_dir/test <<'EOF'
local_root=/data/test
write_enable=YES
virtual_use_local_privs=NO
anon_upload_enable=YES
anon_mkdir_write_enable=YES
anon_other_write_enable=NO
EOF
# readonly:只读
cat > /etc/vsftpd/vusers_dir/readonly <<'EOF'
local_root=/data/pub
write_enable=NO
download_enable=YES
EOF
收尾:
setsebool -P ftpd_full_access on
firewall-cmd --permanent --add-port=21/tcp
firewall-cmd --permanent --add-port=40001-40010/tcp
firewall-cmd --reload
systemctl restart vsftpd
# 验证:三个账号在系统中都不存在
id dev; id test; id readonly # 均应报 no such user
练习七:思考题
- 为什么 vsftpd 不允许 chroot 根目录可写?如果允许会有什么安全风险?
anon_umask=0777为什么能实现"只能上传不能下载"?如果同时设置anon_world_readable_only=NO会发生什么?- 一台服务器同时有内网 IP
192.168.1.10和公网映射 IP1.2.3.4,客户端从公网访问被动模式 FTP 时ls卡死,为什么?如何解决? max_per_ip=3在客户端全部处于同一 NAT 出口时会带来什么问题?- 为什么说 SFTP 比 FTPS 更"防火墙友好"?
- 用户
zhangsan的 shell 是/sbin/nologin,他能通过 FTP 上传文件吗?能 SSH 登录吗?为什么? - 若只放行了 21 端口,主动模式和被动模式分别会出现什么现象?
- 虚拟用户上传的文件属主是谁?为什么?这对多虚拟用户共享目录的权限隔离有什么影响?
参考答案要点:
- chroot 根可写时,攻击者可在根目录写入特制文件(如替换动态库、写入
.forward等)配合其他漏洞实现提权越狱,因此 vsftpd 2.3.5 起默认拒绝。 anon_umask=0777使上传文件权限为000;默认anon_world_readable_only=YES要求文件必须"所有人可读"才能下载,000不满足,故下不了。若改为NO,则 vsftpd 不再校验 world-readable,但文件权限仍是000,而匿名进程(euid=ftp)也不是属主……实际取决于文件属主:若属主是 ftp,Linux 权限位000对属主仍拒绝读,因此依然下载失败;关键仍在于文件权限本身。- 服务端在 NAT 后,
227响应中回给客户端的是内网地址192.168.1.10,客户端无法连通。解决:配置pasv_address=1.2.3.4和pasv_addr_resolve=NO,并在 NAT 设备上映射被动端口范围。 - 同一 NAT 出口的所有客户端共用一个源 IP,
max_per_ip=3会导致该出口后只有 3 个人能用 FTP,其余全部收到421。企业环境需酌情放大或改用其他限流手段。 - SFTP 是单通道协议,全部数据都走 TCP 22 这一条连接,防火墙只放行一个端口即可;FTPS 仍是双通道,数据连接端口动态变化,且加密后防火墙的 FTP conntrack helper 无法解析
PORT/PASV命令,无法自动放行。 - 能上传(FTP 认证走 PAM,不检查 shell 是否为交互式),不能 SSH 登录(sshd 会执行用户 shell,
/sbin/nologin直接退出)。这正是纯 FTP 账号的推荐配置。 - 主动模式:能登录、能
ls(数据连接由服务器 20 端口出站发起,通常出站放行,且客户端侧需允许入站)——但若客户端有防火墙则失败。被动模式:能登录(21 通),但ls/get全部卡死超时,因为客户端要连的高端口被服务端防火墙挡住。 - 属主是
guest_username指定的宿主账号(如virtual)。因为所有虚拟用户在系统层面都以该账号身份操作文件。影响:从文件系统权限层面无法区分不同虚拟用户,若多个虚拟用户共享同一目录,彼此可以互删文件;隔离只能靠user_config_dir中给每人分配不同的local_root来实现。
此处评论已关闭