NGINX反代IMAP配置
NGINX
编译nginx时添加–with-mail –with-mail_ssl_module 参数,以启用mail代理功能。
注意,nginx只支持非ssl的远程连接。也就是说结构如下,
imap服务 <--不支持ssl--> nginx反代服务器 <--支持ssl--> 你的邮件客户端
如果需要ssl连接服务器可以使用stunnel进行加密传输。
nginx.conf添加以下配置文件
mail { auth_http 127.0.0.1:80/auth.php; # 认证使用地址 imap_capabilities "IMAP4rev1" "UIDPLUS"; server { listen 143; # 本地监听端口 protocol imap; # 使用协议 proxy on; } }
PHP
安装php并配置好,确保127.0.0.1:80/auth.php
可以正常访问,auth.php内容如下
<?php if (!isset($_SERVER["HTTP_AUTH_USER"] ) || !isset($_SERVER["HTTP_AUTH_PASS"] )){ fail(); } $username=$_SERVER["HTTP_AUTH_USER"] ; $userpass=$_SERVER["HTTP_AUTH_PASS"] ; $protocol=$_SERVER["HTTP_AUTH_PROTOCOL"] ; // 仅能使用ip地址,可以用php代码自行获取相关服务器地址。不支持访问ssl相关的端口。 if ($protocol=="imap") { $server_ip="123.126.96.208"; //此处使用的是126邮箱的地址 $backend_port=143; } else { fail(); } if (!authuser($username,$userpass)){ fail(); exit; } pass($server_ip, $backend_port, $username, $userpass); //END function authuser($user,$pass){ return true; } function fail(){ header("Auth-Status: Invalid login or password"); exit; } function pass($server,$port,$user,$pass){ header("Auth-Status: OK"); header("Auth-Server: $server"); header("Auth-Port: $port"); header("Auth-User: $user"); header("Auth-Pass: $pass"); exit; } ?>
确认nginx和php服务运行正常,此时可以通过服务器的143端口对imap.126.com的服务器进行访问。
其他
注意:gmail的imap服务器会返回CAPABILITY相关信息,nginx无法解析该信息会报以下异常
upstream sent invalid response: "* CAPABILITY IMAP4rev1 UNSELECT IDLE NAMESPACE QUOTA ID XLIST CHILDREN X-GM-EXT-1 UIDPLUS COMPRESS=DEFLATE ENABLE MOVE CONDSTORE ESEARCH UTF8=ACCEPT LIST-EXTENDED LIST-STATUS LITERAL- SPECIAL-USE APPENDLIMIT=35651584
该异常会导致后续imap访问无法进行,需要对nginx打补丁。
补丁链接:http://nginx.org/pipermail/nginx/attachments/20071111/b395d010/attachment.obj
补丁内容:
(07年的补丁,也一直没有合并到主代码库里,大概是用的人太少了吧,理论上nginx连接的mail服务器也应该是自己的,中间不需要ssl加密增加负载,非要代理gmail这种他人服务器的需求也是比较奇葩的,所以才这样吧。)
--- src/mail/ngx_mail_proxy_module.c.orig 2007-11-11 16:06:45.000000000 +0530 +++ src/mail/ngx_mail_proxy_module.c 2007-11-11 16:19:39.000000000 +0530 @@ -627,6 +627,7 @@ ssize_t n; ngx_buf_t *b; ngx_mail_proxy_conf_t *pcf; + int expect_chunk; s->connection->log->action = "reading response from upstream"; @@ -661,6 +662,7 @@ return NGX_AGAIN; } + expect_chunk = 0; p = b->pos; switch (s->protocol) { @@ -688,10 +690,35 @@ break; case ngx_imap_passwd: - if (ngx_strncmp(p, s->tag.data, s->tag.len) == 0) { - p += s->tag.len; - if (p[0] == 'O' && p[1] == 'K') { - return NGX_OK; + while ((p != NULL) && (p < b->last)) + { + if (ngx_strncmp(p, s->tag.data, s->tag.len) == 0) { + expect_chunk = 0; + p += s->tag.len; + if (p[0] == 'O' && p[1] == 'K') { + return NGX_OK; + } + } + else { + /* be prepared to handle (optional) untagged (capability) + response before the tagged result to the login command + (rfc 3501, section 6.2.3) + */ + + /* it is safe to search for a \n because the check for + b->last[-1,-2] against CRLF has already been done + */ + p = (u_char *)strstr ((char *)p, "\n"); + if (!p) + { + break; + } + else + { + /* advance beyond the newline */ + expect_chunk = 1; + ++p; + } } } break; @@ -720,6 +747,17 @@ break; } + if (expect_chunk == 1) + { + /* expect_chunk can only be 1 if the response to the + LOGIN command contained an optional (untagged) + capability response, followed by the tagged result + (OK or NO), in separate TCP packets which require more + than one call to recv() + */ + return NGX_AGAIN; + } + pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module); if (pcf->pass_error_message == 0) {
另外,如果需要集成的nginx反代服务器,可以考虑使用修改版的openresty。
https://github.com/linuxrc/openresty-nginx-mail-proxy
参考链接:
- https://www.ruby-forum.com/t/imap-connection-to-gmail-closes-connection/238526/
- https://mailman.nginx.org/pipermail/nginx/2007-November/002269.html
- https://www.nginx.com/resources/wiki/start/topics/examples/imapproxyexample/
- https://forum.nginx.org/read.php?2,246483,246483
- https://forum.nginx.org/read.php?18,282754,282754
- https://www.google.com/search?q=site%3Anginx.org+Auth-Server
- https://v2ex.com/t/417280
- https://github.com/emersion/go-imap/issues/79
- https://scaron.info/blog/improve-your-nginx-ssl-configuration.html