> For the complete documentation index, see [llms.txt](https://book.bsdcn.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://book.bsdcn.org/di-34-zhang-wen-jian-chuan-shu-xie-yi-ftp/di-34.3-jie-proftpd-ji-yu-mysql.md).

# 34.3 ProFTPD（基于 MySQL）

> **技巧**
>
> 通过 Windows 自带 FTP 客户端连接 ProFTPD 服务器时不会出现乱码。

## 安装 ProFTPD

> **注意**
>
> 本节基于 MySQL 8.0。有关 MySQL 的安装和基本设置，请参见其他章节。

请自行完成 MySQL 8.x 的安装配置，需确保其版本与 `proftpd-mod_sql_mysql` 安装的 `databases/mysql8X-client` 版本一致。

安装 ProFTPD 及其 MySQL 模块：

```sh
# pkg install proftpd proftpd-mod_sql_mysql
```

或使用 Ports 安装：

```sh
# cd /usr/ports/ftp/proftpd/ && make install clean
# cd /usr/ports/databases/proftpd-mod_sql_mysql/ && make install clean
```

## ProFTPD 配置文件

编辑 ProFTPD 的配置文件 **/usr/local/etc/proftpd.conf**：

> **技巧**
>
> 示例文件参见 **/usr/local/etc/proftpd.conf.sample**。

```ini
ServerName "Test Ftp Server"                     # FTP 服务器名称
ServerType standalone                             # 独立运行模式
DefaultServer on                                  # 设为默认服务器
ServerIdent on "FTP Server ready"                # 服务器标识信息
Port 21                                          # 监听端口
Umask 022                                        # 文件创建掩码

# 超时设置
TimeoutLogin 300                                 # 登录超时时间（秒）
TimeoutIdle 36000                                # 空闲超时时间（秒）
TimeoutNoTransfer 36000                          # 无传输超时时间（秒）

# 资源限制
User proftpd                                     # 运行服务的用户
Group proftpd                                    # 运行服务的用户组
RLimitMemory 256M 256M                           # 内存限制
RLimitOpenFiles 1024 1024                        # 打开文件数量限制
PassivePorts 50000 60000                          # 被动模式端口范围

# 日志配置
LogFormat default "%h %l %u %t \"%r\" %s %b"     # 默认日志格式
LogFormat auth "%v [%P] %h %t \"%r\" %s"         # 认证日志格式
SystemLog /var/log/proftpd/proftpd.log           # 系统日志路径
TransferLog /var/log/proftpd/xfer.log            # 文件传输日志路径
ExtendedLog /var/log/proftpd/auth.log AUTH auth  # 扩展认证日志

# MySQL 模块加载
LoadModule mod_sql.c
LoadModule mod_sql_mysql.c
LoadModule mod_sql_passwd.c

# 只允许访问各自的目录
DefaultRoot ~                                    # 将用户限制于主目录内

# 允许覆盖文件
AllowOverwrite on                                # 允许文件覆盖

<Global>
  # 数据库连接
  SQLConnectInfo proftpd@localhost proftpd <你的数据库密码>   # 数据库连接信息（用户、主机、密码）

  # 密码认证设置
  SQLAuthTypes SHA256                             # 使用 SHA256 加密认证
  SQLPasswordEngine on                            # 启用 SQL 密码引擎

  # 用户表映射
  SQLUserInfo users username password uid gid homedir shell   # 映射 users 表字段
  SQLDefaultGID 2000                              # 默认 GID
  SQLDefaultUID 2000                              # 默认 UID
  RequireValidShell off                            # 不要求有效 Shell

  # 认证顺序
  AuthOrder mod_sql.c
  SQLAuthenticate users                            # 使用 SQL 用户表认证

  # 登录统计
  SQLNamedQuery getcount SELECT "CONCAT('Total logins: ', count) FROM users WHERE username='%u'"   # 查询登录次数
  SQLNamedQuery updatecount UPDATE "count=count+1 WHERE username='%u'" users                       # 更新登录次数
  SQLShowInfo PASS "230" "%{getcount}"              # 显示登录统计信息
  SQLLog PASS updatecount                            # 记录登录统计

  # 文件操作日志
  SQLNamedQuery log_work FREEFORM "INSERT INTO worklog (user_name, file_and_path, bytes, send_time, client_ip, client_name, client_command) VALUES ('%u', '%f', %b, NULLIF('%T', ''), '%a', '%h', '%m')"  # 文件操作日志记录
  SQLLog RETR,STOR,DELE log_work                    # 将 RETR、STOR、DELE 操作记录到 worklog 表
</Global>
```

创建日志目录：

```sh
# mkdir /var/log/proftpd
```

> **警告**
>
> 如果未手动创建目录，系统将提示 `proftpd[3435]: warning: handling possibly truncated configuration data at line 65 of '/usr/local/etc/proftpd.conf'`。

> **技巧**
>
> ProFTPD 配置文件 **/usr/local/etc/proftpd.conf** 有语法检查命令 `proftpd -t -d5`。

本配置中，服务器主动模式使用端口 21，被动模式使用端口 50000-60000，须确保防火墙已放行上述端口。

对于 PF 防火墙，通过以下规则实现：

允许从外部接口 `$ext_if`（需要改为实际网络接口）到本机端口 21 及 50000-60000 的 TCP 入站流量：

```ini
pass in quick on $ext_if proto tcp from any to $ext_if port { 21, 50000:60000 }
```

## 相关文件结构

```sh
/usr/local/etc/
├── proftpd.conf # ProFTPD 主配置文件
└── proftpd.conf.sample # ProFTPD 配置示例文件
```

## 创建用户

出于安全考量，应以非 root 用户身份运行 ProFTPD。添加新用户：

```sh
# adduser
Username: proftpd # 用户名
Full name: FTP User # 用户全名
Uid (Leave empty for default):
Login group [proftpd]:
Login group is proftpd. Invite proftpd into other groups? []:
Login class [default]:
Shell (sh csh tcsh bash nologin) [sh]: nologin # 不允许登录系统
Home directory [/home/proftpd]:
Home directory permissions (Leave empty for default):
Enable ZFS encryption? (yes/no) [no]:
Use password-based authentication? [yes]: no
Lock out the account after creation? [no]:
Username    : proftpd
Password    : <disabled>
Full Name   : FTP User
Uid         : 1002
ZFS dataset : zroot/home/proftpd
Class       :
Groups      : proftpd
Home        : /home/proftpd
Home Mode   :
Shell       : /usr/sbin/nologin
Locked      : no
adduser: INFO: Successfully created ZFS dataset (zroot/home/proftpd).
adduser: INFO: Successfully added (proftpd) to the user database.
Add another user? (yes/no) [no]:
Goodbye!
```

## MySQL 数据库相关

创建 MySQL 数据库和用户：

```sql
CREATE DATABASE `proftpd` CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;	 -- 创建 proftpd 数据库，使用 utf8mb4 字符集和通用校对规则
```

创建数据库用户和密码：

```sql
CREATE USER 'proftpd'@'localhost' IDENTIFIED BY '<你的数据库密码>';   -- 创建 MySQL 用户 proftpd，仅允许从本地主机连接，请替换为实际密码
GRANT SELECT, INSERT, UPDATE, DELETE ON proftpd.* TO 'proftpd'@'localhost';   -- 授予 proftpd 用户对 proftpd 数据库的增删改查权限
FLUSH PRIVILEGES;   -- 刷新权限，使配置立即生效
```

创建数据表：

```sql
USE proftpd;   -- 选择 proftpd 数据库

-- 创建 users 表
DROP TABLE IF EXISTS users;
CREATE TABLE `users` (
   `username` VARCHAR(30) NOT NULL,   -- 用户名
   `descr` TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,   -- 用户描述
   `password` VARCHAR(64) NOT NULL,   -- 用户密码（SHA-256 加密）
   `uid` INT(11) DEFAULT NULL,        -- 用户 UID
   `gid` INT(11) DEFAULT NULL,        -- 用户 GID
   `homedir` VARCHAR(255) DEFAULT NULL,   -- 用户主目录
   `shell` VARCHAR(255) DEFAULT NULL,     -- 用户 Shell
   `count` INT(11) NOT NULL DEFAULT 0,    -- 登录次数统计
   PRIMARY KEY (`username`)                 -- 主键为 username
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

-- 创建 worklog 表
DROP TABLE IF EXISTS worklog;
CREATE TABLE worklog (
   id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,   -- 日志 ID，自增主键
   date TIMESTAMP(0) DEFAULT CURRENT_TIMESTAMP(0),   -- 操作时间
   user_name VARCHAR(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL,  -- 用户名
   file_and_path VARCHAR(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL,  -- 文件及路径
   bytes BIGINT UNSIGNED DEFAULT NULL,   -- 文件大小
   send_time VARCHAR(9) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL,  -- 文件传输时间
   client_ip VARCHAR(15) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL,  -- 客户端 IP
   client_name TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL,        -- 客户端名称
   client_command VARCHAR(5) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL,  -- 客户端命令
   PRIMARY KEY (id),
   UNIQUE INDEX id (id)
) ENGINE=InnoDB CHARACTER SET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC;

-- 插入测试用户
INSERT INTO `proftpd`.`users` (`username`, `descr`, `password`, `uid`, `gid`, `homedir`, `shell`, `count`)
VALUES ('test', 'Test user', SHA2('FTPpassword_here', 256), '1002', '1002', '/home/www/ftp', NULL, '0');   -- 添加测试用户记录
```

已创建以下内容：

| 项目       | 值                  |
| -------- | ------------------ |
| FTP 用户名  | `test`             |
| FTP 登录密码 | `FTPpassword_here` |
| UID      | `1002`             |
| GID      | `1002`             |

> **警告**
>
> 上面的 UID 和 GID 必须与 proftpd 用户相同，否则仅有读取权限而无写入权限。
>
> 可以通过以下方式获取用户 proftpd 的 UID、GID 及所属组信息：
>
> ```sh
> # id proftpd
> uid=1002(proftpd) gid=1002(proftpd) groups=1002(proftpd)
> ```

测试数据库连接：

```sh
# mysql -u proftpd -p -h localhost proftpd	# 使用 proftpd 用户连接本地主机上的 proftpd 数据库，系统提示输入密码

proftpd@localhost [proftpd]> show databases;   -- 显示 MySQL 服务器上的所有数据库列表
+--------------------+
| Database           |
+--------------------+
| information_schema |
| performance_schema |
| proftpd            |
+--------------------+
3 rows in set (0.00 sec)

proftpd@localhost [proftpd]> use proftpd;   -- 选择 proftpd 数据库作为当前操作的数据库
Database changed
proftpd@localhost [proftpd]> select * from users;   -- 查询 users 表中的所有记录
+----------+-----------+------------------------------------------------------------------+------+------+---------------+-------+-------+
| username | descr     | password                                                         | uid  | gid  | homedir       | shell | count |
+----------+-----------+------------------------------------------------------------------+------+------+---------------+-------+-------+
| test     | Test user | d1d6930fda5f964acba51ec4c35d0ddb3b36d25bfef59f1120abd2e4f9f140d9 | 1002 | 1002 | /home/www/ftp | NULL  |     0 |
+----------+-----------+------------------------------------------------------------------+------+------+---------------+-------+-------+
1 row in set (0.00 sec)
```

创建目录和测试 FTP 用户：

```sh
# mkdir -p /home/www/ftp                  # 创建 FTP 用户的主目录及子目录
# chown -R proftpd:proftpd /home/www/ftp # 设置目录所有者和所属组为 proftpd
# chmod -R 775 /home/www/ftp             # 设置目录权限为 775，允许同组用户读写
```

## 服务操作

配置完成后，启动并管理 ProFTPD 服务：

```sh
# service proftpd enable    # 设置 ProFTPD 服务开机启动
# service proftpd start     # 启动 ProFTPD 服务
# service proftpd stop      # 停止 ProFTPD 服务
# service proftpd restart   # 重启 ProFTPD 服务
```

以用户名 `test`、密码 `FTPpassword_here` 登录 FTP。


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://book.bsdcn.org/di-34-zhang-wen-jian-chuan-shu-xie-yi-ftp/di-34.3-jie-proftpd-ji-yu-mysql.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
