Bootstrap

【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接收测试邮件的地址[email protected]如果未设置,则不会发送测试邮件
MYORIGIN发件人的域名domain.com对于像 AWS SES 这样的服务,需要设置该域名
FROMADDRESS更改发件人地址[email protected]对于某些 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
      - [email protected]
      - 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
;