方法一: 使用API或默认隐藏Received信息的smtp服务商
众所周知,大多数smtp发件都会在邮件header中暴露源服务器ip,比如
Received: from example.com ([123.456.78.90])
少数smtp服务商会默认隐藏源站ip,比如阿里云(此外,有一些专业的匿名发件,但我没用过,多数情况下也不需要,另外完全不带发信ip可能更易进垃圾箱)。但有时候并不方便用阿里云的邮件推送服务。好消息是,现在很多网站程序都支持使用api发信,比如现在wordpress有一些smtp插件都支持使用mailgun API发信。
使用 Mailgun API 时,不走 SMTP 协议,而是通过 HTTP API 请求,然后Mailgun 的服务器端会发送邮件。邮件头只显示 Mailgun 自己的服务器 IP,不会显示 API 请求发起方的 IP,因为 API 模式邮件发送时无需遵守 SMTP 协议标准的 Received 头要求。
但平时使用的很多程序并不支持api发信,那么只能想别的办法,即邮件中继服务器。
方法二: 在中继服务器使用端口转发
其实要实现中继功能,有个取巧的方法是直接端口转发,比如直接使用iptables转发tcp。例如,源站服务器ip为 a.a.a.a,中继服务器ip为b.b.b.b,smtp服务商为mailgun:
- 源站在hosts中将smtp.mailgun.com的ip指定为中继服务器ip
echo "b.b.b.b smtp.mailgun.com" | sudo tee -a /etc/hosts
- 中继服务器中配置端口转发
打开网络转发功能
echo 'net.ipv4.ip_forward=1' >> /etc/sysctl.conf
sysctl -p
添加iptables NAT规则,转发本地587到smtp.mailgun.com:587
iptables -t nat -A PREROUTING -p tcp --dport 587 -j DNAT --to-destination smtp.mailgun.com:587
iptables -t nat -A POSTROUTING -p tcp -d smtp.mailgun.com --dport 587 -j MASQUERADE
添加中继服务器防火墙安全规则
# 清理已有相冲突的规则,视具体情况可选
iptables -F INPUT
# 允许已经建立和相关连接数据回程
iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
# 限制只允许源站a.a.a.a发起的SMTP连接
iptables -A INPUT -p tcp -s a.a.a.a --dport 587 -j ACCEPT
# 限制新连接速率,防止扫描和重试攻击(例如每分钟最多15个新连接),测试如果没有问题可以考虑删除
iptables -A INPUT -p tcp --dport 587 --syn -m limit --limit 15/minute --limit-burst 20 -j ACCEPT
# 默认拒绝其他主机访问587端口
iptables -A INPUT -p tcp --dport 587 -j DROP
但这样可靠性还是会低一些
方法三:使用现成的开源可自托管的smtp中继项目
首先需要测试中继服务器是否能正确连接smtp发件商的相应端口
apt install -y telnet
telnet smtp.mailgun.org 587
返回类似以下内容即为正常
Trying 12.34.56.78...
Connected to smtp.mailgun.org.
Escape character is '^]'.
如果卡在trying或连接失败,则需要检查防火墙 或 更换其他服务器商家
1. 配置中继服务器
不知道是不是由于合规性问题,好像没有很简便的解决方案,gpt给我推荐了Postfix、Haraka、Postal ,我先使用最轻量的Postfix试一下。
docker-compose.yml
services:
postfix-relay:
image: boky/postfix:latest
container_name: postfix-relay
restart: unless-stopped
environment:
HOSTNAME: relay.example.com # 解析到中继服务器的域名
RELAYHOST: "[smtp.mailgun.org]:587" # 上游SMTP发件服务商
RELAYHOST_USERNAME: no-reply@mail.example.com # Mailgun SMTP用户名
RELAYHOST_PASSWORD: longpasswordlongpasswordlongpassword # Mailgun SMTP密码
USE_TLS: yes
MYNETWORKS: 987.65.43.21 # 白名单ip 即源站服务器ip
POSTFIX_smtpd_tls_security_level: encrypt
ALLOWED_SENDER_DOMAINS: www.example.net # 白名单域名
# Postfix TLS配置引用Let's Encrypt证书路径
POSTFIX_smtpd_tls_cert_file: /etc/letsencrypt/live/relay.example.com/fullchain.pem
POSTFIX_smtpd_tls_key_file: /etc/letsencrypt/live/relay.example.com/privkey.pem
POSTFIX_smtpd_tls_CAfile: /etc/letsencrypt/live/relay.example.com/chain.pem
POSTFIX_smtp_tls_security_level: encrypt
POSTFIX_smtp_tls_loglevel: "1"
POSTFIX_smtp_sasl_security_options: noanonymous
POSTFIX_smtpd_recipient_restrictions: "permit_mynetworks, reject_non_fqdn_recipient, reject_unknown_recipient_domain, check_sender_access lmdb:/etc/postfix/allowed_senders, reject"
POSTFIX_header_checks: regexp:/etc/postfix/header_checks
POSTFIX_smtp_header_checks: regexp:/etc/postfix/header_checks
POSTFIX_smtp_helo_name: mail.example.com # 使用mail.example.com作为HELO名称
POSTFIX_proxy_interfaces: 123.45.67.89 # 声明代理接口 改成中继服务ip
POSTFIX_smtpd_sasl_authenticated_header: no # 不添加认证信息到头
ports:
- 587:587
volumes:
- postfix-spool:/var/spool/postfix
- postfix-log:/var/log
- ./certbot/data:/etc/letsencrypt # 挂载证书存储文件夹到容器
- ./postfix/header_checks:/etc/postfix/header_checks
certbot:
image: certbot/dns-cloudflare
container_name: certbot
volumes:
- ./certbot/data:/etc/letsencrypt # 证书数据目录
- ./certbot/log:/var/log/letsencrypt
- ./certbot/cloudflare.ini:/cloudflare.ini:ro # Cloudflare API token
command: >
certonly --dns-cloudflare --dns-cloudflare-credentials /cloudflare.ini
--email 123@gmail.com --agree-tos --no-eff-email # 修改邮箱
--key-type rsa --rsa-key-size 2048
--dns-cloudflare-propagation-seconds 30 -d relay.example.com
volumes:
postfix-spool: null
postfix-log: null
networks: {}
以上配置借助ai生成,可能有部分配置冗余,但是整体满足需求。仅通过白名单ip认证。如果您有更完善的配置,请评论告诉我,感谢。
创建cloudflare api文件用于申请证书 vim ./certbot/cloudflare.ini
dns_cloudflare_api_token = yourapitoken # 区域dns api token,不是账户api key
dns_cloudflare_zone_id = yourzoneid # 在域名详情的右侧显示
创建删除邮件头的配置文件 vim ./postfix/header_checks
/^Received:/ IGNORE
/^X-Originating-IP:/ IGNORE
/^X-Mailer:/ IGNORE
/^User-Agent:/ IGNORE
首次运行申请证书
docker compose run --rm certbot
确认证书正确生成后,可以启动中继服务
docker compose up -d postfix-relay
后续可以添加crontab进行自动申请
0 0 * * * cd /你的docker-compose路径 && docker compose run --rm certbot renew --quiet && docker compose restart postfix-relay
脚本说明:
- 每日尝试续签一次证书。如证书即将(小于30天到期),则真正执行更新,否则什么也不做。
- 若证书续签完成,立即重启postfix-relay容器以加载新证书。
2. 测试发信(均在白名单ip服务器测试)
测试中继服务器的SMTP端口是否正确设置了证书
openssl s_client -connect relay.example.com:587 -starttls smtp
返回以下证书信息即为正常
![图片[1]-解决使用标准smtp认证方式通过MailGun等发件服务商发件时暴露源站IP的问题-THsInk](https://www.thsink.com/wp-content/uploads/2025/04/截屏2025-04-13-02.20.15-1024x781.png)
测试能否正常发信
swaks --to "example <example@gmail.com>" \
--from "example <no-reply@mail.example.com>" \
--server relay.example.com \
--port 587 \
--ehlo example.com \
--tls --tls-verify \
--timeout 30 \
--data "Subject: 测试隐藏源服务ip
这是测试邮件内容。"
查看邮件头
![图片[2]-解决使用标准smtp认证方式通过MailGun等发件服务商发件时暴露源站IP的问题-THsInk](https://www.thsink.com/wp-content/uploads/2025/04/截屏2025-04-13-02.27.54-1024x232.png)
发现源服务器ip已被隐藏,其中178.208.x.x 是我的中继服务器ip,其余ip均为mailgun ip。
使用自建邮件中继服务器的方法适合轻度使用、不想泄漏源站ip,但其实泄漏了也基本不会被攻击的情况。但要是真用到了,也只会造成发件服务离线,不会对网站本身造成影响,更换中继服务器显然成本更低、更简单。
暂无评论内容