From 3f7d4ef7d68c7f9f41823b3598d713c616bcaab8 Mon Sep 17 00:00:00 2001 From: Jacques ROUSSEL Date: Tue, 4 Mar 2025 11:57:04 +0100 Subject: [PATCH 1/4] feat: add `TCP tls` monitor --- server/monitor-types/tls.js | 77 ++++++++++++++++++++++++++++++++++++ server/uptime-kuma-server.js | 2 + src/pages/EditMonitor.vue | 9 +++-- 3 files changed, 85 insertions(+), 3 deletions(-) create mode 100644 server/monitor-types/tls.js diff --git a/server/monitor-types/tls.js b/server/monitor-types/tls.js new file mode 100644 index 000000000..ea9b239ed --- /dev/null +++ b/server/monitor-types/tls.js @@ -0,0 +1,77 @@ +const { MonitorType } = require("./monitor-type"); +const { UP, DOWN } = require("../../src/util"); +const { checkCertificate, setting, setSetting } = require("../util-server"); +const tls = require("tls"); + +class TlsCertificateMonitorType extends MonitorType { + name = "tlsCheck"; + + /** + * @inheritdoc + */ + async check(monitor, heartbeat, server) { + const host = monitor.hostname; + const port = monitor.port || 443; + let notifyDays = await setting("tlsExpiryNotifyDays"); + if (notifyDays == null || !Array.isArray(notifyDays)) { + // Reset Default + await setSetting("tlsExpiryNotifyDays", [ 7, 14, 21 ], "general"); + notifyDays = [ 7, 14, 21 ]; + } + + try { + const options = { + host, + port, + servername: host, + }; + + // Convert TLS connect to a Promise and await it + const tlsInfoObject = await new Promise((resolve, reject) => { + const socket = tls.connect(options); + + socket.on("secureConnect", () => { + try { + const info = checkCertificate(socket); + socket.end(); + resolve(info); + } catch (error) { + socket.end(); + reject(error); + } + }); + + socket.on("error", (error) => { + reject(error); + }); + + socket.setTimeout(10000, () => { + socket.end(); + reject(new Error("Connection timed out")); + }); + }); + + const certInfo = tlsInfoObject.certInfo; + + await monitor.updateTlsInfo(tlsInfoObject); + const alertDays = notifyDays.filter(targetDays => targetDays >= certInfo.daysRemaining); + + if (alertDays.length === 0) { + heartbeat.status = UP; + heartbeat.msg = ""; + } else { + const alertDay = Math.min(...alertDays); + heartbeat.status = DOWN; + heartbeat.msg = `Certificate expires in less thant ${alertDay} days`; + } + } catch (error) { + heartbeat.status = DOWN; + heartbeat.msg = `Error checking SSL certificate: ${error.message}`; + } + } +} + +module.exports = { + TlsCertificateMonitorType, +}; + diff --git a/server/uptime-kuma-server.js b/server/uptime-kuma-server.js index 062f098d7..324a6c0ed 100644 --- a/server/uptime-kuma-server.js +++ b/server/uptime-kuma-server.js @@ -116,6 +116,7 @@ class UptimeKumaServer { UptimeKumaServer.monitorTypeList["snmp"] = new SNMPMonitorType(); UptimeKumaServer.monitorTypeList["mongodb"] = new MongodbMonitorType(); UptimeKumaServer.monitorTypeList["rabbitmq"] = new RabbitMqMonitorType(); + UptimeKumaServer.monitorTypeList["tls"] = new TlsCertificateMonitorType(); // Allow all CORS origins (polling) in development let cors = undefined; @@ -554,4 +555,5 @@ const { MqttMonitorType } = require("./monitor-types/mqtt"); const { SNMPMonitorType } = require("./monitor-types/snmp"); const { MongodbMonitorType } = require("./monitor-types/mongodb"); const { RabbitMqMonitorType } = require("./monitor-types/rabbitmq"); +const { TlsCertificateMonitorType } = require("./monitor-types/tls"); const Monitor = require("./model/monitor"); diff --git a/src/pages/EditMonitor.vue b/src/pages/EditMonitor.vue index a83f91cab..70cec0325 100644 --- a/src/pages/EditMonitor.vue +++ b/src/pages/EditMonitor.vue @@ -21,6 +21,9 @@ + @@ -282,7 +285,7 @@ -
+
-
+
@@ -612,7 +615,7 @@

{{ $t("Advanced") }}

-
+