# Authors: # Petr Viktorin # # Copyright (C) 2014 Red Hat # see file 'COPYING' for use and warranty information # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . from __future__ import print_function, absolute_import import logging import os import re import contextlib import pytest from ipaplatform.constants import constants from ipaplatform.paths import paths from ipaplatform.tasks import tasks as platformtasks from ipapython.ipaldap import realm_to_serverid from ipapython.dn import DN from ipapython import ipautil from ipatests.test_integration.base import IntegrationTest from ipatests.pytest_ipa.integration import tasks from ipatests.test_integration.test_dnssec import wait_until_record_is_signed from ipatests.test_integration.test_simple_replication import check_replication from ipatests.test_integration.test_topology import find_segment from ipatests.util import assert_deepequal from ldap.dn import escape_dn_chars logger = logging.getLogger(__name__) def assert_entries_equal(a, b): assert_deepequal(a.dn, b.dn) assert_deepequal(dict(a), dict(b)) def assert_results_equal(a, b): def to_dict(r): return { 'stdout': r.stdout_text, 'stderr': r.stderr_text, 'returncode': r.returncode, } assert_deepequal(to_dict(a), to_dict(b)) def check_admin_in_ldap(host): ldap = host.ldap_connect() basedn = host.domain.basedn user_dn = DN(('uid', 'admin'), ('cn', 'users'), ('cn', 'accounts'), basedn) entry = ldap.get_entry(user_dn) print(entry) assert entry.dn == user_dn assert entry['uid'] == ['admin'] entry.pop('krbLastSuccessfulAuth', None) return entry def check_admin_in_cli(host): result = host.run_command(['ipa', 'user-show', 'admin']) assert 'User login: admin' in result.stdout_text, result.stdout_text # LDAP do not guarantee any order, so the test cannot assume it. Based on # that, the code bellow order the 'Member of groups' field to able to # assert it latter. data = dict(re.findall(r"\W*(.+):\W*(.+)\W*", result.stdout_text)) data["Member of groups"] = ', '.join(sorted(data["Member of groups"] .split(", "))) result.stdout_text = ''.join([' {}: {}\n'.format(k, v) for k, v in data.items()]) return result def check_admin_in_id(host): result = host.run_command(['id', 'admin']) assert 'admin' in result.stdout_text, result.stdout_text return result def check_certs(host): result = host.run_command(['ipa', 'cert-find']) assert re.search(r'^Number of entries returned [1-9]\d*$', result.stdout_text, re.MULTILINE), result.stdout_text return result def check_dns(host): result = host.run_command(['host', host.hostname, 'localhost']) return result def check_kinit(host): result = host.run_command(['kinit', 'admin'], stdin_text=host.config.admin_password) return result def check_custodia_files(host): """regression test for https://pagure.io/freeipa/issue/7247""" assert host.transport.file_exists(paths.IPA_CUSTODIA_KEYS) assert host.transport.file_exists(paths.IPA_CUSTODIA_CONF) return True def check_pkcs11_modules(host): """regression test for https://pagure.io/freeipa/issue/8073""" # Return a dictionary with key = filename, value = file content # containing all the PKCS11 modules modified by the installer result = dict() for filename in platformtasks.get_pkcs11_modules(): assert host.transport.file_exists(filename) result[filename] = host.get_file_contents(filename) return result CHECKS = [ (check_admin_in_ldap, assert_entries_equal), (check_admin_in_cli, assert_results_equal), (check_admin_in_id, assert_results_equal), (check_certs, assert_results_equal), (check_dns, assert_results_equal), (check_kinit, assert_results_equal), (check_custodia_files, assert_deepequal), (check_pkcs11_modules, assert_deepequal) ] @contextlib.contextmanager def restore_checker(host): """Check that the IPA at host works the same at context enter and exit""" tasks.kinit_admin(host) results = [] for check, assert_func in CHECKS: logger.info('Storing result for %s', check.__name__) results.append(check(host)) yield # Wait for SSSD to become online before doing any other check tasks.wait_for_sssd_domain_status_online(host) tasks.kinit_admin(host) for (check, assert_func), expected in zip(CHECKS, results): logger.info('Checking result for %s', check.__name__) got = check(host) assert_func(expected, got) @pytest.fixture def cert_sign_request(request): master = request.instance.master hosts = [master] + request.instance.replicas csrs = {} for host in hosts: request_path = host.run_command(['mktemp']).stdout_text.strip() openssl_command = [ 'openssl', 'req', '-new', '-nodes', '-out', request_path, '-subj', '/CN=' + master.hostname ] host.run_command(openssl_command) csrs[host.hostname] = request_path yield csrs for host in hosts: host.run_command(['rm', csrs[host.hostname]]) class TestBackupAndRestore(IntegrationTest): topology = 'star' def test_full_backup_and_restore(self): """backup, uninstall, restore""" with restore_checker(self.master): backup_path = tasks.get_backup_dir(self.master) self.master.run_command(['ipa-server-install', '--uninstall', '-U']) assert not self.master.transport.file_exists( paths.IPA_CUSTODIA_KEYS) assert not self.master.transport.file_exists( paths.IPA_CUSTODIA_CONF) dirman_password = self.master.config.dirman_password self.master.run_command(['ipa-restore', backup_path], stdin_text=dirman_password + '\nyes') # check the file permssion and ownership is set to 770 and # dirsrv:dirsrv after restore on /var/log/dirsrv/slapd- # related ticket : https://pagure.io/freeipa/issue/7725 instance = realm_to_serverid(self.master.domain.realm) log_path = paths.VAR_LOG_DIRSRV_INSTANCE_TEMPLATE % instance cmd = self.master.run_command(['stat', '-c', '"%a %G:%U"', log_path]) assert "770 dirsrv:dirsrv" in cmd.stdout_text def test_full_backup_and_restore_with_removed_users(self): """regression test for https://fedorahosted.org/freeipa/ticket/3866""" with restore_checker(self.master): backup_path = tasks.get_backup_dir(self.master) logger.info('Backup path for %s is %s', self.master, backup_path) self.master.run_command(['ipa-server-install', '--uninstall', '-U']) homedir = os.path.join(self.master.config.test_dir, 'testuser_homedir') self.master.run_command(['useradd', 'ipatest_user1', '--system', '-d', homedir]) try: dirman_password = self.master.config.dirman_password self.master.run_command(['ipa-restore', backup_path], stdin_text=dirman_password + '\nyes') finally: self.master.run_command(['userdel', 'ipatest_user1']) @pytest.mark.skipif( not platformtasks.is_selinux_enabled(), reason="Test needs SELinux enabled") def test_full_backup_and_restore_with_selinux_booleans_off(self): """regression test for https://fedorahosted.org/freeipa/ticket/4157""" with restore_checker(self.master): backup_path = tasks.get_backup_dir(self.master) logger.info('Backup path for %s is %s', self.master, backup_path) self.master.run_command(['ipa-server-install', '--uninstall', '-U']) self.master.run_command([ 'setsebool', '-P', 'httpd_can_network_connect=off', 'httpd_manage_ipa=off', ]) dirman_password = self.master.config.dirman_password self.master.run_command(['ipa-restore', backup_path], stdin_text=dirman_password + '\nyes') result = self.master.run_command([ 'getsebool', 'httpd_can_network_connect', 'httpd_manage_ipa', ]) assert 'httpd_can_network_connect --> on' in result.stdout_text assert 'httpd_manage_ipa --> on' in result.stdout_text class BaseBackupAndRestoreWithDNS(IntegrationTest): """ Abstract class for DNS restore tests """ topology = 'star' example_test_zone = "example.test." example2_test_zone = "example2.test." @classmethod def install(cls, mh): tasks.install_master(cls.master, setup_dns=True) def _full_backup_restore_with_DNS_zone(self, reinstall=False): """backup, uninstall, restore. Test for bug https://pagure.io/freeipa/issue/7630. """ with restore_checker(self.master): self.master.run_command([ 'ipa', 'dnszone-add', self.example_test_zone, ]) tasks.resolve_record(self.master.ip, self.example_test_zone) backup_path = tasks.get_backup_dir(self.master) self.master.run_command(['ipa-server-install', '--uninstall', '-U']) tasks.uninstall_packages(self.master, ['*ipa-server-dns']) dirman_password = self.master.config.dirman_password result = self.master.run_command( ['ipa-restore', backup_path], stdin_text=dirman_password + '\nyes', raiseonerr=False) assert 'Please install the package' in result.stderr_text tasks.install_packages(self.master, ['*ipa-server-dns']) if reinstall: tasks.install_master(self.master, setup_dns=True) self.master.run_command(['ipa-restore', backup_path], stdin_text=dirman_password + '\nyes') if reinstall: # If the server was reinstalled, reinstall may have changed # the uid and restore reverts to the original value. # clear the cache to make sure we get up-to-date values tasks.clear_sssd_cache(self.master) tasks.resolve_record(self.master.ip, self.example_test_zone) tasks.kinit_admin(self.master) self.master.run_command([ 'ipa', 'dnszone-add', self.example2_test_zone, ]) tasks.resolve_record(self.master.ip, self.example2_test_zone) class TestBackupAndRestoreWithDNS(BaseBackupAndRestoreWithDNS): def test_full_backup_and_restore_with_DNS_zone(self): """backup, uninstall, restore""" self._full_backup_restore_with_DNS_zone(reinstall=False) class TestBackupReinstallRestoreWithDNS(BaseBackupAndRestoreWithDNS): def test_full_backup_reinstall_restore_with_DNS_zone(self): """backup, uninstall, reinstall, restore""" self._full_backup_restore_with_DNS_zone(reinstall=True) class BaseBackupAndRestoreWithDNSSEC(IntegrationTest): """ Abstract class for DNSSEC restore tests """ topology = 'star' example_test_zone = "example.test." example2_test_zone = "example2.test." @classmethod def install(cls, mh): tasks.install_master(cls.master, setup_dns=True) args = [ "ipa-dns-install", "--dnssec-master", "--forwarder", cls.master.config.dns_forwarder, "-U", ] cls.master.run_command(args) def _full_backup_and_restore_with_DNSSEC_zone(self, reinstall=False): with restore_checker(self.master): self.master.run_command([ 'ipa', 'dnszone-add', self.example_test_zone, '--dnssec', 'true', ]) assert ( wait_until_record_is_signed( self.master.ip, self.example_test_zone) ), "Zone is not signed" backup_path = tasks.get_backup_dir(self.master) self.master.run_command(['ipa-server-install', '--uninstall', '-U']) if reinstall: tasks.install_master(self.master, setup_dns=True) dirman_password = self.master.config.dirman_password self.master.run_command(['ipa-restore', backup_path], stdin_text=dirman_password + '\nyes') if reinstall: # If the server was reinstalled, reinstall may have changed # the uid and restore reverts to the original value. # clear the cache to make sure we get up-to-date values tasks.clear_sssd_cache(self.master) assert ( wait_until_record_is_signed( self.master.ip, self.example_test_zone) ), "Zone is not signed after restore" tasks.kinit_admin(self.master) self.master.run_command([ 'ipa', 'dnszone-add', self.example2_test_zone, '--dnssec', 'true', ]) assert ( wait_until_record_is_signed( self.master.ip, self.example2_test_zone) ), "A new zone is not signed" class TestBackupAndRestoreWithDNSSEC(BaseBackupAndRestoreWithDNSSEC): def test_full_backup_and_restore_with_DNSSEC_zone(self): """backup, uninstall, restore""" self._full_backup_and_restore_with_DNSSEC_zone(reinstall=False) class TestBackupReinstallRestoreWithDNSSEC(BaseBackupAndRestoreWithDNSSEC): def test_full_backup_reinstall_restore_with_DNSSEC_zone(self): """backup, uninstall, install, restore""" self._full_backup_and_restore_with_DNSSEC_zone(reinstall=True) class BaseBackupAndRestoreWithKRA(IntegrationTest): """ Abstract class for KRA restore tests """ topology = 'star' vault_name = "ci_test_vault" vault_password = "password" vault_data = "SSBsb3ZlIENJIHRlc3RzCg==" @classmethod def install(cls, mh): tasks.install_master(cls.master, setup_dns=True, setup_kra=True) def _full_backup_restore_with_vault(self, reinstall=False): with restore_checker(self.master): # create vault self.master.run_command([ "ipa", "vault-add", self.vault_name, "--password", self.vault_password, "--type", "symmetric", ]) # archive secret self.master.run_command([ "ipa", "vault-archive", self.vault_name, "--password", self.vault_password, "--data", self.vault_data, ]) # retrieve secret self.master.run_command([ "ipa", "vault-retrieve", self.vault_name, "--password", self.vault_password, ]) backup_path = tasks.get_backup_dir(self.master) # check that no error message in uninstall log for KRA instance cmd = self.master.run_command(['ipa-server-install', '--uninstall', '-U']) assert "failed to uninstall KRA" not in cmd.stderr_text if reinstall: tasks.install_master(self.master, setup_dns=True) dirman_password = self.master.config.dirman_password self.master.run_command(['ipa-restore', backup_path], stdin_text=dirman_password + '\nyes') if reinstall: # If the server was reinstalled, reinstall may have changed # the uid and restore reverts to the original value. # clear the cache to make sure we get up-to-date values tasks.clear_sssd_cache(self.master) tasks.kinit_admin(self.master) # retrieve secret after restore self.master.run_command([ "ipa", "vault-retrieve", self.vault_name, "--password", self.vault_password, ]) class TestBackupAndRestoreWithKRA(BaseBackupAndRestoreWithKRA): def test_full_backup_restore_with_vault(self): """backup, uninstall, restore""" self._full_backup_restore_with_vault(reinstall=False) class TestBackupReinstallRestoreWithKRA(BaseBackupAndRestoreWithKRA): def test_full_backup_reinstall_restore_with_vault(self): """backup, uninstall, reinstall, restore""" self._full_backup_restore_with_vault(reinstall=True) def test_no_error_message_with_uninstall_ipa_with_kra(self): """Test there is no error message in uninstall log for KRA instance There was error message in uninstall log when IPA with KRA was uninstalled. This test check that there is no error message in uninstall log for kra instance. related: https://pagure.io/freeipa/issue/8550 """ cmd = self.master.run_command(['ipa-server-install', '--uninstall', '-U']) assert "failed to uninstall KRA" not in cmd.stderr_text class TestBackupAndRestoreWithReplica(IntegrationTest): """Regression tests for issues 7234 and 7455 https://pagure.io/freeipa/issue/7234 - check that oddjobd service is started after restore - check new replica setup after restore https://pagure.io/freeipa/issue/7455 check that after restore and replication reinitialization - users and CA data are at state before backup - CA can be installed on existing replica - new replica with CA can be setup """ num_replicas = 2 topology = "star" @classmethod def install(cls, mh): cls.replica1 = cls.replicas[0] cls.replica2 = cls.replicas[1] if cls.domain_level is None: domain_level = cls.master.config.domain_level else: domain_level = cls.domain_level # Configure only master and one replica. # Replica is configured without CA tasks.install_topo( cls.topology, cls.master, [cls.replica1], cls.clients, domain_level, setup_replica_cas=False ) def get_users(self, host): res = host.run_command(['ipa', 'user-find']) users = set() for line in res.stdout_text.splitlines(): k, _unused, v = line.strip().partition(': ') if k == 'User login': users.add(v) return users def check_replication_error(self, host): status = r'Error \(19\) Replication error acquiring replica: ' \ 'Replica has different database generation ID' tasks.wait_for_replication( host.ldap_connect(), target_status_re=status, raise_on_timeout=True) def check_replication_success(self, host): status = r'Error \(0\) Replica acquired successfully: ' \ 'Incremental update succeeded' tasks.wait_for_replication( host.ldap_connect(), target_status_re=status, raise_on_timeout=True) def request_test_service_cert(self, host, request_path, expect_connection_error=False): res = host.run_command([ 'ipa', 'cert-request', '--principal=TEST/' + self.master.hostname, request_path ], raiseonerr=not expect_connection_error) if expect_connection_error: assert (1 == res.returncode and '[Errno 111] Connection refused' in res.stderr_text) def test_full_backup_and_restore_with_replica(self, cert_sign_request): # check prerequisites self.check_replication_success(self.master) self.check_replication_success(self.replica1) self.master.run_command( ['ipa', 'service-add', 'TEST/' + self.master.hostname]) tasks.user_add(self.master, 'test1_master') tasks.user_add(self.replica1, 'test1_replica') with restore_checker(self.master): backup_path = tasks.get_backup_dir(self.master) # change data after backup self.master.run_command(['ipa', 'user-del', 'test1_master']) self.replica1.run_command(['ipa', 'user-del', 'test1_replica']) tasks.user_add(self.master, 'test2_master') tasks.user_add(self.replica1, 'test2_replica') # simulate master crash self.master.run_command(['ipactl', 'stop']) tasks.uninstall_master(self.master, clean=False) logger.info("Stopping and disabling oddjobd service") self.master.run_command([ "systemctl", "stop", "oddjobd" ]) self.master.run_command([ "systemctl", "disable", "oddjobd" ]) self.master.run_command(['ipa-restore', '-U', backup_path]) status = self.master.run_command([ "systemctl", "status", "oddjobd" ]) assert "active (running)" in status.stdout_text # replication should not work after restoration # create users to force master and replica to try to replicate tasks.user_add(self.master, 'test3_master') tasks.user_add(self.replica1, 'test3_replica') self.check_replication_error(self.master) self.check_replication_error(self.replica1) assert {'admin', 'test1_master', 'test1_replica', 'test3_master'} == \ self.get_users(self.master) assert {'admin', 'test2_master', 'test2_replica', 'test3_replica'} == \ self.get_users(self.replica1) # reestablish and check replication self.replica1.run_command(['ipa-replica-manage', 're-initialize', '--from', self.master.hostname]) # create users to force master and replica to try to replicate tasks.user_add(self.master, 'test4_master') tasks.user_add(self.replica1, 'test4_replica') self.check_replication_success(self.master) self.check_replication_success(self.replica1) assert {'admin', 'test1_master', 'test1_replica', 'test3_master', 'test4_master', 'test4_replica'} == \ self.get_users(self.master) assert {'admin', 'test1_master', 'test1_replica', 'test3_master', 'test4_master', 'test4_replica'} == \ self.get_users(self.replica1) # CA on master should be accesible from master and replica self.request_test_service_cert( self.master, cert_sign_request[self.master.hostname]) self.request_test_service_cert( self.replica1, cert_sign_request[self.replica1.hostname]) # replica should not be able to sign certificates without CA on master self.master.run_command(['ipactl', 'stop']) try: self.request_test_service_cert( self.replica1, cert_sign_request[self.replica1.hostname], expect_connection_error=True) finally: self.master.run_command(['ipactl', 'start']) tasks.install_ca(self.replica1) # now replica should be able to sign certificates without CA on master self.master.run_command(['ipactl', 'stop']) self.request_test_service_cert( self.replica1, cert_sign_request[self.replica1.hostname]) self.master.run_command(['ipactl', 'start']) # check installation of new replica tasks.install_replica(self.master, self.replica2, setup_ca=True) check_replication(self.master, self.replica2, "testuser") # new replica should be able to sign certificates without CA on master # and old replica self.master.run_command(['ipactl', 'stop']) self.replica1.run_command(['ipactl', 'stop']) try: self.request_test_service_cert( self.replica2, cert_sign_request[self.replica2.hostname]) finally: self.replica1.run_command(['ipactl', 'start']) self.master.run_command(['ipactl', 'start']) class TestUserRootFilesOwnershipPermission(IntegrationTest): """Test to check if userroot.ldif have proper ownership. Before the fix, when ipa-backup was called for the first time, the LDAP database exported to /var/lib/dirsrv/slapd-/ldif/-userRoot.ldif. db2ldif is called for this and it runs under root, hence files were owned by root. When ipa-backup called the next time, the db2ldif fails, because the tool does not have permissions to write to the ldif file which was owned by root (instead of dirsrv). This test check if files are owned by dirsrv and db2ldif doesn't fail related ticket: https://pagure.io/freeipa/issue/7010 This test also checks if the access rights for user/group are set and umask 0022 set while restoring. related ticket: https://pagure.io/freeipa/issue/6844 """ @classmethod def install(cls, mh): super(TestUserRootFilesOwnershipPermission, cls).install(mh) cls.bashrc_file = cls.master.get_file_contents('/root/.bashrc') def test_userroot_ldif_files_ownership_and_permission(self): """backup, uninstall, restore, backup""" tasks.install_master(self.master) backup_path = tasks.get_backup_dir(self.master) self.master.run_command(['ipa-server-install', '--uninstall', '-U']) # set umask to 077 just to check if restore success. self.master.run_command('echo "umask 0077" >> /root/.bashrc') result = self.master.run_command(['umask']) assert '0077' in result.stdout_text dirman_password = self.master.config.dirman_password result = self.master.run_command(['ipa-restore', backup_path], stdin_text=dirman_password + '\nyes') assert 'Temporary setting umask to 022' in result.stderr_text # check if umask reset to 077 after restore. result = self.master.run_command(['umask']) assert '0077' in result.stdout_text # check if files have proper owner and group. dashed_domain = self.master.domain.realm.replace(".", '-') arg = ['stat', '-c', '%U:%G', '{}/ldif/'.format( paths.VAR_LIB_SLAPD_INSTANCE_DIR_TEMPLATE % dashed_domain)] cmd = self.master.run_command(arg) expected = '{}:{}'.format(constants.DS_USER, constants.DS_GROUP) assert expected in cmd.stdout_text # also check of access rights are set to 644. arg = ['stat', '-c', '%U:%G:%a', '{}/ldif/{}-ipaca.ldif'.format( paths.VAR_LIB_SLAPD_INSTANCE_DIR_TEMPLATE % dashed_domain, dashed_domain)] cmd = self.master.run_command(arg) assert '{}:644'.format(expected) in cmd.stdout_text arg = ['stat', '-c', '%U:%G:%a', '{}/ldif/{}-userRoot.ldif'.format( paths.VAR_LIB_SLAPD_INSTANCE_DIR_TEMPLATE % dashed_domain, dashed_domain)] cmd = self.master.run_command(arg) assert '{}:644'.format(expected) in cmd.stdout_text cmd = self.master.run_command(['ipa-backup', '-d']) unexp_str = "CRITICAL: db2ldif failed:" assert cmd.returncode == 0 assert unexp_str not in cmd.stdout_text def test_files_ownership_and_permission_teardown(self): """ Method to restore the default bashrc contents""" if self.bashrc_file is not None: self.master.put_file_contents('/root/.bashrc', self.bashrc_file) class TestBackupAndRestoreDMPassword(IntegrationTest): """Negative tests for incorrect DM password""" topology = 'star' def test_restore_bad_dm_password(self): """backup, uninstall, restore, wrong DM password (expect failure)""" with restore_checker(self.master): backup_path = tasks.get_backup_dir(self.master) # No uninstall, just pure restore, the only case where # prompting for the DM password matters. result = self.master.run_command(['ipa-restore', backup_path], stdin_text='badpass\nyes', raiseonerr=False) assert result.returncode == 1 def test_restore_dirsrv_not_running(self): """backup, restore, dirsrv not running (expect failure)""" # Flying blind without the restore_checker so we can have # an error thrown when dirsrv is down. backup_path = tasks.get_backup_dir(self.master) self.master.run_command(['ipactl', 'stop']) dirman_password = self.master.config.dirman_password result = self.master.run_command( ['ipa-restore', backup_path], stdin_text=dirman_password + '\nyes', raiseonerr=False) assert result.returncode == 1 class TestReplicaInstallAfterRestore(IntegrationTest): """Test to check second replica installation after master restore When master is restored from backup and replica1 is re-initialize, second replica installation was failing. The issue was with ipa-backup tool which was not backing up the /etc/ipa/custodia/custodia.conf and /etc/ipa/custodia/server.keys. related ticket: https://pagure.io/freeipa/issue/7247 """ num_replicas = 2 def test_replica_install_after_restore(self): master = self.master replica1 = self.replicas[0] replica2 = self.replicas[1] tasks.install_master(master) tasks.install_replica(master, replica1) check_replication(master, replica1, "testuser1") # backup master. backup_path = tasks.get_backup_dir(self.master) suffix = ipautil.realm_to_suffix(master.domain.realm) suffix = escape_dn_chars(str(suffix)) entry_ldif = ( "dn: cn=meTo{hostname},cn=replica," "cn={suffix}," "cn=mapping tree,cn=config\n" "changetype: modify\n" "replace: nsds5ReplicaEnabled\n" "nsds5ReplicaEnabled: off\n\n" "dn: cn=caTo{hostname},cn=replica," "cn=o\\3Dipaca,cn=mapping tree,cn=config\n" "changetype: modify\n" "replace: nsds5ReplicaEnabled\n" "nsds5ReplicaEnabled: off").format( hostname=replica1.hostname, suffix=suffix) # disable replication agreement tasks.ldapmodify_dm(master, entry_ldif) # uninstall master. tasks.uninstall_master(master, clean=False) # master restore. dirman_password = master.config.dirman_password master.run_command(['ipa-restore', backup_path], stdin_text=dirman_password + '\nyes') # re-initialize topology after restore. for topo_suffix in 'domain', 'ca': topo_name = find_segment(master, replica1, topo_suffix) arg = ['ipa', 'topologysegment-reinitialize', topo_suffix, topo_name] if topo_name.split('-to-', maxsplit=1)[0] != master.hostname: arg.append('--left') else: arg.append('--right') replica1.run_command(arg) # wait sometime for re-initialization tasks.wait_for_replication(replica1.ldap_connect()) # install second replica after restore tasks.install_replica(master, replica2) check_replication(master, replica2, "testuser2") class TestBackupAndRestoreTrust(IntegrationTest): """Test for Backup and Restore for Trust scenario""" topology = 'star' def test_restore_trust_pkg_before_restore(self): """Test restore with adtrust when trust-ad pkg is missing. Test for bug https://pagure.io/freeipa/issue/7630. """ tasks.install_packages(self.master, ['*ipa-server-trust-ad']) self.master.run_command(['ipa-adtrust-install', '-U', '--enable-compat', '--netbios-name', 'IPA', '-a', self.master.config.admin_password, '--add-sids']) with restore_checker(self.master): backup_path = tasks.get_backup_dir(self.master) self.master.run_command(['ipa-server-install', '--uninstall', '-U']) tasks.uninstall_packages(self.master, ['*ipa-server-trust-ad']) dirman_password = self.master.config.dirman_password result = self.master.run_command( ['ipa-restore', backup_path], stdin_text=dirman_password + '\nyes', raiseonerr=False) assert 'Please install the package' in result.stderr_text tasks.install_packages(self.master, ['*ipa-server-trust-ad']) self.master.run_command(['ipa-restore', backup_path], stdin_text=dirman_password + '\nyes') class TestBackupRoles(IntegrationTest): """ipa-backup should only run on replicas with all the in-use roles https://pagure.io/freeipa/issue/8217 """ num_replicas = 1 topology = 'star' serverroles = { 'ADTA': u'AD trust agent', 'ADTC': u'AD trust controller', 'CA': u'CA server', 'DNS': u'DNS server', 'KRA': u'KRA server' } @classmethod def install(cls, mh): tasks.install_master(cls.master, setup_dns=True) def _check_rolecheck_backup_failure(self, host): result = tasks.ipa_backup(host, raiseonerr=False) assert result.returncode == 1 assert "Error: Local roles" in result.stderr_text assert "do not match globally used roles" in result.stderr_text result = tasks.ipa_backup(host, disable_role_check=True) assert result.returncode == 0 assert "Warning: Local roles" in result.stderr_text assert "do not match globally used roles" in result.stderr_text def _check_rolecheck_backup_success(self, host): result = tasks.ipa_backup(host) assert result.returncode == 0 assert "Local roles match globally used roles, proceeding." \ in result.stderr_text def _ipa_replica_role_check(self, replica, role): return "enabled" in self.master.run_command([ 'ipa', 'server-role-find', '--server', replica, '--role', role ]).stdout_text def test_rolecheck_DNS_CA(self): """ipa-backup rolecheck: start with a master with DNS and CA then gradually upgrade a replica to the DNS and CA roles. """ # single master: check that backup works. assert self._ipa_replica_role_check( self.master.hostname, self.serverroles['DNS'] ) assert self._ipa_replica_role_check( self.master.hostname, self.serverroles['CA'] ) assert not self._ipa_replica_role_check( self.master.hostname, self.serverroles['KRA'] ) self._check_rolecheck_backup_success(self.master) # install CA-less, DNS-less replica tasks.install_replica(self.master, self.replicas[0], setup_ca=False) assert not self._ipa_replica_role_check( self.replicas[0].hostname, self.serverroles['DNS'] ) assert not self._ipa_replica_role_check( self.replicas[0].hostname, self.serverroles['CA'] ) assert not self._ipa_replica_role_check( self.replicas[0].hostname, self.serverroles['KRA'] ) self._check_rolecheck_backup_success(self.master) self._check_rolecheck_backup_failure(self.replicas[0]) # install DNS on replica tasks.install_dns(self.replicas[0]) assert self._ipa_replica_role_check( self.replicas[0].hostname, self.serverroles['DNS'] ) self._check_rolecheck_backup_failure(self.replicas[0]) def test_rolecheck_KRA(self): """ipa-backup rolecheck: add a KRA. """ # install CA on replica. tasks.install_ca(self.replicas[0]) # master and replicas[0] have matching roles now. assert self._ipa_replica_role_check( self.replicas[0].hostname, self.serverroles['CA'] ) self._check_rolecheck_backup_success(self.master) self._check_rolecheck_backup_success(self.replicas[0]) # install KRA on replica tasks.install_kra(self.replicas[0], first_instance=True) assert self._ipa_replica_role_check( self.replicas[0].hostname, self.serverroles['KRA'] ) self._check_rolecheck_backup_success(self.replicas[0]) self._check_rolecheck_backup_failure(self.master) # install KRA on master tasks.install_kra(self.master) # master and replicas[0] have matching roles now. assert self._ipa_replica_role_check( self.master.hostname, self.serverroles['KRA'] ) self._check_rolecheck_backup_success(self.replicas[0]) self._check_rolecheck_backup_success(self.master) def test_rolecheck_Trust(self): """ipa-backup rolecheck: add Trust controllers/agents. """ # install AD Trust packages on replica first tasks.install_packages(self.replicas[0], ['*ipa-server-trust-ad']) self._check_rolecheck_backup_success(self.replicas[0]) self._check_rolecheck_backup_success(self.master) tasks.install_packages(self.master, ['*ipa-server-trust-ad']) # make replicas[0] become Trust Controller self.replicas[0].run_command([ 'ipa-adtrust-install', '-U', '--netbios-name', 'IPA', '-a', self.master.config.admin_password, '--add-sids' ]) # wait for replication to propagate the change on # cn=adtrust agents,cn=sysaccounts,cn=etc,dc=ipa,dc=test # as the ipa server-role-find call is using this entry to # build its output tasks.wait_for_replication(self.replicas[0].ldap_connect()) # double-check assert self._ipa_replica_role_check( self.replicas[0].hostname, self.serverroles['ADTC'] ) # check that master has no AD Trust-related role assert not self._ipa_replica_role_check( self.master.hostname, self.serverroles['ADTA'] ) self._check_rolecheck_backup_success(self.replicas[0]) self._check_rolecheck_backup_failure(self.master) # make master become Trust Agent cmd_input = ( # admin password: self.master.config.admin_password + '\n' + # WARNING: The smb.conf already exists. Running ipa-adtrust-install # will break your existing samba configuration. # Do you wish to continue? [no]: 'yes\n' # Enable trusted domains support in slapi-nis? [no]: '\n' + # WARNING: 1 IPA masters are not yet able to serve information # about users from trusted forests. # Installer can add them to the list of IPA masters allowed to # access information about trusts. # If you choose to do so, you also need to restart LDAP service on # those masters. # Refer to ipa-adtrust-install(1) man page for details. # IPA master[replica1.testrelm.test]?[no]: 'yes\n' ) self.replicas[0].run_command([ 'ipa-adtrust-install', '--add-agents'], stdin_text=cmd_input ) # wait for replication to propagate the change on # cn=adtrust agents,cn=sysaccounts,cn=etc,dc=ipa,dc=test # as the ipa server-role-find call is using this entry to # build its output tasks.wait_for_replication(self.replicas[0].ldap_connect()) # check that master is now an AD Trust agent assert self._ipa_replica_role_check( self.master.hostname, self.serverroles['ADTA'] ) # but not an AD Trust controller assert not self._ipa_replica_role_check( self.master.hostname, self.serverroles['ADTC'] ) self._check_rolecheck_backup_success(self.replicas[0]) self._check_rolecheck_backup_failure(self.master) # make master become Trust Controller self.master.run_command([ 'ipa-adtrust-install', '-U', '--netbios-name', 'IPA', '-a', self.master.config.admin_password, '--add-sids' ]) # wait for replication to propagate the change on # cn=adtrust agents,cn=sysaccounts,cn=etc,dc=ipa,dc=test # as the ipa server-role-find call is using this entry to # build its output tasks.wait_for_replication(self.master.ldap_connect()) # master and replicas[0] are both AD Trust Controllers now. for hostname in [self.master.hostname, self.replicas[0].hostname]: assert self._ipa_replica_role_check( hostname, self.serverroles['ADTC'] ) self._check_rolecheck_backup_success(self.master) self._check_rolecheck_backup_success(self.replicas[0]) # switch replicas[0] to hidden self.replicas[0].run_command([ 'ipa', 'server-state', self.replicas[0].hostname, '--state=hidden' ]) self._check_rolecheck_backup_success(self.master) self._check_rolecheck_backup_success(self.replicas[0])