Stream: client-side PROXY protocol.

The new directive "proxy_protocol" toggles sending out PROXY protocol header
to upstream once connection is established.
This commit is contained in:
Roman Arutyunyan 2015-06-16 13:45:16 +03:00
parent f50f83a2cf
commit 3ed1b3b5b0
4 changed files with 170 additions and 2 deletions

View File

@ -89,3 +89,52 @@ invalid:
return NULL; return NULL;
} }
u_char *
ngx_proxy_protocol_write(ngx_connection_t *c, u_char *buf, u_char *last)
{
ngx_uint_t port, lport;
if (last - buf < NGX_PROXY_PROTOCOL_MAX_HEADER) {
return NULL;
}
if (ngx_connection_local_sockaddr(c, NULL, 0) != NGX_OK) {
return NULL;
}
switch (c->sockaddr->sa_family) {
case AF_INET:
buf = ngx_cpymem(buf, "PROXY TCP4 ", sizeof("PROXY TCP4 ") - 1);
port = ntohs(((struct sockaddr_in *) c->sockaddr)->sin_port);
lport = ntohs(((struct sockaddr_in *) c->local_sockaddr)->sin_port);
break;
#if (NGX_HAVE_INET6)
case AF_INET6:
buf = ngx_cpymem(buf, "PROXY TCP6 ", sizeof("PROXY TCP6 ") - 1);
port = ntohs(((struct sockaddr_in6 *) c->sockaddr)->sin6_port);
lport = ntohs(((struct sockaddr_in6 *) c->local_sockaddr)->sin6_port);
break;
#endif
default:
return ngx_cpymem(buf, "PROXY UNKNOWN" CRLF,
sizeof("PROXY UNKNOWN" CRLF) - 1);
}
buf += ngx_sock_ntop(c->sockaddr, c->socklen, buf, last - buf, 0);
*buf++ = ' ';
buf += ngx_sock_ntop(c->local_sockaddr, c->local_socklen, buf, last - buf,
0);
return ngx_slprintf(buf, last, " %ui %ui" CRLF, port, lport);
}

View File

@ -18,6 +18,8 @@
u_char *ngx_proxy_protocol_parse(ngx_connection_t *c, u_char *buf, u_char *ngx_proxy_protocol_parse(ngx_connection_t *c, u_char *buf,
u_char *last); u_char *last);
u_char *ngx_proxy_protocol_write(ngx_connection_t *c, u_char *buf,
u_char *last);
#endif /* _NGX_PROXY_PROTOCOL_H_INCLUDED_ */ #endif /* _NGX_PROXY_PROTOCOL_H_INCLUDED_ */

View File

@ -21,6 +21,7 @@ typedef struct {
size_t upstream_buf_size; size_t upstream_buf_size;
ngx_uint_t next_upstream_tries; ngx_uint_t next_upstream_tries;
ngx_flag_t next_upstream; ngx_flag_t next_upstream;
ngx_flag_t proxy_protocol;
ngx_addr_t *local; ngx_addr_t *local;
#if (NGX_STREAM_SSL) #if (NGX_STREAM_SSL)
@ -67,6 +68,7 @@ static char *ngx_stream_proxy_pass(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf); void *conf);
static char *ngx_stream_proxy_bind(ngx_conf_t *cf, ngx_command_t *cmd, static char *ngx_stream_proxy_bind(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf); void *conf);
static ngx_int_t ngx_stream_proxy_send_proxy_protocol(ngx_stream_session_t *s);
#if (NGX_STREAM_SSL) #if (NGX_STREAM_SSL)
@ -156,6 +158,13 @@ static ngx_command_t ngx_stream_proxy_commands[] = {
offsetof(ngx_stream_proxy_srv_conf_t, next_upstream_timeout), offsetof(ngx_stream_proxy_srv_conf_t, next_upstream_timeout),
NULL }, NULL },
{ ngx_string("proxy_protocol"),
NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
NGX_STREAM_SRV_CONF_OFFSET,
offsetof(ngx_stream_proxy_srv_conf_t, proxy_protocol),
NULL },
#if (NGX_STREAM_SSL) #if (NGX_STREAM_SSL)
{ ngx_string("proxy_ssl"), { ngx_string("proxy_ssl"),
@ -328,6 +337,8 @@ ngx_stream_proxy_handler(ngx_stream_session_t *s)
u->peer.tries = pscf->next_upstream_tries; u->peer.tries = pscf->next_upstream_tries;
} }
u->proxy_protocol = pscf->proxy_protocol;
p = ngx_pnalloc(c->pool, pscf->downstream_buf_size); p = ngx_pnalloc(c->pool, pscf->downstream_buf_size);
if (p == NULL) { if (p == NULL) {
ngx_stream_proxy_finalize(s, NGX_ERROR); ngx_stream_proxy_finalize(s, NGX_ERROR);
@ -342,6 +353,29 @@ ngx_stream_proxy_handler(ngx_stream_session_t *s)
c->write->handler = ngx_stream_proxy_downstream_handler; c->write->handler = ngx_stream_proxy_downstream_handler;
c->read->handler = ngx_stream_proxy_downstream_handler; c->read->handler = ngx_stream_proxy_downstream_handler;
if (u->proxy_protocol
#if (NGX_STREAM_SSL)
&& pscf->ssl == NULL
#endif
&& pscf->downstream_buf_size >= NGX_PROXY_PROTOCOL_MAX_HEADER
)
{
/* optimization for a typical case */
ngx_log_debug0(NGX_LOG_DEBUG_STREAM, c->log, 0,
"stream proxy send PROXY protocol header");
p = ngx_proxy_protocol_write(c, u->downstream_buf.last,
u->downstream_buf.end);
if (p == NULL) {
ngx_stream_proxy_finalize(s, NGX_ERROR);
return;
}
u->downstream_buf.last = p;
u->proxy_protocol = 0;
}
if (ngx_stream_proxy_process(s, 0, 0) != NGX_OK) { if (ngx_stream_proxy_process(s, 0, 0) != NGX_OK) {
return; return;
} }
@ -417,10 +451,18 @@ ngx_stream_proxy_init_upstream(ngx_stream_session_t *s)
ngx_stream_upstream_t *u; ngx_stream_upstream_t *u;
ngx_stream_proxy_srv_conf_t *pscf; ngx_stream_proxy_srv_conf_t *pscf;
pscf = ngx_stream_get_module_srv_conf(s, ngx_stream_proxy_module);
u = s->upstream; u = s->upstream;
if (u->proxy_protocol) {
if (ngx_stream_proxy_send_proxy_protocol(s) != NGX_OK) {
return;
}
u->proxy_protocol = 0;
}
pscf = ngx_stream_get_module_srv_conf(s, ngx_stream_proxy_module);
pc = u->peer.connection; pc = u->peer.connection;
#if (NGX_STREAM_SSL) #if (NGX_STREAM_SSL)
@ -474,6 +516,76 @@ ngx_stream_proxy_init_upstream(ngx_stream_session_t *s)
} }
static ngx_int_t
ngx_stream_proxy_send_proxy_protocol(ngx_stream_session_t *s)
{
u_char *p;
ssize_t n, size;
ngx_connection_t *c, *pc;
ngx_stream_upstream_t *u;
ngx_stream_proxy_srv_conf_t *pscf;
u_char buf[NGX_PROXY_PROTOCOL_MAX_HEADER];
c = s->connection;
ngx_log_debug0(NGX_LOG_DEBUG_STREAM, c->log, 0,
"stream proxy send PROXY protocol header");
p = ngx_proxy_protocol_write(c, buf, buf + NGX_PROXY_PROTOCOL_MAX_HEADER);
if (p == NULL) {
ngx_stream_proxy_finalize(s, NGX_ERROR);
return NGX_ERROR;
}
u = s->upstream;
pc = u->peer.connection;
size = p - buf;
n = pc->send(pc, buf, size);
if (n == NGX_AGAIN) {
if (ngx_handle_write_event(pc->write, 0) != NGX_OK) {
ngx_stream_proxy_finalize(s, NGX_ERROR);
return NGX_ERROR;
}
pscf = ngx_stream_get_module_srv_conf(s, ngx_stream_proxy_module);
ngx_add_timer(pc->write, pscf->timeout);
pc->write->handler = ngx_stream_proxy_connect_handler;
return NGX_AGAIN;
}
if (n == NGX_ERROR) {
ngx_stream_proxy_finalize(s, NGX_DECLINED);
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, c->log, 0,
"could not send PROXY protocol header at once");
ngx_stream_proxy_finalize(s, NGX_DECLINED);
return NGX_ERROR;
}
return NGX_OK;
}
#if (NGX_STREAM_SSL) #if (NGX_STREAM_SSL)
static char * static char *
@ -1105,6 +1217,7 @@ ngx_stream_proxy_create_srv_conf(ngx_conf_t *cf)
conf->upstream_buf_size = NGX_CONF_UNSET_SIZE; conf->upstream_buf_size = NGX_CONF_UNSET_SIZE;
conf->next_upstream_tries = NGX_CONF_UNSET_UINT; conf->next_upstream_tries = NGX_CONF_UNSET_UINT;
conf->next_upstream = NGX_CONF_UNSET; conf->next_upstream = NGX_CONF_UNSET;
conf->proxy_protocol = NGX_CONF_UNSET;
conf->local = NGX_CONF_UNSET_PTR; conf->local = NGX_CONF_UNSET_PTR;
#if (NGX_STREAM_SSL) #if (NGX_STREAM_SSL)
@ -1146,6 +1259,8 @@ ngx_stream_proxy_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
ngx_conf_merge_value(conf->next_upstream, prev->next_upstream, 1); ngx_conf_merge_value(conf->next_upstream, prev->next_upstream, 1);
ngx_conf_merge_value(conf->proxy_protocol, prev->proxy_protocol, 0);
ngx_conf_merge_ptr_value(conf->local, prev->local, NULL); ngx_conf_merge_ptr_value(conf->local, prev->local, NULL);
#if (NGX_STREAM_SSL) #if (NGX_STREAM_SSL)

View File

@ -86,6 +86,8 @@ typedef struct {
#if (NGX_STREAM_SSL) #if (NGX_STREAM_SSL)
ngx_str_t ssl_name; ngx_str_t ssl_name;
#endif #endif
ngx_uint_t proxy_protocol;
/* unsigned proxy_protocol:1; */
} ngx_stream_upstream_t; } ngx_stream_upstream_t;