freeipa/ipatests/test_integration/test_legacy_clients.py
Alexander Bokovoy c91a1a078a ipatests: when talking to AD DCs, use FQDN credentials
Samba 4.13+ in Fedora 33+ and RHEL 8.4+ defaults to Kerberos
authentication. This means user name used for authentication must be
mapped to a target realm.

We have to remove trust on AD side first before removing it locally or
otherwise MIT Kerberos might not be able to locate DCs from AD as
removal of the trust information would cause SSSD to clear the details
for a KDC locator plugin as well.

For the test that modifies AD DNS zone on IPA side to inject unreachable
DCs addresses, the configuration has to be reverted first, to allow
plain 'kinit' during removal of trust to reach AD DCs directly.

Fixes: https://pagure.io/freeipa/issue/8678
Signed-off-by: Alexander Bokovoy <abokovoy@redhat.com>
Reviewed-By: Christian Heimes <cheimes@redhat.com>
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
Reviewed-By: Florence Blanc-Renaud <frenaud@redhat.com>
2021-01-26 13:05:27 -05:00

581 lines
20 KiB
Python

# Authors:
# Tomas Babej <tbabej@redhat.com>
#
# Copyright (C) 2013 Red Hat
# see file 'COPYING' for use and warranty information
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# FIXME: Pylint errors
# pylint: disable=no-member
from __future__ import absolute_import
import os
import re
import pytest
from ipaplatform.constants import constants as platformconstants
from ipaplatform.paths import paths
from ipatests.pytest_ipa.integration import tasks
# importing test_trust under different name to avoid nose executing the test
# base class imported from this module
from ipatests.test_integration import test_trust as trust_tests
class BaseTestLegacyClient:
"""
Tests legacy client support.
"""
advice_id = None
backup_files = ['/etc/sysconfig/authconfig',
'/etc/pam.d',
'/etc/openldap/cacerts',
'/etc/openldap/ldap.conf',
'/etc/nsswitch.conf',
paths.SSSD_CONF]
homedir_template = "/home/{domain}/{username}"
default_shell = platformconstants.DEFAULT_SHELL
required_extra_roles = ()
optional_extra_roles = ()
# Actual test classes need to override these attributes to set the expected
# values on the UID and GID results, since this varies with the usage of the
# POSIX and non-POSIX ID ranges
testuser_uid_regex = None
testuser_gid_regex = None
subdomain_testuser_uid_regex = None
subdomain_testuser_gid_regex = None
treedomain_testuser_uid_regex = None
treedomain_testuser_gid_regex = None
# To allow custom validation dependent on the trust type
posix_trust = False
def test_apply_advice(self):
# Obtain the advice from the server
tasks.kinit_admin(self.master)
result = self.master.run_command(['ipa-advise', self.advice_id])
advice = result.stdout_text
# Apply the advice on the legacy client
advice_path = os.path.join(self.legacy_client.config.test_dir,
'advice.sh')
self.legacy_client.put_file_contents(advice_path, advice)
result = self.legacy_client.run_command(['bash', '-x', '-e',
advice_path])
# Restart SSHD to load new PAM configuration
self.legacy_client.run_command([paths.SBIN_SERVICE, 'sshd', 'restart'])
def clear_sssd_caches(self):
tasks.clear_sssd_cache(self.master)
tasks.clear_sssd_cache(self.legacy_client)
def test_getent_ipa_user(self):
self.clear_sssd_caches()
result = self.legacy_client.run_command(['getent', 'passwd', 'admin'])
admin_regex = r"admin:\*:(\d+):(\d+):"\
r"Administrator:/home/admin:{}".format(
platformconstants.DEFAULT_ADMIN_SHELL,
)
assert re.search(admin_regex, result.stdout_text)
def test_getent_ipa_group(self):
self.clear_sssd_caches()
result = self.legacy_client.run_command(['getent', 'group', 'admins'])
admin_group_regex = r"admins:\*:(\d+):admin"
assert re.search(admin_group_regex, result.stdout_text)
def test_id_ipa_user(self):
self.clear_sssd_caches()
result = self.legacy_client.run_command(['id', 'admin'])
uid_regex = r"uid=(\d+)\(admin\)"
gid_regex = r"gid=(\d+)\(admins\)"
groups_regex = r"groups=(\d+)\(admins\)"
assert re.search(uid_regex, result.stdout_text)
assert re.search(gid_regex, result.stdout_text)
assert re.search(groups_regex, result.stdout_text)
def test_getent_ad_user(self):
self.clear_sssd_caches()
testuser = 'testuser@%s' % self.ad.domain.name
result = self.legacy_client.run_command(['getent', 'passwd', testuser])
testuser_regex = r"testuser@%s:\*:%s:%s:"\
r"Test User:%s:%s"\
% (re.escape(self.ad.domain.name),
self.testuser_uid_regex,
self.testuser_gid_regex,
self.homedir_template.format(
username='testuser',
domain=re.escape(self.ad.domain.name)),
self.default_shell,
)
assert re.search(testuser_regex, result.stdout_text)
def test_getent_ad_group(self):
self.clear_sssd_caches()
testgroup = 'testgroup@%s' % self.ad.domain.name
result = self.legacy_client.run_command(['getent', 'group', testgroup])
testgroup_regex = r"%s:\*:%s:" % (testgroup, self.testuser_gid_regex)
assert re.search(testgroup_regex, result.stdout_text)
def test_id_ad_user(self):
self.clear_sssd_caches()
testuser = 'testuser@%s' % self.ad.domain.name
testgroup = 'testgroup@%s' % self.ad.domain.name
result = self.legacy_client.run_command(['id', testuser])
# Only for POSIX trust testing does the testuser belong to the
# testgroup
group_name = r'\(%s\)' % testgroup if self.posix_trust else ''
uid_regex = r"uid=%s\(%s\)" % (self.testuser_uid_regex, testuser)
gid_regex = "gid=%s%s" % (self.testuser_gid_regex, group_name)
groups_regex = "groups=%s%s" % (self.testuser_gid_regex, group_name)
assert re.search(uid_regex, result.stdout_text)
assert re.search(gid_regex, result.stdout_text)
assert re.search(groups_regex, result.stdout_text)
def test_login_ipa_user(self):
if not self.master.transport.file_exists('/usr/bin/sshpass'):
pytest.skip('Package sshpass not available on %s'
% self.master.hostname)
result = self.master.run_command(
'sshpass -p %s '
'ssh '
'-o StrictHostKeyChecking=no '
'-l admin '
'%s '
'"echo test"' %
(self.legacy_client.config.admin_password,
self.legacy_client.hostname))
assert "test" in result.stdout_text
def test_login_ad_user(self):
if not self.master.transport.file_exists('/usr/bin/sshpass'):
pytest.skip('Package sshpass not available on %s'
% self.master.hostname)
testuser = 'testuser@%s' % self.ad.domain.name
result = self.master.run_command(
'sshpass -p Secret123 '
'ssh '
'-o StrictHostKeyChecking=no '
'-l %s '
'%s '
'"echo test"' %
(testuser, self.legacy_client.hostname))
assert "test" in result.stdout_text
def test_login_disabled_ipa_user(self):
if not self.master.transport.file_exists('/usr/bin/sshpass'):
pytest.skip('Package sshpass not available on %s'
% self.master.hostname)
self.clear_sssd_caches()
result = self.master.run_command(
'sshpass -p %s '
'ssh '
'-o StrictHostKeyChecking=no '
'-l disabledipauser '
'%s '
'"echo test"'
% (self.legacy_client.config.admin_password,
self.legacy_client.external_hostname),
raiseonerr=False)
assert result.returncode != 0
def test_login_disabled_ad_user(self):
if not self.master.transport.file_exists('/usr/bin/sshpass'):
pytest.skip('Package sshpass not available on %s'
% self.master.hostname)
testuser = 'disabledaduser@%s' % self.ad.domain.name
result = self.master.run_command(
'sshpass -p Secret123 '
'ssh '
'-o StrictHostKeyChecking=no '
'-l %s '
'%s '
'"echo test"' %
(testuser, self.legacy_client.external_hostname),
raiseonerr=False)
assert result.returncode != 0
def test_getent_subdomain_ad_user(self):
if not self.ad_subdomain:
pytest.skip('AD for the subdomain is not available.')
self.clear_sssd_caches()
testuser = 'subdomaintestuser@%s' % self.ad_subdomain
result = self.legacy_client.run_command(['getent', 'passwd', testuser])
testuser_regex = r"subdomaintestuser@%s:\*:%s:%s:"\
r"Subdomaintest User:%s:"\
r"%s"\
% (re.escape(self.ad_subdomain),
self.subdomain_testuser_uid_regex,
self.subdomain_testuser_gid_regex,
self.homedir_template.format(
username='subdomaintestuser',
domain=re.escape(self.ad_subdomain)),
self.default_shell,
)
assert re.search(testuser_regex, result.stdout_text)
def test_getent_subdomain_ad_group(self):
if not self.ad_subdomain:
pytest.skip('AD for the subdomain is not available.')
self.clear_sssd_caches()
testgroup = 'subdomaintestgroup@%s' % self.ad_subdomain
result = self.legacy_client.run_command(['getent', 'group', testgroup])
testgroup_stdout = r"%s:\*:%s:" % (testgroup,
self.subdomain_testuser_gid_regex)
assert re.search(testgroup_stdout, result.stdout_text)
def test_id_subdomain_ad_user(self):
if not self.ad_subdomain:
pytest.skip('AD for the subdomain is not available.')
self.clear_sssd_caches()
testuser = 'subdomaintestuser@%s' % self.ad_subdomain
testgroup = 'subdomaintestgroup@%s' % self.ad_subdomain
result = self.legacy_client.run_command(['id', testuser])
# Only for POSIX trust testing does the testuser belong to the
# testgroup
group_name = r'\(%s\)' % testgroup if self.posix_trust else ''
uid_regex = r"uid=%s\(%s\)" % (self.subdomain_testuser_uid_regex,
testuser)
gid_regex = "gid=%s%s" % (self.subdomain_testuser_gid_regex,
group_name)
groups_regex = "groups=%s%s" % (self.subdomain_testuser_gid_regex,
group_name)
assert re.search(uid_regex, result.stdout_text)
assert re.search(gid_regex, result.stdout_text)
assert re.search(groups_regex, result.stdout_text)
def test_login_subdomain_ad_user(self):
if not self.ad_subdomain:
pytest.skip('AD for the subdomain is not available.')
if not self.master.transport.file_exists('/usr/bin/sshpass'):
pytest.skip('Package sshpass not available on %s'
% self.master.hostname)
testuser = 'subdomaintestuser@%s' % self.ad_subdomain
result = self.master.run_command(
'sshpass -p Secret123 '
'ssh '
'-o StrictHostKeyChecking=no '
'-l %s '
'%s '
'"echo test"' %
(testuser, self.legacy_client.external_hostname))
assert "test" in result.stdout_text
def test_login_disabled_subdomain_ad_user(self):
if not self.ad_subdomain:
pytest.skip('AD for the subdomain is not available.')
if not self.master.transport.file_exists('/usr/bin/sshpass'):
pytest.skip('Package sshpass not available on %s'
% self.master.hostname)
testuser = 'subdomaindisabledaduser@%s' % self.ad_subdomain
result = self.master.run_command(
'sshpass -p Secret123 '
'ssh '
'-o StrictHostKeyChecking=no '
'-l %s '
'%s '
'"echo test"' %
(testuser, self.legacy_client.external_hostname),
raiseonerr=False)
assert result.returncode != 0
def test_getent_treedomain_ad_user(self):
if not self.ad_treedomain:
pytest.skip('AD tree root domain is not available.')
self.clear_sssd_caches()
testuser = 'treetestuser@{0}'.format(self.ad_treedomain)
result = self.legacy_client.run_command(['getent', 'passwd', testuser])
testuser_regex = (r"treetestuser@{0}:\*:{1}:{2}:TreeTest User:"
r"/home/{0}/treetestuser:{3}".format(
re.escape(self.ad_treedomain),
self.treedomain_testuser_uid_regex,
self.treedomain_testuser_gid_regex,
self.default_shell,
))
assert re.search(testuser_regex, result.stdout_text)
def test_getent_treedomain_ad_group(self):
if not self.ad_treedomain:
pytest.skip('AD tree root domain is not available')
self.clear_sssd_caches()
testgroup = 'treetestgroup@{0}'.format(self.ad_treedomain)
result = self.legacy_client.run_command(['getent', 'group', testgroup])
testgroup_stdout = r"{0}:\*:{1}:".format(
testgroup, self.treedomain_testuser_gid_regex)
assert re.search(testgroup_stdout, result.stdout_text)
def test_id_treedomain_ad_user(self):
if not self.ad_treedomain:
pytest.skip('AD tree root domain is not available')
self.clear_sssd_caches()
testuser = 'treetestuser@{0}'.format(self.ad_treedomain)
testgroup = 'treetestgroup@{0}'.format(self.ad_treedomain)
result = self.legacy_client.run_command(['id', testuser])
# Only for POSIX trust testing does the testuser belong to the
# testgroup
group_name = r'\({}\)'.format(testgroup) if self.posix_trust else ''
uid_regex = r"uid={0}\({1}\)".format(
self.treedomain_testuser_uid_regex, testuser)
gid_regex = "gid={0}{1}".format(
self.treedomain_testuser_gid_regex, group_name)
group_regex = "groups={0}{1}".format(
self.treedomain_testuser_gid_regex, group_name)
assert re.search(uid_regex, result.stdout_text)
assert re.search(gid_regex, result.stdout_text)
assert re.search(group_regex, result.stdout_text)
def test_login_treedomain_ad_user(self):
if not self.ad_treedomain:
pytest.skip('AD tree root domain is not available.')
if not self.master.transport.file_exists('/usr/bin/sshpass'):
pytest.skip(
'Package sshpass not available on {}'.format(
self.master.hostname)
)
result = self.master.run_command(
'sshpass -p {0} ssh -o StrictHostKeyChecking=no '
'-l admin {1} "echo test"'.format(
self.legacy_client.config.admin_password,
self.legacy_client.external_hostname))
assert "test" in result.stdout_text
@classmethod
def install(cls, mh):
super(BaseTestLegacyClient, cls).install(mh)
tasks.kinit_admin(cls.master)
password_confirmation = (
cls.master.config.admin_password +
'\n' +
cls.master.config.admin_password
)
cls.master.run_command(['ipa', 'user-add', 'disabledipauser',
'--first', 'disabled',
'--last', 'ipauser',
'--password'],
stdin_text=password_confirmation)
cls.master.run_command(['ipa', 'user-disable', 'disabledipauser'])
cls.ad = cls.ad_domains[0].ads[0]
cls.legacy_client = cls.host_by_role(cls.required_extra_roles[0])
# Determine whether the subdomain AD is available
try:
child_ad = cls.host_by_role(cls.optional_extra_roles[0])
cls.ad_subdomain = '.'.join(
child_ad.hostname.split('.')[1:])
except LookupError:
cls.ad_subdomain = None
# Determine whether the tree domain AD is available
try:
cls.tree_ad = cls.host_by_role(cls.optional_extra_roles[1])
cls.ad_treedomain = '.'.join(
cls.tree_ad.hostname.split('.')[1:])
except LookupError:
cls.ad_treedomain = None
tasks.apply_common_fixes(cls.legacy_client)
for f in cls.backup_files:
tasks.backup_file(cls.legacy_client, f)
@classmethod
def uninstall(cls, mh):
cls.master.run_command(['ipa', 'user-del', 'disabledipauser'],
raiseonerr=False)
# Remove information about trust from AD, if domain was defined
if hasattr(cls, 'ad_domain'):
tasks.remove_trust_info_from_ad(cls.master, cls.ad_domain,
cls.ad_domain.hostname)
# Also unapply fixes on the legacy client, if defined
if hasattr(cls, 'legacy_client'):
tasks.unapply_fixes(cls.legacy_client)
super(BaseTestLegacyClient, cls).uninstall(mh)
# Base classes with attributes that are specific for each legacy client test
class BaseTestLegacySSSDBefore19RedHat:
advice_id = 'config-redhat-sssd-before-1-9'
required_extra_roles = ['legacy_client_sssd_redhat']
optional_extra_roles = ['ad_subdomain', 'ad_treedomain']
class BaseTestLegacyNssPamLdapdRedHat:
advice_id = 'config-redhat-nss-pam-ldapd'
required_extra_roles = ['legacy_client_nss_pam_ldapd_redhat']
optional_extra_roles = ['ad_subdomain', 'ad_treedomain']
def clear_sssd_caches(self):
tasks.clear_sssd_cache(self.master)
class BaseTestLegacyNssLdapRedHat:
advice_id = 'config-redhat-nss-ldap'
required_extra_roles = ['legacy_client_nss_ldap_redhat']
optional_extra_roles = ['ad_subdomain', 'ad_treedomain']
def clear_sssd_caches(self):
tasks.clear_sssd_cache(self.master)
# Base classes that join legacy client specific steps with steps required
# to setup IPA with trust (both with and without using the POSIX attributes)
class BaseTestLegacyClientPosix(trust_tests.BaseTestTrust,
BaseTestLegacyClient):
testuser_uid_regex = '10042'
testuser_gid_regex = '10047'
subdomain_testuser_uid_regex = '10142'
subdomain_testuser_gid_regex = '10147'
treedomain_testuser_uid_regex = '10242'
treedomain_testuser_gid_regex = '10247'
posix_trust = True
def test_remove_trust_with_posix_attributes(self):
pass
class BaseTestLegacyClientNonPosix(trust_tests.BaseTestTrust,
BaseTestLegacyClient):
testuser_uid_regex = r'(?!10042)(\d+)'
testuser_gid_regex = r'(?!10047)(\d+)'
subdomain_testuser_uid_regex = r'(?!10142)(\d+)'
subdomain_testuser_gid_regex = r'(?!10147)(\d+)'
treedomain_testuser_uid_regex = r'(?!10242)(\d+)'
treedomain_testuser_gid_regex = r'(?!10247)(\d+)'
def test_remove_nonposix_trust(self):
pass
class BaseTestSSSDMixin:
def test_apply_advice(self):
super(BaseTestSSSDMixin, self).test_apply_advice()
tasks.setup_sssd_debugging(self.legacy_client)
# Tests definitions themselves. Beauty. Just pure beauty.
class TestLegacySSSDBefore19RedHatNonPosix(BaseTestSSSDMixin,
BaseTestLegacySSSDBefore19RedHat,
BaseTestLegacyClientNonPosix):
pass
class TestLegacyNssPamLdapdRedHatNonPosix(BaseTestLegacyNssPamLdapdRedHat,
BaseTestLegacyClientNonPosix):
pass
class TestLegacyNssLdapRedHatNonPosix(BaseTestLegacyNssLdapRedHat,
BaseTestLegacyClientNonPosix):
pass
class TestLegacySSSDBefore19RedHatPosix(BaseTestSSSDMixin,
BaseTestLegacySSSDBefore19RedHat,
BaseTestLegacyClientPosix):
pass
class TestLegacyNssPamLdapdRedHatPosix(BaseTestLegacyNssPamLdapdRedHat,
BaseTestLegacyClientPosix):
pass
class TestLegacyNssLdapRedHatPosix(BaseTestLegacyNssLdapRedHat,
BaseTestLegacyClientPosix):
pass