Skip to content
itsharexPublic
forked from YanGLweI/MailProxy

About

Go 实现的 SMTP 邮件代理网关。内部业务程序不直接对接真实邮箱 SMTP 服务器,统一连接本代理;代理接收业务侧 SMTP 请求后,转发到后端真实 SMTP 服务器完成发送,支持多套邮箱账号配置与路由策略。

Resources

Stars

0 stars

Watchers

0 watching

Forks

 
 

Latest commit

 

History

18 Commits

Folders and files

Repository files navigation

MailProxy

Go 实现的 SMTP 邮件代理网关。统一对接后端邮箱服务商,业务侧只需连接代理服务器即可发信。

Release License Go Version RPM

解决什么问题

✓ 统一接入点 - 所有业务程序对接同一台 SMTP 服务器,无需感知后端邮箱服务商差异
✓ 多账号路由 - 根据发件人自动选择最优后端账号,提升送达率
✓ 零代码改造 - 只需修改 SMTP 地址配置,现有发邮件逻辑完全不用变
✓ 安全合规 - IP 白名单 + 可选认证,防止被滥用为开放中继
✓ 企业邮箱兼容 - 自动处理「信封发件人必须等于认证账号」限制(阿里/腾讯/网易等)


┌─────────────┐                    ┌──────────────────────────────────────┐
│ 业务程序     │                    │         MailProxy 代理               │
│ (Email App) │                    └──────────────────────────────────────┘
└──────┬──────┘                              ▲           │
       │                                      │           │
       │ SMTP over SSL (465)                 │           │
       │ EHLO → AUTH → MAIL FROM → DATA      │           │
       ├─────────────────────────────────────┤           │
       │                                     │           │
       │        ┌────────────────────────┐   │           │
       └───────►│  路由引擎              │   │           │
                │  • 信封发件人匹配      │◄──┤  配置    │
                │  • 认证账号映射        │   │  (YAML)  │
                │  • IP 白名单校验       │   │          │
                └──────────┬─────────────┘   │          │
                           │                  │          │
                           ▼                  │          │
                    ┌─────────────────────────┴───┐      │
                    │  后端转发队列               │      │
                    └─────────────────────────────┘      │
                           │                              │
                           │ SMTP/SMTPS (465/587)         │
                           │ AUTH PLAIN/LOGIN             │
                           ▼                              ▼
              ┌──────────────────────────┐    ┌──────────────────────────┐
              │ 阿里云企业邮箱            │    │ 其他 SMTP 服务端           │
              │ smtp.qiye.aliyun.com     │    │ QQ/网易/Gmail/自定义     │
              └──────────────────────────┘    └──────────────────────────┘

图:MailProxy 作为中央枢纽,统一管理多个后端 SMTP 服务



功能特性

  • 🚀 高性能 - 基于 Go 并发模型,支持高并发连接
  • 🔒 安全认证 - IP 白名单 + 可选 AUTH 认证,防止被滥用为开放中继
  • 🔄 智能路由 - 根据发件人自动选择最优后端账号,提升送达率
  • 🎯 零代码改造 - 只需修改 SMTP 地址配置,现有发邮件逻辑完全不用变
  • 🔐 TLS 加密 - 支持 SMTP over SSL (465) 和 STARTTLS (587),确保传输安全
  • ♻️ 热重载 - 配置文件 SIGHUP 热加载,无需重启服务
  • 📊 监控指标 - Prometheus 指标导出,实时监控系统状态
  • 📝 详细日志 - 结构化日志记录,每次发信全流程追踪
  • 🎁 企业邮箱兼容 - 自动处理「信封发件人必须等于认证账号」限制(阿里/腾讯/网易等)
  • 🏗️ 易部署 - 单二进制文件 + systemd 托管,提供 RPM 包一键安装


快速开始

# 1. 下载最新版本 (Linux AMD64)
curl -LO https://github.com/YanGLweI/MailProxy/releases/latest/download/mailproxy-1.0.4-linux-amd64.tar.gz
tar -xzf mailproxy-*.tar.gz
chmod +x mailproxy

# 或使用 RPM 包 (RHEL/CentOS/Rocky)
curl -LO https://github.com/YanGLweI/MailProxy/releases/latest/download/mailproxy-1.0.4-1.el9.x86_64.rpm
sudo rpm -ivh mailproxy-1.0.4-1.el9.x86_64.rpm

立即体验:

# 生成自签名证书
./deploy/gen-cert.sh

# 准备配置
cp config.example.yaml config.yaml
vim config.yaml             # 填写后端邮箱账号等

# 启动服务
./mailproxy -config config.yaml

业务接入只需改 3 个参数:

业务接入只需改 3 个参数:

参数 修改前 修改后
SMTP 主机 smtp.qiye.aliyun.com mailproxy.internal:465
SSL 证书 逐个管理各服务商证书 只需信任 mailproxy 一个证书
开发成本 不同服务商 API 差异适配 统一 SMTP 协议,一次开发

构建源码

如需从源码构建:

# 本地构建
go build -o mailproxy .

# 交叉编译 Linux AMD64
GOOS=linux GOARCH=amd64 go build -o mailproxy .

# 运行测试
go test ./...

连通性自测

推荐先验证连接再发送测试邮件:

# 验证 465 TLS 握手与 SMTP 交互(自签证书加 -startls 无关,直接用 s_client)
openssl s_client -connect 127.0.0.1:465 -quiet

# 验证 587 STARTTLS 监听(启用 starttls_listen 后)
openssl s_client -starttls smtp -connect 127.0.0.1:587 -quiet

# 经代理发送一封测试邮件
go run ./testtools/sendmail \
  -addr 127.0.0.1:10465 \
  -from [email protected] -to [email protected]

预期输出示例:

$ go run ./testtools/sendmail ...
✅ 连接成功:127.0.0.1:10465
✅ AUTH LOGIN 认证成功
✅ MAIL FROM:<[email protected]>
✅ RCPT TO:<[email protected]>
✅ DATA 已发送 (Message-ID: <abc123@mailproxy>)
✅ 状态:250 OK

启动参数:

参数 说明
-config 配置文件路径,默认 config.yaml
-log 日志文件路径,覆盖配置中的 log.file

配置字段含义见 config.example.yaml 中的逐项注释。

配置热加载

kill -HUP $(pidof mailproxy)
# 或 systemd: systemctl reload mailproxy

后端账号、代理账号、路由规则、白名单、日志等变更即时生效;server.listen、server.starttls_listen 与 TLS 证书变更需要重启。热加载校验失败时沿用旧配置并记录错误日志。

systemd 部署

install -m 755 mailproxy /usr/local/bin/mailproxy
install -d /etc/mailproxy
install -m 600 config.yaml /etc/mailproxy/config.yaml
install -m 644 deploy/mailproxy.service /etc/systemd/system/
systemctl daemon-reload
systemctl enable --now mailproxy

deploy/mailproxy.service 已通过 AmbientCapabilities=CAP_NET_BIND_SERVICE 解决非 root 绑定 465 特权端口的问题(请确保以 root 启动 unit,capability 会下发给服务用户)。

RPM 打包部署(RHEL/CentOS/Rocky 系)

构建要求(macOS 本地)

本项目使用 Go 交叉编译 + Docker 在 macOS arm64 上构建 RHEL 系列 x86_64 的 rpm 包。构建机需满足以下依赖:

软件 用途
Go 1.25+ 交叉编译 Linux x86_64 静态二进制(CGO_ENABLED=0 GOOS=linux GOARCH=amd64)
Docker 兼容运行时 rpmbuild 容器化工具;支持 docker run --platform linux/amd64(强制 amd64 模拟)

macOS arm64 推荐方案(无原生 Docker 时):

  1. colima + lima(轻量)

    • 下载安装 colima 与 lima,启动 colima start
    • 下载 Docker 静态 CLI(aarch64)并加入 PATH,执行 docker ps 验证
    • Docker Hub 若直连超时,可通过镜像站拉取 Rocky/Alma 镜像(如 docker.m.daocloud.io)
  2. Docker Desktop for Mac(完整)

    • 直接使用 docker,确保开启 QEMU 模拟支持(可运行 amd64 容器)
  3. Podman/Lima(可选)

    • 需要额外配置 docker-兼容 CLI 与 amd64 模拟器;脚本中只需保证 docker 命令存在即可。

构建脚本会在 macOS 本体交叉编译二进制,并在 rockylinux:9 的 amd64 容器中执行 rpmbuild,产物为 dist/mailproxy-<ver>-1.el*.x86_64.rpm。

构建流程

bash deploy/build-rpm.sh        # 产物 dist/mailproxy-1.0.5-1.el9.x86_64.rpm

安装(目标服务器,root):

rpm -ivh mailproxy-1.0.3-1.el9.x86_64.rpm

安装时自动完成:

  • 创建系统用户/组 mailproxy(nologin)
  • 自动生成自签名 TLS 证书到 /etc/mailproxy/certs/,供服务快速启动;生产环境建议替换为自有 CA 或公共 CA 签发的证书,见下方「SSL 证书」
  • 安装配置到 /etc/mailproxy/config.yaml(权限 640 root:mailproxy,服务用户经组权限可读)、systemd unit 到 /usr/lib/systemd/system/
  • 设置开机自启(systemctl enable),但不启动服务

安装完成后按终端提示操作:

vim /etc/mailproxy/config.yaml      # 填写后端邮箱账号/授权码、IP 白名单等
systemctl start mailproxy           # 手动启动
systemctl status mailproxy          # 查看状态

升级与卸载:

rpm -Uvh mailproxy-<new>.rpm        # 升级;已修改的 config.yaml 不被覆盖(新版落地为 config.yaml.rpmnew),证书不重置
rpm -e mailproxy                    # 卸载:停止并禁用服务、删除 mailproxy 用户;config.yaml 按 rpm 惯例保留

打包相关文件:deploy/mailproxy.spec、deploy/build-rpm.sh、deploy/rpm/(rpm 专用配置与 unit 模板)。

SSL 证书

安装自动生成自签名证书

为开箱即用,RPM 安装时会自动生成自签名证书(手工部署则执行 deploy/gen-cert.sh),服务可立即启动。但自签名证书不在任何客户端的默认信任链中,每个接入的业务平台都要单独导入证书才能通过 TLS 校验。

生产环境建议替换为自己企业 CA 或公共 CA 签发的证书:

  • 企业 CA:把根 CA 证书一次性导入各业务平台信任库后,后续证书续期、轮换业务侧零改动
  • 公共 CA(如 Let's Encrypt、DigiCert 等):绝大多数客户端默认信任,业务平台通常无需任何配置

替换步骤

  1. 用 CA 签发服务器证书,SAN 必须包含业务程序实际连接所用的主机名(与 server.hostname 一致),否则客户端主机名校验会失败

  2. 若由中间 CA 签发,把中间证书拼接到服务器证书后面(叶子证书在前)再写入 server.crt,MailProxy 会随握手下发完整证书链:

    cat your_server.crt intermediate.crt > server.crt
  3. 覆盖证书文件并设置权限(服务以非 root 用户运行,须保证服务用户可读):

    # RPM 安装环境
    cp server.crt /etc/mailproxy/certs/server.crt
    cp server.key /etc/mailproxy/certs/server.key
    chmod 644 /etc/mailproxy/certs/server.crt
    chmod 600 /etc/mailproxy/certs/server.key
    chown root:mailproxy /etc/mailproxy/certs/server.*
    systemctl restart mailproxy      # 证书变更需重启,kill -HUP 热加载不生效

第三方业务平台信任证书

业务平台通过 465(SSL)连接代理时会校验服务端证书。使用自签名证书时,常见报错:Java 程序报 PKIX path building failed / unable to find valid certification path,其他平台报「证书不受信任」「SSL 握手失败」等。按优先级选择处理方式:

  1. (推荐)换用业务平台已信任的 CA 签发的证书——企业内统一分发了根 CA 的用自有 CA,否则用公共 CA 证书,业务平台零配置或仅导入一次根证书

  2. 把证书导入业务平台的信任库——自签名证书导入 server.crt 本身;CA 签发证书导入其根 CA 证书(而非服务器证书,这样换证书后无需再动业务侧)。Java 平台示例(如 EventLog Analyzer 等自带 JRE 的产品,导入其 JRE 的 cacerts):

    keytool -importcert -alias mailproxy -file server.crt \
      -keystore $APP_HOME/jre/lib/security/cacerts -storepass changeit
  3. 关闭业务平台的证书校验——仅作为临时手段,存在中间人风险,生产不建议

注意:证书更新/轮换后,凡是按方式 2 单独导入过证书的平台都需要重新导入,这也是建议改用 CA 证书的主要原因。

业务程序接入

只需把业务程序的 SMTP 服务器改为本代理:

  • SMTP 地址:代理服务器 IP
  • 端口:465(SSL)
  • 认证:auth.mode: auth 时填配置中的代理账号/密码;none 时任意填写或留空(代理接受并忽略任意 AUTH)
  • SSL 证书:自签名证书需业务平台单独导入信任;建议直接使用自有 CA/公共 CA 证书,详见「SSL 证书」章节

仅支持 STARTTLS 的客户端(如 Veeam Backup & Replication:官方不支持 465 隐式 TLS,勾选 "Connect using SSL" 仍走 STARTTLS 握手,直连 465 会超时失败):启用 server.starttls_listen: ":587" 并重启后,按如下配置接入:

  • 端口:587,客户端勾选 SSL/STARTTLS 选项(Veeam 即 "Connect using SSL")
  • 防火墙放行 587
  • 465 隐式 TLS 与 587 STARTTLS 双监听共存,其他业务程序零影响

设计约束(基础版)

  • 不做邮件存储:只中转,转发完成即释放报文
  • 不做队列与失败重试:后端故障透传错误,由业务程序自行重试(二期规划)
  • 无 Web 管理界面:仅文件配置

二期迭代规划

邮件队列与本地暂存重试、单客户端/单账号限流、TLS 证书自动更新、HTTP 健康检查、黑名单过滤、Web 配置界面。

风险与注意事项

  1. 465 特权端口:Linux 下需 root 或 cap_net_bind_service 能力(systemd unit 已处理)
  2. SSL 证书:安装自动生成的自签名证书需各业务平台逐一导入信任,否则 SSL 握手失败;生产建议替换为自有 CA 或公共 CA 签发的证书(见「SSL 证书」)
  3. 服务商限制:第三方邮箱的发送频率/额度限制无法绕过,超限错误会透传给业务程序
  4. 密码安全:配置文件保存授权码明文(预留加密存储扩展点),务必限制文件权限(手工部署 chmod 600;rpm 安装为 640 root:mailproxy,服务用户需经组权限读取)

测试

go test ./...

包含配置加载/校验、路由解析、IP 白名单单测,以及基于内存 mock SMTP 后端的全链路集成测试(TLS 监听、免鉴权转发、强制 AUTH 忽略、账号映射、LOGIN 认证、信封发件人改写、MAIL FROM 路由拒绝、白名单拒绝、后端故障透传、STARTTLS 升级前后鉴权控制、STARTTLS 白名单拒绝、双监听共存回归、默认不启用回归)。

About

Go 实现的 SMTP 邮件代理网关。内部业务程序不直接对接真实邮箱 SMTP 服务器,统一连接本代理;代理接收业务侧 SMTP 请求后,转发到后端真实 SMTP 服务器完成发送,支持多套邮箱账号配置与路由策略。

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages