自部署邮件服务器

最近和 @Rockchin 合资购买了一个位于日本东京的 4C8G 的高性能 VPS,用于部署一些服务。在这里记录一下自己搭建邮件服务器的过程。

0x01. SMTP、IMAP、POP3 协议

SMTP(Simple Mail Transfer Protocol)是用于发送邮件的位于应用层的协议,使用 TCP 协议作为传输层协议,端口号为 25。SMTP 协议规定了很多命令,它告诉客户端或服务器要采取什么操作及如何处理任何伴随的数据。比如

  • HELO:客户端向服务器标识自己
  • MAIL FROM:指定邮件的发件人
  • RCPT TO:指定邮件的收件人
  • DATA:发送邮件内容
  • QUIT:结束会话

客户端与服务器建立 TCP 连接后,客户端会首先发送一条 HELO 命令,然后服务器会返回一条 250 OK 命令,表示连接成功。可以使用 telnet 命令测试 SMTP 服务器是否正常工作:

telnet smtp.idoknow.top 25

连接成功后,输入 HELO 命令。

Trying 45.137.180.174...
Connected to smtp.idoknow.top.
Escape character is '^]'.
220-smtp.idoknow.top ESMTP
220 smtp.idoknow.top ESMTP
HELO smtp.idoknow.top
250 smtp.idoknow.top

我们要搭建的邮件服务器需要支持 SMTP 协议才能发送邮件。如今,随着 TLS/SSL 的普及,SMTP 服务器也支持了加密传输,形成 SMTPS 协议,端口号为 465。

IMAP(Internet Message Access Protocol)和 POP3(Post Office Protocol 3)是用于接收邮件的协议。IMAP 和 POP3 都是基于 TCP 协议的应用层协议,IMAP 默认端口号为 143,POP3 默认端口号为 110。IMAP 和 POP3 协议规定了客户端如何从服务器上获取邮件。这两者的区别在于 IMAP 协议是双向的,可以在客户端和服务器之间同步邮件状态,而 POP3 协议是单向的,只能将邮件从服务器下载到客户端。这意味着标记邮件为已读、删除邮件等操作,如果使用 POP3 协议,只会在客户端上生效,不会同步到服务器上。因此我们更常用的是 IMAP 协议。

0x02. 搭建

添加 DNS 记录

  • 添加一条 A 记录:指定邮件服务器的 IP 地址。比如 smtp.idoknow.top。
  • 添加一条 MX 记录:指定邮件服务器的域名。比如 smtp.idoknow.top。

MX 记录是邮件交换记录,它指定了接收邮件的服务器。当发送邮件时,邮件服务器会根据 MX 记录找到接收邮件的服务器。比如我们设置了 idoknow.top 指向 smtp.idoknow.top。因此我们最终的邮箱地址就可以是 admin@idoknow.top

我们使用 docker-mailserver 来搭建邮件服务器,它是一个基于 Docker 的邮件服务器,支持 SMTP、IMAP、POP3 协议。我们可以使用 docker-compose 来管理容器。

申请 SSL 证书

我们使用了免费的 Let’s Encrypt 证书。官方提供了一个方便的工具 Certbot,它可以自动为我们申请证书。当然,由于我们在自己的服务器中使用了 caddy 作为反向代理,caddy 也支持自动申请证书,因此我们使用的是 caddy。

下载、配置

首先,前往 GitHub 下载 compose.yamlmailserver.env 文件。

修改 compose.yaml 文件:

  • hostname: smtp.xxx.com
  • volumes 处新挂载一个目录,用于存放 SSL 证书
    - /root/docker/caddy/config/caddy-data/caddy/certificates/acme-v02.api.letsencrypt.org-directory/smtp.idoknow.top/:/etc/letsencrypt
    

宿主机中的证书路径请根据自己的实际情况修改,需要保证路径下直接含有 xxx.com.crtxxx.com.key 两个文件。

然后修改 mailserver.env 文件:

  • TZ=Asia/Shanghai
  • SSL_TYPE=manual
  • SSL_CERT_PATH=/etc/letsencrypt/smtp.idoknow.top.crt
  • SSL_KEY_PATH=/etc/letsencrypt/smtp.idoknow.top.key

这里的时区和 SSL 证书的文件名请根据自己的实际情况修改。

修改好后,使用 docker compose up -d 启动容器。使用 docker logs -f mailserver 查看日志,如果没有报错,说明启动成功。这时我们需要在 120s 内设置一个新的邮箱,否则容器会自动关闭。

docker exec -it mailserver setup email add admin@idoknow.top

0x03. 后期配置

现在,我们就成功搭建了一个邮件服务器,可以使用一些邮件客户端,比如 OS自带的、Outlook 等来连接到邮件服务器了。

然而,我们一定要给我们的邮件服务器添加一些安全性配置,比如 SPF、DKIM、DMARC 等。这些配置可以提高邮件的送达率,减少被识别为垃圾邮件的概率。

在接下来的操作中,我们可以使用 https://dmarcguide.globalcyberalliance.org/ 这个网站来检测我们是否成功配置了 SPF、DKIM、DMARC。

SPF

SPF(Sender Policy Framework)是一种用于防范伪造邮件的技术,它通过 DNS 记录来验证发件人的 IP 地址是否合法。我们需要在 DNS 记录中添加一条 TXT 记录,记录值为 v=spf1 a mx ip4:xxx.xxx.xxx.xxx -all。其中 a 表示允许发件人的域名解析为发件人的 IP 地址,mx 表示允许发件人的域名解析为发件人的 MX 记录,ip4:xxx.xxx.xxx.xxx 表示允许发件人的 IP 地址为 xxx.xxx.xxx.xxx

DNS 设置如下:

即可应用 SPF。

通过如下实验可以认识到 SPF 的作用:

telnet smtp.idoknow.top 25
HELO smtp.idoknow.top
MAIL FROM:<elonmusk@x.com>
RCPT TO:<soulter@idoknow.top>

我们在这里伪造了 x.com 的邮件地址,期望以 elonmusk@x.com 的名义发送垃圾邮件到 soulter@idoknow.top,但是由于 x.com 设置了 SPF。

因此邮件服务器会拒绝这封邮件:

550 5.7.23 <soulter@idoknow.top>: Recipient address rejected: Message rejected due to: SPF fail - not authorized.

DKIM

DKIM(DomainKeys Identified Mail)是一种用于验证邮件的技术,有助于防止违法人冒充发件人发送邮件。具体来说,DKIM 需要在 DNS 记录中添加一条 TXT 记录,记录值为 v=DKIM1; k=rsa; p=xxx。其中 v 表示 DKIM 的版本,k 表示使用的加密算法,p 表示公钥。所有从该域名发送的邮件都会包含一个 DKIM 标头,其中包含一段使用私钥加密的签名,接收邮件的服务器会使用公钥来验证签名的合法性。在没有 DNS 污染的情况下,这个过程可以保证邮件没有被任何中间人篡改。

docker-mailserver 中,使用

docker exec -it mailserver setup config dkim

来生成 DKIM 的公钥和私钥。然后使用 docker exec -it mailserver cat /tmp/docker-mailserver/opendkim/keys/idoknow.top/mail.txt 来查看 DKIM 的公钥,如下:

mail._domainkey	IN	TXT	( "v=DKIM1; h=sha256; k=rsa; "
	  "p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArOC9Y2APgcfDqRgHKO03dk1ysxRG355vkUD+75a7ghbNZen1H9WTHHCWdgQql2QZ5xhaJFFviBkgyE2zQTXtfnYkaV6+j4Q8/ij51/bgkEGjUVitZuGGvL8x1dGrYph19sYI6tSBgKkIZBG7M1lUtVU/Wt8KpUWklgDylX9v+rhL71mHgIqIhNjB41dj9YxPfXFfRsHBIDwntg"
	  "DlG1ClJC5NXMa94ru/Yl/Aba2Fw/nW4dxkcDq9XazkLMGYVWnG+knoKMwn33ii9yFl9p1RHjqIHIDSSBKNOnLyXhxhgTGqLXrCACsScJbOL20XQeq8PG7VsUX4sK8koUU4sdfrnwIDAQAB" )  ; ----- DKIM key mail for idoknow.top

然后添加 TXT 记录到 DNS 中:

  • 记录名为 mail._domainkey
  • 记录值为 () 当中的内容,并且去掉换行符、引号。

在 Gmail 中,随便打开一个权威网站发来的邮件。点击 View Source,可以看到 DKIM 的签名。下面展示了 noreply@telegram.org 发送给我的邮件的 DKIM 签名:

DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=telegram.org; s=mail2; t=1721635112; bh=Cgi+lNIWbizWIJWQP/B/QYforTgBVB9G6u0qqoSXeWQ=; h=To:Subject:From:Date:From; b=b3Rq7sjIpTpMP3ZEpMudLhf9tcZ5jrm3K9vcCEfOPLMvsNZ6OB9mjSrF32xdPREMP
	 AegNrLYttPXdfyPyTdJVMaivyLQLKyEhIn2AxB9L5uWD6YhPdkL7mYM4EbaE6B+TXk
	 aRkA/LceHnweIkiIGuVz5aa9+RsgUMxbvLP5j41I3HjyFHKJe5+NH2hzxwFOECplgR
	 jC3BKZmFbkLWQwLG0u12cfnhRWNAAObhtZ52AVx+qVrpXjCJjY9bsqGgTykyuT94Ih
	 +4Wpno3WIqXGhFCUBQFyCRRbB8uZiRzhNeef9SPKGgkrt/H/bUiSxkc8728CV51Lij
	 tDODoxHVIstfQ==

其中,h 的内容说明这个数字签名包含了哪部分的邮件内容。bh 是邮件正文的 hash 值,用来在打开正文前就能计算签名。b 是使用了私钥加密的数字签名。

DMARC

DMARC(Domain-based Message Authentication, Reporting and Conformance)是一种用于验证邮件的技术,它可以防止伪造邮件。它需要 SPF 和 DKIM 的支持。

和 SPF 一样,我们需要在 DNS 记录中添加一条 TXT 记录来应用 DMARC。具体的记录值请使用 DMARC 生成。