# # Copyright (C) 2018 FreeIPA Contributors see COPYING for license # """This module provides tests for NFS-related features like krb5 NFS and automount locations. Wishlist * add automount direct and indirect maps * add automount /home for the "seattle" location only * validate it is not available in another location * krb5 /home for IdM users in test_automount * store nfs configuration in a single place """ from __future__ import absolute_import import os import re import time import pytest from ipatests.test_integration.base import IntegrationTest from ipatests.pytest_ipa.integration import tasks # give some time for units to stabilize # otherwise we get transient errors WAIT_AFTER_INSTALL = 5 WAIT_AFTER_UNINSTALL = WAIT_AFTER_INSTALL class TestNFS(IntegrationTest): num_clients = 3 topology = 'line' def cleanup(self): nfssrv = self.clients[0] nfsclt = self.clients[1] automntclt = self.clients[2] time.sleep(WAIT_AFTER_UNINSTALL) nfsclt.run_command(["umount", "-a", "-t", "nfs4"]) nfsclt.run_command(["systemctl", "stop", "rpc-gssd"]) nfssrv.run_command(["systemctl", "stop", "nfs-server"]) nfssrv.run_command(["systemctl", "disable", "nfs-server"]) nfssrv.run_command([ "rm", "-f", "/etc/exports.d/krbnfs.exports", "/etc/exports.d/stdnfs.exports" ]) nfssrv.run_command(["rm", "-rf", "/exports"]) self.master.run_command([ "ipa", "host-mod", automntclt.hostname, "--location", "''" ]) # not strictly necessary, but this exercises automountlocation-del self.master.run_command([ "ipa", "automountlocation-del", "seattle" ]) nfsclt.run_command(["systemctl", "restart", "nfs-utils"]) nfssrv.run_command(["systemctl", "restart", "nfs-utils"]) def test_prepare_users(self): users = { "athena": "p", "euripides": "s" } temp_pass = 'temppass' for user in users: self.master.run_command([ "ipa", "user-add", "%s" % user, "--first", "%s" % user, "--last", "%s" % users[user], '--password'], stdin_text="%s\n%s\n" % (temp_pass, temp_pass) ) self.master.run_command(["kdestroy", "-A"]) password = "Secret123" user_kinit = "%s\n%s\n%s\n" % (temp_pass, password, password) self.master.run_command( ['kinit', user], stdin_text=user_kinit ) self.master.run_command(["kdestroy", "-A"]) tasks.kinit_admin(self.master) def test_krb5_nfsd(self): nfssrv = self.clients[0] # NFS keytab management self.master.run_command([ "ipa", "service-add", "nfs/%s" % nfssrv.hostname ]) nfssrv.run_command([ "ipa-getkeytab", "-p", "nfs/%s" % nfssrv.hostname, "-k", "/etc/krb5.keytab" ]) nfssrv.run_command(["systemctl", "restart", "nfs-server"]) nfssrv.run_command(["systemctl", "enable", "nfs-server"]) time.sleep(WAIT_AFTER_INSTALL) basedir = "exports" exports = { "krbnfs": "*(sec=krb5p,rw)", "stdnfs": "*(ro)", "home": "*(sec=krb5p,rw)" } for export in exports: exportpath = os.sep.join(('', basedir, export)) exportfile = os.sep.join(( '', 'etc', 'exports.d', "%s.exports" % export )) exportline = " ".join((exportpath, exports[export])) nfssrv.run_command(["mkdir", "-p", exportpath]) nfssrv.run_command(["chmod", "770", exportpath]) nfssrv.put_file_contents(exportfile, exportline) nfssrv.run_command(["cat", exportfile]) nfssrv.run_command(["exportfs", "-r"]) nfssrv.run_command(["exportfs", "-s"]) def test_krb5_nfs_manual_configuration(self): nfssrv = self.clients[0] nfsclt = self.clients[1] # for journalctl --since since = time.strftime('%H:%M:%S') nfsclt.run_command(["systemctl", "restart", "rpc-gssd"]) time.sleep(WAIT_AFTER_INSTALL) mountpoints = ("/mnt/krb", "/mnt/std", "/home") for mountpoint in mountpoints: nfsclt.run_command(["mkdir", "-p", mountpoint]) nfsclt.run_command([ "systemctl", "status", "gssproxy" ]) nfsclt.run_command([ "systemctl", "status", "rpc-gssd" ]) nfsclt.run_command([ "mount", "-t", "nfs4", "-o", "sec=krb5p,vers=4.0", "%s:/exports/krbnfs" % nfssrv.hostname, "/mnt/krb", "-v" ]) nfsclt.run_command([ "mount", "-t", "nfs4", "-o", "sec=krb5p,vers=4.0", "%s:/exports/home" % nfssrv.hostname, "/home", "-v" ]) error = "Unspecified GSS failure" check_log = [ 'journalctl', '-u', 'gssproxy', '--since={}'.format(since)] result = nfsclt.run_command(check_log) assert error not in (result.stdout_text, result.stderr_text) def test_automount(self): """ Test if ipa-client-automount behaves as expected """ nfssrv = self.clients[0] automntclt = self.clients[2] self.master.run_command([ "ipa", "automountlocation-add", "seattle" ]) self.master.run_command([ "ipa", "automountmap-add", "seattle", "auto.home" ]) self.master.run_command([ "ipa", "automountkey-add", "seattle", "auto.home", "--key='*'", "--info=sec=krb5p,vers=4" " 'rhel8-nfsserver0.laptop.example.org:/export/home/&'" ]) self.master.run_command([ "ipa", "automountkey-add", "seattle", "auto.master", "--key=/home", "--info=auto.home" ]) self.master.run_command([ "ipa", "host-mod", automntclt.hostname, "--location", "seattle" ]) # systemctl non-fatal errors will only be displayed # if ipa-client-automount is launched with --debug result1 = automntclt.run_command([ 'ipa-client-automount', '--location', 'seattle', '-U', '--debug' ]) time.sleep(WAIT_AFTER_INSTALL) # systemctl non-fatal errors will show up like this: # stderr=Failed to restart nfs-secure.service: \ # Unit nfs-secure.service not found. # normal output: # stderr= m1 = re.search(r'(?<=stderr\=Failed).+', result1.stderr_text) # maybe re-use m1.group(0) if it exists. assert m1 is None # https://pagure.io/freeipa/issue/7918 # check whether idmapd.conf was setup using the IPA domain automntclt.run_command([ "grep", "Domain = %s" % self.master.domain.name, "/etc/idmapd.conf" ]) automntclt.run_command([ "mount", "-t", "nfs4", "-o", "sec=krb5p,vers=4.0", "%s:/exports/home" % nfssrv.hostname, "/home", "-v" ]) # TODO leverage users time.sleep(WAIT_AFTER_UNINSTALL) automntclt.run_command(["umount", "-a", "-t", "nfs4"]) result2 = automntclt.run_command([ 'ipa-client-automount', '--uninstall', '-U', '--debug' ]) m2 = re.search(r'(?<=stderr\=Failed).+', result2.stderr_text) assert m2 is None time.sleep(WAIT_AFTER_UNINSTALL) # https://pagure.io/freeipa/issue/7918 # test for --idmap-domain DNS automntclt.run_command([ 'ipa-client-automount', '--location', 'default', '-U', '--debug', "--idmap-domain", "DNS" ]) time.sleep(WAIT_AFTER_INSTALL) # check whether idmapd.conf was setup properly: # grep must not find any configured Domain. result = automntclt.run_command( ["grep", "^Domain =", "/etc/idmapd.conf"], raiseonerr=False ) assert result.returncode == 1 automntclt.run_command([ 'ipa-client-automount', '--uninstall', '-U', '--debug' ]) time.sleep(WAIT_AFTER_UNINSTALL) # https://pagure.io/freeipa/issue/7918 # test for --idmap-domain exampledomain.net nfs_domain = "exampledomain.net" automntclt.run_command([ 'ipa-client-automount', '--location', 'default', '-U', '--debug', "--idmap-domain", nfs_domain ]) # check whether idmapd.conf was setup using nfs_domain automntclt.run_command([ "grep", "Domain = %s" % nfs_domain, "/etc/idmapd.conf" ]) time.sleep(WAIT_AFTER_INSTALL) automntclt.run_command([ 'ipa-client-automount', '--uninstall', '-U', '--debug' ]) time.sleep(WAIT_AFTER_UNINSTALL) self.cleanup() class TestIpaClientAutomountFileRestore(IntegrationTest): num_clients = 1 topology = 'line' @classmethod def install(cls, mh): tasks.install_master(cls.master, setup_dns=True) @pytest.fixture(autouse=True) def automountfile_restore_setup(self, request): def fin(): tasks.uninstall_client(self.clients[0]) request.addfinalizer(fin) def nsswitch_backup_restore( self, no_sssd=False, ): # In order to get a more pure sum, one that ignores the Generated # header and any whitespace we have to do a bit of work... sha256nsswitch_cmd = \ 'egrep -v "Generated|^$" /etc/nsswitch.conf | sed "s/\\s//g" ' \ '| sort | sha256sum' cmd = self.clients[0].run_command(sha256nsswitch_cmd) orig_sha256 = cmd.stdout_text grep_automount_command = \ "grep automount /etc/nsswitch.conf | cut -d: -f2" tasks.install_client(self.master, self.clients[0]) cmd = self.clients[0].run_command(grep_automount_command) after_ipa_client_install = cmd.stdout_text.split() if no_sssd: ipa_client_automount_command = [ "ipa-client-automount", "--no-sssd", "-U" ] else: ipa_client_automount_command = [ "ipa-client-automount", "-U" ] self.clients[0].run_command(ipa_client_automount_command) cmd = self.clients[0].run_command(grep_automount_command) after_ipa_client_automount = cmd.stdout_text.split() if no_sssd: assert after_ipa_client_automount == ['files', 'ldap'] else: assert after_ipa_client_automount == ['sss', 'files'] cmd = self.clients[0].run_command(grep_automount_command) assert cmd.stdout_text.split() == after_ipa_client_automount self.clients[0].run_command([ "ipa-client-automount", "--uninstall", "-U" ]) if not no_sssd: # https://pagure.io/freeipa/issue/8190 # check that no ipa_automount_location is left in sssd.conf # also check for autofs_provider for good measure grep_automount_in_sssdconf_cmd = \ "egrep ipa_automount_location\\|autofs_provider " \ "/etc/sssd/sssd.conf" cmd = self.clients[0].run_command( grep_automount_in_sssdconf_cmd, raiseonerr=False ) assert cmd.returncode == 1, \ "PG8190 regression found: ipa_automount_location still " \ "present in sssd.conf" cmd = self.clients[0].run_command(grep_automount_command) assert cmd.stdout_text.split() == after_ipa_client_install self.verify_checksum_after_ipaclient_uninstall( sha256nsswitch_cmd=sha256nsswitch_cmd, orig_sha256=orig_sha256 ) def verify_checksum_after_ipaclient_uninstall( self, sha256nsswitch_cmd, orig_sha256 ): tasks.uninstall_client(self.clients[0]) cmd = self.clients[0].run_command(sha256nsswitch_cmd) assert cmd.stdout_text == orig_sha256 @pytest.mark.xfail( reason="https://pagure.io/freeipa/issue/8189", strict=True ) def test_nsswitch_backup_restore_sssd(self): self.nsswitch_backup_restore() def test_nsswitch_backup_restore_no_sssd(self): self.nsswitch_backup_restore(no_sssd=True)