]> git.kaiwu.me - nginx.git/commitdiff
Upstream: the "transparent" parameter of proxy_bind and friends.
authorRoman Arutyunyan <arut@nginx.com>
Fri, 18 Dec 2015 16:05:27 +0000 (19:05 +0300)
committerRoman Arutyunyan <arut@nginx.com>
Fri, 18 Dec 2015 16:05:27 +0000 (19:05 +0300)
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;

auto/unix
src/event/ngx_event_connect.c
src/event/ngx_event_connect.h
src/http/modules/ngx_http_fastcgi_module.c
src/http/modules/ngx_http_memcached_module.c
src/http/modules/ngx_http_proxy_module.c
src/http/modules/ngx_http_scgi_module.c
src/http/modules/ngx_http_uwsgi_module.c
src/http/ngx_http_upstream.c
src/http/ngx_http_upstream.h
src/stream/ngx_stream_proxy_module.c

index 8c0e8135cd03f7eb041c97ec0dae0ef255aa5a50..e6396ef2a25b050695bb9b71a436d8b08d9b6e55 100755 (executable)
--- a/auto/unix
+++ b/auto/unix
@@ -329,6 +329,44 @@ ngx_feature_test="setsockopt(0, SOL_SOCKET, SO_ACCEPTFILTER, NULL, 0)"
 . 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"
index 8aca8625278cd5ca93945223c330f9c61fb8ae7f..5de991e07c1db689e785e8e7e2f331be32aeaf4e 100644 (file)
 #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_connection_t *pc)
     }
 
     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)
 {
index 1bacf820e198e34cefb2da932a3149fddfc42ec8..10b72a154ca610ff6ffdf8912eee70043368f3e2 100644 (file)
@@ -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;
index 2d288ce93768894f914d8ef0798f98769e6c7d62..62502b04f5a4d22e4d28d8863c2f6303d067d757 100644 (file)
@@ -279,7 +279,7 @@ static ngx_command_t  ngx_http_fastcgi_commands[] = {
       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),
index d31996a8ecc655ba76709942f51ff3c7dd6ba1d4..69f28faf8edba4ed6405541a8c6620fd5563b109 100644 (file)
@@ -61,7 +61,7 @@ static ngx_command_t  ngx_http_memcached_commands[] = {
       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),
index c24ef170de5dd1e2843f30901492e1d2f82697f3..5efee7b72eb294ef5ae20502a34d8eaee17d9384 100644 (file)
@@ -316,7 +316,7 @@ static ngx_command_t  ngx_http_proxy_commands[] = {
       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),
index f09617e87f804758d7956faf7f3c8a3839583141..36656ec18e520de32a114f302e8d2e6a09724845 100644 (file)
@@ -136,7 +136,7 @@ static ngx_command_t ngx_http_scgi_commands[] = {
       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),
index fef2c4650aed7262fa86cf18adb760622bd63df4..a03c6f6973ba8a8eb2e86815ed3e85bb81e7f3b2 100644 (file)
@@ -196,7 +196,7 @@ static ngx_command_t ngx_http_uwsgi_commands[] = {
       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),
index ec4e33f1c5c035c267a45938e675f2a8f3c2b8c5..5f08e3909d037aecf656288310a38a5a9ac06283 100644 (file)
@@ -5788,7 +5788,7 @@ ngx_http_upstream_bind_set_slot(ngx_conf_t *cf, ngx_command_t *cmd,
 
     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_t *cf, ngx_command_t *cmd,
         }
     }
 
+    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_request_t *r, ngx_http_upstream_t *u,
         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;
index 7595dcfd7d57dab21036546007b5f3fa04305c49..b288f2851c4f0e64739e41ac70f707052672be44 100644 (file)
@@ -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;
 
 
index d2cbe9984d983dd2e40e5181e42fbc6d90f86a98..d4fa99492212389c1efb7e63b4f36fc6aaff8659 100644 (file)
@@ -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_commands[] = {
       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, ngx_command_t *cmd, void *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) {
         pscf->local = NULL;
         return NGX_CONF_OK;
     }
@@ -1688,25 +1738,44 @@ ngx_stream_proxy_bind(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
 
     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);
+        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;
+        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 */
+        case NGX_DECLINED:
+            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                               "invalid address \"%V\"", &value[1]);
+            /* fall through */
 
-    default:
-        return NGX_CONF_ERROR;
+        default:
+            return NGX_CONF_ERROR;
+        }
+    }
+
+    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;