From 1f720560273e16ca6c5e646a1f4bf0a7ec354aa5 Mon Sep 17 00:00:00 2001 From: Fraser Tweedale Date: Wed, 1 Jul 2020 16:43:08 +1000 Subject: [PATCH] acme: delete ACME RA account on server uninstall For each CA server, a Dogtag user account is created for the ACME service to use to authenticate to the CA subsystem. This commit cleans up the Dogtag account upon server uninstallation. The user deletion behaviour is extracted to a common method used for both ACME RA account deletion (on uninstall) and removal of the temporary admin account (during replica install). Part of: https://pagure.io/freeipa/issue/4751 Reviewed-By: Rob Crittenden --- ipaserver/install/cainstance.py | 7 +++- ipaserver/install/dogtaginstance.py | 53 +++++++++++++++++++++++------ ipaserver/install/server/install.py | 17 ++++++++- 3 files changed, 64 insertions(+), 13 deletions(-) diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py index ce20bb37c..473b17bef 100644 --- a/ipaserver/install/cainstance.py +++ b/ipaserver/install/cainstance.py @@ -1517,6 +1517,11 @@ class CAInstance(DogtagInstance): logger.debug("Successfully updated CRL") api.Backend.ra.override_port = None + @staticmethod + def acme_uid(fqdn: str) -> str: + """Compute ACME RA account uid.""" + return f'acme-{fqdn}' + def setup_acme(self) -> bool: """ Set up ACME service, if needed. @@ -1537,7 +1542,7 @@ class CAInstance(DogtagInstance): # create ACME agent group (if not exist already) and user self.ensure_group(ACME_AGENT_GROUP, "ACME RA accounts") - acme_user = f"acme-{self.fqdn}" + acme_user = self.acme_uid(self.fqdn) result = self.create_user( uid=acme_user, cn=acme_user, diff --git a/ipaserver/install/dogtaginstance.py b/ipaserver/install/dogtaginstance.py index 1e2bfaa07..058aa24fe 100644 --- a/ipaserver/install/dogtaginstance.py +++ b/ipaserver/install/dogtaginstance.py @@ -621,6 +621,47 @@ class DogtagInstance(service.Service): return password + @staticmethod + def delete_user(uid: str) -> bool: + """ + Delete the user, removing group memberships along the way. + + Return True if user was deleted or False if user entry + did not exist. + + """ + dn = _person_dn(uid) + + if not api.Backend.ldap2.isconnected(): + api.Backend.ldap2.connect() + + # remove group memberships + try: + entries = api.Backend.ldap2.get_entries( + OU_GROUPS_DN, filter=f'(uniqueMember={dn})') + except errors.EmptyResult: + entries = [] + except errors.NotFound: + # basedn not found; Dogtag is probably not installed. + # Let's ignore this and keep going. + entries = [] + + for entry in entries: + # remove the uniquemember value + entry['uniquemember'] = [ + v for v in entry['uniquemember'] + if DN(v) != dn + ] + api.Backend.ldap2.update_entry(entry) + + # delete user entry + try: + api.Backend.ldap2.delete_entry(dn) + except errors.NotFound: + return False + else: + return True + def setup_admin(self): self.admin_user = "admin-%s" % self.fqdn self.admin_password = ipautil.ipa_generate_password() @@ -681,18 +722,8 @@ class DogtagInstance(service.Service): attrvalue=self.admin_dn ) - def __remove_admin_from_group(self, group): - mod = [(ldap.MOD_DELETE, 'uniqueMember', self.admin_dn)] - try: - api.Backend.ldap2.modify_s(_group_dn(group), mod) - except ldap.NO_SUCH_ATTRIBUTE: - # already removed - pass - def teardown_admin(self): - for group in self.admin_groups: - self.__remove_admin_from_group(group) - api.Backend.ldap2.delete_entry(self.admin_dn) + self.delete_user(self.admin_user) def backup_config(self): """ diff --git a/ipaserver/install/server/install.py b/ipaserver/install/server/install.py index d6513c53c..8c042f529 100644 --- a/ipaserver/install/server/install.py +++ b/ipaserver/install/server/install.py @@ -40,7 +40,7 @@ from ipaserver.install import ( adtrust, bindinstance, ca, dns, dsinstance, httpinstance, installutils, kra, krbinstance, otpdinstance, custodiainstance, replication, service, - sysupgrade) + sysupgrade, cainstance) from ipaserver.install.installutils import ( IPA_MODULES, BadHostError, get_fqdn, get_server_ip_address, is_ipa_configured, load_pkcs12, read_password, verify_fqdn, @@ -319,6 +319,19 @@ def remove_master_from_managed_topology(api_instance, options): logger.warning("Failed to delete master: %s", e) +def cleanup_dogtag_server_specific_data(): + """ + There are data in Dogtag database related to specific servers. + Some of these data should be left alone, e.g. range assignments. + Some of these data should be cleaned up; that's what this + subroutine does. + + """ + # remove ACME user + acme_uid = cainstance.CAInstance.acme_uid(api.env.host) + cainstance.CAInstance.delete_user(acme_uid) + + @common_cleanup def install_check(installer): options = installer @@ -1103,6 +1116,8 @@ def uninstall_check(installer): ca.uninstall_check(options) + cleanup_dogtag_server_specific_data() + if domain_level == DOMAIN_LEVEL_0: rm = replication.ReplicationManager( realm=api.env.realm,