Changeset: Dynamic Virtual Server Selection Based on Host's Cnamed domain

Devarajan D devarajan.dk at zohocorp.com
Mon Jun 6 06:54:35 UTC 2022


Dear Members,



# HG changeset patch

# User devarajan.dk <mailto:devarajan.dk at zohocorp.com>

# Date 1654428592 -19800

#      Sun Jun 05 16:59:52 2022 +0530

# Node ID 8d73e94b379091440ad8c836e2a052fb5edda160

# Parent  e0cfab501dd11fdd23ad492419692269d3a01fc7

Dynamic Virtual Server Selection Based on Host's Cnamed domain



Use Case:

Preventing nginx.conf reload on new customer domain addition.



Many B2B Cloud Services provide option for rebranding customer domains through CNAME.

eg. Customers - mail.schroeppel.me, mail.vey.de cnamed to ghs.googlehosted.com

The hosting Service can be having multiple such services reverse proxied through

same nginx instance. (eg. Virtual Servers - mail.ghost.com, docs.ghost.com, drive.ghost.com, etc.

all virtual services listening on the same IP and port; consider 100s of such virtual services)



While deployment, the cloud service has to add the customer domain name in 'server_name' directive

of corresponding virtual server for the request to reach the right virtual server block.

Also, as the customer has given control to the hosting cloud service, the service will also

purchase certificate for the customer domain and offload through nginx.



As and when the new rebranding customers starts to subscribe for the cloud service, the cloud service

has to add the certificate name & key in ssl_certificate & ssl_certificate_key directives respectively

and customer's domain name in server_name directive of corresponding virtual server.

So the nginx.conf has to be reloaded every time a new customer starts using the service.



As the frequency of new customers joining the service increases, the hosting service

may suffer from frequent nginx reloads and may not scale well.

(consider 100s of customers joining the service every hour)

Thanks to the Dynamic Certificate Loading feature in nginx that uses $ssl_server_name variable

to dynamically select the certificate on runtime and no need to reload conf after deploying certificate.

But still there is a need to reload the conf due to the addition of customer's domain name in server_name directive.



To prevent this reload, we can dynamically choose Virtual server based on the cname of the customer domains.

Only the cnamed domain ie. mail.ghost.com will be written in the server_name directive of the virtual server.

All customer domains cnamed to mail.ghost.com will be routed to right virtual server based on the cname.

This cname check can be turned on/off by a http level directive - 'resolve_cname' (off by default)



Note:

1. Similar to Dynamic Certificate Loading, the pros of dynamic Virtual Server selection come 

   at the cost of increase in latency during request processing time due to DNS lookup.

2. The cloud service can choose to deploy their customer domains in hybrid mode too. 

   (ie. old customers domains can be explicitly configured in server_name and 

   customer domains who newly subscribed for the cloud service in between two nginx.conf reloads

   can be deployed in nginx dynamically)





diff -r e0cfab501dd1 -r 8d73e94b3790 src/http/ngx_http_core_module.c

--- a/src/http/ngx_http_core_module.c   Tue May 31 00:14:11 2022 +0300

+++ b/src/http/ngx_http_core_module.c   Sun Jun 05 16:59:52 2022 +0530

@@ -252,6 +252,13 @@

       offsetof(ngx_http_core_srv_conf_t, large_client_header_buffers),

       NULL },

 

+    { ngx_string("resolve_cname"),

+      NGX_HTTP_MAIN_CONF|NGX_CONF_FLAG,

+      ngx_conf_set_flag_slot,

+      NGX_HTTP_MAIN_CONF_OFFSET,

+      offsetof(ngx_http_core_main_conf_t, resolve_cname),

+      NULL },

+

     { ngx_string("ignore_invalid_headers"),

       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,

       ngx_conf_set_flag_slot,

@@ -3404,6 +3411,7 @@

     cmcf->variables_hash_max_size = NGX_CONF_UNSET_UINT;

     cmcf->variables_hash_bucket_size = NGX_CONF_UNSET_UINT;

 

+    cmcf->resolve_cname = NGX_CONF_UNSET;

     return cmcf;

 }

 

diff -r e0cfab501dd1 -r 8d73e94b3790 src/http/ngx_http_core_module.h

--- a/src/http/ngx_http_core_module.h   Tue May 31 00:14:11 2022 +0300

+++ b/src/http/ngx_http_core_module.h   Sun Jun 05 16:59:52 2022 +0530

@@ -172,6 +172,8 @@

 

     ngx_array_t               *ports;

 

+    ngx_flag_t                 resolve_cname;

+

     ngx_http_phase_t           phases[NGX_HTTP_LOG_PHASE + 1];

 } ngx_http_core_main_conf_t;

 

diff -r e0cfab501dd1 -r 8d73e94b3790 src/http/ngx_http_request.c

--- a/src/http/ngx_http_request.c       Tue May 31 00:14:11 2022 +0300

+++ b/src/http/ngx_http_request.c       Sun Jun 05 16:59:52 2022 +0530

@@ -8,6 +8,7 @@

 #include <ngx_config.h>

 #include <ngx_core.h>

 #include <ngx_http.h>

+#include<netdb.h>

 

 

 static void ngx_http_wait_request_handler(ngx_event_t *ev);

@@ -36,6 +37,8 @@

 static ngx_int_t ngx_http_find_virtual_server(ngx_connection_t *c,

     ngx_http_virtual_names_t *virtual_names, ngx_str_t *host,

     ngx_http_request_t *r, ngx_http_core_srv_conf_t **cscfp);

+    

+static void ngx_http_find_cname(ngx_str_t* host, ngx_str_t* cname, ngx_http_request_t *r);

 

 static void ngx_http_request_handler(ngx_event_t *ev);

 static void ngx_http_terminate_request(ngx_http_request_t *r, ngx_int_t rc);

@@ -2269,6 +2272,27 @@

     return NGX_OK;

 }

 

+static void

+ngx_http_find_cname(ngx_str_t* host, ngx_str_t* cname, ngx_http_request_t *r)

+{

+    struct hostent *he;

+    char **alias_list;

+

+    if ( (he = gethostbyname( (char*) host->data ) ) == NULL) {

+        return;

+    }

+

+    alias_list = (char **) he->h_aliases;

+

+    for(int i = 0; alias_list[i] != NULL; i++) {

+        if (alias_list[i+1] == NULL) {

+            cname->data = (u_char *) alias_list[i];

+            cname->len = ngx_strlen(cname->data);

+        }

+    }

+    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,

+                   "Host - %s cnamed to %s", host->data, cname->data);

+}

 

 static ngx_int_t

 ngx_http_find_virtual_server(ngx_connection_t *c,

@@ -2290,6 +2314,24 @@

         return NGX_OK;

     }

 

+    ngx_http_core_main_conf_t *cmcf;

+

+    cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);

+

+    if(cmcf->resolve_cname) {

+        ngx_str_t cname = ngx_null_string;

+        ngx_http_find_cname(host, &cname, r);

+        if (cname.data != NULL) {

+            cscf = ngx_hash_find_combined(&virtual_names->names,

+                                      ngx_hash_key(cname.data, cname.len),

+                                      cname.data, cname.len);

+            if (cscf) {

+                *cscfp = cscf;

+                return NGX_OK;

+            }

+        }

+    }

+

 #if (NGX_PCRE)

 

     if (host->len && virtual_names->nregex) {





Thanks & Regards,

Devarajan D.,

Member Technical Staff,

ZOHO Corporation Pvt. Ltd.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mailman.nginx.org/pipermail/nginx-devel/attachments/20220606/dd2708d9/attachment.htm>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: cname.patch
Type: application/octet-stream
Size: 3594 bytes
Desc: not available
URL: <http://mailman.nginx.org/pipermail/nginx-devel/attachments/20220606/dd2708d9/attachment.obj>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: sample_nginx.conf
Type: application/octet-stream
Size: 585 bytes
Desc: not available
URL: <http://mailman.nginx.org/pipermail/nginx-devel/attachments/20220606/dd2708d9/attachment-0001.obj>


More information about the nginx-devel mailing list