Configuring nginx as mail proxy

I need to use nginx as a mail proxy. I am completely new to nginx and
need some help with the configuration.

Here is what I did:

First I built a service that mocks the authentication services described
here: http://wiki.nginx.org/NginxMailCoreModule. For example,

curl -v -H “Host:auth.server.hostname” -H “Auth-Method:plain” -H
“Auth-User:user” -H “Auth-pass:123” -H “Auth-Protocol:imap” -H
“Auth-Login-Attempt:1” -H “Client-IP: 192.168.1.1”
http://localhost:8080/authorize
returns the following response header (no matter what Auth-User,
Auth-Protocol, and Client-IP are for now):

< HTTP/1.1 200 OK
< Content-Type: text/html;charset=ISO-8859-1
< Auth-Status: OK
< Auth-Server:
< Auth-Port: 110
Second I installed nginx on my mac after installing macports:

$ sudo port -d selfupdate
$ sudo port install nginx
Third I created an nginx.conf with the following:

worker_processes 1;

error_log /var/log/nginx/error.log info;

mail {
# not needed because returned in Auth-Server actually?
server_name ;

auth_http    http://localhost:8080/authorize;

pop3_auth         plain apop cram-md5;
pop3_capabilities "LAST" "TOP" "USER" "PIPELINING" "UIDL";

xclient off;

server {
      listen     110;
      protocol   pop3;
      proxy      on;
      proxy_pass_error_message  on;
}

}
Here is what I got running nginx:

$ nginx -V nginx version: nginx/1.2.4 configure arguments:
–prefix=/opt/local --with-cc-opt=’-I/opt/local/include -O2’
–with-ld-opt=-L/opt/local/lib
–conf-path=/opt/local/etc/nginx/nginx.conf
–error-log-path=/opt/local/var/log/nginx/error.log
–http-log-path=/opt/local/var/log/nginx/access.log
–pid-path=/opt/local/var/run/nginx/nginx.pid
–lock-path=/opt/local/var/run/nginx/nginx.lock
–http-client-body-temp-path=/opt/local/var/run/nginx/client_body_temp
–http-proxy-temp-path=/opt/local/var/run/nginx/proxy_temp
–http-fastcgi-temp-path=/opt/local/var/run/nginx/fastcgi_temp
–http-uwsgi-temp-path=/opt/local/var/run/nginx/uwsgi_temp --with-ipv6

$ nginx nginx: [emerg] unknown directive “mail” in
/opt/local/etc/nginx/nginx.conf:6

The only mention of that error on the web brings up a discussion in
Russian and the translation is “This question is no longer relevant”

My questions:

Why am I getting this unknow directive?

Does my config look correct at first sight or am I missing some key
component for the mail proxy to work using the authentication approach
described here: http://wiki.nginx.org/NginxMailCoreModule?

Thanks a lot. мерси!

On 10/24/12 18:27, Laurent Bonetto wrote:

–error-log-path=/opt/local/var/log/nginx/error.log
–http-log-path=/opt/local/var/log/nginx/access.log
–pid-path=/opt/local/var/run/nginx/nginx.pid
–lock-path=/opt/local/var/run/nginx/nginx.lock
–http-client-body-temp-path=/opt/local/var/run/nginx/client_body_temp
–http-proxy-temp-path=/opt/local/var/run/nginx/proxy_temp
–http-fastcgi-temp-path=/opt/local/var/run/nginx/fastcgi_temp
–http-uwsgi-temp-path=/opt/local/var/run/nginx/uwsgi_temp --with-ipv6

$ nginx nginx: [emerg] unknown directive “mail” in
/opt/local/etc/nginx/nginx.conf:6

nginx -V show, that there is no mail module is build.

–with-mail should be added to configure porameters.

Try to see
port variants nginx
my be it is possible to build nginx with mail module via mac ports.


Anton Y.

Hello!

On Wed, Oct 24, 2012 at 10:27:33AM -0400, Laurent Bonetto wrote:

I need to use nginx as a mail proxy. I am completely new to
nginx and need some help with the configuration.

[…]

–http-fastcgi-temp-path=/opt/local/var/run/nginx/fastcgi_temp

My questions:

Why am I getting this unknow directive?

To use nginx mail proxy module, you have to enable it during
compilation using the --with-mail configure argument. From the
“nginx -V” output you’ve provided it’s clear that nginx binary you
are using doesn’t have mail module compiled in.

See here for more details:
http://nginx.org/en/docs/mail/ngx_mail_core_module.html

[…]


Maxim D.
http://nginx.com/support.html

Thanks. That was indeed my first issue. I did sudo port edit nginx,
added --with-mail to the config options, reinstalled, and now I am
passed that error.

I then got an error that no events was present so I just added
events {
worker_connections 1;
}

Now nginx is starting but I never see any hit to my mock service despite
it being specified in auth_http
auth_http http://localhost:8080/authorize;
No errors reported in the error log.

When is nginx expected to hit the url specified in nginx? When it gets
launched? When an event occurs on the ports 110 and 2525 with the
protocols I specified?

I tried sending emails to/from my mail server that uses POP3 on port 110
and SMTP on port 2525 and never saw any hit to the authorize url. Am I
missing another component in my config?

Again, here is my current config:

worker_processes 1;

error_log /var/log/nginx/error.log info;

events {
worker_connections 1;
}

mail {
server_name ;
auth_http http://localhost:8080/authorize;

pop3_auth         plain apop cram-md5;
pop3_capabilities "LAST" "TOP" "USER" "PIPELINING" "UIDL";

smtp_auth         login plain cram-md5;
smtp_capabilities "SIZE 10485760" ENHANCEDSTATUSCODES 8BITMIME DSN;

xclient off;

server {

SMTP on port 2525 not 25

    listen   2525;
    protocol smtp;
}
server {
      listen     110;
      protocol   pop3;

proxy on;

      proxy_pass_error_message  on;
}

}

Hello!

On Wed, Oct 24, 2012 at 11:49:43AM -0400, Laurent Bonetto wrote:

Thanks. That was indeed my first issue. I did sudo port edit
nginx, added --with-mail to the config options, reinstalled, and
now I am passed that error.

I then got an error that no events was present so I just added
events {
worker_connections 1;
}

This isn’t going to work. With such a low number of worker
connections nginx won’t be able to start worker processes properly
(unless you have no listening sockets configured).

Try looking into error log, you should see something like:

2012/10/24 20:17:53 [alert] 58202#0: 1 worker_connections are not enough
2012/10/24 20:17:53 [notice] 58201#0: signal 20 (SIGCHLD) received
2012/10/24 20:17:53 [notice] 58201#0: worker process 58202 exited with
code 2
2012/10/24 20:17:53 [alert] 58201#0: worker process 58202 exited with
fatal code 2 and cannot be respawned

You have to set worker_processes to something reasonable.
Something like 512 as by default is usually a good choice for a
small test server.

Now nginx is starting but I never see any hit to my mock service
despite it being specified in auth_http
auth_http http://localhost:8080/authorize;
No errors reported in the error log.

When is nginx expected to hit the url specified in nginx? When
it gets launched? When an event occurs on the ports 110 and 2525
with the protocols I specified?

The auth service is requested when nginx needs to authenticate a
client and to find out a backend server address to proxy the
client to.


Maxim D.
http://nginx.com/support.html

Hello!

On Wed, Oct 24, 2012 at 08:26:52PM +0400, Maxim D. wrote:

[…]

You have to set worker_processes to something reasonable.

Ops, I meant to write “worker_connections” here.

Something like 512 as by default is usually a good choice for a
small test server.

[…]


Maxim D.
http://nginx.com/support.html

Hi Maxim,

Thank you for sticking with me on this. I appreciate very much.

I did understand you meant to change the number of worker_connections.
The only reason why I had lowered it was that I got a warning:
nginx: [warn] 1024 worker_connections exceed open file resource limit:
256

After pointing my mail client to localhost, I was finally able to see
nginx hit my mock for an authentication request so there is definitely
some progress! Unfortunately, the proxying is still not working. More
precisely:

nginx hits my authenticate mock server with:
Host: localhost
Auth-User:
Auth-Pass:
Auth-Protocol: pop3
Auth-Login-Attempt: 1
Client-IP: 192.168.1.104

  • If my mock responds with
    < HTTP/1.1 200 OK
    < Content-Type: text/html
    < Auth-Status: Invalid login or password
    < Auth-Wait: 3
    < Content-Length: 0
    Then my mail client tells me that I have the incorrect username or
    password, as expected.

  • However, if my mock responds with:
    < Auth-Status: OK
    < Auth-Server:
    < Auth-Port: 110
    The the mail client responds with an internal server error.
    I added the Auth-Pass (which should not be needed anyway) in the
    response and that didn’t help.

Since I didn’t see any error in the error.log from nginx I used
wireshark to monitor traffic. I filtered on tcp.port eq 110 and compared
side by side the traffic coming from an account using a direct
connection to my mail server, and an account going through the nginx
proxy. In the second case (through proxy), I do not see any traffic
going out to my mail server, suggesting it does not get the info it was
expecting from my authentication service.

  • Can you think of something I am missing?
  • How do I even go about debugging what’s happening here apart from what
    I am already doing (using wireshark)?

Again, for info, here is my current config:

worker_processes 1;

error_log /var/log/nginx/error.log info;

events {
worker_connections 1024;
}

mail {

I assume server_name comes from Auth-Server so I tried commenting

out. Same behavior.
server_name ;
auth_http localhost:8080/authorize;

pop3_auth         plain;
pop3_capabilities "TOP" "USER" "UIDL";

smtp_auth         login plain cram-md5;
smtp_capabilities "SIZE 10485760" ENHANCEDSTATUSCODES 8BITMIME DSN;

xclient off;

server {
    listen   2525;
    protocol smtp;
}
server {
      listen     110;
      protocol   pop3;
      proxy      on;
      proxy_pass_error_message  on;
}

}

Maxime,

Thanks so much. This was the key:

Note that this must be an IP address, not a hostname.
My mail server was passing me a hostname, which nginx passed to the
authenticate service. I had assumed it was fine to return a hostname.
Returning the IP instead did the trick.

I have now the proxy working inbound and outbound.

Much appreciated also your clarifications regarding the low open file
resource and server_name.

You were of a big help today.

Laurent

Hello!

On Wed, Oct 24, 2012 at 05:38:29PM -0400, Laurent Bonetto wrote:

I did understand you meant to change the number of
worker_connections. The only reason why I had lowered it was
that I got a warning:
nginx: [warn] 1024 worker_connections exceed open file resource limit: 256

This indicate that you have very low open file resource limit set.
Easiest way to fix this is to use worker_limit_nofile nginx
configuration directive, see here:

http://nginx.org/r/worker_limit_nofile

Of course tuning your OS and/or using ulimit will do the trick as
well. Using worker_connections set to something like 128 will
help as well, but it’s just to low for any real work and may be
only used for testing.

Auth-Login-Attempt: 1
< Auth-Status: OK
< Auth-Server:
< Auth-Port: 110
The the mail client responds with an internal server error.
I added the Auth-Pass (which should not be needed anyway) in the
response and that didn’t help.

Do you return response without http response line, i.e.
“HTTP/1.1 200 OK”?

What’s exactly in the Auth-Server header returned? Note that this
must be an IP address, not a hostname.

  • How do I even go about debugging what’s happening here apart
    from what I am already doing (using wireshark)?

Appropriate errors should be logged by nginx into error log. I
would suggests there should be something like

2012/10/25 02:29:10 [error] 64793#0: *1 auth http server 127.0.0.1:8081
sent invalid server address:“foobar” while in http auth state …

this time. It’s strange you don’t see anything.

Detailed debug information may be obtained using debug log, see
http://nginx.org/en/docs/debugging_log.html.

[…]

mail {

I assume server_name comes from Auth-Server so I tried commenting out. Same

behavior.

server_name       <my mail server>;

Just a side note: server_name is needed mostly to present
something to a client when it connects, see here:

http://nginx.org/en/docs/mail/ngx_mail_core_module.html#server_name

It can’t be from Auth-Server as it’s only available later, after
auth http service request. It should be safe to omit it though,
machine hostname will be used by default.

[…]


Maxim D.
http://nginx.com/support.html

Laurent Bonetto wrote in post #1081041:

Maxime,

Thanks so much. This was the key:

Note that this must be an IP address, not a hostname.
My mail server was passing me a hostname, which nginx passed to the
authenticate service. I had assumed it was fine to return a hostname.
Returning the IP instead did the trick.

I have now the proxy working inbound and outbound.

Much appreciated also your clarifications regarding the low open file
resource and server_name.

You were of a big help today.

Laurent

Thanks for sharing the info.

Can the SMTP/IMAP service behind the mail proxy be SSL based ones like
smtp.gmail.com/imap.gmail.com (of course use IP address instead of DNS
name for Auth-Server here)?

I am looking at proxying to google as well, and thus need SSL on the
backside (and would like it on general principles for other cases as
well),
however it does not appear that nginx supports this. I would expect
this to
be the default if an incoming connection is using ssl, or, at the very
least, specified in the protocol parameter in the server {} block (e.g.
pop3s, imaps, smtps or smtp-starttls). Neither appears to be the case,
nor
do I see any options that suggest the ability to enable it in some other
way…

Posted at Nginx Forum:
http://forum.nginx.org/read.php?2,232147,232466#msg-232466

useopenid Wrote:

I am looking at proxying to google as well, and thus need SSL on the
backside (and would like it on general principles for other cases as
well)

Here you go: http://forum.nginx.org/read.php?2,219069,247910#msg-247910

Posted at Nginx Forum:
http://forum.nginx.org/read.php?2,232147,247911#msg-247911