# HG changeset patch # User Kees Bos <cornelis....@gmail.com> # Date 1500565189 0 # Thu Jul 20 15:39:49 2017 +0000 # Node ID 327f18e079b175b14277a23e75715f5feee34d69 # Parent 863b862534d7ac0dbf8babf68b824de6fb0d6ef4 Add proxy_protocol option to mail listener
Add support for the mail handlers. This enables the use of an upstream loadbalancer/proxy that connects with the proxy protocol. Examples of this are haproxy or a nginx stream handler that uses then proxy protocol in client conections. The proxy protocol source ip address will we exposed to the auth handler as 'Proxy-Protocol-IP'. If the sender ip address matches one or more "set_real_ip_from" directives, the source ip address as specified in the in the proxy protocol will be used as 'Client-IP' in the authentication call and as address in the XCLIENT call. Example config: mail { server_name mail.example.com; auth_http localhost:9000/; server { listen 143 proxy_protocol; protocol imap; } server { listen 25 proxy_protocol; protocol smtp; set_real_ip_from 127.0.0.0/8; set_real_ip_from ::/128; } } In the imap config, the source address given in the proxy protocol will never be used as Client-IP. In the smtp config, the source address given in the proxy protocol will only be used as XCLIENT address when the sender address matches the "set_real_ip_from" settings (in this case only loopback address). diff -r 863b862534d7 -r 327f18e079b1 auto/modules --- a/auto/modules Wed Jul 19 21:39:40 2017 +0800 +++ b/auto/modules Thu Jul 20 15:39:49 2017 +0000 @@ -954,6 +954,12 @@ ngx_module_srcs=src/mail/ngx_mail_proxy_module.c . auto/module + + ngx_module_name=ngx_mail_realip_module + ngx_module_deps= + ngx_module_srcs=src/mail/ngx_mail_realip_module.c + + . auto/module fi diff -r 863b862534d7 -r 327f18e079b1 src/mail/ngx_mail.c --- a/src/mail/ngx_mail.c Wed Jul 19 21:39:40 2017 +0800 +++ b/src/mail/ngx_mail.c Thu Jul 20 15:39:49 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 863b862534d7 -r 327f18e079b1 src/mail/ngx_mail.h --- a/src/mail/ngx_mail.h Wed Jul 19 21:39:40 2017 +0800 +++ b/src/mail/ngx_mail.h Thu Jul 20 15:39:49 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; @@ -54,7 +55,8 @@ typedef struct { ngx_mail_conf_ctx_t *ctx; ngx_str_t addr_text; - ngx_uint_t ssl; /* unsigned ssl:1; */ + 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; @@ -371,6 +375,8 @@ #endif +ngx_int_t ngx_mail_realip_handler(ngx_mail_session_t *s); + void ngx_mail_init_connection(ngx_connection_t *c); ngx_int_t ngx_mail_salt(ngx_mail_session_t *s, ngx_connection_t *c, diff -r 863b862534d7 -r 327f18e079b1 src/mail/ngx_mail_auth_http_module.c --- a/src/mail/ngx_mail_auth_http_module.c Wed Jul 19 21:39:40 2017 +0800 +++ b/src/mail/ngx_mail_auth_http_module.c Thu Jul 20 15:39:49 2017 +0000 @@ -1134,7 +1134,7 @@ { size_t len; ngx_buf_t *b; - ngx_str_t login, passwd; + ngx_str_t *client_addr, login, passwd; #if (NGX_MAIL_SSL) ngx_str_t verify, subject, issuer, serial, fingerprint, raw_cert, cert; @@ -1209,6 +1209,12 @@ cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); + if (ngx_mail_realip_handler(s) == NGX_OK) { + 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 + sizeof("Auth-Method: ") - 1 @@ -1221,8 +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(CRLF) - 1 + + 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 + sizeof("Auth-SMTP-From: ") - 1 + s->smtp_from.len + sizeof(CRLF) - 1 @@ -1240,6 +1245,11 @@ + ahcf->header.len + sizeof(CRLF) - 1; + if (s->connection->proxy_protocol_addr.len) { + len += sizeof("Proxy-Protocol-IP: ") - 1 + + s->connection->proxy_protocol_addr.len + sizeof(CRLF) - 1; + } + b = ngx_create_temp_buf(pool, len); if (b == NULL) { return NULL; @@ -1287,8 +1297,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) { @@ -1298,6 +1307,14 @@ *b->last++ = CR; *b->last++ = LF; } + if (s->connection->proxy_protocol_addr.len) { + b->last = ngx_cpymem(b->last, "Proxy-Protocol-IP: ", + sizeof("Proxy-Protocol-IP: ") - 1); + b->last = ngx_copy(b->last, s->connection->proxy_protocol_addr.data, + s->connection->proxy_protocol_addr.len); + *b->last++ = CR; *b->last++ = LF; + } + if (s->auth_method == NGX_MAIL_AUTH_NONE) { /* HELO, MAIL FROM, and RCPT TO can't contain CRLF, no need to escape */ diff -r 863b862534d7 -r 327f18e079b1 src/mail/ngx_mail_core_module.c --- a/src/mail/ngx_mail_core_module.c Wed Jul 19 21:39:40 2017 +0800 +++ b/src/mail/ngx_mail_core_module.c Thu Jul 20 15:39:49 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 863b862534d7 -r 327f18e079b1 src/mail/ngx_mail_handler.c --- a/src/mail/ngx_mail_handler.c Wed Jul 19 21:39:40 2017 +0800 +++ b/src/mail/ngx_mail_handler.c Thu Jul 20 15:39:49 2017 +0000 @@ -12,6 +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); @@ -143,6 +144,8 @@ 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 +168,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 +181,97 @@ 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], + text[NGX_SOCKADDR_STRLEN]; + size_t size, len; + ssize_t n; + ngx_err_t err; + ngx_connection_t *c; + ngx_mail_session_t *s; + ngx_mail_log_ctx_t *ctx; + + 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; + + ctx = c->log->data; + ctx->client = &c->proxy_protocol_addr; + + len = ngx_sock_ntop(c->sockaddr, c->socklen, text, NGX_SOCKADDR_STRLEN, 1); + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "*%uA client %*s proxy-protocol %*s:%d", + c->number, len, text, + c->proxy_protocol_addr.len, c->proxy_protocol_addr.data, + c->proxy_protocol_port); + +#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 +306,21 @@ 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) { + 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 +328,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; diff -r 863b862534d7 -r 327f18e079b1 src/mail/ngx_mail_proxy_module.c --- a/src/mail/ngx_mail_proxy_module.c Wed Jul 19 21:39:40 2017 +0800 +++ b/src/mail/ngx_mail_proxy_module.c Thu Jul 20 15:39:49 2017 +0000 @@ -450,7 +450,7 @@ { u_char *p; ngx_int_t rc; - ngx_str_t line; + ngx_str_t *client_addr, line; ngx_buf_t *b; ngx_connection_t *c; ngx_mail_session_t *s; @@ -523,11 +523,17 @@ ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, "mail proxy send xclient"); + if (ngx_mail_realip_handler(s) == NGX_OK) { + client_addr = &s->connection->proxy_protocol_addr; + } else { + client_addr = &s->connection->addr_text; + } + 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->len + s->login.len + s->host.len; #if (NGX_HAVE_INET6) if (s->connection->sockaddr->sa_family == AF_INET6) { @@ -549,8 +555,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); diff -r 863b862534d7 -r 327f18e079b1 src/mail/ngx_mail_realip_module.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/mail/ngx_mail_realip_module.c Thu Jul 20 15:39:49 2017 +0000 @@ -0,0 +1,232 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include <ngx_config.h> +#include <ngx_core.h> +#include <ngx_mail.h> + + +typedef struct { + ngx_array_t *from; /* array of ngx_cidr_t */ +} ngx_mail_realip_srv_conf_t; + + +typedef struct { + struct sockaddr *sockaddr; + socklen_t socklen; + ngx_str_t addr_text; +} ngx_mail_realip_ctx_t; + + +static char *ngx_mail_realip_from(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static void *ngx_mail_realip_create_srv_conf(ngx_conf_t *cf); +static char *ngx_mail_realip_merge_srv_conf(ngx_conf_t *cf, void *parent, + void *child); + + +static ngx_command_t ngx_mail_realip_commands[] = { + + { ngx_string("set_realip_from"), + NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1, + ngx_mail_realip_from, + NGX_MAIL_SRV_CONF_OFFSET, + 0, + NULL }, + + ngx_null_command +}; + + +static ngx_mail_module_t ngx_mail_realip_module_ctx = { + NULL, /* protocol */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + ngx_mail_realip_create_srv_conf, /* create server configuration */ + ngx_mail_realip_merge_srv_conf /* merge server configuration */ +}; + + +ngx_module_t ngx_mail_realip_module = { + NGX_MODULE_V1, + &ngx_mail_realip_module_ctx, /* module context */ + ngx_mail_realip_commands, /* module directives */ + NGX_MAIL_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ + NULL, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NGX_MODULE_V1_PADDING +}; + + +ngx_int_t +ngx_mail_realip_handler(ngx_mail_session_t *s) +{ + ngx_connection_t *c; + ngx_mail_realip_srv_conf_t *rscf; + + rscf = ngx_mail_get_module_srv_conf(s, ngx_mail_realip_module); + + if (rscf->from == NULL) { + return NGX_DECLINED; + } + + c = s->connection; + + if (c->proxy_protocol_addr.len == 0) { + return NGX_DECLINED; + } + + if (ngx_cidr_match(c->sockaddr, rscf->from) != NGX_OK) { + return NGX_DECLINED; + } + + return NGX_OK; +} + + +static char * +ngx_mail_realip_from(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_mail_realip_srv_conf_t *rscf = conf; + + ngx_int_t rc; + ngx_str_t *value; + ngx_url_t u; + ngx_cidr_t c, *cidr; + ngx_uint_t i; + struct sockaddr_in *sin; +#if (NGX_HAVE_INET6) + struct sockaddr_in6 *sin6; +#endif + + value = cf->args->elts; + + if (rscf->from == NULL) { + rscf->from = ngx_array_create(cf->pool, 2, + sizeof(ngx_cidr_t)); + if (rscf->from == NULL) { + return NGX_CONF_ERROR; + } + } + +#if (NGX_HAVE_UNIX_DOMAIN) + + if (ngx_strcmp(value[1].data, "unix:") == 0) { + cidr = ngx_array_push(rscf->from); + if (cidr == NULL) { + return NGX_CONF_ERROR; + } + + cidr->family = AF_UNIX; + return NGX_CONF_OK; + } + +#endif + + rc = ngx_ptocidr(&value[1], &c); + + if (rc != NGX_ERROR) { + if (rc == NGX_DONE) { + ngx_conf_log_error(NGX_LOG_WARN, cf, 0, + "low address bits of %V are meaningless", + &value[1]); + } + + cidr = ngx_array_push(rscf->from); + if (cidr == NULL) { + return NGX_CONF_ERROR; + } + + *cidr = c; + + return NGX_CONF_OK; + } + + ngx_memzero(&u, sizeof(ngx_url_t)); + u.host = value[1]; + + if (ngx_inet_resolve_host(cf->pool, &u) != NGX_OK) { + if (u.err) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "%s in set_real_ip_from \"%V\"", + u.err, &u.host); + } + + return NGX_CONF_ERROR; + } + + cidr = ngx_array_push_n(rscf->from, u.naddrs); + if (cidr == NULL) { + return NGX_CONF_ERROR; + } + + ngx_memzero(cidr, u.naddrs * sizeof(ngx_cidr_t)); + + for (i = 0; i < u.naddrs; i++) { + cidr[i].family = u.addrs[i].sockaddr->sa_family; + + switch (cidr[i].family) { + +#if (NGX_HAVE_INET6) + case AF_INET6: + sin6 = (struct sockaddr_in6 *) u.addrs[i].sockaddr; + cidr[i].u.in6.addr = sin6->sin6_addr; + ngx_memset(cidr[i].u.in6.mask.s6_addr, 0xff, 16); + break; +#endif + + default: /* AF_INET */ + sin = (struct sockaddr_in *) u.addrs[i].sockaddr; + cidr[i].u.in.addr = sin->sin_addr.s_addr; + cidr[i].u.in.mask = 0xffffffff; + break; + } + } + + return NGX_CONF_OK; +} + + +static void * +ngx_mail_realip_create_srv_conf(ngx_conf_t *cf) +{ + ngx_mail_realip_srv_conf_t *conf; + + conf = ngx_pcalloc(cf->pool, sizeof(ngx_mail_realip_srv_conf_t)); + if (conf == NULL) { + return NULL; + } + + /* + * set by ngx_pcalloc(): + * + * conf->from = NULL; + */ + + return conf; +} + + +static char * +ngx_mail_realip_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) +{ + ngx_mail_realip_srv_conf_t *prev = parent; + ngx_mail_realip_srv_conf_t *conf = child; + + if (conf->from == NULL) { + conf->from = prev->from; + } + + return NGX_CONF_OK; +} _______________________________________________ nginx-devel mailing list nginx-devel@nginx.org http://mailman.nginx.org/mailman/listinfo/nginx-devel