Linux 安装与配置 vsftpd

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)


目录

  1. 前置条件
  2. [安装 vsftpd](#安装 vsftpd)
  3. 启动服务
  4. [创建 FTP 用户](#创建 FTP 用户)
  5. [配置 vsftpd](#配置 vsftpd)
  6. [确认 FTP 根目录](#确认 FTP 根目录)
  7. 放行端口(安全组与防火墙)
  8. 连通性验证
  9. 上传测试
  10. 常见错误排查
  11. 安全建议

1. 前置条件

项目 说明
服务器 一台可 SSH 登录的 Linux 主机;外网访问需具备公网 IP
权限 root 或具备 sudo 的普通用户
云主机 若使用 ECS/云服务器,需能修改安全组入站规则
替换项 下文 203.0.113.10 为文档用示例公网 IP(RFC 5737 TEST-NET),请改为你的真实公网 IP

为何外网必须配置被动模式?

FTP 控制连接使用 21 端口;传输目录列表与文件时,会再建立数据连接

  • 主动模式(PORT):服务器连回客户端,易被 NAT/防火墙拦截。
  • 被动模式(PASV) :客户端连服务器的 pasv_min_portpasv_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

多为被动模式数据连接失败,按顺序检查:

  1. pasv_enable=YES,且 pasv_address公网 IP
  2. pasv_min_port / pasv_max_port 与安全组、防火墙放行范围一致
  3. 客户端使用被动模式(如 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。

参考

相关推荐
Inhand陈工5 天前
基于台达PLC与映翰通IG502的智慧水产养殖精准投喂与远程运维解决方案
运维·人工智能·物联网·阿里云·信息与通信
半条-咸鱼6 天前
【STM32】I2C协议原理、HAL读写与OLED显示操作
嵌入式硬件·c·信息与通信
OpenIM6 天前
增量版本同步能力介绍 | OpenIM
开源·github·信息与通信
鼎讯信通6 天前
1024J冲击能量+三种放电模式:DLG-1高压发生器覆盖电缆故障全场景
运维·能源·信息与通信
terry6006 天前
5G视频短信服务商选型全攻略:通道资源、架构能力与成本评估2026最新标准
大数据·人工智能·5g·json·asp.net·信息与通信·数据库架构
Inhand陈工6 天前
污水泵站PLC数据上云实战:西门子PLC + 映翰通IG502 + DM平台全流程
人工智能·物联网·网络安全·阿里云·信息与通信·iot
hz567896 天前
基于音视频 PaaS 的实时音视频解决方案:技术架构与落地实践
安全·架构·音视频·实时音视频·信息与通信·paas
曾阿伦6 天前
netcat / ncat / socat 用法详解与示例
linux·http·信息与通信
鼎讯信通7 天前
性能可拓展+功能一体化 走近 TXMN-BLG1 信号模拟设备
运维·能源·信息与通信
terry6007 天前
2026企业5G短信服务商选型全指南:通道、架构、服务全维度评估标准
大数据·人工智能·5g·web安全·信息与通信·数据库架构