import ipa-healthcheck-0.7-6.module+el8.5.0+11410+91a33fe4

This commit is contained in:
CentOS Sources 2021-11-09 04:56:49 -05:00 committed by Stepan Oksanichenko
parent fdeb5dda89
commit 0776009f20
8 changed files with 1251 additions and 1 deletions

View File

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

View File

@ -0,0 +1,207 @@
From a6504bd7d32fe3553b9f6f807f3d84a1b87bb77c Mon Sep 17 00:00:00 2001
From: Antonio Torres <antorres@redhat.com>
Date: Wed, 24 Feb 2021 17:26:08 +0100
Subject: [PATCH] Add tests for KRA Agent validation
Add unit tests for KRA Agent validation.
Signed-off-by: Antonio Torres <antorres@redhat.com>
---
tests/test_ipa_agent.py | 174 +++++++++++++++++++++++++++++++++++++++-
1 file changed, 172 insertions(+), 2 deletions(-)
diff --git a/tests/test_ipa_agent.py b/tests/test_ipa_agent.py
index 6605745..9b691f7 100644
--- a/tests/test_ipa_agent.py
+++ b/tests/test_ipa_agent.py
@@ -4,11 +4,11 @@
from base import BaseTest
from unittest.mock import Mock, patch
-from util import capture_results, CAInstance
+from util import capture_results, CAInstance, KRAInstance
from ipahealthcheck.core import config, constants
from ipahealthcheck.ipa.plugin import registry
-from ipahealthcheck.ipa.certs import IPARAAgent
+from ipahealthcheck.ipa.certs import IPARAAgent, IPAKRAAgent
from ipalib import errors
from ipapython.dn import DN
@@ -218,3 +218,173 @@ class TestNSSAgent(BaseTest):
assert result.result == constants.SUCCESS
assert result.source == 'ipahealthcheck.ipa.certs'
assert result.check == 'IPARAAgent'
+
+
+class TestKRAAgent(BaseTest):
+ cert = IPACertificate()
+ patches = {
+ 'ldap.initialize':
+ Mock(return_value=mock_ldap_conn()),
+ 'ipaserver.install.krainstance.KRAInstance':
+ Mock(return_value=KRAInstance()),
+ 'ipalib.x509.load_certificate_from_file':
+ Mock(return_value=cert),
+ }
+
+ def test_kra_agent_ok(self):
+
+ attrs = dict(
+ description=['2;1;CN=ISSUER;CN=RA AGENT'],
+ usercertificate=[self.cert],
+ )
+ fake_conn = LDAPClient('ldap://localhost', no_schema=True)
+ ldapentry = LDAPEntry(fake_conn,
+ DN('uid=ipakra,ou=people,o=kra,o=ipaca'))
+ for attr, values in attrs.items():
+ ldapentry[attr] = values
+
+ framework = object()
+ registry.initialize(framework, config.Config())
+ f = IPAKRAAgent(registry)
+
+ f.conn = mock_ldap([ldapentry])
+ self.results = capture_results(f)
+
+ assert len(self.results) == 1
+
+ result = self.results.results[0]
+ assert result.result == constants.SUCCESS
+ assert result.source == 'ipahealthcheck.ipa.certs'
+ assert result.check == 'IPAKRAAgent'
+
+ def test_kra_agent_no_description(self):
+
+ attrs = dict(
+ usercertificate=[self.cert],
+ )
+ fake_conn = LDAPClient('ldap://localhost', no_schema=True)
+ ldapentry = LDAPEntry(fake_conn,
+ DN('uid=ipakra,ou=people,o=kra,o=ipaca'))
+ for attr, values in attrs.items():
+ ldapentry[attr] = values
+
+ framework = object()
+ registry.initialize(framework, config.Config())
+ f = IPAKRAAgent(registry)
+
+ f.conn = mock_ldap([ldapentry])
+ self.results = capture_results(f)
+ result = self.results.results[0]
+
+ assert result.result == constants.ERROR
+ assert 'description' in result.kw.get('msg')
+
+ @patch('ipalib.x509.load_certificate_from_file')
+ def test_kra_agent_load_failure(self, mock_load_cert):
+
+ mock_load_cert.side_effect = IOError('test')
+
+ framework = object()
+ registry.initialize(framework, config.Config())
+ f = IPAKRAAgent(registry)
+
+ self.results = capture_results(f)
+ result = self.results.results[0]
+
+ assert result.result == constants.ERROR
+ assert result.kw.get('error') == 'test'
+
+ def test_kra_agent_no_entry_found(self):
+
+ framework = object()
+ registry.initialize(framework, config.Config())
+ f = IPAKRAAgent(registry)
+
+ f.conn = mock_ldap(None) # None == NotFound
+ self.results = capture_results(f)
+ result = self.results.results[0]
+
+ assert result.result == constants.ERROR
+ assert result.kw.get('msg') == 'KRA agent not found in LDAP'
+
+ def test_kra_agent_too_many(self):
+
+ attrs = dict(
+ description=['2;1;CN=ISSUER;CN=RA AGENT'],
+ usercertificate=[self.cert],
+ )
+ fake_conn = LDAPClient('ldap://localhost', no_schema=True)
+ ldapentry = LDAPEntry(fake_conn,
+ DN('uid=ipakra,ou=people,o=kra,o=ipaca'))
+ for attr, values in attrs.items():
+ ldapentry[attr] = values
+
+ ldapentry2 = LDAPEntry(fake_conn,
+ DN('uid=ipakra,ou=people,o=kra,o=ipaca'))
+ for attr, values in attrs.items():
+ ldapentry[attr] = values
+
+ framework = object()
+ registry.initialize(framework, config.Config())
+ f = IPAKRAAgent(registry)
+
+ f.conn = mock_ldap([ldapentry, ldapentry2])
+ self.results = capture_results(f)
+ result = self.results.results[0]
+
+ assert result.result == constants.ERROR
+ assert result.kw.get('found') == 2
+
+ def test_kra_agent_nonmatching_cert(self):
+
+ cert2 = IPACertificate(2)
+
+ attrs = dict(
+ description=['2;1;CN=ISSUER;CN=RA AGENT'],
+ usercertificate=[cert2],
+ )
+ fake_conn = LDAPClient('ldap://localhost', no_schema=True)
+ ldapentry = LDAPEntry(fake_conn,
+ DN('uid=ipakra,ou=people,o=kra,o=ipaca'))
+ for attr, values in attrs.items():
+ ldapentry[attr] = values
+
+ framework = object()
+ registry.initialize(framework, config.Config())
+ f = IPAKRAAgent(registry)
+
+ f.conn = mock_ldap([ldapentry])
+ self.results = capture_results(f)
+ result = self.results.results[0]
+
+ assert result.result == constants.ERROR
+ assert result.kw.get('certfile') == paths.RA_AGENT_PEM
+ assert result.kw.get('dn') == 'uid=ipakra,ou=people,o=kra,o=ipaca'
+
+ def test_kra_agent_multiple_certs(self):
+
+ cert2 = IPACertificate(2)
+
+ attrs = dict(
+ description=['2;1;CN=ISSUER;CN=RA AGENT'],
+ usercertificate=[cert2, self.cert],
+ )
+ fake_conn = LDAPClient('ldap://localhost', no_schema=True)
+ ldapentry = LDAPEntry(fake_conn,
+ DN('uid=ipakra,ou=people,o=kra,o=ipaca'))
+ for attr, values in attrs.items():
+ ldapentry[attr] = values
+
+ framework = object()
+ registry.initialize(framework, config.Config)
+ f = IPAKRAAgent(registry)
+
+ f.conn = mock_ldap([ldapentry])
+ self.results = capture_results(f)
+
+ assert len(self.results) == 1
+
+ result = self.results.results[0]
+ assert result.result == constants.SUCCESS
+ assert result.source == 'ipahealthcheck.ipa.certs'
+ assert result.check == 'IPAKRAAgent'
--
2.26.2

View File

@ -0,0 +1,30 @@
From e23e38124098d37b514e02531341af21e6fb0688 Mon Sep 17 00:00:00 2001
From: Rob Crittenden <rcritten@redhat.com>
Date: Mon, 14 Jun 2021 11:37:23 -0400
Subject: [PATCH] Return user-friendly message when no issues found
Return user-friendly message instead of empty string
when no issues found and using the "human" output type.
Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1780062
Signed-off-by: Antonio Torres <antorres@redhat.com>
---
src/ipahealthcheck/core/output.py | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src/ipahealthcheck/core/output.py b/src/ipahealthcheck/core/output.py
index cfd0f94..784263d 100644
--- a/src/ipahealthcheck/core/output.py
+++ b/src/ipahealthcheck/core/output.py
@@ -129,6 +129,8 @@ class Human(Output):
super(Human, self).__init__(options)
def generate(self, data):
+ if not data:
+ return "No issues found.\n"
output = ''
for line in data:
kw = line.get('kw')
--
2.26.3

View File

@ -0,0 +1,291 @@
From f762f8f5e2f9b6d66d786b426d4d2fe40c994192 Mon Sep 17 00:00:00 2001
From: Antonio Torres <antorres@redhat.com>
Date: Fri, 23 Apr 2021 17:42:21 +0200
Subject: [PATCH] Add checks to detect mismatch of certificates
Add checks to detect mismatch of certificates between LDAP
and NSS databases. Check for existance of entries as well as
ensure the certificates match between the different databases.
Related: https://bugzilla.redhat.com/show_bug.cgi?id=1886770
Signed-off-by: Antonio Torres <antorres@redhat.com>
---
README.md | 24 ++++
src/ipahealthcheck/ipa/certs.py | 219 ++++++++++++++++++++++++++++++++
2 files changed, 243 insertions(+)
diff --git a/README.md b/README.md
index 0f3ed6a..11e0e88 100644
--- a/README.md
+++ b/README.md
@@ -507,6 +507,30 @@ The trust for certificates stored in NSS databases is compared against a known g
}
}
+### IPACertMatchCheck
+Ensure CA certificate entries in LDAP and NSS databases match.
+
+ {
+ "source": "ipahealthcheck.ipa.certs",
+ "check": "IPACertMatchCheck",
+ "result": "ERROR",
+ "kw": {
+ "msg": "CA Certificate from /etc/ipa/nssdb does not match /etc/ipa/ca.crt"
+ }
+ }
+
+### IPADogtagCertsMatchCheck
+Check if Dogtag certificates present in both NSS DB and LDAP match.
+
+ {
+ "source": "ipahealthcheck.ipa.certs",
+ "check": "IPADogtagCertsMatchCheck",
+ "result": "ERROR",
+ "kw": {
+ "msg": "'subsystemCert cert-pki-ca' certificate in NSS DB does not match entry in LDAP"
+ }
+ }
+
### IPANSSChainValidation
Validate the certificate chain of the NSS certificates. This executes: certutil -V -u V -e -d [dbdir] -n [nickname].
diff --git a/src/ipahealthcheck/ipa/certs.py b/src/ipahealthcheck/ipa/certs.py
index c668093..82435f3 100644
--- a/src/ipahealthcheck/ipa/certs.py
+++ b/src/ipahealthcheck/ipa/certs.py
@@ -29,6 +29,7 @@ from ipaserver.plugins import ldap2
from ipapython import certdb
from ipapython import ipautil
from ipapython.dn import DN
+from ipapython.ipaldap import realm_to_serverid
logger = logging.getLogger()
DAY = 60 * 60 * 24
@@ -587,6 +588,224 @@ class IPACertNSSTrust(IPAPlugin):
'verifying trust')
+@registry
+class IPACertMatchCheck(IPAPlugin):
+ """
+ Ensure certificates match between LDAP and NSS databases
+ """
+
+ requires = ('dirsrv',)
+
+ def get_cert_list_from_db(self, nssdb, nickname):
+ """
+ Retrieve all certificates from an NSS database for nickname.
+ """
+ try:
+ args = ["-L", "-n", nickname, "-a"]
+ result = nssdb.run_certutil(args, capture_output=True)
+ return x509.load_certificate_list(result.raw_output)
+ except ipautil.CalledProcessError:
+ return []
+
+ @duration
+ def check(self):
+ if not self.ca.is_configured():
+ logger.debug("No CA configured, skipping certificate match check")
+ return
+
+ # Ensure /etc/ipa/ca.crt matches the NSS DB CA certificates
+ def match_cacert_and_db(plugin, cacerts, dbpath):
+ db = certs.CertDB(api.env.realm, dbpath)
+ nickname = '%s IPA CA' % api.env.realm
+ try:
+ dbcacerts = self.get_cert_list_from_db(db, nickname)
+ except Exception as e:
+ yield Result(plugin, constants.ERROR,
+ error=str(e),
+ msg='Unable to load CA cert: {error}')
+ return False
+
+ ok = True
+ for cert in dbcacerts:
+ if cert not in cacerts:
+ ok = False
+ yield Result(plugin, constants.ERROR,
+ nickname=nickname,
+ serial_number=cert.serial_number,
+ dbdir=dbpath,
+ certdir=paths.IPA_CA_CRT,
+ msg=('CA Certificate nickname {nickname} '
+ 'with serial number {serial} '
+ 'is in {dbdir} but is not in'
+ '%s' % paths.IPA_CA_CRT))
+ return ok
+
+ try:
+ cacerts = x509.load_certificate_list_from_file(paths.IPA_CA_CRT)
+ except Exception:
+ yield Result(self, constants.ERROR,
+ path=paths.IPA_CA_CRT,
+ msg='Unable to load CA cert file {path}: {error}')
+ return
+
+ # Ensure CA cert entry from LDAP matches /etc/ipa/ca.crt
+ dn = DN('cn=%s IPA CA' % api.env.realm,
+ 'cn=certificates,cn=ipa,cn=etc',
+ api.env.basedn)
+ try:
+ entry = self.conn.get_entry(dn)
+ except errors.NotFound:
+ yield Result(self, constants.ERROR,
+ dn=str(dn),
+ msg='CA Certificate entry \'{dn}\' '
+ 'not found in LDAP')
+ return
+
+ cacerts_ok = True
+ # Are all the certs in LDAP for the IPA CA in /etc/ipa/ca.crt
+ for cert in entry['CACertificate']:
+ if cert not in cacerts:
+ cacerts_ok = False
+ yield Result(self, constants.ERROR,
+ dn=str(dn),
+ serial_number=cert.serial_number,
+ msg=('CA Certificate serial number {serial} is '
+ 'in LDAP \'{dn}\' but is not in '
+ '%s' % paths.IPA_CA_CRT))
+
+ # Ensure NSS DBs have matching CA certs for /etc/ipa/ca.crt
+ serverid = realm_to_serverid(api.env.realm)
+ dspath = paths.ETC_DIRSRV_SLAPD_INSTANCE_TEMPLATE % serverid
+
+ cacertds_ok = yield from match_cacert_and_db(self, cacerts, dspath)
+ cacertnss_ok = yield from match_cacert_and_db(self, cacerts,
+ paths.IPA_NSSDB_DIR)
+ if cacerts_ok:
+ yield Result(self, constants.SUCCESS,
+ key=paths.IPA_CA_CRT)
+ if cacertds_ok:
+ yield Result(self, constants.SUCCESS,
+ key=dspath)
+ if cacertnss_ok:
+ yield Result(self, constants.SUCCESS,
+ key=paths.IPA_NSSDB_DIR)
+
+
+@registry
+class IPADogtagCertsMatchCheck(IPAPlugin):
+ """
+ Check if dogtag certs present in both NSS DB and LDAP match
+ """
+ requires = ('dirsrv',)
+
+ @duration
+ def check(self):
+ if not self.ca.is_configured():
+ logger.debug('CA is not configured, skipping connectivity check')
+ return
+
+ def match_ldap_nss_cert(plugin, ldap, db, cert_dn, attr, cert_nick):
+ try:
+ entry = ldap.get_entry(cert_dn)
+ except errors.NotFound:
+ yield Result(plugin, constants.ERROR,
+ msg='%s entry not found in LDAP' % cert_dn)
+ return False
+ try:
+ nsscert = db.get_cert_from_db(cert_nick)
+ except Exception as e:
+ yield Result(plugin, constants.ERROR,
+ error=str(e),
+ msg=('Unable to load %s certificate:'
+ '{error}' % cert_nick))
+ return False
+ cert_matched = any([cert == nsscert for cert in entry[attr]])
+ if not cert_matched:
+ yield Result(plugin, constants.ERROR,
+ key=cert_nick,
+ nickname=cert_nick,
+ dbdir=db.secdir,
+ msg=('{nickname} certificate in NSS DB {dbdir} '
+ 'does not match entry in LDAP'))
+ return False
+ return True
+
+ def match_ldap_nss_certs_by_subject(plugin, ldap, db, dn,
+ expected_nicks_subjects):
+ entries = ldap.get_entries(dn)
+ all_ok = True
+ for nick, subject in expected_nicks_subjects.items():
+ cert = db.get_cert_from_db(nick)
+ ok = any([cert in entry['userCertificate'] and
+ subject == entry['subjectName'][0]
+ for entry in entries
+ if 'userCertificate' in entry])
+ if not ok:
+ all_ok = False
+ yield Result(plugin, constants.ERROR,
+ key=nick,
+ nickname=nick,
+ dbdir=db.secdir,
+ msg=('{nickname} certificate in NSS DB '
+ '{dbdir} does not match entry in LDAP'))
+ return all_ok
+
+ db = certs.CertDB(api.env.realm, paths.PKI_TOMCAT_ALIAS_DIR)
+ dn = DN('uid=pkidbuser,ou=people,o=ipaca')
+ subsystem_nick = 'subsystemCert cert-pki-ca'
+ subsystem_ok = yield from match_ldap_nss_cert(self, self.conn,
+ db, dn,
+ 'userCertificate',
+ subsystem_nick)
+ dn = DN('cn=%s IPA CA' % api.env.realm,
+ 'cn=certificates,cn=ipa,cn=etc',
+ api.env.basedn)
+ casigning_nick = 'caSigningCert cert-pki-ca'
+ casigning_ok = yield from match_ldap_nss_cert(self, self.conn,
+ db, dn, 'CACertificate',
+ casigning_nick)
+
+ expected_nicks_subjects = {
+ 'ocspSigningCert cert-pki-ca':
+ 'CN=OCSP Subsystem,O=%s' % api.env.realm,
+ 'subsystemCert cert-pki-ca':
+ 'CN=CA Subsystem,O=%s' % api.env.realm,
+ 'auditSigningCert cert-pki-ca':
+ 'CN=CA Audit,O=%s' % api.env.realm,
+ 'Server-Cert cert-pki-ca':
+ 'CN=%s,O=%s' % (api.env.host, api.env.realm),
+ }
+
+ kra = krainstance.KRAInstance(api.env.realm)
+ if kra.is_installed():
+ kra_expected_nicks_subjects = {
+ 'transportCert cert-pki-kra':
+ 'CN=KRA Transport Certificate,O=%s' % api.env.realm,
+ 'storageCert cert-pki-kra':
+ 'CN=KRA Storage Certificate,O=%s' % api.env.realm,
+ 'auditSigningCert cert-pki-kra':
+ 'CN=KRA Audit,O=%s' % api.env.realm,
+ }
+ expected_nicks_subjects.update(kra_expected_nicks_subjects)
+
+ ipaca_basedn = DN('ou=certificateRepository,ou=ca,o=ipaca')
+ ipaca_certs_ok = yield from match_ldap_nss_certs_by_subject(
+ self, self.conn, db,
+ ipaca_basedn,
+ expected_nicks_subjects
+ )
+
+ if subsystem_ok:
+ yield Result(self, constants.SUCCESS,
+ key=subsystem_nick)
+ if casigning_ok:
+ yield Result(self, constants.SUCCESS,
+ key=casigning_nick)
+ if ipaca_certs_ok:
+ yield Result(self, constants.SUCCESS,
+ key=str(ipaca_basedn))
+
+
@registry
class IPANSSChainValidation(IPAPlugin):
"""Validate the certificate chain of the certs."""
--
2.26.3

View File

@ -0,0 +1,305 @@
From 94ca49d991b3f5bb404533f84970a1b2485d9cc8 Mon Sep 17 00:00:00 2001
From: Antonio Torres <antorres@redhat.com>
Date: Fri, 23 Apr 2021 17:48:14 +0200
Subject: [PATCH] Add tests for certificate mismatch detection
Add tests for the IPACertMatchCheck and IPADogtagCertsMatchCheck plugins.
Related: https://bugzilla.redhat.com/show_bug.cgi?id=1886770
Signed-off-by: Antonio Torres <antorres@redhat.com>
---
tests/test_ipa_cert_match.py | 282 +++++++++++++++++++++++++++++++++++
1 file changed, 282 insertions(+)
create mode 100644 tests/test_ipa_cert_match.py
diff --git a/tests/test_ipa_cert_match.py b/tests/test_ipa_cert_match.py
new file mode 100644
index 0000000..460e61a
--- /dev/null
+++ b/tests/test_ipa_cert_match.py
@@ -0,0 +1,282 @@
+#
+# Copyright (C) 2021 FreeIPA Contributors see COPYING for license
+#
+
+from util import capture_results, m_api, CAInstance, KRAInstance
+from base import BaseTest
+from ipahealthcheck.core import config, constants
+from ipahealthcheck.ipa.plugin import registry
+from ipahealthcheck.ipa.certs import IPACertMatchCheck
+from ipahealthcheck.ipa.certs import IPADogtagCertsMatchCheck
+from unittest.mock import Mock, patch
+
+from ipalib import errors
+from ipapython.dn import DN
+from ipapython.ipaldap import LDAPClient, LDAPEntry
+
+
+class IPACertificate:
+ def __init__(self, serial_number=1):
+ self.serial_number = serial_number
+
+ def __eq__(self, other):
+ return self.serial_number == other.serial_number
+
+ def __hash__(self):
+ return hash(self.serial_number)
+
+
+class mock_ldap:
+ SCOPE_BASE = 1
+ SCOPE_ONELEVEL = 2
+ SCOPE_SUBTREE = 4
+
+ def __init__(self, entries):
+ """Initialize the results that we will return from get_entry"""
+ self.results = {entry.dn: entry for entry in entries}
+
+ def get_entry(self, dn, attrs_list=None, time_limit=None,
+ size_limit=None, get_effective_rights=False):
+ if self.results is None:
+ raise errors.NotFound(reason='test')
+ return self.results[dn]
+
+ def get_entries(self, base_dn, scope=SCOPE_SUBTREE, filter=None,
+ attrs_list=None, get_effective_rights=False, **kwargs):
+ if self.results is None:
+ raise errors.NotFound(reason='test')
+ return self.results.values()
+
+
+class mock_ldap_conn:
+ def set_option(self, option, invalue):
+ pass
+
+ def search_s(self, base, scope, filterstr=None,
+ attrlist=None, attrsonly=0):
+ return tuple()
+
+
+class mock_CertDB:
+ def __init__(self, trust):
+ """A dict of nickname + NSSdb trust flags"""
+ self.trust = trust
+ self.secdir = '/foo/bar/testdir'
+
+ def get_cert_from_db(self, nickname):
+ if nickname not in self.trust.keys():
+ raise errors.NotFound(reason='test')
+ return IPACertificate()
+
+ def run_certutil(self, args, capture_output):
+ class RunResult:
+ def __init__(self, output):
+ self.raw_output = output
+
+ return RunResult(b'test output')
+
+
+class TestIPACertMatch(BaseTest):
+ patches = {
+ 'ldap.initialize':
+ Mock(return_value=mock_ldap_conn())
+ }
+
+ @patch('ipalib.x509.load_certificate_list_from_file')
+ @patch('ipaserver.install.certs.CertDB')
+ def test_certs_match_ok(self, mock_certdb, mock_load_cert):
+ """ Ensure match check is ok"""
+ fake_conn = LDAPClient('ldap://localhost', no_schema=True)
+ cacertentry = LDAPEntry(fake_conn,
+ DN('cn=%s IPA CA' % m_api.env.realm,
+ 'cn=certificates,cn=ipa,cn=etc',
+ m_api.env.basedn),
+ CACertificate=[IPACertificate()])
+ trust = {
+ ('%s IPA CA' % m_api.env.realm): 'u,u,u'
+ }
+
+ mock_certdb.return_value = mock_CertDB(trust)
+ mock_load_cert.return_value = [IPACertificate()]
+
+ framework = object()
+ registry.initialize(framework, config.Config())
+ f = IPACertMatchCheck(registry)
+ f.conn = mock_ldap([cacertentry])
+ self.results = capture_results(f)
+
+ assert len(self.results) == 3
+ for result in self.results.results:
+ assert result.result == constants.SUCCESS
+ assert result.source == 'ipahealthcheck.ipa.certs'
+ assert result.check == 'IPACertMatchCheck'
+
+ @patch('ipalib.x509.load_certificate_list_from_file')
+ @patch('ipaserver.install.certs.CertDB')
+ def test_etc_cacert_mismatch(self, mock_certdb, mock_load_cert):
+ """ Test mismatch with /etc/ipa/ca.crt """
+ fake_conn = LDAPClient('ldap://localhost', no_schema=True)
+ cacertentry = LDAPEntry(fake_conn,
+ DN('cn=%s IPA CA' % m_api.env.realm,
+ 'cn=certificates,cn=ipa,cn=etc',
+ m_api.env.basedn),
+ CACertificate=[IPACertificate()])
+ trust = {
+ ('%s IPA CA' % m_api.env.realm): 'u,u,u'
+ }
+
+ mock_certdb.return_value = mock_CertDB(trust)
+ mock_load_cert.return_value = [IPACertificate(serial_number=2)]
+
+ framework = object()
+ registry.initialize(framework, config.Config())
+ f = IPACertMatchCheck(registry)
+ f.conn = mock_ldap([cacertentry])
+ self.results = capture_results(f)
+
+ assert len(self.results) == 3
+ result = self.results.results[0]
+ assert result.result == constants.ERROR
+ assert result.source == 'ipahealthcheck.ipa.certs'
+ assert result.check == 'IPACertMatchCheck'
+
+ @patch('ipaserver.install.cainstance.CAInstance')
+ def test_cacert_caless(self, mock_cainstance):
+ """Nothing to check if the master is CALess"""
+
+ mock_cainstance.return_value = CAInstance(False)
+
+ framework = object()
+ registry.initialize(framework, config)
+ f = IPACertMatchCheck(registry)
+
+ self.results = capture_results(f)
+
+ assert len(self.results) == 0
+
+
+class TestIPADogtagCertMatch(BaseTest):
+ patches = {
+ 'ipaserver.install.krainstance.KRAInstance':
+ Mock(return_value=KRAInstance()),
+ }
+
+ @patch('ipaserver.install.certs.CertDB')
+ def test_certs_match_ok(self, mock_certdb):
+ """ Ensure match check is ok"""
+ fake_conn = LDAPClient('ldap://localhost', no_schema=True)
+ pkidbentry = LDAPEntry(fake_conn,
+ DN('uid=pkidbuser,ou=people,o=ipaca'),
+ userCertificate=[IPACertificate()],
+ subjectName=['test'])
+ casignentry = LDAPEntry(fake_conn,
+ DN('cn=%s IPA CA' % m_api.env.realm,
+ 'cn=certificates,cn=ipa,cn=etc',
+ m_api.env.basedn),
+ CACertificate=[IPACertificate()],
+ userCertificate=[IPACertificate()],
+ subjectName=['test'])
+ ldap_entries = [pkidbentry, casignentry]
+ trust = {
+ 'ocspSigningCert cert-pki-ca': 'u,u,u',
+ 'caSigningCert cert-pki-ca': 'u,u,u',
+ 'subsystemCert cert-pki-ca': 'u,u,u',
+ 'auditSigningCert cert-pki-ca': 'u,u,Pu',
+ 'Server-Cert cert-pki-ca': 'u,u,u',
+ 'transportCert cert-pki-kra': 'u,u,u',
+ 'storageCert cert-pki-kra': 'u,u,u',
+ 'auditSigningCert cert-pki-kra': 'u,u,Pu',
+ }
+
+ dogtag_entries_subjects = (
+ 'CN=OCSP Subsystem,O=%s' % m_api.env.realm,
+ 'CN=CA Subsystem,O=%s' % m_api.env.realm,
+ 'CN=CA Audit,O=%s' % m_api.env.realm,
+ 'CN=%s,O=%s' % (m_api.env.host, m_api.env.realm),
+ 'CN=KRA Transport Certificate,O=%s' % m_api.env.realm,
+ 'CN=KRA Storage Certificate,O=%s' % m_api.env.realm,
+ 'CN=KRA Audit,O=%s' % m_api.env.realm,
+ )
+
+ for i, subject in enumerate(dogtag_entries_subjects):
+ entry = LDAPEntry(fake_conn,
+ DN('cn=%i,ou=certificateRepository' % i,
+ 'ou=ca,o=ipaca'),
+ userCertificate=[IPACertificate()],
+ subjectName=[subject])
+ ldap_entries.append(entry)
+
+ mock_certdb.return_value = mock_CertDB(trust)
+
+ framework = object()
+ registry.initialize(framework, config.Config())
+ f = IPADogtagCertsMatchCheck(registry)
+ f.conn = mock_ldap(ldap_entries)
+ self.results = capture_results(f)
+
+ assert len(self.results) == 3
+ for result in self.results.results:
+ assert result.result == constants.SUCCESS
+ assert result.source == 'ipahealthcheck.ipa.certs'
+ assert result.check == 'IPADogtagCertsMatchCheck'
+
+ @patch('ipaserver.install.certs.CertDB')
+ def test_certs_mismatch(self, mock_certdb):
+ """ Ensure mismatches are detected"""
+ fake_conn = LDAPClient('ldap://localhost', no_schema=True)
+ pkidbentry = LDAPEntry(fake_conn,
+ DN('uid=pkidbuser,ou=people,o=ipaca'),
+ userCertificate=[IPACertificate(
+ serial_number=2
+ )],
+ subjectName=['test'])
+ casignentry = LDAPEntry(fake_conn,
+ DN('cn=%s IPA CA' % m_api.env.realm,
+ 'cn=certificates,cn=ipa,cn=etc',
+ m_api.env.basedn),
+ CACertificate=[IPACertificate()],
+ userCertificate=[IPACertificate()],
+ subjectName=['test'])
+ ldap_entries = [pkidbentry, casignentry]
+ trust = {
+ 'ocspSigningCert cert-pki-ca': 'u,u,u',
+ 'caSigningCert cert-pki-ca': 'u,u,u',
+ 'subsystemCert cert-pki-ca': 'u,u,u',
+ 'auditSigningCert cert-pki-ca': 'u,u,Pu',
+ 'Server-Cert cert-pki-ca': 'u,u,u',
+ 'transportCert cert-pki-kra': 'u,u,u',
+ 'storageCert cert-pki-kra': 'u,u,u',
+ 'auditSigningCert cert-pki-kra': 'u,u,Pu',
+ }
+
+ dogtag_entries_subjects = (
+ 'CN=OCSP Subsystem,O=%s' % m_api.env.realm,
+ 'CN=CA Subsystem,O=%s' % m_api.env.realm,
+ 'CN=CA Audit,O=%s' % m_api.env.realm,
+ 'CN=%s,O=%s' % (m_api.env.host, m_api.env.realm),
+ 'CN=KRA Transport Certificate,O=%s' % m_api.env.realm,
+ 'CN=KRA Storage Certificate,O=%s' % m_api.env.realm,
+ 'CN=KRA Audit,O=%s' % m_api.env.realm,
+ )
+
+ for i, subject in enumerate(dogtag_entries_subjects):
+ entry = LDAPEntry(fake_conn,
+ DN('cn=%i,ou=certificateRepository' % i,
+ 'ou=ca,o=ipaca'),
+ userCertificate=[IPACertificate()],
+ subjectName=[subject])
+ ldap_entries.append(entry)
+
+ mock_certdb.return_value = mock_CertDB(trust)
+
+ framework = object()
+ registry.initialize(framework, config.Config())
+ f = IPADogtagCertsMatchCheck(registry)
+ f.conn = mock_ldap(ldap_entries)
+ self.results = capture_results(f)
+
+ assert len(self.results) == 3
+ result = self.results.results[0]
+ assert result.result == constants.ERROR
+ assert result.source == 'ipahealthcheck.ipa.certs'
+ assert result.check == 'IPADogtagCertsMatchCheck'
--
2.26.3

View File

@ -0,0 +1,99 @@
From 7b8acecd7393deba2411192d3f04778a2a4325c5 Mon Sep 17 00:00:00 2001
From: Rob Crittenden <rcritten@redhat.com>
Date: Mon, 14 Jun 2021 11:38:21 -0400
Subject: [PATCH] Add log files to the set of files checked for
owner/group/mode
Extend the list of files to be checked to include most IPA service
log files.
https://bugzilla.redhat.com/show_bug.cgi?id=1780020
Signed-off-by: Rob Crittenden <rcritten@redhat.com>
---
src/ipahealthcheck/ipa/files.py | 62 +++++++++++++++++++++++++++++++++
1 file changed, 62 insertions(+)
diff --git a/src/ipahealthcheck/ipa/files.py b/src/ipahealthcheck/ipa/files.py
index ae74c38..abfa52f 100644
--- a/src/ipahealthcheck/ipa/files.py
+++ b/src/ipahealthcheck/ipa/files.py
@@ -2,6 +2,7 @@
# Copyright (C) 2019 FreeIPA Contributors see COPYING for license
#
+import glob
import logging
import os
@@ -95,6 +96,67 @@ class IPAFileCheck(IPAPlugin, FileCheck):
self.files.append((paths.RESOLV_CONF, 'root', 'root', '0644'))
self.files.append((paths.HOSTS, 'root', 'root', '0644'))
+ # IPA log files that may vary by installation. Only verify
+ # those that exist
+ for filename in (
+ paths.IPABACKUP_LOG,
+ paths.IPARESTORE_LOG,
+ paths.IPACLIENT_INSTALL_LOG,
+ paths.IPACLIENT_UNINSTALL_LOG,
+ paths.IPAREPLICA_CA_INSTALL_LOG,
+ paths.IPAREPLICA_CONNCHECK_LOG,
+ paths.IPAREPLICA_INSTALL_LOG,
+ paths.IPASERVER_INSTALL_LOG,
+ paths.IPASERVER_KRA_INSTALL_LOG,
+ paths.IPASERVER_UNINSTALL_LOG,
+ paths.IPAUPGRADE_LOG,
+ paths.IPATRUSTENABLEAGENT_LOG,
+ ):
+ if os.path.exists(filename):
+ self.files.append((filename, 'root', 'root', '0600'))
+
+ self.files.append((paths.IPA_CUSTODIA_AUDIT_LOG,
+ 'root', 'root', '0644'))
+
+ self.files.append((paths.KADMIND_LOG, 'root', 'root', '0600'))
+ self.files.append((paths.KRB5KDC_LOG, 'root', 'root', '0640'))
+
+ inst = api.env.realm.replace('.', '-')
+ self.files.append((paths.SLAPD_INSTANCE_ACCESS_LOG_TEMPLATE % inst,
+ 'dirsrv', 'dirsrv', '0600'))
+ self.files.append((paths.SLAPD_INSTANCE_ERROR_LOG_TEMPLATE % inst,
+ 'dirsrv', 'dirsrv', '0600'))
+
+ self.files.append((paths.VAR_LOG_HTTPD_ERROR, 'root', 'root', '0644'))
+
+ for globpath in glob.glob("%s/debug*.log" % paths.TOMCAT_CA_DIR):
+ self.files.append((globpath, "pkiuser", "pkiuser", "0644"))
+
+ for globpath in glob.glob(
+ "%s/ca_audit*" % paths.TOMCAT_SIGNEDAUDIT_DIR
+ ):
+ self.files.append((globpath, 'pkiuser', 'pkiuser', '0640'))
+
+ for filename in ('selftests.log', 'system', 'transactions'):
+ self.files.append((
+ os.path.join(paths.TOMCAT_CA_DIR, filename),
+ 'pkiuser', 'pkiuser', '0640'
+ ))
+
+ for globpath in glob.glob("%s/debug*.log" % paths.TOMCAT_KRA_DIR):
+ self.files.append((globpath, "pkiuser", "pkiuser", "0644"))
+
+ for globpath in glob.glob(
+ "%s/ca_audit*" % paths.TOMCAT_KRA_SIGNEDAUDIT_DIR
+ ):
+ self.files.append((globpath, 'pkiuser', 'pkiuser', '0640'))
+
+ for filename in ('selftests.log', 'system', 'transactions'):
+ self.files.append((
+ os.path.join(paths.TOMCAT_KRA_DIR, filename),
+ 'pkiuser', 'pkiuser', '0640'
+ ))
+
return FileCheck.check(self)
--
2.26.3

View File

@ -0,0 +1,63 @@
From 89bca9803d92cd6977c01462e9031fa1e00c950b Mon Sep 17 00:00:00 2001
From: Rob Crittenden <rcritten@redhat.com>
Date: Mon, 14 Jun 2021 11:40:06 -0400
Subject: [PATCH] Handle files that don't exist in FileCheck
A raw os.stat() was called which could raise an exception if the file
doesn't exist. Instead call os.path.exists() and if the result is False
then raise a SUCCESS with a message that the file doesn't exist.
https://github.com/freeipa/freeipa-healthcheck/issues/213
Signed-off-by: Rob Crittenden <rcritten@redhat.com>
---
src/ipahealthcheck/core/files.py | 7 +++++++
tests/test_core_files.py | 17 +++++++++++++++++
2 files changed, 24 insertions(+)
diff --git a/src/ipahealthcheck/core/files.py b/src/ipahealthcheck/core/files.py
index 9441eb2..1b208d1 100644
--- a/src/ipahealthcheck/core/files.py
+++ b/src/ipahealthcheck/core/files.py
@@ -23,6 +23,13 @@ class FileCheck:
@duration
def check(self):
for (path, owner, group, mode) in self.files:
+ if not os.path.exists(path):
+ for type in ('mode', 'owner', 'group'):
+ key = '%s_%s' % (path.replace('/', '_'), type)
+ yield Result(self, constants.SUCCESS, key=key,
+ type=type, path=path,
+ msg='File does not exist')
+ continue
stat = os.stat(path)
fmode = str(oct(stat.st_mode)[-4:])
key = '%s_mode' % path.replace('/', '_')
diff --git a/tests/test_core_files.py b/tests/test_core_files.py
index e6cf354..a4f25ac 100644
--- a/tests/test_core_files.py
+++ b/tests/test_core_files.py
@@ -105,3 +105,20 @@ def test_files_mode(mock_stat):
my_results = get_results(results, 'mode')
assert my_results.results[0].result == constants.WARNING
assert my_results.results[1].result == constants.WARNING
+
+
+@patch('os.path.exists')
+def test_files_not_found(mock_exists):
+ mock_exists.return_value = False
+
+ f = FileCheck()
+ f.files = files
+
+ results = capture_results(f)
+
+ for type in ('mode', 'group', 'owner'):
+ my_results = get_results(results, type)
+ assert len(my_results.results) == 4
+ for result in my_results.results:
+ assert result.result == constants.SUCCESS
+ assert result.kw.get('msg') == 'File does not exist'
--
2.26.3

View File

@ -8,7 +8,7 @@
Name: ipa-healthcheck
Version: 0.7
Release: 3%{?dist}
Release: 6%{?dist}
Summary: Health check tool for IdM
BuildArch: noarch
License: GPLv3
@ -20,6 +20,13 @@ Patch0001: 0001-Remove-requirement-for-pytest-runner-since-PyPI-isn-.patch
Patch0002: 0002-Remove-ipaclustercheck.patch
Patch0003: 0003-Use-trust-find-and-trustdomain-find-to-identify-all-.patch
Patch0004: 0004-result-names-are-not-translated-when-reading-input-f.patch
Patch0005: 0005-Add-check-for-IPA-KRA-Agent.patch
Patch0006: 0006-Add-tests-for-KRA-Agent-validation.patch
Patch0007: 0007-Return-user-friendly-message-when-no-issues-found.patch
Patch0009: 0009-Add-checks-to-detect-mismatch-of-certificates.patch
Patch0010: 0010-Add-tests-for-certificate-mismatch-detection.patch
Patch0011: 0011-Add-log-files-to-the-set-of-files-checked-for-owner-.patch
Patch0012: 0012-Handle-files-that-don-t-exist-in-FileCheck.patch
Requires: %{name}-core = %{version}-%{release}
Requires: ipa-server
@ -123,6 +130,18 @@ install -p -m644 %{_builddir}/%{project}-%{shortname}-%{version}/man/man5/%{long
%changelog
* Mon Jun 14 2021 Rob Crittenden <rcritten@redhat.com> - 0.7-6
- Fix patch fuzz issues, apply add'l upstream for log files (#1780020)
* Wed Jun 2 2021 Rob Crittenden <rcritten@redhat.com> - 0.7-5
- Return a user-friendly message when no issues are found (#1780062)
- Report on FIPS status (#1781107)
- Detect mismatches beteween certificates in LDAP and filesystem (#1886770)
- Verify owner/perms for important log files (#1780020)
* Tue Apr 6 2021 Rob Crittenden <rcritten@redhat.com> - 0.7-4
- Add check to validate the KRA Agent is correct (#1894781)
* Fri Dec 4 2020 Rob Crittenden <rcritten@redhat.com> - 0.7-3
- Translate result names when reading input from a json file (#1866558)