跳到主要内容

使用 Maddy & Snappymail 自建域名邮箱

虽然谷歌的无限别名很好用,但终归还是有自己的域名邮箱是更方便的。

网上看过一圈解决方案,有很多一站式的方案看起来很不错,比如说 poste.io,mailu.io 以及 mailcow 等,它们都提供 docker 解决方案,不过对于我而言,还是更倾向于使用开源的解决方案,而不是免费的解决方案。最终发现 maddy 项目能满足我的需求,它基于 GPL-3.0 开源协议,使用 Golang 开发,性能有保障,同时选用非开源的 SnappyMail 作为前端,等能找到更好的开源 mailbox 前端之前,先暂时选定它了。

本文假设读者已经拥有了自己的域名,并且拥有自己的能运行 docker & docker compose 的云环境。

准备工作 #

检查 25 端口是否开放 #

使用下面的命令检测你将要部署的 VPS 是否开放了 25 端口:

telnet smtp.qq.com 25

如果类似下面的显示则说明成功,否则代表不行,需要工单或者换其他的 VPS

telnet smtp.qq.com 25
Trying 1xxx.xxx.xxx.xxx...
Connected to smtp.qq.com.
Escape character is '^]'.
220 smtp.qq.com Esmtp QQ Mail Server

配置域名 DNS #

记录类型 域名
A mail 服务器ipv4地址
AAAA mail 服务器ipv6地址(可选)
A mx 服务器ipv4地址
AAAA mx 服务器ipv6地址(可选)
MX @ mx.yourdomain.com
TXT @ v=spf1 mx ~all
TXT mx v=spf1 mx ~all
TXT _dmarc v=DMARC1; p=quarantine; ruf=mailto:[email protected]
TXT _mta-sts v=STSv1; id=1
TXT _smtp._tls v=TLSRPTv1;rua=mailto:[email protected]
TXT default._domainkey *稍后请根据后文生成的内容填写
  • mail 域名用作 web 前端,mx 域名用作邮件发送域名
  • @代表你的二级域名,类似 yourdomain.com
  • [email protected] 可以根据你的实际情况调整,后续部署好实例后需要创建相应账号

将除了 default._domainkey 的 DNS 配置好后,就可以进入到部署环境了

部署 #

创建数据卷 #

为保证数据不跟随容器销毁,我们提前创建好接下来需要使用的 volume

docker volume create maddydata
docker volume create snappymail-data

获取证书 #

请自行使用合适的工具获取证书,本文使用的 certbot

配置 docker-compose 文件 #

mkdir -p /opt/mail && cd /opt/mail
nano docker-compose.yml

写入下面的 docker compose 文件

version: "3.8"
services:
  maddy:
    image: foxcpp/maddy:latest
    ports:
      - "25:25"
#      - "143:143"
#      - "587:587"
#      - "993:993"
    volumes:
      - maddydata:/data
      - /etc/letsencrypt/archive/mx.yourdomain.com/:/data/tls/
    environment:
        # REPLACE DOMAINS WITH YOURS
      - MADDY_HOSTNAME=mx.yourdomain.com
      - MADDY_DOMAIN=yourdomain.com
    deploy:
      replicas: 1
      resources:
        limits: { cpus: '0.2', memory: '32M' }
        reservations: { cpus: '0.05', memory: '16M' }

  snappymail:
    image: ratneo/snappymail
    ports:
      - '127.0.0.1:38888:8888'
    volumes:
      - snappymail-data:/snappymail/data
    depends_on:
      - maddy
    deploy:
      resources:
        limits: { cpus: '0.2', memory: '64M' }
        reservations: { cpus: '0.05', memory: '32M' }

volumes:
  maddydata:
    external: true
  snappymail-data:
    external: true
  • 注意将文件里的域名换做你自己的域名
  • 你肯定注意到了 maddy 常用的端口也被注释掉了,理论上如果你和我一样,只想通过 SnappyMail 提供的网页端访问,则保持注释,如果你想用第三方客户端连接你的邮箱,如 Outlook,则请将 587 和 993 端口开放。

启动服务

docker compose up -d

修改 maddy.conf 配置文件 #

cd $(docker volume inspect maddydata --format '{{.Mountpoint}}')
nano maddy.conf

确保证书文件路径和前面映射的相同

tls file /data/tls/fullchain1.pem /data/tls/privkey1.pem

重启 docker 服务

cd /opt/mail
docker compose restart

获取 DKIM 密钥 #

cd $(docker volume inspect maddydata --format '{{.Mountpoint}}')
cat dkim_keys/yezim.com_default.dns

生成的内容写入到 DNS 表中最后一个 default._domainkey 的值里

创建邮箱管理用户 #

docker exec -it mail-maddy-1 /bin/bash
maddyctl creds create [email protected]
maddyctl imap-acct create [email protected]

配置 nginx 反向代理 #

nano /etc/nginx/conf.d/mail.conf

下面是反向代理的样例

server {
        server_name mail.yourdomain.com;

        # Security / XSS Mitigation Headers
        add_header X-Frame-Options "SAMEORIGIN";
        add_header X-XSS-Protection "1; mode=block";
        add_header X-Content-Type-Options "nosniff";

        location / {
            proxy_redirect off;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_pass http://127.0.0.1:38888/;
        }

        listen 443 ssl;
        listen [::]:443 ssl;
        ssl_certificate /etc/letsencrypt/live/mail.yourdomain.com/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/mail.yourdomain.com/privkey.pem;
        include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
        ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot

}
server {
    if ($host = mail.yourdomain.com) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


    listen 80;
    listen [::]:80;
    server_name mail.yourdomain.com;
    return 404; # managed by Certbot
}

请根据你的实际情况自行编辑nginx反向代理配置

邮箱设置 #

登陆 SnappyMail 管理员页面,并编辑域名:https://mail.yourdomain.com/?admin/domains,默认用户名为 admin 密码在需要到挂载卷里取得,命令如下:

cd $(docker volume inspect snappymail-data --format '{{.Mountpoint}}')
cat ./_data_/_default_/adminpassword.txt

请务必修改为强密码

添加域名,在服务器一列请填写 maddy 并指定IMAP端口为 993,SMTP 端口为 465,同时加密都选为 SSL/TLS。

注:填写 maddy 的原因是使用 docker-compose 的 network 机制,在不暴露端口的情况下访问到 docker 实例

Cloudflare Zero Trust(可选) #

虽然我们给 SnappyMail 设置了强密码,但为了确保更安全,建议使用 Cloudflare Zero Trust 的机制保护我们的邮箱网页。 步骤为:

  1. 点亮 Cloudflare 小云朵
  2. 在 Zero Trust 的 Access 中添加 application,并指定保护策略

限于篇幅,此处就不再深入描述,作为一个安全提示

参考资料 #