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

Reply via email to