Files
2026-06-02 20:33:08 +08:00

4.7 KiB

外部邮件投递功能 TODO

目标

实现认证用户通过 Outlook/Web 向外部邮箱地址发送邮件,例如:

  • someone@qq.com
  • someone@gmail.com
  • someone@outlook.com

当前系统只支持本地用户投递,外部收件人会被拒绝。外部投递需要实现 SMTP 出站发送能力,同时避免开放中继风险。

阶段 1:最小可用外部投递

安全策略

  • 仅允许已认证用户外发。
  • MAIL FROM 必须等于登录用户邮箱。
  • Web 发信的 From 必须等于当前登录用户邮箱。
  • 限制单封邮件最大外部收件人数。
  • 限制单封邮件大小。
  • 明确拒绝未认证外部投递,防止开放中继。

DNS / MX 查询

  • 从外部收件人地址解析目标域名。
  • 使用 net.LookupMX(domain) 查询 MX 记录。
  • 按 MX 优先级排序。
  • 如果没有 MX,可按 RFC 规则尝试直接连接域名本身。
  • 记录 MX 查询失败原因。

SMTP 出站发送

  • 新增内部 outbound mailer 模块,例如 internal/outbound/
  • 实现连接目标 MX 的 :25 端口。
  • 发送 EHLO
  • 解析对方 SMTP 能力。
  • 如支持 STARTTLS,执行 STARTTLS。
  • TLS 成功后重新 EHLO
  • 发送 MAIL FROM
  • 发送 RCPT TO
  • 发送 DATA
  • 发送邮件原始内容。
  • 发送 QUIT
  • 区分临时失败和永久失败。

SMTP 客户端提交集成

  • 修改 internal/smtp_server/server.go
  • 对认证用户提交的外部收件人,不再直接拒绝。
  • 本地收件人仍走本地 INBOX 投递。
  • 外部收件人调用 outbound mailer。
  • 外部投递成功后保存 Sent 副本。
  • 外部投递失败时向 SMTP 客户端返回明确错误。

Web 发信集成

  • 修改 internal/web/handlers/mail.go
  • Web 发信支持外部收件人。
  • 本地收件人走本地投递。
  • 外部收件人调用 outbound mailer。
  • 外部投递失败时页面显示错误。
  • 成功后保存 Sent 副本。

日志与错误

  • 记录每次外部投递的目标域名、MX、收件人、结果。
  • 记录 SMTP 响应码和响应文本。
  • 临时失败返回可识别错误。
  • 永久失败返回可识别错误。

验证

  • 使用 Outlook 向外部地址发送测试邮件。
  • 使用 Web 发信向外部地址发送测试邮件。
  • 测试 MX 查询失败。
  • 测试目标邮箱不存在。
  • 测试对方服务器临时拒收。
  • 测试未认证用户不能外部投递。

阶段 2:生产级出站队列

出站队列表

  • 新增 outbound queue 数据表。
  • 保存发件人、收件人、RawData、状态、重试次数、下一次重试时间。
  • 保存最后一次 SMTP 响应。
  • 保存创建时间、更新时间、完成时间。

后台投递 worker

  • 实现后台 worker 扫描待投递队列。
  • 实现指数退避重试。
  • 临时失败进入重试。
  • 永久失败进入失败状态。
  • 超过最大重试周期后生成失败状态。

退信

  • 为永久失败生成退信邮件。
  • 为超过重试周期的临时失败生成退信邮件。
  • 将退信投递到发件人 INBOX。
  • 退信中包含原始错误和目标收件人。

DKIM 签名

  • 使用域名 DKIM 私钥为外发邮件签名。
  • 添加 DKIM-Signature 头。
  • 支持当前域名的 selector。
  • 验证 DNS 中 DKIM TXT 记录匹配。

发送限制与防滥用

  • 每用户每分钟发送限制。
  • 每用户每日发送限制。
  • 单封最大收件人数限制。
  • 单封最大大小限制。
  • 记录异常发送行为。
  • 管理员可禁用用户外发能力。

管理后台

  • 增加外发队列页面。
  • 显示投递状态。
  • 显示失败原因。
  • 支持手动重试。
  • 支持取消队列任务。

DNS 与服务器配置检查

外部投递不仅需要代码,还需要正确 DNS 和服务器信誉配置。

  • SPF 记录,例如:v=spf1 mx ip4:服务器IP -all
  • DKIM 记录,例如:default._domainkey.example.com TXT ...
  • DMARC 记录,例如:v=DMARC1; p=quarantine; rua=mailto:dmarc@example.com
  • PTR / rDNS 反向解析指向邮件主机名。
  • 邮件主机名 A/AAAA 记录指向服务器。
  • 服务器 25 端口出站未被云厂商封锁。
  • 主机名、HELO/EHLO 名称、证书域名尽量一致。

当前边界

  • 当前已实现 Outlook 本地收发和同步兼容性修复。
  • 当前外部地址会被明确拒绝,避免开放中继。
  • 外部投递应优先实现阶段 1,再考虑阶段 2。