SSL/TLS Certificate Rotation Without Restarts in NGINX Open Source

Original: https://www.nginx.com/blog/ssl-tls-certificate-rotation-without-restarts-in-nginx-open-source/

In the world of high-performance web servers, NGINX is a popular choice because its lightweight and efficient architecture enables it to handle large loads of traffic. With the introduction of the shared dictionary function as part of the NGINX JavaScript module (njs), NGINX’s performance capabilities reach the next level.

In this blog post, we explore the njs shared dictionary’s functionality and benefits, and show how to set up NGINX Open Source without the need to restart when rotating SSL/TLS certificates.

Shared Dictionary Basics and Benefits

The new js_shared_dict_zone directive allows NGINX Open Source users to enable shared memory zones for efficient data exchange between worker processes. These shared memory zones act as key-value dictionaries, storing dynamic configuration settings that can be accessed and modified in real-time.

Key benefits of the shared dictionary include:

SSL Rotation with the Shared Dictionary

One of the most impactful use cases for the shared dictionary is SSL/TLS rotation. When using js_shared_dict_zone, there’s no need to restart NGINX in the event of an SSL/TLS certificate or key update. Additionally, it gives you a REST-like API to manage certificates on NGINX.

Below is an example of the NGINX configuration file that sets up the HTTPS server with the js_set and ssl_certificate directives. The JavaScript handlers use js_set to read the SSL/TLS certificate or key from a file.

This configuration snippet uses the shared dictionary to store certificates and keys in shared memory as a cache. If the key is not present, then it reads the certificate or key from the disk and puts it into the cache.

You can also expose a location that clears the cache. Once files on the disk are updated (e.g., the certificates and keys are renewed), the shared dictionary enforces reading from the disk. This adjustment allows rotating certificates/keys without the need to restart the NGINX process.

http {
     ...
    js_shared_dict_zone zone=kv:1m;
   
  server {   …    # Sets an njs function for the variable. Returns a value of cert/key     js_set $dynamic_ssl_cert main.js_cert;     js_set $dynamic_ssl_key main.js_key;  
    # use variable's data     ssl_certificate data:$dynamic_ssl_cert;     ssl_certificate_key data:$dynamic_ssl_key;    
   # a location to clear cache  location = /clear {     js_content main.clear_cache;     # allow 127.0.0.1;     # deny all;   }
  ... }

And here is the JavaScript implementation for rotation of SSL/TLS certificates and keys using js_shared_dict_zone:

function js_cert(r) {
  if (r.variables['ssl_server_name']) {
    return read_cert_or_key(r, '.cert.pem');
  } else {
    return '';
  }
}

function js_key(r) {
  if (r.variables['ssl_server_name']) {
    return read_cert_or_key(r, '.key.pem');
  } else {
    return '';
  }
}
/** 
   * Retrieves the key/cert value from Shared memory or fallback to disk
   */
  function read_cert_or_key(r, fileExtension) {
    let data = '';
    let path = '';
    const zone = 'kv';
    let certName = r.variables.ssl_server_name;
    let prefix =  '/etc/nginx/certs/';
    path = prefix + certName + fileExtension;
    r.log('Resolving ${path}');
    const key = ['certs', path].join(':');
    const cache = zone && ngx.shared && ngx.shared[zone];
   
  if (cache) { data = cache.get(key) || ''; if (data) { r.log(`Read ${key} from cache`); return data; } } try { data = fs.readFileSync(path, 'utf8'); r.log('Read from cache'); } catch (e) { data = ''; r.log(`Error reading from file:${path}. Error=${e}`); } if (cache && data) { try { cache.set(key, data); r.log('Persisted in cache'); } catch (e) { const errMsg = `Error writing to shared dict zone: ${zone}. Error=${e}`; r.log(errMsg); } } return data }

By sending the /clear request, the cache is invalidated and NGINX loads the SSL/TLS certificate or key from the disk on the next SSL/TLS handshake. Additionally, you can implement a js_content that takes an SSL/TLS certificate or key from the request while persisting and updating the cache too.

The full code of this example can be found in the njs GitHub repo.

Get Started Today

The shared dictionary function is a powerful tool for your application’s programmability that brings significant advantages in streamlining and scalability. By harnessing the capabilities of js_shared_dict_zone, you can unlock new opportunities for growth and efficiently handle increasing traffic demands.

Ready to supercharge your NGINX deployment with js_shared_dict_zone? You can upgrade your NGINX deployment with js_shared_dict_zone to unlock new use cases and learn more about this feature in our documentation. In addition, you can see a complete example of a shared dictionary function in the recently introduced njs-acme project, which enables the njs module runtime to work with ACME providers.

If you’re interested in getting started with NGINX Open Source and have questions, join NGINX Community Slack – introduce yourself and get to know this community of NGINX users!

Retrieved by Nick Shadrin from nginx.com website.