Featured image of post FTP 服务自托管:vsftpd / pure-ftpd 主被动模式 + Nginx stream + SSL

FTP 服务自托管:vsftpd / pure-ftpd 主被动模式 + Nginx stream + SSL

部署 vsftpd 与 pure-ftpd 服务,理解 FTP 主被动模式与端口段分配,配置 Nginx stream 模块做四层代理转发,启用 SSL 加密传输

FTP 是企业里"老但稳定"的文件传输方案——服务器间脚本同步、备份归档、跨地域传输都还在用。本篇把 vsftpd 与 pure-ftpd 的 Docker 化部署、主被动模式原理、Nginx stream 四层代理、SSL 加密整理清楚。

阅读对象:运维 / 部署工程师、需要搭建内部文件服务器的技术团队 覆盖范围:FTP 主动 / 被动模式原理 + vsftpd 完整部署 + pure-ftpd 完整部署 + 虚拟用户 + SSL/TLS 加密 + Nginx stream 反代 + 常见客户端测试

一、FTP 协议基础

1.1 两个端口

端口作用
21控制端口(命令)
20数据端口(仅主动模式

主动模式(PORT):服务器从 20 端口主动连客户端告知的端口。 被动模式(PASV):服务器告知客户端自己的地址 + 端口,等客户端来连

实战推荐被动模式——客户端不用开端口,服务器在公网就行。

1.2 主动 vs 被动模式

维度主动模式被动模式
数据连接服务器 → 客户端客户端 → 服务器
客户端要求必须有公网 IP任何网络
防火墙客户端要放行随机端口服务器要放行 PASV 端口段
适用内网公网推荐

核心难点:FTP 协议需要 2 个连接,NAT / 防火墙容易把数据连接搞断——必须正确配置。

二、vsftpd 部署

vsftpd(Very Secure FTPD)是 Linux 默认 FTP 服务端,最稳定

2.1 拉取镜像

1
docker pull fauria/vsftpd

2.2 启动容器

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# 测试端口占用
lsof -i:21100-21110

# 启动
docker run -d --name vsftpd --restart=always \
  -p 20:20 \
  -p 21:21 \
  -p 21100-21110:21100-21110 \
  -v /home/docker/vsftpd/data:/home/vsftpd \
  -v /home/docker/vsftpd/log:/var/log \
  -e FTP_USER=www \
  -e FTP_PASS={{REDACTED}} \
  -e PASV_ADDRESS=203.0.113.10 \
  -e PASV_MIN_PORT=21100 \
  -e PASV_MAX_PORT=21110 \
  -e REVERSE_LOOKUP_ENABLE=NO \
  fauria/vsftpd

关键参数

  • PASV_ADDRESS=203.0.113.10服务器公网 IP——必须设对,否则客户端连不上
  • PASV_MIN_PORT / MAX_PORT:PASV 模式端口段——防火墙必须放行
  • REVERSE_LOOKUP_ENABLE=NO强烈推荐——避免 DNS 反向解析卡登录

2.3 完整环境变量

变量默认说明
FTP_USERadmin默认用户
FTP_PASS随机 16 位不指定就随机
PASV_ADDRESSDocker host IP配服务器公网 IP
PASV_ADDR_RESOLVENO主机名解析
PASV_ENABLEYES启用被动模式
PASV_MIN_PORT21100PASV 起始端口
PASV_MAX_PORT21110PASV 结束端口
REVERSE_LOOKUP_ENABLEYES设为 NO 加快登录
PASV_PROMISCUOUSNO关闭被动模式安全检查
PORT_PROMISCUOUSNO关闭主动模式安全检查

2.4 手动创建用户

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# 进入容器
docker exec -i -t vsftpd bash

# 创建用户目录
mkdir /home/vsftpd/myuser

# 写虚拟用户
echo -e "myuser\nmypass" >> /etc/vsftpd/virtual_users.txt

# 转换数据库
/usr/bin/db_load -T -t hash -f /etc/vsftpd/virtual_users.txt /etc/vsftpd/virtual_users.db

# 退出并重启
exit
docker restart vsftpd

2.5 生成 SSL 证书

1
2
3
4
5
# 创建自签证书(生产用 Let's Encrypt)
openssl req -x509 -nodes -days 365 \
  -newkey rsa:2048 \
  -keyout /etc/vsftpd/vsftpd.pem \
  -out /etc/vsftpd/vsftpd.pem

挂载到容器:

1
-v /home/docker/vsftpd/ssl/vsftpd.pem:/etc/vsftpd/vsftpd.pem:ro

vsftpd.conf 加:

1
2
3
4
5
6
7
8
ssl_enable=YES
ssl_tlsv1=YES
ssl_sslv2=NO
ssl_sslv3=NO
require_ssl_reuse=NO
ssl_ciphers=HIGH
rsa_cert_file=/etc/vsftpd/vsftpd.pem
rsa_private_key_file=/etc/vsftpd/vsftpd.pem

客户端必须选"显式 TLS"(FTPS),不是 SFTP(SSH 文件传输)。

三、pure-ftpd 部署

pure-ftpd 是另一个流行的 FTP 服务端,虚拟用户管理更灵活

3.1 拉取镜像

1
docker pull stilliard/pure-ftpd

3.2 启动容器

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
docker run -d --name ftp --restart=always \
  -p 20-21:20-21 \
  -p 30000-30009:30000-30009 \
  -v /home/ftp/conf:/etc/pure-ftpd/passwd \
  -v /home/ftp/data:/home/ftpusers/www \
  -v /home/ftp/data:/home/ftp \
  -e "PUBLICHOST=localhost" \
  stilliard/pure-ftpd \
  /bin/bash -c "useradd ftp; \
    usermod -d /home/ftp ftp; \
    /run.sh -c 100 -C 10 \
    -l puredb:/etc/pure-ftpd/pureftpd.pdb \
    -j -i -R \
    -P 10.0.0.1 -p 30000:30009"

3.3 参数详解

参数含义
-c 100最大客户端数 100
-C 10每 IP 最多 10 个连接
-l puredb:...虚拟用户文件
-j自动创建家目录
-i匿名用户不允许上传
-R不允许 chmod
-P 10.0.0.1PASV 模式告知客户端的 IP
-p 30000:30009PASV 端口段

3.4 虚拟用户管理

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
# 进入容器
docker exec -it ftp /bin/bash

# 添加用户 www(密码 {{REDACTED}})
pure-pw useradd www \
  -f /etc/pure-ftpd/passwd/pureftpd.passwd \
  -m \
  -u ftpuser \
  -d /home/ftpusers/www

# 修改家目录权限
chmod -R 777 /home/ftp/data/

# 删除用户
pure-pw userdel www -f /etc/pure-ftpd/passwd/pureftpd.passwd -m

# 查看用户列表
pure-pw list -f /etc/pure-ftpd/passwd/pureftpd.passwd

3.5 验证测试

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# 1. 在宿主机创建文件
touch /home/ftp/data/test.txt

# 2. 浏览器访问(匿名)
ftp://<SERVER_IP>:21
# 应该能看到 test.txt(无需密码)

# 3. FTP 工具连接
# 用户名 www,密码 {{REDACTED}}
# 看到 test.txt,能下载能上传能删除

# 4. 主被动模式测试
# 在客户端(FileZilla)切换主动 / 被动模式,都能正常上传下载

3.6 清理

1
2
docker rm -f ftp
rm -rf /home/ftp

四、Nginx stream 四层代理

FTP 服务器在内网,要暴露到公网?Nginx stream 模块做四层代理。

4.1 完整 stream 配置

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
stream {
    # FTP 控制端口
    upstream ftp_control {
        server 10.0.0.1:21;
    }
    server {
        listen 21;
        proxy_pass ftp_control;
        proxy_connect_timeout 1s;
        proxy_timeout 10m;
    }

    # FTP 主动模式数据端口(20)
    upstream ftp_data {
        server 10.0.0.1:20;
    }
    server {
        listen 20;
        proxy_pass ftp_data;
        proxy_connect_timeout 1s;
        proxy_timeout 10m;
    }

    # PASV 端口段
    server {
        listen 21100-21110;
        proxy_pass 10.0.0.1:$server_port;   # $server_port 是动态端口
    }
}

关键点

  • proxy_pass 10.0.0.1:$server_port动态端口转发——客户端连 21100 转到 21100,21101 转到 21101
  • proxy_timeout 10m10 分钟无数据不切断——大文件传输必备
  • proxy_upload_rate / proxy_download_rate限速(生产推荐)
1
2
3
4
5
6
server {
    listen 21100;
    proxy_pass 10.0.0.1:21100;
    proxy_upload_rate 10240k;       # 上传限速 10MB/s
    proxy_download_rate 20480k;     # 下载限速 20MB/s
}

4.2 SSL / FTPS 代理

FTPS(FTP over TLS)的四层代理较复杂——TLS 加密流无法被 Nginx 解密再加密

推荐方案

  • HAProxy(原生支持 SSL 透传)
  • 或直接在 FTP 服务器端处理 TLS,Nginx 只代理明文

五、客户端使用

5.1 命令行

1
2
3
4
5
6
7
8
# 上传 / 下载测试
curl -u www:{{REDACTED}} ftp://203.0.113.10/k8s/test.sh | bash

# 列出
curl -u www:{{REDACTED}} ftp://203.0.113.10/

# 主动模式
curl -u www:{{REDACTED}} --ftp-port - ftp://203.0.113.10/file

5.2 FileZilla

跨平台 FTP 客户端,首选

1
2
3
4
主机:203.0.113.10
用户名:www
密码:{{REDACTED}}
端口:21

关键设置

  • 传输模式:被动(推荐)
  • 加密:显式的 TLS(FTPS)

5.3 WinSCP(Windows)

适合自动化脚本(批处理):

1
2
3
4
5
@echo off
"C:\Program Files\WinSCP\WinSCP.com" ^
  /command "open ftp://www:{{REDACTED}}@203.0.113.10/" ^
  "put C:\local\file.zip /remote/file.zip" ^
  "exit"

5.4 lftp(Linux 推荐)

1
2
3
4
5
# 批量上传
lftp -u www,{{REDACTED}} -e "mirror -R /local/path /remote/path" 203.0.113.10

# 加密传输
lftp -u www,{{REDACTED}} -e "set ftp:ssl-force true; set ftp:ssl-protect-data true; ls" 203.0.113.10

六、典型坑位

6.1 登录成功但列不出文件

症状:PASV 模式登录后卡住,服务器等待客户端连数据端口

原因

  • PASV_ADDRESS 没设对
  • 防火墙没放行 PASV 端口段
  • Nginx 反代没配端口段转发

修复

1
2
3
4
5
# 1. 确认 PASV_ADDRESS 是公网 IP(不是 127.0.0.1)
echo $PASV_ADDRESS
# 2. 防火墙放行
iptables -A INPUT -p tcp --dport 21100:21110 -j ACCEPT
# 3. Nginx stream 加端口段

6.2 主动模式连不上

症状:PORT 模式报 “Connection refused”。

原因:服务器 20 端口主动连客户端,客户端在 NAT / 防火墙后。

修复:改用 PASV 模式,别折腾主动模式

6.3 上传文件 0 字节

症状:客户端显示上传成功,服务器文件是 0 字节。

原因:PASV 端口被防火墙 drop 掉了。

修复

1
2
3
4
5
# 1. tcpdump 看数据连接
tcpdump -i any port 21100-21110

# 2. 防火墙状态
iptables -L -n | grep 21100

6.4 vsftpd 启动报 “500 OOPS”

修复

1
2
3
4
5
6
7
# 容器内检查 vsftpd.conf
docker exec vsftpd cat /etc/vsftpd/vsftpd.conf | grep -v "^#" | grep -v "^$"

# 常见错误:listen_ipv6=YES + listen=YES 冲突
# 修法:二选一
echo "listen=YES" >> /etc/vsftpd/vsftpd.conf
echo "listen_ipv6=NO" >> /etc/vsftpd/vsftpd.conf

七、vsftpd vs pure-ftpd 选型

维度vsftpdpure-ftpd
性能中等
稳定性极高
配置简洁灵活
虚拟用户手动 db_loadpure-pw 命令行
TLS / SSL支持支持
Docker 镜像官方 vsftpd / fauriastilliard
适合生产 / 简单场景多用户 / 复杂权限

简单场景选 vsftpd(稳定为先),多用户管理选 pure-ftpd(命令行友好)。

八、生产安全清单

8.1 强制 TLS

vsftpd

1
2
3
4
5
6
7
8
ssl_enable=YES
allow_anon_ssl=NO
force_local_data_ssl=YES
force_local_logins_ssl=YES
ssl_tlsv1=YES
ssl_sslv2=NO
ssl_sslv3=NO
require_ssl_reuse=NO

pure-ftpd

1
2
echo "TLS 1" > /etc/pure-ftpd/conf/TLS
# 把证书放到 /etc/ssl/private/pure-ftpd.pem

8.2 限制用户目录

1
2
3
# vsftpd
chroot_local_user=YES
allow_writeable_chroot=YES
1
2
# pure-ftpd
echo "YES" > /etc/pure-ftpd/conf/ChrootEveryone

8.3 限速

1
2
3
# vsftpd
local_max_rate=1024000   # 1MB/s
anon_max_rate=512000     # 500KB/s

8.4 禁用匿名

1
anonymous_enable=NO

8.5 fail2ban 防爆破

1
2
3
4
5
6
7
8
# /etc/fail2ban/jail.local
[vsftpd]
enabled  = true
port     = ftp,ftp-data,ftps,ftps-data
filter   = vsftpd
logpath  = /var/log/vsftpd.log
maxretry = 5
bantime  = 3600

九、监控与日志

9.1 vsftpd 日志

1
2
3
4
5
6
7
# 容器内
docker exec vsftpd tail -f /var/log/vsftpd.log

# 输出示例
Mon May 21 03:00:00 2021 [pid 1] CONNECT: Client "1.2.3.4"
Mon May 21 03:00:01 2021 [pid 1] [www] OK LOGIN: Client "1.2.3.4"
Mon May 21 03:00:05 2021 [pid 1] [www] OK UPLOAD: Client "1.2.3.4", "/file.zip", 12345 bytes, 1.2 MB/s

9.2 pure-ftpd 日志

1
docker exec ftp tail -f /var/log/pure-ftpd/pureftpd.log

9.3 接入 Loki

1
2
3
4
logging:
  driver: loki
  options:
    loki-url: "http://internal.example.com:3100/loki/api/v1/push"

十、卸载清理

1
2
3
4
5
6
7
# vsftpd
docker rm -f vsftpd
rm -rf /home/docker/vsftpd

# pure-ftpd
docker rm -f ftp
rm -rf /home/ftp

十一、2024+ 视角补充

本文写于 2021-03,2024-2026 这几年里"FTP 该不该用"的共识有明显变化:

FTP 整体趋势:FTP 协议在 2020s 越来越被视为遗留技术——所有主流浏览器(Chrome、Firefox、Edge、Safari)自 2020-2021 起就在逐步淘汰 FTP 协议。RFC 959 设计于 1985,默认明文传输是致命伤(用户名/密码/文件内容全裸奔)。2024+ 的新项目,优先选 SFTP / HTTPS / MinIO / S3 兼容对象存储

场景2021 主流2024+ 推荐理由
内部脚本同步FTP/FTPSSFTP(SSH 自带)SSH 默认安全,无需额外服务
跨地域文件交换FTP 服务器MinIO / S3 兼容对象存储签名 URL、版本控制、跨域复制
浏览器下载FTP URLHTTPS + 静态站点浏览器已不再支持 ftp:// 链接
客户端工具上传FileZilla/FTPrclone + S3 兼容增量同步、加密、断点续传
老设备/老协议对接FTPFTP/FTPS(保留)无替代品时仍可用

vsftpd / pure-ftpd 项目现状

  • vsftpd 仍维护,但 2024 年发布节奏明显放缓(最后大版本 3.0.5 在 2021)。新特性很少,主要是 CVE 修复
  • pure-ftpd 状态类似。社区分裂(部分分支用 Rust 重写,但未成熟)
  • ProFTPD 仍活跃(2024-2025 有 1.3.8+ 发布),但市场份额不大
  • 新部署不推荐用这俩老牌项目,能用 SFTP 就用 SFTP

SFTP 容器化(vsftpd 替代)

1
2
3
4
5
6
7
8
# atmoz/sftp 是 2024+ 最流行的自托管 SFTP 镜像
docker run -d \
  --name sftp \
  -p 2222:22 \
  -v /home/sftp/users.conf:/etc/sftp/users.conf:ro \
  -v /home/sftp/data:/home \
  atmoz/sftp \
  www:{{REDACTED}}:1001:1001:upload

users.conf 一行一个用户:用户名:密码:UID:GID:家目录——比 vsftpd 虚拟用户简单

FTP 仍必要的场景(2024 仍存在):

  • 老的工业设备(数控机床、医疗仪器)只支持 FTP 上报数据
  • 老的 ERP / 财务系统只支持 FTP 文件交换
  • 与海外客户的 B2B 集成(对方系统只懂 FTP)
  • 备份归档脚本(老脚本不愿重写)

这些场景的加固清单

  • 必须 FTPS(FTP over TLS),不能明文
  • 必须 fail2ban
  • 必须 chroot + 限制用户家目录
  • 必须限速(防 DoS)
  • 定期审计:看 /var/log/vsftpd.log 谁连了、传了什么
  • 计划退出:列入"2026 技术债清单",逐年替换

生产实践补充(2024 更新)

  • HAProxy 替代 Nginx stream 做 FTPS 四层代理(Nginx stream 透传 TLS 麻烦)
  • CrowdSec 替代 fail2ban:社区共享 IP 黑名单,更新更频繁(2024 主流)
  • Caddy + sftp-bridge:Caddy 2024+ 原生支持 SFTP 代理,可以少一层 vsftpd

十二、最佳实践清单

  • PASV 模式 + 公网 IP:避免主动模式 NAT 问题
  • PASV_ADDRESS 必填:客户端才知道连哪里
  • PASV 端口段防火墙放行:21100-21110(或自定义)
  • Nginx stream 四层代理:内网 FTP 暴露公网
  • 强制 TLS / SSL:FTPS 而非明文 FTP
  • chroot 限制用户家目录:防止越权访问
  • 限速:避免大文件占满带宽
  • 禁用匿名:除非业务需要
  • fail2ban / CrowdSec 防爆破:5 次失败封 1 小时
  • 定期清理日志:vsftpd.log 不会自动 rotate
  • 新项目优先 SFTP / S3:FTP 仅作为兼容老系统的备选

下一步

使用 Hugo 构建
主题 StackJimmy 设计