Ubuntu 22.04 安装 Apache mod_ftp 0.9.6 指南
在 Ubuntu 22.04 上从源码编译安装 Apache mod_ftp 0.9.6-beta,并以 httpd 模块方式托管 FTP 服务的完整实测指南。
Ubuntu 22.04 安装 Apache mod_ftp 0.9.6 指南
目标:在一台 Ubuntu 22.04 服务器上通过 apxs 将 Apache mod_ftp 0.9.6-beta 编译为 httpd 动态模块,完成 FTP 站点配置、systemd 服务托管与登录/上传/下载验证,全流程已在 Ubuntu 22.04 实测通过。
一、环境信息
| 项目 | 内容 |
|---|---|
| 操作系统 | Ubuntu 22.04.5 LTS(内核 6.8.0-138-generic) |
| 系统架构 | x86_64(amd64) |
| 编译工具链 | gcc 11.4.0、GNU Make、libtool、apr-1.0 构建脚本 |
| httpd 版本 | Apache/2.4.52(apt 发行包,Server built 2026-10-05) |
| mod_ftp 版本 | 0.9.6-beta(源码包于 2009-10-08 发布,此后未再发版) |
| 测试客户端 | curl 7.81.0、lftp 4.9.2、netcat-openbsd |
| 源码目录 | /opt/httpd-ftp |
| 模块安装位置 | /usr/lib/apache2/modules/mod_ftp.so |
| FTP 根目录 | /srv/ftpdocs |
| 服务管理方式 | systemd(apache2.service) |
说明:mod_ftp 是 Apache httpd 的 FTP 协议模块,由 Covalent 捐赠、Apache 基金会以独立项目维护。截至目前它仍处于 beta 阶段,最新版本 0.9.6-beta 发布于 2009 年 10 月,多年未发新版,API 停留在 httpd 2.2/2.4 早期水平。这正是本文花大量篇幅记录编译排障的原因:在较新的 httpd 头文件下它有多处不兼容,需要少量手工修补。生产环境请谨慎评估,如仅需 FTP 服务可优先考虑 vsftpd/proftpd;选 mod_ftp 的价值在于复用 httpd 的认证、访问控制、日志与 SSL 体系。
💡 技术拓展:FTP 协议每传输一个文件使用两条 TCP 连接——控制连接(默认 21 端口,传输 USER/PASS/RETR 等命令)与数据连接(传文件列表和文件内容)。数据连接的建立方式分为主动模式(PORT)与被动模式(PASV):主动模式由服务器从 20 端口反连客户端指定地址,对客户端防火墙友好、对服务器侧不利;被动模式由服务器开一个临时端口等待客户端连接,是当今主流(应对客户端普遍处于 NAT 之后的现实)。mod_ftp 两种模式均支持,本文实测同时覆盖:
FTPActiveRange控制主动模式服务端出站端口,FTPPASVrange控制被动模式监听端口范围。本文两种模式均实测通过。
二、前置条件与软件包准备
2.1 配置系统软件源并更新索引
apt-get update
回显(输出节选):
Get:12 http://security.ubuntu.com/ubuntu jammy-security/main amd64 Packages [4429 kB]
Fetched 28.9 MB in 8s (3425 kB/s)
Reading package lists...
2.2 安装 apache2 与 apache2-dev
apache2-dev 提供 apxs2 编译接口与 httpd 头文件,是编译第三方模块的必备包:
apt-get install -y apache2 apache2-dev
关键回显:
Setting up apache2-dev (2.4.52-1ubuntu4.24) ...
验证:
apache2 -v
apxs2 -q TARGET
回显:
Server version: Apache/2.4.52 (Ubuntu)
Server built: 2026-10-05T17:25:19
apache2
Ubuntu 上 apxs2 是指向 apxs 的符号链接:
ls -la /usr/bin/apxs*
回显:
lrwxrwxrwx 1 root root 4 Oct 5 17:25 /usr/bin/apxs2 -> apxs
-rwxr-xr-x 1 root root 20093 Oct 5 17:25 /usr/bin/apxs
2.3 安装下载与测试工具
apt-get install -y wget curl ca-certificates lftp netcat-openbsd iproute2 vim-common
说明:lftp 与 netcat-openbsd 用于后续功能验证,iproute2 提供 ss 命令查看端口监听。
三、版本选型与源码下载
3.1 版本说明
mod_ftp 官方发布位于 https://dlcdn.apache.org/httpd/mod_ftp/ ,当前仅有 0.9.6-beta 一个版本可下载。选择它没有版本可选空间,重点在于选配套的 httpd 版本:Ubuntu 22.04 仓库的 httpd 2.4.52 属于较新分支,实测确认 mod_ftp 0.9.6-beta 源码需要打 3 处小补丁才能编译、运行(详见第五章排障记录)。若选择更老的 httpd(如 2.4.29 之前的版本)可能免修补,但不建议为此降级安全版本。
3.2 下载并校验源码包
cd /opt
wget https://dlcdn.apache.org/httpd/mod_ftp/mod_ftp-0.9.6-beta.tar.gz
echo "17a97a15d9c6518875e8075676904471 mod_ftp-0.9.6-beta.tar.gz" | md5sum -c
sha256sum mod_ftp-0.9.6-beta.tar.gz
回显:
-rw-r--r-- 1 root root 129647 Oct 8 2009 mod_ftp-0.9.6-beta.tar.gz
mod_ftp-0.9.6-beta.tar.gz: OK
0ade7a99b19f0a4df3e2816303a8a0b3114aea49d100a0ae641e7b948e22fa3b mod_ftp-0.9.6-beta.tar.gz
说明:Apache 官方随包发布了 MD5 校验文件(17a97a15d9c6518875e8075676904471)与 GPG 签名,md5sum -c 校验通过;SHA256 摘要 0ade7a99...e22fa3b 为本机实测计算值,可作为固定基线留存比对。
3.3 解压源码
tar xzf mod_ftp-0.9.6-beta.tar.gz
mv mod_ftp-0.9.6 httpd-ftp
ls /opt/httpd-ftp
回显:
CHANGES-FTP
LICENSE-FTP
Makefile-ftp.win
Makefile.apxs
NOTICE-FTP
NWGNUftp-makefile
README-FTP
STATUS-FTP
build
configure.apxs
docs
include
mod_ftp.dsw
modules
注意:压缩包内顶层目录名为 mod_ftp-0.9.6(不带 -beta 后缀),本文统一重命名为 /opt/httpd-ftp。
四、编译安装(方案 A:apxs 动态模块,推荐主线)
4.1 运行 configure.apxs
按照源码 README-FTP 的说明,apxs 构建方式为:
cd /opt/httpd-ftp
APXS=/usr/bin/apxs ./configure.apxs
回显:
Detecting features
Finished, run 'make' to compile mod_ftp
Run 'make FTPPORT=8021 install' to install mod_ftp
(The default FTPPORT is 21 if not specified)
The manual pages ftp/index.html and mod/mod_ftp.html
will be installed to help get you started.
The conf/extra/ftpd.conf will be installed as an example
for you to work from. In your configuration file,
/etc/apache2/apache2.conf
uncomment the line '#Include conf/extra/ftpd.conf'
to activate this example mod_ftp configuration.
4.2 首次 make:踩坑一(ftp_config.h 残缺宏)
make
实测报错:
In file included from ftp_internal.h:30,
from mod_ftp.c:24:
ftp_config.h:4:9: error: macro names must be identifiers
4 | #define 1
| ^
ftp_config.h:7:9: error: macro names must be identifiers
7 | #define 1
| ^
make[1]: *** [/usr/share/apache2/build/rules.mk:212: mod_ftp.slo] Error 1
定位:查看 modules/ftp/ftp_config.h,内容全部变成了 #define 1(宏名丢失)。根因在 configure.apxs 第 115-118 行:特性探测生成的 sed 表达式经 echo 与 eval 两次传递时反斜杠被吞掉,#define \1 1 中的反向引用 \1 丢失,只剩 #define 1。这是脚本级 bug,在当前 shell 环境下必然复现。
解决:手工按 modules/ftp/ftp_config.h.in 模板补齐宏名(现代 Linux 全部满足这些特性):
cat > /opt/httpd-ftp/modules/ftp/ftp_config.h <<'EOF'
/* modules/ftp/ftp_config.h.in used only for apxs builds */
/* Define to 1 if you have the `arpa/ftp.h' header. */
#define HAVE_ARPA_FTP_H 1
/* Define to 1 if you have the `fchmod' function. */
#define HAVE_FCHMOD 1
/* Define to 1 if you have the `netinet/ip.h' header. */
#define HAVE_NETINET_IP_H 1
/* Define to 1 if you have the declaration of `SOL_IP'. */
#define HAVE_SOL_IP 1
/* Define to 1 if `msg_accrights' is member of `struct msghdr'. */
#undef HAVE_STRUCT_MSGHDR_MSG_ACCRIGHTS
/* Define to 1 if `msg_control' is member of `struct msghdr'. */
#define HAVE_STRUCT_MSGHDR_MSG_CONTROL 1
/* Define to 1 if you have the `sys/stat.h' header. */
#define HAVE_SYS_STAT_H 1
EOF
逐项解说:HAVE_ARPA_FTP_H/HAVE_NETINET_IP_H/HAVE_SYS_STAT_H 表示对应头文件存在;HAVE_FCHMOD 表示 fchmod 函数可用;HAVE_SOL_IP 与 HAVE_STRUCT_MSGHDR_MSG_CONTROL 用于主动模式低端口数据连接的辅助处理;HAVE_STRUCT_MSGHDR_MSG_ACCRIGHTS 在现代 glibc 中已不存在,保持 #undef 即可。
4.3 第二次 make:踩坑二与三(httpd 2.4 API 改名)
重新 make 后出现两组 API 改名错误,实测报错:
ftp_commands.c:1928:36: error: 'conn_rec' has no member named 'remote_ip'; did you mean 'remote_host'?
1928 | if (strcasecmp(ip_addr, c->remote_ip) != 0) {
ftp_message.c:59:48: error: 'conn_rec' has no member named 'remote_addr'
59 | apr_getnameinfo(&remote_host, c->remote_addr, 0);
定位:mod_ftp 0.9.6 编写时 httpd 2.4 尚未发布,连接结构体 conn_rec 在 2.4 中已把 remote_ip 更名为 client_ip、remote_addr 更名为 client_addr(Ubuntu 22.04 的 /usr/include/apache2/httpd.h 第 1170 行附近可见 char *client_ip; 与 apr_sockaddr_t *client_addr;)。全源码仅 4 个 .c 文件涉及,且全部是对 conn_rec 的成员访问(模块自身结构体中无同名成员,可放心整体替换)。
解决:
cd /opt/httpd-ftp/modules/ftp
sed -i 's/->remote_ip/->client_ip/g' ftp_commands.c ftp_data_connection.c ftp_limitlogin.c ftp_message.c
sed -i 's/c->remote_addr/c->client_addr/g' ftp_message.c
grep -rn "remote_ip\|c->remote_addr" *.c | wc -l
回显:
0
继续编译:
cd /opt/httpd-ftp
make
成功尾部回显:
libtool: link: x86_64-linux-gnu-gcc -shared -fPIC -DPIC .libs/mod_ftp.o ... -Wl,-soname -Wl,mod_ftp.so -o .libs/mod_ftp.so
libtool: link: ( cd ".libs" && rm -f "mod_ftp.la" && ln -s "../mod_ftp.la" "mod_ftp.la" )
make[1]: Leaving directory '/opt/httpd-ftp/modules/ftp'
4.4 安装并清理 apxs 注入的重复配置
make install
关键回显:
Installing configuration files
mkdir /etc/apache2/extra
Installing FTP documents
mkdir /usr/share/apache2/ftpdocs
Installing online manual
mkdir /usr/share/apache2/default-site/htdocs
Installing header files
验证模块文件:
ls -l /usr/lib/apache2/modules/mod_ftp.so
回显:
-rwxr-xr-x 1 root root 457960 Oct 14 17:01 /usr/lib/apache2/modules/mod_ftp.so
踩坑四:make install 内部调用 apxs -i -a,会把一行相对路径的 LoadModule 追加进 /etc/apache2/apache2.conf,实测内容为 LoadModule ftp_module usr/lib/apache2/modules/mod_ftp.so(缺前导斜杠),同时追加注释行 #Include etc/apache2/extra/ftpd.conf。再次执行 apachectl configtest 时会出现 module ftp_module is already loaded, skipping 告警。处理方式是删除这两行,改用 Debian 规范的模块启用方式:
sed -i '/LoadModule ftp_module/d; /Include etc\/apache2\/extra\/ftpd.conf/d' /etc/apache2/apache2.conf
echo "LoadModule ftp_module /usr/lib/apache2/modules/mod_ftp.so" > /etc/apache2/mods-available/ftp.load
a2enmod ftp
回显:
Enabling module ftp.
To activate the new configuration, you need to run:
systemctl restart apache2
语法检查:
apache2ctl configtest
回显:
Syntax OK
五、方案 B:一键脚本(备用方案,整合全部修补)
将第四章的全部修补动作固化为脚本,适合批量装机。以下脚本在干净系统上完整实测通过:
cat > /opt/httpd-ftp/build-modftp.sh <<'EOF'
#!/bin/bash
set -e
test -d /opt/httpd-ftp || { echo "请先解压源码到 /opt/httpd-ftp"; exit 1; }
cd /opt/httpd-ftp
cat > modules/ftp/ftp_config.h <<'CFG'
/* modules/ftp/ftp_config.h.in used only for apxs builds */
#define HAVE_ARPA_FTP_H 1
#define HAVE_FCHMOD 1
#define HAVE_NETINET_IP_H 1
#define HAVE_SOL_IP 1
#undef HAVE_STRUCT_MSGHDR_MSG_ACCRIGHTS
#define HAVE_STRUCT_MSGHDR_MSG_CONTROL 1
#define HAVE_SYS_STAT_H 1
CFG
sed -i 's/->remote_ip/->client_ip/g' \
modules/ftp/ftp_commands.c \
modules/ftp/ftp_data_connection.c \
modules/ftp/ftp_limitlogin.c \
modules/ftp/ftp_message.c
sed -i 's/c->remote_addr/c->client_addr/g' modules/ftp/ftp_message.c
APXS=/usr/bin/apxs ./configure.apxs
make
make install
sed -i '/LoadModule ftp_module/d; /Include etc\/apache2\/extra\/ftpd.conf/d' /etc/apache2/apache2.conf
echo "LoadModule ftp_module /usr/lib/apache2/modules/mod_ftp.so" > /etc/apache2/mods-available/ftp.load
a2enmod ftp
apache2ctl configtest
EOF
chmod +x /opt/httpd-ftp/build-modftp.sh
解说:脚本依次完成特性宏固化、两处 API 改名修补、configure/make/install,以及 Debian 化的模块注册;set -e 保证任一步失败立即中止。重复执行安全:sed 幂等,apxs 重复 -a 注入的行会被随后的 sed 清掉。
六、部署 FTP 站点
6.1 准备 FTP 根目录与测试文件
mkdir -p /srv/ftpdocs
echo "hello mod_ftp" > /srv/ftpdocs/welcome.txt
head -c 1M /dev/urandom > /srv/ftpdocs/bigfile.bin
chown -R www-data:www-data /srv/ftpdocs
ls -l /srv/ftpdocs
回显:
-rw-r--r-- 1 root root 1048576 Oct 14 16:52 bigfile.bin
-rw-r--r-- 1 root root 14 Oct 14 16:52 welcome.txt
说明:文件属主最终应为 www-data(apache2 工作进程用户),否则登录认证虽通过、STOR 上传会因目录权限被拒(实测踩过,见 FAQ Q7)。chown 后再 ls -l 属主列即显示 www-data。
6.2 创建登录账号
mod_ftp 的用户认证完全复用 httpd 的认证体系,这里用 htpasswd 文件认证(示例账号 ftpuser / 密码 q1w2e3r4):
htpasswd -cb /etc/apache2/ftpd.passwd ftpuser q1w2e3r4
回显:
Adding password for user ftpuser
6.3 编写 ftpd.conf
cat > /etc/apache2/conf-available/ftpd.conf <<'EOF'
# mod_ftp FTP 服务配置(Ubuntu apache2 适配版)
LogFormat "%u [%a] %r" command_log_fmt
LogFormat "%{%b %e %H:%M:%S %Y}t %T %a %B %U %M %F %d %W %u %S %Z %Y" transfer_log_fmt
Listen 21 ftp
AcceptFilter ftp none
<VirtualHost _default_:21>
ServerAdmin admin@example.com
DocumentRoot "/srv/ftpdocs"
FTP On
FTPBannerMessage "Welcome to Apache mod_ftp (Ubuntu 22.04)"
FTPTimeoutLogin 120
FTPTimeoutIdle 600
FTPTimeoutData 300
FTPMaxLoginAttempts 3
FTPUmask 022
FTPPASVrange 50000 50100
<Directory "/srv/ftpdocs">
AuthType Basic
AuthName "FTP Authentication"
AuthUserFile "/etc/apache2/ftpd.passwd"
require valid-user
</Directory>
ErrorLog /var/log/apache2/ftp_error.log
CustomLog /var/log/apache2/ftp_command.log command_log_fmt
CustomLog /var/log/apache2/ftp_transfer.log transfer_log_fmt
</VirtualHost>
EOF
a2enconf ftpd
逐项解说:两条 LogFormat 定义命令日志与传输日志的别名,供 CustomLog 引用(若缺失会在日志中写入字面量 command_log_fmt,实测踩过);Listen 21 ftp 让 httpd 在 21 端口监听并标记协议名 ftp,配合 AcceptFilter ftp none 关闭内核 accept 过滤以兼容 FTP 的交互式握手;虚拟主机块中 FTP On 是启用 FTP 协议的总开关;三个 FTPTimeout* 分别限定登录、空闲、数据传输超时(秒);FTPPASVrange 50000 50100 限定被动模式数据端口范围便于防火墙放行;Directory 块把 htpasswd 认证挂到 FTP 根目录,登录即验活;三条日志分列错误/命令/传输。
6.4 启动服务并确认监听
systemctl restart apache2
systemctl is-active apache2
ss -tlnp | grep -E ':21|:80'
回显:
active
LISTEN 0 511 0.0.0.0:80 0.0.0.0:* users:(("apache2",pid=5804,fd=3),("apache2",pid=5803,fd=3),("apache2",pid=5800,fd=3))
LISTEN 0 511 0.0.0.0:21 0.0.0.0:* users:(("apache2",pid=5804,fd=4),("apache2",pid=5803,fd=4),("apache2",pid=5800,fd=4))
说明:同一套 apache2 进程同时服务 HTTP(80) 与 FTP(21),这正是 mod_ftp 的设计价值——FTP 与 HTTP 共享 worker、模块与配置体系。
七、功能验证
7.1 curl 验证列表、下载与上传
curl -s --connect-timeout 10 ftp://127.0.0.1/ --user ftpuser:q1w2e3r4
回显:
-rw-r--r-- 1 root root 1048576 Oct 14 16:52 bigfile.bin
-rw-r--r-- 1 www-data www-data 20 Oct 14 17:02 up.txt
-rw-r--r-- 1 www-data www-data 14 Oct 14 16:52 welcome.txt
下载并校验 1MB 二进制文件:
curl -s -o /tmp/down.txt ftp://127.0.0.1/bigfile.bin --user ftpuser:q1w2e3r4
md5sum /tmp/down.txt /srv/ftpdocs/bigfile.bin
回显:
d56574d3db27a2f448cdc78305522229 /tmp/down.txt
d56574d3db27a2f448cdc78305522229 /srv/ftpdocs/bigfile.bin
上传并校验:
echo test-upload-content > /tmp/up.txt
curl -s -T /tmp/up.txt ftp://127.0.0.1/up.txt --user ftpuser:q1w2e3r4
md5sum /tmp/up.txt /srv/ftpdocs/up.txt
回显:
295faa30d24dde02ec36b4333d7d20b0 /tmp/up.txt
295faa30d24dde02ec36b4333d7d20b0 /srv/ftpdocs/up.txt
7.2 lftp 被动模式与主动模式
被动模式(passive-mode on)完整会话:
echo "file from lftp" > /tmp/lftp-put.txt
lftp -u ftpuser,q1w2e3r4 127.0.0.1 -e "set ftp:passive-mode on; pwd; ls; put /tmp/lftp-put.txt; get welcome.txt -o /tmp/lftp-get.txt; ls; bye"
回显:
ftp://ftpuser:q1w2e3r4@127.0.0.1
-rw-r--r-- 1 www-data www-data 1048576 Oct 14 16:52 bigfile.bin
-rw-r--r-- 1 www-data www-data 20 Oct 14 17:02 up.txt
-rw-r--r-- 1 www-data www-data 14 Oct 14 16:52 welcome.txt
-rw-r--r-- 1 www-data www-data 1048576 Oct 14 16:52 bigfile.bin
-rw-r--r-- 1 www-data www-data 24 Oct 14 17:02 lftp-put.txt
-rw-r--r-- 1 www-data www-data 20 Oct 14 17:02 up.txt
-rw-r--r-- 1 www-data www-data 14 Oct 14 16:52 welcome.txt
主动模式(passive-mode off):
lftp -u ftpuser,q1w2e3r4 127.0.0.1 -e "set ftp:passive-mode off; ls; bye"
回显(输出节选):
-rw-r--r-- 1 www-data www-data 1048576 Oct 14 16:52 bigfile.bin
-rw-r--r-- 1 www-data www-data 24 Oct 14 17:02 lftp-put.txt
-rw-r--r-- 1 www-data www-data 20 Oct 14 17:02 up.txt
-rw-r--r-- 1 www-data www-data 14 Oct 14 16:52 welcome.txt
7.3 原始协议交互验证
用 netcat 直连 21 端口观察控制连接的原始回显:
printf 'USER ftpuser\r\nPASS q1w2e3r4\r\nSYST\r\nFEAT\r\nPWD\r\nQUIT\r\n' | nc 127.0.0.1 21
回显:
220-Welcome to Apache mod_ftp (Ubuntu 22.04)
220 node1.modftp.example FTP Server (Apache/2.4.52 (Ubuntu)) ready.
331 Password required for ftpuser
230 User ftpuser logged in
215 UNIX Type: L8 System: "Apache/2.4.52 (Ubuntu)"
211-Extensions supported
EPSV
TVFS
UTF8
MDTM
EPRT
SIZE
PBSZ
PROT
AUTH TLS
REST STREAM
211 End
257 "/" is current directory
221 Goodbye.
逐行解说:220 双行横幅来自 FTPBannerMessage 与服务器标识;331/230 是登录询问与成功;215 SYST 显示系统类型;211-End 之间为 FEAT 扩展能力表(EPSV/EPRT 扩展被动与主动、SIZE/MDTM 文件属性、PBSZ/PROT/AUTH TLS 为 FTPS 能力);257 显示当前目录;221 退出。FEAT 中出现 UTF8 说明 UTF-8 特性已默认启用(可用 FTPOptions NoUTF8Feature 隐藏)。
八、方案对比
| 维度 | 方案 A:apxs 动态模块(本文主线) | 方案 B:一键脚本(方案 A 固化) | 方案 C:叠加进 httpd 源码树静态编译 |
|---|---|---|---|
| 操作复杂度 | 中(需手工打 3 处补丁) | 低(一条脚本完成) | 高(需自行维护 httpd 源码构建) |
| 适用场景 | 单机快速部署、学习研究 | 批量装机、自动化流水线 | 对模块加载/性能有极致要求的定制发行 |
| 升级 httpd 影响 | 需重编 mod_ftp | 需重跑脚本 | 需整体重编 httpd |
| 模块形态 | mod_ftp.so 动态加载 | 同左 | 静态编入 httpd 二进制 |
| 与 apt httpd 兼容 | 完全兼容(实测 2.4.52) | 完全兼容 | 不兼容(替换发行版 httpd) |
| 实测结论 | 通过 | 通过 | 未采用(成本高收益低) |
解说:mod_ftp 源码 README 提供了 in-tree 静态编译路径(复制源码进 httpd 树后 buildconf/configure --enable-ftp),但对 Ubuntu 发行版 httpd 用户并不现实;动态 DSO 是唯一务实路径,因此方案 C 仅在表中列出供参考。
九、常见问题(FAQ)
Q1:configure.apxs 通过了,make 报 “macro names must be identifiers” 怎么办?
A:这是 configure.apxs 脚本自身的 bug,特性探测的 sed 表达式经 echo/eval 传递后反斜杠丢失,生成了 #define 1 这类残缺宏。按第四章 4.2 节手工重写 modules/ftp/ftp_config.h 的 7 个特性宏即可,或直接使用第五章脚本。
Q2:make 报 “'conn_rec' has no member named 'remote_ip'”?
A:httpd 2.4 已把 conn_rec::remote_ip 改名为 client_ip(remote_addr 同步改为 client_addr)。执行 sed -i 's/->remote_ip/->client_ip/g' ftp_commands.c ftp_data_connection.c ftp_limitlogin.c ftp_message.c 与 sed -i 's/c->remote_addr/c->client_addr/g' ftp_message.c 后重新 make。
Q3:编译安装全部成功,但客户端登录后一执行 USER 就断线(curl 报 56 response reading failed)?
A:这是本次实测最深的一个坑。mod_ftp 0.9.6 手工构造 request_rec 时未初始化新版本 httpd 引入的 trailers_in/trailers_out 字段,而 httpd 2.4.52 的 ap_set_sub_req_protocol 会无条件 apr_table_copy(r->trailers_in),空指针导致子进程段错误(内核日志可见 apache2[...]: segfault at 10 ... in libapr-1.so)。修补方法:在 modules/ftp/ftp_protocol.c 的 ftp_read_request() 中,r->notes = apr_table_make(r->pool, 5); 之后追加四行:
r->trailers_in = apr_table_make(r->pool, 5);
r->trailers_out = apr_table_make(r->pool, 5);
r->useragent_addr = c->client_addr;
r->useragent_ip = c->client_ip;
重新 make && make install 并重启 apache2。用 gdb 单进程复现(gdb --args /usr/sbin/apache2 -DFOREGROUND -X)可看到完整调用栈:apr_table_copy ← ap_set_sub_req_protocol ← ap_sub_req_method_uri ← ftp_run_handler(ftp_commands.c:156)。
Q4:apache2ctl configtest 提示 “module ftp_module is already loaded, skipping”? A:make install 时 apxs -a 已把 LoadModule 写进 apache2.conf,与 mods-enabled/ftp.load 重复。删除 apache2.conf 中由 apxs 注入的那行(注意它还是相对路径,即使不重复也无法加载),保留 mods-available/ftp.load 方式即可。
Q5:mod_ftp 现在还在维护吗?用在生产稳妥吗? A:必须如实说明:mod_ftp 仍为 beta,最新版 0.9.6-beta 发布于 2009 年 10 月(源码包时间戳 Oct 8 2009),多年未发新版。它可以工作(本文完整实测),但缺少持续的安全审计。生产环境若只是要 FTP,优先 vsftpd/proftpd;只有当业务强依赖 httpd 认证/日志/SSL 联动时才建议 mod_ftp,并自行跟踪 httpd 安全公告。
Q6:Listen 21 ftp 里的 “ftp” 协议名是什么意思?
A:httpd 2.2 起支持带协议参数的 Listen,为该端口选择协议处理链。mod_ftp 注册了 ftp 协议名,httpd 才会把 21 端口的连接交给 FTP 处理器而非 HTTP。同时建议配 AcceptFilter ftp none,因为 FTP 控制连接由服务器先发横幅,内核 accept 过滤等待数据会卡住握手。
Q7:登录正常但上传(STOR)失败、目录里没有文件?
A:检查 FTP 根目录属主。apache2 以 www-data 身份写文件,实测目录属主为 root 时 curl -T 静默失败。执行 chown -R www-data:www-data /srv/ftpdocs 后恢复正常(md5 校验一致)。若仍失败,看 /var/log/apache2/ftp_error.log 与系统权限位。
Q8:21 端口被占用或不想用默认端口怎么办?
A:把 ftpd.conf 中 Listen 21 ftp 与 VirtualHost default:21 的端口同步改为目标端口即可,例如 2121。make install 时也可用 make FTPPORT=8021 install 让示例配置直接按指定端口生成。
Q9:被动模式数据端口如何规划防火墙?
A:用 FTPPASVrange 指令限定范围(本文用 FTPPASVrange 50000 50100),防火墙放行 21 与 50000-50100 即可。不配置时 mod_ftp 使用默认被动端口策略,在云环境下容易出现数据连接被防火墙拦截、LIST 卡住的现象。
Q10:如何彻底卸载 mod_ftp? A:见下一节。
十、卸载
a2dismod ftp
a2disconf ftpd
rm -f /usr/lib/apache2/modules/mod_ftp.so /etc/apache2/mods-available/ftp.load /etc/apache2/conf-available/ftpd.conf /etc/apache2/ftpd.passwd
sed -i '/LoadModule ftp_module/d' /etc/apache2/apache2.conf
rm -rf /opt/httpd-ftp /usr/share/apache2/ftpdocs /etc/apache2/extra/ftpd.conf
apache2ctl configtest && systemctl restart apache2
回显:
Module ftp disabled.
Your MPM module, mpm_prefork, could not be found...
Syntax OK
说明:a2dismod 输出因系统而异,关键是最后 configtest 为 Syntax OK 且服务可启动。日志文件 /var/log/apache2/ftp_*.log 如需留证可自行备份后删除。
十一、参考链接
- mod_ftp 官方下载(含校验文件):https://dlcdn.apache.org/httpd/mod_ftp/
- mod_ftp 项目说明与英文手册:https://httpd.apache.org/mod_ftp/
- Apache httpd 2.4 文档:https://httpd.apache.org/docs/2.4/
- Ubuntu apache2 软件包页:https://launchpad.net/ubuntu/+source/apache2
- RFC 959(FTP 协议):https://www.rfc-editor.org/rfc/rfc959
- RFC 2428(EPSV/EPRT 扩展):https://www.rfc-editor.org/rfc/rfc2428