Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix: Getting TLS certificate through proxy & prometheus update #4700

Merged
merged 5 commits into from
Apr 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 33 additions & 15 deletions server/model/monitor.js
Original file line number Diff line number Diff line change
Expand Up @@ -512,10 +512,16 @@ class Monitor extends BeanModel {
}
}

let tlsInfo;
// Store tlsInfo when key material is received
options.httpsAgent.on("keylog", (line, tlsSocket) => {
tlsInfo = checkCertificate(tlsSocket);
let tlsInfo = {};
// Store tlsInfo when secureConnect event is emitted
// The keylog event listener is a workaround to access the tlsSocket
options.httpsAgent.once("keylog", async (line, tlsSocket) => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch on the on vs once difference.
Did not know said function. We are likely misusing on in other places as well

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't be a big problem, the socket handlers do need on since they are repeatedly handling messages.

tlsSocket.once("secureConnect", async () => {
tlsInfo = checkCertificate(tlsSocket);
tlsInfo.valid = tlsSocket.authorized || false;

await this.handleTlsInfo(tlsInfo);
});
});

log.debug("monitor", `[${this.name}] Axios Options: ${JSON.stringify(options)}`);
Expand All @@ -527,19 +533,16 @@ class Monitor extends BeanModel {
bean.msg = `${res.status} - ${res.statusText}`;
bean.ping = dayjs().valueOf() - startTime;

// Store certificate and check for expiry if https is used
if (this.getUrl()?.protocol === "https:") {
// No way to listen for the `secureConnection` event, so we do it here
const tlssocket = res.request.res.socket;
// fallback for if kelog event is not emitted, but we may still have tlsInfo,
// e.g. if the connection is made through a proxy
if (this.getUrl()?.protocol === "https:" && tlsInfo.valid === undefined) {
const tlsSocket = res.request.res.socket;

if (tlssocket) {
tlsInfo.valid = tlssocket.authorized || false;
}
if (tlsSocket) {
tlsInfo = checkCertificate(tlsSocket);
tlsInfo.valid = tlsSocket.authorized || false;

await this.updateTlsInfo(tlsInfo);
if (!this.getIgnoreTls() && this.isEnabledExpiryNotification()) {
log.debug("monitor", `[${this.name}] call checkCertExpiryNotifications`);
await this.checkCertExpiryNotifications(tlsInfo);
await this.handleTlsInfo(tlsInfo);
}
}

Expand Down Expand Up @@ -1679,6 +1682,21 @@ class Monitor extends BeanModel {
const parentActive = await Monitor.isParentActive(parent.id);
return parent.active && parentActive;
}

/**
* Store TLS certificate information and check for expiry
* @param {Object} tlsInfo Information about the TLS connection
* @returns {Promise<void>}
*/
async handleTlsInfo(tlsInfo) {
await this.updateTlsInfo(tlsInfo);
this.prometheus?.update(null, tlsInfo);

if (!this.getIgnoreTls() && this.isEnabledExpiryNotification()) {
log.debug("monitor", `[${this.name}] call checkCertExpiryNotifications`);
await this.checkCertExpiryNotifications(tlsInfo);
}
}
}

module.exports = Monitor;
32 changes: 17 additions & 15 deletions server/prometheus.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,23 +79,25 @@ class Prometheus {
}
}

try {
monitorStatus.set(this.monitorLabelValues, heartbeat.status);
} catch (e) {
log.error("prometheus", "Caught error");
log.error("prometheus", e);
}
if (heartbeat) {
try {
monitorStatus.set(this.monitorLabelValues, heartbeat.status);
} catch (e) {
log.error("prometheus", "Caught error");
log.error("prometheus", e);
}

try {
if (typeof heartbeat.ping === "number") {
monitorResponseTime.set(this.monitorLabelValues, heartbeat.ping);
} else {
// Is it good?
monitorResponseTime.set(this.monitorLabelValues, -1);
try {
if (typeof heartbeat.ping === "number") {
monitorResponseTime.set(this.monitorLabelValues, heartbeat.ping);
} else {
// Is it good?
monitorResponseTime.set(this.monitorLabelValues, -1);
}
} catch (e) {
log.error("prometheus", "Caught error");
log.error("prometheus", e);
}
} catch (e) {
log.error("prometheus", "Caught error");
log.error("prometheus", e);
}
}

Expand Down