强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

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-CRYPTBLF-CRYPT(bcrypt)
  • 禁用 PLAINLOGIN 机制(除非在 TLS 保护下)

⚠️ 权限问题

  • Dovecot 和 Postfix 的 SASL socket 权限必须正确
  • /var/spool/postfix/private/auth 的权限应为 0660,owner 为 postfix:postfix

💡 虚拟用户 vs 系统用户

  • 虚拟用户:不占用系统账号,更安全,适合生产环境
  • 系统用户:使用 /etc/passwd 和 PAM,适合小型/测试环境

4.10 扩展阅读


上一章← 第 3 章:main.cf 配置详解 下一章第 5 章:TLS/SSL 加密配置 →