From bf7957ebffd2f74ca7d867e40b4975e9f9a6bd99 Mon Sep 17 00:00:00 2001 From: Simon Smith Date: Sun, 12 May 2024 15:45:24 +0100 Subject: [PATCH] add zerossl acme (#6084) Signed-off-by: si458 --- letsencrypt.js | 33 +++++++++++++++++++++++---------- meshcentral-config-schema.json | 23 ++++++++++++++++++++++- sample-config-advanced.json | 6 +++++- 3 files changed, 50 insertions(+), 12 deletions(-) diff --git a/letsencrypt.js b/letsencrypt.js index c83cf102..c1823ba4 100644 --- a/letsencrypt.js +++ b/letsencrypt.js @@ -28,6 +28,8 @@ module.exports.CreateLetsEncrypt = function (parent) { obj.challenges = {}; obj.runAsProduction = false; obj.redirWebServerHooked = false; + obj.zerossl = false; + obj.csr = null; obj.configErr = null; obj.configOk = false; obj.pendingRequest = false; @@ -57,6 +59,7 @@ module.exports.CreateLetsEncrypt = function (parent) { // Get the current certificate obj.getCertificate = function(certs, func) { obj.runAsProduction = (obj.parent.config.letsencrypt.production === true); + obj.zerossl = ((typeof obj.parent.config.letsencrypt.zerossl == 'object') ? obj.parent.config.letsencrypt.zerossl : false); obj.log("Getting certs from local store (" + (obj.runAsProduction ? "Production" : "Staging") + ")"); if (certs.CommonName.indexOf('.') == -1) { obj.configErr = "Add \"cert\" value to settings in config.json before using Let's Encrypt."; parent.addServerWarning(obj.configErr); obj.log("WARNING: " + obj.configErr); func(certs); return; } if (obj.parent.config.letsencrypt == null) { obj.configErr = "No Let's Encrypt configuration"; parent.addServerWarning(obj.configErr); obj.log("WARNING: " + obj.configErr); func(certs); return; } @@ -164,26 +167,36 @@ module.exports.CreateLetsEncrypt = function (parent) { obj.log("Generating private key..."); acme.forge.createPrivateKey().then(function (accountKey) { - // TODO: ZeroSSL - // https://acme.zerossl.com/v2/DV90 - // Create the ACME client obj.log("Setting up ACME client..."); - obj.client = new acme.Client({ - directoryUrl: obj.runAsProduction ? acme.directory.letsencrypt.production : acme.directory.letsencrypt.staging, - accountKey: accountKey - }); + if (obj.zerossl) { + if (obj.zerossl.kid == "") { obj.log("EAB KID hasn't been set, invalid configuration."); return; } + if (obj.zerossl.hmackey == "") { obj.log("EAB HMAC KEY hasn't been set, invalid configuration."); return; } + obj.client = new acme.Client({ + directoryUrl: acme.directory.zerossl.production, + accountKey: accountKey, + externalAccountBinding: { + kid: obj.zerossl.kid, + hmacKey: obj.zerossl.hmackey + } + }); + } else { + obj.client = new acme.Client({ + directoryUrl: obj.runAsProduction ? acme.directory.letsencrypt.production : acme.directory.letsencrypt.staging, + accountKey: accountKey + }); + } // Create Certificate Request (CSR) obj.log("Creating certificate request..."); var certRequest = { commonName: obj.leDomains[0] }; if (obj.leDomains.length > 1) { certRequest.altNames = obj.leDomains; } acme.forge.createCsr(certRequest).then(function (r) { - var csr = r[1]; + obj.csr = r[1]; obj.tempPrivateKey = r[0]; - obj.log("Requesting certificate from Let's Encrypt..."); + if(obj.zerossl) { obj.log("Requesting certificate from ZeroSSL..."); } else { obj.log("Requesting certificate from Let's Encrypt..."); } obj.client.auto({ - csr, + csr: obj.csr, email: obj.parent.config.letsencrypt.email, termsOfServiceAgreed: true, skipChallengeVerification: (obj.parent.config.letsencrypt.skipchallengeverification === true), diff --git a/meshcentral-config-schema.json b/meshcentral-config-schema.json index 084c59b5..2f5500d3 100644 --- a/meshcentral-config-schema.json +++ b/meshcentral-config-schema.json @@ -3504,12 +3504,33 @@ "production": { "type": "boolean", "default": false, - "description": "By default a test certificate will be obtained from Let's Encrypt. Always start by getting a test certificate and make sure that works before setting this to true and obtaining a production certificate. Making too many bad requests for a production certificate will get you banned for a long period of time." + "description": "By default a test certificate will be obtained from Let's Encrypt. Setting \"zerossl\", will ignore this setting. Always start by getting a test certificate and make sure that works before setting this to true and obtaining a production certificate. Making too many bad requests for a production certificate will get you banned for a long period of time." }, "nochecks": { "type": "boolean", "default": false, "description": "If you choose \"true\", MeshCentral won't verify if \"email\" is valid, has a valid MX record, AND if \"names\" doesn't contain a wildcard, can be resolved by DNS A/AAAA record." + }, + "zerossl": { + "type": "object", + "description": "If this object is set, we will use ZeroSSL for SSL creation instead of Let's Encrypt", + "required": [ + "kid", + "hmacKey" + ], + "properties": { + "kid": { + "type": "string", + "description": "EAB KID", + "default": "" + }, + "hmackey": { + "type": "string", + "description": "EAB HMAC KEY", + "default": "" + } + }, + "additionalProperties": false } }, "required": [ diff --git a/sample-config-advanced.json b/sample-config-advanced.json index 3a8362a4..d7242ddc 100644 --- a/sample-config-advanced.json +++ b/sample-config-advanced.json @@ -565,7 +565,11 @@ "email": "myemail@myserver.com", "names": "myserver.com,customer1.myserver.com", "skipChallengeVerification": false, - "production": false + "production": false, + "zerossl": { + "kid": "a1b2c3d4e5", + "hmacKey": "a1b2c3d4e5" + } }, "_peers": { "serverId": "server1",