URL: https://github.com/freeipa/freeipa/pull/5611 Author: wladich Title: #5611: ipatests: add test for kdcproxy handling reply split to several TCP packets Action: opened
PR body: """ This is a regression test for the bug in python-kdcproxy mentioned in https://github.com/latchset/kdcproxy/pull/44 When the reply from AD is split into several TCP packets the kdc proxy software cannot handle it and returns a false error message indicating it cannot contact the KDC server. This could be observed as login failures of AD user on IPA clients when: * IPA client was configured to use kdcproxy to communicate with AD * kdcproxy used TCP to communicate with AD * response from AD to kdcproxy was split into several packets This patch also refactors and improves existing tests: * switch to using pytest fixtures for test setup and cleanup steps to make them isolated and reusable * simulate a much more restricted network environment: instead of blocking single 88 port we now block all outgoing traffic except few essential ports * add basic tests for using kdcproxy to communicate between IPA client and AD DC. """ To pull the PR as Git branch: git remote add ghfreeipa https://github.com/freeipa/freeipa git fetch ghfreeipa pull/5611/head:pr5611 git checkout pr5611
From a27a22fe3f747e5b5164880df3ec55b7a92daacf Mon Sep 17 00:00:00 2001 From: Sergey Orlov <sor...@redhat.com> Date: Fri, 5 Mar 2021 11:13:33 +0100 Subject: [PATCH 1/4] ipatests: return result of kinit_as_user, pass raiseonerr parameter Similar to kinit_admin, this allows to check for error values returned by kinit. --- ipatests/pytest_ipa/integration/tasks.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/ipatests/pytest_ipa/integration/tasks.py b/ipatests/pytest_ipa/integration/tasks.py index 80a5e811dc6..023179a1e06 100755 --- a/ipatests/pytest_ipa/integration/tasks.py +++ b/ipatests/pytest_ipa/integration/tasks.py @@ -2118,7 +2118,7 @@ def run_command_as_user(host, user, command, *args, **kwargs): return host.run_command(command, *args, **kwargs) -def kinit_as_user(host, user, password, krb5_trace=False): +def kinit_as_user(host, user, password, krb5_trace=False, raiseonerr=True): """Launch kinit as user on host. If krb5_trace, then set KRB5_TRACE=/dev/stdout and collect /var/lib/sss/pubconf/kdcinfo.$REALM @@ -2144,9 +2144,14 @@ def kinit_as_user(host, user, password, krb5_trace=False): # Retrieve kdc.$REALM after the password change, just in case SSSD # domain status flipped to online during the password change. get_kdcinfo(host) - assert result.returncode == 0 + if raiseonerr: + assert result.returncode == 0 + return result else: - host.run_command(['kinit', user], stdin_text='{0}\n'.format(password)) + return host.run_command( + ['kinit', user], + stdin_text='{0}\n'.format(password), + raiseonerr=raiseonerr) def get_kdcinfo(host): From 276032fe2e19e1ec0c0a650b482a636d1cf3eec9 Mon Sep 17 00:00:00 2001 From: Sergey Orlov <sor...@redhat.com> Date: Wed, 29 Apr 2020 11:50:28 +0200 Subject: [PATCH 2/4] ipatests: add test for kdcproxy handling reply split to several TCP packets This is a regression test for the bug in python-kdcproxy mentioned in https://github.com/latchset/kdcproxy/pull/44 When the reply from AD is split into several TCP packets the kdc proxy software cannot handle it and returns a false error message indicating it cannot contact the KDC server. This could be observed as login failures of AD user on IPA clients when: * IPA client was configured to use kdcproxy to communicate with AD * kdcproxy used TCP to communicate with AD * response from AD to kdcproxy was split into several packets This patch also refactors and improves existing tests: * switch to using pytest fixtures for test setup and cleanup steps to make them isolated and reusable * simulate a much more restricted network environment: instead of blocking single 88 port we now block all outgoing traffic except few essential ports * add basic tests for using kdcproxy to communicate between IPA client and AD DC. --- .../test_integration/test_http_kdc_proxy.py | 223 +++++++++++++++--- 1 file changed, 188 insertions(+), 35 deletions(-) diff --git a/ipatests/test_integration/test_http_kdc_proxy.py b/ipatests/test_integration/test_http_kdc_proxy.py index aeb19d6fa96..1a06cee78c6 100644 --- a/ipatests/test_integration/test_http_kdc_proxy.py +++ b/ipatests/test_integration/test_http_kdc_proxy.py @@ -4,53 +4,206 @@ from __future__ import absolute_import -import six +import re +from contextlib import contextmanager + +import pytest + from ipatests.pytest_ipa.integration import tasks from ipatests.pytest_ipa.integration.firewall import Firewall from ipatests.test_integration.base import IntegrationTest from ipaplatform.paths import paths -if six.PY3: - unicode = str - - class TestHttpKdcProxy(IntegrationTest): topology = "line" num_clients = 1 - # Firewall rules without --append/-A, --delete/-D, .. First entry of - # each rule is the chain name, the argument to add or delete the rule - # will be added by the used Firewall method. See firewall.py for more - # information. - fw_rules = [['OUTPUT', '-p', 'tcp', '--dport', '88', '-j', 'DROP'], - ['OUTPUT', '-p', 'udp', '--dport', '88', '-j', 'DROP']] + num_ad_domains = 1 @classmethod def install(cls, mh): - super(TestHttpKdcProxy, cls).install(mh) - # Block access from client to master's port 88 - Firewall(cls.clients[0]).prepend_passthrough_rules(cls.fw_rules) - # configure client - cls.clients[0].run_command( - r"sed -i 's/ kdc = .*$/ kdc = https:\/\/%s\/KdcProxy/' %s" % ( - cls.master.hostname, paths.KRB5_CONF) - ) - cls.clients[0].run_command( - r"sed -i 's/master_kdc = .*$/master_kdc" - r" = https:\/\/%s\/KdcProxy/' %s" % ( - cls.master.hostname, paths.KRB5_CONF) - ) - # Workaround for https://fedorahosted.org/freeipa/ticket/6443 - cls.clients[0].run_command(['systemctl', 'restart', 'sssd.service']) - # End of workaround + super().install(mh) + + cls.client = cls.clients[0] + cls.ad = cls.ads[0] + + tasks.install_adtrust(cls.master) + tasks.configure_dns_for_trust(cls.master, cls.ad) + tasks.establish_trust_with_ad(cls.master, cls.ad.domain.name) @classmethod def uninstall(cls, mh): - super(TestHttpKdcProxy, cls).uninstall(mh) - Firewall(cls.clients[0]).remove_passthrough_rules(cls.fw_rules) - - def test_http_kdc_proxy_works(self): - result = tasks.kinit_admin(self.clients[0], raiseonerr=False) - assert(result.returncode == 0), ( - "Unable to kinit using KdcProxy: %s" % result.stderr_text - ) + tasks.remove_trust_info_from_ad( + cls.master, cls.ad.domain.name, cls.ad.hostname) + super().uninstall(mh) + + @pytest.fixture(scope='class') + def users(self, mh): + ad = mh.ads[0] + users = { + 'ipa': { + 'name': 'ipa_test_user', + 'password': 'SecretIpaTestUser', + 'domain': mh.master.domain + }, + 'ad': { + 'name': 'testuser@{}'.format(ad.domain.realm), + 'password': 'Secret123', + 'domain': ad.domain + } + } + tasks.kinit_admin(mh.master) + tasks.create_active_user( + mh.master, users['ipa']['name'], users['ipa']['password']) + yield users + tasks.kinit_admin(mh.master) + mh.master.run_command(['ipa', 'user-del', users['ipa']['name']]) + + @pytest.fixture() + def restrict_network_for_client(self, mh): + fw_rules_allow = [ + ['OUTPUT', '-p', 'udp', '--dport', '53', '-j', 'ACCEPT'], + ['OUTPUT', '-p', 'tcp', '--dport', '80', '-j', 'ACCEPT'], + ['OUTPUT', '-p', 'tcp', '--dport', '443', '-j', 'ACCEPT'], + ['OUTPUT', '-p', 'tcp', '--sport', '22', '-j', 'ACCEPT']] + fw = Firewall(self.client) + fw.prepend_passthrough_rules(fw_rules_allow) + fw.passthrough_rule(['-P', 'OUTPUT', 'DROP']) + yield + fw.passthrough_rule(['-P', 'OUTPUT', 'ACCEPT']) + fw.remove_passthrough_rules(fw_rules_allow) + + @pytest.fixture() + def client_use_kdcproxy(self, mh): + """Configure client for using kdcproxy for IPA and AD domains.""" + krb5conf_backup = tasks.FileBackup(self.client, paths.KRB5_CONF) + krb5conf = self.client.get_file_contents( + paths.KRB5_CONF, encoding='utf-8') + kdc_url = 'https://{}/KdcProxy'.format(self.master.hostname) + kdc_option = 'kdc = {}'.format(kdc_url) + + # configure kdc proxy for IPA realm + krb5conf, n = re.subn(r' kdc = .+', kdc_option, krb5conf) + assert n == 1 + + # configure kdc proxy for Windows AD realm + ad_realm_config = ''' + {realm} = {{ + {kdc} + }} + '''.format(realm=self.ad.domain.realm, kdc=kdc_option) + krb5conf, n = re.subn( + r'\[realms\]', '[realms]' + ad_realm_config, krb5conf) + assert n == 1 + + self.client.put_file_contents(paths.KRB5_CONF, krb5conf) + self.client.run_command(['systemctl', 'restart', 'sssd.service']) + yield + krb5conf_backup.restore() + self.client.run_command(['systemctl', 'restart', 'sssd.service']) + + @contextmanager + def configure_kdc_proxy_for_ad_trust(self, use_tcp): + backup = tasks.FileBackup(self.master, paths.KDCPROXY_CONFIG) + with tasks.remote_ini_file(self.master, paths.KDCPROXY_CONFIG) as conf: + conf.set('global', 'use_dns', 'true') + conf.set('global', 'configs', 'mit') + if use_tcp: + conf.add_section(self.ad.domain.realm) + conf.set(self.ad.domain.realm, 'kerberos', + 'kerberos+tcp://{}:88'.format(self.ad.hostname)) + conf.set(self.ad.domain.realm, 'kpasswd', + 'kpasswd+tcp://{}:464'.format(self.ad.hostname)) + try: + self.master.run_command(['ipactl', 'restart']) + yield + finally: + backup.restore() + self.master.run_command(['ipactl', 'restart']) + + @pytest.mark.parametrize('user_origin', ['ipa', 'ad']) + def test_user_login_on_client_without_firewall(self, users, user_origin): + """Basic check for test setup.""" + tasks.clear_sssd_cache(self.master) + user = users[user_origin] + tasks.kinit_as_user(self.client, user['name'], user['password']) + + @pytest.mark.usefixtures('restrict_network_for_client') + @pytest.mark.parametrize('user_origin', ['ipa', 'ad']) + def test_access_blocked_on_client_without_kdcproxy( + self, users, user_origin): + """Check for test firewall setup.""" + tasks.clear_sssd_cache(self.master) + user = users[user_origin] + result = tasks.kinit_as_user( + self.client, user['name'], user['password'], raiseonerr=False) + expected_error = ( + "Cannot contact any KDC for realm '{}' while getting initial " + "credentials".format(user['domain'].realm)) + assert result.returncode == 1 and expected_error in result.stderr_text + + @pytest.mark.usefixtures('restrict_network_for_client', + 'client_use_kdcproxy') + def test_ipa_user_login_on_client_with_kdcproxy(self, users): + tasks.clear_sssd_cache(self.master) + user = users['ipa'] + tasks.kinit_as_user(self.client, user['name'], user['password']) + + @pytest.mark.usefixtures('restrict_network_for_client', + 'client_use_kdcproxy') + @pytest.mark.parametrize('use_tcp', [True, False]) + def test_ad_user_login_on_client_with_kdcproxy(self, users, use_tcp): + tasks.clear_sssd_cache(self.master) + user = users['ad'] + with self.configure_kdc_proxy_for_ad_trust(use_tcp): + tasks.kinit_as_user(self.client, user['name'], user['password']) + + @pytest.fixture() + def windows_small_mtu_size(self, mh): + new_mtu = 70 + + def get_iface_name(): + result = self.ad.run_command([ + 'powershell', '-c', + '(Get-NetIPAddress -IPAddress {}).InterfaceAlias'.format( + self.ad.ip)]) + return result.stdout_text.strip() + + def get_mtu(iface_name): + result = self.ad.run_command([ + 'netsh', 'interface', 'ipv4', 'show', 'subinterface', + iface_name]) + mtu = result.stdout_text.strip().splitlines()[-1].split()[0] + return int(mtu) + + def set_mtu(iface_name, mtu): + self.ad.run_command([ + 'netsh', 'interface', 'ipv4', 'set', 'subinterface', + iface_name, 'mtu={}'.format(mtu)]) + + iface_name = get_iface_name() + original_mtu = get_mtu(iface_name) + set_mtu(iface_name, new_mtu) + # `netsh` does not report failures with return code so we check + # it was successful by inspecting the actual value of MTU + assert get_mtu(iface_name) == new_mtu + yield + set_mtu(iface_name, original_mtu) + assert get_mtu(iface_name) == original_mtu + + @pytest.mark.usefixtures('restrict_network_for_client', + 'client_use_kdcproxy', + 'windows_small_mtu_size') + def test_kdcproxy_handles_small_packets_from_ad(self, users): + """Check that kdcproxy handles AD response split to several TCP packets + + This is a regression test for the bug in python-kdcproxy: + https://github.com/latchset/kdcproxy/pull/44 + When the reply from AD is split into several TCP packets the kdc + proxy software cannot handle it and returns a false error message + indicating it cannot contact the KDC server. + """ + tasks.clear_sssd_cache(self.master) + user = users['ad'] + with self.configure_kdc_proxy_for_ad_trust(use_tcp=True): + tasks.kinit_as_user(self.client, user['name'], user['password']) From ac416bee9d2452d0b2484af8943fc98237740200 Mon Sep 17 00:00:00 2001 From: Sergey Orlov <sor...@redhat.com> Date: Wed, 29 Apr 2020 12:57:05 +0200 Subject: [PATCH 3/4] ipatests: update prci definitions for test_http_kdc_proxy the new tests require an AD instance --- ipatests/prci_definitions/nightly_latest.yaml | 6 +++--- ipatests/prci_definitions/nightly_latest_selinux.yaml | 6 +++--- ipatests/prci_definitions/nightly_latest_testing.yaml | 6 +++--- .../prci_definitions/nightly_latest_testing_selinux.yaml | 6 +++--- ipatests/prci_definitions/nightly_previous.yaml | 6 +++--- ipatests/prci_definitions/nightly_rawhide.yaml | 6 +++--- 6 files changed, 18 insertions(+), 18 deletions(-) diff --git a/ipatests/prci_definitions/nightly_latest.yaml b/ipatests/prci_definitions/nightly_latest.yaml index 2678520c6b1..3bba0667e47 100644 --- a/ipatests/prci_definitions/nightly_latest.yaml +++ b/ipatests/prci_definitions/nightly_latest.yaml @@ -155,13 +155,13 @@ jobs: requires: [fedora-latest/build] priority: 50 job: - class: RunPytest + class: RunADTests args: build_url: '{fedora-latest/build_url}' test_suite: test_integration/test_http_kdc_proxy.py template: *ci-master-latest - timeout: 3600 - topology: *master_1repl_1client + timeout: 7200 + topology: *ad_master_2client fedora-latest/test_fips: requires: [fedora-latest/build] diff --git a/ipatests/prci_definitions/nightly_latest_selinux.yaml b/ipatests/prci_definitions/nightly_latest_selinux.yaml index e6e26c4051b..1759d6404fb 100644 --- a/ipatests/prci_definitions/nightly_latest_selinux.yaml +++ b/ipatests/prci_definitions/nightly_latest_selinux.yaml @@ -163,14 +163,14 @@ jobs: requires: [fedora-latest/build] priority: 50 job: - class: RunPytest + class: RunADTests args: build_url: '{fedora-latest/build_url}' selinux_enforcing: True test_suite: test_integration/test_http_kdc_proxy.py template: *ci-master-latest - timeout: 3600 - topology: *master_1repl_1client + timeout: 7200 + topology: *ad_master_2client fedora-latest/test_fips: requires: [fedora-latest/build] diff --git a/ipatests/prci_definitions/nightly_latest_testing.yaml b/ipatests/prci_definitions/nightly_latest_testing.yaml index 94c2ec1e88a..7b4bed1bf54 100644 --- a/ipatests/prci_definitions/nightly_latest_testing.yaml +++ b/ipatests/prci_definitions/nightly_latest_testing.yaml @@ -163,14 +163,14 @@ jobs: requires: [testing-fedora/build] priority: 50 job: - class: RunPytest + class: RunADTests args: build_url: '{testing-fedora/build_url}' update_packages: True test_suite: test_integration/test_http_kdc_proxy.py template: *testing-master-latest - timeout: 3600 - topology: *master_1repl_1client + timeout: 7200 + topology: *ad_master_2client testing-fedora/test_fips: requires: [testing-fedora/build] diff --git a/ipatests/prci_definitions/nightly_latest_testing_selinux.yaml b/ipatests/prci_definitions/nightly_latest_testing_selinux.yaml index 0bc1047df1d..538a4ce0d1e 100644 --- a/ipatests/prci_definitions/nightly_latest_testing_selinux.yaml +++ b/ipatests/prci_definitions/nightly_latest_testing_selinux.yaml @@ -171,15 +171,15 @@ jobs: requires: [testing-fedora/build] priority: 50 job: - class: RunPytest + class: RunADTests args: build_url: '{testing-fedora/build_url}' update_packages: True selinux_enforcing: True test_suite: test_integration/test_http_kdc_proxy.py template: *testing-master-latest - timeout: 3600 - topology: *master_1repl_1client + timeout: 7200 + topology: *ad_master_2client testing-fedora/test_fips: requires: [testing-fedora/build] diff --git a/ipatests/prci_definitions/nightly_previous.yaml b/ipatests/prci_definitions/nightly_previous.yaml index 7a170e3066a..122d4dec82c 100644 --- a/ipatests/prci_definitions/nightly_previous.yaml +++ b/ipatests/prci_definitions/nightly_previous.yaml @@ -155,13 +155,13 @@ jobs: requires: [fedora-previous/build] priority: 50 job: - class: RunPytest + class: RunADTests args: build_url: '{fedora-previous/build_url}' test_suite: test_integration/test_http_kdc_proxy.py template: *ci-master-previous - timeout: 3600 - topology: *master_1repl_1client + timeout: 7200 + topology: *ad_master_2client fedora-previous/test_fips: requires: [fedora-previous/build] diff --git a/ipatests/prci_definitions/nightly_rawhide.yaml b/ipatests/prci_definitions/nightly_rawhide.yaml index f2f2d6f6d10..8fff07f2c0f 100644 --- a/ipatests/prci_definitions/nightly_rawhide.yaml +++ b/ipatests/prci_definitions/nightly_rawhide.yaml @@ -163,14 +163,14 @@ jobs: requires: [fedora-rawhide/build] priority: 50 job: - class: RunPytest + class: RunADTests args: build_url: '{fedora-rawhide/build_url}' update_packages: True test_suite: test_integration/test_http_kdc_proxy.py template: *ci-master-frawhide - timeout: 3600 - topology: *master_1repl_1client + timeout: 7200 + topology: *ad_master_2client fedora-rawhide/test_fips: requires: [fedora-rawhide/build] From 9895eebaaff31641ae3eb55978464a7040df5e26 Mon Sep 17 00:00:00 2001 From: Sergey Orlov <sor...@redhat.com> Date: Fri, 5 Mar 2021 12:40:41 +0100 Subject: [PATCH 4/4] temp commit --- .freeipa-pr-ci.yaml | 2 +- ipatests/prci_definitions/temp_commit.yaml | 37 +++++++++++++++++++--- 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/.freeipa-pr-ci.yaml b/.freeipa-pr-ci.yaml index abcf8c5b634..80656690080 120000 --- a/.freeipa-pr-ci.yaml +++ b/.freeipa-pr-ci.yaml @@ -1 +1 @@ -ipatests/prci_definitions/gating.yaml \ No newline at end of file +ipatests/prci_definitions/temp_commit.yaml \ No newline at end of file diff --git a/ipatests/prci_definitions/temp_commit.yaml b/ipatests/prci_definitions/temp_commit.yaml index 8f8a357acd1..ade2a3e1a20 100644 --- a/ipatests/prci_definitions/temp_commit.yaml +++ b/ipatests/prci_definitions/temp_commit.yaml @@ -61,14 +61,41 @@ jobs: timeout: 1800 topology: *build - fedora-latest/temp_commit: + fedora-latest/test_http_kdc_proxy: requires: [fedora-latest/build] priority: 50 job: - class: RunPytest + class: RunADTests args: build_url: '{fedora-latest/build_url}' - test_suite: test_integration/test_REPLACEME.py + test_suite: test_integration/test_http_kdc_proxy.py template: *ci-master-latest - timeout: 3600 - topology: *master_1repl_1client + timeout: 7200 + topology: *ad_master_2client + + + fedora-latest-33/build: + requires: [] + priority: 100 + job: + class: Build + args: + git_repo: '{git_repo}' + git_refspec: '{git_refspec}' + template: &ci-master-latest + name: freeipa/ci-master-f33 + version: 0.0.6 + timeout: 1800 + topology: *build + + fedora-latest-33/test_http_kdc_proxy: + requires: [fedora-latest-33/build] + priority: 50 + job: + class: RunADTests + args: + build_url: '{fedora-latest-33/build_url}' + test_suite: test_integration/test_http_kdc_proxy.py + template: *ci-master-latest + timeout: 7200 + topology: *ad_master_2client
_______________________________________________ FreeIPA-devel mailing list -- freeipa-devel@lists.fedorahosted.org To unsubscribe send an email to freeipa-devel-le...@lists.fedorahosted.org Fedora Code of Conduct: https://docs.fedoraproject.org/en-US/project/code-of-conduct/ List Guidelines: https://fedoraproject.org/wiki/Mailing_list_guidelines List Archives: https://lists.fedorahosted.org/archives/list/freeipa-devel@lists.fedorahosted.org Do not reply to spam on the list, report it: https://pagure.io/fedora-infrastructure