Added certificate expiration warning.
Ylian Saint-Hilaire committed
Apr 23, 2021 at 14:17 UTC
5d11173f1000f727d0ea514fb23609d2eea9b661
6 files changed
+36
-5
meshcentral.js
+5
-3
@@ -1443,9 +1443,6 @@ function CreateMeshCentralServer(config, args) {
1443
}
1444
}
1445
1446
- // Update proxy certificates
1447
- if (obj.supportsProxyCertificatesRequest == true) { obj.updateProxyCertificates(true); }
1448
-
1446
// Load CloudFlare trusted proxies list if needed
1447
if ((obj.config.settings.trustedproxy != null) && (typeof obj.config.settings.trustedproxy == 'string') && (obj.config.settings.trustedproxy.toLowerCase() == 'cloudflare')) {
1448
obj.config.settings.extrascriptsrc = 'ajax.cloudflare.com'; // Add CloudFlare as a trusted script source. This allows for CloudFlare's RocketLoader feature.
@@ -1536,6 +1533,9 @@ function CreateMeshCentralServer(config, args) {
1533
obj.webserver = require('./webserver.js').CreateWebServer(obj, obj.db, obj.args, obj.certificates);
1534
if (obj.redirserver != null) { obj.redirserver.hookMainWebServer(obj.certificates); }
1535
1536
+ // Update proxy certificates
1537
+ if (obj.supportsProxyCertificatesRequest == true) { obj.updateProxyCertificates(true); }
1538
+
1539
// Setup the Intel AMT event handler
1540
obj.amtEventHandler = require('./amtevents.js').CreateAmtEventsHandler(obj);
1541
@@ -1789,8 +1789,10 @@ function CreateMeshCentralServer(config, args) {
1789
// Decode a RSA certificate and hash the public key, if this is not RSA, skip this.
1790
var forgeCert = obj.certificateOperations.forge.pki.certificateFromAsn1(obj.certificateOperations.forge.asn1.fromDer(cert));
1791
xdomain.certkeyhash = obj.certificateOperations.forge.pki.getPublicKeyFingerprint(forgeCert.publicKey, { md: obj.certificateOperations.forge.md.sha384.create(), encoding: 'hex' });
1792
+ obj.webserver.webCertificateExpire[xdomain.id] = Date.parse(forgeCert.validity.notAfter); // Update certificate expire time
1793
//console.log('V1: ' + xdomain.certkeyhash);
1794
} catch (ex) {
1795
+ delete obj.webserver.webCertificateExpire[xdomain.id]; // Remove certificate expire time
1796
delete xdomain.certkeyhash;
1797
}
1798
meshuser.js
+10
-1
@@ -495,6 +495,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
495
if (user.siteadmin === SITERIGHT_ADMIN) {
496
if (parent.parent.config.settings.managealldevicegroups.indexOf(user._id) >= 0) { serverinfo.manageAllDeviceGroups = true; }
497
if (obj.crossDomain === true) { serverinfo.crossDomain = []; for (var i in parent.parent.config.domains) { serverinfo.crossDomain.push(i); } }
498
+ if (typeof parent.webCertificateExpire[domain.id] == 'number') { serverinfo.certExpire = parent.webCertificateExpire[domain.id]; }
499
}
500
if (typeof domain.terminal == 'object') { // Settings used for remote terminal feature
501
if ((typeof domain.terminal.linuxshell == 'string') && (domain.terminal.linuxshell != 'any')) { serverinfo.linuxshell = domain.terminal.linuxshell; }
@@ -904,7 +905,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
905
906
switch (cmd) {
907
case 'help': {
907
- var fin = '', f = '', availcommands = 'help,maintenance,info,versions,resetserver,usersessions,closeusersessions,tasklimiter,setmaxtasks,cores,migrationagents,agentstats,agentissues,webstats,mpsstats,swarmstats,acceleratorsstats,updatecheck,serverupdate,nodeconfig,heapdump,relays,autobackup,backupconfig,dupagents,dispatchtable,badlogins,showpaths,le,lecheck,leevents,dbstats,dbcounters,sms,amtacm,certhashes,watchdog,amtmanager,amtpasswords';
908
+ var fin = '', f = '', availcommands = 'help,maintenance,info,versions,resetserver,usersessions,closeusersessions,tasklimiter,setmaxtasks,cores,migrationagents,agentstats,agentissues,webstats,mpsstats,swarmstats,acceleratorsstats,updatecheck,serverupdate,nodeconfig,heapdump,relays,autobackup,backupconfig,dupagents,dispatchtable,badlogins,showpaths,le,lecheck,leevents,dbstats,dbcounters,sms,amtacm,certhashes,watchdog,amtmanager,amtpasswords,certexpire';
909
if (parent.parent.config.settings.heapdump === true) { availcommands += ',heapdump'; }
910
availcommands = availcommands.split(',').sort();
911
while (availcommands.length > 0) { if (f.length > 80) { fin += (f + ',\r\n'); f = ''; } f += (((f != '') ? ', ' : ' ') + availcommands.shift()); }
@@ -925,6 +926,14 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
926
}
927
break;
928
}
929
+ case 'certexpire': {
930
+ const now = Date.now();
931
+ for (var i in parent.webCertificateExpire) {
932
+ const domainName = (i == '') ? '[Default]' : i;
933
+ r += domainName + ', expires in ' + Math.floor((parent.webCertificateExpire[i] - now) / 86400000) + ' day(s)\r\n';
934
+ }
935
+ break;
936
+ }
937
case 'webpush': {
938
if (parent.parent.webpush == null) {
939
r = "Web push not supported.";
public/styles/style.css
+1
-1
@@ -1024,7 +1024,7 @@ NoMeshesPanel img {
1024
padding-left: 15px;
1025
}
1026
1027
-#p2noMeshFound, #serverStats, #serverWarnings {
1027
+#p2noMeshFound, #serverStats, #serverWarnings, #serverCertWarnings {
1028
margin-left: 40px;
1029
}
1030
views/default-mobile.handlebars
+6
@@ -1398,6 +1398,12 @@
1398
serverinfo = message.serverinfo;
1399
if (serverinfo.timeout) { setInterval(checkIdleSessionTimeout, 10000); checkIdleSessionTimeout(); }
1400
if (userinfo != null) updateSelf();
1401
+ if (serverinfo.certExpire != null) {
1402
+ var days = Math.floor((serverinfo.certExpire - Date.now()) / 86400000);
1403
+ if ((days >= 0) && (days < 20)) {
1404
+ addNotification({ text: format("Certificate expires in {0} day(s)", days) });
1405
+ }
1406
+ }
1407
break;
1408
}
1409
case 'authcookie': {
views/default.handlebars
+9
@@ -525,6 +525,7 @@
525
</div>
526
<div id="serverWarningsDiv" style="display:none">
527
<br /><strong>Server Warnings</strong><br /><br />
528
+ <div id="serverCertWarnings"></div>
529
<div id="serverWarnings"></div>
530
</div>
531
</div>
@@ -2138,6 +2139,14 @@
2139
if (serverinfo.timeout) { setInterval(checkIdleSessionTimeout, 10000); checkIdleSessionTimeout(); }
2140
if (debugmode == 1) { console.log('Server time: ', printDateTime(new Date(serverinfo.serverTime))); }
2141
setupServiceWorker();
2142
+ if (serverinfo.certExpire != null) {
2143
+ var days = Math.floor((serverinfo.certExpire - Date.now()) / 86400000);
2144
+ if ((days >= 0) && (days < 20)) {
2145
+ QH('serverCertWarnings', '<div style=color:red;padding-bottom:6px><b>' + "WARNING: " + format("Certificate expires in {0} day(s)", days) + '</b></div>');
2146
+ QV('serverWarningsDiv', true);
2147
+ addNotification({ text: format("Certificate expires in {0} day(s)", days) });
2148
+ }
2149
+ }
2150
break;
2151
}
2152
case 'userinfo': {
webserver.js
+5
@@ -114,6 +114,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
114
obj.webCertificateHashBase64 = Buffer.from(obj.webCertificateHash, 'binary').toString('base64').replace(/\+/g, '@').replace(/\//g, '$');
115
obj.webCertificateFullHash = parent.certificateOperations.getCertHashBinary(obj.certificates.web.cert);
116
obj.webCertificateFullHashs = { '': obj.webCertificateFullHash };
117
+ obj.webCertificateExpire = { '': Date.parse(parent.certificateOperations.forge.pki.certificateFromPem(parent.certificates.web.cert).validity.notAfter) };
118
obj.agentCertificateHashHex = parent.certificateOperations.getPublicKeyHash(obj.certificates.agent.cert);
119
obj.agentCertificateHashBase64 = Buffer.from(obj.agentCertificateHashHex, 'hex').toString('base64').replace(/\+/g, '@').replace(/\//g, '$');
120
obj.agentCertificateAsn1 = parent.certificateOperations.forge.asn1.toDer(parent.certificateOperations.forge.pki.certificateToAsn1(parent.certificateOperations.forge.pki.certificateFromPem(parent.certificates.agent.cert))).getBytes();
@@ -126,10 +127,12 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
127
// If the web certificate hash is provided, use it.
128
obj.webCertificateHashs[i] = obj.webCertificateFullHashs[i] = Buffer.from(obj.parent.config.domains[i].certhash, 'hex').toString('binary');
129
if (obj.parent.config.domains[i].certkeyhash != null) { obj.webCertificateHashs[i] = Buffer.from(obj.parent.config.domains[i].certkeyhash, 'hex').toString('binary'); }
130
+ delete obj.webCertificateExpire[i]; // Expire time is not provided
131
} else if ((obj.parent.config.domains[i].dns != null) && (obj.parent.config.domains[i].certs != null)) {
132
// If the domain has a different DNS name, use a different certificate hash.
133
// Hash the full certificate
134
obj.webCertificateFullHashs[i] = parent.certificateOperations.getCertHashBinary(obj.parent.config.domains[i].certs.cert);
135
+ obj.webCertificateExpire[i] = Date.parse(parent.certificateOperations.forge.pki.certificateFromPem(obj.parent.config.domains[i].certs.cert).validity.notAfter);
136
try {
137
// Decode a RSA certificate and hash the public key.
138
obj.webCertificateHashs[i] = parent.certificateOperations.getPublicKeyHashBinary(obj.parent.config.domains[i].certs.cert);
@@ -141,10 +144,12 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
144
// If this domain has a DNS and a matching DNS cert, use it. This case works for wildcard certs.
145
obj.webCertificateFullHashs[i] = parent.certificateOperations.getCertHashBinary(obj.certificates.dns[i].cert);
146
obj.webCertificateHashs[i] = parent.certificateOperations.getPublicKeyHashBinary(obj.certificates.dns[i].cert);
147
+ obj.webCertificateExpire[i] = Date.parse(parent.certificateOperations.forge.pki.certificateFromPem(obj.certificates.dns[i].cert).validity.notAfter);
148
} else if (i != '') {
149
// For any other domain, use the default cert.
150
obj.webCertificateFullHashs[i] = obj.webCertificateFullHashs[''];
151
obj.webCertificateHashs[i] = obj.webCertificateHashs[''];
152
+ obj.webCertificateExpire[i] = obj.webCertificateExpire[''];
153
}
154
}
155