October 6, 2011

 Using Nginx as reverse IMAP/POP Proxy

This document covers the steps you need to take to have Atmail working with the NginX platform.

Nginx is a free, open-source, high-performance HTTP server and IMAP/POP3 proxy. This is useful for environments that wish to geographically split machines into independent entities, with a central routing system that forwards a user to his/her specific server.

First, download spawn-fcgi from http://redmine.lighttpd.net/projects/spawn-fcgi/news:

% wget "http://www.lighttpd.net/download/spawn-fcgi-1.6.3.tar.gz"

Untar, install:

% tar xvfz spawn-fcgi-1.6.3.tar.gz
% cd spawn-fcgi-1.6.3
% ./configure && make && make install

Then, download nginx from http://nginx.org/en/download.html:

% wget "http://nginx.org/download/nginx-1.0.8.tar.gz"

Untar, install:

% tar xvfz nginx-0.8.40.tar.gz
% cd nginx-0.8.40
% ./configure && make && make install

This will install nginx in /usr/local/nginx/. You will then need to move the /usr/local/nginx/conf/nginx.conf file:

% mv /usr/local/nginx/conf/nginx.conf /usr/local/nginx/conf/nginx.conf.old
% cd /usr/local/nginx/conf

Replace the nginx.conf file with these contents:

user  atmail;
worker_processes  2;
error_log  logs/error.log  info;
pid        logs/nginx.pid;
events {
    worker_connections  1024;
    }

mail {
  auth_http  localhost:80/auth.php;
  proxy  on;
  imap_capabilities  "IMAP4rev1"  "UIDPLUS";
  server {
    listen     143;
    protocol   imap;
  }
 
  pop3_capabilities  "TOP"  "USER";
  server {
    listen     110;
    protocol   pop3;
  }
}

http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  10;

server {
    # Your server's IP address. Leave as an asterisk to bind to all interfaces
    listen       *:80;
    # Your domain name
    server_name  domain.com;      
    location / {
    # Path to your Atmail webmail directory
        root   /usr/local/atmail/webmail/;  
        index  index.php;
        # this serves static files that exist without running other rewrite tests
        if (-f $request_filename) {
            expires 30d;
            break;
        }
    # Routes directory requests to index.php.
        if (!-e $request_filename) {
            rewrite ^(.+)$ /index.php?q=$1 last;
        }
    }

    location ~ \.php$ {
        fastcgi_pass   127.0.0.1:34480;  # IP and Port of your spawn-fcgi process
        fastcgi_index  index.php;
        fastcgi_param  SCRIPT_FILENAME    /usr/local/atmail/webmail/$fastcgi_script_name;
        fastcgi_param  QUERY_STRING       $query_string;
        fastcgi_param  REQUEST_METHOD     $request_method;
        fastcgi_param  CONTENT_TYPE       $content_type;
        fastcgi_param  CONTENT_LENGTH     $content_length;
        fastcgi_param  SCRIPT_NAME        $fastcgi_script_name;
        fastcgi_param  REQUEST_URI        $request_uri;
        fastcgi_param  DOCUMENT_URI       $document_uri;
        fastcgi_param  DOCUMENT_ROOT      $document_root;
        fastcgi_param  SERVER_PROTOCOL    $server_protocol;
        fastcgi_param  GATEWAY_INTERFACE  CGI/1.1;
        fastcgi_param  SERVER_SOFTWARE    nginx/$nginx_version;
        fastcgi_param  REMOTE_ADDR        $remote_addr;
        fastcgi_param  REMOTE_PORT        $remote_port;
        fastcgi_param  SERVER_ADDR        $server_addr;
        fastcgi_param  SERVER_PORT        $server_port;
        fastcgi_param  SERVER_NAME        $server_name;
        fastcgi_param  REDIRECT_STATUS    200;
    }
}
}

Spawn the FastCGI process afterwards. For this example, we will run it in port 34480:

% /usr/local/bin/spawn-fcgi -f /usr/bin/php-cgi -a 127.0.0.1 -p 34480 -P /var/run/fastcgi-php.pid -C 2

You will then need to edit the /usr/local/nginx/conf/nginx.conf file. It will contain this line:

fastcgi_pass   127.0.0.1:34480;  # IP and Port of your spawn-fcgi process

If you change the spawn-fcgi port, you will need to edit it in the nginx.conf file as well. For example, a spawn-fcgi port of 51000 will have this command:

/usr/local/bin/spawn-fcgi -f /usr/bin/php-cgi -a 127.0.0.1 -p 51000 -P /var/run/fastcgi-php.pid -C 2

And this configuration line:

fastcgi_pass   127.0.0.1:51000;  # IP and Port of your spawn-fcgi process

The configuration file will also contain the following lines of interest:

root   /usr/local/atmail/webmail/;

This defines the document root you wish to have.

user  atmail;

This defines the username you want to run nginx as.

worker_processes  2;

This sets the number of processes that nginx spawns. We recommend it to be set to the number of CPU cores you have available.

Note the document root specified above (in this case, /usr/local/atmail/webmail/). Create the authentication php file at /usr/local/atmail/webmail/auth.php, and fill it with these lines:

$username=$_SERVER["HTTP_AUTH_USER"] ;
$userpass=$_SERVER["HTTP_AUTH_PASS"] ;
$protocol=$_SERVER["HTTP_AUTH_PROTOCOL"] ;

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

if (!authuser($username,$userpass)) {
fail();
exit;
}

$userserver=getmailserver($username);
pass($userserver, $backend_port);

// Authentication block
function authuser($user,$pass)
{
// You can put a query for authentication with the DB here.
// Since auth will be done post-proxy, we're just returning true
return true;
}

// MySQL connection function
function mysqlconn($user,$query){
$atmail_dbuser="atmail";
$atmail_dbpass="changeme";
$atmail_db="nginx_map";
$atmail_dbhost="127.0.0.1";

mysql_connect($atmail_dbhost,$atmail_dbuser,$atmail_dbpass);
@mysql_select_db($atmail_db) or die( "Unable to select database");

$server1 = mysql_query($query);

$server = mysql_fetch_array($server1);
$server2 = $server['Server'];
return $server2;
mysql_close();

}

function getmailserver($user){
$query = "select Server from UserMap where Account='$user'";
$server = mysqlconn($user,$query);
return $server;
mysql_close();

}

function fail(){
header("Auth-Status: Invalid login or password");
exit;
}

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

Save the file. After it is created, login to MySQL:

% mysql -u [dbuser] -p

Specify [dbuser] as your MySQL root user. Create the database (in this case, nginx_map, with the user 'atmail', the password 'changeme' and the host '127.0.0.1'):

> create database nginx_map;
> grant all privileges on nginx_map.* to 'atmail'@'localhost' identified by 'changeme';
> grant all privileges on nginx_map.* to 'atmail'@'127.0.0.1' identified by 'changeme';
> flush privileges;

Create the database skeleton:

CREATE TABLE `UserMap` (
`Account` varchar(255) NOT NULL default '',
`Server` varchar(255) NOT NULL default '',
PRIMARY KEY  (`Account`) );
);

You can create preliminary entries like so:

> insert into UserMap values("john@juno.com","192.168.0.71");

This will forward the user 'john@juno.com' to the IMAP host at '192.168.0.71'.

When you are satisfied, run the nginx process:

% /usr/local/nginx/sbin/nginx

To test it out, login locally with your sample user:

% telnet localhost 143
Trying 127.0.0.1...
Connected to localhost.localdomain (127.0.0.1).
Escape character is '^]'.
* OK IMAP4 ready
1 login john@juno.com changeme
1 OK [CAPABILITY IMAP4rev1 LITERAL+ SASL-IR LOGIN-REFERRALS ID ENABLE IDLE SORT SORT=DISPLAY THREAD=REFERENCES THREAD=REFS MULTIAPPEND UNSELECT CHILDREN NAMESPACE UIDPLUS LIST-EXTENDED I18NLEVEL=1 CONDSTORE QRESYNC ESEARCH ESORT SEARCHRES WITHIN CONTEXT=SEARCH LIST-STATUS QUOTA] Logged in

Congratulations! Now you can set your Nginx process as a forwarding proxy for IMAP and POP requests. This is useful for very large installations that wish to install Atmail as a geographically separated cluster install.


Filed under: Uncategorized — John Contad @ 7:01 pm

No Comments »

No comments yet.

RSS feed for comments on this post. TrackBack URI

Leave a comment