From b758be1f5152c8bb75d29e01655d311d9821059c Mon Sep 17 00:00:00 2001 From: Tomas Babej Date: Wed, 4 Sep 2013 16:29:06 +0200 Subject: ipatests: Add AD-integration related tasks Part of: https://fedorahosted.org/freeipa/ticket/3834 --- ipatests/ipa-test-task | 117 ++++++++++++++++++++++-- ipatests/man/ipa-test-task.1 | 32 +++++++ ipatests/test_integration/tasks.py | 180 +++++++++++++++++++++++++++++++++++++ 3 files changed, 324 insertions(+), 5 deletions(-) diff --git a/ipatests/ipa-test-task b/ipatests/ipa-test-task index 1e204648..e6ba527e 100755 --- a/ipatests/ipa-test-task +++ b/ipatests/ipa-test-task @@ -27,6 +27,7 @@ import argparse from ipapython.ipa_log_manager import log_mgr, standard_logging_setup from ipatests.test_integration import config from ipatests.test_integration import tasks +from ipatests.test_integration.host import Host from ipatests.beakerlib_plugin import BeakerLibProcess @@ -39,10 +40,10 @@ class TaskRunner(object): def get_parser(self): parser = argparse.ArgumentParser( - description="Perform an operation for integration testing." - "All operations are performed on configured hosts, see" - "http://www.freeipa.org/page/V3/Integration_testing" - "see for configuration details") + description="Perform an operation for integration testing. " + "All operations are performed on configured hosts, see " + "http://www.freeipa.org/page/V3/Integration_testing " + "for configuration details") parser.add_argument('--with-beakerlib', action='store_true', dest='with_beakerlib', @@ -153,6 +154,65 @@ class TaskRunner(object): (Default: all hosts from config)""") subparser.set_defaults(func=self.cleanup) + subparser = subparsers.add_parser( + 'install-adtrust', + help='Runs ipa-adtrust-install on the host') + subparser.add_argument('host', type=str, + help='Host to run ipa-adtrust-install on') + subparser.set_defaults(func=self.install_adtrust) + + subparser = subparsers.add_parser( + 'configure-dns-for-trust', + help='Sets DNS on the given host for trust with the given AD') + subparser.add_argument('host', type=str, + help='Host to change DNS configuration on') + subparser.add_argument('ad', type=str, + help='AD that trust will be established with') + subparser.set_defaults(func=self.configure_dns_for_trust) + + subparser = subparsers.add_parser( + 'establish-trust-with-ad', + help='Establishes trust between IPA host and AD') + subparser.add_argument('host', type=str, + help='IPA Host to establish AD trust on') + subparser.add_argument('ad', type=str, + help='AD to establish trust with') + subparser.set_defaults(func=self.establish_trust_with_ad) + + subparser = subparsers.add_parser( + 'remove-trust-with-ad', + help='Removes trust between IPA host and AD') + subparser.add_argument('host', type=str, + help='IPA Host to remove AD trust on') + subparser.add_argument('ad', type=str, + help='AD to remove trust with') + subparser.set_defaults(func=self.remove_trust_with_ad) + + subparser = subparsers.add_parser( + 'configure-auth-to-local-rule', + help='Configures auth_to_local rule on IPA host with respect to AD') + subparser.add_argument('host', type=str, + help='IPA Host to configure auth_to_local rule on') + subparser.add_argument('ad', type=str, + help='AD to configure the rule with') + subparser.set_defaults(func=self.configure_auth_to_local_rule) + + subparser = subparsers.add_parser( + 'clear-sssd-cache', + help='Clears SSSD cache on the IPA host.') + subparser.add_argument('host', type=str, + help='IPA Host to clear SSSD cache on') + subparser.set_defaults(func=self.clear_sssd_cache) + + subparser = subparsers.add_parser( + 'sync-time', + help='Synchronize time on host with respect to server') + subparser.add_argument('host', type=str, + help='IPA Host to set the time on') + subparser.add_argument('server', type=str, + help='Server that serves as a time source') + subparser.set_defaults(func=self.sync_time) + return parser def main(self, argv): @@ -180,6 +240,11 @@ class TaskRunner(object): args.domain = self.config.domains[0] + if self.config.ad_domains: + args.ad_domain = self.config.ad_domains[0] + else: + args.ad_domain = None + import logging; logging.basicConfig() try: @@ -214,12 +279,17 @@ class TaskRunner(object): return [self.prepare_host(h) for h in default] def prepare_host(self, host): - if host not in self._prepared_hosts: + # Prepare only UNIX hosts + if host not in self._prepared_hosts and isinstance(host, Host): host.add_log_collector(self.collect_log) tasks.prepare_host(host) self._prepared_hosts.add(host) return host + def require_ad_domain(self, args): + if not args.ad_domain: + SystemExit("At least one AD domain is required for this task") + def install_master(self, args): master = self.get_host(args.host, default=args.domain.master) log.info('Installing master %s', master.hostname) @@ -289,6 +359,43 @@ class TaskRunner(object): tasks.install_topo(args.topo, master, replicas, clients, skip_master=args.skip_master) + def install_adtrust(self, args): + master = self.get_host(args.host, default=args.domain.master) + log.info('Configuring AD trust support on %s', master.hostname) + tasks.install_adtrust(master) + + def configure_dns_for_trust(self, args): + self.require_ad_domain(args) + host = self.get_host(args.host, default=args.domain.master) + ad = self.get_host(args.ad, default=args.ad_domain.ads[0]) + tasks.configure_dns_for_trust(host, ad) + + def establish_trust_with_ad(self, args): + self.require_ad_domain(args) + host = self.get_host(args.host, default=args.domain.master) + ad = self.get_host(args.ad, default=args.ad_domain.ads[0]) + tasks.establish_trust_with_ad(host, ad) + + def remove_trust_with_ad(self, args): + self.require_ad_domain(args) + host = self.get_host(args.host, default=args.domain.master) + ad = self.get_host(args.ad, default=args.ad_domain.ads[0]) + tasks.remove_trust_with_ad(host, ad) + + def configure_auth_to_local_rule(self, args): + self.require_ad_domain(args) + host = self.get_host(args.host, default=args.domain.master) + ad = self.get_host(args.ad, default=args.ad_domain.ads[0]) + tasks.configure_auth_to_local_rule(host, ad) + + def clear_sssd_cache(self, args): + host = self.get_host(args.host, default=args.domain.master) + tasks.clear_sssd_cache(host) + + def sync_time(self, args): + host = self.get_host(args.host, default=args.domain.master) + server = self.get_host(args.server) + tasks.sync_time(host, server) if __name__ == '__main__': exit(TaskRunner().main(sys.argv[1:])) diff --git a/ipatests/man/ipa-test-task.1 b/ipatests/man/ipa-test-task.1 index b625b285..e73584bd 100644 --- a/ipatests/man/ipa-test-task.1 +++ b/ipatests/man/ipa-test-task.1 @@ -116,6 +116,38 @@ Servers used for client installation are selected in a round-robin fashion. \fBipa\-test\-task list-topos\fR List the topologies available for the install-topo subcommand. +.TP +\fBipa\-test\-task install\-adtrust HOST\fR +Run ipa-adtrust-install on the IPA and generate SIDs for the entries in IPA. + +.TP +\fBipa\-test\-task configure\-dns\-for\-trust HOST AD\fR +Based on the relationship of the domains configures the IPA DNS for trust. +AD DNS needs to be setup manually. + +.TP +\fBipa\-test\-task estabilish\-trust\-with\-ad HOST AD\fR +Estabilishes trust with Active Directory. Trust type is detected depending on +the presence of SfU (Services for Unix) support on the AD. + +.TP +\fBipa\-test\-task remove\-trust\-with\-ad HOST AD\fR +Removes trust with Active Directory. Also removes the associated ID range. + +.TP +\fBipa\-test\-task configure\-auth\-to\-local\-rule HOST AD\fR +Configures auth_to_local rule in /etc/krb5.conf + +.TP +\fBipa\-test\-task clear\-sssd\-cache HOST\fR +Clears SSSD cache by removing the cache files. Restarts SSSD. + +.TP +\fBipa\-test\-task sync\-time HOST SERVER\fR +Syncs the time with the remote server. Please note that this function leaves +ntpd stopped. + + .SH "EXIT STATUS" 0 if the command was successful diff --git a/ipatests/test_integration/tasks.py b/ipatests/test_integration/tasks.py index de650f58..a93a583e 100644 --- a/ipatests/test_integration/tasks.py +++ b/ipatests/test_integration/tasks.py @@ -32,6 +32,7 @@ from ldif import LDIFWriter from ipapython import ipautil from ipapython.dn import DN from ipapython.ipa_log_manager import log_mgr +from ipatests.test_integration import util from ipatests.test_integration.config import env_to_script log = log_mgr.get_logger(__name__) @@ -197,6 +198,7 @@ def install_replica(master, replica, setup_ca=True): kinit_admin(replica) + def install_client(master, client): client.collect_log('/var/log/ipaclient-install.log') @@ -212,6 +214,184 @@ def install_client(master, client): kinit_admin(client) +def install_adtrust(host): + """ + Runs ipa-adtrust-install on the client and generates SIDs for the entries. + Configures the compat tree for the legacy clients. + """ + + # ipa-adtrust-install appends to ipaserver-install.log + host.collect_log('/var/log/ipaserver-install.log') + + inst = host.domain.realm.replace('.', '-') + host.collect_log('/var/log/dirsrv/slapd-%s/errors' % inst) + host.collect_log('/var/log/dirsrv/slapd-%s/access' % inst) + + kinit_admin(host) + host.run_command(['ipa-adtrust-install', '-U', + '--enable-compat', + '--netbios-name', host.netbios, + '-a', host.config.admin_password, + '--add-sids']) + + # Restart named because it lost connection to dirsrv + # (Directory server restarts during the ipa-adtrust-install) + host.run_command(['systemctl', 'restart', 'named']) + + # Check that named is running and has loaded the information from LDAP + dig_command = ['dig', 'SRV', '+short', '@localhost', + '_ldap._tcp.%s' % host.domain.name] + dig_output = '0 100 389 %s.' % host.hostname + dig_test = lambda x: re.search(re.escape(dig_output), x) + + util.run_repeatedly(host, dig_command, test=dig_test) + + +def configure_dns_for_trust(master, ad): + """ + This configures DNS on IPA master according to the relationship of the + IPA's and AD's domains. + """ + + def is_subdomain(subdomain, domain): + subdomain_unpacked = subdomain.split('.') + domain_unpacked = domain.split('.') + + subdomain_unpacked.reverse() + domain_unpacked.reverse() + + subdomain = False + + if len(subdomain_unpacked) > len(domain_unpacked): + subdomain = True + + for subdomain_segment, domain_segment in zip(subdomain_unpacked, + domain_unpacked): + subdomain = subdomain and subdomain_segment == domain_segment + + return subdomain + + kinit_admin(master) + + if is_subdomain(master.domain.name, ad.domain.name): + master.run_command(['ipa', 'dnszone-add', ad.domain.name, + '--name-server', ad.hostname, + '--admin-email', 'hostmaster@%s' % ad.domain.name, + '--forwarder', ad.ip, + '--forward-policy', 'only', + '--ip-address', ad.ip, + '--force']) + elif is_subdomain(ad.domain.name, master.domain.name): + master.run_command(['ipa', 'dnsrecord-add', master.domain.name, + '%s.%s' % (ad.shortname, ad.netbios), + '--a-ip-address', ad.ip]) + + master.run_command(['ipa', 'dnsrecord-add', master.domain.name, + ad.netbios, + '--ns-hostname', + '%s.%s' % (ad.shortname, ad.netbios)]) + + master.run_command(['ipa', 'dnszone-mod', master.domain.name, + '--allow-transfer', ad.ip]) + else: + master.run_command(['ipa', 'dnszone-add', ad.domain.name, + '--name-server', ad.hostname, + '--admin-email', 'hostmaster@%s' % ad.domain.name, + '--forwarder', ad.ip, + '--forward-policy', 'only', + '--ip-address', ad.ip, + '--force']) + + +def establish_trust_with_ad(master, ad, extra_args=()): + """ + Establishes trust with Active Directory. Trust type is detected depending + on the presence of SfU (Services for Unix) support on the AD. + + Use extra arguments to pass extra arguments to the trust-add command, such + as --range-type="ipa-ad-trust" to enfroce a particular range type. + """ + + # Force KDC to reload MS-PAC info by trying to get TGT for HTTP + master.run_command(['kinit', '-kt', '/etc/httpd/conf/ipa.keytab', + 'HTTP/%s' % master.hostname]) + master.run_command(['systemctl', 'restart', 'krb5kdc.service']) + master.run_command(['kdestroy', '-A']) + + kinit_admin(master) + master.run_command(['klist']) + master.run_command(['smbcontrol', 'all', 'debug', '100']) + util.run_repeatedly(master, + ['ipa', 'trust-add', + '--type', 'ad', ad.domain.name, + '--admin', 'Administrator', + '--password'] + list(extra_args), + stdin_text=master.config.ad_admin_password) + master.run_command(['smbcontrol', 'all', 'debug', '1']) + clear_sssd_cache(master) + + +def remove_trust_with_ad(master, ad): + """ + Removes trust with Active Directory. Also removes the associated ID range. + """ + + kinit_admin(master) + + # Remove the trust + master.run_command(['ipa', 'trust-del', ad.domain.name]) + + # Remove the range + range_name = ad.domain.name.upper() + '_id_range' + master.run_command(['ipa', 'idrange-del', range_name]) + + +def configure_auth_to_local_rule(master, ad): + """ + Configures auth_to_local rule in /etc/krb5.conf + """ + + section_identifier = " %s = {" % master.domain.realm + line1 = (" auth_to_local = RULE:[1:$1@$0](^.*@%s$)s/@%s/@%s/" + % (ad.domain.realm, ad.domain.realm, ad.domain.name)) + line2 = " auth_to_local = DEFAULT" + + krb5_conf_content = master.get_file_contents('/etc/krb5.conf') + krb5_lines = [line.rstrip() for line in krb5_conf_content.split('\n')] + realm_section_index = krb5_lines.index(section_identifier) + + krb5_lines.insert(realm_section_index + 1, line1) + krb5_lines.insert(realm_section_index + 2, line2) + + krb5_conf_new_content = '\n'.join(krb5_lines) + master.put_file_contents('/etc/krb5.conf', krb5_conf_new_content) + + master.run_command(['systemctl', 'restart', 'sssd']) + + +def clear_sssd_cache(host): + """ + Clears SSSD cache by removing the cache files. Restarts SSSD. + """ + + host.run_command(['systemctl', 'stop', 'sssd']) + host.run_command(['rm', '-rfv', '/var/lib/sss/db/cache_%s.ldb' + % host.domain.name]) + host.run_command(['rm', '-rfv', '/var/lib/sss/mc/group']) + host.run_command(['rm', '-rfv', '/var/lib/sss/mc/passwd']) + host.run_command(['systemctl', 'start', 'sssd']) + + +def sync_time(host, server): + """ + Syncs the time with the remote server. Please note that this function + leaves ntpd stopped. + """ + + host.run_command(['sudo', 'systemctl', 'stop', 'ntpd']) + host.run_command(['sudo', 'ntpdate', server.hostname]) + + def connect_replica(master, replica): kinit_admin(replica) replica.run_command(['ipa-replica-manage', 'connect', master.hostname]) -- cgit