# # Copyright (C) 2020 FreeIPA Contributors see COPYING for license # """ Module provides tests for ipa-cert-fix CLI. """ import pytest import re import time import logging from ipaplatform.paths import paths from ipatests.pytest_ipa.integration import tasks from ipatests.test_integration.base import IntegrationTest from ipatests.test_integration.test_caless import CALessBase, ipa_certs_cleanup logger = logging.getLogger(__name__) def server_install_teardown(func): def wrapped(*args): master = args[0].master try: func(*args) finally: ipa_certs_cleanup(master) return wrapped def check_status(host, cert_count, state, timeout=600): """Helper method to check that if all the certs are in given state :param host: the host :param cert_count: no of cert to look for :param state: state to check for :param timeout: max time in seconds to wait for the state """ for _i in range(0, timeout, 10): result = host.run_command(['getcert', 'list']) count = result.stdout_text.count(f"status: {state}") logger.info("cert count in %s state : %s", state, count) if int(count) == cert_count: break time.sleep(10) else: raise RuntimeError("request timed out") return count def move_date(host, chrony_state, date_str): """Helper method to move the date on given host :param host: The host on which date is to be moved :param chrony_state: State to which chrony service to be moved :param date_str: date string to move the date i.e 2years1month1days """ host.run_command(['systemctl', chrony_state, 'chronyd']) host.run_command(['date', '-s', date_str]) @pytest.fixture def expire_cert_critical(): """ Fixture to expire the certs by moving the system date using date -s command and revert it back """ hosts = dict() def _expire_cert_critical(host, setup_kra=False): hosts['host'] = host # Do not install NTP as the test plays with the date tasks.install_master(host, setup_dns=False, extra_args=['--no-ntp']) if setup_kra: tasks.install_kra(host) # move date to expire certs move_date(host, 'stop', '+3Years+1day') yield _expire_cert_critical host = hosts.pop('host') tasks.uninstall_master(host) move_date(host, 'start', '-3Years-1day') class TestIpaCertFix(IntegrationTest): @classmethod def uninstall(cls, mh): # Uninstall method is empty as the uninstallation is done in # the fixture pass @pytest.fixture def expire_ca_cert(self): tasks.install_master(self.master, setup_dns=False, extra_args=['--no-ntp']) move_date(self.master, 'stop', '+20Years+1day') yield tasks.uninstall_master(self.master) move_date(self.master, 'start', '-20Years-1day') def test_missing_csr(self, expire_cert_critical): """ Test that ipa-cert-fix succeeds when CSR is missing from CS.cfg Test case for https://pagure.io/freeipa/issue/8618 Scenario: - move the date so that ServerCert cert-pki-ca is expired - remove the ca.sslserver.certreq directive from CS.cfg - call getcert resubmit in order to create the CSR in certmonger file - use ipa-cert-fix, no issue should be seen """ expire_cert_critical(self.master) # pki must be stopped in order to edit CS.cfg self.master.run_command(['ipactl', 'stop']) self.master.run_command(['sed', '-i', r'/ca\.sslserver\.certreq=/d', paths.CA_CS_CFG_PATH]) # dirsrv needs to be up in order to run ipa-cert-fix self.master.run_command(['ipactl', 'start', '--ignore-service-failures']) # It's the call to getcert resubmit that creates the CSR in certmonger. # In normal operations it would be launched automatically when the # expiration date is near but in the test we force the CSR creation. self.master.run_command(['getcert', 'resubmit', '-n', 'Server-Cert cert-pki-ca', '-d', paths.PKI_TOMCAT_ALIAS_DIR]) # Wait a few secs time.sleep(3) # Now the real test, call ipa-cert-fix and ensure it doesn't # complain about missing sslserver.crt result = self.master.run_command(['ipa-cert-fix', '-v'], stdin_text='yes\n', raiseonerr=False) msg = ("No such file or directory: " "'/etc/pki/pki-tomcat/certs/sslserver.crt'") assert msg not in result.stderr_text # Because of BZ 1897120, pki-cert-fix fails on pki-core 10.10.0 # https://bugzilla.redhat.com/show_bug.cgi?id=1897120 if (tasks.get_pki_version(self.master) != tasks.parse_version('10.10.0')): assert result.returncode == 0 # get the number of certs track by certmonger cmd = self.master.run_command(['getcert', 'list']) certs = cmd.stdout_text.count('Request ID') timeout = 600 renewed = 0 start = time.time() # wait up to 10 min for all certs to renew while time.time() - start < timeout: cmd = self.master.run_command(['getcert', 'list']) renewed = cmd.stdout_text.count('status: MONITORING') if renewed == certs: break time.sleep(100) else: # timeout raise AssertionError('Timeout: Failed to renew all the certs') def test_renew_expired_cert_on_master(self, expire_cert_critical): """Test if ipa-cert-fix renews expired certs Test moves system date to expire certs. Then calls ipa-cert-fix to renew them. This certs include subsystem, audit-signing, OCSP signing, Dogtag HTTPS, IPA RA agent, LDAP and KDC certs. related: https://pagure.io/freeipa/issue/7885 """ expire_cert_critical(self.master) # wait for cert expiry check_status(self.master, 8, "CA_UNREACHABLE") self.master.run_command(['ipa-cert-fix', '-v'], stdin_text='yes\n') check_status(self.master, 9, "MONITORING") # second iteration of ipa-cert-fix result = self.master.run_command( ['ipa-cert-fix', '-v'], stdin_text='yes\n' ) assert "Nothing to do" in result.stdout_text check_status(self.master, 9, "MONITORING") def test_ipa_cert_fix_non_ipa(self): """Test ipa-cert-fix doesn't work on non ipa system ipa-cert-fix tool should not work on non ipa system. related: https://pagure.io/freeipa/issue/7885 """ result = self.master.run_command(['ipa-cert-fix', '-v'], stdin_text='yes\n', raiseonerr=False) assert result.returncode == 2 def test_missing_startup(self, expire_cert_critical): """ Test ipa-cert-fix fails/warns when startup directive is missing This test checks that if 'selftests.container.order.startup' directive is missing from CS.cfg, ipa-cert-fix fails and throw proper error message. It also checks that underlying command 'pki-server cert-fix' should fail to renew the cert. related: https://pagure.io/freeipa/issue/8721 With https://github.com/dogtagpki/pki/pull/3466, it changed to display a warning than failing. This test also checks that if 'selftests.container.order.startup' directive is missing from CS.cfg, ipa-cert-fix dsplay proper warning (depending on pki version) related: https://pagure.io/freeipa/issue/8890 """ expire_cert_critical(self.master) # pki must be stopped in order to edit CS.cfg self.master.run_command(['ipactl', 'stop']) self.master.run_command([ 'sed', '-i', r'/selftests\.container\.order\.startup/d', paths.CA_CS_CFG_PATH ]) # dirsrv needs to be up in order to run ipa-cert-fix self.master.run_command(['ipactl', 'start', '--ignore-service-failures']) result = self.master.run_command(['ipa-cert-fix', '-v'], stdin_text='yes\n', raiseonerr=False) err_msg1 = "ERROR: 'selftests.container.order.startup'" # check that pki-server cert-fix command fails err_msg2 = ("ERROR: CalledProcessError(Command " "['pki-server', 'cert-fix'") warn_msg = ("WARNING: No selftests configured in " f"{paths.CA_CS_CFG_PATH} " "(selftests.container.order.startup)") if (tasks.get_pki_version(self.master) < tasks.parse_version('10.11.0')): assert (err_msg1 in result.stderr_text and err_msg2 in result.stderr_text) else: assert warn_msg in result.stdout_text def test_expired_CA_cert(self, expire_ca_cert): """Test to check ipa-cert-fix when CA certificate is expired In order to fix expired certs using ipa-cert-fix, CA cert should be valid. If CA cert expired, ipa-cert-fix won't work. related: https://pagure.io/freeipa/issue/8721 """ result = self.master.run_command(['ipa-cert-fix', '-v'], stdin_text='yes\n', raiseonerr=False) # check that pki-server cert-fix command fails err_msg = ("ERROR: CalledProcessError(Command " "['pki-server', 'cert-fix'") assert err_msg in result.stderr_text class TestIpaCertFixThirdParty(CALessBase): """ Test that ipa-cert-fix works with an installation with custom certs. """ @classmethod def install(cls, mh): cls.nickname = 'ca1/server' super(TestIpaCertFixThirdParty, cls).install(mh) tasks.install_master(cls.master, setup_dns=True) @server_install_teardown def test_third_party_certs(self): self.create_pkcs12(self.nickname, password=self.cert_password, filename='server.p12') self.prepare_cacert('ca1') # We have a chain length of one. If this is extended then the # additional cert names will need to be calculated. nick_chain = self.nickname.split('/') ca_cert = '%s.crt' % nick_chain[0] # Add the CA to the IPA store self.copy_cert(self.master, ca_cert) self.master.run_command(['ipa-cacert-manage', 'install', ca_cert]) # Apply the new cert chain otherwise ipa-server-certinstall will fail self.master.run_command(['ipa-certupdate']) # Install the updated certs and restart the world self.copy_cert(self.master, 'server.p12') args = ['ipa-server-certinstall', '-p', self.master.config.dirman_password, '--pin', self.master.config.admin_password, '-d', 'server.p12'] self.master.run_command(args) self.master.run_command(['ipactl', 'restart']) # Run ipa-cert-fix. This is basically a no-op but tests that # the DS nickname is used and not a hardcoded value. result = self.master.run_command(['ipa-cert-fix', '-v'],) assert self.nickname in result.stderr_text class TestCertFixKRA(IntegrationTest): @classmethod def uninstall(cls, mh): # Uninstall method is empty as the uninstallation is done in # the fixture pass def test_renew_expired_cert_with_kra(self, expire_cert_critical): """Test if ipa-cert-fix renews expired certs with kra installed This test check if ipa-cert-fix renews certs with kra certificate installed. related: https://pagure.io/freeipa/issue/7885 """ expire_cert_critical(self.master, setup_kra=True) # check if all subsystem cert expired check_status(self.master, 11, "CA_UNREACHABLE") self.master.run_command(['ipa-cert-fix', '-v'], stdin_text='yes\n') check_status(self.master, 12, "MONITORING") class TestCertFixReplica(IntegrationTest): num_replicas = 1 @classmethod def install(cls, mh): tasks.install_master( mh.master, setup_dns=False, extra_args=['--no-ntp'] ) tasks.install_replica( mh.master, mh.replicas[0], setup_dns=False, extra_args=['--no-ntp'] ) def test_renew_expired_cert_replica(self): """Test renewal of certificates on replica with ipa-cert-fix This is to check that ipa-cert-fix renews the certificates on replica related: https://pagure.io/freeipa/issue/7885 """ move_date(self.master, 'stop', '+3years+1days') # wait for cert expiry check_status(self.master, 8, "CA_UNREACHABLE") self.master.run_command(['ipa-cert-fix', '-v'], stdin_text='yes\n') check_status(self.master, 9, "MONITORING") # move system date to expire cert on replica move_date(self.replicas[0], 'stop', '+3years+1days') # RA agent cert will be expired and in CA_UNREACHABLE state check_status(self.replicas[0], 1, "CA_UNREACHABLE") # renew RA agent cert self.replicas[0].run_command( ['ipa-cert-fix', '-v'], stdin_text='yes\n' ) # LDAP/HTTP/PKINIT certs will be renewed automaticaly # after moving date on replica. This 3, 1 CA cert, # 1 RA agent cert. Check for total 5 valid certs. check_status(self.replicas[0], 5, "MONITORING") # get the req ids of all certs to renew remaining # certs by re-submitting it result = self.replicas[0].run_command(['getcert', 'list']) req_ids = re.findall(r'\d{14}', result.stdout_text) # resubmit the certs to renew them for req_id in req_ids: self.replicas[0].run_command( ['getcert', 'resubmit', '-i', req_id] ) check_status(self.master, 9, "MONITORING") # move date back on replica and master move_date(self.replicas[0], 'start', '-3years-1days') move_date(self.master, 'start', '-3years-1days')