import ipa-healthcheck-0.16-3.el9

c9-beta imports/c9-beta/ipa-healthcheck-0.16-3.el9
MSVSphere Packaging Team 11 months ago
parent 2d2dd58418
commit 9a38dd100c

2
.gitignore vendored

@ -1 +1 @@
SOURCES/0.12.tar.gz
SOURCES/0.16.tar.gz

@ -1 +1 @@
dc05dc0ca441dcb1a87e3b3bd7d440d79c17ac0a SOURCES/0.12.tar.gz
b6edbda881bceb9e0266169e06eb35984b1a7a77 SOURCES/0.16.tar.gz

@ -1,6 +1,6 @@
From 9d5f9d21442ee483044fc55a5c02039af23869d7 Mon Sep 17 00:00:00 2001
From 378e0a353e670a6b498d454558a9139a859890d4 Mon Sep 17 00:00:00 2001
From: Rob Crittenden <rcritten@redhat.com>
Date: Thu, 1 Dec 2022 14:22:46 -0500
Date: Thu, 9 Nov 2023 10:49:05 -0500
Subject: [PATCH] Remove ipaclustercheck
---
@ -26,13 +26,13 @@ Subject: [PATCH] Remove ipaclustercheck
delete mode 100644 tests/test_cluster_ruv.py
diff --git a/setup.py b/setup.py
index 0cfa486..b9e1ca1 100644
index d926302..cb6265a 100644
--- a/setup.py
+++ b/setup.py
@@ -4,7 +4,7 @@ from setuptools import find_packages, setup
setup(
name='ipahealthcheck',
version='0.12',
version='0.16',
- namespace_packages=['ipahealthcheck', 'ipaclustercheck'],
+ namespace_packages=['ipahealthcheck'],
package_dir={'': 'src'},

@ -1,64 +0,0 @@
From d2cd8292d8a1d7c2fd2a5f978f8ed76c0769e5e9 Mon Sep 17 00:00:00 2001
From: Rob Crittenden <rcritten@redhat.com>
Date: Tue, 8 Feb 2022 14:16:06 -0500
Subject: [PATCH] Disable two failing tests
These test that healthcheck can properly detect when IPA
is not installed or configured. Its not ideal to remove them
from the check process but they aren't critical.
---
tests/test_commands.py | 41 -----------------------------------------
1 file changed, 41 deletions(-)
diff --git a/tests/test_commands.py b/tests/test_commands.py
index 988d7fc..e14114b 100644
--- a/tests/test_commands.py
+++ b/tests/test_commands.py
@@ -14,44 +14,3 @@ def test_version():
"""
output = run(['ipa-healthcheck', '--version'], env=os.environ)
assert 'ipahealthcheck' in output.raw_output.decode('utf-8')
-
-
-@pytest.fixture
-def python_ipalib_dir(tmpdir):
- ipalib_dir = tmpdir.mkdir("ipalib")
- ipalib_dir.join("__init__.py").write("")
-
- def _make_facts(configured=None):
- if configured is None:
- module_text = ""
- elif isinstance(configured, bool):
- module_text = f"def is_ipa_configured(): return {configured}"
- else:
- raise TypeError(
- f"'configured' must be None or bool, got '{configured!r}'"
- )
-
- ipalib_dir.join("facts.py").write(module_text)
- return str(tmpdir)
-
- return _make_facts
-
-
-def test_ipa_notinstalled(python_ipalib_dir, monkeypatch):
- """
- Test ipa-healthcheck handles the missing IPA stuff
- """
- monkeypatch.setenv("PYTHONPATH", python_ipalib_dir(configured=None))
- output = run(["ipa-healthcheck"], raiseonerr=False, env=os.environ)
- assert output.returncode == 1
- assert "IPA server is not installed" in output.raw_output.decode("utf-8")
-
-
-def test_ipa_unconfigured(python_ipalib_dir, monkeypatch):
- """
- Test ipa-healthcheck handles the unconfigured IPA server
- """
- monkeypatch.setenv("PYTHONPATH", python_ipalib_dir(configured=False))
- output = run(["ipa-healthcheck"], raiseonerr=False, env=os.environ)
- assert output.returncode == 1
- assert "IPA server is not configured" in output.raw_output.decode("utf-8")
--
2.31.1

@ -0,0 +1,44 @@
From 35ff77300758c12110132d6d638802d5b223bd6d Mon Sep 17 00:00:00 2001
From: Rob Crittenden <rcritten@redhat.com>
Date: Mon, 13 Nov 2023 14:09:16 -0500
Subject: [PATCH] Don't fail if a service name cannot be looked up in LDAP
A new method was introduced to handle more IPA services. This
requires looking some of them up in LDAP. dirsrv not running
was not being caught so raised an error instead.
Fixes: https://github.com/freeipa/freeipa-healthcheck/issues/312
Signed-off-by: Rob Crittenden <rcritten@redhat.com>
---
src/ipahealthcheck/meta/services.py | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/src/ipahealthcheck/meta/services.py b/src/ipahealthcheck/meta/services.py
index 10fa83f..9838128 100644
--- a/src/ipahealthcheck/meta/services.py
+++ b/src/ipahealthcheck/meta/services.py
@@ -25,10 +25,18 @@ class IPAServiceCheck(ServiceCheck):
def get_service_name(self, role):
"""Roles define broad services. Translate a role name into
an individual service name.
+
+ Returns a string on success, None if the service is not
+ configured or cannot be determined.
"""
conn = api.Backend.ldap2
- if not api.Backend.ldap2.isconnected():
- api.Backend.ldap2.connect()
+ try:
+ if not api.Backend.ldap2.isconnected():
+ api.Backend.ldap2.connect()
+ except errors.NetworkError:
+ logger.debug("Service '%s' is not running", self.service_name)
+ return None
+
dn = DN(
("cn", role), ("cn", api.env.host),
("cn", "masters"), ("cn", "ipa"), ("cn", "etc"),
--
2.41.0

@ -1,340 +0,0 @@
From 30471ebdc9fe5871c115ca06f78a415275a320e6 Mon Sep 17 00:00:00 2001
From: Rob Crittenden <rcritten@redhat.com>
Date: Thu, 16 Jun 2022 20:02:51 +0000
Subject: [PATCH] Skip AD domains with posix ranges in the catalog check
The catalog check is intended to ensure that the trust is
working by looking up a user. For a non-posix range we can use
the Administrator user because it has a predicible SID.
With a posix range the UID/GID may not be set so the lookup
can fail (with an empty return value).
So skip domain which have a posix range associated with it.
Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1775199
Signed-off-by: Rob Crittenden <rcritten@redhat.com>
---
src/ipahealthcheck/ipa/trust.py | 34 ++++-
tests/test_ipa_trust.py | 214 +++++++++++++++++++++++++++++++-
2 files changed, 243 insertions(+), 5 deletions(-)
diff --git a/src/ipahealthcheck/ipa/trust.py b/src/ipahealthcheck/ipa/trust.py
index 27a2c86..b962807 100644
--- a/src/ipahealthcheck/ipa/trust.py
+++ b/src/ipahealthcheck/ipa/trust.py
@@ -183,6 +183,7 @@ class IPATrustDomainsCheck(IPAPlugin):
except Exception as e:
yield Result(self, constants.WARNING,
key='domain-status',
+ domain=domain,
error=str(e),
msg='Execution of {key} failed: {error}')
continue
@@ -262,6 +263,10 @@ class IPATrustCatalogCheck(IPAPlugin):
This should populate the 'AD Global catalog' and 'AD Domain Controller'
fields in 'sssctl domain-status' output (means SSSD actually talks to AD
DCs)
+
+ If the associated idrange type is ipa-ad-trust-posix then the
+ check will be skipped because we can't predict what the UID of the
+ Administrator account will be.
"""
@duration
def check(self):
@@ -280,20 +285,41 @@ class IPATrustCatalogCheck(IPAPlugin):
for trust_domain in trust_domains:
sid = trust_domain.get('domainsid')
+ domain = trust_domain['domain']
+ idrange = api.Command.idrange_find(sid)
+ if len(idrange['result']) == 0:
+ yield Result(self, constants.WARNING,
+ key=sid,
+ domain=domain,
+ msg='Domain {domain} does not have an idrange')
+ continue
+
+ if 'ipa-ad-trust-posix' in idrange['result'][0]['iparangetyperaw']:
+ yield Result(self, constants.SUCCESS,
+ key=sid,
+ domain=domain,
+ type='ipa-ad-trust-posix')
+ logger.debug("Domain %s is a POSIX range, skip the lookup",
+ domain)
+ continue
+
try:
id = pysss_nss_idmap.getnamebysid(sid + '-500')
except Exception as e:
yield Result(self, constants.ERROR,
- key=sid,
+ key=id,
+ domain=domain,
error=str(e),
- msg='Look up of{key} failed: {error}')
+ msg='Look up of ID {key} for {domain} failed: '
+ '{error}')
continue
if not id:
yield Result(self, constants.WARNING,
- key=sid,
+ key=id,
+ domain=trust_domain['domain'],
error='returned nothing',
- msg='Look up of {key} {error}')
+ msg='Look up of ID {key} for {domain} {error}')
else:
yield Result(self, constants.SUCCESS,
key='Domain Security Identifier',
diff --git a/tests/test_ipa_trust.py b/tests/test_ipa_trust.py
index c314b70..6c4754a 100644
--- a/tests/test_ipa_trust.py
+++ b/tests/test_ipa_trust.py
@@ -129,6 +129,74 @@ def trustdomain_find():
]
+def idrange_find_adrange_type():
+ """
+ Return a set of idranges of type "Active Directory domain range"
+ """
+
+ return {
+ "result": [
+ {
+ "cn": ["AD.EXAMPLE_id_range"],
+ "ipabaseid": ["1664000000"],
+ "ipabaserid": ["0"],
+ "ipaidrangesize": ["200000"],
+ "ipanttrusteddomainsid": ["S-1-5-21-abc"],
+ "iparangetype": ["Active Directory domain range"],
+ "iparangetyperaw": ["ipa-ad-trust"]
+ },
+ {
+ "cn": ["CHILD.AD.EXAMPLE_id_range"],
+ "ipabaseid": ["538600000"],
+ "ipabaserid": ["0"],
+ "ipaidrangesize": ["200000"],
+ "ipanttrusteddomainsid": [
+ "S-1-5-21-38045160-610119595-3099869984"
+ ],
+ "iparangetype": ["Active Directory domain range"],
+ "iparangetyperaw": ["ipa-ad-trust"]
+ },
+ {
+ "cn": ["IPA.EXAMPLE_id_range"],
+ "ipabaseid": ["447400000"],
+ "ipabaserid": ["1000"],
+ "ipaidrangesize": ["200000"],
+ "iparangetype": ["local domain range"],
+ "iparangetyperaw": ["ipa-local"],
+ "ipasecondarybaserid": ["100000000"]
+ }]
+ }
+
+
+def idrange_find_adrange_posix():
+ """
+ Return a set of idranges of type
+ "Active Directory trust range with POSIX attributes"
+ """
+
+ return {
+ "result": [
+ {
+ "cn": ["AD.EXAMPLE_id_range"],
+ "ipabaseid": ["1664000000"],
+ "ipaidrangesize": ["200000"],
+ "ipanttrusteddomainsid": ["S-1-5-21-abc"],
+ "iparangetype": [
+ "Active Directory trust range with POSIX attributes"],
+ "iparangetyperaw": ["ipa-ad-trust-posix"]
+ },
+ {
+ "cn": ["IPA.EXAMPLE_id_range"],
+ "ipabaseid": ["447400000"],
+ "ipabaserid": ["1000"],
+ "ipaidrangesize": ["200000"],
+ "iparangetype": ["local domain range"],
+ "iparangetyperaw": ["ipa-local"],
+ "ipasecondarybaserid": ["100000000"]
+ }]
+ }
+
+
class SSSDDomain:
def __init__(self, return_ipa_server_mode=True, provider='ipa'):
self.return_ipa_server_mode = return_ipa_server_mode
@@ -454,7 +522,8 @@ class TestTrustCatalog(BaseTest):
@patch('pysss_nss_idmap.getnamebysid')
@patch('ipapython.ipautil.run')
- def test_trust_catalog_ok(self, mock_run, mock_getnamebysid):
+ def test_trust_catalog_adrange(self, mock_run, mock_getnamebysid):
+ """The associated ID ranges are Active Directory domain range"""
# id Administrator@ad.example
dsresult = namedtuple('run', ['returncode', 'error_log'])
dsresult.returncode = 0
@@ -478,6 +547,11 @@ class TestTrustCatalog(BaseTest):
# get_trust_domains()
m_api.Command.trust_find.side_effect = trust_find()
m_api.Command.trustdomain_find.side_effect = trustdomain_find()
+ m_api.Command.idrange_find.side_effect = [
+ idrange_find_adrange_type(),
+ idrange_find_adrange_type(),
+ idrange_find_adrange_type()
+ ]
framework = object()
registry.initialize(framework, config.Config)
@@ -550,6 +624,144 @@ class TestTrustCatalog(BaseTest):
assert result.kw.get('key') == 'AD Domain Controller'
assert result.kw.get('domain') == 'child.example'
+ @patch('pysss_nss_idmap.getnamebysid')
+ @patch('ipapython.ipautil.run')
+ def test_trust_catalog_posix(self, mock_run, mock_getnamebysid):
+ """AD POSIX ranges"""
+ # id Administrator@ad.example
+ dsresult = namedtuple('run', ['returncode', 'error_log'])
+ dsresult.returncode = 0
+ dsresult.error_log = ''
+ dsresult.output = 'Active servers:\nAD Global Catalog: ' \
+ 'root-dc.ad.vm\nAD Domain Controller: root-dc.ad.vm\n' \
+ 'IPA: master.ipa.vm\n\n'
+ ds2result = namedtuple('run', ['returncode', 'error_log'])
+ ds2result.returncode = 0
+ ds2result.error_log = ''
+ ds2result.output = 'Active servers:\nAD Global Catalog: ' \
+ 'root-dc.ad.vm\nAD Domain Controller: root-dc.ad.vm\n' \
+
+ mock_run.side_effect = [dsresult, dsresult, ds2result]
+ mock_getnamebysid.side_effect = [
+ {'S-1-5-21-abc-500': {'name': 'admin@ad.example', 'type': 3}},
+ {'S-1-5-21-ghi-500': {'name': 'admin@child.ad.example', 'type': 3}},
+ {'S-1-5-21-def-500': {'name': 'admin@child.example', 'type': 3}}
+ ]
+
+ # get_trust_domains()
+ m_api.Command.trust_find.side_effect = trust_find()
+ m_api.Command.trustdomain_find.side_effect = trustdomain_find()
+ m_api.Command.idrange_find.side_effect = [
+ idrange_find_adrange_posix(),
+ idrange_find_adrange_posix(),
+ idrange_find_adrange_posix()
+ ]
+
+ framework = object()
+ registry.initialize(framework, config.Config)
+ registry.trust_agent = True
+ f = IPATrustCatalogCheck(registry)
+
+ self.results = capture_results(f)
+
+ assert len(self.results) == 3
+
+ result = self.results.results[0]
+ assert result.result == constants.SUCCESS
+ assert result.source == 'ipahealthcheck.ipa.trust'
+ assert result.check == 'IPATrustCatalogCheck'
+ assert result.kw.get('key') == 'S-1-5-21-abc'
+ assert result.kw.get('domain') == 'ad.example'
+ assert result.kw.get('type') == 'ipa-ad-trust-posix'
+
+ result = self.results.results[1]
+ assert result.result == constants.SUCCESS
+ assert result.source == 'ipahealthcheck.ipa.trust'
+ assert result.check == 'IPATrustCatalogCheck'
+ assert result.kw.get('key') == 'S-1-5-22-def'
+ assert result.kw.get('domain') == 'child.ad.example'
+ assert result.kw.get('type') == 'ipa-ad-trust-posix'
+
+ result = self.results.results[2]
+ assert result.result == constants.SUCCESS
+ assert result.source == 'ipahealthcheck.ipa.trust'
+ assert result.check == 'IPATrustCatalogCheck'
+ assert result.kw.get('key') == 'S-1-5-21-ghi'
+ assert result.kw.get('domain') == 'child.example'
+ assert result.kw.get('type') == 'ipa-ad-trust-posix'
+
+ @patch('pysss_nss_idmap.getnamebysid')
+ @patch('ipapython.ipautil.run')
+ def test_trust_catalog_posix_missing(self, mock_run, mock_getnamebysid):
+ """AD POSIX ranges"""
+ # id Administrator@ad.example
+ dsresult = namedtuple('run', ['returncode', 'error_log'])
+ dsresult.returncode = 0
+ dsresult.error_log = ''
+ dsresult.output = 'Active servers:\nAD Global Catalog: ' \
+ 'root-dc.ad.vm\nAD Domain Controller: root-dc.ad.vm\n' \
+ 'IPA: master.ipa.vm\n\n'
+ ds2result = namedtuple('run', ['returncode', 'error_log'])
+ ds2result.returncode = 0
+ ds2result.error_log = ''
+ ds2result.output = 'Active servers:\nAD Global Catalog: ' \
+ 'root-dc.ad.vm\nAD Domain Controller: root-dc.ad.vm\n' \
+
+ mock_run.side_effect = [dsresult, dsresult, ds2result]
+ mock_getnamebysid.side_effect = [
+ {'S-1-5-21-abc-500': {'name': 'admin@ad.example', 'type': 3}},
+ {'S-1-5-21-ghi-500': {'name': 'admin@child.ad.example', 'type': 3}},
+ {'S-1-5-21-def-500': {'name': 'admin@child.example', 'type': 3}}
+ ]
+
+ # get_trust_domains()
+ m_api.Command.trust_find.side_effect = trust_find()
+ m_api.Command.trustdomain_find.side_effect = trustdomain_find()
+ m_api.Command.idrange_find.side_effect = [
+ idrange_find_adrange_posix(),
+ {'result': []},
+ {'result': []}
+ ]
+
+ framework = object()
+ registry.initialize(framework, config.Config)
+ registry.trust_agent = True
+ f = IPATrustCatalogCheck(registry)
+
+ self.results = capture_results(f)
+
+ assert len(self.results) == 3
+
+ result = self.results.results[0]
+ assert result.result == constants.SUCCESS
+ assert result.source == 'ipahealthcheck.ipa.trust'
+ assert result.check == 'IPATrustCatalogCheck'
+ assert result.kw.get('key') == 'S-1-5-21-abc'
+ assert result.kw.get('domain') == 'ad.example'
+ assert result.kw.get('type') == 'ipa-ad-trust-posix'
+
+ result = self.results.results[1]
+ assert result.result == constants.WARNING
+ assert result.source == 'ipahealthcheck.ipa.trust'
+ assert result.check == 'IPATrustCatalogCheck'
+ assert result.kw.get('key') == 'S-1-5-22-def'
+ assert result.kw.get('domain') == 'child.ad.example'
+ assert (
+ result.kw.get('msg')
+ == 'Domain {domain} does not have an idrange'
+ )
+
+ result = self.results.results[2]
+ assert result.result == constants.WARNING
+ assert result.source == 'ipahealthcheck.ipa.trust'
+ assert result.check == 'IPATrustCatalogCheck'
+ assert result.kw.get('key') == 'S-1-5-21-ghi'
+ assert result.kw.get('domain') == 'child.example'
+ assert (
+ result.kw.get('msg')
+ == 'Domain {domain} does not have an idrange'
+ )
+
class Testsidgen(BaseTest):
patches = {
--
2.39.2

@ -0,0 +1,44 @@
From d1cb1997737c938bbc61d547aae277e308e78fce Mon Sep 17 00:00:00 2001
From: Rob Crittenden <rcritten@redhat.com>
Date: Tue, 14 Nov 2023 20:32:54 -0500
Subject: [PATCH] Temporarily disable the ipa-ods-exporter service status check
There is a bug in this service such that it will almost always
report as down. Rather than spamming users with this error give
time for it to be fixed in IPA upstream.
See https://pagure.io/freeipa/issue/9463
Signed-off-by: Rob Crittenden <rcritten@redhat.com>
---
src/ipahealthcheck/meta/services.py | 14 --------------
1 file changed, 14 deletions(-)
diff --git a/src/ipahealthcheck/meta/services.py b/src/ipahealthcheck/meta/services.py
index 9838128..b8973cb 100644
--- a/src/ipahealthcheck/meta/services.py
+++ b/src/ipahealthcheck/meta/services.py
@@ -202,20 +202,6 @@ class ods_enforcerd(IPAServiceCheck):
return super().check()
-@registry
-class ipa_ods_exporter(IPAServiceCheck):
- requires = ('dirsrv',)
-
- def check(self, instance=''):
- self.service_name = self.get_service_name('DNSKeyExporter')
-
- if self.service_name is None:
- # No service name means it is not configured
- return ()
-
- return super().check()
-
-
@registry
class ipa_dnskeysyncd(IPAServiceCheck):
requires = ('dirsrv',)
--
2.40.1

@ -1,142 +0,0 @@
From 4906c52b629bfce275558d4701c083f4c020ef32 Mon Sep 17 00:00:00 2001
From: Rob Crittenden <rcritten@redhat.com>
Date: Fri, 30 Jun 2023 14:35:40 -0400
Subject: [PATCH] Catch exceptions during user/group name lookup in FileCheck
It's possible that one or more of the allowed users/groups
in a file check do not exist on the system. Catch this
exception and try to proceed as best as possible.
https://github.com/freeipa/freeipa-healthcheck/issues/296
Signed-off-by: Rob Crittenden <rcritten@redhat.com>
---
src/ipahealthcheck/core/files.py | 33 +++++++++++++++++++++++----
tests/test_core_files.py | 38 ++++++++++++++++++++++++++++++++
2 files changed, 67 insertions(+), 4 deletions(-)
diff --git a/src/ipahealthcheck/core/files.py b/src/ipahealthcheck/core/files.py
index 59e8b76..85d42bc 100644
--- a/src/ipahealthcheck/core/files.py
+++ b/src/ipahealthcheck/core/files.py
@@ -3,12 +3,15 @@
#
import grp
+import logging
import os
import pwd
from ipahealthcheck.core import constants
from ipahealthcheck.core.plugin import Result, duration
+logger = logging.getLogger()
+
class FileCheck:
"""Generic check to validate permission and ownership of files
@@ -77,14 +80,25 @@ class FileCheck:
found = False
for o in owner:
- fowner = pwd.getpwnam(o)
+ try:
+ fowner = pwd.getpwnam(o)
+ except Exception as e:
+ logging.debug('user lookup "%s" for "%s" failed: %s',
+ o, path, e)
+ continue
if fowner.pw_uid == stat.st_uid:
found = True
break
if not found:
- actual = pwd.getpwuid(stat.st_uid)
key = '%s_owner' % path.replace('/', '_')
+ try:
+ actual = pwd.getpwuid(stat.st_uid)
+ except Exception:
+ yield Result(self, constants.WARNING, key=key,
+ path=path, type='owner', expected=owner,
+ got='Unknown uid %s' % stat.st_uid)
+ continue
if len(owner) == 1:
msg = 'Ownership of %s is %s and should ' \
'be %s' % \
@@ -104,14 +118,25 @@ class FileCheck:
found = False
for g in group:
- fgroup = grp.getgrnam(g)
+ try:
+ fgroup = grp.getgrnam(g)
+ except Exception as e:
+ logging.debug('group lookup "%s" for "%s" failed: %s',
+ g, path, e)
+ continue
if fgroup.gr_gid == stat.st_gid:
found = True
break
if not found:
key = '%s_group' % path.replace('/', '_')
- actual = grp.getgrgid(stat.st_gid)
+ try:
+ actual = grp.getgrgid(stat.st_gid)
+ except Exception:
+ yield Result(self, constants.WARNING, key=key,
+ path=path, type='group', expected=group,
+ got='Unknown gid %s' % stat.st_gid)
+ continue
if len(group) == 1:
msg = 'Group of %s is %s and should ' \
'be %s' % \
diff --git a/tests/test_core_files.py b/tests/test_core_files.py
index 6e3ec38..924d7fa 100644
--- a/tests/test_core_files.py
+++ b/tests/test_core_files.py
@@ -197,3 +197,41 @@ def test_files_not_found(mock_exists):
for result in my_results.results:
assert result.result == constants.SUCCESS
assert result.kw.get('msg') == 'File does not exist'
+
+
+@patch('os.stat')
+@patch('pwd.getpwnam')
+@patch('pwd.getpwuid')
+def test_files_owner_not_found(mock_pwuid, mock_pwnam, mock_stat):
+ mock_pwuid.side_effect = KeyError('getpwnam(): name not found')
+ mock_pwnam.side_effect = KeyError('getpwuid(): uid not found')
+ mock_stat.return_value = make_stat()
+
+ f = FileCheck()
+ f.files = files
+
+ results = capture_results(f)
+
+ my_results = get_results(results, 'owner')
+ for result in my_results.results:
+ assert result.result == constants.WARNING
+ assert result.kw.get('got') == 'Unknown uid 0'
+
+
+@patch('os.stat')
+@patch('grp.getgrnam')
+@patch('grp.getgrgid')
+def test_files_group_not_found(mock_grgid, mock_grnam, mock_stat):
+ mock_grgid.side_effect = KeyError('getgrnam(): name not found')
+ mock_grnam.side_effect = KeyError('getgruid(): gid not found')
+ mock_stat.return_value = make_stat()
+
+ f = FileCheck()
+ f.files = files
+
+ results = capture_results(f)
+
+ my_results = get_results(results, 'group')
+ for result in my_results.results:
+ assert result.result == constants.WARNING
+ assert result.kw.get('got') == 'Unknown gid 0'
--
2.41.0

@ -0,0 +1,46 @@
From e556edc0b1cb607caa50f760d5059877f35fbcdc Mon Sep 17 00:00:00 2001
From: Rob Crittenden <rcritten@redhat.com>
Date: Thu, 11 Jan 2024 14:40:02 -0500
Subject: [PATCH] Skip DogtagCertsConfigCheck for PKI versions >= 11.5.0
In 11.5.0 the PKI project stopped storing the certificate
blobs in CS.cfg. If we continue to check it we will report a
false positive so skip it in that case.
Fixes: https://github.com/freeipa/freeipa-healthcheck/issues/317
Signed-off-by: Rob Crittenden <rcritten@redhat.com>
---
src/ipahealthcheck/dogtag/ca.py | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/src/ipahealthcheck/dogtag/ca.py b/src/ipahealthcheck/dogtag/ca.py
index 4afa5d7..ddf5ece 100644
--- a/src/ipahealthcheck/dogtag/ca.py
+++ b/src/ipahealthcheck/dogtag/ca.py
@@ -16,6 +16,8 @@ from ipaserver.install import krainstance
from ipapython.directivesetter import get_directive
from cryptography.hazmat.primitives.serialization import Encoding
+import pki.util
+
logger = logging.getLogger()
@@ -30,6 +32,13 @@ class DogtagCertsConfigCheck(DogtagPlugin):
logger.debug("No CA configured, skipping dogtag config check")
return
+ pki_version = pki.util.Version(pki.specification_version())
+ if pki_version >= pki.util.Version("11.5.0"):
+ logger.debug(
+ "PKI 11.5.0 no longer stores certificats in CS.cfg"
+ )
+ return
+
kra = krainstance.KRAInstance(api.env.realm)
blobs = {'auditSigningCert cert-pki-ca': 'ca.audit_signing.cert',
--
2.42.0

@ -1,372 +0,0 @@
From 29855ec76bcb445543e1f2b16b13e5bcfeb67723 Mon Sep 17 00:00:00 2001
From: Rob Crittenden <rcritten@redhat.com>
Date: Mon, 27 Mar 2023 16:43:11 -0400
Subject: [PATCH] Don't error in DogtagCertsConnectivityCheck with external CAs
The purpose of the check is to validate that communication
with the CA works. In the past we looked up serial number 1
for this check. The problem is that if the server was
installed with RSNv3 so had no predictable CA serial number.
It also was broken with externally-issued CA certificate which
cannot be looked up in IPA.
Instead use the IPA RA agent certificate which should definitely
have a serial number in the IPA CA if one is configured.
Fixes: https://github.com/freeipa/freeipa-healthcheck/issues/285
Signed-off-by: Rob Crittenden <rcritten@redhat.com>
---
src/ipahealthcheck/dogtag/ca.py | 45 +++-----
tests/test_dogtag_connectivity.py | 175 +++++-------------------------
2 files changed, 39 insertions(+), 181 deletions(-)
diff --git a/src/ipahealthcheck/dogtag/ca.py b/src/ipahealthcheck/dogtag/ca.py
index 868876f..4afa5d7 100644
--- a/src/ipahealthcheck/dogtag/ca.py
+++ b/src/ipahealthcheck/dogtag/ca.py
@@ -12,10 +12,8 @@ from ipahealthcheck.core import constants
from ipalib import api, errors, x509
from ipaplatform.paths import paths
from ipaserver.install import certs
-from ipaserver.install import ca
from ipaserver.install import krainstance
from ipapython.directivesetter import get_directive
-from ipapython.dn import DN
from cryptography.hazmat.primitives.serialization import Encoding
logger = logging.getLogger()
@@ -95,6 +93,10 @@ class DogtagCertsConfigCheck(DogtagPlugin):
class DogtagCertsConnectivityCheck(DogtagPlugin):
"""
Test basic connectivity by using cert-show to fetch a cert
+
+ The RA agent certificate is used because if a CA is configured we
+ know this certificate should exist. Use its serial number to do
+ the lookup.
"""
requires = ('dirsrv',)
@@ -104,59 +106,38 @@ class DogtagCertsConnectivityCheck(DogtagPlugin):
logger.debug('CA is not configured, skipping connectivity check')
return
- config = api.Command.config_show()
-
- subject_base = config['result']['ipacertificatesubjectbase'][0]
- ipa_subject = ca.lookup_ca_subject(api, subject_base)
try:
- certs = x509.load_certificate_list_from_file(paths.IPA_CA_CRT)
+ cert = x509.load_certificate_from_file(paths.RA_AGENT_PEM)
except Exception as e:
yield Result(self, constants.ERROR,
- key='ipa_ca_crt_file_missing',
- path=paths.IPA_CA_CRT,
+ key='ipa_ra_crt_file_missing',
+ path=paths.RA_AGENT_PEM,
error=str(e),
- msg='The IPA CA cert file {path} could not be '
+ msg='The IPA RA cert file {path} could not be '
'opened: {error}')
return
- found = False
- for cert in certs:
- if DN(cert.subject) == ipa_subject:
- found = True
- break
-
- if not found:
- yield Result(self, constants.ERROR,
- key='ipa_ca_cert_not_found',
- subject=str(ipa_subject),
- path=paths.IPA_CA_CRT,
- msg='The CA certificate with subject {subject} '
- 'was not found in {path}')
- return
- # Load the IPA CA certificate to obtain its serial number. This
- # was traditionally 1 prior to random serial number support.
- # There is nothing special about cert 1. Even if there is no cert
- # serial number 1 but the connection is ok it is considered passing.
+ # We used to use serial #1 but with RSNv3 it can be anything.
try:
api.Command.cert_show(cert.serial_number, all=True)
except errors.CertificateOperationError as e:
if 'not found' in str(e):
yield Result(self, constants.ERROR,
- key='cert_show_1',
+ key='cert_show_ra',
error=str(e),
serial=str(cert.serial_number),
msg='Serial number not found: {error}')
else:
yield Result(self, constants.ERROR,
- key='cert_show_1',
+ key='cert_show_ra',
error=str(e),
serial=str(cert.serial_number),
msg='Request for certificate failed: {error}')
except Exception as e:
yield Result(self, constants.ERROR,
- key='cert_show_1',
+ key='cert_show_ra',
error=str(e),
serial=str(cert.serial_number),
- msg='Request for certificate failed: {error')
+ msg='Request for certificate failed: {error}')
else:
yield Result(self, constants.SUCCESS)
diff --git a/tests/test_dogtag_connectivity.py b/tests/test_dogtag_connectivity.py
index d81e598..4413fe1 100644
--- a/tests/test_dogtag_connectivity.py
+++ b/tests/test_dogtag_connectivity.py
@@ -13,14 +13,23 @@ from ipahealthcheck.dogtag.ca import DogtagCertsConnectivityCheck
from ipalib.errors import CertificateOperationError
from ipaplatform.paths import paths
-from ipapython.dn import DN
+
+
+default_subject_base = [{
+ 'result':
+ {
+ 'ipacertificatesubjectbase': [f'O={m_api.env.realm}'],
+ },
+}]
class IPACertificate:
def __init__(self, serial_number=1,
- subject='CN=Certificate Authority, O=%s' % m_api.env.realm):
+ subject='CN=Certificate Authority, O=%s' % m_api.env.realm,
+ issuer='CN=Certificate Authority, O=%s' % m_api.env.realm):
self.serial_number = serial_number
self.subject = subject
+ self.issuer = issuer
def __eq__(self, other):
return self.serial_number == other.serial_number
@@ -50,18 +59,15 @@ class TestCAConnectivity(BaseTest):
Mock(return_value=CAInstance()),
}
- @patch('ipaserver.install.ca.lookup_ca_subject')
- @patch('ipalib.x509.load_certificate_list_from_file')
- def test_ca_connection_ok(self, mock_load_cert, mock_ca_subject):
+ @patch('ipalib.x509.load_certificate_from_file')
+ def test_ca_connection_ok(self, mock_load_cert):
"""CA connectivity check when cert_show returns a valid value"""
m_api.Command.cert_show.side_effect = None
m_api.Command.config_show.side_effect = subject_base
m_api.Command.cert_show.return_value = {
u'result': {u'revoked': False}
}
- mock_load_cert.return_value = [IPACertificate(12345)]
- mock_ca_subject.return_value = DN(('cn', 'Certificate Authority'),
- f'O={m_api.env.realm}')
+ mock_load_cert.return_value = IPACertificate(12345)
framework = object()
registry.initialize(framework, config.Config)
@@ -76,10 +82,8 @@ class TestCAConnectivity(BaseTest):
assert result.source == 'ipahealthcheck.dogtag.ca'
assert result.check == 'DogtagCertsConnectivityCheck'
- @patch('ipaserver.install.ca.lookup_ca_subject')
- @patch('ipalib.x509.load_certificate_list_from_file')
- def test_ca_connection_cert_not_found(self, mock_load_cert,
- mock_ca_subject):
+ @patch('ipalib.x509.load_certificate_from_file')
+ def test_ca_connection_cert_not_found(self, mock_load_cert):
"""CA connectivity check for a cert that doesn't exist"""
m_api.Command.cert_show.reset_mock()
m_api.Command.config_show.side_effect = subject_base
@@ -87,9 +91,7 @@ class TestCAConnectivity(BaseTest):
message='Certificate operation cannot be completed: '
'EXCEPTION (Certificate serial number 0x0 not found)'
)
- mock_load_cert.return_value = [IPACertificate()]
- mock_ca_subject.return_value = DN(('cn', 'Certificate Authority'),
- f'O={m_api.env.realm}')
+ mock_load_cert.return_value = IPACertificate(serial_number=7)
framework = object()
registry.initialize(framework, config.Config)
@@ -103,46 +105,16 @@ class TestCAConnectivity(BaseTest):
assert result.result == constants.ERROR
assert result.source == 'ipahealthcheck.dogtag.ca'
assert result.check == 'DogtagCertsConnectivityCheck'
- assert result.kw.get('key') == 'cert_show_1'
- assert result.kw.get('serial') == '1'
+ assert result.kw.get('key') == 'cert_show_ra'
+ assert result.kw.get('serial') == '7'
assert result.kw.get('msg') == 'Serial number not found: {error}'
- @patch('ipaserver.install.ca.lookup_ca_subject')
- @patch('ipalib.x509.load_certificate_list_from_file')
- def test_ca_connection_cert_file_not_found(self, mock_load_cert,
- mock_ca_subject):
+ @patch('ipalib.x509.load_certificate_from_file')
+ def test_ca_connection_cert_file_not_found(self, mock_load_cert):
"""CA connectivity check for a cert that doesn't exist"""
m_api.Command.cert_show.reset_mock()
m_api.Command.config_show.side_effect = subject_base
mock_load_cert.side_effect = FileNotFoundError()
- mock_ca_subject.return_value = DN(('cn', 'Certificate Authority'),
- f'O={m_api.env.realm}')
-
- framework = object()
- registry.initialize(framework, config.Config)
- f = DogtagCertsConnectivityCheck(registry)
-
- self.results = capture_results(f)
-
- assert len(self.results) == 1
-
- result = self.results.results[0]
- assert result.result == constants.ERROR
- assert result.source == 'ipahealthcheck.dogtag.ca'
- assert result.check == 'DogtagCertsConnectivityCheck'
- assert result.kw.get('key') == 'ipa_ca_crt_file_missing'
- assert result.kw.get('path') == paths.IPA_CA_CRT
-
- @patch('ipaserver.install.ca.lookup_ca_subject')
- @patch('ipalib.x509.load_certificate_list_from_file')
- def test_ca_connection_cert_not_in_file_list(self, mock_load_cert,
- mock_ca_subject):
- """CA connectivity check for a cert that isn't in IPA_CA_CRT"""
- m_api.Command.cert_show.reset_mock()
- m_api.Command.config_show.side_effect = bad_subject_base
- mock_load_cert.return_value = [IPACertificate()]
- mock_ca_subject.return_value = DN(('cn', 'Certificate Authority'),
- 'O=BAD')
framework = object()
registry.initialize(framework, config.Config)
@@ -156,26 +128,18 @@ class TestCAConnectivity(BaseTest):
assert result.result == constants.ERROR
assert result.source == 'ipahealthcheck.dogtag.ca'
assert result.check == 'DogtagCertsConnectivityCheck'
- bad = bad_subject_base[0]['result']['ipacertificatesubjectbase'][0]
- bad_subject = DN(f'CN=Certificate Authority,{bad}')
- assert DN(result.kw['subject']) == bad_subject
- assert result.kw['path'] == paths.IPA_CA_CRT
- assert result.kw['msg'] == (
- 'The CA certificate with subject {subject} was not found in {path}'
- )
+ assert result.kw.get('key') == 'ipa_ra_crt_file_missing'
+ assert result.kw.get('path') == paths.RA_AGENT_PEM
- @patch('ipaserver.install.ca.lookup_ca_subject')
- @patch('ipalib.x509.load_certificate_list_from_file')
- def test_ca_connection_down(self, mock_load_cert, mock_ca_subject):
+ @patch('ipalib.x509.load_certificate_from_file')
+ def test_ca_connection_down(self, mock_load_cert):
"""CA connectivity check with the CA down"""
m_api.Command.cert_show.side_effect = CertificateOperationError(
message='Certificate operation cannot be completed: '
'Unable to communicate with CMS (503)'
)
m_api.Command.config_show.side_effect = subject_base
- mock_load_cert.return_value = [IPACertificate()]
- mock_ca_subject.return_value = DN(('cn', 'Certificate Authority'),
- f'O={m_api.env.realm}')
+ mock_load_cert.return_value = IPACertificate()
framework = object()
registry.initialize(framework, config.Config)
@@ -192,90 +156,3 @@ class TestCAConnectivity(BaseTest):
assert result.kw.get('msg') == (
'Request for certificate failed: {error}'
)
-
- @patch('ipaserver.install.ca.lookup_ca_subject')
- @patch('ipalib.x509.load_certificate_list_from_file')
- def test_ca_connection_multiple_ok(self, mock_load_cert, mock_ca_subject):
- """CA connectivity check when cert_show returns a valid value"""
- m_api.Command.cert_show.side_effect = None
- m_api.Command.config_show.side_effect = subject_base
- m_api.Command.cert_show.return_value = {
- u'result': {u'revoked': False}
- }
- mock_load_cert.return_value = [
- IPACertificate(1, 'CN=something'),
- IPACertificate(12345),
- ]
- mock_ca_subject.return_value = DN(('cn', 'Certificate Authority'),
- f'O={m_api.env.realm}')
-
- framework = object()
- registry.initialize(framework, config.Config)
- f = DogtagCertsConnectivityCheck(registry)
-
- self.results = capture_results(f)
-
- assert len(self.results) == 1
-
- result = self.results.results[0]
- assert result.result == constants.SUCCESS
- assert result.source == 'ipahealthcheck.dogtag.ca'
-
- @patch('ipaserver.install.ca.lookup_ca_subject')
- @patch('ipalib.x509.load_certificate_list_from_file')
- def test_ca_connection_multiple_ok_reverse(self, mock_load_cert,
- mock_ca_subject):
- """CA connectivity check when cert_show returns a valid value"""
- m_api.Command.cert_show.side_effect = None
- m_api.Command.config_show.side_effect = subject_base
- m_api.Command.cert_show.return_value = {
- u'result': {u'revoked': False}
- }
- mock_load_cert.return_value = [
- IPACertificate(12345),
- IPACertificate(1, 'CN=something'),
- ]
- mock_ca_subject.return_value = DN(('cn', 'Certificate Authority'),
- f'O={m_api.env.realm}')
-
- framework = object()
- registry.initialize(framework, config.Config)
- f = DogtagCertsConnectivityCheck(registry)
-
- self.results = capture_results(f)
-
- assert len(self.results) == 1
-
- result = self.results.results[0]
- assert result.result == constants.SUCCESS
- assert result.source == 'ipahealthcheck.dogtag.ca'
-
- @patch('ipaserver.install.ca.lookup_ca_subject')
- @patch('ipalib.x509.load_certificate_list_from_file')
- def test_ca_connection_not_found(self, mock_load_cert, mock_ca_subject):
- """CA connectivity check when cert_show returns a valid value"""
- m_api.Command.cert_show.side_effect = None
- m_api.Command.config_show.side_effect = subject_base
- m_api.Command.cert_show.return_value = {
- u'result': {u'revoked': False}
- }
- mock_load_cert.return_value = [
- IPACertificate(1, 'CN=something'),
- ]
- mock_ca_subject.return_value = DN(('cn', 'Certificate Authority'),
- f'O={m_api.env.realm}')
-
- framework = object()
- registry.initialize(framework, config.Config)
- f = DogtagCertsConnectivityCheck(registry)
-
- self.results = capture_results(f)
-
- assert len(self.results) == 1
-
- result = self.results.results[0]
- assert result.result == constants.ERROR
- assert result.source == 'ipahealthcheck.dogtag.ca'
- assert result.kw['msg'] == (
- 'The CA certificate with subject {subject} was not found in {path}'
- )
--
2.41.0

@ -0,0 +1,59 @@
From cafe01a23a36c408c8c60357ad2651688dc63599 Mon Sep 17 00:00:00 2001
From: Rob Crittenden <rcritten@redhat.com>
Date: Fri, 12 Jan 2024 10:17:18 -0500
Subject: [PATCH] test: Handle PKI >= 11.5.0 not storing certs in CS.cfg
Update the test to expect 0 results if the PKI version is
>= 11.5.0.
Fixes: https://github.com/freeipa/freeipa-healthcheck/issues/317
Signed-off-by: Rob Crittenden <rcritten@redhat.com>
---
tests/test_dogtag_ca.py | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/tests/test_dogtag_ca.py b/tests/test_dogtag_ca.py
index 0820aba..1f61dea 100644
--- a/tests/test_dogtag_ca.py
+++ b/tests/test_dogtag_ca.py
@@ -2,12 +2,16 @@
# Copyright (C) 2019 FreeIPA Contributors see COPYING for license
#
+import pki.util
from util import capture_results, CAInstance, KRAInstance
from base import BaseTest
from ipahealthcheck.core import config, constants
from ipahealthcheck.dogtag.plugin import registry
from ipahealthcheck.dogtag.ca import DogtagCertsConfigCheck
from unittest.mock import Mock, patch
+import pytest
+
+pki_version = pki.util.Version(pki.specification_version())
class mock_Cert:
@@ -43,6 +47,9 @@ class TestCACerts(BaseTest):
Mock(return_value=KRAInstance()),
}
+ @pytest.mark.skipif(
+ pki_version >= pki.util.Version("11.5.0"),
+ reason='Does not apply to PKI 11.5.0+')
@patch('ipahealthcheck.dogtag.ca.get_directive')
@patch('ipaserver.install.certs.CertDB')
def test_ca_certs_ok(self, mock_certdb, mock_directive):
@@ -71,6 +78,9 @@ class TestCACerts(BaseTest):
assert result.source == 'ipahealthcheck.dogtag.ca'
assert result.check == 'DogtagCertsConfigCheck'
+ @pytest.mark.skipif(
+ pki_version >= pki.util.Version("11.5.0"),
+ reason='Does not apply to PKI 11.5.0+')
@patch('ipahealthcheck.dogtag.ca.get_directive')
@patch('ipaserver.install.certs.CertDB')
def test_cert_missing_from_file(self, mock_certdb, mock_directive):
--
2.42.0

@ -16,8 +16,8 @@
%bcond_without tests
Name: %{prefix}-healthcheck
Version: 0.12
Release: 4%{?dist}
Version: 0.16
Release: 3%{?dist}
Summary: Health check tool for %{productname}
BuildArch: noarch
License: GPLv3
@ -26,10 +26,10 @@ Source0: https://github.com/freeipa/freeipa-healthcheck/archive/%{version
Source1: ipahealthcheck.conf
Patch0001: 0001-Remove-ipaclustercheck.patch
Patch0002: 0002-Disable-two-failing-tests.patch
Patch0003: 0003-Skip-AD-domains-with-posix-ranges-in-the-catalog-che.patch
Patch0004: 0004-Catch-exceptions-during-user-group-name-lookup-in-Fi.patch
Patch0005: 0005-Don-t-error-in-DogtagCertsConnectivityCheck-with-ext.patch
Patch0002: 0002-Don-t-fail-if-a-service-name-cannot-be-looked-up-in-.patch
Patch0003: 0003-Temporarily-disable-the-ipa-ods-exporter-service-sta.patch
Patch0004: 0004-Skip-DogtagCertsConfigCheck-for-PKI-versions-11.5.0.patch
Patch0005: 0005-test-Handle-PKI-11.5.0-not-storing-certs-in-CS.cfg.patch
Requires: %{name}-core = %{version}-%{release}
Requires: %{prefix}-server
@ -159,6 +159,16 @@ PYTHONPATH=src PATH=$PATH:$RPM_BUILD_ROOT/usr/bin pytest-3 tests/test_*
%changelog
* Fri Jan 12 2024 Rob Crittenden <rcritten@redhat.com> - 0.16-3
- Skip DogtagCertsConfigCheck for PKI versions 11.5.0 (RHEL-21367)
* Tue Nov 14 2023 Rob Crittenden <rcritten@redhat.com> - 0.16-2
- Don't fail if a service name cannot be looked up in LDAP
- Disable the ipa-ods-exporter service check
* Thu Nov 9 2023 Rob Crittenden <rcritten@redhat.com> - 0.16-1
- Update to upstream 0.16 (RHEL-12494)
* Mon Jul 24 2023 Rob Crittenden <rcritten@redhat.com> - 0.12-4
- Error in DogtagCertsConnectivityCheckCA with external CA (#2224595)

Loading…
Cancel
Save