2

I am running a secure websocket proxy to a TCP service. This uses a standard http.Server set up like:

var webserver = http.createServer(                                                                          
    {                                                                                                       
        key: fs.readFileSync(srv.ssl_key),                                                                  
        cert: fs.readFileSync(srv.ssl_cert),                                                                
    },                                                                                                      
    function(request, response) {                                                                           
        response.writeHead(404);                                                                            
        response.end();                                                                                     
    },                                                                                                      
    function(err) {                                                                                         
        srv.log(err);                                                                                       
    }                                                                                                       
);

As you can see, we're already using hilariously undocumented facilities: the options and error handler arguments to http.createServer().

The SSL key and cert are regenerated periodically by LetsEncrypt certbot. When this happens, I would like to inject the new key and cert into the webserver without having to regenerate a new one or reinitialize my websocket.

What further undocumented facility will allow me to do this?

chaos
  • 122,029
  • 33
  • 303
  • 309

2 Answers2

1

Use SNICallback

Actually, you should not provide cert and key and instead use SNICallback in their place.

(the HTTPS is actually just a wrapper around HTTP and TLS, so it's often better to look directly at the TLS documentation for anything dealing with certificates)

For example:

function getCerts(servername, cb) {
  cb(null, tls.createSecureContext({
    key: '...' // privkey.pem
  , cert: '...' // cert.pem + '\\r\\n' + chain.pem
  }));
}

http.createServer({ SNICallback: getCerts });

It's important to note that errors are swallowed and result in a browser tls connection error. If you have an error condition I highly recommend that you console.error(err) before you cb(err) as there is no way to catch it or know what type of error it was. This was an intentional decision by the node team.

Greenlock will do this for you

This is exactly what I do with the ssl certificate managers that I wrote, Greenlock.js and Greenlock for Express.js, which will:

  • provision SSL automatically
  • dynamically for any domain that you allow
  • renew expiring certificates automatically
  • hot-load newer certificates
  • storage plugin can handle custom logic, as it runs before issuance or renewal

It issues certificates automatically through ACME / Let's Encrypt, however, if you need a more custom solution you could override the default storage plugin to always return a certificate and then it will never even hit the ACME API.

coolaj86
  • 74,004
  • 20
  • 105
  • 125
0

You have to:

  1. Close the current webserver with webserver.close()
  2. Create a new webserver instance with your new key and cert (there's a .setOptions() method on it that you might think you could use to change the key and cert options, but there's no way to get it to generate new TLS credentials after instantiation)
  3. Mount the new webserver on the websocket server with websocketServer.mount(websocketOptions), where websocketOptions.httpServer is your new webserver instance

This will work nice and smoothly, not interfering with running connections.

chaos
  • 122,029
  • 33
  • 303
  • 309