Skip to content

Commit 6de931d

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 0b3151a commit 6de931d

2 files changed

Lines changed: 207 additions & 3 deletions

File tree

src/ipahealthcheck/ipa/config.py

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,22 @@
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
17+
18+
19+
logger = logging.getLogger()
1020

1121

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

tests/test_ipa_config.py

Lines changed: 143 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,108 @@ 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 now allowed_uids option in the pac section"""
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) == 1
106+
result = self.results.results[0]
107+
assert result.result == constants.ERROR
108+
assert result.source == 'ipahealthcheck.ipa.config'
109+
assert result.check == 'SSSDAllowedUids389Check'
110+
111+
@patch('SSSDConfig.SSSDConfig')
112+
def test_sssd_no_allowed_uids_configured(self, mock_sssd):
113+
"""There is now allowed_uids option in the pac section"""
114+
mock_sssd.return_value = SSSDConfig(return_service=True,
115+
return_option=False)
116+
framework = object()
117+
registry.initialize(framework, config.Config())
118+
f = SSSDAllowedUids389Check(registry)
119+
self.results = capture_results(f)
120+
121+
assert len(self.results) == 1
122+
result = self.results.results[0]
123+
assert result.result == constants.SUCCESS
124+
assert result.source == 'ipahealthcheck.ipa.config'
125+
assert result.check == 'SSSDAllowedUids389Check'
126+
127+
@patch('SSSDConfig.SSSDConfig')
128+
def test_sssd_ok_allowed_uids_configured(self, mock_sssd):
129+
"""There is now allowed_uids option in the pac section"""
130+
mock_sssd.return_value = SSSDConfig(return_service=True,
131+
return_option=True,
132+
uids='0')
133+
framework = object()
134+
registry.initialize(framework, config.Config())
135+
f = SSSDAllowedUids389Check(registry)
136+
self.results = capture_results(f)
137+
138+
assert len(self.results) == 1
139+
result = self.results.results[0]
140+
assert result.result == constants.SUCCESS
141+
assert result.source == 'ipahealthcheck.ipa.config'
142+
assert result.check == 'SSSDAllowedUids389Check'
143+
144+
@patch('SSSDConfig.SSSDConfig')
145+
def test_sssd_ok_multiple_allowed_uids_configured(self, mock_sssd):
146+
"""There is now allowed_uids option in the pac section"""
147+
mock_sssd.return_value = SSSDConfig(return_service=True,
148+
return_option=True,
149+
uids='0')
150+
framework = object()
151+
registry.initialize(framework, config.Config())
152+
f = SSSDAllowedUids389Check(registry)
153+
self.results = capture_results(f)
154+
155+
assert len(self.results) == 1
156+
result = self.results.results[0]
157+
assert result.result == constants.SUCCESS
158+
assert result.source == 'ipahealthcheck.ipa.config'
159+
assert result.check == 'SSSDAllowedUids389Check'
160+
161+
@patch('SSSDConfig.SSSDConfig')
162+
def test_sssd_bad_allowed_uids_configured(self, mock_sssd):
163+
"""There is now allowed_uids option in the pac section"""
164+
mock_sssd.return_value = SSSDConfig(return_service=True,
165+
return_option=True,
166+
uids='0, 389')
167+
framework = object()
168+
registry.initialize(framework, config.Config())
169+
f = SSSDAllowedUids389Check(registry)
170+
self.results = capture_results(f)
171+
172+
assert len(self.results) == 1
173+
result = self.results.results[0]
174+
assert result.result == constants.ERROR
175+
assert result.kw.get('invalid') == '389'
176+
assert result.source == 'ipahealthcheck.ipa.config'
177+
assert result.check == 'SSSDAllowedUids389Check'
178+
179+
@patch('SSSDConfig.SSSDConfig')
180+
def test_sssd_bad_alpha_allowed_uids_configured(self, mock_sssd):
181+
"""There is now allowed_uids option in the pac section"""
182+
mock_sssd.return_value = SSSDConfig(return_service=True,
183+
return_option=True,
184+
uids='root, dirsrv')
185+
framework = object()
186+
registry.initialize(framework, config.Config())
187+
f = SSSDAllowedUids389Check(registry)
188+
self.results = capture_results(f)
189+
190+
assert len(self.results) == 1
191+
result = self.results.results[0]
192+
assert result.result == constants.ERROR
193+
assert result.kw.get('invalid') == 'dirsrv'
194+
assert result.source == 'ipahealthcheck.ipa.config'
195+
assert result.check == 'SSSDAllowedUids389Check'

0 commit comments

Comments
 (0)