Blame SOURCES/0001-Fix-pki-healthcheck-for-clones.patch

c16a19
From 7d62105c676fc79e0c32766c41cd034655a524ff Mon Sep 17 00:00:00 2001
c16a19
From: "Endi S. Dewata" <edewata@redhat.com>
c16a19
Date: Tue, 25 Jan 2022 16:29:53 -0600
c16a19
Subject: [PATCH] Fix pki-healthcheck for clones
c16a19
c16a19
Previously the ClonesConnectivyAndDataCheck.check_kra_clones()
c16a19
was trying to check KRA clone status by retrieving a key using
c16a19
the subsystem cert. This operation did not work since the user
c16a19
associated with the cert did not have access to the keys. The
c16a19
code has been changed to get the status from GetStatus service
c16a19
instead. The original code might be moved into IPA later so it
c16a19
could run with IPA's RA agent credentials which would allow
c16a19
access to the keys.
c16a19
c16a19
Previously the ClonesPlugin.contact_subsystem_using_sslget()
c16a19
used sslget to call GetStatus service and returned the entire
c16a19
output which was then incorrectly processed in XML format. The
c16a19
method has been renamed to get_status() and changed to use
c16a19
PKIConnection and process the response in either JSON or XML
c16a19
format, then only return the subsystem status. All callers
c16a19
have been updated accordingly.
c16a19
c16a19
The ClonesPlugin.contact_subsystem_using_pki() is no longer
c16a19
used so it has been removed.
c16a19
---
c16a19
 .../clones/connectivity_and_data.py           | 130 ++++++++----------
c16a19
 .../pki/server/healthcheck/clones/plugin.py   |  75 ++++------
c16a19
 base/server/python/pki/server/__init__.py     |   8 +-
c16a19
 3 files changed, 91 insertions(+), 122 deletions(-)
c16a19
c16a19
diff --git a/base/server/healthcheck/pki/server/healthcheck/clones/connectivity_and_data.py b/base/server/healthcheck/pki/server/healthcheck/clones/connectivity_and_data.py
c16a19
index ca5d6dae48..d9bb480f7f 100644
c16a19
--- a/base/server/healthcheck/pki/server/healthcheck/clones/connectivity_and_data.py
c16a19
+++ b/base/server/healthcheck/pki/server/healthcheck/clones/connectivity_and_data.py
c16a19
@@ -46,93 +46,83 @@ class ClonesConnectivyAndDataCheck(ClonesPlugin):
c16a19
 
c16a19
     def check_kra_clones(self):
c16a19
         for host in self.clone_kras:
c16a19
-            cur_clone_msg = ' Host: ' + host.Hostname + ' Port: ' + host.SecurePort
c16a19
-            # Reach out and get some keys or requests , to serve as a data and connectivity check
c16a19
+
c16a19
+            url = 'https://' + host.Hostname + ':' + host.SecurePort
c16a19
+
c16a19
             try:
c16a19
-                client_nick = self.security_domain.config.get('ca.connector.KRA.nickName')
c16a19
-
c16a19
-                output = self.contact_subsystem_using_pki(
c16a19
-                    host.SecurePort, host.Hostname, client_nick,
c16a19
-                    self.passwd, self.db_dir, 'kra-key-show', ['0x01'])
c16a19
-
c16a19
-                # check to see if we either got a key or a key not found exception
c16a19
-                # of which either will imply a successful connection
c16a19
-                if output is not None:
c16a19
-                    key_found = output.find('Key ID:')
c16a19
-                    key_not_found = output.find('KeyNotFoundException:')
c16a19
-                    if key_found >= 0:
c16a19
-                        logger.info('Key material found from kra clone.')
c16a19
-
c16a19
-                    if key_not_found >= 0:
c16a19
-                        logger.info('key not found, possibly empty kra')
c16a19
-
c16a19
-                    if key_not_found == -1 and key_found == -1:
c16a19
-                        logger.info('Failure to get key material from kra')
c16a19
-                        raise BaseException('KRA clone problem detected ' + cur_clone_msg)
c16a19
-                else:
c16a19
-                    raise BaseException('No data obtained from KRA clone.' + cur_clone_msg)
c16a19
+                status = self.get_status(
c16a19
+                    host.Hostname,
c16a19
+                    host.SecurePort,
c16a19
+                    '/kra/admin/kra/getStatus')
c16a19
 
c16a19
-            except BaseException as e:
c16a19
-                logger.error("Internal error testing KRA clone. %s", e)
c16a19
-                raise BaseException('Internal error testing KRA clone.' + cur_clone_msg)
c16a19
+                logger.info('KRA at %s is %s', url, status)
c16a19
 
c16a19
-        return
c16a19
+                if status != 'running':
c16a19
+                    raise Exception('KRA at %s is %s' % (url, status))
c16a19
+
c16a19
+            except Exception as e:
c16a19
+                logger.error('Unable to reach KRA at %s: %s', url, e)
c16a19
+                raise Exception('Unable to reach KRA at %s: %s' % (url, e))
c16a19
 
c16a19
     def check_ocsp_clones(self):
c16a19
         for host in self.clone_ocsps:
c16a19
-            cur_clone_msg = ' Host: ' + host.Hostname + ' Port: ' + host.SecurePort
c16a19
-            # Reach out to the ocsp clones
c16a19
+
c16a19
+            url = 'https://' + host.Hostname + ':' + host.SecurePort
c16a19
+
c16a19
             try:
c16a19
-                output = self.contact_subsystem_using_sslget(
c16a19
-                    host.SecurePort, host.Hostname, None,
c16a19
-                    self.passwd, self.db_dir, None, '/ocsp/admin/ocsp/getStatus')
c16a19
-
c16a19
-                good_status = output.find('<State>1</State>')
c16a19
-                if good_status == -1:
c16a19
-                    raise BaseException('OCSP clone problem detected.' + cur_clone_msg)
c16a19
-                logger.info('good_status %s ', good_status)
c16a19
-            except BaseException as e:
c16a19
-                logger.error("Internal error testing OCSP clone.  %s", e)
c16a19
-                raise BaseException('Internal error testing OCSP clone.' + cur_clone_msg)
c16a19
+                status = self.get_status(
c16a19
+                    host.Hostname,
c16a19
+                    host.SecurePort,
c16a19
+                    '/ocsp/admin/ocsp/getStatus')
c16a19
 
c16a19
-        return
c16a19
+                logger.info('OCSP at %s is %s', url, status)
c16a19
+
c16a19
+                if status != 'running':
c16a19
+                    raise Exception('OCSP at %s is %s' % (url, status))
c16a19
+
c16a19
+            except Exception as e:
c16a19
+                logger.error('Unable to reach OCSP at %s: %s', url, e)
c16a19
+                raise Exception('Unable to reach OCSP at %s: %s' % (url, e))
c16a19
 
c16a19
     def check_tks_clones(self):
c16a19
         for host in self.clone_tkss:
c16a19
-            cur_clone_msg = ' Host: ' + host.Hostname + ' Port: ' + host.SecurePort
c16a19
-            # Reach out to the tks clones
c16a19
+
c16a19
+            url = 'https://' + host.Hostname + ':' + host.SecurePort
c16a19
+
c16a19
             try:
c16a19
-                output = self.contact_subsystem_using_sslget(
c16a19
-                    host.SecurePort, host.Hostname, None,
c16a19
-                    self.passwd, self.db_dir, None, '/tks/admin/tks/getStatus')
c16a19
-
c16a19
-                good_status = output.find('<State>1</State>')
c16a19
-                if good_status == -1:
c16a19
-                    raise BaseException('TKS clone problem detected.' + cur_clone_msg)
c16a19
-                logger.info('good_status %s ', good_status)
c16a19
-            except BaseException as e:
c16a19
-                logger.error("Internal error testing TKS clone. %s", e)
c16a19
-                raise BaseException('Internal error testing TKS clone.' + cur_clone_msg)
c16a19
+                status = self.get_status(
c16a19
+                    host.Hostname,
c16a19
+                    host.SecurePort,
c16a19
+                    '/tks/admin/tks/getStatus')
c16a19
 
c16a19
-        return
c16a19
+                logger.info('TKS at %s is %s', url, status)
c16a19
+
c16a19
+                if status != 'running':
c16a19
+                    raise Exception('TKS at %s is %s' % (url, status))
c16a19
+
c16a19
+            except Exception as e:
c16a19
+                logger.error('Unable to reach TKS at %s: %s', url, e)
c16a19
+                raise Exception('Unable to reach TKS at %s: %s' % (url, e))
c16a19
 
c16a19
     def check_tps_clones(self):
c16a19
         for host in self.clone_tpss:
c16a19
-            cur_clone_msg = ' Host: ' + host.Hostname + ' Port: ' + host.SecurePort
c16a19
-            # Reach out to the tps clones
c16a19
+
c16a19
+            url = 'https://' + host.Hostname + ':' + host.SecurePort
c16a19
+
c16a19
             try:
c16a19
-                output = self.contact_subsystem_using_sslget(
c16a19
-                    host.SecurePort, host.Hostname, None,
c16a19
-                    self.passwd, self.db_dir, None, '/tps/admin/tps/getStatus')
c16a19
-
c16a19
-                good_status = output.find('<State>1</State>')
c16a19
-                if good_status == -1:
c16a19
-                    raise BaseException('TPS clone problem detected.' + cur_clone_msg)
c16a19
-                logger.info('good_status  %s ', good_status)
c16a19
-            except BaseException as e:
c16a19
-                logger.error("Internal error testing TPS clone. %s", e)
c16a19
-                raise BaseException('Internal error testing TPS clone.' + cur_clone_msg)
c16a19
-        return
c16a19
+                status = self.get_status(
c16a19
+                    host.Hostname,
c16a19
+                    host.SecurePort,
c16a19
+                    '/tps/admin/tps/getStatus')
c16a19
+
c16a19
+                logger.info('TPS at %s is %s', url, status)
c16a19
+
c16a19
+                if status != 'running':
c16a19
+                    raise Exception('TPS at %s is %s' % (url, status))
c16a19
+
c16a19
+            except Exception as e:
c16a19
+                logger.error('Unable to reach TPS at %s: %s', url, e)
c16a19
+                raise Exception('Unable to reach TPS at %s: %s' % (url, e))
c16a19
 
c16a19
     @duration
c16a19
     def check(self):
c16a19
diff --git a/base/server/healthcheck/pki/server/healthcheck/clones/plugin.py b/base/server/healthcheck/pki/server/healthcheck/clones/plugin.py
c16a19
index 2472f35b5b..824c36a1a9 100644
c16a19
--- a/base/server/healthcheck/pki/server/healthcheck/clones/plugin.py
c16a19
+++ b/base/server/healthcheck/pki/server/healthcheck/clones/plugin.py
c16a19
@@ -6,6 +6,10 @@
c16a19
 # SPDX-License-Identifier: GPL-2.0-or-later
c16a19
 #
c16a19
 
c16a19
+import json
c16a19
+import logging
c16a19
+import xml.etree.ElementTree as ET
c16a19
+
c16a19
 from ipahealthcheck.core.plugin import Plugin, Registry
c16a19
 from pki.server.instance import PKIInstance
c16a19
 from pki.client import PKIConnection
c16a19
@@ -13,9 +17,6 @@ from pki.system import SecurityDomainClient
c16a19
 
c16a19
 from pki.server.healthcheck.core.main import merge_dogtag_config
c16a19
 
c16a19
-import logging
c16a19
-import subprocess
c16a19
-
c16a19
 logger = logging.getLogger(__name__)
c16a19
 
c16a19
 # Temporary workaround to skip VERBOSE data. Fix already pushed to upstream
c16a19
@@ -46,60 +47,36 @@ class ClonesPlugin(Plugin):
c16a19
 
c16a19
         self.instance = PKIInstance(self.config.instance_name)
c16a19
 
c16a19
-    def contact_subsystem_using_pki(
c16a19
-            self, subport, subhost, subsystemnick,
c16a19
-            token_pwd, db_path, cmd, exts=None):
c16a19
-        command = ["/usr/bin/pki",
c16a19
-                   "-p", str(subport),
c16a19
-                   "-h", subhost,
c16a19
-                   "-n", subsystemnick,
c16a19
-                   "-P", "https",
c16a19
-                   "-d", db_path,
c16a19
-                   "-c", token_pwd,
c16a19
-                   cmd]
c16a19
-
c16a19
-        if exts is not None:
c16a19
-            command.extend(exts)
c16a19
-
c16a19
-        output = None
c16a19
-        try:
c16a19
-            output = subprocess.check_output(command, stderr=subprocess.STDOUT)
c16a19
-        except subprocess.CalledProcessError as e:
c16a19
-            output = e.output.decode('utf-8')
c16a19
-            return output
c16a19
+    def get_status(self, host, port, path):
c16a19
 
c16a19
-        output = output.decode('utf-8')
c16a19
+        self.instance.export_ca_cert()
c16a19
 
c16a19
-        return output
c16a19
+        connection = PKIConnection(
c16a19
+            protocol='https',
c16a19
+            hostname=host,
c16a19
+            port=port,
c16a19
+            cert_paths=self.instance.ca_cert)
c16a19
 
c16a19
-    def contact_subsystem_using_sslget(
c16a19
-            self, port, host, subsystemnick,
c16a19
-            token_pwd, db_path, params, url):
c16a19
+        response = connection.get(path)
c16a19
 
c16a19
-        command = ["/usr/bin/sslget"]
c16a19
+        content_type = response.headers['Content-Type']
c16a19
+        content = response.text
c16a19
+        logger.info('Content:\n%s', content)
c16a19
 
c16a19
-        if subsystemnick is not None:
c16a19
-            command.extend(["-n", subsystemnick])
c16a19
+        # https://github.com/dogtagpki/pki/wiki/GetStatus-Service
c16a19
+        if content_type == 'application/json':
c16a19
+            json_response = json.loads(content)
c16a19
+            status = json_response['Response']['Status']
c16a19
 
c16a19
-        command.extend(["-p", token_pwd, "-d", db_path])
c16a19
-
c16a19
-        if params is not None:
c16a19
-            command.extend(["-e", params])
c16a19
-
c16a19
-        command.extend([
c16a19
-            "-r", url, host + ":" + port])
c16a19
-
c16a19
-        logger.info(' command : %s ', command)
c16a19
-        output = None
c16a19
-        try:
c16a19
-            output = subprocess.check_output(command, stderr=subprocess.STDOUT)
c16a19
-        except subprocess.CalledProcessError as e:
c16a19
-            output = e.output.decode('utf-8')
c16a19
-            return output
c16a19
+        elif content_type == 'application/xml':
c16a19
+            root = ET.fromstring(content)
c16a19
+            status = root.findtext('Status')
c16a19
 
c16a19
-        output = output.decode('utf-8')
c16a19
+        else:
c16a19
+            raise Exception('Unsupported content-type: %s' % content_type)
c16a19
 
c16a19
-        return output
c16a19
+        logger.info('Status: %s', status)
c16a19
+        return status
c16a19
 
c16a19
     def get_security_domain_data(self, host, port):
c16a19
         domain_data = None
c16a19
diff --git a/base/server/python/pki/server/__init__.py b/base/server/python/pki/server/__init__.py
c16a19
index 4fbb74684b..0515bbb197 100644
c16a19
--- a/base/server/python/pki/server/__init__.py
c16a19
+++ b/base/server/python/pki/server/__init__.py
c16a19
@@ -241,6 +241,10 @@ class PKIServer(object):
c16a19
     def jss_conf(self):
c16a19
         return os.path.join(self.conf_dir, 'jss.conf')
c16a19
 
c16a19
+    @property
c16a19
+    def ca_cert(self):
c16a19
+        return os.path.join(self.nssdb_dir, 'ca.crt')
c16a19
+
c16a19
     def is_valid(self):
c16a19
         return self.exists()
c16a19
 
c16a19
@@ -259,8 +263,6 @@ class PKIServer(object):
c16a19
 
c16a19
     def export_ca_cert(self):
c16a19
 
c16a19
-        ca_path = os.path.join(self.nssdb_dir, 'ca.crt')
c16a19
-
c16a19
         token = pki.nssdb.INTERNAL_TOKEN_NAME
c16a19
         nickname = self.get_sslserver_cert_nickname()
c16a19
 
c16a19
@@ -272,7 +274,7 @@ class PKIServer(object):
c16a19
         nssdb = self.open_nssdb(token=token)
c16a19
 
c16a19
         try:
c16a19
-            nssdb.extract_ca_cert(ca_path, nickname)
c16a19
+            nssdb.extract_ca_cert(self.ca_cert, nickname)
c16a19
         finally:
c16a19
             nssdb.close()
c16a19
 
c16a19
-- 
c16a19
2.33.1
c16a19