Blob Blame History Raw
From a0626e09b3eaf5d030982e2ff03e95841ad1b4b9 Mon Sep 17 00:00:00 2001
From: Rob Crittenden <rcritten@redhat.com>
Date: Wed, 3 Feb 2021 15:52:05 -0500
Subject: [PATCH] ipa-cert-fix: Don't hardcode the NSS certificate nickname

The nickname of the 389-ds certificate was hardcoded as
Server-Cert which failed if the user had installed a
third-party certificate using ipa-server-certinstall.

Instead pull the nickname from the DS configuration and
retrieve it based on that.

https://pagure.io/freeipa/issue/8600

Signed-off-by: Rob Crittenden <rcritten@redhat.com>
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
---
 ipaserver/install/ipa_cert_fix.py | 17 +++++++++++------
 1 file changed, 11 insertions(+), 6 deletions(-)

diff --git a/ipaserver/install/ipa_cert_fix.py b/ipaserver/install/ipa_cert_fix.py
index 2f2c15613..29af89cd5 100644
--- a/ipaserver/install/ipa_cert_fix.py
+++ b/ipaserver/install/ipa_cert_fix.py
@@ -203,9 +203,12 @@ def expired_ipa_certs(now):
         certs.append((IPACertType.HTTPS, cert))
 
     # LDAPS
-    ds_dbdir = dsinstance.config_dirname(realm_to_serverid(api.env.realm))
+    serverid = realm_to_serverid(api.env.realm)
+    ds = dsinstance.DsInstance(realm_name=api.env.realm)
+    ds_dbdir = dsinstance.config_dirname(serverid)
+    ds_nickname = ds.get_server_cert_nickname(serverid)
     db = NSSDatabase(nssdir=ds_dbdir)
-    cert = db.get_cert('Server-Cert')
+    cert = db.get_cert(ds_nickname)
     if cert.not_valid_after <= now:
         certs.append((IPACertType.LDAPS, cert))
 
@@ -344,11 +347,13 @@ def install_ipa_certs(subject_base, ca_subject_dn, certs):
         elif certtype is IPACertType.HTTPS:
             shutil.copyfile(cert_path, paths.HTTPD_CERT_FILE)
         elif certtype is IPACertType.LDAPS:
-            ds_dbdir = dsinstance.config_dirname(
-                realm_to_serverid(api.env.realm))
+            serverid = realm_to_serverid(api.env.realm)
+            ds = dsinstance.DsInstance(realm_name=api.env.realm)
+            ds_dbdir = dsinstance.config_dirname(serverid)
             db = NSSDatabase(nssdir=ds_dbdir)
-            db.delete_cert('Server-Cert')
-            db.import_pem_cert('Server-Cert', EMPTY_TRUST_FLAGS, cert_path)
+            ds_nickname = ds.get_server_cert_nickname(serverid)
+            db.delete_cert(ds_nickname)
+            db.import_pem_cert(ds_nickname, EMPTY_TRUST_FLAGS, cert_path)
         elif certtype is IPACertType.KDC:
             shutil.copyfile(cert_path, paths.KDC_CERT)
 
-- 
2.29.2

From 660507fda2394b17d709c47a05ce5df548a47990 Mon Sep 17 00:00:00 2001
From: Rob Crittenden <rcritten@redhat.com>
Date: Thu, 4 Feb 2021 08:25:48 -0500
Subject: [PATCH] ipatests: test third-party 389-ds cert with ipa-cert-fix

ipa-cert-fix was hardcoded to use Server-Cert as the nickname
so would fail if a third-party certificate was installed for DS.

https://pagure.io/freeipa/issue/8600

Signed-off-by: Rob Crittenden <rcritten@redhat.com>
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
---
 .../test_integration/test_ipa_cert_fix.py     | 57 +++++++++++++++++++
 1 file changed, 57 insertions(+)

diff --git a/ipatests/test_integration/test_ipa_cert_fix.py b/ipatests/test_integration/test_ipa_cert_fix.py
index 2f7de5526..f9e5fe6e2 100644
--- a/ipatests/test_integration/test_ipa_cert_fix.py
+++ b/ipatests/test_integration/test_ipa_cert_fix.py
@@ -11,6 +11,17 @@ import time
 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
+
+
+def server_install_teardown(func):
+    def wrapped(*args):
+        master = args[0].master
+        try:
+            func(*args)
+        finally:
+            ipa_certs_cleanup(master)
+    return wrapped
 
 
 class TestIpaCertFix(IntegrationTest):
@@ -94,3 +105,49 @@ class TestIpaCertFix(IntegrationTest):
             else:
                 # timeout
                 raise AssertionError('Timeout: Failed to renew all the certs')
+
+
+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
-- 
2.29.2

From 4cb6f0ba0df928eea60b20892a6fc85373627946 Mon Sep 17 00:00:00 2001
From: Rob Crittenden <rcritten@redhat.com>
Date: Fri, 5 Feb 2021 09:00:54 -0500
Subject: [PATCH] Set pki-core dependency to 10.3.3 for pki-server cert-fix bug

Related: https://github.com/dogtagpki/pki/issues/3387
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
---
 freeipa.spec.in | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/freeipa.spec.in b/freeipa.spec.in
index 93e473ac4..0e261285b 100755
--- a/freeipa.spec.in
+++ b/freeipa.spec.in
@@ -128,11 +128,11 @@
 %if 0%{?rhel} == 8
 # PKIConnection has been modified to always validate certs.
 # https://pagure.io/freeipa/issue/8379
-%global pki_version 10.9.0-0.4
+%global pki_version 10.10.4-1
 %else
 # New KRA profile, ACME support
 # https://pagure.io/freeipa/issue/8545
-%global pki_version 10.10.0-2
+%global pki_version 10.10.3-1
 %endif
 
 # RHEL 8.3+, F32+ has 0.79.13
-- 
2.29.2

From f3463728f2196589d36e14cedccb26c03730a7c0 Mon Sep 17 00:00:00 2001
From: Rob Crittenden <rcritten@redhat.com>
Date: Wed, 10 Feb 2021 16:07:13 -0500
Subject: [PATCH] Don't renew non-IPA issued certs in ipa-cert-fix

If the Apache, 389-ds or KDC certificate was issued by
a third party there is nothing we can do, regardless of
whether it is expired or not.

Report which certificates will not be renewed so the
admin can manually do do (likely in the event of a
third-party certificate).

https://pagure.io/freeipa/issue/8600

Signed-off-by: Rob Crittenden <rcritten@redhat.com>
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
---
 ipaserver/install/ipa_cert_fix.py | 53 +++++++++++++++++++++++++------
 1 file changed, 43 insertions(+), 10 deletions(-)

diff --git a/ipaserver/install/ipa_cert_fix.py b/ipaserver/install/ipa_cert_fix.py
index 29af89cd5..210cf80f1 100644
--- a/ipaserver/install/ipa_cert_fix.py
+++ b/ipaserver/install/ipa_cert_fix.py
@@ -43,6 +43,7 @@ from ipapython.certdb import NSSDatabase, EMPTY_TRUST_FLAGS
 from ipapython.dn import DN
 from ipapython.ipaldap import realm_to_serverid
 from ipaserver.install import ca, cainstance, dsinstance
+from ipaserver.install.certs import is_ipa_issued_cert
 from ipapython import directivesetter
 from ipapython import ipautil
 
@@ -104,6 +105,13 @@ class IPACertFix(AdminTool):
 
         api.bootstrap(in_server=True, confdir=paths.ETC_IPA)
         api.finalize()
+
+        if not dsinstance.is_ds_running(realm_to_serverid(api.env.realm)):
+            print(
+                "The LDAP server is not running; cannot proceed."
+            )
+            return 1
+
         api.Backend.ldap2.connect()  # ensure DS is up
 
         subject_base = dsinstance.DsInstance().find_subject_base()
@@ -113,7 +121,7 @@ class IPACertFix(AdminTool):
         ca_subject_dn = ca.lookup_ca_subject(api, subject_base)
 
         now = datetime.datetime.now() + datetime.timedelta(weeks=2)
-        certs, extra_certs = expired_certs(now)
+        certs, extra_certs, non_renewed = expired_certs(now)
 
         if not certs and not extra_certs:
             print("Nothing to do.")
@@ -121,7 +129,7 @@ class IPACertFix(AdminTool):
 
         print(msg)
 
-        print_intentions(certs, extra_certs)
+        print_intentions(certs, extra_certs, non_renewed)
 
         response = ipautil.user_input('Enter "yes" to proceed')
         if response.lower() != 'yes':
@@ -133,7 +141,10 @@ class IPACertFix(AdminTool):
             fix_certreq_directives(certs)
             run_cert_fix(certs, extra_certs)
         except ipautil.CalledProcessError:
-            if any(x[0] is IPACertType.LDAPS for x in extra_certs):
+            if any(
+                x[0] is IPACertType.LDAPS
+                for x in extra_certs + non_renewed
+            ):
                 # The DS cert was expired.  This will cause
                 # 'pki-server cert-fix' to fail at the final
                 # restart.  Therefore ignore the CalledProcessError
@@ -152,13 +163,15 @@ class IPACertFix(AdminTool):
             print("Becoming renewal master.")
             cainstance.CAInstance().set_renewal_master()
 
+        print("Restarting IPA")
         ipautil.run(['ipactl', 'restart'], raiseonerr=True)
 
         return 0
 
 
 def expired_certs(now):
-    return expired_dogtag_certs(now), expired_ipa_certs(now)
+    expired_ipa, non_renew_ipa = expired_ipa_certs(now)
+    return expired_dogtag_certs(now), expired_ipa, non_renew_ipa
 
 
 def expired_dogtag_certs(now):
@@ -191,6 +204,7 @@ def expired_ipa_certs(now):
 
     """
     certs = []
+    non_renewed = []
 
     # IPA RA
     cert = x509.load_certificate_from_file(paths.RA_AGENT_PEM)
@@ -200,7 +214,10 @@ def expired_ipa_certs(now):
     # Apache HTTPD
     cert = x509.load_certificate_from_file(paths.HTTPD_CERT_FILE)
     if cert.not_valid_after <= now:
-        certs.append((IPACertType.HTTPS, cert))
+        if not is_ipa_issued_cert(api, cert):
+            non_renewed.append((IPACertType.HTTPS, cert))
+        else:
+            certs.append((IPACertType.HTTPS, cert))
 
     # LDAPS
     serverid = realm_to_serverid(api.env.realm)
@@ -210,18 +227,24 @@ def expired_ipa_certs(now):
     db = NSSDatabase(nssdir=ds_dbdir)
     cert = db.get_cert(ds_nickname)
     if cert.not_valid_after <= now:
-        certs.append((IPACertType.LDAPS, cert))
+        if not is_ipa_issued_cert(api, cert):
+            non_renewed.append((IPACertType.LDAPS, cert))
+        else:
+            certs.append((IPACertType.LDAPS, cert))
 
     # KDC
     cert = x509.load_certificate_from_file(paths.KDC_CERT)
     if cert.not_valid_after <= now:
-        certs.append((IPACertType.KDC, cert))
+        if not is_ipa_issued_cert(api, cert):
+            non_renewed.append((IPACertType.HTTPS, cert))
+        else:
+            certs.append((IPACertType.KDC, cert))
 
-    return certs
+    return certs, non_renewed
 
 
-def print_intentions(dogtag_certs, ipa_certs):
-    print("The following certificates will be renewed: ")
+def print_intentions(dogtag_certs, ipa_certs, non_renewed):
+    print("The following certificates will be renewed:")
     print()
 
     for certid, cert in dogtag_certs:
@@ -230,6 +253,16 @@ def print_intentions(dogtag_certs, ipa_certs):
     for certtype, cert in ipa_certs:
         print_cert_info("IPA", certtype.value, cert)
 
+    if non_renewed:
+        print(
+            "The following certificates will NOT be renewed because "
+            "they were not issued by the IPA CA:"
+        )
+        print()
+
+        for certtype, cert in non_renewed:
+            print_cert_info("IPA", certtype.value, cert)
+
 
 def print_cert_info(context, desc, cert):
     print("{} {} certificate:".format(context, desc))
-- 
2.29.2