mirror of
https://github.com/louislam/uptime-kuma.git
synced 2025-05-20 14:02:34 +02:00
Merge 179d9597ae
into c67f6efe29
This commit is contained in:
commit
a17ae97f0f
5 changed files with 190 additions and 7 deletions
|
@ -4,7 +4,7 @@ const { Prometheus } = require("../prometheus");
|
||||||
const { log, UP, DOWN, PENDING, MAINTENANCE, flipStatus, MAX_INTERVAL_SECOND, MIN_INTERVAL_SECOND,
|
const { log, UP, DOWN, PENDING, MAINTENANCE, flipStatus, MAX_INTERVAL_SECOND, MIN_INTERVAL_SECOND,
|
||||||
SQL_DATETIME_FORMAT, evaluateJsonQuery
|
SQL_DATETIME_FORMAT, evaluateJsonQuery
|
||||||
} = require("../../src/util");
|
} = require("../../src/util");
|
||||||
const { tcping, ping, checkCertificate, checkStatusCode, getTotalClientInRoom, setting, mssqlQuery, postgresQuery, mysqlQuery, setSetting, httpNtlm, radius, grpcQuery,
|
const { ping, checkCertificate, checkStatusCode, getTotalClientInRoom, setting, mssqlQuery, postgresQuery, mysqlQuery, setSetting, httpNtlm, radius, grpcQuery,
|
||||||
redisPingAsync, kafkaProducerAsync, getOidcTokenClientCredentials, rootCertificatesFingerprints, axiosAbortSignal
|
redisPingAsync, kafkaProducerAsync, getOidcTokenClientCredentials, rootCertificatesFingerprints, axiosAbortSignal
|
||||||
} = require("../util-server");
|
} = require("../util-server");
|
||||||
const { R } = require("redbean-node");
|
const { R } = require("redbean-node");
|
||||||
|
@ -579,11 +579,6 @@ class Monitor extends BeanModel {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (this.type === "port") {
|
|
||||||
bean.ping = await tcping(this.hostname, this.port);
|
|
||||||
bean.msg = "";
|
|
||||||
bean.status = UP;
|
|
||||||
|
|
||||||
} else if (this.type === "ping") {
|
} else if (this.type === "ping") {
|
||||||
bean.ping = await ping(this.hostname, this.packetSize);
|
bean.ping = await ping(this.hostname, this.packetSize);
|
||||||
bean.msg = "";
|
bean.msg = "";
|
||||||
|
|
74
server/monitor-types/tcp.js
Normal file
74
server/monitor-types/tcp.js
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
const { MonitorType } = require("./monitor-type");
|
||||||
|
const { UP, DOWN } = require("../../src/util");
|
||||||
|
const { tcping, checkCertificate } = require("../util-server");
|
||||||
|
const tls = require("tls");
|
||||||
|
|
||||||
|
class TCPMonitorType extends MonitorType {
|
||||||
|
name = "port";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
async check(monitor, heartbeat, _server) {
|
||||||
|
try {
|
||||||
|
heartbeat.ping = await tcping(monitor.hostname, monitor.port);
|
||||||
|
heartbeat.msg = "";
|
||||||
|
heartbeat.status = UP;
|
||||||
|
} catch (error) {
|
||||||
|
heartbeat.status = DOWN;
|
||||||
|
heartbeat.msg = "Connection failed";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (monitor.isEnabledExpiryNotification()) {
|
||||||
|
let socket = null;
|
||||||
|
try {
|
||||||
|
const options = {
|
||||||
|
host: monitor.hostname,
|
||||||
|
port: monitor.port,
|
||||||
|
servername: monitor.hostname,
|
||||||
|
};
|
||||||
|
|
||||||
|
const tlsInfoObject = await new Promise((resolve, reject) => {
|
||||||
|
socket = tls.connect(options);
|
||||||
|
|
||||||
|
socket.on("secureConnect", () => {
|
||||||
|
try {
|
||||||
|
const info = checkCertificate(socket);
|
||||||
|
resolve(info);
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("error", (error) => {
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.setTimeout(monitor.interval * 1000 * 0.8, () => {
|
||||||
|
reject(new Error("Connection timed out"));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
await monitor.handleTlsInfo(tlsInfoObject);
|
||||||
|
if (!tlsInfoObject.valid) {
|
||||||
|
heartbeat.status = DOWN;
|
||||||
|
heartbeat.msg = "Certificate is invalid";
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
const message = error instanceof Error ? error.message : "Unknown error";
|
||||||
|
heartbeat.status = DOWN;
|
||||||
|
heartbeat.msg = `TLS Connection failed: ${message}`;
|
||||||
|
} finally {
|
||||||
|
if (socket && !socket.destroyed) {
|
||||||
|
socket.end();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
TCPMonitorType,
|
||||||
|
};
|
||||||
|
|
|
@ -118,6 +118,7 @@ class UptimeKumaServer {
|
||||||
UptimeKumaServer.monitorTypeList["snmp"] = new SNMPMonitorType();
|
UptimeKumaServer.monitorTypeList["snmp"] = new SNMPMonitorType();
|
||||||
UptimeKumaServer.monitorTypeList["mongodb"] = new MongodbMonitorType();
|
UptimeKumaServer.monitorTypeList["mongodb"] = new MongodbMonitorType();
|
||||||
UptimeKumaServer.monitorTypeList["rabbitmq"] = new RabbitMqMonitorType();
|
UptimeKumaServer.monitorTypeList["rabbitmq"] = new RabbitMqMonitorType();
|
||||||
|
UptimeKumaServer.monitorTypeList["port"] = new TCPMonitorType();
|
||||||
|
|
||||||
// Allow all CORS origins (polling) in development
|
// Allow all CORS origins (polling) in development
|
||||||
let cors = undefined;
|
let cors = undefined;
|
||||||
|
@ -558,4 +559,5 @@ const { GroupMonitorType } = require("./monitor-types/group");
|
||||||
const { SNMPMonitorType } = require("./monitor-types/snmp");
|
const { SNMPMonitorType } = require("./monitor-types/snmp");
|
||||||
const { MongodbMonitorType } = require("./monitor-types/mongodb");
|
const { MongodbMonitorType } = require("./monitor-types/mongodb");
|
||||||
const { RabbitMqMonitorType } = require("./monitor-types/rabbitmq");
|
const { RabbitMqMonitorType } = require("./monitor-types/rabbitmq");
|
||||||
|
const { TCPMonitorType } = require("./monitor-types/tcp.js");
|
||||||
const Monitor = require("./model/monitor");
|
const Monitor = require("./model/monitor");
|
||||||
|
|
|
@ -627,7 +627,7 @@
|
||||||
|
|
||||||
<h2 v-if="monitor.type !== 'push'" class="mt-5 mb-2">{{ $t("Advanced") }}</h2>
|
<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 === 'port' " 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">
|
<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">
|
<label class="form-check-label" for="expiry-notification">
|
||||||
{{ $t("Certificate Expiry Notification") }}
|
{{ $t("Certificate Expiry Notification") }}
|
||||||
|
|
112
test/backend-test/test-tcp.js
Normal file
112
test/backend-test/test-tcp.js
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
const { describe, test } = require("node:test");
|
||||||
|
const assert = require("node:assert");
|
||||||
|
const { TCPMonitorType } = require("../../server/monitor-types/tcp");
|
||||||
|
const { UP, DOWN, PENDING } = require("../../src/util");
|
||||||
|
const net = require("net");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test suite for TCP Monitor functionality
|
||||||
|
* This test suite checks the behavior of the TCPMonitorType class
|
||||||
|
* under different network connection scenarios.
|
||||||
|
*/
|
||||||
|
describe("TCP Monitor", () => {
|
||||||
|
/**
|
||||||
|
* Creates a TCP server on a specified port
|
||||||
|
* @param {number} port - The port number to listen on
|
||||||
|
* @returns {Promise<net.Server>} A promise that resolves with the created server
|
||||||
|
*/
|
||||||
|
async function createTCPServer(port) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const server = net.createServer();
|
||||||
|
|
||||||
|
server.listen(port, () => {
|
||||||
|
resolve(server);
|
||||||
|
});
|
||||||
|
|
||||||
|
server.on("error", (err) => {
|
||||||
|
reject(err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test case to verify TCP monitor works when a server is running
|
||||||
|
* Checks that the monitor correctly identifies an active TCP server
|
||||||
|
*/
|
||||||
|
test("TCP server is running", async () => {
|
||||||
|
const port = 12345;
|
||||||
|
const server = await createTCPServer(port);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const tcpMonitor = new TCPMonitorType();
|
||||||
|
|
||||||
|
const monitor = {
|
||||||
|
hostname: "localhost",
|
||||||
|
port: port,
|
||||||
|
isEnabledExpiryNotification: () => false
|
||||||
|
};
|
||||||
|
|
||||||
|
const heartbeat = {
|
||||||
|
msg: "",
|
||||||
|
status: PENDING,
|
||||||
|
};
|
||||||
|
|
||||||
|
await tcpMonitor.check(monitor, heartbeat, {});
|
||||||
|
|
||||||
|
assert.strictEqual(heartbeat.status, UP);
|
||||||
|
assert.strictEqual(heartbeat.msg, "");
|
||||||
|
} finally {
|
||||||
|
server.close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test case to verify TCP monitor handles non-running servers
|
||||||
|
* Checks that the monitor correctly identifies an inactive TCP server
|
||||||
|
*/
|
||||||
|
test("TCP server is not running", async () => {
|
||||||
|
const tcpMonitor = new TCPMonitorType();
|
||||||
|
|
||||||
|
const monitor = {
|
||||||
|
hostname: "localhost",
|
||||||
|
port: 54321,
|
||||||
|
isEnabledExpiryNotification: () => false
|
||||||
|
};
|
||||||
|
|
||||||
|
const heartbeat = {
|
||||||
|
msg: "",
|
||||||
|
status: PENDING,
|
||||||
|
};
|
||||||
|
|
||||||
|
await tcpMonitor.check(monitor, heartbeat, {});
|
||||||
|
|
||||||
|
assert.strictEqual(heartbeat.status, DOWN);
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test case to verify TCP monitor handles servers with expired or invalid TLS certificates
|
||||||
|
* Checks that the monitor correctly identifies TLS certificate issues
|
||||||
|
*/
|
||||||
|
test("TCP server with expired or invalid TLS certificate", async (t) => {
|
||||||
|
const tcpMonitor = new TCPMonitorType();
|
||||||
|
|
||||||
|
const monitor = {
|
||||||
|
hostname: "expired.badssl.com",
|
||||||
|
port: 443,
|
||||||
|
isEnabledExpiryNotification: () => true,
|
||||||
|
handleTlsInfo: async (tlsInfo) => {
|
||||||
|
return tlsInfo;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const heartbeat = {
|
||||||
|
msg: "",
|
||||||
|
status: PENDING,
|
||||||
|
};
|
||||||
|
|
||||||
|
await tcpMonitor.check(monitor, heartbeat, {});
|
||||||
|
|
||||||
|
assert.strictEqual(heartbeat.status, DOWN);
|
||||||
|
assert([ "Certificate is invalid", "TLS Connection failed:" ].some(prefix => heartbeat.msg.startsWith(prefix)));
|
||||||
|
});
|
||||||
|
});
|
Loading…
Add table
Reference in a new issue