Simo Sorce wrote:
On Fri, 28 Jan 2011 10:09:18 -0500
Rob Crittenden<[email protected]>  wrote:

Fix the "is the server configured" detection code to allow an
external CA installation to proceed.

We cache the install values between the first and second stage of a
CA installation. The install would fail in stage two if for some
reason this cache file didn't exist, this should also be fixed.

Also add a small loop in the restart code to wait for the Java webapp
to be available before proceeding. I also added another restart to
ensure that nonces are disabled in the running server. We had a
report where CS.cfg had nonces disabled but requests were failing
with nonce failures, this additional restart resolved it.

I tested the following scenarios:

1. Basic IPA install: ipa-server-install
2. External CA install: ipa-server-install --external-ca;
ipa-server-install --external_cert_file=/path/to/file
--external_ca_file=/path/to/file
3. External CA install with cache removal: ipa-server-instasll
--external-ca; rm /root/.ipa_cache; ipa-server-install
--external_cert_file=/path/to/file --external_ca_file=/path/to/file
4. Using just stage two of an external CA install which should fail:
ipa-server-install --external_cert_file=/path/to/file
--external_ca_file=/path/to/file

ticket 835

Mostly good but I have found a few issues with the approach.

1. ntpd got reinstalled, this probably wiped the original sysrestore
backups for it, installation of ntpd should be skipped in the seocnd
invocation.

2. .ipa_cache makes me *really* nervous and the fact you wipe it out
even on syntax errors when running the second step is fragile.

I suggest you encrypt the file with the DM password like we do for
replica files, and ask just for the DM password if the "external"
options are passed and the file is around.

If there is any error leave the file around (it's encrypted anyway)
Delete it only if the second step is successful.

Simo.


Updated patch attached.

rob
>From 4254339ae9191c4171bea97073d6ea240dfdbb88 Mon Sep 17 00:00:00 2001
From: Rob Crittenden <[email protected]>
Date: Wed, 26 Jan 2011 10:53:02 -0500
Subject: [PATCH] Fix installing with an external CA and wait for dogtag to come up

There wasn't an exception in the "is the server already installed"
check for a two-stage CA installation.

Made the installer slightly more robust. We create a cache file of
answers so the next run won't ask all the questions again. This cache
is removed when the installation is complete. Previously nothing would work
if the installer was run more than once, this should be fixed now.
The cache is encrypted using the DM password.

The second problem is that the tomcat6 init script returns control
before the web apps are up. Add a small loop in our restart method
to wait for the 9180 port to be available.

This also adds an additional restart to ensure that nonces are disabled.

ticket 835

revise
---
 install/tools/ipa-server-install |   50 ++++++++++++++++++++++++++++++-------
 ipaserver/install/cainstance.py  |   23 +++++++++++++++++
 2 files changed, 63 insertions(+), 10 deletions(-)

diff --git a/install/tools/ipa-server-install b/install/tools/ipa-server-install
index f1cab63..823026d 100755
--- a/install/tools/ipa-server-install
+++ b/install/tools/ipa-server-install
@@ -39,6 +39,7 @@ import glob
 import traceback
 from ConfigParser import RawConfigParser
 import random
+import tempfile
 
 from ipaserver.install import dsinstance
 from ipaserver.install import krbinstance
@@ -209,25 +210,33 @@ def signal_handler(signum, frame):
 
 ANSWER_CACHE = "/root/.ipa_cache"
 
-def read_cache():
+def read_cache(dm_password):
     """
     Returns a dict of cached answers or None if no cache file exists.
     """
     if not ipautil.file_exists(ANSWER_CACHE):
         return {}
 
+    top_dir = tempfile.mkdtemp("ipa")
+    try:
+        clearfile = "%s/cache" % top_dir
+        decrypt_file(ANSWER_CACHE, clearfile, dm_password, top_dir)
+    except Exception, e:
+        shutil.rmtree(top_dir)
+        raise RuntimeError("Problem decrypting answer cache in %s, check your password." % ANSWER_CACHE)
+
     optdict={}
     parser = RawConfigParser()
     try:
-        fp = open(ANSWER_CACHE, "r")
+        fp = open(clearfile, "r")
         parser.readfp(fp)
         optlist = parser.items('options')
         fp.close()
 
-        # this is one-use only
-        os.remove(ANSWER_CACHE)
     except IOError, e:
-        raise RuntimeError("Unable to determine serial number: %s" % str(e))
+        raise RuntimeError("Error reading cache file %s: %s" % (ANSWER_CACHE, str(e)))
+    finally:
+        shutil.rmtree(top_dir)
 
     for opt in optlist:
         value = opt[1]
@@ -253,15 +262,19 @@ def write_cache(options):
     # convert the options instance into a dict
     optdict = eval(str(options))
     parser = RawConfigParser()
+    top_dir = tempfile.mkdtemp("ipa")
     try:
-        fp = open(ANSWER_CACHE, "w")
+        fp = open("%s/cache" % top_dir, "w")
         parser.add_section('options')
         for opt in optdict:
             parser.set('options', opt, optdict[opt])
         parser.write(fp)
         fp.close()
+        ipautil.encrypt_file("%s/cache" % top_dir, ANSWER_CACHE, options.dm_password, top_dir);
     except IOError, e:
         raise RuntimeError("Unable to cache command-line options %s" % str(e))
+    finally:
+        shutil.rmtree(top_dir)
 
 def read_host_name(host_default,no_host_dns=False):
     host_name = ""
@@ -484,7 +497,7 @@ def main():
     else:
         standard_logging_setup("/var/log/ipaserver-install.log", options.debug)
         print "\nThe log file for this installation can be found in /var/log/ipaserver-install.log"
-        if dsinstance.DsInstance().is_configured() or cainstance.CADSInstance().is_configured():
+        if (dsinstance.DsInstance().is_configured() or cainstance.CADSInstance().is_configured()) and not options.external_cert_file:
             sys.exit("IPA server is already configured on this system.")
 
     logging.debug('%s was invoked with options: %s' % (sys.argv[0], safe_options))
@@ -518,7 +531,9 @@ def main():
         return uninstall()
 
     # This will override any settings passed in on the cmdline
-    options._update_loose(read_cache())
+    if ipautil.file_exists(ANSWER_CACHE):
+        dm_password = read_dm_password()
+        options._update_loose(read_cache(dm_password))
 
     print "=============================================================================="
     print "This program will set up the FreeIPA Server."
@@ -541,7 +556,9 @@ def main():
     print "To accept the default shown in brackets, press the Enter key."
     print ""
 
-    if not options.external_ca:
+    if not options.external_ca and not options.external_cert_file:
+        # Let it past if there is an external_cert_file defined on the chance
+        # that we are coming in without a cache file.
         check_dirsrv(options.unattended)
 
     ds_user = ""
@@ -697,7 +714,8 @@ def main():
     # Configure ntpd
     if options.conf_ntp:
         ntp = ntpinstance.NTPInstance(fstore)
-        ntp.create_instance()
+        if not ntp.is_configured():
+            ntp.create_instance()
 
     if options.selfsign:
         ca = certs.CertDB(realm_name, host_name=host_name,
@@ -712,6 +730,10 @@ def main():
 
         # Figure out what state we're in. See cainstance.py for more info on
         # the 3 states.
+        if options.external_cert_file is not None and options.external_ca_file is not None:
+            # These options imply this and this is required to install the CA.
+            # This is needed otherwise the setup of dogtag will fail.
+            options.external_ca = True
         external = 0
         if options.external_ca:
             external = 1
@@ -738,11 +760,17 @@ def main():
             options.domain_name = domain_name
             options.ds_user = ds_user
             options.master_password = master_password
+            options.dm_password = dm_password
+            options.admin_password = admin_password
             options.host_name = host_default
             options.unattended = True
             write_cache(options)
             ca.configure_instance("pkiuser", host_name, dm_password, dm_password, csr_file="/root/ipa.csr", subject_base=options.subject)
         else:
+            if not ca.is_installed():
+                # This can happen if someone passes external_ca_file without
+                # already having done the first stage of the CA install.
+                sys.exit('CA is not installed yet. To install with an external CA is a two-stage process.\nFirst run the installer with --external-ca.')
             ca.configure_instance("pkiuser", host_name, dm_password, dm_password, cert_file=options.external_cert_file, cert_chain_file=options.external_ca_file, subject_base=options.subject)
 
     # Now put the CA cert where other instances exepct it
@@ -913,6 +941,8 @@ def main():
             print "This file is required to create replicas. The password for this"
             print "file is the Directory Manager password"
 
+    if ipautil.file_exists(ANSWER_CACHE):
+        os.remove(ANSWER_CACHE)
     return 0
 
 try:
diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py
index dfe036d..a353263 100644
--- a/ipaserver/install/cainstance.py
+++ b/ipaserver/install/cainstance.py
@@ -35,6 +35,7 @@ import httplib
 import urllib
 import xml.dom.minidom
 import stat
+import socket
 from ipapython import dogtag
 from ipapython.certdb import get_ca_nickname
 from ipalib import pkcs10
@@ -389,6 +390,15 @@ class CAInstance(service.Service):
     def __del__(self):
         shutil.rmtree(self.ca_agent_db, ignore_errors=True)
 
+    def is_installed(self):
+        """
+        Installing with an external CA is a two-step process. This
+        is used to determine if the first step has been done.
+
+        Returns True/False
+        """
+        return os.path.exists(self.server_root + '/' + PKI_INSTANCE_NAME)
+
     def configure_instance(self, pki_user, host_name, dm_password,
                            admin_password, ds_port=DEFAULT_DSPORT,
                            pkcs12_info=None, master_host=None, csr_file=None,
@@ -441,6 +451,7 @@ class CAInstance(service.Service):
                 self.step("creating CA agent PKCS#12 file in /root", self.__create_ca_agent_pkcs12)
             self.step("creating RA agent certificate database", self.__create_ra_agent_db)
             self.step("importing CA chain to RA certificate database", self.__import_ca_chain)
+            self.step("restarting certificate server", self.__restart_instance)
             if not self.clone:
                 self.step("requesting RA certificate from CA", self.__request_ra_certificate)
                 self.step("issuing RA agent certificate", self.__issue_ra_cert)
@@ -626,6 +637,18 @@ class CAInstance(service.Service):
     def __restart_instance(self):
         try:
             self.restart()
+            # Wait until the dogtag webapp responds
+            while True:
+                try:
+                    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+                    s.connect(('localhost', 9180))
+                    s.close()
+                    break
+                except socket.error, e:
+                    if e.errno == 111: # Connection refused
+                        time.sleep(1)
+                    else:
+                        raise e
         except Exception:
             # TODO: roll back here?
             logging.critical("Failed to restart the certificate server. See the installation log for details.")
-- 
1.7.3.4

_______________________________________________
Freeipa-devel mailing list
[email protected]
https://www.redhat.com/mailman/listinfo/freeipa-devel

Reply via email to