Skip to content

Commit c92acfd

Browse files
committed
Check that allowed_uids in the SSSD config is valid
If UID 389 or user dirsrv is found in 'allowed_uids' in sssd.conf section [pac] then this will prevent SSSD from resolving the LDAP service account locally and can cause IPA services to fail. Fixes: #362 Signed-off-by: Rob Crittenden <rcritten@redhat.com>
1 parent d558499 commit c92acfd

3 files changed

Lines changed: 215 additions & 3 deletions

File tree

README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -618,6 +618,21 @@ Check that the certmonger CA configuration is correct. Evaluates dogtag-ipa-ca-r
618618
}
619619
}
620620

621+
## ipahealthcheck.ipa.config
622+
623+
### IPAkrbLastSuccessfulAuth
624+
Warn if logging krbLastSuccessfulAuth is enabled by removing
625+
'KDC:Disable Last Success' from the ipa config string. This is known
626+
to cause performance issues. No check is done whether the replication
627+
exclusion rule has been modified to allow replication of the attribute
628+
which will cause even more performance issues.
629+
630+
### SSSDAllowedUids389Check
631+
Adding the 389/dirsrv user to the [pac] section of sssd.conf can cause
632+
timeouts and performance problems as SSSD will try to de-reference
633+
the value against LDAP. This can cause looping and failures in trust
634+
setups.
635+
621636
## ipahealthcheck.ipa.dna
622637

623638
### IPADNARangeCheck

src/ipahealthcheck/ipa/config.py

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,19 @@
1-
1+
#
22
# Copyright (C) 2025 FreeIPA Contributors see COPYING for license
33
#
44

5+
import logging
6+
import SSSDConfig
7+
58
from ipahealthcheck.ipa.plugin import IPAPlugin, registry
69
from ipahealthcheck.core.plugin import Result, duration
710
from ipahealthcheck.core import constants
811

912
from ipalib import api
13+
from ipaplatform.constants import constants as platformconstants
14+
15+
logger = logging.getLogger(__name__)
16+
DS_USER = platformconstants.DS_USER
1017

1118

1219
@registry
@@ -44,3 +51,53 @@ def check(self):
4451
constants.SUCCESS,
4552
key='krbLastSuccessfulAuth'
4653
)
54+
55+
56+
@registry
57+
class SSSDAllowedUids389Check(IPAPlugin):
58+
"""
59+
Checks if UID 389 (LDAP service account) is listed in allowed_uids
60+
in sssd.conf.
61+
62+
If UID 389 is in allowed_uids, SSSD will prevent local resolution
63+
which will cause issues with the IPA services.
64+
"""
65+
66+
@duration
67+
def check(self):
68+
try:
69+
sssdconfig = SSSDConfig.SSSDConfig()
70+
sssdconfig.import_config()
71+
except Exception as e:
72+
logger.debug('Failed to parse sssd.conf: %s', e)
73+
yield Result(self, constants.CRITICAL, error=str(e),
74+
msg='Unable to parse sssd.conf: {error}')
75+
return
76+
77+
try:
78+
service = sssdconfig.get_service('pac')
79+
except SSSDConfig.NoServiceError:
80+
logger.debug('No pac section found.')
81+
return
82+
83+
try:
84+
uids = service.get_option('allowed_uids')
85+
except SSSDConfig.NoOptionError:
86+
logger.debug('ok, allowed_uids is undefined')
87+
yield Result(self, constants.SUCCESS, key='SSSD_allowed_uids')
88+
return
89+
else:
90+
uids = {s.strip() for s in uids.split(',') if s.strip()}
91+
candidates = {'389', DS_USER}
92+
invalid = uids.intersection(candidates)
93+
if invalid:
94+
yield Result(
95+
self, constants.ERROR,
96+
key='SSSD_allowed_uids',
97+
invalid=', '.join(invalid),
98+
msg="User/UID {invalid} found in 'allowed_uids' in "
99+
"the [pac] section of sssd.conf."
100+
)
101+
return
102+
103+
yield Result(self, constants.SUCCESS, key='SSSD_allowed_uids')

tests/test_ipa_config.py

Lines changed: 142 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,48 @@
11
#
2-
# Copyright (C) 2025 FreeIPA Contributors see COPYING for license
2+
# Copyright (C) 2024 FreeIPA Contributors see COPYING for license
33
#
44

55
from util import capture_results, m_api
66
from base import BaseTest
7+
from unittest.mock import patch
78
from ipahealthcheck.core import config, constants
89
from ipahealthcheck.ipa.plugin import registry
9-
from ipahealthcheck.ipa.config import IPAkrbLastSuccessfulAuth
10+
from ipahealthcheck.ipa.config import (
11+
IPAkrbLastSuccessfulAuth,
12+
SSSDAllowedUids389Check
13+
)
14+
15+
from SSSDConfig import NoOptionError
16+
from SSSDConfig import NoServiceError
17+
18+
19+
class SSSDService():
20+
def __init__(self, return_option, uids):
21+
self.uids = uids
22+
self.return_option = return_option
23+
24+
def get_option(self, option):
25+
if not self.return_option:
26+
raise NoOptionError
27+
return self.uids
28+
29+
30+
class SSSDConfig():
31+
def __init__(self, return_service=True, return_option=False, uids=None):
32+
"""
33+
Knobs to control what data the configuration returns.
34+
"""
35+
self.return_service = return_service
36+
self.return_option = return_option
37+
self.uids = uids
38+
39+
def import_config(self):
40+
pass
41+
42+
def get_service(self, service):
43+
if not self.return_service:
44+
raise NoServiceError()
45+
return SSSDService(self.return_option, self.uids)
1046

1147

1248
class TestkrbLastSuccessfulAuth(BaseTest):
@@ -52,3 +88,107 @@ def test_last_success_enabled(self):
5288
assert result.result == constants.WARNING
5389
assert result.source == 'ipahealthcheck.ipa.config'
5490
assert result.check == 'IPAkrbLastSuccessfulAuth'
91+
92+
93+
class TestSSSDAllowedUids389Check(BaseTest):
94+
95+
@patch('SSSDConfig.SSSDConfig')
96+
def test_sssd_no_pac_section(self, mock_sssd):
97+
"""There is no pac section in sssd.conf"""
98+
mock_sssd.return_value = SSSDConfig(return_service=False,
99+
return_option=False)
100+
framework = object()
101+
registry.initialize(framework, config.Config())
102+
f = SSSDAllowedUids389Check(registry)
103+
self.results = capture_results(f)
104+
105+
assert len(self.results) == 0
106+
107+
@patch('SSSDConfig.SSSDConfig')
108+
def test_sssd_no_allowed_uids_configured(self, mock_sssd):
109+
"""There is no allowed_uids option in the pac section"""
110+
mock_sssd.return_value = SSSDConfig(return_service=True,
111+
return_option=False)
112+
framework = object()
113+
registry.initialize(framework, config.Config())
114+
f = SSSDAllowedUids389Check(registry)
115+
self.results = capture_results(f)
116+
117+
assert len(self.results) == 1
118+
result = self.results.results[0]
119+
assert result.result == constants.SUCCESS
120+
assert result.source == 'ipahealthcheck.ipa.config'
121+
assert result.check == 'SSSDAllowedUids389Check'
122+
123+
@patch('SSSDConfig.SSSDConfig')
124+
def test_sssd_ok_allowed_uids_configured(self, mock_sssd):
125+
"""There is now allowed_uids option in the pac section"""
126+
mock_sssd.return_value = SSSDConfig(return_service=True,
127+
return_option=True,
128+
uids='0')
129+
framework = object()
130+
registry.initialize(framework, config.Config())
131+
f = SSSDAllowedUids389Check(registry)
132+
self.results = capture_results(f)
133+
134+
assert len(self.results) == 1
135+
result = self.results.results[0]
136+
assert result.result == constants.SUCCESS
137+
assert result.source == 'ipahealthcheck.ipa.config'
138+
assert result.check == 'SSSDAllowedUids389Check'
139+
140+
@patch('SSSDConfig.SSSDConfig')
141+
def test_sssd_ok_multiple_allowed_uids_configured(self, mock_sssd):
142+
"""There is now allowed_uids option in the pac section"""
143+
mock_sssd.return_value = SSSDConfig(return_service=True,
144+
return_option=True,
145+
uids='0, 100000')
146+
147+
# uid 100000 is a value I picked out of the air. It doesn't
148+
# matter what it is as it isn't prohibited
149+
framework = object()
150+
registry.initialize(framework, config.Config())
151+
f = SSSDAllowedUids389Check(registry)
152+
self.results = capture_results(f)
153+
154+
assert len(self.results) == 1
155+
result = self.results.results[0]
156+
assert result.result == constants.SUCCESS
157+
assert result.source == 'ipahealthcheck.ipa.config'
158+
assert result.check == 'SSSDAllowedUids389Check'
159+
160+
@patch('SSSDConfig.SSSDConfig')
161+
def test_sssd_bad_allowed_uids_configured(self, mock_sssd):
162+
"""There is now allowed_uids option in the pac section"""
163+
mock_sssd.return_value = SSSDConfig(return_service=True,
164+
return_option=True,
165+
uids='0, 389')
166+
framework = object()
167+
registry.initialize(framework, config.Config())
168+
f = SSSDAllowedUids389Check(registry)
169+
self.results = capture_results(f)
170+
171+
assert len(self.results) == 1
172+
result = self.results.results[0]
173+
assert result.result == constants.ERROR
174+
assert result.kw.get('invalid') == '389'
175+
assert result.source == 'ipahealthcheck.ipa.config'
176+
assert result.check == 'SSSDAllowedUids389Check'
177+
178+
@patch('SSSDConfig.SSSDConfig')
179+
def test_sssd_bad_alpha_allowed_uids_configured(self, mock_sssd):
180+
"""There is now allowed_uids option in the pac section"""
181+
mock_sssd.return_value = SSSDConfig(return_service=True,
182+
return_option=True,
183+
uids='root, dirsrv')
184+
framework = object()
185+
registry.initialize(framework, config.Config())
186+
f = SSSDAllowedUids389Check(registry)
187+
self.results = capture_results(f)
188+
189+
assert len(self.results) == 1
190+
result = self.results.results[0]
191+
assert result.result == constants.ERROR
192+
assert result.kw.get('invalid') == 'dirsrv'
193+
assert result.source == 'ipahealthcheck.ipa.config'
194+
assert result.check == 'SSSDAllowedUids389Check'

0 commit comments

Comments
 (0)