Linux 安装与配置 vsftpd:外网登录、上传与 530/425 排错
本文以 vsftpd 为例,在常见 Linux 发行版上完成安装、配置与验证,使外网客户端能够登录并上传文件。文中涉及 IP、路径、账号均为示例,请按你的环境替换。
适用环境 :RHEL / CentOS / Alibaba Cloud Linux(dnf/yum)、Ubuntu / Debian(apt)。
你将完成:安装服务 → 创建本地用户 → 配置被动模式 → 放行端口 → 命令行/Java 上传测试 → 处理 530、425 常见错误。
安全提示 :标准 FTP 以明文传输账号与密码,不适合直接用于生产。生产环境请优先考虑 SFTP(SSH) 或 FTPS(FTP over TLS)。
目录
- 前置条件
- [安装 vsftpd](#安装 vsftpd)
- 启动服务
- [创建 FTP 用户](#创建 FTP 用户)
- [配置 vsftpd](#配置 vsftpd)
- [确认 FTP 根目录](#确认 FTP 根目录)
- 放行端口(安全组与防火墙)
- 连通性验证
- 上传测试
- 常见错误排查
- 安全建议
1. 前置条件
| 项目 | 说明 |
|---|---|
| 服务器 | 一台可 SSH 登录的 Linux 主机;外网访问需具备公网 IP |
| 权限 | root 或具备 sudo 的普通用户 |
| 云主机 | 若使用 ECS/云服务器,需能修改安全组入站规则 |
| 替换项 | 下文 203.0.113.10 为文档用示例公网 IP(RFC 5737 TEST-NET),请改为你的真实公网 IP |
为何外网必须配置被动模式?
FTP 控制连接使用 21 端口;传输目录列表与文件时,会再建立数据连接。
- 主动模式(PORT):服务器连回客户端,易被 NAT/防火墙拦截。
- 被动模式(PASV) :客户端连服务器的
pasv_min_port~pasv_max_port区间;公网场景通常必须启用,并正确设置pasv_address与端口放行,否则易出现 425。
2. 安装 vsftpd
RHEL / CentOS / Alibaba Cloud Linux
bash
sudo dnf install -y vsftpd
# 较老系统可使用:sudo yum install -y vsftpd
Ubuntu / Debian
bash
sudo apt update
sudo apt install -y vsftpd
3. 启动服务
bash
sudo systemctl enable --now vsftpd
sudo systemctl status vsftpd --no-pager
输出中出现 active (running) 表示服务已启动。
4. 创建 FTP 用户
vsftpd 默认使用本地系统用户 登录(非匿名)。以下示例用户名为 ftpuser:
bash
sudo useradd -m -s /bin/bash ftpuser
sudo passwd ftpuser
验证账号与密码(能成功切换即表示凭据有效):
bash
su - ftpuser
pwd
exit
5. 配置 vsftpd
5.1 备份原配置
bash
sudo cp /etc/vsftpd/vsftpd.conf /etc/vsftpd/vsftpd.conf.bak.$(date +%F)
5.2 写入推荐配置
将 pasv_address 改为你的公网 IP(勿使用内网 IP,否则外网客户端会连错地址导致 425):
bash
sudo tee /etc/vsftpd/vsftpd.conf > /dev/null << 'EOF'
listen=YES
listen_ipv6=NO
anonymous_enable=NO
local_enable=YES
write_enable=YES
local_umask=022
use_localtime=YES
xferlog_enable=YES
connect_from_port_20=YES
# 将用户限制在其主目录内(chroot)
chroot_local_user=YES
allow_writeable_chroot=YES
pam_service_name=vsftpd
userlist_enable=NO
# 被动模式(公网访问必备)
pasv_enable=YES
pasv_min_port=50000
pasv_max_port=50100
pasv_address=203.0.113.10
EOF
| 配置项 | 作用 |
|---|---|
local_enable=YES |
允许本地用户登录 |
write_enable=YES |
允许上传、删除、重命名等写操作 |
chroot_local_user=YES |
登录后限制在用户主目录,降低越权风险 |
allow_writeable_chroot=YES |
在 chroot 下仍允许可写目录(配合上传) |
pasv_* |
被动模式地址与端口范围,需与安全组/防火墙一致 |
应用配置:
bash
sudo systemctl restart vsftpd
sudo systemctl status vsftpd --no-pager
确认被动模式相关项已生效:
bash
grep -nE '^(pasv_enable|pasv_min_port|pasv_max_port|pasv_address)=' /etc/vsftpd/vsftpd.conf
6. 确认 FTP 根目录
未设置 local_root 时,用户登录后的默认目录为其家目录:
bash
getent passwd ftpuser
示例输出:
text
ftpuser:x:1000:1000::/home/ftpuser:/bin/bash
第六个字段 /home/ftpuser 即为 FTP 根目录。应用侧若需配置「远程基础路径」,可填写该路径,例如:
yaml
# 应用配置示例(按你的项目字段名调整)
remote-base-dir: /home/ftpuser
固定到自定义目录(可选)
若希望统一使用 /data/ftpuser:
bash
sudo mkdir -p /data/ftpuser
sudo chown ftpuser:ftpuser /data/ftpuser
在 /etc/vsftpd/vsftpd.conf 末尾追加:
conf
local_root=/data/ftpuser
然后执行 sudo systemctl restart vsftpd。
7. 放行端口(安全组与防火墙)
7.1 云安全组(外网访问时优先检查)
入方向至少放行:
| 协议 | 端口 | 说明 |
|---|---|---|
| TCP | 21 | FTP 控制连接 |
| TCP | 50000--50100 | 被动模式数据连接(须与 pasv_min_port / pasv_max_port 一致) |
仅放行 21 时,常见现象为:能登录,但无法列目录或上传(425)。
7.2 firewalld(RHEL 系常见)
bash
sudo firewall-cmd --permanent --add-port=21/tcp
sudo firewall-cmd --permanent --add-port=50000-50100/tcp
sudo firewall-cmd --reload
7.3 ufw(Ubuntu/Debian 常见)
bash
sudo ufw allow 21/tcp
sudo ufw allow 50000:50100/tcp
sudo ufw reload
8. 连通性验证
在客户端 (例如 Windows PowerShell)测试端口是否可达,将 203.0.113.10 换成你的公网 IP:
powershell
Test-NetConnection 203.0.113.10 -Port 21
Test-NetConnection 203.0.113.10 -Port 50000
| 结果 | 含义 |
|---|---|
| 21 通、50000 不通 | 被动端口未放行或 pasv_address 配置错误,易出现 425 |
| 均不通 | 检查安全组、本机防火墙、vsftpd 是否监听 |
9. 上传测试
9.1 Windows 自带 ftp 客户端
powershell
ftp 203.0.113.10
连接成功后依次输入(密码输入时不回显,属正常现象):
text
user ftpuser
# 按提示输入密码
binary
pwd
ls
put D:\local\test_upload.txt
bye
已启用
chroot_local_user时,用户通常已在主目录内,pwd可能显示/,即对应其家目录根。
9.2 Java(Apache Commons Net)
Maven 依赖:
xml
<dependency>
<groupId>commons-net</groupId>
<artifactId>commons-net</artifactId>
<version>3.11.1</version>
</dependency>
示例代码(请修改 host、账号、密码与本地文件路径;勿将真实密码提交到版本库):
java
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import java.io.FileInputStream;
import java.io.InputStream;
public class FtpUploadTest {
public static void main(String[] args) throws Exception {
String host = System.getenv().getOrDefault("FTP_HOST", "203.0.113.10");
int port = 21;
String username = System.getenv().getOrDefault("FTP_USER", "ftpuser");
String password = System.getenv().getOrDefault("FTP_PASS", "请改为强密码");
String baseDir = "/home/ftpuser";
String localFile = "D:/local/test_upload.txt";
String remoteFileName = "test_upload.txt";
FTPClient ftp = new FTPClient();
ftp.connect(host, port);
try {
if (!ftp.login(username, password)) {
throw new RuntimeException("FTP login failed: " + ftp.getReplyString());
}
ftp.enterLocalPassiveMode();
ftp.setFileType(FTP.BINARY_FILE_TYPE);
if (baseDir != null && !baseDir.isBlank()) {
if (!ftp.changeWorkingDirectory(baseDir)) {
throw new RuntimeException(
"changeWorkingDirectory failed: " + baseDir + ", reply=" + ftp.getReplyString());
}
}
try (InputStream input = new FileInputStream(localFile)) {
if (!ftp.storeFile(remoteFileName, input)) {
throw new RuntimeException("Upload failed, reply=" + ftp.getReplyString());
}
}
System.out.println("Upload success: " + remoteFileName);
} finally {
try { ftp.logout(); } catch (Exception ignored) {}
try { ftp.disconnect(); } catch (Exception ignored) {}
}
}
}
运行前可设置环境变量(避免硬编码密码):
bash
export FTP_HOST=203.0.113.10
export FTP_USER=ftpuser
export FTP_PASS='你的强密码'
10. 常见错误排查
10.1 530 Login incorrect
| 可能原因 | 处理步骤 |
|---|---|
| 未启用本地用户 | 确认 local_enable=YES |
| 用户在黑名单中 | 检查 /etc/vsftpd/ftpusers、/etc/vsftpd/user_list,删除对应用户名行后重启服务 |
| 密码错误 | sudo passwd ftpuser,并用 su - ftpuser 验证 |
bash
sudo grep -n '^ftpuser$' /etc/vsftpd/ftpusers /etc/vsftpd/user_list 2>/dev/null
sudo systemctl restart vsftpd
10.2 425 Failed to establish connection
多为被动模式数据连接失败,按顺序检查:
pasv_enable=YES,且pasv_address为公网 IPpasv_min_port/pasv_max_port与安全组、防火墙放行范围一致- 客户端使用被动模式(如 Java 中
enterLocalPassiveMode())
bash
grep -nE '^(pasv_enable|pasv_min_port|pasv_max_port|pasv_address)=' /etc/vsftpd/vsftpd.conf
11. 安全建议
- 使用强密码,并通过环境变量或密钥管理注入,避免写入代码仓库。
- 限制安全组来源 IP,仅允许业务需要的网段访问 21 与被动端口段。
- 定期审计
vsftpd日志与上传目录权限。 - 生产环境优先采用 SFTP 或配置 FTPS(TLS),避免明文 FTP。