[nginx] Upstream: the "transparent" parameter of proxy_bind and friends.
Ruslan Ermilov
ru at nginx.com
Tue Apr 26 15:59:57 UTC 2016
details: http://hg.nginx.org/nginx/rev/1d0e03db9f8e
branches:
changeset: 6530:1d0e03db9f8e
user: Roman Arutyunyan <arut at nginx.com>
date: Fri Dec 18 19:05:27 2015 +0300
description:
Upstream: the "transparent" parameter of proxy_bind and friends.
This parameter lets binding the proxy connection to a non-local address.
Upstream will see the connection as coming from that address.
When used with $remote_addr, upstream will accept the connection from real
client address.
Example:
proxy_bind $remote_addr transparent;
diffstat:
auto/unix | 38 +++++++++
src/event/ngx_event_connect.c | 103 ++++++++++++++++++++++++++
src/event/ngx_event_connect.h | 3 +
src/http/modules/ngx_http_fastcgi_module.c | 2 +-
src/http/modules/ngx_http_memcached_module.c | 2 +-
src/http/modules/ngx_http_proxy_module.c | 2 +-
src/http/modules/ngx_http_scgi_module.c | 2 +-
src/http/modules/ngx_http_uwsgi_module.c | 2 +-
src/http/ngx_http_upstream.c | 22 +++++-
src/http/ngx_http_upstream.h | 3 +
src/stream/ngx_stream_proxy_module.c | 105 ++++++++++++++++++++++----
11 files changed, 260 insertions(+), 24 deletions(-)
diffs (463 lines):
diff -r cb8177ca0990 -r 1d0e03db9f8e auto/unix
--- a/auto/unix Wed Apr 13 15:42:47 2016 +0300
+++ b/auto/unix Fri Dec 18 19:05:27 2015 +0300
@@ -329,6 +329,44 @@ ngx_feature_test="setsockopt(0, SOL_SOCK
. auto/feature
+# NetBSD bind to any address for transparent proxying
+
+ngx_feature="SO_BINDANY"
+ngx_feature_name="NGX_HAVE_TRANSPARENT_PROXY"
+ngx_feature_run=no
+ngx_feature_incs="#include <sys/socket.h>"
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="setsockopt(0, SOL_SOCKET, SO_BINDANY, NULL, 0)"
+. auto/feature
+
+
+# Linux transparent proxying
+
+ngx_feature="IP_TRANSPARENT"
+ngx_feature_name="NGX_HAVE_TRANSPARENT_PROXY"
+ngx_feature_run=no
+ngx_feature_incs="#include <sys/socket.h>
+ #include <netinet/in.h>"
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="setsockopt(0, IPPROTO_IP, IP_TRANSPARENT, NULL, 0)"
+. auto/feature
+
+
+# FreeBSD bind to any address for transparent proxying
+
+ngx_feature="IP_BINDANY"
+ngx_feature_name="NGX_HAVE_TRANSPARENT_PROXY"
+ngx_feature_run=no
+ngx_feature_incs="#include <sys/socket.h>
+ #include <netinet/in.h>"
+ngx_feature_path=
+ngx_feature_libs=
+ngx_feature_test="setsockopt(0, IPPROTO_IP, IP_BINDANY, NULL, 0)"
+. auto/feature
+
+
# BSD way to get IPv4 datagram destination address
ngx_feature="IP_RECVDSTADDR"
diff -r cb8177ca0990 -r 1d0e03db9f8e src/event/ngx_event_connect.c
--- a/src/event/ngx_event_connect.c Wed Apr 13 15:42:47 2016 +0300
+++ b/src/event/ngx_event_connect.c Fri Dec 18 19:05:27 2015 +0300
@@ -11,6 +11,12 @@
#include <ngx_event_connect.h>
+#if (NGX_HAVE_TRANSPARENT_PROXY)
+static ngx_int_t ngx_event_connect_set_transparent(ngx_peer_connection_t *pc,
+ ngx_socket_t s);
+#endif
+
+
ngx_int_t
ngx_event_connect_peer(ngx_peer_connection_t *pc)
{
@@ -72,6 +78,15 @@ ngx_event_connect_peer(ngx_peer_connecti
}
if (pc->local) {
+
+#if (NGX_HAVE_TRANSPARENT_PROXY)
+ if (pc->transparent) {
+ if (ngx_event_connect_set_transparent(pc, s) != NGX_OK) {
+ goto failed;
+ }
+ }
+#endif
+
if (bind(s, pc->local->sockaddr, pc->local->socklen) == -1) {
ngx_log_error(NGX_LOG_CRIT, pc->log, ngx_socket_errno,
"bind(%V) failed", &pc->local->name);
@@ -249,6 +264,94 @@ failed:
}
+#if (NGX_HAVE_TRANSPARENT_PROXY)
+
+static ngx_int_t
+ngx_event_connect_set_transparent(ngx_peer_connection_t *pc, ngx_socket_t s)
+{
+ int value;
+
+ value = 1;
+
+#if defined(SO_BINDANY)
+
+ if (setsockopt(s, SOL_SOCKET, SO_BINDANY,
+ (const void *) &value, sizeof(int)) == -1)
+ {
+ ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno,
+ "setsockopt(SO_BINDANY) failed");
+ return NGX_ERROR;
+ }
+
+#else
+
+ switch (pc->local->sockaddr->sa_family) {
+
+ case AF_INET:
+
+#if defined(IP_TRANSPARENT)
+
+ if (setsockopt(s, IPPROTO_IP, IP_TRANSPARENT,
+ (const void *) &value, sizeof(int)) == -1)
+ {
+ ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno,
+ "setsockopt(IP_TRANSPARENT) failed");
+ return NGX_ERROR;
+ }
+
+#elif defined(IP_BINDANY)
+
+ if (setsockopt(s, IPPROTO_IP, IP_BINDANY,
+ (const void *) &value, sizeof(int)) == -1)
+ {
+ ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno,
+ "setsockopt(IP_BINDANY) failed");
+ return NGX_ERROR;
+ }
+
+#endif
+
+ break;
+
+#if (NGX_HAVE_INET6)
+
+ case AF_INET6:
+
+#if defined(IPV6_TRANSPARENT)
+
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_TRANSPARENT,
+ (const void *) &value, sizeof(int)) == -1)
+ {
+ ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno,
+ "setsockopt(IPV6_TRANSPARENT) failed");
+ return NGX_ERROR;
+ }
+
+#elif defined(IPV6_BINDANY)
+
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_BINDANY,
+ (const void *) &value, sizeof(int)) == -1)
+ {
+ ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno,
+ "setsockopt(IPV6_BINDANY) failed");
+ return NGX_ERROR;
+ }
+
+#endif
+ break;
+
+#endif /* NGX_HAVE_INET6 */
+
+ }
+
+#endif /* SO_BINDANY */
+
+ return NGX_OK;
+}
+
+#endif
+
+
ngx_int_t
ngx_event_get_peer(ngx_peer_connection_t *pc, void *data)
{
diff -r cb8177ca0990 -r 1d0e03db9f8e src/event/ngx_event_connect.h
--- a/src/event/ngx_event_connect.h Wed Apr 13 15:42:47 2016 +0300
+++ b/src/event/ngx_event_connect.h Fri Dec 18 19:05:27 2015 +0300
@@ -61,6 +61,9 @@ struct ngx_peer_connection_s {
ngx_log_t *log;
unsigned cached:1;
+#if (NGX_HAVE_TRANSPARENT_PROXY)
+ unsigned transparent:1;
+#endif
/* ngx_connection_log_error_e */
unsigned log_error:2;
diff -r cb8177ca0990 -r 1d0e03db9f8e src/http/modules/ngx_http_fastcgi_module.c
--- a/src/http/modules/ngx_http_fastcgi_module.c Wed Apr 13 15:42:47 2016 +0300
+++ b/src/http/modules/ngx_http_fastcgi_module.c Fri Dec 18 19:05:27 2015 +0300
@@ -279,7 +279,7 @@ static ngx_command_t ngx_http_fastcgi_c
NULL },
{ ngx_string("fastcgi_bind"),
- NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12,
ngx_http_upstream_bind_set_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_fastcgi_loc_conf_t, upstream.local),
diff -r cb8177ca0990 -r 1d0e03db9f8e src/http/modules/ngx_http_memcached_module.c
--- a/src/http/modules/ngx_http_memcached_module.c Wed Apr 13 15:42:47 2016 +0300
+++ b/src/http/modules/ngx_http_memcached_module.c Fri Dec 18 19:05:27 2015 +0300
@@ -61,7 +61,7 @@ static ngx_command_t ngx_http_memcached
NULL },
{ ngx_string("memcached_bind"),
- NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12,
ngx_http_upstream_bind_set_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_memcached_loc_conf_t, upstream.local),
diff -r cb8177ca0990 -r 1d0e03db9f8e src/http/modules/ngx_http_proxy_module.c
--- a/src/http/modules/ngx_http_proxy_module.c Wed Apr 13 15:42:47 2016 +0300
+++ b/src/http/modules/ngx_http_proxy_module.c Fri Dec 18 19:05:27 2015 +0300
@@ -316,7 +316,7 @@ static ngx_command_t ngx_http_proxy_com
NULL },
{ ngx_string("proxy_bind"),
- NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12,
ngx_http_upstream_bind_set_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_proxy_loc_conf_t, upstream.local),
diff -r cb8177ca0990 -r 1d0e03db9f8e src/http/modules/ngx_http_scgi_module.c
--- a/src/http/modules/ngx_http_scgi_module.c Wed Apr 13 15:42:47 2016 +0300
+++ b/src/http/modules/ngx_http_scgi_module.c Fri Dec 18 19:05:27 2015 +0300
@@ -136,7 +136,7 @@ static ngx_command_t ngx_http_scgi_comma
NULL },
{ ngx_string("scgi_bind"),
- NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12,
ngx_http_upstream_bind_set_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_scgi_loc_conf_t, upstream.local),
diff -r cb8177ca0990 -r 1d0e03db9f8e src/http/modules/ngx_http_uwsgi_module.c
--- a/src/http/modules/ngx_http_uwsgi_module.c Wed Apr 13 15:42:47 2016 +0300
+++ b/src/http/modules/ngx_http_uwsgi_module.c Fri Dec 18 19:05:27 2015 +0300
@@ -196,7 +196,7 @@ static ngx_command_t ngx_http_uwsgi_comm
NULL },
{ ngx_string("uwsgi_bind"),
- NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12,
ngx_http_upstream_bind_set_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_uwsgi_loc_conf_t, upstream.local),
diff -r cb8177ca0990 -r 1d0e03db9f8e src/http/ngx_http_upstream.c
--- a/src/http/ngx_http_upstream.c Wed Apr 13 15:42:47 2016 +0300
+++ b/src/http/ngx_http_upstream.c Fri Dec 18 19:05:27 2015 +0300
@@ -5788,7 +5788,7 @@ ngx_http_upstream_bind_set_slot(ngx_conf
value = cf->args->elts;
- if (ngx_strcmp(value[1].data, "off") == 0) {
+ if (cf->args->nelts == 2 && ngx_strcmp(value[1].data, "off") == 0) {
*plocal = NULL;
return NGX_CONF_OK;
}
@@ -5841,6 +5841,22 @@ ngx_http_upstream_bind_set_slot(ngx_conf
}
}
+ if (cf->args->nelts > 2) {
+ if (ngx_strcmp(value[2].data, "transparent") == 0) {
+#if (NGX_HAVE_TRANSPARENT_PROXY)
+ local->transparent = 1;
+#else
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "transparent proxying is not supported "
+ "on this platform, ignored");
+#endif
+ } else {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid parameter \"%V\"", &value[2]);
+ return NGX_CONF_ERROR;
+ }
+ }
+
return NGX_CONF_OK;
}
@@ -5858,6 +5874,10 @@ ngx_http_upstream_set_local(ngx_http_req
return NGX_OK;
}
+#if (NGX_HAVE_TRANSPARENT_PROXY)
+ u->peer.transparent = local->transparent;
+#endif
+
if (local->value == NULL) {
u->peer.local = local->addr;
return NGX_OK;
diff -r cb8177ca0990 -r 1d0e03db9f8e src/http/ngx_http_upstream.h
--- a/src/http/ngx_http_upstream.h Wed Apr 13 15:42:47 2016 +0300
+++ b/src/http/ngx_http_upstream.h Fri Dec 18 19:05:27 2015 +0300
@@ -133,6 +133,9 @@ struct ngx_http_upstream_srv_conf_s {
typedef struct {
ngx_addr_t *addr;
ngx_http_complex_value_t *value;
+#if (NGX_HAVE_TRANSPARENT_PROXY)
+ ngx_uint_t transparent; /* unsigned transparent:1; */
+#endif
} ngx_http_upstream_local_t;
diff -r cb8177ca0990 -r 1d0e03db9f8e src/stream/ngx_stream_proxy_module.c
--- a/src/stream/ngx_stream_proxy_module.c Wed Apr 13 15:42:47 2016 +0300
+++ b/src/stream/ngx_stream_proxy_module.c Fri Dec 18 19:05:27 2015 +0300
@@ -12,6 +12,9 @@
typedef struct {
ngx_addr_t *addr;
+#if (NGX_HAVE_TRANSPARENT_PROXY)
+ ngx_uint_t transparent; /* unsigned transparent:1; */
+#endif
} ngx_stream_upstream_local_t;
@@ -120,7 +123,7 @@ static ngx_command_t ngx_stream_proxy_c
NULL },
{ ngx_string("proxy_bind"),
- NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,
+ NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE12,
ngx_stream_proxy_bind,
NGX_STREAM_SRV_CONF_OFFSET,
0,
@@ -443,16 +446,63 @@ static ngx_int_t
ngx_stream_proxy_set_local(ngx_stream_session_t *s, ngx_stream_upstream_t *u,
ngx_stream_upstream_local_t *local)
{
+ ngx_addr_t *addr;
+ ngx_connection_t *c;
+ struct sockaddr_in *sin;
+#if (NGX_HAVE_INET6)
+ struct sockaddr_in6 *sin6;
+#endif
+
if (local == NULL) {
u->peer.local = NULL;
return NGX_OK;
}
+#if (NGX_HAVE_TRANSPARENT_PROXY)
+ u->peer.transparent = local->transparent;
+#endif
+
if (local->addr) {
u->peer.local = local->addr;
return NGX_OK;
}
+ /* $remote_addr */
+
+ c = s->connection;
+
+ addr = ngx_palloc(c->pool, sizeof(ngx_addr_t));
+ if (addr == NULL) {
+ return NGX_ERROR;
+ }
+
+ addr->socklen = c->socklen;
+
+ addr->sockaddr = ngx_palloc(c->pool, addr->socklen);
+ if (addr->sockaddr == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_memcpy(addr->sockaddr, c->sockaddr, c->socklen);
+
+ switch (addr->sockaddr->sa_family) {
+
+ case AF_INET:
+ sin = (struct sockaddr_in *) addr->sockaddr;
+ sin->sin_port = 0;
+ break;
+
+#if (NGX_HAVE_INET6)
+ case AF_INET6:
+ sin6 = (struct sockaddr_in6 *) addr->sockaddr;
+ sin6->sin6_port = 0;
+ break;
+#endif
+ }
+
+ addr->name = c->addr_text;
+ u->peer.local = addr;
+
return NGX_OK;
}
@@ -1676,7 +1726,7 @@ ngx_stream_proxy_bind(ngx_conf_t *cf, ng
value = cf->args->elts;
- if (ngx_strcmp(value[1].data, "off") == 0) {
+ if (cf->args->nelts == 2 && ngx_strcmp(value[1].data, "off") == 0) {
pscf->local = NULL;
return NGX_CONF_OK;
}
@@ -1688,25 +1738,44 @@ ngx_stream_proxy_bind(ngx_conf_t *cf, ng
pscf->local = local;
- local->addr = ngx_palloc(cf->pool, sizeof(ngx_addr_t));
- if (local->addr == NULL) {
- return NGX_CONF_ERROR;
+ if (ngx_strcmp(value[1].data, "$remote_addr") != 0) {
+ local->addr = ngx_palloc(cf->pool, sizeof(ngx_addr_t));
+ if (local->addr == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ rc = ngx_parse_addr(cf->pool, local->addr, value[1].data, value[1].len);
+
+ switch (rc) {
+ case NGX_OK:
+ local->addr->name = value[1];
+ break;
+
+ case NGX_DECLINED:
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid address \"%V\"", &value[1]);
+ /* fall through */
+
+ default:
+ return NGX_CONF_ERROR;
+ }
}
- rc = ngx_parse_addr(cf->pool, local->addr, value[1].data, value[1].len);
+ if (cf->args->nelts > 2) {
+ if (ngx_strcmp(value[2].data, "transparent") == 0) {
+#if (NGX_HAVE_TRANSPARENT_PROXY)
+ local->transparent = 1;
- switch (rc) {
- case NGX_OK:
- local->addr->name = value[1];
- break;
-
- case NGX_DECLINED:
- ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
- "invalid address \"%V\"", &value[1]);
- /* fall through */
-
- default:
- return NGX_CONF_ERROR;
+#else
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "transparent proxying is not supported "
+ "on this platform, ignored");
+#endif
+ } else {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid parameter \"%V\"", &value[2]);
+ return NGX_CONF_ERROR;
+ }
}
return NGX_CONF_OK;
More information about the nginx-devel
mailing list