最近和 @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 服务器是否正常工作:
1 | telnet smtp.idoknow.top 25 |
连接成功后,输入 HELO 命令。
1 | Trying 45.137.180.174... |
我们要搭建的邮件服务器需要支持 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.yaml
和 mailserver.env
文件。
修改 compose.yaml
文件:
- hostname: smtp.xxx.com
- volumes 处新挂载一个目录,用于存放 SSL 证书
1
- /root/docker/caddy/config/caddy-data/caddy/certificates/acme-v02.api.letsencrypt.org-directory/smtp.idoknow.top/:/etc/letsencrypt
宿主机中的证书路径请根据自己的实际情况修改,需要保证路径下直接含有 xxx.com.crt
和 xxx.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 内设置一个新的邮箱,否则容器会自动关闭。
1 | 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 的作用:
1 | telnet smtp.idoknow.top 25 |
我们在这里伪造了 x.com 的邮件地址,期望以 elonmusk@x.com 的名义发送垃圾邮件到 soulter@idoknow.top,但是由于 x.com 设置了 SPF。
因此邮件服务器会拒绝这封邮件:
1 | 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
中,使用
1 | docker exec -it mailserver setup config dkim |
来生成 DKIM 的公钥和私钥。然后使用 docker exec -it mailserver cat /tmp/docker-mailserver/opendkim/keys/idoknow.top/mail.txt
来查看 DKIM 的公钥,如下:
1 | mail._domainkey IN TXT ( "v=DKIM1; h=sha256; k=rsa; " |
然后添加 TXT 记录到 DNS 中:
- 记录名为
mail._domainkey
- 记录值为 () 当中的内容,并且去掉换行符、引号。
在 Gmail 中,随便打开一个权威网站发来的邮件。点击 View Source,可以看到 DKIM 的签名。下面展示了 noreply@telegram.org
发送给我的邮件的 DKIM 签名:
1 | 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 |
其中,h
的内容说明这个数字签名包含了哪部分的邮件内容。bh
是邮件正文的 hash 值,用来在打开正文前就能计算签名。b
是使用了私钥加密的数字签名。
DMARC
DMARC(Domain-based Message Authentication, Reporting and Conformance)是一种用于验证邮件的技术,它可以防止伪造邮件。它需要 SPF 和 DKIM 的支持。
和 SPF 一样,我们需要在 DNS 记录中添加一条 TXT 记录来应用 DMARC。具体的记录值请使用 DMARC 生成。