From 940755e37b06ea95c32abd056277da19fb05ed3e Mon Sep 17 00:00:00 2001 From: Florence Blanc-Renaud Date: Dec 06 2018 10:40:02 +0000 Subject: ipatest: add test for ipa-pkinit-manage enable|disable Add a test for ipa-pkinit-manage with the following scenario: - install master with option --no-pkinit - call ipa-pkinit-manage enable - call ipa-pkinit-manage disable - call ipa-pkinit-manage enable At each step, check that the PKINIT cert is consistent with the expectations: when pkinit is enabled, the cert is signed by IPA CA and tracked by 'IPA' ca helper, but when pkinit is disabled, the cert is self-signed and tracked by 'SelfSign' CA helper. The new test is added in the nightly definitons. Related to https://pagure.io/freeipa/issue/7200 Reviewed-By: Alexander Bokovoy Reviewed-By: Christian Heimes --- #diff --git a/ipatests/prci_definitions/nightly_f28.yaml b/ipatests/prci_definitions/nightly_f28.yaml #index ae8cacc..8462c14 100644 #--- a/ipatests/prci_definitions/nightly_f28.yaml #+++ b/ipatests/prci_definitions/nightly_f28.yaml #@@ -639,3 +639,15 @@ jobs: # template: *ci-master-f28 # timeout: 16000 # topology: *ipaserver #+ #+ fedora-28/test_pkinit_manage: #+ requires: [fedora-28/build] #+ priority: 50 #+ job: #+ class: RunPytest #+ args: #+ build_url: '{fedora-28/build_url}' #+ test_suite: test_integration/test_pkinit_manage.py #+ template: *ci-master-f28 #+ timeout: 3600 #+ topology: *master_1repl diff --git a/ipatests/prci_definitions/nightly_master.yaml b/ipatests/prci_definitions/nightly_master.yaml index 66921b6..3f2b346 100644 --- a/ipatests/prci_definitions/nightly_master.yaml +++ b/ipatests/prci_definitions/nightly_master.yaml @@ -639,3 +639,15 @@ jobs: template: *ci-master-f29 timeout: 16000 topology: *ipaserver + + fedora-29/test_pkinit_manage: + requires: [fedora-29/build] + priority: 50 + job: + class: RunPytest + args: + build_url: '{fedora-29/build_url}' + test_suite: test_integration/test_pkinit_manage.py + template: *ci-master-f29 + timeout: 3600 + topology: *master_1repl diff --git a/ipatests/prci_definitions/nightly_rawhide.yaml b/ipatests/prci_definitions/nightly_rawhide.yaml index 24c26be..bdc34d2 100644 --- a/ipatests/prci_definitions/nightly_rawhide.yaml +++ b/ipatests/prci_definitions/nightly_rawhide.yaml @@ -627,3 +627,15 @@ jobs: template: *ci-master-frawhide timeout: 7200 topology: *ipaserver + + fedora-rawhide/test_pkinit_manage: + requires: [fedora-rawhide/build] + priority: 50 + job: + class: RunPytest + args: + build_url: '{fedora-rawhide/build_url}' + test_suite: test_integration/test_pkinit_manage.py + template: *ci-master-frawhide + timeout: 3600 + topology: *master_1repl diff --git a/ipatests/test_integration/test_pkinit_manage.py b/ipatests/test_integration/test_pkinit_manage.py new file mode 100644 index 0000000..bc1d9e3 --- /dev/null +++ b/ipatests/test_integration/test_pkinit_manage.py @@ -0,0 +1,111 @@ +# +# Copyright (C) 2018 FreeIPA Contributors see COPYING for license +# + +""" +Module provides tests for the ipa-pkinit-manage command. +""" + +from __future__ import absolute_import + +from ipalib import x509 +from ipaplatform.paths import paths +from ipapython.dn import DN +from ipatests.test_integration.base import IntegrationTest +from ipatests.pytest_ipa.integration import tasks + + +SELFSIGNED_CA_HELPER = 'SelfSign' +IPA_CA_HELPER = 'IPA' +PKINIT_STATUS_ENABLED = 'enabled' +PKINIT_STATUS_DISABLED = 'disabled' + + +def check_pkinit_status(host, status): + """Ensures that ipa-pkinit-manage status returns the expected state""" + result = host.run_command(['ipa-pkinit-manage', 'status'], + raiseonerr=False) + assert result.returncode == 0 + assert 'PKINIT is {}'.format(status) in result.stdout_text + + +def check_pkinit_tracking(host, ca_helper): + """Ensures that the PKINIT cert is tracked by the expected helper""" + result = host.run_command(['getcert', 'list', '-f', paths.KDC_CERT], + raiseonerr=False) + assert result.returncode == 0 + # Make sure that only one request exists + assert result.stdout_text.count('Request ID') == 1 + # Make sure that the right CA helper is used to track the cert + assert 'CA: {}'.format(ca_helper) in result.stdout_text + + +def check_pkinit_cert_issuer(host, issuer): + """Ensures that the PKINIT cert is signed by the expected issuer""" + data = host.get_file_contents(paths.KDC_CERT) + pkinit_cert = x509.load_pem_x509_certificate(data) + # Make sure that the issuer is the expected one + assert DN(pkinit_cert.issuer) == DN(issuer) + + +def check_pkinit(host, enabled=True): + """Checks that PKINIT is configured as expected + + If enabled: + ipa-pkinit-manage status must return 'PKINIT is enabled' + the certificate must be tracked by IPA CA helper + the certificate must be signed by IPA CA + If disabled: + ipa-pkinit-manage status must return 'PKINIT is disabled' + the certificate must be tracked by SelfSign CA helper + the certificate must be self-signed + """ + if enabled: + # When pkinit is enabled: + # cert is tracked by IPA CA helper + # cert is signed by IPA CA + check_pkinit_status(host, PKINIT_STATUS_ENABLED) + check_pkinit_tracking(host, IPA_CA_HELPER) + check_pkinit_cert_issuer( + host, + 'CN=Certificate Authority,O={}'.format(host.domain.realm)) + else: + # When pkinit is disabled + # cert is tracked by 'SelfSign' CA helper + # cert is self-signed + check_pkinit_status(host, PKINIT_STATUS_DISABLED) + check_pkinit_tracking(host, SELFSIGNED_CA_HELPER) + check_pkinit_cert_issuer( + host, + 'CN={},O={}'.format(host.hostname, host.domain.realm)) + + +class TestPkinitManage(IntegrationTest): + """Tests the ipa-pkinit-manage command. + + ipa-pkinit-manage can be used to enable, disable or check + the status of PKINIT. + When pkinit is enabled, the kerberos server is using a certificate + signed either externally or by IPA CA. In the latter case, certmonger + is tracking the cert with IPA helper. + When pkinit is disabled, the kerberos server is using a self-signed + certificate that is tracked by certmonger with the SelfSigned helper. + """ + + @classmethod + def install(cls, mh): + # Install the master with PKINIT disabled + tasks.install_master(cls.master, extra_args=['--no-pkinit']) + check_pkinit(cls.master, enabled=False) + + def test_pkinit_enable(self): + self.master.run_command(['ipa-pkinit-manage', 'enable']) + check_pkinit(self.master, enabled=True) + + def test_pkinit_disable(self): + self.master.run_command(['ipa-pkinit-manage', 'disable']) + check_pkinit(self.master, enabled=False) + + def test_pkinit_reenable(self): + self.master.run_command(['ipa-pkinit-manage', 'enable']) + check_pkinit(self.master, enabled=True) From ffa04a1862be198b9e1a5f6205d1ae0909ac5a4d Mon Sep 17 00:00:00 2001 From: Florence Blanc-Renaud Date: Dec 06 2018 10:40:02 +0000 Subject: PKINIT: fix ipa-pkinit-manage enable|disable The command ipa-pkinit-manage enable|disable is reporting success even though the PKINIT cert is not re-issued. The command triggers the request of a new certificate (signed by IPA CA when state=enable, selfsigned when disabled), but as the cert file is still present, certmonger does not create a new request and the existing certificate is kept. The fix consists in deleting the cert and key file before calling certmonger to request a new cert. There was also an issue in the is_pkinit_enabled() function: if no tracking request was found for the PKINIT cert, is_pkinit_enabled() was returning True while it should not. Fixes https://pagure.io/freeipa/issue/7200 Reviewed-By: Alexander Bokovoy Reviewed-By: Christian Heimes --- diff --git a/ipaserver/install/ipa_pkinit_manage.py b/ipaserver/install/ipa_pkinit_manage.py index 4a79bba..86bd1ba 100644 --- a/ipaserver/install/ipa_pkinit_manage.py +++ b/ipaserver/install/ipa_pkinit_manage.py @@ -72,6 +72,8 @@ class PKINITManage(AdminTool): if ca_enabled: logger.warning( "Failed to stop tracking certificates: %s", e) + # remove the cert and key + krb.delete_pkinit_cert() krb.enable_ssl() diff --git a/ipaserver/install/krbinstance.py b/ipaserver/install/krbinstance.py index 4ead1c5..850946a 100644 --- a/ipaserver/install/krbinstance.py +++ b/ipaserver/install/krbinstance.py @@ -77,7 +77,7 @@ def is_pkinit_enabled(): if os.path.exists(paths.KDC_CERT): pkinit_request_ca = get_pkinit_request_ca() - if pkinit_request_ca != "SelfSign": + if pkinit_request_ca and pkinit_request_ca != "SelfSign": return True return False @@ -602,6 +602,10 @@ class KrbInstance(service.Service): def stop_tracking_certs(self): certmonger.stop_tracking(certfile=paths.KDC_CERT) + def delete_pkinit_cert(self): + installutils.remove_file(paths.KDC_CERT) + installutils.remove_file(paths.KDC_KEY) + def uninstall(self): if self.is_configured(): self.print_msg("Unconfiguring %s" % self.service_name) @@ -627,8 +631,7 @@ class KrbInstance(service.Service): # stop tracking and remove certificates self.stop_tracking_certs() installutils.remove_file(paths.CACERT_PEM) - installutils.remove_file(paths.KDC_CERT) - installutils.remove_file(paths.KDC_KEY) + self.delete_pkinit_cert() if running: self.restart()