From 152715b8514b1b94e1c353baedff12d24efaacb7 Mon Sep 17 00:00:00 2001 From: Martin Babinsky Date: Fri, 31 Mar 2017 15:06:46 +0200 Subject: [PATCH] Allow for configuration of all three PKINIT variants when deploying KDC The PKINIT setup code now can configure PKINIT using IPA CA signed certificate, 3rd party certificate and local PKINIT with self-signed keypair. The local PKINIT is also selected as a fallback mechanism if the CSR is rejected by CA master or `--no-pkinit` is used. http://www.freeipa.org/page/V4/Kerberos_PKINIT https://pagure.io/freeipa/issue/6830 Reviewed-By: Alexander Bokovoy Reviewed-By: Jan Cholasta Reviewed-By: Martin Basti Reviewed-By: Simo Sorce --- ipaserver/install/krbinstance.py | 145 +++++++++++++++++++++++++-------------- 1 file changed, 93 insertions(+), 52 deletions(-) diff --git a/ipaserver/install/krbinstance.py b/ipaserver/install/krbinstance.py index 6c105f74c8da2bfd34ace607b13170bc96a8ff1d..80215788cf4031ef82e9ec7e08bde6cfc4390303 100644 --- a/ipaserver/install/krbinstance.py +++ b/ipaserver/install/krbinstance.py @@ -38,6 +38,7 @@ from ipalib.constants import ANON_USER from ipalib.install import certmonger from ipapython.ipa_log_manager import root_logger from ipapython.dn import DN +from ipapython.dogtag import KDC_PROFILE from ipaserver.install import replication from ipaserver.install import ldapupdate @@ -354,61 +355,84 @@ class KrbInstance(service.Service): remote_ldap.gssapi_bind() replication.wait_for_entry(remote_ldap, kdc_dn, timeout=60) - def setup_pkinit(self): - if self.pkcs12_info: - certs.install_pem_from_p12(self.pkcs12_info[0], - self.pkcs12_info[1], - paths.KDC_CERT) - certs.install_key_from_p12(self.pkcs12_info[0], - self.pkcs12_info[1], - paths.KDC_KEY) - else: - subject = str(DN(('cn', self.fqdn), self.subject_base)) - krbtgt = "krbtgt/" + self.realm + "@" + self.realm - certpath = (paths.KDC_CERT, paths.KDC_KEY) + def _call_certmonger(self, certmonger_ca='IPA'): + subject = str(DN(('cn', self.fqdn), self.subject_base)) + krbtgt = "krbtgt/" + self.realm + "@" + self.realm + certpath = (paths.KDC_CERT, paths.KDC_KEY) - try: - prev_helper = None - if self.master_fqdn is None: - ca_args = [ - paths.CERTMONGER_DOGTAG_SUBMIT, - '--ee-url', 'https://%s:8443/ca/ee/ca' % self.fqdn, - '--certfile', paths.RA_AGENT_PEM, - '--keyfile', paths.RA_AGENT_KEY, - '--cafile', paths.IPA_CA_CRT, - '--agent-submit' - ] - helper = " ".join(ca_args) - prev_helper = certmonger.modify_ca_helper('IPA', helper) - else: - self._wait_for_replica_kdc_entry() - - certmonger.request_and_wait_for_cert( - certpath, - subject, - krbtgt, - dns=self.fqdn, - storage='FILE', - profile='KDCs_PKINIT_Certs') - except dbus.DBusException as e: - # if the certificate is already tracked, ignore the error - name = e.get_dbus_name() - if name != 'org.fedorahosted.certmonger.duplicate': - root_logger.error("Failed to initiate the request: %s", e) - return - finally: - if prev_helper is not None: - certmonger.modify_ca_helper('IPA', prev_helper) - - # Finally copy the cacert in the krb directory so we don't - # have any selinux issues with the file context + try: + prev_helper = None + # on the first CA-ful master without '--no-pkinit', we issue the + # certificate by contacting Dogtag directly + use_dogtag_submit = all( + [self.master_fqdn is None, + self.pkcs12_info is None, + self.config_pkinit]) + + if use_dogtag_submit: + ca_args = [ + paths.CERTMONGER_DOGTAG_SUBMIT, + '--ee-url', 'https://%s:8443/ca/ee/ca' % self.fqdn, + '--certfile', paths.RA_AGENT_PEM, + '--keyfile', paths.RA_AGENT_KEY, + '--cafile', paths.IPA_CA_CRT, + '--agent-submit' + ] + helper = " ".join(ca_args) + prev_helper = certmonger.modify_ca_helper(certmonger_ca, helper) + + certmonger.request_and_wait_for_cert( + certpath, + subject, + krbtgt, + ca=certmonger_ca, + dns=self.fqdn, + storage='FILE', + profile=KDC_PROFILE) + except dbus.DBusException as e: + # if the certificate is already tracked, ignore the error + name = e.get_dbus_name() + if name != 'org.fedorahosted.certmonger.duplicate': + root_logger.error("Failed to initiate the request: %s", e) + return + finally: + if prev_helper is not None: + certmonger.modify_ca_helper(certmonger_ca, prev_helper) + + def issue_selfsigned_pkinit_certs(self): + self._call_certmonger(certmonger_ca="SelfSign") + # for self-signed certificate, the certificate is its own CA, copy it + # as CA cert + shutil.copyfile(paths.KDC_CERT, paths.CACERT_PEM) + + def issue_ipa_ca_signed_pkinit_certs(self): + try: + self._call_certmonger() + # copy IPA CA bundle to the KDC's CA cert bundle + shutil.copyfile(paths.IPA_CA_CRT, paths.CACERT_PEM) + except RuntimeError as e: + root_logger.error("PKINIT certificate request failed: %s", e) + root_logger.error("Failed to configure PKINIT") + self.stop_tracking_certs() + self.issue_selfsigned_pkinit_certs() + + def install_external_pkinit_certs(self): + certs.install_pem_from_p12(self.pkcs12_info[0], + self.pkcs12_info[1], + paths.KDC_CERT) + certs.install_key_from_p12(self.pkcs12_info[0], + self.pkcs12_info[1], + paths.KDC_KEY) + # copy IPA CA bundle to the KDC's CA cert bundle + # NOTE: this may not be the same set of CA certificates trusted by + # externally provided PKINIT cert. shutil.copyfile(paths.IPA_CA_CRT, paths.CACERT_PEM) - try: - self.restart() - except Exception: - root_logger.critical("krb5kdc service failed to restart") - raise + def setup_pkinit(self): + if self.pkcs12_info: + self.install_external_pkinit_certs() + elif self.config_pkinit: + self.issue_ipa_ca_signed_pkinit_certs() def test_anonymous_pkinit(self): with ipautil.private_ccache() as anon_ccache: @@ -418,6 +442,15 @@ class KrbInstance(service.Service): raise RuntimeError("Failed to configure anonymous PKINIT") def enable_ssl(self): + """ + generate PKINIT certificate for KDC. If `--no-pkinit` was specified, + only configure local self-signed KDC certificate for use as a FAST + channel generator for WebUI. Do not advertise the installation steps in + this case. + """ + if self.master_fqdn is not None: + self._wait_for_replica_kdc_entry() + if self.config_pkinit: self.steps = [] self.step("installing X509 Certificate for PKINIT", @@ -425,6 +458,14 @@ class KrbInstance(service.Service): self.step("testing anonymous PKINIT", self.test_anonymous_pkinit) self.start_creation() + else: + self.issue_selfsigned_pkinit_certs() + + try: + self.restart() + except Exception: + root_logger.critical("krb5kdc service failed to restart") + raise def get_anonymous_principal_name(self): return "%s@%s" % (ANON_USER, self.realm) -- 2.12.2