Ubuntu 系统搭建邮件服务器
构建一个稳定、安全的邮件服务器是企业或个人提供电子邮件服务的基础任务。一个完整的邮件系统通常由多个模块组成,它们各司其职,协同工作,确保邮件的接收、发送、存储及反垃圾邮件机制正常运行。
邮件系统核心模块
Postfix(SMTP Server)
- 功能:Postfix 是一个开源的 邮件传输代理(MTA),负责邮件的接收、本地投递与跨服务器中继。它能接收来自本地客户端或远程服务器的邮件,并根据目标地址决定是否本地投递或转发至其他服务器。
- 接收端口:
25
(SMTP):用于服务器之间通信(入站)587
(Submission):推荐用于客户端提交邮件(支持 STARTTLS 加密)465
(SMTPS):旧版加密端口(已逐步淘汰)
- 发送端口:
- 默认通过本地动态端口连接远程服务器的
25
端口进行邮件传输(出站)
- 默认通过本地动态端口连接远程服务器的
- 特点:
- 支持虚拟域管理、用户别名映射、内容过滤等功能
- 可与 Dovecot 配合实现 SMTP 认证、IMAP 服务及本地邮件投递
- 支持通过 milter 接口接入 OpenDKIM、OpenDMARC、Amavis 等插件,构建完整的邮件安全与反垃圾邮件体系
Dovecot(IMAP/POP3 Server)
- 功能:Dovecot 是一个开源的 IMAP 和 POP3 服务器,为用户提供对本地邮箱的访问能力。
- 协议支持:
- IMAP(默认端口
143
,加密993
) - POP3(默认端口
110
,加密995
)
- IMAP(默认端口
- 特点:
- 提供用户认证、邮件检索、本地存储管理(支持 Maildir 和 mbox 格式)
- 支持 SSL/TLS 加密通信
- 可与 Postfix 共享用户数据库(如系统用户、MySQL、LDAP 等)
- 提供高效的邮件索引和搜索功能
Roundcube(Webmail 客户端)
- 功能:Roundcube 是一个开源的 基于 Web 的邮件客户端,提供图形化界面供用户收发和管理邮件。
- 特点:
- 使用 PHP + MySQL/PostgreSQL 构建,支持多种数据库后端
- 支持多语言、主题定制和丰富的插件扩展(如日历、联系人、加密等)
- 通过 IMAP 连接 Dovecot 获取邮件,通过 SMTP 连接 Postfix 发送邮件
- 支持移动端访问、全文搜索、邮件归档等功能
OpenDKIM(DKIM 签名验证模块)
- 功能:OpenDKIM 是一个开源的 DKIM(DomainKeys Identified Mail)实现模块,用于验证邮件来源,防止邮件伪造。
- 工作方式:
- 发送时:在邮件头部添加数字签名(使用私钥加密),并绑定到发件域名
- 接收时:通过 DNS 查询该域名的公钥记录,验证签名是否合法
- 优势:
- 提升邮件可信度,降低被标记为垃圾邮件的概率
- 可与 Postfix 集成,通过 milter 接口进行实时签名与验证
- 支持多域名、多密钥管理,便于企业部署
模块协同工作逻辑
用户通过 Roundcube 发送邮件
流程说明:
- 用户登录 Roundcube Webmail,填写邮件内容并点击“发送”。
- Roundcube 通过 SMTP 协议连接 Postfix,并使用 SMTP 身份认证(如 SASL)提交邮件。
- Postfix 接收邮件后,通过 milter 接口 将邮件转发给 OpenDKIM 模块进行签名处理。
- OpenDKIM 使用本地保存的私钥对邮件头部字段进行签名,并将生成的
DKIM-Signature
头部插入邮件中。 - Postfix 收回已签名邮件,将其发送至目标邮件服务器。
安全性增强:
- 在域名 DNS 中配置 DKIM 公钥记录(TXT 类型),供接收方验证签名。
- 建议同时配置 SPF、DMARC 等机制,形成完整的邮件身份验证体系,防止伪造发件人和提高投递成功率。
外部邮件发送至本系统(接收)
流程说明:
- 外部邮件服务器通过 SMTP 向 Postfix 提交邮件。
- Postfix 接收到邮件后,通过 milter 接口 将其传递给 OpenDKIM 模块进行 DKIM 验证。
- OpenDKIM 解析邮件中的
DKIM-Signature
字段,提取域名信息,并从 DNS 查询对应的公钥。 - OpenDKIM 对邮件头部进行校验,判断签名是否有效,并将结果返回给 Postfix。
- Postfix 根据验证结果执行策略操作(如拒绝伪造邮件、打标签或放行)。
- 验证通过的邮件由 Postfix 投递到本地邮箱,通常通过 LDA 或 LMTP 方式交给 Dovecot 存储。
- 用户可通过 Roundcube Webmail 或 IMAP/POP3 客户端连接 Dovecot 服务器查看邮件。
注意事项:
- 需在 Postfix 的
main.cf
中配置smtpd_milters
和non_smtpd_milters
参数,启用 OpenDKIM。 - 可结合 SpamAssassin、Amavis、ClamAV 等工具实现垃圾邮件和病毒扫描,进一步提升邮件安全。
所需环境与软件配置
操作系统
- 推荐版本:
Ubuntu 22.04 LTS
- 系统要求:
- 至少 2GB 内存
- 20GB 磁盘空间
- 能够访问公网并绑定固定 IP 地址
必需软件包
软件 | 版本建议 | 作用 |
---|---|---|
Postfix | 最新稳定版 | 邮件传输代理(MTA),负责邮件收发中转 |
Dovecot | 2.3.x 或以上 | 提供 IMAP/POP3 服务,支持本地邮箱访问 |
MariaDB | 10.x+ | 存储用户、别名、域等虚拟邮箱相关数据 |
PHP | 8.2+ | 运行 Roundcube Webmail 所需,推荐启用常见扩展 |
Nginx | 最新稳定版 | Web 服务器,托管 Roundcube 和静态资源 |
Roundcube | 最新稳定版(如 1.6.x) | WebMail 客户端,提供图形化邮件收发界面 |
OpenDKIM | 最新版本 | 实现 DKIM 签名与验证,防止邮件伪造 |
Let's Encrypt | certbot | 免费申请和自动续订 SSL/TLS 证书,保障加密通信 |
域名配置要求
域名介绍
类型 | 示例 | 说明 |
---|---|---|
顶级域名 | example.com | 主域名 |
二级域名 | mail.example.com | 邮件服务器主机名 |
DNS 记录 | A、MX、TXT(SPF/DKIM/DMARC) | 邮件路由与反欺诈配置 |
域名备案说明:
- 国际域名:绑定国外服务器无需备案;绑定国内服务器必须备案。
- 国内域名:无论绑定何处,均需完成 ICP 备案才能解析使用。
解耦邮件服务器与邮件域名
- 邮件服务器的 主机名(hostname) 是
mail.example.com
- 实际使用的 邮件域名(email domain) 是
example.com
- 这两个可以不同,也不需要一致
- 只要 Postfix、DNS 和相关配置正确,就可以正常收发
user@example.com
的邮件
将邮件服务器的主机名(如 mail.example.com
)与实际使用的邮件域名(如 example.com
)分开,是一种良好的架构设计:
- 灵活性:更换邮件服务器的主机名,不影响用户邮箱地址
- 可扩展:同一台服务器可托管多个域名,只需在 Postfix 中配置即可
DNS 记录需正确指向邮件服务器
为了让外部邮件系统知道如何将 example.com
的邮件发送到你的服务器,你需要确保 DNS 设置正确:
必须配置的记录:
类型 | 内容 | 示例 |
---|---|---|
MX 记录 | 指向邮件服务器的主机名或 IP 地址 | MX 10 mail.example.com |
A/AAAA 记录 | mail.example.com 应解析为服务器 IP |
A 192.168.1.100 |
SPF 记录 | 允许哪些服务器可以发送该域的邮件 | v=spf1 mx ~all |
DKIM / DMARC | 可选但建议添加,提升邮件可信度 | default._domainkey TXT "v=DKIM1; k=rsa; p=..." |
配置域名记录
域名记录需要在域名服务上或云主机服务上的控制面板中设置。下文以 DNSPOD 域名服务商为例。
A 记录(地址记录)
用于将域名解析到服务器的公网 IP。
- 登录 DNSPod 管理控制台。
- 在 “我的域名” 中,选择需要进行 A 记录转发的域名,单击“域名”,进入该域名的【记录管理】页面。
- 单击【添加记录】,填写以下记录信息。如下所示:
字段 | 示例值 |
---|---|
主机记录 | mail |
记录类型 | A |
记录值 | 192.0.2.10 |
TTL | 默认(如 600 秒) |
MX 记录(邮件交换器)
指定接收该域名邮件的邮件服务器。
字段 | 示例值 |
---|---|
主机记录 | @ |
记录类型 | MX |
记录值 | mail.example.com |
优先级 | 5 (数值越小优先级越高) |
如果有多个邮件服务器,主机名分别是:
mx1.example.com
mx2.example.com
BIND 格式示例:
example.com. IN MX 5 mx1.example.com.
IN MX 10 mx2.example.com.
mx1 IN A 192.0.2.10
mx2 IN A 192.0.2.11
SPF 记录(发件人策略框架)
声明哪些邮件服务器被允许代表该域发送邮件。
字段 | 示例值 |
---|---|
主机记录 | @ |
记录类型 | SPF |
记录值 | v=spf1 mx ip4:192.0.2.0/24 -all |
常用 SPF 语句示例:
v=spf1 mx -all # 仅允许 MX 发送邮件
v=spf1 a mx ip4:192.0.2.0/24 ~all # 允许 A、MX、IP 范围
v=spf1 include:_spf.google.com ~all # 使用 Google Workspace 时
DKIM 记录(域名密钥识别邮件)
用于邮件签名验证,确保来源真实性。
生成密钥对(在服务器上执行)
# OpenDKIM 的密钥存储路径
mkdir -p /etc/opendkim/keys/
# 生成 DKIM 密钥对
opendkim-genkey -D /etc/opendkim/keys/ -d example.com -s mail
# 更改权限,确保 opendkim 用户可访问
chown -R opendkim:opendkim /etc/opendkim/keys/
>-s mail
表示选择器(selector
),可以是任意字符串(如 default
、dkim1
、mail
等),添加 DNS 记录时应与此保持一致 。
查看并整理公钥内容
cat /etc/opendkim/keys/mail.txt
输出公钥内容:
mail._domainkey IN TXT ( "v=DKIM1; h=sha256; k=rsa; "
"p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzRFVh94avDEYsDVvzKZxHDZ9avHraE/prfLlu4DTlNQFJdZfpgeb8nl1SvVjJg4aWodiVWHu/nHRY/ZX89Ouw14fYMyDl/Ow/+N25yZfekg0hVgzLKvQiKQxttxFKCAsoZMDH710rTkm3Lcy+SLZHcBTSJRB01KgBkNrzA8cnlilkK2uWtartlm8Ros+vhoHZ9KdPEMpdv/Y2+"
"eMgfcgT6JBkvNt606myo7+lq2YgPL7Jv9O0ruvXyUDCrPy8H2vzgO/yuGxiN9db7lZtFlXdFLmELI8hvAD7CijND9rZzV/nH8zaKARD43jr2fQUc/IEKMJ+1sqqtnvLa320VlQ0wIDAQAB" ) ; ----- DKIM key mail for example.com
需要将上面的多行公钥合并为一行,并去掉所有引号和空格。
输出示例:
v=DKIM1; h=sha256; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzRFVh94avDEYsDVvzKZxHDZ9avHraE/prfLlu4DTlNQFJdZfpgeb8nl1SvVjJg4aWodiVWHu/nHRY/ZX89Ouw14fYMyDl/Ow/+N25yZfekg0hVgzLKvQiKQxttxFKCAsoZMDH710rTkm3Lcy+SLZHcBTSJRB01KgBkNrzA8cnlilkK2uWtartlm8Ros+vhoHZ9KdPEMpdv/Y2+eMgfcgT6JBkvNt606myo7+lq2YgPL7Jv9O0ruvXyUDCrPy8H2vzgO/yuGxiN9db7lZtFlXdFLmELI8hvAD7CijND9rZzV/nH8zaKARD43jr2fQUc/IEKMJ+1sqqtnvLa320VlQ0wIDAQAB
添加 DNS TXT 记录
字段 | 示例值 |
---|---|
主机记录 | mail._domainkey.base-domain |
记录类型 | TXT |
TTL | 默认 |
记录值 | 上一步输出整理后的 DKIM 公钥内容 |
说明:mail._domainkey.base-domain
:其中,mail
是生成密钥时指定的选择器,base-domain
是对应的邮件主域名(要签名的邮件地址中 @
后面的部分)。
DMARC 记录(域消息认证报告与合规)
控制未通过 SPF/DKIM 的邮件如何处理,并提供统计报告。
字段 | 示例值 |
---|---|
主机记录 | _dmarc |
记录类型 | TXT |
TTL | 默认 |
记录值 | v=DMARC1; p=quarantine; rua=mailto:admin@example.com; ruf=mailto:admin@example.com; fo=1 |
阶段式部署建议:
# 观察模式
v=DMARC1; p=none; rua=mailto:admin@example.com; ruf=mailto:admin@example.com
# 隔离可疑邮件
v=DMARC1; p=quarantine; rua=mailto:admin@example.com; ruf=mailto:admin@example.com; pct=100
# 拒绝伪造邮件
v=DMARC1; p=reject; rua=mailto:admin@example.com; ruf=mailto:admin@example.com; pct=100
PTR 记录(反向 DNS)
将公网 IP 解析回邮件服务器主机名,增强邮件可信度。
字段 | 示例值 |
---|---|
主机记录 | 8.219.247.247 (由 ISP 设置) |
记录类型 | PTR |
记录值 | mail.example.com |
BIND 格式示例:
IPv4 的 PTR 记录格式是基于 in-addr.arpa 域名构建的。
8.219.247.247.in-addr.arpa. IN PTR mail.example.com.
在邮件服务器中,PTR 记录非常重要,原因如下:
-
提升邮件可信度:
- 很多邮件服务器会检查你的邮件来源 IP 是否有合理的 PTR 记录。
- 如果没有 PTR 或者 PTR 不匹配,可能会被标记为垃圾邮件或直接拒绝接收。
-
防止伪造邮件:
- 反向解析帮助验证邮件是否来自合法的邮件服务器。
-
正向与反向 DNS 一致性(FCrDNS):
- 最佳实践是确保:
- 正向 DNS:
mail.example.com → 8.219.247.247
- 反向 DNS:
8.219.247.247 → mail.example.com
- 正向 DNS:
- 这种相互确认对邮件认证非常友好。
- 最佳实践是确保:
注意事项:
- PTR 记录由 ISP 或云服务商控制:
- 和普通 A、TXT 等 DNS 记录不同,PTR 记录不能在你自己的 DNS 控制台(如 DNSPod、Cloudflare)里修改。
- 必须联系你的 VPS 提供商(如阿里云、腾讯云、Linode、Vultr 等)来设置。
- 国内云服务需备案后才能申请 PTR:
- 比如阿里云和腾讯云通常要求你的域名已完成 ICP 备案,才能设置 PTR。
参考文档:https://docs.dnspod.cn/dns/help-a/
验证与测试
查看 DNS 记录是否生效:
dig MX example.com +short
dig TXT example.com +short
dig TXT mail._domainkey.example.com.example.com +short
dig TXT _dmarc.example.com +short
- 指定
+short
参数,返回简洁的结果。 ANSWER: 0
或返回空表示没有找到对应的记录。
SSL/TLS 证书推荐
- Let’s Encrypt(推荐):
- 免费、自动更新
- 使用 Certbot 工具一键申请
- 商业证书(如 Comodo、DigiCert):
- 适用于需要品牌信任的企业场景
部署前配置建议
系统设置建议
# 设置主机名
sudo hostnamectl set-hostname mail.example.com
# 添加本地解析(仅限测试环境)
echo "127.0.0.1 mail.example.com" | sudo tee -a /etc/hosts
# 启用基础防火墙并开放必要端口(推荐生产环境使用)
sudo ufw allow OpenSSH
sudo ufw allow http
sudo ufw allow https
sudo ufw allow 25,587,465,143,993/tcp
sudo ufw enable
# CentOS/RedHat 用户:临时关闭 SELinux(仅限测试)
setenforce 0
# 永久关闭 SELinux(生产建议改为 permissive)
sudo sed -i 's/^SELINUX=enforcing/SELINUX=permissive/' /etc/selinux/config
# 更新系统软件包
sudo apt update && sudo apt upgrade -y
生产环境 DNS 配置(在域名服务商控制台添加)
; A 记录:指向邮件服务器 IP
mail.example.com. IN A 8.219.247.247
; MX 记录:指定邮件接收服务器
example.com. IN MX 10 mail.example.com.
; SPF 记录(防止伪造发件人)
example.com. IN TXT "v=spf1 mx ~all"
; DKIM 记录(示例,实际由 OpenDKIM 生成)
default._domainkey.example.com. IN TXT "v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG..."
; DMARC 记录(可选,用于报告垃圾邮件来源)
_dmarc.example.com. IN TXT "v=DMARC1; p=none"
>注意:请将 example.com
替换为你自己的域名,IP 地址为实际使用的公网 IP 地址。
防火墙/安全组规则开放建议
确保外部可以访问邮件服务器主机的如下端口:
协议 | 端口 | 说明 |
---|---|---|
TCP | 22 |
SSH 登录 |
TCP | 25 |
SMTP(邮件发送) |
TCP | 587 |
Submission(客户端提交) |
TCP | 465 |
SMTPS(旧加密方式) |
TCP | 143 |
IMAP(未加密) |
TCP | 993 |
IMAPS(加密 IMAP) |
TCP | 80 /443 |
Roundcube Webmail(HTTP/HTTPS) |
> 如果你使用的是云服务商(AWS/Azure/阿里云等),请检查对应的安全组规则是否允许这些端口对公网开放。
核心模块安装
更新软件包索引
sudo apt update -y
>建议始终在安装前更新软件源列表,确保获取最新版本。
软件包更新升级
sudo apt upgrade -y
安装关键软件包
apt install -y mariadb-server && \
apt install -y postfix postfix-mysql && \
apt install -y dovecot-core dovecot-imapd dovecot-pop3d dovecot-lmtpd dovecot-mysql&& \
apt install -y opendkim && \
apt install -y nginx
安装 policyd-spf
插件(Debian/Ubuntu
):
apt install postfix-policyd-spf-python
- 可以在 Postfix 中配置了 SPF 检查策略(
policyd-spf
),用于验证入站邮件的 SPF 来源。
PHP 安装说明
Roundcube 是基于 PHP 的 Web 应用,因此需安装 PHP 及其扩展模块。根据实际需求,可以选择不同的安装方式。
方式一:编译源码安装
因为涉及许多依赖扩展模块,PHP 源码编译安装 提供了更大的灵活性,但也相对耗时且容易遇到依赖问题。
- 其中,要扩展
intl
模块,需要依赖库icu
,使用 ICU 源码编译安装-tmp。 imagick
模块不是和 PHP 绑定的扩展,使用 imagick 源码编译安装-tmp。
方式二:通过 PPA 安装较新版本的 PHP
Ondřej Surý 维护的 PPA 源,提供了多个 PHP 版本的最新稳定包。
执行以下命令来添加该 PPA:
sudo apt update
sudo apt install software-properties-common
sudo add-apt-repository ppa:ondrej/php
sudo apt update
使用如下命令安装指定版本的 PHP(例如 PHP 8.2):
sudo apt install -y php8.2
还可以安装常用扩展模块(根据需要选择):
sudo apt install -y \
php8.2-cli \
php8.2-fpm \
php8.2-mbstring \
php8.2-mysql \
php8.2-intl \
php8.2-exif \
php8.2-gd \
php8.2-xml \
php8.2-curl \
php8.2-zip \
php8.2-imap \
php8.2-ldap \
php8.2-opcache \
php8.2-bcmath \
php8.2-imagick \
php-pear
参考:PHP 安装手册
查看软件版本
方法1:通过包管理器查看已安装软件版本
apt show xxx | grep Version
方法2:直接调用软件包命令查看运行版本
软件 | 查看命令 |
---|---|
MariaDB | mariadb --version |
Postfix | postconf mail_version |
Dovecot | dovecot --version |
OpenDKIM | opendkim -V |
Nginx | nginx -v |
PHP CLI | php --version |
查询结果:
root@mail:~# mariadb --version
mariadb Ver 15.1 Distrib 10.11.13-MariaDB, for debian-linux-gnu (x86_64) using EditLine wrapper
root@mail:~# postconf mail_version
mail_version = 3.8.6
root@mail:~# dovecot --version
2.3.21 (47349e2482)
root@mail:~# opendkim -V
opendkim: OpenDKIM Filter v2.11.0
...
root@mail:~# nginx -v
nginx version: nginx/1.24.0 (Ubuntu)
配置 MySQL
使用 MySQL 开源分支的 MariaDB 数据库。
初始化 MySQL
启动服务
systemctl start mariadb
设置开机自启动
systemctl enable mariadb
安全设置
mysql_secure_installation
设置管理员用户验证密码,其他按 Y
回车。
邮件服务器数据库
创建邮件服务数据库,存储服务器管理的邮件域名、邮箱账户等信息。
进入 MySQL 命令行界面:
[root@mail]# mysql -u root -p
Enter password:
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MariaDB connection id is 28
Server version: 5.5.68-MariaDB MariaDB Server
Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
MariaDB [(none)]>
说明:按照提示输入 root
用户密码回车。出现 MariaDB [(none)]>
即表示进入 MySQL
命令行。
>注意:MySQL 语句后面要加上分号执行。
创建用户
创建用户用于读取邮件系统数据库。
MariaDB [(none)]> CREATE USER 'mail_sys'@'localhost' IDENTIFIED BY 'mail_sys';
Query OK, 0 rows affected (0.00 sec)
说明:
CREATE USER
:表示创建一个新用户。'mail_sys'@'localhost'
:定义了用户的标识符。'mail_sys'
是用户名,'localhost'
表示该用户只能从本地连接到数据库。这是一个安全措施的限制,以确保该用户只能通过本地访问,而不能通过远程连接到数据库。IDENTIFIED BY 'mail_sys'
:设置用户的密码为'mail_sys'
。
创建数据库
MariaDB [(none)]> CREATE DATABASE mail_sys;
Query OK, 1 row affected (0.00 sec)
说明:这里的邮件系统数据库只用作域名、用户、别名的验证。
为用户授予读取权限
MariaDB [(none)]> GRANT SELECT ON mail_sys.* TO 'mail_sys'@'localhost' IDENTIFIED BY 'mail_sys';
Query OK, 0 rows affected (0.10 sec)
说明:
GRANT SELECT ON mail_sys.*
:对数据库mail_sys
中所有表授予SELECT
权限。SELECT
权限允许用户查询数据库中的数据。TO 'mail_sys'@'localhost'
:指定授权名为'mail_sys'
且只能从本地连接数据库的用户。
刷新权限
FLUSH PRIVILEGES;
连接数据库
MariaDB [(none)]> USE mail_sys;
Database changed
创建数据表
创建域名表
CREATE TABLE `domains` ( `id` int(20) NOT NULL auto_increment, `name` varchar(100) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
说明:
id int(20) NOT NULL auto_increment
:定义一个名为id
的整数类型的列。其中,auto_increment
表示该列是一个自增的列,每次插入数据时,其值会自动递增。name varchar(100) NOT NULL, PRIMARY KEY (id)
:定义一个名为name
的变长字符串类型的列,最大长度为100个字符。PRIMARY KEY (id
),定义了表的主键,即
id` 列。ENGINE=InnoDB
:定义了表的存储引擎。使用了 InnoDB 存储引擎,它是一个支持事务和外键的存储引擎。DEFAULT CHARSET=utf8
:定义了表的默认字符集,即utf8字符集。
注意:在 MySQL 和 MariaDB 中,反引号(`)用于标识表名、列名等标识符,以避免与 SQL 关键字冲突或处理特殊命名;而在标准 SQL 和其他数据库中,通常使用双引号("),且反引号可能不被支持。
创建用户表
CREATE TABLE `users` ( `id` int(20) NOT NULL auto_increment, `domain_id` int(20) NOT NULL, `password` varchar(200) NOT NULL, `email` varchar(200) NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `email` (`email`), FOREIGN KEY (domain_id) REFERENCES domains(id) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
说明:
FOREIGN KEY (domain_id) REFERENCES domains (id) ON DELETE CASCADE
:表示users
表的domain_id
列是一个外键,它引用了domains
表中的id
列。当在domains
表中删除一行(具体说是删除一个特定的id
),所有在users
表中具有相应domain_id
的行也将被自动删除。UNIQUE KEY email (email)
:创建唯一键(Unique Key)约束,确保 eamil 列中的值是唯一的。其中,email
表示唯一键的名称,(email
) 表示唯一键应用的列,即 email 列。
创建别名表
CREATE TABLE `aliases` ( `id` int(20) NOT NULL auto_increment, `domain_id` int(20) NOT NULL, `source` varchar(200) NOT NULL, `destination` varchar(200) NOT NULL, PRIMARY KEY (`id`), FOREIGN KEY (domain_id) REFERENCES domains(id) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
操作数据库
添加域名
MariaDB [mail_sys]> INSERT INTO `mail_sys`.`domains` (`id` ,`name`)
VALUES('1', 'example.com');
删除域名
DELETE FROM `mail_sys`.`domains` WHERE `id`='<域名索引号>';
添加用户
MariaDB [mail_sys]> INSERT INTO `mail_sys`.`users` (`id`, `domain_id`, `password` , `email`) VALUES
('1', '1', ENCRYPT('12345678', CONCAT('$6$', SUBSTRING(SHA(RAND()), -16))), 'user1@example.com'),
('2', '1', ENCRYPT('22222222', CONCAT('$6$', SUBSTRING(SHA(RAND()), -16))), 'user2@example.com'),
('3', '1', ENCRYPT('33333333', CONCAT('$6$', SUBSTRING(SHA(RAND()), -16))), 'user3@example.com');
说明:
ENCRYPT('22222222', CONCAT('$6$', SUBSTRING(SHA(RAND()), -16)))
:表示使用ENCRYPT
函数对password
字段的明文值进行加密。CONCAT('$6$', SUBSTRING(SHA(RAND()), -16))
:'$6$'
:这是一个字符串,通常用于指定加密算法的标识符。这个表示SHA-512
算法的方式。SUBSTRING(SHA(RAND()), -16))
:这一系列函数,首先是RAND()
生成一个随机的浮点数,然后,SHA()
函数对这个浮点数进行散列运算,最后,SUBSTRING
函数选择了 SHA 散列值的最后16
个字符。CONCAT
函数将$6$
和选择的 SHA 散列的字符连接在一起,形成一个包含标识符和随机生成盐的字符串。
- 所以,这部分目的是生成一个随机的
16
个字符串,作为随机盐。
删除用户
DELETE FROM `mail_sys`.`users` WHERE `id`='<用户索引号>';
添加别名
INSERT INTO `mail_sys`.`aliases` (`id`, `domain_id`, `source`, `destination`) VALUES
('1', '1', 'user11@example.com', 'user1@example.com'),
('2', '1', 'user22@example.com', 'user2@example.com'),
('3', '1', 'user33@example.com', 'user3@example.com');
说明:user11@example.com
是 user1@example.com
的别名,当其他用户向 user11@example.com
发送邮件时,user1@example.com
邮箱可以收到。
删除别名
DELETE FROM `mail_sys`.`aliases` WHERE `id`='<别名索引号>';
检查域名表
MariaDB [mail_sys]> SELECT * FROM mail_sys.domains;
+----+--------------------+
| id | name |
+----+--------------------+
| 1 | example.com |
+----+--------------------+
1 row in set (0.001 sec)
检查用户表
MariaDB [mail_sys]> SELECT * FROM mail_sys.users;
+----+-----------+------------------------------------------------------------------------------------------------------------+--------------------------+
| id | domain_id | password | email |
+----+-----------+------------------------------------------------------------------------------------------------------------+--------------------------+
| 1 | 1 | $6$0a8d0d2d8f928623$u85DH26pxrTnpFfNY2NtTcoefGnAJF/c9Lyik0vxT0Qokjqt4rMmTFjLvuwrRjTmh8My7TWSOGF3i3FuVAx8f. | user1@example.com |
| 2 | 1 | $6$cc7bfd9a772b513f$q2a/2lE3.TLBS.dEHr5MYyafXNhmjBZd9Eh5Dk7B/os8vkJd6TXOoO15AXEnuWGrtb9nZPIdRO/WnhtGNHCcQ. | user2@example.com |
| 3 | 1 | $6$833bcec3573b0d5c$w6jtitlxC4HhaCH8fp7gKN4hv2N5kcRXNpWJfOS.K3i0GvCk7g1vFIIgdE.2yBAVV2sWALCVoMsdZ35xMLTig/ | user3@example.com |
+----+-----------+------------------------------------------------------------------------------------------------------------+--------------------------+
3 rows in set (0.000 sec)
检查别名表
MariaDB [mail_sys]> SELECT * FROM mail_sys.aliases;
+----+-----------+---------------------------+--------------------------+
| id | domain_id | source | destination |
+----+-----------+---------------------------+--------------------------+
| 1 | 1 | user11@example.com | user1@example.com |
| 2 | 1 | user22@example.com | user2@example.com |
| 3 | 1 | user33@example.com | user3@example.com |
+----+-----------+---------------------------+--------------------------+
3 rows in set (0.001 sec)
说明:数据库设置完成,按 Ctrl + D
退出 MySQL 命令行界面。
创建专用用户及组
为了提高安全性和可维护性,建议为邮件服务创建专用的系统用户和组。
创建专用组
groupadd -g 2000 mail_sys
-g 2000
:指定固定 GID,避免与其他系统组冲突。
创建专用用户
useradd -g mail_sys -u 2000 -d /var/spool/mail -s /sbin/nologin mail_sys
说明:
-g mail_sys
:所属主组-u 2000
:指定 UID,与 GID 保持一致便于管理-d /var/spool/mail
:指定主目录,用于存放用户邮箱-s /sbin/nologin
:禁止交互式登录,增强系统安全性
修改邮件目录所有者
chown -R mail_sys:mail_sys /var/spool/mail
>确保 Postfix 和 Dovecot 有权限读写邮箱文件,否则会导致邮件投递失败或用户无法访问邮箱。
配置 Postfix
使用中继服务
背景与核心问题
- 端口 25 被屏蔽:大多数 ISP/云服务商(如 AWS、阿里云)出于反垃圾邮件政策,默认屏蔽出站端口 25,导致 Postfix 直接投递(MTA-to-MTA)失败。
- 替代方案:使用 Submission 端口(587) 或 SMTPS 端口(465),配合 TLS 加密和 SASL 认证,通过中继服务器(Relay Host)发送邮件。
方案对比与选择
方案 | 端口 | 认证需求 | 适用场景 | 优点 | 缺点 |
---|---|---|---|---|---|
方案一:MTA-to-MTA(端口 465/587) | 465/587 | SASL 认证 | 自建邮件服务器或私有中继 | 高度可控,支持加密 | 配置复杂,需管理证书和认证 |
方案二:中继服务(端口 465/587) | 465/587 | API 密钥/SASL | 第三方服务(如 Resend、腾讯企业邮) | 简化配置,免维护中继服务器 | 依赖第三方服务稳定性 |
推荐方案 |
- 优先选择中继服务(方案二):适合大多数用户,尤其是没有自建邮件服务器能力的场景。
- 使用端口 587:符合现代标准(RFC 6409),支持 STARTTLS 和 SASL 认证,兼容性更好。
配置 Postfix 使用 Relay Host 示例
前提条件:
- 已在 Resend 中添加验证域名
example.com
- 已设置 Custom Return Path(退信路径)
- 已设置好 SPF/DKIM/DMARC 记录
- 已创建 API Key(用于认证)
创建 SASL 认证文件:
sudo mkdir -p /etc/postfix/sasl
sudo vim /etc/postfix/sasl_passwd
写入以下内容(替换 your_api_key
):
[smtp.resend.com]:587 resend:your_api_key
保存后设置权限并生成映射:
sudo chmod 600 /etc/postfix/sasl_passwd
sudo postmap /etc/postfix/sasl_passwd
修改 Postfix 配置:
编辑 /etc/postfix/main.cf
,配置如下内容:
# 指定中继服务器(Resend SMTP)
relayhost = [smtp.resend.com]:587
# 启用 SASL 认证
smtp_sasl_auth_enable = yes
smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd
smtp_sasl_security_options = noanonymous
# TLS 加密设置
smtp_use_tls = yes
smtp_tls_security_level = encrypt
备份默认配置文件
cp -r /etc/postfix /etc/postfix.bak
编辑主配置文件
该文件包含 Postfix 的全局配置参数。
vim /etc/postfix/main.cf
示例配置:
# ================== 主机与域名设置 ==================
myhostname = mail.example.com
mydomain = example.com
myorigin = $mydomain
# 强制统一发件人域
masquerade_domains = example.com
# ================== 网络控制 ==================
inet_protocols = ipv4
inet_interfaces = all
mydestination = localhost.$mydomain, localhost
mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128
# 启用 transport map 实现灵活路由
# transportmaps = hash:/etc/postfix/transport
# ================== 邮件存储格式 ==================
home_mailbox = Maildir/
# ================== 虚拟用户支持(MySQL) ==================
virtual_mailbox_domains = mysql:/etc/postfix/mysql-virtual-mailbox-domains.cf
virtual_mailbox_maps = mysql:/etc/postfix/mysql-virtual-mailbox-maps.cf
virtual_alias_maps = mysql:/etc/postfix/mysql-virtual-alias-maps.cf
# ================== 别名设置 ==================
alias_maps = hash:/etc/aliases, nis:mail.aliases
alias_database = hash:/etc/aliases, hash:/opt/majordomo/aliases
# ================== 登录验证相关 ==================
smtpd_sender_login_maps = mysql:/etc/postfix/mysql-virtual-mailbox-maps.cf, mysql:/etc/postfix/mysql-virtual-alias-maps.cf
local_recipient_maps = mysql:/etc/postfix/mysql-virtual-mailbox-maps.cf, mysql:/etc/postfix/mysql-virtual-alias-maps.cf
# ================== 邮件传输方式 ==================
virtual_transport = lmtp:unix:private/dovecot-lmtp
# ================== 中继服务配置(Outbound 邮件路由) ==================
# TLS for outbound (SMTP client)
# 指定中继服务器(Resend SMTP)
relayhost = [smtp.resend.com]:587
# 启用 SASL 认证
smtp_sasl_auth_enable = yes
smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd
smtp_sasl_security_options = noanonymous
# 启用 TLS 加密
smtp_use_tls = yes
smtp_tls_security_level = encrypt
# 465 端口强制 SSL/TLS 加密
# smtptlswrappermode = yes
# 587 端口使用 STARTTLS 加密
smtp_tls_wrappermode = no
smtp_tls_CAfile = /etc/ssl/certs/ca-certificates.crt
smtp_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1, TLSv1.2, TLSv1.3
smtp_tls_ciphers = HIGH:!aNULL:!MD5
smtp_tls_loglevel = 1
smtp_tls_session_cache_database = btree:${data_directory}/smtp_tls_cache
# ================== SASL 认证(Dovecot 集成) ==================
smtpd_sasl_type = dovecot
smtpd_sasl_path = private/auth
smtpd_sasl_auth_enable = yes
smtpd_relay_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_unauth_destination
# ================== SMTP 安全限制 ==================
smtpd_recipient_restrictions =
permit_mynetworks,
# 确保在 reject_non_fqdn_helo_hostname 前面
permit_sasl_authenticated,
reject_invalid_helo_hostname,
reject_non_fqdn_helo_hostname,
reject_unknown_hostname,
reject_unknown_recipient_domain,
reject_rbl_client zen.spamhaus.org,
reject_unauth_destination,
check_policy_service unix:private/policyd-spf
smtpd_sender_restrictions =
reject_non_fqdn_sender,
reject_unknown_sender_domain,
reject_sender_login_mismatch
# ================== TLS 加密传输 ==================
# TLS for inbound (SMTP server)
smtpd_tls_cert_file = /etc/letsencrypt/live/mail.example.com/fullchain.pem
smtpd_tls_key_file = /etc/letsencrypt/live/mail.example.com/privkey.pem
smtpd_tls_received_header = yes
smtpd_tls_ciphers = high
smtpd_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
tls_preempt_cipherlist = yes
smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache
# ================== 策略服务 ==================
policyd-spf_time_limit = 10s
# ================== 邮箱大小限制 ==================
mailbox_size_limit = 0
message_size_limit = 102400000
recipient_delimiter = +
# ================== 其他基础设置 ==================
smtp_address_preference = ipv4
smtpd_banner = ESMTP
disable_vrfy_command = yes
strict_rfc821_envelopes = yes
append_dot_mydomain = no
biff = no
readme_directory = no
# ================== 版本兼容 ==================
compatibility_level = 3.6
# ================== UID/GID 设置 ==================
virtual_uid_maps = static:2000
virtual_gid_maps = static:2000
# 启用 Milter(OpenDKIM)
milter_default_action = accept
smtpd_milters = unix:/var/run/opendkim/opendkim.sock
non_smtpd_milters = $smtpd_milters
mydomain
:表示邮件系统的主域名,是你拥有、用于收发电子邮件的“根域”。即使使用的是二级域名(如mail.example.com
)作为邮件服务器主机名,mydomain
仍然应设为顶级域名example.com
。- 对邮件服务器主机域名
mail.example.com
申请 Let's Encrypt 证书,使用 Certbot 实现自动续签。
模块级配置文件
该文件配置 Postfix 中各模块的参数。
编辑 master.cf
文件:
vim /etc/postfix/master.cf
示例配置:
# ==========================================================================
# service type private unpriv chroot wakeup maxproc command + args
# (yes) (yes) (yes) (never) (100)
# ==========================================================================
# SMTP 接收服务(默认监听 25 端口)
smtp inet n - n - - smtpd
# 提交端口(Submission,推荐用于客户端发送邮件,端口 587)
submission inet n - n - - smtpd
-o syslog_name=postfix/submission
-o smtpd_tls_security_level=encrypt
-o smtpd_sasl_security_options=noanonymous
-o smtpd_sasl_local_domain=$myhostname
-o smtpd_client_restrictions=permit_sasl_authenticated,reject
# 安全 SMTP 端口(SMTPS,端口 465,已逐步弃用)
smtps inet n - n - - smtpd
-o syslog_name=postfix/smtps
-o smtpd_tls_wrappermode=yes
-o smtpd_sasl_security_options=noanonymous
-o smtpd_sasl_local_domain=$myhostname
-o smtpd_client_restrictions=permit_sasl_authenticated,reject
# Pickup 服务:从 maildrop 目录中提取由本地用户提交的邮件
pickup unix n - n 60 1 pickup
# Cleanup 服务:对邮件进行标准化处理
cleanup unix n - n - 0 cleanup
# QMGR 邮件队列管理器
qmgr unix n - n 300 1 qmgr
# TLS 会话缓存管理器
tlsmgr unix - - n 1000? 1 tlsmgr
# 地址重写服务
rewrite unix - - n - - trivial-rewrite
# 邮件投递失败通知
bounce unix - - n - 0 bounce
defer unix - - n - 0 bounce
trace unix - - n - 0 bounce
# SPF 政策守护进程
verify unix - - n - 1 verify
# 刷新队列(如 DNS 故障后恢复)
flush unix n - n 1000? 0 flush
# Proxy 映射服务
proxymap unix - - n - - proxymap
proxywrite unix - - n - 1 proxymap
# 外发邮件服务(smtp)
smtp unix - - n - - smtp
# Relay 服务(转发)
relay unix - - n - - smtp
-o smtp_helo_timeout=120
-o smtp_connect_timeout=120
# 队列查看工具
showq unix n - n - - showq
# 错误模拟服务
error unix - - n - - error
retry unix - - n - - error
# 丢弃邮件(测试用途)
discard unix - - n - - discard
# 本地邮箱投递
local unix - n n - - local
# 虚拟邮箱投递
virtual unix - - n - - virtual
# LMTP 投递(用于 Dovecot)
lmtp unix - - n - - lmtp
# 资源限制与统计服务
anvil unix - - n - 1 anvil
scache unix - - n - 1 scache
# Policyd SPF 插件
policyd-spf unix - n n - 0 spawn
user=mail_sys argv=/usr/bin/policyd-spf
policyd-spf
插件:一般安装路径:/usr/bin/policyd-spf
,确保在 /etc/postfix/master.cf
中正确配置了 policyd-spf
服务监听的位置。
policyd-spf unix - n n - 0 spawn
user=mail_sys argv=/usr/bin/policyd-spf
另外,在 /etc/postfix/main.cf
中添加或修改相关的策略服务调用。让 Postfix 在处理邮件时调用 policyd-spf
来执行 SPF 检查。
smtpd_recipient_restrictions =
...
check_policy_service unix:private/policyd-spf
private/policyd-spf
是相对于 Postfix 的 chroot 环境下的private
目录的一个相对路径,即/var/spool/postfix/private/policyd-spf
(假设你的 Postfix 安装目录是默认的/var/spool/postfix
)。
与数据库建立连接
指定域名数据表
命令交互式输入内容:
- 命令结构:
cat > /etc/postfix/mysql-virtual-mailbox-domains.cf << EOF
# 输入的内容在这里
EOF
cat
: 用于连接文件并显示文件内容的命令。>
: 重定向符号,用于将输出写入文件。/etc/postfix/mysql-virtual-mailbox-domains.cf
: 要写入的文件路径。<< EOF
是一种 shell 的 Here Document 语法,它允许你在脚本中嵌入多行文本。在这个例子中,EOF
是结束标记,表示用户输入的内容将一直写入到遇到EOF
为止。
命令执行:
cat > /etc/postfix/mysql-virtual-mailbox-domains.cf << EOF
以下修改内容直接粘贴到命令行窗口回车即可。
user = mail_sys
password = mail_sys
hosts = 127.0.0.1
dbname = mail_sys
query = SELECT id FROM domains WHERE name='%s';
EOF
指定用户数据表
命令交互式输入内容:
cat > /etc/postfix/mysql-virtual-mailbox-maps.cf << EOF
以下内容直接粘贴到命令行回车即可。
user = mail_sys
password = mail_sys
hosts = 127.0.0.1
dbname = mail_sys
query = SELECT email FROM users WHERE email='%s';
EOF
指定别名数据表
命令交互式输入内容:
cat > /etc/postfix/mysql-virtual-alias-maps.cf << EOF
以下内容直接粘贴到命令行回车即可。
user = mail_sys
password = mail_sys
hosts = 127.0.0.1
dbname = mail_sys
query = SELECT destination FROM aliases WHERE source='%s';
EOF
测试数据库读取功能
在完成 Postfix 的数据库相关配置后,建议使用 postmap -q
命令进行测试,确保 Postfix 能够正确读取 MySQL 数据库中的虚拟域名、用户和别名信息。
>修改完数据库或配置文件后,务必重启或重载 Postfix 服务以使更改生效。
启动并管理服务
# 重新加载配置
postfix reload
# 检查 Postfix 配置语法
postfix check
# 查看当前生效配置
postconf -n
# 启动并管理服务
sudo systemctl start postfix
sudo systemctl enable postfix
# 查看状态
sudo systemctl status postfix
测试虚拟域名查询
数据库中存储的邮箱域名地址为 example.com
执行命令:
[root@mail]# postmap -q example.com mysql:/etc/postfix/mysql-virtual-mailbox-domains.cf
1
- 输出
1
表示该域名example.com
已被识别为有效的虚拟域名。 - 若无输出,表示未找到该域名,请检查数据库连接及查询语句。
测试虚拟用户查询
执行命令:
[root@mail]# postmap -q user2@example.com mysql:/etc/postfix/mysql-virtual-mailbox-maps.cf
user2@example.com
- 返回完整的邮箱地址表示该用户存在且配置正确。
- Postfix 会将邮件投递至该用户的邮箱目录。
测试别名查询
执行命令:
[root@mail]# postmap -q user11@example.com mysql:/etc/postfix/mysql-virtual-alias-maps.cf
user1@example.com
- 返回目标邮箱地址表示别名配置正确。
- 发送给
user11@example.com
的邮件将被转发至user1@example.com
。
验证配置是否生效
方法1:发送测试邮件检查日志
使用 mail
命令发送测试邮件:
echo "This is a test from Resend via Postfix" | mail -s "Test Email via Resend" example@qq.com
查看 Postfix 日志以确认连接状态:
sudo tail -f /var/log/mail.log
成功预期如下类似信息:
mail postfix/smtp[294107]: Trusted TLS connection established to smtp.resend.com[54.157.71.137]:587
方法2:使用 telnet/openssl 测试连接
测试端口 587(STARTTLS):
openssl s_client -connect smtp.resend.com:587 -starttls smtp
成功后应看到 SMTP 服务响应。
配置 Dovecot
备份默认配置文件
执行命令:
cp -r /etc/dovecot /etc/dovecot.bak
全局配置文件
/etc/dovecot/dovecot.conf
,配置 Dovecot 的全局参数。
执行命令:
cat > /etc/dovecot/dovecot.conf << EOF
!include_try /usr/share/dovecot/protocols.d/*.protocol
protocols = imap pop3 lmtp
dict {
}
!include conf.d/*.conf
!include_try local.conf
base_dir = /var/run/dovecot/
login_greeting = Welcome to Dovecot Mail Server
EOF
增加打印调试信息:
auth_verbose = yes
mail_debug = yes
邮箱存储配置
/etc/dovecot/conf.d/10-mail.conf
文件配置邮箱文件存储的位置和命令空间。
执行命令:
cat > /etc/dovecot/conf.d/10-mail.conf << EOF
mail_location = maildir:/var/spool/mail/%d/%n
namespace inbox {
inbox = yes
}
mail_uid = mail_sys
mail_gid = mail_sys
# Typically this is set to "mail" to give access to /var/mail.
mail_privileged_group = mail
mail_access_groups = mail
first_valid_uid = 1000
protocol !indexer-worker {
}
EOF
说明:
mail_uid
指定用于送达邮件的用户的 UIDmail_gid
指定用于送达邮件的组的 GIDmail_privileged_group
对邮箱目录的特权访问权限的组。mail_access_groups
指定访问组,即可以访问邮箱目录的组的列表。用逗号分隔组名。
邮箱目录结构
conf.d/15-mailboxes.conf
文件配置邮箱内部的目录结构。
执行命令:
cat > /etc/dovecot/conf.d/15-mailboxes.conf << EOF
namespace inbox {
mailbox Drafts {
auto = create
special_use = \Drafts
}
mailbox Junk {
special_use = \Junk
}
mailbox Trash {
auto = create
special_use = \Trash
}
mailbox Sent {
auto = create
special_use = \Sent
}
mailbox "Sent Messages" {
special_use = \Sent
}
}
EOF
说明:auto = create
自动创建,但没有自动订阅。
用户认证配置
修改 conf.d/10-auth.conf
,配置用户身份认证流程。
执行命令:
cat > /etc/dovecot/conf.d/10-auth.conf << EOF
# 指定Dovecot作为认证后端
auth_mechanisms = plain login
#!include auth-system.conf.ext
!include auth-sql.conf.ext
EOF
说明:
auth_mechanisms
指定了 Dovecot 支持的认证机制。plain
:使用明文文本进行身份验证。login
:通过安全套接字层(SSL)或传输层安全性(TLS)加密的方式在网络上安全地传输用户凭据。digest-md5
、cram-md5
等是更为安全的认证机制,但需要额外配置依赖。
SQL 认证扩展
修改 conf.d/auth-sql.conf.ext
文件,确保 Dovecot 通过 MySQL 数据库进行用户认证,并指定邮件存储路径。
执行命令:
cat > /etc/dovecot/conf.d/auth-sql.conf.ext << EOF
passdb {
driver = sql
args = /etc/dovecot/dovecot-sql.conf.ext
}
userdb {
driver = static
args = uid=mail_sys gid=mail_sys home=/var/spool/mail/%d/%n
}
EOF
数据库连接配置
dovecot-sql.conf.ext
文件与 /etc/dovecot/conf.d/auth-sql.conf.ext
中的 passdb
配置项相关联。
修改该文件,配置验证用户名密码所用的数据表及认证方法。
执行命令:
cat > /etc/dovecot/dovecot-sql.conf.ext << EOF
driver = mysql
connect = host=127.0.0.1 dbname=mail_sys user=mail_sys password=mail_sys
default_pass_scheme = SHA512-CRYPT
password_query = SELECT email as user, password FROM mail_sys.users WHERE email='%u';
EOF
SSL/TLS 配置
conf.d/10-ssl.conf
文件配置 SSL 加密参数。
执行命令:
cat > /etc/dovecot/conf.d/10-ssl.conf << EOF
ssl = yes
ssl_cert = </etc/letsencrypt/live/mail.example.com/fullchain.pem
ssl_key = </etc/letsencrypt/live/mail.example.com/privkey.pem
# 最低允许的协议版本(推荐 TLSv1.2 或 TLSv1.3)
ssl_min_protocol = TLSv1.2
# 允许的加密套件(推荐现代安全标准)
ssl_cipher_list = ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
# 优先使用服务器端定义的 cipher 顺序
ssl_prefer_server_ciphers = yes
# 启用 DH parameters(可选)
# ssldhparameterslength = 2048
EOF
说明:
ssl_min_protocol
指定要使用的最小 SSL 协议版本。控制 Dovecot 所接受的 SSL 连接的最低安全标准。ssl_cipher_list
指定了 SSL 密码列表。密码列表中的密码用于协商 SSL 连接时的加密算法。
>注意:替换实际生成域名证书的文件路径。
主服务监听配置
conf.d/10-master.conf
文件配置主服务监听的各种参数。
执行命令:
cat > /etc/dovecot/conf.d/10-master.conf << EOF
# IMAP 登录
service imap-login {
inet_listener imap {
port = 143
}
inet_listener imaps {
port = 993
ssl = yes
}
}
# POP3 登录(可选)
service pop3-login {
inet_listener pop3 {
port = 110
}
inet_listener pop3s {
port = 995
ssl = yes
}
}
# LMTP 用于 Postfix 投递
service lmtp {
unix_listener /var/spool/postfix/private/dovecot-lmtp {
mode = 0600
user = postfix
group = postfix
}
}
# SASL 认证服务
service auth {
unix_listener auth-userdb {
mode = 0600
user = mail_sys
group = mail_sys
}
# Postfix smtp-auth
unix_listener /var/spool/postfix/private/auth {
mode = 0600
user = postfix
group = postfix
}
}
# 执行数据库查询
service auth-worker {
#user = root
user = mail_sys
}
# 高并发连接限制
service imap {
# Max. number of IMAP processes (connections)
#process_limit = 1024
}
service pop3 {
# Max. number of POP3 processes (connections)
#process_limit = 1024
}
EOF
说明:
mode
字段用于指定 UNIX 监听器(UNIX listener)创建的文件的权限模式。unix_listener /var/spool/postfix/private/dovecot-lmtp
:Postfix 使用 LMTP 协议将邮件投递给 Dovecot。- 所有发往虚拟用户的邮件,都通过
/var/spool/postfix/private/dovecot-lmtp
这个 socket 文件,使用 LMTP 协议发送给 Dovecot 处理。 - 确保 Postfix 的
main.cf
中有如下配置:virtual_transport = lmtp:unix:private/dovecot-lmtp
- 所有发往虚拟用户的邮件,都通过
LDA 配置
修改 conf.d/15-lda.conf
文件,配置特定域的 postmaster
邮箱。如不设定 postmaster
邮箱的话,可能会导致收不到邮件。
执行命令:
cat > /etc/dovecot/conf.d/15-lda.conf << EOF
postmaster_address = postmaster@%d
protocol lda {
}
EOF
说明:
postmaster_address
用于指定邮件系统中的postmaster
地址。通常情况下,postmaster
地址是用于接收系统相关的通知和错误报告的邮箱地址。%d
是一个变量,表示域名。
启动 Dovecot 服务
启动服务并查看服务状态是否正常。
systemctl enable dovecot
systemctl start dovecot
systemctl status dovecot
配置 OpenDKIM
DomainKeys Identified Mail (DKIM) 是一种电子邮件认证机制,通过在邮件头中添加一个数字签名,来验证邮件是否确实来自你所声称的域名,并且内容未被篡改。
OpenDKIM 是 DKIM(DomainKeys Identified Mail)电子邮件身份验证标准的开源实现。配置和管理 OpenDKIM 可以帮助提高电子邮件系统的安全性和可信度。
备份默认配置文件
cp -r /etc/opendkim.conf /etc/opendkim.conf.bak
修改主配置文件
修改 OpenDKIM 主配置文件:
vim /etc/opendkim.conf
添加或修改以下内容:
# 日志设置
Syslog yes
SyslogSuccess yes
# 操作模式:s=签名,v=验证,sv=两者都启用
Mode sv
# Canonicalization 模式
Canonicalization relaxed/simple
# Oversign headers
OversignHeaders From
# 域名(请替换为你自己的主域名)
Domain example.com
# Selector(DNS 中使用的标签)
Selector mail
# 私钥文件路径(需确认文件真实存在)
KeyFile /etc/opendkim/keys/mail.private
# 用户与权限
UserID opendkim
UMask 007
# Socket 地址(Postfix 使用此路径连接 OpenDKIM)
Socket local:/run/opendkim/opendkim.sock
# PID 文件
PidFile /run/opendkim/opendkim.pid
# DNSSEC 信任锚点
#TrustAnchorFile /usr/share/dns/root.key
# 自动发送失败报告
SendReports yes
# 添加签名软件头
SoftwareHeader yes
# 最小密钥长度(安全增强)
MinimumKeyBits 2048
创建并设置 Socket 目录权限
mkdir -p /run/opendkim
chown opendkim:opendkim /run/opendkim
chmod 750 /run/opendkim
# 确保 postfix 用户能访问 socket
usermod -aG opendkim postfix
配置签名规则(可选)
创建签名规则文件(适用于多域名):
cat > /etc/opendkim/SigningTable << EOF
*@example.com mail._domainkey.example.com
EOF
cat > /etc/opendkim/KeyTable << EOF
mail._domainkey.example.com example.com:mail:/etc/opendkim/keys/example.private
EOF
cat > /etc/opendkim/TrustedHosts << EOF
127.0.0.1
::1
localhost
example.com
*.example.com
EOF
配置 Postfix 集成
Postfix 将邮件发送给 Milter(用于 DKIM 签名),确保 Postfix 中的配置与 OpenDKIM 匹配:
将以下内容追加到 /etc/postfix/main.cf
:
cat >> /etc/postfix/main.cf << 'EOF'
# 启用 Milter(OpenDKIM)
milter_default_action = accept
smtpd_milters = unix:/var/run/opendkim/opendkim.sock
non_smtpd_milters = $smtpd_milters
EOF
- 如果 OpenDKIM 使用的是 TCP 模式,请改为:
smtpd_milters = inet:localhost:8891
>使用 << 'EOF'
中的单引号会禁用整个内容块的变量扩展,避免被 shell 解释。
启动并管理服务
执行命令:
systemctl restart postfix opendkim
systemctl enable postfix opendkim
验证邮件签名
发送一封测试邮件,检查邮件是否可以发送并被对方接收:
echo "Test email body" | mail -s "Test Subject" example@qq.com
检查邮件签名状态,查看邮件头部是否有 DKIM-Signature
字段:
tail -f /var/log/mail.log
journalctl -u opendkim -f
部署 Web 邮箱系统
配置 Nginx
Nginx 是一个高性能开源的 Web 服务器,也常用于反向代理、负载均衡等场景。本节将配置其作为 Roundcube Web 邮箱的前端服务。
创建配置文件:
vim /etc/nginx/conf.d/mail.conf
根据实际情况填写以下内容:
server {
listen 80;
server_name mail.example.com; # 配置邮件服务器域名
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name mail.example.com; # 配置邮件服务器域名
# 域名证书文件位置
ssl_certificate "/etc/letsencrypt/live/mail.example.com/fullchain.pem";
ssl_certificate_key "/etc/letsencrypt/live/mail.example.com/privkey.pem";
add_header Strict-Transport-Security "max-age=15552000; includeSubDomains";
# 兼容性更好的协议版本
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers EECDH+CHACHA20:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:!MD5;
ssl_prefer_server_ciphers on;
client_max_body_size 20M; # 限制上传大小
root /usr/share/roundcube;
index index.php index.html index.htm;
location / {
# 路由请求到 index.php 文件
try_files $uri $uri/ /index.php$is_args$args;
}
location ~ \.php$ {
include fastcgi_params;
fastcgi_pass unix:/run/php/php8.2-fpm.sock; # 推荐使用 Unix Socket 提升性能
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param HTTPS on; # 确保 PHP 检测到 HTTPS
}
location ~ /\.ht {
deny all;
}
# 添加日志路径以方便调试
access_log /var/log/nginx/roundcube.access.log;
error_log /var/log/nginx/roundcube.error.log;
}
说明:
fastcgi_pass
推荐使用 Unix 套接字(如/run/php/php-fpm.sock
)以提升性能和安全性。
检查配置语法:
nginx -t
配置 PHP
编辑 PHP 主配置文件:
sudo vim /etc/php/8.2/fpm/php.ini
设置时区
date.timezone = Asia/Shanghai
配置 FPM 套接字
修改 PHP-FPM 配置文件(通常位于 /etc/php/{版本}/fpm/pool.d/www.conf
):
# 将监听方式改为 Unix 套接字
listen = /run/php/php8.2-fpm.sock
# 设置套接字权限(确保 Nginx 可读写)
listen.owner = www-data # Nginx 进程用户(根据实际调整)
listen.group = www-data # Nginx 进程组
listen.mode = 0660 # 所有者与组可读写
创建会话目录
PHP 在处理用户会话(session
)时,需要一个服务器上的本地目录来存储 session
文件。这些文件用于保存用户的登录状态、临时数据等。
默认情况下,PHP 会在配置文件 php.ini
中指定一个路径来保存 session
数据。
比如:检查 /etc/php/{版本}/fpm/php.ini
中是否设置:
session.save_path = "/var/lib/php/session"
创建会话目录,并设置 PHP(通过 Web 服务器运行)读写权限,执行以下命令:
sudo mkdir -p /var/lib/php/session
sudo chown www-data:www-data /var/lib/php/session
/var/lib/php/session
所属用户和组应与/etc/php/{版本}/fpm/pool.d/www.conf
中user
和group
字段值保持一致。
启动服务
sudo systemctl start nginx php8.2-fpm
sudo systemctl enable nginx php8.2-fpm
配置 Roundcube
Roundcube 是一款基于 Web 的开源邮件客户端,支持 IMAP 和 SMTP 协议,用户可通过浏览器访问邮箱。
安装 Roundcube 客户端
下载并解压至指定目录
cd /tmp
wget https://github.com/roundcube/roundcubemail/releases/download/1.6.6/roundcubemail-1.6.6-complete.tar.gz
sudo tar -xf roundcubemail-1.6.6-complete.tar.gz
sudo mv roundcubemail-1.6.6 /usr/share/roundcube
设置权限
sudo chown -R www-data:www-data /usr/share/roundcube
sudo chmod -R 755 /usr/share/roundcube
sudo chmod -R 775 /usr/share/roundcube/{temp,logs}
创建 Roundcube 数据库
为 Roundcube 创建一个单独的数据库用于存储用户的配置信息、联系人列表、身份验证数据(如 IMAP 和 SMTP 服务器的设置)、消息搜索索引等。
进入 MySQL 命令行:
mysql -u root -p
执行以下 SQL 语句:
-- 创建数据库
CREATE DATABASE roundcubemail DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- 创建专用用户并授权
CREATE USER 'roundcube'@'localhost' IDENTIFIED BY 'your_secure_password';
-- 授权
GRANT ALL PRIVILEGES ON roundcubemail.* TO 'roundcube'@'localhost';
FLUSH PRIVILEGES;
配置 Roundcube 客户端
如果配置文件不存在,复制示例配置文件:
sudo cp /usr/share/roundcube/config/config.inc.php.sample /usr/share/roundcube/config/config.inc.php
设置文件权限:
sudo chown -R www-data:www-data /usr/share/roundcube/config/config.inc.php
sudo chmod -R 755 /usr/share/roundcube/config/config.inc.php
编辑配置文件:
sudo vim /usr/share/roundcube/config/config.inc.php
添加以下配置项:
<?php
$config['db_dsnw'] = 'mysql://roundcube:your_secure_password@127.0.0.1/roundcubemail';
// IMAP 设置
$config['imap_host'] = 'ssl://mail.example.com:993';
$config['imap_auth_type'] = 'PLAIN';
// SMTP 设置
$config['smtp_host'] = 'tls://mail.example.com:587';
$config['smtp_user'] = '%u';
$config['smtp_pass'] = '%p';
$config['smtp_auth_type'] = 'LOGIN';
// 系统设置
$config['support_url'] = 'https://mail.example.com/support';
$config['ip_check'] = true;
$config['username_domain'] = 'example.com';
$config['language'] = 'zh_CN';
$config['plugins'] = ['archive', 'zipdownload'];
// 开启安装器(仅首次使用)
$config['enable_installer'] = true;
- 使用
127.0.0.1
而非localhost
,避免因socket
连接导致的认证问题。 - 推荐通过访问 Roundcube 安装页面,设置配置项并更新至配置文件中。
安装 GuzzleHttp(可选)
使用 Composer 安装 GuzzleHttp(解决 GuzzleHttp\Client)
安装 Composer(如果还没安装):
sudo apt install composer
进入 Roundcube 目录并安装依赖:
cd /usr/share/roundcube
sudo composer require guzzlehttp/guzzle
这会自动下载并安装 GuzzleHttp
到 vendor/
文件夹。
启动服务并设置开机自启
sudo systemctl start nginx php8.2-fpm
sudo systemctl enable nginx php8.2-fpm
访问 Roundcube 安装页面
在浏览器中访问:
https://mail.example.com/installer/
- 确保
mail.example.com
已正确指向 Roundcube 服务的 Web 服务器。
检查环境依赖
PHP 必需扩展模块和可选扩展模块均正常安装和启用,如下所示:
检查配置项
检查数据库连接、SMTP 和 IMAP 连接等配置内容:
最后,在安装页面测试 SMTP/IMAP 连接是否正常。如下所示:
完成后 删除 installer
目录 并关闭安装器选项:
$config['enable_installer'] = false;
安全加固建议
自动清理 session
文件(每天凌晨执行)
设置 session
清理周期任务:
sudo crontab -e
添加:
0 0 * * * find /var/lib/php/session -type f -mtime +7 -delete
测试邮件发送与接收
使用 Roundcube 登录邮箱:
https://mail.example.com/
测试邮件收发:
测试个人邮件服务器内部电子邮件地址互发,以及与外部邮件服务器的电子邮件地址互发,验证连通状态。
使用 mail-tester.com 检查配置:
使用 https://www.mail-tester.com/ ,向其随机生成的邮件地址发送一封邮件,检查 SPF/DKIM/DMARC 配置是否正确,测试邮件得分情况。
例如:发送邮件
echo "Test mail" | mail -s "Test Subject" -r user1@example.com test-nsfo2jurz@srv1.mail-tester.com
测试邮件得分:
安全与最佳实践
类别 | 建议 |
---|---|
邮件安全 | 启用 SPF、DKIM、DMARC 防止伪造邮件 |
日志审计 | 启用日志记录,定期检查可疑行为 |
权限控制 | 限制用户权限,避免越权操作 |
自动化运维 | 使用 Ansible、SaltStack 等自动化部署工具 |
定期备份 | 定期备份用户数据、配置文件、证书 |
监控报警 | 使用 Zabbix、Prometheus 监控邮件服务状态 |
后续可拓展功能
功能 | 描述 |
---|---|
SpamAssassin | 邮件内容过滤,识别垃圾邮件 |
ClamAV | 邮件病毒扫描 |
Sieve | 邮件规则过滤(Dovecot 支持) |
Rspamd / OpenDMARC | 更强的邮件反垃圾机制 |
多租户支持 | 支持多个域名、多个组织的邮件服务 |
参考
Configure an Email Server with Postfix, Dovecot, and MySQL on Debian and Ubuntu