totally transparent proxying with nginx on openbsd

David Gwynne loki at animata.net
Thu Oct 28 18:14:48 MSD 2010


openbsd has a setsockopt option called SO_BINDANY that allows a
process to bind to any ip address, even if it is not local to the
system. the patch below uses it to allow nginx to connect to a
backend server using the ip of the client making the request.

my main goal here is to allow the backend server to know the ip of
the client actually making the request without having to look at
extra hhtp headers

i thought id throw this out there to get some help since this is
my first attempt at tweaking nginx. there are a few issues with
this implementation:

1. it is completely specific to openbsd.
2. it needs root privileges to use the SO_BINDANY sockopt.
3. im not sure if connections to backends are cached. if so then
it is probable that a different client will reuse a previous clients
proxy connection, so it will appear that the same client made both
requests to the backend.

to use this you just configure nginx to run as root and add
"proxy_transparent on" to the sections you want this feature enabled
on. you will need to add appropriate "pass out proto tcp divert-reply"
rules to pf for the SO_BINDANY sockopt to work too.

if anyone has some tips on how to handle problems 2 and 3 i would
be grateful.

cheers,
dlg


--- src/event/ngx_event_connect.c.orig	Thu Nov 26 04:03:59 2009
+++ src/event/ngx_event_connect.c	Thu Oct 28 23:22:37 2010
@@ -11,7 +11,7 @@
 
 
 ngx_int_t
-ngx_event_connect_peer(ngx_peer_connection_t *pc)
+ngx_event_connect_peer(ngx_peer_connection_t *pc, ngx_connection_t *cc)
 {
     int                rc;
     ngx_int_t          event;
@@ -20,6 +20,7 @@ ngx_event_connect_peer(ngx_peer_connection_t *pc)
     ngx_socket_t       s;
     ngx_event_t       *rev, *wev;
     ngx_connection_t  *c;
+    int                bindany;
 
     rc = pc->get(pc, pc->data);
     if (rc != NGX_OK) {
@@ -46,6 +47,40 @@ ngx_event_connect_peer(ngx_peer_connection_t *pc)
         }
 
         return NGX_ERROR;
+    }
+
+    if (cc != NULL) {
+        bindany = 1;
+        if (setsockopt(s, SOL_SOCKET, SO_BINDANY,
+	               &bindany, sizeof(bindany)) == -1)
+        {
+            ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno,
+                          "setsockopt(SO_BINDANY) failed");
+
+            ngx_free_connection(c);
+
+            if (ngx_close_socket(s) == -1) {
+                ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno,
+                              ngx_close_socket_n " failed");
+            }
+
+            return NGX_ERROR;
+        }
+
+	if (bind(s, cc->sockaddr, cc->socklen) == -1)
+	{
+            ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno,
+                          "bind() failed");
+
+            ngx_free_connection(c);
+
+            if (ngx_close_socket(s) == -1) {
+                ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno,
+                              ngx_close_socket_n " failed");
+            }
+
+            return NGX_ERROR;
+	}
     }
 
     if (pc->rcvbuf) {
--- src/event/ngx_event_connect.h.orig	Tue Nov  3 01:24:02 2009
+++ src/event/ngx_event_connect.h	Thu Oct 28 23:22:37 2010
@@ -68,7 +68,8 @@ struct ngx_peer_connection_s {
 };
 
 
-ngx_int_t ngx_event_connect_peer(ngx_peer_connection_t *pc);
+ngx_int_t ngx_event_connect_peer(ngx_peer_connection_t *pc,
+    ngx_connection_t *cc);
 ngx_int_t ngx_event_get_peer(ngx_peer_connection_t *pc, void *data);
 
 
--- src/http/modules/ngx_http_proxy_module.c.orig	Mon May 24 21:01:05 2010
+++ src/http/modules/ngx_http_proxy_module.c	Thu Oct 28 23:42:10 2010
@@ -71,6 +71,7 @@ typedef struct {
     ngx_http_proxy_vars_t          vars;
 
     ngx_flag_t                     redirect;
+    ngx_flag_t                     transparent;
 
     ngx_uint_t                     headers_hash_max_size;
     ngx_uint_t                     headers_hash_bucket_size;
@@ -196,6 +197,13 @@ static ngx_command_t  ngx_http_proxy_commands[] = {
       0,
       NULL },
 
+    { ngx_string("proxy_transparent"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+      ngx_conf_set_flag_slot,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      offsetof(ngx_http_proxy_loc_conf_t, transparent),
+      NULL },
+
     { ngx_string("proxy_store"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
       ngx_http_proxy_store,
@@ -626,6 +634,7 @@ ngx_http_proxy_handler(ngx_http_request_t *r)
     u->abort_request = ngx_http_proxy_abort_request;
     u->finalize_request = ngx_http_proxy_finalize_request;
     r->state = 0;
+    r->transparent = (plcf->transparent == 1);
 
     if (plcf->redirects) {
         u->rewrite_redirect = ngx_http_proxy_rewrite_redirect;
@@ -1940,6 +1949,7 @@ ngx_http_proxy_create_loc_conf(ngx_conf_t *cf)
     conf->upstream.cyclic_temp_file = 0;
 
     conf->redirect = NGX_CONF_UNSET;
+    conf->transparent = NGX_CONF_UNSET;
     conf->upstream.change_buffering = 1;
 
     conf->headers_hash_max_size = NGX_CONF_UNSET_UINT;
@@ -2214,6 +2224,8 @@ ngx_http_proxy_merge_loc_conf(ngx_conf_t *cf, void *pa
             }
         }
     }
+
+    ngx_conf_merge_value(conf->transparent, prev->transparent, 0);
 
     /* STUB */
     if (prev->proxy_lengths) {
--- src/http/ngx_http_request.h.orig	Mon May 24 22:35:10 2010
+++ src/http/ngx_http_request.h	Thu Oct 28 23:22:37 2010
@@ -511,6 +511,8 @@ struct ngx_http_request_s {
     unsigned                          stat_writing:1;
 #endif
 
+    unsigned                          transparent:1;
+
     /* used to parse HTTP headers */
 
     ngx_uint_t                        state;
--- src/http/ngx_http_upstream.c.orig	Mon May 24 22:35:10 2010
+++ src/http/ngx_http_upstream.c	Thu Oct 28 23:22:37 2010
@@ -1066,7 +1066,8 @@ ngx_http_upstream_connect(ngx_http_request_t *r, ngx_h
     u->state->response_sec = tp->sec;
     u->state->response_msec = tp->msec;
 
-    rc = ngx_event_connect_peer(&u->peer);
+    rc = ngx_event_connect_peer(&u->peer, r->transparent ?
+                                r->connection : NULL);
 
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    "http upstream connect: %i", rc);
--- src/mail/ngx_mail_auth_http_module.c.orig	Fri May 14 19:56:37 2010
+++ src/mail/ngx_mail_auth_http_module.c	Thu Oct 28 23:22:37 2010
@@ -191,7 +191,7 @@ ngx_mail_auth_http_init(ngx_mail_session_t *s)
     ctx->peer.log = s->connection->log;
     ctx->peer.log_error = NGX_ERROR_ERR;
 
-    rc = ngx_event_connect_peer(&ctx->peer);
+    rc = ngx_event_connect_peer(&ctx->peer, NULL);
 
     if (rc == NGX_ERROR || rc == NGX_BUSY || rc == NGX_DECLINED) {
         if (ctx->peer.connection) {
--- src/mail/ngx_mail_proxy_module.c.orig	Thu Oct 28 23:32:15 2010
+++ src/mail/ngx_mail_proxy_module.c	Thu Oct 28 23:30:53 2010
@@ -147,7 +147,7 @@ ngx_mail_proxy_init(ngx_mail_session_t *s, ngx_addr_t 
     p->upstream.log = s->connection->log;
     p->upstream.log_error = NGX_ERROR_ERR;
 
-    rc = ngx_event_connect_peer(&p->upstream);
+    rc = ngx_event_connect_peer(&p->upstream, NULL);
 
     if (rc == NGX_ERROR || rc == NGX_BUSY || rc == NGX_DECLINED) {
         ngx_mail_proxy_internal_server_error(s);



More information about the nginx-devel mailing list