From 44b7d0aa19545e6ab2ff1d346cbfe311617c36f1 Mon Sep 17 00:00:00 2001 From: Hornwitser Date: Thu, 22 Aug 2019 14:25:19 +0200 Subject: [PATCH] Refactor SSL certificate creation Rewrite the generateSSLcert lib to be async, use the sslCert and sslPrivKey config entries and invoke it only if needed during master server startup instead of in the post install script. Also add note to the changelog covering both this and the fix in c645fac. --- CHANGELOG.md | 3 +++ lib/generateSSLcert.js | 51 +++++++++++++++++-------------------- lib/generateSSLcert.spec.js | 25 +++++++++--------- lib/npmPostinstall.js | 10 -------- master.js | 21 +++++++++++++-- 5 files changed, 58 insertions(+), 52 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ecd3e298..65db5d1b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,9 @@ Version 2.0.0 ============= +- Fixed sslCert and sslPrivKey entries being ignored in the config. +- Changed ssl key creation to be done at startup instead of on npm install. + Breaking Changes ---------------- - Node.js versions below 10 are no longer supported. diff --git a/lib/generateSSLcert.js b/lib/generateSSLcert.js index 7731b2f7..4fbafd1b 100644 --- a/lib/generateSSLcert.js +++ b/lib/generateSSLcert.js @@ -1,49 +1,44 @@ -var forge = require("node-forge"); -forge.options.usePureJavaScript = true; -var fs = require("fs"); -var mkdirp = require("mkdirp"); +const forge = require("node-forge"); +const fs = require("fs-extra"); +const util = require("util"); -var pki = forge.pki; -var rsa = forge.pki.rsa; -var startTime, endTime; +const hrtime = process.hrtime.bigint; - -module.exports = function({ +module.exports = async function({ bits = 2048, - certificatePath = "database/certificate", + sslCertPath, + sslPrivKeyPath, doLogging = false, } = {}){ - function log(t){ + function log(t) { if(doLogging) console.log("lib/generateSSLcert.js | "+t); } - log("Generating SSL certificate with "+bits+" bits at "+certificatePath); + log("Generating SSL certificate with "+bits+" bits at "+sslCertPath); - startTime = Date.now(); - var keypair = rsa.generateKeyPair({bits: bits, e: 0x10001}); - endTime = Date.now(); - log("Generated 2048bit keypair in: "+(endTime-startTime)+" ms"); + let startNs = hrtime(); + let keypair = await util.promisify(forge.pki.rsa.generateKeyPair)({bits: bits, e: 0x10001}); + log("Generated 2048bit keypair in: "+(Number(hrtime()-startNs) / 1e6)+" ms"); - startTime = Date.now(); - var cert = pki.createCertificate(); + startNs = hrtime(); + let cert = forge.pki.createCertificate(); cert.publicKey = keypair.publicKey; cert.serialNumber = "01"; cert.validity.notBefore = new Date(); cert.validity.notAfter = new Date(Date.now()+(1000*60*60*24*365)); // valid for one year + // XXX This should probably be limited to the actual hostname + let attrs = [{ name: "commonName", value: "*" }]; + cert.setSubject(attrs); + cert.setIssuer(attrs); + // self-sign certificate cert.sign(keypair.privateKey); - // Convert certificate to pem type and write it to a file - if(certificatePath[certificatePath.length-1] != "/") certificatePath += "/"; - if(!fs.existsSync(certificatePath)) { - mkdirp.sync(certificatePath); - console.log("Created directory: "+certificatePath); - } - fs.writeFileSync(certificatePath+ "cert.key", pki.privateKeyToPem(keypair.privateKey)); - fs.writeFileSync(certificatePath+ "cert.crt", pki.certificateToPem(cert)); + // Write certificate and private key to pem files + await fs.outputFile(sslPrivKeyPath, forge.pki.privateKeyToPem(keypair.privateKey)); + await fs.outputFile(sslCertPath, forge.pki.certificateToPem(cert)); - endTime = Date.now(); - log("Generated certificate in: "+(endTime-startTime)+" ms"); + log("Generated certificate in: "+(Number(hrtime()-startNs) / 1e6)+" ms"); } diff --git a/lib/generateSSLcert.spec.js b/lib/generateSSLcert.spec.js index d65a35ee..b8e9306d 100644 --- a/lib/generateSSLcert.spec.js +++ b/lib/generateSSLcert.spec.js @@ -1,25 +1,26 @@ var assert = require("assert"); -var fs = require("fs"); +var fs = require("fs-extra"); var fileOps = require("lib/fileOps"); -var generateSSLcert = require("./generateSSLcert.js") +var generateSSLcert = require("lib/generateSSLcert.js") describe("generateSSLcert.js(options)", ()=>{ - it("Creates a folder with a .crt and .key file in it", ()=>{ - let certPath = "lib/certTestPlsDelete"; - assert(!fs.existsSync(certPath)); // if it already exsits, the test might pass because of leftover data (which is bad) + it("Creates a folder with a .crt and .key file in it", async function() { + let certPath = "lib/certTestPlsDelete/cert.crt"; + let privKeyPath = "lib/certTestPlsDelete/cert.key"; + assert(!await fs.exists(certPath)); // if it already exsits, the test might pass because of leftover data (which is bad) - generateSSLcert({ + await generateSSLcert({ // bits:512, // supported, but I just use the default of 2048 for this test - certificatePath:certPath, + sslCertPath: certPath, + sslPrivKeyPath: privKeyPath, doLogging: false, }); - assert(fs.existsSync(certPath)); - assert(fs.existsSync(certPath+"/cert.key")); // private key - assert(fs.existsSync(certPath+"/cert.crt")); // certificate file + assert(await fs.exists(certPath)); // private key + assert(await fs.exists(privKeyPath)); // certificate file // remove certificates and data from the test - fileOps.deleteFolderRecursiveSync(certPath); + fileOps.deleteFolderRecursiveSync("lib/certTestPlsDelete"); }).timeout(10000); -}); \ No newline at end of file +}); diff --git a/lib/npmPostinstall.js b/lib/npmPostinstall.js index 70ee1417..04300505 100644 --- a/lib/npmPostinstall.js +++ b/lib/npmPostinstall.js @@ -123,13 +123,3 @@ function processDownloadRecursive(callback){ } }); } - -// generate SSL certificates if they are missing -if(!fs.existsSync("database/certificates") || !fs.existsSync("database/certificates/cert.crt") || !fs.existsSync("database/certificates/cert.key")){ - var generateSSLcert = require("./generateSSLcert.js"); - - generateSSLcert({ - certificatePath: "database/certificates", - doLogging: true, - }); -} \ No newline at end of file diff --git a/master.js b/master.js index 6111539f..858d0843 100644 --- a/master.js +++ b/master.js @@ -41,6 +41,7 @@ mkdirp.sync(config.databaseDirectory); mkdirp.sync(masterModFolder); // homebrew modules +const generateSSLcert = require("lib/generateSSLcert"); const getFactorioLocale = require("lib/getFactorioLocale"); // homemade express middleware for token auth @@ -1005,6 +1006,15 @@ async function startServer() { process.exit(1); } + // Create a self signed certificate if the certificate files doesn't exist + if (httpsPort && !await fs.exists(config.sslCert) && !await fs.exists(config.sslPrivKey)) { + await generateSSLcert({ + sslCertPath: config.sslCert, + sslPrivKeyPath: config.sslPrivKey, + doLogging: true, + }); + } + // Load plugins await pluginManagement(); @@ -1016,8 +1026,15 @@ async function startServer() { } if (httpsPort) { - var certificate = fs.readFileSync(config.sslCert); - var privateKey = fs.readFileSync(config.sslPrivKey); + let certificate, privateKey; + try { + certificate = await fs.readFile(config.sslCert); + privateKey = await fs.readFile(config.sslPrivKey); + + } catch (err) { + console.error(`Error loading ssl certificate: ${err.message}`); + await shutdown() + } let httpsServer = require("https").createServer({ key: privateKey,