[Freeipa-devel] [PATCH] 0072..0075 Lightweight CA renewal

Fraser Tweedale ftweedal at redhat.com
Fri Jun 24 06:49:43 UTC 2016


On Thu, Jun 23, 2016 at 09:51:02AM +0200, Jan Cholasta wrote:
> Hi,
> 
> On 21.6.2016 08:24, Fraser Tweedale wrote:
> > The attached patches add lightweight CA renewal.  There are two
> > substantive aspects:
> > 
> > 1. The renew_ca_cert updates the serial number in the lightweight
> > CA's entry in the Dogtag database.  This causes CA clones to observe
> > the renewal and update the certs in their own NSSDBs.
> > 
> > 2. The ipa-certupdate command adds Certmonger tracking requests for
> > lightweight CAs (on the renewal master only).
> > 
> > Correct behaviour also depends on my patch 0069 (in-server API for
> > renew_ca_cert script).
> 
> Patch 0072-0074: LGTM
> 
> Patch 0075:
> 
> 1) Lightweight CA certs should be tracked by certmonger on all CA servers,
> not just on the renewal master. The behavior should be the same as for the
> main CA cert, i.e. the actual renewal is done only on the renewal master,
> other CA servers only update their NSS DBs (this is handled in
> dogtag-ipa-ca-renew-agent-submit).
> 
> This is important because CA renewal master can change at any time, and
> without all CA certs being tracked on all CA servers, there is no guarantee
> the renewal would happen.
> 
> 2) Since CA clones update their NSS DBs on their own,
> dogtag-ipa-ca-renew-agent should be updated not to put them in
> cn=ca_renewal,cn=ipa,cn=etc.
> 
Thanks for the review, Honza.  Updated patch 0075-2 attached.
-------------- next part --------------
From 9256f36d8df206642a51964ae2f40f4905e0c0bc Mon Sep 17 00:00:00 2001
From: Fraser Tweedale <ftweedal at redhat.com>
Date: Tue, 21 Jun 2016 15:01:41 +1000
Subject: [PATCH] ipa-certupdate: track lightweight CA certificates

Enhance the ipa-certupdate program to add Certmonger tracking
requests for lightweight CA certificates.

Also update the dogtag-ipa-ca-renew-agent-submit to not store or
retrieve lightweight CA certificates, becaues Dogtag clones observe
renewals and update their NSSDBs on their own, and allow the helper
to request non-self-signed certificates.

Part of: https://fedorahosted.org/freeipa/ticket/4559
---
 .../certmonger/dogtag-ipa-ca-renew-agent-submit    | 39 +++++++++++++---
 ipaclient/ipa_certupdate.py                        | 52 ++++++++++++++++++++--
 2 files changed, 82 insertions(+), 9 deletions(-)

diff --git a/install/certmonger/dogtag-ipa-ca-renew-agent-submit b/install/certmonger/dogtag-ipa-ca-renew-agent-submit
index 3f7333c0e0bb6059e8b3791ef5230c7e5663d2eb..7ab3ec15db37894ed443aa16b7edcf85d69c8192 100755
--- a/install/certmonger/dogtag-ipa-ca-renew-agent-submit
+++ b/install/certmonger/dogtag-ipa-ca-renew-agent-submit
@@ -62,6 +62,24 @@ if six.PY3:
     unicode = str
 
 
+IPA_CA_NICKNAME = 'caSigningCert cert-pki-ca'
+
+def get_nickname():
+    csr = os.environ.get('CERTMONGER_CSR')
+    return pkcs10.get_friendlyname(csr) if csr else None
+
+def is_lightweight_ca():
+    nickname = get_nickname() or ''
+    return nickname != IPA_CA_NICKNAME and nickname.startswith(IPA_CA_NICKNAME)
+
+def is_renewable():
+    cert = os.environ.get('CERTMONGER_CERTIFICATE')
+    if not cert:
+        return False
+    else:
+        return x509.is_self_signed(cert) or is_lightweight_ca()
+
+
 @contextlib.contextmanager
 def ldap_connect():
     conn = None
@@ -210,6 +228,11 @@ def store_cert():
     if not cert:
         return (REJECTED, "New certificate requests not supported")
 
+    if is_lightweight_ca():
+        # Lightweight CAs are updated in Dogtag's NSSDB
+        # by Dogtag itself, so do not store it
+        return (ISSUED, cert)
+
     dercert = x509.normalize_certificate(cert)
 
     dn = DN(('cn', nickname), ('cn', 'ca_renewal'),
@@ -338,6 +361,12 @@ def retrieve_cert_continuous():
     if old_cert:
         old_cert = x509.normalize_certificate(old_cert)
 
+    if is_lightweight_ca():
+        # Lightweight CAs are updated in Dogtag's NSSDB
+        # by Dogtag itself, so do not try to retrieve it.
+        # Everything is fine as is.
+        return (ISSUED, os.environ.get('CERTMONGER_CERTIFICATE'))
+
     result = call_handler(retrieve_or_reuse_cert)
     if result[0] != ISSUED:
         return result
@@ -393,13 +422,12 @@ def renew_ca_cert():
     cert = os.environ.get('CERTMONGER_CERTIFICATE')
     if not cert:
         return (REJECTED, "New certificate requests not supported")
-    is_self_signed = x509.is_self_signed(cert)
 
     operation = os.environ.get('CERTMONGER_OPERATION')
     if operation == 'SUBMIT':
         state = 'retrieve'
 
-        if is_self_signed:
+        if is_renewable():
             ca = cainstance.CAInstance(host_name=api.env.host, ldapi=False)
             if ca.is_renewal_master():
                 state = 'request'
@@ -419,10 +447,11 @@ def renew_ca_cert():
 
     if state == 'retrieve':
         result = call_handler(retrieve_cert)
-        if result[0] == REJECTED and not is_self_signed:
+        if result[0] == REJECTED and not is_renewable():
             syslog.syslog(syslog.LOG_ALERT,
-                          "IPA CA certificate is about to expire, "
-                          "use ipa-cacert-manage to renew it")
+                          "Certificate with subject '%s' is about to expire, "
+                          "use ipa-cacert-manage to renew it"
+                          % (os.environ.get("CERTMONGER_REQ_SUBJECT"),))
     elif state == 'request':
         profile = os.environ['CERTMONGER_CA_PROFILE']
         os.environ['CERTMONGER_CA_PROFILE'] = 'caCACert'
diff --git a/ipaclient/ipa_certupdate.py b/ipaclient/ipa_certupdate.py
index b9572196c0eab3b35aa62790eed4ce8a21e3c130..e59047a2705eb8ccb98b5213c4c8771f55a29bc5 100644
--- a/ipaclient/ipa_certupdate.py
+++ b/ipaclient/ipa_certupdate.py
@@ -29,7 +29,10 @@ from ipaplatform import services
 from ipaplatform.paths import paths
 from ipaplatform.tasks import tasks
 from ipalib import api, errors, x509, certstore
+from ipalib.constants import IPA_CA_CN
 
+IPA_CA_NICKNAME = 'caSigningCert cert-pki-ca'
+RENEWAL_CA_NAME = 'dogtag-ipa-ca-renew-agent'
 
 class CertUpdate(admintool.AdminTool):
     command_name = 'ipa-certupdate'
@@ -76,18 +79,27 @@ class CertUpdate(admintool.AdminTool):
                     version=u'2.0',
                 )
                 ca_enabled = result['result']['enable_ra']
-            api.Backend.rpcclient.disconnect()
 
             ldap.do_sasl_gssapi_bind()
 
             certs = certstore.get_ca_certs(ldap, api.env.basedn,
                                            api.env.realm, ca_enabled)
+
+            # find lightweight CAs (on renewal master only)
+            lwcas = []
+            for ca_obj in api.Command.ca_find()['result']:
+                if IPA_CA_CN not in ca_obj['cn']:
+                    lwcas.append(ca_obj)
+
+            api.Backend.rpcclient.disconnect()
         finally:
             shutil.rmtree(tmpdir)
 
         server_fstore = sysrestore.FileStore(paths.SYSRESTORE)
         if server_fstore.has_files():
             self.update_server(certs)
+            for entry in lwcas:
+                self.server_track_lightweight_ca(entry)
 
         self.update_client(certs)
 
@@ -122,11 +134,10 @@ class CertUpdate(admintool.AdminTool):
         if services.knownservices.httpd.is_running():
             services.knownservices.httpd.restart()
 
-        nickname = 'caSigningCert cert-pki-ca'
         criteria = {
             'cert-database': paths.PKI_TOMCAT_ALIAS_DIR,
-            'cert-nickname': nickname,
-            'ca-name': 'dogtag-ipa-ca-renew-agent',
+            'cert-nickname': IPA_CA_NICKNAME,
+            'ca-name': RENEWAL_CA_NAME
         }
         request_id = certmonger.get_request_id(criteria)
         if request_id is not None:
@@ -152,6 +163,39 @@ class CertUpdate(admintool.AdminTool):
 
         self.update_file(paths.CA_CRT, certs)
 
+    def server_track_lightweight_ca(self, entry):
+        nickname = "{} {}".format(IPA_CA_NICKNAME, entry['ipacaid'][0])
+        criteria = {
+            'cert-database': paths.PKI_TOMCAT_ALIAS_DIR,
+            'cert-nickname': nickname,
+            'ca-name': RENEWAL_CA_NAME,
+        }
+        request_id = certmonger.get_request_id(criteria)
+        if request_id is None:
+            try:
+                certmonger.dogtag_start_tracking(
+                    secdir=paths.PKI_TOMCAT_ALIAS_DIR,
+                    pin=certmonger.get_pin('internal'),
+                    pinfile=None,
+                    nickname=nickname,
+                    ca=RENEWAL_CA_NAME,
+                    pre_command='stop_pkicad',
+                    post_command='renew_ca_cert "%s"' % nickname,
+                )
+                request_id = certmonger.get_request_id(criteria)
+                certmonger.modify(request_id, profile='ipaCACertRenewal')
+                self.log.debug(
+                    'Lightweight CA renewal: '
+                    'added tracking request for "%s"', nickname)
+            except RuntimeError as e:
+                self.log.error(
+                    'Lightweight CA renewal: Certmonger failed to '
+                    'start tracking certificate: %s', e)
+        else:
+            self.log.debug(
+                'Lightweight CA renewal: '
+                'already tracking certificate "%s"', nickname)
+
     def update_file(self, filename, certs, mode=0o444):
         certs = (c[0] for c in certs if c[2] is not False)
         try:
-- 
2.5.5



More information about the Freeipa-devel mailing list