前言
vsftpd(Very Secure FTP Daemon)是 Linux 上最主流的 FTP 服务端,特点是轻量、稳定、安全性好。适合这些场景:
- 给外包/客户开个账号传文件
- 老设备、老系统只支持 FTP
- 内网快速搭个文件中转站
先说句实在话:FTP 是明文协议,账号密码和数据在网上裸奔。除非有历史包袱,否则新项目建议直接用 SFTP(基于 SSH,零配置成本)或 FTPS。本文最后会讲怎么给 vsftpd 加上 TLS 加密。
环境:Rocky Linux 8 / CentOS 7(Ubuntu 差异已标注)
一、安装
# Rocky / CentOS dnf install -y vsftpd # Ubuntu / Debian # apt install -y vsftpd vsftpd -v # 确认版本,后面有些配置项和版本相关先备份配置,这步别省:
cp /etc/vsftpd/vsftpd.conf /etc/vsftpd/vsftpd.conf.bak注意路径差异:CentOS 系在
/etc/vsftpd/,Ubuntu 是/etc/vsftpd.conf(没有子目录)。
二、创建 FTP 用户
# -d 指定家目录,-s 指定不可登录的 shell(安全) useradd -d /data/ftp/ftpuser -s /sbin/nologin ftpuser # 设置系统密码 passwd ftpuser # 目录准备 mkdir -p /data/ftp/ftpuser chown -R ftpuser:ftpuser /data/ftp/ftpuser chmod 755 /data/ftp/ftpuser为什么要
-s /sbin/nologin?FTP 用户只需要能传文件,不需要能 SSH 登录服务器。给它/sbin/nologin能有效降低被爆破后的危害。但要注意:某些发行版需要把 shell 加入/etc/shells,详见 6.2。
三、配置 vsftpd.conf
完整配置如下(同样给精简可用的版本):
# ========== 基本设置 ========== # 禁止匿名登录(重要!默认可能是 YES) anonymous_enable=NO # 允许本地用户登录 local_enable=YES # 允许写操作(上传、删除、改名都靠它) write_enable=YES # 本地用户上传文件的权限掩码,022 = 755 local_umask=022 # ========== 目录与日志 ========== dirmessage_enable=YES xferlog_enable=YES xferlog_std_format=YES connect_from_port_20=YES # 以独立模式运行并监听 listen=YES listen_ipv6=NO # PAM 认证模块 pam_service_name=vsftpd userlist_enable=YES # ========== 禁锢用户在家目录 ========== # 用户登录后只能在自家目录,无法 cd 到上级 chroot_local_user=YES allow_writeable_chroot=YES # ========== 被动模式(NAT/防火墙环境必配)========== pasv_enable=YES pasv_min_port=30000 pasv_max_port=30100 # 公网访问时必填,填服务器的公网 IP # pasv_address=203.0.113.10核心参数说明
| 参数 | 说明 |
|---|---|
anonymous_enable=NO | 必改项。默认开启匿名访问,等于谁都能传文件 |
write_enable=YES | 不开这个,用户只能下载不能上传 |
chroot_local_user=YES | 把用户锁在自家目录,防止翻看服务器其他文件 |
allow_writeable_chroot=YES | 关键项,见 6.1,不加会直接报错登不上 |
pasv_min_port/pasv_max_port | 被动模式端口范围,防火墙要放行这一段 |
pasv_address | 服务器在 NAT 后面时,必须填公网 IP |
四、启动服务与防火墙
systemctl enable --now vsftpd systemctl status vsftpd防火墙:
firewall-cmd --permanent --add-service=ftp firewall-cmd --permanent --add-port=30000-30100/tcp firewall-cmd --reload为什么还要加端口范围?FTP 有两种模式:
- 主动模式(Active):服务端主动连客户端。客户端在 NAT 后面时基本用不了。
- 被动模式(Passive):客户端连服务端的随机高端口。现代客户端默认都是被动模式,所以必须放行
pasv_min_port~pasv_max_port。
SELinux:
getenforce setsebool -P ftpd_full_access on # 允许 vsftpd 读写用户家目录五、验证测试
5.1 服务端自测
ftp localhost # 输入用户名密码,能登录就说明基本 OK5.2 Linux 客户端测试
# 方式一:ftp 命令 ftp 192.168.1.10 # 方式二:lftp(更好用,支持被动模式切换) dnf install -y lftp lftp -u ftpuser,密码 192.168.1.105.3 curl 测试(适合脚本化验证)
# 下载 curl -u ftpuser:密码 ftp://192.168.1.10/test.txt # 上传 curl -u ftpuser:密码 -T local.txt ftp://192.168.1.10/ # 列目录 curl -u ftpuser:密码 ftp://192.168.1.10/注意:curl 默认走主动模式,在某些网络环境下会失败。加上
--ftp-pasv强制被动模式再试:curl --ftp-pasv -u ftpuser:密码 ftp://192.168.1.10/如果加了这个参数就好了,说明是主动/被动模式搞错了,不是服务端问题。
5.4 用 FileZilla 等图形工具
连接时把「传输模式」设为被动,字符集设为UTF-8,中文文件名才不会乱码。
六、常见问题排查(重点)
6.1500 OOPS: vsftpd: refusing to run with writable root inside chroot()
这是配chroot_local_user=YES后最经典的错误。
原因:你把用户禁锢在家目录了,但家目录本身是可写的。用户如果能写自己的"根目录",就能通过上传.ssh之类的目录实现越权,所以 vsftpd 拒绝启动。
解法一(推荐):确认 vsftpd 版本 ≥ 3.0,加上
allow_writeable_chroot=YES解法二(更安全,不依赖配置项):把家目录设成不可写,另建子目录给用户写:
chmod 755 /data/ftp/ftpuser mkdir /data/ftp/ftpuser/upload chown ftpuser:ftpuser /data/ftp/ftpuser/upload这样用户能进家目录(只读),但要写东西得进upload子目录。安全性比解法一好。
版本坑:
allow_writeable_chroot在 vsftpd 3.0.2 曾被移除,3.0.3 又加回来了。如果你加了这行还报unknown option,说明版本不对,只能用解法二。
6.2530 Login incorrect
这个错误信息很笼统,原因有好几个,按下面顺序排查:
① shell 不在/etc/shells里
grep nologin /etc/shells如果没输出,说明/sbin/nologin没被列为合法 shell,PAM 会拒绝。加上:
echo /sbin/nologin >> /etc/shells② 用户被列入了黑名单
cat /etc/vsftpd/ftpusers # 这个文件里的用户一律禁止 FTP 登录 cat /etc/vsftpd/user_list # 配合 userlist_enable=YES,默认是黑名单root默认就在这两个文件里,这是故意的,不要删。你自己的用户如果也在里面,删掉对应行。
③ PAM 配置问题
检查/etc/pam.d/vsftpd,确认pam_shells.so和pam_listfile.so没有误伤。
④ 密码本身错了
先确认能su - ftpuser切换成功,排除密码问题。
6.3 能登录能列目录,但上传失败
检查这三点:
grep write_enable /etc/vsftpd/vsftpd.conf # 是不是 NO ls -ld /data/ftp/ftpuser # 目录属主对不对 getsebool ftpd_full_access # SELinux 放行了吗文件系统权限和 SELinux 都要过,和 NFS 一样是两层校验。
6.4 被动模式连不上(能登录但列不出目录 / 一直卡住)
症状:能连上、能认证,但执行ls就卡住或超时。
原因:被动模式的数据端口被防火墙拦了。
# 确认配置生效 grep pasv /etc/vsftpd/vsftpd.conf # 确认防火墙放行了整个范围 firewall-cmd --list-all | grep -A2 ports如果服务器在云上(阿里云/腾讯云),别忘了安全组也要放行 30000-30100。这是最容易被忽略的一层。
用抓包定位到底卡在哪:
tcpdump -i any -n port 21 or portrange 30000-301006.5 中文文件名乱码
确认客户端用UTF-8编码连接。FileZilla 里:站点管理器 → 字符集 → 强制 UTF-8。
vsftpd 本身在较新版本默认就是 UTF-8,老版本可能需要:
utf8_filesystem=YES七、进阶:给 FTP 加上 TLS 加密(FTPS)
既然 FTP 是明文的,生产环境强烈建议启用 TLS。
① 生成自签证书(内网够用;公网建议买正式证书)
openssl req -x509 -nodes -days 3650 -newkey rsa:2048 \ -keyout /etc/vsftpd/vsftpd.key \ -out /etc/vsftpd/vsftpd.crt \ -subj "/C=CN/ST=Beijing/L=Beijing/O=MyOrg/CN=192.168.1.10" chmod 600 /etc/vsftpd/vsftpd.key② 配置中追加
ssl_enable=YES allow_anon_ssl=NO force_local_data_ssl=YES force_local_logins_ssl=YES ssl_tlsv1_2=YES ssl_tlsv1_1=NO ssl_tlsv1=NO ssl_sslv2=NO ssl_sslv3=NO rsa_cert_file=/etc/vsftpd/vsftpd.crt rsa_private_key_file=/etc/vsftpd/vsftpd.key require_ssl_reuse=NO ssl_ciphers=HIGH重启:
systemctl restart vsftpd客户端就要用FTPS(不是 SFTP)连接,并且因为是自签证书,需要选择"信任该证书"。
require_ssl_reuse=NO这行建议加上。某些客户端(尤其是 FileZilla)在 TLS 会话复用上和 vsftpd 有兼容性问题,不加这个可能连不上。
八、安全加固清单
| 措施 | 做法 |
|---|---|
| 禁用匿名 | anonymous_enable=NO |
| 禁锢目录 | chroot_local_user=YES+ 不可写家目录 |
| 限制登录 | 用user_list白名单,只放需要的用户 |
| 禁用 root | 保持/etc/vsftpd/ftpusers里有 root |
| 加密传输 | 启用 TLS(第七节) |
| 限制来源 | 防火墙 + 云安全组限制源 IP |
| 防爆破 | 配合 fail2ban 监控/var/log/secure |
| 被动端口收窄 | pasv_min_port/pasv_max_port范围开小一点 |
九、总结
| 环节 | 关键点 |
|---|---|
| 用户 | useradd -s /sbin/nologin,shell 要进/etc/shells |
| 核心配置 | anonymous_enable=NO、write_enable=YES、chroot_local_user=YES |
| chroot 报错 | allow_writeable_chroot=YES或改用手法更稳的"不可写家目录 + 子目录" |
| 防火墙 | 放行 ftp +被动端口范围(云服务器还要配安全组) |
| SELinux | setsebool -P ftpd_full_access on |
| 排错 | 卡在列目录 = 被动模式端口没通;530 = shell/黑名单/PAM |
最后再提醒一次:FTP 明文传输,新项目优先考虑 SFTP。如果必须用 FTP,请务必按第七节把 TLS 打开。