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