From 1a2f5b6380ce9c68fbda1476c1a6ae5405ed5bb8 Mon Sep 17 00:00:00 2001 From: Nelson Chan Date: Tue, 26 Dec 2023 19:21:16 +0800 Subject: [PATCH 1/3] Feat: Improve TLS certificate display & handling --- server/model/monitor.js | 37 +++++++++++----------- server/util-server.js | 20 +++++++----- src/components/CertificateInfo.vue | 49 ++++++++++++++++++------------ src/pages/Details.vue | 2 +- 4 files changed, 61 insertions(+), 47 deletions(-) diff --git a/server/model/monitor.js b/server/model/monitor.js index b2fed86f..e6921574 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -529,6 +529,12 @@ class Monitor extends BeanModel { } } + let tlsInfo; + // Store TLS Info when key material is received + options.httpsAgent.on("keylog", async (line, tlsSocket) => { + tlsInfo = checkCertificate(tlsSocket); + }); + log.debug("monitor", `[${this.name}] Axios Options: ${JSON.stringify(options)}`); log.debug("monitor", `[${this.name}] Axios Request`); @@ -538,29 +544,22 @@ class Monitor extends BeanModel { bean.msg = `${res.status} - ${res.statusText}`; bean.ping = dayjs().valueOf() - startTime; - // Check certificate if https is used - let certInfoStartTime = dayjs().valueOf(); + // Store certificate and check for expiry if https is used if (this.getUrl()?.protocol === "https:") { - log.debug("monitor", `[${this.name}] Check cert`); - try { - let tlsInfoObject = checkCertificate(res); - tlsInfo = await this.updateTlsInfo(tlsInfoObject); - - if (!this.getIgnoreTls() && this.isEnabledExpiryNotification()) { - log.debug("monitor", `[${this.name}] call checkCertExpiryNotifications`); - await this.checkCertExpiryNotifications(tlsInfoObject); - } - - } catch (e) { - if (e.message !== "No TLS certificate in response") { - log.error("monitor", "Caught error"); - log.error("monitor", e.message); + // No way to listen for the `secureConnection` event, so we do it here + const tlssocket = res.request.res.socket; + if (tlssocket) { + tlsInfo.valid = tlssocket.authorized || false; + if (!tlssocket.authorized) { + tlsInfo.authorizationError = tlssocket.authorizationError; } } - } - if (process.env.TIMELOGGER === "1") { - log.debug("monitor", "Cert Info Query Time: " + (dayjs().valueOf() - certInfoStartTime) + "ms"); + await this.updateTlsInfo(tlsInfo); + if (!this.getIgnoreTls() && this.isEnabledExpiryNotification()) { + log.debug("monitor", `[${this.name}] call checkCertExpiryNotifications`); + await this.checkCertExpiryNotifications(tlsInfo); + } } if (process.env.UPTIME_KUMA_LOG_RESPONSE_BODY_MONITOR_ID === this.id) { diff --git a/server/util-server.js b/server/util-server.js index 754626ea..3951ac3c 100644 --- a/server/util-server.js +++ b/server/util-server.js @@ -653,21 +653,27 @@ const parseCertificateInfo = function (info) { /** * Check if certificate is valid - * @param {object} res Response object from axios + * @param {tls.TLSSocket} socket TLSSocket, which may or may not be connected * @returns {object} Object containing certificate information - * @throws No socket was found to check certificate for */ -exports.checkCertificate = function (res) { - if (!res.request.res.socket) { - throw new Error("No socket found"); +exports.checkCertificate = function (socket) { + let certInfoStartTime = dayjs().valueOf(); + + // Return null if there is no socket + if (socket === undefined || socket == null) { + return null; } - const info = res.request.res.socket.getPeerCertificate(true); - const valid = res.request.res.socket.authorized || false; + const info = socket.getPeerCertificate(true); + const valid = socket.authorized || false; log.debug("cert", "Parsing Certificate Info"); const parsedInfo = parseCertificateInfo(info); + if (process.env.TIMELOGGER === "1") { + log.debug("monitor", "Cert Info Query Time: " + (dayjs().valueOf() - certInfoStartTime) + "ms"); + } + return { valid: valid, certInfo: parsedInfo diff --git a/src/components/CertificateInfo.vue b/src/components/CertificateInfo.vue index cb1a8291..fd9d0775 100644 --- a/src/components/CertificateInfo.vue +++ b/src/components/CertificateInfo.vue @@ -1,20 +1,34 @@ @@ -25,16 +39,11 @@ export default { CertificateInfoRow, }, props: { - /** Object representing certificate */ - certInfo: { + /** Object representing TLS information */ + tlsInfo: { type: Object, required: true, }, - /** Is the TLS certificate valid? */ - valid: { - type: Boolean, - required: true, - }, }, }; diff --git a/src/pages/Details.vue b/src/pages/Details.vue index f1692027..360f9291 100644 --- a/src/pages/Details.vue +++ b/src/pages/Details.vue @@ -166,7 +166,7 @@
- +
From 1f62c78f4da979561dbc0b4b1a14490402191742 Mon Sep 17 00:00:00 2001 From: Nelson Chan <3271800+chakflying@users.noreply.github.com> Date: Sun, 7 Apr 2024 21:44:38 +0800 Subject: [PATCH 2/3] Chore: Record start time after null check Co-authored-by: Adam Stachowicz --- server/util-server.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/util-server.js b/server/util-server.js index 3951ac3c..f7056593 100644 --- a/server/util-server.js +++ b/server/util-server.js @@ -657,12 +657,12 @@ const parseCertificateInfo = function (info) { * @returns {object} Object containing certificate information */ exports.checkCertificate = function (socket) { - let certInfoStartTime = dayjs().valueOf(); - // Return null if there is no socket if (socket === undefined || socket == null) { return null; } + + let certInfoStartTime = dayjs().valueOf(); const info = socket.getPeerCertificate(true); const valid = socket.authorized || false; From 63c13de5f2db9ea86bbdd91466bb4797bb933e22 Mon Sep 17 00:00:00 2001 From: Nelson Chan Date: Sun, 7 Apr 2024 21:47:00 +0800 Subject: [PATCH 3/3] Chore: Formatting --- server/util-server.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/util-server.js b/server/util-server.js index f7056593..955379fb 100644 --- a/server/util-server.js +++ b/server/util-server.js @@ -661,7 +661,7 @@ exports.checkCertificate = function (socket) { if (socket === undefined || socket == null) { return null; } - + let certInfoStartTime = dayjs().valueOf(); const info = socket.getPeerCertificate(true);