如何用 nginx 做 postfix 的 SMTP 反向代理,以及 XCLIENT 的支持

作为 lighttpd 的竞争者,轻量级web服务器 nginx 最近才开始崭露头角,知道它还可以用来做 pop3/imap4 反向代理的估计就比较少了,至于用 nginx 做 smtp 的反向代理,估计全中国现在和我一样想到这个需求的人一只手就能数过来。

需要 smtp 反向代理是因为我们的 vip 邮箱是可以免费试用的,希望在策略上对已交费用户和免费试用用户(其中有相当部分是 spammer)做出不同处理。前面用 nginx 把不同的用户请求代理到后台不同的 postfix 上,然后 postfix 再各自配置不同的 Milter Server 做过滤。另外需要前台能支持 XCLIENT,这样 postfix/milter 可以得到客户端的 IP,对于 anti-spam 来说是很有意义的。

nginx 在大约一年前增加了对 XCLIENT 的支持,对于 webmail 服务来说,nginx 可以说是再完美不过的反向代理前台了。

nginx 配置 smtp 反向代理需要在配置文件里加这么一段:

mail {
    auth_http http://127.0.0.1/auth;
    server {
        listen 26;
        protocol smtp;
        proxy on;
        smtp_auth login plain;
    }
}

用 python 写一个简单的 auth 服务来做测试:

  1. import SimpleHTTPServer
  2.  
  3. class handler(SimpleHTTPServer.SimpleHTTPRequestHandler):
  4.     def do_GET(self):
  5.         if (self.path == '/auth'):
  6.             # verify 'Auth-User', 'Auth-Pass', 'Client-IP'
  7.             if self.headers.get('Auth-Protocol') == 'smtp':
  8.                 self.send_response(200)
  9.                 self.send_header("Auth-Status", "OK");
  10.                 self.send_header("Auth-Server", "127.0.0.1");
  11.                 self.send_header("Auth-Port", "25");
  12.                 self.end_headers()
  13.                 return
  14.  
  15. addr = ('', 80)
  16. httpd = SimpleHTTPServer.BaseHTTPServer.HTTPServer(addr, handler)
  17. httpd.serve_forever()

修改 postfix 的 main.cf 配置,允许 nginx 代理服务器发送 XCLIENT 命令。

smtpd_authorized_xclient_hosts = 127.0.0.0/8

理论来说,到这里就应该就都配置好了。但是 nginx 会带上一个 LOGIN=foobar 的属性发给后台,而 postfix 是不支持该属性的,这将导致 postfix 报告一个 Bad XCLIENT attribute name: LOGIN 的 501 错误。很疑惑当初贡献这段代码的人是用什么 smtp server 做后台的,总之和 postfix 配合的话,必须修改 nginx 的程序(比修改 postfix 要简单些): 找到 src/mail/ngx_mail_proxy_module.c 里 "case ngx_smtp_helo:" 的那一部分,把和 "LOGIN" 相关的代码去掉就好了

附:最后决定利用周末时间写一个 patch,希望会被 nginx 接纳.
nginx-0.5.35-xclient.patch

评论

Good,
I can't print in chinese.
Sorry.

this is good to against GFW

太好了,我总算找到有做nginx方面的工作了,你有用nginx做pop的代理么,能说明一下你是怎么实现的么?谢谢啊

您好:
我现在在弄NGINX的SMTP及POP的代理,和POSTFIX连用。。
但是网上没有详细的配置教程,不知道您是否配置过,

请指导。谢谢。

Nginx wiki 上应该有配置的例子

nginx做imap pop比较好做。我还是对smtp很迷糊。我就是不知道如何断定用户在那个服务器。比如我有A/B两台服务器分别放上test1=A与test2=B两个用户。前端放一个nginx当有验证的时候怎么知道test1她在什么服务器上。这个我一直没有弄明白。

nginx 不是作 MX 反向代理的,而是 SMTP 反向代理; 服务的是桌面客户端,而不是发信服务器。

求您的联系方式。有nginx代理邮件服务的问题请假,非常感谢

微博上 @邱英波

如果使用第3方的邮件系统发送邮件,发送的邮件量比较大。那么如何使用nginx呢?

如果使用第3方的邮件系统发送邮件,发送的邮件量比较大。那么如何使用nginx呢?

这种情况下 nginx 不适用

终于可确认 nginx 的原版代码是配合一个修改过的 postfix 工作的(for 2.3.7)。见原作者 Anton Yuzhaninov 的俄文页面http://www.citrin.ru/nginx:xclient-login-patch.

另外俺的 patch 被另外一个 mail_module 的维护者 Maxim Dounin 打击了一下... 下个周末有时间再提交新的 patch 吧..

改那么10来行,达到了目的。其实挺不错。这样nginx就不需要怎么动了。

n久前就想整下nginx的mail代理,居然最近才有空。感谢yingbo兄的好文

越来越Joel on Software 风格啦。

好东东,这个文档写的太好了,我找了好久相关的东西都没有相关的文档,在这里总算找到了,你有用nginx做过pop的proxy么,我现在有个项目需要用到,你是否有这个方面的操作指导,谢谢了

很简单,看 nginx 的文档

nginx 的 pop 反向代理还是比较好配置的

关键是自己要设定一个 HTTP 的 AUTH 服务

你做的这个http auth是用啥做的了,我昨天用php实现了一个,但是希望用perl完成,现在还是没有搞定,苦闷……

用perl也是实现的说,…………

php和perl差异蛮大的哦

有写出perl脚本的smtp auth服务吗?

smtp的auth脚本果然比pop3麻烦啊

我做这个nginx已经好几天了,现在正在测试你的smtp,我有点疑问,希望请教一下,所谓的xclient在smtp里是啥东西,一般的smtp服务器怎么实现的xclient的支持了?是不是所有的smtp服务器都可以实现?

谢谢。

xclient 是 postfix 的扩展

nginx 的 smtp 反向代理就是配合一个 postfix 做的... 不过这个 postfix 是他们自己打的 patch

总算看见大侠的身影了,我用nginx做了smtp的proxy,但是在conf文件中将xclient给off了,这样一来,又有问题,我需要两次验证,每次都成功了的哦,这样就导致我用mail客户端不能验证发邮件;痛苦……

220 rhas4-i86 ESMTP ready
helo winking
250 rhas4-i86
auth login
334 VXNlcm5hbWU6
xxxxxx
334 UGFzc3dvcmQ6
xxxxxxP
235 2.0.0 OK
mail from:winking
530 Authentication required
auth login
334 VXNlcm5hbWU6
xxxxxx
334 UGFzc3dvcmQ6
xxxxxxP
235 Authentication successful
mail from:y
250 y... Sender OK
rcpt to:system@hw.com
250 system@xx.com... Recipient OK
data
354 Enter message, end with "." on a line by itself
fjeifjeifjeif
.

250 Message accepted for delivery

这该怎么办好啊,大侠可有试过这种方式成功的?

后台 real smtp server 设置成从 nginx 反向代理连接过来的 IP 不需要认证即可发信

我试试看,我直接将后台的smtp服务器搞成不用验证试试

兄弟的方案是正解,我已经测试通过了,为啥nginx自身会有个验证了?其实他自身的验证也是代码来实现,能否将这个给关闭了,而自用后台smtp服务器哦验证了?另外一个问题,怎么实现特定ip来的smtp请求不需要验证了?不是所有的smtp服务器都能支持吧,呵呵

它这个验证的主要目的是完成针对特定用户转发到不同服务器上....我是这样以为的

如果从其他邮件服务器来的邮件,并没有auth,nginx能代理吗?

看你这个方案,似乎只有客户端鉴权用户才能经过这个代理啊!

从其他邮件服务器来的邮件,需要这么一层代理吗?

nginx 的反向代理,说白了是一种负载均衡方式,从 pop3/imap4 可能看得更清楚.

smtp 反向代理到特定的服务器还可以实现一些其他的应用。比如类似 gmail 那样,把 smtp 客户端外发邮件保存一份在用户的"Sent"

从外部邮件过来的,代理到内部不同的主机,可以做简单的负载均衡处理!同时如果增加一些审计功能,还可以防止DDOS连接。稍微修改,可以作为反垃圾网关和负载均衡器。
目前sohu等,在外面是不是均配置了四层交换的功能?
想利用nginx框架,来改造MTA,不知道有没有跟我一样有这个想法?

postfix做MTA,使用传统的单连接单进程的模式,对于恶意攻击和DDOS,只能保护系统资源不崩溃,但很多正常连接也进不来。

nginx 并不具备这样的功能

按你说的情况,我觉得用 haproxy 可能更合适..

haproxy 的开发者好像就用它来做 MX 的前端代理,在今年6月20号,他还很得意的

Two lines... That's all what is needed with the new TCP content inspection system to stop half of the spams I got home. One of my major customers who uses HAProxy a lot has sponsored the development of some preliminary content inspection which is used to decide whether to forward a connection or not. The very first usage of this feature consists in checking that only SSL is spoken on a connection. But most likely more protocols will come soon. As a nice side effect, I could now add a delay before the HELO message of my SMTP server, and reject all robots which talk first (forbidden). And since many spam bots have small timeout values, many of them abort before the timeout is reached, resulting in my incoming spam rate dropping from about 300/hour to "only" 150/hour. Those who keep up with the time out slow down due to limited resources. The small addition simply consists in adding those two lines in the frontend :

      		tcp-request inspect-delay  35s
      		tcp-request content reject if REQ_CONTENT

我觉得可以按照这个思路,给 haproxy 做做 patch 或者写个插件(不知道它是否有插件框架)

我现在想做一个SMTP代理,需要发送邮件的服务器在内网不能访问公网的邮件服务器。

但我写了一下,邮件服务器是exchange,总是报501

mail {
auth_http mail.xxx.com;
server {
listen 25;
server_name mail.xxx.com;
protocol smtp;Anonymous
proxy on;
}

}

不知道那里可以修正,另外有没有其他的代替品让我实现内网通过代理发送邮件功能呢?

另外我的SMTP使用的匿名身份验证,一定要写那个auth_http吗?

希望能得到帮助

如果只是要做一个 relay,安装一个标准的 MTA 就好了

smtp的资料太少了 特来想请教
我想做smtp的反向代理

比如:smtp.163.com 内网是联不上 需要一台对外的主机(192.168.1.2)联

对外那台主机有没有办法用nginx做:

在内网机器邮件客户端smtp设成192.168.1.2来发信

感谢

nginx 不是作这个用的。你看看其它的 smtp proxy 吧

这个只能验证SMTP帐号的有效性,如果像本文讲的将 xclient 中的"LOGIN"参数去掉,那后面怎样检测伪造邮件?

我想用nginx做smtp反向代理做集群,根据邮件的某个规则,比如某个字母开头的连到某个特定的postfix去,这个可以实现吗?可以的话具体怎么实现,麻烦能给一些资料,貌似nginx对smtp的支持不能像http那样根据sessionid,或者某个特定的cookie来实现转发到制定服务器

你是要建立 MX 集群的话,就别考虑 nginx 了,直接用 postfix 就搞定了

你好,我试了下nginx反向代理imap,发现 输入
A01 LOGIN abc 123
会返回
a01 NO Parse command error
9} BAD command not support

同样的命令在163的imap服务器上是没有问题的,不知道为什么

你好,我试了下nginx反向代理imap,发现 输入
A01 LOGIN abc 123
会返回
a01 NO Parse command error
9} BAD command not support

同样的命令在163的imap服务器上是没有问题的,不知道为什么