Hi Folks, I was not sure if you would be interested in this, but decided to run it by you anyway.
We need to run Nginx as an SMTP proxy sitting behind ELB in AWS, but we also want the upstream SMTP server to get the real client ip, so Nginx is configured to provide it via an XCLIENT command. However the stock version of Nginx provides ELB's ip instead, because it does not recognize the Proxy Protocol header (http://docs.aws.amazon.com/elasticloadbalancing/latest/ classic/enable-proxy-protocol.html#proxy-protocol) sent to it by ELB. The following patch updates the mail module so that it can be configured to expect Proxy Protocol header by setting `proxy_protocol on`. In that case Proxy Protocol header is parsed, a client IP is retrieved and passed to an SMTP upstream in an XCLIENT command. Here it is: # HG changeset patch # User Maxim Vladimirskiy <ma...@mailgunhq.com> # Date 1478116426 25200 # Wed Nov 02 12:53:46 2016 -0700 # Node ID fcb527c8ade6db113acbee677f321fa0249348a2 # Parent 0fba3ed4e7eba474e3f518763d9e02851c9ff024 Add support for Proxy Protocol to mail SMTP When nginx is not the first proxy in a proxy chain it needs to be able to understand the Proxy Protocol Header to retrieve client IP and pass it downstream in an XCLIENT command. With these changes one can do that by setting `proxy_protocol on;` in the mail module config. diff -r 0fba3ed4e7eb -r fcb527c8ade6 src/mail/ngx_mail.h --- a/src/mail/ngx_mail.h Wed Nov 02 20:05:21 2016 +0300 +++ b/src/mail/ngx_mail.h Wed Nov 02 12:53:46 2016 -0700 @@ -185,6 +185,7 @@ void **ctx; void **main_conf; void **srv_conf; + ngx_mail_addr_conf_t *addr_conf; ngx_resolver_ctx_t *resolver_ctx; diff -r 0fba3ed4e7eb -r fcb527c8ade6 src/mail/ngx_mail_handler.c --- a/src/mail/ngx_mail_handler.c Wed Nov 02 20:05:21 2016 +0300 +++ b/src/mail/ngx_mail_handler.c Wed Nov 02 12:53:46 2016 -0700 @@ -9,9 +9,12 @@ #include <ngx_core.h> #include <ngx_event.h> #include <ngx_mail.h> +#include <ngx_mail_proxy_module.h> static void ngx_mail_init_session(ngx_connection_t *c); +static void ngx_mail_proxy_protocol_handler(ngx_event_t *rev); +static void ngx_mail_init_session_handler(ngx_event_t *rev); #if (NGX_MAIL_SSL) static void ngx_mail_ssl_init_connection(ngx_ssl_t *ssl, ngx_connection_t *c); @@ -34,6 +37,8 @@ ngx_mail_session_t *s; ngx_mail_addr_conf_t *addr_conf; ngx_mail_core_srv_conf_t *cscf; + ngx_mail_proxy_conf_t *pcf; + ngx_event_t *rev; u_char text[NGX_SOCKADDR_STRLEN]; #if (NGX_HAVE_INET6) struct sockaddr_in6 *sin6; @@ -128,6 +133,7 @@ s->main_conf = addr_conf->ctx->main_conf; s->srv_conf = addr_conf->ctx->srv_conf; + s->addr_conf = addr_conf; s->addr_text = &addr_conf->addr_text; @@ -159,10 +165,31 @@ c->log_error = NGX_ERROR_INFO; + rev = c->read; + + pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module); + if (pcf->proxy_protocol) { + rev->handler = ngx_mail_proxy_protocol_handler; + ngx_mail_proxy_protocol_handler(rev); + return; + } + + ngx_mail_init_session_handler(rev); +} + + +static void +ngx_mail_init_session_handler(ngx_event_t *rev) +{ + ngx_connection_t *c; + + c = rev->data; + #if (NGX_MAIL_SSL) - { ngx_mail_ssl_conf_t *sslcf; + ngx_mail_session_t *s; + s = c->data; sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module); if (sslcf->enable) { @@ -172,7 +199,7 @@ return; } - if (addr_conf->ssl) { + if (s->addr_conf->ssl) { c->log->action = "SSL handshaking"; @@ -187,8 +214,6 @@ ngx_mail_ssl_init_connection(&sslcf->ssl, c); return; } - - } #endif ngx_mail_init_session(c); @@ -898,3 +923,73 @@ return p; } + + +static void +ngx_mail_proxy_protocol_handler(ngx_event_t *rev) +{ + ngx_mail_session_t *s; + ngx_mail_core_srv_conf_t *cscf; + u_char *p, buf[NGX_PROXY_PROTOCOL_MAX_HEADER + 1]; + size_t size; + ssize_t n; + ngx_connection_t *c; + + c = rev->data; + s = c->data; + + cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, + "read proxy protocol header"); + + if (rev->timedout) { + ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); + ngx_mail_close_connection(c); + return; + } + + if (c->close) { + ngx_mail_close_connection(c); + return; + } + + n = recv(c->fd, (char *) buf, sizeof(buf), MSG_PEEK); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, rev->log, 0, "http recv(): %z", n); + + if (n == -1) { + if (ngx_socket_errno == NGX_EAGAIN) { + rev->ready = 0; + + if (!rev->timer_set) { + ngx_add_timer(rev, cscf->timeout); + ngx_reusable_connection(c, 1); + } + + if (ngx_handle_read_event(rev, 0) != NGX_OK) { + ngx_mail_close_connection(c); + } + return; + } + + ngx_connection_error(c, ngx_socket_errno, "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; + } + + // Purge the Proxy Protocol header from the network buffer. + size = p - buf; + if (c->recv(c, buf, size) != (ssize_t) size) { + ngx_mail_close_connection(c); + return; + } + + ngx_mail_init_session_handler(rev); +} \ No newline at end of file diff -r 0fba3ed4e7eb -r fcb527c8ade6 src/mail/ngx_mail_proxy_module.c --- a/src/mail/ngx_mail_proxy_module.c Wed Nov 02 20:05:21 2016 +0300 +++ b/src/mail/ngx_mail_proxy_module.c Wed Nov 02 12:53:46 2016 -0700 @@ -10,15 +10,7 @@ #include <ngx_event.h> #include <ngx_event_connect.h> #include <ngx_mail.h> - - -typedef struct { - ngx_flag_t enable; - ngx_flag_t pass_error_message; - ngx_flag_t xclient; - size_t buffer_size; - ngx_msec_t timeout; -} ngx_mail_proxy_conf_t; +#include <ngx_mail_proxy_module.h> static void ngx_mail_proxy_block_read(ngx_event_t *rev); @@ -74,6 +66,13 @@ offsetof(ngx_mail_proxy_conf_t, xclient), 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 }; @@ -456,6 +455,7 @@ ngx_mail_session_t *s; ngx_mail_proxy_conf_t *pcf; ngx_mail_core_srv_conf_t *cscf; + ngx_str_t client_addr; ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, "mail proxy smtp auth handler"); @@ -525,9 +525,12 @@ s->connection->log->action = "sending XCLIENT to upstream"; - line.len = sizeof("XCLIENT ADDR= LOGIN= NAME=" - CRLF) - 1 - + s->connection->addr_text.len + s->login.len + s->host.len; + client_addr = s->connection->addr_text; + if (s->connection->proxy_protocol_addr.data != NULL) { + client_addr = s->connection->proxy_protocol_addr; + } + line.len = sizeof("XCLIENT ADDR= LOGIN= NAME=" CRLF) - 1 + + client_addr.len + s->login.len + s->host.len; #if (NGX_HAVE_INET6) if (s->connection->sockaddr->sa_family == AF_INET6) { @@ -549,9 +552,7 @@ } #endif - p = ngx_copy(p, s->connection->addr_text.data, - s->connection->addr_text.len); - + p = ngx_copy(p, client_addr.data, client_addr.len); if (s->login.len) { p = ngx_cpymem(p, " LOGIN=", sizeof(" LOGIN=") - 1); p = ngx_copy(p, s->login.data, s->login.len); @@ -1099,6 +1100,7 @@ pcf->enable = NGX_CONF_UNSET; pcf->pass_error_message = NGX_CONF_UNSET; pcf->xclient = NGX_CONF_UNSET; + pcf->proxy_protocol = NGX_CONF_UNSET; pcf->buffer_size = NGX_CONF_UNSET_SIZE; pcf->timeout = NGX_CONF_UNSET_MSEC; @@ -1115,6 +1117,7 @@ ngx_conf_merge_value(conf->enable, prev->enable, 0); 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->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); diff -r 0fba3ed4e7eb -r fcb527c8ade6 src/mail/ngx_mail_proxy_module.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/mail/ngx_mail_proxy_module.h Wed Nov 02 12:53:46 2016 -0700 @@ -0,0 +1,29 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_MAIL_PROXY_MODULE_H_INCLUDED_ +#define _NGX_MAIL_PROXY_MODULE_H_INCLUDED_ + + +#include <ngx_config.h> +#include <ngx_core.h> + + +typedef struct { + ngx_flag_t enable; + ngx_flag_t pass_error_message; + ngx_flag_t xclient; + ngx_flag_t proxy_protocol; + size_t buffer_size; + ngx_msec_t timeout; +} ngx_mail_proxy_conf_t; + + +extern ngx_module_t ngx_mail_proxy_module; + + +#endif /* _NGX_MAIL_PROXY_MODULE_H_INCLUDED_ */ Cheers Maxim Vladimirskiy GitHub Profile: https://github.com/horkhe
_______________________________________________ nginx-devel mailing list nginx-devel@nginx.org http://mailman.nginx.org/mailman/listinfo/nginx-devel