From e64bf0e3fe02a874c73cf3974b90d7bb55ceb2d4 Mon Sep 17 00:00:00 2001 From: Nelson Chan <3271800+chakflying@users.noreply.github.com> Date: Mon, 16 Oct 2023 02:20:38 +0800 Subject: [PATCH] Fix: Stop notification check on root certs (#3874) * Fix: Stop notification check on root certs * Chore: Use Set for optimization * Fix: Manually calculate SHA256 to support node v14 --- server/model/monitor.js | 9 +++++++-- server/util-server.js | 25 +++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/server/model/monitor.js b/server/model/monitor.js index 7fffe630c..5cd94aa5d 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -6,7 +6,7 @@ const { log, UP, DOWN, PENDING, MAINTENANCE, flipStatus, TimeLogger, MAX_INTERVA SQL_DATETIME_FORMAT } = require("../../src/util"); const { tcping, ping, dnsResolve, checkCertificate, checkStatusCode, getTotalClientInRoom, setting, mssqlQuery, postgresQuery, mysqlQuery, mqttAsync, setSetting, httpNtlm, radius, grpcQuery, - redisPingAsync, mongodbPing, kafkaProducerAsync, getOidcTokenClientCredentials, + redisPingAsync, mongodbPing, kafkaProducerAsync, getOidcTokenClientCredentials, rootCertificatesFingerprints } = require("../util-server"); const { R } = require("redbean-node"); const { BeanModel } = require("redbean-node/dist/bean-model"); @@ -23,6 +23,8 @@ const Gamedig = require("gamedig"); const jsonata = require("jsonata"); const jwt = require("jsonwebtoken"); +const rootCertificates = rootCertificatesFingerprints(); + /** * status: * 0 = DOWN @@ -1428,7 +1430,10 @@ class Monitor extends BeanModel { let certInfo = tlsInfoObject.certInfo; while (certInfo) { let subjectCN = certInfo.subject["CN"]; - if (certInfo.daysRemaining > targetDays) { + if (rootCertificates.has(certInfo.fingerprint256)) { + log.debug("monitor", `Known root cert: ${certInfo.certType} certificate "${subjectCN}" (${certInfo.daysRemaining} days valid) on ${targetDays} deadline.`); + break; + } else if (certInfo.daysRemaining > targetDays) { log.debug("monitor", `No need to send cert notification for ${certInfo.certType} certificate "${subjectCN}" (${certInfo.daysRemaining} days valid) on ${targetDays} deadline.`); } else { log.debug("monitor", `call sendCertNotificationByTargetDays for ${targetDays} deadline on certificate ${subjectCN}.`); diff --git a/server/util-server.js b/server/util-server.js index bab804053..afb147497 100644 --- a/server/util-server.js +++ b/server/util-server.js @@ -22,6 +22,7 @@ const protojs = require("protobufjs"); const radiusClient = require("node-radius-client"); const redis = require("redis"); const oidc = require("openid-client"); +const tls = require("tls"); const { dictionaries: { @@ -1073,6 +1074,30 @@ module.exports.grpcQuery = async (options) => { }); }; +/** + * Returns an array of SHA256 fingerprints for all known root certificates. + * @returns {Set} A set of SHA256 fingerprints. + */ +module.exports.rootCertificatesFingerprints = () => { + let fingerprints = tls.rootCertificates.map(cert => { + let certLines = cert.split("\n"); + certLines.shift(); + certLines.pop(); + let certBody = certLines.join(""); + let buf = Buffer.from(certBody, "base64"); + + const shasum = crypto.createHash("sha256"); + shasum.update(buf); + + return shasum.digest("hex").toUpperCase().replace(/(.{2})(?!$)/g, "$1:"); + }); + + fingerprints.push("6D:99:FB:26:5E:B1:C5:B3:74:47:65:FC:BC:64:8F:3C:D8:E1:BF:FA:FD:C4:C2:F9:9B:9D:47:CF:7F:F1:C2:4F"); // ISRG X1 cross-signed with DST X3 + fingerprints.push("8B:05:B6:8C:C6:59:E5:ED:0F:CB:38:F2:C9:42:FB:FD:20:0E:6F:2F:F9:F8:5D:63:C6:99:4E:F5:E0:B0:27:01"); // ISRG X2 cross-signed with ISRG X1 + + return new Set(fingerprints); +}; + module.exports.SHAKE256_LENGTH = 16; /**