mirror of
https://github.com/louislam/uptime-kuma.git
synced 2025-03-30 13:02:20 +00:00
feat: Refacto TCP tls
monitor
This commit is contained in:
parent
2e2acbd48a
commit
054d2f7aab
4 changed files with 162 additions and 44 deletions
|
@ -612,50 +612,6 @@ class Monitor extends BeanModel {
|
|||
|
||||
}
|
||||
|
||||
} else if (this.type === "port") {
|
||||
bean.ping = await tcping(this.hostname, this.port);
|
||||
if (this.isEnabledExpiryNotification()) {
|
||||
const host = this.hostname;
|
||||
const port = this.port || 443;
|
||||
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"));
|
||||
});
|
||||
});
|
||||
await this.handleTlsInfo(tlsInfoObject);
|
||||
} catch (error) {
|
||||
console.log("Retrieve certificate failed");
|
||||
}
|
||||
}
|
||||
bean.msg = "";
|
||||
bean.status = UP;
|
||||
|
||||
} else if (this.type === "ping") {
|
||||
bean.ping = await ping(this.hostname, this.packetSize);
|
||||
bean.msg = "";
|
||||
|
|
73
server/monitor-types/tcp.js
Normal file
73
server/monitor-types/tcp.js
Normal file
|
@ -0,0 +1,73 @@
|
|||
const { MonitorType } = require("./monitor-type");
|
||||
const { UP, DOWN, log, evaluateJsonQuery } = 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(10000, () => {
|
||||
reject(new Error("Connection timed out"));
|
||||
});
|
||||
});
|
||||
|
||||
await monitor.handleTlsInfo(tlsInfoObject);
|
||||
if (!tlsInfoObject.valid) {
|
||||
heartbeat.status = DOWN;
|
||||
heartbeat.msg = "Certificate is invalid";
|
||||
}
|
||||
} catch (error) {
|
||||
heartbeat.status = DOWN;
|
||||
heartbeat.msg = "Connection failed";
|
||||
} finally {
|
||||
if (socket && !socket.destroyed) {
|
||||
socket.end();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
TCPMonitorType,
|
||||
};
|
||||
|
|
@ -116,6 +116,7 @@ class UptimeKumaServer {
|
|||
UptimeKumaServer.monitorTypeList["snmp"] = new SNMPMonitorType();
|
||||
UptimeKumaServer.monitorTypeList["mongodb"] = new MongodbMonitorType();
|
||||
UptimeKumaServer.monitorTypeList["rabbitmq"] = new RabbitMqMonitorType();
|
||||
UptimeKumaServer.monitorTypeList["port"] = new TCPMonitorType();
|
||||
|
||||
// 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 { TCPMonitorType } = require("./monitor-types/tcp.js");
|
||||
const Monitor = require("./model/monitor");
|
||||
|
|
87
test/backend-test/test-tcp.js
Normal file
87
test/backend-test/test-tcp.js
Normal file
|
@ -0,0 +1,87 @@
|
|||
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");
|
||||
|
||||
describe("TCP Monitor", () => {
|
||||
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("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("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("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", "Connection failed"].includes(heartbeat.msg));
|
||||
});
|
||||
});
|
Loading…
Add table
Reference in a new issue