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