在 Debian 11 上设置邮件服务
本教程及后续教程将介绍如何搭建带有网页界面的邮件服务器。本部分将介绍启用服务器接收邮件和从服务器发送邮件的技术细节。第二和第三篇教程将分别介绍如何 转发最终用户的电子邮件 以及如何设置 网页邮件界面。
使用您自己的邮件服务器的一个优势是,您可以轻松设置联系表单,让用户通过该表单向您发送邮件。
安装和运行邮件服务器所需的最低硬件配置为:1 个虚拟中央处理器、1 GB 内存和 3 GB 硬盘空间。0. 打开端口
要设置邮件服务器,您需要打开特定的端口以允许邮件的收发。需要打开的端口取决于您计划使用的电子邮件协议。最常见的电子邮件协议是用于发送电子邮件的 SMTP(简单邮件传输协议)以及用于接收电子邮件的 IMAP(互联网消息访问协议)或 POP3(邮局协议)。以下是通常与这些协议关联的端口:SMTP:
端口 25:这是 SMTP 的默认端口,用于发送邮件。IMAP:
端口 143:这是 IMAP 的默认端口,用于接收邮件。它不安全,通常需要配合 STARTTLS 或 SSL/TLS 加密使用。端口 993:这是安全的 IMAP 端口,用于使用 IMAPS(基于 SSL/TLS 的 IMAP)加密。
POP3:端口 110:这是 POP3 的默认端口,用于接收未加密的邮件。
端口 995:这是安全的 POP3 端口,用于使用 POP3S(基于 SSL/TLS 的 POP3)加密时。在本教程中,我们仅使用 SMTP 和 IMAP,因此需要打开以下端口:
- SMTP(入站):端口 25,端口 587 (用于客户端安全提交邮件)
- IMAP(入站):端口 143,端口 993(可选,带 SSL/TLS)
- SMTP(出站):端口 25(可选。许多云服务提供商会屏蔽此端口。Hetzner 将在 1 个月后解除屏蔽。)
请登录您的网站并打开上述端口。此外,还需要为网络服务打开 HTTP(入站):端口 80 和 HTTPS(入站):端口 443。
1. 软件安装
以下教程适用于 Debian 11 上的 MariaDB。对于 Debian 12 上的 PostgreSQL,请参阅 Debian 12 上的邮件服务器教程。
在 Linux 系统上,需要安装一系列软件:pwgen(生成随机密码)、MariaDB 服务器(数据库管理系统)、postfix(邮件服务器)、Apache 和 PHP(网络服务器)、swaks(测试邮件发送)和 mutt(在命令行中查看电子邮件)。用于存储电子邮件的附加软件是 dovecot。
pwgen
让我们从 pwgen 工具开始。 它可以帮助您创建安全的密码。除非您已经有了类似的工具……sudo apt install -y pwgen
稍后您需要一个随机密码来为邮件服务器创建数据库用户。例如:要创建一个长度为 20 个字符的安全密码,您可以运行:pwgen -s 20 1
您将获得一个类似 “W2EzNUFJzjEmA8tQT7A0” 的字符串。MariaDB server
如果您之前使用过 MySQL,您可能还记得必须为“root”数据库用户指定密码。但在 Debian 系统上的 MariaDB 中,这种情况已经改变了——而且是朝着更好的方向改变。现在,如果您以“root”用户身份登录服务器,则无需密码即可访问数据库服务器。您当然也可以设置密码,但这并非必需。现在安装 MariaDB 服务器软件包:
sudo apt install -y mariadb-server
如果一切顺利,您现在可以运行“mysql”命令并连接到您的 MySQL 数据库:
azureuser@md:~# sudo mysql
欢迎使用 MariaDB 监视器。命令以 ; 或 \g 结尾。
您的 MariaDB 连接 ID 为 395
服务器版本:10.5.29-MariaDB-0+deb11u1 Debian 11
版权所有 (c) 2000, 2018, Oracle, MariaDB Corporation Ab 和其他公司。
输入“help;”或“h”获取帮助。输入“c”清除当前输入语句。
输入“exit”或按 CTRL-D 退出 SQL shell。
Postfix
现在安装 Postfix 软件包:sudo apt install postfix
sudo apt install postfix-mysql
Apache 和 PHP
要提供网页邮件服务,您需要 Apache 网络服务器软件和 PHP 脚本语言支持:sudo apt install -y apache2 php
swaks
SWAKS(SMTP 的瑞士军刀)是一个非常实用的工具,可用于测试电子邮件发送功能:sudo apt install -y swaks
mutt
这是一个功能齐全的 IMAP 邮件客户端。可以把它想象成邮件客户端中的 vi 编辑器。它无法显示 HTML 邮件,但对于测试 IMAP 邮件服务器非常有用。而且一些资深用户仍然更喜欢它而不是其他任何邮件客户端。sudo apt install -y mutt
Dovecot
除了 Postfix(用于处理 SMTP 通信)之外,您还需要 Dovecot 来存储接收到的电子邮件,并为您的用户提供 IMAP(以及可选的 POP3)访问:sudo apt install -y dovecot-mysql dovecot-pop3d dovecot-imapd dovecot-managesieved dovecot-lmtpd
Adminer
SQL 数据库之所以这样命名,是因为 SQL(结构化查询语言)是与数据库交互的方式。但由于我们只是普通人,所以需要一种更友好的方式来管理数据库。我推荐使用 Adminer,它是一款类似于 phpMyAdmin 的工具。sudo apt install -y adminer
2. 配置 Apache 网络服务器以支持 HTTP
让我们从网络服务器开始。我假设您想为用户提供主机名 webmail.example.org。当然,您的服务器会使用您的域名。在本教程中,我将始终使用这个示例,并将该名称以粗体显示,以提醒您需要使用自己的主机名。
您只是想试用一下新服务器,暂时不想使用独立域名吗?没问题。您可以使用 nip.io 或在 Azure 门户中设置 DNS 名称标签,这将为您的网站创建一个子域名。如果您拥有独立域名,则需要为该主机设置指向服务器 IP 地址的 DNS “A”记录或“AAAA”记录(如果您使用 IPv6)。打开浏览器,访问您网站的 DNS 名称。您应该会看到类似以下内容:
首先,您需要为该主机创建一个网络根目录:
sudo mkdir /var/www/webmail.example.org
sudo chown www-data:www-data /var/www/webmail.example.org
- /etc/apache2/sites-available/*.conf 包含每个虚拟主机的实际配置文件。但是,将文件放在此处并不会启用该主机。启用操作将在下一步完成。默认情况下有两个配置文件。“000-default.conf”是 HTTP 虚拟主机,“default-ssl.conf”是 HTTPS 虚拟主机。
- /etc/apache2/sites-enabled/*.conf 包含指向 /etc/apache2/sites-available 目录中配置文件的符号链接(“软链接”)。只有此目录中的 *.conf 链接才会被 Apache 加载。
除非您已经在使用默认的符号链接,否则可以删除 /etc/apache2/sites-enabled/* 中的默认符号链接。
创建一个新的虚拟主机配置文件 /etc/apache2/sites-available/webmail.example.org-http.conf,并添加以下内容:
<VirtualHost *:80>
ServerName webmail.example.org
DocumentRoot /var/www/webmail.example.org
</VirtualHost>
启用该站点:
sudo a2ensite webmail.example.org-http
您将收到以下提示:
要激活新配置,您需要运行:
systemctl reload apache2
您可能需要在命令开头添加 sudo: sudo systemctl reload apache2
3. 获取 Let's Encrypt 证书
安装 Let's Encrypt 证书的方法有很多种。以下是基于 Linux (snapd) 的安装方法:安装系统依赖项
系统依赖项包括snapd:sudo apt update
sudo apt install snapd
接下来,使用 snap 命令安装核心 snap。这将会在您的服务器上安装 snap 应用(包括 Certbot snap)所需的依赖项:
sudo snap install core
然后刷新核心 snap。这样做可以确保您安装的是最新版本的 snapd 及其依赖项:sudo snap refresh core
安装 Certbot
请注意,snap 软件包可以安装在三种不同的隔离级别下,这些级别提供不同程度的系统隔离。例如,大多数 snap 软件包默认安装在严格隔离级别下,这会阻止这些程序访问系统的文件或网络。由于 Certbot 必须能够编辑某些配置文件才能正确设置证书,因此此命令包含 --classic 选项。此隔离级别允许在此级别下安装的任何 snap 软件包拥有与传统软件包相同的系统资源访问权限。考虑到这一点,您可以使用以下命令安装 certbot snap 软件包。
sudo snap install --classic certbot
准备并运行 Certbot 命令
此安装过程会将 certbot 可执行文件安装到 /snap/bin/ 目录。在 /usr/bin/ 目录中创建指向此文件的符号链接,以确保您可以在系统的任何位置运行 certbot 命令:
sudo ln -s /snap/bin/certbot /usr/bin/certbot
Certbot 通过插件提供多种获取 SSL 证书的方法。Apache 插件会在必要时重新配置 Apache 并重新加载配置。要使用此插件,请运行以下命令:sudo certbot --apache -d your_domain -d www.your_domain -d anothername.com
这将使用 --apache 插件运行 certbot,并使用 -d 指定您的域名。如果您是首次运行 certbot,系统会提示您输入电子邮件地址并同意服务条款。此外,它还会询问您是否愿意与电子前沿基金会 (Electronic Frontier Foundation) 共享您的电子邮件地址。电子前沿基金会是一个倡导数字权利的非营利组织,也是 Certbot 的开发者。您可以输入 Y 共享您的电子邮件地址,或输入 N 拒绝。
完成上述步骤后,certbot 将与 Let's Encrypt 服务器通信,然后运行验证程序以验证您是否拥有您的域名。如果验证成功,配置将自动更新,并且 Apache 将重新加载以应用新设置。certbot 将显示一条消息,告知您成功以及证书的存储位置:
成功获取证书。
证书已保存到: /etc/letsencrypt/live/umd.eastus.cloudapp.azure.com/fullchain.pem
密钥已保存到: /etc/letsencrypt/live/umd.eastus.cloudapp.azure.com/privkey.pem
此证书将于 2025-09-18 到期。
证书续订时,这些文件将自动更新。
Certbot 已设置计划任务,将在后台自动续订此证书。
部署证书
已成功将 umd.eastus.cloudapp.azure.com 的证书部署到
/etc/apache2/sites-available/umd.eastus.cloudapp.azure.com-http-le-ssl.conf
恭喜!您已成功在 https://umd.eastus.cloudapp.azure.com 上启用 HTTPS。
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
如果您喜欢 Certbot,请考虑通过以下方式支持我们的工作:
* 向 ISRG / Let's Encrypt 捐款: https://letsencrypt.org/donate
* 向 EFF 捐款: https://eff.org/donate-le
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
您的证书已下载、安装并加载完毕。请尝试使用 https:// 重新加载您的网站,并留意浏览器的安全指示器。它应该会显示网站已安全加密,通常会显示一个锁形图标。如果您使用 SSL Labs 服务器测试工具测试您的服务器,它将获得 A 级评分。
最后,让我们测试一下证书续订流程。验证 Certbot 自动续订
Let’s Encrypt 证书的有效期只有九十天。这是为了鼓励用户自动化证书续订流程。您安装的 certbot 软件包会通过在 /etc/cron.d 中添加一个续订脚本来为您处理此事。该脚本每天运行两次,并会自动续订任何在三十天内即将过期的证书。要测试续订流程,您可以使用 certbot 进行模拟运行:
sudo certbot renew --dry-run
如果您没有收到任何错误,则一切正常。如有必要,Certbot 将续订您的证书并重新加载 Apache 以应用更改。如果自动续订过程失败,Let’s Encrypt 会向您指定的电子邮件地址发送消息,警告您证书即将过期。如果您只想获取证书,之后手动配置,请参考以下说明。
要获取域名的证书,请运行:
sudo certbot certonly --webroot --webroot-path /var/www/webmail.example.org -d webmail.example.org
您可以在此处使用多个 “-d” 参数来获取适用于多个域名的证书。例如: “-d webmail.example.org -d something-else.example.org”。(另请参阅:https://eff-certbot.readthedocs.io/en/stable/using.html#webroot) 首次执行此操作时,系统会要求您提供电子邮件地址,以便 Let’s Encrypt 在您的证书即将过期时向您发送提醒。您还需要同意其服务条款。
如果一切顺利,您应该会看到类似以下的输出:
正在为 webmail.example.org 申请证书
已成功收到证书。
证书已保存至: /etc/letsencrypt/live/webmail.example.org/fullchain.pem
密钥已保存至: /etc/letsencrypt/live/webmail.example.org/privkey.pem
证书将于 2024年1月2日到期。
证书续期时,这些文件将会更新。
Certbot 已设置计划任务,在后台自动续期此证书。
在 /etc/letsencrypt/live/webmail.example.org 目录下,您可以看到以下四个文件:
- cert.pem:证书文件
- chain.pem:证书链 或 中间证书。此证书提供 LetsEncrypt 证书与其他已知证书颁发机构的链接信息。对于可能还不熟悉 LetsEncrypt 的客户,通常建议始终将此证书与您自己的证书一起发送。
- fullchain.pem:此文件包含 cert.pem 和 chain.pem的拼接结果。当软件询问证书位置时,建议使用此文件。
- privkey.pem:私钥文件。请妥善保管。
将 HTTPS 设为默认
您可以为您的网络服务器启用 HTTPS 默认功能,即使用户通过 HTTP 访问您的网站,也会将其重定向到 HTTPS。创建一个名为 `/etc/apache2/sites-available/webmail.example.org-https.conf` 的新文件,内容如下:
<VirtualHost *:443>
ServerName webmail.example.org
DocumentRoot /var/www/webmail.example.org
SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/webmail.example.org/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/webmail.example.org/privkey.pem
</VirtualHost>
在 Apache 中启用SSL模块:
sudo a2enmod ssl
然后启用 HTTPS 虚拟主机:sudo a2ensite webmail.example.org-https
重启网络服务器。这次仅仅重新加载页面是不够的,因为您已经添加了一模块。sudo systemctl restart apache2
将 HTTP 重定向到 HTTPS
有时用户在访问网页邮件时,会忘记输入 https://… ,从而访问 HTTP 网站。我们显然不希望他们通过 HTTP 发送密码。因此,我们应该将所有 HTTP 连接重定向到 HTTPS。但有一个例外。Let’s Encrypt 会使用 HTTP 来验证您的挑战令牌。因此,我们需要直接在 http://webmail.example.org/.well-known/acme-challenge/… 提供文件,同时将所有其他请求重定向到 HTTPS。您通过在 `/etc/apache2/sites-available/webmail.example.org-http.conf` 文件的`<VirtualHost>` 部分中添加以下几行来实现:
RewriteEngine On
RewriteCond %{REQUEST_URI} !.well-known/acme-challenge
RewriteRule ^(.*)$ https://%{SERVER_NAME}$1 [R=301,L]
sudo a2enmod rewrite
sudo systemctl restart apache2
4. 准备数据库
现在该准备 MariaDB 数据库了,它用于存储控制邮件服务器的信息。在此过程中,您需要输入 SQL 查询语句 ---- 关系型数据库的语言。您可以使用 ‘mysql’ 命令在终端输入这些语句。但如果您不太熟悉 SQL,您可能更喜欢网页界面。这就是您安装 Adminer 的原因。设置 Adminer
Adminer 本质上只是几个由 Apache 网页服务器提供的 PHP 文件。设置很简单。编辑您的 /etc/apache2/sites-available/webmail.example.org-https.conf 文件,并在 <VirtualHost> 和 </VirtualHost> 之间添加以下几行:Alias /adminer /usr/share/adminer/adminer
重新加载 Apache 进程:systemctl reload apache2
安全警告
在您的网站在公开提供 SQL 管理界面会给网络不法分子带来可乘之机。考虑为 `/adminer` 目录添加额外的密码进行保护。Apache 文档中介绍了具体操作方法。或者,您也可以在别名中使用比 “/adminer” 更隐蔽的路径。
您目前还无法登录。数据库用户只有 ‘root’,但默认情况下只能通过 shell 访问,无法通过网络访问。生成两个随机密码
在本节中,您将创建基本数据库 “mailserver” 和两个用户。一个用户“mailadmin”可以修改数据库中的数据,供您使用。另一个用户“mailserver”只能读取数据库数据,供服务器进程使用。
使用 pwgen 工具为这两个用户生成两个随机密码:pwgen -s1 30 2
记下密码或将其保存在安全的地方。创建 ‘mailserver’ 数据库
这一步很简单。使用‘mysql’命令连接到数据库:sudo mysql
您应该会看到 MariaDB 提示符,允许您输入更多 SQL 命令:MariaDB [(none)]>
现在您需要使用 SQL 了。为了创建一个满足我们需求的新数据库,请输入:CREATE DATABASE mailserver;
系统会提示您的查询成功,并且添加了一行新数据。创建数据库用户
现在您有了一个空数据库。让我们为 “mailadmin” 数据库用户授予管理该数据库所需的权限。您仍然连接到数据库,对吗?要创建一个具有完整权限的用户,请输入以下 SQL 命令。请使用您刚刚生成的第一个密码,而不是我的密码:
grant all privileges on mailserver.* to 'mailadmin'@'localhost' identified by 'E2zhrYD1156RtbPRgWLfU4uC0uCQ0g';
同时创建一个只读用户,稍后将授予 Postfix 和 Dovecot 数据库访问权限(此处使用您的 第二个 随机密码)。
grant select on mailserver.* to 'mailserver'@'127.0.0.1' identified by 'uoDFE981mMpM0X996QNirfq4rJJ5ZR';
127.0.0.1 与 localhost
等等。为什么第二个SQL命令中是 “127.0.0.1” 而不是 “localhost” ?是笔误吗?不是。在网络术语中,这两者是相同的。但是 MariaDB(以及甲骨文的的 MySQL)会区分这两者。如果您向 “localhost” 发起数据库连接,那么您实际上是在与位于 /var/run/mysqld/mysqld.sock 的套接字文件通信。但是,如果您连接到 “127.0.0.1”,它将创建一个网络连接,与您服务器上的3306端口上的 TCP 套接字通信。区别在于,您服务器上的任何进程都可以与 127.0.0.1通信。但是,套接字文件与系统中的任何其他文件一样,具有特定的用户/组/其他权限。Postfix 将被限制在其 /var/spool/postfix 目录中,默认情况下无法访问该套接字文件。因此,通过 127.0.0.1,我们可以绕开此限制。当您使用 Adminer 时,如果使用 ‘mailadmin’用户,则必须使用‘localhost’作为数据库服务器;如果使用 ‘mailserver’用户,则必须使用‘127.0.0.1’。
现在,您可以使用 mailadmin 帐户和第一个密码登录 Adminer。
您应该可以登录并看到“邮件服务器”数据库:
创建数据库表
我们需要三个 Postfix 映射:一个用于虚拟域,一个用于虚拟别名,另一个用于虚拟用户。每一个映射都需要一个数据库表,您现在就来创建它们。您可以使用 Adminer。不过,我将展示用于创建表的 SQL 语句,您可以在 ‘mysql’ 命令行工具中输入该语句。 要创建的第一个表是 virtual_domains。此表保存您在 Postfix 中用作virtual_mailbox_domains 的域列表。
USE mailserver;
CREATE TABLE IF NOT EXISTS `virtual_domains` (
`id` int(11) NOT NULL auto_increment,
`name` varchar(50) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `virtual_users` (
`id` int(11) NOT NULL auto_increment,
`domain_id` int(11) NOT NULL,
`email` varchar(100) NOT NULL,
`password` varchar(150) NOT NULL,
`quota` bigint(11) NOT NULL DEFAULT 0,
PRIMARY KEY (`id`),
UNIQUE KEY `email` (`email`),
FOREIGN KEY (domain_id) REFERENCES virtual_domains(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE IF NOT EXISTS `virtual_aliases` (
`id` int(11) NOT NULL auto_increment,
`domain_id` int(11) NOT NULL,
`source` varchar(100) NOT NULL,
`destination` varchar(100) NOT NULL,
PRIMARY KEY (`id`),
FOREIGN KEY (domain_id) REFERENCES virtual_domains(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
示例数据供您参考
理论太多了吗?我能理解。让我们在数据库中填充 example.org 域名,john@example.org 邮箱帐户以及 jack@example.org 到 john@example.org 的邮件转发。我们将在第5节“让 Postfix 访问 MariaDB”中使用这些信息。要添加示例数据,只需运行以下 SQL 查询语句:
USE mailserver;
REPLACE INTO virtual_domains (id,name) VALUES (1,'example.org');
REPLACE INTO virtual_users (id,domain_id,password,email)
VALUES (1, 1,
'{BLF-CRYPT}$2y$05$xAEZd6RSftRCRKWuGwbc4.2HR/RFI5JwMFpymE/.TWZXWUp1.Krqi',
'john@example.org');
REPLACE INTO virtual_aliases (id,domain_id,source,destination)
VALUES (1, 1, 'jack@example.org', 'john@example.org');
sudo doveadm pw -s BLF-CRYPT
......来生成简单密码“summersun”的安全哈希值。安装 Dovecot 后,您可以自己尝试一下,但结果你有所不同。这是因为密码经过 加盐 处理,以提高安全性。5. 让 Postfix 访问 MariaDB
在第4节,我们已经创建了 SQL 数据库模式并插入了一些数据进行练习。让我们从系统中所有邮件的入口点 Postfix 开始。因此,我们需要告诉 Postfix 如何从数据库中获取信息。首先,让我们告诉它如何判断某个域名是否为有效的邮件域名。virtual_mailbox_domains
Postfix 中的映射是一个包含左侧(LHS)和右侧(RHS)的表。为了让 Postfix 从数据库中获取虚拟域名的信息,我们需要创建一个 ‘cf’ 文件(配置文件)。首先,创建一个名为 /etc/postfix/mysql-virtual-mailbox-domains.cf 的文件,用于配置 virtual_mailbox_domains 映射。请使其包含以下内容:
user = mailserver
password = uoDFE981mMpM0X996QNirfq4rJJ5ZR
hosts = 127.0.0.1
dbname = mailserver
query = SELECT 1 FROM virtual_domains WHERE name=‘%s’
假设 Postfix 收到一封发往 somebody@example.org 的邮件,并想知道 example.org 是否是一个虚拟邮箱域。它将运行上述 SQL 查询,并将 ‘%s’ 替换为 ‘example.org’。如果在 virtual_domains 中找到这样的行,它将返回 ‘1’。实际上,只要有结果,返回的具体内容并不重要。
现在,您需要让 Postfix 使用以下数据库映射:sudo postconf virtual_mailbox_domains=mysql:/etc/postfix/mysql-virtual-mailbox-domains.cf
“postconf” 命令会方便地将配置行添加到您的 /etc/postfix/main.cf 文件中。它还会立即激活新设置,因些您无需重新加载 Postfix 进程。您之前创建的测试数据已将 “example.org” 添加为您的邮箱域之一。让我们询问 Postfix 是否识别该域名:
sudo postmap -q example.org mysql:/etc/postfix/mysql-virtual-mailbox-domains.cf
您应该会得到 ‘1’ 作为结果。这意味着您的第一个映射已生效。您可以尝试在该行命令的 -q 参数后添加其他域名。您应该不会收到任何响应。现在,您需要定义 virtual_mailbox_maps。它会将收件人的邮件地址(左侧)映射到用户硬盘上的邮箱位置(右侧)。Postfix 内置了一个名为 “virtual” 的传输服务,可以接收邮件并将其存储到收件人的邮件目录中。该服务的功能相当有限,因此我们将委托给 Dovecot,以便更好地处理邮件。
Postfix 会将所有邮件转发给 Dovecot 进行进一步投递。但在此这前,我们需要确保收件人确实存在。因此,Postfix 需要检查邮件地址是否有效。这简化了操作,因为我们只需要映射的左侧部分。与上述 virtual_domains 映射类似,您需要一个 SQL 查询来查找邮件地址,如果找到则返回 “1” 。
为此,请在 /etc/postfix/mysql-virtual-mailbox-maps.cf 创建另一个配置文件:user = mailserver
password = uoDFE981mMpM0X996QNirfq4rJJ5ZR
hosts = 127.0.0.1
dbname = mailserver
query = SELECT 1 FROM virtual_users WHERE email=‘%s’
告诉 Postfix 此映射用于 virtual_mailbox_maps 映射:
sudo postconf virtual_mailbox_maps=mysql:/etc/postfix/mysql-virtual-mailbox-maps.cf
测试 Postfix 是否接受些映射,方法是询问 john@example.org 用户的邮箱目录在哪里:sudo postmap -q john@example.org mysql:/etc/postfix/mysql-virtual-mailbox-maps.cf
virtual_alias_maps
virtual_alias_maps 映射用于将邮件从一个地址转发到一个或多个其他地址。在数据库中,可以用多行来实现多个目标。在/etc/postfix/mysql-virtual-alias-maps.cf 创建另一个 “.cf” 文件:
user = mailserver
password = uoDFE981mMpM0X996QNirfq4rJJ5ZR
hosts = 127.0.0.1
dbname = mailserver
query = SELECT destination FROM virtual_aliases WHERE source=‘%s’
让 Postfix 使用此数据库映射:
sudo postconf virtual_alias_maps=mysql:/etc/postfix/mysql-virtual-alias-maps.cf
测试映射文件是否按预期工作:sudo postmap -q jack@example.org mysql:/etc/postfix/mysql-virtual-alias-maps.cf
您应该看到预期的目标地址:john@example.org
6. 通用别名
本节完全可选,跳过此部分不会影响您的邮件服务器。正如教程前面部分所述,有一种方法可以将域中的所有电子邮件地址转发到特定的目标电子邮件地址。这称为通用别名。如果某个电子邮件地址没有特定的虚拟用户,这些别名就会捕获该域的所有电子邮件。通用别名被认为是一个糟糕的主意。例如,如果您的市场部门每周都要求创建一个新的电子邮件别名,您可能会很想将所有电子邮件地址都转发给一个人。但缺点是您会收到更多垃圾邮件,因为垃圾邮件发送者会向您域中的任何地址发送垃圾邮件。或者,发件人可能拼错了收件人的地址,但邮件服务器会转发邮件而不是出于正当理由拒绝它。因此,在使用通用别名之前请三思。
您仍然想使用通用别名吗?好吧,那就试试吧。通用别名看起来像“@example.org”,它会将整个域的电子邮件转发到其他地址。我们已经创建了 john@example.org 用户,并希望将该域上的所有其他电子邮件转发到 kerstin@example.com。因此,我们可以添加一个通用别名,如下所示:
| 来源 | 目标 |
|---|---|
| @example.org | kerstin@example.com |
但这里有一个小问题。Postfix 总是先检查 virtual_alias_maps 映射,然后再在 virtual_mailbox_maps 中查找用户。想象一下,当 Postfix 收到发往 ‘john@example.org’ 的电子邮件时会发生什么。Postfix 会检查 virtual_alias_maps 中的别名。它会找到上面的通用别名条目,由于没有更具体的别名,因此通用别名匹配,邮件将被重定向到 ‘kerstin@example.com’。
换句话说:别名总是首先被处理。因此,通用别名会截获电子邮件。John 将永远收不到任何电子邮件。这不是您想要的结果。但是,假设别名包含第二个条目,如下所示:
| 电子邮件 | 目标 |
|---|---|
| @example.org | kerstin@example.com |
| john@example.org | john@example.org |
因此,example.org 域上的任何电子邮件地址都将被转发到 kerstin 的地址。但是第二行是什么意思?为什么我们要将 John 的电子邮件转发给他自己?这说不通。
实际上,它是说得通的。Postfix 会优先考虑更具体的别名。而 john@example.org 比 @example.org 更具体。假设有人尝试向 john@example.org 的邮箱发送邮件。如果 Postfix 只是从上到下读取这张表,它会首先看到 @example.org,这将匹配成功。然后它会将该邮件重定向到 kerstin。这样一来,John 就永远收不到邮件了。
因此,为了使通配地址和特定地址的组合能够正常工作,我们需要用到这个小技巧。Postfix 会为以下每个地址查找所有这些映射:
- john@example.org(最具体)
- @example.org(通配地址 - 最不具体)
我们不想手动为每个电子邮件地址添加“更具体”的条目。幸运的是,我们可以轻松地实现自动化。对于 “john 发送给自己” 的映射,您需要为后者创建另一个 “.cf” 文件: /etc/postfix/mysql-email2email.cf。
user = mailserver
password = x893dNj4stkHy1MKQq0USWBaX4ZZdq
hosts = 127.0.0.1
dbname = mailserver
query = SELECT email FROM virtual_users WHERE email=‘%s’
sudo postmap -q john@example.org mysql:/etc/postfix/mysql-email2email.cf
结果应该与原地址相同:john@example.org
现在你需要告诉 Postfix,它应该同时检查别名和 “john 发送给自己” 的映射:sudo postconf virtual_alias_maps=mysql:/etc/postfix/mysql-virtual-alias-maps.cf,mysql:/etc/postfix/mysql-email2email.cf
这两个映射的顺序并不重要。Postfix 会检查所有 ‘cf’ 文件并合并找到的内容。你成功了!所有映射都已设置完毕,数据库也已准备好填充域名和用户信息。请确保只有“root”用户和“postfix”用户可以读取“.cf”文件——毕竟你的数据库密码就存储在这些文件中:
sudo chgrp postfix /etc/postfix/mysql-*.cf
sudo chmod u=rw,g=r,o= /etc/postfix/mysql-*.cf
7. 设置 Dovecot
我们旅程的这一部分将介绍 Dovecot——这款软件的功能包括:- 从 Postfix 接收发送给用户的电子邮件并将其保存到磁盘;
- 执行基于用户的过滤规则(例如,可以根据特定条件将电子邮件移动到不同的文件夹或发送自动假期回复);
- 允许用户使用 POP3 或 IMAP 协议收取电子邮件。
sudo groupadd -g 5000 vmail
sudo useradd -g vmail -u 5000 vmail -d /var/vmail -m
!include conf.d/*.conf
它会按字母数字顺序加载 /etc/dovecot/conf.d/ 目录中所有以“.conf”结尾的文件。因此,“10-auth.conf”会首先加载,“90-sieve-extprograms.conf”会最后加载。这样做的一个巨大优势是,您可以编辑或替换配置的某些部分,而无需覆盖整个配置文件。主配置文件 /etc/dovecot/dovecot.conf 不需要进行任何更改。但是,conf.d/ 目录中的其他文件需要进行修改。/etc/dovecot/conf.d/10-auth.conf
最常见的身份验证机制称为 PLAIN. 但是,如果您有 Outlook 用户,则可能还需要添加 LOGIN 机制:auth_mechanisms = plain login
这两种机制都会要求输入密码,但不会强制使用加密来保护密码。不过不用担心。默认情况下,Dovecot 会设置 disable_plaintext_auth = yes,这确保只有通过 TLS 加密连接才能接受身份验证。在此文件的末尾,您会找到 Dovecot 提供的各种身份验证后端。默认情况下,它将使用系统用户(来自 /etc/passwd)。但我们想要使用 MariaDB 数据库后端,因此请将此代码块更改为:
#!include auth-system.conf.ext
!include auth-sql.conf.ext
#!include auth-ldap.conf.ext
#!include auth-passwdfile.conf.ext
#!include auth-checkpassword.conf.ext
#!include auth-static.conf.ext
10-mail.conf
将 mail_location 设置更改为:mail_location = maildir:~/Maildir
这是 Dovecot 查找特定用户电子邮件的目录。波浪号字符 (~) 表示用户的主目录。这目前可能还不太好理解。但在本页的后面部分,我们将告诉 Dovecot 主目录具体指的是什么。例如,john@example.org 的主目录将位于 /var/vmail/example.org/john。在 10-mail.conf 文件中,您会找到定义命名空间的部分。这些是您的电子邮件程序连接到邮件服务器时看到的文件夹结构。如果您使用 POP3,则只能访问“收件箱”——所有收到的电子邮件都存储在此处。使用 IMAP 协议,您可以访问文件夹和子文件夹的层次结构。您甚至可以在用户之间共享文件夹。或者使用任何人都可以访问的公共文件夹——甚至可以匿名访问。因此,通常建议使用 IMAP。
此外,编辑“mail_plugins”行,启用可选的配额插件,将其修改为:mail_plugins = quota
10-master.conf
此配置文件处理典型的服务端口,例如 IMAP 或 POP3。这里大多数设置都是合理的,无需更改。但是,“service auth”部分需要进行一项更改,因为我们希望 Postfix 允许 Dovecot 作为身份验证服务。将其修改为如下所示:
# Postfix smtp-auth
unix_listener /var/spool/postfix/private/auth {
mode = 0660
user = postfix
group = postfix
}
Postfix 运行在位于 /var/spool/postfix 的 chroot 环境中。它无法访问该目录之外的任何内容。因此,为了允许与 Postfix 进行通信,我们指示 Dovecot 将通信套接字放置在该 chroot 环境中。
10-ssl.conf
您已经创建了密钥和证书文件,用于加密用户与邮件服务器之间通过 POP3、IMAPs 和 HTTPS 进行的通信。您需要告诉 Dovecot 这些文件的位置:ssl_cert = </etc/letsencrypt/live/webmail.example.org/fullchain.pem
ssl_key = </etc/letsencrypt/live/webmail.example.org/privkey.pem
ssl = required
接下来,让我们看看 Dovecot 如何识别用户及其密码:auth-sql.conf.ext
Dovecot 读取 auth-sql.conf.ext 文件,该文件定义了如何在数据库中查找用户信息。打开该文件。其中包含两个部分:- userdb: 在文件系统中查找用户邮箱的位置
- passdb: 查找用户哈希密码的位置
“userdb”部分的内容如下:
userdb {
driver = sql
args = /etc/dovecot/dovecot-sql.conf.ext
}
/etc/dovecot/dovecot-sql.conf.ext
(此配置文件位于上一级目录,而不是“conf.d”目录中。)您会发现此文件注释非常详细,尽管所有配置指令都被注释掉了。在文件末尾添加以下几行:
driver = mysql
connect = \
host=127.0.0.1 \
dbname=mailserver \
user=mailserver \
password=uoDFE981mMpM0X996QNirfq4rJJ5ZR
user_query = SELECT email as user, ‘/var/vmail/%d/%n’ As home, 5000 AS uid, 5000 AS gid FROM virtual_users WHERE email=‘%u’
password_query = SELECT password FROM virtual_users WHERE email=‘%u’
反斜杠
行尾的反斜杠(\)表示该行将在下一行继续。当配置信息跨越多行时,使用反斜杠可以使配置更易于阅读。这些行的含义:
- driver: 数据库类型。MariaDB 与 MySQL 类型相同。
- connect: 指定 MySQL 数据库的位置以及如何访问它(用户名、密码)。
- user_query: 一个 SQL 查询,用于返回用户名(即电子邮件地址)、配额、主目录、用户 ID 和组 ID。
- password_query: 此 SQL 查询仅从数据库获取密码哈希值。
- email AS user
它从数据库中获取与用户名对应的 email 字段。Dovecot 需要将其作为 user 字段,因此我们设置了一个别名 “user”。 - userdb_home
这指向存储该用户所有电子邮件和各种控制文件的目录。占位符“%d”代表域名,“%n”代表用户名。因此,对于 John 来说,它就是“/var/vmail/example.org/john”。 - userdb_uid 和 userdb_gid
这些是 vmail 用户的用户 ID 和组 ID,两者均为 5000。Dovecot 使用它们来设置其创建的文件的权限。由于所有用户都共享同一个系统用户“vmail”,因此这是一个静态数字。
修复权限
确保只有 root 用户才能访问 SQL 配置文件,以防止其他人读取您的数据库访问密码:sudo chown root:root /etc/dovecot/dovecot-sql.conf.ext
sudo chmod go= /etc/dovecot/dovecot-sql.conf.ext
sudo systemctl restart dovecot
查看您的 /var/log/mail.log 日志文件。您应该会看到以下内容:... Dovecot v2.3.13 (89f716dc2) starting up for imap, lmtp, sieve, pop3 (core dumps disabled)
如果您收到任何错误消息,请仔细检查您的配置文件。8. 让 Postfix 将电子邮件发送到 Dovecot
在第 7 节中,我们确保 Postfix 知道它允许接收哪些电子邮件。那么接下来该如何处理这些电子邮件呢?它们必须保存到磁盘上,存入正在焦急等待的邮件用户的邮箱中。你可以让 Postfix 使用其内置的邮件投递代理 (MDA)“虚拟”来处理此事。然而,与 Dovecot 提供的功能(例如基于服务器的过滤规则或配额)相比,Postfix 的投递代理功能相当基础。我们无论如何都要使用 Dovecot 来提供 IMAP(以及可选的 POP3)服务。所以,让我们使用它的投递代理。
我们如何让 Postfix 将电子邮件交给 Dovecot 呢?通常有两种方法可以建立这种连接。- 使用 dovecot-lda (本地投递代理)进程。它可以一次处理一封电子邮件。并且它会为每封电子邮件启动一个新进程。这在很长一段时间内都是默认方式。但正如你所想,它的可伸缩性并不好。
- 更好的选择是使用为此目的而设计的 LMTP(本地邮件传输协议) 它可以同时处理多个收件人,并且有一个永久运行的进程,比使用 LDA 性能更好。简而言之,LMTP 是 SMTP 的一个变体,功能较少。它用于相互信任的内部服务之间的电子邮件通信。
你已经猜到了--我们将选择第二种方法。你之前已经安装了 dovecot-lmtpd 软件包。让我们来配置它。
告诉 Dovecot 在哪里监听来自 Postfix 的 LMTP 连接
编辑 Dovecot 的配置文件,该文件用于处理 LMTP 守护进程——你可以在 /etc/dovecot/conf.d/10-master.conf 找到它。找到“service lmtp”部分并进行编辑,使其看起来如下所示:
service lmtp {
unix_listener /var/spool/postfix/private/dovecot-lmtp {
group = postfix
mode = 0600
user = postfix
}
}
这将使 Dovecot 的 lmtp 守护进程在 /var/spool/postfix/private/dovecot-lmtp 创建一个 UNIX 套接字。就像在设置 Dovecot 的部分一样,我们让它将套接字放在 /var/spool/postfix chroot 目录中,因为 Postfix 被限制在该目录中,无法访问其外部的任何内容。因此,从 Postfix 的角度来看,该套接字位于“private/dovecot-lmtp”。
重启 Dovecot…
sudo systemctl restart dovecot
检查 Dovecot 是否接受了此更改:systemctl status dovecot
输出应包含 “Active: active (running)”.让 Postfix 使用 LMTP 将电子邮件投递到 Dovecot
这更简单。“virtual_transport” 在 Postfix 中定义了用于将电子邮件投递到本地系统的服务。Dovecot 已经创建了一个套接字文件,并准备好监听传入的 LMTP 连接。我们只需要告诉 Postfix 将电子邮件发送到那里即可:sudo postconf virtual_transport=lmtp:unix:private/dovecot-lmtp
这个语法看起来很复杂,但实际上很简单。您只是告诉 Postfix 使用 LMTP 协议。并且我们想要使用同一系统上的 UNIX 套接字(而不是 TCP 连接)。套接字文件位于 /var/spool/postfix/private/dovecot-lmtp。启用服务器端邮件规则
Dovecot 的功能之一是服务器端处理的自动接收邮件规则。您可以将邮件列表的电子邮件分类到特定的文件夹中。您可以拒绝某些发件人。或者您可以设置假期自动回复。无需运行邮件客户端——即使您的邮件用户未连接,所有操作也会在服务器上自动进行。此类规则的开放标准 (RFC 5228) 称为 Sieve。基本上,Sieve 是一种管理服务器端电子邮件规则的方法。规则由条件和操作组成。例如,如果发件人地址与 steve@example.org 匹配,您可以告诉 Dovecot 自动将此类电子邮件移动到您的“steve”文件夹。这些规则存储在 Dovecot 服务器上并自动执行。无论您是从智能手机、笔记本电脑还是使用网页邮件访问,这些规则始终有效,并且无需在客户端进行任何配置。
由于我们使用 LMTP,因此我们需要告诉 lmtp 服务我们想要使用 Dovecot 的“sieve”插件。编辑文件 /etc/dovecot/conf.d/20-lmtp.conf,并在“protocol lmtp”部分中将“mail_plugins”行更改为:mail_plugins = $mail_plugins sieve
重启 Dovecot 即可完成:sudo systemctl restart dovecot
9. 测试 IMAP
您已经完成了 Dovecot 的配置。因此,通过 IMAP 收取电子邮件应该已经可以正常工作了。让我们使用一个看似简单但功能强大的 IMAP 客户端 mutt 来尝试一下:
mutt -f imaps://john@example.org@**webmail.example.org**
由于有两个“@”字符,连接 URL 看起来可能有点令人困惑。通常,mutt 期望的格式是 imaps://user@server。由于我们使用电子邮件地址作为“user”部分,所以会显示成这样。系统会提示您输入密码,我们之前设置的密码是 “summersun”。
如果您收到任何证书警告,请检查您是否使用了正确的服务器名称进行连接,以及您是否已按照本指南前面的步骤完成了证书/Let's Encrypt 的配置。登录后,您将看到一个空的收件箱:
10. 测试电子邮件投递
到目前为止,您已经花费了大量时间进行理论学习和配置。您是否担心您所做的一切最终能否构建一个正常工作的邮件服务器?在进行最后步骤之前,让我们休息一下,验证一下您目前所做的一切是否都按预期工作。此时,/var/vmail 目录应该是空的,或者如果您之前使用过 john@example.org 帐户,则可能包含一个“example.org”目录。您可以通过运行以下命令获取其中所有文件和目录的列表:
sudo find /var/vmail
尽管服务器上实际上还没有任何电子邮件,但您仍然可能会看到类似以下内容:/var/vmail
/var/vmail/.profile
/var/vmail/.bash_logout
/var/vmail/4.213.182.137.nip.io
/var/vmail/4.213.182.137.nip.io/john
/var/vmail/4.213.182.137.nip.io/john/Maildir
/var/vmail/4.213.182.137.nip.io/john/Maildir/cur
/var/vmail/4.213.182.137.nip.io/john/Maildir/tmp
/var/vmail/4.213.182.137.nip.io/john/Maildir/dovecot.list.index.log
/var/vmail/4.213.182.137.nip.io/john/Maildir/dovecot.index.log
/var/vmail/4.213.182.137.nip.io/john/Maildir/new
/var/vmail/4.213.182.137.nip.io/john/Maildir/dovecot-uidlist
/var/vmail/4.213.182.137.nip.io/john/Maildir/maildirfolder
/var/vmail/4.213.182.137.nip.io/john/Maildir/dovecot-uidvalidity
/var/vmail/4.213.182.137.nip.io/john/Maildir/dovecot-uidvalidity.6852ff0a
每个IMAP 邮件文件夹包含三个子目录:
- new – 此目录中的每个文件都是存储在此邮件文件夹中但尚未读取的电子邮件
- cur – 与上面类似,但用于已读邮件
- tmp – 用于存放邮件服务器的临时文件
- …/Maildir/new/… – 主收件箱
- …/Maildir/.INBOX.reddit/new/… – 收件箱下的“reddit”邮件文件夹
- …/Maildir/.INBOX.servers.inga/new/… – 收件箱下的“servers”/“inga”邮件文件夹
检查 Postfix
要检查 Postfix 中是否存在明显的配置错误,请运行:sudo postfix check
发送测试邮件
现在是时候向系统中发送一封新邮件了。打开一个新的终端窗口并运行:
tail -f /var/log/mail.log
以查看邮件服务器的运行情况。现在,让我们向 John 发送一封电子邮件。我最喜欢的邮件测试工具是您之前安装的 swaks。在原始终端中运行:swaks --to john@example.org --server localhost
如果一切正常,您的 mail.log 文件将显示大量有关电子邮件投递的技术信息。让我解释一下每个阶段发生的情况。- postfix/smtpd[1756927]: connect from localhost[::1]
Postfix 接收到传入的 SMTP 连接。 - postfix/smtpd[1756927]: 88DB5120225: client=localhost[::1]
Postfix 为此连接分配了一个唯一的标识符 (88DB5120225),以便您可以查看哪些日志行属于同一连接。这对于处理多个邮件的繁忙邮件服务器尤其重要。 - postfix/cleanup[717245]: 88DB5120225: message-id=<20251109120838.717233@webmail.example.org>
swaks 为电子邮件创建了一个唯一的 邮件 ID,这有助于您在日志文件中识别特定的邮件。 - postfix/smtpd[1756927]: disconnect from localhost[::1] ehlo=1 mail=1 rcpt=1 data=1 quit=1 commands=5
SMTP 通信结束。Postfix 已接收并排队等待发送电子邮件。 - postfix/qmgr[1437]: 88DB5120225: from=<azureuser@webmail.example.org>, size=463, nrcpt=1 (queue active)
发件人是 azureuser@webmail.example.org。 这是在 swaks 在 SMTP 对话期间发送“MAIL FROM”行后记录的。 - dovecot: lmtp(1756942): Connect from local
Postfix 连接到 Dovecot,通过 LMTP 接口将电子邮件移交给 Dovecot。 - dovecot: lmtp(john@example.org)<1756942><VFTrI9FkTGkOzxoATRJD4g>: sieve: msgid=<20251224221025.1756926@webmail.example.org>: stored mail into mailbox 'INBOX'
Dovecot 收到电子邮件并将其存储到收件箱中。 - postfix/lmtp[1756941]: 88DB5120225: to=<john@example.org>, relay=webmail.example.org[private/dovecot-lmtp], delay=0.1, delays=0.03/0.01/0.02/0.05, dsn=2.0.0, status=sent (250 2.0.0 <john@example.org> VFTrI9FkTGkOzxoATRJD4g Saved)
这是您的邮件日志中最有趣的一行。它告诉您特定电子邮件的处理情况。在这种情况下,它表明邮件已移交给 dovecot-lmtp,并且投递成功 (status=sent)。状态码(例如 2.0.0)在 RFC 3463 中定义,其工作方式类似于 HTTP 中的状态码。以“2”开头的代码表示成功。以“4”开头的代码表示临时错误。而“5”则表示永久性故障。 - postfix/qmgr[1437]: 88DB5120225: removed
连接 88DB5120225 已从队列中移除。 - dovecot: lmtp(1756942): Disconnect from local: Client has quit the connection (state=READY)
Postfix 和 Dovecot 之间的 LMTP 连接已关闭。
再次查看:
sudo find /var/vmail/example.org/john您还可以使用一个更便捷的工具来访问 Maildir,这对于邮件服务器管理员来说非常实用:“mutt”。
sudo mutt -f /var/vmail/example.org/john/Maildir
(您可能会被要求创建 /root/Mail 目录——这是正常步骤。只需按 Enter 键即可。)
现在您看到的是 John 邮箱的内容:
使用 mutt 是一种在登录邮件服务器时检查邮箱的好方法。
重申一下收到电子邮件时会发生什么:- Postfix 接收电子邮件(在本例中使用了“swaks”命令——但通常是通过网络使用 SMTP 协议从其他服务器接收)
- Postfix 通过 LMTP 与 Dovecot 通信并传递电子邮件
- Dovecot 会执行用户的 Sieve 规则。
- Dovecot 将电子邮件文件写入磁盘
本教程介绍了如何设置邮件服务器以接收和从服务器发送电子邮件。关于如何配置邮件服务器以转发最终用户的电子邮件,请参阅 “邮件中继”。