[nginx] Mail: sending of the PROXY protocol to backends.

Maxim Dounin mdounin at mdounin.ru
Fri Mar 5 15:32:01 UTC 2021


details:   https://hg.nginx.org/nginx/rev/4b8f23a36ebf
branches:  
changeset: 7796:4b8f23a36ebf
user:      Maxim Dounin <mdounin at mdounin.ru>
date:      Fri Mar 05 17:16:32 2021 +0300
description:
Mail: sending of the PROXY protocol to backends.

Activated with the "proxy_protocol" directive.  Can be combined with
"listen ... proxy_protocol;" and "set_real_ip_from ...;" to pass
client address provided to nginx in the PROXY protocol header.

diffstat:

 src/mail/ngx_mail.h              |    1 +
 src/mail/ngx_mail_proxy_module.c |  139 +++++++++++++++++++++++++++++++++++++-
 2 files changed, 133 insertions(+), 7 deletions(-)

diffs (245 lines):

diff -r ef4bdbbce57e -r 4b8f23a36ebf src/mail/ngx_mail.h
--- a/src/mail/ngx_mail.h	Fri Mar 05 17:16:29 2021 +0300
+++ b/src/mail/ngx_mail.h	Fri Mar 05 17:16:32 2021 +0300
@@ -178,6 +178,7 @@ typedef enum {
 typedef struct {
     ngx_peer_connection_t   upstream;
     ngx_buf_t              *buffer;
+    ngx_uint_t              proxy_protocol;  /* unsigned  proxy_protocol:1; */
 } ngx_mail_proxy_ctx_t;
 
 
diff -r ef4bdbbce57e -r 4b8f23a36ebf src/mail/ngx_mail_proxy_module.c
--- a/src/mail/ngx_mail_proxy_module.c	Fri Mar 05 17:16:29 2021 +0300
+++ b/src/mail/ngx_mail_proxy_module.c	Fri Mar 05 17:16:32 2021 +0300
@@ -17,6 +17,7 @@ typedef struct {
     ngx_flag_t  pass_error_message;
     ngx_flag_t  xclient;
     ngx_flag_t  smtp_auth;
+    ngx_flag_t  proxy_protocol;
     size_t      buffer_size;
     ngx_msec_t  timeout;
 } ngx_mail_proxy_conf_t;
@@ -26,7 +27,8 @@ static void ngx_mail_proxy_block_read(ng
 static void ngx_mail_proxy_pop3_handler(ngx_event_t *rev);
 static void ngx_mail_proxy_imap_handler(ngx_event_t *rev);
 static void ngx_mail_proxy_smtp_handler(ngx_event_t *rev);
-static void ngx_mail_proxy_dummy_handler(ngx_event_t *ev);
+static void ngx_mail_proxy_write_handler(ngx_event_t *wev);
+static ngx_int_t ngx_mail_proxy_send_proxy_protocol(ngx_mail_session_t *s);
 static ngx_int_t ngx_mail_proxy_read_response(ngx_mail_session_t *s,
     ngx_uint_t state);
 static void ngx_mail_proxy_handler(ngx_event_t *ev);
@@ -82,6 +84,13 @@ static ngx_command_t  ngx_mail_proxy_com
       offsetof(ngx_mail_proxy_conf_t, smtp_auth),
       NULL },
 
+    { ngx_string("proxy_protocol"),
+      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_FLAG,
+      ngx_conf_set_flag_slot,
+      NGX_MAIL_SRV_CONF_OFFSET,
+      offsetof(ngx_mail_proxy_conf_t, proxy_protocol),
+      NULL },
+
       ngx_null_command
 };
 
@@ -156,7 +165,7 @@ ngx_mail_proxy_init(ngx_mail_session_t *
     p->upstream.connection->pool = s->connection->pool;
 
     s->connection->read->handler = ngx_mail_proxy_block_read;
-    p->upstream.connection->write->handler = ngx_mail_proxy_dummy_handler;
+    p->upstream.connection->write->handler = ngx_mail_proxy_write_handler;
 
     pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module);
 
@@ -167,6 +176,8 @@ ngx_mail_proxy_init(ngx_mail_session_t *
         return;
     }
 
+    s->proxy->proxy_protocol = pcf->proxy_protocol;
+
     s->out.len = 0;
 
     switch (s->protocol) {
@@ -186,6 +197,12 @@ ngx_mail_proxy_init(ngx_mail_session_t *
         s->mail_state = ngx_smtp_start;
         break;
     }
+
+    if (rc == NGX_AGAIN) {
+        return;
+    }
+
+    ngx_mail_proxy_write_handler(p->upstream.connection->write);
 }
 
 
@@ -230,6 +247,17 @@ ngx_mail_proxy_pop3_handler(ngx_event_t 
         return;
     }
 
+    if (s->proxy->proxy_protocol) {
+        ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "mail proxy pop3 busy");
+
+        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
+            ngx_mail_proxy_internal_server_error(s);
+            return;
+        }
+
+        return;
+    }
+
     rc = ngx_mail_proxy_read_response(s, 0);
 
     if (rc == NGX_AGAIN) {
@@ -353,6 +381,17 @@ ngx_mail_proxy_imap_handler(ngx_event_t 
         return;
     }
 
+    if (s->proxy->proxy_protocol) {
+        ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "mail proxy imap busy");
+
+        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
+            ngx_mail_proxy_internal_server_error(s);
+            return;
+        }
+
+        return;
+    }
+
     rc = ngx_mail_proxy_read_response(s, s->mail_state);
 
     if (rc == NGX_AGAIN) {
@@ -499,6 +538,17 @@ ngx_mail_proxy_smtp_handler(ngx_event_t 
         return;
     }
 
+    if (s->proxy->proxy_protocol) {
+        ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, "mail proxy smtp busy");
+
+        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
+            ngx_mail_proxy_internal_server_error(s);
+            return;
+        }
+
+        return;
+    }
+
     rc = ngx_mail_proxy_read_response(s, s->mail_state);
 
     if (rc == NGX_AGAIN) {
@@ -799,19 +849,92 @@ ngx_mail_proxy_smtp_handler(ngx_event_t 
 
 
 static void
-ngx_mail_proxy_dummy_handler(ngx_event_t *wev)
+ngx_mail_proxy_write_handler(ngx_event_t *wev)
 {
     ngx_connection_t    *c;
     ngx_mail_session_t  *s;
 
-    ngx_log_debug0(NGX_LOG_DEBUG_MAIL, wev->log, 0, "mail proxy dummy handler");
+    ngx_log_debug0(NGX_LOG_DEBUG_MAIL, wev->log, 0, "mail proxy write handler");
+
+    c = wev->data;
+    s = c->data;
+
+    if (s->proxy->proxy_protocol) {
+        if (ngx_mail_proxy_send_proxy_protocol(s) != NGX_OK) {
+            return;
+        }
+
+        s->proxy->proxy_protocol = 0;
+    }
 
     if (ngx_handle_write_event(wev, 0) != NGX_OK) {
-        c = wev->data;
-        s = c->data;
+        ngx_mail_proxy_internal_server_error(s);
+    }
+
+    if (c->read->ready) {
+        ngx_post_event(c->read, &ngx_posted_events);
+    }
+}
+
+
+static ngx_int_t
+ngx_mail_proxy_send_proxy_protocol(ngx_mail_session_t *s)
+{
+    u_char            *p;
+    ssize_t            n, size;
+    ngx_connection_t  *c;
+    u_char             buf[NGX_PROXY_PROTOCOL_MAX_HEADER];
+
+    s->connection->log->action = "sending PROXY protocol header to upstream";
+
+    ngx_log_debug0(NGX_LOG_DEBUG_MAIL, s->connection->log, 0,
+                   "mail proxy send PROXY protocol header");
+
+    p = ngx_proxy_protocol_write(s->connection, buf,
+                                 buf + NGX_PROXY_PROTOCOL_MAX_HEADER);
+    if (p == NULL) {
+        ngx_mail_proxy_internal_server_error(s);
+        return NGX_ERROR;
+    }
+
+    c = s->proxy->upstream.connection;
+
+    size = p - buf;
 
-        ngx_mail_proxy_close_session(s);
+    n = c->send(c, buf, size);
+
+    if (n == NGX_AGAIN) {
+        if (ngx_handle_write_event(c->write, 0) != NGX_OK) {
+            ngx_mail_proxy_internal_server_error(s);
+            return NGX_ERROR;
+        }
+
+        return NGX_AGAIN;
+    }
+
+    if (n == NGX_ERROR) {
+        ngx_mail_proxy_internal_server_error(s);
+        return NGX_ERROR;
     }
+
+    if (n != size) {
+
+        /*
+         * PROXY protocol specification:
+         * The sender must always ensure that the header
+         * is sent at once, so that the transport layer
+         * maintains atomicity along the path to the receiver.
+         */
+
+        ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
+                      "could not send PROXY protocol header at once");
+
+        ngx_mail_proxy_internal_server_error(s);
+
+        return NGX_ERROR;
+    }
+
+    return NGX_OK;
 }
 
 
@@ -1212,6 +1335,7 @@ ngx_mail_proxy_create_conf(ngx_conf_t *c
     pcf->pass_error_message = NGX_CONF_UNSET;
     pcf->xclient = NGX_CONF_UNSET;
     pcf->smtp_auth = NGX_CONF_UNSET;
+    pcf->proxy_protocol = NGX_CONF_UNSET;
     pcf->buffer_size = NGX_CONF_UNSET_SIZE;
     pcf->timeout = NGX_CONF_UNSET_MSEC;
 
@@ -1229,6 +1353,7 @@ ngx_mail_proxy_merge_conf(ngx_conf_t *cf
     ngx_conf_merge_value(conf->pass_error_message, prev->pass_error_message, 0);
     ngx_conf_merge_value(conf->xclient, prev->xclient, 1);
     ngx_conf_merge_value(conf->smtp_auth, prev->smtp_auth, 0);
+    ngx_conf_merge_value(conf->proxy_protocol, prev->proxy_protocol, 0);
     ngx_conf_merge_size_value(conf->buffer_size, prev->buffer_size,
                               (size_t) ngx_pagesize);
     ngx_conf_merge_msec_value(conf->timeout, prev->timeout, 24 * 60 * 60000);


More information about the nginx-devel mailing list