|
|
cf0866 |
From 3f6ed4393dfa9ddf982e326065a3ea160bef90b6 Mon Sep 17 00:00:00 2001
|
|
|
cf0866 |
From: Antonio Torres <antorres@redhat.com>
|
|
|
cf0866 |
Date: Tue, 23 Feb 2021 16:11:59 +0100
|
|
|
cf0866 |
Subject: [PATCH] Add check for IPA KRA Agent
|
|
|
cf0866 |
|
|
|
cf0866 |
Add check to validate KRA Agent in case KRA is installed, including
|
|
|
cf0866 |
checking for the KRA Agent LDAP entry.
|
|
|
cf0866 |
|
|
|
cf0866 |
Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1894781
|
|
|
cf0866 |
Signed-off-by: Antonio Torres <antorres@redhat.com>
|
|
|
cf0866 |
---
|
|
|
cf0866 |
README.md | 16 ++-
|
|
|
cf0866 |
src/ipahealthcheck/ipa/certs.py | 167 +++++++++++++++++++-------------
|
|
|
cf0866 |
2 files changed, 112 insertions(+), 71 deletions(-)
|
|
|
cf0866 |
|
|
|
cf0866 |
diff --git a/README.md b/README.md
|
|
|
cf0866 |
index b9c60a2..0f3ed6a 100644
|
|
|
cf0866 |
--- a/README.md
|
|
|
cf0866 |
+++ b/README.md
|
|
|
cf0866 |
@@ -547,7 +547,21 @@ Verify the description and userCertificate values in uid=ipara,ou=People,o=ipaca
|
|
|
cf0866 |
"kw": {
|
|
|
cf0866 |
"expected": "2;125;CN=Certificate Authority,O=EXAMPLE.TEST;CN=IPA RA,O=EXAMPLE.TEST",
|
|
|
cf0866 |
"got": "2;7;CN=Certificate Authority,O=EXAMPLE.TEST;CN=IPA RA,O=EXAMPLE.TEST",
|
|
|
cf0866 |
- "msg": "RA agent description does not match 2;7;CN=Certificate Authority,O=EXAMPLE.TEST;CN=IPA RA,O=EXAMPLE.TEST in LDAP and expected 2;125;CN=Certificate Authority,O=EXAMPLE.TEST;CN=IPA RA,O=EXAMPLE.TEST"
|
|
|
cf0866 |
+ "msg": "RA agent description does not match. Found 2;7;CN=Certificate Authority,O=EXAMPLE.TEST;CN=IPA RA,O=EXAMPLE.TEST in LDAP and expected 2;125;CN=Certificate Authority,O=EXAMPLE.TEST;CN=IPA RA,O=EXAMPLE.TEST"
|
|
|
cf0866 |
+ }
|
|
|
cf0866 |
+ }
|
|
|
cf0866 |
+
|
|
|
cf0866 |
+### IPAKRAAgent
|
|
|
cf0866 |
+Verify the description and userCertificate values in uid=ipakra,ou=people,o=kra,o=ipaca.
|
|
|
cf0866 |
+
|
|
|
cf0866 |
+ {
|
|
|
cf0866 |
+ "source": "ipahealthcheck.ipa.certs",
|
|
|
cf0866 |
+ "check": "IPAKRAAgent",
|
|
|
cf0866 |
+ "result": "ERROR",
|
|
|
cf0866 |
+ "kw": {
|
|
|
cf0866 |
+ "expected": "2;125;CN=Certificate Authority,O=EXAMPLE.TEST;CN=IPA RA,O=EXAMPLE.TEST",
|
|
|
cf0866 |
+ "got": "2;7;CN=Certificate Authority,O=EXAMPLE.TEST;CN=IPA RA,O=EXAMPLE.TEST",
|
|
|
cf0866 |
+ "msg": "KRA agent description does not match. Found 2;7;CN=Certificate Authority,O=EXAMPLE.TEST;CN=IPA RA,O=EXAMPLE.TEST in LDAP and expected 2;125;CN=Certificate Authority,O=EXAMPLE.TEST;CN=IPA RA,O=EXAMPLE.TEST"
|
|
|
cf0866 |
}
|
|
|
cf0866 |
}
|
|
|
cf0866 |
|
|
|
cf0866 |
diff --git a/src/ipahealthcheck/ipa/certs.py b/src/ipahealthcheck/ipa/certs.py
|
|
|
cf0866 |
index d3043d0..32c0d76 100644
|
|
|
cf0866 |
--- a/src/ipahealthcheck/ipa/certs.py
|
|
|
cf0866 |
+++ b/src/ipahealthcheck/ipa/certs.py
|
|
|
cf0866 |
@@ -724,6 +724,83 @@ class IPAOpenSSLChainValidation(IPAPlugin):
|
|
|
cf0866 |
self, constants.SUCCESS, key=cert)
|
|
|
cf0866 |
|
|
|
cf0866 |
|
|
|
cf0866 |
+def check_agent(plugin, base_dn, agent_type):
|
|
|
cf0866 |
+ """Check RA/KRA Agent"""
|
|
|
cf0866 |
+
|
|
|
cf0866 |
+ try:
|
|
|
cf0866 |
+ cert = x509.load_certificate_from_file(paths.RA_AGENT_PEM)
|
|
|
cf0866 |
+ except Exception as e:
|
|
|
cf0866 |
+ yield Result(plugin, constants.ERROR,
|
|
|
cf0866 |
+ error=str(e),
|
|
|
cf0866 |
+ msg='Unable to load RA cert: {error}')
|
|
|
cf0866 |
+ return
|
|
|
cf0866 |
+ serial_number = cert.serial_number
|
|
|
cf0866 |
+ subject = DN(cert.subject)
|
|
|
cf0866 |
+ issuer = DN(cert.issuer)
|
|
|
cf0866 |
+ description = '2;%d;%s;%s' % (serial_number, issuer, subject)
|
|
|
cf0866 |
+ logger.debug('%s agent description should be %s', agent_type, description)
|
|
|
cf0866 |
+ db_filter = ldap2.ldap2.combine_filters(
|
|
|
cf0866 |
+ [
|
|
|
cf0866 |
+ ldap2.ldap2.make_filter({'objectClass': 'inetOrgPerson'}),
|
|
|
cf0866 |
+ ldap2.ldap2.make_filter(
|
|
|
cf0866 |
+ {'description': ';%s;%s' % (issuer, subject)},
|
|
|
cf0866 |
+ exact=False, trailing_wildcard=False),
|
|
|
cf0866 |
+ ],
|
|
|
cf0866 |
+ ldap2.ldap2.MATCH_ALL)
|
|
|
cf0866 |
+ try:
|
|
|
cf0866 |
+ entries = plugin.conn.get_entries(base_dn,
|
|
|
cf0866 |
+ plugin.conn.SCOPE_SUBTREE,
|
|
|
cf0866 |
+ db_filter)
|
|
|
cf0866 |
+ except errors.NotFound:
|
|
|
cf0866 |
+ yield Result(plugin, constants.ERROR,
|
|
|
cf0866 |
+ description=description,
|
|
|
cf0866 |
+ msg='%s agent not found in LDAP' % agent_type)
|
|
|
cf0866 |
+ return
|
|
|
cf0866 |
+ except Exception as e:
|
|
|
cf0866 |
+ yield Result(plugin, constants.ERROR,
|
|
|
cf0866 |
+ error=str(e),
|
|
|
cf0866 |
+ msg='Retrieving %s agent from LDAP failed {error}'
|
|
|
cf0866 |
+ % agent_type)
|
|
|
cf0866 |
+ return
|
|
|
cf0866 |
+ else:
|
|
|
cf0866 |
+ logger.debug('%s agent description is %s', agent_type, description)
|
|
|
cf0866 |
+ if len(entries) != 1:
|
|
|
cf0866 |
+ yield Result(plugin, constants.ERROR,
|
|
|
cf0866 |
+ found=len(entries),
|
|
|
cf0866 |
+ msg='Too many %s agent entries found, {found}'
|
|
|
cf0866 |
+ % agent_type)
|
|
|
cf0866 |
+ return
|
|
|
cf0866 |
+ entry = entries[0]
|
|
|
cf0866 |
+ raw_desc = entry.get('description')
|
|
|
cf0866 |
+ if raw_desc is None:
|
|
|
cf0866 |
+ yield Result(plugin, constants.ERROR,
|
|
|
cf0866 |
+ msg='%s agent is missing the description '
|
|
|
cf0866 |
+ 'attribute or it is not readable' % agent_type)
|
|
|
cf0866 |
+ return
|
|
|
cf0866 |
+ ra_desc = raw_desc[0]
|
|
|
cf0866 |
+ ra_certs = entry.get('usercertificate')
|
|
|
cf0866 |
+ if ra_desc != description:
|
|
|
cf0866 |
+ yield Result(plugin, constants.ERROR,
|
|
|
cf0866 |
+ expected=description,
|
|
|
cf0866 |
+ got=ra_desc,
|
|
|
cf0866 |
+ msg='%s agent description does not match. Found '
|
|
|
cf0866 |
+ '{got} in LDAP and expected {expected}' % agent_type)
|
|
|
cf0866 |
+ return
|
|
|
cf0866 |
+ found = False
|
|
|
cf0866 |
+ for candidate in ra_certs:
|
|
|
cf0866 |
+ if candidate == cert:
|
|
|
cf0866 |
+ found = True
|
|
|
cf0866 |
+ break
|
|
|
cf0866 |
+ if not found:
|
|
|
cf0866 |
+ yield Result(plugin, constants.ERROR,
|
|
|
cf0866 |
+ certfile=paths.RA_AGENT_PEM,
|
|
|
cf0866 |
+ dn=str(entry.dn),
|
|
|
cf0866 |
+ msg='%s agent certificate in {certfile} not '
|
|
|
cf0866 |
+ 'found in LDAP userCertificate attribute '
|
|
|
cf0866 |
+ 'for the entry {dn}' % agent_type)
|
|
|
cf0866 |
+ yield Result(plugin, constants.SUCCESS)
|
|
|
cf0866 |
+
|
|
|
cf0866 |
+
|
|
|
cf0866 |
@registry
|
|
|
cf0866 |
class IPARAAgent(IPAPlugin):
|
|
|
cf0866 |
"""Validate the RA Agent used to talk to the CA
|
|
|
cf0866 |
@@ -739,82 +816,32 @@ class IPARAAgent(IPAPlugin):
|
|
|
cf0866 |
logger.debug('CA is not configured, skipping RA Agent check')
|
|
|
cf0866 |
return
|
|
|
cf0866 |
|
|
|
cf0866 |
- try:
|
|
|
cf0866 |
- cert = x509.load_certificate_from_file(paths.RA_AGENT_PEM)
|
|
|
cf0866 |
- except Exception as e:
|
|
|
cf0866 |
- yield Result(self, constants.ERROR,
|
|
|
cf0866 |
- error=str(e),
|
|
|
cf0866 |
- msg='Unable to load RA cert: {error}')
|
|
|
cf0866 |
- return
|
|
|
cf0866 |
+ base_dn = DN('uid=ipara,ou=people,o=ipaca')
|
|
|
cf0866 |
+ yield from check_agent(self, base_dn, 'RA')
|
|
|
cf0866 |
|
|
|
cf0866 |
- serial_number = cert.serial_number
|
|
|
cf0866 |
- subject = DN(cert.subject)
|
|
|
cf0866 |
- issuer = DN(cert.issuer)
|
|
|
cf0866 |
- description = '2;%d;%s;%s' % (serial_number, issuer, subject)
|
|
|
cf0866 |
|
|
|
cf0866 |
- logger.debug('RA agent description should be %s', description)
|
|
|
cf0866 |
+@registry
|
|
|
cf0866 |
+class IPAKRAAgent(IPAPlugin):
|
|
|
cf0866 |
+ """Validate the KRA Agent
|
|
|
cf0866 |
|
|
|
cf0866 |
- db_filter = ldap2.ldap2.combine_filters(
|
|
|
cf0866 |
- [
|
|
|
cf0866 |
- ldap2.ldap2.make_filter({'objectClass': 'inetOrgPerson'}),
|
|
|
cf0866 |
- ldap2.ldap2.make_filter({'sn': 'ipara'}),
|
|
|
cf0866 |
- ldap2.ldap2.make_filter(
|
|
|
cf0866 |
- {'description': ';%s;%s' % (issuer, subject)},
|
|
|
cf0866 |
- exact=False, trailing_wildcard=False),
|
|
|
cf0866 |
- ],
|
|
|
cf0866 |
- ldap2.ldap2.MATCH_ALL)
|
|
|
cf0866 |
+ Compare the description and usercertificate values.
|
|
|
cf0866 |
+ """
|
|
|
cf0866 |
|
|
|
cf0866 |
- base_dn = DN(('o', 'ipaca'))
|
|
|
cf0866 |
- try:
|
|
|
cf0866 |
- entries = self.conn.get_entries(base_dn,
|
|
|
cf0866 |
- self.conn.SCOPE_SUBTREE,
|
|
|
cf0866 |
- db_filter)
|
|
|
cf0866 |
- except errors.NotFound:
|
|
|
cf0866 |
- yield Result(self, constants.ERROR,
|
|
|
cf0866 |
- description=description,
|
|
|
cf0866 |
- msg='RA agent not found in LDAP')
|
|
|
cf0866 |
+ requires = ('dirsrv',)
|
|
|
cf0866 |
+
|
|
|
cf0866 |
+ @duration
|
|
|
cf0866 |
+ def check(self):
|
|
|
cf0866 |
+ if not self.ca.is_configured():
|
|
|
cf0866 |
+ logger.debug('CA is not configured, skipping KRA Agent check')
|
|
|
cf0866 |
return
|
|
|
cf0866 |
- except Exception as e:
|
|
|
cf0866 |
- yield Result(self, constants.ERROR,
|
|
|
cf0866 |
- error=str(e),
|
|
|
cf0866 |
- msg='Retrieving RA agent from LDAP failed {error}')
|
|
|
cf0866 |
+
|
|
|
cf0866 |
+ kra = krainstance.KRAInstance(api.env.realm)
|
|
|
cf0866 |
+ if not kra.is_installed():
|
|
|
cf0866 |
+ logger.debug('KRA is not installed, skipping KRA Agent check')
|
|
|
cf0866 |
return
|
|
|
cf0866 |
- else:
|
|
|
cf0866 |
- logger.debug('RA agent description is %s', description)
|
|
|
cf0866 |
- if len(entries) != 1:
|
|
|
cf0866 |
- yield Result(self, constants.ERROR,
|
|
|
cf0866 |
- found=len(entries),
|
|
|
cf0866 |
- msg='Too many RA agent entries found, {found}')
|
|
|
cf0866 |
- return
|
|
|
cf0866 |
- entry = entries[0]
|
|
|
cf0866 |
- raw_desc = entry.get('description')
|
|
|
cf0866 |
- if raw_desc is None:
|
|
|
cf0866 |
- yield Result(self, constants.ERROR,
|
|
|
cf0866 |
- msg='RA agent is missing the description '
|
|
|
cf0866 |
- 'attribute or it is not readable')
|
|
|
cf0866 |
- return
|
|
|
cf0866 |
- ra_desc = raw_desc[0]
|
|
|
cf0866 |
- ra_certs = entry.get('usercertificate')
|
|
|
cf0866 |
- if ra_desc != description:
|
|
|
cf0866 |
- yield Result(self, constants.ERROR,
|
|
|
cf0866 |
- expected=description,
|
|
|
cf0866 |
- got=ra_desc,
|
|
|
cf0866 |
- msg='RA agent description does not match. Found '
|
|
|
cf0866 |
- '{got} in LDAP and expected {expected}')
|
|
|
cf0866 |
- return
|
|
|
cf0866 |
- found = False
|
|
|
cf0866 |
- for candidate in ra_certs:
|
|
|
cf0866 |
- if candidate == cert:
|
|
|
cf0866 |
- found = True
|
|
|
cf0866 |
- break
|
|
|
cf0866 |
- if not found:
|
|
|
cf0866 |
- yield Result(self, constants.ERROR,
|
|
|
cf0866 |
- certfile=paths.RA_AGENT_PEM,
|
|
|
cf0866 |
- dn=str(entry.dn),
|
|
|
cf0866 |
- msg='RA agent certificate in {certfile} not '
|
|
|
cf0866 |
- 'found in LDAP userCertificate attribute '
|
|
|
cf0866 |
- 'for the entry {dn}')
|
|
|
cf0866 |
- yield Result(self, constants.SUCCESS)
|
|
|
cf0866 |
+
|
|
|
cf0866 |
+ base_dn = DN('uid=ipakra,ou=people,o=kra,o=ipaca')
|
|
|
cf0866 |
+ yield from check_agent(self, base_dn, 'KRA')
|
|
|
cf0866 |
|
|
|
cf0866 |
|
|
|
cf0866 |
@registry
|
|
|
cf0866 |
--
|
|
|
cf0866 |
2.26.2
|
|
|
cf0866 |
|