Add check to validate KRA Agent LDAP entry is correct
Resolves: #1947043
This commit is contained in:
parent
d293b70fc2
commit
fc47662be3
236
0004-Add-check-for-IPA-KRA-Agent.patch
Normal file
236
0004-Add-check-for-IPA-KRA-Agent.patch
Normal 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
|
||||
|
||||
207
0005-Add-tests-for-KRA-Agent-validation.patch
Normal file
207
0005-Add-tests-for-KRA-Agent-validation.patch
Normal 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
|
||||
|
||||
@ -17,7 +17,7 @@
|
||||
|
||||
Name: %{prefix}-healthcheck
|
||||
Version: 0.8
|
||||
Release: 6.1%{?dist}
|
||||
Release: 7%{?dist}
|
||||
Summary: Health check tool for %{productname}
|
||||
BuildArch: noarch
|
||||
License: GPLv3
|
||||
@ -28,6 +28,8 @@ Source1: ipahealthcheck.conf
|
||||
Patch0001: 0001-Remove-ipaclustercheck.patch
|
||||
Patch0002: 0002-Handle-slight-different-in-exception-output-in-Pytho.patch
|
||||
Patch0003: 0003-Failed-expected-group-should-be-a-string-not-a-list.patch
|
||||
Patch0004: 0004-Add-check-for-IPA-KRA-Agent.patch
|
||||
Patch0005: 0005-Add-tests-for-KRA-Agent-validation.patch
|
||||
|
||||
Requires: %{name}-core = %{version}-%{release}
|
||||
Requires: %{prefix}-server
|
||||
@ -158,6 +160,9 @@ tox -epy3
|
||||
|
||||
|
||||
%changelog
|
||||
* Tue Apr 6 2021 Rob Crittenden <rcritten@redhat.com> - 0.8-7
|
||||
- Add check to validate the KRA Agent is correct (#1894781)
|
||||
|
||||
* Thu Apr 15 2021 Mohan Boddu <mboddu@redhat.com> - 0.8-6.1
|
||||
- Rebuilt for RHEL 9 BETA on Apr 15th 2021. Related: rhbz#1947937
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user