From ccda6f05f59ac273830c406733f98a0c7daa4557 Mon Sep 17 00:00:00 2001 From: Nelson Chan Date: Wed, 21 Jul 2021 12:09:09 +0800 Subject: [PATCH] Feat: Add Barebones certificate info display --- server/model/monitor.js | 13 ++++++++++- server/util-server.js | 50 +++++++++++++++++++++++++++++++++++++++++ src/mixins/socket.js | 5 +++++ src/pages/Details.vue | 40 +++++++++++++++++++++++++++++++++ 4 files changed, 107 insertions(+), 1 deletion(-) diff --git a/server/model/monitor.js b/server/model/monitor.js index 04feea6b0..6c5f86f6f 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -5,7 +5,7 @@ var timezone = require('dayjs/plugin/timezone') dayjs.extend(utc) dayjs.extend(timezone) const axios = require("axios"); -const {tcping, ping} = require("../util-server"); +const {tcping, ping, checkCertificate} = require("../util-server"); const {R} = require("redbean-node"); const {BeanModel} = require("redbean-node/dist/bean-model"); const {Notification} = require("../notification") @@ -74,6 +74,9 @@ class Monitor extends BeanModel { }) bean.msg = `${res.status} - ${res.statusText}` bean.ping = dayjs().valueOf() - startTime; + if (this.url.startsWith("https")) { + Monitor.sendCertInfo(checkCertificate(res), io, this.id, this.user_id); + } if (this.type === "http") { bean.status = 1; @@ -189,6 +192,14 @@ class Monitor extends BeanModel { io.to(userID).emit("avgPing", monitorID, avgPing); } + /** + * + * @param checkCertificateResult : Object return result of checkCertificate + */ + static async sendCertInfo(checkCertificateResult, io, monitorID, userID) { + io.to(userID).emit("certInfo", monitorID, checkCertificateResult); + } + /** * Uptime with calculation * Calculation based on: diff --git a/server/util-server.js b/server/util-server.js index b387f4c7c..e0e255345 100644 --- a/server/util-server.js +++ b/server/util-server.js @@ -70,3 +70,53 @@ exports.getSettings = async function (type) { return result; } + + +// ssl-checker by @dyaa +// param: res - response object from axios +// return an object containing the certificate information + +const getDaysBetween = (validFrom, validTo) => + Math.round(Math.abs(+validFrom - +validTo) / 8.64e7); + +const getDaysRemaining = (validFrom, validTo) => { + const daysRemaining = getDaysBetween(validFrom, validTo); + if (new Date(validTo).getTime() < new Date().getTime()) { + return -daysRemaining; + } + return daysRemaining; +}; + +exports.checkCertificate = function (res) { + const { + valid_from, + valid_to, + subjectaltname, + issuer, + fingerprint, + } = res.request.res.socket.getPeerCertificate(false); + + if (!valid_from || !valid_to || !subjectaltname) { + reject(new Error('No certificate')); + return; + } + + const valid = res.request.res.socket.authorized || false; + + const validTo = new Date(valid_to); + + const validFor = subjectaltname + .replace(/DNS:|IP Address:/g, "") + .split(", "); + + const daysRemaining = getDaysRemaining(new Date(), validTo); + + return { + valid, + validFor, + validTo, + daysRemaining, + issuer, + fingerprint, + }; +} \ No newline at end of file diff --git a/src/mixins/socket.js b/src/mixins/socket.js index 6ad627244..14f78c872 100644 --- a/src/mixins/socket.js +++ b/src/mixins/socket.js @@ -25,6 +25,7 @@ export default { importantHeartbeatList: { }, avgPingList: { }, uptimeList: { }, + certInfoList: {}, notificationList: [], windowWidth: window.innerWidth, showListMobile: false, @@ -114,6 +115,10 @@ export default { this.uptimeList[`${monitorID}_${type}`] = data }); + socket.on('certInfo', (monitorID, data) => { + this.certInfoList[monitorID] = data + }); + socket.on('importantHeartbeatList', (monitorID, data) => { if (! (monitorID in this.importantHeartbeatList)) { this.importantHeartbeatList[monitorID] = data; diff --git a/src/pages/Details.vue b/src/pages/Details.vue index f8c4879ad..727f0aab4 100644 --- a/src/pages/Details.vue +++ b/src/pages/Details.vue @@ -54,6 +54,38 @@ +
+
+
+

Certificate Info

+ + + + + + + + + + + + + + + + + + + + + + + +
Valid: {{ certInfo.valid }}
Valid To: {{ certInfo.validTo ? new Date(certInfo.validTo).toLocaleString() : "" }}
Days Remaining: {{ certInfo.daysRemaining }}
Issuer: {{ certInfo.issuer }}
Fingerprint: {{ certInfo.fingerprint }}
+
+
+
+
@@ -180,6 +212,14 @@ export default { } }, + certInfo() { + if (this.$root.certInfoList[this.monitor.id]) { + return this.$root.certInfoList[this.monitor.id] + } else { + return { } + } + }, + displayedRecords() { const startIndex = this.perPage * (this.page - 1); const endIndex = startIndex + this.perPage;