# HG changeset patch # User Roman Arutyunyan # Date 1395063684 -14400 # Node ID 3a72b1805c52c84a5567dcfa4562f44722bede40 # Parent 22d485944c2097fc4d281a741c8bcb43a549a654 Added server-side support for PROXY protocol v1 (ticket #355). Client address specified in the PROXY protocol header is now saved in the $proxy_protocol_addr variable and can be used in the realip module. This is currently not implemented for mail. diff --git a/auto/sources b/auto/sources --- a/auto/sources +++ b/auto/sources @@ -36,7 +36,8 @@ CORE_DEPS="src/core/nginx.h \ src/core/ngx_conf_file.h \ src/core/ngx_resolver.h \ src/core/ngx_open_file_cache.h \ - src/core/ngx_crypt.h" + src/core/ngx_crypt.h \ + src/core/ngx_proxy_protocol.h" CORE_SRCS="src/core/nginx.c \ @@ -67,7 +68,8 @@ CORE_SRCS="src/core/nginx.c \ src/core/ngx_conf_file.c \ src/core/ngx_resolver.c \ src/core/ngx_open_file_cache.c \ - src/core/ngx_crypt.c" + src/core/ngx_crypt.c \ + src/core/ngx_proxy_protocol.c" REGEX_MODULE=ngx_regex_module diff --git a/src/core/ngx_connection.h b/src/core/ngx_connection.h --- a/src/core/ngx_connection.h +++ b/src/core/ngx_connection.h @@ -139,6 +139,8 @@ struct ngx_connection_s { socklen_t socklen; ngx_str_t addr_text; + ngx_str_t proxy_protocol_addr; + #if (NGX_SSL) ngx_ssl_connection_t *ssl; #endif diff --git a/src/core/ngx_core.h b/src/core/ngx_core.h --- a/src/core/ngx_core.h +++ b/src/core/ngx_core.h @@ -77,6 +77,7 @@ typedef void (*ngx_connection_handler_pt #include #include #include +#include #define LF (u_char) 10 diff --git a/src/core/ngx_proxy_protocol.c b/src/core/ngx_proxy_protocol.c new file mode 100644 --- /dev/null +++ b/src/core/ngx_proxy_protocol.c @@ -0,0 +1,91 @@ + +/* + * Copyright (C) Roman Arutyunyan + * Copyright (C) Nginx, Inc. + */ + + +#include +#include + + +u_char * +ngx_proxy_protocol_parse(ngx_connection_t *c, u_char *buf, u_char *last) +{ + size_t len; + u_char ch, *p, *addr; + + p = buf; + len = last - buf; + + if (len < 8 || ngx_strncmp(p, "PROXY ", 6) != 0) { + goto invalid; + } + + p += 6; + len -= 6; + + if (len >= 7 && ngx_strncmp(p, "UNKNOWN", 7) == 0) { + ngx_log_debug0(NGX_LOG_DEBUG_CORE, c->log, 0, + "PROXY protocol unknown protocol"); + p += 7; + goto skip; + } + + if (len < 5 || ngx_strncmp(p, "TCP", 3) != 0 + || (p[3] != '4' && p[3] != '6') || p[4] != ' ') + { + goto invalid; + } + + p += 5; + addr = p; + + for ( ;; ) { + if (p == last) { + goto invalid; + } + + ch = *p++; + + if (ch == ' ') { + break; + } + + if (ch != ':' && ch != '.' + && (ch < 'a' || ch > 'f') + && (ch < 'A' || ch > 'F') + && (ch < '0' || ch > '9')) + { + goto invalid; + } + } + + len = p - addr - 1; + c->proxy_protocol_addr.data = ngx_pnalloc(c->pool, len); + + if (c->proxy_protocol_addr.data == NULL) { + return NULL; + } + + ngx_memcpy(c->proxy_protocol_addr.data, addr, len); + c->proxy_protocol_addr.len = len; + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0, + "PROXY protocol address: \"%V\"", &c->proxy_protocol_addr); + +skip: + + for ( /* void */ ; p < last - 1; p++) { + if (p[0] == CR && p[1] == LF) { + return p + 2; + } + } + +invalid: + + ngx_log_error(NGX_LOG_ERR, c->log, 0, + "broken header: \"%*s\"", (size_t) (last - buf), buf); + + return NULL; +} diff --git a/src/core/ngx_proxy_protocol.h b/src/core/ngx_proxy_protocol.h new file mode 100644 --- /dev/null +++ b/src/core/ngx_proxy_protocol.h @@ -0,0 +1,23 @@ + +/* + * Copyright (C) Roman Arutyunyan + * Copyright (C) Nginx, Inc. + */ + + +#ifndef _NGX_PROXY_PROTOCOL_H_INCLUDED_ +#define _NGX_PROXY_PROTOCOL_H_INCLUDED_ + + +#include +#include + + +#define NGX_PROXY_PROTOCOL_MAX_HEADER 107 + + +u_char *ngx_proxy_protocol_parse(ngx_connection_t *c, u_char *buf, + u_char *last); + + +#endif /* _NGX_PROXY_PROTOCOL_H_INCLUDED_ */ diff --git a/src/http/modules/ngx_http_realip_module.c b/src/http/modules/ngx_http_realip_module.c --- a/src/http/modules/ngx_http_realip_module.c +++ b/src/http/modules/ngx_http_realip_module.c @@ -13,6 +13,7 @@ #define NGX_HTTP_REALIP_XREALIP 0 #define NGX_HTTP_REALIP_XFWD 1 #define NGX_HTTP_REALIP_HEADER 2 +#define NGX_HTTP_REALIP_PROXY 3 typedef struct { @@ -156,6 +157,18 @@ ngx_http_realip_handler(ngx_http_request break; + case NGX_HTTP_REALIP_PROXY: + + value = &r->connection->proxy_protocol_addr; + + if (value->len == 0) { + return NGX_DECLINED; + } + + xfwd = NULL; + + break; + default: /* NGX_HTTP_REALIP_HEADER */ part = &r->headers_in.headers.part; @@ -343,6 +356,11 @@ ngx_http_realip(ngx_conf_t *cf, ngx_comm return NGX_CONF_OK; } + if (ngx_strcmp(value[1].data, "proxy_protocol") == 0) { + rlcf->type = NGX_HTTP_REALIP_PROXY; + return NGX_CONF_OK; + } + rlcf->type = NGX_HTTP_REALIP_HEADER; rlcf->hash = ngx_hash_strlow(value[1].data, value[1].data, value[1].len); rlcf->header = value[1]; diff --git a/src/http/ngx_http.c b/src/http/ngx_http.c --- a/src/http/ngx_http.c +++ b/src/http/ngx_http.c @@ -1849,6 +1849,7 @@ ngx_http_add_addrs(ngx_conf_t *cf, ngx_h #if (NGX_HTTP_SPDY) addrs[i].conf.spdy = addr[i].opt.spdy; #endif + addrs[i].conf.proxy_protocol = addr[i].opt.proxy_protocol; if (addr[i].hash.buckets == NULL && (addr[i].wc_head == NULL diff --git a/src/http/ngx_http_core_module.c b/src/http/ngx_http_core_module.c --- a/src/http/ngx_http_core_module.c +++ b/src/http/ngx_http_core_module.c @@ -4287,6 +4287,11 @@ ngx_http_core_listen(ngx_conf_t *cf, ngx #endif } + if (ngx_strcmp(value[n].data, "proxy_protocol") == 0) { + lsopt.proxy_protocol = 1; + continue; + } + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid parameter \"%V\"", &value[n]); return NGX_CONF_ERROR; diff --git a/src/http/ngx_http_core_module.h b/src/http/ngx_http_core_module.h --- a/src/http/ngx_http_core_module.h +++ b/src/http/ngx_http_core_module.h @@ -82,6 +82,7 @@ typedef struct { unsigned ipv6only:1; #endif unsigned so_keepalive:2; + unsigned proxy_protocol:1; int backlog; int rcvbuf; @@ -243,6 +244,7 @@ struct ngx_http_addr_conf_s { #if (NGX_HTTP_SPDY) unsigned spdy:1; #endif + unsigned proxy_protocol:1; }; diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -343,6 +343,11 @@ ngx_http_init_connection(ngx_connection_ } #endif + if (hc->addr_conf->proxy_protocol) { + hc->proxy_protocol = 1; + c->log->action = "reading PROXY protocol"; + } + if (rev->ready) { /* the deferred accept(), rtsig, aio, iocp */ @@ -368,6 +373,7 @@ ngx_http_init_connection(ngx_connection_ static void ngx_http_wait_request_handler(ngx_event_t *rev) { + u_char *p; size_t size; ssize_t n; ngx_buf_t *b; @@ -458,6 +464,27 @@ ngx_http_wait_request_handler(ngx_event_ b->last += n; + if (hc->proxy_protocol) { + hc->proxy_protocol = 0; + + p = ngx_proxy_protocol_parse(c, b->pos, b->last); + + if (p == NULL) { + ngx_http_close_connection(c); + return; + } + + b->pos = p; + + if (b->pos == b->last) { + c->log->action = "waiting for request"; + b->pos = b->start; + b->last = b->start; + ngx_post_event(rev, &ngx_posted_events); + return; + } + } + c->log->action = "reading client request line"; ngx_reusable_connection(c, 0); @@ -589,7 +616,8 @@ ngx_http_create_request(ngx_connection_t static void ngx_http_ssl_handshake(ngx_event_t *rev) { - u_char buf[1]; + u_char *p, buf[NGX_PROXY_PROTOCOL_MAX_HEADER + 1]; + size_t size; ssize_t n; ngx_err_t err; ngx_int_t rc; @@ -598,6 +626,7 @@ ngx_http_ssl_handshake(ngx_event_t *rev) ngx_http_ssl_srv_conf_t *sscf; c = rev->data; + hc = c->data; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, "http check ssl handshake"); @@ -613,7 +642,9 @@ ngx_http_ssl_handshake(ngx_event_t *rev) return; } - n = recv(c->fd, (char *) buf, 1, MSG_PEEK); + size = hc->proxy_protocol ? sizeof(buf) : 1; + + n = recv(c->fd, (char *) buf, size, MSG_PEEK); err = ngx_socket_errno; @@ -640,12 +671,39 @@ ngx_http_ssl_handshake(ngx_event_t *rev) return; } + if (hc->proxy_protocol) { + hc->proxy_protocol = 0; + + p = ngx_proxy_protocol_parse(c, buf, buf + n); + + if (p == NULL) { + ngx_http_close_connection(c); + return; + } + + size = p - buf; + + if (c->recv(c, buf, size) != (ssize_t) size) { + ngx_http_close_connection(c); + return; + } + + c->log->action = "SSL handshaking"; + + if (n == (ssize_t) size) { + ngx_post_event(rev, &ngx_posted_events); + return; + } + + n = 1; + buf[0] = *p; + } + if (n == 1) { if (buf[0] & 0x80 /* SSLv2 */ || buf[0] == 0x16 /* SSLv3/TLSv1 */) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, rev->log, 0, "https ssl handshake: 0x%02Xd", buf[0]); - hc = c->data; sscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_ssl_module); diff --git a/src/http/ngx_http_request.h b/src/http/ngx_http_request.h --- a/src/http/ngx_http_request.h +++ b/src/http/ngx_http_request.h @@ -309,8 +309,9 @@ typedef struct { ngx_int_t nfree; #if (NGX_HTTP_SSL) - ngx_uint_t ssl; /* unsigned ssl:1; */ + unsigned ssl:1; #endif + unsigned proxy_protocol:1; } ngx_http_connection_t; diff --git a/src/http/ngx_http_spdy.c b/src/http/ngx_http_spdy.c --- a/src/http/ngx_http_spdy.c +++ b/src/http/ngx_http_spdy.c @@ -95,6 +95,8 @@ static void ngx_http_spdy_read_handler(n static void ngx_http_spdy_write_handler(ngx_event_t *wev); static void ngx_http_spdy_handle_connection(ngx_http_spdy_connection_t *sc); +static u_char *ngx_http_spdy_proxy_protocol(ngx_http_spdy_connection_t *sc, + u_char *pos, u_char *end); static u_char *ngx_http_spdy_state_head(ngx_http_spdy_connection_t *sc, u_char *pos, u_char *end); static u_char *ngx_http_spdy_state_syn_stream(ngx_http_spdy_connection_t *sc, @@ -421,6 +423,11 @@ ngx_http_spdy_init(ngx_event_t *rev) sc->handler = ngx_http_spdy_state_head; + if (hc->proxy_protocol) { + c->log->action = "reading PROXY protocol"; + sc->handler = ngx_http_spdy_proxy_protocol; + } + sc->zstream_in.zalloc = ngx_http_spdy_zalloc; sc->zstream_in.zfree = ngx_http_spdy_zfree; sc->zstream_in.opaque = sc; @@ -810,6 +817,22 @@ ngx_http_spdy_handle_connection(ngx_http static u_char * +ngx_http_spdy_proxy_protocol(ngx_http_spdy_connection_t *sc, u_char *pos, + u_char *end) +{ + pos = ngx_proxy_protocol_parse(sc->connection, pos, end); + + if (pos == NULL) { + return ngx_http_spdy_state_protocol_error(sc); + } + + sc->connection->log->action = "processing SPDY"; + + return ngx_http_spdy_state_complete(sc, pos, end); +} + + +static u_char * ngx_http_spdy_state_head(ngx_http_spdy_connection_t *sc, u_char *pos, u_char *end) { diff --git a/src/http/ngx_http_variables.c b/src/http/ngx_http_variables.c --- a/src/http/ngx_http_variables.c +++ b/src/http/ngx_http_variables.c @@ -54,6 +54,8 @@ static ngx_int_t ngx_http_variable_remot ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_variable_remote_port(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_proxy_protocol_addr(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_variable_server_addr(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_variable_server_port(ngx_http_request_t *r, @@ -183,6 +185,9 @@ static ngx_http_variable_t ngx_http_cor { ngx_string("remote_port"), NULL, ngx_http_variable_remote_port, 0, 0, 0 }, + { ngx_string("proxy_protocol_addr"), NULL, + ngx_http_variable_proxy_protocol_addr, 0, 0, 0 }, + { ngx_string("server_addr"), NULL, ngx_http_variable_server_addr, 0, 0, 0 }, { ngx_string("server_port"), NULL, ngx_http_variable_server_port, 0, 0, 0 }, @@ -1206,6 +1211,20 @@ ngx_http_variable_remote_port(ngx_http_r static ngx_int_t +ngx_http_variable_proxy_protocol_addr(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data) +{ + v->len = r->connection->proxy_protocol_addr.len; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = r->connection->proxy_protocol_addr.data; + + return NGX_OK; +} + + +static ngx_int_t ngx_http_variable_server_addr(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) {