From d6d06a3a22d441fbfb208eeb63caae7aafd434b4 Mon Sep 17 00:00:00 2001
From: Rob Crittenden <rcritten@redhat.com>
Date: Thu, 8 Aug 2019 18:46:32 +0000
Subject: [PATCH 7/8] Lookup AD user by SID and not by hardcoded username
Looking up the user user as Administrator@REALM is not
portable because the administrator login name may be
localized.
---
.travis.yml | 2 +-
src/ipahealthcheck/ipa/plugin.py | 8 ++++
src/ipahealthcheck/ipa/trust.py | 44 +++++++++++++++----
tests/test_ipa_trust.py | 72 ++++++++++++++++++--------------
4 files changed, 85 insertions(+), 41 deletions(-)
diff --git a/.travis.yml b/.travis.yml
index 16722fe..25dbf1e 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -19,4 +19,4 @@ install:
script:
- tox -epep8,flake8
- - docker run -v ${TRAVIS_BUILD_DIR}:/root/src/ fedora:29 /bin/sh -c "dnf -y install freeipa-server tox python3-pytest; cd /root/src; tox -epy3"
+ - docker run -v ${TRAVIS_BUILD_DIR}:/root/src/ fedora:29 /bin/sh -c "dnf -y install freeipa-server freeipa-server-trust-ad tox python3-pytest; cd /root/src; tox -epy3"
diff --git a/src/ipahealthcheck/ipa/plugin.py b/src/ipahealthcheck/ipa/plugin.py
index bd95c16..34cb8c9 100644
--- a/src/ipahealthcheck/ipa/plugin.py
+++ b/src/ipahealthcheck/ipa/plugin.py
@@ -57,6 +57,14 @@ class IPARegistry(Registry):
logging.debug('Failed to connect to LDAP: %s', e)
return
+ # This package is pulled in when the trust package is installed
+ # and is required to lookup trust users. If this is not installed
+ # then it can be inferred that trust is not enabled.
+ try:
+ import pysss_nss_idmap # noqa: F401
+ except ImportError:
+ return
+
roles = (
ADtrustBasedRole(u"ad_trust_agent_server",
u"AD trust agent"),
diff --git a/src/ipahealthcheck/ipa/trust.py b/src/ipahealthcheck/ipa/trust.py
index 2da20c0..6c5978d 100644
--- a/src/ipahealthcheck/ipa/trust.py
+++ b/src/ipahealthcheck/ipa/trust.py
@@ -16,6 +16,12 @@ from ipaplatform.paths import paths
from ipapython import ipautil
from ipapython.dn import DN
+try:
+ import pysss_nss_idmap
+except ImportError:
+ # agent and controller will be set to False in init, all tests will
+ # be skipped
+ pass
try:
from ipaserver.masters import ENABLED_SERVICE
except ImportError:
@@ -33,13 +39,19 @@ def get_trust_domains():
Get the list of AD trust domains from IPA
The caller is expected to catch any exceptions.
+
+ Each entry is a dictionary representating an AD domain.
"""
result = api.Command.trust_find()
results = result['result']
trust_domains = []
for result in results:
if result.get('trusttype')[0] == 'Active Directory domain':
- trust_domains.append(result.get('cn')[0])
+ domain = dict()
+ domain['domain'] = result.get('cn')[0]
+ domain['domainsid'] = result.get('ipanttrusteddomainsid')[0]
+ domain['netbios'] = result.get('ipantflatname')[0]
+ trust_domains.append(domain)
return trust_domains
@@ -123,14 +135,17 @@ class IPATrustDomainsCheck(IPAPlugin):
if 'implicit_files' in sssd_domains:
sssd_domains.remove('implicit_files')
+ trust_domains = []
try:
- trust_domains = get_trust_domains()
+ domains = get_trust_domains()
except Exception as e:
yield Result(self, constants.WARNING,
key='trust-find',
error=str(e),
msg='Execution of {key} failed: {error}')
- trust_domains = []
+ else:
+ for entry in domains:
+ trust_domains.append(entry.get('domain'))
if api.env.domain in sssd_domains:
sssd_domains.remove(api.env.domain)
@@ -204,17 +219,28 @@ class IPATrustCatalogCheck(IPAPlugin):
msg='Execution of {key} failed: {error}')
trust_domains = []
- for domain in trust_domains:
+ for trust_domain in trust_domains:
+ sid = trust_domain.get('domainsid')
try:
- ipautil.run(['/bin/id', "Administrator@%s" % domain],
- capture_output=True)
+ id = pysss_nss_idmap.getnamebysid(sid + '-500')
except Exception as e:
- yield Result(self, constants.WARNING,
- key='/bin/id',
+ yield Result(self, constants.ERROR,
+ key=sid,
error=str(e),
- msg='Execution of {key} failed: {error}')
+ msg='Look up of{key} failed: {error}')
continue
+ if not id:
+ yield Result(self, constants.WARNING,
+ key=sid,
+ error='returned nothing',
+ msg='Look up of {key} {error}')
+ else:
+ yield Result(self, constants.SUCCESS,
+ key='Domain Security Identifier',
+ sid=sid)
+
+ domain = trust_domain.get('domain')
args = [paths.SSSCTL, "domain-status", domain, "--active-server"]
try:
result = ipautil.run(args, capture_output=True)
diff --git a/tests/test_ipa_trust.py b/tests/test_ipa_trust.py
index 0a1d58c..89d3bff 100644
--- a/tests/test_ipa_trust.py
+++ b/tests/test_ipa_trust.py
@@ -261,12 +261,14 @@ class TestTrustDomains(BaseTest):
{
'cn': ['ad.example'],
'ipantflatname': ['ADROOT'],
- "trusttype": ["Active Directory domain"],
+ 'ipanttrusteddomainsid': ['S-1-5-21-abc'],
+ 'trusttype': ['Active Directory domain'],
},
{
'cn': ['child.example'],
'ipantflatname': ['ADROOT'],
- "trusttype": ["Active Directory domain"],
+ 'ipanttrusteddomainsid': ['S-1-5-21-def'],
+ 'trusttype': ['Active Directory domain'],
},
]
}]
@@ -324,12 +326,14 @@ class TestTrustDomains(BaseTest):
{
'cn': ['ad.example'],
'ipantflatname': ['ADROOT'],
- "trusttype": ["Active Directory domain"],
+ 'ipanttrusteddomainsid': ['S-1-5-21-abc'],
+ 'trusttype': ['Active Directory domain'],
},
{
'cn': ['child.example'],
'ipantflatname': ['ADROOT'],
- "trusttype": ["Active Directory domain"],
+ 'ipanttrusteddomainsid': ['S-1-5-21-def'],
+ 'trusttype': ['Active Directory domain'],
},
]
}]
@@ -373,41 +377,27 @@ class TestTrustCatalog(BaseTest):
# Zero because the call was skipped altogether
assert len(self.results) == 0
+ @patch('pysss_nss_idmap.getnamebysid')
@patch('ipapython.ipautil.run')
- def test_trust_catalog_ok(self, mock_run):
+ def test_trust_catalog_ok(self, mock_run, mock_getnamebysid):
# id Administrator@ad.example
- idresult = namedtuple('run', ['returncode', 'error_log'])
- idresult.returncode = 0
- idresult.error_log = ''
- idresult.output = '797600500(administrator@ad.example),' \
- '1797600520(group policy creator owners@ad.example),' \
- '1797600519(enterprise admins@ad.example),' \
- '1797600512(domain admins@ad.example),' \
- '1797600518(schema admins@ad.example)' \
- ',1797600513(domain users@ad.example)\n'
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'
- # id Administrator@client.example
- id2result = namedtuple('run', ['returncode', 'error_log'])
- id2result.returncode = 0
- id2result.error_log = ''
- id2result.output = '797600500(administrator@client.example),' \
- '1797600520(group policy creator owners@client.example),' \
- '1797600519(enterprise admins@client.example),' \
- '1797600512(domain admins@client.example),' \
- '1797600518(schema admins@client.example)' \
- ',1797600513(domain users@client.example)\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 = [idresult, dsresult, id2result, ds2result]
+ mock_run.side_effect = [dsresult, ds2result]
+ mock_getnamebysid.side_effect = [
+ {'S-1-5-21-abc-500': {'name': 'admin@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 = [{
@@ -415,12 +405,14 @@ class TestTrustCatalog(BaseTest):
{
'cn': ['ad.example'],
'ipantflatname': ['ADROOT'],
- "trusttype": ["Active Directory domain"],
+ 'ipanttrusteddomainsid': ['S-1-5-21-abc'],
+ 'trusttype': ['Active Directory domain'],
},
{
'cn': ['child.example'],
'ipantflatname': ['ADROOT'],
- "trusttype": ["Active Directory domain"],
+ 'ipanttrusteddomainsid': ['S-1-5-21-def'],
+ 'trusttype': ['Active Directory domain'],
},
]
}]
@@ -433,31 +425,49 @@ class TestTrustCatalog(BaseTest):
f.config = config.Config()
self.results = capture_results(f)
- assert len(self.results) == 4
+ assert len(self.results) == 6
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') == 'AD Global Catalog'
+ assert result.kw.get('key') == 'Domain Security Identifier'
+ assert result.kw.get('sid') == 'S-1-5-21-abc'
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') == 'AD Domain Controller'
+ assert result.kw.get('key') == 'AD Global Catalog'
+ assert result.kw.get('domain') == 'ad.example'
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') == 'AD Domain Controller'
+ assert result.kw.get('domain') == 'ad.example'
+
+ result = self.results.results[3]
+ assert result.result == constants.SUCCESS
+ assert result.source == 'ipahealthcheck.ipa.trust'
+ assert result.check == 'IPATrustCatalogCheck'
+ assert result.kw.get('key') == 'Domain Security Identifier'
+ assert result.kw.get('sid') == 'S-1-5-21-def'
+
+ result = self.results.results[4]
+ assert result.result == constants.SUCCESS
+ assert result.source == 'ipahealthcheck.ipa.trust'
+ assert result.check == 'IPATrustCatalogCheck'
assert result.kw.get('key') == 'AD Global Catalog'
+ assert result.kw.get('domain') == 'child.example'
- result = self.results.results[1]
+ result = self.results.results[5]
assert result.result == constants.SUCCESS
assert result.source == 'ipahealthcheck.ipa.trust'
assert result.check == 'IPATrustCatalogCheck'
assert result.kw.get('key') == 'AD Domain Controller'
+ assert result.kw.get('domain') == 'child.example'
class Testsidgen(BaseTest):
--
2.20.1