自建邮件服务小记

咋回事啊

LWL 之前用过很多家的邮件服务。腾讯云企业邮箱、阿里云邮、ZOHO Mail、Submail (Push)…… 但是由于各种各样的原因,这些邮局都没能让我满意(例如国内大部分邮件服务甚至都不支持 DKIM 这种基础功能),于是咱决定自建一台邮件服务器。

前置准备

机器要求

LWL 使用 mailcow: dockerized 项目完成邮件服务器基础建设,因此需要满足一定的配置要求,具体如下:

  • CPU: 1 GHz
  • RAM: 1 GiB (1.5 GiB + Swap 最佳)
  • Disk: 5 GiB (不包括邮件储存空间)
  • System: x86_64 (所以 32 位系统大概用不了吧)

ClamAV 会大量占用 RAM. 可以在 mailcow.conf 中加入 SKIP_CLAMD=y 来关闭它。

另外,邮件服务器的真实 IP 无法隐藏,因此建议使用带高防的机器以防止遭受攻击。

检查端口

可以在 BASH 使用以下指令快速检查端口占用情况

netstat -tulpn | grep -E -w '25|80|110|143|443|465|587|993|995'

也可以参照下表自行检查

如有冲突情况,可以选择关闭正在占用端口的程序或是修改 mailcow.conf 中的默认端口设置。
服务器原有 NGINX 等网页服务时,可参看官方文档中的反向代理设置方法。这里顺带提供我使用的 NGINX 反代配置:

location ~* {
        proxy_pass http://127.0.0.1:8080;
        proxy_redirect https://127.0.0.1:8080 $scheme://$host:$server_port;
        proxy_redirect https://127.0.0.1:8443 $scheme://$host:$server_port;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

设置 DNS

对于邮件服务,DNS 配置错误将极有可能导致发出的所有邮件被收信方判定为 SPAM。因此正确的 DNS 配置是必要的。如果只提供程序使用(而非个人日常使用)的话,一般需要配置以下内容:

  • A / AAAA
  • MX Record
  • rDNS (即 PTR Record)
  • SPF
  • DKIM
  • DMARC

如果需要作为日常使用,为了便于邮件客户端配置,还可添加关于 autodiscoverautoconfig 相关的记录。这部分内容请参看官方文档

A / AAAA

A 记录,用于访问 mailcow 等组件的 Web 控制台,指向邮件服务器即可。
在稍后的安装过程中,程序会自动签发一张证书来保证 SSL/TLS 链接正常建立。由于使用 webroot 方式验证,请务必正确设置该记录。

MX Record

MX 记录的用途想必不用我再赘述,将其指向邮件服务器地址即可。

根据规范,不建议直接将 MX 记录指向 IP地址,应指向能够映射到一个或者多个 DNS 中类型为 A 或者 AAAA 的地址记录。且根据RFC2181,原则上禁止指向 CNAME 记录。

rDNS

rDNS,即反向解析,在 MTA (Mail Transfer Agent, 邮件传送代理)中通常被用于判断发送方邮件服务器域名与给定IP是否互相关联。

有些邮件系统例如会拒绝没有进行反向解析的发件服务器发出的所有消息,所以建议设置好反向解析。

由于 rDNS 需要你的服务器(IP)提供商协助才能设置,因此具体设置方法需咨询您的服务器提供商。部分服务商并不支持设置反向解析记录,如果为了搭建邮局特意购入服务器,请务必提前询问其对 rDNS 的支持情况。(当然,实在不支持那就没办法了╮(╯▽╰)╭)

SPF

SPF 记录的用途是阻止垃圾邮件发件人发送假冒您网域中的“发件人”地址的电子邮件。收件人可以参考 SPF 记录来确定号称来自您网域的邮件是否来自授权的邮件服务器。

如果你已经配置了 MX 记录,只需要添加一个内容为 "v=spf1 mx ~all" 的 TXT 记录即可完成 SPF 记录的配置。

例如,假设您的网域为 example.com。您为网域创建了 SPF 记录,将您的邮件服务器(们)识别为获得授权的邮件服务器。当收件人的邮件服务器收到来自 user@example.com 的邮件时,会检查 example.com 的 SPF 记录以确定该邮件是否有效。如果发送邮件的服务器不在 SPF 记录的您的邮件服务器列表中,收件人的邮件服务器会将其视为垃圾邮件而拒绝接收。

如果您的网域没有 SPF 记录,那么有些收件人可能会拒绝您发送的邮件,因为收件人无法验证相应邮件是否来自获得授权的邮件服务器。

(该文段修改自 Google)

DKIM

DKIM 记录是一个包含公钥的记录。邮件发出时,邮局会对邮局的标题、ID 等内容进行签名,收件方通过 DKIM 记录中的公钥验证该邮件签名是否有效,进而确认该邮件是否由真实的域名持有者发出。

正确的设置 DKIM 可有效提高邮件的可信度并提升到信率。由于密钥对生成需要后文内容支持,lwl 将在稍后讲述如何添加 DKIM 记录。不过,可以先让你看看它大概长什么样:

dkim._domainkey  IN TXT     "v=DKIM1; k=rsa; t=s; s=email; p=..."

DMARC

DMARC 记录用来指示收件方对 SPF 或 DKIM 检查情况的响应方式。由于 lwl 搭建只是作为系统发信使用,所以一步到位上了最严格的方式,即 DKIM / SPF 校验不通过就直接拒信(连对方垃圾箱都进不去)。具体配置条目如下:

_dmarc TXT "v=DMARC1; p=reject; pct=100;"

完成前置准备后,就可以开始进行安装了。

安装

Docker

Mailcow-dockerized 是基于 Docker 的项目,因此需要先安装 Docker。这不是本文的重点,如果有任何出错情况请自己多搜索一下(问我也没用我也不会)
在 BASH 执行以下指令即可完成安装:

curl -sSL https://get.docker.com/ | sh

Docker-Compose

还需要安装一个似乎是 Docker 包管理器的程序:

curl -L https://github.com/docker/compose/releases/download/$(curl -Ls https://www.servercow.de/docker-compose/latest.php)/docker-compose-$(uname -s)-$(uname -m) > /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose

Mailcow

终于到了今天的主角了,下面就来开始安装 Mailcow!

准备文件

使用 Git 下载相关文件即可:

git clone https://github.com/mailcow/mailcow-dockerized && cd mailcow-dockerized

生成配置文件

执行以下命令来生成配置文件。当要求输入域名时,请输入前文 rDNS 中设置的那个。(如果没设置 rDNS,请保证你输入的域名符合 FQDN 格式。如果你设置了 rDNS,服务商应该会保证你的格式符合 FQDN)

./generate_config.sh

如有需要,可以修改 mailcow.conf 中的配置项

拉取镜像并启动 mailcow

马上就要完成了!输入以下指令下载相关镜像并启动 mailcow:

docker-compose pull
docker-compose up -d

Done!

访问你之前输入的域名,就应该能看到 mailcow 的后台了。使用默认帐号 admin 和密码 moohoo 登录后台。

进一步配置

先修改掉默认的帐号和密码,如果想进一步提高安全性,设置两步验证(OTP),然后继续下面的步骤吧~

添加域名

选择顶部导航栏上的 Configuration -> Mailboxes -> Add domain 来添加一个域名

设置 DKIM

添加完域名后,我们终于可以完成上面没有设置完成的 DKIM 了。

回到 Configuration -> Administration, 选择下方的 Configuration 选项卡,在 Add ARC/DKIM key 区域填写域名,选择密钥长度(如果 DNS 服务商可以支持 2048 位长密钥的公钥,选择 2048 更安全。如果不支持,那就只能选 1024 位了)。Selector 如果没有冲突,一般不需要修改。

设置完成后,在上方应该能看到类似下图的信息:

前往 DNS 服务商,添加一条记录名为 Selector + ._domainkey. + Domain (host),类型 TXT,记录值为灰色框内内容的记录。如上图应该添加记录名为 dkim._domainkey,记录值为灰框内容的记录即可。如果上图中 Domain 改为 mail.example.com ,则相对应的,记录名应改为 dkim._domainkey.mail

添加用户

再次前往 Mailboxes 页面,选择 Add mailbox 并填写相关信息即可。

至此,您应该可以开始发送邮件了。用上面的信息通过 SMTP 发信试试吧!

IPv6

对于有 IPv6 地址的服务器,可编辑 data/conf/postfix/main.cf,搜索 smtp_address_preference 配置项,将其值改为 ipv6 以优先使用 v6 地址发信。
修改后执行 docker-compose restart 重启即可。

匿名化发送人

为了避免 IP 暴露可能引起的攻击,你可能使用了 CDN 等技术保护了后端 IP。但是在默认情况下,使用 SMTP 连接到邮件系统发信时,会将请求发信的 IP 信息写入邮件头中。使用下列步骤来去除这些信息:

新建 data/conf/postfix/mailcow_anonymize_headers.pcre,写入以下内容:

/^\s*Received:[^\)]+\)\s+\(Authenticated sender:(.+)/
    REPLACE Received: from localhost (localhost [127.0.0.1]) (Authenticated sender:$1
/^\s*User-Agent/        IGNORE
/^\s*X-Enigmail/        IGNORE
/^\s*X-Mailer/          IGNORE
/^\s*X-Originating-IP/  IGNORE
/^\s*X-Forward/         IGNORE

然后编辑 data/conf/postfix/main.cf,加入一句

smtp_header_checks = pcre:/opt/postfix/conf/mailcow_anonymize_headers.pcre

最后执行 docker-compose restart 重启即可。

设置 No Reply 邮箱

一般系统发信的邮箱是没人去看内容的,这时有必要设置拒绝所有来信。
在 mailcow 后台找到 SOGo 页面,登录要设置 no-reply 的帐号,选择左侧栏的设置 -> Mail -> FILTERS,按下图设置(其中 message 可以自己编一条)

让 WordPress 使用 SMTP 发信

global $phpmailer;
add_action('phpmailer_init', function (&$phpmailer) {
    $phpmailer->isSMTP();
    $phpmailer->Host = '你的邮件服务器地址';
    $phpmailer->Port = "465"; //使用 SSL 端口连接
    $phpmailer->SMTPAuth = true;
    $phpmailer->Username = "上文设置的用户名";
    $phpmailer->Password = '和密码';
    $phpmailer->SMTPSecure = 'ssl'; //使用 SSL 加密
    $phpmailer->From = "发送人邮件地址";
    $phpmailer->FromName = "发送人名称";
});

将以上内容加入主题的 function.php 即可

一些跑题的事情

至此全文的主要内容结束啦!去 Mail Tester 试试能拿多少分吧~

其实这篇文章刚开始写的时候标题打算叫 “欢迎来到实力至上主义的邮局”的( 后来觉得太尴尬了就还是没用……
于是下面是原定的两张题图,辛辛苦苦做了不发出来也太可惜了

最后,安利一下欢迎来到实力至上主义的教室这部番!超好看 233,本文的第一首 BGM 就是这部番的 ED 了!

- EOF -

46 条评论

昵称
  1. Saiki

    像我这种咸鱼还是…乖乖的用网易云的免费邮箱吧…
    对了问下大佬是用的什么服务器😂

    1. liwanglin12

      大概可以选择自己 dig 一下 IP 然后 ipip.net 直接查一下

  2. Tom

    我在Mail Tester是10分,然而自建的邮件系统发给国内的邮件服务还是进垃圾箱╮(╯▽╰)╭

    1. liwanglin12

      没办法,国内很多服务商不按套路来

    2. liwanglin12

      你们博客挺不错啊,可惜没有留言板

      1. Tom

        啊...留言板一直没做......折腾自建邮局,最后又用回免费邮了......

      2. liwanglin12

        所以大概,我博客发到你 126 的邮件正常么

      3. Tom

        正常ヾ(≧∇≦*)ゝ 我觉得网易比qq稍微宽容些......

      4. liwanglin12

        改到 QQ 邮箱看看你还能不能收到。。。(

      5. Tom

        好的w 你试下

      6. liwanglin12

        已经测试完了啊,上一封就是送达你关于页写的 QQ 邮箱的
        所以看你这么快跑过来应该是正确送达了吧

      7. Tom

        没问题 收到了

      8. Tom

        邮件正常接收,可以进收件箱,不会进垃圾箱。不过会提示:这不是腾讯公司的官方邮件。 为了保护邮箱安全,内容中的图片未被显示。 显示图片 信任此发件人的图片。

      9. liwanglin12

        有可能是因为提到了账号之类的词语的原因。不过其实图片也只有右上的那个小旗子,所以问题不大啦(

      10. Tom

        嗯嗯
        已经很优秀了……😀

  3. 狂放

    围观大佬

  4. jlqwer

    腾讯阿里都在封25端口🙃

  5. xtlsoft

    内存占用太大,大佬可以折腾下Postfix+Extmail吗?

    1. liwanglin12

      没这个打算诶(
      ram 的话,也许可以试试关掉 ClamAV

  6. Ailoli

    围观一下大佬facial

  7. Doracoin

    番不错,嗯,真不错facial

    1. liwanglin12

      重点不是番啊喂

  8. Axton

    虽然不是前排还是要挤挤
    话说mailcow吃性能不,有没有必要新开一台服务器单独搭着玩
    结尾才是中心内容啦(丢

    1. liwanglin12

      CPU 还好,Ram 有点高。文章里写了这个啊
      由于邮件服务 ip 藏不了 我是开了高防机器用来放。。同时做博客的热备
      另外你评论莫名被判垃圾了(

      4天前 - Akismet认为这是垃圾评论。

      1. Axton

        emmmm以及邮件再次成功投进hotmail的垃圾箱
        标记好几次了facial

      2. liwanglin12

        ∠( ᐛ 」∠)_微软系邮箱无能为力,可以看看这个
        建议加白名单(联系人)

      3. Axton

        用错邮箱了facial

  9. Fly

    facial

    我永远喜欢(已删除)facial

    1. liwanglin12

      剧透打死(╯‵□′)╯︵┴─┴

  10. 冯小贤

    学习了,又有个折腾的玩意了facial
    顺带说句实力至上的教室好看

  11. TiSpH

    结尾重点压的不错

  12. kn007

    现在都不用netstat用ss。。。netstat在链接打开多时,会卡死

  13. kn007

    辣鸡~不自建跟咸鱼有什么区别。

    1. liwanglin12

      仍然是自建,无非是省了自己搭而已,都是些反复没意义的事情

  14. Genteure

    ∠( ᐛ 」∠)_啊,月色真美

    1. liwanglin12

      →_→你可真皮

      1. Aqi

        不应该是狗粮真香吗୧(๑•̀⌄•́๑)૭

      2. liwanglin12

        去看番,第九集结尾

  15. DIYgod

    实力至上主义的教室可还行

    1. liwanglin12

      喂这不是本文重点啊

  16. 极雪

    说得好,我选择第三方facial

    1. liwanglin12

      不选第三方的原因文章开头就说的很清楚了吧……

  17. FGHRSH

    facial 刚好要自建

  18. neoFelhz

    罒▽罒(抢个地板

  19. METO

    第二

  20. Troy

    前排~