NGINX反代IMAP配置

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

参考链接:

上一篇
下一篇