URL: https://github.com/freeipa/freeipa/pull/2001
Author: tiran
 Title: #2001: Use one Custodia peer to retrieve all secrets
Action: opened

PR body:
"""
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.

Signed-off-by: Christian Heimes <chei...@redhat.com>
"""

To pull the PR as Git branch:
git remote add ghfreeipa https://github.com/freeipa/freeipa
git fetch ghfreeipa pull/2001/head:pr2001
git checkout pr2001
From 1a085def934af91b13606de1947640f8b01dfc19 Mon Sep 17 00:00:00 2001
From: Christian Heimes <chei...@redhat.com>
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.

Signed-off-by: Christian Heimes <chei...@redhat.com>
---
 ipaserver/install/ca.py                    |  1 -
 ipaserver/install/cainstance.py            |  2 +-
 ipaserver/install/custodiainstance.py      | 73 +++++++++++++++++-------------
 ipaserver/install/kra.py                   |  1 -
 ipaserver/install/server/install.py        |  4 +-
 ipaserver/install/server/replicainstall.py |  2 +-
 6 files changed, 45 insertions(+), 38 deletions(-)

diff --git a/ipaserver/install/ca.py b/ipaserver/install/ca.py
index 16d9ea2843..2352958e5f 100644
--- a/ipaserver/install/ca.py
+++ b/ipaserver/install/ca.py
@@ -280,7 +280,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 a616ebce00..57b0c08bd4 100644
--- a/ipaserver/install/cainstance.py
+++ b/ipaserver/install/cainstance.py
@@ -763,7 +763,7 @@ def import_ra_cert(self, rafile, password=''):
         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 67bfc9d224..62e3eeb3d3 100644
--- a/ipaserver/install/custodiainstance.py
+++ b/ipaserver/install/custodiainstance.py
@@ -71,40 +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
         logger.info("Custodia uses LDAPI.")
-        ldap_uri = None
     else:
-        logger.info("Custodia uses '%s' as master peer.", custodia_master)
-        ldap_uri = 'ldap://{}'.format(custodia_master)
+        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)
@@ -124,7 +129,7 @@ def __config_file(self):
             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)
@@ -195,25 +200,24 @@ def __create_container(self):
         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
         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:
@@ -223,8 +227,11 @@ def _wait_keys(self, host, timeout=300):
                 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)):
                     logger.debug(
@@ -234,23 +241,25 @@ def _wait_keys(self, host, timeout=300):
                     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()
 
         with NSSDatabase(None) as tmpdb:
             tmpdb.create_db()
@@ -287,23 +296,23 @@ def _get_keys(self, ca_host, cacerts_file, cacerts_pwd, data):
                 '-o', cacerts_file
             ])
 
-    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 792e82409f..15149ed557 100644
--- a/ipaserver/install/kra.py
+++ b/ipaserver/install/kra.py
@@ -93,7 +93,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 31110c8f7e..f84493b774 100644
--- a/ipaserver/install/server/install.py
+++ b/ipaserver/install/server/install.py
@@ -1113,9 +1113,9 @@ def uninstall(installer):
     dsinstance.DsInstance(fstore=fstore).uninstall()
     if _server_trust_ad_installed:
         adtrustinstance.ADTRUSTInstance(fstore).uninstall()
-    # ldap_uri isn't used, but IPAKEMKeys parses /etc/ipa/default.conf
+    # realm isn't used, but IPAKEMKeys parses /etc/ipa/default.conf
     # otherwise, see https://pagure.io/freeipa/issue/7474 .
-    custodiainstance.CustodiaInstance(ldap_uri='ldapi://invalid').uninstall()
+    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 f9653ce788..76f1025a8c 100644
--- a/ipaserver/install/server/replicainstall.py
+++ b/ipaserver/install/server/replicainstall.py
@@ -1515,7 +1515,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)
 
_______________________________________________
FreeIPA-devel mailing list -- freeipa-devel@lists.fedorahosted.org
To unsubscribe send an email to freeipa-devel-le...@lists.fedorahosted.org
Fedora Code of Conduct: https://getfedora.org/code-of-conduct.html
List Guidelines: https://fedoraproject.org/wiki/Mailing_list_guidelines
List Archives: 
https://lists.fedoraproject.org/archives/list/freeipa-devel@lists.fedorahosted.org/message/WZP5FQZEPLLCSVP3VZIDZNXVCCIMIX37/

Reply via email to