[PATCH] Add proxy_protocol option to mail listener

Kees Bos cornelis.bos at gmail.com
Fri Jul 7 13:38:02 UTC 2017


# HG changeset patch
# User Kees Bos <cornelis.bos at gmail.com>
# Date 1499422505 0
#      Fri Jul 07 10:15:05 2017 +0000
# Node ID bc79b2baf494aabb889de1e5dbe3184ff0cb9bfa
# Parent  70e65bf8dfd7a8d39aae8ac3a209d426e6947735
Add proxy_protocol option to mail listener

Add support for the mail handlers. This enables the use of an upstream
loadbalancer/proxy (like haproxy) that connects with the proxy protocol.

The original ip (as exposed with the proxy protocol) will be used as
parameter for the 'Client-IP' in the authentication call.

Example config:
mail {
    server_name mail.example.com;
    auth_http   localhost:9000/;

    server {
        listen 143 proxy_protocol;
        protocol imap;
    }
}

diff -r 70e65bf8dfd7 -r bc79b2baf494 src/mail/ngx_mail.c
--- a/src/mail/ngx_mail.c	Tue Jul 04 18:50:41 2017 +0300
+++ b/src/mail/ngx_mail.c	Fri Jul 07 10:15:05 2017 +0000
@@ -408,6 +408,7 @@
 #if (NGX_MAIL_SSL)
         addrs[i].conf.ssl = addr[i].opt.ssl;
 #endif
+        addrs[i].conf.proxy_protocol = addr[i].opt.proxy_protocol;
 
         len = ngx_sock_ntop(&addr[i].opt.sockaddr.sockaddr, addr[i].opt.socklen,
                             buf, NGX_SOCKADDR_STRLEN, 1);
@@ -457,6 +458,7 @@
 #if (NGX_MAIL_SSL)
         addrs6[i].conf.ssl = addr[i].opt.ssl;
 #endif
+        addrs6[i].conf.proxy_protocol = addr[i].opt.proxy_protocol;
 
         len = ngx_sock_ntop(&addr[i].opt.sockaddr.sockaddr, addr[i].opt.socklen,
                             buf, NGX_SOCKADDR_STRLEN, 1);
diff -r 70e65bf8dfd7 -r bc79b2baf494 src/mail/ngx_mail.h
--- a/src/mail/ngx_mail.h	Tue Jul 04 18:50:41 2017 +0300
+++ b/src/mail/ngx_mail.h	Fri Jul 07 10:15:05 2017 +0000
@@ -40,6 +40,7 @@
     unsigned                ipv6only:1;
 #endif
     unsigned                so_keepalive:2;
+    unsigned                proxy_protocol:1;
 #if (NGX_HAVE_KEEPALIVE_TUNABLE)
     int                     tcp_keepidle;
     int                     tcp_keepintvl;
@@ -55,6 +56,7 @@
     ngx_mail_conf_ctx_t    *ctx;
     ngx_str_t               addr_text;
     ngx_uint_t              ssl;    /* unsigned   ssl:1; */
+    unsigned                proxy_protocol:1;
 } ngx_mail_addr_conf_t;
 
 typedef struct {
@@ -204,6 +206,8 @@
     unsigned                esmtp:1;
     unsigned                auth_method:3;
     unsigned                auth_wait:1;
+    unsigned                ssl:1;
+    unsigned                proxy_protocol:1;
 
     ngx_str_t               login;
     ngx_str_t               passwd;
diff -r 70e65bf8dfd7 -r bc79b2baf494 src/mail/ngx_mail_auth_http_module.c
--- a/src/mail/ngx_mail_auth_http_module.c	Tue Jul 04 18:50:41 2017 +0300
+++ b/src/mail/ngx_mail_auth_http_module.c	Fri Jul 07 10:15:05 2017 +0000
@@ -1142,6 +1142,7 @@
     ngx_mail_ssl_conf_t       *sslcf;
 #endif
     ngx_mail_core_srv_conf_t  *cscf;
+    ngx_str_t                 *client_addr;
 
     if (ngx_mail_auth_http_escape(pool, &s->login, &login) != NGX_OK) {
         return NULL;
@@ -1208,6 +1209,11 @@
 #endif
 
     cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
+    if (s->connection->proxy_protocol_addr.len) {
+        client_addr = &s->connection->proxy_protocol_addr;
+    } else {
+        client_addr = &s->connection->addr_text;
+    }
 
     len = sizeof("GET ") - 1 + ahcf->uri.len + sizeof(" HTTP/1.0" CRLF) - 1
           + sizeof("Host: ") - 1 + ahcf->host_header.len + sizeof(CRLF) - 1
@@ -1221,7 +1227,7 @@
                 + sizeof(CRLF) - 1
           + sizeof("Auth-Login-Attempt: ") - 1 + NGX_INT_T_LEN
                 + sizeof(CRLF) - 1
-          + sizeof("Client-IP: ") - 1 + s->connection->addr_text.len
+          + sizeof("Client-IP: ") - 1 + client_addr->len
                 + sizeof(CRLF) - 1
           + sizeof("Client-Host: ") - 1 + s->host.len + sizeof(CRLF) - 1
           + sizeof("Auth-SMTP-Helo: ") - 1 + s->smtp_helo.len + sizeof(CRLF) - 1
@@ -1287,8 +1293,7 @@
                           s->login_attempt);
 
     b->last = ngx_cpymem(b->last, "Client-IP: ", sizeof("Client-IP: ") - 1);
-    b->last = ngx_copy(b->last, s->connection->addr_text.data,
-                       s->connection->addr_text.len);
+    b->last = ngx_copy(b->last, client_addr->data, client_addr->len);
     *b->last++ = CR; *b->last++ = LF;
 
     if (s->host.len) {
diff -r 70e65bf8dfd7 -r bc79b2baf494 src/mail/ngx_mail_core_module.c
--- a/src/mail/ngx_mail_core_module.c	Tue Jul 04 18:50:41 2017 +0300
+++ b/src/mail/ngx_mail_core_module.c	Fri Jul 07 10:15:05 2017 +0000
@@ -574,6 +574,11 @@
 #endif
         }
 
+        if (ngx_strcmp(value[i].data, "proxy_protocol") == 0) {
+            ls->proxy_protocol = 1;
+            continue;
+        }
+
         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                            "the invalid \"%V\" parameter", &value[i]);
         return NGX_CONF_ERROR;
diff -r 70e65bf8dfd7 -r bc79b2baf494 src/mail/ngx_mail_handler.c
--- a/src/mail/ngx_mail_handler.c	Tue Jul 04 18:50:41 2017 +0300
+++ b/src/mail/ngx_mail_handler.c	Fri Jul 07 10:15:05 2017 +0000
@@ -12,7 +12,7 @@
 
 
 static void ngx_mail_init_session(ngx_connection_t *c);
-
+static void ngx_mail_proxy_protocol_handler(ngx_event_t *rev);
 #if (NGX_MAIL_SSL)
 static void ngx_mail_ssl_init_connection(ngx_ssl_t *ssl, ngx_connection_t *c);
 static void ngx_mail_ssl_handshake_handler(ngx_connection_t *c);
@@ -143,6 +143,7 @@
     ngx_log_error(NGX_LOG_INFO, c->log, 0, "*%uA client %*s connected to %V",
                   c->number, len, text, s->addr_text);
 
+    s->proxy_protocol = addr_conf->proxy_protocol;
     ctx = ngx_palloc(c->pool, sizeof(ngx_mail_log_ctx_t));
     if (ctx == NULL) {
         ngx_mail_close_connection(c);
@@ -165,16 +166,11 @@
 
     sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);
 
+    s->ssl = 0;
     if (sslcf->enable) {
-        c->log->action = "SSL handshaking";
-
-        ngx_mail_ssl_init_connection(&sslcf->ssl, c);
-        return;
-    }
-
-    if (addr_conf->ssl) {
-
-        c->log->action = "SSL handshaking";
+        s->ssl = 1;
+    } else if (addr_conf->ssl) {
+        s->ssl = 1;
 
         if (sslcf->ssl.ctx == NULL) {
             ngx_log_error(NGX_LOG_ERR, c->log, 0,
@@ -183,11 +179,78 @@
             ngx_mail_close_connection(c);
             return;
         }
+    }
 
+    }
+#endif
+
+    if (s->proxy_protocol) {
+        c->log->action = "reading PROXY protocol";
+        ngx_add_timer(c->read, cscf->timeout);
+        c->read->handler = ngx_mail_proxy_protocol_handler;
+        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
+            ngx_mail_close_connection(c);
+        }
+        return;
+    }
+
+#if (NGX_MAIL_SSL)
+    if (s->ssl) {
+        ngx_mail_ssl_conf_t  *sslcf;
+        c->log->action = "SSL handshaking";
+        sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);
         ngx_mail_ssl_init_connection(&sslcf->ssl, c);
         return;
     }
+# endif
 
+    ngx_mail_init_session(c);
+}
+
+
+void
+ngx_mail_proxy_protocol_handler(ngx_event_t *rev)
+{
+    u_char                    *p, buf[NGX_PROXY_PROTOCOL_MAX_HEADER + 1];
+    size_t                     size;
+    ssize_t                    n;
+    ngx_err_t                  err;
+    ngx_connection_t          *c;
+    ngx_mail_session_t   *s;
+
+    size = sizeof(buf);
+    c = rev->data;
+
+    n = recv(c->fd, (char *) buf, size, MSG_PEEK);
+    err = ngx_socket_errno;
+
+    if (n == -1) {
+        ngx_connection_error(c, err, "recv() failed");
+        ngx_mail_close_connection(c);
+        return;
+    }
+    p = ngx_proxy_protocol_read(c, buf, buf + n);
+    if (p == NULL) {
+        ngx_mail_close_connection(c);
+        return;
+    }
+    size = p - buf;
+    if (c->recv(c, buf, size) != (ssize_t) size) {
+        ngx_mail_close_connection(c);
+        return;
+    }
+    s = c->data;
+    s->proxy_protocol = 0;
+
+#if (NGX_MAIL_SSL)
+    {
+    ngx_mail_ssl_conf_t  *sslcf;
+    if (s->ssl) {
+        c->log->action = "SSL handshaking";
+        sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);
+        ngx_mail_ssl_init_connection(&sslcf->ssl, c);
+        return;
+    }
     }
 #endif
 
@@ -222,6 +285,20 @@
     ngx_mail_session_t        *s;
     ngx_mail_core_srv_conf_t  *cscf;
 
+    s = c->data;
+    cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
+
+    if (s->proxy_protocol) {
+        ngx_log_error(NGX_LOG_INFO, c->log, 0, "Has proxy_protocol");
+        c->log->action = "reading PROXY protocol";
+        ngx_add_timer(c->read, cscf->timeout);
+        c->read->handler = ngx_mail_proxy_protocol_handler;
+        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
+            ngx_mail_close_connection(c);
+        }
+        return;
+    }
+
     if (ngx_ssl_create_connection(ssl, c, 0) != NGX_OK) {
         ngx_mail_close_connection(c);
         return;
@@ -229,10 +306,6 @@
 
     if (ngx_ssl_handshake(c) == NGX_AGAIN) {
 
-        s = c->data;
-
-        cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);
-
         ngx_add_timer(c->read, cscf->timeout);
 
         c->ssl->handler = ngx_mail_ssl_handshake_handler;


More information about the nginx-devel mailing list