nginx as reverse SMTP proxy

I’ve noticed that not that many resources are online telling you how you can use nginx as a reverse SMTP proxy. Using a reverse SMTP proxy makes sense even if you have just one mail server back-end, either because you can easily switch towards another one, or because you want to put additional checks before handing off the mail to the back-end.

In the below example, a back-end mail server is running on localhost (in my case it’s a Postfix back-end, but that doesn’t matter). Mails received by Nginx will be forwarded to this server.

user nginx nginx;
worker_processes 1;

error_log /var/log/nginx/error_log debug;

events {
        worker_connections 1024;
        use epoll;
}
http {

        log_format main
                '$remote_addr - $remote_user [$time_local] '
                '"$request" $status $bytes_sent '
                '"$http_referer" "$http_user_agent" '
                '"$gzip_ratio"';


        server {
                listen 127.0.0.1:8008;
                server_name localhost;
                access_log /var/log/nginx/localhost.access_log main;
                error_log /var/log/nginx/localhost.error_log info;

                root /var/www/localhost/htdocs;

                location ~ .php$ {
                        add_header Auth-Server 127.0.0.1;
                        add_header Auth-Port 25;
                        return 200;
                }
        }
}

mail {
        server_name localhost;

        auth_http localhost:8008/auth-smtppass.php;

        server {
                listen 192.168.100.102:25;
                protocol smtp;
                timeout 5s;
                proxy on;
                xclient off;
                smtp_auth none;
        }
}

If you first look at the mail setting, you notice that I include an auth_http directive. This is needed by Nginx as it will consult this back-end service on what to do with the mail (the moment that it receives the recipient information). The URL I use is arbitrarily chosen here, as I don’t really run a PHP service in the background (yet).

In the http section, I create the same resource that the mails’ auth_http wants to connect to. I then declare the two return headers that Nginx needs (Auth-Server and Auth-Port) with the back-end information (127.0.0.1:25). If I ever need to do load balancing or other tricks, I’ll write up a simple PHP script and serve it from PHP-FPM or so.

Next on the list is to enable SSL (not difficult) with client authentication (which isn’t supported by Nginx for the mail module (yet) sadly, so I’ll need to look at a different approach for that).

BTW, this is all on a simple Gentoo Hardened with SELinux enabled. The following booleans were set to true: nginx_enable_http_server, nginx_enable_smtp_server and nginx_can_network_connect_http.

This page has been translated into Spanish language by Maria Ramos from Webhostinghub.com/support/edu.

8 Comments

  1. Hate to be a cynic, but I did chuckle a bit when PHP and Security were juxtaposed. What are you actually trying to accomplish? In general, I can’t think of many reasons to proxy SMTP. Remember, mail is routed and deployment strategies should be more analgous to internetworking.

    For large service providers, have your first hop MX handle only login and initial acceptance and perform content filtering and such on another cluster of servers. For very large service providers, Geo DNS, Performance DNS, and/or round-robin the front ends.

  2. Well, in this case there is no PHP involved (static declaration), but I did use PHP in the URL, mainly as a reminder to myself that, if I ever need to include authentication & authorization, then I could easily involve a phpfpm instance in the background to handle this for me. I agree that using PHP as a back-end (since it isn’t directly called by clients, only by nginx) seems strange, but you can easily change it to being a perl/cgi or even c/cgi back-end. Hell, you can even write your own backend as all you need to do is capture and interpret the headers and reply back with headers.

    I was considering using a simple perl-driven back-end (nginx supports that too) but I read a few posts that this wasn’t as powerful (throughput-wise) as the HTTP-driven backend services.

    Now on the question of “why having a reverse SMTP proxy”, in my case it has to do with the policy on handling the flows and managing the components. One of the policies is that DMZ systems should not store data. Data-in-transit is okay (of course, otherwise there’s no point ;-) but a service triggered from the Internet (or other party) may not acknowledge data received unless it is certain that it has been delivered at the right service back-end. Positioning a regular SMTP server here would queue the messages and violate this policy.

    The second part is on managing the components. Most MTA software support a proxy-like setup as well. If you’re in an organization with sufficient knowledge on managing and administering the MTA software, you’re fine to use this software for this purpose of course. But if the organization has less knowledge about secure setup of Postfix for proxy-purposes, or you rely on appliances that don’t offer this, then using a third party service might help you out here.

  3. Ayman

    Hello
    i’m trying to use nginx as mail server load balancer but i got this from the error_log
    ——————————
    2013/03/05 11:48:18 [error] 32692#0: *1 recv() failed (111: Connection refused) while reading response from upstream, client: 192.168.6.7, server: 192.168.6.195:25
    ———————————————-

    this is my nginx.conf file

    ———————————-

    #user nginx nginx;
    worker_processes 4;

    error_log /opt/nginx/logs/error_log debug;

    events {
    worker_connections 1024;
    use epoll;
    }
    http {
    proxy_connect_timeout 600s;
    proxy_send_timeout 600s;
    proxy_read_timeout 600s;
    fastcgi_send_timeout 600s;
    fastcgi_read_timeout 600s;
    keepalive_timeout 65;
    log_format main
    ‘$remote_addr – $remote_user [$time_local] ‘
    ‘”$request” $status $bytes_sent ‘
    ‘”$http_referer” “$http_user_agent” ‘
    ‘”$gzip_ratio”‘;
    upstream smtp_upstream {
    server 127.0.0.1:7777;
    }

    server {
    listen 127.0.0.1:8008;
    server_name localhost;
    access_log /opt/nginx/logs/jo-test.access_log main;
    error_log /opt/nginx/logs/jo-test.error_log info;

    root /opt/nginx/html;
    set $proxy_pass “192.168.6.195:25″;

    location ~* / {
    add_header Auth-Server 192.168.6.195;
    add_header Auth-Port 25;
    return 200;
    fastcgi_pass 127.0.0.1:7777;
    #include /opt/nginx/conf/nginx.conf;
    proxy_pass http://smtp_upstream;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
    }
    }

    mail {
    server_name localhost;

    auth_http localhost:7777/auth.php;

    server {
    listen 192.168.6.195:25;
    protocol smtp;
    timeout 60s;
    proxy on;
    xclient off;
    smtp_auth none;
    }
    }

    ——————–
    i’m trying to test the local postifx on 192.168.6.195 and if success i want to add 3 or more postfix from remote server.

    Please advise and you quick reply is highly apprecated.

    Thank you.

  4. Hi; I don’t have access to the SMTP nginx set up I did (it was for a friend’s company) but a connection refused can usually be debugged easily by using tools like netcat or telnet (see if you can make the connection) and checking the error logs of your “upstream” server (the one on port 7777).

  5. linuxgp

    Hello,

    we have swiftmailer as our mailing list. we require script like proxy smtp or any script which transfer user received mail to Swiftmailer & swiftmailer then our swiftmailer send that to our mta postfix etc.

    Example Proxy ip : 127.0.0.2
    Port:2525
    Auth:admin
    Pass:admin

    User use our proxy as smtp setting in php or any mail sending. then our proxy transfer all mail to swiftmailer for send mail.

    So do you have any idea of this type of proxy or any script or method?….

  6. I don’t think nginx is what you are looking for here then. Swiftmailer is a PHP library afaik, so you would be looking for a tool that converts SMTP (incoming) to HTTP(S) requests. Although some heavy commercial enterprise bus solutions could support such a thing, I have no experience with such gateways.

    Does sound like a (very) complex setup. Why not just have the mails sent to your Postfix MTA directly?

Leave a Reply

Your email address will not be published. Required fields are marked *