Compare commits

...

No commits in common. "c8-stream-DL1" and "c8s-stream-DL1" have entirely different histories.

8 changed files with 1 additions and 1210 deletions

View File

@ -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

View File

@ -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

View File

@ -1,47 +0,0 @@
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 1/4] 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

View File

@ -1,189 +0,0 @@
From 54e2e9b8bff0bc84b6179eac44993b460f02ad02 Mon Sep 17 00:00:00 2001
From: Rob Crittenden <rcritten@redhat.com>
Date: Fri, 21 Jun 2024 15:15:36 -0400
Subject: [PATCH 1/2] 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 | 71 +++++++++++++++++++++++++++++++-
tests/util.py | 1 +
4 files changed, 85 insertions(+), 5 deletions(-)
diff --git a/src/ipahealthcheck/core/files.py b/src/ipahealthcheck/core/files.py
index 59e8b76..58dd74a 100644
--- a/src/ipahealthcheck/core/files.py
+++ b/src/ipahealthcheck/core/files.py
@@ -28,7 +28,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 6e3ec38..09fc216 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.
@@ -197,3 +236,33 @@ 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'
+
+
+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 8081595..5dcb0cd 100644
--- a/tests/util.py
+++ b/tests/util.py
@@ -140,6 +140,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

View File

@ -1,28 +0,0 @@
From 79cca342b3c440a045cadbff871ff977e35222c6 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

View File

@ -1,70 +0,0 @@
From 18178ba09b221eef7f0bb869980e1c043a8e764f Mon Sep 17 00:00:00 2001
From: Rob Crittenden <rcritten@redhat.com>
Date: Wed, 31 May 2023 17:21:55 -0400
Subject: [PATCH] Address issues uncovered by pylint 2.15.5
Two variables used before assignment
Three Useless suppression of 'unexpected-keyword-arg'
Fixes: https://github.com/freeipa/freeipa-healthcheck/issues/295
Signed-off-by: Rob Crittenden <rcritten@redhat.com>
---
src/ipahealthcheck/ipa/certs.py | 5 +----
src/ipahealthcheck/ipa/trust.py | 2 +-
2 files changed, 2 insertions(+), 5 deletions(-)
diff --git a/src/ipahealthcheck/ipa/certs.py b/src/ipahealthcheck/ipa/certs.py
index 11ac0c1..4ea5112 100644
--- a/src/ipahealthcheck/ipa/certs.py
+++ b/src/ipahealthcheck/ipa/certs.py
@@ -343,7 +343,6 @@ class IPACertfileExpirationCheck(IPAPlugin):
try:
if 'pwd_file' in signature(certdb.NSSDatabase).parameters:
- # pylint: disable=unexpected-keyword-arg
db = certdb.NSSDatabase(
dbdir, token=token,
pwd_file=pwd_file.name if pwd_file else None)
@@ -624,7 +623,6 @@ class IPACertNSSTrust(IPAPlugin):
pwd_file = get_token_password_file(self.ca.hsm_enabled,
token)
- # pylint: disable=unexpected-keyword-arg
db = certdb.NSSDatabase(
paths.PKI_TOMCAT_ALIAS_DIR, token=token,
pwd_file=pwd_file.name if pwd_file else None)
@@ -987,7 +985,7 @@ class IPANSSChainValidation(IPAPlugin):
key=key,
dbdir=dbdir,
nickname=nickname,
- reason=response.output_error,
+ reason=str(e),
msg='Validation of {nickname} in {dbdir} failed: '
'{reason}')
else:
@@ -1251,7 +1249,6 @@ class IPACertRevocation(IPAPlugin):
dbdir = request.get('cert-database')
try:
if 'pwd_file' in signature(certdb.NSSDatabase).parameters:
- # pylint: disable=unexpected-keyword-arg
db = certdb.NSSDatabase(
dbdir, token=token,
pwd_file=pwd_file.name if pwd_file else None
diff --git a/src/ipahealthcheck/ipa/trust.py b/src/ipahealthcheck/ipa/trust.py
index b962807..243502f 100644
--- a/src/ipahealthcheck/ipa/trust.py
+++ b/src/ipahealthcheck/ipa/trust.py
@@ -307,7 +307,7 @@ class IPATrustCatalogCheck(IPAPlugin):
id = pysss_nss_idmap.getnamebysid(sid + '-500')
except Exception as e:
yield Result(self, constants.ERROR,
- key=id,
+ key='getnamebysid',
domain=domain,
error=str(e),
msg='Look up of ID {key} for {domain} failed: '
--
2.48.1

View File

@ -1,139 +0,0 @@
From 7539b4aee19c7e28539ec853369a3230f2ae08f3 Mon Sep 17 00:00:00 2001
From: Rob Crittenden <rcritten@redhat.com>
Date: Mon, 23 Jun 2025 13:30:26 -0400
Subject: [PATCH] Don't rely on order in trust agent/controller role check
The code expected that the local server would always be the
first one returned. Instead loop through the returned list
to find the current server and set the state based on that.
Fixes: https://github.com/freeipa/freeipa-healthcheck/issues/356
Signed-off-by: Rob Crittenden <rcritten@redhat.com>
---
src/ipahealthcheck/ipa/plugin.py | 15 ++++---
tests/test_ipa_trust.py | 71 +++++++++++++++++++++++++++++++-
2 files changed, 79 insertions(+), 7 deletions(-)
diff --git a/src/ipahealthcheck/ipa/plugin.py b/src/ipahealthcheck/ipa/plugin.py
index f1a325c..efaa947 100644
--- a/src/ipahealthcheck/ipa/plugin.py
+++ b/src/ipahealthcheck/ipa/plugin.py
@@ -35,6 +35,13 @@ class IPARegistry(Registry):
self.trust_controller = False
self.ca_configured = False
+ def has_role(self, roles):
+ for role in roles:
+ if role.get('server_server') == api.env.host:
+ if role.get('status') == 'enabled':
+ return True
+ return False
+
def initialize(self, framework, config, options=None):
super().initialize(framework, config)
# deferred import for mock
@@ -81,12 +88,8 @@ class IPARegistry(Registry):
component_services=['ADTRUST']
),
)
- role = roles[0].status(api)[0]
- if role.get('status') == 'enabled':
- self.trust_agent = True
- role = roles[1].status(api)[0]
- if role.get('status') == 'enabled':
- self.trust_controller = True
+ self.trust_agent = self.has_role(roles[0].status(api))
+ self.trust_controller = self.has_role(roles[1].status(api))
registry = IPARegistry()
diff --git a/tests/test_ipa_trust.py b/tests/test_ipa_trust.py
index 6c4754a..0faa702 100644
--- a/tests/test_ipa_trust.py
+++ b/tests/test_ipa_trust.py
@@ -11,7 +11,8 @@ from util import capture_results
from util import m_api
from ipahealthcheck.core import config, constants
-from ipahealthcheck.ipa.plugin import registry
+from ipahealthcheck.core.plugin import Results
+from ipahealthcheck.ipa.plugin import registry, IPARegistry
from ipahealthcheck.ipa.trust import (IPATrustAgentCheck,
IPATrustDomainsCheck,
IPADomainCheck,
@@ -1287,3 +1288,71 @@ class TestPackageCheck(BaseTest):
assert result.source == 'ipahealthcheck.ipa.trust'
assert result.check == 'IPATrustPackageCheck'
sys.modules['ipaserver.install'] = save
+
+
+class TestHasRole(BaseTest):
+ """Verify that the output of server-role-find which is used to
+ determine whether a host is a trust agent or controller
+ (or neither) isn't dependent upon the order the hosts are
+ returned.
+
+ Only trust agent is tested here but there is no difference
+ between an agent and a trust in the way they are stored in
+ a server role.
+ """
+ def test_role_last(self):
+ self.results = Results()
+ reg = IPARegistry()
+
+ roles = [
+ {
+ "role_servrole": "AD trust agent",
+ "server_server": "replica.ipa.example",
+ "status": "absent",
+ },
+ {
+ "role_servrole": "AD trust agent",
+ "server_server": "server.ipa.example",
+ "status": "enabled",
+ },
+ ]
+
+ assert reg.has_role(roles) is True
+
+ def test_role_first(self):
+ self.results = Results()
+ reg = IPARegistry()
+
+ roles = [
+ {
+ "role_servrole": "AD trust agent",
+ "server_server": "server.ipa.example",
+ "status": "enabled",
+ },
+ {
+ "role_servrole": "AD trust agent",
+ "server_server": "replica.ipa.example",
+ "status": "absent",
+ },
+ ]
+
+ assert reg.has_role(roles) is True
+
+ def test_no_role(self):
+ self.results = Results()
+ reg = IPARegistry()
+
+ roles = [
+ {
+ "role_servrole": "AD trust agent",
+ "server_server": "server.ipa.example",
+ "status": "absent",
+ },
+ {
+ "role_servrole": "AD trust agent",
+ "server_server": "replica.ipa.example",
+ "status": "enabled",
+ },
+ ]
+
+ assert reg.has_role(roles) is False
--
2.49.0

View File

@ -8,7 +8,7 @@
Name: ipa-healthcheck
Version: 0.12
Release: 6%{?dist}
Release: 1%{?dist}
Summary: Health check tool for IdM
BuildArch: noarch
License: GPLv3
@ -19,20 +19,12 @@ Source1: %{longname}.conf
Patch0001: 0001-Remove-ipaclustercheck.patch
Patch0002: 0002-Disable-two-failing-tests.patch
Patch0003: 0003-Fix-logging-issue-related-to-dtype.patch
Patch0004: 0004-Skip-AD-domains-with-posix-ranges-in-the-catalog-che.patch
Patch0005: 0005-Don-t-error-in-DogtagCertsConnectivityCheck-with-ext.patch
Patch0006: 0006-Fixes-log-file-permissions-as-per-CIS-benchmark.patch
Patch0007: 0007-Fix-some-file-mode-format-issues.patch
Patch0008: 0008-Allow-WARNING-in-the-files-test.patch
Patch0009: 0009-Address-issues-uncovered-by-pylint-2.15.5.patch
Patch0010: 0010-Don-t-rely-on-order-in-trust-agent-controller-role-c.patch
Requires: %{name}-core = %{version}-%{release}
Requires: ipa-server
Requires: python3-ipalib
Requires: python3-ipaserver
Requires: python3-lib389
Requires: python3-libsss_nss_idmap
# cronie-anacron provides anacron
Requires: anacron
Requires: logrotate
@ -130,22 +122,6 @@ install -p -m644 %{_builddir}/%{project}-%{shortname}-%{version}/man/man5/%{long
%changelog
* Mon Jun 23 2025 Rob Crittenden <rcritten@redhat.com> - 0.12-6
- Don't rely on order in trust roles (RHEL-99487)
* Thu Feb 27 2025 Rob Crittenden <rcritten@redhat.com> - 0.12-5
- Pull in lint fixes. Prevents exception when testing for AD trust (RHEL-79081)
- Add direct requires on python3-libsss_nss_idmap.
* Fri Jun 21 2024 Rob Crittenden <rcritten@redhat.com> - 0.12-4
- Change log file permissions of IPA as per CIS benchmark (RHEL-38929)
* Mon Jul 24 2023 Rob Crittenden <rcritten@redhat.com> - 0.12-3
- Error in DogtagCertsConnectivityCheckCA with external CA (#2223942)
* Wed May 03 2023 Rob Crittenden <rcritten@redhat.com> - 0.12-2
- Skip AD domains with posix ranges in the catalog check (#1775199)
* Thu Dec 01 2022 Rob Crittenden <rcritten@redhat.com> - 0.12-1
- Update to upstream 0.12 (#2139529)
- Verify that the number of krb5kdc worker processes is aligned to the