【Postfix】Docker Postfix中继服务的实践与优化
Microsoft 正在积极地将 Office 365 和 Microsoft 365 用户从传统的基础认证(Basic Authentication)迁移到更为先进的安全措施。这其中,SMTP AUTH 这种曾经广泛使用的身份验证方式,如今正逐渐走向历史舞台的边缘。针对新创建的租户,默认情况下已经关闭了 SMTP AUTH 的使用;而对于现有租户,Microsoft 正鼓励其尽快过渡到更安全的身份验证机制。
对于那些依赖 SMTP 发送通知邮件的多功能设备,如打印机或扫描仪,寻找替代方案变得尤为关键。微软推荐了几种解决方案。
其中 SMTP 中继服务因其稳定性和可靠性脱颖而出,尽管它的设置过程可能会让人感到有些棘手。然而在实践过程中,曾试图在 Windows Server 2022 上构建 SMTP 中继服务器,但遇到了一系列问题与挑战,未能解决。幸运的是,在 GitHub 上发现了一个名为 docker-postfixrelay 的项目,它提供了一种利用 Docker 部署 Postfix SMTP 中继的新途径,这或许能成为解决此难题的一把钥匙。
在尝试使用 GitHub 上的指南部署 SMTP 服务器后,我发现了一些功能上的局限性,特别是缺乏对发件人和收件人以及客户端 IP 地址的控制。为了弥补这些不足,我决定对现有的 项目进行定制化改造。
首先,我增加了对 smtpd_client_restrictions
参数的配置。这个设置允许我们精细控制哪些网络范围内的客户端能够连接到 SMTP 服务器。在配置中定义了mynetworks
后,可以限制未授权的外部访问,同时确保内部网络中的设备可以无障碍地发送邮件。
postconf -e "smtpd_client_restrictions = permit_mynetworks"
其次,为了确保只有来自特定域名的邮件能够通过我们的 SMTP 中继服务器,开发了一段 Bash 脚本。该脚本可以根据环境变量动态调整域名限制设置,并自动更新 Postfix 配置。这样的设计让我们能够根据实际需求,灵活控制哪些域名的邮件被允许通过 SMTP 中继。这不仅大大提升了系统的安全性,也确保了只有合法的邮件源能够利用中继服务。
# 检查是否启用域名限制
if [[ "$RESTRICT_DOMAIN" == "true" ]]; then
# 启用发送者和接收者的域名限制
printf "# STATE: Enabling domain restriction for sender and recipient\n"
# 对域名进行转义处理,以适应正则表达式
escaped_origin=$(echo "$MYORIGIN" | sed 's/\./\\./g')
# 构建正则表达式
regex="^(.*@$escaped_origin)$"
formatted_regex="/$regex/ OK"
# 将正则表达式写入 access_regexp 文件
echo "$formatted_regex" > /etc/postfix/access_regexp
else
# 如果未启用域名限制,则允许所有域名
printf "# STATE: Domain restriction is disabled\n"
echo "/.*/ OK" > /etc/postfix/access_regexp
fi
# 设置发送者限制,只允许符合 access_regexp 规则的邮件发送
postconf -e "smtpd_sender_restrictions = check_sender_access regexp:/etc/postfix/access_regexp, reject"
# 设置接收者限制,只允许符合 access_regexp 规则的邮件接收
postconf -e "smtpd_recipient_restrictions = check_recipient_access regexp:/etc/postfix/access_regexp, reject"
以上的改动都在项目中的entrypoint.sh中进行。
然后重构Docker镜像,就可以使用了,参数方面的话就多了一个RESTRICT_DOMAIN
docker build -t docker-postfixrelay:latest .
环境变量
变量名称 | 是否必需 | 定义 | 示例 | 备注 |
---|---|---|---|---|
TZ | 是 | 时区 | Asia/Shanghai | 参考时区列表 https://en.wikipedia.org/wiki/List_of_tz_database_time_zones |
RELAY_HOST | 是 | 使用的公共 SMTP 服务器 | smtp.163.com | |
RELAY_PORT | 是 | 公共 SMTP 服务器端口 | 587 | |
RELAY_USER | 否 | 登录到 $RELAY_HOST 的用户名 | SMTP 用户名 | |
RELAY_PASS | 否 | 登录到 $RELAY_HOST 的密码 | SMTP 密码 | 如果使用 Gmail 的两步验证,需要设置应用密码 |
TEST_EMAIL | 否 | 接收测试邮件的地址 | receive_address@domain.com | 如果未设置,则不会发送测试邮件 |
MYORIGIN | 否 | 发件人的域名 | domain.com | 对于像 AWS SES 这样的服务,需要设置该域名 |
FROMADDRESS | 否 | 更改发件人地址 | my_email@domain.com | 对于某些 SMTP 服务,需要设置 FROM 地址 |
MYNETWORKS | 否(默认:0.0.0.0/0) | Postfix 将转发邮件的网络 | 1.2.3.4/24, 5.6.7.8/24 | 单个或多个受信任网络,用逗号分隔 |
MSG_SIZE | 否(默认:10240000) | Postfix 消息大小限制(字节) | 30720000 | |
LOG_DISABLE | 否(默认:false) | 设置为 true 禁用日志 | true | |
RESTRICT_DOMAIN | 否(默认:false) | 设置为 true 限制发件人收件人域名 | true | 前提条件:设置了 MYORIGIN |
docker-compose.yml 示例
version: '3'
services:
postfixrelay:
container_name: docker-postfixrelay
restart: unless-stopped
environment:
- TZ=Asia/Shanghai
- RELAY_HOST=abc-com.mail.protection.outlook.com
- RELAY_PORT=25
- TEST_EMAIL=knightyang@abc.com
- MYORIGIN=abc.com
- MYNETWORKS=10.0.0.0/8
- MSG_SIZE=30720000
- LOG_DISABLE=false
- RESTRICT_DOMAIN=true
networks:
- postfixrelay
ports:
- '1125:25'
volumes:
- 'postfixrelay_data:/var/spool/postfix'
image: docker-postfixrelay:latest
networks:
postfixrelay:
volumes:
postfixrelay_data:
driver: local
参考
- https://learn.microsoft.com/en-us/exchange/mail-flow-best-practices/how-to-set-up-a-multifunction-device-or-application-to-send-email-using-microsoft-365-or-office-365#option-2-send-mail-directly-from-your-printer-or-application-to-microsoft-365-or-office-365-direct-send
- https://github.com/loganmarchione/docker-postfixrelay