1、Using a php script on apache server as the auth backend

Start with the configuration from NginxImapProxyExample. For detail information about different configuration parameters, see the NginxMailCoreModule page.


  1. Your Proxy server for pop/imap is running on 192.168.1.1
  2. You have 2 backend pop/imap servers: 192.168.1.22 and 192.168.1.33
  3. You have a webserver that you will use for the authentication and redirection logic 192.168.1.44.
  4. The authentication script is /mail/auth.php

nginx.conf

  1. user  nobody;  
  2. worker_processes  1;  
  3. error_log  logs/error.log  info;  
  4. pid        logs/nginx.pid;  
  5.  
  6. events {  
  7.   worker_connections  1024;  
  8.   multi_accept on;  
  9. }  
  10.  
  11. mail {  
  12.   auth_http  192.168.1.44:80/mail/auth.php;  
  13.   pop3_capabilities  "TOP"  "USER";  
  14.   imap_capabilities  "IMAP4rev1"  "UIDPLUS";  
  15.  
  16.   server {  
  17.     listen     110;  
  18.     protocol   pop3;  
  19.     proxy      on;  
  20.   }  
  21.  
  22.   server {  
  23.     listen     143;  
  24.     protocol   imap;  
  25.     proxy      on;  
  26.   }  
/mail/auth.php
  1. <?php  
  2. /*  
  3. Nginx sends headers as  
  4. Auth-User: somuser  
  5. Auth-Pass: somepass  
  6. On my php app server these are seen as  
  7. HTTP_AUTH_USER and HTTP_AUTH_PASS  
  8. */ 
  9. if (!isset($_SERVER["HTTP_AUTH_USER"] ) || !isset($_SERVER["HTTP_AUTH_PASS"] )){  
  10.   fail();  
  11. }  
  12. $username=$_SERVER["HTTP_AUTH_USER"] ;  
  13. $userpass=$_SERVER["HTTP_AUTH_PASS"] ;  
  14. $protocol=$_SERVER["HTTP_AUTH_PROTOCOL"] ;  
  15. // default backend port  
  16. $backend_port=110;  
  17. if ($protocol=="imap") {  
  18.   $backend_port=143;  
  19. }  
  20. if ($protocol=="smtp") {  
  21.   $backend_port=25;  
  22. }  
  23. // nginx likes ip address so if your  
  24. // application gives back hostname, convert it to ip address here  
  25. $backend_ip["mailhost01"] ="192.168.1.22";  
  26. $backend_ip["mailhost02"] ="192.168.1.33";  
  27. // Authenticate the user or fail  
  28. if (!authuser($username,$userpass)){  
  29.   fail();  
  30.   exit;  
  31. }  
  32. // Get the server for this user if we have reached so far  
  33. $userserver=getmailserver($username);  
  34. // Get the ip address of the server  
  35. // We are assuming that you backend returns hostname  
  36. // We try to get the ip else return what we got back  
  37. $server_ip=(isset($backend_ip[$userserver]))?$backend_ip[$userserver] :$userserver;  
  38. // Pass!  
  39. pass($server_ip$backend_port);  
  40.    
  41. //END  
  42.    
  43.    
  44. function authuser($user,$pass){  
  45.   // put your logic here to authen the user to any backend  
  46.   // you want (datbase, ldap, etc)  
  47.   // for example, we will just return true;  
  48.   return true;  
  49. }  
  50.    
  51. function getmailserver($user){  
  52.   // put the logic here to get the mailserver  
  53.   // backend for the user. You can get this from  
  54.   // some database or ldap etc  
  55.   // dummy logic, all users that start with a,c,f and g get mailhost01  
  56.   // the others get mailhost02  
  57.   if in_array(substr($user,0,1), array("a""c""f""g")){  
  58.     return "mailhost01";  
  59.   } else {  
  60.     return "mailhost02";  
  61.   }  
  62. }  
  63.    
  64. function fail(){  
  65.   header("Auth-Status: Invalid login or password");  
  66.   exit;  
  67. }  
  68.    
  69. function pass($server,$port){  
  70.   header("Auth-Status: OK");  
  71.   header("Auth-Server: $server");  
  72.   header("Auth-Port: $port");  
  73.   exit;  
  74. }  
  75. ?> 
2、基于nginx的pop3/imap/smtp的反向代理解决方案
本文介绍基于nginx的邮局反向代理配置方案。nginx对来源于客户端的pop3/smtp/imap请求予以转发到后端postfix,后端邮件服务器采用postfix 2.8.0,已配置并正常运行。
配置nginx.conf

#user nobody;
worker_processes 1;
error_log logs/error.log info;
events {
worker_connections 1024;
}
mail {
auth_http 指定IP:80/auth.php;
pop3_capabilities "TOP" "USER";
imap_capabilities "IMAP4rev1" "UIDPLUS";

server {
listen 110;
protocol pop3;
proxy on;
}
server {
listen 143;
protocol imap;
proxy on;
}
server {
listen 25;
protocol smtp;
proxy on;
smtp_auth login plain;
xclient off;
}
}

说明:
1.安装nginx时禁掉了http(–without-http),因为我们的目标只是转发pop3/smtp/imap请求,故nginx.conf也是相当简单,只有mail模块。如果还需要代理80端口(例如webmail),可以自行编译对http的支持。

2.smtp的配置模块里必须加入xclient off,否则当nginx向后转发smtp请求时,postfix将报“lost connection after XCLIENT”,同时nginx报“550 5.7.0 Error: insufficient authorization”. nginx对smtp的代理,与pop3/imap是不同的,详细见后文。

3.指定IP是用于认证的,需要放认证脚本auth.php. 认证脚本的作用就是验证用户和密码,一般自定义,可以放在任意的服务器上。本方案中选择放在后端邮件服务器上,便于管理。

这里有一个问题,postfix本身已经集成了认证机制(本人采用的是cyrus sasl2+courier-authlib),为什么加了反向代理,认证过程就要移动到反向代理上呢?这样岂不是就变成非透明代理了吗?为什么不作纯碎的透明代理呢?
根据测试,如果这个认证脚本不设验证,直接透传所有pop3/imap请求到后端,在后端邮件服务器还会进行一次认证,但是对于smtp请求,将不再认证,而直接按照转发规则进行转发(因为反向代理的ip加到了postfix的mynetworks中,见后文)。这两种不同的差异应该是跟协议有关。
为了保持统一,在本文的方案中,auth.php集成了pop3/imap/smtp的三种认证。这样的功能架构类似于游戏服务器的,登录服务器和游戏服务器是分开的。

4.在邮件服务器postfix/etc/main.cf中,修改mynetworks值,加入本反向代理的ip,并重载postfix:postfix -s reload

关于xclient:xclient的作用,是将前端的服务器模拟作为一个邮件客户端,而向后端的postfix进行认证和执行发送,但是postfix还需要一个打patch才能完美支持xclient。
关于此问题的讨论可以参见 http://forum.nginx.org/read.php?2,173197,173246#msg-173246

auth.php:

<?php
/**
* @see xiabaibai.net
*/
if(!isset($_SERVER ["HTTP_AUTH_USER"] ) || ! isset($_SERVER ["HTTP_AUTH_PASS"] )) {
fail(0);
}
$username = $_SERVER ["HTTP_AUTH_USER"];
$userpass = $_SERVER ["HTTP_AUTH_PASS"];
$protocol = $_SERVER ["HTTP_AUTH_PROTOCOL"];

$backend_port = 110;
if($protocol == "imap") {
$backend_port = 143;
} elseif ($protocol == "smtp") {
$backend_port = 25;
}

list($uid, $domain) = explode("@", $username);

$auth = authuser($username, $userpass);
if(!$auth) fail (-2);

pass($_SERVER["SERVER_ADDR"], $backend_port);

//自定义认证,sql查询或者api
function authuser($user, $pass) {
return true;
}

function fail($code) {
switch($code){
case 0: header("Auth-Status: Parameter lost"); break;
case -1: header("Auth-Status: No Back-end Server"); break;
case -2: header("Auth-Status: Invalid login or password" ); break;
}
exit();
}

function pass($server, $port) {
header("Auth-Status: OK" );
header("Auth-Server: $server" );
header("Auth-Port: $port" );
exit();
}

?>

此文章由 flyinweb 于 2012-02-01 15:32:37 编辑

本日志由 flyinweb 于 2012-01-12 11:44:05 发表,目前已经被浏览 397 次,评论 0 次;

作者添加了以下标签: Reverse Proxy

引用通告:http://www.517sou.net/Article/751/Trackback.ashx

评论订阅:http://www.517sou.net/Article/751/Feeds.ashx

评论列表

    暂时没有评论
(必填)
(必填,不会被公开)