More work on 802.1x support with MeshCentral Satellite.
Ylian Saint-Hilaire committed
Apr 5, 2022 at 15:09 UTC
631a3a6a7441308498a8ba7b4d8de33929e1096f
2 files changed
+126
-23
amtmanager.js
+123
-20
@@ -212,7 +212,7 @@ module.exports.CreateAmtManager = function (parent) {
212
// If a 802.1x profile is active with MeshCentral Satellite, notify Satellite of the removal
213
if (domain.amtmanager['802.1x'] != null) {
214
var reqId = Buffer.from(parent.crypto.randomBytes(16), 'binary').toString('base64'); // Generate a crypto-secure request id.
215
- parent.DispatchEvent([domain.amtmanager['802.1x'].satellitecredentials], obj, { action: 'satellite', satelliteFlags: 2, nodeid: nodeid, icon: 0, domain: nodeid.split('/')[1], nolog: 1, reqid: reqId, authProtocol: -1 });
215
+ parent.DispatchEvent([domain.amtmanager['802.1x'].satellitecredentials], obj, { action: 'satellite', subaction: '802.1x-Profile-Remove', satelliteFlags: 2, nodeid: nodeid, domain: nodeid.split('/')[1], nolog: 1 });
216
}
217
218
return true;
@@ -364,6 +364,7 @@ module.exports.CreateAmtManager = function (parent) {
364
var dev = devices[i];
365
dev.name = event.node.name;
366
dev.icon = event.node.icon;
367
+ dev.rname = event.node.rname;
368
369
// If there are any changes, apply them.
370
if (event.node.intelamt != null) {
@@ -396,6 +397,9 @@ module.exports.CreateAmtManager = function (parent) {
397
}
398
case 'meshchange': {
399
// TODO
400
+
401
+ // TODO: If a device changes to a device group that does not have a 802.1x policy, we may need to tell MeshCentral Satellite to remove the 802.1x profile.
402
+
403
break;
404
}
405
case 'satelliteResponse': {
@@ -403,23 +407,63 @@ module.exports.CreateAmtManager = function (parent) {
407
var devices = obj.amtDevices[event.nodeid], devFound = null;
408
if (devices != null) { for (var i in devices) { if (devices[i].netAuthSatReqId == event.reqid) { devFound = devices[i]; } } }
409
if (devFound == null) return; // Unable to find a device for this 802.1x profile
406
- delete devFound.netAuthSatReqId;
407
- if (devFound.netAuthSatReqTimer != null) { clearTimeout(devFound.netAuthSatReqTimer); delete devFound.netAuthSatReqTimer; }
408
- if ((event.response == null) || (typeof event.response != 'object') || (typeof event.response.authProtocol != 'number')) {
409
- // Unable to create a 802.1x profile
410
- if (isAmtDeviceValid(devFound) == false) return; // Device no longer exists, ignore this request.
411
- delete devFound.netAuthSatReqData;
412
- devFound.consoleMsg("MeshCentral Satellite could not create a 802.1x profile for this device.");
413
- devTaskCompleted(devFound);
414
- } else {
415
- // We got a new 802.1x profile
416
- devFound.netAuthCredentials = event.response;
417
- devFound.consoleMsg("Setting MeshCentral Satellite 802.1x profile...");
418
-
419
- // Set the 802.1x wired profile in the device
420
- var netAuthSatReqData = devFound.netAuthSatReqData;
421
- delete devFound.netAuthSatReqData;
422
- attempt8021xSyncEx(devFound, netAuthSatReqData);
410
+ switch (event.subaction) {
411
+ case '802.1x-KeyPair-Request': {
412
+ // 802.1x request for public/private key pair be generated
413
+ attempt8021xKeyGeneration(devFound);
414
+ break;
415
+ }
416
+ case '802.1x-CSR-Request': {
417
+ // 802.1x request for a Certificate Signing Request
418
+ attempt8021xCRSRequest(devFound, event);
419
+ break;
420
+ }
421
+ case '802.1x-Profile-Completed': {
422
+ // The 802.1x profile request is done, set it in Intel AMT.
423
+ if (devFound.netAuthSatReqTimer != null) { clearTimeout(devFound.netAuthSatReqTimer); delete devFound.netAuthSatReqTimer; }
424
+
425
+ if ((event.response == null) || (typeof event.response != 'object') || (typeof event.response.action != 'string')) {
426
+ // Unable to create a 802.1x profile
427
+ delete devFound.netAuthSatReqId;
428
+ if (isAmtDeviceValid(devFound) == false) return; // Device no longer exists, ignore this request.
429
+ delete devFound.netAuthSatReqData;
430
+ devFound.consoleMsg("MeshCentral Satellite could not create a 802.1x profile for this device.");
431
+ devTaskCompleted(devFound);
432
+ }
433
+
434
+ delete devFound.netAuthSatReqId;
435
+ if (typeof event.response.authProtocol != 'number') break;
436
+
437
+ // We got a new 802.1x profile
438
+ devFound.netAuthCredentials = event.response;
439
+ if (devFound.netAuthCredentials.certificate) {
440
+ // The new 802.1x profile includes a new certificate, add it now before adding the 802.1x profiles
441
+ // devFound.netAuthCredentials.certificate must be in DER encoded format
442
+ devFound.consoleMsg("Setting up new 802.1x certificate...");
443
+ devFound.amtstack.AMT_PublicKeyManagementService_AddCertificate(devFound.netAuthCredentials.certificate, function (stack, name, response, status) {
444
+ if (status != 200) {
445
+ devFound.consoleMsg("Unable to set 802.1x certificate.");
446
+ } else {
447
+
448
+ console.log('AddCertificate - TODO', response);
449
+ // TODO: Keep the certificate reference since we need it to add 802.1x profiles
450
+
451
+ // Set the 802.1x wired profile in the device
452
+ devFound.consoleMsg("Setting MeshCentral Satellite 802.1x profile...");
453
+ var netAuthSatReqData = devFound.netAuthSatReqData;
454
+ delete devFound.netAuthSatReqData;
455
+ attempt8021xSyncEx(devFound, netAuthSatReqData);
456
+ }
457
+ });
458
+ } else {
459
+ // No 802.1x certificate, set the 802.1x wired profile in the device
460
+ devFound.consoleMsg("Setting MeshCentral Satellite 802.1x profile...");
461
+ var netAuthSatReqData = devFound.netAuthSatReqData;
462
+ delete devFound.netAuthSatReqData;
463
+ attempt8021xSyncEx(devFound, netAuthSatReqData);
464
+ }
465
+ break;
466
+ }
467
}
468
break;
469
}
@@ -464,6 +508,7 @@ module.exports.CreateAmtManager = function (parent) {
508
509
// Setup the monitored device
510
dev.name = node.name;
511
+ dev.rname = node.rname;
512
dev.icon = node.icon;
513
dev.meshid = node.meshid;
514
dev.intelamt = node.intelamt;
@@ -1461,7 +1506,7 @@ module.exports.CreateAmtManager = function (parent) {
1506
dev.consoleMsg("Requesting 802.1x credentials for " + netAuthStrings[srvNetAuthProfile.authenticationprotocol] + " from MeshCentral Satellite...");
1507
dev.netAuthSatReqId = Buffer.from(parent.crypto.randomBytes(16), 'binary').toString('base64'); // Generate a crypto-secure request id.
1508
dev.netAuthSatReqData = { domain: domain, wiredConfig: wiredConfig, wirelessConfig: wirelessConfig, devNetAuthProfile: devNetAuthProfile, srvNetAuthProfile: srvNetAuthProfile, profilesToAdd: profilesToAdd, prioritiesInUse: prioritiesInUse, responses: responses }
1464
- parent.DispatchEvent([srvNetAuthProfile.satellitecredentials], obj, { action: 'satellite', satelliteFlags: 2, nodeid: dev.nodeid, icon: dev.icon, domain: dev.nodeid.split('/')[1], nolog: 1, reqid: dev.netAuthSatReqId, authProtocol: srvNetAuthProfile.authenticationprotocol, devname: dev.name });
1509
+ parent.DispatchEvent([srvNetAuthProfile.satellitecredentials], obj, { action: 'satellite', subaction: '802.1x-ProFile-Request', satelliteFlags: 2, nodeid: dev.nodeid, icon: dev.icon, domain: dev.nodeid.split('/')[1], nolog: 1, reqid: dev.netAuthSatReqId, authProtocol: srvNetAuthProfile.authenticationprotocol, devname: dev.name, osname: dev.rname });
1510
1511
// Set a response timeout
1512
const netAuthTimeoutFunc = function netAuthTimeout() {
@@ -1474,7 +1519,7 @@ module.exports.CreateAmtManager = function (parent) {
1519
}
1520
}
1521
netAuthTimeoutFunc.dev = dev;
1477
- dev.netAuthSatReqTimer = setTimeout(netAuthTimeoutFunc, 10000);
1522
+ dev.netAuthSatReqTimer = setTimeout(netAuthTimeoutFunc, 20000);
1523
return;
1524
} else {
1525
// No need to call MeshCentral Satellite for a 802.1x profile, so configure everything now.
@@ -1639,6 +1684,64 @@ module.exports.CreateAmtManager = function (parent) {
1684
devTaskCompleted(dev);
1685
}
1686
1687
+ // Request for a RSA key pair generation. This will be used to generate the 802.1x certificate
1688
+ function attempt8021xKeyGeneration(dev) {
1689
+ dev.amtstack.AMT_PublicKeyManagementService_GenerateKeyPair(0, 2048, function (stack, name, response, status) {
1690
+ if ((status != 200) || (response.Body['ReturnValue'] != 0)) {
1691
+ // Failed to generate a key pair
1692
+ dev.consoleMsg("Failed to generate the requested RSA key pair.");
1693
+ } else {
1694
+ dev.amtstack.Enum('AMT_PublicPrivateKeyPair', function (stack, name, xresponse, status, keyInstanceId) {
1695
+ if (status != 200) {
1696
+ // Failed to get the generated key pair
1697
+ dev.consoleMsg("Failed to get the generated RSA key pair.");
1698
+ } else {
1699
+ // We got the key pair
1700
+ var DERKey = null;
1701
+ for (var i in xresponse) {
1702
+ if (xresponse[i]['InstanceID'] == keyInstanceId) {
1703
+ // We found our matching DER key
1704
+ DERKey = xresponse[i]['DERKey'];
1705
+ } else {
1706
+ // This is not a matching key, since we are here, clean it up.
1707
+ dev.amtstack.Delete('AMT_PublicPrivateKeyPair', { 'InstanceID': xresponse[i]['InstanceID'] }, function (stack, name, response, status) {
1708
+ //if (status == 200) { dev.consoleMsg("Removed unassigned private key pair."); }
1709
+ });
1710
+ }
1711
+ }
1712
+ if (DERKey == null) { dev.consoleMsg("Failed to match the generated RSA key pair."); return; }
1713
+ dev.consoleMsg("Generated a RSA key pair.");
1714
+ var domain = parent.config.domains[dev.domainid];
1715
+ parent.DispatchEvent([domain.amtmanager['802.1x'].satellitecredentials], obj, { action: 'satellite', subaction: '802.1x-KeyPair-Response', satelliteFlags: 2, nodeid: dev.nodeid, icon: dev.icon, domain: dev.nodeid.split('/')[1], nolog: 1, reqid: dev.netAuthSatReqId, authProtocol: domain.amtmanager['802.1x'].authenticationprotocol, devname: dev.name, osname: dev.rname, DERKey: DERKey, keyInstanceId: keyInstanceId });
1716
+ }
1717
+ }, response.Body['KeyPair']['ReferenceParameters']['SelectorSet']['Selector']['Value']);
1718
+ }
1719
+ });
1720
+ }
1721
+
1722
+ // 802.1x request to process a Certificate Signing Request, we ask Intel AMT to sign the request
1723
+ function attempt8021xCRSRequest(dev, event) {
1724
+ console.log('attempt8021xCRSRequest', event);
1725
+
1726
+ var keyPair = '<a:EndpointReference><a:Address>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:Address><a:ReferenceParameters><w:ResourceURI>http://intel.com/wbem/wscim/1/amt-schema/1/AMT_PublicPrivateKeyPair</w:ResourceURI><w:SelectorSet><w:Selector Name="InstanceID">' + event.keyInstanceId + '</w:Selector></w:SelectorSet></a:ReferenceParameters></a:EndpointReference>'; // keyPair EPR Reference
1727
+ var signingAlgorithm = 1; // SHA-256 RSA, highest allowed value.
1728
+ var nullSignedCertificateRequest = null; // DEREncodedRequest
1729
+
1730
+ dev.amtstack.AMT_PublicKeyManagementService_GeneratePKCS10RequestEx(keyPair, signingAlgorithm, nullSignedCertificateRequest, function (stack, name, response, status) {
1731
+ if (status != 200) {
1732
+ // Failed to get the generated key pair
1733
+ dev.consoleMsg("Failed to sign the certificate request.");
1734
+ } else {
1735
+ console.log('GeneratePKCS10RequestEx', status, response);
1736
+
1737
+ // We got a signed certificate request, return that to the server
1738
+ dev.consoleMsg("Generated a signed certificate request.");
1739
+ var domain = parent.config.domains[dev.domainid];
1740
+ parent.DispatchEvent([domain.amtmanager['802.1x'].satellitecredentials], obj, { action: 'satellite', subaction: '802.1x-CSR-Response', satelliteFlags: 2, nodeid: dev.nodeid, icon: dev.icon, domain: dev.nodeid.split('/')[1], nolog: 1, reqid: dev.netAuthSatReqId, authProtocol: domain.amtmanager['802.1x'].authenticationprotocol, devname: dev.name, osname: dev.rname });
1741
+ }
1742
+ });
1743
+ }
1744
+
1745
1746
//
1747
// Intel AMT Server Root Certificate
meshuser.js
+3
-3
@@ -4824,10 +4824,10 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
4824
case 'satellite': {
4825
// Command indicates this is a MeshCentral Satellite session and what featues it supports
4826
if ((command.setFlags != null) && (typeof command.setFlags == 'number')) { obj.satelliteFlags = command.setFlags; }
4827
- if ((command.reqid != null) && (command.response != null) && (typeof command.satelliteFlags == 'number')) {
4828
- const event = { action: 'satelliteResponse', reqid: command.reqid, response: command.response, satelliteFlags: command.satelliteFlags, nolog: 1 }
4827
+ if ((command.reqid != null) && (typeof command.satelliteFlags == 'number')) {
4828
+ const event = { action: 'satelliteResponse', subaction: command.subaction, reqid: command.reqid, response: command.response, satelliteFlags: command.satelliteFlags, nolog: 1 }
4829
if (typeof command.nodeid == 'string') { event.nodeid = command.nodeid; }
4830
- parent.parent.DispatchEvent([ '*' ], obj, event);
4830
+ parent.parent.DispatchEvent(['*'], obj, event);
4831
}
4832
break;
4833
}