On 15/10/15 11:39, Martin Basti wrote:
Without this patch the ipa-ca-install is broken in current master.
Unexpected error - see /var/log/ipareplica-ca-install.log for details:
AttributeError: Values instance has no attribute 'promote'

Should be fixed with the attached patches.

--
Simo Sorce * Red Hat, Inc * New York
>From 78ddd9c8866713de182e65595fbf9e428ba67880 Mon Sep 17 00:00:00 2001
From: Simo Sorce <s...@redhat.com>
Date: Thu, 20 Aug 2015 17:10:23 -0400
Subject: [PATCH 1/2] Allow ipa-ca-install to use the new promotion code

This makes it possible to install a CA after-the-fact on a server
that has been promoted (and has no replica file available).

Signed-off-by: Simo Sorce <s...@redhat.com>
---
 install/tools/ipa-ca-install | 92 ++++++++++++++++++++++++++++++++++++--------
 ipaserver/install/ca.py      |  2 -
 2 files changed, 77 insertions(+), 17 deletions(-)

diff --git a/install/tools/ipa-ca-install b/install/tools/ipa-ca-install
index 6564e4d0304d4e189b133c495b75f200b04e2988..e59f016e43c76d4bd40ad4535596a37b5edaeb6e 100755
--- a/install/tools/ipa-ca-install
+++ b/install/tools/ipa-ca-install
@@ -21,12 +21,16 @@
 import sys
 import os
 import shutil
+import tempfile
 from ipapython import ipautil
 
 from ipaserver.install import installutils
 from ipaserver.install import certs
 from ipaserver.install.installutils import create_replica_config
+from ipaserver.install.installutils import check_creds, ReplicaConfig
 from ipaserver.install import dsinstance, ca
+from ipaserver.install import cainstance, custodiainstance
+from ipapython import dogtag
 from ipapython import version
 from ipalib import api
 from ipapython.dn import DN
@@ -67,6 +71,8 @@ def parse_options():
                       type="choice",
                       choices=('SHA1withRSA', 'SHA256withRSA', 'SHA512withRSA'),
                       help="Signing algorithm of the IPA CA certificate")
+    parser.add_option("-P", "--principal", dest="principal", sensitive=True,
+                      default=None, help="User allowed to manage replicas")
 
     options, args = parser.parse_args()
     safe_options = parser.get_safe_opts(options)
@@ -107,15 +113,24 @@ def install_replica(safe_options, options, filename):
         sys.argv[0], filename, safe_options)
     root_logger.debug('IPA version %s', version.VENDOR_VERSION)
 
-    if not ipautil.file_exists(filename):
-        sys.exit("Replica file %s does not exist" % filename)
-
     if not dsinstance.DsInstance().is_configured():
         sys.exit("IPA server is not configured on this system.\n")
 
     api.bootstrap(in_server=True)
     api.finalize()
 
+    domain_level = dsinstance.get_domain_level(api)
+    if domain_level > 0:
+        options.promote = True
+    else:
+        options.promote = False
+        if not ipautil.file_exists(filename):
+            sys.exit("Replica file %s does not exist" % filename)
+
+
+    # Check if we have admin creds already, otherwise acquire them
+    check_creds(options, api.env.realm)
+
     # get the directory manager password
     dirman_password = options.password
     if not dirman_password:
@@ -132,13 +147,36 @@ def install_replica(safe_options, options, filename):
             options.unattended:
         sys.exit('admin password required')
 
-    config = create_replica_config(dirman_password, filename, options)
+    if options.promote:
+        config = ReplicaConfig()
+        config.master_host_name = None
+        config.realm_name = api.env.realm
+        config.host_name = api.env.host
+        config.domain_name = api.env.domain
+        config.dirman_password = dirman_password
+        config.ca_ds_port = dogtag.install_constants.DS_PORT
+        config.top_dir = tempfile.mkdtemp("ipa")
+        config.dir = config.top_dir
+    else:
+        config = create_replica_config(dirman_password, filename, options)
+
     global REPLICA_INFO_TOP_DIR
     REPLICA_INFO_TOP_DIR = config.top_dir
     config.setup_ca = True
 
-    api.Backend.ldap2.connect(bind_dn=DN(('cn', 'Directory Manager')),
-                              bind_pw=dirman_password)
+    conn = api.Backend.ldap2
+    conn.connect(bind_dn=DN(('cn', 'Directory Manager')),
+                            bind_pw=dirman_password)
+
+    if config.subject_base is None:
+        attrs = conn.get_ipa_config()
+        config.subject_base = attrs.get('ipacertificatesubjectbase')[0]
+
+    if config.master_host_name is None:
+        config.ca_host_name = cainstance.find_ca_server(api.env.ca_host, conn)
+        config.master_host_name = config.ca_host_name
+    else:
+        config.ca_host_name = config.master_host_name
 
     options.realm_name = config.realm_name
     options.domain_name = config.domain_name
@@ -147,7 +185,22 @@ def install_replica(safe_options, options, filename):
     options.subject = config.subject_base
 
     ca.install_check(True, config, options)
-    ca.install(True, config, options)
+    if options.promote:
+        ca_data = (os.path.join(config.dir, 'cacert.p12'),
+                   config.dirman_password)
+        custodia = custodiainstance.CustodiaInstance(config.host_name,
+                                                     config.realm_name)
+        custodia.get_ca_keys(config.ca_host_name, ca_data[0], ca_data[1])
+
+        CA = cainstance.CAInstance(config.realm_name, certs.NSS_DIR,
+                                   dogtag_constants=dogtag.install_constants,
+                                   host_name=config.host_name,
+                                   dm_password=config.dirman_password)
+        CA.configure_replica(config.ca_host_name,
+                             subject_base=config.subject_base,
+                             ca_cert_bundle=ca_data)
+    else:
+        ca.install(True, config, options)
 
 
 def install_master(safe_options, options):
@@ -198,10 +251,20 @@ def main():
     if os.geteuid() != 0:
         sys.exit("\nYou must be root to run this script.\n")
 
-    if filename is not None:
-        install_replica(safe_options, options, filename)
-    else:
-        install_master(safe_options, options)
+    try:
+        if options.replica or filename is not None:
+            install_replica(safe_options, options, filename)
+        else:
+            install_master(safe_options, options)
+
+    finally:
+        # Clean up if we created custom credentials
+        created_ccache_file = getattr(options, 'created_ccache_file', None)
+        if created_ccache_file is not None:
+            try:
+                os.unlink(created_ccache_file)
+            except OSError:
+                pass
 
 fail_message = '''
 Your system may be partly configured.
@@ -210,10 +273,9 @@ Run /usr/sbin/ipa-server-install --uninstall to clean up.
 
 if __name__ == '__main__':
     try:
-        with ipautil.private_ccache():
-            installutils.run_script(main, log_file_name=log_file_name,
-                                    operation_name='ipa-ca-install',
-                                    fail_message=fail_message)
+        installutils.run_script(main, log_file_name=log_file_name,
+                                operation_name='ipa-ca-install',
+                                fail_message=fail_message)
     finally:
         # always try to remove decrypted replica file
         try:
diff --git a/ipaserver/install/ca.py b/ipaserver/install/ca.py
index 3eb9208c035002dce9c2a7faf3ebd3be935d2752..303a9da6f69d09a6337c6f4c3445a9f111a236b2 100644
--- a/ipaserver/install/ca.py
+++ b/ipaserver/install/ca.py
@@ -39,8 +39,6 @@ def install_check(standalone, replica_config, options):
 
         if standalone and not options.skip_conncheck:
             principal = options.principal
-            if principal is None:
-                principal = "admin"
             replica_conn_check(
                 replica_config.master_host_name, host_name, realm_name, True,
                 replica_config.ca_ds_port, options.admin_password,
-- 
2.4.3

>From 4232509c71f832aaaf3ccf38735dc73e8debee29 Mon Sep 17 00:00:00 2001
From: Simo Sorce <s...@redhat.com>
Date: Tue, 25 Aug 2015 15:42:25 -0400
Subject: [PATCH 2/2] Allow to install the KRA on a promoted server

Signed-off-by: Simo Sorce <s...@redhat.com>
---
 install/tools/ipa-ca-install               |   5 +-
 ipaserver/install/cainstance.py            | 108 +----------------------------
 ipaserver/install/custodiainstance.py      |  29 ++++++--
 ipaserver/install/dogtaginstance.py        |  91 ++++++++++++++++++++++--
 ipaserver/install/ipa_kra_install.py       |  60 ++++++++++++----
 ipaserver/install/kra.py                   |  31 ++++++++-
 ipaserver/install/krainstance.py           |  70 ++++++++++++++++---
 ipaserver/install/server/replicainstall.py |  29 +++++---
 ipaserver/install/service.py               |  30 +++++++-
 9 files changed, 301 insertions(+), 152 deletions(-)
 mode change 100644 => 100755 ipaserver/install/ipa_kra_install.py

diff --git a/install/tools/ipa-ca-install b/install/tools/ipa-ca-install
index e59f016e43c76d4bd40ad4535596a37b5edaeb6e..0620ae349ac7e0eba0b982dec9323f1439d8da46 100755
--- a/install/tools/ipa-ca-install
+++ b/install/tools/ipa-ca-install
@@ -29,7 +29,7 @@ from ipaserver.install import certs
 from ipaserver.install.installutils import create_replica_config
 from ipaserver.install.installutils import check_creds, ReplicaConfig
 from ipaserver.install import dsinstance, ca
-from ipaserver.install import cainstance, custodiainstance
+from ipaserver.install import cainstance, custodiainstance, service
 from ipapython import dogtag
 from ipapython import version
 from ipalib import api
@@ -173,7 +173,8 @@ def install_replica(safe_options, options, filename):
         config.subject_base = attrs.get('ipacertificatesubjectbase')[0]
 
     if config.master_host_name is None:
-        config.ca_host_name = cainstance.find_ca_server(api.env.ca_host, conn)
+        config.ca_host_name = \
+            service.find_providing_server('CA', conn, api.env.ca_host)
         config.master_host_name = config.ca_host_name
     else:
         config.ca_host_name = config.master_host_name
diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py
index 3a860b7ab25b33625cc8d91d50e3616ff29e6fee..22155cac335666889e090843af04a9449d16e68d 100644
--- a/ipaserver/install/cainstance.py
+++ b/ipaserver/install/cainstance.py
@@ -269,32 +269,6 @@ def is_step_one_done():
     return False
 
 
-def find_ca_server(host_name, conn, api=api):
-    """
-    :param host_name: the preferred server
-    :param conn: a connection to the LDAP server
-    :return: the selected host name
-
-    Find a server that is a CA.
-    """
-    dn = DN(('cn', 'masters'), ('cn', 'ipa'), ('cn', 'etc'), api.env.basedn)
-    query_filter = conn.make_filter({'objectClass': 'ipaConfigObject',
-                                     'ipaConfigString': 'enabledService',
-                                     'cn': 'CA'}, rules='&')
-    try:
-        entries, trunc = conn.find_entries(filter=query_filter, base_dn=dn)
-    except errors.NotFound:
-        return None
-    if len(entries):
-        if host_name is not None:
-            for entry in entries:
-                if entry.dn[1].value == host_name:
-                    return host_name
-        # if the preferred is not found, return the first in the list
-        return entries[0].dn[1].value
-    return None
-
-
 def is_ca_installed_locally():
     """Check if CA is installed locally by checking for existence of CS.cfg
     :return:True/False
@@ -1540,83 +1514,6 @@ class CAInstance(DogtagInstance):
         # Activate Topology for o=ipaca segments
         self.__update_topology()
 
-    def __add_admin_to_group(self, group):
-        dn = DN(('cn', group), ('ou', 'groups'), ('o', 'ipaca'))
-        entry = self.admin_conn.get_entry(dn)
-        members = entry.get('uniqueMember', [])
-        members.append(self.admin_dn)
-        mod = [(ldap.MOD_REPLACE, 'uniqueMember', members)]
-        try:
-            self.admin_conn.modify_s(dn, mod)
-        except ldap.TYPE_OR_VALUE_EXISTS:
-            # already there
-            pass
-
-    def __setup_admin(self):
-        self.admin_user = "admin-%s" % self.fqdn
-        self.admin_password = binascii.hexlify(os.urandom(16))
-
-        if not self.admin_conn:
-            self.ldap_connect()
-
-        self.admin_dn = DN(('uid', self.admin_user),
-                           ('ou', 'people'), ('o', 'ipaca'))
-
-        # remove user if left-over exists
-        try:
-            entry = self.admin_conn.delete_entry(self.admin_dn)
-        except errors.NotFound:
-            pass
-
-        # add user
-        entry = self.admin_conn.make_entry(
-            self.admin_dn,
-            objectclass=["top", "person", "organizationalPerson",
-                         "inetOrgPerson", "cmsuser"],
-            uid=[self.admin_user],
-            cn=[self.admin_user],
-            sn=[self.admin_user],
-            usertype=['adminType'],
-            mail=['root@localhost'],
-            userPassword=[self.admin_password],
-            userstate=['1']
-        )
-        self.admin_conn.add_entry(entry)
-
-        for group in ADMIN_GROUPS:
-            self.__add_admin_to_group(group)
-
-        # Now wait until the other server gets replicated this data
-        master_conn = ipaldap.IPAdmin(self.master_host,
-                                      port=replication.DEFAULT_PORT,
-                                      protocol='ldap')
-        master_conn.do_sasl_gssapi_bind()
-        replication.wait_for_entry(master_conn, entry)
-        del master_conn
-
-    def __remove_admin_from_group(self, group):
-        dn = DN(('cn', group), ('ou', 'groups'), ('o', 'ipaca'))
-        entry = self.admin_conn.get_entry(dn)
-        mod = [(ldap.MOD_DELETE, 'uniqueMember', self.admin_dn)]
-        try:
-            self.admin_conn.modify_s(dn, mod)
-        except ldap.NO_SUCH_ATTRIBUTE:
-            # already removed
-            pass
-
-    def __teardown_admin(self):
-
-        if not self.admin_conn:
-            self.ldap_connect()
-
-        for group in ADMIN_GROUPS:
-            self.__remove_admin_from_group(group)
-        self.admin_conn.delete_entry(self.admin_dn)
-
-    def __restart_ds_instance(self):
-        self.ldap_disconnect()
-        services.knownservices.dirsrv.restart()
-
     def __client_auth_to_db(self):
         self.enable_client_auth_to_db(self.dogtag_constants.CS_CFG_PATH)
 
@@ -1651,6 +1548,7 @@ class CAInstance(DogtagInstance):
         else:
             self.ca_type = 'generic'
 
+        self.admin_groups = ADMIN_GROUPS
         self.pkcs12_info = ca_cert_bundle
         self.no_db_setup = True
         self.clone = True
@@ -1664,7 +1562,7 @@ class CAInstance(DogtagInstance):
         self.step("creating certificate server db", self.__create_ds_db)
         self.step("setting up initial replication", self.__setup_replication)
 
-        self.step("creating installation admin user", self.__setup_admin)
+        self.step("creating installation admin user", self.setup_admin)
 
         # Setup instance
         self.step("setting up certificate server", self.__spawn_instance)
@@ -1675,7 +1573,7 @@ class CAInstance(DogtagInstance):
         self.step("enable PKIX certificate path discovery and validation",
                   self.enable_pkix)
         self.step("set up client auth to db", self.__client_auth_to_db)
-        self.step("destroying installation admin user", self.__teardown_admin)
+        self.step("destroying installation admin user", self.teardown_admin)
         self.step("starting instance", self.start_instance)
 
         self.step("importing CA chain to RA certificate database",
diff --git a/ipaserver/install/custodiainstance.py b/ipaserver/install/custodiainstance.py
index 6f4afb3a96b12562e794932966ac5f35581669c5..eb9512bf131cf73af3c6bf69bcaecc7b57d100ad 100644
--- a/ipaserver/install/custodiainstance.py
+++ b/ipaserver/install/custodiainstance.py
@@ -78,13 +78,12 @@ class CustodiaInstance(SimpleServiceInstance):
         cli = CustodiaClient(self.fqdn, master_host_name, self.realm)
         cli.fetch_key('dm/DMHash')
 
-    def get_ca_keys(self, ca_host, cacerts_file, cacerts_pwd):
+    def __get_keys(self, ca_host, cacerts_file, cacerts_pwd, data):
         # Fecth all needed certs one by one, then combine them in a single
         # p12 file
-        certlist = ['caSigningCert cert-pki-ca',
-                    'ocspSigningCert cert-pki-ca',
-                    'auditSigningCert cert-pki-ca',
-                    'subsystemCert cert-pki-ca']
+
+        prefix = data['prefix']
+        certlist = data['list']
 
         cli = CustodiaClient(self.fqdn, ca_host, self.realm)
 
@@ -104,7 +103,7 @@ class CustodiaInstance(SimpleServiceInstance):
                 f.flush()
 
             for nickname in certlist:
-                value = cli.fetch_key(os.path.join('ca', nickname), False)
+                value = cli.fetch_key(os.path.join(prefix, nickname), False)
                 v = json_decode(value)
                 pk12pwfile = os.path.join(tmpnssdir, 'pk12pwfile')
                 with open(pk12pwfile, 'w+') as f:
@@ -129,6 +128,24 @@ class CustodiaInstance(SimpleServiceInstance):
         finally:
             shutil.rmtree(tmpnssdir)
 
+    def get_ca_keys(self, ca_host, 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)
+
+    def get_kra_keys(self, ca_host, 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)
+
     def __start(self):
         super(CustodiaInstance, self).__start()
 
diff --git a/ipaserver/install/dogtaginstance.py b/ipaserver/install/dogtaginstance.py
index 5471dcfb777e80409369920a460630cee5fd0a64..106b75bc2c6b013eb5a0e216834b9201c7945881 100644
--- a/ipaserver/install/dogtaginstance.py
+++ b/ipaserver/install/dogtaginstance.py
@@ -18,6 +18,8 @@
 #
 
 import base64
+import binascii
+import ldap
 import os
 import shutil
 import tempfile
@@ -28,6 +30,8 @@ import pwd
 from pki.client import PKIConnection
 import pki.system
 
+from ipalib import errors
+
 from ipaplatform import services
 from ipaplatform.paths import paths
 from ipapython import certmonger
@@ -37,6 +41,7 @@ from ipapython import ipautil
 from ipapython.dn import DN
 from ipaserver.install import service
 from ipaserver.install import installutils
+from ipaserver.install import replication
 from ipaserver.install.installutils import stopped_service
 from ipapython.ipa_log_manager import log_mgr
 
@@ -144,7 +149,10 @@ class DogtagInstance(service.Service):
         self.clone = False
 
         self.basedn = DN(('o', 'ipa%s' % subsystem.lower()))
-        self.admin_user = DN(('uid', 'admin'), ('ou', 'people'), ('o', 'ipaca'))
+        self.admin_user = "admin"
+        self.admin_dn = DN(('uid', self.admin_user),
+                           ('ou', 'people'), ('o', 'ipaca'))
+        self.admin_groups = None
         self.agent_db = tempfile.mkdtemp(prefix="tmp-")
         self.ds_port = DEFAULT_DSPORT
         self.server_root = dogtag_constants.SERVER_ROOT
@@ -435,12 +443,10 @@ class DogtagInstance(service.Service):
         conn = None
 
         try:
-            conn = ipaldap.IPAdmin(self.fqdn, self.ds_port)
-            conn.do_simple_bind(
-                DN(('cn', 'Directory Manager')),
-                self.dm_password)
+            conn = ipaldap.IPAdmin(self.fqdn, ldapi=True, realm=self.realm)
+            conn.do_external_bind('root')
 
-            entry_attrs = conn.get_entry(self.admin_user, ['usercertificate'])
+            entry_attrs = conn.get_entry(self.admin_dn, ['usercertificate'])
             admin_cert = entry_attrs.get('usercertificate')[0]
 
             # TODO(edewata) Add check to warn if there is more than one cert.
@@ -462,3 +468,76 @@ class DogtagInstance(service.Service):
             self.log.critical("  %s" % log)
 
         raise RuntimeError("%s configuration failed." % self.subsystem)
+
+    def __add_admin_to_group(self, group):
+        dn = DN(('cn', group), ('ou', 'groups'), ('o', 'ipaca'))
+        entry = self.admin_conn.get_entry(dn)
+        members = entry.get('uniqueMember', [])
+        members.append(self.admin_dn)
+        mod = [(ldap.MOD_REPLACE, 'uniqueMember', members)]
+        try:
+            self.admin_conn.modify_s(dn, mod)
+        except ldap.TYPE_OR_VALUE_EXISTS:
+            # already there
+            pass
+
+    def setup_admin(self):
+        self.admin_user = "admin-%s" % self.fqdn
+        self.admin_password = binascii.hexlify(os.urandom(16))
+
+        if not self.admin_conn:
+            self.ldap_connect()
+
+        self.admin_dn = DN(('uid', self.admin_user),
+                           ('ou', 'people'), ('o', 'ipaca'))
+
+        # remove user if left-over exists
+        try:
+            entry = self.admin_conn.delete_entry(self.admin_dn)
+        except errors.NotFound:
+            pass
+
+        # add user
+        entry = self.admin_conn.make_entry(
+            self.admin_dn,
+            objectclass=["top", "person", "organizationalPerson",
+                         "inetOrgPerson", "cmsuser"],
+            uid=[self.admin_user],
+            cn=[self.admin_user],
+            sn=[self.admin_user],
+            usertype=['adminType'],
+            mail=['root@localhost'],
+            userPassword=[self.admin_password],
+            userstate=['1']
+        )
+        self.admin_conn.add_entry(entry)
+
+        for group in self.admin_groups:
+            self.__add_admin_to_group(group)
+
+        # Now wait until the other server gets replicated this data
+        master_conn = ipaldap.IPAdmin(self.master_host,
+                                      port=DEFAULT_DSPORT,
+                                      protocol='ldap')
+        master_conn.do_sasl_gssapi_bind()
+        replication.wait_for_entry(master_conn, entry)
+        del master_conn
+
+    def __remove_admin_from_group(self, group):
+        dn = DN(('cn', group), ('ou', 'groups'), ('o', 'ipaca'))
+        entry = self.admin_conn.get_entry(dn)
+        mod = [(ldap.MOD_DELETE, 'uniqueMember', self.admin_dn)]
+        try:
+            self.admin_conn.modify_s(dn, mod)
+        except ldap.NO_SUCH_ATTRIBUTE:
+            # already removed
+            pass
+
+    def teardown_admin(self):
+
+        if not self.admin_conn:
+            self.ldap_connect()
+
+        for group in self.admin_groups:
+            self.__remove_admin_from_group(group)
+        self.admin_conn.delete_entry(self.admin_dn)
diff --git a/ipaserver/install/ipa_kra_install.py b/ipaserver/install/ipa_kra_install.py
old mode 100644
new mode 100755
index ef2b2f9857c313f68bdc58bf8d3d15bf42a0debd..069c872782c12271568791dcc31a9067e3cd9cf1
--- a/ipaserver/install/ipa_kra_install.py
+++ b/ipaserver/install/ipa_kra_install.py
@@ -20,6 +20,8 @@
 
 from __future__ import print_function
 
+import tempfile
+
 from textwrap import dedent
 from ipalib import api
 from ipaplatform import services
@@ -28,11 +30,14 @@ from ipapython import admintool
 from ipapython import dogtag
 from ipapython import ipautil
 from ipapython.dn import DN
+from ipaserver.install import service
 from ipaserver.install import krainstance
+from ipaserver.install import dsinstance
 from ipaserver.install import installutils
 from ipaserver.install.installutils import create_replica_config
 from ipaserver.install import dogtaginstance
 from ipaserver.install import kra
+from ipaserver.install.installutils import ReplicaConfig
 
 
 class KRAInstall(admintool.AdminTool):
@@ -129,8 +134,14 @@ class KRAInstaller(KRAInstall):
             )
 
         self.installing_replica = dogtaginstance.is_installing_replica("KRA")
+        self.options.promote = False
 
         if self.installing_replica:
+            domain_level = dsinstance.get_domain_level(api)
+            if domain_level > 0:
+                self.options.promote = True
+                return
+
             if not self.args:
                 self.option_parser.error("A replica file is required.")
             if len(self.args) > 1:
@@ -160,29 +171,48 @@ class KRAInstaller(KRAInstall):
         super(KRAInstaller, self).run()
         print(dedent(self.INSTALLER_START_MESSAGE))
 
-        if not self.installing_replica:
-            replica_config = None
-        else:
-            replica_config = create_replica_config(
-                self.options.password,
-                self.replica_file,
-                self.options)
-
         self.options.dm_password = self.options.password
         self.options.setup_ca = False
 
-        api.Backend.ldap2.connect(bind_dn=DN('cn=Directory Manager'),
-                                  bind_pw=self.options.dm_password)
+        conn = api.Backend.ldap2
+        conn.connect(bind_dn=DN(('cn', 'Directory Manager')),
+                     bind_pw=self.options.password)
+
+        config = None
+        if self.installing_replica:
+            if self.options.promote:
+                config = ReplicaConfig()
+                config.master_host_name = None
+                config.realm_name = api.env.realm
+                config.host_name = api.env.host
+                config.domain_name = api.env.domain
+                config.dirman_password = self.options.password
+                config.ca_ds_port = dogtag.install_constants.DS_PORT
+                config.top_dir = tempfile.mkdtemp("ipa")
+                config.dir = config.top_dir
+            else:
+                config = create_replica_config(
+                    self.options.password,
+                    self.replica_file,
+                    self.options)
+
+            if config.subject_base is None:
+                attrs = conn.get_ipa_config()
+                config.subject_base = attrs.get('ipacertificatesubjectbase')[0]
+
+            if config.master_host_name is None:
+                config.kra_host_name = \
+                    service.find_providing_server('KRA', conn, api.env.ca_host)
+                config.master_host_name = config.kra_host_name
+            else:
+                config.kra_host_name = config.master_host_name
 
         try:
-            kra.install_check(api, replica_config, self.options)
+            kra.install_check(api, config, self.options)
         except RuntimeError as e:
             raise admintool.ScriptError(str(e))
 
-        kra.install(api, replica_config, self.options)
-
-        # Restart apache for new proxy config file
-        services.knownservices.httpd.restart(capture_output=True)
+        kra.install(api, config, self.options)
 
     def run(self):
         try:
diff --git a/ipaserver/install/kra.py b/ipaserver/install/kra.py
index f3a0fe5c6945039315839af53c7b6f5649f67cd7..40c283e6d5ca2dc9647abf70624d56d05edd2c51 100644
--- a/ipaserver/install/kra.py
+++ b/ipaserver/install/kra.py
@@ -2,11 +2,15 @@
 # Copyright (C) 2015  FreeIPA Contributors see COPYING for license
 #
 
+import os
+
 from ipalib import api, errors
+from ipaplatform import services
 from ipapython import certdb
 from ipapython import dogtag
 from ipapython import ipautil
 from ipapython.dn import DN
+from ipaserver.install import custodiainstance
 from ipaserver.install import cainstance
 from ipaserver.install import krainstance
 from ipaserver.install import dsinstance
@@ -36,6 +40,9 @@ def install_check(api, replica_config, options):
         if not api.Command.kra_is_enabled()['result']:
             raise RuntimeError("KRA is not installed on the master system")
 
+        if options.promote:
+            return
+
         with certdb.NSSDatabase() as tmpdb:
             pw = ipautil.write_tmp_file(ipautil.ipa_generate_password())
             tmpdb.create_db(pw.name)
@@ -62,7 +69,26 @@ def install(api, replica_config, options):
             api.env.realm, api.env.host, options.dm_password,
             options.dm_password, subject_base=subject)
     else:
-        kra = krainstance.install_replica_kra(replica_config)
+        if options.promote:
+            ca_data = (os.path.join(replica_config.dir, 'kracert.p12'),
+                       replica_config.dirman_password)
+
+            custodia = custodiainstance.CustodiaInstance(
+                replica_config.host_name, replica_config.realm_name)
+            custodia.get_kra_keys(replica_config.kra_host_name,
+                                  ca_data[0], ca_data[1])
+
+            kra = krainstance.KRAInstance(
+                replica_config.realm_name,
+                dogtag_constants=dogtag.install_constants)
+            kra.configure_replica(replica_config.host_name,
+                                  replica_config.kra_host_name,
+                                  replica_config.dirman_password,
+                                  kra_cert_bundle=ca_data)
+            return
+
+        else:
+            kra = krainstance.install_replica_kra(replica_config)
 
     service.print_msg("Restarting the directory server")
     ds = dsinstance.DsInstance()
@@ -72,6 +98,9 @@ def install(api, replica_config, options):
 
     kra.enable_client_auth_to_db(kra.dogtag_constants.KRA_CS_CFG_PATH)
 
+    # Restart apache for new proxy config file
+    services.knownservices.httpd.restart(capture_output=True)
+
 
 def uninstall(standalone):
     dogtag_constants = dogtag.configured_constants(api)
diff --git a/ipaserver/install/krainstance.py b/ipaserver/install/krainstance.py
index 52b6a58c6c09e216d7345181ea8fc0ac072b2d49..e75860d2802bc54cdf7fc47fdcaee60842e1a350 100644
--- a/ipaserver/install/krainstance.py
+++ b/ipaserver/install/krainstance.py
@@ -47,6 +47,11 @@ from ipapython.ipa_log_manager import log_mgr
 # replicas with KRA configured
 IPA_KRA_RECORD = "ipa-kra"
 
+ADMIN_GROUPS = [
+    'Enterprise CA Administrators',
+    'Enterprise KRA Administrators',
+    'Security Domain Administrators'
+]
 
 class KRAInstance(DogtagInstance):
     """
@@ -147,7 +152,7 @@ class KRAInstance(DogtagInstance):
         # Security Domain Authentication
         config.set("KRA", "pki_security_domain_https_port", "443")
         config.set("KRA", "pki_security_domain_password", self.admin_password)
-        config.set("KRA", "pki_security_domain_user", "admin")
+        config.set("KRA", "pki_security_domain_user", self.admin_user)
 
         # issuing ca
         config.set("KRA", "pki_issuing_ca_uri", "https://%s"; %
@@ -166,8 +171,8 @@ class KRAInstance(DogtagInstance):
         config.set("KRA", "pki_client_pkcs12_password", self.admin_password)
 
         # Administrator
-        config.set("KRA", "pki_admin_name", "admin")
-        config.set("KRA", "pki_admin_uid", "admin")
+        config.set("KRA", "pki_admin_name", self.admin_user)
+        config.set("KRA", "pki_admin_uid", self.admin_user)
         config.set("KRA", "pki_admin_email", "root@localhost")
         config.set("KRA", "pki_admin_password", self.admin_password)
         config.set("KRA", "pki_admin_nickname", "ipa-ca-agent")
@@ -227,15 +232,16 @@ class KRAInstance(DogtagInstance):
             pent = pwd.getpwnam(PKI_USER)
             os.chown(p12_tmpfile_name, pent.pw_uid, pent.pw_gid)
 
-            # create admin cert file if it does not exist
-            cert = DogtagInstance.get_admin_cert(self)
-            with open(paths.ADMIN_CERT_PATH, "w") as admin_path:
-                admin_path.write(cert)
+            # FIXME
+            # # create admin cert file if it does not exist
+            # cert = DogtagInstance.get_admin_cert(self)
+            # with open(paths.ADMIN_CERT_PATH, "w") as admin_path:
+            #     admin_path.write(cert)
 
             # Security domain registration
             config.set("KRA", "pki_security_domain_hostname", self.master_host)
             config.set("KRA", "pki_security_domain_https_port", "443")
-            config.set("KRA", "pki_security_domain_user", "admin")
+            config.set("KRA", "pki_security_domain_user", self.admin_user)
             config.set("KRA", "pki_security_domain_password",
                        self.admin_password)
 
@@ -342,6 +348,54 @@ class KRAInstance(DogtagInstance):
             dogtag.configured_constants().KRA_CS_CFG_PATH,
             dogtag_constants)
 
+    def __enable_instance(self):
+        self.ldap_enable('KRA', self.fqdn, None, self.suffix)
+
+    def configure_replica(self, host_name, master_host, dm_password,
+                          kra_cert_bundle=None, subject_base=None):
+        """Create a KRA instance.
+
+           To create a clone, pass in pkcs12_info.
+        """
+        self.fqdn = host_name
+        self.dm_password = dm_password
+        self.ds_port = DEFAULT_DSPORT
+        self.master_host = master_host
+        if subject_base is None:
+            self.subject_base = DN(('O', self.realm))
+        else:
+            self.subject_base = subject_base
+        self.suffix = ipautil.realm_to_suffix(self.realm)
+
+        self.pkcs12_info = kra_cert_bundle
+        self.clone = True
+        self.admin_groups = ADMIN_GROUPS
+
+        # Confirm that a KRA does not already exist
+        if self.is_installed():
+            raise RuntimeError(
+                "KRA already installed.")
+        # Confirm that a Dogtag 10 CA instance already exists
+        ca = cainstance.CAInstance(self.realm, certs.NSS_DIR,
+                                   dogtag_constants=dogtag.Dogtag10Constants)
+        if not ca.is_installed():
+            raise RuntimeError(
+                "KRA configuration failed.  "
+                "A Dogtag CA must be installed first")
+
+        self.step("creating installation admin user", self.setup_admin)
+        self.step("configuring KRA instance", self.__spawn_instance)
+        self.step("destroying installation admin user", self.teardown_admin)
+        self.step("restarting KRA", self.restart_instance)
+        self.step("configure certmonger for renewals",
+                  self.configure_certmonger_renewal)
+        self.step("configure certificate renewals", self.configure_renewal)
+        self.step("add vault container", self.__add_vault_container)
+
+        self.step("enabling KRA instance", self.__enable_instance)
+
+        self.start_creation(runtime=126)
+
 
 def install_replica_kra(config, postinstall=False):
     """
diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py
index 071b12bc883bef7d7dce810dfefa2034427d91a7..93ab75e01bd532032af5d83718f2567ee2a73146 100644
--- a/ipaserver/install/server/replicainstall.py
+++ b/ipaserver/install/server/replicainstall.py
@@ -28,8 +28,8 @@ import ipaclient.ipachangeconf
 import ipaclient.ntpconf
 from ipaserver.install import (
     bindinstance, ca, cainstance, certs, dns, dsinstance, httpinstance,
-    installutils, kra, krbinstance, memcacheinstance, ntpinstance,
-    otpdinstance, custodiainstance, service)
+    installutils, kra, krainstance, krbinstance, memcacheinstance,
+    ntpinstance, otpdinstance, custodiainstance, service)
 from ipaserver.install.installutils import create_replica_config
 from ipaserver.install.installutils import ReplicaConfig
 from ipaserver.install.replication import (
@@ -772,10 +772,6 @@ def promote_check(installer):
 
     installer._top_dir = tempfile.mkdtemp("ipa")
 
-    # FIXME: to implement yet
-    if options.setup_kra:
-        raise NotImplementedError
-
     tasks.check_selinux_status()
 
     client_fstore = sysrestore.FileStore(paths.IPA_CLIENT_SYSRESTORE)
@@ -922,7 +918,7 @@ def promote_check(installer):
             config.subject_base = DN(subject_base)
 
         # Find if any server has a CA
-        ca_host = cainstance.find_ca_server(api.env.server, conn)
+        ca_host = service.find_providing_server('CA', conn, api.env.server)
         if ca_host is not None:
             config.ca_host_name = ca_host
             ca_enabled = True
@@ -932,6 +928,13 @@ def promote_check(installer):
                               "installed, can't proceed without certs")
             sys.exit(3)
 
+        config.kra_host_name = service.find_providing_server('KRA', conn,
+                                                             api.env.server)
+        if options.setup_kra and config.kra_host_name is None:
+            root_logger.error("There is no KRA server in the domain, can't "
+                              "setup a KRA clone")
+            sys.exit(3)
+
         if options.setup_ca:
             if not ca_enabled:
                 root_logger.error("The remote master does not have a CA "
@@ -1083,7 +1086,17 @@ def promote(installer):
                              ca_cert_bundle=ca_data)
 
     if options.setup_kra:
-        kra.install(api, config, options)
+        ca_data = (os.path.join(config.dir, 'kracert.p12'),
+                   config.dirman_password)
+        custodia.get_kra_keys(config.kra_host_name, ca_data[0], ca_data[1])
+
+        constants = dogtag.install_constants
+        kra = krainstance.KRAInstance(config.realm_name,
+                                      dogtag_constants=constants)
+        kra.configure_replica(config.host_name, config.kra_host_name,
+                              config.dirman_password,
+                              kra_cert_bundle=ca_data)
+
 
     ds.replica_populate()
 
diff --git a/ipaserver/install/service.py b/ipaserver/install/service.py
index b2d111cdf4f29d95b53ccf5df9d19ade4cceff5b..7b7b68c15adc477b751e8a03aed9627685898f0f 100644
--- a/ipaserver/install/service.py
+++ b/ipaserver/install/service.py
@@ -27,7 +27,7 @@ import traceback
 from ipapython import sysrestore, ipautil, dogtag, ipaldap
 from ipapython.dn import DN
 from ipapython.ipa_log_manager import *
-from ipalib import errors, certstore
+from ipalib import api, errors, certstore
 from ipaplatform import services
 from ipaplatform.paths import paths
 
@@ -100,6 +100,34 @@ def add_principals_to_group(admin_conn, group, member_attr, principals):
         # If there are no changes just pass
         pass
 
+
+def find_providing_server(svcname, conn, host_name=None, api=api):
+    """
+    :param svcname: The service to find
+    :param conn: a connection to the LDAP server
+    :param host_name: the preferred server
+    :return: the selected host name
+
+    Find a server that is a CA.
+    """
+    dn = DN(('cn', 'masters'), ('cn', 'ipa'), ('cn', 'etc'), api.env.basedn)
+    query_filter = conn.make_filter({'objectClass': 'ipaConfigObject',
+                                     'ipaConfigString': 'enabledService',
+                                     'cn': svcname}, rules='&')
+    try:
+        entries, trunc = conn.find_entries(filter=query_filter, base_dn=dn)
+    except errors.NotFound:
+        return None
+    if len(entries):
+        if host_name is not None:
+            for entry in entries:
+                if entry.dn[1].value == host_name:
+                    return host_name
+        # if the preferred is not found, return the first in the list
+        return entries[0].dn[1].value
+    return None
+
+
 class Service(object):
     def __init__(self, service_name, service_desc=None, sstore=None,
                  dm_password=None, ldapi=True, autobind=ipaldap.AUTOBIND_AUTO,
-- 
2.4.3

-- 
Manage your subscription for the Freeipa-devel mailing list:
https://www.redhat.com/mailman/listinfo/freeipa-devel
Contribute to FreeIPA: http://www.freeipa.org/page/Contribute/Code

Reply via email to