mirror of
https://github.com/louislam/uptime-kuma.git
synced 2025-03-04 08:25:57 +00:00
tls: server: Parametrize STARTTLS prompt/command/response
This commit is contained in:
parent
110fde8ac9
commit
6a58451dc0
3 changed files with 62 additions and 23 deletions
|
@ -162,7 +162,11 @@ class Monitor extends BeanModel {
|
||||||
screenshot,
|
screenshot,
|
||||||
remote_browser: this.remote_browser,
|
remote_browser: this.remote_browser,
|
||||||
tcpRequest: this.tcpRequest,
|
tcpRequest: this.tcpRequest,
|
||||||
|
tcpEnableTls: this.getTcpEnableTls(),
|
||||||
tcpStartTls: this.getTcpStartTls(),
|
tcpStartTls: this.getTcpStartTls(),
|
||||||
|
tcpStartTlsPrompt: this.tcpStartTlsPrompt,
|
||||||
|
tcpStartTlsCommand: this.tcpStartTlsCommand,
|
||||||
|
tcpStartTlsResponse: this.tcpStartTlsResponse,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (includeSensitiveData) {
|
if (includeSensitiveData) {
|
||||||
|
@ -327,6 +331,14 @@ class Monitor extends BeanModel {
|
||||||
return Boolean(this.kafkaProducerAllowAutoTopicCreation);
|
return Boolean(this.kafkaProducerAllowAutoTopicCreation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse to boolean
|
||||||
|
* @returns {boolean} Require TLS for TCP Port (TLS)
|
||||||
|
*/
|
||||||
|
getTcpEnableTls() {
|
||||||
|
return Boolean(this.tcpEnableTls);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse to boolean
|
* Parse to boolean
|
||||||
* @returns {boolean} Enable STARTTLS for TCP Port (TLS)
|
* @returns {boolean} Enable STARTTLS for TCP Port (TLS)
|
||||||
|
|
|
@ -2,6 +2,7 @@ const { MonitorType } = require("./monitor-type");
|
||||||
const { log, UP } = require("../../src/util");
|
const { log, UP } = require("../../src/util");
|
||||||
const net = require("net");
|
const net = require("net");
|
||||||
const tls = require("tls");
|
const tls = require("tls");
|
||||||
|
const unescape = require("unescape-js");
|
||||||
|
|
||||||
class TlsMonitorType extends MonitorType {
|
class TlsMonitorType extends MonitorType {
|
||||||
name = "port-tls";
|
name = "port-tls";
|
||||||
|
@ -12,19 +13,41 @@ class TlsMonitorType extends MonitorType {
|
||||||
async check(monitor, heartbeat, _server) {
|
async check(monitor, heartbeat, _server) {
|
||||||
const abortController = new AbortController();
|
const abortController = new AbortController();
|
||||||
|
|
||||||
const timeoutMs = (monitor.interval || 30) * 1000 * 0.8;
|
const intervalS = monitor.interval || 30;
|
||||||
|
const timeoutMs = intervalS * 1000 * 0.8;
|
||||||
const timeoutID = setTimeout(() => {
|
const timeoutID = setTimeout(() => {
|
||||||
log.info(this.name, `timeout after ${timeoutMs} ms`);
|
log.info(this.name, `timeout after ${timeoutMs} ms`);
|
||||||
abortController.abort();
|
abortController.abort();
|
||||||
}, timeoutMs);
|
}, timeoutMs);
|
||||||
|
|
||||||
const tlsSocket = await this.connect(abortController.signal, monitor.hostname, monitor.port, monitor.tcpStartTls);
|
// Create a set of TLS options for better readability and to avoid passing the Monitor
|
||||||
|
// object into what is fairly generic STARTTLS code.
|
||||||
|
/**
|
||||||
|
* @typedef TlsOptions
|
||||||
|
* @type {object}
|
||||||
|
* @property {string} hostname - Host name to connect to
|
||||||
|
* @property {int} port - TCP port to connect to
|
||||||
|
* @property {boolean} useStartTls - True if STARTTLS should be used, false for native TLS
|
||||||
|
* @property {string} prompt - The server prompt to wait for before initiating STARTTLS
|
||||||
|
* @property {string} command - The command to send to initiate STARTTLS
|
||||||
|
* @property {string} response - The server response that indicates TLS negotiation readiness
|
||||||
|
*/
|
||||||
|
const tlsOptions = {
|
||||||
|
hostname: monitor.hostname,
|
||||||
|
port: monitor.port,
|
||||||
|
useStartTls: monitor.tcpStartTls,
|
||||||
|
prompt: unescape(monitor.tcpStartTlsPrompt || ""),
|
||||||
|
command: unescape(monitor.tcpStartTlsCommand || ""),
|
||||||
|
response: unescape(monitor.tcpStartTlsResponse || ""),
|
||||||
|
};
|
||||||
|
|
||||||
|
const tlsSocket = await this.connect(abortController.signal, tlsOptions);
|
||||||
let tlsSocketClosed = false;
|
let tlsSocketClosed = false;
|
||||||
tlsSocket.on("close", () => {
|
tlsSocket.on("close", () => {
|
||||||
tlsSocketClosed = true;
|
tlsSocketClosed = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
const request = monitor.tcpRequest || null;
|
const request = unescape(monitor.tcpRequest || "");
|
||||||
const result = await this.getResponseFromTlsPort(abortController.signal, tlsSocket, request)
|
const result = await this.getResponseFromTlsPort(abortController.signal, tlsSocket, request)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
clearTimeout(timeoutID);
|
clearTimeout(timeoutID);
|
||||||
|
@ -99,7 +122,7 @@ class TlsMonitorType extends MonitorType {
|
||||||
*/
|
*/
|
||||||
async getResponseFromTlsPort(aborter, tlsSocket, request) {
|
async getResponseFromTlsPort(aborter, tlsSocket, request) {
|
||||||
if (request) {
|
if (request) {
|
||||||
log.debug(this.name, `sending request: '${request}'`);
|
log.debug(this.name, `sending request: ${JSON.stringify(request)}`);
|
||||||
tlsSocket.write(request);
|
tlsSocket.write(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,20 +132,18 @@ class TlsMonitorType extends MonitorType {
|
||||||
/**
|
/**
|
||||||
* Connects to a given host and port using native TLS or STARTTLS.
|
* Connects to a given host and port using native TLS or STARTTLS.
|
||||||
* @param {AbortController} aborter Abort controller used to abort the connection
|
* @param {AbortController} aborter Abort controller used to abort the connection
|
||||||
* @param {string} hostname Host to connect to
|
* @param {TlsOptions} tlsOptions TLS options to use for the connection
|
||||||
* @param {int} port TCP port to connect to
|
|
||||||
* @param {boolean} useStartTls True if STARTTLS should be used, false for native TLS
|
|
||||||
* @returns {Promise<tls.TLSSocket>} TLS socket instance if successful or rejected promise on error
|
* @returns {Promise<tls.TLSSocket>} TLS socket instance if successful or rejected promise on error
|
||||||
*/
|
*/
|
||||||
async connect(aborter, hostname, port, useStartTls) {
|
async connect(aborter, tlsOptions) {
|
||||||
if (useStartTls) {
|
if (tlsOptions.useStartTls) {
|
||||||
const socket = new net.Socket({
|
const socket = new net.Socket({
|
||||||
signal: aborter
|
signal: aborter
|
||||||
});
|
});
|
||||||
socket.connect(port, hostname);
|
socket.connect(tlsOptions.port, tlsOptions.hostname);
|
||||||
log.debug(this.name, "TCP connected");
|
log.debug(this.name, "TCP connected");
|
||||||
|
|
||||||
await this.startTls(aborter, socket);
|
await this.startTls(aborter, socket, tlsOptions);
|
||||||
log.debug(this.name, "STARTTLS prelude done");
|
log.debug(this.name, "STARTTLS prelude done");
|
||||||
|
|
||||||
const tlsSocket = await this.upgradeConnection(socket);
|
const tlsSocket = await this.upgradeConnection(socket);
|
||||||
|
@ -132,9 +153,9 @@ class TlsMonitorType extends MonitorType {
|
||||||
});
|
});
|
||||||
return tlsSocket;
|
return tlsSocket;
|
||||||
} else {
|
} else {
|
||||||
const tlsSocket = tls.connect(port, hostname, {
|
const tlsSocket = tls.connect(tlsOptions.port, tlsOptions.hostname, {
|
||||||
signal: aborter,
|
signal: aborter,
|
||||||
servername: hostname
|
servername: tlsOptions.hostname
|
||||||
});
|
});
|
||||||
log.debug(this.name, "TLS connected");
|
log.debug(this.name, "TLS connected");
|
||||||
return tlsSocket;
|
return tlsSocket;
|
||||||
|
@ -207,15 +228,17 @@ class TlsMonitorType extends MonitorType {
|
||||||
* Performs STARTTLS on the given socket.
|
* Performs STARTTLS on the given socket.
|
||||||
* @param {AbortController} aborter Abort controller used to abort the STARTTLS process
|
* @param {AbortController} aborter Abort controller used to abort the STARTTLS process
|
||||||
* @param {net.Socket | tls.TLSSocket} socket Socket instance to use
|
* @param {net.Socket | tls.TLSSocket} socket Socket instance to use
|
||||||
|
* @param {TlsOptions} tlsOptions TLS options to use for the connection
|
||||||
* @returns {Promise<void>} Rejected promise if the STARTTLS process failed
|
* @returns {Promise<void>} Rejected promise if the STARTTLS process failed
|
||||||
*/
|
*/
|
||||||
async startTls(aborter, socket) {
|
async startTls(aborter, socket, tlsOptions) {
|
||||||
log.debug(this.name, "waiting for prompt");
|
log.debug(this.name, `waiting for prompt ${JSON.stringify(tlsOptions.prompt)}…`);
|
||||||
await this.expectDataStartsWith(aborter, socket, "220 ");
|
await this.expectDataStartsWith(aborter, socket, tlsOptions.prompt);
|
||||||
log.debug(this.name, "sending STARTTLS");
|
log.debug(this.name, `got prompt. sending STARTTLS command ${JSON.stringify(tlsOptions.command)}`);
|
||||||
socket.write("STARTTLS\n");
|
socket.write(tlsOptions.command);
|
||||||
log.debug(this.name, "waiting for ready-to-TLS");
|
log.debug(this.name, `sent command. waiting for response ${JSON.stringify(tlsOptions.response)}…`);
|
||||||
await this.expectDataStartsWith(aborter, socket, "220 ");
|
await this.expectDataStartsWith(aborter, socket, tlsOptions.response);
|
||||||
|
log.debug(this.name, "got response");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -832,7 +832,11 @@ let needSetup = false;
|
||||||
bean.gamedigGivenPortOnly = monitor.gamedigGivenPortOnly;
|
bean.gamedigGivenPortOnly = monitor.gamedigGivenPortOnly;
|
||||||
bean.remote_browser = monitor.remote_browser;
|
bean.remote_browser = monitor.remote_browser;
|
||||||
bean.tcpRequest = monitor.tcpRequest;
|
bean.tcpRequest = monitor.tcpRequest;
|
||||||
|
bean.tcpEnableTls = monitor.tcpEnableTls;
|
||||||
bean.tcpStartTls = monitor.tcpStartTls;
|
bean.tcpStartTls = monitor.tcpStartTls;
|
||||||
|
bean.tcpStartTlsPrompt = monitor.tcpStartTlsPrompt;
|
||||||
|
bean.tcpStartTlsCommand = monitor.tcpStartTlsCommand;
|
||||||
|
bean.tcpStartTlsResponse = monitor.tcpStartTlsResponse;
|
||||||
|
|
||||||
bean.validate();
|
bean.validate();
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue