diff --git a/0002-Add-iparepltopoconf-objectclass-to-topology-permissi.patch b/0002-Add-iparepltopoconf-objectclass-to-topology-permissi.patch new file mode 100644 index 0000000..373c903 --- /dev/null +++ b/0002-Add-iparepltopoconf-objectclass-to-topology-permissi.patch @@ -0,0 +1,79 @@ +From ebccaac3cf8a5688739d76426924469d5b4df6b1 Mon Sep 17 00:00:00 2001 +From: Rob Crittenden +Date: Mon, 10 Jun 2024 14:54:41 -0400 +Subject: [PATCH] Add iparepltopoconf objectclass to topology permissions + +The domain and ca objects were unreadable which caused +the conneciton lines between nodes in the UI to not be +visible. + +Also add a manual ACI to allow reading the min/max +domain level. + +Fixes: https://pagure.io/freeipa/issue/9594 + +Signed-off-by: Rob Crittenden +Reviewed-By: Michal Polovka +--- + ACI.txt | 8 ++++---- + install/updates/40-replication.update | 11 +++++++++++ + ipaserver/plugins/topology.py | 2 +- + 3 files changed, 16 insertions(+), 5 deletions(-) + +diff --git a/ACI.txt b/ACI.txt +index 13b0a64bde6b29503b048630f1c718e5e30759b2..50c8824d43cd6d3ca9a381b5d34425cb0197508c 100644 +--- a/ACI.txt ++++ b/ACI.txt +@@ -375,13 +375,13 @@ aci: (targetattr = "cmdcategory || cn || createtimestamp || description || entry + dn: dc=ipa,dc=example + aci: (targetattr = "cn || createtimestamp || description || entryusn || modifytimestamp || objectclass || ou || sudocommand || sudohost || sudonotafter || sudonotbefore || sudooption || sudoorder || sudorunas || sudorunasgroup || sudorunasuser || sudouser")(target = "ldap:///ou=sudoers,dc=ipa,dc=example")(version 3.0;acl "permission:System: Read Sudoers compat tree";allow (compare,read,search) userdn = "ldap:///anyone";) + dn: cn=topology,cn=ipa,cn=etc,dc=ipa,dc=example +-aci: (targetfilter = "(objectclass=iparepltoposegment)")(version 3.0;acl "permission:System: Add Topology Segments";allow (add) groupdn = "ldap:///cn=System: Add Topology Segments,cn=permissions,cn=pbac,dc=ipa,dc=example";) ++aci: (targetfilter = "(|(objectclass=iparepltopoconf)(objectclass=iparepltoposegment))")(version 3.0;acl "permission:System: Add Topology Segments";allow (add) groupdn = "ldap:///cn=System: Add Topology Segments,cn=permissions,cn=pbac,dc=ipa,dc=example";) + dn: cn=topology,cn=ipa,cn=etc,dc=ipa,dc=example +-aci: (targetattr = "iparepltoposegmentdirection || iparepltoposegmentleftnode || iparepltoposegmentrightnode || nsds5replicastripattrs || nsds5replicatedattributelist || nsds5replicatedattributelisttotal")(targetfilter = "(objectclass=iparepltoposegment)")(version 3.0;acl "permission:System: Modify Topology Segments";allow (write) groupdn = "ldap:///cn=System: Modify Topology Segments,cn=permissions,cn=pbac,dc=ipa,dc=example";) ++aci: (targetattr = "iparepltoposegmentdirection || iparepltoposegmentleftnode || iparepltoposegmentrightnode || nsds5replicastripattrs || nsds5replicatedattributelist || nsds5replicatedattributelisttotal")(targetfilter = "(|(objectclass=iparepltopoconf)(objectclass=iparepltoposegment))")(version 3.0;acl "permission:System: Modify Topology Segments";allow (write) groupdn = "ldap:///cn=System: Modify Topology Segments,cn=permissions,cn=pbac,dc=ipa,dc=example";) + dn: cn=topology,cn=ipa,cn=etc,dc=ipa,dc=example +-aci: (targetattr = "cn || createtimestamp || entryusn || iparepltopoconfroot || iparepltoposegmentdirection || iparepltoposegmentleftnode || iparepltoposegmentrightnode || iparepltoposegmentstatus || modifytimestamp || nsds5replicastripattrs || nsds5replicatedattributelist || nsds5replicatedattributelisttotal || objectclass")(targetfilter = "(objectclass=iparepltoposegment)")(version 3.0;acl "permission:System: Read Topology Segments";allow (compare,read,search) groupdn = "ldap:///cn=System: Read Topology Segments,cn=permissions,cn=pbac,dc=ipa,dc=example";) ++aci: (targetattr = "cn || createtimestamp || entryusn || iparepltopoconfroot || iparepltoposegmentdirection || iparepltoposegmentleftnode || iparepltoposegmentrightnode || iparepltoposegmentstatus || modifytimestamp || nsds5replicastripattrs || nsds5replicatedattributelist || nsds5replicatedattributelisttotal || objectclass")(targetfilter = "(|(objectclass=iparepltopoconf)(objectclass=iparepltoposegment))")(version 3.0;acl "permission:System: Read Topology Segments";allow (compare,read,search) groupdn = "ldap:///cn=System: Read Topology Segments,cn=permissions,cn=pbac,dc=ipa,dc=example";) + dn: cn=topology,cn=ipa,cn=etc,dc=ipa,dc=example +-aci: (targetfilter = "(objectclass=iparepltoposegment)")(version 3.0;acl "permission:System: Remove Topology Segments";allow (delete) groupdn = "ldap:///cn=System: Remove Topology Segments,cn=permissions,cn=pbac,dc=ipa,dc=example";) ++aci: (targetfilter = "(|(objectclass=iparepltopoconf)(objectclass=iparepltoposegment))")(version 3.0;acl "permission:System: Remove Topology Segments";allow (delete) groupdn = "ldap:///cn=System: Remove Topology Segments,cn=permissions,cn=pbac,dc=ipa,dc=example";) + dn: cn=trusts,dc=ipa,dc=example + aci: (targetattr = "cn || createtimestamp || entryusn || ipantadditionalsuffixes || ipantflatname || ipantsecurityidentifier || ipantsidblacklistincoming || ipantsidblacklistoutgoing || ipanttrustdirection || ipanttrusteddomainsid || ipanttrustpartner || modifytimestamp || objectclass")(version 3.0;acl "permission:System: Read Trust Information";allow (compare,read,search) userdn = "ldap:///all";) + dn: cn=trusts,dc=ipa,dc=example +diff --git a/install/updates/40-replication.update b/install/updates/40-replication.update +index 06b6613ed4c9ede935f879ee46ed5e7d5a0935ba..6dc38e36d96b4e019eb35f9d0367bfc7a202af98 100644 +--- a/install/updates/40-replication.update ++++ b/install/updates/40-replication.update +@@ -28,3 +28,14 @@ default:member: cn=Replication Administrators,cn=privileges,cn=pbac,$SUFFIX + dn: cn=Posix IDs,cn=Distributed Numeric Assignment Plugin,cn=plugins,cn=config + remove:aci: (targetattr=cn || dnaMaxValue || dnaNextRange || dnaNextValue || dnaThreshold || dnaType || objectclass)(version 3.0;acl "permission:Read DNA Range";allow (read, search, compare) groupdn = "ldap:///cn=Read DNA Range,cn=permissions,cn=pbac,$SUFFIX";) + add:aci: (targetattr = "cn || dnaMaxValue || dnaNextRange || dnaNextValue || dnaThreshold || dnaType || objectclass")(version 3.0;acl "permission:Read DNA Range";allow (read, search, compare) groupdn = "ldap:///cn=Read DNA Range,cn=permissions,cn=pbac,$SUFFIX";) ++ ++dn: cn=Read domain level,cn=permissions,cn=pbac,$SUFFIX ++default:objectClass: top ++default:objectClass: groupofnames ++default:objectClass: ipapermission ++default:cn: Read domain level ++default:ipapermissiontype: SYSTEM ++default:member: cn=Replication Administrators,cn=privileges,cn=pbac,$SUFFIX ++ ++dn: cn=masters,cn=ipa,cn=etc,$SUFFIX ++add:aci: (targetattr = "ipamaxdomainlevel || ipamindomainlevel")(version 3.0;acl "permission:Read domain level";allow (read, search, compare) groupdn = "ldap:///cn=Read domain level,cn=permissions,cn=pbac,$SUFFIX";) +diff --git a/ipaserver/plugins/topology.py b/ipaserver/plugins/topology.py +index be0cf3d705267af66e20fb990b2fed72b61d2c49..1401fe259226c12abe42a5670d3ce1812c27cc05 100644 +--- a/ipaserver/plugins/topology.py ++++ b/ipaserver/plugins/topology.py +@@ -104,7 +104,7 @@ class topologysegment(LDAPObject): + object_name = _('segment') + object_name_plural = _('segments') + object_class = ['iparepltoposegment'] +- permission_filter_objectclasses = ['iparepltoposegment'] ++ permission_filter_objectclasses = ['iparepltoposegment', 'iparepltopoconf'] + default_attributes = [ + 'cn', + 'ipaReplTopoSegmentdirection', 'ipaReplTopoSegmentrightNode', +-- +2.45.2 + diff --git a/0003-ipa-otptoken-import-open-the-key-file-in-binary-mode.patch b/0003-ipa-otptoken-import-open-the-key-file-in-binary-mode.patch new file mode 100644 index 0000000..7e7c943 --- /dev/null +++ b/0003-ipa-otptoken-import-open-the-key-file-in-binary-mode.patch @@ -0,0 +1,35 @@ +From 9de053ef02db8cb63e14edc64ac22ec2d3d7bbc9 Mon Sep 17 00:00:00 2001 +From: Florence Blanc-Renaud +Date: Mon, 17 Jun 2024 17:01:33 +0200 +Subject: [PATCH] ipa-otptoken-import: open the key file in binary mode + +ipa-otptoken-import provides an option (-k KEYFILE) to import +an encrypted PSKC file but this option does not work with python3 +in RHEL8 and above, because the key should be passed in binary +format to the cryptography functions instead of string format. + +Open the keyfile in binary mode to pass the expected format. + +Fixes: https://pagure.io/freeipa/issue/9609 +Signed-off-by: Florence Blanc-Renaud +Reviewed-By: Rob Crittenden +--- + ipaserver/install/ipa_otptoken_import.py | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/ipaserver/install/ipa_otptoken_import.py b/ipaserver/install/ipa_otptoken_import.py +index dbaeacdf6885d3238f2d0294e24a5adad5a5c38d..d3f3d3cfa84e4a4bf57383e0ba543f4543e25c92 100644 +--- a/ipaserver/install/ipa_otptoken_import.py ++++ b/ipaserver/install/ipa_otptoken_import.py +@@ -539,7 +539,7 @@ class OTPTokenImport(admintool.AdminTool): + + # Load the keyfile. + keyfile = self.safe_options.keyfile +- with open(keyfile) as f: ++ with open(keyfile, "rb") as f: + self.doc.setKey(f.read()) + + def run(self): +-- +2.45.2 + diff --git a/0004-spec-file-do-not-create-etc-ssh-ssh_config.orig-if-u.patch b/0004-spec-file-do-not-create-etc-ssh-ssh_config.orig-if-u.patch new file mode 100644 index 0000000..e03537c --- /dev/null +++ b/0004-spec-file-do-not-create-etc-ssh-ssh_config.orig-if-u.patch @@ -0,0 +1,39 @@ +From 09e66dc936cf2d99bcc44d60d6851aafa9ede46a Mon Sep 17 00:00:00 2001 +From: Florence Blanc-Renaud +Date: Wed, 19 Jun 2024 15:38:36 +0200 +Subject: [PATCH] spec file: do not create /etc/ssh/ssh_config.orig if + unchanged + +The upgrade removes the line +HostKeyAlgorithms ssh-rsa,ssh-dss +if present in /etc/ssh/ssh_config and creates a backup in +/etc/ssh/ssh_config.orig, even if no change was applied. + +Create the backup file only if the file was changed. + +Fixes: https://pagure.io/freeipa/issue/9610 + +Signed-off-by: Florence Blanc-Renaud +Reviewed-By: Michal Polovka +--- + freeipa.spec.in | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/freeipa.spec.in b/freeipa.spec.in +index 6803de752bc122bf6e1eafd610d399cde994cad5..1e1a0c04728972c6c53beb47dafb25d7898ab0ea 100755 +--- a/freeipa.spec.in ++++ b/freeipa.spec.in +@@ -1320,7 +1320,9 @@ if [ $1 -gt 1 ] ; then + chmod 0600 /var/log/ipaupgrade.log + SSH_CLIENT_SYSTEM_CONF="/etc/ssh/ssh_config" + if [ -f "$SSH_CLIENT_SYSTEM_CONF" ]; then +- sed -E --in-place=.orig 's/^(HostKeyAlgorithms ssh-rsa,ssh-dss)$/# disabled by ipa-client update\n# \1/' "$SSH_CLIENT_SYSTEM_CONF" ++ if grep -E -q '^HostKeyAlgorithms ssh-rsa,ssh-dss' $SSH_CLIENT_SYSTEM_CONF 2>/dev/null; then ++ sed -E --in-place=.orig 's/^(HostKeyAlgorithms ssh-rsa,ssh-dss)$/# disabled by ipa-client update\n# \1/' "$SSH_CLIENT_SYSTEM_CONF" ++ fi + # https://pagure.io/freeipa/issue/9536 + # replace sss_ssh_knownhostsproxy with sss_ssh_knownhosts + if [ -f '/usr/bin/sss_ssh_knownhosts' ]; then +-- +2.45.2 + diff --git a/0005-ipatests-add-test-for-ticket-9610.patch b/0005-ipatests-add-test-for-ticket-9610.patch new file mode 100644 index 0000000..2bf0ad3 --- /dev/null +++ b/0005-ipatests-add-test-for-ticket-9610.patch @@ -0,0 +1,69 @@ +From 4d51446bd3cd9ab222f9978f8f5def1f3a37fa0e Mon Sep 17 00:00:00 2001 +From: Florence Blanc-Renaud +Date: Thu, 20 Jun 2024 08:13:27 +0200 +Subject: [PATCH] ipatests: add test for ticket 9610 + +Test scenario: +- ensure there is no /etc/ssh/ssh_config.orig file +- force ipa-client package reinstallation +- ensure no backup file is created in /etc/ssh/ssh_config.orig + +Related: https://pagure.io/freeipa/issue/9610 +Signed-off-by: Florence Blanc-Renaud +Reviewed-By: Michal Polovka +--- + ipatests/pytest_ipa/integration/tasks.py | 15 +++++++++++++++ + ipatests/test_integration/test_upgrade.py | 14 ++++++++++++++ + 2 files changed, 29 insertions(+) + +diff --git a/ipatests/pytest_ipa/integration/tasks.py b/ipatests/pytest_ipa/integration/tasks.py +index 6665f361e0880a149ecca8c6f7c3fe1feb1f42d0..9d6b5f67a311a28c335801d59e0ff0f0c7faccdd 100755 +--- a/ipatests/pytest_ipa/integration/tasks.py ++++ b/ipatests/pytest_ipa/integration/tasks.py +@@ -2550,6 +2550,21 @@ def install_packages(host, pkgs): + host.run_command(install_cmd + pkgs) + + ++def reinstall_packages(host, pkgs): ++ """Install packages on a remote host. ++ :param host: the host where the installation takes place ++ :param pkgs: packages to install, provided as a list of strings ++ """ ++ platform = get_platform(host) ++ if platform in {'rhel', 'fedora'}: ++ install_cmd = ['/usr/bin/dnf', 'reinstall', '-y'] ++ elif platform in {'debian', 'ubuntu'}: ++ install_cmd = ['apt-get', '--reinstall', 'install', '-y'] ++ else: ++ raise ValueError('install_packages: unknown platform %s' % platform) ++ host.run_command(install_cmd + pkgs) ++ ++ + def download_packages(host, pkgs): + """Download packages on a remote host. + :param host: the host where the download takes place +diff --git a/ipatests/test_integration/test_upgrade.py b/ipatests/test_integration/test_upgrade.py +index 182e3b5da3c758cc10913ad4eed119b0983fcc23..011de939e92790734d63da2f85be1c25349116a8 100644 +--- a/ipatests/test_integration/test_upgrade.py ++++ b/ipatests/test_integration/test_upgrade.py +@@ -477,3 +477,17 @@ class TestUpgrade(IntegrationTest): + self.master.run_command(['ipa-server-upgrade']) + assert self.master.transport.file_exists( + paths.SYSTEMD_PKI_TOMCAT_IPA_CONF) ++ ++ def test_ssh_config(self): ++ """Test that pkg upgrade does not create /etc/ssh/ssh_config.orig ++ ++ Test for ticket 9610 ++ The upgrade of ipa-client package should not create a backup file ++ /etc/ssh/ssh_config.orig if no change is applied. ++ """ ++ # Ensure there is no backup file before the test ++ self.master.run_command(["rm", "-f", paths.SSH_CONFIG + ".orig"]) ++ # Force client package reinstallation to trigger %post scriptlet ++ tasks.reinstall_packages(self.master, ['*ipa-client']) ++ assert not self.master.transport.file_exists( ++ paths.SSH_CONFIG + ".orig") +-- +2.45.2 + diff --git a/0006-PKINIT-certificate-fix-renewal-on-hidden-replica.patch b/0006-PKINIT-certificate-fix-renewal-on-hidden-replica.patch new file mode 100644 index 0000000..38dfd03 --- /dev/null +++ b/0006-PKINIT-certificate-fix-renewal-on-hidden-replica.patch @@ -0,0 +1,41 @@ +From c8e3fdeb0015f9c52c64816d6cd39279c5d3ad5a Mon Sep 17 00:00:00 2001 +From: Florence Blanc-Renaud +Date: Thu, 20 Jun 2024 08:36:04 +0200 +Subject: [PATCH] PKINIT certificate: fix renewal on hidden replica + +The renewal of PKINIT cert on hidden replica is failing because +of a test ensuring that the KDC service is either enabled or +configured. The test needs to be extended and allow hidden, too. + +Fixes: https://pagure.io/freeipa/issue/9611 +Signed-off-by: Florence Blanc-Renaud +Reviewed-By: Rob Crittenden +--- + ipaserver/plugins/cert.py | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/ipaserver/plugins/cert.py b/ipaserver/plugins/cert.py +index df415c375189a54ceb0a00670f9c15e2f154a94e..6249c6d6f24acdca4fc3e9dd989f58344192b567 100644 +--- a/ipaserver/plugins/cert.py ++++ b/ipaserver/plugins/cert.py +@@ -55,7 +55,7 @@ from ipapython.dn import DN + from ipapython.ipautil import datetime_from_utctimestamp + from ipaserver.plugins.service import normalize_principal, validate_realm + from ipaserver.masters import ( +- ENABLED_SERVICE, CONFIGURED_SERVICE, is_service_enabled ++ ENABLED_SERVICE, CONFIGURED_SERVICE, HIDDEN_SERVICE, is_service_enabled + ) + + try: +@@ -300,7 +300,7 @@ def caacl_check(principal, ca, profile_id): + def ca_kdc_check(api_instance, hostname): + master_dn = api_instance.Object.server.get_dn(unicode(hostname)) + kdc_dn = DN(('cn', 'KDC'), master_dn) +- wanted = {ENABLED_SERVICE, CONFIGURED_SERVICE} ++ wanted = {ENABLED_SERVICE, CONFIGURED_SERVICE, HIDDEN_SERVICE} + try: + kdc_entry = api_instance.Backend.ldap2.get_entry( + kdc_dn, ['ipaConfigString']) +-- +2.45.2 + diff --git a/0007-ipatests-add-test-for-PKINIT-renewal-on-hidden-repli.patch b/0007-ipatests-add-test-for-PKINIT-renewal-on-hidden-repli.patch new file mode 100644 index 0000000..e19f7f4 --- /dev/null +++ b/0007-ipatests-add-test-for-PKINIT-renewal-on-hidden-repli.patch @@ -0,0 +1,54 @@ +From 467ec04f93a29fd31ba037cef348c09547541fe7 Mon Sep 17 00:00:00 2001 +From: Florence Blanc-Renaud +Date: Mon, 24 Jun 2024 09:18:54 +0200 +Subject: [PATCH] ipatests: add test for PKINIT renewal on hidden replica + +Test scenario: on a hidden replica, force the renewal of +PKINIT cert by calling getcert resubmit. + +Related: https://pagure.io/freeipa/issue/9611 +Signed-off-by: Florence Blanc-Renaud +Reviewed-By: Rob Crittenden +--- + .../test_integration/test_replica_promotion.py | 18 ++++++++++++++++++ + 1 file changed, 18 insertions(+) + +diff --git a/ipatests/test_integration/test_replica_promotion.py b/ipatests/test_integration/test_replica_promotion.py +index b71f2d5d7e1517ab73d79b62477a3377839b0b7a..7ef44c571c8a4106577d27f4712f661be873dacc 100644 +--- a/ipatests/test_integration/test_replica_promotion.py ++++ b/ipatests/test_integration/test_replica_promotion.py +@@ -26,6 +26,7 @@ from ipalib.constants import ( + ) + from ipaplatform.paths import paths + from ipapython import certdb ++from ipatests.test_integration.test_cert import get_certmonger_fs_id + from ipatests.test_integration.test_dns_locations import ( + resolve_records_from_server, IPA_DEFAULT_MASTER_SRV_REC + ) +@@ -1241,6 +1242,23 @@ class TestHiddenReplicaPromotion(IntegrationTest): + 'ipa-crlgen-manage', 'status']) + assert "CRL generation: enabled" in result.stdout_text + ++ def test_hidden_replica_renew_pkinit_cert(self): ++ """Renew the PKINIT cert on a hidden replica. ++ ++ Test for https://pagure.io/freeipa/issue/9611 ++ """ ++ # Get Request ID ++ cmd = ['getcert', 'list', '-f', paths.KDC_CERT] ++ result = self.replicas[0].run_command(cmd) ++ req_id = get_certmonger_fs_id(result.stdout_text) ++ ++ self.replicas[0].run_command([ ++ 'getcert', 'resubmit', '-f', paths.KDC_CERT ++ ]) ++ tasks.wait_for_certmonger_status( ++ self.replicas[0], ('MONITORING'), req_id, timeout=600 ++ ) ++ + + class TestHiddenReplicaKRA(IntegrationTest): + """Test KRA & hidden replica features. +-- +2.45.2 + diff --git a/0008-ipatests-Tests-for-ipa-ipa-migration-tool.patch b/0008-ipatests-Tests-for-ipa-ipa-migration-tool.patch new file mode 100644 index 0000000..499c07c --- /dev/null +++ b/0008-ipatests-Tests-for-ipa-ipa-migration-tool.patch @@ -0,0 +1,917 @@ +From 90b22ff888cc55132c78024d08ffcf0ce8021cea Mon Sep 17 00:00:00 2001 +From: Sudhir Menon +Date: Tue, 25 Jun 2024 11:00:28 +0530 +Subject: [PATCH] ipatests: Tests for ipa-ipa migration tool + +This patch includes tests for ipa-ipa migration +tool + +Signed-off-by: Sudhir Menon +Reviewed-By: Florence Blanc-Renaud +Reviewed-By: Mark Reynolds +--- + ipaplatform/base/paths.py | 1 + + .../test_ipa_ipa_migration.py | 879 ++++++++++++++++++ + 2 files changed, 880 insertions(+) + create mode 100644 ipatests/test_integration/test_ipa_ipa_migration.py + +diff --git a/ipaplatform/base/paths.py b/ipaplatform/base/paths.py +index 2b0fc6b5aa954a1018f602605eb0cdcebcee0592..b339d2202f440e0277d50073060f4a3b55e312fe 100644 +--- a/ipaplatform/base/paths.py ++++ b/ipaplatform/base/paths.py +@@ -425,6 +425,7 @@ class BasePathNamespace: + IPA_CUSTODIA_HANDLER = "/usr/libexec/ipa/custodia" + IPA_CUSTODIA_CHECK = "/usr/libexec/ipa/ipa-custodia-check" + IPA_GETKEYTAB = '/usr/sbin/ipa-getkeytab' ++ IPA_MIGRATE_LOG = '/var/log/ipa-migrate.log' + EXTERNAL_SCHEMA_DIR = '/usr/share/ipa/schema.d' + GSSPROXY_CONF = '/etc/gssproxy/10-ipa.conf' + KRB5CC_HTTPD = '/tmp/krb5cc-httpd' +diff --git a/ipatests/test_integration/test_ipa_ipa_migration.py b/ipatests/test_integration/test_ipa_ipa_migration.py +new file mode 100644 +index 0000000000000000000000000000000000000000..7e2d4a34216f6cf168f15dda10ce10538a3c3cb9 +--- /dev/null ++++ b/ipatests/test_integration/test_ipa_ipa_migration.py +@@ -0,0 +1,879 @@ ++# Copyright (C) 2020 FreeIPA Contributors see COPYING for license ++# ++ ++""" ++Tests to verify ipa-migrate tool. ++""" ++ ++from __future__ import absolute_import ++from ipatests.test_integration.base import IntegrationTest ++from ipatests.pytest_ipa.integration import tasks ++from ipaplatform.paths import paths ++ ++import pytest ++import textwrap ++ ++ ++def prepare_ipa_server(master): ++ """ ++ Setup remote IPA server environment ++ """ ++ # Setup IPA users ++ for i in range(1, 5): ++ master.run_command( ++ [ ++ "ipa", ++ "user-add", ++ "testuser%d" % i, ++ "--first", ++ "Test", ++ "--last", ++ "User%d" % i, ++ ] ++ ) ++ ++ # Setup IPA group ++ master.run_command(["ipa", "group-add", "testgroup"]) ++ ++ # Add respective members to each group ++ master.run_command( ++ ["ipa", "group-add-member", "testgroup", "--users=testuser1"] ++ ) ++ ++ # Adding stage user ++ master.run_command( ++ [ ++ "ipa", ++ "stageuser-add", ++ "--first=Tim", ++ "--last=User", ++ "--password", ++ "tuser1", ++ ] ++ ) ++ ++ # Add Custom idrange ++ master.run_command( ++ [ ++ "ipa", ++ "idrange-add", ++ "testrange", ++ "--base-id=10000", ++ "--range-size=10000", ++ "--rid-base=300000", ++ "--secondary-rid-base=400000", ++ ] ++ ) ++ ++ # Add Automount locations and maps ++ master.run_command(["ipa", "automountlocation-add", "baltimore"]) ++ master.run_command(["ipa", "automountmap-add", "baltimore", "auto.share"]) ++ master.run_command( ++ [ ++ "ipa", ++ "automountmap-add-indirect", ++ "baltimore", ++ "--parentmap=auto.share", ++ "--mount=sub auto.man", ++ ] ++ ) ++ master.run_command( ++ [ ++ "ipa", ++ "automountkey-add", ++ "baltimore", ++ "auto.master", ++ "--key=/share", ++ "--info=auto.share", ++ ] ++ ) ++ ++ # Run ipa-adtrust-install ++ master.run_command(["dnf", "install", "-y", "ipa-server-trust-ad"]) ++ master.run_command( ++ [ ++ "ipa-adtrust-install", ++ "-a", ++ master.config.admin_password, ++ "--add-sids", ++ "-U", ++ ] ++ ) ++ ++ # Generate subids for users ++ master.run_command(["ipa", "subid-generate", "--owner=testuser1"]) ++ master.run_command(["ipa", "subid-generate", "--owner=admin"]) ++ ++ # Add Sudo rules ++ master.run_command(["ipa", "sudorule-add", "readfiles"]) ++ master.run_command(["ipa", "sudocmd-add", "/usr/bin/less"]) ++ master.run_command( ++ [ ++ "ipa", ++ "sudorule-add-allow-command", ++ "readfiles", ++ "--sudocmds", ++ "/usr/bin/less", ++ ] ++ ) ++ master.run_command( ++ [ ++ "ipa", ++ "sudorule-add-host", ++ "readfiles", ++ "--hosts", ++ "server.example.com", ++ ] ++ ) ++ master.run_command( ++ ["ipa", "sudorule-add-user", "readfiles", "--users", "testuser1"] ++ ) ++ ++ # Add Custom CA ++ master.run_command( ++ [ ++ "ipa", ++ "ca-add", ++ "puppet", ++ "--desc", ++ '"Puppet"', ++ "--subject", ++ "CN=Puppet CA,O=TESTRELM.TEST", ++ ] ++ ) ++ ++ # Add ipa roles and add privileges to the role ++ master.run_command( ++ ["ipa", "role-add", "--desc=Junior-level admin", "junioradmin"] ++ ) ++ master.run_command( ++ [ ++ "ipa", ++ "role-add-privilege", ++ "--privileges=User Administrators", ++ "junioradmin", ++ ] ++ ) ++ ++ # Add permission ++ master.run_command( ++ [ ++ "ipa", ++ "permission-add", ++ "--type=user", ++ "--permissions=add", ++ "Add Users", ++ ] ++ ) ++ ++ # Add otp token for testuser1 ++ master.run_command( ++ [ ++ "ipa", ++ "otptoken-add", ++ "--type=totp", ++ "--owner=testuser1", ++ '--desc="My soft token', ++ ] ++ ) ++ ++ # Add a netgroup and user to the netgroup ++ master.run_command( ++ ["ipa", "netgroup-add", '--desc="NFS admins"', "admins"] ++ ) ++ master.run_command( ++ ["ipa", "netgroup-add-member", "--users=testuser2", "admins"] ++ ) ++ ++ # Set krbpolicy policy ++ master.run_command( ++ ["ipa", "krbtpolicy-mod", "--maxlife=99999", "--maxrenew=99999"] ++ ) ++ master.run_command(["ipa", "krbtpolicy-mod", "admin", "--maxlife=9600"]) ++ ++ # Add IPA location ++ master.run_command( ++ ["ipa", "location-add", "location", "--description", "My location"] ++ ) ++ ++ # Add idviews and overrides ++ master.run_command(["ipa", "idview-add", "idview1"]) ++ master.run_command(["ipa", "idoverrideuser-add", "idview1", "testuser1"]) ++ master.run_command( ++ [ ++ "ipa", ++ "idoverrideuser-mod", ++ "idview1", ++ "testuser1", ++ "--shell=/bin/sh", ++ ] ++ ) ++ ++ # Add DNSzone ++ master.run_command( ++ [ ++ "ipa", ++ "dnszone-add", ++ "example.test", ++ "--admin-email=admin@example.test", ++ ] ++ ) ++ master.run_command( ++ ["ipa", "dnszone-mod", "example.test", "--dynamic-update=TRUE"] ++ ) ++ ++ # Add hbac rule ++ master.run_command(["ipa", "hbacrule-add", "--usercat=all", "test1"]) ++ master.run_command( ++ ["ipa", "hbacrule-add", "--hostcat=all", "testuser_sshd"] ++ ) ++ master.run_command( ++ ["ipa", "hbacrule-add-user", "--users=testuser1", "testuser_sshd"] ++ ) ++ master.run_command( ++ ["ipa", "hbacrule-add-service", "--hbacsvcs=sshd", "testuser_sshd"] ++ ) ++ ++ # Vault addition ++ master.run_command( ++ [ ++ "ipa", ++ "vault-add", ++ "--password", ++ "vault1234", ++ "--type", ++ "symmetric", ++ ] ++ ) ++ ++ # Add Selinuxusermap ++ master.run_command( ++ [ ++ "ipa", ++ "selinuxusermap-add", ++ "--usercat=all", ++ "--selinuxuser=xguest_u:s0", ++ "test1", ++ ] ++ ) ++ ++ # Modify passkeyconfig ++ master.run_command( ++ ["ipa", "passkeyconfig-mod", "--require-user-verification=FALSE"] ++ ) ++ ++ ++def run_migrate( ++ host, mode, remote_host, bind_dn=None, bind_pwd=None, extra_args=None ++): ++ """ ++ ipa-migrate tool command ++ """ ++ cmd = ["ipa-migrate"] ++ if mode: ++ cmd.append(mode) ++ if remote_host: ++ cmd.append(remote_host) ++ if bind_dn: ++ cmd.append("-D") ++ cmd.append(bind_dn) ++ if bind_pwd: ++ cmd.append("-w") ++ cmd.append(bind_pwd) ++ if extra_args: ++ for arg in extra_args: ++ cmd.append(arg) ++ result = host.run_command(cmd, raiseonerr=False) ++ return result ++ ++ ++class TestIPAMigrateScenario1(IntegrationTest): ++ """ ++ Tier-1 tests for ipa-migrate tool with DNS enabled on ++ local and remote server ++ """ ++ ++ num_replicas = 1 ++ num_clients = 1 ++ topology = "line" ++ ++ @classmethod ++ def install(cls, mh): ++ tasks.install_master(cls.master, setup_dns=True, setup_kra=True) ++ prepare_ipa_server(cls.master) ++ tasks.install_client(cls.master, cls.clients[0], nameservers=None) ++ ++ def test_remote_server(self): ++ """ ++ This test installs IPA server instead of replica on ++ system under test with the same realm and domain name. ++ """ ++ tasks.install_master(self.replicas[0], setup_dns=True, setup_kra=True) ++ ++ def test_ipa_migrate_without_kinit_as_admin(self): ++ """ ++ This test checks that ipa-migrate tool displays ++ error when kerberos ticket is missing for admin ++ """ ++ self.replicas[0].run_command(["kdestroy", "-A"]) ++ KINIT_ERR_MSG = "ipa: ERROR: Did not receive Kerberos credentials\n" ++ result = run_migrate( ++ self.replicas[0], ++ "stage-mode", ++ self.master.hostname, ++ "cn=Directory Manager", ++ self.master.config.admin_password, ++ extra_args=['-x'], ++ ) ++ assert result.returncode == 1 ++ assert KINIT_ERR_MSG in result.stderr_text ++ tasks.kinit_admin(self.replicas[0]) ++ ++ def test_ipa_migrate_log_file_is_created(self): ++ """ ++ This test checks that ipa-migrate.log file is created when ipa-migrate ++ tool is run ++ """ ++ run_migrate( ++ self.replicas[0], ++ "stage-mode", ++ self.master.hostname, ++ "cn=Directory Manager", ++ self.master.config.admin_password, ++ extra_args=['-x'], ++ ) ++ assert self.replicas[0].transport.file_exists(paths.IPA_MIGRATE_LOG) ++ ++ def test_ipa_migrate_with_incorrect_bind_pwd(self): ++ """ ++ This test checks that ipa-migrate tool fails with incorrect ++ bind password ++ """ ++ ERR_MSG = ( ++ "IPA to IPA migration starting ...\n" ++ "Failed to bind to remote server: Insufficient access: " ++ "Invalid credentials\n" ++ ) ++ result = run_migrate( ++ self.replicas[0], ++ "stage-mode", ++ self.master.hostname, ++ "cn=Directory Manager", ++ "incorrect_bind_pwd", ++ extra_args=['-x'], ++ ) ++ assert result.returncode == 1 ++ assert ERR_MSG in result.stderr_text ++ ++ def test_ipa_migrate_with_incorrect_bind_dn(self): ++ """ ++ This test checks that ipa-migrate tool fails with incorrect ++ bind dn ++ """ ++ ERR_MSG = ( ++ "IPA to IPA migration starting ...\n" ++ "Failed to bind to remote server: Insufficient access: " ++ "Invalid credentials\n" ++ ) ++ result = run_migrate( ++ self.replicas[0], ++ "stage-mode", ++ self.master.hostname, ++ "cn=Dir Manager", ++ self.master.config.admin_password, ++ extra_args=['-x'], ++ ) ++ assert result.returncode == 1 ++ assert ERR_MSG in result.stderr_text ++ ++ def test_ipa_migrate_with_invalid_host(self): ++ """ ++ This test checks that ipa-migrate tools fails with ++ invalid host ++ """ ++ hostname = "server.invalid.host" ++ ERR_MSG = ( ++ "IPA to IPA migration starting ...\n" ++ "Failed to bind to remote server: cannot connect to " ++ "'ldap://" ++ "{}': \n".format(hostname) ++ ) ++ result = run_migrate( ++ self.replicas[0], ++ "stage-mode", ++ "server.invalid.host", ++ "cn=Directory Manager", ++ self.master.config.admin_password, ++ extra_args=['-x'], ++ ) ++ assert result.returncode == 1 ++ assert ERR_MSG in result.stderr_text ++ ++ def test_dry_run_record_output_ldif(self): ++ """ ++ This testcase run ipa-migrate tool with the ++ -o option which captures the output to ldif file ++ """ ++ ldif_file = "/tmp/test.ldif" ++ param = ['-x', '-o', ldif_file] ++ run_migrate( ++ self.replicas[0], ++ "stage-mode", ++ self.master.hostname, ++ "cn=Directory Manager", ++ self.master.config.admin_password, ++ extra_args=param, ++ ) ++ assert self.replicas[0].transport.file_exists("/tmp/test.ldif") ++ ++ @pytest.fixture() ++ def empty_log_file(self): ++ """ ++ This fixture empties the log file before ipa-migrate tool ++ is run since the log is appended everytime the tool is run. ++ """ ++ self.replicas[0].run_command( ++ ["truncate", "-s", "0", paths.IPA_MIGRATE_LOG] ++ ) ++ yield ++ ++ def test_ipa_sigden_plugin_fail_error(self, empty_log_file): ++ """ ++ This testcase checks that sidgen plugin fail error is ++ not seen during migrate prod-mode ++ """ ++ SIDGEN_ERR_MSG = "SIDGEN task failed: \n" ++ run_migrate( ++ self.replicas[0], ++ "stage-mode", ++ self.master.hostname, ++ "cn=Directory Manager", ++ self.master.config.admin_password, ++ extra_args=['-x'], ++ ) ++ error_msg = self.replicas[0].get_file_contents( ++ paths.IPA_MIGRATE_LOG, encoding="utf-8" ++ ) ++ assert SIDGEN_ERR_MSG not in error_msg ++ ++ def test_ipa_migrate_stage_mode_dry_run(self, empty_log_file): ++ """ ++ Test ipa-migrate stage mode with dry-run option ++ """ ++ tasks.kinit_admin(self.master) ++ tasks.kinit_admin(self.replicas[0]) ++ IPA_MIGRATE_STAGE_DRY_RUN_LOG = "--dryrun=True\n" ++ IPA_SERVER_UPRGADE_LOG = "Skipping ipa-server-upgrade in dryrun mode.\n" ++ IPA_SKIP_SIDGEN_LOG = "Skipping SIDGEN task in dryrun mode." ++ result = run_migrate( ++ self.replicas[0], ++ "stage-mode", ++ self.master.hostname, ++ "cn=Directory Manager", ++ self.master.config.admin_password, ++ extra_args=['-x'], ++ ) ++ install_msg = self.replicas[0].get_file_contents( ++ paths.IPA_MIGRATE_LOG, encoding="utf-8" ++ ) ++ assert result.returncode == 0 ++ assert IPA_MIGRATE_STAGE_DRY_RUN_LOG in install_msg ++ assert IPA_SERVER_UPRGADE_LOG in install_msg ++ assert IPA_SKIP_SIDGEN_LOG in install_msg ++ ++ def test_ipa_migrate_prod_mode_dry_run(self, empty_log_file): ++ """ ++ Test ipa-migrate prod mode with dry run option ++ """ ++ tasks.kinit_admin(self.master) ++ tasks.kinit_admin(self.replicas[0]) ++ IPA_MIGRATE_PROD_DRY_RUN_LOG = "--dryrun=True\n" ++ IPA_SERVER_UPRGADE_LOG = ( ++ "Skipping ipa-server-upgrade in dryrun mode.\n" ++ ) ++ IPA_SIDGEN_LOG = "Skipping SIDGEN task in dryrun mode.\n" ++ result = run_migrate( ++ self.replicas[0], ++ "prod-mode", ++ self.master.hostname, ++ "cn=Directory Manager", ++ self.master.config.admin_password, ++ extra_args=['-x'], ++ ) ++ install_msg = self.replicas[0].get_file_contents( ++ paths.IPA_MIGRATE_LOG, encoding="utf-8" ++ ) ++ assert result.returncode == 0 ++ assert IPA_MIGRATE_PROD_DRY_RUN_LOG in install_msg ++ assert IPA_SERVER_UPRGADE_LOG in install_msg ++ assert IPA_SIDGEN_LOG in install_msg ++ ++ def test_ipa_migrate_with_skip_schema_option_dry_run(self, empty_log_file): ++ """ ++ This test checks that ipa-migrate tool works ++ with -S(schema) options in stage mode ++ """ ++ param = ['-x', '-S'] ++ tasks.kinit_admin(self.master) ++ tasks.kinit_admin(self.replicas[0]) ++ SKIP_SCHEMA_MSG_LOG = "Schema Migration " \ ++ "(migrated 0 definitions)\n" ++ run_migrate( ++ self.replicas[0], ++ "stage-mode", ++ self.master.hostname, ++ "cn=Directory Manager", ++ self.master.config.admin_password, ++ extra_args=param, ++ ) ++ install_msg = self.replicas[0].get_file_contents( ++ paths.IPA_MIGRATE_LOG, encoding="utf-8" ++ ) ++ assert SKIP_SCHEMA_MSG_LOG in install_msg ++ ++ def test_ipa_migrate_with_skip_config_option_dry_run(self, empty_log_file): ++ """ ++ This test checks that ipa-migrate tool works ++ with -C(config) options in stage mode ++ """ ++ SKIP_MIGRATION_CONFIG_LOG = "DS Configuration Migration " \ ++ "(migrated 0 entries)\n" ++ param = ['-x', '-C'] ++ tasks.kinit_admin(self.master) ++ tasks.kinit_admin(self.replicas[0]) ++ ++ run_migrate( ++ self.replicas[0], ++ "stage-mode", ++ self.master.hostname, ++ "cn=Directory Manager", ++ self.master.config.admin_password, ++ extra_args=param, ++ ) ++ install_msg = self.replicas[0].get_file_contents( ++ paths.IPA_MIGRATE_LOG, encoding="utf-8" ++ ) ++ assert SKIP_MIGRATION_CONFIG_LOG in install_msg ++ ++ def test_ipa_migrate_reset_range(self, empty_log_file): ++ """ ++ This test checks the reset range option -r ++ along with prod-mode, since stage-mode this is done ++ automatically. ++ """ ++ param = ['-r', '-n'] ++ tasks.kinit_admin(self.master) ++ tasks.kinit_admin(self.replicas[0]) ++ RESET_RANGE_LOG = "--reset-range=True\n" ++ run_migrate( ++ self.replicas[0], ++ "prod-mode", ++ self.master.hostname, ++ "cn=Directory Manager", ++ self.master.config.admin_password, ++ extra_args=param, ++ ) ++ install_msg = self.replicas[0].get_file_contents( ++ paths.IPA_MIGRATE_LOG, encoding="utf-8" ++ ) ++ assert RESET_RANGE_LOG in install_msg ++ ++ def test_ipa_migrate_stage_mode_dry_override_schema(self, empty_log_file): ++ """ ++ This test checks that -O option (override schema) works ++ in dry mode ++ """ ++ param = ['-x', '-O', '-n'] ++ tasks.kinit_admin(self.master) ++ tasks.kinit_admin(self.replicas[0]) ++ SCHEMA_OVERRIDE_LOG = "--schema-overwrite=True\n" ++ run_migrate( ++ self.replicas[0], ++ "stage-mode", ++ self.master.hostname, ++ "cn=Directory Manager", ++ self.master.config.admin_password, ++ extra_args=param, ++ ) ++ install_msg = self.replicas[0].get_file_contents( ++ paths.IPA_MIGRATE_LOG, encoding="utf-8" ++ ) ++ assert SCHEMA_OVERRIDE_LOG in install_msg ++ ++ @pytest.mark.xfail( ++ reason="https://issues.redhat.com/browse/RHEL-45463", strict=True ++ ) ++ def test_ipa_migrate_stage_mode(self, empty_log_file): ++ """ ++ This test checks that ipa-migrate is successful ++ in dry run mode ++ """ ++ tasks.kinit_admin(self.master) ++ tasks.kinit_admin(self.replicas[0]) ++ MIGRATION_SCHEMA_LOG_MSG = "Migrating schema ...\n" ++ MIGRATION_CONFIG_LOG_MSG = "Migrating configuration ...\n" ++ IPA_UPGRADE_LOG_MSG = ( ++ "Running ipa-server-upgrade ... (this make take a while)\n" ++ ) ++ SIDGEN_TASK_LOG_MSG = "Running SIDGEN task ...\n" ++ MIGRATION_COMPLETE_LOG_MSG = "Migration complete!\n" ++ result = run_migrate( ++ self.replicas[0], ++ "stage-mode", ++ self.master.hostname, ++ "cn=Directory Manager", ++ self.master.config.admin_password, ++ extra_args=['-n'], ++ ) ++ install_msg = self.replicas[0].get_file_contents( ++ paths.IPA_MIGRATE_LOG, encoding="utf-8" ++ ) ++ assert result.returncode == 0 ++ assert MIGRATION_SCHEMA_LOG_MSG in install_msg ++ assert MIGRATION_CONFIG_LOG_MSG in install_msg ++ assert IPA_UPGRADE_LOG_MSG in install_msg ++ assert SIDGEN_TASK_LOG_MSG in install_msg ++ assert MIGRATION_COMPLETE_LOG_MSG in install_msg ++ ++ def test_ipa_migrate_prod_mode(self, empty_log_file): ++ """ ++ This test checks that ipa-migrate is successful ++ in prod run mode ++ """ ++ tasks.kinit_admin(self.master) ++ tasks.kinit_admin(self.replicas[0]) ++ MIGRATION_SCHEMA_LOG_MSG = "Migrating schema ...\n" ++ MIGRATION_DATABASE_LOG_MSG = ( ++ "Migrating database ... (this make take a while)\n" ++ ) ++ IPA_UPGRADE_LOG_MSG = ( ++ "Running ipa-server-upgrade ... (this make take a while)\n" ++ ) ++ SIDGEN_TASK_LOG_MSG = "Running SIDGEN task ...\n" ++ result = run_migrate( ++ self.replicas[0], ++ "prod-mode", ++ self.master.hostname, ++ "cn=Directory Manager", ++ self.master.config.admin_password, ++ extra_args=['-n'], ++ ) ++ install_msg = self.replicas[0].get_file_contents( ++ paths.IPA_MIGRATE_LOG, encoding="utf-8" ++ ) ++ assert result.returncode == 0 ++ assert MIGRATION_SCHEMA_LOG_MSG in install_msg ++ assert MIGRATION_DATABASE_LOG_MSG in install_msg ++ assert IPA_UPGRADE_LOG_MSG in install_msg ++ assert SIDGEN_TASK_LOG_MSG in install_msg ++ ++ def test_ipa_migrate_with_bind_pwd_file_option(self, empty_log_file): ++ """ ++ This testcase checks that ipa-migrate tool ++ works with valid bind_pwd specified in a file using '-j' ++ option ++ """ ++ DEBUG_MSG = "--bind-pw-file=/tmp/pwd.txt\n" ++ bind_pwd_file = "/tmp/pwd.txt" ++ bind_pwd_file_content = self.master.config.admin_password ++ self.replicas[0].put_file_contents( ++ bind_pwd_file, bind_pwd_file_content ++ ) ++ param = ['-j', bind_pwd_file, '-x'] ++ result = run_migrate( ++ host=self.replicas[0], ++ mode="stage-mode", ++ remote_host=self.master.hostname, ++ bind_dn="cn=Directory Manager", ++ bind_pwd=None, ++ extra_args=param, ++ ) ++ install_msg = self.replicas[0].get_file_contents( ++ paths.IPA_MIGRATE_LOG, encoding="utf-8" ++ ) ++ assert DEBUG_MSG in install_msg ++ assert result.returncode == 0 ++ ++ def test_ipa_migrate_using_db_ldif(self): ++ """ ++ This test checks that ipa-migrate tool ++ works with db ldif file using -C option ++ """ ++ DB_LDIF_LOG = "--db-ldif=/tmp/dse.ldif\n" ++ tasks.kinit_admin(self.master) ++ tasks.kinit_admin(self.replicas[0]) ++ ldif_file_path = "/tmp/dse.ldif" ++ param = ["-f", ldif_file_path, "-n", "-x"] ++ realm_name = self.master.domain.realm ++ base_dn = str(self.master.domain.basedn) ++ dse_ldif = textwrap.dedent( ++ f""" ++ dn: cn={realm_name},cn=kerberos,{base_dn} ++ cn: {realm_name} ++ objectClass: top ++ objectClass: krbrealmcontainer ++ """ ++ ).format( ++ realm_name=self.master.domain.realm, ++ base_dn=str(self.master.domain.basedn), ++ ) ++ self.replicas[0].put_file_contents(ldif_file_path, dse_ldif) ++ result = run_migrate( ++ self.replicas[0], ++ "stage-mode", ++ self.master.hostname, ++ "cn=Directory Manager", ++ self.master.config.admin_password, ++ extra_args=param, ++ ) ++ install_msg = self.replicas[0].get_file_contents( ++ paths.IPA_MIGRATE_LOG, encoding="utf-8" ++ ) ++ assert result.returncode == 0 ++ assert DB_LDIF_LOG in install_msg ++ ++ def test_ipa_migrate_using_invalid_dbldif_file(self): ++ """ ++ This testcase checks that proper error msg is ++ displayed when invalid ldif file without realm is used ++ as input to schema config option -f ++ """ ++ ERR_MSG = ( ++ "IPA to IPA migration starting ...\n" ++ "Unable to find realm from remote LDIF\n" ++ ) ++ tasks.kinit_admin(self.master) ++ tasks.kinit_admin(self.replicas[0]) ++ base_dn = str(self.master.domain.basedn) ++ ldif_file = "/tmp/ldif_file" ++ param = ["-f", ldif_file, "-n", "-x"] ++ dse_ldif = textwrap.dedent( ++ """ ++ version: 1 ++ dn: cn=schema,{} ++ ++ """ ++ ).format(base_dn) ++ self.replicas[0].put_file_contents(ldif_file, dse_ldif) ++ result = run_migrate( ++ self.replicas[0], ++ "prod-mode", ++ self.master.hostname, ++ "cn=Directory Manager", ++ self.master.config.admin_password, ++ extra_args=param, ++ ) ++ assert result.returncode == 2 ++ assert ERR_MSG in result.stderr_text ++ ++ def test_ipa_migrate_subtree_option(self): ++ """ ++ This testcase checks the subtree option ++ -s along with the ipa-migrate command ++ """ ++ base_dn = str(self.master.domain.basedn) ++ subtree = 'cn=security,{}'.format(base_dn) ++ params = ['-s', subtree, '-n', '-x'] ++ base_dn = str(self.master.domain.basedn) ++ CUSTOM_SUBTREE_LOG = ( ++ "Add db entry 'cn=security,{} - custom'" ++ ).format(base_dn) ++ dse_ldif = textwrap.dedent( ++ """ ++ dn: cn=security,{base_dn} ++ changetype: add ++ objectClass:top ++ objectClass: nscontainer ++ """ ++ ).format(base_dn=base_dn) ++ tasks.ldapmodify_dm(self.master, dse_ldif) ++ result = run_migrate( ++ self.replicas[0], ++ "stage-mode", ++ self.master.hostname, ++ "cn=Directory Manager", ++ self.master.config.admin_password, ++ extra_args=params, ++ ) ++ assert result.returncode == 0 ++ install_msg = self.replicas[0].get_file_contents( ++ paths.IPA_MIGRATE_LOG, encoding="utf-8" ++ ) ++ assert CUSTOM_SUBTREE_LOG in install_msg ++ ++ @pytest.fixture() ++ def modify_dns_zone(self): ++ zone_name = 'ipatest.test' ++ self.master.run_command( ++ ["ipa", "dnszone-add", zone_name, "--force"] ++ ) ++ yield ++ self.replicas[0].run_command( ++ ["ipa", "dnszone-del", zone_name] ++ ) ++ ++ def test_ipa_migrate_dns_option(self, modify_dns_zone): ++ """ ++ This testcase checks that when migrate dns option ++ -B is used the dns entry is migrated to the ++ local host. ++ """ ++ zone_name = "ipatest.test." ++ base_dn = str(self.master.domain.basedn) ++ DNS_LOG1 = "--migrate-dns=True\n" ++ DNS_LOG2 = ( ++ "DEBUG Added entry: idnsname={},cn=dns,{}\n" ++ ).format(zone_name, base_dn) ++ DNS_LOG3 = ( ++ "DEBUG Added entry: idnsname=_kerberos," ++ "idnsname={},cn=dns,{}\n" ++ ).format(zone_name, base_dn) ++ params = ["-B", "-n"] ++ run_migrate( ++ self.replicas[0], ++ "prod-mode", ++ self.master.hostname, ++ "cn=Directory Manager", ++ self.master.config.admin_password, ++ extra_args=params, ++ ) ++ result = self.replicas[0].run_command(["ipa", "dnszone-find"]) ++ assert "Zone name: ipatest.test." in result.stdout_text ++ install_msg = self.replicas[0].get_file_contents( ++ paths.IPA_MIGRATE_LOG, encoding="utf-8" ++ ) ++ assert DNS_LOG1 in install_msg ++ assert DNS_LOG2 in install_msg ++ assert DNS_LOG3 in install_msg ++ ++ @pytest.mark.xfail(reason="https://issues.redhat.com/browse/RHEL-46003", ++ strict=True) ++ def test_ipa_migrate_version_option(self): ++ """ ++ This testcase checks the version of ++ the ipa-migrate tool using -v option ++ """ ++ CONSOLE_LOG = ( ++ "ipa-migrate: error: the following arguments are " ++ "required: mode, hostname" ++ ) ++ result = self.master.run_command(["ipa-migrate", "-V"]) ++ assert result.returncode == 0 ++ assert CONSOLE_LOG not in result.stderr_text ++ ++ def test_ipa_migrate_with_log_file_option(self): ++ """ ++ This testcase checks that log file is created ++ with -l option ++ """ ++ custom_log_file = "/tmp/test.log" ++ params = ['-x', '-n', '-l', custom_log_file] ++ run_migrate( ++ self.replicas[0], ++ "stage-mode", ++ self.master.hostname, ++ "cn=Directory Manager", ++ self.master.config.admin_password, ++ extra_args=params, ++ ) ++ assert self.replicas[0].transport.file_exists(custom_log_file) +-- +2.45.2 + diff --git a/0009-ipa_sidgen-Allow-sidgen_task-to-continue-after-findi.patch b/0009-ipa_sidgen-Allow-sidgen_task-to-continue-after-findi.patch new file mode 100644 index 0000000..c275835 --- /dev/null +++ b/0009-ipa_sidgen-Allow-sidgen_task-to-continue-after-findi.patch @@ -0,0 +1,104 @@ +From a8e75bbb77e15e3a42adb2d30933cf9e1edd2f0b Mon Sep 17 00:00:00 2001 +From: Thomas Woerner +Date: Tue, 11 Jun 2024 10:50:51 +0200 +Subject: [PATCH] ipa_sidgen: Allow sidgen_task to continue after finding + issues + +find_sid_for_ldap_entry could fail in several ways if a Posix ID can not +be converted to an unused SID. This could happen for example for ducplicate +IDs or user/group out of range. + +This change enables ipa_sidgen_task to continue in the error case to try +to convert the entries without errors. The error messages have been +extended to additionally show the DN string for the bad entries. + +Fixes: https://pagure.io/freeipa/issue/9618 + +Signed-off-by: Thomas Woerner +Reviewed-By: Alexander Bokovoy +--- + .../ipa-slapi-plugins/ipa-sidgen/ipa_sidgen_common.c | 11 ++++++----- + .../ipa-slapi-plugins/ipa-sidgen/ipa_sidgen_task.c | 11 ++++++++--- + 2 files changed, 14 insertions(+), 8 deletions(-) + +diff --git a/daemons/ipa-slapi-plugins/ipa-sidgen/ipa_sidgen_common.c b/daemons/ipa-slapi-plugins/ipa-sidgen/ipa_sidgen_common.c +index cb763ebf8c733e50483c23856a248eb536c796f1..13f4de5416606df1911f14f60ab1af1a8ba0184b 100644 +--- a/daemons/ipa-slapi-plugins/ipa-sidgen/ipa_sidgen_common.c ++++ b/daemons/ipa-slapi-plugins/ipa-sidgen/ipa_sidgen_common.c +@@ -491,7 +491,7 @@ int find_sid_for_ldap_entry(struct slapi_entry *entry, + } + + if (uid_number >= UINT32_MAX || gid_number >= UINT32_MAX) { +- LOG_FATAL("ID value too large.\n"); ++ LOG_FATAL("ID value too large on entry [%s].\n", dn_str); + ret = LDAP_CONSTRAINT_VIOLATION; + goto done; + } +@@ -508,7 +508,7 @@ int find_sid_for_ldap_entry(struct slapi_entry *entry, + &has_posix_group, + &has_ipa_id_object); + if (ret != 0) { +- LOG_FATAL("Cannot determine objectclasses.\n"); ++ LOG_FATAL("Cannot determine objectclasses on entry [%s].\n", dn_str); + goto done; + } + +@@ -522,15 +522,16 @@ int find_sid_for_ldap_entry(struct slapi_entry *entry, + id = (uid_number != 0) ? uid_number : gid_number; + objectclass_to_add = NULL; + } else { +- LOG_FATAL("Inconsistent objectclasses and attributes, nothing to do.\n"); ++ LOG_FATAL("Inconsistent objectclasses and attributes on entry " ++ "[%s], nothing to do.\n", dn_str); + ret = 0; + goto done; + } + + ret = find_sid_for_id(id, plugin_id, base_dn, dom_sid, ranges, &sid); + if (ret != 0) { +- LOG_FATAL("Cannot convert Posix ID [%lu] into an unused SID.\n", +- (unsigned long) id); ++ LOG_FATAL("Cannot convert Posix ID [%lu] into an unused SID on " ++ "entry [%s].\n", (unsigned long) id, dn_str); + goto done; + } + +diff --git a/daemons/ipa-slapi-plugins/ipa-sidgen/ipa_sidgen_task.c b/daemons/ipa-slapi-plugins/ipa-sidgen/ipa_sidgen_task.c +index 007b1c945d0e37c4061f6a33cfdd667c45118c99..67979cb9fb0b5560009643c84be7eb07d767d77f 100644 +--- a/daemons/ipa-slapi-plugins/ipa-sidgen/ipa_sidgen_task.c ++++ b/daemons/ipa-slapi-plugins/ipa-sidgen/ipa_sidgen_task.c +@@ -89,7 +89,7 @@ static void free_pblock(void *arg) + static int do_work(struct worker_ctx *worker_ctx) + { + Slapi_PBlock *pb; +- int ret; ++ int ret, failures = 0; + size_t c; + char *filter = NULL; + char *attrs[] = { OBJECTCLASS, UID_NUMBER, GID_NUMBER, NULL }; +@@ -151,8 +151,7 @@ static int do_work(struct worker_ctx *worker_ctx) + worker_ctx->base_dn, worker_ctx->dom_sid, + worker_ctx->ranges); + if (ret != 0) { +- LOG_FATAL("Cannot add SID to existing entry.\n"); +- goto done; ++ failures++; + } + + if (worker_ctx->delay != 0) { +@@ -162,6 +161,12 @@ static int do_work(struct worker_ctx *worker_ctx) + } + }; + ++ ret = failures; ++ if (ret > 0) { ++ LOG_FATAL("Finished with %d failures, please check the log.\n", ++ failures); ++ } ++ + done: + slapi_ch_free_string(&filter); + pthread_cleanup_pop(1); +-- +2.45.2 + diff --git a/freeipa.spec b/freeipa.spec index e101cbc..bd78dda 100644 --- a/freeipa.spec +++ b/freeipa.spec @@ -205,7 +205,7 @@ Name: %{package_name} Version: %{IPA_VERSION} -Release: 1%{?rc_version:.%rc_version}%{?dist}.1 +Release: 2%{?rc_version:.%rc_version}%{?dist} Summary: The Identity, Policy and Audit system License: GPL-3.0-or-later @@ -238,6 +238,14 @@ Patch1002: 1002-Revert-freeipa.spec-depend-on-bind-dnssec-utils.patch %endif %if 0%{?rhel} >= 9 Patch0001: 0001-Revert-Replace-netifaces-with-ifaddr.patch +Patch0002: 0002-Add-iparepltopoconf-objectclass-to-topology-permissi.patch +Patch0003: 0003-ipa-otptoken-import-open-the-key-file-in-binary-mode.patch +Patch0004: 0004-spec-file-do-not-create-etc-ssh-ssh_config.orig-if-u.patch +Patch0005: 0005-ipatests-add-test-for-ticket-9610.patch +Patch0006: 0006-PKINIT-certificate-fix-renewal-on-hidden-replica.patch +Patch0007: 0007-ipatests-add-test-for-PKINIT-renewal-on-hidden-repli.patch +Patch0008: 0008-ipatests-Tests-for-ipa-ipa-migration-tool.patch +Patch0009: 0009-ipa_sidgen-Allow-sidgen_task-to-continue-after-findi.patch Patch1001: 1001-Change-branding-to-IPA-and-Identity-Management.patch %endif %endif @@ -1296,7 +1304,9 @@ if [ $1 -gt 1 ] ; then chmod 0600 /var/log/ipaupgrade.log SSH_CLIENT_SYSTEM_CONF="/etc/ssh/ssh_config" if [ -f "$SSH_CLIENT_SYSTEM_CONF" ]; then - sed -E --in-place=.orig 's/^(HostKeyAlgorithms ssh-rsa,ssh-dss)$/# disabled by ipa-client update\n# \1/' "$SSH_CLIENT_SYSTEM_CONF" + if grep -E -q '^HostKeyAlgorithms ssh-rsa,ssh-dss' $SSH_CLIENT_SYSTEM_CONF 2>/dev/null; then + sed -E --in-place=.orig 's/^(HostKeyAlgorithms ssh-rsa,ssh-dss)$/# disabled by ipa-client update\n# \1/' "$SSH_CLIENT_SYSTEM_CONF" + fi # https://pagure.io/freeipa/issue/9536 # replace sss_ssh_knownhostsproxy with sss_ssh_knownhosts if [ -f '/usr/bin/sss_ssh_knownhosts' ]; then @@ -1854,6 +1864,14 @@ fi %endif %changelog +* Mon Jul 08 2024 Florence Blanc-Renaud - 4.12.1-2 +- Resolves: RHEL-46607 kdc.crt certificate not getting automatically renewed by certmonger in IPA Hidden replica +- Resolves: RHEL-46606 ipa-client rpm post script creates always ssh_config.orig even if nothing needs to be changed +- Resolves: RHEL-46605 IPA Web UI not showing replication agreement for non-admin users +- Resolves: RHEL-46592 [RFE] Allow IPA SIDgen task to continue if it finds an entity that SID can't be assigned to +- Resolves: RHEL-46556 Include latest fixes in python3-ipatests packages +- Resolves: RHEL-42705 PSKC.xml issues with ipa_otptoken_import.py + * Mon Jun 24 2024 Troy Dawson - 4.12.1-1.1 - Bump release for June 2024 mass rebuild