简体中文
cover

一、为什么需要这套方案?

当你在云服务器尝试搭建邮箱服务时,常常会遇到运营商封锁 25 端口的难题,25 端口就像邮局的发货通道,一旦被封,你的邮件服务器就像被没有腿的邮递员,无法对外发送信件

如果无法解除此端口的屏蔽,需要通过其他服务来转发出站邮件,以便其他服务代您发送。我的解决方案是使用 Resend,也就是让人帮他送

并且我需要一台可以创建无限邮箱地址的服务器,因为有个网站会每隔一段时间给账户发兑换码:)方便我白嫖,还有我用邮箱注册了数百个网站,验证码都发在一堆不方便管理,有了自己的服务器后可以按照网站随便注册

比如说,我需要注册 Bilibili,那么邮箱就填 bilibili@qladgk.com ,我不用在服务器中手动添加这个邮箱,可以直接设置一个规则,让不存在的邮箱接受的邮件全部转发给 info@qladgk.com,这样所有的验证码都在 info 这个邮箱。如果哪天我的邮箱泄露,可以很方便的知道是哪个网站对应的邮箱

二、准备你的工具箱

工具/服务作用说明获取方式
Docker容器化部署环境官网下载
Resend 账户邮件中继服务(每月免费额度 3000 封)注册
域名邮箱域名(如 qladgk.com)域名注册商购买
SSL 证书加密通信保障使用受信任的 CA 颁布的证书

三、五步搭建实战

步骤 1:创建目录结构

mkdir -p mail.qladgk.com/docker-data/{dms/config,certs}
cd mail.qladgk.com

我个人喜欢在服务器的 ~ 位置创建用具体域名的文件夹来管理,所以就用了 mail.qladgk.com,然后需要确认你的 SSL 证书中有这个域名

步骤 2:准备 docker-compose.yml

# docker-compose.yml
version: "3.8"

services:
  mailserver:
    image: ghcr.io/docker-mailserver/docker-mailserver:latest
    container_name: mailserver
    # Provide the FQDN of your mail server here (Your DNS MX record should point to this value)
    hostname: mail.qladgk.com # 修改成你的域名
    env_file: mailserver.env
    # More information about the mail-server ports:
    # https://docker-mailserver.github.io/docker-mailserver/latest/config/security/understanding-the-ports/
    ports:
      - "25:25" # SMTP  (explicit TLS => STARTTLS, Authentication is DISABLED => use port 465/587 instead)
      - "143:143" # IMAP4 (explicit TLS => STARTTLS)
      - "465:465" # ESMTP (implicit TLS)
      - "587:587" # ESMTP (explicit TLS => STARTTLS)
      - "993:993" # IMAP4 (implicit TLS)
    volumes:
      - ./docker-data/dms/mail-data/:/var/mail/
      - ./docker-data/dms/mail-state/:/var/mail-state/
      - ./docker-data/dms/mail-logs/:/var/log/mail/
      - ./docker-data/dms/config/:/tmp/docker-mailserver/
      - ./docker-data/certs/:/tmp/certs/ # 修改成你的证书目录
      - /etc/localtime:/etc/localtime:ro
    environment:
      - SSL_TYPE=manual # 如果要手动设置路径证书类型必须是手动
      - SSL_CERT_PATH=/tmp/certs/nginx.crt # 改成你自己的证书地址
      - SSL_KEY_PATH=/tmp/certs/nginx.key # 改成你自己的证书地址
      - DEFAULT_RELAY_HOST=[服务商获取]:端口 # 中继服务主机
      - RELAY_USER=服务商获取 # 中继服务用户名
      - RELAY_PASSWORD=服务商获取 # 中继服务密码
    restart: always
    stop_grace_period: 1m
    # Uncomment if using `ENABLE_FAIL2BAN=1`:
    # cap_add:
    #   - NET_ADMIN
    healthcheck:
      test: "ss --listening --tcp | grep -P 'LISTEN.+:smtp' || exit 1"
      timeout: 3s
      retries: 0

官网的 compose 对比只修改了这些部分:

hostname: mail.qladgk.com # 修改为你的域名
volumes:
  - ./docker-data/certs/:/tmp/certs/ # 准备你的域名证书
environment:
  - SSL_TYPE=manual # 如果要自定路径证书类型必须选手动
  - SSL_CERT_PATH=/tmp/certs/certs.crt # 根据自己的证书名设定
  - SSL_KEY_PATH=/tmp/certs/certs.key # 根据自己的证书名设定
  - DEFAULT_RELAY_HOST=[服务商获取]:端口 # 中继服务主机
  - RELAY_USER=服务商获取 # 中继服务用户名
  - RELAY_PASSWORD=服务商获取 # 中继服务密码

然后还需要将官网的 mailserver.env 下载下来,这个文件尽量不要改动,东西很多容易错,我们就用 Docker 的环境变量就好,而且环境变量优先于 mailserver.env

步骤 3:配置 Postfix 中继主机

注册好 Resend 后添加域名并且使用 DNS 验证

设置 Resend DNS 解析

然后需要等待验证通过,可能需要两三分钟

Resend DNS 验证

然后就可以设置主机、用户名、密码了,修改 docker-compose.yml

- DEFAULT_RELAY_HOST=[smtp.resend.com]:587
- RELAY_USER=resend
- RELAY_PASSWORD=创建一个 api 就是密码

以上配置是根据 Resend 设置,在官网的这个地方

Resend STMP 服务器

默认用户名应该都是 resend,密码就是你创建的 apikey

步骤 4 :初始化邮件服务器

# 首次启动初始化
docker compose up -d

# 在 120 秒内创建管理员邮箱
docker exec mailserver setup email add postmaster@qladgk.com <你的邮箱密码>

# 然后创建一个自己用的邮箱
docker exec mailserver setup email add hi@qladgk.com <你的邮箱密码>

# 生成 DKIM 签名
docker exec -it mailserver setup config dkim

docker-data/dms/config/opendkim/keys/qladgk.com/mail.txt() 中的复制出来,并删掉其中的 ""多余的空格 拼接成一行备用

比如生成的 txt 如下

mail.txt

拼接之后的 txt 如下:

v=DKIM1; h=sha256; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAybULjMm1Pv2qn8l0SIan+I8NpBTanNQaVFNVLvR5We9QITGAfxJtVUJv9e9e0BV3XVR/hDWRwxfw4KG6szRzUB1VABp4knYgwr1i3UNgKag3pSgTS4v1k5BccwIod/UK/E6+6iRiIzVoTikd3eSuygDAnAf+/FbZcnVF2Y/W4BvC9+aOyyDUYVCsSmPMgK99fm6fj5Q1LGMAvDapZ4XP+A7aFIHOL+AffPquPcsPAymJgC9guOJH4+D+ejqL5T6loTIljcGzcbavd82sRt01BxX3vRWqsqQ4K5I3yNVX5aLm1vrk5EIssGlyKxjjW9c2+6N7TUz6BD78AaKQmlBlfwIDAQAB

步骤 5:配置 DNS 记录

类型名称说明
Amail你的服务器 ip邮件路由
MX@mail.qladgk.com优先级设置成 10
TXT@v=spf1 mx -allSPF 发件人验证
TXT_dmarcv=DMARC1; p=none;DMARC 记录
TXTmail._domainkey刚才准备的那串 txtDKIM 记录

算上开始配置的 Resend 的 DNS ,你的 DNS 解析应该如下

类型名称说明
Amail你的服务器 ip邮件路由
MX@mail.qladgk.com优先级设置成 10
TXT@v=spf1 mx -allSPF 发件人验证
TXT_dmarcv=DMARC1; p=none;DMARC 记录
TXTmail._domainkey刚才准备的那串 txtDKIM 记录
-----------------------------------------------------
MXsendResend 提供的值优先级设置成 10
TXTsendResend 提供的值SPF 发件人验证
MXresend._domainkeyResend 提供的值DKIM 记录

这里同时配置了两个邮箱的 SPF/DKIM ,因为最后邮件是 Resend 用 @send.qladgk.com 这个邮箱地址代发,那么 send.qladgk.com 子域的 SPF/DKIM 记录确实需要按 Resend 要求配置。同时,根域名 @ 的 DMARC 记录对于邮件 From: 是 user@qladgk.com 的情况仍然重要,它需要与 send.qladgk.com 的 SPF/DKIM 对齐(通常是宽松对齐)。

四、验证你的邮箱系统

发送测试邮件

使用雷鸟客户端,添加邮箱账户和密码

雷鸟客户端

这里的收件和发件都是 mail.qladgk.com 是没有问题的

添加账户后给你的 163 或者 qq 尝试发送邮件,可以看到成功收到

发送邮件测试

发件人是 hi@qladgk.com 后面显示的是由 xxxxx@send.qladgk.com 代发

测试 163 直接回复

直接回复测试

可以看到直接回复的是 origin 地址而不是代发地址

https://www.mail-tester.com/ 发送测试邮件,查看邮件评分

邮件评分测试

同时你也能看见 Resend 中帮你转发的邮件

Resend 记录

接收邮件测试

接受邮件测试

发现我们的 hi@qladgk.com 邮箱成功接收到 163 发来的邮件,但是 Resend 中没有接收记录,后面会解释

五、原理解释

涉及的服务器与 DNS 配置

自建服务器配置(mail.qladgk.com)

类型完整主机名值示例作用说明
Amail.qladgk.com11.22.33.44邮件服务器 IP 地址
MX@qladgk.commail.qladgk.com声明主域名的邮件服务器地址(优先级 10)
TXT@qladgk.comv=spf1 mx -all仅允许 mail.qladgk.com 的 IP 发送邮件(防御伪造)
TXTmail._domainkey.qladgk.comv=DKIM1; p=公钥...邮件数字签名公钥(防止邮件被篡改)

Resend 中继配置(send.qladgk.com)

类型完整主机名值示例作用说明
MXsend.qladgk.comfeedback-smtp.us-east-1.amazonses.comResend 要求的特殊 MX 记录(用于建立发送域身份)
TXTsend.qladgk.comv=spf1 include:amazonses.com ~all授权 Resend 的服务器代发邮件
TXTresend._domainkey.qladgk.comk=rsa; p=公钥...Resend 提供的 DKIM 公钥(验证邮件来源合法性)

全局反垃圾邮件策略

类型完整的主机名称值示例作用
TXT_dmarc.qladgk.comv=DMARC1; p=none;邮件验证策略(告知收件方如何处理验证失败的邮件)

也就是说我们一共在解析中设置了两个 MX 方式,所以会出现两个邮箱地址 比如 hi@qladgk.comhi@send.qladgk.com

涉及到的端口和认证信息

为了让主机认证我们,需要提供端口和账户密码

在 IMAP 邮件服务器中常见的端口分别如下

端口协议使用场景加密方式
25SMTP服务器之间的邮件传输
465SMTP over SSL客户端到服务器的加密邮件发送SSL/TLS
587SMTP with STARTTLS客户端到服务器的加密邮件发送STARTTLS
143IMAP客户端从服务器接收邮件(不加密)
993IMAP over SSL客户端从服务器加密接收邮件SSL/TLS

这里容易混淆,需要慢慢理解

  ┏━━━━━━━━━━ Submission ━━━━━━━━━━━━━┓┏━━━━━━━━━━━━━ Transfer/Relay ━━━━━━━━━━━┓

                            ┌─────────────────────┐                    ┌┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┐
MUA ----- STARTTLS -------> ┤(587)   MTA ╮    (25)├ <-- cleartext ---> ┊ Third-party MTA ┊
    ----- implicit TLS ---> ┤(465)       │        |                    └┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┘
    ----- cleartext ------> ┤(25)        │        |
                            |┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄|
MUA <---- STARTTLS -------- ┤(143)   MDA ╯        |
    <---- implicit TLS ---- ┤(993)                |
                            └─────────────────────┘

  ┗━━━━━━━━━━ Retrieval ━━━━━━━━━━━━━━┛

MUA 是客户端、MTA 是服务器

发邮件过程会经理 MUA - MTA - MTA - MUA 这过程,我们所说的加密邮件指的是 MTA 与 MUA 之间的过程,而 MUA 之间的传输我们不能干预

邮件工作流程图

之所以之前只能接受邮件而不能发送,是因为服务器封锁 25 端口的出站流量,而没有封锁入站流量。如下图:

25 端口测试

可以看到用服务器通过 25 去访问别人的 stmp 会失败,但是个人电脑上去访问服务器的 25 则成功

接下来是认证信息

其中邮箱客户端(雷鸟)里:

  • imap 主机是 mail.qladgk.com、端口是 993(SSL/TLS 加密方式)、用户名是邮箱、密码是邮箱密码
  • smtp 主机是 mail.qladgk.com、端口是 587(STARTTLS 加密方式)、用户名是邮箱、密码是邮箱密码

因为接受邮件只有 993 有加密,所以选他。关于 465 和 587 选谁网上说法不一,但是 587 是现在邮箱常见默认端口,所以选他

而我们的 Resend 的 SMTP 的认证信息则直接存放在 docker-mailserver 里

# 配置中继
relayhost = [smtp.resend.com]:587
smtp_sasl_auth_enable = yes
smtp_sasl_password_maps = static:resend的用户名:resend的密码
smtp_sasl_security_options = noanonymous

这里的 587 指的是我们的邮件服务器现在作为客户端与 Resend 服务端通过 STARTTLS 加密方式将邮件传到他们的 STMP 上

邮件发送流程(中继模式):

  1. 客户端提交:雷鸟通过加密连接将邮件提交到 mail.qladgk.com(587 端口),使用邮箱账户密码认证
  2. 中继路由决策:Postfix 检测到目标为外部域(如 163.com),根据 relayhost = [smtp.resend.com]:587 启动中继流程
  3. 中继服务器认证:使用 SASL 认证机制,通过 Resend 提供的 API Key 完成身份验证:
  4. 信封发件人重写:Resend 会将信封发件人(Envelope From)改为 xxx@send.qladgk.com,这是邮件显示"代发"的原因
  5. 最终投递:Resend 通过 25 端口将邮件投递到目标服务器,完全绕开本地服务器的出站限制

所以最后收到的邮件的代发人是 xxx@send.qladgk.com

邮件接收流程

  1. MX 记录查询:外部服务器(163)给 hi@qladgk.com 发邮件时通过 MX 记录找到他的邮件服务器(mail.qladgk.com)
  2. 直接投递:发送方通过 25 端口直连你的服务器(云厂商通常允许入站 25 端口)
  3. 本地存储:Postfix 将邮件存入 /var/mail/ 目录下的用户邮箱
  4. 客户端同步:雷鸟通过 IMAPS(993 端口)加密拉取邮件

为什么接收邮件不经过 Resend?

接收流程是标准的邮件服务器间直接通信(Server to Server),仅依赖:

  • 正确的 MX 记录指向
  • 25 端口的入站连接(通常云厂商允许入站 25 端口)
  • 有效的 SSL 证书

而发送时需要出站 25 端口连接(被禁止),因此需要通过中继 "借道" 发送。

Resend 的角色

Resend 在本方案中充当邮件中继服务器,主要负责将你的邮件投递到目标邮箱服务器。

通过配置 Postfix 的 relayhost 参数,将本地邮件服务器的发送任务委托给 Resend 的 SMTP 服务器。

Resend 提供了每月 3000 封免费额度,对于个人用户或小型团队来说,基本可以满足日常需求。

六、常见问题排雷

1. 邮件进入垃圾箱

  • 检查 SPF/DKIM/DMARC 记录
  • 申请反向解析 PTR

其中阿里云的 PTR 很好申请,发个工单就行,三分钟分钟之内添加好

阿里云 PTR 申请

我是这么发的:

主题:

申请添加PTR记录

内容:

尊敬的阿里云售后工程师:
您好!我正在搭建邮件服务器,需要为我的服务器添加 PTR 记录,以便提高邮件的送达率。现向您提交申请,以下是相关信息:
ECS 实例所属地域:[具体地域,如北京、深圳等]
ECS 实例的公网 IP 地址:[公网IP地址]
对应的域名:[域名]
请协助我完成 PTR 记录的添加,谢谢!

2. 客户端连接失败

# 检查端口监听状态
docker exec mailserver netstat -tulpn | grep -E '25|587'
0
0
0
0