feat: add `TCP tls` monitor

pull/5678/head
Jacques ROUSSEL 2025-03-04 11:57:04 +01:00
parent c7d7fdd632
commit e9fa79e8a6
3 changed files with 85 additions and 3 deletions

View File

@ -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,
};

View File

@ -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");

View File

@ -21,6 +21,9 @@
<option value="port">
TCP Port
</option>
<option value="tls">
TCP Port (tls check)
</option>
<option value="ping">
Ping
</option>
@ -282,7 +285,7 @@
<!-- Hostname -->
<!-- TCP Port / Ping / DNS / Steam / MQTT / Radius / Tailscale Ping / SNMP only -->
<div v-if="monitor.type === 'port' || monitor.type === 'ping' || monitor.type === 'dns' || monitor.type === 'steam' || monitor.type === 'gamedig' || monitor.type === 'mqtt' || monitor.type === 'radius' || monitor.type === 'tailscale-ping' || monitor.type === 'snmp'" class="my-3">
<div v-if="monitor.type === 'port' || monitor.type === 'ping' || monitor.type === 'dns' || monitor.type === 'steam' || monitor.type === 'gamedig' || monitor.type === 'mqtt' || monitor.type === 'radius' || monitor.type === 'tailscale-ping' || monitor.type === 'snmp' || monitor.type === 'tls'" class="my-3">
<label for="hostname" class="form-label">{{ $t("Hostname") }}</label>
<input
id="hostname"
@ -297,7 +300,7 @@
<!-- Port -->
<!-- For TCP Port / Steam / MQTT / Radius Type / SNMP -->
<div v-if="monitor.type === 'port' || monitor.type === 'steam' || monitor.type === 'gamedig' || monitor.type === 'mqtt' || monitor.type === 'radius' || monitor.type === 'snmp'" class="my-3">
<div v-if="monitor.type === 'port' || monitor.type === 'steam' || monitor.type === 'gamedig' || monitor.type === 'mqtt' || monitor.type === 'radius' || monitor.type === 'snmp' || monitor.type === 'tls'" class="my-3">
<label for="port" class="form-label">{{ $t("Port") }}</label>
<input id="port" v-model="monitor.port" type="number" class="form-control" required min="0" max="65535" step="1">
</div>
@ -612,7 +615,7 @@
<h2 v-if="monitor.type !== 'push'" class="mt-5 mb-2">{{ $t("Advanced") }}</h2>
<div v-if="monitor.type === 'http' || monitor.type === 'keyword' || monitor.type === 'json-query' " class="my-3 form-check" :title="monitor.ignoreTls ? $t('ignoredTLSError') : ''">
<div v-if="monitor.type === 'http' || monitor.type === 'keyword' || monitor.type === 'json-query' || monitor.type === 'tls' " class="my-3 form-check" :title="monitor.ignoreTls ? $t('ignoredTLSError') : ''">
<input id="expiry-notification" v-model="monitor.expiryNotification" class="form-check-input" type="checkbox" :disabled="monitor.ignoreTls">
<label class="form-check-label" for="expiry-notification">
{{ $t("Certificate Expiry Notification") }}