From 5adb5fa780f9cf67da884465b36751504c5e7a7c Mon Sep 17 00:00:00 2001 From: Jarrod Johnson Date: Mon, 3 Nov 2025 14:02:33 -0500 Subject: [PATCH] Automatically sign XCC certificates on discover If an XCC doesn't have a 'real' certificate, sign it with the confluent CA for 47 days. --- confluent_server/confluent/discovery/core.py | 6 ++- .../confluent/discovery/handlers/generic.py | 38 +++++++++++++++++++ .../discovery/handlers/redfishbmc.py | 9 +++++ .../confluent/discovery/handlers/xcc.py | 10 +++++ 4 files changed, 62 insertions(+), 1 deletion(-) diff --git a/confluent_server/confluent/discovery/core.py b/confluent_server/confluent/discovery/core.py index 6941845c..1e7aed8d 100644 --- a/confluent_server/confluent/discovery/core.py +++ b/confluent_server/confluent/discovery/core.py @@ -98,6 +98,7 @@ import eventlet import eventlet.greenpool import eventlet.semaphore + autosensors = set() scanner = None @@ -1472,7 +1473,7 @@ def discover_node(cfg, handler, info, nodename, manual): break log.log({'info': 'Discovered {0} ({1})'.format(nodename, handler.devname)}) - if nodeconfig: + if nodeconfig or handler.current_cert_self_signed(): bmcaddr = cfg.get_node_attributes(nodename, 'hardwaremanagement.manager') bmcaddr = bmcaddr.get(nodename, {}).get('hardwaremanagement.manager', {}).get('value', '') if not bmcaddr: @@ -1481,9 +1482,12 @@ def discover_node(cfg, handler, info, nodename, manual): bmcaddr = bmcaddr.split('/', 1)[0] wait_for_connection(bmcaddr) socket.getaddrinfo(bmcaddr, 443) + if nodeconfig: subprocess.check_call(['/opt/confluent/bin/nodeconfig', nodename] + nodeconfig) log.log({'info': 'Configured {0} ({1})'.format(nodename, handler.devname)}) + if handler.current_cert_self_signed(): + handler.autosign_certificate() info['discostatus'] = 'discovered' for i in pending_by_uuid.get(curruuid, []): diff --git a/confluent_server/confluent/discovery/handlers/generic.py b/confluent_server/confluent/discovery/handlers/generic.py index 2e941238..5f00639f 100644 --- a/confluent_server/confluent/discovery/handlers/generic.py +++ b/confluent_server/confluent/discovery/handlers/generic.py @@ -17,6 +17,10 @@ import errno import eventlet import socket webclient = eventlet.import_patched('pyghmi.util.webclient') +try: + import cryptography.x509 as x509 +except ImportError: + x509 = None class NodeHandler(object): https_supported = True @@ -59,6 +63,40 @@ class NodeHandler(object): # may occur against the target in a short while return True + def current_cert_self_signed(self): + if not x509: + return + if not self._ipaddr: + return + try: + wc = webclient.SecureHTTPConnection(self._ipaddr, verifycallback=self._savecert, port=443) + wc.connect() + wc.close() + if not self._fp: + return False + # Check if certificate is self-signed by comparing issuer and subject + cert = self._fp + certobj = x509.load_der_x509_certificate(cert) + skid = None + akid = None + for ext in certobj.extensions: + if ext.oid == x509.ExtensionOID.SUBJECT_KEY_IDENTIFIER: + skid = ext.value + elif ext.oid == x509.ExtensionOID.AUTHORITY_KEY_IDENTIFIER: + akid = ext.value + if akid: + if skid.digest == akid.key_identifier: + return True + elif certobj.issuer == certobj.subject: + return True + except Exception: + pass + return False + + def autosign_certificate(self): + # A no-op by default + return + def scan(self): # Do completely passive things to enhance data. # Probe is permitted to for example attempt a login diff --git a/confluent_server/confluent/discovery/handlers/redfishbmc.py b/confluent_server/confluent/discovery/handlers/redfishbmc.py index 58b53054..7573370f 100644 --- a/confluent_server/confluent/discovery/handlers/redfishbmc.py +++ b/confluent_server/confluent/discovery/handlers/redfishbmc.py @@ -23,6 +23,7 @@ try: from urllib import urlencode except ImportError: from urllib.parse import urlencode +import eventlet.green.subprocess as subprocess getaddrinfo = eventlet.support.greendns.getaddrinfo @@ -326,6 +327,14 @@ class NodeHandler(generic.NodeHandler): raise exc.TargetEndpointUnreachable( 'hardwaremanagement.manager must be set to desired address (No IPv6 Link Local detected)') + def autosign_certificate(self): + nodename = self.nodename + hwmgt_method = self.configmanager.get_node_attributes( + nodename, 'hardwaremanagement.method').get( + nodename, {}).get('hardwaremanagement.method', {}).get('value', 'ipmi') + if hwmgt_method != 'redfish': + return + subprocess.check_call(['/opt/confluent/bin/nodecertutil', nodename, 'signbmccert', '--days', '47']) def remote_nodecfg(nodename, cfm): cfg = cfm.get_node_attributes( diff --git a/confluent_server/confluent/discovery/handlers/xcc.py b/confluent_server/confluent/discovery/handlers/xcc.py index b39574d5..2637a6aa 100644 --- a/confluent_server/confluent/discovery/handlers/xcc.py +++ b/confluent_server/confluent/discovery/handlers/xcc.py @@ -29,6 +29,7 @@ import eventlet.green.socket as socket webclient = eventlet.import_patched('pyghmi.util.webclient') import struct getaddrinfo = eventlet.support.greendns.getaddrinfo +import eventlet.green.subprocess as subprocess def fixuuid(baduuid): @@ -704,6 +705,15 @@ class NodeHandler(immhandler.NodeHandler): if em: self.configmanager.set_node_attributes( {em: {'id.uuid': enclosureuuid}}) + def autosign_certificate(self): + nodename = self.nodename + hwmgt_method = self.configmanager.get_node_attributes( + nodename, 'hardwaremanagement.method').get( + nodename, {}).get('hardwaremanagement.method', {}).get('value', 'ipmi') + if hwmgt_method != 'redfish': + return + subprocess.check_call(['/opt/confluent/bin/nodecertutil', nodename, 'signbmccert', '--days', '47']) + def remote_nodecfg(nodename, cfm): cfg = cfm.get_node_attributes(