邮件中继
之前教程 “在 Linux 上设置邮件服务” 讲解了软件安装以及服务器设置以接收和发送电子邮件。在本教程中,我们将设置服务器以转发最终用户的电子邮件。我们还将向您展示如何使用 PHPMailer和管理电子邮件数据库。
1. 通过 Postfix 中继外发电子邮件
我们使用 Postfix 来中继我们的电子邮件。中继
请注意, 用户 发送电子邮件的方式与 服务器 发送电子邮件的方式有所不同。例如:
- 邮件服务器会获取收件人电子邮件地址域名的MX 记录。这会告诉它应该与哪个邮件服务器通信。然后,它会打开一个 SMTP 连接(TCP 端口 25)并发送电子邮件。
- 使用雷鸟、Evolution 或 Mutt 等邮件客户端的最终用户无法以这种方式发送电子邮件。这些邮件客户端没有内置获取 MX 记录的功能。而且,用户很可能使用的是动态 IP 地址,其他邮件服务器不信任这些地址并会拒绝连接。最终用户应该将电子邮件发送到其服务提供商(也就是您的!)的邮件服务器,发送登录信息进行身份验证,然后发送电子邮件。这称为中继,因为您的邮件服务器充当用户和互联网上其他邮件服务器之间的中继站。出于安全原因,用户也必须进行身份验证才能发送电子邮件。
接收电子邮件
当互联网上的某人向 john@example.org 发送电子邮件时,其他邮件服务器会使用 SMTP 协议将电子邮件发送到您的邮件服务器。Postfix 会判断自己负责处理 example.org 域名的电子邮件地址,并接收该邮件。然后,John 就可以从您的服务器接收这封邮件。
发送电子邮件(未经身份验证)
约翰位于互联网上的某个位置,想要向 lisa@example.com 发送电子邮件。您的邮件服务器不负责“example.com”域名,因此它会接收约翰的电子邮件,并需要将其转发(中继)到负责处理 …@example.com 电子邮件地址的实际邮件服务器。这看起来似乎没什么问题,但您的邮件服务器必须拒绝这种操作。
为什么?因为任何人都可以冒充约翰,并让您的邮件服务器转发邮件。如果攻击者(例如垃圾邮件发送者)以约翰的名义通过您的服务器发送数百万封垃圾邮件,那么其他组织会指责您(作为邮件服务器的运营商)发送垃圾邮件。您的邮件服务器就会成为人们所说的 开放中继。这不是您想要的结果,因为您的邮件服务器会被列入黑名单,您将无法向大多数其他服务器发送邮件。因此,在没有任何证据证明发件人确实是约翰的情况下,您的服务器必须拒绝该电子邮件。
发送电子邮件(经过身份验证)
那么约翰如何证明自己的身份呢?他需要使用经过身份验证的 SMTP。这与前面的情况类似,但约翰的电子邮件程序还会发送他的用户名和密码。我们会确保他的身份验证是通过加密连接进行的,因此约翰的密码是安全的。
Postfix 设置 “mynetworks”
除了使用 SMTP 身份验证之外,您还可以告诉 Postfix 始终允许特定 IP 地址进行中继。mynetworks 设置包含您信任的 IP 网络或 IP 地址列表。通常,您会在此处定义自己的本地网络。在上面的示例中,John 需要进行身份验证的原因是他的 IP 地址不在 mynetworks 列表中。让 Postfix 使用 Dovecot 进行身份验证
在 Postfix 中启用 SMTP 身份验证非常简单。您已经配置了 Dovecot,因此它了解 SQL 数据库中的所有用户。因此,我们只需让 Postfix 使用它,方法是告诉它向 Dovecot 服务器验证用户名和密码。Postfix 只需要一些额外的配置。在 shell 中运行以下命令:sudo postconf smtpd_sasl_type=dovecot
sudo postconf smtpd_sasl_path=private/auth
sudo postconf smtpd_sasl_auth_enable=yes
启用加密
以下设置启用加密,并为 Postfix 设置密钥和证书路径。只需运行以下命令:
sudo postconf smtpd_tls_security_level=may
sudo postconf smtpd_tls_auth_only=yes
sudo postconf smtpd_tls_cert_file=/etc/letsencrypt/live/webmail.example.org/fullchain.pem
sudo postconf smtpd_tls_key_file=/etc/letsencrypt/live/webmail.example.org/privkey.pem
sudo postconf smtp_tls_security_level=may
smtp 还是 smtpd?
仔细看。有些设置以“smtp_”开头,有些则以“smtpd_”开头。这不是拼写错误。“smtp_”指的是 SMTP 客户端。它是 Postfix 中负责将电子邮件 发送 到其他服务器的组件。
而“smtpd_”指的是 SMTP 服务器。它是负责 接收来自 其他系统(无论是远程邮件服务器还是您的用户)的电子邮件的组件。上述设置允许加密的入站 (smtpd_) 和出站 (smtp_) 连接。但它们并不强制执行加密。如果远程邮件服务器未启用加密,我们仍然会接受它们的电子邮件。您可能想要强制执行加密,但这会导致拒绝与未配置加密的服务器进行电子邮件通信。
但是,smtpd_tls_auth_only=yes 设置确保用户身份验证信息(电子邮件地址和密码)在用户和您的邮件服务器之间始终加密传输。
SMTP 身份验证的工作原理
您是否好奇 SMTP 身份验证在底层是如何工作的?您可能并不好奇。但我们还是来讲解一下。就一次,让您了解一下。
在本指南的早期版本中,我们使用“telnet”连接到 TCP 端口 25 并使用 SMTP 协议进行通信。但现在我们强制执行加密,因此无法进行未加密的 SMTP 身份验证。无论如何,我们还是尝试一下:
如有必要,安装 telnet,然后通过 SMTP 连接到 localhost:
sudo apt install telnettelnet localhost smtp
服务器将允许您连接:正在尝试 ::1…
已连接到 localhost。
转义字符为 '^\]'。
220 webmail.example.org ESMTP Postfix (Debian/GNU)
ehlo example.com
Postfix 将显示可用的功能列表:250-webmail.example.org
250-PIPELINING
250-SIZE 10240000
250-VRFY
250-ETRN
250-STARTTLS
250-ENHANCEDSTATUSCODES
250-8BITMIME
250-DSN
250-SMTPUTF8
250 CHUNKING
- PIPELINING
这是一项用于加速 SMTP 通信的功能。通常,远程系统必须等待对它发送的每个命令的响应。管道传输允许远程服务器批量发送多个命令,而无需等待响应。Postfix 只会存储这些命令并逐个执行。如果您配置 Postfix 禁止管道传输,则当远程服务器尝试批量发送命令而不等待相应的回复时,Postfix 将断开与远程服务器的连接。这主要是一项用于防范恶意发送者的功能。 - SIZE 10240000
远程服务器可以发送最大 10 MB 的电子邮件。这长期以来一直是电子邮件的常见最大大小。但是,现在有些邮件服务器允许 40 MB。 - VRFY
允许远程服务器验证给定的姓名或电子邮件地址。例如,远程服务器可以发送“VRFY john”,您的服务器可能会响应 250 John Doe <john@example.org>。它可以用于验证某个收件人电子邮件地址是否可送达。 - ETRN
远程系统可以发送此命令来刷新 Postfix 队列中某个域的邮件。如果远程系统出现技术问题并暂时无法接收电子邮件,则可以使用此命令。然后,它可以发送 ETRN 命令,使您的服务器开始发送该域的待发送电子邮件。此命令很少使用。 - STARTTLS
这会通知远程系统,可以通过发送“STARTTLS”命令,将当前未加密的连接切换到加密连接。然后,系统将开始协商建立 TLS 加密连接。您可以将其类比为 HTTP 连接突然切换到加密的 HTTPS 连接。这样做的好处是,您可以在 TCP 端口 25 上开始使用 SMTP 协议进行通信,而无需打开第二个 TCP 端口,例如端口 465,该端口是“SSMTP”(安全 SMTP)端口,仅接受加密连接 - ENHANCEDSTATUSCODES
这会启用更多三位数的返回代码,用于表示各种状态。如果您感兴趣,可以参阅 RFC 2034。 - 8BITMIME
在早期,SMTP 只能处理 7 位字符。如果不进行特殊编码,就无法传输“闰”或“曌”等特殊字符。8BITMIME 允许使用 8 位字符传输电子邮件。尽管如此,许多电子邮件仍然使用 ISO8859-1 或 UTF-8 进行特殊编码。 - DSN
它启用 DSN(投递状态通知),允许发件人控制 Postfix 在电子邮件无法按预期投递时生成的邮件。 - SMTPUTF8
除了 8BITMIME 之外,您还可以在邮件头字段中使用 UTF-8 编码的字符。 - CHUNKING
此功能(在 RFC 3030) 中描述)可以提高发送大型邮件的效率。
250-AUTH PLAIN LOGIN
我们告诉 Postfix 只允许在连接加密的情况下进行身份验证。因此,在这种明文连接下,服务器不会提供身份验证选项。这正是我们想要的。你仍然连接着吗?好的,很好。所以,我们需要使用 TLS 建立加密连接。使用 STARTTLS 功能,我们可以在不重新连接的情况下从非加密连接切换到加密连接。输入……
STARTTLS
服务器回复:220 2.0.0 Ready to start TLS
然而,现在情况变得有点奇怪,因为你需要使用 TLS 加密协议进行通信,而这并不是人类使用的语言。所以,让我们使用“QUIT”命令退出,然后换一种方式:QUIT
在进入下一部分之前,我们需要知道用户名和密码的 base64 编码格式。在 bash 中输入以下命令:printf '\0john@example.org\0summersun' | base64
您将获得:AGpvaG5ANC4yMTMuMTgyLjEzNy5uaXAuaW8Ac3VtbWVyc3Vu
现在我们可以使用 OpenSSL 来帮助我们进行解密。运行:openssl s_client -connect localhost:25 -starttls smtp
您会看到很多输出。OpenSSL 已连接到 TCP 端口 25 并发出 STARTTLS 命令以切换到加密连接。因此,您现在输入的任何内容都将被加密。输入:EHLO umd.eastus.cloudapp.azure.com
Postfix 将发送一个功能列表,如下所示:250-PIPELINING
250-SIZE 10240000
250-VRFY
250-ETRN
250-AUTH PLAIN LOGIN
250-ENHANCEDSTATUSCODES
250-8BITMIME
250 DSN
AUTH PLAIN AGpvaG5ANC4yMTMuMTgyLjEzNy5uaXAuaW8Ac3VtbWVyc3Vu
服务器应该接受此身份验证:235 2.7.0 Authentication successful
太好了。您已通过 SMTP 登录。您现在可以发送电子邮件,该邮件将被转发到另一个邮件服务器。我只是想展示一下,如果您使用加密连接,身份验证是有效的。断开与 Postfix 的连接:
QUIT
身份验证成功。做得好。Base64 编码的密码
等等,那串奇怪的乱码是什么?里面没有用户名和密码啊。难道是加密过的?
不,那不是加密。那只是用户名和密码的编码版本。为什么要这样做呢?通常在 SMTP 协议中,只能传输 ASCII 字符。但是密码可能包含 ASCII 字符集之外的特殊字符。因此,在 PLAIN 方法中,这些信息会进行 Base64 编码。实际被转换为 Base64 的内容是……
空字节 + 用户名 + 空字节 + 密码
所以,对于约翰的情况,你可以使用以下命令轻松创建 Base64 字符串:printf '\0john@example.org\0summersun' | base64
最终,你会得到用于“AUTH PLAIN”命令的字符串。提交端口
虽然我一直在讨论使用端口 25 的 SMTP 协议来中继邮件,但这实际上并非正确的方法。最终用户不应该使用端口 25,而应该使用 TCP 端口 587 上的提交服务(如 RFC 4409 中所述)。其理念是使用端口 25 进行服务器之间的电子邮件传输(MTA = 邮件传输代理),而使用端口 587 进行用户到邮件服务器的电子邮件提交(MSA = 邮件提交代理)。
对比:| TCP 端口 | 服务名称 | 加密 | 身份验证 | 用途 | 您的 ISP |
|---|---|---|---|---|---|
| 25 | STMP | 可选 | 可选 | 服务器到服务器 | 可能会阻止此端口 |
| 587 | 提交 | 强制 | 强制 | 用户到服务器 | 应该允许此端口 |
希望这能让区别更加清晰。现在,让我们启用提交服务。所有 Postfix 服务都在 /etc/postfix/master.cf 文件中声明。请编辑该文件,找到默认情况下被注释掉的提交部分。将其修改为如下所示。基本上,我删除了该部分所有行开头的“#”字符,并删除了包含 mua_* 变量的行。即使您更改了收件人和中继限制的值,也没有关系。
submission inet n - y - - smtpd
-o syslog_name=postfix/submission
-o smtpd_tls_security_level=encrypt
-o smtpd_sasl_auth_enable=yes
-o smtpd_tls_auth_only=yes
-o smtpd_reject_unlisted_recipient=no
-o smtpd_client_restrictions=
-o smtpd_helo_restrictions=
-o smtpd_sender_restrictions=
-o smtpd_relay_restrictions=permit_sasl_authenticated,reject
-o smtpd_recipient_restrictions=
-o milter_macro_daemon_name=ORIGINATING
确保第一行从第一列开始,后续行缩进。
此服务使用“smtpd”守护进程(参见第一行的最后一个单词),该软件会在您打开 TCP 端口 25 的 SMTP 连接时做出响应。但它会设置一些额外的选项……
- 在 /var/log/mail.log 日志文件中,您将看到到提交端口的连接显示为“postfix/submission”(上面的 syslog_name 设置)
- 在此端口强制执行加密(smtpd_tls_security_level)
- 启用身份验证smtpd_sasl_auth_enable)
- 在身份验证期间强制执行加密 (smtpd_tls_auth_only)
- 允许向此邮件服务器以外的收件人发送电子邮件 (smtpd_reject_unlisted_recipient)
- 移除特殊限制 (smtpd_*_restrictions)
- 如果发件人已通过身份验证,则允许中继
- 将字符串 ORIGINATING 发送到 milter 服务 (milter_macro_daemon_name) -- 您可以保持原样
重启 Postfix 服务器:
sudo systemctl restart postfix
您的用户现在可以使用提交端口发送电子邮件。他们只需在邮件客户端中使用端口 587 而不是端口 25。
您可以尝试使用提交端口和加密发送测试邮件。您需要先安装 libnet-ssleay-perl 软件包,以便 SWAKS 能够支持 TLS:
sudo apt install -y libnet-ssleay-perl
swaks --server localhost --to john@example.org \
--port 587 -tls \
--auth-user john@example.org \
--auth-password summersun
请注意,以这种方式设置的密码不应包含特殊字符,例如 $!,因此我已将其改回 summersun。
端口 465 是什么?
这个 TCP 端口属于“基于 TLS 的邮件提交”服务。它用于邮件提交服务,但要求从第一个字节开始就使用加密连接。这个端口很少使用,所以您无需担心。您刚刚配置的邮件提交服务也使用了加密,但它使用 STARTTLS 机制在收到欢迎消息后切换到 TLS 连接。现在您可以使用 PHPmailer 发送邮件。发送邮件的设置如下:
$mail->isSMTP(); //使用 SMTP 发送
$mail->Host = 'tls://172.191.204.33.nip.io'; //设置 SMTP 服务器
$mail->SMTPAuth = true; //启用 SMTP 身份验证
$mail->Username = 'john@172.191.204.33.nip.io'; //SMTP 用户名
$mail->Password = 'summersun'; //SMTP 密码
$mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS; //启用显式 TLS 加密
$mail->Port = 587; //要连接的 TCP 端口
//收件人
$mail->setFrom('john@172.191.204.33.nip.io', 'UMD MSGIS');
$mail->addAddress('john@172.191.204.33.nip.io', 'Xin Tao'); //添加收件人
使用电子邮件管理软件(例如 Thunderbird 或 Outlook),并使用您的帐户(例如 john@example.org )和密码登录。登录后,您应该能够在邮箱中看到邮件。
2. 管理用户、别名和域
您可能已经知道如何创建邮件域和邮件用户。毕竟,我在准备数据库的部分已经解释了数据库架构。但如果解释得不够清楚,让我来解释一下如何管理您的邮件账户。使用 SQL 查询
以下部分解释了您可以用于常见管理任务的更改和 SQL 查询:创建新的邮件域
INSERT INTO virtual_domains (name) VALUES ("example.org");
删除邮件域
DELETE FROM virtual_domains where name='example.org';
创建邮件用户
INSERT INTO virtual_users (domain_id, email, password) VALUES ((SELECT id FROM virtual_domains WHERE name='example.org'), 'j@example.org', '{BLF-CRYPT}$2y$05$sKw0P0FqUQnh.tzSbuA7aO7j4SRf9lZFoh9a70V4scxLI2Vb91p1q');
更改用户密码
UPDATE virtual_users SET password='{BLF-CRYPT}$2y$05$.We…' WHERE email='email@address';
删除邮件用户
在 virtual_users 表中查找正确的“email”字段,找到相应的行并将其删除。邮箱仍会保留在磁盘上的 /var/vmail/... 目录中,您需要手动删除它。创建邮件转发
您可以将电子邮件从一个(源)电子邮件地址转发到其他地址(目标地址)——甚至可以转发到您的邮件服务器之外的地址。从 virtual_domains 表中找到正确域的“id”(源电子邮件地址“@”符号后面的部分)。为每个目标地址在 virtual_aliases 表中创建一行(如果您有多个目标地址)。将“source”字段设置为完整的源电子邮件地址。并将“destination”字段设置为相应的完整目标电子邮件地址。
每个您托管的域名都必须设置两个电子邮件地址:postmaster@domain 和 abuse@domain。这些要求在 RFC 521 和 RFC 2142 中有详细说明。请务必为这两个地址添加别名。如果发送到这些地址的邮件被退回,您的域名信誉将会受到影响。删除邮件转发
在 virtual_aliases 表中查找所有具有正确“source”电子邮件地址的行。删除所有指向您不想转发电子邮件的“destination”地址的行。