ipa/0097-Add-test-for-master-key-upgrade.patch
Florence Blanc-Renaud a96d03c543 ipa-4.12.2-19
- Resolves: RHEL-100450 eDNS: multiple issues during encrypted DNS setup
- Resolves: RHEL-89907 Privilege escalation from host to domain admin in FreeIPA
- Resolves: RHEL-99315 Include latest fixes in python3-ipatests package
- Resolves: RHEL-98565 ipa-idrange-fix: 'Env' object has no attribute 'basedn'
- Resolves: RHEL-96920 Nightly test failure (rawhide) in test_trust.py::TestTrust::test_server_option_with_unreachable_ad
- Resolves: RHEL-31907 kdb: support storing and retrieving multiple master keys

Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
2025-06-30 11:07:39 +02:00

332 lines
13 KiB
Diff

From b69616ed48224b48b5615044314787f69af13c13 Mon Sep 17 00:00:00 2001
From: Julien Rische <jrische@redhat.com>
Date: Wed, 12 Mar 2025 13:50:46 +0100
Subject: [PATCH] Add test for master key upgrade
Since commit 4ed7da378940198cf4415f86d4eb013de6ac6455 in MIT krb5,
kdb5_util sets the required bits on the modification mask of updated
principal entries to enable ipa-kdb to switch the active master key.
This commit creates a "test_fedora_legacy" ipaplatform where the AES
HMAC-SHA2 encryption types are not enabled. "test_mkey_upgrade" uses
this platform to initialize a domain with an aes256-cts-hmac-sha1-96
master key, and test its upgrade to aes256-cts-hmac-sha384-192.
Some parts of the test infrastructure had to be made aware of this new
platform (e.g. firewall integration).
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
---
ipaplatform/setup.py | 1 +
ipaplatform/test_fedora_legacy/__init__.py | 7 +
ipaplatform/test_fedora_legacy/constants.py | 16 +++
ipaplatform/test_fedora_legacy/paths.py | 13 ++
ipaplatform/test_fedora_legacy/services.py | 27 ++++
ipaplatform/test_fedora_legacy/tasks.py | 35 +++++
ipatests/pytest_ipa/integration/firewall.py | 1 +
.../test_integration/test_mkey_upgrade.py | 135 ++++++++++++++++++
8 files changed, 235 insertions(+)
create mode 100644 ipaplatform/test_fedora_legacy/__init__.py
create mode 100644 ipaplatform/test_fedora_legacy/constants.py
create mode 100644 ipaplatform/test_fedora_legacy/paths.py
create mode 100644 ipaplatform/test_fedora_legacy/services.py
create mode 100644 ipaplatform/test_fedora_legacy/tasks.py
create mode 100644 ipatests/test_integration/test_mkey_upgrade.py
diff --git a/ipaplatform/setup.py b/ipaplatform/setup.py
index 086a99d1fa602551d2ff3270113608aa0fbc026c..1d6df1eff62b434162a7ce815e8273241d424c78 100644
--- a/ipaplatform/setup.py
+++ b/ipaplatform/setup.py
@@ -42,6 +42,7 @@ if __name__ == '__main__':
"ipaplatform.rhel",
"ipaplatform.rhel_container",
"ipaplatform.suse",
+ "ipaplatform.test_fedora_legacy",
"ipaplatform.opencloudos",
"ipaplatform.tencentos"
],
diff --git a/ipaplatform/test_fedora_legacy/__init__.py b/ipaplatform/test_fedora_legacy/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..a4f4cff5a7084d5d1697aefbe2bebcfb89e0a694
--- /dev/null
+++ b/ipaplatform/test_fedora_legacy/__init__.py
@@ -0,0 +1,7 @@
+#
+# Copyright (C) 2025 FreeIPA Contributors see COPYING for license
+#
+"""
+This module contains Fedora AES HMAC-SHA1 master key specific platform files.
+"""
+NAME = 'test_fedora_legacy'
diff --git a/ipaplatform/test_fedora_legacy/constants.py b/ipaplatform/test_fedora_legacy/constants.py
new file mode 100644
index 0000000000000000000000000000000000000000..0cebac86a9eea45803af669d8237dffd34675d74
--- /dev/null
+++ b/ipaplatform/test_fedora_legacy/constants.py
@@ -0,0 +1,16 @@
+#
+# Copyright (C) 2025 FreeIPA Contributors see COPYING for license
+#
+"""Fedora AES HMAC-SHA1 master key constants
+"""
+from ipaplatform.fedora.constants import FedoraConstantsNamespace, User, Group
+
+
+__all__ = ("constants", "User", "Group")
+
+
+class TestFedoraLegacyConstantsNamespace(FedoraConstantsNamespace):
+ pass
+
+
+constants = TestFedoraLegacyConstantsNamespace()
diff --git a/ipaplatform/test_fedora_legacy/paths.py b/ipaplatform/test_fedora_legacy/paths.py
new file mode 100644
index 0000000000000000000000000000000000000000..71fb8acdc9143a4daa14a0b0dc7cf46fe5fdc2ce
--- /dev/null
+++ b/ipaplatform/test_fedora_legacy/paths.py
@@ -0,0 +1,13 @@
+#
+# Copyright (C) 2025 FreeIPA Contributors see COPYING for license
+#
+"""Fedora AES HMAC-SHA1 master key paths
+"""
+from ipaplatform.fedora.paths import FedoraPathNamespace
+
+
+class TestFedoraLegacyPathNamespace(FedoraPathNamespace):
+ pass
+
+
+paths = TestFedoraLegacyPathNamespace()
diff --git a/ipaplatform/test_fedora_legacy/services.py b/ipaplatform/test_fedora_legacy/services.py
new file mode 100644
index 0000000000000000000000000000000000000000..adbe21f82e215349e54b586cdd5201374dd1378e
--- /dev/null
+++ b/ipaplatform/test_fedora_legacy/services.py
@@ -0,0 +1,27 @@
+#
+# Copyright (C) 2025 FreeIPA Contributors see COPYING for license
+#
+"""Fedora AES HMAC-SHA1 master key services
+"""
+from ipaplatform.fedora import services as fedora_services
+
+
+test_fedora_legacy_system_units = fedora_services.fedora_system_units.copy()
+
+
+class TestFedoraLegacyService(fedora_services.FedoraService):
+ system_units = test_fedora_legacy_system_units
+
+
+def test_fedora_legacy_service_class_factory(name, api=None):
+ return fedora_services.fedora_service_class_factory(name, api)
+
+
+class TestFedoraLegacyServices(fedora_services.FedoraServices):
+ def service_class_factory(self, name, api=None):
+ return test_fedora_legacy_service_class_factory(name, api)
+
+
+timedate_services = fedora_services.timedate_services
+service = test_fedora_legacy_service_class_factory
+knownservices = TestFedoraLegacyServices()
diff --git a/ipaplatform/test_fedora_legacy/tasks.py b/ipaplatform/test_fedora_legacy/tasks.py
new file mode 100644
index 0000000000000000000000000000000000000000..e6e72d7237cbc7d2a14b92cbb746bf8d66b197f2
--- /dev/null
+++ b/ipaplatform/test_fedora_legacy/tasks.py
@@ -0,0 +1,35 @@
+#
+# Copyright (C) 2025 FreeIPA Contributors see COPYING for license
+#
+"""Fedora AES HMAC-SHA1 master key tasks
+"""
+from ipaplatform.fedora.tasks import FedoraTaskNamespace
+
+from re import compile
+
+
+def add_aes_sha1(enctypes):
+ return tuple({*enctypes,
+ 'aes256-cts:special', 'aes128-cts:special',
+ 'aes256-cts:normal', 'aes128-cts:normal'})
+
+
+class TestFedoraLegacyTaskNamespace(FedoraTaskNamespace):
+
+ def get_masterkey_enctype(self):
+ return 'aes256-cts'
+
+ def get_supported_enctypes(self):
+ aes_sha2_pattern = compile('^aes[0-9]+-sha2:')
+
+ return tuple(e for e in super().get_supported_enctypes()
+ if not aes_sha2_pattern.match(e))
+
+ def get_removed_supported_enctypes(self):
+ return add_aes_sha1(super().get_removed_supported_enctypes())
+
+ def get_removed_default_enctypes(self):
+ return add_aes_sha1(super().get_removed_default_enctypes())
+
+
+tasks = TestFedoraLegacyTaskNamespace()
diff --git a/ipatests/pytest_ipa/integration/firewall.py b/ipatests/pytest_ipa/integration/firewall.py
index 1a5af5c5e71f71da4c939cddede7cab4817dd3a3..01661b6c6ab84b5ad0f13ebd537b25a2adf0ec1c 100644
--- a/ipatests/pytest_ipa/integration/firewall.py
+++ b/ipatests/pytest_ipa/integration/firewall.py
@@ -239,6 +239,7 @@ class Firewall(FirewallBase):
firewalls = {
'rhel': FirewallD,
'fedora': FirewallD,
+ 'test_fedora_legacy': FirewallD,
'debian': FirewallD,
'ubuntu': FirewallD,
'altlinux': NoOpFirewall,
diff --git a/ipatests/test_integration/test_mkey_upgrade.py b/ipatests/test_integration/test_mkey_upgrade.py
new file mode 100644
index 0000000000000000000000000000000000000000..933623fbfc913ed6efe5409b3a35af1524305c8e
--- /dev/null
+++ b/ipatests/test_integration/test_mkey_upgrade.py
@@ -0,0 +1,135 @@
+#
+# Copyright (C) 2015 FreeIPA Contributors see COPYING for license
+#
+
+import re
+import textwrap
+
+from ipatests.pytest_ipa.integration import tasks
+from ipatests.test_integration.base import IntegrationTest
+
+
+class TestMkeyUpgrade(IntegrationTest):
+
+ num_replicas = 1
+ topology = 'line'
+
+ @classmethod
+ def install(cls, mh):
+ cls.master.put_file_contents(
+ '/etc/profile.d/ipaplatform.sh',
+ 'export IPAPLATFORM_OVERRIDE=test_fedora_legacy')
+ cls.master.run_command(['mkdir', '/etc/systemd/system/ipa.service.d'])
+ cls.master.put_file_contents(
+ '/etc/systemd/system/ipa.service.d/platform.conf',
+ '[Service]\n'
+ 'Environment="IPAPLATFORM_OVERRIDE=test_fedora_legacy"')
+ cls.master.run_command(['systemctl', 'daemon-reload'])
+ tasks.install_master(cls.master, setup_dns=False)
+ tasks.install_replica(cls.master, cls.replicas[0], setup_dns=False)
+
+ def test_old_active_mkey(self):
+ p = re.compile('^KVNO: 1, Enctype: aes256-cts-hmac-sha1-96, .+ \\*$',
+ flags=re.MULTILINE)
+
+ result = self.master.run_command(['kdb5_util', 'list_mkeys'])
+ assert p.search(result.stdout_text)
+ result = self.replicas[0].run_command(['kdb5_util', 'list_mkeys'])
+ assert p.search(result.stdout_text)
+
+ def test_enable_new_entypes(self):
+ base_dn = "dc=%s" % (",dc=".join(self.master.domain.name.split(".")))
+ realm = self.master.domain.name.upper()
+
+ entry_ldif = textwrap.dedent("""
+ dn: cn={realm},cn=kerberos,{base_dn}
+ changetype: modify
+ replace: krbSupportedEncSaltTypes
+ krbSupportedEncSaltTypes: aes128-sha2:normal
+ krbSupportedEncSaltTypes: aes128-sha2:special
+ krbSupportedEncSaltTypes: aes256-sha2:normal
+ krbSupportedEncSaltTypes: aes256-sha2:special
+ krbSupportedEncSaltTypes: aes256-cts:normal
+ krbSupportedEncSaltTypes: aes256-cts:special
+ krbSupportedEncSaltTypes: aes128-cts:normal
+ krbSupportedEncSaltTypes: aes128-cts:special
+ krbSupportedEncSaltTypes: camellia128-cts-cmac:normal
+ krbSupportedEncSaltTypes: camellia128-cts-cmac:special
+ krbSupportedEncSaltTypes: camellia256-cts-cmac:normal
+ krbSupportedEncSaltTypes: camellia256-cts-cmac:special
+ -
+ replace: krbDefaultEncSaltTypes
+ krbDefaultEncSaltTypes: aes256-sha2:special
+ krbDefaultEncSaltTypes: aes128-sha2:special
+ krbDefaultEncSaltTypes: aes256-cts:special
+ krbDefaultEncSaltTypes: aes128-cts:special""").format(
+ base_dn=base_dn, realm=realm)
+ tasks.ldapmodify_dm(self.master, entry_ldif)
+
+ def test_add_new_mkey(self):
+ self.master.run_command('kdb5_util add_mkey -e aes256-sha2 -s',
+ stdin_text='Secret123\nSecret123')
+
+ def test_new_inactive_mkey(self):
+ p = re.compile('^KVNO: 2, Enctype: aes256-cts-hmac-sha384-192, ',
+ flags=re.MULTILINE)
+
+ result = self.master.run_command(['kdb5_util', 'list_mkeys'])
+ assert p.search(result.stdout_text)
+ result = self.replicas[0].run_command(['kdb5_util', 'list_mkeys'])
+ assert p.search(result.stdout_text)
+
+ def test_switch_mkey(self):
+ self.master.run_command(['kdb5_util', 'use_mkey', '2'])
+
+ def test_new_active_mkey(self):
+ p = re.compile('^KVNO: 2, Enctype: aes256-cts-hmac-sha384-192, .+ \\*$',
+ flags=re.MULTILINE)
+
+ result = self.master.run_command(['kdb5_util', 'list_mkeys'])
+ assert p.search(result.stdout_text)
+ result = self.replicas[0].run_command(['kdb5_util', 'list_mkeys'])
+ assert p.search(result.stdout_text)
+
+ def test_used_old_mkey(self):
+ p = re.compile('^MKey: vno 1$', flags=re.MULTILINE)
+
+ result = self.master.run_command(['kadmin.local', 'getprinc',
+ f'ldap/{self.master.hostname}'])
+ assert p.search(result.stdout_text)
+ result = self.replicas[0].run_command(['kadmin.local', 'getprinc',
+ f'ldap/{self.master.hostname}'])
+ assert p.search(result.stdout_text)
+
+ def test_reencrypt_with_new_mkey(self):
+ self.master.run_command(['kdb5_util', '-x', 'unlockiter',
+ 'update_princ_encryption', '-vf'])
+
+ def test_used_new_mkey(self):
+ p = re.compile('^MKey: vno 2$', flags=re.MULTILINE)
+
+ result = self.master.run_command(['kadmin.local', 'getprinc',
+ f'ldap/{self.master.hostname}'])
+ assert p.search(result.stdout_text)
+ result = self.replicas[0].run_command(['kadmin.local', 'getprinc',
+ f'ldap/{self.master.hostname}'])
+ assert p.search(result.stdout_text)
+
+ def test_purge_old_mkey(self):
+ self.master.run_command(['kdb5_util', 'purge_mkeys', '-vf'])
+
+ def test_only_new_mkey(self):
+ p = re.compile('^KVNO: 1,', flags=re.MULTILINE)
+
+ result = self.master.run_command(['kdb5_util', 'list_mkeys'])
+ assert not p.search(result.stdout_text)
+ result = self.replicas[0].run_command(['kdb5_util', 'list_mkeys'])
+ assert not p.search(result.stdout_text)
+
+ @classmethod
+ def uninstall(cls, mh):
+ cls.master.run_command([
+ 'rm', '/etc/profile.d/ipaplatform.sh',
+ '/etc/systemd/system/ipa.service.d/platform.conf'])
+ cls.master.run_command(['rmdir', '/etc/systemd/system/ipa.service.d'])
+ super().uninstall(mh)
--
2.50.0