Hi folks,
A few months ago @Paul-Reed shared with us a very interesting article about automatic renewal of certificates using Letsencrypt. However from that article it appeared that you have to restart Node-RED, every time a certificate is renewed (in the key file):
Would be nice if we could implement automatic certificate renewal in Node-RED.
It seems to me that not much code is required to accomplish this. But I might be mistaken ...
The current Node-RED behaviour
In the settings.js file, a āhttpsā property allows us to specify the location of the keypair (i.e. both private and public key files):
In red.js this settings.js file is loaded (which means readFileSync is executed) during Node-RED startup. And the resulting āhttpsā property is passed, when the web server is started:
The problem is that those readFileSync functions are only executed once (via 'require'), which means both key files are only loaded when Node-RED starts. This means you will need to restart Node-RED to load updated key filesā¦
It is easy to test this behaviour:
-
Open your flow editor and view the original certificate (in the browser):
-
Generate two new key files (as described in this tutorial).
-
When a new flow editor session is being started, the original certificate will still be used (instead of the new one)!
-
Restart Node-RED.
-
When a new flow editor session is being started, the new certificate is being used:
NodeJs version 11 comes to the rescue
The above āserverā variable is of type Class https.Server, which extends from tls.Server. As a result we have following function available (as a result of this pull request):
Remark: it is a pity that this feature is not available in NodeJs version 10, since that is the recommended version for Node-RED. But at least version 12 seems to be supported by Node-RED...
Node-RED pull-request proposal
Based on this feature in NodeJs, I implemented following mechanism:
I assume that that not every customer will need automatic renewal, and that the time interval needs to be adjustable. As a result, a new option needs to be added to the settings.js file:
// The following property can be used to renew credential files at regular time intervals (seconds).
// Caution: NodeJs version 11 or above is required to use this option!
//credentialRenewalTime: 3600,
When this new option is uncommented, a timer will be started (at Node-RED startup time) to read the settings.js file again. Which means to check for renewed credentials at the specified time interval:
if (settings.https) {
server = https.createServer(settings.https,function(req,res) {
app(req,res);
});
// Setup automatic certificate renewal for NodeJs version 11 and above
if (settings.credentialRenewalTime) {
if (server.setSecureContext) {
console.log("Checking renewed credentials every " + parseInt(settings.credentialRenewalTime) + " seconds.");
setInterval(function () {
//console.log("Checking for renewed credentials.");
try {
// The ārequireā mechanism will cache the previous loading result of the settings.js file.
// Which means the cache contains the result of 'readFileSync', so it contains the previous credentialsā¦
delete require.cache[settings.settingsFile];
// Read the settings file again, to have the 'readFileSync' statements read the updated files
var renewedSettings = require(settings.settingsFile);
server.setSecureContext(renewedSettings.https);
} catch(err) {
console.log("Cannot renew the credentials: " + err);
}
}, parseInt(settings.credentialRenewalTime) * 1000);
}
else {
console.log("Cannot renew credentials automatically. NodeJs version 11 or above is required.");
}
}
} else {
server = http.createServer(function(req,res) {app(req,res);});
}
It is very easy to test this new behaviour:
-
When the new setting is in comment, Node-RED still behaves like previously (which means no automatic certificate renewal). So no impact on existing flows!
-
When this setting is uncommented but on an older NodeJs version (10.x), you get a warning and Node-RED will behave the same way as before:
-
Afterwards I have installed NodeJs version 11.x.
-
I create a new key pair.
-
I wait until the new timer has been executed.
-
The original flow editor session keeps using the original certificate in my browser!
-
When I open a new flow editor session, the new certificate is automatically being used!
Remark: I had to remove the settings.js file from the ārequire cacheā because that cache contains the result of the previous 'readFileSync' statements:
When you would read the settings.js file again, the (previous) result would be read from this cache (instead of from the filesystem). Which means the 'readFileSync' statements would NOT be executed again! Clearing that cache is the only way I found, to get rid of the cached previous content of both key files...
Other impact ?
The Node-RED settings instance is also being passed to all nodes. This means that those nodes would keep using the old key files (from memory). But if Iām not mistaken, this is currently nowhere used...
Hopefully it makes sense what I'm saying ...
Would be nice if this proposal would be accepted.
Thanks for reading!!
Bart