Apache mod_ftp 0.9.6 配置参数详解
系统梳理 Apache mod_ftp 0.9.6-beta 全部 26 个配置指令与 FTPOptions 14 个子选项,含默认值、生效验证与生产模板。
Apache mod_ftp 0.9.6 配置参数详解
目标:逐项讲清 Apache mod_ftp 0.9.6-beta 的全部配置指令(含义、默认值、允许的上下文),并在 Ubuntu 22.04 + Apache 2.4.52 环境中对关键参数逐一实测验证,最后给出可直接落地的生产推荐模板。已在 Ubuntu 22.04 实测通过。
一、环境信息
| 项目 | 内容 |
|---|---|
| 操作系统 | Ubuntu 22.04.5 LTS |
| 系统架构 | x86_64 |
| httpd 版本 | Apache/2.4.52(apt 发行包,MPM:event/prefork 均实测) |
| mod_ftp 版本 | 0.9.6-beta(模块安装路径 /usr/lib/apache2/modules/mod_ftp.so) |
| 源码目录 | /opt/httpd-ftp |
| 指令清单来源 | /opt/httpd-ftp/modules/ftp/mod_ftp.c 中 ftp_cmds[] 指令注册表 |
| 配置文件 | /etc/apache2/conf-available/ftpd.conf |
| 示例账号 | ftpuser / q1w2e3r4(htpasswd 文件认证) |
再次强调:mod_ftp 0.9.6-beta 发布于 2009 年 10 月且至今仍是 beta、多年未发新版,本文所有默认值均从源码指令注册表与实测行为两方面核对,凡实测与官方文档不一致处均已注明。
二、配置体系与加载链
mod_ftp 作为 httpd 模块,配置完全融入 httpd 配置体系。Ubuntu apache2 的加载链为:
/etc/apache2/apache2.conf
├─ IncludeOptional mods-enabled/*.load ← ftp.load: LoadModule ftp_module /usr/lib/apache2/modules/mod_ftp.so
├─ IncludeOptional mods-enabled/*.conf
├─ IncludeOptional conf-enabled/*.conf ← ftpd.conf: FTP 站点与指令
└─ IncludeOptional sites-enabled/*.conf
解说:mod_ftp 的模块注册放 mods-enabled(a2enmod ftp 生成),站点指令放 conf-enabled(a2enconf ftpd 生成)。指令分为两类上下文:多数 FTP 指令为 server config / virtual host 级(源码标注 RSRC_CONF),FTPUmask/FTPDirUmask 可用于 Directory 与 .htaccess(OR_FILEINFO),FTPReadmeMessage 任意位置(OR_ALL)。
启动后从错误日志可直接确认模块版本:
[Wed Oct 14 17:47:36.347380 2026] [mpm_prefork:notice] [pid 7455] AH00163: Apache/2.4.52 (Ubuntu) mod_ftp/0.9.6 configured -- resuming normal operations
三、端口监听与协议启用
这三个指令是 FTP 服务能“接通”的前提,放在站点配置最顶部:
Listen 21 ftp
AcceptFilter ftp none
<VirtualHost _default_:21>
FTP On
</VirtualHost>
| 指令 | 说明 |
|---|---|
| Listen 21 ftp | 监听 21 端口并标记为 ftp 协议;协议标记决定该端口连接走 FTP 处理链 |
| AcceptFilter ftp none | 关闭该协议的内核 accept 过滤;FTP 由服务器先发 220 横幅,不过滤才能正常握手 |
| FTP On | 在当前虚拟主机启用 FTP 服务(默认 Off),是 mod_ftp 的总开关 |
逐项解说:Listen 的第二个参数是协议名,mod_ftp 注册了 "ftp" 协议名;若写成 Listen 21 不带协议名,端口虽开但按 HTTP 处理,客户端会收到 HTTP 响应。AcceptFilter 取 none 是官方示例配置的原样写法。FTP 只能在虚拟主机/服务器级配置,不能放进 Directory。
四、指令详解
以下 26 个指令来自源码 ftp_cmds[] 注册表,按功能分组。所有“实测”一栏均在本文环境验证。
4.1 横幅与消息类
| 指令 | 取值 | 默认 | 说明 |
|---|---|---|---|
| FTPBannerMessage | 字符串 | 系统默认横幅 | 登录后 220 横幅,支持 %-变量 |
| FTPExitMessage | 字符串 | 无 | QUIT 时 221 附加消息 |
| FTPReadmeMessage | 字符串 | 无 | 按目录设置的 README 提示消息 |
| FTPDocRootEnv | 环境变量名 | 无 | 依据环境变量(如 LDAP 属性)动态设定 DocumentRoot |
| FTPHomeDir | 路径 | 无 | 用户主目录所在父目录,配合 FTPJailUser 使用 |
| FTPJailUser | On/Off | Off | 禁止用户离开其主目录 |
实测:本文配置 FTPBannerMessage "Welcome to Apache mod_ftp (Ubuntu 22.04)" 后,netcat 登录横幅第一行为:
220-Welcome to Apache mod_ftp (Ubuntu 22.04)
220 node1.modftp.example FTP Server (Apache/2.4.52 (Ubuntu)) ready.
解说:横幅支持 %T 时间、%a 客户端 IP、%u 用户名等变量(与 FTP 日志变量同源,源码 ftp_message.c 的 ftp_message_generate())。多行消息用 220- 前缀逐行发出,最后一行 220 结束。
4.2 超时与登录限制类
| 指令 | 取值 | 默认 | 说明 |
|---|---|---|---|
| FTPTimeoutLogin | 秒 | 120 | 发出 USER 前允许的等待时间 |
| FTPTimeoutIdle | 秒 | 600 | 两条命令间最大空闲时间,超时断开 |
| FTPTimeoutData | 秒 | 300 | 数据连接建立/传输超时 |
| FTPMaxLoginAttempts | 次数 | 3 | 密码错误最大尝试次数,超限断开 |
| FTPLimitLoginUser | 数字 | 无 | 单用户并发登录上限 |
| FTPLimitLoginIP | 数字 | 无 | 单 IP 并发登录上限 |
| FTPLimitLoginServer | 数字 | 无 | 服务器总并发登录上限 |
| FTPLimitDBFile | 路径 | 无 | 并发限制计数 DBM 文件位置 |
| FTPDataBlockSize | 字节数 | 32768 | 数据传输块大小 |
实测:本文按官方推荐设置 FTPTimeoutLogin 120 / FTPTimeoutIdle 600 / FTPTimeoutData 300 / FTPMaxLoginAttempts 3,正常登录与传输不受影响。FTPLimitLogin* 三件套需同时配置 FTPLimitDBFile 才持久生效,DBM 文件目录需对 www-data 可写;未配置时指令不生效(源码在无 DB 文件时直接返回成功)。
4.3 数据连接类(主动/被动模式核心)
💡 技术拓展:再次对照两种数据通道。主动模式(PORT/EPRT):客户端把自己的地址端口通过 PORT 告诉服务器,服务器发起数据连接——服务端出站端口可由 FTPActiveRange 限定;被动模式(PASV/EPSV):服务器开数据端口等客户端来连——监听范围由 FTPPASVrange 限定。防火墙友好度上被动模式占优,是当前主流;mod_ftp 两种模式同时支持,实测均通过。本文配置以被动模式为主、FTPActiveRange 作为主动模式加固。
| 指令 | 取值 | 默认 | 说明 |
|---|---|---|---|
| FTPPASVrange | 最小端口 最大端口 | 随机高端口 | 被动模式数据端口监听范围 |
| FTPPASVaddr | IP | 自动探测 | 响应 PASV 时通告的地址(多 IP/NAT 场景) |
| FTPPASVbindaddr | IP | 全部地址 | 被动数据端口实际绑定的地址 |
| FTPActiveRange | 最小 [最大] | 随机 | 主动模式服务器出站端口范围 |
| FTPEPSVIgnoreFamily | On/Off | Off | EPSV 忽略客户端地址族(NAT/翻译环境) |
| FTPLowPortSock | 套接字名 | 无 | 低端口号数据连接辅助守护进程的套接字 |
实测一:配置 FTPPASVrange 50000 50100 后用原始会话验证 PASV 响应端口:
printf 'USER ftpuser\r\nPASS q1w2e3r4\r\nPASV\r\nQUIT\r\n' | nc 127.0.0.1 21
回显(输出节选):
230 User ftpuser logged in
227 Entering Passive Mode (127,0,0,1,195,80)
解说:227 响应按 h1,h2,h3,h4,p1,p2 编码端口,实际端口 = p1×256 + p2 = 195×256 + 80 = 50000,正好落在配置区间下界,指令生效。
实测二:FTPLowPortSock 启动时会在错误日志打印守护进程就绪信息:
[Wed Oct 14 17:47:36.345010 2026] [:notice] [pid 7456] FTP low numbered port daemon waiting for port requests
解说:mod_ftp 通过一个独立的低端口辅助进程(源码 ftp_lowportd.c)以 root 权限创建 20 端口等低端口数据连接套接字,worker 进程按需取用;这是 Apache 以非 root 运行时仍能做主动模式 20 端口连接的机制。
4.4 文件属性类
| 指令 | 取值 | 默认 | 说明 |
|---|---|---|---|
| FTPUmask | 八进制 | 0022 | 新建文件的权限掩码 |
| FTPDirUmask | 八进制 | 0022 | 新建目录的权限掩码 |
实测:FTPUmask 022 下上传的文件权限为 644(rw-r--r--),与 7.1 节 lftp 列表中 -rw-r--r-- 一致。这两个指令可用于 Directory 块,实现不同目录不同掩码。
4.5 FTPImplicitSSL 与 FTPOptions
FTPImplicitSSL(On/Off,默认 Off):启用隐式 FTPS——控制连接建立即进入 TLS 握手(经典 990 端口风格)。FEAT 能力表中出现的 AUTH TLS 属于显式 FTPS(客户端发 AUTH TLS 命令升级连接),两者不同:显式方式由 mod_ssl 提供、客户端主动升级;隐式方式由本指令声明。启用前需先为虚拟主机配置 mod_ssl(SSLEngine on 与证书),官方示例配置中给出了完整写法。
FTPOptions(RAW_ARGS,可写多个子选项)是 mod_ftp 的行为开关集,源码 mod_ftp.c 的 ftp_options() 定义了 14 个子选项,逐项说明如下表:
| 子选项 | 默认 | 说明 |
|---|---|---|
| RequireSSL | 关 | 要求客户端在 USER/PASS 前完成 AUTH TLS |
| CheckMaxClients | 关 | 按 httpd 并发能力检查服务器是否已满 |
| RemoveUserGroup | 关 | 列表中隐藏真实 user/group 归属 |
| NLSTShowDirs | 关 | NLST 输出中包含目录 |
| NLSTIsLIST | 关 | 把 NLST 直接映射为 LIST(含权限列) |
| LISTIsNLST | 关 | 把 LIST 直接映射为 NLST(仅文件名) |
| CreateHomeDirs | 关 | 登录时主目录不存在则自动创建(需配 FTPHomeDir) |
| ShowUnAuthorizedFiles | 关 | LIST 中显示无权限文件(默认隐藏) |
| AllowProxyPORT | 关 | 允许主动模式数据连接来自非客户端 IP |
| AllowProxyPASV | 关 | 允许被动模式数据连接来自非客户端 IP |
| VirtualHostByUser | 关 | 按 user@hostname 解析虚拟主机 |
| StripHostname | 关 | 从 user@hostname 中剥离主机名部分 |
| NoUTF8Feature | 关 | 从 FEAT 能力表隐藏 UTF8 |
实测:本文实测 FTPOptions NoUTF8Feature,重启后 FEAT 能力表仍显示 UTF8(回显如下)——该子选项在 0.9.6-beta 对 httpd 2.4.52 下未生效,疑为模块缺陷,仅作现象记录,不影响传输正确性:
211-Extensions supported
EPSV
TVFS
UTF8
MDTM
EPRT
SIZE
PBSZ
PROT
AUTH TLS
REST STREAM
211 End
解说:AllowProxyPASV/AllowProxyPORT 默认关闭是安全设计——被动数据连接只接受与控制连接相同来源 IP 的客户端(源码 ftp_data_connection.c 校验),防止他人抢占数据端口(FTP Bounce 攻击面之一)。公网部署建议保持默认关闭。
五、认证与访问控制
mod_ftp 登录认证挂在 DocumentRoot 对应的 Directory 块上(登录动作等价于对根目录做一次权限检查)。本文实测配置:
<Directory "/srv/ftpdocs">
AuthType Basic
AuthName "FTP Authentication"
AuthUserFile "/etc/apache2/ftpd.passwd"
require valid-user
</Directory>
逐项解说:AuthType Basic 指定 HTTP Basic 机制(FTP 的 USER/PASS 被映射为 Basic 凭据);AuthUserFile 指向 htpasswd 生成的口令文件;require valid-user 表示任一文件内用户均可登录。生成口令文件的命令与回显:
htpasswd -cb /etc/apache2/ftpd.passwd ftpuser q1w2e3r4
Adding password for user ftpuser
需要控制“谁能写”时,用 httpd 的 Limit 指令包裹写命令。官方示例的思路(限定仅 incoming 可写):
<Directory "/srv/ftpdocs">
<Limit MKD RMD STOR DELE RNFR>
Order deny,allow
Deny from all
</Limit>
</Directory>
<Directory "/srv/ftpdocs/incoming">
<LimitExcept MKD RMD STOR DELE RNFR>
Allow from all
</LimitExcept>
</Directory>
解说:mod_ftp 把 STOR(上传)、DELE(删除)、MKD/RMD(建/删目录)、RNFR/RNTO(改名)等 FTP 命令映射为可被 Limit 管控的方法名,因此 httpd 的授权语法可直接约束 FTP 行为。匿名访问可改用 AuthBasicProvider anon 加 mod_authn_anon(若发行包提供),本文以文件认证为准。
六、日志参数详解
mod_ftp 定义了两类专用日志:命令日志(记录每条 FTP 命令)与传输日志(记录每次文件传输)。
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
CustomLog /var/log/apache2/ftp_command.log command_log_fmt
CustomLog /var/log/apache2/ftp_transfer.log transfer_log_fmt
命令日志实测内容:
ftpuser [127.0.0.1] TYPE A
ftpuser [127.0.0.1] LIST
ftpuser [127.0.0.1] QUIT
传输日志实测内容(13 列):
Oct 14 17:02:24 2026 0 127.0.0.1 0 / a _ - r ftpuser ftp 0 *
传输日志字段逐列解说:
| 列 | 内容 | 实测值 | 含义 |
|---|---|---|---|
| 1 | %t 自定义时间 | Oct 14 17:02:24 2026 | 传输时间 |
| 2 | %T | 0 | 传输耗时(秒) |
| 3 | %a | 127.0.0.1 | 客户端地址 |
| 4 | %B | 0 | 字节数 |
| 5 | %U | / | 文件名 |
| 6 | %M | a | 类型:a=ASCII b=BINARY |
| 7 | %F | _ | 压缩标记:C/U/T/_ |
| 8 | %d | - | 方向:o=下载 i=上传 |
| 9 | %W | r | 访问类别:r=真实用户 a=匿名 g=来宾 |
| 10 | %u | ftpuser | 用户名 |
| 11 | %S | ftp | 服务名 |
| 12 | %Z | 0 | 认证方式位掩码 |
| 13 | %Y | * | 认证用户 ID |
注意:LogFormat 别名必须先定义再被 CustomLog 引用,若漏定义,日志文件会写入字面量 command_log_fmt(实测踩坑)。两条日志可用 SetEnvIf 条件开关(示例配置中提供了按匿名用户记录命令日志的模板)。
七、生产推荐模板
# /etc/apache2/conf-available/ftpd.conf —— 生产推荐模板
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 "FTP service ready"
# 会话与传输超时
FTPTimeoutLogin 60
FTPTimeoutIdle 300
FTPTimeoutData 300
FTPMaxLoginAttempts 3
# 文件/目录权限掩码
FTPUmask 027
FTPDirUmask 027
# 被动端口区间(同步放行防火墙)
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>
模板要点逐项说明:横幅去版本信息;登录超时收紧到 60 秒;掩码 027 使文件 640/目录 750,组内可协作、其他不可读;PASV 端口 100 个,匹配中等并发;如需 SSL,追加 SSLEngine、证书三件与 FTPOptions RequireSSL。模板中示例账号 ftpuser/q1w2e3r4 仅用于演示,生产请通过 htpasswd 生成独立口令。
八、常见问题(FAQ)
Q1:FTPPASVaddr 什么时候必须配? A:当服务器在 NAT/负载均衡之后、对外地址与网卡地址不一致时。PASV 响应默认通告连接进入的本机地址,客户端在公网会拿到内网地址导致数据连接失败。把对外 IP 写进 FTPPASVaddr 即可,本文内网实测无需配置。
Q2:FTPTimeoutIdle 设多大合适? A:默认 600 秒平衡了体验与资源占用。若客户端使用图形工具长时间挂机,可放宽到 1800;面向公网建议收紧到 300 以减少空置连接。超时断开后客户端报 "connection closed by remote host"。
Q3:FTPLimitLoginUser/IP/Server 配了却不生效? A:三个限制指令依赖 FTPLimitDBFile 指定的 DBM 计数文件。未配置 DB 文件时限制不生效;配置后还需保证目录对 www-data 可写,并确认加载了 mod_dbm 依赖(Ubuntu 默认已含)。
Q4:FTPOptions 可以写多个子选项吗?分隔符是什么?
A:可以,FTPOptions 是 RAW_ARGS 指令,空格分隔即可,例如 FTPOptions RemoveUserGroup NLSTShowDirs。全部 14 个子选项见 4.5 节表格。
Q5:为什么我的 FEAT 里没有 AUTH TLS? A:AUTH TLS/PBSZ/PROT 出现在 FEAT 的前提是虚拟主机启用了 mod_ssl。只设 FTPImplicitSSL 而不装证书,客户端发起 TLS 升级仍会失败。本文实测 FEAT 显示 AUTH TLS 是因为虚拟主机具备 SSL 能力的模块注册,但未配证书时不应生产启用。
Q6:FTPJailUser 要配 FTPHomeDir 吗? A:是。FTPJailUser Off 时用户以 DocumentRoot 为根;On 时用户进入 FTPHomeDir/用户名 作为家目录并被禁越界。两者必须成对出现,且目录需预先存在或配合 FTPOptions CreateHomeDirs 自动创建。
Q7:FTPUmask 与系统 umask 的关系? A:FTPUmask 只作用于 FTP 新建文件/目录(配合 mod_ftp 的写入路径),不影响 httpd 其他行为。实测 022 上传文件为 644;改为 027 则为 640。指令允许 Directory 级覆写,可对 incoming 目录用更宽松掩码。
Q8:NoUTF8Feature 配置后 FEAT 仍显示 UTF8,是指令无效吗? A:实测确实如此(见 4.5 节回显),0.9.6-beta 在 httpd 2.4.52 上该子选项未生效,属上游缺陷。UTF8 能力仅是声明,传输内容仍按字节流处理,一般无需处理;如必须隐藏需改源码重编。
Q9:能在一台 httpd 上跑多个 FTP 站点吗? A:可以。FTP 端口绑定在 VirtualHost 上,配多个不同端口的虚拟主机(21、2121...)各挂 DocumentRoot 与认证即可;同端口区分用户再分站可尝试 FTPOptions VirtualHostByUser + StripHostname(按 user@host 解析),本文未实测该组合,建议先在测试环境验证。
Q10:指令写错位置为何 configtest 直接报错? A:mod_ftp 指令有上下文限制(多数 RSRC_CONF)。放进 Directory 会得到 "FTP not allowed here" 类错误。速查上下文见第四章各表;FTPUmask/FTPDirUmask/FTPReadmeMessage 是仅有的三个可进 Directory/.htaccess 的 FTP 指令。
九、参考链接
- mod_ftp 英文指令手册(源码自带):https://httpd.apache.org/mod_ftp/docs/
- 源码指令注册表:https://dlcdn.apache.org/httpd/mod_ftp/mod_ftp-0.9.6-beta.tar.gz(modules/ftp/mod_ftp.c)
- Apache httpd 2.4 核心指令:https://httpd.apache.org/docs/2.4/mod/core.html
- httpd 认证鉴权指南:https://httpd.apache.org/docs/2.4/howto/auth.html
- mod_ssl 文档:https://httpd.apache.org/docs/2.4/mod/mod_ssl.html
- RFC 959 / RFC 2228(FTPS 安全扩展):https://www.rfc-editor.org/rfc/rfc959 ;https://www.rfc-editor.org/rfc/rfc2228