mirror of
https://github.com/louislam/uptime-kuma.git
synced 2024-11-27 16:54:04 +00:00
Merge pull request #2594 from skaempfe/skaempfe#2593
Improvement: Support TLS Expiry alerts also for CA certs in cert chain
This commit is contained in:
commit
0735f12d19
3 changed files with 56 additions and 45 deletions
|
@ -365,8 +365,8 @@ class Monitor extends BeanModel {
|
||||||
tlsInfo = await this.updateTlsInfo(tlsInfoObject);
|
tlsInfo = await this.updateTlsInfo(tlsInfoObject);
|
||||||
|
|
||||||
if (!this.getIgnoreTls() && this.isEnabledExpiryNotification()) {
|
if (!this.getIgnoreTls() && this.isEnabledExpiryNotification()) {
|
||||||
log.debug("monitor", `[${this.name}] call sendCertNotification`);
|
log.debug("monitor", `[${this.name}] call checkCertExpiryNotifications`);
|
||||||
await this.sendCertNotification(tlsInfoObject);
|
await this.checkCertExpiryNotifications(tlsInfoObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -1212,13 +1212,19 @@ class Monitor extends BeanModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send notification about a certificate
|
* checks certificate chain for expiring certificates
|
||||||
* @param {Object} tlsInfoObject Information about certificate
|
* @param {Object} tlsInfoObject Information about certificate
|
||||||
*/
|
*/
|
||||||
async sendCertNotification(tlsInfoObject) {
|
async checkCertExpiryNotifications(tlsInfoObject) {
|
||||||
if (tlsInfoObject && tlsInfoObject.certInfo && tlsInfoObject.certInfo.daysRemaining) {
|
if (tlsInfoObject && tlsInfoObject.certInfo && tlsInfoObject.certInfo.daysRemaining) {
|
||||||
const notificationList = await Monitor.getNotificationList(this);
|
const notificationList = await Monitor.getNotificationList(this);
|
||||||
|
|
||||||
|
if (! notificationList.length > 0) {
|
||||||
|
// fail fast. If no notification is set, all the following checks can be skipped.
|
||||||
|
log.debug("monitor", "No notification, no need to send cert notification");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let notifyDays = await setting("tlsExpiryNotifyDays");
|
let notifyDays = await setting("tlsExpiryNotifyDays");
|
||||||
if (notifyDays == null || !Array.isArray(notifyDays)) {
|
if (notifyDays == null || !Array.isArray(notifyDays)) {
|
||||||
// Reset Default
|
// Reset Default
|
||||||
|
@ -1226,10 +1232,19 @@ class Monitor extends BeanModel {
|
||||||
notifyDays = [ 7, 14, 21 ];
|
notifyDays = [ 7, 14, 21 ];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (notifyDays != null && Array.isArray(notifyDays)) {
|
if (Array.isArray(notifyDays)) {
|
||||||
for (const day of notifyDays) {
|
for (const targetDays of notifyDays) {
|
||||||
log.debug("monitor", "call sendCertNotificationByTargetDays", day);
|
let certInfo = tlsInfoObject.certInfo;
|
||||||
await this.sendCertNotificationByTargetDays(tlsInfoObject.certInfo.daysRemaining, day, notificationList);
|
while (certInfo) {
|
||||||
|
let subjectCN = certInfo.subject["CN"];
|
||||||
|
if (certInfo.daysRemaining > targetDays) {
|
||||||
|
log.debug("monitor", `No need to send cert notification for ${certInfo.certType} certificate "${subjectCN}" (${certInfo.daysRemaining} days valid) on ${targetDays} deadline.`);
|
||||||
|
} else {
|
||||||
|
log.debug("monitor", `call sendCertNotificationByTargetDays for ${targetDays} deadline on certificate ${subjectCN}.`);
|
||||||
|
await this.sendCertNotificationByTargetDays(subjectCN, certInfo.certType, certInfo.daysRemaining, targetDays, notificationList);
|
||||||
|
}
|
||||||
|
certInfo = certInfo.issuerCertificate;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1238,19 +1253,14 @@ class Monitor extends BeanModel {
|
||||||
/**
|
/**
|
||||||
* Send a certificate notification when certificate expires in less
|
* Send a certificate notification when certificate expires in less
|
||||||
* than target days
|
* than target days
|
||||||
* @param {number} daysRemaining Number of days remaining on certifcate
|
* @param {string} certCN Common Name attribute from the certificate subject
|
||||||
|
* @param {string} certType certificate type
|
||||||
|
* @param {number} daysRemaining Number of days remaining on certificate
|
||||||
* @param {number} targetDays Number of days to alert after
|
* @param {number} targetDays Number of days to alert after
|
||||||
* @param {LooseObject<any>[]} notificationList List of notification providers
|
* @param {LooseObject<any>[]} notificationList List of notification providers
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
async sendCertNotificationByTargetDays(daysRemaining, targetDays, notificationList) {
|
async sendCertNotificationByTargetDays(certCN, certType, daysRemaining, targetDays, notificationList) {
|
||||||
|
|
||||||
if (daysRemaining > targetDays) {
|
|
||||||
log.debug("monitor", `No need to send cert notification. ${daysRemaining} > ${targetDays}`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (notificationList.length > 0) {
|
|
||||||
|
|
||||||
let row = await R.getRow("SELECT * FROM notification_sent_history WHERE type = ? AND monitor_id = ? AND days <= ?", [
|
let row = await R.getRow("SELECT * FROM notification_sent_history WHERE type = ? AND monitor_id = ? AND days <= ?", [
|
||||||
"certificate",
|
"certificate",
|
||||||
|
@ -1270,7 +1280,7 @@ class Monitor extends BeanModel {
|
||||||
for (let notification of notificationList) {
|
for (let notification of notificationList) {
|
||||||
try {
|
try {
|
||||||
log.debug("monitor", "Sending to " + notification.name);
|
log.debug("monitor", "Sending to " + notification.name);
|
||||||
await Notification.send(JSON.parse(notification.config), `[${this.name}][${this.url}] Certificate will expire in ${daysRemaining} days`);
|
await Notification.send(JSON.parse(notification.config), `[${this.name}][${this.url}] ${certType} certificate ${certCN} will be expired in ${daysRemaining} days`);
|
||||||
sent = true;
|
sent = true;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log.error("monitor", "Cannot send cert notification to " + notification.name);
|
log.error("monitor", "Cannot send cert notification to " + notification.name);
|
||||||
|
@ -1285,9 +1295,6 @@ class Monitor extends BeanModel {
|
||||||
targetDays,
|
targetDays,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
log.debug("monitor", "No notification, no need to send cert notification");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -519,12 +519,16 @@ const parseCertificateInfo = function (info) {
|
||||||
|
|
||||||
// Move up the chain until loop is encountered
|
// Move up the chain until loop is encountered
|
||||||
if (link.issuerCertificate == null) {
|
if (link.issuerCertificate == null) {
|
||||||
|
link.certType = (i === 0) ? "self-signed" : "root CA";
|
||||||
break;
|
break;
|
||||||
} else if (link.issuerCertificate.fingerprint in existingList) {
|
} else if (link.issuerCertificate.fingerprint in existingList) {
|
||||||
|
// a root CA certificate is typically "signed by itself" (=> "self signed certificate") and thus the "issuerCertificate" is a reference to itself.
|
||||||
log.debug("cert", `[Last] ${link.issuerCertificate.fingerprint}`);
|
log.debug("cert", `[Last] ${link.issuerCertificate.fingerprint}`);
|
||||||
|
link.certType = (i === 0) ? "self-signed" : "root CA";
|
||||||
link.issuerCertificate = null;
|
link.issuerCertificate = null;
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
|
link.certType = (i === 0) ? "server" : "intermediate CA";
|
||||||
link = link.issuerCertificate;
|
link = link.issuerCertificate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
<Tag v-for="tag in monitor.tags" :key="tag.id" :item="tag" :size="'sm'" />
|
<Tag v-for="tag in monitor.tags" :key="tag.id" :item="tag" :size="'sm'" />
|
||||||
</div>
|
</div>
|
||||||
<p class="url">
|
<p class="url">
|
||||||
<a v-if="monitor.type === 'http' || monitor.type === 'keyword' " :href="monitor.url" target="_blank" rel="noopener noreferrer">{{ filterPassword(monitor.url) }}</a>
|
<a v-if="monitor.type === 'http' || monitor.type === 'keyword' || monitor.type === 'mp-health' " :href="monitor.url" target="_blank" rel="noopener noreferrer">{{ filterPassword(monitor.url) }}</a>
|
||||||
<span v-if="monitor.type === 'port'">TCP Port {{ monitor.hostname }}:{{ monitor.port }}</span>
|
<span v-if="monitor.type === 'port'">TCP Port {{ monitor.hostname }}:{{ monitor.port }}</span>
|
||||||
<span v-if="monitor.type === 'ping'">Ping: {{ monitor.hostname }}</span>
|
<span v-if="monitor.type === 'ping'">Ping: {{ monitor.hostname }}</span>
|
||||||
<span v-if="monitor.type === 'keyword'">
|
<span v-if="monitor.type === 'keyword'">
|
||||||
|
|
Loading…
Reference in a new issue