vsftpd + Apache 共享目录:从零到生产环境部署指南
本文记录了在 Ubuntu 24.04 系统上部署 vsftpd 服务,并与 Apache 共享同一文件目录的完整过程。内容涵盖基础安装、用户管理、FTPS 加密配置、IPv6 支持以及 PAM 认证问题的排查与解决,适合需要快速搭建“通过 FTP 上传、通过网页展示”的文件共享服务。
一、需求背景与方案选型
1.1 核心需求
- 通过 FTP 上传文件到服务器的
/var/www/files目录 - 该目录同时作为 Apache 的网站目录,通过网页展示文件列表
- 传输过程需要加密(FTPS)
- 支持 IPv4 和 IPv6 双栈访问
- 只需一个 FTP 用户,暂不需要多用户隔离
1.2 方案选型
| 考虑因素 | 选择 |
|---|---|
| 部署方式 | 直接在 Ubuntu 系统安装,避免 Docker 带来的端口映射和权限复杂性 |
| 用户类型 | 系统本地用户(ftpuser),使用 /sbin/nologin 作为登录 shell |
| 加密方式 | 显式 FTPS(FTP over TLS),端口 21 |
| IP 支持 | 双栈(IPv4 + IPv6) |
| 权限方案 | 用户组共享:ftpuser 与 www-data 加入同一组,实现权限共享 |
二、环境准备与软件安装
2.1 安装 vsftpd 和 OpenSSL
sudo apt update
sudo apt install vsftpd openssl -y
安装完成后,系统会自动创建一个名为 ftp 的系统用户,这个用户是 vsftpd 服务的运行者,与你后续创建的 FTP 登录用户无关。
2.2 准备目标目录
sudo mkdir -p /var/www/files
三、用户与权限设计(核心)
3.1 创建 FTP 登录用户
使用 -M 参数不创建家目录,-s /sbin/nologin 禁止 SSH 登录:
sudo useradd -M -s /sbin/nologin ftpuser
sudo passwd ftpuser
注意:/etc/passwd 中的 /home/ftpuser 只是一个配置记录,不代表该目录真实存在。FTP 用户的实际工作目录由 vsftpd 的 local_root 或 chroot 决定。
3.2 建立用户组共享机制
将 ftpuser 和 www-data 加入同一个组 webgroup,实现目录权限共享:
sudo groupadd webgroup
sudo usermod -aG webgroup ftpuser
sudo usermod -aG webgroup www-data
3.3 设置目录权限(含 setgid)
# 目录属主为 ftpuser,属组为 webgroup
sudo chown -R ftpuser:webgroup /var/www/files
# 权限 775:属主和组可读写执行,其他人只读执行
sudo chmod 775 /var/www/files
# setgid:让新建文件自动继承 webgroup
sudo chmod g+s /var/www/files
setgid 的作用:确保 ftpuser 上传的新文件属组自动继承为 webgroup,这样 www-data 就能通过组权限读写这些文件,无需手动修改权限。
四、SSL 证书配置
使用 Let’s Encrypt 受信任证书(如果已有证书,直接复用):
# 证书路径(假设域名为 ftp.tanglinux.com)
rsa_cert_file=/etc/letsencrypt/live/ftp.tanglinux.com/fullchain.pem
rsa_private_key_file=/etc/letsencrypt/live/ftp.tanglinux.com/privkey.pem
Let’s Encrypt 证书会自动续期,vsftpd 无需重启即可使用新证书。
备用方案:如果没有域名,可使用自签名证书:
sudo mkdir -p /etc/ssl/vsftpd
sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout /etc/ssl/vsftpd/vsftpd.key \
-out /etc/ssl/vsftpd/vsftpd.crt \
-subj "/C=CN/ST=Guangdong/L=Shenzhen/O=MyCompany/CN=ftp.tanglinux.com"
sudo chmod 600 /etc/ssl/vsftpd/vsftpd.key
五、vsftpd 核心配置
5.1 配置文件 /etc/vsftpd.conf
# ===== 基本运行设置 =====
listen=NO
listen_ipv6=YES
anonymous_enable=NO
local_enable=YES
write_enable=YES
local_umask=002
utf8_filesystem=YES
# ===== 用户隔离 =====
chroot_local_user=YES
allow_writeable_chroot=YES
# ===== 被动模式(双栈支持)=====
pasv_enable=YES
pasv_min_port=40000
pasv_max_port=40010
# 如果域名同时解析 IPv4 和 IPv6,可以使用域名
pasv_address=ftp.tanglinux.com
pasv_addr_resolve=YES
# ===== SSL/TLS 加密 =====
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
rsa_cert_file=/etc/letsencrypt/live/ftp.tanglinux.com/fullchain.pem
rsa_private_key_file=/etc/letsencrypt/live/ftp.tanglinux.com/privkey.pem
ssl_ciphers=HIGH
# ===== 日志 =====
xferlog_enable=YES
xferlog_file=/var/log/vsftpd.log
log_ftp_protocol=YES
5.2 关键配置说明
| 配置项 | 作用 | 说明 |
|---|---|---|
listen=NO; listen_ipv6=YES | 启用 IPv6 监听 | 同时接受 IPv4 和 IPv6 连接 |
local_umask=002 | 新文件权限掩码 | 配合 webgroup 组权限,确保新文件组可写 |
chroot_local_user=YES | 锁定用户在家目录 | 防止越权访问系统其他目录 |
pasv_address=域名 | 被动模式地址 | 域名同时解析 IPv4 和 IPv6 时,双栈均可工作 |
force_local_*_ssl=YES | 强制加密 | 禁止明文传输,确保安全 |
六、PAM 认证配置(关键)
这是整个部署过程中最容易出错的环节。vsftpd 默认的 PAM 配置使用 @include common-session,而 common-session 中包含 pam_systemd.so,该模块对 /sbin/nologin 用户会创建 systemd 用户会话失败,导致 vsftpd 返回 530 Login incorrect。
6.1 问题现象
- 用户名存在,密码正确
- TLS 握手成功
- 返回
530 Login incorrect
6.2 根本原因
/etc/pam.d/common-session 中的 pam_systemd.so 对 /sbin/nologin 用户处理失败,导致整个 PAM 认证栈失败。
6.3 解决方案
将 /etc/pam.d/vsftpd 修改为以下内容:
auth required pam_listfile.so item=user sense=deny file=/etc/ftpusers onerr=succeed
auth required pam_unix.so shadow
account required pam_unix.so
session required pam_unix.so
修改说明:
| 原始配置 | 修改后配置 | 变化 |
|---|---|---|
@include common-account | account required pam_unix.so | 直接调用,去除 pam_systemd.so 干扰 |
@include common-session | session required pam_unix.so | 直接调用,去除 pam_systemd.so 干扰 |
@include common-auth | auth required pam_unix.so shadow | 直接调用,流程更清晰 |
执行修改命令:
sudo tee /etc/pam.d/vsftpd << 'EOF'
auth required pam_listfile.so item=user sense=deny file=/etc/ftpusers onerr=succeed
auth required pam_unix.so shadow
account required pam_unix.so
session required pam_unix.so
EOF
sudo systemctl restart vsftpd
注意:如果 PAM 配置中仍有 auth required pam_shells.so,也需要注释掉或删除,因为 /sbin/nologin 不在 /etc/shells 白名单中。
七、防火墙与端口放行
# 放行控制端口和被动模式端口范围
sudo ufw allow 21/tcp
sudo ufw allow 40000:40010/tcp
sudo ufw reload
云服务器注意:还需在云服务商控制台的安全组中放行上述端口(TCP 协议)。
八、Apache 目录列表配置
开启 Apache 的 mod_autoindex 功能,使 /var/www/files 目录在网页上显示文件列表。
8.1 开启目录列表
# /etc/apache2/apache2.conf 或站点配置
<Directory "/var/www/files">
Options Indexes FollowSymLinks
AllowOverride None
Require all granted
</Directory>
8.2 重启 Apache
sudo systemctl restart apache2
九、连接测试
使用支持 FTPS 的客户端(如 FileZilla)连接:
| 连接参数 | 值 |
|---|---|
| 协议 | FTP over TLS (explicit encryption) |
| 主机 | ftp.tanglinux.com(或 IP) |
| 端口 | 21 |
| 用户名 | ftpuser |
| 密码 | 你设置的密码 |
成功连接后,上传一个测试文件,访问 http://你的域名/ 或 http://你的IP/ 确认 Apache 目录列表正常显示。
十、常见问题与解决方法
| 问题 | 解决方案 |
|---|---|
530 Login incorrect(密码正确) | 检查 PAM 配置,确保去掉了 pam_systemd.so 干扰 |
| 能登录但无法列出目录 | 检查被动模式端口 40000-40010 是否在防火墙和安全组中放行 |
| 上传文件后 Apache 无法写入 | 确认 setgid 生效(ls -ld /var/www/files 显示 drwxrwsr-x) |
| IPv6 连接失败 | 确认 listen_ipv6=YES,且域名正确解析 AAAA 记录 |
| 自签名证书警告 | 正常现象,接受即可;生产环境建议使用 Let’s Encrypt |
| vsftpd 启动失败 | sudo journalctl -u vsftpd -n 20 查看详细错误 |
十一、经验总结
在整个部署过程中,有几个关键点值得特别关注:
1. PAM 配置是最大陷阱
vsftpd 默认的 PAM 配置使用 @include common-session,会引入 pam_systemd.so。对于使用 /sbin/nologin 的 FTP 用户,pam_systemd.so 会尝试创建 systemd 用户会话并失败,导致 530 Login incorrect。解决方案是直接使用 pam_unix.so,去除系统默认的 common-session 引用。
2. IPv6 和被动模式的配合
pasv_address 可以设置域名,只要域名同时解析 A 和 AAAA 记录,vsftpd 会根据客户端的地址族返回对应的 IP 地址,实现双栈支持。
3. 文件权限的协作
通过 webgroup 组 + setgid + local_umask=002 的组合,确保 vsftpd 上传的文件 Apache 可以读写,无需手动干预。
4. 证书管理
Let’s Encrypt 证书的自动续期机制对 vsftpd 友好,续期后无需重启服务。
十二、最终配置清单
| 配置项 | 状态 | 路径/内容 |
|---|---|---|
| vsftpd 安装 | ✅ | vsftpd openssl |
| FTP 用户 | ✅ | ftpuser,shell 为 /sbin/nologin |
| 共享组 | ✅ | webgroup(包含 ftpuser 和 www-data) |
| 目录权限 | ✅ | 775,setgid,属主 ftpuser:webgroup |
| vsftpd.conf | ✅ | 双栈 + FTPS + 被动模式 |
| PAM 配置 | ✅ | 直接调用 pam_unix.so,去除 common-session |
| SSL 证书 | ✅ | Let’s Encrypt 或自签名 |
| 防火墙 | ✅ | 放行 21/tcp 和 40000-40010/tcp |
| Apache 目录列表 | ✅ | Options Indexes |
十三、验证结果
部署完成后,通过 FileZilla 连接成功:
状态: 连接建立,等待欢迎消息...
响应: 220 (vsFTPd 3.0.5)
命令: AUTH TLS
响应: 234 Proceed with negotiation.
状态: 初始化 TLS 中...
状态: TLS 连接已建立。
命令: USER ftpuser
响应: 331 Please specify the password.
命令: PASS ********
响应: 230 Login successful.
状态: 已登录
状态: 读取目录列表...
命令: PWD
响应: 257 "/" is the current directory
状态: 列出"/"的目录成功
至此,vsftpd + Apache 共享目录服务部署完成,支持 IPv4/IPv6 双栈和 FTPS 加密传输。