Compare commits

..

No commits in common. 'c9' and 'cs10' have entirely different histories.
c9 ... cs10

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> 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 Subject: [PATCH] Remove ipaclustercheck
--- ---
@ -26,13 +26,13 @@ Subject: [PATCH] Remove ipaclustercheck
delete mode 100644 tests/test_cluster_ruv.py delete mode 100644 tests/test_cluster_ruv.py
diff --git a/setup.py b/setup.py diff --git a/setup.py b/setup.py
index 0cfa486..b9e1ca1 100644 index d926302..cb6265a 100644
--- a/setup.py --- a/setup.py
+++ b/setup.py +++ b/setup.py
@@ -4,7 +4,7 @@ from setuptools import find_packages, setup @@ -4,7 +4,7 @@ from setuptools import find_packages, setup
setup( setup(
name='ipahealthcheck', name='ipahealthcheck',
version='0.12', version='0.16',
- namespace_packages=['ipahealthcheck', 'ipaclustercheck'], - namespace_packages=['ipahealthcheck', 'ipaclustercheck'],
+ namespace_packages=['ipahealthcheck'], + namespace_packages=['ipahealthcheck'],
package_dir={'': 'src'}, package_dir={'': 'src'},
@ -635,5 +635,5 @@ index 7583c84..0000000
- assert result.kw.get('name') == 'dangling_csruv' - assert result.kw.get('name') == 'dangling_csruv'
- assert result.kw.get('value') == '9' - assert result.kw.get('value') == '9'
-- --
2.38.1 2.41.0

@ -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.45.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 3d85d43f62a0c52e44a2228c872307152b2b0de1 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.45.0

@ -0,0 +1,92 @@
From c780755c57286949d4c6d62dec6f0ce7d718dd13 Mon Sep 17 00:00:00 2001
From: Rob Crittenden <rcritten@redhat.com>
Date: Mon, 18 Mar 2024 16:54:47 -0400
Subject: [PATCH] Handle CS.cfg file missing in DogtagCertsConfigCheck
This should never happen but if that file disappears things have
gone really, really badly. Throw a CRITICAL error.
Fixes: https://github.com/freeipa/freeipa-healthcheck/issues/327
Signed-off-by: Rob Crittenden <rcritten@redhat.com>
---
src/ipahealthcheck/dogtag/ca.py | 10 ++++++++++
tests/test_dogtag_ca.py | 9 +++++++--
2 files changed, 17 insertions(+), 2 deletions(-)
diff --git a/src/ipahealthcheck/dogtag/ca.py b/src/ipahealthcheck/dogtag/ca.py
index ddf5ece..5c2f6af 100644
--- a/src/ipahealthcheck/dogtag/ca.py
+++ b/src/ipahealthcheck/dogtag/ca.py
@@ -3,6 +3,7 @@
#
import logging
+import os
from ipahealthcheck.dogtag.plugin import DogtagPlugin, registry
from ipahealthcheck.core.plugin import Result
@@ -32,6 +33,15 @@ class DogtagCertsConfigCheck(DogtagPlugin):
logger.debug("No CA configured, skipping dogtag config check")
return
+ if not os.path.exists(paths.CA_CS_CFG_PATH):
+ yield Result(
+ self, constants.CRITICAL,
+ key=f'{paths.CA_CS_CFG_PATH}_missing',
+ configfile=paths.CA_CS_CFG_PATH,
+ msg=f'Configuration file {paths.CA_CS_CFG_PATH} is missing'
+ )
+ return
+
pki_version = pki.util.Version(pki.specification_version())
if pki_version >= pki.util.Version("11.5.0"):
logger.debug(
diff --git a/tests/test_dogtag_ca.py b/tests/test_dogtag_ca.py
index 1f61dea..a78e5de 100644
--- a/tests/test_dogtag_ca.py
+++ b/tests/test_dogtag_ca.py
@@ -50,9 +50,10 @@ class TestCACerts(BaseTest):
@pytest.mark.skipif(
pki_version >= pki.util.Version("11.5.0"),
reason='Does not apply to PKI 11.5.0+')
+ @patch('os.path.exists')
@patch('ipahealthcheck.dogtag.ca.get_directive')
@patch('ipaserver.install.certs.CertDB')
- def test_ca_certs_ok(self, mock_certdb, mock_directive):
+ def test_ca_certs_ok(self, mock_certdb, mock_directive, mock_exists):
"""Test what should be the standard case"""
trust = {
'ocspSigningCert cert-pki-ca': 'u,u,u',
@@ -62,6 +63,7 @@ class TestCACerts(BaseTest):
'caSigningCert cert-pki-ca': 'CT,C,C',
'transportCert cert-pki-kra': 'u,u,u',
}
+ mock_exists.return_value = True
mock_certdb.return_value = mock_CertDB(trust)
mock_directive.side_effect = [name for name, nsstrust in trust.items()]
@@ -81,9 +83,11 @@ class TestCACerts(BaseTest):
@pytest.mark.skipif(
pki_version >= pki.util.Version("11.5.0"),
reason='Does not apply to PKI 11.5.0+')
+ @patch('os.path.exists')
@patch('ipahealthcheck.dogtag.ca.get_directive')
@patch('ipaserver.install.certs.CertDB')
- def test_cert_missing_from_file(self, mock_certdb, mock_directive):
+ def test_cert_missing_from_file(self, mock_certdb, mock_directive,
+ mock_exists):
"""Test a missing certificate.
Note that if it is missing from the database then this check
@@ -103,6 +107,7 @@ class TestCACerts(BaseTest):
location = nicknames.index('auditSigningCert cert-pki-ca')
nicknames[location] = 'NOT auditSigningCert cert-pki-ca'
+ mock_exists.return_value = True
mock_certdb.return_value = mock_CertDB(trust)
mock_directive.side_effect = nicknames
--
2.45.0

@ -0,0 +1,47 @@
From e0c09f9f1388bbce43775f40a39266e692e231da Mon Sep 17 00:00:00 2001
From: Thorsten Scherf <tscherf@redhat.com>
Date: Wed, 13 Mar 2024 12:57:34 +0100
Subject: [PATCH] Fixes log file permissions as per CIS benchmark
As per CIS benchmark the log file permissions should be 640 for some log
files but if we change /var/log/ipa-custodia.audit.log permissions to
640 then "ipa-healthcheck" reports a permission issue.
Fixes: https://github.com/freeipa/freeipa-healthcheck/issues/325
Signed-off-by: Thorsten Scherf <tscherf@redhat.com>
---
src/ipahealthcheck/ipa/files.py | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/src/ipahealthcheck/ipa/files.py b/src/ipahealthcheck/ipa/files.py
index b7ca116..d914014 100644
--- a/src/ipahealthcheck/ipa/files.py
+++ b/src/ipahealthcheck/ipa/files.py
@@ -121,7 +121,7 @@ class IPAFileCheck(IPAPlugin, FileCheck):
self.files.append((filename, 'root', 'root', '0600'))
self.files.append((paths.IPA_CUSTODIA_AUDIT_LOG,
- 'root', 'root', '0644'))
+ 'root', 'root', '0644', '0640'))
self.files.append((paths.KADMIND_LOG, 'root', 'root',
('0600', '0640')))
@@ -133,11 +133,13 @@ class IPAFileCheck(IPAPlugin, FileCheck):
self.files.append((paths.SLAPD_INSTANCE_ERROR_LOG_TEMPLATE % inst,
constants.DS_USER, constants.DS_GROUP, '0600'))
- self.files.append((paths.VAR_LOG_HTTPD_ERROR, 'root', 'root', '0644'))
+ self.files.append((paths.VAR_LOG_HTTPD_ERROR, 'root', 'root',
+ '0644', '0640'))
for globpath in glob.glob("%s/debug*.log" % paths.TOMCAT_CA_DIR):
self.files.append(
- (globpath, constants.PKI_USER, constants.PKI_GROUP, "0644")
+ (globpath, constants.PKI_USER, constants.PKI_GROUP,
+ "0644", "0640")
)
for globpath in glob.glob(
--
2.45.0

@ -0,0 +1,190 @@
From 2206b9915606c555163dec775a99a355dc02bee0 Mon Sep 17 00:00:00 2001
From: Rob Crittenden <rcritten@redhat.com>
Date: Tue, 28 May 2024 11:15:48 -0400
Subject: [PATCH] Fix some file mode format issues
When specifying multiple possible modes for a file the values must
be a tuple. There were two occurances where they were listed
separately.
Add in a pre-check on the formatting to raise an error for badly
formatted files. This may be annoying for users if one sneaks in
again but the CI should catch it.
Related: https://github.com/freeipa/freeipa-healthcheck/issues/325
Signed-off-by: Rob Crittenden <rcritten@redhat.com>
---
src/ipahealthcheck/core/files.py | 12 +++++-
src/ipahealthcheck/ipa/files.py | 6 +--
tests/test_core_files.py | 72 +++++++++++++++++++++++++++++++-
tests/util.py | 1 +
4 files changed, 85 insertions(+), 6 deletions(-)
diff --git a/src/ipahealthcheck/core/files.py b/src/ipahealthcheck/core/files.py
index 85d42bc..32bc5b2 100644
--- a/src/ipahealthcheck/core/files.py
+++ b/src/ipahealthcheck/core/files.py
@@ -31,7 +31,17 @@ class FileCheck:
@duration
def check(self):
- for (path, owner, group, mode) in self.files:
+ # first validate that the list of files to check is in the correct
+ # format
+ process_files = []
+ for file in self.files:
+ if len(file) == 4:
+ process_files.append(file)
+ else:
+ yield Result(self, constants.ERROR, key=file,
+ msg='Code format is incorrect for file')
+
+ for (path, owner, group, mode) in process_files:
if not isinstance(owner, tuple):
owner = tuple((owner,))
if not isinstance(group, tuple):
diff --git a/src/ipahealthcheck/ipa/files.py b/src/ipahealthcheck/ipa/files.py
index d914014..c80fd5b 100644
--- a/src/ipahealthcheck/ipa/files.py
+++ b/src/ipahealthcheck/ipa/files.py
@@ -121,7 +121,7 @@ class IPAFileCheck(IPAPlugin, FileCheck):
self.files.append((filename, 'root', 'root', '0600'))
self.files.append((paths.IPA_CUSTODIA_AUDIT_LOG,
- 'root', 'root', '0644', '0640'))
+ 'root', 'root', ('0644', '0640')))
self.files.append((paths.KADMIND_LOG, 'root', 'root',
('0600', '0640')))
@@ -134,12 +134,12 @@ class IPAFileCheck(IPAPlugin, FileCheck):
constants.DS_USER, constants.DS_GROUP, '0600'))
self.files.append((paths.VAR_LOG_HTTPD_ERROR, 'root', 'root',
- '0644', '0640'))
+ ('0644', '0640')))
for globpath in glob.glob("%s/debug*.log" % paths.TOMCAT_CA_DIR):
self.files.append(
(globpath, constants.PKI_USER, constants.PKI_GROUP,
- "0644", "0640")
+ ("0644", "0640"))
)
for globpath in glob.glob(
diff --git a/tests/test_core_files.py b/tests/test_core_files.py
index 924d7fa..e7010a9 100644
--- a/tests/test_core_files.py
+++ b/tests/test_core_files.py
@@ -2,14 +2,22 @@
# Copyright (C) 2019 FreeIPA Contributors see COPYING for license
#
+from ldap import OPT_X_SASL_SSF_MIN
import pwd
import posix
+from util import m_api
+from util import capture_results
+
+from ipahealthcheck.core import config
from ipahealthcheck.core.files import FileCheck
from ipahealthcheck.core import constants
from ipahealthcheck.core.plugin import Results
+from ipahealthcheck.ipa.files import IPAFileCheck
+from ipahealthcheck.system.plugin import registry
from unittest.mock import patch
+from ipapython.dn import DN
+from ipapython.ipaldap import LDAPClient, LDAPEntry
-from util import capture_results
nobody = pwd.getpwnam('nobody')
@@ -20,6 +28,37 @@ files = (('foo', 'root', 'root', '0660'),
('fiz', ('root', 'bin'), ('root', 'bin'), '0664'),
('zap', ('root', 'bin'), ('root', 'bin'), ('0664', '0640'),))
+bad_modes = (('biz', ('root', 'bin'), ('root', 'bin'), '0664', '0640'),)
+
+
+class mock_ldap:
+ SCOPE_BASE = 1
+ SCOPE_ONELEVEL = 2
+ SCOPE_SUBTREE = 4
+
+ def __init__(self, ldapentry):
+ """Initialize the results that we will return from get_entries"""
+ self.results = ldapentry
+
+ def get_entry(self, dn, attrs_list=None, time_limit=None,
+ size_limit=None, get_effective_rights=False):
+ return [] # the call doesn't check the value
+
+
+class mock_ldap_conn:
+ def set_option(self, option, invalue):
+ pass
+
+ def get_option(self, option):
+ if option == OPT_X_SASL_SSF_MIN:
+ return 256
+
+ return None
+
+ def search_s(self, base, scope, filterstr=None,
+ attrlist=None, attrsonly=0):
+ return tuple()
+
def make_stat(mode=33200, uid=0, gid=0):
"""Return a mocked-up stat.
@@ -234,4 +273,33 @@ def test_files_group_not_found(mock_grgid, mock_grnam, mock_stat):
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'
+
+
+def test_bad_modes():
+ f = FileCheck()
+ f.files = bad_modes
+
+ results = capture_results(f)
+
+ for result in results.results:
+ assert result.result == constants.ERROR
+ assert result.kw.get('msg') == 'Code format is incorrect for file'
+
+
+@patch('ipaserver.install.krbinstance.is_pkinit_enabled')
+def test_ipa_files_format(mock_pkinit):
+ mock_pkinit.return_value = True
+
+ fake_conn = LDAPClient('ldap://localhost', no_schema=True)
+ ldapentry = LDAPEntry(fake_conn, DN(m_api.env.container_dns,
+ m_api.env.basedn))
+ framework = object()
+ registry.initialize(framework, config.Config)
+ f = IPAFileCheck(registry)
+
+ f.conn = mock_ldap(ldapentry)
+
+ results = capture_results(f)
+
+ for result in results.results:
+ assert result.result == constants.SUCCESS
diff --git a/tests/util.py b/tests/util.py
index 12c1688..fb8750a 100644
--- a/tests/util.py
+++ b/tests/util.py
@@ -141,6 +141,7 @@ m_api.env.container_host = DN(('cn', 'computers'), ('cn', 'accounts'))
m_api.env.container_sysaccounts = DN(('cn', 'sysaccounts'), ('cn', 'etc'))
m_api.env.container_service = DN(('cn', 'services'), ('cn', 'accounts'))
m_api.env.container_masters = DN(('cn', 'masters'))
+m_api.env.container_dns = DN(('cn', 'dns'))
m_api.Backend = Mock()
m_api.Command = Mock()
m_api.Command.ping.return_value = {
--
2.45.0

@ -0,0 +1,28 @@
From b6346fedcc158a3ed3a70691350bf7ebee4a8460 Mon Sep 17 00:00:00 2001
From: Rob Crittenden <rcritten@redhat.com>
Date: Thu, 20 Jun 2024 14:27:16 -0400
Subject: [PATCH] Allow WARNING in the files test
We are only validating the format and don't need to actually
enforce the results in CI. The validation raises ERROR.
Related: https://github.com/freeipa/freeipa-healthcheck/issues/325
Signed-off-by: Rob Crittenden <rcritten@redhat.com>
---
tests/test_core_files.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tests/test_core_files.py b/tests/test_core_files.py
index e7010a9..d308410 100644
--- a/tests/test_core_files.py
+++ b/tests/test_core_files.py
@@ -302,4 +302,4 @@ def test_ipa_files_format(mock_pkinit):
results = capture_results(f)
for result in results.results:
- assert result.result == constants.SUCCESS
+ assert result.result in (constants.SUCCESS, constants.WARNING)
--
2.45.0

@ -16,20 +16,24 @@
%bcond_without tests %bcond_without tests
Name: %{prefix}-healthcheck Name: %{prefix}-healthcheck
Version: 0.12 Version: 0.16
Release: 4%{?dist} Release: 7%{?dist}
Summary: Health check tool for %{productname} Summary: Health check tool for %{productname}
BuildArch: noarch BuildArch: noarch
License: GPLv3 License: GPL-3.0-or-later
URL: https://github.com/freeipa/freeipa-healthcheck URL: https://github.com/freeipa/freeipa-healthcheck
Source0: https://github.com/freeipa/freeipa-healthcheck/archive/%{version}.tar.gz Source0: https://github.com/freeipa/freeipa-healthcheck/archive/%{version}.tar.gz
Source1: ipahealthcheck.conf Source1: ipahealthcheck.conf
Patch0001: 0001-Remove-ipaclustercheck.patch Patch0001: 0001-Remove-ipaclustercheck.patch
Patch0002: 0002-Disable-two-failing-tests.patch Patch0002: 0002-Don-t-fail-if-a-service-name-cannot-be-looked-up-in-.patch
Patch0003: 0003-Skip-AD-domains-with-posix-ranges-in-the-catalog-che.patch Patch0003: 0003-Temporarily-disable-the-ipa-ods-exporter-service-sta.patch
Patch0004: 0004-Catch-exceptions-during-user-group-name-lookup-in-Fi.patch Patch0004: 0004-Skip-DogtagCertsConfigCheck-for-PKI-versions-11.5.0.patch
Patch0005: 0005-Don-t-error-in-DogtagCertsConnectivityCheck-with-ext.patch Patch0005: 0005-test-Handle-PKI-11.5.0-not-storing-certs-in-CS.cfg.patch
Patch0006: 0006-Handle-CS.cfg-file-missing-in-DogtagCertsConfigCheck.patch
Patch0007: 0007-Fixes-log-file-permissions-as-per-CIS-benchmark.patch
Patch0008: 0008-Fix-some-file-mode-format-issues.patch
Patch0009: 0009-Allow-WARNING-in-the-files-test.patch
Requires: %{name}-core = %{version}-%{release} Requires: %{name}-core = %{version}-%{release}
Requires: %{prefix}-server Requires: %{prefix}-server
@ -159,72 +163,107 @@ PYTHONPATH=src PATH=$PATH:$RPM_BUILD_ROOT/usr/bin pytest-3 tests/test_*
%changelog %changelog
* Mon Jul 24 2023 Rob Crittenden <rcritten@redhat.com> - 0.12-4 * Tue Oct 29 2024 Troy Dawson <tdawson@redhat.com> - 0.16-7
- Error in DogtagCertsConnectivityCheckCA with external CA (#2224595) - Bump release for October 2024 mass rebuild:
Resolves: RHEL-64018
* Thu Jul 06 2023 Rob Crittenden <rcritten@redhat.com> - 0.12-3 * Fri Jul 19 2024 Rob Crittenden <rcritten@redhat.com> - 0.16-6
- Catch exceptions during user/group name lookup in FileCheck (#2218912) - Skip DogtagCertsConfigCheck for PKI versions >= 11.5.0 (RHEL-39701)
- Need to change log file permissions of IPA as per CIS benchmark (RHEL-44305)
* Tue Apr 25 2023 Rob Crittenden <rcritten@redhat.com> - 0.12-2 * Mon Jun 24 2024 Troy Dawson <tdawson@redhat.com> - 0.16-5
- Skip AD domains with posix ranges in the catalog check (#2188135) - Bump release for June 2024 mass rebuild
* Thu Dec 01 2022 Rob Crittenden <rcritten@redhat.com> - 0.12-1 * Wed Jan 24 2024 Fedora Release Engineering <releng@fedoraproject.org> - 0.16-4
- Update to upstream 0.12 (#2139531) - Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild
* Wed Jul 06 2022 Rob Crittenden <rcritten@redhat.com> - 0.9-9 * Fri Jan 19 2024 Fedora Release Engineering <releng@fedoraproject.org> - 0.16-3
- Add support for the DNS URI type (#2104495) - Rebuilt for https://fedoraproject.org/wiki/Fedora_40_Mass_Rebuild
* Wed May 18 2022 Rob Crittenden <rcritten@redhat.com> - 0.9-8 * Tue Nov 14 2023 Rob Crittenden <rcritten@redhat.com> - 0.16-2
- Validate that a known output type has been selected (#2079698) - Don't fail if a service name cannot be looked up in LDAP
- Disable the ipa-ods-exporter service check
* Wed May 04 2022 Rob Crittenden <rcritten@redhat.com> - 0.9-7 * Wed Nov 8 2023 Rob Crittenden <rcritten@redhat.com> - 0.16-1
- debug='True' in ipahealthcheck.conf doesn't enable debug output (#2079861) - Update to 0.16 release
- Validate value formats in the ipahealthcheck.conf file (#2079739) - This fixes pki-healthcheck
- Validate output_type options from ipahealthcheck.conf file (#2079698)
* Thu Apr 28 2022 Rob Crittenden <rcritten@redhat.com> - 0.9-6 * Tue Nov 7 2023 Rob Crittenden <rcritten@redhat.com> - 0.15-1
- Allow multiple file modes in the FileChecker (#2072708) - Update to 0.15 release
* Wed Apr 06 2022 Rob Crittenden <rcritten@redhat.com> - 0.9-5 * Mon Aug 21 2023 Rob Crittenden <rcritten@redhat.com> - 0.14-1
- Add CLI options to healthcheck configuration file (#2070981) - Update to 0.14 release
* Wed Mar 30 2022 Rob Crittenden <rcritten@redhat.com> - 0.9-4 * Wed Jul 19 2023 Rob Crittenden <rcritten@redhat.com> - 0.13-1
- Use the subject base from the IPA configuration, not REALM (#2067213) - Update to 0.13 release
* Tue Oct 12 2021 Rob Crittenden <rcritten@redhat.com> - 0.9-3 * Thu Jun 29 2023 Python Maint <python-maint@redhat.com> - 0.12-5
- IPATrustControllerServiceCheck doesn't handle HIDDEN_SERVICE (#1976878) - Rebuilt for Python 3.12
* Mon Aug 09 2021 Mohan Boddu <mboddu@redhat.com> - 0.9-2 * Wed Jun 28 2023 Python Maint <python-maint@redhat.com> - 0.12-4
- Rebuilt for IMA sigs, glibc 2.34, aarch64 flags - Bootstrap for Python 3.12
Related: rhbz#1991688
* Thu Jun 17 2021 Rob Crittenden <rcritten@redhat.com> - 0.9-1 * Wed Mar 29 2023 Rob Crittenden <rcritten@redhat.com> - 0.12-3
- Rebase to upstream 0.9 (#1969539) - Migrated to SPDX license
* Thu Apr 22 2021 Rob Crittenden <rcritten@redhat.com> - 0.8-7.2 * Thu Jan 19 2023 Fedora Release Engineering <releng@fedoraproject.org> - 0.12-2
- rpminspect: specname match on suffix to allow for differing - Rebuilt for https://fedoraproject.org/wiki/Fedora_38_Mass_Rebuild
spec/package naming (#1951733)
* Mon Apr 19 2021 Rob Crittenden <rcritten@redhat.com> - 0.8-7.1 * Thu Dec 1 2022 Rob Crittenden <rcritten@redhat.com> - 0.12
- Switch from tox to pytest as the test runner. tox is being deprecated - Update to 0.12 release
in some distros. (#1942157)
* Thu Jul 21 2022 Fedora Release Engineering <releng@fedoraproject.org> - 0.11-5
- Rebuilt for https://fedoraproject.org/wiki/Fedora_37_Mass_Rebuild
* Thu Jun 16 2022 Python Maint <python-maint@redhat.com> - 0.11-4
- Rebuilt for Python 3.11
* Thu Jun 16 2022 Python Maint <python-maint@redhat.com> - 0.11-3
- Bootstrap for Python 3.11
* Mon Jun 06 2022 Rob Crittenden <rcritten@redhat.com> - 0.11-2
- Don't WARN on KDC workers if cpus == 1 and KRB5KDC_ARGS is empty
* Thu Jun 02 2022 Rob Crittenden <rcritten@redhat.com> - 0.11-1
- Update to 0.11 release
* Tue Feb 8 2022 Rob Crittenden <rcritten@redhat.com> - 0.10-1
- Update to 0.10 release
* Mon Apr 19 2021 Rob Crittenden <rcritten@redhat.com> - 0.8-7 * Thu Jan 20 2022 Fedora Release Engineering <releng@fedoraproject.org> - 0.9-4
- Add check to validate the KRA Agent is correct (#1894781) - Rebuilt for https://fedoraproject.org/wiki/Fedora_36_Mass_Rebuild
* Thu Apr 15 2021 Mohan Boddu <mboddu@redhat.com> - 0.8-6.1 * Wed Jul 21 2021 Fedora Release Engineering <releng@fedoraproject.org> - 0.9-3
- Rebuilt for RHEL 9 BETA on Apr 15th 2021. Related: rhbz#1947937 - Rebuilt for https://fedoraproject.org/wiki/Fedora_35_Mass_Rebuild
* Fri Mar 12 2021 Alexander Bokovoy <abokovoy@redhat.com> - 0.8-5.1 * Mon Jun 14 2021 Rob Crittenden <rcritten@redhat.com> - 0.9-2
- Re-enable package self-tests after bootstrap - FileCheck would raise a CRITICAL for non-existent files
* Mon Mar 8 2021 François Cami <fcami@redhat.com> - 0.8-5 * Tue Jun 8 2021 Rob Crittenden <rcritten@redhat.com> - 0.9-1
- Update to upstream 0.9
- Fix bad date in 0.8-6.1 changelog entry
* Mon Jun 07 2021 Python Maint <python-maint@redhat.com> - 0.8-8.1
- Rebuilt for Python 3.10
* Fri Jun 04 2021 Python Maint <python-maint@redhat.com> - 0.8-7.1
- Bootstrap for Python 3.10
* Thu Apr 15 2021 Rob Crittenden <rcritten@redhat.com> - 0.8-6.1
- Switch from tox to pytest as the test runner. tox is being deprecated
in some distros.
* Mon Mar 8 2021 François Cami <fcami@redhat.com> - 0.8-6
- Make the spec file distribution-agnostic (rhbz#1935773). - Make the spec file distribution-agnostic (rhbz#1935773).
* Tue Mar 2 2021 Alexander Scheel <ascheel@redhat.com> - 0.8-4 * Tue Mar 2 2021 Alexander Scheel <ascheel@redhat.com> - 0.8-5
- Make the spec file more distribution-agnostic - Make the spec file more distribution-agnostic
- Use tox as the test runner when tests are enabled - Use tox as the test runner when tests are enabled
* Tue Mar 02 2021 Zbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl> - 0.8-4
- Rebuilt for updated systemd-rpm-macros
See https://pagure.io/fesco/issue/2583.
* Tue Jan 26 2021 Fedora Release Engineering <releng@fedoraproject.org> - 0.8-3 * Tue Jan 26 2021 Fedora Release Engineering <releng@fedoraproject.org> - 0.8-3
- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild - Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild

Loading…
Cancel
Save