From d3c09a6de06d8ae19d05c0b96cb9c2a5b789b472 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Thu, 7 Jun 2018 18:17:20 +0200 Subject: [PATCH] Use one Custodia peer to retrieve all secrets Fix 994f71ac8a1bb7ba6bc9caf0f6e4f59af44ad9c4 was incomplete. Under some circumstancs the DM hash and CA keys were still retrieved from two different machines. Custodia client now uses a single remote to upload keys and download all secrets. Fixes: https://pagure.io/freeipa/issue/7518 Signed-off-by: Christian Heimes Reviewed-By: Simo Sorce --- ipaserver/install/ca.py | 1 - ipaserver/install/cainstance.py | 2 +- ipaserver/install/custodiainstance.py | 74 +++++++++++++++++------------- ipaserver/install/kra.py | 1 - ipaserver/install/server/install.py | 4 +- ipaserver/install/server/replicainstall.py | 2 +- 6 files changed, 46 insertions(+), 38 deletions(-) diff --git a/ipaserver/install/ca.py b/ipaserver/install/ca.py index 2ac4382..1c7d375 100644 --- a/ipaserver/install/ca.py +++ b/ipaserver/install/ca.py @@ -235,7 +235,6 @@ def install_step_0(standalone, replica_config, options, custodia): cafile = os.path.join(replica_config.dir, 'cacert.p12') if options.promote: custodia.get_ca_keys( - replica_config.ca_host_name, cafile, replica_config.dirman_password) diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py index eefc30b..cc0dbf3 100644 --- a/ipaserver/install/cainstance.py +++ b/ipaserver/install/cainstance.py @@ -712,7 +712,7 @@ class CAInstance(DogtagInstance): self.configure_agent_renewal() def __import_ra_key(self): - self._custodia.import_ra_key(self.master_host) + self._custodia.import_ra_key() self.__set_ra_cert_perms() self.configure_agent_renewal() diff --git a/ipaserver/install/custodiainstance.py b/ipaserver/install/custodiainstance.py index 262ae58..ada8d03 100644 --- a/ipaserver/install/custodiainstance.py +++ b/ipaserver/install/custodiainstance.py @@ -71,41 +71,45 @@ def get_custodia_instance(config, mode): if mode == CustodiaModes.CA_PEER: # In case we install replica with CA, prefer CA host as source for # all Custodia secret material. - custodia_master = config.ca_host_name + custodia_peer = config.ca_host_name elif mode == CustodiaModes.KRA_PEER: - custodia_master = config.kra_host_name + custodia_peer = config.kra_host_name elif mode == CustodiaModes.MASTER_PEER: - custodia_master = config.master_host_name + custodia_peer = config.master_host_name elif mode == CustodiaModes.STANDALONE: - custodia_master = None + custodia_peer = None else: - custodia_master = None + custodia_peer = None - if custodia_master is None: + if custodia_peer is None: # use ldapi with local dirsrv instance root_logger.info("Custodia uses LDAPI.") - ldap_uri = None else: - root_logger.info("Custodia uses '%s' as master peer.", - custodia_master) - ldap_uri = 'ldap://{}'.format(custodia_master) + root_logger.info("Custodia uses '%s' as master peer.", custodia_peer) return CustodiaInstance( host_name=config.host_name, realm=config.realm_name, - ldap_uri=ldap_uri + custodia_peer=custodia_peer ) class CustodiaInstance(SimpleServiceInstance): - def __init__(self, host_name=None, realm=None, ldap_uri=None): + def __init__(self, host_name=None, realm=None, custodia_peer=None): super(CustodiaInstance, self).__init__("ipa-custodia") self.config_file = paths.IPA_CUSTODIA_CONF self.server_keys = paths.IPA_CUSTODIA_KEYS - self.ldap_uri = ldap_uri + self.custodia_peer = custodia_peer self.fqdn = host_name self.realm = realm + @property + def ldap_uri(self): + if self.custodia_peer is None: + return installutils.realm_to_ldapi_uri(self.realm) + else: + return "ldap://{}".format(self.custodia_peer) + def __config_file(self): template_file = os.path.basename(self.config_file) + '.template' template = os.path.join(paths.USR_SHARE_IPA_DIR, template_file) @@ -125,7 +129,7 @@ class CustodiaInstance(SimpleServiceInstance): ipautil.flush_sync(f) def create_instance(self): - if self.ldap_uri is None or self.ldap_uri.startswith('ldapi://'): + if self.ldap_uri.startswith('ldapi://'): # local case, ensure container exists self.step("Making sure custodia container exists", self.__create_container) @@ -174,25 +178,24 @@ class CustodiaInstance(SimpleServiceInstance): updater = ldapupdate.LDAPUpdate(sub_dict=sub_dict) updater.update([os.path.join(paths.UPDATES_DIR, '73-custodia.update')]) - def import_ra_key(self, master_host_name): - cli = self._get_custodia_client(server=master_host_name) + def import_ra_key(self): + cli = self._get_custodia_client() # please note that ipaCert part has to stay here for historical # reasons (old servers expect you to ask for ra/ipaCert during # replication as they store the RA agent cert in an NSS database # with this nickname) cli.fetch_key('ra/ipaCert') - def import_dm_password(self, master_host_name): - cli = self._get_custodia_client(server=master_host_name) + def import_dm_password(self): + cli = self._get_custodia_client() cli.fetch_key('dm/DMHash') - def _wait_keys(self, host, timeout=300): - ldap_uri = 'ldap://%s' % host + def _wait_keys(self, timeout=300): deadline = int(time.time()) + timeout root_logger.info("Waiting up to %s seconds to see our keys " - "appear on host: %s", timeout, host) + "appear on host %s", timeout, self.ldap_uri) - konn = KEMLdap(ldap_uri) + konn = KEMLdap(self.ldap_uri) saved_e = None while True: try: @@ -202,8 +205,11 @@ class CustodiaInstance(SimpleServiceInstance): if saved_e is None: # FIXME: Change once there's better way to show this # message in installer output, - print(" Waiting for keys to appear on host: {}, please " - "wait until this has completed.".format(host)) + print( + " Waiting for keys to appear on host: {}, please " + "wait until this has completed.".format( + self.ldap_uri) + ) # log only once for the same error if not isinstance(e, type(saved_e)): root_logger.debug( @@ -213,23 +219,25 @@ class CustodiaInstance(SimpleServiceInstance): raise RuntimeError("Timed out trying to obtain keys.") time.sleep(1) - def _get_custodia_client(self, server): + def _get_custodia_client(self): + if self.custodia_peer is None: + raise ValueError("Can't replicate secrets without Custodia peer") # Before we attempt to fetch keys from this host, make sure our public # keys have been replicated there. - self._wait_keys(server) + self._wait_keys() return CustodiaClient( client_service='host@{}'.format(self.fqdn), keyfile=self.server_keys, keytab=paths.KRB5_KEYTAB, - server=server, realm=self.realm + server=self.custodia_peer, realm=self.realm ) - def _get_keys(self, ca_host, cacerts_file, cacerts_pwd, data): + def _get_keys(self, cacerts_file, cacerts_pwd, data): # Fetch all needed certs one by one, then combine them in a single # PKCS12 file prefix = data['prefix'] certlist = data['list'] - cli = self._get_custodia_client(server=ca_host) + cli = self._get_custodia_client() # Temporary nssdb tmpnssdir = tempfile.mkdtemp(dir=paths.TMP) @@ -272,23 +280,23 @@ class CustodiaInstance(SimpleServiceInstance): finally: shutil.rmtree(tmpnssdir) - def get_ca_keys(self, ca_host, cacerts_file, cacerts_pwd): + def get_ca_keys(self, cacerts_file, cacerts_pwd): certlist = ['caSigningCert cert-pki-ca', 'ocspSigningCert cert-pki-ca', 'auditSigningCert cert-pki-ca', 'subsystemCert cert-pki-ca'] data = {'prefix': 'ca', 'list': certlist} - self._get_keys(ca_host, cacerts_file, cacerts_pwd, data) + self._get_keys(cacerts_file, cacerts_pwd, data) - def get_kra_keys(self, ca_host, cacerts_file, cacerts_pwd): + def get_kra_keys(self, cacerts_file, cacerts_pwd): certlist = ['auditSigningCert cert-pki-kra', 'storageCert cert-pki-kra', 'subsystemCert cert-pki-ca', 'transportCert cert-pki-kra'] data = {'prefix': 'ca', 'list': certlist} - self._get_keys(ca_host, cacerts_file, cacerts_pwd, data) + self._get_keys(cacerts_file, cacerts_pwd, data) def __start(self): super(CustodiaInstance, self).__start() diff --git a/ipaserver/install/kra.py b/ipaserver/install/kra.py index 3333970..d8773b9 100644 --- a/ipaserver/install/kra.py +++ b/ipaserver/install/kra.py @@ -91,7 +91,6 @@ def install(api, replica_config, options, custodia): paths.KRB5_KEYTAB, ccache) custodia.get_kra_keys( - replica_config.kra_host_name, krafile, replica_config.dirman_password) else: diff --git a/ipaserver/install/server/install.py b/ipaserver/install/server/install.py index f4338ed..3651cde 100644 --- a/ipaserver/install/server/install.py +++ b/ipaserver/install/server/install.py @@ -1085,7 +1085,9 @@ def uninstall(installer): dsinstance.DsInstance(fstore=fstore).uninstall() if _server_trust_ad_installed: adtrustinstance.ADTRUSTInstance(fstore).uninstall() - custodiainstance.CustodiaInstance().uninstall() + # realm isn't used, but IPAKEMKeys parses /etc/ipa/default.conf + # otherwise, see https://pagure.io/freeipa/issue/7474 . + custodiainstance.CustodiaInstance(realm='REALM.INVALID').uninstall() otpdinstance.OtpdInstance().uninstall() tasks.restore_hostname(fstore, sstore) fstore.restore_all_files() diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py index ef61590..b9cd518 100644 --- a/ipaserver/install/server/replicainstall.py +++ b/ipaserver/install/server/replicainstall.py @@ -1512,7 +1512,7 @@ def install(installer): krb.restart() if promote: - custodia.import_dm_password(config.master_host_name) + custodia.import_dm_password() promote_sssd(config.host_name) promote_openldap_conf(config.host_name, config.master_host_name) -- 2.9.3