用 NGINX 构建高效 SMTP 代理`ngx_mail_smtp_module`

一、模块定位与作用

  1. 协议代理

    • NGINX 监听指定端口(如 25、587、465 等),接收客户端的 SMTP 会话请求。
    • 代理层在会话中透明转发客户端的 EHLO、MAIL FROM、RCPT TO、DATA 等命令到后端 MTA。
  2. 认证控制

    • 通过 smtp_auth 指令指定允许的 SASL 认证方式(例如 PLAINLOGINCRAM-MD5EXTERNAL);
    • 也可设置为 none,直接跳过代理层认证,让后端 MTA 完成验证。
  3. 功能协商(EHLO 扩展)

    • 使用 smtp_capabilities 定义代理层响应给客户端的 SMTP 扩展列表;
    • NGINX 会自动在扩展列表中添加 AUTH=...STARTTLS(若启用)等,使客户端知道可用功能。
  4. 性能与安全优化

    • smtp_client_buffer 控制读取客户端命令的缓冲区大小,避免大命令时分段造成性能抖动;
    • smtp_greeting_delay 可在发送 220 欢迎消息前加入延迟,用于拦截不等待问候就发送命令的恶意客户端。

二、核心指令详解

2.1 smtp_auth

smtp_auth method ...;

默认值smtp_auth plain login;
上下文mail, server

功能
  • 指定允许客户端使用的 SASL 身份验证机制。
  • 如果未包含 plainlogin,则 AUTH PLAINAUTH LOGIN 仍可执行,但不会自动列入 EHLO 扩展列表。
支持的认证方式
方法说明
plainAUTH PLAIN:客户端以 Base64 传输 \0username\0password。需 TLS 加密,否则存在明文风险。
loginAUTH LOGIN:分两步传输用户名与密码,也需 TLS 加密才能保证安全。
cram-md5AUTH CRAM-MD5:质询-响应式验证,后端需保存明文或可生成 MD5 摘要。更安全,但需后端支持。
externalAUTH EXTERNAL (1.11.6+):使用客户端 TLS 证书进行外部认证。
none禁用代理层认证,让后端 MTA 或其他机制处理身份验证;客户可直接继续后续 SMTP 对话。
示例
mail {
    server {
        listen      587;            # SMTP Submission (STARTTLS)
        protocol    smtp;

        # 只允许 AUTH PLAIN、AUTH LOGIN 和 AUTH CRAM-MD5
        smtp_auth   plain login cram-md5;

        # 后端 MTA 地址
        proxy_pass  smtp_backend:25;
    }
}
  • 客户端在执行 EHLO example.com 后会看到 250-AUTH PLAIN LOGIN CRAM-MD5
  • 如果用户发送 AUTH CRAM-MD5 <response>,NGINX 会将验证请求转发到后端 MTA。

2.2 smtp_capabilities

smtp_capabilities extension ...;

默认值:无(若不配置,则仅显示后端默认 EHLO 响应,并附加 AUTHSTARTTLS
上下文mail, server

功能
  • 定义 NGINX 代理在客户端 EHLO 响应中通告的 SMTP 扩展列表;
  • NGINX 会自动添加当前 smtp_auth 中定义的 AUTH=...,以及 STARTTLS(如果启用 starttls);
  • 建议将后端 MTA 实际支持的扩展在此列出,例如 SIZEPIPELINING8BITMIMEENHANCEDSTATUSCODES 等。
常见扩展选项
扩展含义
SIZE通告客户端最大邮件大小(如 SIZE 10485760 表示最大 10MB)。
PIPELINING支持 SMTP 管道式命令,允许一次发送多个命令减少网络往返。
8BITMIME支持 8 位传输编码(允许直接发送 8 位字符)。
ENHANCEDSTATUSCODES支持扩展状态码格式 250 2.1.0 OK,提高错误信息的可读性。
DSN支持传输状态通知(Delivery Status Notification)。
STARTTLS支持在 587/25 端口上升级到 TLS 加密通道;若启用 starttls on;,NGINX 会自动添加此扩展。
AUTH=PLAIN LOGIN CRAM-MD5smtp_auth 自动补充,通知客户端可用的 SASL 机制。
示例
mail {
    server {
        listen      587;             # SMTP Submission
        protocol    smtp;

        # 启用 STARTTLS
        starttls    on;

        # 允许 PLAIN、LOGIN、CRAM-MD5 三种认证方式
        smtp_auth     plain login cram-md5;

        # 明确通告客户端支持的扩展;NGINX 会自动附加 AUTH 和 STARTTLS
        smtp_capabilities SIZE PIPELINING 8BITMIME ENHANCEDSTATUSCODES DSN;

        proxy_pass    smtp_backend:25;
    }
}
  • 客户端 EHLO mail.example.com 时,NGINX 返回:

    250-mail.example.com Hello [192.0.2.1]
    250-SIZE
    250-PIPELINING
    250-8BITMIME
    250-ENHANCEDSTATUSCODES
    250-DSN
    250-AUTH PLAIN LOGIN CRAM-MD5
    250-STARTTLS
    250 HELP
    

2.3 smtp_client_buffer

smtp_client_buffer size;

默认值:与系统内存页大小相当(4K 或 8K)
上下文mail, server

功能
  • 指定 NGINX 在读取客户端发送的 SMTP 命令时所使用的缓冲区大小
  • 当客户端发出长字符串、Base64 编码凭证或大型邮件头时,可能超过默认缓冲区,需要适当放大;
  • 过小会导致分段读取、性能抖动;过大则占用更多内存。
示例
mail {
    server {
        listen              25;      # 普通 SMTP
        protocol            smtp;

        # 将缓冲区增大到 16K,以一次性消费大型输入
        smtp_client_buffer  16k;

        smtp_auth           plain login;
        smtp_capabilities   SIZE PIPELINING 8BITMIME;

        proxy_pass          smtp_backend:25;
    }
}
  • 当客户端发送长 AUTH PLAIN <base64-credentials> 时,16K 缓冲可一次读取完整凭证,避免折行或多次读取增加延迟。

2.4 smtp_greeting_delay

smtp_greeting_delay time;

默认值0(无延迟)
上下文mail, server

功能
  • 在向客户端发送 SMTP 220 欢迎消息之前延迟指定时间;
  • 主要用于防御“恶意客户端”或“探测脚本”——它们可能不会等待 220,再立刻发送 EHLO 或其他命令;
  • 若客户端在延迟期内提前发送命令,NGINX 可以直接拒绝或丢弃连接,节省后端资源。
示例
mail {
    server {
        listen               25;
        protocol             smtp;

        # 在发送 220 欢迎前延迟 3 秒
        smtp_greeting_delay  3s;

        smtp_auth            plain;
        smtp_capabilities    SIZE PIPELINING;

        proxy_pass           smtp_backend:25;
    }
}
  • 攻击脚本若不等待延迟直接发命令,将收到连接重置或拒绝;
  • 合法客户端会在 3 秒后收到标准 220 欢迎并继续会话。

三、综合示例:部署 SMTP 代理(Submission 与 SMTPS)

下面给出一个完整的生产环境示例,包含 Submission(587)和 SMTPS(465)两种监听方式,以及启动 STARTTLS、SASL 认证、协议扩展等配置。

worker_processes auto;
events { worker_connections 2048; }

mail {
    # 全局缓冲区,可根据实际负载调整
    smtp_client_buffer 8k;

    # 587 端口:支持 STARTTLS
    server {
        listen               587;            # Submission
        protocol             smtp;
        starttls             on;             # 启用 STARTTLS

        # 在发送 220 欢迎前延迟 2 秒,拦截恶意探测
        smtp_greeting_delay  2s;

        # 允许明文登录(PLAIN/LOGIN)及质询式 CRAM-MD5
        smtp_auth            plain login cram-md5;

        # 声明支持的扩展;NGINX 会自动附加 AUTH=PLAIN LOGIN CRAM-MD5 和 STARTTLS
        smtp_capabilities    SIZE PIPELINING 8BITMIME ENHANCEDSTATUSCODES DSN;

        # 转发到后端 MTA
        proxy_pass           smtp_backend:25;
        proxy_timeout        2m;
        proxy_pass_error_message on;
    }

    # 465 端口:直接 SMTPS(TLS 加密)
    server {
        listen               465 ssl;         # SMTPS
        protocol             smtp;

        # 无需 STARTTLS,因为连接一开始即为 TLS
        smtp_auth            plain login cram-md5;

        smtp_capabilities    SIZE PIPELINING 8BITMIME ENHANCEDSTATUSCODES DSN;

        # TLS 配置
        ssl_certificate      /etc/nginx/ssl/smtp.crt;
        ssl_certificate_key  /etc/nginx/ssl/smtp.key;
        ssl_protocols        TLSv1.2 TLSv1.3;
        ssl_ciphers          HIGH:!aNULL:!MD5;
        ssl_session_cache    shared:mail_ssl:10m;
        ssl_session_timeout  10m;

        proxy_pass           smtp_backend:25;
        proxy_timeout        2m;
        proxy_pass_error_message on;
    }
}

核心解析

  1. Submission(587)与 SMTPS(465)并行

    • Submission:587 端口支持明文连接后升级到 TLS(starttls on;);
    • SMTPS:465 端口直接强制 TLS 加密,无需 STARTTLS
  2. 延迟问候

    • smtp_greeting_delay 2s;:在发送 220 example.com ESMTP 前等待 2 秒,以拦截不等待问候的恶意连接。
  3. 认证方式

    • smtp_auth plain login cram-md5;:允许 AUTH PLAINAUTH LOGINAUTH CRAM-MD5,保证兼容大多数客户端;
    • 若后端 MTA 支持客户端证书验证,可改用 smtp_auth external;
  4. 通告扩展

    • smtp_capabilities size pipelining 8bitmime enhancedstatuscodes dsn;:明确告诉客户端后端支持的功能;
    • NGINX 会自动在 EHLO 响应中加入 AUTH=PLAIN LOGIN CRAM-MD5 以及 STARTTLS
  5. 缓冲区与超时

    • smtp_client_buffer 8k;:保证一次性读取大部分 SMTP 命令,避免多次系统调用;
    • proxy_timeout 2m;:如果后端在 2 分钟内未响应,则关闭连接。
  6. 错误透传

    • proxy_pass_error_message on;:当后端返回错误(如认证失败、邮箱满等),直接将错误信息转发给客户端,方便客户端做相应处理。

四、最佳实践与注意事项

  1. TLS 与明文认证绝配

    • 当启用 plain/login,务必使用 TLS(Submission 或 SMTPS),否则用户名和密码会以 Base64 明文形式在网络中传输。
    • 可结合 ssl_verify_client on; 强制客户端出示证书并使用 smtp_auth external; 进行双向认证。
  2. 通告与后端保持一致

    • smtp_capabilities 中仅列出后端 MTA 真实支持的扩展;
    • 如果后端不支持 DSNENHANCEDSTATUSCODES,切勿在代理层通告,以免客户端发送不受支持的命令。
  3. 缓冲区大小调整

    • 默认 4k/8k 缓冲适配大多数场景;
    • 若观察到大型 AUTH PLAIN 字符串或大邮件标头导致分段读取,可将 smtp_client_buffer 调至 16k 或更高;
    • 注意内存占用与并发量的平衡。
  4. 延迟问候谨慎使用

    • smtp_greeting_delay 可拦截那些“不等待 220 就发命令”的垃圾邮件机器或探测脚本;
    • 但若延迟过长会让合法客户端感觉连通性不佳,一般设为 1s~3s 即可。
  5. 日志与调试

    • 通过 mail_log 记录 SMTP 会话关键日志(认证成功/失败、邮件投递状态等);
    • 通过 error_log 捕获代理层错误与连接异常。
    • 在调试阶段,可将 proxy_pass_error_message on; 打开,观察后端具体的错误响应。

五、总结

ngx_mail_smtp_module 为 NGINX 增加了强大的 SMTP 代理能力,让你能够:

  • 灵活控制 SASL 认证方式(PLAIN、LOGIN、CRAM-MD5、EXTERNAL 或跳过),
  • 自定义 EHLO 扩展通告,与后端 MTA 完美协同,
  • 优化客户端缓冲与超时,提升并发与稳定性,
  • 通过问候延迟拦截恶意探测,减少垃圾邮件源头连接。

结合 NGINX 在并发、TLS 加速与日志收集方面的优势,你可以轻松搭建安全、高效且易于扩展的 SMTP 代理网关,为企业级邮件系统保驾护航。希望本文的指令详解与配置示例,能够帮助你快速上线 SMTP 代理,并在生产环境中获得稳定可靠的运行效果。祝你部署顺利!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Hello.Reader

请我喝杯咖啡吧😊

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值