邮件中继

之前教程 “在 Linux 上设置邮件服务” 讲解了软件安装以及服务器设置以接收和发送电子邮件。在本教程中,我们将设置服务器以转发最终用户的电子邮件。我们还将向您展示如何使用 PHPMailer和管理电子邮件数据库。

1. 通过 Postfix 中继外发电子邮件

我们使用 Postfix 来中继我们的电子邮件。

中继

请注意, 用户 发送电子邮件的方式与 服务器 发送电子邮件的方式有所不同。例如:

接收电子邮件

当互联网上的某人向 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

这将启用 SMTP 身份验证,并告诉 Postfix 可以通过位于 /var/spool/postfix/private/auth 的套接字文件与 Dovecot 通信。您还记得 Postfix 运行在沙盒 chroot 目录中吗?该目录位于 /var/spool/postfix。它无法访问该目录之外的任何文件。但幸运的是,在前面的章节中,您编辑了 /etc/dovecot/conf.d/10-master.conf 文件,并使 Dovecot 将套接字文件放置到 /var/spool/postfix/private/auth 中,以允许 Postfix 进行通信。

启用加密

以下设置启用加密,并为 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 telnet

telnet 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

让我简要解释一下这些行的含义: 但是,这里缺少一行重要的内容,这行内容可以让我们发送用户名和密码:

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

现在我们使用的是加密连接,Postfix 允许我们进行身份验证。因此,让我们发送包含 Base64 编码密码的身份验证字符串(用户名 john@example.org,密码 summersun):

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
25STMP可选可选服务器到服务器可能会阻止此端口
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 连接时做出响应。但它会设置一些额外的选项……

重启 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”地址的行。

下一步

本教程介绍如何设置邮件服务器以转发最终用户的电子邮件。有关如何配置邮件服务器的网页界面, 请参阅以下说明“使用 Roundcube 的网页邮件”。