SMTP 服务器搭建完全指南 / 第 4 章:SASL 认证与 Dovecot 集成
第 4 章:SASL 认证与 Dovecot 集成
没有认证的 SMTP 服务器就像没有门锁的银行——任何人都能进来。
4.1 SASL 认证概述
4.1.1 什么是 SASL
SASL(Simple Authentication and Security Layer)是一种框架协议(RFC 4422),为应用层协议提供认证机制。SMTP 通过 SASL 扩展(RFC 4954)实现用户身份验证。
客户端 Postfix SMTP
│ │
│──── EHLO client ─────────────────────►│
│◄─── 250-AUTH PLAIN LOGIN ────────────│
│ │
│──── AUTH PLAIN AHVzZXIAcGFzc3dvcmQ=──►│
│◄─── 235 Authentication successful ────│
│ │
│──── MAIL FROM:<[email protected]> ────►│
│ (已认证,允许中继) │
4.1.2 SASL 认证机制
| 机制 | 安全性 | 说明 |
|---|---|---|
| PLAIN | 低(需 TLS) | 用户名和密码 Base64 编码传输 |
| LOGIN | 低(需 TLS) | 类似 PLAIN,兼容旧客户端 |
| CRAM-MD5 | 中 | 挑战-响应机制,不传输明文密码 |
| DIGEST-MD5 | 中 | 类似 CRAM-MD5,更安全 |
| SCRAM-SHA-256 | 高 | 现代挑战-响应机制 |
| EXTERNAL | 高 | 基于 TLS 客户端证书认证 |
💡 推荐:在生产环境中,始终使用 TLS 加密后再进行 PLAIN/LOGIN 认证。
4.1.3 Postfix SASL 后端
| 后端 | 说明 | 推荐场景 |
|---|---|---|
| Dovecot SASL | 集成 Dovecot 认证 | 生产环境首选 |
| Cyrus SASL (saslauthd) | 独立认证守护进程 | 简单场景 |
| Postfix 内置 | 使用 smtpd_sasl_passwd 文件 | 仅测试环境 |
4.2 安装 Dovecot
4.2.1 安装软件包
# Ubuntu / Debian
sudo apt install -y dovecot-core dovecot-imapd dovecot-pop3d dovecot-lmtpd
# RHEL / Rocky Linux
sudo dnf install -y dovecot
4.2.2 验证安装
# 检查版本
dovecot --version
# 检查服务状态
sudo systemctl status dovecot
# 检查默认配置
doveconf -n
4.3 配置 Dovecot 认证
4.3.1 Dovecot 主配置
# /etc/dovecot/dovecot.conf — 主配置
# 监听协议
protocols = imap pop3 lmtp
# 监听地址
listen = *, ::
4.3.2 认证配置
# /etc/dovecot/conf.d/10-auth.conf
# 禁用明文认证(非 TLS 连接时)
disable_plaintext_auth = yes
# 认证机制
auth_mechanisms = plain login
# 使用系统用户认证
!include auth-system.conf.ext
# 或使用密码文件认证(虚拟用户)
# !include auth-passwdfile.conf.ext
4.3.3 系统用户认证
# /etc/dovecot/conf.d/10-auth.conf
# 使用系统用户和密码
# 默认配置即可
# 认证由 PAM 或 shadow 完成
# 测试用户是否存在
id mailuser
getent passwd mailuser
4.3.4 虚拟用户认证(推荐生产环境)
# /etc/dovecot/conf.d/auth-passwdfile.conf.ext
passdb {
driver = passwd-file
args = /etc/dovecot/users
}
userdb {
driver = passwd-file
args = /etc/dovecot/users
}
创建虚拟用户密码文件:
# 生成密码哈希
doveadm pw -s SHA512-CRYPT -p "mypassword"
# 输出: {SHA512-CRYPT}$6$...
# /etc/dovecot/users — 虚拟用户列表
# 格式: user:{password_scheme}password:uid:gid::home
[email protected]:{SHA512-CRYPT}$6$xxxxx...:5000:5000::/var/mail/vhosts/example.com/admin
[email protected]:{SHA512-CRYPT}$6$yyyyy...:5000:5000::/var/mail/vhosts/example.com/user1
[email protected]:{SHA512-CRYPT}$6$zzzzz...:5000:5000::/var/mail/vhosts/example.com/user2
4.3.5 集成 MySQL/MariaDB(大规模用户管理)
# 安装 MySQL 驱动
sudo apt install -y dovecot-mysql
# /etc/dovecot/conf.d/auth-sql.conf.ext
passdb {
driver = sql
args = /etc/dovecot/dovecot-sql.conf.ext
}
userdb {
driver = sql
args = /etc/dovecot/dovecot-sql.conf.ext
}
# /etc/dovecot/dovecot-sql.conf.ext
driver = mysql
connect = host=127.0.0.1 dbname=mail user=mailadmin password=secret
default_pass_scheme = SHA512-CRYPT
password_query = SELECT \
username AS user, \
password, \
CONCAT('/var/mail/vhosts/', maildir) AS userdb_home, \
5000 AS userdb_uid, \
5000 AS userdb_gid, \
CONCAT('*:bytes=', quota) AS userdb_quota_rule \
FROM users \
WHERE username = '%u' AND active = '1'
user_query = SELECT \
CONCAT('/var/mail/vhosts/', maildir) AS home, \
5000 AS uid, \
5000 AS gid, \
CONCAT('*:bytes=', quota) AS quota_rule \
FROM users \
WHERE username = '%u'
创建数据库:
-- 创建数据库
CREATE DATABASE mail CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- 创建用户表
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(255) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL,
maildir VARCHAR(255) NOT NULL,
quota BIGINT DEFAULT 1073741824, -- 1GB
active TINYINT(1) DEFAULT 1,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_username (username),
INDEX idx_active (active)
) ENGINE=InnoDB;
-- 创建别名表
CREATE TABLE aliases (
id INT AUTO_INCREMENT PRIMARY KEY,
source VARCHAR(255) NOT NULL,
destination VARCHAR(255) NOT NULL,
active TINYINT(1) DEFAULT 1,
INDEX idx_source (source),
INDEX idx_active (active)
) ENGINE=InnoDB;
-- 插入测试用户
INSERT INTO users (username, password, maildir) VALUES
('[email protected]', '{SHA512-CRYPT}$6$xxxx...', 'example.com/admin/'),
('[email protected]', '{SHA512-CRYPT}$6$yyyy...', 'example.com/user1/');
-- 创建数据库用户
CREATE USER 'mailadmin'@'localhost' IDENTIFIED BY 'secret';
GRANT SELECT ON mail.* TO 'mailadmin'@'localhost';
FLUSH PRIVILEGES;
4.4 配置 Dovecot 邮箱存储
4.3.1 Maildir 配置
# /etc/dovecot/conf.d/10-mail.conf
# 邮箱格式(推荐 Maildir)
mail_location = maildir:~/Maildir
# 虚拟用户邮箱路径
mail_location = maildir:/var/mail/vhosts/%d/%n
# 邮箱目录权限
mail_uid = 5000
mail_gid = 5000
mail_privileged_group = vmail
# 第一个有效 UID
first_valid_uid = 5000
last_valid_uid = 5000
# 邮箱目录自动创建
maildir_stat_dirs = yes
4.3.2 创建邮箱目录
# 创建 vmail 用户
sudo groupadd -g 5000 vmail
sudo useradd -g vmail -u 5000 -d /var/mail -s /usr/sbin/nologin vmail
# 创建邮箱目录
sudo mkdir -p /var/mail/vhosts/example.com
sudo chown -R vmail:vmail /var/mail/vhosts
sudo chmod -R 770 /var/mail/vhosts
4.5 配置 Postfix 使用 Dovecot SASL
4.5.1 Postfix SASL 配置
# /etc/postfix/main.cf — SASL 相关配置
# 启用 SASL 认证
smtpd_sasl_auth_enable = yes
# SASL 类型(dovecot)
smtpd_sasl_type = dovecot
# SASL 路径
smtpd_sasl_path = private/auth
# SASL 安全选项
smtpd_sasl_security_options = noanonymous, noplaintext
# 已认证用户不需要再检查发件人
smtpd_sasl_authenticated_header = yes
# 强制使用 TLS 后才允许认证
smtpd_tls_auth_only = yes
4.5.2 Dovecot SASL Socket 配置
# /etc/dovecot/conf.d/10-master.conf
service auth {
# Postfix SASL socket
unix_listener /var/spool/postfix/private/auth {
mode = 0660
user = postfix
group = postfix
}
}
4.5.3 Postfix 中继限制更新
# /etc/postfix/main.cf
smtpd_relay_restrictions =
permit_mynetworks,
permit_sasl_authenticated,
reject_unauth_destination
# 端口 587 的提交限制(在 master.cf 中)
# submission inet n - y - - smtpd
# -o smtpd_relay_restrictions=permit_sasl_authenticated,reject
4.6 启动并测试
4.6.1 启动服务
# 启动 Dovecot
sudo systemctl enable --now dovecot
# 重新加载 Postfix
sudo systemctl reload postfix
# 检查 Dovecot 日志
sudo tail -f /var/log/mail.log | grep -i dovecot
4.6.2 测试 SASL 认证
# 使用 swaks 测试 SMTP 认证
sudo apt install swaks
swaks --to [email protected] \
--from [email protected] \
--server mail.example.com \
--port 587 \
--auth-user [email protected] \
--auth-password "mypassword" \
-tls
# 使用 openssl 测试
openssl s_client -starttls smtp -connect mail.example.com:587
# 在交互界面输入:
# EHLO localhost
# AUTH PLAIN AHVzZXIAcGFzc3dvcmQ=
# (Base64 编码的 \0user\0password)
4.6.3 Base64 认证字符串生成
# 生成 AUTH PLAIN 的 Base64 字符串
# 格式: \0username\0password
echo -ne '\[email protected]\0mypassword' | base64
# 输出: AHVzZXIAcGFzc3dvcmQ=
# 使用脚本生成
#!/bin/bash
# gen-auth-plain.sh
read -p "用户名: " USER
read -s -p "密码: " PASS
echo ""
ENCODED=$(echo -ne "\0${USER}\0${PASS}" | base64)
echo "AUTH PLAIN $ENCODED"
4.7 用户管理脚本
4.7.1 用户管理工具
#!/bin/bash
# mail-user-admin.sh — 邮件用户管理脚本
VMAIL_DIR="/var/mail/vhosts"
USERS_FILE="/etc/dovecot/users"
UID_NUM=5000
GID_NUM=5000
add_user() {
local email="$1"
local password="$2"
local domain=$(echo "$email" | cut -d@ -f2)
local user=$(echo "$email" | cut -d@ -f1)
# 生成密码哈希
local hash=$(doveadm pw -s SHA512-CRYPT -p "$password")
# 创建邮箱目录
local maildir="${domain}/${user}/"
mkdir -p "${VMAIL_DIR}/${domain}/${user}"
# 写入用户文件
echo "${email}:${hash}:${UID_NUM}:${GID_NUM}::${VMAIL_DIR}/${maildir}" >> "$USERS_FILE"
# 设置权限
chown -R ${UID_NUM}:${GID_NUM} "${VMAIL_DIR}/${domain}/${user}"
echo "✅ 用户 $email 已创建"
}
delete_user() {
local email="$1"
local domain=$(echo "$email" | cut -d@ -f2)
local user=$(echo "$email" | cut -d@ -f1)
# 从用户文件中删除
sed -i "/^${email}:/d" "$USERS_FILE"
# 删除邮箱目录(可选)
read -p "是否删除邮箱目录?(y/N): " confirm
if [ "$confirm" = "y" ] || [ "$confirm" = "Y" ]; then
rm -rf "${VMAIL_DIR}/${domain}/${user}"
echo "✅ 邮箱目录已删除"
fi
echo "✅ 用户 $email 已删除"
}
change_password() {
local email="$1"
local password="$2"
# 生成新密码哈希
local hash=$(doveadm pw -s SHA512-CRYPT -p "$password")
# 更新用户文件
sed -i "s|^${email}:[^:]*:|${email}:${hash}:|" "$USERS_FILE"
echo "✅ 用户 $email 密码已更新"
}
list_users() {
echo "=== 邮件用户列表 ==="
while IFS=: read -r email hash uid gid gecos home rest; do
echo " $email -> $home"
done < "$USERS_FILE"
}
case "$1" in
add)
add_user "$2" "$3"
;;
delete)
delete_user "$2"
;;
passwd)
change_password "$2" "$3"
;;
list)
list_users
;;
*)
echo "用法: $0 {add|delete|passwd|list} [email] [password]"
echo ""
echo "示例:"
echo " $0 add [email protected] mypassword"
echo " $0 delete [email protected]"
echo " $0 passwd [email protected] newpassword"
echo " $0 list"
exit 1
;;
esac
4.8 业务场景:企业邮件认证方案
场景描述
一家 500 人的企业需要邮件服务,要求:
- 统一使用公司域名
company.com - 用户密码存储在 LDAP 中
- 支持邮箱配额管理
- 禁止弱密码
LDAP 集成方案
# 安装 LDAP 模块
sudo apt install -y dovecot-ldap
# /etc/dovecot/conf.d/auth-ldap.conf.ext
passdb {
driver = ldap
args = /etc/dovecot/dovecot-ldap.conf.ext
}
userdb {
driver = ldap
args = /etc/dovecot/dovecot-ldap.conf.ext
}
# /etc/dovecot/dovecot-ldap.conf.ext
uris = ldap://ldap.company.com
dn = cn=dovecot,ou=services,dc=company,dc=com
dnpass = secret
auth_bind = yes
auth_bind_userdn = cn=%n,ou=people,dc=company,dc=com
base = ou=people,dc=company,dc=com
scope = subtree
user_attrs = \
=home=/var/mail/vhosts/company.com/%n, \
=uid=5000, \
=gid=5000, \
=quota_rule=*:bytes=%{ldap:mailQuota}
user_filter = (&(objectClass=inetOrgPerson)(uid=%n))
pass_filter = (&(objectClass=inetOrgPerson)(uid=%n))
4.9 注意事项
⚠️ 密码安全:
- 使用
doveadm pw生成密码哈希,不要存储明文密码- 推荐使用
SHA512-CRYPT或BLF-CRYPT(bcrypt)- 禁用
PLAIN和LOGIN机制(除非在 TLS 保护下)
⚠️ 权限问题:
- Dovecot 和 Postfix 的 SASL socket 权限必须正确
/var/spool/postfix/private/auth的权限应为0660,owner 为postfix:postfix
💡 虚拟用户 vs 系统用户:
- 虚拟用户:不占用系统账号,更安全,适合生产环境
- 系统用户:使用
/etc/passwd和 PAM,适合小型/测试环境