Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -618,6 +618,21 @@ Check that the certmonger CA configuration is correct. Evaluates dogtag-ipa-ca-r
}
}

## ipahealthcheck.ipa.config

### IPAkrbLastSuccessfulAuth
Warn if logging krbLastSuccessfulAuth is enabled by removing
'KDC:Disable Last Success' from the ipa config string. This is known
to cause performance issues. No check is done whether the replication
exclusion rule has been modified to allow replication of the attribute
which will cause even more performance issues.

### SSSDAllowedUids389Check
Adding the 389/dirsrv user to the [pac] section of sssd.conf can cause
timeouts and performance problems as SSSD will try to de-reference
the value against LDAP. This can cause looping and failures in trust
setups.

## ipahealthcheck.ipa.dna

### IPADNARangeCheck
Expand Down
59 changes: 58 additions & 1 deletion src/ipahealthcheck/ipa/config.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@

#
# Copyright (C) 2025 FreeIPA Contributors see COPYING for license
#

import logging
import SSSDConfig

from ipahealthcheck.ipa.plugin import IPAPlugin, registry
from ipahealthcheck.core.plugin import Result, duration
from ipahealthcheck.core import constants

from ipalib import api
from ipaplatform.constants import constants as platformconstants

logger = logging.getLogger(__name__)
DS_USER = platformconstants.DS_USER


@registry
Expand Down Expand Up @@ -44,3 +51,53 @@ def check(self):
constants.SUCCESS,
key='krbLastSuccessfulAuth'
)


@registry
class SSSDAllowedUids389Check(IPAPlugin):
Comment thread
rcritten marked this conversation as resolved.
"""
Checks if UID 389 (LDAP service account) is listed in allowed_uids
in sssd.conf.

If UID 389 is in allowed_uids, SSSD will prevent local resolution
which will cause issues with the IPA services.
"""

@duration
def check(self):
try:
sssdconfig = SSSDConfig.SSSDConfig()
sssdconfig.import_config()
except Exception as e:
logger.debug('Failed to parse sssd.conf: %s', e)
yield Result(self, constants.CRITICAL, error=str(e),
msg='Unable to parse sssd.conf: {error}')
return

try:
service = sssdconfig.get_service('pac')
except SSSDConfig.NoServiceError:
logger.debug('No pac section found.')
return

try:
uids = service.get_option('allowed_uids')
except SSSDConfig.NoOptionError:
logger.debug('ok, allowed_uids is undefined')
yield Result(self, constants.SUCCESS, key='SSSD_allowed_uids')
return
else:
uids = {s.strip() for s in uids.split(',') if s.strip()}
candidates = {'389', DS_USER}
invalid = uids.intersection(candidates)
Comment thread
sourcery-ai[bot] marked this conversation as resolved.
if invalid:
Comment thread
sourcery-ai[bot] marked this conversation as resolved.
yield Result(
self, constants.ERROR,
key='SSSD_allowed_uids',
invalid=', '.join(invalid),
msg="User/UID {invalid} found in 'allowed_uids' in "
"the [pac] section of sssd.conf."
)
return

yield Result(self, constants.SUCCESS, key='SSSD_allowed_uids')
144 changes: 142 additions & 2 deletions tests/test_ipa_config.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,48 @@
#
# Copyright (C) 2025 FreeIPA Contributors see COPYING for license
# Copyright (C) 2024 FreeIPA Contributors see COPYING for license
#

from util import capture_results, m_api
from base import BaseTest
from unittest.mock import patch
from ipahealthcheck.core import config, constants
from ipahealthcheck.ipa.plugin import registry
from ipahealthcheck.ipa.config import IPAkrbLastSuccessfulAuth
from ipahealthcheck.ipa.config import (
IPAkrbLastSuccessfulAuth,
SSSDAllowedUids389Check
)

from SSSDConfig import NoOptionError
from SSSDConfig import NoServiceError


class SSSDService():
def __init__(self, return_option, uids):
self.uids = uids
self.return_option = return_option

def get_option(self, option):
if not self.return_option:
raise NoOptionError
return self.uids


class SSSDConfig():
def __init__(self, return_service=True, return_option=False, uids=None):
"""
Knobs to control what data the configuration returns.
"""
self.return_service = return_service
self.return_option = return_option
self.uids = uids

def import_config(self):
pass

def get_service(self, service):
if not self.return_service:
raise NoServiceError()
return SSSDService(self.return_option, self.uids)


class TestkrbLastSuccessfulAuth(BaseTest):
Expand Down Expand Up @@ -52,3 +88,107 @@ def test_last_success_enabled(self):
assert result.result == constants.WARNING
assert result.source == 'ipahealthcheck.ipa.config'
assert result.check == 'IPAkrbLastSuccessfulAuth'


class TestSSSDAllowedUids389Check(BaseTest):

@patch('SSSDConfig.SSSDConfig')
def test_sssd_no_pac_section(self, mock_sssd):
"""There is no pac section in sssd.conf"""
mock_sssd.return_value = SSSDConfig(return_service=False,
return_option=False)
framework = object()
registry.initialize(framework, config.Config())
f = SSSDAllowedUids389Check(registry)
self.results = capture_results(f)

assert len(self.results) == 0

@patch('SSSDConfig.SSSDConfig')
def test_sssd_no_allowed_uids_configured(self, mock_sssd):
"""There is no allowed_uids option in the pac section"""
mock_sssd.return_value = SSSDConfig(return_service=True,
return_option=False)
framework = object()
registry.initialize(framework, config.Config())
f = SSSDAllowedUids389Check(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.ipa.config'
assert result.check == 'SSSDAllowedUids389Check'

@patch('SSSDConfig.SSSDConfig')
def test_sssd_ok_allowed_uids_configured(self, mock_sssd):
"""There is now allowed_uids option in the pac section"""
mock_sssd.return_value = SSSDConfig(return_service=True,
return_option=True,
uids='0')
framework = object()
registry.initialize(framework, config.Config())
f = SSSDAllowedUids389Check(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.ipa.config'
assert result.check == 'SSSDAllowedUids389Check'

@patch('SSSDConfig.SSSDConfig')
def test_sssd_ok_multiple_allowed_uids_configured(self, mock_sssd):
Comment thread
sourcery-ai[bot] marked this conversation as resolved.
"""There is now allowed_uids option in the pac section"""
mock_sssd.return_value = SSSDConfig(return_service=True,
return_option=True,
uids='0, 100000')

# uid 100000 is a value I picked out of the air. It doesn't
# matter what it is as it isn't prohibited
framework = object()
registry.initialize(framework, config.Config())
f = SSSDAllowedUids389Check(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.ipa.config'
assert result.check == 'SSSDAllowedUids389Check'

@patch('SSSDConfig.SSSDConfig')
def test_sssd_bad_allowed_uids_configured(self, mock_sssd):
"""There is now allowed_uids option in the pac section"""
mock_sssd.return_value = SSSDConfig(return_service=True,
return_option=True,
uids='0, 389')
framework = object()
registry.initialize(framework, config.Config())
f = SSSDAllowedUids389Check(registry)
self.results = capture_results(f)

assert len(self.results) == 1
result = self.results.results[0]
assert result.result == constants.ERROR
assert result.kw.get('invalid') == '389'
assert result.source == 'ipahealthcheck.ipa.config'
assert result.check == 'SSSDAllowedUids389Check'

@patch('SSSDConfig.SSSDConfig')
def test_sssd_bad_alpha_allowed_uids_configured(self, mock_sssd):
"""There is now allowed_uids option in the pac section"""
mock_sssd.return_value = SSSDConfig(return_service=True,
return_option=True,
uids='root, dirsrv')
framework = object()
registry.initialize(framework, config.Config())
f = SSSDAllowedUids389Check(registry)
self.results = capture_results(f)

assert len(self.results) == 1
result = self.results.results[0]
assert result.result == constants.ERROR
assert result.kw.get('invalid') == 'dirsrv'
assert result.source == 'ipahealthcheck.ipa.config'
assert result.check == 'SSSDAllowedUids389Check'