使用 Maddy+RainLoop 搭建自己的邮箱

一直想用自己的域名搭建一个的邮箱,但传统的 Dovecot+Postfix 方案十分复杂,封装起来的 docker-mailserver 虽整洁不少,但仍然存在占用资源很高的问题。

想起 Golang 效率挺高的,也已经有了不错的生态,于是找到了 Maddy,一个使用 Golang 编写的一体化邮件服务器,占用较少,也免去了各种模块相互配合的。它充当了 MTA(中转服务器)和 MDA(投递服务器)的角色。

而 Rainloop 则是一个 PHP 写成的 webmail,可以作为一个类似于 Gmail 的网页端。

一般的 VPS 都可以搭建邮件服务器,不过某些服务商会屏蔽必要的 25 端口,可能需要发工单解决,不过上网搜一下一般就能知道。

本文以 Nginx 1.20.2、PHP 8.1.1、Ubuntu 18.04 LTS 为例

邮件服务器:Maddy

GitHub

foxcpp/maddy

下载与安装

Maddy 的文档 说得其实挺明白的,但如果你不想看英语,也可以跟我走。这里使用预编译的二进制文件来搭建。

首先要从 GitHub Releases 下载最新的二进制文件到服务器中,文件名为 maddy-x.x.x-x86_64-linux-musl.tar.zst。

解压 zst 文件需要 zstd 依赖(Debian 系可直接用 apt 安装),安装后使用

bash
1
tar --use-compress-program=unzstd -xvf archive.tar.zst

来指定 zstd 程序解压这个文件。然后将文件夹内的 maddy 和 maddycli 复制到 / usr/local/bin 目录下。

除此之外,Maddy 无法以 root 用户运行,所以你还需要新建一个用户:

bash
1
sudo useradd -mrU -s /sbin/nologin -d /var/lib/maddy -c "maddy mail server" maddy

预编译二进制文件的压缩包内还带有 systemd 服务,可以直接拷贝到系统文件夹内使用:

bash
1
2
sudo cp ./systemd/*.service /etc/systemd/system
sudo systemctl reload

将压缩包内附带的 maddy.conf 复制到 etc 目录下,然后用你喜欢的文本编辑器打开。暂时只需要编辑 Base variables 部分,记得要将上面的内容都换成你对应的。

bash
1
2
3
4
5
$(hostname) = mx1.example.org # 外界通过这个域名找到你的邮件服务器。
$(primary_domain) = example.org # 你的邮箱 @后面的域名,比如 test@example.org,而不一定是 test@mx1.example.org
$(local_domains) = $(primary_domain) # @后面可以添加的其他域名,比如 test@one.example.org。你需要在几个域名中选一个主要的,将其填入 primary domain。

tls file /etc/maddy/certs/$(hostname)/fullchain.pem /etc/maddy/certs/$(hostname)/privkey.pem # TLS 证书文件。

对于证书文件,如果你使用 Certbot 获取 hostname 的 TLS 证书,你可以直接进行一个软连接,使得 Maddy 可以识别,而且要确保其有权限读取:

bash
1
2
ln -s /etc/letsencrypt/live /etc/maddy/certs
sudo setfacl -R -m u:maddy:rX /etc/letsencrypt/{live,archive}

如果由其他方法获取证书,可以将文件拷贝至 / etc/maddy/certs 并用相似的命令赋予 maddy 用户读取权限。

现在我们可以将其启动了:

bash
1
systemctl start maddy

配置 DNS

首先是各个 domain 的 A/AAAA record,即上面提到 example.org 和 one.example.org 的指向。这个没必要指向你的邮件服务器 IP,也就是说你可以拿这些域名指向其他服务器建个站什么的,但如果没有另外的用处,不设置应该也没什么问题。

然后仍然是上面几个 domain,这次是 MX record。它由 domain 指向 hostname。举个例子,将 example.org 的 MX 记录指向 mx1.example.org,意思是任何发送到 @example.org 的邮件服务都由 mx1.example.org 来处理。如果你有多个 domain,那么需要逐个设置。

之后设置 hostname 即邮件服务器的 A/AAAA 记录。它需要将类似于 mx1.example.org 的地址解析为 IP 地址。

要设置 MTA-STS(后面会讲),需要将 mta-sts.example.org 和 mta-sts.one.example.org(即各个 domain)的 A/AAAA 记录指向邮件服务器 IP。

还有一系列的 TXT 类型的解析如下。

SPF 为 domain 和 hostname(非必要)都添加 txt 解析,内容如下。用于表明 MX 解析的域名是可以发邮件的。

text
1
v=spf1 mx ~all

DMARC 用于处理损坏的邮件。需要为所有 _dmarc.yourdomain(如 _dmarc.example.org 和 _dmarc.one.example.org)添加。

text
1
v=DMARC1; p=quarantine; ruf=mailto:postmaster@example.org

MTA-STS 标记 加上之后,失败的报告会被送到指定的邮箱(默认 postmaster)。为 _mta-sts.example.org 添加(其他 domain 不要漏下)

text
1
v=STSv1; id=1

然后为 _smtp._tls.example.org 添加

text
1
v=TLSRPTv1;rua=mailto:postmaster@example.org

DKIM Key 在 / var/lib/maddy/dkim_keys/example.org_default.dns(文件名因人而异)找到该项 TXT 记录值,并将其给予 default._domainkey.example.org 的 TXT 解析。

MTA-STS 保护

其实包括 MTA-STS 和 DKIM 等措施,对于发送邮件来说并不是必要的,但如果没有这几个措施,那么一些大电子邮件服务商(比如 Gmail)会认为从你的服务器发出的是垃圾邮件。

MTA-STS(RFC 8461)是一个预防中间人攻击的防护措施。它的标记已经在上一步做好。然后,我们需要使用一个网页服务器来返回一串文本。

官方文档推荐的方式是 serve 一个文本文档,内容如下:

plain
1
2
3
4
version: STSv1
mode: enforce
max_age: 604800
mx: mx1.example.org

当访问 https://mta-sts.example.org/.well-known/mta-sts.txt 的时候,就返回它。

对于 Nginx,也可以使用以下方式直接返回这串文字,在 Nginx 配置文件的 HTTP 块内加入一个 server:

nginx
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
server {
    server_name mta-sts.example.org;
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;
    ssl_certificate /path/to/fullchain.pem; # 路径因人而异,下同
    ssl_certificate_key /path/to/privkey.pem;
    location = /.well-known/mta-sts.txt {
        default_type text/plain;
        return 200 "version: STSv1\r\nmode: enforce\r\nmx: mx1.example.org\r\nmax_age: 604800\r\n";
    }
}

当然,这之前需要确保你将 mta-sts.example.org 指向正确的 IP,并申请其对应的 TLS 证书。

如果你使用 Nginx、Caddy 或 Litespeed 等网页服务器,请自行研究。

要验证是否添加成功,可以直接访问 https://example.org/.well-known/mta-sts.txt ,查看返回的内容。

TLSA/DANE 记录这里不再指引,因为我使用的阿里云 DNS 是不能添加 DLSA 记录的,有兴趣的可以自行看官方文档。

添加账户

Maddy 的账户体系是 “虚拟用户”,也就是说验证账户和 IMAP 邮箱是分离开来的。

首先,要创建用户验证,使用命令:

bash
1
maddyctl creds create example@example.org

输入账户密码,然后再创建它的邮箱:

bash
1
maddyctl imap-acct create example@example.org

这样,一个传统意义上的邮件账户就创建完成了。

如果需要更多帮助,可以使用

bash
1
2
maddyctl creds --help
maddyctl imap-acct --help

命令来获取。

到现在,你可以使用任何一个现代的邮件客户端,连接你刚刚创建的邮件服务器。IMAP 协议即可,发送和接收可以全都选 SSL。为安全起见,建议以后的邮件客户端全部启用 SSL。

发一封邮件,如果你上述的安全措施做得比较好,发送给 Gmail 等对安全比较严格的邮箱,应该能够作为重要邮件,起码不会被作为垃圾邮件。使用 IMAP+SMTP+SSL,收件端口号为 993,发件端口号为 465。

Webmail:RainLoop

RainLoop/rainloop-webmail

大部分看这篇文章的人都应该是搭着玩的个人用户吧。如果不是,那么请首先参阅 RainLoop 的 不同版本比较页面,他们针对不同用户有不同的版本和 Licence。这里直接选对无盈利的个人免费的 Standard Edition,因其带有比较简便的更新功能。

下载与安装

从这里 可以下载两种版本到服务器上。下载下来是 ZIP 格式,所以我们需要安装 unzip 解压(apt 包管理器内有,不带 apt 的发行版请自行想办法)。

然后输入以下命令,将 RainLoop 的文件拷贝至 / var/www/rainloop,并设置权限。你也可以使用其他自己喜欢的路径,别忘了修改后面的配置文件对应部分。

bash
1
2
3
4
unzip rainloop-latest.zip -d /var/www/rainloop
cd /var/www/rainloop
find . -type d -exec chmod 755 {} \;
find . -type f -exec chmod 644 {} \;

然后编辑 Nginx 配置文件,加入 server 块:

nginx
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name webmail.example.org;

    ssl_certificate /path/to/fullchain.pem;
    ssl_certificate_key /path/to/privkey.pem;

    index index.html index.php;
    root /var/www/rainloop;
    client_max_body_size 2G;

    error_log /var/log/nginx/rainloop.error.log;
    access_log /var/log/nginx/rainloop.access.log;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ^~/data {
        deny all;
    }

    location ~ \.php$ {
        fastcgi_pass php-handler-https;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }
}

其中你必须更改的是 server_namessl_certificatessl_certificate_key。你可以把页面直接放到邮件服务器上,也可以换个服务器或者域名,但如果你想直接用上文提到的 mx1.example.org 即 hostname,那么可以做正向代理,但更推荐的做法是把 RainLoop 直接部署到邮件服务器上。其他 root 等项视情况更改。根据官方文档,data 目录不能允许被外网访问从而直接获取配置文件,所以直接 deny all。

然后使用 nginx -t 测试配置文件,如果没有问题,就 systemctl restart nginx 重启 nginx,然后访问 webmail.example.org(例)试试吧。

若仍提示 no data folder write permission 权限问题,大不了给对应目录文件直接 777 权限(问题不大、

初始配置

访问 webmail.example.org/?admin 来进行初始配置。默认管理员用户名为 admin,密码为 12345。

首先建议在 “安全” 选项卡中修改默认管理员密码。在 “域名“选项卡中,你可以添加你刚刚架设好的邮件 domain,也可以添加其他商用邮箱域名。添加之后点击测试,可以测试一下邮件服务器在此设定下能否连通。正常来说,你不需要勾选” 使用短域名登录“。

此外,” 白名单 “功能能够限制某域中能登陆到 Webmail 的账户列表,如设置 example.com 的白名单为 test@example.com,那么除上述邮箱外,后缀为 @example.com 的邮箱全部不能登录。

这两项基础设置做完之后,你就可以重新打开 webmail.example.org,输入你的邮箱和密码,登录 Webmail。如果无法登录,请检查你刚刚的初始配置。如果能够成功进入邮箱,那就……enjoy it!

参考文献